From 69bc2043f93890048e415dd7c5f1feba5a20d145 Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Tue, 9 Jun 2009 09:01:50 -0500 Subject: enable *real* IO parsing for the libxml, nokogiri and rexml backends [#2659 state:resolved] Signed-off-by: Joshua Peek --- activesupport/lib/active_support/xml_mini/libxml.rb | 14 ++++++++------ activesupport/lib/active_support/xml_mini/nokogiri.rb | 10 ++++++---- activesupport/lib/active_support/xml_mini/rexml.rb | 18 ++++++++++++------ 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index d4c4dc7be5..2ae22c35fb 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -9,16 +9,18 @@ module ActiveSupport # data:: # XML Document string or IO to parse def parse(data) - if data.respond_to?(:read) - data = data.read + if !data.respond_to?(:read) + data = StringIO.new(data || '') end - + LibXML::XML.default_keep_blanks = false - - if data.blank? + + char = data.getc + if char.nil? {} else - LibXML::XML::Parser.string(data.strip).parse.to_hash + data.ungetc(char) + LibXML::XML::Parser.io(data).parse.to_hash end end diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 7337c143c9..622523a1b9 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -9,13 +9,15 @@ module ActiveSupport # data:: # XML Document string or IO to parse def parse(data) - if data.respond_to?(:read) - data = data.read + if !data.respond_to?(:read) + data = StringIO.new(data || '') end - - if data.blank? + + char = data.getc + if char.nil? {} else + data.ungetc(char) doc = Nokogiri::XML(data) raise doc.errors.first if doc.errors.length > 0 doc.to_hash diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index 1184d2d6c9..aa2461535b 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -15,13 +15,19 @@ module ActiveSupport # data:: # XML Document string or IO to parse def parse(data) - if data.respond_to?(:read) - data = data.read + if !data.respond_to?(:read) + data = StringIO.new(data || '') + end + + char = data.getc + if char.nil? + {} + else + data.ungetc(char) + require 'rexml/document' unless defined?(REXML::Document) + doc = REXML::Document.new(data) + merge_element!({}, doc.root) end - - require 'rexml/document' unless defined?(REXML::Document) - doc = REXML::Document.new(data) - merge_element!({}, doc.root) end private -- cgit v1.2.3 From d7e0cb05cc8adad430d0e97efef1608eef58db80 Mon Sep 17 00:00:00 2001 From: Matt Jones Date: Tue, 9 Jun 2009 12:26:52 -0400 Subject: Fix incorrect specification path in GemDependency#from_directory_name Signed-off-by: Michael Koziarski --- railties/lib/rails/gem_dependency.rb | 2 +- railties/test/gem_dependency_test.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index 3a82202bd0..3cc75494e4 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -34,7 +34,7 @@ module Rails name = directory_name_parts[0..-2].join('-') version = directory_name_parts.last result = self.new(name, :version => version) - spec_filename = File.join(unpacked_path, directory_name, '.specification') + spec_filename = File.join(directory_name, '.specification') if load_spec raise "Missing specification file in #{File.dirname(spec_filename)}. Perhaps you need to do a 'rake gems:refresh_specs'?" unless File.exists?(spec_filename) spec = YAML::load_file(spec_filename) diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index 195494a926..70f4496685 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -178,6 +178,13 @@ class GemDependencyTest < Test::Unit::TestCase assert_equal '= 1.1', dummy_gem.version_requirements.to_s end + def test_gem_from_directory_name_loads_specification_successfully + assert_nothing_raised do + dummy_gem = Rails::GemDependency.from_directory_name(File.join(Rails::GemDependency.unpacked_path, 'dummy-gem-g-1.0.0')) + assert_not_nil dummy_gem.specification + end + end + def test_gem_from_invalid_directory_name assert_raises RuntimeError do dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem') -- cgit v1.2.3 From acb244778587ff400f5b1d54e27028a2dae91101 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 9 Jun 2009 14:50:01 -0700 Subject: Write documentation for AbstractController::Helpers --- .../lib/action_controller/abstract/helpers.rb | 52 ++++++++++++++-------- .../lib/action_controller/abstract/renderer.rb | 3 ++ .../lib/action_controller/new_base/helpers.rb | 4 +- actionpack/test/controller/helper_test.rb | 19 ++++++-- 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/action_controller/abstract/helpers.rb index 0a2776de9c..6b73f887c1 100644 --- a/actionpack/lib/action_controller/abstract/helpers.rb +++ b/actionpack/lib/action_controller/abstract/helpers.rb @@ -5,33 +5,26 @@ module AbstractController include Renderer included do - extlib_inheritable_accessor :master_helper_module - self.master_helper_module = Module.new + extlib_inheritable_accessor(:_helpers) { Module.new } end + # Override AbstractController::Renderer's _action_view to include the + # helper module for this class into its helpers module. def _action_view - @_action_view ||= begin - av = super - av.helpers.send(:include, master_helper_module) - av - end + @_action_view ||= super.tap { |av| av.helpers.include(_helpers) } end module ClassMethods + # When a class is inherited, wrap its helper module in a new module. + # This ensures that the parent class's module can be changed + # independently of the child class's. def inherited(klass) - klass.master_helper_module = Module.new - klass.master_helper_module.__send__ :include, master_helper_module + helpers = _helpers + klass._helpers = Module.new { include helpers } super end - # Makes all the (instance) methods in the helper module available to templates rendered through this controller. - # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules - # available to the templates. - def add_template_helper(mod) - master_helper_module.module_eval { include mod } - end - # Declare a controller method as a helper. For example, the following # makes the +current_user+ controller method available to the view: # class ApplicationController < ActionController::Base @@ -48,9 +41,13 @@ module AbstractController # # In a view: # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%> + # + # ==== Parameters + # meths:: The name of a method on the controller + # to be made available on the view. def helper_method(*meths) meths.flatten.each do |meth| - master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 + _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 def #{meth}(*args, &blk) controller.send(%(#{meth}), *args, &blk) end @@ -58,6 +55,14 @@ module AbstractController end end + # Make a number of helper modules part of this class' default + # helpers. + # + # ==== Parameters + # *args:: Modules to be included + # block:: Evalulate the block in the context + # of the helper module. Any methods defined in the block + # will be helpers. def helper(*args, &block) args.flatten.each do |arg| case arg @@ -65,7 +70,18 @@ module AbstractController add_template_helper(arg) end end - master_helper_module.module_eval(&block) if block_given? + _helpers.module_eval(&block) if block_given? + end + + private + # Makes all the (instance) methods in the helper module available to templates + # rendered through this controller. + # + # ==== Parameters + # mod:: The module to include into the current helper module + # for the class + def add_template_helper(mod) + _helpers.module_eval { include mod } end end end diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb index 28bdda4a75..611d3a16ce 100644 --- a/actionpack/lib/action_controller/abstract/renderer.rb +++ b/actionpack/lib/action_controller/abstract/renderer.rb @@ -29,6 +29,9 @@ module AbstractController # partial:: Whether or not the template to render is a partial # _partial:: If a partial, rather than a template, was rendered, return # the partial. + # helpers:: A module containing the helpers to be used in the view. This + # module should respond_to include. + # controller:: The controller that initialized the ActionView # # Override this method in a to change the default behavior. def _action_view diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb index e8000be87b..8925dd3969 100644 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -83,7 +83,7 @@ module ActionController end # Evaluate block in template class if given. - master_helper_module.module_eval(&block) if block_given? + _helpers.module_eval(&block) if block_given? end # Declares helper accessors for controller attributes. For example, the @@ -99,7 +99,7 @@ module ActionController def helpers unless @helper_proxy @helper_proxy = ActionView::Base.new - @helper_proxy.extend master_helper_module + @helper_proxy.extend _helpers else @helper_proxy end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 342cbfbcd3..515c4c9f52 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -127,7 +127,11 @@ class HelperTest < Test::Unit::TestCase end def test_all_helpers - methods = AllHelpersController.master_helper_module.instance_methods.map {|m| m.to_s} + methods = if defined?(ActionController::Http) + AllHelpersController._helpers.instance_methods.map {|m| m.to_s} + else + AllHelpersController.master_helper_module.instance_methods.map {|m| m.to_s} + end # abc_helper.rb assert methods.include?('bare_a') @@ -143,7 +147,12 @@ class HelperTest < Test::Unit::TestCase @controller_class.helpers_dir = File.dirname(__FILE__) + '/../fixtures/alternate_helpers' # Reload helpers - @controller_class.master_helper_module = Module.new + if defined?(ActionController::Http) + @controller_class._helpers = Module.new + else + @controller_class.master_helper_module = Module.new + end + @controller_class.helper :all # helpers/abc_helper.rb should not be included @@ -175,7 +184,11 @@ class HelperTest < Test::Unit::TestCase end def master_helper_methods - @controller_class.master_helper_module.instance_methods.map {|m| m.to_s } + if defined?(ActionController::Http) + @controller_class._helpers.instance_methods.map {|m| m.to_s } + else + @controller_class.master_helper_module.instance_methods.map {|m| m.to_s } + end end def missing_methods -- cgit v1.2.3 From f35f47b8c0bbb181352e9c957f02693cb1801b76 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 9 Jun 2009 16:46:42 -0700 Subject: Cleaning up and documenting AbstractController::Layouts --- .../lib/action_controller/abstract/layouts.rb | 114 ++++++++++---- .../action_controller/new_base/compatibility.rb | 5 +- .../lib/action_controller/new_base/layouts.rb | 171 ++++++++++++++++++++- .../abstract_controller_test.rb | 2 +- .../test/abstract_controller/layouts_test.rb | 6 +- 5 files changed, 255 insertions(+), 43 deletions(-) diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 273063f74b..9ff8e9beb1 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -5,16 +5,26 @@ module AbstractController include Renderer included do - extlib_inheritable_accessor :_layout_conditions - self._layout_conditions = {} + extlib_inheritable_accessor(:_layout_conditions) { Hash.new } end module ClassMethods + # Specify the layout to use for this class. + # + # If the specified layout is a: + # String:: the String is the template name + # Symbol:: call the method specified by the symbol, which will return + # the template name + # false:: There is no layout + # true:: raise an ArgumentError + # + # ==== Parameters + # layout:: The layout to use. + # + # ==== Options (conditions) + # :only<#to_s, Array[#to_s]>:: A list of actions to apply this layout to. + # :except<#to_s, Array[#to_s]>:: Apply this layout to all actions but this one def layout(layout, conditions = {}) - unless [String, Symbol, FalseClass, NilClass].include?(layout.class) - raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil" - end - conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} } self._layout_conditions = conditions @@ -22,6 +32,11 @@ module AbstractController _write_layout_method end + # If no layout is supplied, look for a template named the return + # value of this method. + # + # ==== Returns + # String:: A template name def _implied_layout_name name.underscore end @@ -29,23 +44,31 @@ module AbstractController # Takes the specified layout and creates a _layout method to be called # by _default_layout # - # If the specified layout is a: - # String:: return the string - # Symbol:: call the method specified by the symbol - # false:: return nil - # none:: If a layout is found in the view paths with the controller's - # name, return that string. Otherwise, use the superclass' - # layout (which might also be implied) + # If there is no explicit layout specified: + # If a layout is found in the view paths with the controller's + # name, return that string. Otherwise, use the superclass' + # layout (which might also be implied) def _write_layout_method case @_layout when String self.class_eval %{def _layout(details) #{@_layout.inspect} end} when Symbol - self.class_eval %{def _layout(details) #{@_layout} end} + self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 + def _layout(details) + #{@_layout}.tap do |layout| + unless layout.is_a?(String) || !layout + raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \ + "should have returned a String, false, or nil" + end + end + end + ruby_eval when false self.class_eval %{def _layout(details) end} - else - self.class_eval %{ + when true + raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil" + when nil + self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 def _layout(details) if view_paths.find_by_parts?("#{_implied_layout_name}", details, "layouts") "#{_implied_layout_name}" @@ -53,33 +76,54 @@ module AbstractController super end end - } + ruby_eval end end end private - # This will be overwritten - def _layout(details) - end + # This will be overwritten by _write_layout_method + def _layout(details) end - # :api: plugin - # ==== - # Override this to mutate the inbound layout name + # Determine the layout for a given name and details. + # + # ==== Parameters + # name:: The name of the template + # details Object}>:: A list of details to restrict + # the lookup to. By default, layout lookup is limited to the + # formats specified for the current request. def _layout_for_name(name, details = {:formats => formats}) - unless [String, FalseClass, NilClass].include?(name.class) - raise ArgumentError, "String, false, or nil expected; you passed #{name.inspect}" - end - - name && view_paths.find_by_parts(name, details, _layout_prefix(name)) + name && _find_by_parts(name, details) end - # TODO: Decide if this is the best hook point for the feature - def _layout_prefix(name) - "layouts" + # Take in the name and details and find a Template. + # + # ==== Parameters + # name:: The name of the template to retrieve + # details:: A list of details to restrict the search by. This + # might include details like the format or locale of the template. + # + # ==== Returns + # Template:: A template object matching the name and details + def _find_by_parts(name, details) + # TODO: Make prefix actually part of details in ViewPath#find_by_parts + prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts" + view_paths.find_by_parts(name, details, prefix) end - def _default_layout(require_layout = false, details = {:formats => formats}) + # Returns the default layout for this controller and a given set of details. + # Optionally raises an exception if the layout could not be found. + # + # ==== Parameters + # details:: A list of details to restrict the search by. This + # might include details like the format or locale of the template. + # require_layout:: If this is true, raise an ArgumentError + # with details about the fact that the exception could not be + # found (defaults to false) + # + # ==== Returns + # Template:: The template object for the default layout (or nil) + def _default_layout(details, require_layout = false) if require_layout && _action_has_layout? && !_layout(details) raise ArgumentError, "There was no default layout for #{self.class} in #{view_paths.inspect}" @@ -93,6 +137,12 @@ module AbstractController end end + # Determines whether the current action has a layout by checking the + # action name against the :only and :except conditions set on the + # layout. + # + # ==== Returns + # Boolean:: True if the action has a layout, false otherwise. def _action_has_layout? conditions = _layout_conditions if only = conditions[:only] diff --git a/actionpack/lib/action_controller/new_base/compatibility.rb b/actionpack/lib/action_controller/new_base/compatibility.rb index f278c2da14..29ba43a879 100644 --- a/actionpack/lib/action_controller/new_base/compatibility.rb +++ b/actionpack/lib/action_controller/new_base/compatibility.rb @@ -114,8 +114,9 @@ module ActionController super || (respond_to?(:method_missing) && "_handle_method_missing") end - def _layout_prefix(name) - super unless name =~ /\blayouts/ + def _find_by_parts(name, details) + details[:prefix] = nil if name =~ /\blayouts/ + super end def performed? diff --git a/actionpack/lib/action_controller/new_base/layouts.rb b/actionpack/lib/action_controller/new_base/layouts.rb index 0ff71587d6..ace4b148c9 100644 --- a/actionpack/lib/action_controller/new_base/layouts.rb +++ b/actionpack/lib/action_controller/new_base/layouts.rb @@ -1,4 +1,163 @@ module ActionController + # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in + # repeated setups. The inclusion pattern has pages that look like this: + # + # <%= render "shared/header" %> + # Hello World + # <%= render "shared/footer" %> + # + # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose + # and if you ever want to change the structure of these two includes, you'll have to change all the templates. + # + # With layouts, you can flip it around and have the common structure know where to insert changing content. This means + # that the header and footer are only mentioned in one place, like this: + # + # // The header part of this layout + # <%= yield %> + # // The footer part of this layout + # + # And then you have content pages that look like this: + # + # hello world + # + # At rendering time, the content page is computed and then inserted in the layout, like this: + # + # // The header part of this layout + # hello world + # // The footer part of this layout + # + # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance + # variable. The preferred notation now is to use yield, as documented above. + # + # == Accessing shared variables + # + # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with + # references that won't materialize before rendering time: + # + #

<%= @page_title %>

+ # <%= yield %> + # + # ...and content pages that fulfill these references _at_ rendering time: + # + # <% @page_title = "Welcome" %> + # Off-world colonies offers you a chance to start a new life + # + # The result after rendering is: + # + #

Welcome

+ # Off-world colonies offers you a chance to start a new life + # + # == Layout assignment + # + # You can either specify a layout declaratively (using the #layout class method) or give + # it the same name as your controller, and place it in app/views/layouts. + # If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance. + # + # For instance, if you have PostsController and a template named app/views/layouts/posts.html.erb, + # that template will be used for all actions in PostsController and controllers inheriting + # from PostsController. + # + # If you use a module, for instance Weblog::PostsController, you will need a template named + # app/views/layouts/weblog/posts.html.erb. + # + # Since all your controllers inherit from ApplicationController, they will use + # app/views/layouts/application.html.erb if no other layout is specified + # or provided. + # + # == Inheritance Examples + # + # class BankController < ActionController::Base + # layout "bank_standard" + # + # class InformationController < BankController + # + # class TellerController < BankController + # # teller.html.erb exists + # + # class TillController < TellerController + # + # class VaultController < BankController + # layout :access_level_layout + # + # class EmployeeController < BankController + # layout nil + # + # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites + # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. + # + # The TellerController uses +teller.html.erb+, and TillController inherits that layout and + # uses it as well. + # + # == Types of layouts + # + # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes + # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can + # be done either by specifying a method reference as a symbol or using an inline method (as a proc). + # + # The method reference is the preferred approach to variable layouts and is used like this: + # + # class WeblogController < ActionController::Base + # layout :writers_and_readers + # + # def index + # # fetching posts + # end + # + # private + # def writers_and_readers + # logged_in? ? "writer_layout" : "reader_layout" + # end + # + # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing + # is logged in or not. + # + # If you want to use an inline method, such as a proc, do something like this: + # + # class WeblogController < ActionController::Base + # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" } + # + # Of course, the most common way of specifying a layout is still just as a plain template name: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # If no directory is specified for the template name, the template will by default be looked for in app/views/layouts/. + # Otherwise, it will be looked up relative to the template root. + # + # == Conditional layouts + # + # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering + # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The + # :only and :except options can be passed to the layout call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard", :except => :rss + # + # # ... + # + # end + # + # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout + # around the rendered view. + # + # Both the :only and :except condition can accept an arbitrary number of method references, so + # #:except => [ :rss, :text_only ] is valid, as is :except => :rss. + # + # == Using a different layout in the action render call + # + # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. + # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller. + # You can do this by passing a :layout option to the render call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # def help + # render :action => "help", :layout => "help" + # end + # end + # + # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout. module Layouts extend ActiveSupport::Concern @@ -6,6 +165,7 @@ module ActionController include AbstractController::Layouts module ClassMethods + # If no layout is provided, look for a layout with this name. def _implied_layout_name controller_path end @@ -14,16 +174,17 @@ module ActionController private def _determine_template(options) super - if (!options.key?(:text) && !options.key?(:inline) && !options.key?(:partial)) || options.key?(:layout) - options[:_layout] = _layout_for_option(options.key?(:layout) ? options[:layout] : :none, options[:_template].details) - end + + return if (options.key?(:text) || options.key?(:inline) || options.key?(:partial)) && !options.key?(:layout) + layout = options.key?(:layout) ? options[:layout] : :none + options[:_layout] = _layout_for_option(layout, options[:_template].details) end def _layout_for_option(name, details) case name when String then _layout_for_name(name, details) - when true then _default_layout(true, details) - when :none then _default_layout(false, details) + when true then _default_layout(details, true) + when :none then _default_layout(details, false) when false, nil then nil else raise ArgumentError, diff --git a/actionpack/test/abstract_controller/abstract_controller_test.rb b/actionpack/test/abstract_controller/abstract_controller_test.rb index c7eaaeb4ba..05b55216c8 100644 --- a/actionpack/test/abstract_controller/abstract_controller_test.rb +++ b/actionpack/test/abstract_controller/abstract_controller_test.rb @@ -154,7 +154,7 @@ module AbstractController end def render_to_body(options = {}) - options[:_layout] = options[:layout] || _default_layout + options[:_layout] = options[:layout] || _default_layout({}) super end end diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb index 4b66f063f3..64c435abb7 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract_controller/layouts_test.rb @@ -25,7 +25,7 @@ module AbstractControllerTests def controller_path() self.class.controller_path end def render_to_body(options) - options[:_layout] = _default_layout + options[:_layout] = _default_layout({}) super end end @@ -221,11 +221,11 @@ module AbstractControllerTests test "raises an exception when specifying layout true" do assert_raises ArgumentError do - Object.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + Object.class_eval do class ::BadOmgFailLolLayout < AbstractControllerTests::Layouts::Base layout true end - RUBY_EVAL + end end end end -- cgit v1.2.3 From b6fde6b4801fae26cdd0e790f6bfd06e7afe9941 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 9 Jun 2009 17:03:02 -0700 Subject: Clean up AbstractController::Logger and write documentation --- actionpack/lib/action_controller/abstract/logger.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/abstract/logger.rb b/actionpack/lib/action_controller/abstract/logger.rb index d6fa843485..b960e152e3 100644 --- a/actionpack/lib/action_controller/abstract/logger.rb +++ b/actionpack/lib/action_controller/abstract/logger.rb @@ -5,6 +5,13 @@ module AbstractController module Logger extend ActiveSupport::Concern + # A class that allows you to defer expensive processing + # until the logger actually tries to log. Otherwise, you are + # forced to do the processing in advance, and send the + # entire processed String to the logger, which might + # just discard the String if the log level is too low. + # + # TODO: Require that Rails loggers accept a block. class DelayedLog def initialize(&blk) @blk = blk @@ -20,8 +27,10 @@ module AbstractController cattr_accessor :logger end - def process(action) - ret = super + # Override process_action in the AbstractController::Base + # to log details about the method. + def process_action(action) + super if logger log = DelayedLog.new do @@ -32,10 +41,9 @@ module AbstractController logger.info(log) end - - ret end + private def request_origin # this *needs* to be cached! # otherwise you'd get different results if calling it more than once -- cgit v1.2.3 From c014c3e5c14beb71fa7c67f15448386d0ffaba28 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 10 Jun 2009 12:10:13 +1200 Subject: Whitelist the methods which are called by multiparameter attribute assignment. This prevents users from causing NoMethodErrors and the like by editing the parameter names, and closes a potential exploit of CVE-2009-1904. --- activerecord/lib/active_record/base.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index ec49d40a12..98898e9c18 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -3043,11 +3043,11 @@ module ActiveRecord #:nodoc: def execute_callstack_for_multiparameter_attributes(callstack) errors = [] callstack.each do |name, values| - klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass - if values.empty? - send(name + "=", nil) - else - begin + begin + klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass + if values.empty? + send(name + "=", nil) + else value = if Time == klass instantiate_time_object(name, values) elsif Date == klass @@ -3061,9 +3061,9 @@ module ActiveRecord #:nodoc: end send(name + "=", value) - rescue => ex - errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name) end + rescue => ex + errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name) end end unless errors.empty? @@ -3089,7 +3089,7 @@ module ActiveRecord #:nodoc: end def type_cast_attribute_value(multiparameter_name, value) - multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value + multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value end def find_parameter_position(multiparameter_name) -- cgit v1.2.3 From 4fc0778123fc775d106f6bc8a4a2a362bf0047ed Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 12:12:15 -0700 Subject: Simplify helper use of ActiveSupport::Dependencies, and use super better for in #helpers --- actionmailer/lib/action_mailer/helpers.rb | 17 ++--- .../lib/action_controller/new_base/helpers.rb | 83 +++++++++------------- activesupport/lib/active_support/dependencies.rb | 15 ++-- 3 files changed, 53 insertions(+), 62 deletions(-) diff --git a/actionmailer/lib/action_mailer/helpers.rb b/actionmailer/lib/action_mailer/helpers.rb index 31f7de8d60..1bb8682315 100644 --- a/actionmailer/lib/action_mailer/helpers.rb +++ b/actionmailer/lib/action_mailer/helpers.rb @@ -48,13 +48,14 @@ module ActionMailer file_name = arg.to_s.underscore + '_helper' class_name = file_name.camelize - begin - require_dependency(file_name) - rescue LoadError => load_error - requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] - msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}" - raise LoadError.new(msg).copy_blame!(load_error) - end + require_dependency(file_name, "Missing helper file helpers/%s.rb") + # begin + # require_dependency(file_name) + # rescue LoadError => load_error + # requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] + # msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}" + # raise LoadError.new(msg).copy_blame!(load_error) + # end add_template_helper(class_name.constantize) else @@ -97,7 +98,7 @@ module ActionMailer child.master_helper_module.__send__(:include, master_helper_module) child.helper child.name.to_s.underscore rescue MissingSourceFile => e - raise unless e.is_missing?("helpers/#{child.name.to_s.underscore}_helper") + raise unless e.is_missing?("#{child.name.to_s.underscore}_helper") end end end diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb index 8925dd3969..60d1a6f775 100644 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -56,34 +56,7 @@ module ActionController # helper(:three, BlindHelper) { def mice() 'mice' end } # def helper(*args, &block) - args.flatten.each do |arg| - case arg - when :all - helper all_application_helpers - when String, Symbol - file_name = arg.to_s.underscore + '_helper' - class_name = file_name.camelize - - begin - require_dependency(file_name) - rescue LoadError => load_error - requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] - if requiree == file_name - msg = "Missing helper file helpers/#{file_name}.rb" - raise LoadError.new(msg).copy_blame!(load_error) - else - raise - end - end - - super class_name.constantize - else - super args - end - end - - # Evaluate block in template class if given. - _helpers.module_eval(&block) if block_given? + super(*_modules_for_helpers(args), &block) end # Declares helper accessors for controller attributes. For example, the @@ -97,33 +70,45 @@ module ActionController # Provides a proxy to access helpers methods from outside the view. def helpers - unless @helper_proxy - @helper_proxy = ActionView::Base.new - @helper_proxy.extend _helpers - else - @helper_proxy - end + @helper_proxy ||= ActionView::Base.new.extend(_helpers) end - private - def default_helper_module! - unless name.blank? - module_name = name.sub(/Controller$|$/, 'Helper') - module_path = module_name.split('::').map { |m| m.underscore }.join('/') - require_dependency module_path - helper module_name.constantize + private + def _modules_for_helpers(args) + args.flatten.map! do |arg| + case arg + when :all + _modules_for_helpers all_application_helpers + when String, Symbol + file_name = "#{arg.to_s.underscore}_helper" + require_dependency(file_name, "Missing helper file helpers/%s.rb") + file_name.camelize.constantize + when Module + arg + else + raise ArgumentError, "helper must be a String, Symbol, or Module" end - rescue MissingSourceFile => e - raise unless e.is_missing? module_path - rescue NameError => e - raise unless e.missing_name? module_name end + end - # Extract helper names from files in app/helpers/**/*.rb - def all_application_helpers - extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ - Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } + def default_helper_module! + unless name.blank? + module_name = name.sub(/Controller$|$/, 'Helper') + module_path = module_name.split('::').map { |m| m.underscore }.join('/') + require_dependency module_path + helper module_name.constantize end + rescue MissingSourceFile => e + raise e unless e.is_missing? module_path + rescue NameError => e + raise e unless e.missing_name? module_name + end + + # Extract helper names from files in app/helpers/**/*.rb + def all_application_helpers + extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ + Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } + end end end end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 855b720ef1..7f6f012721 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -143,8 +143,8 @@ module ActiveSupport #:nodoc: Dependencies.require_or_load(file_name) end - def require_dependency(file_name) - Dependencies.depend_on(file_name) + def require_dependency(file_name, message = "No such file to load -- %s") + Dependencies.depend_on(file_name, false, message) end def require_association(file_name) @@ -230,11 +230,16 @@ module ActiveSupport #:nodoc: mechanism == :load end - def depend_on(file_name, swallow_load_errors = false) + def depend_on(file_name, swallow_load_errors = false, message = "No such file to load -- %s.rb") path = search_for_file(file_name) require_or_load(path || file_name) - rescue LoadError - raise unless swallow_load_errors + rescue LoadError => load_error + unless swallow_load_errors + if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1] + raise MissingSourceFile.new(message % file_name, load_error.path).copy_blame!(load_error) + end + raise + end end def associate_with(file_name) -- cgit v1.2.3 From 37be453a8709eb30c97f26369e352e5935436d97 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 12:26:03 -0700 Subject: Adding "test" to the lib directory when running action pack tests on new base --- actionpack/Rakefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 074bc90ca9..53387d305c 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -24,7 +24,9 @@ task :default => [ :test ] desc "Run all unit tests" task :test => [:test_action_pack, :test_active_record_integration, :test_new_base, :test_new_base_on_old_tests] -test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"] +test_lib_dirs = ENV["NEW"] ? ["test/new_base"] : [] +test_lib_dirs.push "test", "test/lib" +# test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"] Rake::TestTask.new(:test_action_pack) do |t| t.libs.concat test_lib_dirs -- cgit v1.2.3 From 316fab7de66041daf616a7126d1a9f293a928144 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 14:15:21 -0700 Subject: Cleaned up the #default_helper_module method to make better use of #helper instead of duplicating code. --- actionpack/lib/action_controller/new_base/helpers.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb index 60d1a6f775..8ddfc70607 100644 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -16,7 +16,7 @@ module ActionController module ClassMethods def inherited(klass) - klass.__send__ :default_helper_module! + klass.class_eval { default_helper_module! unless name.blank? } super end @@ -92,16 +92,13 @@ module ActionController end def default_helper_module! - unless name.blank? - module_name = name.sub(/Controller$|$/, 'Helper') - module_path = module_name.split('::').map { |m| m.underscore }.join('/') - require_dependency module_path - helper module_name.constantize - end + module_name = name.sub(/Controller$/, '') + module_path = module_name.underscore + helper module_path rescue MissingSourceFile => e - raise e unless e.is_missing? module_path + raise e unless e.is_missing? "#{module_path}_helper" rescue NameError => e - raise e unless e.missing_name? module_name + raise e unless e.missing_name? "#{module_name}Helper" end # Extract helper names from files in app/helpers/**/*.rb -- cgit v1.2.3 From fbb6b936e9b896e6b8cbd6d05148a77fcf14b40b Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 14:28:58 -0700 Subject: Document ActionController::Helpers --- .../lib/action_controller/new_base/helpers.rb | 75 ++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb index 8ddfc70607..2fa5ea6519 100644 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -3,6 +3,51 @@ require 'active_support/core_ext/name_error' require 'active_support/dependencies' module ActionController + # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, + # +numbers+ and model objects, to name a few. These helpers are available to all templates + # by default. + # + # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to + # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will + # include a helper whose name matches that of the controller, e.g., MyController will automatically + # include MyHelper. + # + # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any + # controller which inherits from it. + # + # ==== Examples + # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if + # the Time object is blank: + # + # module FormattedTimeHelper + # def format_time(time, format=:long, blank_message=" ") + # time.blank? ? blank_message : time.to_s(format) + # end + # end + # + # FormattedTimeHelper can now be included in a controller, using the +helper+ class method: + # + # class EventsController < ActionController::Base + # helper FormattedTimeHelper + # def index + # @events = Event.find(:all) + # end + # end + # + # Then, in any view rendered by EventController, the format_time method can be called: + # + # <% @events.each do |event| -%> + #

+ # <% format_time(event.time, :short, "N/A") %> | <%= event.name %> + #

+ # <% end -%> + # + # Finally, assuming we have two event instances, one which has a time and one which does not, + # the output might look like this: + # + # 23 Aug 11:30 | Carolina Railhawks Soccer Match + # N/A | Carolina Railhaws Training Workshop + # module Helpers extend ActiveSupport::Concern @@ -10,8 +55,9 @@ module ActionController included do # Set the default directory for helpers - class_inheritable_accessor :helpers_dir - self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") + extlib_inheritable_accessor(:helpers_dir) do + defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers" + end end module ClassMethods @@ -22,8 +68,9 @@ module ActionController # The +helper+ class method can take a series of helper module names, a block, or both. # - # * *args: One or more modules, strings or symbols, or the special symbol :all. - # * &block: A block defining helper methods. + # ==== Parameters + # *args + # block:: A block defining helper methods # # ==== Examples # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file @@ -64,6 +111,10 @@ module ActionController # controller and makes them available to the view: # helper_attr :name # attr_accessor :name + # + # ==== Parameters + # *attrs:: Names of attributes to be converted + # into helpers. def helper_attr(*attrs) attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } end @@ -74,6 +125,22 @@ module ActionController end private + # Returns a list of modules, normalized from the acceptable kinds of + # helpers with the following behavior: + # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper", + # and "foo_bar_helper.rb" is loaded using require_dependency. + # :all:: Loads all modules in the #helpers_dir + # Module:: No further processing + # + # After loading the appropriate files, the corresponding modules + # are returned. + # + # ==== Parameters + # args:: A list of helpers + # + # ==== Returns + # Array[Module]:: A normalized list of modules for the list of + # helpers provided. def _modules_for_helpers(args) args.flatten.map! do |arg| case arg -- cgit v1.2.3 From 82a10ce9f61a02a97d85915bb5fca53730ae28c8 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 14:43:43 -0700 Subject: Removed unnecessary code --- .../lib/action_controller/new_base/hide_actions.rb | 39 ++++++++++------------ actionpack/lib/action_controller/new_base/http.rb | 10 ------ 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/actionpack/lib/action_controller/new_base/hide_actions.rb b/actionpack/lib/action_controller/new_base/hide_actions.rb index b45e520bee..86852a26cd 100644 --- a/actionpack/lib/action_controller/new_base/hide_actions.rb +++ b/actionpack/lib/action_controller/new_base/hide_actions.rb @@ -1,39 +1,34 @@ module ActionController + # ActionController::HideActions adds the ability to prevent public methods on a controller + # to be called as actions. module HideActions extend ActiveSupport::Concern included do - extlib_inheritable_accessor :hidden_actions - self.hidden_actions ||= Set.new + extlib_inheritable_accessor(:hidden_actions) { Set.new } end def action_methods - self.class.action_names + self.class.action_methods end - def action_names - action_methods - end - - private - def action_method?(action_name) - !hidden_actions.include?(action_name) && super - end + private - module ClassMethods - def hide_action(*args) - args.each do |arg| - self.hidden_actions << arg.to_s - end - end + def action_method?(action_name) + !hidden_actions.include?(action_name) && super + end - def action_methods - @action_names ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)}) + module ClassMethods + # Sets + def hide_action(*args) + args.each do |arg| + self.hidden_actions << arg.to_s end + end - def self.action_names - action_methods - end + def action_methods + @action_methods ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)}) end + end end end diff --git a/actionpack/lib/action_controller/new_base/http.rb b/actionpack/lib/action_controller/new_base/http.rb index c96aaaa865..b3a80094dd 100644 --- a/actionpack/lib/action_controller/new_base/http.rb +++ b/actionpack/lib/action_controller/new_base/http.rb @@ -28,16 +28,6 @@ module ActionController self.class.controller_path end - # :api: private - def self.action_names - action_methods - end - - # :api: private - def action_names - action_methods - end - # :api: plugin def self.call(env) controller = new -- cgit v1.2.3 From 47ff57f6d14fe161900bf85e2d2cf6d7e21a1eb8 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 15:27:53 -0700 Subject: Document and clean up HideActions and Http --- actionpack/lib/action_controller/abstract/base.rb | 7 +--- .../lib/action_controller/new_base/hide_actions.rb | 19 +++++----- actionpack/lib/action_controller/new_base/http.rb | 41 ++++++++++++++++------ actionpack/test/controller/base_test.rb | 11 ++++-- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb index c3daef8759..a19a236ef7 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/action_controller/abstract/base.rb @@ -95,11 +95,6 @@ module AbstractController end private - # See AbstractController::Base.action_methods - def action_methods - self.class.action_methods - end - # Returns true if the name can be considered an action. This can # be overridden in subclasses to modify the semantics of what # can be considered an action. @@ -110,7 +105,7 @@ module AbstractController # ==== Returns # TrueClass, FalseClass def action_method?(name) - action_methods.include?(name) + self.class.action_methods.include?(name) end # Call the action. Override this in a subclass to modify the diff --git a/actionpack/lib/action_controller/new_base/hide_actions.rb b/actionpack/lib/action_controller/new_base/hide_actions.rb index 86852a26cd..af68c772b1 100644 --- a/actionpack/lib/action_controller/new_base/hide_actions.rb +++ b/actionpack/lib/action_controller/new_base/hide_actions.rb @@ -8,26 +8,27 @@ module ActionController extlib_inheritable_accessor(:hidden_actions) { Set.new } end - def action_methods - self.class.action_methods - end - private + # Overrides AbstractController::Base#action_method? to return false if the + # action name is in the list of hidden actions. def action_method?(action_name) !hidden_actions.include?(action_name) && super end module ClassMethods - # Sets + # Sets all of the actions passed in as hidden actions. + # + # ==== Parameters + # *args<#to_s>:: A list of actions def hide_action(*args) - args.each do |arg| - self.hidden_actions << arg.to_s - end + hidden_actions.merge(args.map! {|a| a.to_s }) end + # Overrides AbstractController::Base#action_methods to remove any methods + # that are listed as hidden methods. def action_methods - @action_methods ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)}) + @action_methods ||= Set.new(super.reject {|name| hidden_actions.include?(name)}) end end end diff --git a/actionpack/lib/action_controller/new_base/http.rb b/actionpack/lib/action_controller/new_base/http.rb index b3a80094dd..2e73561f93 100644 --- a/actionpack/lib/action_controller/new_base/http.rb +++ b/actionpack/lib/action_controller/new_base/http.rb @@ -2,38 +2,48 @@ require 'action_controller/abstract' require 'active_support/core_ext/module/delegation' module ActionController + # ActionController::Http provides a way to get a valid Rack application from a controller. + # + # In AbstractController, dispatching is triggered directly by calling #process on a new controller. + # ActionController::Http provides an #action method that returns a valid Rack application for a + # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router, + # can dispatch directly to the action returned by FooController.action(:index). class Http < AbstractController::Base abstract! # :api: public attr_internal :params, :env - # :api: public + # Returns the last part of the controller's name, underscored, without the ending + # "Controller". For instance, MyApp::MyPostsController would return "my_posts" for + # controller_name + # + # ==== Returns + # String def self.controller_name @controller_name ||= controller_path.split("/").last end - # :api: public + # Delegates to the class' #controller_name def controller_name self.class.controller_name end - # :api: public + # Returns the full controller name, underscored, without the ending Controller. + # For instance, MyApp::MyPostsController would return "my_app/my_posts" for + # controller_name. + # + # ==== Returns + # String def self.controller_path @controller_path ||= self.name.sub(/Controller$/, '').underscore end - # :api: public + # Delegates to the class' #controller_path def controller_path self.class.controller_path end - # :api: plugin - def self.call(env) - controller = new - controller.call(env).to_rack - end - # The details below can be overridden to support a specific # Request and Response object. The default ActionController::Base # implementation includes RackConvenience, which makes a request @@ -47,7 +57,7 @@ module ActionController super end - # Basic implements for content_type=, location=, and headers are + # Basic implementations for content_type=, location=, and headers are # provided to reduce the dependency on the RackConvenience module # in Renderer and Redirector. @@ -71,6 +81,15 @@ module ActionController [status, headers, response_body] end + # Return a rack endpoint for the given action. Memoize the endpoint, so + # multiple calls into MyController.action will return the same object + # for the same action. + # + # ==== Parameters + # action<#to_s>:: An action name + # + # ==== Returns + # Proc:: A rack application def self.action(name) @actions ||= {} @actions[name.to_s] ||= proc do |env| diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index 3a4cdb81d9..03fd98a85c 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -87,11 +87,11 @@ class ControllerInstanceTests < Test::Unit::TestCase def test_action_methods @empty_controllers.each do |c| hide_mocha_methods_from_controller(c) - assert_equal Set.new, c.__send__(:action_methods), "#{c.controller_path} should be empty!" + assert_equal Set.new, c.class.__send__(:action_methods), "#{c.controller_path} should be empty!" end @non_empty_controllers.each do |c| hide_mocha_methods_from_controller(c) - assert_equal Set.new(%w(public_action)), c.__send__(:action_methods), "#{c.controller_path} should not be empty!" + assert_equal Set.new(%w(public_action)), c.class.__send__(:action_methods), "#{c.controller_path} should not be empty!" end end @@ -145,7 +145,12 @@ class PerformActionTest < ActionController::TestCase def test_method_missing_is_not_an_action_name use_controller MethodMissingController - assert ! @controller.__send__(:action_methods).include?('method_missing') + + if defined?(ActionController::Http) + assert ! @controller.__send__(:action_method?, 'method_missing') + else + assert ! @controller.__send__(:action_methods).include?('method_missing') + end get :method_missing assert_response :success -- cgit v1.2.3