diff options
Diffstat (limited to 'actionpack/lib')
30 files changed, 331 insertions, 241 deletions
diff --git a/actionpack/lib/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb index e5d5bef6b4..e4261068d8 100644 --- a/actionpack/lib/abstract_controller/url_for.rb +++ b/actionpack/lib/abstract_controller/url_for.rb @@ -1,3 +1,9 @@ +# Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class +# has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an +# exception will be raised. +# +# Note that this module is completely decoupled from HTTP - the only requirement is a valid +# <tt>_routes</tt> implementation. module AbstractController module UrlFor extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 881af74147..93241fc056 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/module/anonymous' require 'action_dispatch/http/mime_types' module ActionController @@ -136,15 +137,25 @@ module ActionController # this could be done by trying to find the defined model that has the # same singularize name as the controller. For example, +UsersController+ # will try to find if the +User+ model exists. - def _default_wrap_model + # + # This method also does namespace lookup. Foo::Bar::UsersController will + # try to find Foo::Bar::User, Foo::User and finally User. + def _default_wrap_model #:nodoc: + return nil if self.anonymous? + model_name = self.name.sub(/Controller$/, '').singularize begin model_klass = model_name.constantize - rescue NameError => e - unscoped_model_name = model_name.split("::", 2).last - break if unscoped_model_name == model_name - model_name = unscoped_model_name + rescue NameError, ArgumentError => e + if e.message =~ /is not missing constant|uninitialized constant #{model_name}/ + namespaces = model_name.split("::") + namespaces.delete_at(-2) + break if namespaces.last == model_name + model_name = namespaces.join("::") + else + raise + end end until model_klass model_klass @@ -155,12 +166,12 @@ module ActionController unless options[:only] || options[:except] model ||= _default_wrap_model - if model.respond_to?(:column_names) - options[:only] = model.column_names + if model.respond_to?(:attribute_names) && model.attribute_names.present? + options[:only] = model.attribute_names end end - unless options[:name] + unless options[:name] || self.anonymous? model ||= _default_wrap_model options[:name] = model ? model.to_s.demodulize.underscore : controller_name.singularize @@ -218,7 +229,7 @@ module ActionController # Checks if we should perform parameters wrapping. def _wrapper_enabled? ref = request.content_mime_type.try(:ref) - _wrapper_formats.include?(ref) && !request.request_parameters[_wrapper_key] + _wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key] end end end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 1cd93a188c..13044a7450 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -73,7 +73,10 @@ module ActionController #:nodoc: protected # The actual before_filter that is used. Modify this to change how you handle unverified requests. def verify_authenticity_token - verified_request? || handle_unverified_request + unless verified_request? + logger.debug "WARNING: Can't verify CSRF token authenticity" if logger + handle_unverified_request + end end def handle_unverified_request diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 6fc0cf1fb8..08132b1900 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -1,3 +1,24 @@ +# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing +# the <tt>_routes</tt> method. Otherwise, an exception will be raised. +# +# In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define +# url options like the +host+. In order to do so, this module requires the host class +# to implement +env+ and +request+, which need to be a Rack-compatible. +# +# Example: +# +# class RootUrl +# include ActionController::UrlFor +# include Rails.application.routes.url_helpers +# +# delegate :env, :request, :to => :controller +# +# def initialize(controller) +# @controller = controller +# @url = root_path # named route from the application. +# end +# end +# => module ActionController module UrlFor extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 0085f542aa..89ff5ba174 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -2,6 +2,7 @@ require 'rack/session/abstract/id' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/class/attribute' +require 'active_support/core_ext/module/anonymous' module ActionController module TemplateAssertions @@ -413,7 +414,11 @@ module ActionController @request.env['REQUEST_METHOD'] = http_method parameters ||= {} - @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters) + controller_class_name = @controller.class.anonymous? ? + "anonymous_controller" : + @controller.class.name.underscore.sub(/_controller$/, '') + + @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters) @request.session = ActionController::TestSession.new(session) if session @request.session["flash"] = @request.flash.update(flash || {}) diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb index 4f4cb96a74..aaed0d750f 100644 --- a/actionpack/lib/action_dispatch/http/cache.rb +++ b/actionpack/lib/action_dispatch/http/cache.rb @@ -42,20 +42,6 @@ module ActionDispatch attr_reader :cache_control, :etag alias :etag? :etag - def initialize(*) - super - - @cache_control = {} - @etag = self["ETag"] - - if cache_control = self["Cache-Control"] - cache_control.split(/,\s*/).each do |segment| - first, last = segment.split("=") - @cache_control[first.to_sym] = last || true - end - end - end - def last_modified if last = headers['Last-Modified'] Time.httpdate(last) @@ -77,6 +63,18 @@ module ActionDispatch private + def prepare_cache_control! + @cache_control = {} + @etag = self["ETag"] + + if cache_control = self["Cache-Control"] + cache_control.split(/,\s*/).each do |segment| + first, last = segment.split("=") + @cache_control[first.to_sym] = last || true + end + end + end + def handle_conditional_get! if etag? || last_modified? || !@cache_control.empty? set_conditional_cache_control! diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 1f4f3ac0da..3a6b1da4fd 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -56,26 +56,25 @@ module ActionDispatch # :nodoc: cattr_accessor(:default_charset) { "utf-8" } - module Setup - def initialize(status = 200, header = {}, body = []) - self.body, self.header, self.status = body, header, status + include Rack::Response::Helpers + include ActionDispatch::Http::Cache::Response - @sending_file = false - @blank = false + def initialize(status = 200, header = {}, body = []) + self.body, self.header, self.status = body, header, status - if content_type = self["Content-Type"] - type, charset = content_type.split(/;\s*charset=/) - @content_type = Mime::Type.lookup(type) - @charset = charset || "UTF-8" - end + @sending_file = false + @blank = false - yield self if block_given? + if content_type = self["Content-Type"] + type, charset = content_type.split(/;\s*charset=/) + @content_type = Mime::Type.lookup(type) + @charset = charset || "UTF-8" end - end - include Rack::Response::Helpers - include Setup - include ActionDispatch::Http::Cache::Response + prepare_cache_control! + + yield self if block_given? + end def status=(status) @status = Rack::Utils.status_code(status) @@ -116,9 +115,32 @@ module ActionDispatch # :nodoc: EMPTY = " " + class BodyBuster #:nodoc: + def initialize(response) + @response = response + @body = "" + end + + def bust(body) + body.call(@response, self) + body.close if body.respond_to?(:close) + @body + end + + def write(string) + @body << string.to_s + end + end + def body=(body) @blank = true if body == EMPTY + if body.respond_to?(:call) + ActiveSupport::Deprecation.warn "Setting a Proc or an object that responds to call " \ + "in response_body is no longer supported", caller + body = BodyBuster.new(self).bust(body) + end + # Explicitly check for strings. This is *wrong* theoretically # but if we don't check this, the performance on string bodies # is bad on Ruby 1.8 (because strings responds to each then). @@ -150,6 +172,10 @@ module ActionDispatch # :nodoc: headers['Location'] = url end + def close + @body.close if @body.respond_to?(:close) + end + def to_a assign_default_content_type_and_charset! handle_conditional_get! diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index d9c07d6ca3..8487b0fc8c 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -162,7 +162,7 @@ module ActionDispatch # Returns all the \subdomains as a string, 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> + # such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt> # in "www.rubyonrails.co.uk". def subdomain(tld_length = @@tld_length) subdomains(tld_length).join(".") diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 43fd93adf6..74c090f260 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -49,8 +49,8 @@ module ActionDispatch # You may wish to organize groups of controllers under a namespace. Most # commonly, you might group a number of administrative controllers under # an +admin+ namespace. You would place these controllers under the - # app/controllers/admin directory, and you can group them together in your - # router: + # <tt>app/controllers/admin</tt> directory, and you can group them together + # in your router: # # namespace "admin" do # resources :posts, :comments @@ -152,7 +152,7 @@ module ActionDispatch # } # end # - # Using the multiline match modifier will raise an ArgumentError. + # Using the multiline match modifier will raise an +ArgumentError+. # Encoding regular expression modifiers are silently ignored. The # match will always use the default encoding or ASCII. # diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index a65f6e1fce..3ba6094fbb 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -335,7 +335,7 @@ module ActionDispatch # # [:on] # Shorthand for wrapping routes in a specific RESTful context. Valid - # values are :member, :collection, and :new. Only use within + # values are +:member+, +:collection+, and +:new+. Only use within # <tt>resource(s)</tt> block. For example: # # resource :bar do @@ -352,7 +352,7 @@ module ActionDispatch # # [:constraints] # Constrains parameters with a hash of regular expressions or an - # object that responds to #matches? + # object that responds to <tt>matches?</tt> # # match 'path/:id', :constraints => { :id => /[A-Z]\d{5}/ } # @@ -373,7 +373,7 @@ module ActionDispatch # See <tt>Scoping#defaults</tt> for its scope equivalent. # # [:anchor] - # Boolean to anchor a #match pattern. Default is true. When set to + # Boolean to anchor a <tt>match</tt> pattern. Default is true. When set to # false, the pattern matches any request prefixed with the given path. # # # Matches any request starting with 'path' @@ -517,15 +517,15 @@ module ActionDispatch # You may wish to organize groups of controllers under a namespace. # Most commonly, you might group a number of administrative controllers # under an +admin+ namespace. You would place these controllers under - # the app/controllers/admin directory, and you can group them together - # in your router: + # the <tt>app/controllers/admin</tt> directory, and you can group them + # together in your router: # # namespace "admin" do # resources :posts, :comments # end # # This will create a number of routes for each of the posts and comments - # controller. For Admin::PostsController, Rails will create: + # controller. For <tt>Admin::PostsController</tt>, Rails will create: # # GET /admin/posts # GET /admin/posts/new @@ -536,7 +536,7 @@ module ActionDispatch # DELETE /admin/posts/1 # # If you want to route /posts (without the prefix /admin) to - # Admin::PostsController, you could use + # <tt>Admin::PostsController</tt>, you could use # # scope :module => "admin" do # resources :posts @@ -546,7 +546,7 @@ module ActionDispatch # # resources :posts, :module => "admin" # - # If you want to route /admin/posts to PostsController + # If you want to route /admin/posts to +PostsController+ # (without the Admin:: module prefix), you could use # # scope "/admin" do @@ -555,11 +555,11 @@ module ActionDispatch # # or, for a single case # - # resources :posts, :path => "/admin" + # resources :posts, :path => "/admin/posts" # # In each of these cases, the named routes remain the same as if you did # not use scope. In the last case, the following paths map to - # PostsController: + # +PostsController+: # # GET /admin/posts # GET /admin/posts/new @@ -587,7 +587,7 @@ module ActionDispatch # # === Examples # - # # route /posts (without the prefix /admin) to Admin::PostsController + # # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt> # scope :module => "admin" do # resources :posts # end @@ -597,7 +597,7 @@ module ActionDispatch # resources :posts # end # - # # prefix the routing helper name: sekret_posts_path instead of posts_path + # # prefix the routing helper name: +sekret_posts_path+ instead of +posts_path+ # scope :as => "sekret" do # resources :posts # end @@ -679,12 +679,12 @@ module ActionDispatch # resources :posts # end # - # # maps to Sekret::PostsController rather than Admin::PostsController + # # maps to <tt>Sekret::PostsController</tt> rather than <tt>Admin::PostsController</tt> # namespace :admin, :module => "sekret" do # resources :posts # end # - # # generates sekret_posts_path rather than admin_posts_path + # # generates +sekret_posts_path+ rather than +admin_posts_path+ # namespace :admin, :as => "sekret" do # resources :posts # end @@ -712,6 +712,7 @@ module ActionDispatch # constraints(:post_id => /\d+\.\d+) do # resources :comments # end + # end # # === Restricting based on IP # @@ -846,20 +847,20 @@ module ActionDispatch # You may wish to organize groups of controllers under a namespace. Most # commonly, you might group a number of administrative controllers under # an +admin+ namespace. You would place these controllers under the - # app/controllers/admin directory, and you can group them together in your - # router: + # <tt>app/controllers/admin</tt> directory, and you can group them together + # in your router: # # namespace "admin" do # resources :posts, :comments # end # - # By default the :id parameter doesn't accept dots. If you need to - # use dots as part of the :id parameter add a constraint which + # By default the +:id+ parameter doesn't accept dots. If you need to + # use dots as part of the +:id+ parameter add a constraint which # overrides this restriction, e.g: # # resources :articles, :id => /[^\/]+/ # - # This allows any character other than a slash as part of your :id. + # This allows any character other than a slash as part of your +:id+. # module Resources # CANONICAL_ACTIONS holds all actions that does not need a prefix or @@ -975,7 +976,7 @@ module ActionDispatch # resource :geocoder # # creates six different routes in your application, all mapping to - # the GeoCoders controller (note that the controller is named after + # the +GeoCoders+ controller (note that the controller is named after # the plural): # # GET /geocoder/new @@ -1024,7 +1025,7 @@ module ActionDispatch # resources :photos # # creates seven different routes in your application, all mapping to - # the Photos controller: + # the +Photos+ controller: # # GET /photos/new # POST /photos @@ -1107,11 +1108,11 @@ module ActionDispatch # # === Examples # - # # routes call Admin::PostsController + # # routes call <tt>Admin::PostsController</tt> # resources :posts, :module => "admin" # # # resource actions are at /admin/posts. - # resources :posts, :path => "admin" + # resources :posts, :path => "admin/posts" def resources(*resources, &block) options = resources.extract_options! @@ -1151,7 +1152,7 @@ module ActionDispatch # end # # This will enable Rails to recognize paths such as <tt>/photos/search</tt> - # with GET, and route to the search action of PhotosController. It will also + # with GET, and route to the search action of +PhotosController+. It will also # create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt> # route helpers. def collection @@ -1175,7 +1176,7 @@ module ActionDispatch # end # # This will recognize <tt>/photos/1/preview</tt> with GET, and route to the - # preview action of PhotosController. It will also create the + # preview action of +PhotosController+. It will also create the # <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers. def member unless resource_scope? diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 97e8ccc9a5..5097f6732d 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -224,6 +224,7 @@ module ActionDispatch self.valid_conditions.push(:controller, :action) @append = [] + @prepend = [] @disable_clear_and_finalize = false clear! end @@ -232,7 +233,6 @@ module ActionDispatch clear! unless @disable_clear_and_finalize eval_block(block) finalize! unless @disable_clear_and_finalize - nil end @@ -240,6 +240,10 @@ module ActionDispatch @append << block end + def prepend(&block) + @prepend << block + end + def eval_block(block) if block.arity == 1 raise "You are using the old router DSL which has been removed in Rails 3.1. " << @@ -262,8 +266,6 @@ module ActionDispatch end def clear! - # Clear the controller cache so we may discover new ones - @controller_constraints = nil @finalized = false routes.clear named_routes.clear @@ -271,6 +273,7 @@ module ActionDispatch :parameters_key => PARAMETERS_KEY, :request_class => request_class ) + @prepend.each { |blk| eval_block(blk) } end def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false) diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index d4db78a25a..5893f86798 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -131,10 +131,10 @@ module ActionDispatch # # Examples: # - # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing' - # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok' - # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/' - # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33' + # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :port => '8080' # => 'http://somehost.org:8080/tasks/testing' + # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok' + # url_for :controller => 'tasks', :action => 'testing', :trailing_slash => true # => 'http://somehost.org/tasks/testing/' + # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33' def url_for(options = nil) case options when String diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb index c67a0664dc..5fa91d1a76 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb @@ -169,7 +169,7 @@ module ActionDispatch # assert_select "title", "Welcome" # # # Page title is "Welcome" and there is only one title element - # assert_select "title", {:count=>1, :text=>"Welcome"}, + # assert_select "title", {:count => 1, :text => "Welcome"}, # "Wrong title or more than one title element" # # # Page contains no forms diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb index d430691429..397bda41d5 100644 --- a/actionpack/lib/action_dispatch/testing/test_process.rb +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -29,7 +29,7 @@ module ActionDispatch @response.redirect_url end - # Shortcut for <tt>ARack::Test::UploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>: + # Shortcut for <tt>Rack::Test::UploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>: # # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png') # diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 92b6f7c770..a67b61c1ef 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -50,6 +50,7 @@ module ActionView autoload :Resolver autoload :PathResolver autoload :FileSystemResolver + autoload :OptimizedFileSystemResolver autoload :FallbackFileSystemResolver end diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index c98110353f..fd2970b8e2 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -85,11 +85,11 @@ module ActionView #:nodoc: # # Here are some basic examples: # - # xml.em("emphasized") # => <em>emphasized</em> - # xml.em { xml.b("emph & bold") } # => <em><b>emph & bold</b></em> - # xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a> - # xml.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\> - # # NOTE: order of attributes is not specified. + # xml.em("emphasized") # => <em>emphasized</em> + # xml.em { xml.b("emph & bold") } # => <em><b>emph & bold</b></em> + # xml.a("A Link", "href" => "http://onestepback.org") # => <a href="http://onestepback.org">A Link</a> + # xml.target("name" => "compile", "option" => "fast") # => <target option="fast" name="compile"\> + # # NOTE: order of attributes is not specified. # # Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following: # diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index 96e5722252..889ea8a763 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -81,8 +81,8 @@ module ActionView # # The Atom spec defines five elements (content rights title subtitle # summary) which may directly contain xhtml content if :type => 'xhtml' - # is specified as an attribute. If so, this helper will take care of - # the enclosing div and xhtml namespace declaration. Example usage: + # is specified as an attribute. If so, this helper will take care of + # the enclosing div and xhtml namespace declaration. Example usage: # # entry.summary :type => 'xhtml' do |xhtml| # xhtml.p pluralize(order.line_items.count, "line item") @@ -91,8 +91,8 @@ module ActionView # end # # - # atom_feed yields an AtomFeedBuilder instance. Nested elements yield - # an AtomBuilder instance. + # <tt>atom_feed</tt> yields an +AtomFeedBuilder+ instance. Nested elements yield + # an +AtomBuilder+ instance. def atom_feed(options = {}, &block) if options[:schema_date] options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime) diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index 385378ea29..e81d03b537 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -3,7 +3,7 @@ module ActionView module Helpers module CacheHelper # This helper exposes a method for caching fragments of a view - # rather than an entire action or page. This technique is useful + # rather than an entire action or page. This technique is useful # caching pieces like menus, lists of newstopics, static HTML # fragments, and so on. This method takes a block that contains # the content you wish to cache. diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index ead7feb091..3b5f4e694f 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -135,8 +135,12 @@ module ActionView # for elements that will be fragment cached. def content_for(name, content = nil, &block) content = capture(&block) if block_given? - result = @view_flow.append(name, content) if content - result unless content + if content + @view_flow.append(name, content) + nil + else + @view_flow.get(name) + end end # The same as +content_for+ but when used with streaming flushes diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index d2ddaafcb3..c78c03a5eb 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -12,14 +12,14 @@ module ActionView # select-type methods share a number of common options that are as follows: # # * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday" - # would give birthday[month] instead of date[month] if passed to the select_month method. + # would give birthday[month] instead of date[month] if passed to the <tt>select_month</tt> method. # * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date. # * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true, - # the select_month method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead of - # "date[month]". + # the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead + # of "date[month]". module DateHelper # Reports the approximate distance in time between two Time or Date objects or integers as seconds. - # Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs + # Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs. # Distances are reported based on the following table: # # 0 <-> 29 secs # => less than a minute @@ -119,7 +119,7 @@ module ActionView end end - # Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>. + # Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>. # # ==== Examples # time_ago_in_words(3.minutes.from_now) # => 3 minutes @@ -176,7 +176,7 @@ module ActionView # NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed. # # ==== Examples - # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute + # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute. # date_select("post", "written_on") # # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute, @@ -197,7 +197,7 @@ module ActionView # # lacking a year field. # date_select("user", "birthday", :order => [:month, :day]) # - # # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute + # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute # # which is initially set to the date 3 days from the current date # date_select("post", "written_on", :default => 3.days.from_now) # @@ -205,7 +205,7 @@ module ActionView # # that will have a default day of 20. # date_select("credit_card", "bill_due", :default => { :day => 20 }) # - # # Generates a date select with custom prompts + # # Generates a date select with custom prompts. # date_select("post", "written_on", :prompt => { :day => 'Select day', :month => 'Select month', :year => 'Select year' }) # # The selects are prepared for multi-parameter assignment to an Active Record object. @@ -222,22 +222,23 @@ module ActionView # with <tt>:ampm</tt> option. # # This method will also generate 3 input hidden tags, for the actual year, month and day unless the option - # <tt>:ignore_date</tt> is set to +true+. + # <tt>:ignore_date</tt> is set to +true+. If you set the <tt>:ignore_date</tt> to +true+, you must have a + # +date_select+ on the same method within the form otherwise an exception will be raised. # # If anything is passed in the html_options hash it will be applied to every select tag in the set. # # ==== Examples - # # Creates a time select tag that, when POSTed, will be stored in the post variable in the sunrise attribute + # # Creates a time select tag that, when POSTed, will be stored in the post variable in the sunrise attribute. # time_select("post", "sunrise") # # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the post variables in # # the sunrise attribute. # time_select("post", "start_time", :include_seconds => true) # - # # You can set the :minute_step to 15 which will give you: 00, 15, 30 and 45. + # # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45. # time_select 'game', 'game_time', {:minute_step => 15} # - # # Creates a time select tag with a custom prompt. Use :prompt => true for generic prompts. + # # Creates a time select tag with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts. # time_select("post", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'}) # time_select("post", "written_on", :prompt => {:hour => true}) # generic prompt for hours # time_select("post", "written_on", :prompt => true) # generic prompts for all @@ -261,7 +262,7 @@ module ActionView # # ==== Examples # # Generates a datetime select that, when POSTed, will be stored in the post variable in the written_on - # # attribute + # # attribute. # datetime_select("post", "written_on") # # # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the @@ -279,7 +280,7 @@ module ActionView # # as the written_on attribute. # datetime_select("post", "written_on", :discard_type => true) # - # # Generates a datetime select with a custom prompt. Use :prompt=>true for generic prompts. + # # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts. # datetime_select("post", "written_on", :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'}) # datetime_select("post", "written_on", :prompt => {:hour => true}) # generic prompt for hours # datetime_select("post", "written_on", :prompt => true) # generic prompts for all @@ -301,7 +302,7 @@ module ActionView # ==== Examples # my_date_time = Time.now + 4.days # - # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today) + # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today). # select_datetime(my_date_time) # # # Generates a datetime select that defaults to today (no specified datetime) @@ -331,7 +332,7 @@ module ActionView # # prefixed with 'payday' rather than 'date' # select_datetime(my_date_time, :prefix => 'payday') # - # # Generates a datetime select with a custom prompt. Use :prompt=>true for generic prompts. + # # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts. # select_datetime(my_date_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'}) # select_datetime(my_date_time, :prompt => {:hour => true}) # generic prompt for hours # select_datetime(my_date_time, :prompt => true) # generic prompts for all @@ -350,10 +351,10 @@ module ActionView # ==== Examples # my_date = Time.today + 6.days # - # # Generates a date select that defaults to the date in my_date (six days after today) + # # Generates a date select that defaults to the date in my_date (six days afteri today). # select_date(my_date) # - # # Generates a date select that defaults to today (no specified date) + # # Generates a date select that defaults to today (no specified date). # select_date() # # # Generates a date select that defaults to the date in my_date (six days after today) @@ -361,18 +362,18 @@ module ActionView # select_date(my_date, :order => [:year, :month, :day]) # # # Generates a date select that discards the type of the field and defaults to the date in - # # my_date (six days after today) + # # my_date (six days after today). # select_date(my_date, :discard_type => true) # # # Generates a date select that defaults to the date in my_date, - # # which has fields separated by '/' + # # which has fields separated by '/'. # select_date(my_date, :date_separator => '/') # # # Generates a date select that defaults to the datetime in my_date (six days after today) - # # prefixed with 'payday' rather than 'date' + # # prefixed with 'payday' rather than 'date'. # select_date(my_date, :prefix => 'payday') # - # # Generates a date select with a custom prompt. Use :prompt=>true for generic prompts. + # # Generates a date select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts. # select_date(my_date, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'}) # select_date(my_date, :prompt => {:hour => true}) # generic prompt for hours # select_date(my_date, :prompt => true) # generic prompts for all @@ -381,7 +382,7 @@ module ActionView DateTimeSelector.new(date, options, html_options).select_date end - # Returns a set of html select-tags (one for hour and minute) + # Returns a set of html select-tags (one for hour and minute). # You can set <tt>:time_separator</tt> key to format the output, and # the <tt>:include_seconds</tt> option to include an input for seconds. # @@ -390,28 +391,28 @@ module ActionView # ==== Examples # my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds # - # # Generates a time select that defaults to the time in my_time + # # Generates a time select that defaults to the time in my_time. # select_time(my_time) # - # # Generates a time select that defaults to the current time (no specified time) + # # Generates a time select that defaults to the current time (no specified time). # select_time() # # # Generates a time select that defaults to the time in my_time, - # # which has fields separated by ':' + # # which has fields separated by ':'. # select_time(my_time, :time_separator => ':') # # # Generates a time select that defaults to the time in my_time, - # # that also includes an input for seconds + # # that also includes an input for seconds. # select_time(my_time, :include_seconds => true) # # # Generates a time select that defaults to the time in my_time, that has fields - # # separated by ':' and includes an input for seconds + # # separated by ':' and includes an input for seconds. # select_time(my_time, :time_separator => ':', :include_seconds => true) # # # Generate a time select field with hours in the AM/PM format # select_time(my_time, :ampm => true) # - # # Generates a time select with a custom prompt. Use :prompt=>true for generic prompts. + # # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts. # select_time(my_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'}) # select_time(my_time, :prompt => {:hour => true}) # generic prompt for hours # select_time(my_time, :prompt => true) # generic prompts for all @@ -427,17 +428,17 @@ module ActionView # ==== Examples # my_time = Time.now + 16.minutes # - # # Generates a select field for seconds that defaults to the seconds for the time in my_time + # # Generates a select field for seconds that defaults to the seconds for the time in my_time. # select_second(my_time) # - # # Generates a select field for seconds that defaults to the number given + # # Generates a select field for seconds that defaults to the number given. # select_second(33) # # # Generates a select field for seconds that defaults to the seconds for the time in my_time - # # that is named 'interval' rather than 'second' + # # that is named 'interval' rather than 'second'. # select_second(my_time, :field_name => 'interval') # - # # Generates a select field for seconds with a custom prompt. Use :prompt=>true for a + # # Generates a select field for seconds with a custom prompt. Use <tt>:prompt => true</tt> for a # # generic prompt. # select_minute(14, :prompt => 'Choose seconds') # @@ -453,17 +454,17 @@ module ActionView # ==== Examples # my_time = Time.now + 6.hours # - # # Generates a select field for minutes that defaults to the minutes for the time in my_time + # # Generates a select field for minutes that defaults to the minutes for the time in my_tiime. # select_minute(my_time) # - # # Generates a select field for minutes that defaults to the number given + # # Generates a select field for minutes that defaults to the number given. # select_minute(14) # # # Generates a select field for minutes that defaults to the minutes for the time in my_time - # # that is named 'stride' rather than 'second' + # # that is named 'stride' rather than 'second'. # select_minute(my_time, :field_name => 'stride') # - # # Generates a select field for minutes with a custom prompt. Use :prompt=>true for a + # # Generates a select field for minutes with a custom prompt. Use <tt>:prompt => true</tt> for a # # generic prompt. # select_minute(14, :prompt => 'Choose minutes') # @@ -478,17 +479,17 @@ module ActionView # ==== Examples # my_time = Time.now + 6.hours # - # # Generates a select field for hours that defaults to the hour for the time in my_time + # # Generates a select field for hours that defaults to the hour for the time in my_time. # select_hour(my_time) # - # # Generates a select field for hours that defaults to the number given + # # Generates a select field for hours that defaults to the number given. # select_hour(13) # # # Generates a select field for hours that defaults to the minutes for the time in my_time - # # that is named 'stride' rather than 'second' + # # that is named 'stride' rather than 'second'. # select_hour(my_time, :field_name => 'stride') # - # # Generates a select field for hours with a custom prompt. Use :prompt => true for a + # # Generates a select field for hours with a custom prompt. Use <tt>:prompt => true</tt> for a # # generic prompt. # select_hour(13, :prompt => 'Choose hour') # @@ -506,17 +507,17 @@ module ActionView # ==== Examples # my_date = Time.today + 2.days # - # # Generates a select field for days that defaults to the day for the date in my_date + # # Generates a select field for days that defaults to the day for the date in my_date. # select_day(my_time) # - # # Generates a select field for days that defaults to the number given + # # Generates a select field for days that defaults to the number given. # select_day(5) # # # Generates a select field for days that defaults to the day for the date in my_date - # # that is named 'due' rather than 'day' + # # that is named 'due' rather than 'day'. # select_day(my_time, :field_name => 'due') # - # # Generates a select field for days with a custom prompt. Use :prompt => true for a + # # Generates a select field for days with a custom prompt. Use <tt>:prompt => true</tt> for a # # generic prompt. # select_day(5, :prompt => 'Choose day') # @@ -539,7 +540,7 @@ module ActionView # select_month(Date.today) # # # Generates a select field for months that defaults to the current month that - # # is named "start" rather than "month" + # # is named "start" rather than "month". # select_month(Date.today, :field_name => 'start') # # # Generates a select field for months that defaults to the current month that @@ -558,7 +559,7 @@ module ActionView # # will use keys like "Januar", "Marts." # select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...)) # - # # Generates a select field for months with a custom prompt. Use :prompt => true for a + # # Generates a select field for months with a custom prompt. Use <tt>:prompt => true</tt> for a # # generic prompt. # select_month(14, :prompt => 'Choose month') # @@ -574,22 +575,22 @@ module ActionView # # ==== Examples # # Generates a select field for years that defaults to the current year that - # # has ascending year values + # # has ascending year values. # select_year(Date.today, :start_year => 1992, :end_year => 2007) # # # Generates a select field for years that defaults to the current year that - # # is named 'birth' rather than 'year' + # # is named 'birth' rather than 'year'. # select_year(Date.today, :field_name => 'birth') # # # Generates a select field for years that defaults to the current year that - # # has descending year values + # # has descending year values. # select_year(Date.today, :start_year => 2005, :end_year => 1900) # # # Generates a select field for years that defaults to the year 2006 that - # # has ascending year values + # # has ascending year values. # select_year(2006, :start_year => 2000, :end_year => 2010) # - # # Generates a select field for years with a custom prompt. Use :prompt => true for a + # # Generates a select field for years with a custom prompt. Use <tt>:prompt => true</tt> for a # # generic prompt. # select_year(14, :prompt => 'Choose year') # @@ -783,7 +784,7 @@ module ActionView end # Returns translated month names, but also ensures that a custom month - # name array has a leading nil element + # name array has a leading nil element. def month_names month_names = @options[:use_month_names] || translated_month_names month_names.unshift(nil) if month_names.size < 13 @@ -791,13 +792,13 @@ module ActionView end memoize :month_names - # Returns translated month names + # Returns translated month names. # => [nil, "January", "February", "March", # "April", "May", "June", "July", # "August", "September", "October", # "November", "December"] # - # If :use_short_month option is set + # If <tt>:use_short_month</tt> option is set # => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun", # "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] def translated_month_names @@ -805,13 +806,13 @@ module ActionView I18n.translate(key, :locale => @options[:locale]) end - # Lookup month name for number + # Lookup month name for number. # month_name(1) => "January" # - # If :use_month_numbers option is passed + # If <tt>:use_month_numbers</tt> option is passed # month_name(1) => 1 # - # If :add_month_numbers option is passed + # If <tt>:add_month_numbers</tt> option is passed # month_name(1) => "1 - January" def month_name(number) if @options[:use_month_numbers] @@ -832,16 +833,22 @@ module ActionView I18n.translate(:'date.order', :locale => @options[:locale]) || [] end - # Build full select tag from date type and options + # Build full select tag from date type and options. def build_options_and_select(type, selected, options = {}) build_select(type, build_options(selected, options)) end - # Build select option html from date value and options + # Build select option html from date value and options. # build_options(15, :start => 1, :end => 31) # => "<option value="1">1</option> - # <option value=\"2\">2</option> - # <option value=\"3\">3</option>..." + # <option value="2">2</option> + # <option value="3">3</option>..." + # + # If <tt>:step</tt> options is passed + # build_options(15, :start => 1, :end => 31, :step => 2) + # => "<option value="1">1</option> + # <option value="3">3</option> + # <option value="5">5</option>..." def build_options(selected, options = {}) start = options.delete(:start) || 0 stop = options.delete(:end) || 59 @@ -860,7 +867,7 @@ module ActionView (select_options.join("\n") + "\n").html_safe end - # Builds select tag from date type and html select options + # Builds select tag from date type and html select options. # build_select(:month, "<option value="1">January</option>...") # => "<select id="post_written_on_2i" name="post[written_on(2i)]"> # <option value="1">January</option>... @@ -880,7 +887,7 @@ module ActionView (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe end - # Builds a prompt option tag with supplied options or from default options + # Builds a prompt option tag with supplied options or from default options. # prompt_option_tag(:month, :prompt => 'Select month') # => "<option value="">Select month</option>" def prompt_option_tag(type, options) @@ -897,7 +904,7 @@ module ActionView prompt ? content_tag(:option, prompt, :value => '') : '' end - # Builds hidden input tag for date part and value + # Builds hidden input tag for date part and value. # build_hidden(:year, 2008) # => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />" def build_hidden(type, value) @@ -909,7 +916,7 @@ module ActionView }.merge(@html_options.slice(:disabled))) + "\n").html_safe end - # Returns the name attribute for the input tag + # Returns the name attribute for the input tag. # => post[written_on(1i)] def input_name_from_type(type) prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX @@ -923,7 +930,7 @@ module ActionView @options[:discard_type] ? prefix : "#{prefix}[#{field_name}]" end - # Returns the id attribute for the input tag + # Returns the id attribute for the input tag. # => "post_written_on_1i" def input_id_from_type(type) input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '') @@ -940,7 +947,7 @@ module ActionView select.html_safe end - # Returns the separator for a given datetime component + # Returns the separator for a given datetime component. def separator(type) case type when :year diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index b6bb90a3ce..07e2c8d341 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -243,7 +243,7 @@ module ActionView # # === Setting the method # - # You can force the form to use the full array of HTTP verbs by setting + # You can force the form to use the full array of HTTP verbs by setting # # :method => (:get|:post|:put|:delete) # @@ -584,8 +584,8 @@ module ActionView # <% end %> # ... # <% end %> - def fields_for(record, record_object = nil, options = {}, &block) - builder = instantiate_builder(record, record_object, options, &block) + def fields_for(record_name, record_object = nil, options = {}, &block) + builder = instantiate_builder(record_name, record_object, options, &block) output = capture(builder, &block) output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id? output @@ -898,16 +898,13 @@ module ActionView private - def instantiate_builder(record, *args, &block) - options = args.extract_options! - record_object = args.shift - - case record + def instantiate_builder(record_name, record_object, options, &block) + case record_name when String, Symbol object = record_object - object_name = record + object_name = record_name else - object = record + object = record_name object_name = ActiveModel::Naming.param_key(object) end @@ -1219,35 +1216,30 @@ module ActionView RUBY_EVAL end - def fields_for(record_or_name_or_array, *args, &block) - if options.has_key?(:index) - index = "[#{options[:index]}]" - elsif defined?(@auto_index) - self.object_name = @object_name.to_s.sub(/\[\]$/,"") - index = "[#{@auto_index}]" - else - index = "" - end - - args << {} unless args.last.is_a?(Hash) - args.last[:builder] ||= options[:builder] - args.last[:parent_builder] = self + def fields_for(record_name, record_object = nil, fields_options = {}, &block) + fields_options, record_object = record_object, nil if record_object.is_a?(Hash) + fields_options[:builder] ||= options[:builder] + fields_options[:parent_builder] = self - case record_or_name_or_array + case record_name when String, Symbol - if nested_attributes_association?(record_or_name_or_array) - return fields_for_with_nested_attributes(record_or_name_or_array, args, block) - else - name = record_or_name_or_array + if nested_attributes_association?(record_name) + return fields_for_with_nested_attributes(record_name, record_object, fields_options, block) end else - object = record_or_name_or_array.is_a?(Array) ? record_or_name_or_array.last : record_or_name_or_array - name = ActiveModel::Naming.param_key(object) - args.unshift(object) + record_object = record_name.is_a?(Array) ? record_name.last : record_name + record_name = ActiveModel::Naming.param_key(record_object) + end + + index = if options.has_key?(:index) + "[#{options[:index]}]" + elsif defined?(@auto_index) + self.object_name = @object_name.to_s.sub(/\[\]$/,"") + "[#{@auto_index}]" end - name = "#{object_name}#{index}[#{name}]" + record_name = "#{object_name}#{index}[#{record_name}]" - @template.fields_for(name, *args, &block) + @template.fields_for(record_name, record_object, fields_options, &block) end def label(method, text = nil, options = {}, &block) @@ -1336,10 +1328,8 @@ module ActionView @object.respond_to?("#{association_name}_attributes=") end - def fields_for_with_nested_attributes(association_name, args, block) + def fields_for_with_nested_attributes(association_name, association, options, block) name = "#{object_name}[#{association_name}_attributes]" - options = args.extract_options! - association = args.shift association = convert_to_model(association) if association.respond_to?(:persisted?) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 7698602022..0aaa690129 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -274,10 +274,10 @@ module ActionView # You can optionally provide html attributes as the last element of the array. # # Examples: - # options_for_select([ "Denmark", ["USA", {:class=>'bold'}], "Sweden" ], ["USA", "Sweden"]) + # options_for_select([ "Denmark", ["USA", {:class => 'bold'}], "Sweden" ], ["USA", "Sweden"]) # <option value="Denmark">Denmark</option>\n<option value="USA" class="bold" selected="selected">USA</option>\n<option value="Sweden" selected="selected">Sweden</option> # - # options_for_select([["Dollar", "$", {:class=>"bold"}], ["Kroner", "DKK", {:onclick => "alert('HI');"}]]) + # options_for_select([["Dollar", "$", {:class => "bold"}], ["Kroner", "DKK", {:onclick => "alert('HI');"}]]) # <option value="$" class="bold">Dollar</option>\n<option value="DKK" onclick="alert('HI');">Kroner</option> # # If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 9e0f8f32b7..65a98fb27a 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -82,22 +82,22 @@ module ActionView # select_tag "people", options_from_collection_for_select(@people, "id", "name") # # <select id="people" name="people"><option value="1">David</option></select> # - # select_tag "people", "<option>David</option>" + # select_tag "people", "<option>David</option>".html_safe # # => <select id="people" name="people"><option>David</option></select> # - # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>" + # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe # # => <select id="count" name="count"><option>1</option><option>2</option> # # <option>3</option><option>4</option></select> # - # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>", :multiple => true + # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, :multiple => true # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option> # # <option>Green</option><option>Blue</option></select> # - # select_tag "locations", "<option>Home</option><option selected="selected">Work</option><option>Out</option>" + # select_tag "locations", "<option>Home</option><option selected="selected">Work</option><option>Out</option>".html_safe # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option> # # <option>Out</option></select> # - # select_tag "access", "<option>Read</option><option>Write</option>", :multiple => true, :class => 'form_input' + # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, :multiple => true, :class => 'form_input' # # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option> # # <option>Write</option></select> # @@ -107,7 +107,7 @@ module ActionView # select_tag "people", options_from_collection_for_select(@people, "id", "name"), :prompt => "Select something" # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select> # - # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>", :disabled => true + # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, :disabled => true # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option> # # <option>Paris</option><option>Rome</option></select> def select_tag(name, option_tags = nil, options = {}) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index ec9bdd5320..fd8fe417d0 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -15,10 +15,10 @@ module ActionView # = Action View Translation Helpers module Helpers module TranslationHelper - # Delegates to I18n#translate but also performs three additional functions. + # Delegates to <tt>I18n#translate</tt> but also performs three additional functions. # - # First, it'll pass the :rescue_format => :html option to I18n so that any - # thrown MissingTranslation messages will be turned into inline spans that + # First, it'll pass the <tt>:rescue_format => :html</tt> option to I18n so that any + # thrown +MissingTranslation+ messages will be turned into inline spans that # # * have a "translation-missing" class set, # * contain the missing key as a title attribute and @@ -54,7 +54,7 @@ module ActionView end alias :t :translate - # Delegates to I18n.localize with no additional functionality. + # Delegates to <tt>I18n.localize</tt> with no additional functionality. def localize(*args) I18n.localize(*args) end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index ffa9a5bb0b..5488c752cc 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -420,7 +420,7 @@ module ActionView end # Creates a link tag of the given +name+ using a URL created by the set of - # +options+ if +condition+ is true, in which case only the name is + # +options+ if +condition+ is true, otherwise only the name is # returned. To specialize the default behavior, you can pass a block that # accepts the name or the full argument list for +link_to_unless+ (see the examples # in +link_to_unless+). diff --git a/actionpack/lib/action_view/path_set.rb b/actionpack/lib/action_view/path_set.rb index 8b840a6463..e0cb5d6a70 100644 --- a/actionpack/lib/action_view/path_set.rb +++ b/actionpack/lib/action_view/path_set.rb @@ -35,7 +35,7 @@ module ActionView #:nodoc: each_with_index do |path, i| path = path.to_s if path.is_a?(Pathname) next unless path.is_a?(String) - self[i] = FileSystemResolver.new(path) + self[i] = OptimizedFileSystemResolver.new(path) end end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 98ecd15aa0..b99d24d281 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -79,9 +79,9 @@ module ActionView # you are handling out-of-band metadata, you are # also responsible for alerting the user to any # problems with converting the user's data to - # the default_internal. + # the <tt>default_internal</tt>. # - # To do so, simply raise the raise WrongEncodingError + # To do so, simply raise the raise +WrongEncodingError+ # as follows: # # raise WrongEncodingError.new( @@ -198,7 +198,7 @@ module ActionView # Among other things, this method is responsible for properly setting # the encoding of the source. Until this point, we assume that the # source is BINARY data. If no additional information is supplied, - # we assume the encoding is the same as Encoding.default_external. + # we assume the encoding is the same as <tt>Encoding.default_external</tt>. # # The user can also specify the encoding via a comment on the first # line of the template (# encoding: NAME-OF-ENCODING). This will work @@ -212,8 +212,8 @@ module ActionView # specifying the encoding. For instance, ERB supports <%# encoding: %> # # Otherwise, after we figure out the correct encoding, we then - # encode the source into Encoding.default_internal. In general, - # this means that templates will be UTF-8 inside of Rails, + # encode the source into <tt>Encoding.default_internal</tt>. + # In general, this means that templates will be UTF-8 inside of Rails, # regardless of the original source encoding. def compile(view, mod) #:nodoc: method_name = self.method_name diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index e246646963..d4448a7b33 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -1,3 +1,4 @@ +require "active_support/core_ext/array/wrap" require "active_support/core_ext/enumerable" module ActionView @@ -29,6 +30,7 @@ module ActionView def initialize(paths, path, prefixes, partial, details, *) @path = path + prefixes = Array.wrap(prefixes) display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ") template_type = if partial "partial" diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 870897958a..2b9427ace5 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -10,17 +10,16 @@ module ActionView attr_reader :name, :prefix, :partial, :virtual alias_method :partial?, :partial - def initialize(name, prefix, partial) - @name, @prefix, @partial = name, prefix, partial - rebuild(@name, @prefix, @partial) + def self.build(name, prefix, partial) + virtual = "" + virtual << "#{prefix}/" unless prefix.empty? + virtual << (partial ? "_#{name}" : name) + new name, prefix, partial, virtual end - def rebuild(name, prefix, partial) - @virtual = "" - @virtual << "#{prefix}/" unless prefix.empty? - @virtual << (partial ? "_#{name}" : name) - - self.replace(@virtual) + def initialize(name, prefix, partial, virtual) + @name, @prefix, @partial = name, prefix, partial + super(virtual) end end @@ -60,7 +59,7 @@ module ActionView # Helpers that builds a path. Useful for building virtual paths. def build_path(name, prefix, partial) - Path.new(name, prefix, partial) + Path.build(name, prefix, partial) end # Handles templates caching. If a key is given and caching is on @@ -112,7 +111,8 @@ module ActionView end end - class PathResolver < Resolver + # An abstract class that implements a Resolver with path semantics. + class PathResolver < Resolver #:nodoc: EXTENSIONS = [:locale, :formats, :handlers] DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}" @@ -124,13 +124,12 @@ module ActionView private def find_templates(name, prefix, partial, details) - path = build_path(name, prefix, partial) - extensions = Hash[EXTENSIONS.map { |ext| [ext, details[ext]] }.flatten(0)] - query(path, extensions, details[:formats]) + path = Path.build(name, prefix, partial) + query(path, details, details[:formats]) end - def query(path, exts, formats) - query = build_query(path, exts) + def query(path, details, formats) + query = build_query(path, details) templates = [] sanitizer = Hash.new { |h,k| h[k] = Dir["#{File.dirname(k)}/*"] } @@ -138,7 +137,7 @@ module ActionView next if File.directory?(p) || !sanitizer[p].include?(p) handler, format = extract_handler_and_format(p, formats) - contents = File.open(p, "rb") {|io| io.read } + contents = File.open(p, "rb") { |io| io.read } templates << Template.new(contents, File.expand_path(p), handler, :virtual_path => path.virtual, :format => format, :updated_at => mtime(p)) @@ -147,18 +146,15 @@ module ActionView templates end - # Helper for building query glob string based on resolver's pattern. - def build_query(path, exts) + # Helper for building query glob string based on resolver's pattern. + def build_query(path, details) query = @pattern.dup query.gsub!(/\:prefix(\/)?/, path.prefix.empty? ? "" : "#{path.prefix}\\1") # prefix can be empty... query.gsub!(/\:action/, path.partial? ? "_#{path.name}" : path.name) - exts.each { |ext, variants| + details.each do |ext, variants| query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}") - } - - query.gsub!('.{html,', '.{html,text.html,') - query.gsub!('.{text,', '.{text,text.plain,') + end File.expand_path(query, @path) end @@ -235,9 +231,25 @@ module ActionView alias :== :eql? end + # An Optimized resolver for Rails' most common case. + class OptimizedFileSystemResolver < FileSystemResolver #:nodoc: + def build_query(path, details) + exts = EXTENSIONS.map { |ext| details[ext] } + query = File.join(@path, path) + + exts.each do |ext| + query << "{" + ext.compact.each { |e| query << ".#{e}," } + query << "}" + end + + query + end + end + # The same as FileSystemResolver but does not allow templates to store # a virtual path since it is invalid for such resolvers. - class FallbackFileSystemResolver < FileSystemResolver + class FallbackFileSystemResolver < FileSystemResolver #:nodoc: def self.instances [new(""), new("/")] end diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index 9c10decd60..8cee3babe2 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -34,12 +34,12 @@ module Sprockets app.assets = asset_environment(app) ActiveSupport.on_load(:action_view) do - app.assets.context.instance_eval do + app.assets.context_class.instance_eval do include ::ActionView::Helpers::SprocketsHelper end end - app.routes.append do + app.routes.prepend do mount app.assets => assets.prefix end @@ -91,7 +91,7 @@ module Sprockets compressor when :yui require 'yui/compressor' - YUI::JavaScriptCompressor.new(:munge => true) + YUI::CssCompressor.new else sym end |