`
hideto
  • 浏览: 2666315 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Rails源码研究之ActionController:一,基本架构、render、redirect

    博客分类:
  • Ruby
阅读更多
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
分享到:
评论
1 楼 wuxu1314 2007-10-07  
def self.view_class
 [color=blue]@view_class ||=[/color]
   # create a new class based on the default template class and include helper methods
 [color=red]returning[/color] Class.new(ActionView::Base) do |view_class|
       view_class.send(:include, master_helper_module)
        end
end

def initialize_template_class(response)
        raise "You must assign a template class through ActionController.template_class= before processing a request" unless @@template_class

        response.template = [color=blue]self.class.view_class.new[/color](self.class.view_root, {}, self)
        response.redirected_to = nil
        @performed_render = @performed_redirect = false
      end


我不太理解self.view_class,首先关键字returning没见过,我没理解template_class的意思,response.template为什么不直接用
ActiveView::Base.new()来构造,希望能够帮忙解惑,谢谢了!

相关推荐

    Rails的精简版本Rails::API.zip

    Rails::API 移除了 ActionView 和其他一些渲染功能,不关心Web前端的开发者可更容易、快速地开发应用程序,因此运行速度比正常的 Rails 应用程序要快。 Rails::API 可以用来创建只提供API服务(API-Only)的 Rails ...

    Rails上的API:使用Rails构建REST APIAPIs on Rails: Building REST APIs with Rails

    Rails以其简洁优雅的语法、高效的开发速度以及良好的社区支持而闻名,这使得它成为构建现代API的理想选择之一。 ### 一、什么是RESTful API REST(Representational State Transfer)是一种软件架构风格,用于定义...

    Rails进行敏捷Web开发(所有版本的源码rails3.0-4.0)

    1. Rails 3.0: Rails 3是重大升级,引入了ActionController::Metal,这是一个轻量级的控制器,用于提高性能。同时,它引入了多路由引擎支持,如Rack中间件,使得与其他Web服务器的集成更加容易。此外,ActiveRecord...

    rails-exporter-源码.rar

    Rails 是 Ruby 语言的一个 web 开发框架,以其 MVC(Model-View-Controller)架构闻名,提供了一套高效、简洁的开发模式。在 Rails 应用中,模型负责数据操作,视图负责展示,控制器则作为两者之间的桥梁,处理用户...

    Rails中应用Ext.tree:以中国的省市地区三级联动选择为例

    Rails是基于Ruby语言的开源Web开发框架,它遵循MVC(模型-视图-控制器)架构模式,提倡DRY(Don't Repeat Yourself)原则,提供了一种简洁、高效的方式来构建动态网站。而Ext.js是一个JavaScript库,专门用于构建富...

    live-stream:Rails 4 ActionController Live Stream 演示简单的聊天应用程序

    使用 Rails 4 的简单聊天应用程序 - ActionController::Live 应用组件: 1 . 使用 Rails 4 ActionController::Live 的聊天应用程序 2 . 基本 LDAP 身份验证 3 . Redis 服务器集成 4 . 彪马服务器 1 . Rails 4 ...

    ruby on rails 教程源码

    Ruby on Rails,简称Rails,是基于Ruby语言的开源Web应用框架,它遵循MVC(Model-View-Controller)架构模式,旨在使开发过程更加简洁高效。这个“ruby on rails 教程源码”很可能是为了辅助学习者深入理解Rails的...

    Rails项目源代码

    Ruby on Rails,通常简称为Rails,是一个基于Ruby编程语言的开源Web应用框架,遵循MVC(Model-View-Controller)架构模式。这个“Rails项目源代码”是一个使用Rails构建的图片分享网站的完整源代码,它揭示了如何...

    Rails 3 in Action

    《Rails 3 in Action》是2011年由Ryan Bigg撰写的一本关于Ruby on Rails框架的权威指南,专门针对当时最新的Rails 3.1版本进行了深入解析。这本书旨在帮助开发者充分利用Rails 3.1的强大功能,提升Web应用开发的效率...

    Web开发敏捷之道--应用Rails进行敏捷Web开发 之 Depot代码。

    标题中的“Web开发敏捷之道--应用Rails进行敏捷Web开发 之 Depot代码”表明这是一个关于使用Ruby on Rails框架进行敏捷Web开发的示例项目,名为Depot。Ruby on Rails(简称Rails)是一个开源的Web应用程序框架,它...

    Rails相关电子书汇总

    Ruby on Rails,通常简称为Rails,是一个基于Ruby语言的开源Web应用程序框架,它遵循MVC(模型-视图-控制器)架构模式,以简洁、高效的代码和“约定优于配置”的理念著称。此压缩包中的"rubyonrails21-cn.pdf"可能是...

    ruby on rails社区网站开发源码

    通过研究这个源码,你可以深入理解Rails的工作原理,学习如何设计和实现社区网站的核心功能,如用户注册、论坛讨论、个人资料管理等。同时,这也是一个绝佳的机会去实践敏捷开发和TDD(测试驱动开发)原则,提升你的...

    基于ruby on rails开发示例源码

    Rails的许多特性,如ActiveRecord(ORM)、ActiveModel、ActionController和ActionView,都是为了支持这种分层架构。 压缩包中的`seanatnci-rails_space-53c56b4`可能是一个具体的Rails项目仓库,包含以下关键组成...

    Rails 4 in Action, Second Edition.pdf

    - **MVC架构**:Rails采用了经典的Model-View-Controller架构,其中模型负责处理业务逻辑,视图负责显示界面,控制器则作为二者之间的桥梁。 - **Active Record**:这是Rails提供的ORM(对象关系映射)解决方案,...

    Ruby on Rails入门经典代码

    Ruby on Rails,简称Rails,是基于Ruby语言的一个开源Web应用程序框架,它遵循MVC(Model-View-Controller)架构模式,旨在使Web开发过程更加高效、简洁。本压缩包中的"Ruby on Rails入门经典代码"提供了新手学习...

    Rails相关电子书汇总二

    Ruby on Rails(简称Rails)是一个开源的Web应用程序框架,它遵循Model-View-Controller(MVC)架构模式,以Ruby编程语言为基础,以其简洁、高效和DRY(Don't Repeat Yourself)的设计哲学著称。 Rubyisms in Rails...

    Ruby on Rails入门例子

    Ruby on Rails,简称Rails,是一种基于Ruby语言的开源Web应用程序框架,它遵循MVC(Model-View-Controller)架构模式,旨在使Web开发过程更加高效、简洁。本篇将通过一个入门实例,深入探讨Rails的基本概念和核心...

    Rails

    标题 "Rails" 指的是 Ruby on Rails,一个开源的Web应用程序框架,它基于Ruby编程语言,遵循MVC(模型-视图-控制器)架构模式。Rails由David Heinemeier Hansson在2004年创建,其设计理念是强调代码的简洁性、DRY...

    bhl_rails_solr-源码.rar

    本文将围绕"bhl_rails_solr-源码.rar"这一主题,深入剖析其内部机制,帮助开发者理解如何在Rails应用中有效利用Solr进行数据检索。 首先,我们要了解bhl_rails_solr的基本结构。源码中包含的核心组件可能包括以下...

Global site tag (gtag.js) - Google Analytics