ruby 代码
- def process(request, response, method = :perform_action, *arguments) #:nodoc:
- initialize_template_class(response)
- assign_shortcuts(request, response)
- initialize_current_url
- assign_names
- forget_variables_added_to_assigns
- log_processing
- send(method, *arguments) #这一步调用perform_action方法,并且传入arguments数组作为参数(虽然perform_action不需要参数)
- assign_default_content_type_and_charset
- response.request = request
- response.prepare! unless component_request?
- response
- ensure
- process_cleanup
- end
初始化视图模板类
java 代码
- 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 = ActionView::Base.new(view_paths, {}, self) #注意,ActionView这个时候出现了,传入本controller对应的view模板的路径
- response.template.extend self.class.master_helper_module
- response.redirected_to = nil
- @performed_render = @performed_redirect = false #标记render和redirect事件都没有发生
- end
step 2:
ruby 代码
- def assign_shortcuts(request, response)
- @_request, @_params, @_cookies = request, request.parameters, request.cookies
- @_response = response
- @_response.session = request.session
- @_session = @_response.session
- @template = @_response.template
- @assigns = @_response.template.assigns
- @_headers = @_response.headers
- end
step 3:
ruby 代码
- def initialize_current_url
- @url = UrlRewriter.new(request, params.clone)
- end
step 4:
经过了这一步,就可以通过调用action_name来获得action的名称
ruby 代码
- def assign_names
- @action_name = (params['action'] || 'index') #取得当前action的名称,如果没有指定action,则默认action为index
- end
step 5:
ruby 代码
- def forget_variables_added_to_assigns
- @variables_added = nil
- end
step 6:
ruby 代码
- def log_processing
- if logger
- logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
- logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
- logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
- end
- end
step 7:
ruby 代码
- 默认对待一个请求的方式是perform_action
- 可以看到对待一个action,rails的处理方式是:
- 1。假设action存在,则执行action代码,如果action代码内没有调用过render方法,就调用render方法
- 2。如果action不存在,则查找method_missing方法,若方法存在,调用之,如果action代码内没有调用过render方法,就调用render方法
- 3。如果视图模板存在而且模板是public的,则直接调用render方法渲染
- 4。以上都不满足,抛出UnkownAction异常。
- 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
- def action_methods
- self.class.action_methods
- end
- def self.action_methods
- @action_methods ||= Set.new(public_instance_methods - hidden_actions)
- end
step 8:
ruby 代码
- step 8:
- def assign_default_content_type_and_charset
- response.content_type ||= Mime::HTML
- response.charset ||= self.class.default_charset unless sending_file?
- end
- 最后,一个process完成后返回一个response
- 由这个process过程引出的,顺便简单看一下render方法
- def render(options = nil, &block) #:doc:
- raise DoubleRenderError, "Can only render or redirect once per action" if performed?
- #可以看出来,在响应action的过程中,如果没有在action中使用过render方法,实际上每次默认都是调用render_for_file方法
- #参数中使用default_template_name方法获得与当前action同名的视图模板的相对路径
- if options.nil?
- return render_for_file(default_template_name, nil, true)
- else
- #render的参数只能是:update或者一个hash
- if options == :update
- options = { :update => true }
- elsif !options.is_a?(Hash)
- raise RenderError, "You called render with invalid options : #{options}"
- end
- end
- if content_type = options[:content_type]
- response.content_type = content_type.to_s
- end
- if location = options[:location]
- response.headers["Location"] = url_for(location)
- end
- 指定不同的渲染方式
- if text = options[:text]
- render_for_text(text, options[:status])
- else
- if file = options[:file]
- render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
- elsif template = options[:template]
- render_for_file(template, options[:status], true)
- elsif inline = options[:inline]
- add_variables_to_assigns
- render_for_text(@template.render_template(options[:type] || :erb, inline, nil, options[:locals] || {}), options[:status])
- #当在action中显式调用render方式时会执行到此分支
- elsif action_name = options[:action]
- template = default_template_name(action_name.to_s)
- #根据:layout来决定调用那个方法
- if options[:layout] && !template_exempt_from_layout?(template)
- render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)
- else
- render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
- end
- elsif xml = options[:xml]
- response.content_type = Mime::XML
- render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
- elsif json = options[:json]
- json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
- response.content_type = Mime::JSON
- render_for_text(json, options[:status])
- elsif partial = options[:partial]
- partial = default_template_name if partial == true
- add_variables_to_assigns
- if collection = options[:collection]
- render_for_text(
- @template.send(:render_partial_collection, partial, collection,
- options[:spacer_template], options[:locals]), options[:status]
- )
- else
- render_for_text(
- @template.send(: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)
- response.content_type = Mime::JS
- render_for_text(generator.to_s)
- elsif options[:nothing]
- # Safari doesn't pass the headers of the return if the response is zero length
- render_for_text(" ", options[:status])
- else
- render_for_file(default_template_name, options[:status], true)
- end
- end
- end