diff options
Diffstat (limited to 'actionpack/lib')
23 files changed, 270 insertions, 121 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 389ade425b..c1a56deca4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -260,30 +260,44 @@ module ActionController #:nodoc: include StatusCodes - cattr_reader :protected_instance_variables + ## + # :singleton-method: # Controller specific instance variables which will not be accessible inside views. + cattr_reader :protected_instance_variables @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller @action_name @before_filter_chain_aborted @action_cache_path @_session @_cookies @_headers @_params @_flash @_response) + @@asset_host = "" + ## + # :singleton-method: # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, # and images to a dedicated asset server away from the main web server. Example: # ActionController::Base.asset_host = "http://assets.example.com" - @@asset_host = "" cattr_accessor :asset_host + @@consider_all_requests_local = true + ## + # :singleton-method: # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors. # When the application is ready to go public, this should be set to false, and the protected method <tt>local_request?</tt> # should instead be implemented in the controller to determine when debugging screens should be shown. - @@consider_all_requests_local = true cattr_accessor :consider_all_requests_local + @@allow_concurrency = false + ## + # :singleton-method: # Indicates whether to allow concurrent action processing. Your # controller actions and any other code they call must also behave well # when called from concurrent threads. Turned off by default. - @@allow_concurrency = false cattr_accessor :allow_concurrency + @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form, + Mime::URL_ENCODED_FORM => :url_encoded_form, + Mime::XML => :xml_simple, + Mime::JSON => :json } + ## + # :singleton-method: # Modern REST web services often need to submit complex data to the web application. # The <tt>@@param_parsers</tt> hash lets you register handlers which will process the HTTP body and add parameters to the # <tt>params</tt> hash. These handlers are invoked for POST and PUT requests. @@ -310,37 +324,47 @@ module ActionController #:nodoc: # A YAML parser is also available and can be turned on with: # # ActionController::Base.param_parsers[Mime::YAML] = :yaml - @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form, - Mime::URL_ENCODED_FORM => :url_encoded_form, - Mime::XML => :xml_simple, - Mime::JSON => :json } cattr_accessor :param_parsers - # Controls the default charset for all renders. @@default_charset = "utf-8" + ## + # :singleton-method: + # Controls the default charset for all renders. cattr_accessor :default_charset + ## + # :singleton-method: # The logger is used for generating information on the action run-time (including benchmarking) if available. # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers. cattr_accessor :logger - # Controls the resource action separator @@resource_action_separator = "/" + ## + # :singleton-method: + # Controls the resource action separator cattr_accessor :resource_action_separator - # Allow to override path names for default resources' actions @@resources_path_names = { :new => 'new', :edit => 'edit' } + ## + # :singleton-method: + # Allow to override path names for default resources' actions cattr_accessor :resources_path_names + ## + # :singleton-method: # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ # sets it to <tt>:authenticity_token</tt> by default. cattr_accessor :request_forgery_protection_token + ## + # :singleton-method: # Indicates whether or not optimise the generated named # route helper methods cattr_accessor :optimise_named_routes self.optimise_named_routes = true + ## + # :singleton-method: # Indicates whether the response format should be determined by examining the Accept HTTP header, # or by using the simpler params + ajax rules. # @@ -351,38 +375,54 @@ module ActionController #:nodoc: cattr_accessor :use_accept_header self.use_accept_header = true + ## + # :singleton-method: # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. class_inheritable_accessor :allow_forgery_protection self.allow_forgery_protection = true + ## + # :singleton-method: # If you are deploying to a subdirectory, you will need to set # <tt>config.action_controller.relative_url_root</tt> # This defaults to ENV['RAILS_RELATIVE_URL_ROOT'] cattr_accessor :relative_url_root self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT'] + ## + # :singleton-method: # Holds the request object that's primarily used to get environment variables through access like # <tt>request.env["REQUEST_URI"]</tt>. attr_internal :request + ## + # :singleton-method: # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like <tt>params["post_id"]</tt> # to get the post_id. No type casts are made, so all values are returned as strings. attr_internal :params + ## + # :singleton-method: # Holds the response object that's primarily used to set additional HTTP headers through access like # <tt>response.headers["Cache-Control"] = "no-cache"</tt>. Can also be used to access the final body HTML after a template # has been rendered through response.body -- useful for <tt>after_filter</tt>s that wants to manipulate the output, # such as a OutputCompressionFilter. attr_internal :response + ## + # :singleton-method: # Holds a hash of objects in the session. Accessed like <tt>session[:person]</tt> to get the object tied to the "person" # key. The session will hold any type of object as values, but the key should be a string or symbol. attr_internal :session + ## + # :singleton-method: # Holds a hash of header names and values. Accessed like <tt>headers["Cache-Control"]</tt> to get the value of the Cache-Control # directive. Values should always be specified as strings. attr_internal :headers + ## + # :singleton-method: # Returns the name of the action this controller is processing. attr_accessor :action_name @@ -1241,7 +1281,7 @@ module ActionController #:nodoc: parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup parameters = parameters.except!(:controller, :action, :format, :_method) - logger.info " Parameters: #{parameters.inspect}" + logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? end def default_render #:nodoc: diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index a70ed72f03..22e4fbec43 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -33,28 +33,26 @@ module ActionController #:nodoc: # # Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be # expired. - # - # == Setting the cache directory - # - # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>. - # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing - # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your - # web server to look in the new location for cached files. - # - # == Setting the cache extension - # - # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in - # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>. - # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an - # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps. module Pages def self.included(base) #:nodoc: base.extend(ClassMethods) base.class_eval do @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : "" + ## + # :singleton-method: + # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>. + # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing + # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your + # web server to look in the new location for cached files. cattr_accessor :page_cache_directory @@page_cache_extension = '.html' + ## + # :singleton-method: + # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in + # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>. + # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an + # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps. cattr_accessor :page_cache_extension end end diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index f3e173004a..2d5e80f0bb 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -23,7 +23,6 @@ module ActionController if defined?(ActiveRecord) after_dispatch :checkin_connections - before_dispatch { ActiveRecord::Base.verify_active_connections! } to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers } end diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb index 26edca3b69..5ef52f45a6 100644 --- a/actionpack/lib/action_controller/mime_type.rb +++ b/actionpack/lib/action_controller/mime_type.rb @@ -20,8 +20,22 @@ module Mime # end class Type @@html_types = Set.new [:html, :all] + cattr_reader :html_types + + @@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form] + ## + # :singleton-method: + # These are the content types which browsers can generate without using ajax, flash, etc + # i.e. following a link, getting an image or posting a form. CSRF protection + # only needs to protect against these types. + cattr_reader :browser_generated_types + + @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml] - cattr_reader :html_types, :unverifiable_types + def self.unverifiable_types + ActiveSupport::Deprecation.warn("unverifiable_types is deprecated and has no effect", caller) + @@unverifiable_types + end # A simple helper class used in parsing the accept header class AcceptItem #:nodoc: @@ -167,13 +181,17 @@ module Mime # Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See # ActionController::RequestForgerProtection. def verify_request? - !@@unverifiable_types.include?(to_sym) + browser_generated? end def html? @@html_types.include?(to_sym) || @string =~ /html/ end + def browser_generated? + @@browser_generated_types.include?(to_sym) + end + private def method_missing(method, *args) if method.to_s =~ /(\w+)\?$/ diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index cc228c4230..2644c7f7c7 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -73,7 +73,7 @@ module ActionController # def polymorphic_url(record_or_hash_or_array, options = {}) if record_or_hash_or_array.kind_of?(Array) - record_or_hash_or_array = record_or_hash_or_array.dup + record_or_hash_or_array = record_or_hash_or_array.compact end record = extract_record(record_or_hash_or_array) diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index a6d4abf029..c079895683 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -13,7 +13,7 @@ module ActionController ActiveSupport::Deprecation.warn( "ActionController::AbstractRequest.relative_url_root= has been renamed." + "You can now set it with config.action_controller.relative_url_root=", caller) - ActionController::base.relative_url_root=relative_url_root + ActionController::Base.relative_url_root=relative_url_root end HTTP_METHODS = %w(get head put post delete options) diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb index 05a6d8bb79..3e0e94a06b 100644 --- a/actionpack/lib/action_controller/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/request_forgery_protection.rb @@ -99,7 +99,7 @@ module ActionController #:nodoc: end def verifiable_request_format? - request.content_type.nil? || request.content_type.verify_request? + !request.content_type.nil? && request.content_type.verify_request? end # Sets the token value for the current session. Pass a <tt>:secret</tt> option diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 872b0dab3d..3b97916682 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -42,7 +42,11 @@ module ActionController # # Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer module Resources + INHERITABLE_OPTIONS = :namespace, :shallow, :actions + class Resource #:nodoc: + DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy + attr_reader :collection_methods, :member_methods, :new_methods attr_reader :path_prefix, :name_prefix, :path_segment attr_reader :plural, :singular @@ -57,6 +61,7 @@ module ActionController arrange_actions add_default_actions + set_allowed_actions set_prefixes end @@ -113,6 +118,10 @@ module ActionController @singular.to_s == @plural.to_s end + def has_action?(action) + !DEFAULT_ACTIONS.include?(action) || @options[:actions].nil? || @options[:actions].include?(action) + end + protected def arrange_actions @collection_methods = arrange_actions_by_methods(options.delete(:collection)) @@ -125,6 +134,25 @@ module ActionController add_default_action(new_methods, :get, :new) end + def set_allowed_actions + only = @options.delete(:only) + except = @options.delete(:except) + + if only && except + raise ArgumentError, 'Please supply either :only or :except, not both.' + elsif only == :all || except == :none + options[:actions] = DEFAULT_ACTIONS + elsif only == :none || except == :all + options[:actions] = [] + elsif only + options[:actions] = DEFAULT_ACTIONS & Array(only).map(&:to_sym) + elsif except + options[:actions] = DEFAULT_ACTIONS - Array(except).map(&:to_sym) + else + # leave options[:actions] alone + end + end + def set_prefixes @path_prefix = options.delete(:path_prefix) @name_prefix = options.delete(:name_prefix) @@ -255,7 +283,12 @@ module ActionController # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action. # * <tt>:controller</tt> - Specify the controller name for the routes. # * <tt>:singular</tt> - Specify the singular name used in the member routes. - # * <tt>:requirements</tt> - Set custom routing parameter requirements. + # * <tt>:requirements</tt> - Set custom routing parameter requirements; this is a hash of either + # regular expressions (which must match for the route to match) or extra parameters. For example: + # + # map.resource :profile, :path_prefix => ':name', :requirements => { :name => /[a-zA-Z]+/, :extra => 'value' } + # + # will only match if the first part is alphabetic, and will pass the parameter :extra to the controller. # * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes. # * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example: # # products_path == '/productos' @@ -353,6 +386,25 @@ module ActionController # # map.resources :users, :has_many => { :posts => :comments }, :shallow => true # + # * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to. + # + # <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a + # list of action names. By default, routes are generated for all seven actions. + # + # For example: + # + # map.resources :posts, :only => [:index, :show] do |post| + # post.resources :comments, :except => [:update, :destroy] + # end + # # --> GET /posts (maps to the PostsController#index action) + # # --> POST /posts (fails) + # # --> GET /posts/1 (maps to the PostsController#show action) + # # --> DELETE /posts/1 (fails) + # # --> POST /posts/1/comments (maps to the CommentsController#create action) + # # --> PUT /posts/1/comments/1 (fails) + # + # The <tt>:only</tt> and <tt>:except</tt> options are inherited by any nested resource(s). + # # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied. # # Examples: @@ -478,7 +530,7 @@ module ActionController map_associations(resource, options) if block_given? - with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block) + with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) end end end @@ -495,7 +547,7 @@ module ActionController map_associations(resource, options) if block_given? - with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block) + with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) end end end @@ -507,7 +559,7 @@ module ActionController name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}" Array(options[:has_one]).each do |association| - resource(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace], :shallow => options[:shallow]) + resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix)) end end @@ -522,7 +574,7 @@ module ActionController map_has_many_associations(resource, association, options) end when Symbol, String - resources(associations, :path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], :has_many => options[:has_many]) + resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many])) else end end @@ -531,41 +583,39 @@ module ActionController resource.collection_methods.each do |method, actions| actions.each do |action| [method].flatten.each do |m| - action_options = action_options_for(action, resource, m) - map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options) + map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m) end end end end def map_default_collection_actions(map, resource) - index_action_options = action_options_for("index", resource) index_route_name = "#{resource.name_prefix}#{resource.plural}" if resource.uncountable? index_route_name << "_index" end - map_named_routes(map, index_route_name, resource.path, index_action_options) - - create_action_options = action_options_for("create", resource) - map_unnamed_routes(map, resource.path, create_action_options) + map_resource_routes(map, resource, :index, resource.path, index_route_name) + map_resource_routes(map, resource, :create, resource.path, index_route_name) end def map_default_singleton_actions(map, resource) - create_action_options = action_options_for("create", resource) - map_unnamed_routes(map, resource.path, create_action_options) + map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}") end def map_new_actions(map, resource) resource.new_methods.each do |method, actions| actions.each do |action| - action_options = action_options_for(action, resource, method) - if action == :new - map_named_routes(map, "new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options) - else - map_named_routes(map, "#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options) + route_path = resource.new_path + route_name = "new_#{resource.name_prefix}#{resource.singular}" + + unless action == :new + route_path = "#{route_path}#{resource.action_separator}#{action}" + route_name = "#{action}_#{route_name}" end + + map_resource_routes(map, resource, action, route_path, route_name, method) end end end @@ -574,34 +624,33 @@ module ActionController resource.member_methods.each do |method, actions| actions.each do |action| [method].flatten.each do |m| - action_options = action_options_for(action, resource, m) - action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) action_path ||= Base.resources_path_names[action] || action - map_named_routes(map, "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options) + map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m) end end end - show_action_options = action_options_for("show", resource) - map_named_routes(map, "#{resource.shallow_name_prefix}#{resource.singular}", resource.member_path, show_action_options) - - update_action_options = action_options_for("update", resource) - map_unnamed_routes(map, resource.member_path, update_action_options) - - destroy_action_options = action_options_for("destroy", resource) - map_unnamed_routes(map, resource.member_path, destroy_action_options) - end - - def map_unnamed_routes(map, path_without_format, options) - map.connect(path_without_format, options) - map.connect("#{path_without_format}.:format", options) + route_path = "#{resource.shallow_name_prefix}#{resource.singular}" + map_resource_routes(map, resource, :show, resource.member_path, route_path) + map_resource_routes(map, resource, :update, resource.member_path, route_path) + map_resource_routes(map, resource, :destroy, resource.member_path, route_path) end - def map_named_routes(map, name, path_without_format, options) - map.named_route(name, path_without_format, options) - map.named_route("formatted_#{name}", "#{path_without_format}.:format", options) + def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil) + if resource.has_action?(action) + action_options = action_options_for(action, resource, method) + formatted_route_path = "#{route_path}.:format" + + if route_name && @set.named_routes[route_name.to_sym].nil? + map.named_route(route_name, route_path, action_options) + map.named_route("formatted_#{route_name}", formatted_route_path, action_options) + else + map.connect(route_path, action_options) + map.connect(formatted_route_path, action_options) + end + end end def add_conditions_for(conditions, method) diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index 8d51e823a6..2dcdac150a 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -84,9 +84,11 @@ module ActionController # This sets up +blog+ as the default controller if no other is specified. # This means visiting '/' would invoke the blog controller. # - # More formally, you can define defaults in a route with the <tt>:defaults</tt> key. + # More formally, you can include arbitrary parameters in the route, thus: # - # map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' } + # map.connect ':controller/:action/:id', :action => 'show', :page => 'Dashboard' + # + # This will pass the :page parameter to all incoming requests that match this route. # # Note: The default routes, as provided by the Rails generator, make all actions in every # controller accessible via GET requests. You should consider removing them or commenting diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb index 7b888fa8d2..d4e501e780 100644 --- a/actionpack/lib/action_controller/routing/builder.rb +++ b/actionpack/lib/action_controller/routing/builder.rb @@ -1,23 +1,16 @@ module ActionController module Routing class RouteBuilder #:nodoc: - attr_accessor :separators, :optional_separators + attr_reader :separators, :optional_separators + attr_reader :separator_regexp, :nonseparator_regexp, :interval_regexp def initialize - self.separators = Routing::SEPARATORS - self.optional_separators = %w( / ) - end - - def separator_pattern(inverted = false) - "[#{'^' if inverted}#{Regexp.escape(separators.join)}]" - end - - def interval_regexp - Regexp.new "(.*?)(#{separators.source}|$)" - end + @separators = Routing::SEPARATORS + @optional_separators = %w( / ) - def multiline_regexp?(expression) - expression.options & Regexp::MULTILINE == Regexp::MULTILINE + @separator_regexp = /[#{Regexp.escape(separators.join)}]/ + @nonseparator_regexp = /\A([^#{Regexp.escape(separators.join)}]+)/ + @interval_regexp = /(.*?)(#{separator_regexp}|$)/ end # Accepts a "route path" (a string defining a route), and returns the array @@ -30,7 +23,7 @@ module ActionController rest, segments = path, [] until rest.empty? - segment, rest = segment_for rest + segment, rest = segment_for(rest) segments << segment end segments @@ -39,20 +32,20 @@ module ActionController # A factory method that returns a new segment instance appropriate for the # format of the given string. def segment_for(string) - segment = case string - when /\A:(\w+)/ - key = $1.to_sym - case key - when :controller then ControllerSegment.new(key) - else DynamicSegment.new key - end - when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true) - when /\A\?(.*?)\?/ - StaticSegment.new($1, :optional => true) - when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1) - when Regexp.new(separator_pattern) then - DividerSegment.new($&, :optional => (optional_separators.include? $&)) - end + segment = + case string + when /\A:(\w+)/ + key = $1.to_sym + key == :controller ? ControllerSegment.new(key) : DynamicSegment.new(key) + when /\A\*(\w+)/ + PathSegment.new($1.to_sym, :optional => true) + when /\A\?(.*?)\?/ + StaticSegment.new($1, :optional => true) + when nonseparator_regexp + StaticSegment.new($1) + when separator_regexp + DividerSegment.new($&, :optional => optional_separators.include?($&)) + end [segment, $~.post_match] end @@ -98,7 +91,7 @@ module ActionController if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z} raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}" end - if multiline_regexp?(requirement) + if requirement.multiline? raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}" end segment.regexp = requirement diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb index 6c47ced6d1..3b98b16683 100644 --- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb +++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb @@ -148,7 +148,7 @@ module ActionController end nil end - }, __FILE__, __LINE__ + }, '(recognize_optimized)', 1 end def clear_recognize_optimized! diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb index 3b2cb28545..a610ec7e84 100644 --- a/actionpack/lib/action_controller/routing/route.rb +++ b/actionpack/lib/action_controller/routing/route.rb @@ -219,7 +219,7 @@ module ActionController next_capture = 1 extraction = segments.collect do |segment| x = segment.match_extraction(next_capture) - next_capture += Regexp.new(segment.regexp_chunk).number_of_captures + next_capture += segment.number_of_captures x end extraction.compact diff --git a/actionpack/lib/action_controller/routing/routing_ext.rb b/actionpack/lib/action_controller/routing/routing_ext.rb index 5f4ba90d0c..4a82b2af5f 100644 --- a/actionpack/lib/action_controller/routing/routing_ext.rb +++ b/actionpack/lib/action_controller/routing/routing_ext.rb @@ -27,6 +27,10 @@ class Regexp #:nodoc: Regexp.new("|#{source}").match('').captures.length end + def multiline? + options & MULTILINE == MULTILINE + end + class << self def optionalize(pattern) case unoptionalize(pattern) diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index e5f174ae2c..f6b03edcca 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -13,6 +13,10 @@ module ActionController @is_optional = false end + def number_of_captures + Regexp.new(regexp_chunk).number_of_captures + end + def extraction_code nil end @@ -84,6 +88,10 @@ module ActionController optional? ? Regexp.optionalize(chunk) : chunk end + def number_of_captures + 0 + end + def build_pattern(pattern) escaped = Regexp.escape(value) if optional? && ! pattern.empty? @@ -194,10 +202,16 @@ module ActionController end end + def number_of_captures + if regexp + regexp.number_of_captures + 1 + else + 1 + end + end + def build_pattern(pattern) - chunk = regexp_chunk - chunk = "(#{chunk})" if Regexp.new(chunk).number_of_captures == 0 - pattern = "#{chunk}#{pattern}" + pattern = "#{regexp_chunk}#{pattern}" optional? ? Regexp.optionalize(pattern) : pattern end @@ -230,6 +244,10 @@ module ActionController "(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))" end + def number_of_captures + 1 + end + # Don't URI.escape the controller name since it may contain slashes. def interpolation_chunk(value_code = local_name) "\#{#{value_code}.to_s}" @@ -275,6 +293,10 @@ module ActionController regexp || "(.*)" end + def number_of_captures + regexp ? regexp.number_of_captures : 1 + end + def optionality_implied? true end diff --git a/actionpack/lib/action_controller/session/active_record_store.rb b/actionpack/lib/action_controller/session/active_record_store.rb index 1e8eb57acb..fadf2a6b32 100644 --- a/actionpack/lib/action_controller/session/active_record_store.rb +++ b/actionpack/lib/action_controller/session/active_record_store.rb @@ -56,6 +56,8 @@ class CGI class ActiveRecordStore # The default Active Record class. class Session < ActiveRecord::Base + ## + # :singleton-method: # Customizable data column name. Defaults to 'data'. cattr_accessor :data_column_name self.data_column_name = 'data' @@ -166,17 +168,25 @@ class CGI # binary session data in a +text+ column. For higher performance, # store in a +blob+ column instead and forgo the Base64 encoding. class SqlBypass + ## + # :singleton-method: # Use the ActiveRecord::Base.connection by default. cattr_accessor :connection + ## + # :singleton-method: # The table name defaults to 'sessions'. cattr_accessor :table_name @@table_name = 'sessions' + ## + # :singleton-method: # The session id field defaults to 'session_id'. cattr_accessor :session_id_column @@session_id_column = 'session_id' + ## + # :singleton-method: # The data field defaults to 'data'. cattr_accessor :data_column @@data_column = 'data' diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 7a31f0e8d5..1e3a646bc9 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -395,6 +395,7 @@ module ActionController #:nodoc: @html_document = nil @request.env['REQUEST_METHOD'] ||= "GET" + @request.action = action.to_s parameters ||= {} diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb index 12c8405101..ae20f9947c 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb @@ -160,7 +160,7 @@ module HTML if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value) node.attributes.delete(attr_name) else - node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(value) + node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value)) end end end diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 288b62778e..126d16e5f4 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 2 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 945246a39a..a8ca0f685f 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -183,13 +183,17 @@ module ActionView #:nodoc: @@exempt_from_layout.merge(regexps) end + @@debug_rjs = false + ## + # :singleton-method: # Specify whether RJS responses should be wrapped in a try/catch block # that alert()s the caught exception (and then re-raises it). - @@debug_rjs = false cattr_accessor :debug_rjs - # A warning will be displayed whenever an action results in a cache miss on your view paths. @@warn_cache_misses = false + ## + # :singleton-method: + # A warning will be displayed whenever an action results in a cache miss on your view paths. cattr_accessor :warn_cache_misses attr_internal :request diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index ccb7df212a..cd25684940 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -1,3 +1,5 @@ +require 'set' + # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERb or any other # template languages). module ActionView @@ -121,6 +123,8 @@ module ActionView end class AtomBuilder + XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set + def initialize(xml) @xml = xml end @@ -140,14 +144,15 @@ module ActionView @xml.__send__(method, *arguments, &block) end end - + # True if the method name matches one of the five elements defined # in the Atom spec as potentially containing XHTML content and # if :type => 'xhtml' is, in fact, specified. def xhtml_block?(method, arguments) - %w( content rights title subtitle summary ).include?(method.to_s) && - arguments.last.respond_to?(:[]) && - arguments.last[:type].to_s == 'xhtml' + if XHTML_TAG_NAMES.include?(method.to_s) + last = arguments.last + last.is_a?(Hash) && last[:type].to_s == 'xhtml' + end end end diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index de08672d2d..d37ca766af 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -133,10 +133,12 @@ module ActionView unless options.blank? attrs = [] if escape - options.each do |key, value| - next unless value - value = BOOLEAN_ATTRIBUTES.include?(key) ? key : escape_once(value) - attrs << %(#{key}="#{value}") + options.each_pair do |key, value| + if BOOLEAN_ATTRIBUTES.include?(key) + attrs << %(#{key}="#{key}") if value + else + attrs << %(#{key}="#{escape_once(value)}") if !value.nil? + end end else attrs = options.map { |key, value| %(#{key}="#{value}") } diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index d80e7c6e57..36f7575652 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -559,7 +559,7 @@ module ActionView (?:\.[-\w]+)* # remaining subdomains or domain (?::\d+)? # port (?:/(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))*)* # path - (?:\?[\w\+@%&=.;-]+)? # query string + (?:\?[\w\+@%&=.;:-]+)? # query string (?:\#[\w\-]*)? # trailing anchor ) ([[:punct:]]|<|$|) # trailing text @@ -598,4 +598,4 @@ module ActionView end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index 3def949f1e..0fec1e0b18 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -42,6 +42,8 @@ module ActionView class ERB < TemplateHandler include Compilable + ## + # :singleton-method: # Specify trim mode for the ERB compiler. Defaults to '-'. # See ERb documentation for suitable values. cattr_accessor :erb_trim_mode |