diff options
Diffstat (limited to 'actionpack/lib')
24 files changed, 286 insertions, 296 deletions
diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb index 2264d5d3a3..bcbb570e4b 100644 --- a/actionpack/lib/action_controller/assertions/selector_assertions.rb +++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb @@ -396,54 +396,31 @@ module ActionController # # The same, but shorter. # assert_select "ol>li", 4 def assert_select_rjs(*args, &block) - rjs_type = nil - arg = args.shift + rjs_type = args.first.is_a?(Symbol) ? args.shift : nil + id = args.first.is_a?(String) ? args.shift : nil # If the first argument is a symbol, it's the type of RJS statement we're looking # for (update, replace, insertion, etc). Otherwise, we're looking for just about # any RJS statement. - if arg.is_a?(Symbol) - rjs_type = arg - + if rjs_type if rjs_type == :insert - arg = args.shift - position = arg - insertion = "insert_#{arg}".to_sym - raise ArgumentError, "Unknown RJS insertion type #{arg}" unless RJS_STATEMENTS[insertion] + position = args.shift + insertion = "insert_#{position}".to_sym + raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion] statement = "(#{RJS_STATEMENTS[insertion]})" else raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type] statement = "(#{RJS_STATEMENTS[rjs_type]})" end - arg = args.shift else statement = "#{RJS_STATEMENTS[:any]}" end - position ||= Regexp.new(RJS_INSERTIONS.join('|')) # Next argument we're looking for is the element identifier. If missing, we pick - # any element. - if arg.is_a?(String) - id = Regexp.quote(arg) - arg = args.shift - else - id = "[^\"]*" - end - - pattern = - case rjs_type - when :chained_replace, :chained_replace_html - Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE) - when :remove, :show, :hide, :toggle - Regexp.new("#{statement}\\(\"#{id}\"\\)") - when :replace, :replace_html - Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)") - when :insert, :insert_html - Regexp.new("Element.insert\\(\\\"#{id}\\\", \\{ #{position}: #{RJS_PATTERN_HTML} \\}\\);") - else - Regexp.union(Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)"), - Regexp.new("Element.insert\\(\\\"#{id}\\\", \\{ #{position}: #{RJS_PATTERN_HTML} \\}\\);")) - end + # any element, otherwise we replace it in the statement. + pattern = Regexp.new( + id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement + ) # Duplicate the body since the next step involves destroying it. matches = nil @@ -588,26 +565,23 @@ module ActionController protected unless const_defined?(:RJS_STATEMENTS) - RJS_STATEMENTS = { - :replace => /Element\.replace/, - :replace_html => /Element\.update/, - :chained_replace => /\.replace/, - :chained_replace_html => /\.update/, - :remove => /Element\.remove/, - :show => /Element\.show/, - :hide => /Element\.hide/, - :toggle => /Element\.toggle/ + RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\"" + RJS_ANY_ID = "\"([^\"])*\"" + RJS_STATEMENTS = { + :chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)", + :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)", + :replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", + :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)" } - RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})") - RJS_PATTERN_HTML = /"((\\"|[^"])*)"/ - RJS_INSERTIONS = [:top, :bottom, :before, :after] + [:remove, :show, :hide, :toggle].each do |action| + RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)" + end + RJS_INSERTIONS = ["top", "bottom", "before", "after"] RJS_INSERTIONS.each do |insertion| - RJS_STATEMENTS["insert_#{insertion}".to_sym] = /Element.insert\(\"([^\"]*)\", \{ #{insertion.to_s.downcase}: #{RJS_PATTERN_HTML} \}\);/ + RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)" end - RJS_STATEMENTS[:insert_html] = Regexp.new(RJS_INSERTIONS.collect do |insertion| - /Element.insert\(\"([^\"]*)\", \{ #{insertion.to_s.downcase}: #{RJS_PATTERN_HTML} \}\);/ - end.join('|')) - RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE) + RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)" + RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})") RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/ end @@ -621,8 +595,8 @@ module ActionController root = HTML::Node.new(nil) while true - next if body.sub!(RJS_PATTERN_EVERYTHING) do |match| - html = unescape_rjs($3) + next if body.sub!(RJS_STATEMENTS[:any]) do |match| + html = unescape_rjs(match) matches = HTML::Document.new(html).root.children.select { |n| n.tag? } root.children.concat matches "" diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 32c1dcbcaf..670a049497 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -260,10 +260,11 @@ module ActionController #:nodoc: include StatusCodes + cattr_reader :protected_instance_variables # Controller specific instance variables which will not be accessible inside views. - @@protected_view_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller - @action_name @before_filter_chain_aborted @action_cache_path @_session @_cookies @_headers @_params - @_flash @_response) + @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller + @action_name @before_filter_chain_aborted @action_cache_path @_session @_cookies @_headers @_params + @_flash @_response) # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, # and images to a dedicated asset server away from the main web server. Example: @@ -393,16 +394,9 @@ module ActionController #:nodoc: # directive. Values should always be specified as strings. attr_internal :headers - # Holds the hash of variables that are passed on to the template class to be made available to the view. This hash - # is generated by taking a snapshot of all the instance variables in the current scope just before a template is rendered. - attr_accessor :assigns - # Returns the name of the action this controller is processing. attr_accessor :action_name - # Templates that are exempt from layouts - @@exempt_from_layout = Set.new([/\.rjs$/]) - class << self # Factory for the standard create, process loop where the controller is discarded after processing. def process(request, response) #:nodoc: @@ -520,13 +514,7 @@ module ActionController #:nodoc: protected :filter_parameters end - # Don't render layouts for templates with the given extensions. - def exempt_from_layout(*extensions) - regexps = extensions.collect do |extension| - extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/ - end - @@exempt_from_layout.merge regexps - end + delegate :exempt_from_layout, :to => 'ActionView::Base' end public @@ -538,7 +526,6 @@ module ActionController #:nodoc: assign_shortcuts(request, response) initialize_current_url assign_names - forget_variables_added_to_assigns log_processing @@ -863,7 +850,7 @@ module ActionController #:nodoc: raise DoubleRenderError, "Can only render or redirect once per action" if performed? if options.nil? - return render_for_file(default_template_name, nil, true) + return render(:file => default_template_name, :layout => true) elsif !extra_options.is_a?(Hash) raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}" else @@ -874,6 +861,9 @@ module ActionController #:nodoc: end end + response.layout = layout = pick_layout(options) + logger.info("Rendering template within #{layout}") if logger && layout + if content_type = options[:content_type] response.content_type = content_type.to_s end @@ -883,26 +873,21 @@ module ActionController #:nodoc: end if options.has_key?(:text) - render_for_text(options[:text], options[:status]) + text = layout ? @template.render(options.merge(:text => options[:text], :layout => layout)) : options[:text] + render_for_text(text, options[:status]) else if file = options[:file] - render_for_file(file, options[:status], nil, options[:locals] || {}) + render_for_file(file, options[:status], layout, options[:locals] || {}) elsif template = options[:template] - render_for_file(template, options[:status], true, options[:locals] || {}) + render_for_file(template, options[:status], layout, options[:locals] || {}) elsif inline = options[:inline] - add_variables_to_assigns - render_for_text(@template.render(options), options[:status]) + render_for_text(@template.render(options.merge(:layout => layout)), options[:status]) elsif action_name = options[:action] - template = default_template_name(action_name.to_s) - if options[:layout] && !template_exempt_from_layout?(template) - render_with_a_layout(:file => template, :status => options[:status], :layout => true) - else - render_with_no_layout(:file => template, :status => options[:status]) - end + render_for_file(default_template_name(action_name.to_s), options[:status], layout) elsif xml = options[:xml] response.content_type ||= Mime::XML @@ -916,12 +901,14 @@ module ActionController #:nodoc: elsif options[:partial] options[:partial] = default_template_name if options[:partial] == true - add_variables_to_assigns - render_for_text(@template.render(options), options[:status]) + if layout + render_for_text(@template.render(:text => @template.render(options), :layout => layout), options[:status]) + else + render_for_text(@template.render(options), options[:status]) + end elsif options[:update] - add_variables_to_assigns - @template.send! :evaluate_assigns + @template.send(:_evaluate_assigns_and_ivars) generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block) response.content_type = Mime::JS @@ -931,7 +918,7 @@ module ActionController #:nodoc: render_for_text(nil, options[:status]) else - render_for_file(default_template_name, options[:status], true) + render_for_file(default_template_name, options[:status], layout) end end end @@ -942,7 +929,6 @@ module ActionController #:nodoc: render(options, &block) ensure erase_render_results - forget_variables_added_to_assigns reset_variables_added_to_assigns end @@ -1127,10 +1113,9 @@ module ActionController #:nodoc: private - def render_for_file(template_path, status = nil, use_full_path = nil, locals = {}) #:nodoc: - add_variables_to_assigns + def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc: logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger - render_for_text(@template.render(:file => template_path, :locals => locals), status) + render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status end def render_for_text(text = nil, status = nil, append_response = false) #:nodoc: @@ -1165,7 +1150,6 @@ module ActionController #:nodoc: @_session = @_response.session @template = @_response.template - @assigns = @_response.template.assigns @_headers = @_response.headers end @@ -1229,27 +1213,10 @@ module ActionController #:nodoc: hidden_actions end - def add_variables_to_assigns - unless @variables_added - add_instance_variables_to_assigns - @variables_added = true - end - end - - def forget_variables_added_to_assigns - @variables_added = nil - end - def reset_variables_added_to_assigns @template.instance_variable_set("@assigns_added", nil) end - def add_instance_variables_to_assigns - (instance_variable_names - @@protected_view_variables).each do |var| - @assigns[var[1..-1]] = instance_variable_get(var) - end - end - def request_origin # this *needs* to be cached! # otherwise you'd get different results if calling it more than once @@ -1265,12 +1232,7 @@ module ActionController #:nodoc: end def template_exists?(template_name = default_template_name) - @template.file_exists?(template_name) - end - - def template_exempt_from_layout?(template_name = default_template_name) - template_name = @template.pick_template(template_name).to_s if @template - @@exempt_from_layout.any? { |ext| template_name =~ ext } + @template.send(:_pick_template, template_name) ? true : false rescue ActionView::MissingTemplate false end diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index f3535f8330..e72434074e 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -90,7 +90,7 @@ module ActionController #:nodoc: set_content_type!(controller, cache_path.extension) options = { :text => cache } options.merge!(:layout => true) if cache_layout? - controller.send!(:render, options) + controller.__send__(:render, options) false else controller.action_cache_path = cache_path @@ -121,7 +121,7 @@ module ActionController #:nodoc: end def content_for_layout(controller) - controller.response.layout && controller.response.template.instance_variable_get('@content_for_layout') + controller.response.layout && controller.response.template.instance_variable_get('@cached_content_for_layout') end end diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb index 61559e9ec7..c7992d7769 100644 --- a/actionpack/lib/action_controller/caching/sweeping.rb +++ b/actionpack/lib/action_controller/caching/sweeping.rb @@ -83,13 +83,13 @@ module ActionController #:nodoc: controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}" action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}" - send!(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) - send!(action_callback_method_name) if respond_to?(action_callback_method_name, true) + __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) + __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) end def method_missing(method, *arguments) return if @controller.nil? - @controller.send!(method, *arguments) + @controller.__send__(method, *arguments) end end end diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb index 0ca27b30db..d381af1b84 100644 --- a/actionpack/lib/action_controller/cgi_process.rb +++ b/actionpack/lib/action_controller/cgi_process.rb @@ -48,7 +48,7 @@ module ActionController #:nodoc: def initialize(cgi, session_options = {}) @cgi = cgi @session_options = session_options - @env = @cgi.send!(:env_table) + @env = @cgi.__send__(:env_table) super() end @@ -107,7 +107,7 @@ module ActionController #:nodoc: end def method_missing(method_id, *arguments) - @cgi.send!(method_id, *arguments) rescue super + @cgi.__send__(method_id, *arguments) rescue super end private @@ -164,7 +164,7 @@ end_msg begin output.write(@cgi.header(@headers)) - if @cgi.send!(:env_table)['REQUEST_METHOD'] == 'HEAD' + if @cgi.__send__(:env_table)['REQUEST_METHOD'] == 'HEAD' return elsif @body.respond_to?(:call) # Flush the output now in case the @body Proc uses diff --git a/actionpack/lib/action_controller/components.rb b/actionpack/lib/action_controller/components.rb index a9b4559852..f446b91e7e 100644 --- a/actionpack/lib/action_controller/components.rb +++ b/actionpack/lib/action_controller/components.rb @@ -65,7 +65,7 @@ module ActionController #:nodoc: module HelperMethods def render_component(options) - @controller.send!(:render_component_as_string, options) + @controller.__send__(:render_component_as_string, options) end end diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/filters.rb index 1d7236f18a..9022b8b279 100644 --- a/actionpack/lib/action_controller/filters.rb +++ b/actionpack/lib/action_controller/filters.rb @@ -199,8 +199,8 @@ module ActionController #:nodoc: Proc.new do |controller, action| method.before(controller) - if controller.send!(:performed?) - controller.send!(:halt_filter_chain, method, :rendered_or_redirected) + if controller.__send__(:performed?) + controller.__send__(:halt_filter_chain, method, :rendered_or_redirected) else begin action.call @@ -223,8 +223,8 @@ module ActionController #:nodoc: def call(controller, &block) super - if controller.send!(:performed?) - controller.send!(:halt_filter_chain, method, :rendered_or_redirected) + if controller.__send__(:performed?) + controller.__send__(:halt_filter_chain, method, :rendered_or_redirected) end end end diff --git a/actionpack/lib/action_controller/helpers.rb b/actionpack/lib/action_controller/helpers.rb index ce5e8be54c..9cffa07b26 100644 --- a/actionpack/lib/action_controller/helpers.rb +++ b/actionpack/lib/action_controller/helpers.rb @@ -204,8 +204,8 @@ module ActionController #:nodoc: begin child.master_helper_module = Module.new - child.master_helper_module.send! :include, master_helper_module - child.send! :default_helper_module! + child.master_helper_module.__send__ :include, master_helper_module + child.__send__ :default_helper_module! rescue MissingSourceFile => e raise unless e.is_missing?("helpers/#{child.controller_path}_helper") end diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index 31db012ef2..2ed810db7d 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -117,7 +117,7 @@ module ActionController def authentication_request(controller, realm) controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}") - controller.send! :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized + controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized end end end diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 198a22e8dc..a98c1af7f9 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -444,7 +444,7 @@ EOF reset! unless @integration_session # reset the html_document variable, but only for new get/post calls @html_document = nil unless %w(cookies assigns).include?(method) - returning @integration_session.send!(method, *args) do + returning @integration_session.__send__(method, *args) do copy_session_variables! end end @@ -469,12 +469,12 @@ EOF self.class.fixture_table_names.each do |table_name| name = table_name.tr(".", "_") next unless respond_to?(name) - extras.send!(:define_method, name) { |*args| delegate.send(name, *args) } + extras.__send__(:define_method, name) { |*args| delegate.send(name, *args) } end end # delegate add_assertion to the test case - extras.send!(:define_method, :add_assertion) { test_result.add_assertion } + extras.__send__(:define_method, :add_assertion) { test_result.add_assertion } session.extend(extras) session.delegate = self session.test_result = @_result @@ -488,7 +488,7 @@ EOF def copy_session_variables! #:nodoc: return unless @integration_session %w(controller response request).each do |var| - instance_variable_set("@#{var}", @integration_session.send!(var)) + instance_variable_set("@#{var}", @integration_session.__send__(var)) end end diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb index 8b6febe254..8432a008eb 100644 --- a/actionpack/lib/action_controller/layout.rb +++ b/actionpack/lib/action_controller/layout.rb @@ -3,11 +3,6 @@ module ActionController #:nodoc: def self.included(base) base.extend(ClassMethods) base.class_eval do - # NOTE: Can't use alias_method_chain here because +render_without_layout+ is already - # defined as a publicly exposed method - alias_method :render_with_no_layout, :render - alias_method :render, :render_with_a_layout - class << self alias_method_chain :inherited, :layout end @@ -221,10 +216,10 @@ module ActionController #:nodoc: # object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return # weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard. def active_layout(passed_layout = nil) - layout = passed_layout || self.class.default_layout(response.template.template_format) + layout = passed_layout || self.class.default_layout(default_template_format) active_layout = case layout when String then layout - when Symbol then send!(layout) + when Symbol then __send__(layout) when Proc then layout.call(self) end @@ -240,51 +235,24 @@ module ActionController #:nodoc: end end - protected - def render_with_a_layout(options = nil, extra_options = {}, &block) #:nodoc: - template_with_options = options.is_a?(Hash) - - if (layout = pick_layout(template_with_options, options)) && apply_layout?(template_with_options, options) - options = options.merge :layout => false if template_with_options - logger.info("Rendering template within #{layout}") if logger - - content_for_layout = render_with_no_layout(options, extra_options, &block) - erase_render_results - add_variables_to_assigns - @template.instance_variable_set("@content_for_layout", content_for_layout) - response.layout = layout - status = template_with_options ? options[:status] : nil - render_for_text(@template.render(layout), status) - else - render_with_no_layout(options, extra_options, &block) - end - end - - private - def apply_layout?(template_with_options, options) - return false if options == :update - template_with_options ? candidate_for_layout?(options) : !template_exempt_from_layout? - end - def candidate_for_layout?(options) - (options.has_key?(:layout) && options[:layout] != false) || - options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing).compact.empty? && - !template_exempt_from_layout?(options[:template] || default_template_name(options[:action])) + options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty? && + !@template.__send__(:_exempt_from_layout?, options[:template] || default_template_name(options[:action])) end - def pick_layout(template_with_options, options) - if template_with_options - case layout = options[:layout] - when FalseClass - nil - when NilClass, TrueClass - active_layout if action_has_layout? - else - active_layout(layout) + def pick_layout(options) + if options.has_key?(:layout) + case layout = options.delete(:layout) + when FalseClass + nil + when NilClass, TrueClass + active_layout if action_has_layout? && !@template.__send__(:_exempt_from_layout?, default_template_name) + else + active_layout(layout) end else - active_layout if action_has_layout? + active_layout if action_has_layout? && candidate_for_layout?(options) end end @@ -304,7 +272,13 @@ module ActionController #:nodoc: end def layout_directory?(layout_name) - @template.file_exists?("#{File.join('layouts', layout_name)}.#{@template.template_format}") + @template.__send__(:_pick_template, "#{File.join('layouts', layout_name)}.#{@template.template_format}") ? true : false + rescue ActionView::MissingTemplate + false + end + + def default_template_format + response.template.template_format end end end diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index 30564c7bb3..cc228c4230 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -108,7 +108,7 @@ module ActionController args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options end - send!(named_route, *args) + __send__(named_route, *args) end # Returns the path component of a URL for the given record. It uses @@ -149,7 +149,7 @@ module ActionController if parent.is_a?(Symbol) || parent.is_a?(String) string << "#{parent}_" else - string << "#{RecordIdentifier.send!("singular_class_name", parent)}_" + string << "#{RecordIdentifier.__send__("singular_class_name", parent)}_" end end end @@ -157,7 +157,7 @@ module ActionController if record.is_a?(Symbol) || record.is_a?(String) route << "#{record}_" else - route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_" + route << "#{RecordIdentifier.__send__("#{inflection}_class_name", record)}_" end action_prefix(options) + namespace + route + routing_type(options).to_s diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index a1a9d68a35..1492c4ec61 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -177,11 +177,8 @@ module ActionController #:nodoc: # Render detailed diagnostics for unhandled exceptions rescued from # a controller action. def rescue_action_locally(exception) - add_variables_to_assigns @template.instance_variable_set("@exception", exception) @template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub"))) - @template.send!(:assign_variables_from_controller) - @template.instance_variable_set("@contents", @template.render(:file => template_path_for_local_rescue(exception))) response.content_type = Mime::HTML diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index b4ac0db262..31710caccb 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -85,16 +85,24 @@ module ActionController @new_path ||= "#{path}/#{new_action}" end + def shallow_path_prefix + @shallow_path_prefix ||= "#{path_prefix unless @options[:shallow]}" + end + def member_path - @member_path ||= "#{path}/:id" + @member_path ||= "#{shallow_path_prefix}/#{path_segment}/:id" end def nesting_path_prefix - @nesting_path_prefix ||= "#{path}/:#{singular}_id" + @nesting_path_prefix ||= "#{shallow_path_prefix}/#{path_segment}/:#{singular}_id" + end + + def shallow_name_prefix + @shallow_name_prefix ||= "#{name_prefix unless @options[:shallow]}" end def nesting_name_prefix - "#{name_prefix}#{singular}_" + "#{shallow_name_prefix}#{singular}_" end def action_separator @@ -141,6 +149,8 @@ module ActionController super end + alias_method :shallow_path_prefix, :path_prefix + alias_method :shallow_name_prefix, :name_prefix alias_method :member_path, :path alias_method :nesting_path_prefix, :path end @@ -318,6 +328,31 @@ module ActionController # comments_url(@article) # comment_url(@article, @comment) # + # * <tt>:shallow</tt> - If true, paths for nested resources which reference a specific member + # (ie. those with an :id parameter) will not use the parent path prefix or name prefix. + # + # The <tt>:shallow</tt> option is inherited by any nested resource(s). + # + # For example, 'users', 'posts' and 'comments' all use shallow paths with the following nested resources: + # + # map.resources :users, :shallow => true do |user| + # user.resources :posts do |post| + # post.resources :comments + # end + # end + # # --> GET /users/1/posts (maps to the PostsController#index action as usual) + # # also adds the usual named route called "user_posts" + # # --> GET /posts/2 (maps to the PostsController#show action as if it were not nested) + # # also adds the named route called "post" + # # --> GET /posts/2/comments (maps to the CommentsController#index action) + # # also adds the named route called "post_comments" + # # --> GET /comments/2 (maps to the CommentsController#show action as if it were not nested) + # # also adds the named route called "comment" + # + # You may also use <tt>:shallow</tt> in combination with the +has_one+ and +has_many+ shorthand notations like: + # + # map.resources :users, :has_many => { :posts => :comments }, :shallow => true + # # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied. # # Examples: @@ -443,7 +478,7 @@ module ActionController map_associations(resource, options) if block_given? - with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], &block) + with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block) end end end @@ -460,21 +495,35 @@ module ActionController map_associations(resource, options) if block_given? - with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], &block) + with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block) end end end def map_associations(resource, options) + map_has_many_associations(resource, options.delete(:has_many), options) if options[:has_many] + path_prefix = "#{options.delete(:path_prefix)}#{resource.nesting_path_prefix}" name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}" - Array(options[:has_many]).each do |association| - resources(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace]) + Array(options[:has_one]).each do |association| + resource(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace], :shallow => options[:shallow]) end + end - Array(options[:has_one]).each do |association| - resource(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace]) + def map_has_many_associations(resource, associations, options) + case associations + when Hash + associations.each do |association,has_many| + map_has_many_associations(resource, association, options.merge(:has_many => has_many)) + end + when Array + associations.each do |association| + map_has_many_associations(resource, association, options) + end + when Symbol, String + resources(associations, :path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], :has_many => options[:has_many]) + else end end @@ -530,13 +579,13 @@ module ActionController action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) action_path ||= Base.resources_path_names[action] || action - map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options) + map_named_routes(map, "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options) end end end show_action_options = action_options_for("show", resource) - map_named_routes(map, "#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options) + map_named_routes(map, "#{resource.shallow_name_prefix}#{resource.singular}", resource.member_path, show_action_options) update_action_options = action_options_for("update", resource) map_unnamed_routes(map, resource.member_path, update_action_options) @@ -579,4 +628,4 @@ end class ActionController::Routing::RouteSet::Mapper include ActionController::Resources -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/routing/optimisations.rb b/actionpack/lib/action_controller/routing/optimisations.rb index 0fe836606c..894d4109e4 100644 --- a/actionpack/lib/action_controller/routing/optimisations.rb +++ b/actionpack/lib/action_controller/routing/optimisations.rb @@ -103,9 +103,10 @@ module ActionController end # This case uses almost the same code as positional arguments, - # but add an args.last.to_query on the end + # but add a question mark and args.last.to_query on the end, + # unless the last arg is empty def generation_code - super.insert(-2, '?#{args.last.to_query}') + super.insert(-2, '#{\'?\' + args.last.to_query unless args.last.empty?}') end # To avoid generating "http://localhost/?host=foo.example.com" we diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 8dfc22f94f..9d48f289db 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -115,7 +115,7 @@ module ActionController def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false) reset! if regenerate Array(destinations).each do |dest| - dest.send! :include, @module + dest.__send__(:include, @module) end end @@ -353,7 +353,7 @@ module ActionController if generate_all # Used by caching to expire all paths for a resource return routes.collect do |route| - route.send!(method, options, merged, expire_on) + route.__send__(method, options, merged, expire_on) end.compact end @@ -361,7 +361,7 @@ module ActionController routes = routes_by_controller[controller][action][options.keys.sort_by { |x| x.object_id }] routes.each do |route| - results = route.send!(method, options, merged, expire_on) + results = route.__send__(method, options, merged, expire_on) return results if results && (!results.is_a?(Array) || results.first) end end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index a11fa7cd10..cde1f2052b 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -3,6 +3,8 @@ require 'action_controller/test_case' module ActionController #:nodoc: class Base + attr_reader :assigns + # Process a test request called with a TestRequest object. def self.process_test(request) new.process_test(request) @@ -14,7 +16,12 @@ module ActionController #:nodoc: def process_with_test(*args) returning process_without_test(*args) do - add_variables_to_assigns + @assigns = {} + (instance_variable_names - @@protected_instance_variables).each do |var| + value = instance_variable_get(var) + @assigns[var[1..-1]] = value + response.template.assigns[var[1..-1]] = value if response + end end end @@ -211,7 +218,7 @@ module ActionController #:nodoc: # Returns the template of the file which was used to # render this response (or nil) def rendered_template - template._first_render + template.send(:_first_render) end # A shortcut to the flash. Returns an empty hash if no session flash exists. @@ -350,7 +357,7 @@ module ActionController #:nodoc: alias local_path path def method_missing(method_name, *args, &block) #:nodoc: - @tempfile.send!(method_name, *args, &block) + @tempfile.__send__(method_name, *args, &block) end end @@ -396,7 +403,7 @@ module ActionController #:nodoc: def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' @request.env['HTTP_ACCEPT'] = 'text/javascript, text/html, application/xml, text/xml, */*' - returning send!(request_method, action, parameters, session, flash) do + returning __send__(request_method, action, parameters, session, flash) do @request.env.delete 'HTTP_X_REQUESTED_WITH' @request.env.delete 'HTTP_ACCEPT' end @@ -429,7 +436,7 @@ module ActionController #:nodoc: def build_request_uri(action, parameters) unless @request.env['REQUEST_URI'] - options = @controller.send!(:rewrite_options, parameters) + options = @controller.__send__(:rewrite_options, parameters) options.update(:only_path => true, :action => action) url = ActionController::UrlRewriter.new(@request, parameters) diff --git a/actionpack/lib/action_controller/verification.rb b/actionpack/lib/action_controller/verification.rb index 35b12a7f13..7bf09ba6ea 100644 --- a/actionpack/lib/action_controller/verification.rb +++ b/actionpack/lib/action_controller/verification.rb @@ -80,7 +80,7 @@ module ActionController #:nodoc: # array (may also be a single value). def verify(options={}) before_filter :only => options[:only], :except => options[:except] do |c| - c.send! :verify_action, options + c.__send__ :verify_action, options end end end @@ -116,7 +116,7 @@ module ActionController #:nodoc: end def apply_redirect_to(redirect_to_option) # :nodoc: - (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.send!(redirect_to_option) : redirect_to_option + (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option end def apply_remaining_actions(options) # :nodoc: diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index c65048bfa0..6c4da9067d 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -162,7 +162,6 @@ module ActionView #:nodoc: attr_accessor :base_path, :assigns, :template_extension attr_accessor :controller - attr_accessor :_first_render, :_last_render attr_writer :template_format @@ -185,6 +184,17 @@ module ActionView #:nodoc: "deprecated and has no effect. Please remove it from your config files.", caller) end + # Templates that are exempt from layouts + @@exempt_from_layout = Set.new([/\.rjs$/]) + + # Don't render layouts for templates with the given extensions. + def self.exempt_from_layout(*extensions) + regexps = extensions.collect do |extension| + extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/ + end + @@exempt_from_layout.merge(regexps) + end + # Specify whether RJS responses should be wrapped in a try/catch block # that alert()s the caught exception (and then re-raises it). @@debug_rjs = false @@ -246,33 +256,20 @@ module ActionView #:nodoc: update_page(&block) elsif options.is_a?(Hash) options = options.reverse_merge(:locals => {}) - - if partial_layout = options.delete(:layout) - if block_given? - begin - @_proc_for_layout = block - concat(render(options.merge(:partial => partial_layout))) - ensure - @_proc_for_layout = nil - end - else - begin - original_content_for_layout, @content_for_layout = @content_for_layout, render(options) - render(options.merge(:partial => partial_layout)) - ensure - @content_for_layout = original_content_for_layout - end - end + if options[:layout] + _render_with_layout(options, local_assigns, &block) elsif options[:file] if options[:use_full_path] ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller) end - pick_template(options[:file]).render_template(self, options[:locals]) + _pick_template(options[:file]).render_template(self, options[:locals]) elsif options[:partial] render_partial(options) elsif options[:inline] InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals]) + elsif options[:text] + options[:text] end end end @@ -290,74 +287,95 @@ module ActionView #:nodoc: end end - def file_exists?(template_path) - pick_template(template_path) ? true : false - rescue MissingTemplate - false - end + private + attr_accessor :_first_render, :_last_render - # Gets the extension for an existing template with the given template_path. - # Returns the format with the extension if that template exists. - # - # pick_template('users/show') - # # => 'users/show.html.erb' - # - # pick_template('users/legacy') - # # => 'users/legacy.rhtml' - # - def pick_template(template_path) - return template_path if template_path.respond_to?(:render) - - path = template_path.sub(/^\//, '') - if m = path.match(/(.*)\.(\w+)$/) - template_file_name, template_file_extension = m[1], m[2] - else - template_file_name = path - end + # Evaluate the local assigns and pushes them to the view. + def _evaluate_assigns_and_ivars #:nodoc: + unless @assigns_added + @assigns.each { |key, value| instance_variable_set("@#{key}", value) } - # OPTIMIZE: Checks to lookup template in view path - if template = self.view_paths["#{template_file_name}.#{template_format}"] - template - elsif template = self.view_paths[template_file_name] - template - elsif _first_render && template = self.view_paths["#{template_file_name}.#{_first_render.format_and_extension}"] - template - elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"] - @template_format = :html - template - else - template = Template.new(template_path, view_paths) - - if self.class.warn_cache_misses && logger - logger.debug "[PERFORMANCE] Rendering a template that was " + - "not found in view path. Templates outside the view path are " + - "not cached and result in expensive disk operations. Move this " + - "file into #{view_paths.join(':')} or add the folder to your " + - "view path list" + if @controller + variables = @controller.instance_variables + variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables) + variables.each {|name| instance_variable_set(name, @controller.instance_variable_get(name)) } + end + + @assigns_added = true end + end - template + def _set_controller_content_type(content_type) #:nodoc: + if controller.respond_to?(:response) + controller.response.content_type ||= content_type + end end - end - memoize :pick_template - private - # Evaluate the local assigns and pushes them to the view. - def evaluate_assigns - unless @assigns_added - assign_variables_from_controller - @assigns_added = true + def _pick_template(template_path) + return template_path if template_path.respond_to?(:render) + + path = template_path.sub(/^\//, '') + if m = path.match(/(.*)\.(\w+)$/) + template_file_name, template_file_extension = m[1], m[2] + else + template_file_name = path + end + + # OPTIMIZE: Checks to lookup template in view path + if template = self.view_paths["#{template_file_name}.#{template_format}"] + template + elsif template = self.view_paths[template_file_name] + template + elsif _first_render && template = self.view_paths["#{template_file_name}.#{_first_render.format_and_extension}"] + template + elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"] + @template_format = :html + template + else + template = Template.new(template_path, view_paths) + + if self.class.warn_cache_misses && logger + logger.debug "[PERFORMANCE] Rendering a template that was " + + "not found in view path. Templates outside the view path are " + + "not cached and result in expensive disk operations. Move this " + + "file into #{view_paths.join(':')} or add the folder to your " + + "view path list" + end + + template end end + memoize :_pick_template - # Assigns instance variables from the controller to the view. - def assign_variables_from_controller - @assigns.each { |key, value| instance_variable_set("@#{key}", value) } + def _exempt_from_layout?(template_path) #:nodoc: + template = _pick_template(template_path).to_s + @@exempt_from_layout.any? { |ext| template =~ ext } + rescue ActionView::MissingTemplate + return false end - def set_controller_content_type(content_type) - if controller.respond_to?(:response) - controller.response.content_type ||= content_type + def _render_with_layout(options, local_assigns, &block) #:nodoc: + partial_layout = options.delete(:layout) + + if block_given? + begin + @_proc_for_layout = block + concat(render(options.merge(:partial => partial_layout))) + ensure + @_proc_for_layout = nil + end + else + begin + original_content_for_layout, @content_for_layout = @content_for_layout, render(options) + if (options[:inline] || options[:file] || options[:text]) + @cached_content_for_layout = @content_for_layout + render(:file => partial_layout, :locals => local_assigns) + else + render(options.merge(:partial => partial_layout)) + end + ensure + @content_for_layout = original_content_for_layout + end end end end diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 2ce818cd71..2f08583b16 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -255,6 +255,14 @@ module ActionView link_to_function(name, remote_function(options), html_options || options.delete(:html)) end + # Creates a button with an onclick event which calls a remote action + # via XMLHttpRequest + # The options for specifying the target with :url + # and defining callbacks is the same as link_to_remote. + def button_to_remote(name, options = {}, html_options = {}) + button_to_function(name, remote_function(options), html_options) + end + # Periodically calls the specified url (<tt>options[:url]</tt>) every # <tt>options[:frequency]</tt> seconds (default is 10). Usually used to # update a specified div (<tt>options[:update]</tt>) with the results @@ -1052,7 +1060,7 @@ module ActionView js_options['asynchronous'] = options[:type] != :synchronous js_options['method'] = method_option_to_s(options[:method]) if options[:method] - js_options['insertion'] = options[:position].to_s.downcase if options[:position] + js_options['insertion'] = "'#{options[:position].to_s.downcase}'" if options[:position] js_options['evalScripts'] = options[:script].nil? || options[:script] if options[:form] diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 443c49b870..373bb92dc4 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -196,7 +196,7 @@ module ActionView path = "_#{partial_path}" end - pick_template(path) + _pick_template(path) end memoize :_pick_partial_template end diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index fa45edd436..d960335220 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -26,11 +26,11 @@ module ActionView def render(view, local_assigns = {}) compile(local_assigns) - view._first_render ||= self - view._last_render = self + view.send(:_first_render=, self) unless view.send(:_first_render) + view.send(:_last_render=, self) - view.send(:evaluate_assigns) - view.send(:set_controller_content_type, mime_type) if respond_to?(:mime_type) + view.send(:_evaluate_assigns_and_ivars) + view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type) view.send(method_name(local_assigns), local_assigns) do |*names| if proc = view.instance_variable_get("@_proc_for_layout") diff --git a/actionpack/lib/action_view/template_handlers/builder.rb b/actionpack/lib/action_view/template_handlers/builder.rb index 7d24a5c423..788dc93326 100644 --- a/actionpack/lib/action_view/template_handlers/builder.rb +++ b/actionpack/lib/action_view/template_handlers/builder.rb @@ -6,7 +6,7 @@ module ActionView include Compilable def compile(template) - "set_controller_content_type(Mime::XML);" + + "_set_controller_content_type(Mime::XML);" + "xml = ::Builder::XmlMarkup.new(:indent => 2);" + "self.output_buffer = xml.target!;" + template.source + diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index adbb37fd09..c69f9455b2 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -54,7 +54,7 @@ module ActionView private def method_missing(selector, *args) controller = TestController.new - return controller.send!(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector) + return controller.__send__(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector) super end end |