diff options
Diffstat (limited to 'actionpack/lib')
52 files changed, 681 insertions, 626 deletions
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index e58071d4af..2efd0dad2e 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -54,6 +54,7 @@ require 'action_controller/rack_process' require 'action_controller/record_identifier' require 'action_controller/request_forgery_protection' require 'action_controller/headers' +require 'action_controller/translation' require 'action_view' @@ -74,4 +75,5 @@ ActionController::Base.class_eval do include ActionController::Components include ActionController::RecordIdentifier include ActionController::RequestForgeryProtection + include ActionController::Translation end diff --git a/actionpack/lib/action_controller/assertions/routing_assertions.rb b/actionpack/lib/action_controller/assertions/routing_assertions.rb index 312b4e228b..8a837c592c 100644 --- a/actionpack/lib/action_controller/assertions/routing_assertions.rb +++ b/actionpack/lib/action_controller/assertions/routing_assertions.rb @@ -10,32 +10,32 @@ module ActionController # and a :method containing the required HTTP verb. # # # assert that POSTing to /items will call the create action on ItemsController - # assert_recognizes({:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post}) + # assert_recognizes {:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post} # # You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used # to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the # extras argument, appending the query string on the path directly will not work. For example: # # # assert that a path of '/items/list/1?view=print' returns the correct options - # assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" }) + # assert_recognizes {:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" } # # The +message+ parameter allows you to pass in an error message that is displayed upon failure. # # ==== Examples # # Check the default route (i.e., the index action) - # assert_recognizes({:controller => 'items', :action => 'index'}, 'items') + # assert_recognizes {:controller => 'items', :action => 'index'}, 'items' # # # Test a specific action - # assert_recognizes({:controller => 'items', :action => 'list'}, 'items/list') + # assert_recognizes {:controller => 'items', :action => 'list'}, 'items/list' # # # Test an action with a parameter - # assert_recognizes({:controller => 'items', :action => 'destroy', :id => '1'}, 'items/destroy/1') + # assert_recognizes {:controller => 'items', :action => 'destroy', :id => '1'}, 'items/destroy/1' # # # Test a custom route - # assert_recognizes({:controller => 'items', :action => 'show', :id => '1'}, 'view/item1') + # assert_recognizes {:controller => 'items', :action => 'show', :id => '1'}, 'view/item1' # # # Check a Simply RESTful generated route - # assert_recognizes(list_items_url, 'items/list') + # assert_recognizes list_items_url, 'items/list' def assert_recognizes(expected_options, path, extras={}, message=nil) if path.is_a? Hash request_method = path[:method] @@ -67,13 +67,13 @@ module ActionController # # ==== Examples # # Asserts that the default action is generated for a route with no action - # assert_generates("/items", :controller => "items", :action => "index") + # assert_generates "/items", :controller => "items", :action => "index" # # # Tests that the list action is properly routed - # assert_generates("/items/list", :controller => "items", :action => "list") + # assert_generates "/items/list", :controller => "items", :action => "list" # # # Tests the generation of a route with a parameter - # assert_generates("/items/list/1", { :controller => "items", :action => "list", :id => "1" }) + # assert_generates "/items/list/1", { :controller => "items", :action => "list", :id => "1" } # # # Asserts that the generated route gives us our custom route # assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" } @@ -104,19 +104,19 @@ module ActionController # # ==== Examples # # Assert a basic route: a controller with the default action (index) - # assert_routing('/home', :controller => 'home', :action => 'index') + # assert_routing '/home', :controller => 'home', :action => 'index' # # # Test a route generated with a specific controller, action, and parameter (id) - # assert_routing('/entries/show/23', :controller => 'entries', :action => 'show', id => 23) + # assert_routing '/entries/show/23', :controller => 'entries', :action => 'show', id => 23 # # # Assert a basic route (controller + default action), with an error message if it fails - # assert_routing('/store', { :controller => 'store', :action => 'index' }, {}, {}, 'Route for store index not generated properly') + # assert_routing '/store', { :controller => 'store', :action => 'index' }, {}, {}, 'Route for store index not generated properly' # # # Tests a route, providing a defaults hash # assert_routing 'controller/action/9', {:id => "9", :item => "square"}, {:controller => "controller", :action => "action"}, {}, {:item => "square"} # # # Tests a route with a HTTP method - # assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" }) + # assert_routing { :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" } def assert_routing(path, options, defaults={}, extras={}, message=nil) assert_recognizes(options, path, extras, message) diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb index 9114894b1d..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 @@ -472,7 +449,13 @@ module ActionController matches else # RJS statement not found. - flunk args.shift || "No RJS statement that replaces or inserts HTML content." + case rjs_type + when :remove, :show, :hide, :toggle + flunk_message = "No RJS statement that #{rjs_type.to_s}s '#{id}' was rendered." + else + flunk_message = "No RJS statement that replaces or inserts HTML content." + end + flunk args.shift || flunk_message end end @@ -582,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 @@ -615,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 91023cd774..457b9e85bc 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: @@ -440,7 +434,11 @@ module ActionController #:nodoc: # render("test/template") will be looked up in the view load paths array and the closest match will be # returned. def view_paths - @view_paths || superclass.view_paths + if defined? @view_paths + @view_paths + else + superclass.view_paths + end end def view_paths=(value) @@ -455,7 +453,7 @@ module ActionController #:nodoc: # ArticleController.prepend_view_path(["views/default", "views/custom"]) # def prepend_view_path(path) - @view_paths = superclass.view_paths.dup if @view_paths.nil? + @view_paths = superclass.view_paths.dup if !defined?(@view_paths) || @view_paths.nil? @view_paths.unshift(*path) end @@ -520,13 +518,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 +530,6 @@ module ActionController #:nodoc: assign_shortcuts(request, response) initialize_current_url assign_names - forget_variables_added_to_assigns log_processing @@ -548,12 +539,16 @@ module ActionController #:nodoc: @@guard.synchronize { send(method, *arguments) } end - response.prepare! unless component_request? - response + send_response ensure process_cleanup end + def send_response + response.prepare! unless component_request? + response + end + # Returns a URL that has been rewritten according to the options hash and the defined Routes. # (For doing a complete redirect, use redirect_to). # @@ -859,7 +854,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 @@ -870,6 +865,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 @@ -879,26 +877,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 @@ -912,12 +905,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 @@ -927,7 +922,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 @@ -938,7 +933,6 @@ module ActionController #:nodoc: render(options, &block) ensure erase_render_results - forget_variables_added_to_assigns reset_variables_added_to_assigns end @@ -1123,10 +1117,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: @@ -1148,7 +1141,7 @@ module ActionController #:nodoc: def initialize_template_class(response) response.template = ActionView::Base.new(self.class.view_paths, {}, self) - response.template.extend self.class.master_helper_module + response.template.helpers.send :include, self.class.master_helper_module response.redirected_to = nil @performed_render = @performed_redirect = false end @@ -1161,7 +1154,6 @@ module ActionController #:nodoc: @_session = @_response.session @template = @_response.template - @assigns = @_response.template.assigns @_headers = @_response.headers end @@ -1225,27 +1217,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 @@ -1261,12 +1236,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/benchmarking.rb b/actionpack/lib/action_controller/benchmarking.rb index 98b0325ba3..fa572ebf3d 100644 --- a/actionpack/lib/action_controller/benchmarking.rb +++ b/actionpack/lib/action_controller/benchmarking.rb @@ -24,7 +24,7 @@ module ActionController #:nodoc: if logger && logger.level == log_level result = nil seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield } - logger.add(log_level, "#{title} (#{'%.5f' % seconds})") + logger.add(log_level, "#{title} (#{('%.1f' % (seconds * 1000))}ms)") result else yield @@ -42,53 +42,66 @@ module ActionController #:nodoc: protected def render_with_benchmark(options = nil, extra_options = {}, &block) - unless logger - render_without_benchmark(options, extra_options, &block) - else - db_runtime = ActiveRecord::Base.connection.reset_runtime if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? + if logger + if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? + db_runtime = ActiveRecord::Base.connection.reset_runtime + end render_output = nil - @rendering_runtime = Benchmark::realtime{ render_output = render_without_benchmark(options, extra_options, &block) } + @view_runtime = Benchmark::realtime { render_output = render_without_benchmark(options, extra_options, &block) } if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? @db_rt_before_render = db_runtime @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime - @rendering_runtime -= @db_rt_after_render + @view_runtime -= @db_rt_after_render end render_output + else + render_without_benchmark(options, extra_options, &block) end end private def perform_action_with_benchmark - unless logger - perform_action_without_benchmark - else - runtime = [ Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001 ].max + if logger + seconds = [ Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001 ].max + logging_view = defined?(@view_runtime) + logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? + + log_message = "Completed in #{sprintf("%.0f", seconds * 1000)}ms" + + if logging_view || logging_active_record + log_message << " (" + log_message << view_runtime if logging_view + + if logging_active_record + log_message << ", " if logging_view + log_message << active_record_runtime + ")" + else + ")" + end + end - log_message = "Completed in #{sprintf("%.5f", runtime)} (#{(1 / runtime).floor} reqs/sec)" - log_message << rendering_runtime(runtime) if defined?(@rendering_runtime) - log_message << active_record_runtime(runtime) if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? log_message << " | #{headers["Status"]}" log_message << " [#{complete_request_uri rescue "unknown"}]" logger.info(log_message) - response.headers["X-Runtime"] = sprintf("%.5f", runtime) + response.headers["X-Runtime"] = "#{sprintf("%.0f", seconds * 1000)}ms" + else + perform_action_without_benchmark end end - def rendering_runtime(runtime) - percentage = @rendering_runtime * 100 / runtime - " | Rendering: %.5f (%d%%)" % [@rendering_runtime, percentage.to_i] + def view_runtime + "View: %.0f" % (@view_runtime * 1000) end - def active_record_runtime(runtime) - db_runtime = ActiveRecord::Base.connection.reset_runtime - db_runtime += @db_rt_before_render if @db_rt_before_render - db_runtime += @db_rt_after_render if @db_rt_after_render - db_percentage = db_runtime * 100 / runtime - " | DB: %.5f (%d%%)" % [db_runtime, db_percentage.to_i] + def active_record_runtime + db_runtime = ActiveRecord::Base.connection.reset_runtime + db_runtime += @db_rt_before_render if @db_rt_before_render + db_runtime += @db_rt_after_render if @db_rt_after_render + "DB: %.0f" % (db_runtime * 1000) end end end diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index f3535f8330..7c803a9830 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -38,8 +38,8 @@ module ActionController #:nodoc: # caches_action :show, :cache_path => { :project => 1 }, :expires_in => 1.hour # caches_action :feed, :cache_path => Proc.new { |controller| # controller.params[:user_id] ? - # controller.send(:user_list_url, c.params[:user_id], c.params[:id]) : - # controller.send(:list_url, c.params[:id]) } + # controller.send(:user_list_url, controller.params[:user_id], controller.params[:id]) : + # controller.send(:list_url, controller.params[:id]) } # end # # If you pass :layout => false, it will only cache your action content. It is useful when your layout has dynamic information. @@ -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..fabacd9b83 100644 --- a/actionpack/lib/action_controller/cgi_process.rb +++ b/actionpack/lib/action_controller/cgi_process.rb @@ -42,13 +42,14 @@ module ActionController #:nodoc: :prefix => "ruby_sess.", # prefix session file names :session_path => "/", # available to all paths in app :session_key => "_session_id", - :cookie_only => true + :cookie_only => true, + :session_http_only=> true } 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 +108,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 +165,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 8275bd380a..f446b91e7e 100644 --- a/actionpack/lib/action_controller/components.rb +++ b/actionpack/lib/action_controller/components.rb @@ -38,6 +38,7 @@ module ActionController #:nodoc: def self.included(base) #:nodoc: base.class_eval do include InstanceMethods + include ActiveSupport::Deprecation extend ClassMethods helper HelperMethods @@ -64,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 @@ -82,6 +83,7 @@ module ActionController #:nodoc: render_for_text(component_response(options, true).body, response.headers["Status"]) end end + deprecate :render_component => "Please install render_component plugin from http://github.com/rails/render_component/tree/master" # Returns the component response as a string def render_component_as_string(options) #:doc: @@ -95,6 +97,7 @@ module ActionController #:nodoc: end end end + deprecate :render_component_as_string => "Please install render_component plugin from http://github.com/rails/render_component/tree/master" def flash_with_components(refresh = false) #:nodoc: if !defined?(@_flash) || refresh 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..3631ce86af 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 @@ -169,17 +164,17 @@ module ActionController #:nodoc: # performance and have access to them as any normal template would. def layout(template_name, conditions = {}, auto = false) add_layout_conditions(conditions) - write_inheritable_attribute "layout", template_name - write_inheritable_attribute "auto_layout", auto + write_inheritable_attribute(:layout, template_name) + write_inheritable_attribute(:auto_layout, auto) end def layout_conditions #:nodoc: - @layout_conditions ||= read_inheritable_attribute("layout_conditions") + @layout_conditions ||= read_inheritable_attribute(:layout_conditions) end def default_layout(format) #:nodoc: - layout = read_inheritable_attribute("layout") - return layout unless read_inheritable_attribute("auto_layout") + layout = read_inheritable_attribute(:layout) + return layout unless read_inheritable_attribute(:auto_layout) @default_layout ||= {} @default_layout[format] ||= default_layout_with_format(format, layout) @default_layout[format] @@ -199,7 +194,7 @@ module ActionController #:nodoc: end def add_layout_conditions(conditions) - write_inheritable_hash "layout_conditions", normalize_conditions(conditions) + write_inheritable_hash(:layout_conditions, normalize_conditions(conditions)) end def normalize_conditions(conditions) @@ -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/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb index a7215e6ea3..26edca3b69 100644 --- a/actionpack/lib/action_controller/mime_type.rb +++ b/actionpack/lib/action_controller/mime_type.rb @@ -1,3 +1,5 @@ +require 'set' + module Mime SET = [] EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? } 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/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb index 1ace16da07..e8ea3704a8 100644 --- a/actionpack/lib/action_controller/rack_process.rb +++ b/actionpack/lib/action_controller/rack_process.rb @@ -14,7 +14,8 @@ module ActionController #:nodoc: :prefix => "ruby_sess.", # prefix session file names :session_path => "/", # available to all paths in app :session_key => "_session_id", - :cookie_only => true + :cookie_only => true, + :session_http_only=> true } def initialize(env, session_options = DEFAULT_SESSION_OPTIONS) diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 364e6201cc..8e6cfb41dc 100644..100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -22,7 +22,7 @@ module ActionController # such as { 'RAILS_ENV' => 'production' }. attr_reader :env - # The true HTTP request method as a lowercase symbol, such as <tt>:get</tt>. + # The true HTTP request \method as a lowercase symbol, such as <tt>:get</tt>. # UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS. def request_method method = @env['REQUEST_METHOD'] @@ -32,7 +32,7 @@ module ActionController end memoize :request_method - # The HTTP request method as a lowercase symbol, such as <tt>:get</tt>. + # The HTTP request \method as a lowercase symbol, such as <tt>:get</tt>. # Note, HEAD is returned as <tt>:get</tt> since the two are functionally # equivalent from the application's perspective. def method @@ -59,19 +59,21 @@ module ActionController request_method == :delete end - # Is this a HEAD request? <tt>request.method</tt> sees HEAD as <tt>:get</tt>, - # so check the HTTP method directly. + # Is this a HEAD request? Since <tt>request.method</tt> sees HEAD as <tt>:get</tt>, + # this \method checks the actual HTTP \method directly. def head? request_method == :head end # Provides access to the request's HTTP headers, for example: - # request.headers["Content-Type"] # => "text/plain" + # + # request.headers["Content-Type"] # => "text/plain" def headers ActionController::Http::Headers.new(@env) end memoize :headers + # Returns the content length of the request as an integer. def content_length @env['CONTENT_LENGTH'].to_i end @@ -79,14 +81,14 @@ module ActionController # The MIME type of the HTTP request, such as Mime::XML. # - # For backward compatibility, the post format is extracted from the + # For backward compatibility, the post \format is extracted from the # X-Post-Data-Format HTTP header if present. def content_type Mime::Type.lookup(content_type_without_parameters) end memoize :content_type - # Returns the accepted MIME type for the request + # Returns the accepted MIME type for the request. def accepts header = @env['HTTP_ACCEPT'].to_s.strip @@ -123,7 +125,7 @@ module ActionController not_modified?(response.last_modified) || etag_matches?(response.etag) end - # Returns the Mime type for the format used in the request. + # Returns the Mime type for the \format used in the request. # # GET /posts/5.xml | request.format => Mime::XML # GET /posts/5.xhtml | request.format => Mime::HTML @@ -142,8 +144,8 @@ module ActionController end - # Sets the format by string extension, which can be used to force custom formats that are not controlled by the extension. - # Example: + # Sets the \format by string extension, which can be used to force custom formats + # that are not controlled by the extension. # # class ApplicationController < ActionController::Base # before_filter :adjust_format_for_iphone @@ -159,7 +161,7 @@ module ActionController end # Returns a symbolized version of the <tt>:format</tt> parameter of the request. - # If no format is given it returns <tt>:js</tt>for AJAX requests and <tt>:html</tt> + # If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt> # otherwise. def template_format parameter_format = parameters[:format] @@ -190,17 +192,19 @@ module ActionController # the right-hand-side of X-Forwarded-For TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i - # Determine originating IP address. REMOTE_ADDR is the standard + # Determines originating IP address. REMOTE_ADDR is the standard # but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or # HTTP_X_FORWARDED_FOR are set by proxies so check for these if # REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma- # delimited list in the case of multiple chained proxies; the last # address which is not trusted is the originating IP. def remote_ip - if TRUSTED_PROXIES !~ @env['REMOTE_ADDR'] - return @env['REMOTE_ADDR'] - end + remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].split(',').collect(&:strip) + unless remote_addr_list.blank? + not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES} + return not_trusted_addrs.first unless not_trusted_addrs.empty? + end remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',') if @env.include? 'HTTP_CLIENT_IP' @@ -235,13 +239,13 @@ EOM memoize :server_software - # Returns the complete URL used for this request + # Returns the complete URL used for this request. def url protocol + host_with_port + request_uri end memoize :url - # Return 'https://' if this is an SSL request and 'http://' otherwise. + # Returns 'https://' if this is an SSL request and 'http://' otherwise. def protocol ssl? ? 'https://' : 'http://' end @@ -252,6 +256,7 @@ EOM @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https' end + # Returns the \host for this request, such as "example.com". def raw_host_with_port if forwarded = env["HTTP_X_FORWARDED_HOST"] forwarded.split(/,\s?/).last @@ -266,8 +271,8 @@ EOM end memoize :host - # Returns a host:port string for this request, such as example.com or - # example.com:8080. + # Returns a \host:\port string for this request, such as "example.com" or + # "example.com:8080". def host_with_port "#{host}#{port_string}" end @@ -283,7 +288,7 @@ EOM end memoize :port - # Returns the standard port number for this request's protocol + # Returns the standard \port number for this request's protocol. def standard_port case protocol when 'https://' then 443 @@ -291,13 +296,13 @@ EOM end end - # Returns a port suffix like ":8080" if the port number of this request - # is not the default HTTP port 80 or HTTPS port 443. + # Returns a \port suffix like ":8080" if the \port number of this request + # is not the default HTTP \port 80 or HTTPS \port 443. def port_string port == standard_port ? '' : ":#{port}" end - # Returns the domain part of a host, such as rubyonrails.org in "www.rubyonrails.org". You can specify + # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify # a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk". def domain(tld_length = 1) return nil unless named_host?(host) @@ -305,8 +310,9 @@ EOM host.split('.').last(1 + tld_length).join('.') end - # Returns all the subdomains as an array, so ["dev", "www"] would be returned for "dev.www.rubyonrails.org". - # You can specify a different <tt>tld_length</tt>, such as 2 to catch ["www"] instead of ["www", "rubyonrails"] + # Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be + # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>, + # such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt> # in "www.rubyonrails.co.uk". def subdomains(tld_length = 1) return [] unless named_host?(host) @@ -314,7 +320,7 @@ EOM parts[0..-(tld_length+2)] end - # Return the query string, accounting for server idiosyncrasies. + # Returns the query string, accounting for server idiosyncrasies. def query_string if uri = @env['REQUEST_URI'] uri.split('?', 2)[1] || '' @@ -324,7 +330,7 @@ EOM end memoize :query_string - # Return the request URI, accounting for server idiosyncrasies. + # Returns the request URI, accounting for server idiosyncrasies. # WEBrick includes the full URL. IIS leaves REQUEST_URI blank. def request_uri if uri = @env['REQUEST_URI'] @@ -350,7 +356,8 @@ EOM end memoize :request_uri - # Returns the interpreted path to requested resource after all the installation directory of this application was taken into account + # Returns the interpreted \path to requested resource after all the installation + # directory of this application was taken into account. def path path = (uri = request_uri) ? uri.split('?').first.to_s : '' @@ -360,7 +367,7 @@ EOM end memoize :path - # Read the request body. This is useful for web services that need to + # Read the request \body. This is useful for web services that need to # work with raw requests directly. def raw_post unless env.include? 'RAW_POST_DATA' @@ -370,7 +377,7 @@ EOM env['RAW_POST_DATA'] end - # Returns both GET and POST parameters in a single hash. + # Returns both GET and POST \parameters in a single hash. def parameters @parameters ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access end @@ -380,17 +387,17 @@ EOM @symbolized_path_parameters = @parameters = nil end - # The same as <tt>path_parameters</tt> with explicitly symbolized keys + # The same as <tt>path_parameters</tt> with explicitly symbolized keys. def symbolized_path_parameters @symbolized_path_parameters ||= path_parameters.symbolize_keys end - # Returns a hash with the parameters used to form the path of the request. - # Returned hash keys are strings. See <tt>symbolized_path_parameters</tt> for symbolized keys. - # - # Example: + # Returns a hash with the \parameters used to form the \path of the request. + # Returned hash keys are strings: # # {'action' => 'my_action', 'controller' => 'my_controller'} + # + # See <tt>symbolized_path_parameters</tt> for symbolized keys. def path_parameters @path_parameters ||= {} end diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index a1a9d68a35..83c4218af4 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -148,7 +148,7 @@ module ActionController #:nodoc: end # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>). By - # default will call render_optional_error_file. Override this method to provide more user friendly error messages.s + # default will call render_optional_error_file. Override this method to provide more user friendly error messages. def rescue_action_in_public(exception) #:doc: render_optional_error_file response_code_for_rescue(exception) end @@ -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 5f579cdb11..872b0dab3d 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -1,23 +1,23 @@ module ActionController # == Overview # - # ActionController::Resources are a way of defining RESTful resources. A RESTful resource, in basic terms, + # ActionController::Resources are a way of defining RESTful \resources. A RESTful \resource, in basic terms, # is something that can be pointed at and it will respond with a representation of the data requested. # In real terms this could mean a user with a browser requests an HTML page, or that a desktop application # requests XML data. # # RESTful design is based on the assumption that there are four generic verbs that a user of an - # application can request from a resource (the noun). + # application can request from a \resource (the noun). # - # Resources can be requested using four basic HTTP verbs (GET, POST, PUT, DELETE), the method used + # \Resources can be requested using four basic HTTP verbs (GET, POST, PUT, DELETE), the method used # denotes the type of action that should take place. # # === The Different Methods and their Usage # - # [+GET+] Requests for a resource, no saving or editing of a resource should occur in a GET request - # [+POST+] Creation of resources - # [+PUT+] Editing of attributes on a resource - # [+DELETE+] Deletion of a resource + # * GET - Requests for a \resource, no saving or editing of a \resource should occur in a GET request. + # * POST - Creation of \resources. + # * PUT - Editing of attributes on a \resource. + # * DELETE - Deletion of a \resource. # # === Examples # @@ -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,12 +149,14 @@ 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 # Creates named routes for implementing verb-oriented controllers - # for a collection resource. + # for a collection \resource. # # For example: # @@ -238,23 +248,24 @@ module ActionController # # The +resources+ method accepts the following options to customize the resulting routes: # * <tt>:collection</tt> - Add named routes for other actions that operate on the collection. - # Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt> - # or <tt>:any</tt> if the method does not matter. These routes map to a URL like /messages/rss, with a route of +rss_messages_url+. + # Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>, + # an array of any of the previous, or <tt>:any</tt> if the method does not matter. + # These routes map to a URL like /messages/rss, with a route of +rss_messages_url+. # * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member. - # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new resource action. + # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action. # * <tt>:controller</tt> - Specify the controller name for the routes. # * <tt>:singular</tt> - Specify the singular name used in the member routes. # * <tt>:requirements</tt> - Set custom routing parameter requirements. - # * <tt>:conditions</tt> - Specify custom routing recognition conditions. Resources sets the <tt>:method</tt> value for the method-specific routes. - # * <tt>:as</tt> - Specify a different resource name to use in the URL path. For example: + # * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes. + # * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example: # # products_path == '/productos' # map.resources :products, :as => 'productos' do |product| # # product_reviews_path(product) == '/productos/1234/comentarios' # product.resources :product_reviews, :as => 'comentarios' # end # - # * <tt>:has_one</tt> - Specify nested resources, this is a shorthand for mapping singleton resources beneath the current. - # * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural resources. + # * <tt>:has_one</tt> - Specify nested \resources, this is a shorthand for mapping singleton \resources beneath the current. + # * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural \resources. # # You may directly specify the routing association with +has_one+ and +has_many+ like: # @@ -277,18 +288,18 @@ module ActionController # # * <tt>:path_prefix</tt> - Set a prefix to the routes with required route variables. # - # Weblog comments usually belong to a post, so you might use resources like: + # Weblog comments usually belong to a post, so you might use +resources+ like: # # map.resources :articles # map.resources :comments, :path_prefix => '/articles/:article_id' # - # You can nest resources calls to set this automatically: + # You can nest +resources+ calls to set this automatically: # # map.resources :articles do |article| # article.resources :comments # end # - # The comment resources work the same, but must now include a value for <tt>:article_id</tt>. + # The comment \resources work the same, but must now include a value for <tt>:article_id</tt>. # # article_comments_url(@article) # article_comment_url(@article, @comment) @@ -306,17 +317,42 @@ module ActionController # map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_' # map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_' # - # You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested resource: + # You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested \resource: # # map.resources :articles do |article| # article.resources :comments, :name_prefix => nil # end # - # This will yield named resources like so: + # This will yield named \resources like so: # # 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: @@ -349,28 +385,28 @@ module ActionController # # The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an # HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in - # <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for resource routes. + # <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for \resource routes. def resources(*entities, &block) options = entities.extract_options! entities.each { |entity| map_resource(entity, options.dup, &block) } end - # Creates named routes for implementing verb-oriented controllers for a singleton resource. - # A singleton resource is global to its current context. For unnested singleton resources, - # the resource is global to the current user visiting the application, such as a user's - # /account profile. For nested singleton resources, the resource is global to its parent - # resource, such as a <tt>projects</tt> resource that <tt>has_one :project_manager</tt>. - # The <tt>project_manager</tt> should be mapped as a singleton resource under <tt>projects</tt>: + # Creates named routes for implementing verb-oriented controllers for a singleton \resource. + # A singleton \resource is global to its current context. For unnested singleton \resources, + # the \resource is global to the current user visiting the application, such as a user's + # <tt>/account</tt> profile. For nested singleton \resources, the \resource is global to its parent + # \resource, such as a <tt>projects</tt> \resource that <tt>has_one :project_manager</tt>. + # The <tt>project_manager</tt> should be mapped as a singleton \resource under <tt>projects</tt>: # # map.resources :projects do |project| # project.resource :project_manager # end # - # See map.resources for general conventions. These are the main differences: - # * A singular name is given to map.resource. The default controller name is still taken from the plural name. + # See +resources+ for general conventions. These are the main differences: + # * A singular name is given to <tt>map.resource</tt>. The default controller name is still taken from the plural name. # * To specify a custom plural name, use the <tt>:plural</tt> option. There is no <tt>:singular</tt> option. - # * No default index route is created for the singleton resource controller. - # * When nesting singleton resources, only the singular name is used as the path prefix (example: 'account/messages/1') + # * No default index route is created for the singleton \resource controller. + # * When nesting singleton \resources, only the singular name is used as the path prefix (example: 'account/messages/1') # # For example: # @@ -442,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 @@ -459,29 +495,45 @@ 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 def map_collection_actions(map, resource) resource.collection_methods.each do |method, actions| actions.each do |action| - action_options = action_options_for(action, resource, method) - map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options) + [method].flatten.each do |m| + action_options = action_options_for(action, resource, m) + map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options) + end end end end @@ -521,17 +573,19 @@ module ActionController def map_member_actions(map, resource) resource.member_methods.each do |method, actions| actions.each do |action| - action_options = action_options_for(action, resource, method) + [method].flatten.each do |m| + action_options = action_options_for(action, resource, m) - action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) - action_path ||= Base.resources_path_names[action] || action + 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) @@ -574,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/response.rb b/actionpack/lib/action_controller/response.rb index 5dac4128bb..54a99996ef 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -129,8 +129,8 @@ module ActionController # :nodoc: def prepare! assign_default_content_type_and_charset! - set_content_length! handle_conditional_get! + set_content_length! convert_content_type! end diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb index 03427e41de..5704d9d01a 100644 --- a/actionpack/lib/action_controller/routing/builder.rb +++ b/actionpack/lib/action_controller/routing/builder.rb @@ -187,12 +187,14 @@ module ActionController private def validate_route_conditions(conditions) if method = conditions[:method] - if method == :head - raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers" - end + [method].flatten.each do |m| + if m == :head + raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers" + end - unless HTTP_METHODS.include?(method.to_sym) - raise ArgumentError, "Invalid HTTP method specified in route conditions: #{conditions.inspect}" + unless HTTP_METHODS.include?(m.to_sym) + raise ArgumentError, "Invalid HTTP method specified in route conditions: #{conditions.inspect}" + end end end 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/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb index 6d54d0334c..4935432d87 100644 --- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb +++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb @@ -134,6 +134,9 @@ module ActionController def write_recognize_optimized! tree = segment_tree(routes) body = generate_code(tree) + + remove_recognize_optimized! + instance_eval %{ def recognize_optimized(path, env) segments = to_plain_segments(path) @@ -147,6 +150,25 @@ module ActionController end }, __FILE__, __LINE__ end + + def clear_recognize_optimized! + remove_recognize_optimized! + + class << self + def recognize_optimized(path, environment) + write_recognize_optimized! + recognize_optimized(path, environment) + end + end + end + + def remove_recognize_optimized! + if respond_to?(:recognize_optimized) + class << self + remove_method :recognize_optimized + end + end + end end end end diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb index 2106ac09e0..3b2cb28545 100644 --- a/actionpack/lib/action_controller/routing/route.rb +++ b/actionpack/lib/action_controller/routing/route.rb @@ -201,7 +201,7 @@ module ActionController # recognition, not generation. def recognition_conditions result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"] - result << "conditions[:method] === env[:method]" if conditions[:method] + result << "[conditions[:method]].flatten.include?(env[:method])" if conditions[:method] result end diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 8dfc22f94f..ff448490e9 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 @@ -195,7 +195,7 @@ module ActionController self.routes = [] self.named_routes = NamedRouteCollection.new - write_recognize_optimized! + clear_recognize_optimized! end # Subclasses and plugins may override this method to specify a different @@ -217,7 +217,7 @@ module ActionController @routes_by_controller = nil # This will force routing/recognition_optimization.rb # to refresh optimisations. - @compiled_recognize_optimized = nil + clear_recognize_optimized! end def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false) @@ -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/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index 9d4b740a44..e5f174ae2c 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -160,7 +160,7 @@ module ActionController s << "\n#{expiry_statement}" end - def interpolation_chunk(value_code = "#{local_name}") + def interpolation_chunk(value_code = local_name) "\#{URI.escape(#{value_code}.to_s, ActionController::Routing::Segment::UNSAFE_PCHAR)}" end @@ -231,7 +231,7 @@ module ActionController end # Don't URI.escape the controller name since it may contain slashes. - def interpolation_chunk(value_code = "#{local_name}") + def interpolation_chunk(value_code = local_name) "\#{#{value_code}.to_s}" end @@ -251,7 +251,7 @@ module ActionController end class PathSegment < DynamicSegment #:nodoc: - def interpolation_chunk(value_code = "#{local_name}") + def interpolation_chunk(value_code = local_name) "\#{#{value_code}}" end diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb index 5bf7503f04..f2fb200950 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_controller/session/cookie_store.rb @@ -70,7 +70,8 @@ class CGI::Session::CookieStore 'path' => options['session_path'], 'domain' => options['session_domain'], 'expires' => options['session_expires'], - 'secure' => options['session_secure'] + 'secure' => options['session_secure'], + 'http_only' => options['session_http_only'] } # Set no_hidden and no_cookies since the session id is unused and we diff --git a/actionpack/lib/action_controller/session_management.rb b/actionpack/lib/action_controller/session_management.rb index 80a3ddd2c5..fd3d94ed97 100644 --- a/actionpack/lib/action_controller/session_management.rb +++ b/actionpack/lib/action_controller/session_management.rb @@ -60,6 +60,10 @@ module ActionController #:nodoc: # # the session will only work over HTTPS, but only for the foo action # session :only => :foo, :session_secure => true # + # # the session by default uses HttpOnly sessions for security reasons. + # # this can be switched off. + # session :only => :foo, :session_http_only => false + # # # the session will only be disabled for 'foo', and only if it is # # requested as a web service # session :off, :only => :foo, @@ -86,14 +90,14 @@ module ActionController #:nodoc: raise ArgumentError, "only one of either :only or :except are allowed" end - write_inheritable_array("session_options", [options]) + write_inheritable_array(:session_options, [options]) end # So we can declare session options in the Rails initializer. alias_method :session=, :session def cached_session_options #:nodoc: - @session_options ||= read_inheritable_attribute("session_options") || [] + @session_options ||= read_inheritable_attribute(:session_options) || [] end def session_options_for(request, action) #:nodoc: diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index c6b1470070..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. @@ -331,7 +338,7 @@ module ActionController #:nodoc: attr_reader :original_filename # The content type of the "uploaded" file - attr_reader :content_type + attr_accessor :content_type def initialize(path, content_type = Mime::TEXT, binary = false) raise "#{path} file does not exist" unless File.exist?(path) @@ -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/translation.rb b/actionpack/lib/action_controller/translation.rb new file mode 100644 index 0000000000..9bb63cdb15 --- /dev/null +++ b/actionpack/lib/action_controller/translation.rb @@ -0,0 +1,13 @@ +module ActionController + module Translation + def translate(*args) + I18n.translate *args + end + alias :t :translate + + def localize(*args) + I18n.localize *args + end + alias :l :localize + end +end
\ No newline at end of file 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.rb b/actionpack/lib/action_view.rb index 3590ab6d49..7cd9b633ac 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -43,9 +43,7 @@ require 'action_view/base' require 'action_view/partials' require 'action_view/template_error' -I18n.backend.populate do - I18n.load_translations "#{File.dirname(__FILE__)}/action_view/locale/en-US.yml" -end +I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en-US.yml" require 'action_view/helpers' diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index d174c784f3..8c00670087 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 @@ -208,10 +218,24 @@ module ActionView #:nodoc: ActionView::PathSet.new(Array(value)) end + attr_reader :helpers + + class ProxyModule < Module + def initialize(receiver) + @receiver = receiver + end + + def include(*args) + super(*args) + @receiver.extend(*args) + end + end + def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc: @assigns = assigns_for_first_render @assigns_added = nil @controller = controller + @helpers = ProxyModule.new(self) self.view_paths = view_paths end @@ -232,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 @@ -267,83 +278,106 @@ module ActionView #:nodoc: # the same name but differing formats. See +Request#template_format+ # for more details. def template_format - return @template_format if @template_format - - if controller && controller.respond_to?(:request) + if defined? @template_format + @template_format + elsif controller && controller.respond_to?(:request) @template_format = controller.request.template_format else @template_format = :html 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 if defined?(@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.rb b/actionpack/lib/action_view/helpers.rb index 05e1cf990a..ff97df204c 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -21,7 +21,6 @@ module ActionView #:nodoc: include CaptureHelper include DateHelper include DebugHelper - include FormCountryHelper include FormHelper include FormOptionsHelper include FormTagHelper diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index c339e10701..8b56d241ae 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -246,7 +246,7 @@ module ActionView alias_method :tag_without_error_wrapping, :tag def tag(name, options) - if object.respond_to?("errors") && object.errors.respond_to?("on") + if object.respond_to?(:errors) && object.errors.respond_to?(:on) error_wrapping(tag_without_error_wrapping(name, options), object.errors.on(@method_name)) else tag_without_error_wrapping(name, options) @@ -255,7 +255,7 @@ module ActionView alias_method :content_tag_without_error_wrapping, :content_tag def content_tag(name, value, options) - if object.respond_to?("errors") && object.errors.respond_to?("on") + if object.respond_to?(:errors) && object.errors.respond_to?(:on) error_wrapping(content_tag_without_error_wrapping(name, value, options), object.errors.on(@method_name)) else content_tag_without_error_wrapping(name, value, options) @@ -264,7 +264,7 @@ module ActionView alias_method :to_date_select_tag_without_error_wrapping, :to_date_select_tag def to_date_select_tag(options = {}, html_options = {}) - if object.respond_to?("errors") && object.errors.respond_to?("on") + if object.respond_to?(:errors) && object.errors.respond_to?(:on) error_wrapping(to_date_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name)) else to_date_select_tag_without_error_wrapping(options, html_options) @@ -273,7 +273,7 @@ module ActionView alias_method :to_datetime_select_tag_without_error_wrapping, :to_datetime_select_tag def to_datetime_select_tag(options = {}, html_options = {}) - if object.respond_to?("errors") && object.errors.respond_to?("on") + if object.respond_to?(:errors) && object.errors.respond_to?(:on) error_wrapping(to_datetime_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name)) else to_datetime_select_tag_without_error_wrapping(options, html_options) @@ -282,7 +282,7 @@ module ActionView alias_method :to_time_select_tag_without_error_wrapping, :to_time_select_tag def to_time_select_tag(options = {}, html_options = {}) - if object.respond_to?("errors") && object.errors.respond_to?("on") + if object.respond_to?(:errors) && object.errors.respond_to?(:on) error_wrapping(to_time_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name)) else to_time_select_tag_without_error_wrapping(options, html_options) @@ -298,7 +298,7 @@ module ActionView end def column_type - object.send("column_for_attribute", @method_name).type + object.send(:column_for_attribute, @method_name).type end end end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 623ed1e8df..a926599e25 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -104,7 +104,7 @@ module ActionView ASSETS_DIR = defined?(Rails.public_path) ? Rails.public_path : "public" JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts" STYLESHEETS_DIR = "#{ASSETS_DIR}/stylesheets" - JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].map(&:to_s).freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) + JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or @@ -463,7 +463,8 @@ module ActionView end private - COMPUTED_PUBLIC_PATHS = ActiveSupport::Cache::MemoryStore.new.silence! + COMPUTED_PUBLIC_PATHS = {} + COMPUTED_PUBLIC_PATHS_GUARD = Mutex.new # Add the the extension +ext+ if not present. Return full URLs otherwise untouched. # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL @@ -483,23 +484,24 @@ module ActionView dir, source, ext, include_host ].join end - source = COMPUTED_PUBLIC_PATHS.fetch(cache_key) do - begin - source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}")) - - if source =~ %r{^[-a-z]+://} - source - else - source = "/#{dir}/#{source}" unless source[0] == ?/ - if has_request - unless source =~ %r{^#{ActionController::Base.relative_url_root}/} - source = "#{ActionController::Base.relative_url_root}#{source}" + COMPUTED_PUBLIC_PATHS_GUARD.synchronize do + source = COMPUTED_PUBLIC_PATHS[cache_key] ||= + begin + source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}")) + + if source =~ %r{^[-a-z]+://} + source + else + source = "/#{dir}/#{source}" unless source[0] == ?/ + if has_request + unless source =~ %r{^#{ActionController::Base.relative_url_root}/} + source = "#{ActionController::Base.relative_url_root}#{source}" + end end - end - rewrite_asset_path(source) + rewrite_asset_path(source) + end end - end end if include_host && source !~ %r{^[-a-z]+://} diff --git a/actionpack/lib/action_view/helpers/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb index 743d1d40ec..bd72cda700 100644 --- a/actionpack/lib/action_view/helpers/benchmark_helper.rb +++ b/actionpack/lib/action_view/helpers/benchmark_helper.rb @@ -15,15 +15,15 @@ module ActionView # <%= expensive_files_operation %> # <% end %> # - # That would add something like "Process data files (0.34523)" to the log, + # That would add something like "Process data files (345.2ms)" to the log, # which you can then use to compare timings when optimizing your code. # # You may give an optional logger level as the second argument # (:debug, :info, :warn, :error); the default value is :info. def benchmark(message = "Benchmarking", level = :info) if controller.logger - real = Benchmark.realtime { yield } - controller.logger.send(level, "#{message} (#{'%.5f' % real})") + seconds = Benchmark.realtime { yield } + controller.logger.send(level, "#{message} (#{'%.1f' % (seconds * 1000)}ms)") else yield end diff --git a/actionpack/lib/action_view/helpers/form_country_helper.rb b/actionpack/lib/action_view/helpers/form_country_helper.rb deleted file mode 100644 index 84e811f61d..0000000000 --- a/actionpack/lib/action_view/helpers/form_country_helper.rb +++ /dev/null @@ -1,92 +0,0 @@ -require 'action_view/helpers/form_options_helper' - -module ActionView - module Helpers - module FormCountryHelper - - # Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags. - def country_select(object, method, priority_countries = nil, options = {}, html_options = {}) - InstanceTag.new(object, method, self, options.delete(:object)).to_country_select_tag(priority_countries, options, html_options) - end - - # Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to - # have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so - # that they will be listed above the rest of the (long) list. - # - # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. - def country_options_for_select(selected = nil, priority_countries = nil) - country_options = "" - - if priority_countries - country_options += options_for_select(priority_countries, selected) - country_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n" - end - - return country_options + options_for_select(COUNTRIES, selected) - end - - private - - # All the countries included in the country_options output. - COUNTRIES = ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", - "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", - "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", - "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil", - "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", - "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", - "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", - "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", - "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", - "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", - "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", - "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", - "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)", - "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", - "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", - "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", - "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", - "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of", - "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", - "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", - "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", - "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", - "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", - "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", - "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", - "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", - "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", - "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", - "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", - "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", - "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", - "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste", - "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", - "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", - "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", - "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", - "Yemen", "Zambia", "Zimbabwe"] unless const_defined?("COUNTRIES") - end - - class InstanceTag #:nodoc: - include FormCountryHelper - - def to_country_select_tag(priority_countries, options, html_options) - html_options = html_options.stringify_keys - add_default_name_and_id(html_options) - value = value(object) - content_tag("select", - add_options( - country_options_for_select(value, priority_countries), - options, value - ), html_options - ) - end - end - - class FormBuilder - def country_select(method, priority_countries = nil, options = {}, html_options = {}) - @template.country_select(@object_name, method, priority_countries, objectify_options(options), @default_options.merge(html_options)) - end - end - end -end
\ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 9aae945408..33f8aaf9ed 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -324,9 +324,6 @@ module ActionView value == selected end end - - # All the countries included in the country_options output. - COUNTRIES = ActiveSupport::Deprecation::DeprecatedConstantProxy.new 'COUNTRIES', 'ActionView::Helpers::FormCountryHelper::COUNTRIES' end class InstanceTag #:nodoc: diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index e8ca02d760..294c22521e 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -116,7 +116,7 @@ module ActionView # Creates a label field # - # ==== Options + # ==== Options # * Creates standard HTML attributes for the tag. # # ==== Examples @@ -155,10 +155,10 @@ module ActionView # Creates a file upload field. If you are using file uploads then you will also need # to set the multipart option for the form tag: # - # <%= form_tag { :action => "post" }, { :multipart => true } %> + # <% form_tag '/upload', :multipart => true do %> # <label for="file">File to Upload</label> <%= file_field_tag "file" %> # <%= submit_tag %> - # <%= end_form_tag %> + # <% end %> # # The specified URL will then be passed a File object containing the selected file, or if the field # was left blank, a StringIO object. @@ -351,19 +351,16 @@ module ActionView disable_with = "this.value='#{disable_with}'" disable_with << ";#{options.delete('onclick')}" if options['onclick'] - options["onclick"] = [ - "this.setAttribute('originalValue', this.value)", - "this.disabled=true", - disable_with, - "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit())", - "if (result == false) { this.value = this.getAttribute('originalValue'); this.disabled = false }", - "return result;", - ].join(";") + options["onclick"] = "if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }" + options["onclick"] << "else { hiddenCommit = this.cloneNode(false);hiddenCommit.setAttribute('type', 'hidden');this.form.appendChild(hiddenCommit); }" + options["onclick"] << "this.setAttribute('originalValue', this.value);this.disabled = true;#{disable_with};" + options["onclick"] << "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());" + options["onclick"] << "if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;" end if confirm = options.delete("confirm") options["onclick"] ||= '' - options["onclick"] += "return #{confirm_javascript_function(confirm)};" + options["onclick"] << "return #{confirm_javascript_function(confirm)};" end tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys) @@ -374,6 +371,9 @@ module ActionView # <tt>source</tt> is passed to AssetTagHelper#image_path # # ==== Options + # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm + # prompt with the question specified. If the user accepts, the form is + # processed normally, otherwise no action is taken. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input. # * Any other key creates standard HTML options for the tag. # @@ -390,6 +390,13 @@ module ActionView # image_submit_tag("agree.png", :disabled => true, :class => "agree-disagree-button") # # => <input class="agree-disagree-button" disabled="disabled" src="/images/agree.png" type="image" /> def image_submit_tag(source, options = {}) + options.stringify_keys! + + if confirm = options.delete("confirm") + options["onclick"] ||= '' + options["onclick"] += "return #{confirm_javascript_function(confirm)};" + end + tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys) end diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index ff83494e94..ff41a6d417 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 @@ -588,26 +596,8 @@ module ActionView private def include_helpers_from_context - unless generator_methods_module = @context.instance_variable_get(:@__javascript_generator_methods__) - modules = @context.extended_by - ([ActionView::Helpers] + ActionView::Helpers.included_modules) - - generator_methods_module = Module.new do - modules.each do |mod| - begin - include mod - rescue Exception => e - # HACK: Probably not a good idea to suppress these warnings - # AFAIK exceptions are only raised in while testing with mocha - # because the module does not like to be included into other - # non TestUnit classes - end - end - include GeneratorMethods - end - @context.instance_variable_set(:@__javascript_generator_methods__, generator_methods_module) - end - - extend generator_methods_module + extend @context.helpers if @context.respond_to?(:helpers) + extend GeneratorMethods end # JavaScriptGenerator generates blocks of JavaScript code that allow you @@ -624,7 +614,7 @@ module ActionView # Example: # # # Generates: - # # new Element.insert("list", { bottom: <li>Some item</li>" }); + # # new Element.insert("list", { bottom: "<li>Some item</li>" }); # # new Effect.Highlight("list"); # # ["status-indicator", "cancel-link"].each(Element.hide); # update_page do |page| @@ -1070,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/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 9bb235175e..0cdb70e217 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -49,9 +49,9 @@ module ActionView # def content_tag_for(tag_name, record, *args, &block) prefix = args.first.is_a?(Hash) ? nil : args.shift - options = args.first.is_a?(Hash) ? args.shift : {} - concat content_tag(tag_name, capture(&block), - options.merge({ :class => "#{dom_class(record)} #{options[:class]}".strip, :id => dom_id(record, prefix) })) + options = args.extract_options! + options.merge!({ :class => "#{dom_class(record)} #{options[:class]}".strip, :id => dom_id(record, prefix) }) + content_tag(tag_name, options, &block) end end end diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index 637caf203b..435ba936e1 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -1,5 +1,14 @@ require 'action_view/helpers/tag_helper' -require 'html/document' + +begin + require 'html/document' +rescue LoadError + html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner" + if File.directory?(html_scanner_path) + $:.unshift html_scanner_path + require 'html/document' + end +end module ActionView module Helpers #:nodoc: diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index b938c1a801..1d01dafd0e 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -95,7 +95,7 @@ module ActionView # * <tt>:containment</tt> - Takes an element or array of elements to treat as # potential drop targets (defaults to the original target element). # - # * <tt>:only</tt> - A CSS class name or arry of class names used to filter + # * <tt>:only</tt> - A CSS class name or array of class names used to filter # out child elements as candidates. # # * <tt>:scroll</tt> - Determines whether to scroll the list during drag diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index f9096d0029..ab1fdc80bc 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -1,5 +1,14 @@ require 'action_view/helpers/tag_helper' -require 'html/document' + +begin + require 'html/document' +rescue LoadError + html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner" + if File.directory?(html_scanner_path) + $:.unshift html_scanner_path + require 'html/document' + end +end module ActionView module Helpers #:nodoc: @@ -289,7 +298,7 @@ module ActionView "" else textilized = RedCloth.new(text, [ :hard_breaks ]) - textilized.hard_breaks = true if textilized.respond_to?("hard_breaks=") + textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=) textilized.to_html end end @@ -439,8 +448,10 @@ module ActionView # array every time it is called. This can be used for example, to alternate # classes for table rows. You can use named cycles to allow nesting in loops. # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a - # named cycle. You can manually reset a cycle by calling reset_cycle and passing the - # name of the cycle. + # named cycle. The default name for a cycle without a +:name+ key is + # <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle + # and passing the name of the cycle. The current cycle string can be obtained + # anytime using the current_cycle method. # # ==== Examples # # Alternate CSS classes for even and odd numbers... @@ -487,6 +498,23 @@ module ActionView return cycle.to_s end + # Returns the current cycle string after a cycle has been started. Useful + # for complex table highlighing or any other design need which requires + # the current cycle string in more than one place. + # + # ==== Example + # # Alternate background colors + # @items = [1,2,3,4] + # <% @items.each do |item| %> + # <div style="background-color:<%= cycle("red","white","blue") %>"> + # <span style="background-color:<%= current_cycle %>"><%= item %></span> + # </div> + # <% end %> + def current_cycle(name = "default") + cycle = get_cycle(name) + cycle.current_value unless cycle.nil? + end + # Resets a cycle so that it starts from the first element the next time # it is called. Pass in +name+ to reset a named cycle. # @@ -523,11 +551,29 @@ module ActionView @index = 0 end + def current_value + @values[previous_index].to_s + end + def to_s value = @values[@index].to_s - @index = (@index + 1) % @values.size + @index = next_index return value end + + private + + def next_index + step_index(1) + end + + def previous_index + step_index(-1) + end + + def step_index(n) + (@index + n) % @values.size + end end private 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 c011f21550..0134bc988f 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -1,8 +1,7 @@ module ActionView - module Renderable - # NOTE: The template that this mixin is beening include into is frozen - # So you can not set or modify any instance variables - + # NOTE: The template that this mixin is being included into is frozen + # so you cannot set or modify any instance variables + module Renderable #:nodoc: extend ActiveSupport::Memoizable def self.included(base) @@ -26,17 +25,18 @@ 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") + ivar = :@_proc_for_layout + if view.instance_variable_defined?(ivar) and proc = view.instance_variable_get(ivar) view.capture(*names, &proc) - else - view.instance_variable_get("@content_for_#{names.first || 'layout'}") + elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}") + view.instance_variable_get(ivar) end end end @@ -72,12 +72,9 @@ module ActionView end_src begin - logger = Base.logger - logger.debug "Compiling template #{render_symbol}" if logger - ActionView::Base::CompiledTemplates.module_eval(source, filename, 0) rescue Exception => e # errors from template code - if logger + if logger = defined?(ActionController) && Base.logger logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" logger.debug "Function body: #{source}" logger.debug "Backtrace: #{e.backtrace.join("\n")}" diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb index 342850f0f0..d92ff1b8d3 100644 --- a/actionpack/lib/action_view/renderable_partial.rb +++ b/actionpack/lib/action_view/renderable_partial.rb @@ -1,8 +1,7 @@ module ActionView - module RenderablePartial - # NOTE: The template that this mixin is beening include into is frozen - # So you can not set or modify any instance variables - + # NOTE: The template that this mixin is being included into is frozen + # so you cannot set or modify any instance variables + module RenderablePartial #:nodoc: extend ActiveSupport::Memoizable def variable_name @@ -16,15 +15,28 @@ module ActionView memoize :counter_name def render(view, local_assigns = {}) - ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do + if defined? ActionController + ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do + super + end + else super end end def render_partial(view, object = nil, local_assigns = {}, as = nil) object ||= local_assigns[:object] || - local_assigns[variable_name] || - view.controller.instance_variable_get("@#{variable_name}") if view.respond_to?(:controller) + local_assigns[variable_name] + + if view.respond_to?(:controller) + ivar = :"@#{variable_name}" + object ||= + if view.controller.instance_variable_defined?(ivar) + ActiveSupport::Deprecation::DeprecatedObjectProxy.new( + view.controller.instance_variable_get(ivar), + "#{ivar} will no longer be implicitly assigned to #{variable_name}") + end + end # Ensure correct object is reassigned to other accessors local_assigns[:object] = local_assigns[variable_name] = object diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 5dc6708431..64597b3d39 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -1,3 +1,5 @@ +require 'action_controller/mime_type' + module ActionView #:nodoc: class Template extend TemplateHandlers 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 |