原创作者: hideto   阅读:1192次   评论:0条   更新时间:2011-05-26    
Rails从HTTP Accept header得到客户端需要的response format信息
默认的MIME types见mime_type.rb:
  ALL   = Type.new "*/*", :all
  TEXT  = Type.new "text/plain", :text
  HTML  = Type.new "text/html", :html, %w( application/xhtml+xml )
  JS    = Type.new "text/javascript", :js, %w( application/javascript application/x-javascript )
  ICS   = Type.new "text/calendar", :ics
  CSV   = Type.new "text/csv", :csv
  XML   = Type.new "application/xml", :xml, %w( text/xml application/x-xml )
  RSS   = Type.new "application/rss+xml", :rss
  ATOM  = Type.new "application/atom+xml", :atom
  YAML  = Type.new "application/x-yaml", :yaml, %w( text/yaml )
  JSON  = Type.new "application/json", :json, %w( text/x-json )

  SET   = [ ALL, TEXT, HTML, JS, ICS, XML, RSS, ATOM, YAML, JSON ]

def register(string, symbol, synonyms = [])
  Mime.send :const_set, symbol.to_s.upcase, Type.new(string, symbol, synonyms)
  SET << Mime.send(:const_get, symbol.to_s.upcase)
  LOOKUP[string] = EXTENSION_LOOKUP[symbol.to_s] = SET.last        
end

我们可以在environment.rb里注册自己的MIME type:
Mime::Type.register "image/jpg", :jpg


看看源码mime_responds.rb:
module ActionController
  module MimeResponds

    def self.included(base)
      base.send(:include, ActionController::MimeResponds::InstanceMethods)
    end

    module InstanceMethods
      def respond_to(*types, &block)
        raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
        block ||= lambda { |responder| types.each { |type| responder.send(type) } }
        responder = Responder.new(block.binding)
        block.call(responder)
        responder.respond
      end
    end

    class Responder
      def initialize(block_binding)
        @block_binding = block_binding
        @mime_type_priority = eval(
          "(params[:format] && Mime::EXTENSION_LOOKUP[params[:format]]) ? " +
          "[ Mime::EXTENSION_LOOKUP[params[:format]] ] : request.accepts", 
          block_binding
        )
        @order     = []
        @responses = {}
      end

      def custom(mime_type, &block)
        mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
        
        @order << mime_type
        
        if block_given?
          @responses[mime_type] = Proc.new do
            eval "response.content_type = '#{mime_type.to_s}'", @block_binding
            block.call
          end
        else
          if source = DEFAULT_BLOCKS[mime_type.to_sym]
            @responses[mime_type] = eval(source, @block_binding)
          else
            raise ActionController::RenderError, "Expected a block but none was given for custom mime handler #{mime_type}"
          end
        end
      end

      def method_missing(symbol, &block)
        mime_constant = symbol.to_s.upcase
        
        if Mime::SET.include?(Mime.const_get(mime_constant))
          custom(Mime.const_get(mime_constant), &block)
        else
          super
        end
      end

      def respond
        for priority in @mime_type_priority
          if priority == Mime::ALL
            @responses[@order.first].call
            return
          else
            if priority === @order
              @responses[priority].call
              return
            end
          end
        end
        if @order.include?(Mime::ALL)
          @responses[Mime::ALL].call
        else
          eval 'render(:nothing => true, :status => "406 Not Acceptable")', @block_binding
        end
      end
    end

  end
end

我们看到,respond_to方法调用responder.send(type),如format.js,但是Responder类没有js方法,所以调用了method_missing方法
method_missing调用了custom方法,custom则eval了response.content_type的设置,然后block.call
最后调用responder.respond,@responses[priority].call调用了content_type的设置以及参数给的block
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics