diff options
Diffstat (limited to 'actionpack/lib/action_dispatch')
18 files changed, 252 insertions, 165 deletions
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index fa2948c8db..8a9f9c4315 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -256,6 +256,10 @@ module Mime @@html_types.include?(to_sym) || @string =~ /html/ end + def respond_to?(method, include_private = false) #:nodoc: + super || method.to_s =~ /(\w+)\?$/ + end + private def method_missing(method, *args) if method.to_s =~ /(\w+)\?$/ diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index b80574f497..7a5237dcf3 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -29,9 +29,9 @@ module ActionDispatch ENV_METHODS.each do |env| class_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{env.sub(/^HTTP_/n, '').downcase} - @env["#{env}"] - end + def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset + @env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"] + end # end METHOD end @@ -177,6 +177,16 @@ module ActionDispatch @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s end + # Returns the unique request id, which is based off either the X-Request-Id header that can + # be generated by a firewall, load balancer, or web server or by the RequestId middleware + # (which sets the action_dispatch.request_id environment variable). + # + # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging. + # This relies on the rack variable set by the ActionDispatch::RequestId middleware. + def uuid + @uuid ||= env["action_dispatch.request_id"] + end + # Returns the lowercase name of the HTTP server software. def server_software (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb index a15ad28f16..94fa747a79 100644 --- a/actionpack/lib/action_dispatch/http/upload.rb +++ b/actionpack/lib/action_dispatch/http/upload.rb @@ -11,24 +11,13 @@ module ActionDispatch raise(ArgumentError, ':tempfile is required') unless @tempfile end - def open - @tempfile.open - end - - def path - @tempfile.path - end - def read(*args) @tempfile.read(*args) end - def rewind - @tempfile.rewind - end - - def size - @tempfile.size + # Delegate these methods to the tempfile. + [:open, :path, :rewind, :size].each do |method| + class_eval "def #{method}; @tempfile.#{method}; end" end private diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 8487b0fc8c..c8ddd07bfa 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -45,7 +45,7 @@ module ActionDispatch rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path) rewritten_url << "?#{params.to_query}" unless params.empty? - rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor] + rewritten_url << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor] rewritten_url end @@ -64,14 +64,16 @@ module ActionDispatch end def host_or_subdomain_and_domain(options) - return options[:host] unless options[:subdomain] || options[:domain] + return options[:host] if options[:subdomain].nil? && options[:domain].nil? tld_length = options[:tld_length] || @@tld_length host = "" - host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)) - host << "." - host << (options[:domain] || extract_domain(options[:host], tld_length)) + unless options[:subdomain] == false + host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)) + host << "." + end + host << (options[:domain] || extract_domain(options[:host], tld_length)) host end end diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 1c312f2587..a4ffd40a66 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -85,6 +85,7 @@ module ActionDispatch class CookieOverflow < StandardError; end class CookieJar #:nodoc: + include Enumerable # This regular expression is used to split the levels of a domain. # The top level domain can be any string without a period or @@ -124,6 +125,10 @@ module ActionDispatch alias :closed? :closed def close!; @closed = true end + def each(&block) + @cookies.each(&block) + end + # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. def [](name) @cookies[name.to_s] @@ -169,7 +174,7 @@ module ActionDispatch options = { :value => value } end - value = @cookies[key.to_s] = value + @cookies[key.to_s] = value handle_options(options) diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 2adbce031b..e59404ef68 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -70,6 +70,10 @@ module ActionDispatch end end + # Implementation detail: please do not change the signature of the + # FlashHash class. Doing that will likely affect all Rails apps in + # production as the FlashHash currently stored in their sessions will + # become invalid. class FlashHash include Enumerable diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb new file mode 100644 index 0000000000..bee446c8a5 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/request_id.rb @@ -0,0 +1,39 @@ +require 'securerandom' +require 'active_support/core_ext/string/access' +require 'active_support/core_ext/object/blank' + +module ActionDispatch + # Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through + # ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header. + # + # The unique request id is either based off the X-Request-Id header in the request, which would typically be generated + # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the + # header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only. + # + # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files + # from multiple pieces of the stack. + class RequestId + def initialize(app) + @app = app + end + + def call(env) + env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id + status, headers, body = @app.call(env) + + headers["X-Request-Id"] = env["action_dispatch.request_id"] + [ status, headers, body ] + end + + private + def external_request_id(env) + if request_id = env["HTTP_X_REQUEST_ID"].presence + request_id.gsub(/[^\w\-]/, "").first(255) + end + end + + def internal_request_id + SecureRandom.hex(16) + end + end +end diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index a70d814749..6bcf099d2c 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -59,7 +59,10 @@ module ActionDispatch # Note that the regexp does not allow $1 to end with a ':' $1.constantize rescue LoadError, NameError => const_error - raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n" + raise ActionDispatch::Session::SessionRestoreError, + "Session contains objects whose class definition isn't available.\n" + + "Remember to require the classes for all objects kept in the session.\n" + + "(Original exception: #{const_error.message} [#{const_error.class}])\n" end retry else diff --git a/actionpack/lib/action_dispatch/middleware/session/cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb new file mode 100644 index 0000000000..d3b6fd12fa --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb @@ -0,0 +1,50 @@ +require 'action_dispatch/middleware/session/abstract_store' +require 'rack/session/memcache' + +module ActionDispatch + module Session + # Session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful + # if you don't store critical data in your sessions and you don't need them to live for extended periods + # of time. + class CacheStore < AbstractStore + # Create a new store. The cache to use can be passed in the <tt>:cache</tt> option. If it is + # not specified, <tt>Rails.cache</tt> will be used. + def initialize(app, options = {}) + @cache = options[:cache] || Rails.cache + options[:expire_after] ||= @cache.options[:expires_in] + super + end + + # Get a session from the cache. + def get_session(env, sid) + sid ||= generate_sid + session = @cache.read(cache_key(sid)) + session ||= {} + [sid, session] + end + + # Set a session in the cache. + def set_session(env, sid, session, options) + key = cache_key(sid) + if session + @cache.write(key, session, :expires_in => options[:expire_after]) + else + @cache.delete(key) + end + sid + end + + # Remove a session from the cache. + def destroy_session(env, sid, options) + @cache.delete(cache_key(sid)) + generate_sid + end + + private + # Turn the session id into a cache key. + def cache_key(sid) + "_session_id:#{sid}" + end + end + end +end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index a765c23dae..2fa68c64c5 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -86,8 +86,8 @@ module ActionDispatch :framework_trace => framework_trace(exception), :full_trace => full_trace(exception) ) - file = "rescues/#{@@rescue_templates[exception.class.name]}.erb" - body = template.render(:file => file, :layout => 'rescues/layout.erb') + file = "rescues/#{@@rescue_templates[exception.class.name]}" + body = template.render(:template => file, :layout => 'rescues/layout') render(status_code(exception), body) end diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index f51cc3711b..1af89858d1 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -1,10 +1,9 @@ require "action_dispatch" -require "rails" module ActionDispatch class Railtie < Rails::Railtie config.action_dispatch = ActiveSupport::OrderedOptions.new - config.action_dispatch.x_sendfile_header = "" + config.action_dispatch.x_sendfile_header = nil config.action_dispatch.ip_spoofing_check = true config.action_dispatch.show_exceptions = true config.action_dispatch.best_standards_support = true diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index a5c1501f61..970236a05a 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -49,7 +49,7 @@ module ActionDispatch class Mapping #:nodoc: IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix] ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z} - SHORTHAND_REGEX = %r{^/[\w/]+$} + SHORTHAND_REGEX = %r{/[\w/]+$} WILDCARD_PATH = %r{\*([^/\)]+)\)?$} def initialize(set, scope, path, options) @@ -70,7 +70,7 @@ module ActionDispatch if using_match_shorthand?(path_without_format, @options) to_shorthand = @options[:to].blank? - @options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1') + @options[:to] ||= path_without_format.gsub(/\(.*\)/, "")[1..-1].sub(%r{/([^/]*)$}, '#\1') end @options.merge!(default_controller_and_action(to_shorthand)) @@ -90,7 +90,7 @@ module ActionDispatch # match "account/overview" def using_match_shorthand?(path, options) - path && options.except(:via, :anchor, :to, :as).empty? && path =~ SHORTHAND_REGEX + path && (options[:to] || options[:action]).nil? && path =~ SHORTHAND_REGEX end def normalize_path(path) @@ -213,8 +213,8 @@ module ActionDispatch end def segment_keys - @segment_keys ||= Rack::Mount::RegexpWithNamedGroups.new( - Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS) + @segment_keys ||= Journey::Path::Pattern.new( + Journey::Router::Strexp.compile(@path, requirements, SEPARATORS) ).names end @@ -235,7 +235,7 @@ module ActionDispatch # (:locale) becomes (/:locale) instead of /(:locale). Except # for root cases, where the latter is the correct one. def self.normalize_path(path) - path = Rack::Mount::Utils.normalize_path(path) + path = Journey::Router::Utils.normalize_path(path) path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^/]+\)$} path end @@ -285,7 +285,7 @@ module ActionDispatch # A pattern can also point to a +Rack+ endpoint i.e. anything that # responds to +call+: # - # match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon" } + # match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon"] } # match 'photos/:id' => PhotoRackApp # # Yes, controller actions are just rack endpoints # match 'photos/:id' => PhotosController.action(:show) @@ -452,7 +452,9 @@ module ActionDispatch prefix_options = options.slice(*_route.segment_keys) # we must actually delete prefix segment keys to avoid passing them to next url_for _route.segment_keys.each { |k| options.delete(k) } - _routes.url_helpers.send("#{name}_path", prefix_options) + prefix = _routes.url_helpers.send("#{name}_path", prefix_options) + prefix = '' if prefix == '/' + prefix end end end @@ -694,7 +696,7 @@ module ActionDispatch # Allows you to constrain the nested routes based on a set of rules. # For instance, in order to change the routes to allow for a dot character in the +id+ parameter: # - # constraints(:id => /\d+\.\d+) do + # constraints(:id => /\d+\.\d+/) do # resources :posts # end # @@ -704,7 +706,7 @@ module ActionDispatch # You may use this to also restrict other parameters: # # resources :posts do - # constraints(:post_id => /\d+\.\d+) do + # constraints(:post_id => /\d+\.\d+/) do # resources :comments # end # end @@ -733,7 +735,7 @@ module ActionDispatch # if the user should be given access to that route, or +false+ if the user should not. # # class Iphone - # def self.matches(request) + # def self.matches?(request) # request.env["HTTP_USER_AGENT"] =~ /iPhone/ # end # end @@ -1021,6 +1023,7 @@ module ActionDispatch # creates seven different routes in your application, all mapping to # the +Photos+ controller: # + # GET /photos # GET /photos/new # POST /photos # GET /photos/:id @@ -1036,12 +1039,13 @@ module ActionDispatch # # This generates the following comments routes: # - # GET /photos/:id/comments/new - # POST /photos/:id/comments - # GET /photos/:id/comments/:id - # GET /photos/:id/comments/:id/edit - # PUT /photos/:id/comments/:id - # DELETE /photos/:id/comments/:id + # GET /photos/:photo_id/comments + # GET /photos/:photo_id/comments/new + # POST /photos/:photo_id/comments + # GET /photos/:photo_id/comments/:id + # GET /photos/:photo_id/comments/:id/edit + # PUT /photos/:photo_id/comments/:id + # DELETE /photos/:photo_id/comments/:id # # === Options # Takes same options as <tt>Base#match</tt> as well as: @@ -1436,7 +1440,7 @@ module ActionDispatch name_prefix = @scope[:as] if parent_resource - return nil if as.nil? && action.nil? + return nil unless as || action collection_name = parent_resource.collection_name member_name = parent_resource.member_name @@ -1463,9 +1467,9 @@ module ActionDispatch end module Shorthand #:nodoc: - def match(*args) - if args.size == 1 && args.last.is_a?(Hash) - options = args.pop + def match(path, *rest) + if rest.empty? && Hash === path + options = path path, to = options.find { |name, value| name.is_a?(String) } options.merge!(:to => to).delete(path) super(path, options) diff --git a/actionpack/lib/action_dispatch/routing/route.rb b/actionpack/lib/action_dispatch/routing/route.rb deleted file mode 100644 index 10b3d38346..0000000000 --- a/actionpack/lib/action_dispatch/routing/route.rb +++ /dev/null @@ -1,60 +0,0 @@ -module ActionDispatch - module Routing - class Route #:nodoc: - attr_reader :app, :conditions, :defaults, :name - attr_reader :path, :requirements, :set - - def initialize(set, app, conditions, requirements, defaults, name, anchor) - @set = set - @app = app - @defaults = defaults - @name = name - - # FIXME: we should not be doing this much work in a constructor. - - @requirements = requirements.merge(defaults) - @requirements.delete(:controller) if @requirements[:controller].is_a?(Regexp) - @requirements.delete_if { |k, v| - v == Regexp.compile("[^#{SEPARATORS.join}]+") - } - - if path = conditions[:path_info] - @path = path - conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS, anchor) - end - - @verbs = conditions[:request_method] || [] - - @conditions = conditions.dup - - # 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. - @conditions[:request_method] = %r[^#{verb}$] unless @verbs.empty? - @conditions[:path_info] = Rack::Mount::RegexpWithNamedGroups.new(@conditions[:path_info]) if @conditions[:path_info] - @conditions.delete_if{ |k,v| k != :path_info && !valid_condition?(k) } - @requirements.delete_if{ |k,v| !valid_condition?(k) } - end - - def verb - @verbs.join '|' - end - - def segment_keys - @segment_keys ||= conditions[:path_info].names.compact.map { |key| key.to_sym } - end - - def to_s - @to_s ||= begin - "%-6s %-40s %s" % [(verb || :any).to_s.upcase, path, requirements.inspect] - end - end - - private - def valid_condition?(method) - segment_keys.include?(method) || set.valid_conditions.include?(method) - end - end - end -end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 11228c597d..2bcde16110 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -1,4 +1,4 @@ -require 'rack/mount' +require 'journey' require 'forwardable' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/to_query' @@ -37,7 +37,7 @@ module ActionDispatch # If this is a default_controller (i.e. a controller specified by the user) # we should raise an error in case it's not found, because it usually means - # an user error. However, if the controller was retrieved through a dynamic + # a user error. However, if the controller was retrieved through a dynamic # segment, as in :controller(/:action), we should simply return nil and # delegate the control back to Rack cascade. Besides, if this is not a default # controller, it means we should respect the @scope[:module] parameter. @@ -165,13 +165,14 @@ module ActionDispatch remove_possible_method :#{selector} def #{selector}(*args) options = args.extract_options! + result = #{options.inspect} if args.any? - options[:_positional_args] = args - options[:_positional_keys] = #{route.segment_keys.inspect} + result[:_positional_args] = args + result[:_positional_keys] = #{route.segment_keys.inspect} end - options ? #{options.inspect}.merge(options) : #{options.inspect} + result.merge(options) end protected :#{selector} END_EVAL @@ -205,29 +206,42 @@ module ActionDispatch end end - attr_accessor :set, :routes, :named_routes, :default_scope + 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 + alias :routes :set + def self.default_resources_path_names { :new => 'new', :edit => 'edit' } end def initialize(request_class = ActionDispatch::Request) - self.routes = [] 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 - self.valid_conditions = request_class.public_instance_methods.map { |m| m.to_sym } + @valid_conditions = {} + + request_class.public_instance_methods.each { |m| + @valid_conditions[m.to_sym] = true + } + @valid_conditions[:controller] = true + @valid_conditions[:action] = true + self.valid_conditions.delete(:id) - self.valid_conditions.push(:controller, :action) - @append = [] - @prepend = [] + @append = [] + @prepend = [] @disable_clear_and_finalize = false - clear! + @finalized = false + + @set = Journey::Routes.new + @router = Journey::Router.new(@set, { + :parameters_key => PARAMETERS_KEY, + :request_class => request_class}) + @formatter = Journey::Formatter.new @set end def draw(&block) @@ -263,17 +277,13 @@ module ActionDispatch return if @finalized @append.each { |blk| eval_block(blk) } @finalized = true - @set.freeze end def clear! @finalized = false - routes.clear named_routes.clear - @set = ::Rack::Mount::RouteSet.new( - :parameters_key => PARAMETERS_KEY, - :request_class => request_class - ) + set.clear + formatter.clear @prepend.each { |blk| eval_block(blk) } end @@ -341,26 +351,54 @@ module ActionDispatch def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true) raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i) - route = Route.new(self, app, conditions, requirements, defaults, name, anchor) - @set.add_route(route.app, route.conditions, route.defaults, route.name) + + path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor) + conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym }) + + route = @set.add_route(app, path, conditions, defaults, name) named_routes[name] = route if name - routes << route route end + def build_path(path, requirements, separators, anchor) + strexp = Journey::Router::Strexp.new( + path, + requirements, + SEPARATORS, + anchor) + + Journey::Path::Pattern.new(strexp) + end + private :build_path + + def build_conditions(current_conditions, req_predicates, 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. + unless verbs.empty? + conditions[:request_method] = %r[^#{verbs.join('|')}$] + end + conditions.delete_if { |k,v| !(req_predicates.include?(k) || path_values.include?(k)) } + + conditions + end + private :build_conditions + class Generator #:nodoc: - PARAMETERIZE = { - :parameterize => lambda do |name, value| - if name == :controller - value - elsif value.is_a?(Array) - value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/') - else - return nil unless param = value.to_param - param.split('/').map { |v| Rack::Mount::Utils.escape_uri(v) }.join("/") - end + PARAMETERIZE = lambda do |name, value| + if name == :controller + value + elsif value.is_a?(Array) + value.map { |v| v.to_param }.join('/') + elsif param = value.to_param + param end - } + end attr_reader :options, :recall, :set, :named_route @@ -450,14 +488,14 @@ module ActionDispatch end def generate - path, params = @set.set.generate(:path_info, named_route, options, recall, PARAMETERIZE) + path, params = @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE) raise_routing_error unless path return [path, params.keys] if @extras [path, params] - rescue Rack::Mount::RoutingError + rescue Journey::Router::RoutingError raise_routing_error end @@ -529,12 +567,12 @@ module ActionDispatch def call(env) finalize! - @set.call(env) + @router.call(env) end def recognize_path(path, environment = {}) method = (environment[:method] || "GET").to_s.upcase - path = Rack::Mount::Utils.normalize_path(path) unless path =~ %r{://} + path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://} begin env = Rack::MockRequest.env_for(path, {:method => method}) @@ -543,7 +581,7 @@ module ActionDispatch end req = @request_class.new(env) - @set.recognize(req) do |route, matches, params| + @router.recognize(req) do |route, matches, params| params.each do |key, value| if value.is_a?(String) value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware? diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index 480144fe9d..8fc8dc191b 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -116,9 +116,10 @@ module ActionDispatch # If <tt>:only_path</tt> is false, this option must be # provided either explicitly, or via +default_url_options+. # * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+ - # to split the domain from the host. - # * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+ # to split the subdomain from the host. + # If false, removes all subdomains from the host part of the link. + # * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+ + # to split the domain from the host. # * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if # <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to # <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1. @@ -131,16 +132,20 @@ 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 options when nil, Hash - _routes.url_for((options || {}).reverse_merge!(url_options).symbolize_keys) + _routes.url_for((options || {}).reverse_merge(url_options).symbolize_keys) else polymorphic_url(options) end diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb index 5fa91d1a76..b4555f4f59 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb @@ -415,9 +415,9 @@ module ActionDispatch assert !deliveries.empty?, "No e-mail in delivery list" for delivery in deliveries - for part in delivery.parts + for part in (delivery.parts.empty? ? [delivery] : delivery.parts) if part["Content-Type"].to_s =~ /^text\/html\W/ - root = HTML::Document.new(part.body).root + root = HTML::Document.new(part.body.to_s).root assert_select root, ":root", &block end end diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index aae5752c93..0f1bb9f260 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -241,8 +241,8 @@ module ActionDispatch end # Performs the actual request. - def process(method, path, parameters = nil, env = nil) - env ||= {} + def process(method, path, parameters = nil, rack_env = nil) + rack_env ||= {} if path =~ %r{://} location = URI.parse(path) https! URI::HTTPS === location if location.scheme @@ -258,7 +258,7 @@ module ActionDispatch hostname, port = host.split(':') - default_env = { + env = { :method => method, :params => parameters, @@ -276,7 +276,7 @@ module ActionDispatch session = Rack::Test::Session.new(_mock_session) - env.reverse_merge!(default_env) + env.merge!(rack_env) # NOTE: rack-test v0.5 doesn't build a default uri correctly # Make sure requested path is always a full uri diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb index f668b81b45..b08ff41950 100644 --- a/actionpack/lib/action_dispatch/testing/test_process.rb +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -5,12 +5,7 @@ require 'active_support/core_ext/hash/indifferent_access' module ActionDispatch module TestProcess def assigns(key = nil) - assigns = {}.with_indifferent_access - @controller.instance_variable_names.each do |ivar| - next if ActionController::Base.protected_instance_variables.include?(ivar) - assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar) - end - + assigns = @controller.view_assigns.with_indifferent_access key.nil? ? assigns : assigns[key] end |
