### Home Classes Methods

### In Files

- lib/merb-core/controller/mixins/responder.rb

### Namespace

- MODULE Merb::ResponderMixin::ClassMethods
- CLASS Merb::ResponderMixin::ContentTypeAlreadySet

### Methods

- ::included
- #_accept_types
- #_perform_content_negotiation
- #_provided_formats
- #content_type
- #content_type=
- #does_not_provide
- #only_provides
- #provides

### Files

- CHANGELOG
- LICENSE
- README
- TODO

### Class/Module Index

Quicksearch

- Merb
- Merb::AbstractController
- Merb::AcceptType
- Merb::AuthenticationMixin
- Merb::AuthenticationMixin::BasicAuthentication
- Merb::BootLoader
- Merb::BootLoader::AfterAppLoads
- Merb::BootLoader::BackgroundServices
- Merb::BootLoader::BeforeAppLoads
- Merb::BootLoader::BuildFramework
- Merb::BootLoader::ChooseAdapter
- Merb::BootLoader::Cookies
- Merb::BootLoader::Defaults
- Merb::BootLoader::Dependencies
- Merb::BootLoader::DropPidFile
- Merb::BootLoader::LoadClasses
- Merb::BootLoader::Logger
- Merb::BootLoader::MimeTypes
- Merb::BootLoader::MixinSession
- Merb::BootLoader::RackUpApplication
- Merb::BootLoader::ReloadClasses
- Merb::BootLoader::ReloadClasses::TimedExecutor
- Merb::BootLoader::Router
- Merb::BootLoader::SetupSession
- Merb::BootLoader::SetupStubClasses
- Merb::BootLoader::Templates
- Merb::ConditionalGetMixin
- Merb::Config
- Merb::Config::ConfigBlock
- Merb::Const
- Merb::Controller
- Merb::ControllerExceptions
- Merb::ControllerExceptions::Accepted
- Merb::ControllerExceptions::ActionNotFound
- Merb::ControllerExceptions::BadGateway
- Merb::ControllerExceptions::BadRequest
- Merb::ControllerExceptions::Base
- Merb::ControllerExceptions::ClientError
- Merb::ControllerExceptions::Conflict
- Merb::ControllerExceptions::Continue
- Merb::ControllerExceptions::Created
- Merb::ControllerExceptions::ExpectationFailed
- Merb::ControllerExceptions::Forbidden
- Merb::ControllerExceptions::GatewayTimeout
- Merb::ControllerExceptions::Gone
- Merb::ControllerExceptions::HTTPVersionNotSupported
- Merb::ControllerExceptions::Informational
- Merb::ControllerExceptions::InternalServerError
- Merb::ControllerExceptions::LayoutNotFound
- Merb::ControllerExceptions::LengthRequired
- Merb::ControllerExceptions::MethodNotAllowed
- Merb::ControllerExceptions::MovedPermanently
- Merb::ControllerExceptions::MovedTemporarily
- Merb::ControllerExceptions::MultiPartParseError
- Merb::ControllerExceptions::MultipleChoices
- Merb::ControllerExceptions::NoContent
- Merb::ControllerExceptions::NonAuthoritativeInformation
- Merb::ControllerExceptions::NotAcceptable
- Merb::ControllerExceptions::NotFound
- Merb::ControllerExceptions::NotImplemented
- Merb::ControllerExceptions::NotModified
- Merb::ControllerExceptions::OK
- Merb::ControllerExceptions::PartialContent
- Merb::ControllerExceptions::PaymentRequired
- Merb::ControllerExceptions::PreconditionFailed
- Merb::ControllerExceptions::ProxyAuthenticationRequired
- Merb::ControllerExceptions::Redirection
- Merb::ControllerExceptions::RequestEntityTooLarge
- Merb::ControllerExceptions::RequestRangeNotSatisfiable
- Merb::ControllerExceptions::RequestTimeout
- Merb::ControllerExceptions::RequestURITooLarge
- Merb::ControllerExceptions::ResetContent
- Merb::ControllerExceptions::SeeOther
- Merb::ControllerExceptions::ServerError
- Merb::ControllerExceptions::ServiceUnavailable
- Merb::ControllerExceptions::Successful
- Merb::ControllerExceptions::SwitchingProtocols
- Merb::ControllerExceptions::TemplateNotFound
- Merb::ControllerExceptions::TemporaryRedirect
- Merb::ControllerExceptions::Unauthorized
- Merb::ControllerExceptions::UnsupportedMediaType
- Merb::ControllerExceptions::UseProxy
- Merb::ControllerMixin
- Merb::CookieSession
- Merb::CookieSession::CookieOverflow
- Merb::CookieSession::TamperedWithCookie
- Merb::Cookies
- Merb::CookiesMixin
- Merb::CookiesMixin::RequestMixin
- Merb::Counter
- Merb::Dispatcher
- Merb::Dispatcher::DefaultException
- Merb::Dispatcher::DefaultExceptionHelper
- Merb::GlobalHelpers
- Merb::InlineTemplates
- Merb::Logger
- Merb::MemcacheSession
- Merb::MemcacheStore
- Merb::MemorySession
- Merb::MemorySessionStore
- Merb::Parse
- Merb::Plugins
- Merb::Rack
- Merb::Rack::AbstractAdapter
- Merb::Rack::Adapter
- Merb::Rack::Application
- Merb::Rack::ConditionalGet
- Merb::Rack::Console
- Merb::Rack::ContentLength
- Merb::Rack::DeferrableMiddleware
- Merb::Rack::Ebb
- Merb::Rack::EventedMongrel
- Merb::Rack::FastCGI
- Merb::Rack::Handler
- Merb::Rack::Handler::Mongrel
- Merb::Rack::Head
- Merb::Rack::Helpers
- Merb::Rack::Irb
- Merb::Rack::Middleware
- Merb::Rack::Mongrel
- Merb::Rack::PathPrefix
- Merb::Rack::Profiler
- Merb::Rack::Runner
- Merb::Rack::Static
- Merb::Rack::StreamWrapper
- Merb::Rack::SwiftipliedMongrel
- Merb::Rack::Thin
- Merb::Rack::ThinTurbo
- Merb::Rack::Tracer
- Merb::Rack::WEBrick
- Merb::RenderMixin
- Merb::RenderMixin::ClassMethods
- Merb::Request
- Merb::ReservedError
- Merb::Responder
- Merb::ResponderMixin
- Merb::ResponderMixin::ClassMethods
- Merb::ResponderMixin::ContentTypeAlreadySet
- Merb::Router
- Merb::Router::Behavior
- Merb::Router::Behavior::Error
- Merb::Router::Behavior::Proxy
- Merb::Router::CachedProc
- Merb::Router::GenerationError
- Merb::Router::NotCompiledError
- Merb::Router::Resources
- Merb::Router::RouteNotFound
- Merb::Server
- Merb::Session
- Merb::SessionContainer
- Merb::SessionMixin
- Merb::SessionMixin::NoSessionContainer
- Merb::SessionMixin::RequestMixin
- Merb::SessionMixin::RequestMixin::ClassMethods
- Merb::SessionMixin::SessionOverflow
- Merb::SessionStoreContainer
- Merb::System
- Merb::System::PortablePoller
- Merb::Template
- Merb::Template::Erubis
- Merb::Template::Erubis::Mixin
- Merb::Test
- Merb::Test::ControllerHelper
- Merb::Test::Cookie
- Merb::Test::CookieJar
- Merb::Test::ExampleGroup
- Merb::Test::Helpers
- Merb::Test::MakeRequest
- Merb::Test::Matchers
- Merb::Test::MultipartRequestHelper
- Merb::Test::MultipartRequestHelper::FileParam
- Merb::Test::MultipartRequestHelper::Param
- Merb::Test::MultipartRequestHelper::Post
- Merb::Test::RequestHelper
- Merb::Test::RequestHelper::CookieJar
- Merb::Test::RequestHelper::FakeRequest
- Merb::Test::RouteHelper
- Merb::Test::Rspec
- Merb::Test::Rspec::ControllerMatchers
- Merb::Test::Rspec::ControllerMatchers::BeError
- Merb::Test::Rspec::ControllerMatchers::BeRedirect
- Merb::Test::Rspec::ControllerMatchers::Provide
- Merb::Test::Rspec::RouteMatchers
- Merb::Test::Rspec::RouteMatchers::ParameterMatcher
- Merb::Test::Rspec::RouteMatchers::RouteToMatcher
- Merb::Test::ViewHelper
- Merb::Test::WebratHelper
- Merb::Worker
- Erubis
- Erubis::Basic
- Erubis::Basic::Converter
- Erubis::BlockAwareEnhancer
- Erubis::BlockAwareEruby
- Erubis::MEruby
- Spec
- Spec::Matchers
- Spec::Matchers::MatcherDSL
- Spec::Runner
- Spec::Runner::Formatter
- Spec::Runner::Formatter::BaseTextFormatter
- Gem
- Gem::Dependency
- Hpricot
- Hpricot::Elem
- Mongrel
- Mongrel::HttpResponse
- Class
- Exception
- Hash
- Kernel
- MemCache
- Memcached
- Object
- String
- Time

