diff options
Diffstat (limited to 'actionpack/lib/action_dispatch')
| -rw-r--r-- | actionpack/lib/action_dispatch/http/mime_negotiation.rb | 21 | ||||
| -rw-r--r-- | actionpack/lib/action_dispatch/http/parameters.rb | 5 | ||||
| -rw-r--r-- | actionpack/lib/action_dispatch/http/request.rb | 11 | ||||
| -rw-r--r-- | actionpack/lib/action_dispatch/http/response.rb | 9 | ||||
| -rw-r--r-- | actionpack/lib/action_dispatch/http/url.rb | 6 | ||||
| -rw-r--r-- | actionpack/lib/action_dispatch/railtie.rb | 9 | ||||
| -rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 67 | ||||
| -rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 63 | ||||
| -rw-r--r-- | actionpack/lib/action_dispatch/testing/integration.rb | 2 |
9 files changed, 153 insertions, 40 deletions
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index e31f3b823d..0f98e84788 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -80,6 +80,27 @@ module ActionDispatch @env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])] end + # Sets the \formats by string extensions. This differs from #format= by allowing you + # to set multiple, ordered formats, which is useful when you want to have a fallback. + # + # In this example, the :iphone format will be used if it's available, otherwise it'll fallback + # to the :html format. + # + # class ApplicationController < ActionController::Base + # before_filter :adjust_format_for_iphone_with_html_fallback + # + # private + # def adjust_format_for_iphone_with_html_fallback + # request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/] + # end + # end + def formats=(extensions) + parameters[:format] = extensions.first.to_s + @env["action_dispatch.request.formats"] = extensions.collect do |extension| + Mime::Type.lookup_by_extension(extension) + end + end + # Receives an array of mimes and return the first user sent mime that # matches the order array. # diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index bcfd0b0d00..9a7b5bc8c7 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -4,6 +4,11 @@ require 'active_support/core_ext/hash/indifferent_access' module ActionDispatch module Http module Parameters + def initialize(env) + super + @symbolized_path_params = nil + end + # Returns both GET and POST \parameters in a single hash. def parameters @env["action_dispatch.request.parameters"] ||= begin diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 1377e53ce8..d24c7c7f3f 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -38,6 +38,17 @@ module ActionDispatch METHOD end + def initialize(env) + super + @method = nil + @request_method = nil + @remote_ip = nil + @original_fullpath = nil + @fullpath = nil + @ip = nil + @uuid = nil + end + def key?(key) @env.key?(key) end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index d336808e7c..11b7534ea4 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -58,6 +58,7 @@ module ActionDispatch # :nodoc: LOCATION = "Location".freeze cattr_accessor(:default_charset) { "utf-8" } + cattr_accessor(:default_headers) include Rack::Response::Helpers include ActionDispatch::Http::Cache::Response @@ -96,6 +97,8 @@ module ActionDispatch # :nodoc: def initialize(status = 200, header = {}, body = []) super() + header = merge_default_headers(header, self.class.default_headers) + self.body, self.header, self.status = body, header, status @sending_file = false @@ -238,6 +241,12 @@ module ActionDispatch # :nodoc: private + def merge_default_headers(original, default) + return original unless default.respond_to?(:merge) + + default.merge(original) + end + def build_buffer(response, body) Buffer.new response, body end diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 4266ec042e..8aa02ec482 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -87,6 +87,12 @@ module ActionDispatch end end + def initialize(env) + super + @protocol = nil + @port = nil + end + # Returns the complete URL used for this request. def url protocol + host_with_port + fullpath diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 62f906219c..ccc0435a39 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -19,10 +19,19 @@ module ActionDispatch :verbose => false } + config.action_dispatch.default_headers = { + 'X-Frame-Options' => 'SAMEORIGIN', + 'X-XSS-Protection' => '1; mode=block', + 'X-Content-Type-Options' => 'nosniff' + } + + config.eager_load_namespaces << ActionDispatch + initializer "action_dispatch.configure" do |app| ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding + ActionDispatch::Response.default_headers = app.config.action_dispatch.default_headers ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses) ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 5e2f1ff1e0..f64cff8394 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -444,9 +444,10 @@ module ActionDispatch raise "A rack application must be specified" unless path - options[:as] ||= app_name(app) + options[:as] ||= app_name(app) + options[:via] ||= :all - match(path, options.merge(:to => app, :anchor => false, :format => false, :via => :all)) + match(path, options.merge(:to => app, :anchor => false, :format => false)) define_generate_prefix(app, options[:as]) self @@ -909,7 +910,7 @@ module ActionDispatch # CANONICAL_ACTIONS holds all actions that does not need a prefix or # a path appended since they fit properly in their scope level. VALID_ON_OPTIONS = [:new, :collection, :member] - RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param] + RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns] CANONICAL_ACTIONS = %w(index create new show update destroy) class Resource #:nodoc: @@ -1046,6 +1047,8 @@ module ActionDispatch resource_scope(:resource, SingletonResource.new(resources.pop, options)) do yield if block_given? + concerns(options[:concerns]) if options[:concerns] + collection do post :create end if parent_resource.actions.include?(:create) @@ -1210,6 +1213,8 @@ module ActionDispatch resource_scope(:resources, Resource.new(resources.pop, options)) do yield if block_given? + concerns(options[:concerns]) if options[:concerns] + collection do get :index if parent_resource.actions.include?(:index) post :create if parent_resource.actions.include?(:create) @@ -1580,15 +1585,71 @@ module ActionDispatch end end + # Routing Concerns allows you to declare common routes that can be reused + # inside others resources and routes. + # + # concern :commentable do + # resources :comments + # end + # + # concern :image_attachable do + # resources :images, only: :index + # end + # + # These concerns are used in Resources routing: + # + # resources :messages, concerns: [:commentable, :image_attachable] + # + # or in a scope or namespace: + # + # namespace :posts do + # concerns :commentable + # end + module Concerns + # Define a routing concern using a name. + # + # concern :commentable do + # resources :comments + # end + # + # Any routing helpers can be used inside a concern. + def concern(name, &block) + @concerns[name] = block + end + + # Use the named concerns + # + # resources :posts do + # concerns :commentable + # end + # + # concerns also work in any routes helper that you want to use: + # + # namespace :posts do + # concerns :commentable + # end + def concerns(*names) + names.flatten.each do |name| + if concern = @concerns[name] + instance_eval(&concern) + else + raise ArgumentError, "No concern named #{name} was found!" + end + end + end + end + def initialize(set) #:nodoc: @set = set @scope = { :path_names => @set.resources_path_names } + @concerns = {} end include Base include HttpHelpers include Redirection include Scoping + include Concerns include Resources end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 6bb15ba3b6..32d267d1d6 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -161,19 +161,12 @@ module ActionDispatch end private - def url_helper_name(name, only_path) - if only_path - :"#{name}_path" - else - :"#{name}_url" - end - end def define_named_route_methods(name, route) - [true, false].each do |only_path| - hash = route.defaults.merge(:use_route => name, :only_path => only_path) - define_url_helper route, name, hash - end + define_url_helper route, :"#{name}_path", + route.defaults.merge(:use_route => name, :only_path => true) + define_url_helper route, :"#{name}_url", + route.defaults.merge(:use_route => name, :only_path => false) end # Create a url helper allowing ordered parameters to be associated @@ -190,11 +183,9 @@ module ActionDispatch # foo_url(bar, baz, bang, :sort_by => 'baz') # def define_url_helper(route, name, options) - selector = url_helper_name(name, options[:only_path]) - @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - remove_possible_method :#{selector} - def #{selector}(*args) + remove_possible_method :#{name} + def #{name}(*args) if #{optimize_helper?(route)} && args.size == #{route.required_parts.size} && !args.last.is_a?(Hash) && optimize_routes_generation? options = #{options.inspect} options.merge!(url_options) if respond_to?(:url_options) @@ -206,7 +197,7 @@ module ActionDispatch end END_EVAL - helpers << selector + helpers << name end # Clause check about when we need to generate an optimized helper. @@ -235,7 +226,7 @@ module ActionDispatch attr_accessor :formatter, :set, :named_routes, :default_scope, :router attr_accessor :disable_clear_and_finalize, :resources_path_names - attr_accessor :default_url_options, :request_class, :valid_conditions + attr_accessor :default_url_options, :request_class alias :routes :set @@ -247,13 +238,7 @@ module ActionDispatch self.named_routes = NamedRouteCollection.new self.resources_path_names = self.class.default_resources_path_names.dup self.default_url_options = {} - self.request_class = request_class - @valid_conditions = { :controller => true, :action => true } - request_class.public_instance_methods.each { |m| - @valid_conditions[m] = true - } - @valid_conditions.delete(:id) @append = [] @prepend = [] @@ -384,7 +369,7 @@ module ActionDispatch raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i) path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor) - conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym }) + conditions = build_conditions(conditions, path.names.map { |x| x.to_sym }) route = @set.add_route(app, path, conditions, defaults, name) named_routes[name] = route if name && !named_routes[name] @@ -421,21 +406,22 @@ module ActionDispatch end private :build_path - def build_conditions(current_conditions, req_predicates, path_values) + def build_conditions(current_conditions, path_values) conditions = current_conditions.dup - verbs = conditions[:request_method] || [] - # Rack-Mount requires that :request_method be a regular expression. # :request_method represents the HTTP verb that matches this route. # # Here we munge values before they get sent on to rack-mount. + verbs = conditions[:request_method] || [] unless verbs.empty? conditions[:request_method] = %r[^#{verbs.join('|')}$] end - conditions.delete_if { |k,v| !(req_predicates.include?(k) || path_values.include?(k)) } - conditions + conditions.keep_if do |k, _| + k == :action || k == :controller || + @request_class.public_method_defined?(k) || path_values.include?(k) + end end private :build_conditions @@ -476,9 +462,7 @@ module ActionDispatch def use_recall_for(key) if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key]) - if named_route_exists? - @options[key] = @recall.delete(key) if segment_keys.include?(key) - else + if !named_route_exists? || segment_keys.include?(key) @options[key] = @recall.delete(key) end end @@ -588,7 +572,8 @@ module ActionDispatch end RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length, - :trailing_slash, :anchor, :params, :only_path, :script_name] + :trailing_slash, :anchor, :params, :only_path, :script_name, + :original_script_name] def mounted? false @@ -607,13 +592,19 @@ module ActionDispatch options = default_url_options.merge(options || {}) user, password = extract_authentication(options) - path_segments = options.delete(:_path_segments) - script_name = options.delete(:script_name).presence || _generate_prefix(options) + recall = options.delete(:_recall) + + original_script_name = options.delete(:original_script_name).presence + script_name = options.delete(:script_name).presence || _generate_prefix(options) + + if script_name && original_script_name + script_name = original_script_name + script_name + end path_options = options.except(*RESERVED_OPTIONS) path_options = yield(path_options) if block_given? - path, params = generate(path_options, path_segments || {}) + path, params = generate(path_options, recall || {}) params.merge!(options[:params] || {}) ActionDispatch::Http::URL.url_for(options.merge!({ diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 2b3095a234..ab584abf68 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -345,7 +345,7 @@ module ActionDispatch define_method(method) do |*args| reset! unless integration_session # reset the html_document variable, but only for new get/post calls - @html_document = nil unless method.in?(["cookies", "assigns"]) + @html_document = nil unless method == 'cookies' || method == 'assigns' integration_session.__send__(method, *args).tap do copy_session_variables! end |
