原创作者: hideto   阅读:2047次   评论:0条   更新时间:2011-05-26    
1,action_controller.rb:
$:.unshift(File.dirname(__FILE__)) unless
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

unless defined?(ActiveSupport)
  begin
    $:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
    require 'active_support'  
  rescue LoadError
    require 'rubygems'
    gem 'activesupport'
  end
end

require 'action_controller/base'
require 'action_controller/deprecated_redirects'
require 'action_controller/request'
require 'action_controller/deprecated_request_methods'
require 'action_controller/rescue'
require 'action_controller/benchmarking'
require 'action_controller/flash'
require 'action_controller/filters'
require 'action_controller/layout'
require 'action_controller/deprecated_dependencies'
require 'action_controller/mime_responds'
require 'action_controller/pagination'
require 'action_controller/scaffolding'
require 'action_controller/helpers'
require 'action_controller/cookies'
require 'action_controller/cgi_process'
require 'action_controller/caching'
require 'action_controller/verification'
require 'action_controller/streaming'
require 'action_controller/session_management'
require 'action_controller/components'
require 'action_controller/macros/auto_complete'
require 'action_controller/macros/in_place_editing'

require 'action_view'
ActionController::Base.template_class = ActionView::Base

ActionController::Base.class_eval do
  include ActionController::Flash
  include ActionController::Filters
  include ActionController::Layout
  include ActionController::Benchmarking
  include ActionController::Rescue
  include ActionController::Dependencies
  include ActionController::MimeResponds
  include ActionController::Pagination
  include ActionController::Scaffolding
  include ActionController::Helpers
  include ActionController::Cookies
  include ActionController::Caching
  include ActionController::Verification
  include ActionController::Streaming
  include ActionController::SessionManagement
  include ActionController::Components
  include ActionController::Macros::AutoComplete
  include ActionController::Macros::InPlaceEditing
end

和active_record.rb很类似,加载当前文件,加载activesupport,加载一坨一坨的组件

2,base.rb:
require 'action_controller/mime_type'
require 'action_controller/request'
require 'action_controller/response'
require 'action_controller/routing'
require 'action_controller/resources'
require 'action_controller/url_rewriter'
require 'action_controller/status_codes'
require 'drb'
require 'set'

module ActionController

  class Base
    DEFAULT_RENDER_STATUS_CODE = "200 OK"

    include StatusCodes

    @@default_charset = "utf-8"
    cattr_accessor :default_charset

    class_inheritable_accessor :template_root

    cattr_accessor :logger

    attr_internal :request

    attr_internal :params

    attr_internal :response

    attr_internal :session

    attr_internal :headers

    attr_accessor :action_name

    class << self

      def controller_class_name
        @controller_class_name ||= name.demodulize
      end

      def controller_name
        @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore
      end

    end

    public

      def process(request, response, method = :perform_action, *arguments)
        initialize_template_class(response)
        assign_shortcuts(request, response)
        initialize_current_url
        assign_names
        forget_variables_added_to_assigns

        log_processing
        send(method, *arguments)

        assign_default_content_type_and_charset
        response
      ensure
        process_cleanup
      end

      def url_for(options = {}, *parameters_for_method_reference)
        case options
          when String
            options

          when Symbol
            ActiveSupport::Deprecation.warn(
              "You called url_for(:#{options}), which is a deprecated API call. Instead you should use the named " +
              "route directly, like #{options}(). Using symbols and parameters with url_for will be removed from Rails 2.0.",
              caller
            )

            send(options, *parameters_for_method_reference)

          when Hash
            @url.rewrite(rewrite_options(options))
        end
      end

      def controller_class_name
        self.class.controller_class_name
      end

      def controller_name
        self.class.controller_name
      end

      def session_enabled?
        request.session_options && request.session_options[:disabled] != false
      end

    protected

      def render(options = nil, deprecated_status = nil, &block)
        raise DoubleRenderError, "Can only render or redirect once per action" if performed?

        if options.nil?
          return render_file(default_template_name, deprecated_status, true)
        else
          unless options.is_a?(Hash)
            if options == :update
              options = { :update => true }
            else
              ActiveSupport::Deprecation.warn(
                "You called render('#{options}'), which is a deprecated API call. Instead you use " +
                "render :file => #{options}. Calling render with just a string will be removed from Rails 2.0.",
                caller
              )

              return render_file(options, deprecated_status, true)
            end
          end
        end

        if content_type = options[:content_type]
          response.content_type = content_type.to_s
        end

        if text = options[:text]
          render_text(text, options[:status])

        else
          if file = options[:file]
            render_file(file, options[:status], options[:use_full_path], options[:locals] || {})

          elsif template = options[:template]
            render_file(template, options[:status], true)

          elsif inline = options[:inline]
            render_template(inline, options[:status], options[:type], options[:locals] || {})

          elsif action_name = options[:action]
            ActiveSupport::Deprecation.silence do
              render_action(action_name, options[:status], options[:layout])
            end

          elsif xml = options[:xml]
            render_xml(xml, options[:status])

          elsif json = options[:json]
            render_json(json, options[:callback], options[:status])

          elsif partial = options[:partial]
            partial = default_template_name if partial == true
            if collection = options[:collection]
              render_partial_collection(partial, collection, options[:spacer_template], options[:locals], options[:status])
            else
              render_partial(partial, ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals], options[:status])
            end

          elsif options[:update]
            add_variables_to_assigns
            @template.send :evaluate_assigns

            generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
            render_javascript(generator.to_s)

          elsif options[:nothing]
            render_text(" ", options[:status])

          else
            render_file(default_template_name, options[:status], true)

          end
        end
      end

      def render_action(action_name, status = nil, with_layout = true)
        template = default_template_name(action_name.to_s)
        if with_layout && !template_exempt_from_layout?(template)
          render_with_layout(:file => template, :status => status, :use_full_path => true, :layout => true)
        else
          render_without_layout(:file => template, :status => status, :use_full_path => true)
        end
      end

      def render_file(template_path, status = nil, use_full_path = false, locals = {})
        add_variables_to_assigns
        assert_existence_of_template_file(template_path) if use_full_path
        logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
        render_text(@template.render_file(template_path, use_full_path, locals), status)
      end

      def render_template(template, status = nil, type = :rhtml, local_assigns = {})
        add_variables_to_assigns
        render_text(@template.render_template(type, template, nil, local_assigns), status)
      end

      def render_text(text = nil, status = nil, append_response = false)
        @performed_render = true

        response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)

        if append_response
          response.body ||= ''
          response.body << text
        else
          response.body = text
        end
      end

      def render_javascript(javascript, status = nil, append_response = true)
        response.content_type = Mime::JS
        render_text(javascript, status, append_response)
      end

      def render_xml(xml, status = nil)
        response.content_type = Mime::XML
        render_text(xml, status)
      end

      def render_json(json, callback = nil, status = nil)
        json = "#{callback}(#{json})" unless callback.blank?

        response.content_type = Mime::JSON
        render_text(json, status)
      end

      def render_nothing(status = nil)
        render_text(' ', status)
      end

      def render_partial(partial_path = default_template_name, object = nil, local_assigns = nil, status = nil)
        add_variables_to_assigns
        render_text(@template.render_partial(partial_path, object, local_assigns), status)
      end

      def render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = nil, status = nil)
        add_variables_to_assigns
        render_text(@template.render_partial_collection(partial_name, collection, partial_spacer_template, local_assigns), status)
      end

      def render_with_layout(template_name = default_template_name, status = nil, layout = nil)
        render_with_a_layout(template_name, status, layout)
      end

      def render_without_layout(template_name = default_template_name, status = nil)
        render_with_no_layout(template_name, status)
      end


      def head(*args)
        if args.length > 2
          raise ArgumentError, "too many arguments to head"
        elsif args.empty?
          raise ArgumentError, "too few arguments to head"
        elsif args.length == 2
          status = args.shift
          options = args.shift
        elsif args.first.is_a?(Hash)
          options = args.first
        else
          status = args.first
          options = {}
        end

        raise ArgumentError, "head requires an options hash" if !options.is_a?(Hash)

        status = interpret_status(status || options.delete(:status) || :ok)

        options.each do |key, value|
          headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
        end

        render :nothing => true, :status => status
      end

      def erase_render_results
        response.body = nil
        @performed_render = false
      end

      def erase_redirect_results
        @performed_redirect = false
        response.redirected_to = nil
        response.redirected_to_method_params = nil
        response.headers['Status'] = DEFAULT_RENDER_STATUS_CODE
        response.headers.delete('Location')
      end

      def erase_results
        erase_render_results
        erase_redirect_results
      end

      def redirect_to(options = {}, *parameters_for_method_reference)
        case options
          when %r{^\w+://.*}
            raise DoubleRenderError if performed?
            logger.info("Redirected to #{options}") if logger
            response.redirect(options)
            response.redirected_to = options
            @performed_redirect = true

          when String
            redirect_to(request.protocol + request.host_with_port + options)

          when :back
            request.env["HTTP_REFERER"] ? redirect_to(request.env["HTTP_REFERER"]) : raise(RedirectBackError)

          else
            if parameters_for_method_reference.empty?
              redirect_to(url_for(options))
              response.redirected_to = options
            else
              redirect_to(url_for(options, *parameters_for_method_reference))
              response.redirected_to, response.redirected_to_method_params = options, parameters_for_method_reference
            end
        end
      end

      def expires_in(seconds, options = {})
        cache_options = { 'max-age' => seconds, 'private' => true }.symbolize_keys.merge!(options.symbolize_keys)
        cache_options.delete_if { |k,v| v.nil? or v == false }
        cache_control = cache_options.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
        response.headers["Cache-Control"] = cache_control.join(', ')
      end

      def expires_now
        response.headers["Cache-Control"] = "no-cache"
      end

      def reset_session
        request.reset_session
        @_session = request.session
        response.session = @_session
      end

    private
      def perform_action
        if self.class.action_methods.include?(action_name)
          send(action_name)
          render unless performed?
        elsif respond_to? :method_missing
          send(:method_missing, action_name)
          render unless performed?
        elsif template_exists? && template_public?
          render
        else
          raise UnknownAction, "No action responded to #{action_name}", caller
        end
      end

  end
end

节选了部分代码,调用流程为process -> perform_action => action || method_missing || template || raise UnknownAction
其中需要注意的几点:
1,@@default_charset = "utf-8"
2,attr_internal的属性有request、params、response、session、headers
3,cattr_accessor的属性有default_charset、logger、consider_all_requests_local等
catter_accessor标识的是Class Attributes,见这篇BlogSo, cattr_accessor doesn’t work like it should?
catter_accessor方法的定义在active_support\core_ext\class\attribute_accessors.rb:
class Class
  def cattr_reader(*syms)
    syms.flatten.each do |sym|
      next if sym.is_a?(Hash)
      class_eval(<<-EOS, __FILE__, __LINE__)
        unless defined? @@#{sym}
          @@#{sym} = nil
        end

        def self.#{sym}
          @@#{sym}
        end

        def #{sym}
          @@#{sym}
        end
      EOS
    end
  end

  def cattr_writer(*syms)
    options = syms.last.is_a?(Hash) ? syms.pop : {}
    syms.flatten.each do |sym|
      class_eval(<<-EOS, __FILE__, __LINE__)
        unless defined? @@#{sym}
          @@#{sym} = nil
        end

        def self.#{sym}=(obj)
          @@#{sym} = obj
        end

        #{"
        def #{sym}=(obj)
          @@#{sym} = obj
        end
        " unless options[:instance_writer] == false }
      EOS
    end
  end

  def cattr_accessor(*syms)
    cattr_reader(*syms)
    cattr_writer(*syms)
  end
end

4,render方法的参数有:
1)nil -> render_file
2):text -> render_text
3):file -> render_file
4):template -> render_file
5):inline -> render_template
6):action -> render_action
7):xml -> render_xml
8):json -> render_json
9):partial -> render_partial || render_partial_collection
10):update -> render_javascript
11):noting -> render_text
5,redirect_to方法的参数有:
1)Hash -> redirect_to(url_for(options))
redirect_to :action => "show", :id => 5

2)URL -> response.redirect(options)
redirect_to "http://www.rubyonrails.org"

3)String -> redirect_to(request.protocol + request.host_with_port + options)
redirect_to "/images/screenshot.jpg"

4):back -> redirect_to(request.env["HTTP_REFERER"])
redirect_to :back
评论 共 0 条 请登录后发表评论

发表评论

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

文章信息

Global site tag (gtag.js) - Google Analytics