No matching classes.

# Merb::ResponderMixin

The ResponderMixin adds methods that help you manage what formats your controllers have available, determine what format(s) the client requested and is capable of handling, and perform content negotiation to pick the proper content format to deliver.

If you hear someone say "Use provides" they're talking about the Responder. If you hear someone ask "What happened to respond_to?" it was replaced by provides and the other Responder methods.

## A simple example

The best way to understand how all of these pieces fit together is with an example. Here's a simple web-service ready resource that provides a list of all the widgets we know about. The widget list is available in 3 formats: :html (the default), plus :xml and :text.

    class Widgets < Application
      provides :html   # This is the default, but you can
                       # be explicit if you like.
      provides :xml, :text

      def index
        @widgets = Widget.fetch
        render @widgets
      end
    end

Let's look at some example requests for this list of widgets. We'll assume they're all GET requests, but that's only to make the examples easier; this works for the full set of RESTful methods.

1. The simplest case, /widgets.html Since the request includes a specific format (.html) we know what format to return. Since :html is in our list of provided formats, that's what we'll return. `render` will look for an index.html.erb (or another template format like index.html.mab; see the documentation on Template engines)

2. Almost as simple, /widgets.xml This is very similar. They want :xml, we have :xml, so that's what they get. If `render` doesn't find an index.xml.builder or similar template, it will call `to_xml` on @widgets. This may or may not do something useful, but you can see how it works.

3. A browser request for /widgets This time the URL doesn't say what format is being requested, so we'll look to the HTTP Accept: header. If it's '/' (anything), we'll use the first format on our list, :html by default.

   If it parses to a list of accepted formats, we'll look through them, in order, until we find one we have available. If we find one, we'll use that. Otherwise, we can't fulfill the request: they asked for a format we don't have. So we raise 406: Not Acceptable.

## A more complex example

Sometimes you don't have the same code to handle each available format. Sometimes you need to load different data to serve /widgets.xml versus /widgets.txt. In that case, you can use `content_type` to determine what format will be delivered.

    class Widgets < Application
      def action1
        if content_type == :text
          Widget.load_text_formatted(params[:id])
        else
          render
        end
      end

      def action2
        case content_type
        when :html
          handle_html()
        when :xml
          handle_xml()
        when :text
          handle_text()
        else
          render
        end
      end
    end

You can do any standard Ruby flow control using `content_type`. If you don't call it yourself, it will be called (triggering content negotiation) by `render`.

Once `content_type` has been called, the output format is frozen, and none of the provides methods can be used.

### Constants

ACCEPT_RESULTS
MIMES
MIME_MUTEX
TYPES

### Public Class Methods

included(base) click to toggle source

#### Parameters

base<Module> | The module that ResponderMixin was mixed into

:api: private

    # File lib/merb-core/controller/mixins/responder.rb, line 111
    def self.included(base)
      base.extend(ClassMethods)
      base.class_eval do
        class_inheritable_accessor :class_provided_formats
        self.class_provided_formats = []
      end
      base.reset_provides
    end

### Public Instance Methods

_accept_types() click to toggle source

    # File lib/merb-core/controller/mixins/responder.rb, line 262
    def _accept_types
      accept = request.accept

      MIME_MUTEX.synchronize do
        return ACCEPT_RESULTS[accept] if ACCEPT_RESULTS[accept]
      end

      types = request.accept.split(Merb::Const::ACCEPT_SPLIT).map do |entry|
        entry =~ Merb::Const::MEDIA_RANGE
        media_range, quality = $1, $3

        kind, sub_type = media_range.split(Merb::Const::SLASH_SPLIT)
        mime_sym = Merb.available_accepts[media_range]
        mime = Merb.available_mime_types[mime_sym]
        (quality ||= 0.0) if media_range == "*/*"
        quality = quality ? (quality.to_f * 100).to_i : 100
        quality *= (mime && mime[:default_quality] || 1)
        [quality, mime_sym, media_range, kind, sub_type, mime]
      end

      accepts = types.sort_by {|x| x.first }.reverse!.map! {|x| x[1]}

      MIME_MUTEX.synchronize do
        ACCEPT_RESULTS[accept] = accepts.freeze
      end

      accepts
    end

_perform_content_negotiation() click to toggle source

Do the content negotiation:

1. if params is there, and provided, use it

2. Parse the Accept header

3. If it's /, use the first provided format

4. Look for one that is provided, in order of request

5. Raise 406 if none found

:api: private

    # File lib/merb-core/controller/mixins/responder.rb, line 299
    def _perform_content_negotiation
      if fmt = params[:format] and !fmt.blank?
        accepts = [fmt.to_sym]
      else
        accepts = _accept_types
      end

      provided_formats = _provided_formats

      specifics = accepts & provided_formats
      return specifics.first unless specifics.length == 0
      return provided_formats.first if accepts.include?(:all) && !provided_formats.empty?

      message  = "A format (%s) that isn't provided (%s) has been requested. "
      message += "Make sure the action provides the format, and be "
      message += "careful of before filters which won't recognize "
      message += "formats provided within actions."
      raise Merb::ControllerExceptions::NotAcceptable,
        (message % [accepts.join(', '), provided_formats.join(', ')])
    end

_provided_formats() click to toggle source

#### Returns

Array | The current list of formats provided for this instance of the controller. It starts with what has been set in the controller (or :html by default) but can be modifed on a per-action basis.

:api: private

    # File lib/merb-core/controller/mixins/responder.rb, line 201
    def _provided_formats
      @_provided_formats ||= class_provided_formats.dup
    end

content_type(fmt = nil) click to toggle source

Returns the output format for this request, based on the provided formats, `params[:format]` and the client's HTTP Accept header.

The first time this is called, it triggers content negotiation and caches the value. Once you call `content_type` you can not set or change the list of provided formats.

Called automatically by `render`, so you should only call it if you need the value, not to trigger content negotiation.

#### Parameters

fmt<String> | An optional format to use instead of performing content negotiation. This can be used to pass in the values of opts from the render function to short-circuit content-negotiation when it's not necessary. This optional parameter should not be considered part of the public API.

#### Returns

Symbol | The content-type that will be used for this controller.

:api: public

    # File lib/merb-core/controller/mixins/responder.rb, line 345
    def content_type(fmt = nil)
      self.content_type = (fmt || _perform_content_negotiation) unless @_content_type
      @_content_type
    end

content_type=(type) click to toggle source

Sets the content type of the current response to a value based on a passed in key. The Content-Type header will be set to the first registered header for the mime-type.

#### Parameters

type<Symbol> | The content type.

#### Raises

ArgumentError | type is not in the list of registered mime-types.

#### Returns

Symbol | The content-type that was passed in.

:api: plugin

    # File lib/merb-core/controller/mixins/responder.rb, line 364
    def content_type=(type)
      unless Merb.available_mime_types.has_key?(type)
        raise Merb::ControllerExceptions::NotAcceptable.new("Unknown content_type for response: #{type}")
      end

      @_content_type = type

      mime = Merb.available_mime_types[type]

      headers["Content-Type"] = mime[:content_type]

      # merge any format specific response headers
      mime[:response_headers].each { |k,v| headers[k] ||= v }

      # if given, use a block to finetune any runtime headers
      mime[:response_block].call(self) if mime[:response_block]

      @_content_type
    end

does_not_provide(*formats) click to toggle source

Removes formats from the list of provided formats for this particular request. Usually used to remove formats from a single action. See also the controller-level does_not_provide that affects all actions in a controller.

#### Parameters

*formats<Symbol> | Registered mime-type

#### Returns

Array | List of formats that remain after removing the ones not to provide.

:api: public

    # File lib/merb-core/controller/mixins/responder.rb, line 258
    def does_not_provide(*formats)
      @_provided_formats -= formats.flatten
    end

only_provides(*formats) click to toggle source

Sets list of provided formats for this particular request. Usually used to limit formats to a single action. See also the controller-level only_provides that affects all actions in a controller.

#### Parameters

*formats<Symbol> | A list of formats to use as the per-action list of provided formats.

#### Returns

Array | List of formats passed in.

:api: public

    # File lib/merb-core/controller/mixins/responder.rb, line 240
    def only_provides(*formats)
      @_provided_formats = []
      provides(*formats)
    end

provides(*formats) click to toggle source

Adds formats to the list of provided formats for this particular request. Usually used to add formats to a single action. See also the controller-level provides that affects all actions in a controller.

#### Parameters

*formats<Symbol> | A list of formats to add to the per-action list of provided formats.

#### Raises

Merb::ResponderMixin::ContentTypeAlreadySet | Content negotiation already occured, and the content_type is set.

#### Returns

Array | List of formats passed in.

:api: public

    # File lib/merb-core/controller/mixins/responder.rb, line 221
    def provides(*formats)
      if @_content_type
        raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set"
      end
      @_provided_formats = self._provided_formats | formats # merges with class_provided_formats if not already
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.