diff options
Diffstat (limited to 'actionpack/lib')
55 files changed, 480 insertions, 276 deletions
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index fd6a46fbec..43cea3b79e 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -42,8 +42,8 @@ module AbstractController controller.public_instance_methods(true) end - # The list of hidden actions to an empty array. Defaults to an - # empty array. This can be modified by other modules or subclasses + # The list of hidden actions. Defaults to an empty array. + # This can be modified by other modules or subclasses # to specify particular actions as hidden. # # ==== Returns @@ -85,7 +85,7 @@ module AbstractController # Returns the full controller name, underscored, without the ending Controller. # For instance, MyApp::MyPostsController would return "my_app/my_posts" for - # controller_name. + # controller_path. # # ==== Returns # * <tt>string</tt> diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index 44c9ea34ba..c0fa28cae9 100644 --- a/actionpack/lib/abstract_controller/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb @@ -46,7 +46,7 @@ module AbstractController # callbacks. Note that skipping uses Ruby equality, so it's # impossible to skip a callback defined using an anonymous proc # using #skip_filter - def skip_filter(*names, &blk) + def skip_filter(*names) skip_before_filter(*names) skip_after_filter(*names) skip_around_filter(*names) @@ -64,7 +64,7 @@ module AbstractController # ==== Block Parameters # * <tt>name</tt> - The callback to be added # * <tt>options</tt> - A hash of options to be used when adding the callback - def _insert_callbacks(callbacks, block) + def _insert_callbacks(callbacks, block = nil) options = callbacks.last.is_a?(Hash) ? callbacks.pop : {} _normalize_callback_options(options) callbacks.push(block) if block @@ -90,7 +90,7 @@ module AbstractController ## # :method: skip_before_filter # - # :call-seq: skip_before_filter(names, block) + # :call-seq: skip_before_filter(names) # # Skip a before filter. See _insert_callbacks for parameter details. @@ -118,7 +118,7 @@ module AbstractController ## # :method: skip_after_filter # - # :call-seq: skip_after_filter(names, block) + # :call-seq: skip_after_filter(names) # # Skip an after filter. See _insert_callbacks for parameter details. @@ -146,7 +146,7 @@ module AbstractController ## # :method: skip_around_filter # - # :call-seq: skip_around_filter(names, block) + # :call-seq: skip_around_filter(names) # # Skip an around filter. See _insert_callbacks for parameter details. @@ -179,8 +179,8 @@ module AbstractController # Skip a before, after or around filter. See _insert_callbacks # for details on the allowed parameters. - def skip_#{filter}_filter(*names, &blk) # def skip_before_filter(*names, &blk) - _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| + def skip_#{filter}_filter(*names) # def skip_before_filter(*names) + _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options| skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options) end # end end # end diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index 6a6387632c..92e93cbee7 100644 --- a/actionpack/lib/abstract_controller/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -280,6 +280,10 @@ module AbstractController <<-RUBY lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first || super RUBY + else + <<-RUBY + super + RUBY end layout_definition = case _layout @@ -322,7 +326,7 @@ module AbstractController super if _include_layout?(options) - layout = options.key?(:layout) ? options.delete(:layout) : :default + layout = options.delete(:layout) { :default } options[:layout] = _layout_for_option(layout) end end diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index bd3b0b5df3..ba96735e56 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -103,8 +103,10 @@ module ActionController #:nodoc: end def _save_fragment(name, options) - content = response_body - content = content.join if content.is_a?(Array) + content = "" + response_body.each do |parts| + content << parts + end if caching_allowed? write_fragment(name, content, options) diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 1645400693..5b25a0d303 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -111,15 +111,22 @@ module ActionController # Examples: # expires_in 20.minutes # expires_in 3.hours, :public => true - # expires_in 3.hours, 'max-stale' => 5.hours, :public => true + # expires_in 3.hours, :public => true, :must_revalidate => true # # This method will overwrite an existing Cache-Control header. # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities. + # + # The method will also ensure a HTTP Date header for client compatibility. def expires_in(seconds, options = {}) #:doc: - response.cache_control.merge!(:max_age => seconds, :public => options.delete(:public)) + response.cache_control.merge!( + :max_age => seconds, + :public => options.delete(:public), + :must_revalidate => options.delete(:must_revalidate) + ) options.delete(:private) response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"} + response.date = Time.now unless response.date? end # Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index 69e37d8713..a1e40fc4e0 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -18,15 +18,29 @@ module ActionController # Force the request to this particular controller or specified actions to be # under HTTPS protocol. # - # Note that this method will not be effective on development environment. + # If you need to disable this for any reason (e.g. development) then you can use + # an +:if+ or +:unless+ condition. + # + # class AccountsController < ApplicationController + # force_ssl :if => :ssl_configured? + # + # def ssl_configured? + # !Rails.env.development? + # end + # end # # ==== Options + # * <tt>host</tt> - Redirect to a different host name # * <tt>only</tt> - The callback should be run only for this action # * <tt>except<tt> - The callback should be run for all actions except this action + # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback + # will be called only when it returns a true value. + # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback + # will be called only when it returns a false value. def force_ssl(options = {}) host = options.delete(:host) before_filter(options) do - if !request.ssl? && !Rails.env.development? + unless request.ssl? redirect_options = {:protocol => 'https://', :status => :moved_permanently} redirect_options.merge!(:host => host) if host redirect_options.merge!(:params => request.query_parameters) diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 3d46163b74..44d2f740e6 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -263,7 +263,7 @@ module ActionController # The quality of the implementation depends on a good choice. # A nonce might, for example, be constructed as the base 64 encoding of # - # => time-stamp H(time-stamp ":" ETag ":" private-key) + # time-stamp H(time-stamp ":" ETag ":" private-key) # # where time-stamp is a server-generated time or other non-repeating value, # ETag is the value of the HTTP ETag header associated with the requested entity, @@ -279,7 +279,7 @@ module ActionController # # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for - # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 + # POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 # of this document. # # The nonce is opaque to the client. Composed of Time, and hash of Time with secret @@ -293,7 +293,7 @@ module ActionController end # Might want a shorter timeout depending on whether the request - # is a PUT or POST, and if client is browser or web service. + # is a PATCH, PUT, or POST, and if client is browser or web service. # Can be much shorter if the Stale directive is implemented. This would # allow a user to use new nonce without prompting user again for their # username and password. diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 80ecc16d53..55de7e7d8e 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -280,7 +280,7 @@ module ActionController #:nodoc: if format self.content_type ||= format.to_s - lookup_context.freeze_formats([format.to_sym]) + lookup_context.formats = [format.to_sym] collector else head :not_acceptable diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index a677cdf15d..c5e7d4e357 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -14,7 +14,7 @@ module ActionController def render(*args) #:nodoc: raise ::AbstractController::DoubleRenderError if response_body super - self.content_type ||= Mime[formats.first].to_s + self.content_type ||= Mime[lookup_context.rendered_format].to_s response_body end diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index 4ad64bff20..ccda01ed44 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -53,7 +53,7 @@ module ActionController #:nodoc: # end # end # - # The same happens for PUT and DELETE requests. + # The same happens for PATCH/PUT and DELETE requests. # # === Nested resources # @@ -116,8 +116,9 @@ module ActionController #:nodoc: class Responder attr_reader :controller, :request, :format, :resource, :resources, :options - ACTIONS_FOR_VERBS = { + DEFAULT_ACTIONS_FOR_VERBS = { :post => :new, + :patch => :edit, :put => :edit } @@ -132,7 +133,7 @@ module ActionController #:nodoc: end delegate :head, :render, :redirect_to, :to => :controller - delegate :get?, :post?, :put?, :delete?, :to => :request + delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request # Undefine :to_json and :to_yaml since it's defined on Object undef_method(:to_json) if method_defined?(:to_json) @@ -259,15 +260,15 @@ module ActionController #:nodoc: resource.respond_to?(:errors) && !resource.errors.empty? end - # By default, render the <code>:edit</code> action for HTML requests with failure, unless - # the verb is POST. + # By default, render the <code>:edit</code> action for HTML requests with errors, unless + # the verb was POST. # def default_action - @action ||= ACTIONS_FOR_VERBS[request.request_method_symbol] + @action ||= DEFAULT_ACTIONS_FOR_VERBS[request.request_method_symbol] end def resource_errors - respond_to?("#{format}_resource_errors") ? send("#{format}_resource_errors") : resource.errors + respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors end def json_resource_errors diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 0b40b1fc4c..1177a703b3 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -1,7 +1,7 @@ -# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing +# 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 +# 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. # @@ -18,7 +18,7 @@ # @url = root_path # named route from the application. # end # end -# +# module ActionController module UrlFor extend ActiveSupport::Concern @@ -42,6 +42,5 @@ module ActionController @_url_options end end - end end diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index a288e69649..3e170d7872 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -6,7 +6,7 @@ require "abstract_controller/railties/routes_helpers" require "action_controller/railties/paths" module ActionController - class Railtie < Rails::Railtie + class Railtie < Rails::Railtie #:nodoc: config.action_controller = ActiveSupport::OrderedOptions.new initializer "action_controller.logger" do diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 1e226fc336..3509e74d5e 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -225,7 +225,7 @@ module ActionController # == Basic example # # Functional tests are written as follows: - # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate + # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate # an HTTP request. # 2. Then, one asserts whether the current state is as expected. "State" can be anything: # the controller's HTTP response, the database contents, etc. @@ -392,6 +392,11 @@ module ActionController process(action, "POST", *args) end + # Executes a request simulating PATCH HTTP method and set/volley the response + def patch(action, *args) + process(action, "PATCH", *args) + end + # Executes a request simulating PUT HTTP method and set/volley the response def put(action, *args) process(action, "PUT", *args) diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb index bea62b94d2..5ee4c044ea 100644 --- a/actionpack/lib/action_dispatch/http/cache.rb +++ b/actionpack/lib/action_dispatch/http/cache.rb @@ -60,6 +60,20 @@ module ActionDispatch headers[LAST_MODIFIED] = utc_time.httpdate end + def date + if date_header = headers['Date'] + Time.httpdate(date_header) + end + end + + def date? + headers.include?('Date') + end + + def date=(utc_time) + headers['Date'] = utc_time.httpdate + end + def etag=(etag) key = ActiveSupport::Cache.expand_cache_key(etag) @etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}") diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index 02a15ad599..132b0c82bc 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -50,7 +50,7 @@ module ActionDispatch end def env_filter - parameter_filter_for(Array(@env["action_dispatch.parameter_filter"]) << /RAW_POST_DATA/) + parameter_filter_for(Array(@env["action_dispatch.parameter_filter"]) + [/RAW_POST_DATA/, "rack.request.form_vars"]) end def parameter_filter_for(filters) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 0a0ebe7fad..796e0dbc45 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -17,7 +17,8 @@ module ActionDispatch include ActionDispatch::Http::Upload include ActionDispatch::Http::URL - LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, "::1", /^0:0:0:0:0:0:0:1(%.*)?$/].freeze + LOCALHOST = Regexp.union [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/] + ENV_METHODS = %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR @@ -97,6 +98,12 @@ module ActionDispatch HTTP_METHOD_LOOKUP[request_method] == :post end + # Is this a PATCH request? + # Equivalent to <tt>request.request_method == :patch</tt>. + def patch? + HTTP_METHOD_LOOKUP[request_method] == :patch + end + # Is this a PUT request? # Equivalent to <tt>request.request_method_symbol == :put</tt>. def put? @@ -244,7 +251,7 @@ module ActionDispatch # True if the request came from localhost, 127.0.0.1. def local? - LOCALHOST.any? { |local_ip| local_ip === remote_addr && local_ip === remote_ip } + LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip end private diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 80ffbe575b..f9dae5dad7 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -40,7 +40,9 @@ module ActionDispatch rewritten_url << ":#{options.delete(:port)}" if options[:port] end - path = options.delete(:path) || '' + path = "" + path << options.delete(:script_name).to_s.chomp("/") + path << options.delete(:path).to_s params = options[:params] || {} params.reject! {|k,v| v.to_param.nil? } diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb index 8c0f4052ec..338b116940 100644 --- a/actionpack/lib/action_dispatch/middleware/callbacks.rb +++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb @@ -5,7 +5,7 @@ module ActionDispatch class Callbacks include ActiveSupport::Callbacks - define_callbacks :call, :rescuable => true + define_callbacks :call class << self delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader" @@ -24,9 +24,15 @@ module ActionDispatch end def call(env) - run_callbacks :call do - @app.call(env) + error = nil + result = run_callbacks :call do + begin + @app.call(env) + rescue => error + end end + raise error if error + result end end end diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb index d5a0b80fd5..6fff94707c 100644 --- a/actionpack/lib/action_dispatch/middleware/request_id.rb +++ b/actionpack/lib/action_dispatch/middleware/request_id.rb @@ -19,10 +19,7 @@ module ActionDispatch 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 ] + @app.call(env).tap { |status, headers, body| headers["X-Request-Id"] = env["action_dispatch.request_id"] } end private diff --git a/actionpack/lib/action_dispatch/middleware/session/cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb index d3b6fd12fa..1db6194271 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb @@ -1,5 +1,4 @@ require 'action_dispatch/middleware/session/abstract_store' -require 'rack/session/memcache' module ActionDispatch module Session diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 404943d720..63b7422287 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -1,4 +1,5 @@ require 'rack/utils' +require 'active_support/core_ext/uri' module ActionDispatch class FileHandler @@ -11,14 +12,14 @@ module ActionDispatch def match?(path) path = path.dup - full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path)) + full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(unescape_path(path))) paths = "#{full_path}#{ext}" matches = Dir[paths] match = matches.detect { |m| File.file?(m) } if match match.sub!(@compiled_root, '') - match + ::Rack::Utils.escape(match) end end @@ -32,6 +33,14 @@ module ActionDispatch "{,#{ext},/index#{ext}}" end end + + def unescape_path(path) + URI.parser.unescape(path) + end + + def escape_glob_chars(path) + path.gsub(/[*?{}\[\]]/, "\\\\\\&") + end end class Static diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 35f901c575..62f906219c 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -16,7 +16,7 @@ module ActionDispatch config.action_dispatch.rack_cache = { :metastore => "rails:/", :entitystore => "rails:/", - :verbose => true + :verbose => false } initializer "action_dispatch.configure" do |app| diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 107fe80d1f..38a0270151 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -182,10 +182,13 @@ module ActionDispatch # # == HTTP Methods # - # Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method. - # Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. - # If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>. - # The default value is <tt>:any</tt> which means that the route will respond to any of the HTTP methods. + # Using the <tt>:via</tt> option when specifying a route allows you to + # restrict it to a specific HTTP method. Possible values are <tt>:post</tt>, + # <tt>:get</tt>, <tt>:patch</tt>, <tt>:put</tt>, <tt>:delete</tt> and + # <tt>:any</tt>. If your route needs to respond to more than one method you + # can use an array, e.g. <tt>[ :get, :post ]</tt>. The default value is + # <tt>:any</tt> which means that the route will respond to any of the HTTP + # methods. # # Examples: # @@ -198,7 +201,7 @@ module ActionDispatch # === HTTP helper methods # # An alternative method of specifying which HTTP method a route should respond to is to use the helper - # methods <tt>get</tt>, <tt>post</tt>, <tt>put</tt> and <tt>delete</tt>. + # methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>. # # Examples: # @@ -283,6 +286,6 @@ module ActionDispatch autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes' SEPARATORS = %w( / . ? ) #:nodoc: - HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] #:nodoc: + HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc: end end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index cf9c0d7b6a..80fcdab643 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -328,7 +328,7 @@ module ActionDispatch # +call+ or a string representing a controller's action. # # match 'path', :to => 'controller#action' - # match 'path', :to => lambda { [200, {}, "Success!"] } + # match 'path', :to => lambda { |env| [200, {}, "Success!"] } # match 'path', :to => RackApp # # [:on] @@ -446,7 +446,11 @@ module ActionDispatch _route = @set.named_routes.routes[name.to_sym] _routes = @set app.routes.define_mounted_helper(name) - app.routes.class_eval do + app.routes.singleton_class.class_eval do + define_method :mounted? do + true + end + define_method :_generate_prefix do |options| prefix_options = options.slice(*_route.segment_keys) # we must actually delete prefix segment keys to avoid passing them to next url_for @@ -465,7 +469,7 @@ module ActionDispatch # # Example: # - # get 'bacon', :to => 'food#bacon' + # get 'bacon', :to => 'food#bacon' def get(*args, &block) map_method(:get, args, &block) end @@ -475,17 +479,27 @@ module ActionDispatch # # Example: # - # post 'bacon', :to => 'food#bacon' + # post 'bacon', :to => 'food#bacon' def post(*args, &block) map_method(:post, args, &block) end + # Define a route that only recognizes HTTP PATCH. + # For supported arguments, see <tt>Base#match</tt>. + # + # Example: + # + # patch 'bacon', :to => 'food#bacon' + def patch(*args, &block) + map_method(:patch, args, &block) + end + # Define a route that only recognizes HTTP PUT. # For supported arguments, see <tt>Base#match</tt>. # # Example: # - # put 'bacon', :to => 'food#bacon' + # put 'bacon', :to => 'food#bacon' def put(*args, &block) map_method(:put, args, &block) end @@ -495,7 +509,7 @@ module ActionDispatch # # Example: # - # delete 'broccoli', :to => 'food#broccoli' + # delete 'broccoli', :to => 'food#broccoli' def delete(*args, &block) map_method(:delete, args, &block) end @@ -522,13 +536,13 @@ module ActionDispatch # This will create a number of routes for each of the posts and comments # controller. For <tt>Admin::PostsController</tt>, Rails will create: # - # GET /admin/posts - # GET /admin/posts/new - # POST /admin/posts - # GET /admin/posts/1 - # GET /admin/posts/1/edit - # PUT /admin/posts/1 - # DELETE /admin/posts/1 + # GET /admin/posts + # GET /admin/posts/new + # POST /admin/posts + # GET /admin/posts/1 + # GET /admin/posts/1/edit + # PATCH/PUT /admin/posts/1 + # DELETE /admin/posts/1 # # If you want to route /posts (without the prefix /admin) to # <tt>Admin::PostsController</tt>, you could use @@ -556,13 +570,13 @@ module ActionDispatch # not use scope. In the last case, the following paths map to # +PostsController+: # - # GET /admin/posts - # GET /admin/posts/new - # POST /admin/posts - # GET /admin/posts/1 - # GET /admin/posts/1/edit - # PUT /admin/posts/1 - # DELETE /admin/posts/1 + # GET /admin/posts + # GET /admin/posts/new + # POST /admin/posts + # GET /admin/posts/1 + # GET /admin/posts/1/edit + # PATCH/PUT /admin/posts/1 + # DELETE /admin/posts/1 module Scoping # Scopes a set of routes to the given default options. # @@ -651,13 +665,13 @@ module ActionDispatch # # This generates the following routes: # - # admin_posts GET /admin/posts(.:format) admin/posts#index - # admin_posts POST /admin/posts(.:format) admin/posts#create - # new_admin_post GET /admin/posts/new(.:format) admin/posts#new - # edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit - # admin_post GET /admin/posts/:id(.:format) admin/posts#show - # admin_post PUT /admin/posts/:id(.:format) admin/posts#update - # admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy + # admin_posts GET /admin/posts(.:format) admin/posts#index + # admin_posts POST /admin/posts(.:format) admin/posts#create + # new_admin_post GET /admin/posts/new(.:format) admin/posts#new + # edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit + # admin_post GET /admin/posts/:id(.:format) admin/posts#show + # admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update + # admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy # # === Options # @@ -972,12 +986,12 @@ module ActionDispatch # the +GeoCoders+ controller (note that the controller is named after # the plural): # - # GET /geocoder/new - # POST /geocoder - # GET /geocoder - # GET /geocoder/edit - # PUT /geocoder - # DELETE /geocoder + # GET /geocoder/new + # POST /geocoder + # GET /geocoder + # GET /geocoder/edit + # PATCH/PUT /geocoder + # DELETE /geocoder # # === Options # Takes same options as +resources+. @@ -1000,9 +1014,12 @@ module ActionDispatch end if parent_resource.actions.include?(:new) member do - get :edit if parent_resource.actions.include?(:edit) - get :show if parent_resource.actions.include?(:show) - put :update if parent_resource.actions.include?(:update) + get :edit if parent_resource.actions.include?(:edit) + get :show if parent_resource.actions.include?(:show) + if parent_resource.actions.include?(:update) + patch :update + put :update + end delete :destroy if parent_resource.actions.include?(:destroy) end end @@ -1020,13 +1037,13 @@ 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 - # GET /photos/:id/edit - # PUT /photos/:id - # DELETE /photos/:id + # GET /photos + # GET /photos/new + # POST /photos + # GET /photos/:id + # GET /photos/:id/edit + # PATCH/PUT /photos/:id + # DELETE /photos/:id # # Resources can also be nested infinitely by using this block syntax: # @@ -1036,13 +1053,13 @@ module ActionDispatch # # This generates the following comments routes: # - # 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 + # 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 + # PATCH/PUT /photos/:photo_id/comments/:id + # DELETE /photos/:photo_id/comments/:id # # === Options # Takes same options as <tt>Base#match</tt> as well as: @@ -1104,13 +1121,13 @@ module ActionDispatch # # The +comments+ resource here will have the following routes generated for it: # - # post_comments GET /posts/:post_id/comments(.:format) - # post_comments POST /posts/:post_id/comments(.:format) - # new_post_comment GET /posts/:post_id/comments/new(.:format) - # edit_comment GET /sekret/comments/:id/edit(.:format) - # comment GET /sekret/comments/:id(.:format) - # comment PUT /sekret/comments/:id(.:format) - # comment DELETE /sekret/comments/:id(.:format) + # post_comments GET /posts/:post_id/comments(.:format) + # post_comments POST /posts/:post_id/comments(.:format) + # new_post_comment GET /posts/:post_id/comments/new(.:format) + # edit_comment GET /sekret/comments/:id/edit(.:format) + # comment GET /sekret/comments/:id(.:format) + # comment PATCH/PUT /sekret/comments/:id(.:format) + # comment DELETE /sekret/comments/:id(.:format) # # === Examples # @@ -1139,9 +1156,12 @@ module ActionDispatch end if parent_resource.actions.include?(:new) member do - get :edit if parent_resource.actions.include?(:edit) - get :show if parent_resource.actions.include?(:show) - put :update if parent_resource.actions.include?(:update) + get :edit if parent_resource.actions.include?(:edit) + get :show if parent_resource.actions.include?(:show) + if parent_resource.actions.include?(:update) + patch :update + put :update + end delete :destroy if parent_resource.actions.include?(:destroy) end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 8e3975e369..30e9e5634b 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -9,6 +9,12 @@ require 'action_controller/metal/exceptions' module ActionDispatch module Routing class RouteSet #:nodoc: + # Since the router holds references to many parts of the system + # like engines, controllers and the application itself, inspecting + # the route set can actually be really slow, therefore we default + # alias inspect to to_s. + alias inspect to_s + PARAMETERS_KEY = 'action_dispatch.request.path_parameters' class Dispatcher #:nodoc: @@ -31,6 +37,7 @@ module ActionDispatch end def prepare_params!(params) + normalize_controller!(params) merge_default_action!(params) split_glob_param!(params) if @glob_param end @@ -66,6 +73,10 @@ module ActionDispatch controller.action(action).call(env) end + def normalize_controller!(params) + params[:controller] = params[:controller].underscore if params.key?(:controller) + end + def merge_default_action!(params) params[:action] ||= 'index' end @@ -180,14 +191,50 @@ module ActionDispatch selector = url_helper_name(name, kind) hash_access_method = hash_access_name(name, kind) - @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - remove_possible_method :#{selector} - def #{selector}(*args) - url_for(#{hash_access_method}(*args)) - end - END_EVAL + if optimize_helper?(route) + @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 + remove_possible_method :#{selector} + def #{selector}(*args) + if args.size == #{route.required_parts.size} && !args.last.is_a?(Hash) && optimize_routes_generation? + options = #{options.inspect}.merge!(url_options) + options[:path] = "#{optimized_helper(route)}" + ActionDispatch::Http::URL.url_for(options) + else + url_for(#{hash_access_method}(*args)) + end + end + END_EVAL + else + @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 + remove_possible_method :#{selector} + def #{selector}(*args) + url_for(#{hash_access_method}(*args)) + end + END_EVAL + end + helpers << selector end + + # Clause check about when we need to generate an optimized helper. + def optimize_helper?(route) #:nodoc: + route.ast.grep(Journey::Nodes::Star).empty? && route.requirements.except(:controller, :action).empty? + end + + # Generates the interpolation to be used in the optimized helper. + def optimized_helper(route) + string_route = route.ast.to_s + + while string_route.gsub!(/\([^\)]*\)/, "") + true + end + + route.required_parts.each_with_index do |part, i| + string_route.gsub!(part.inspect, "\#{Journey::Router::Utils.escape_fragment(args[#{i}].to_param)}") + end + + string_route + end end attr_accessor :formatter, :set, :named_routes, :default_scope, :router @@ -246,8 +293,7 @@ module ActionDispatch def eval_block(block) if block.arity == 1 raise "You are using the old router DSL which has been removed in Rails 3.1. " << - "Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/ " << - "or add the rails_legacy_mapper gem to your Gemfile" + "Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/" end mapper = Mapper.new(self) if default_scope @@ -313,7 +359,7 @@ module ActionDispatch # Rails.application.routes.url_helpers.url_for(args) @_routes = routes class << self - delegate :url_for, :to => '@_routes' + delegate :url_for, :optimize_routes_generation?, :to => '@_routes' end # Make named_routes available in the module singleton @@ -547,6 +593,14 @@ module ActionDispatch RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length, :trailing_slash, :anchor, :params, :only_path, :script_name] + def mounted? + false + end + + def optimize_routes_generation? + !mounted? && default_url_options.empty? + end + def _generate_prefix(options = {}) nil end @@ -558,19 +612,17 @@ module ActionDispatch user, password = extract_authentication(options) path_segments = options.delete(:_path_segments) - script_name = options.delete(:script_name) - - path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s + script_name = options.delete(:script_name).presence || _generate_prefix(options) path_options = options.except(*RESERVED_OPTIONS) path_options = yield(path_options) if block_given? - path_addition, params = generate(path_options, path_segments || {}) - path << path_addition + path, params = generate(path_options, path_segments || {}) params.merge!(options[:params] || {}) ActionDispatch::Http::URL.url_for(options.merge!({ :path => path, + :script_name => script_name, :params => params, :user => user, :password => password @@ -584,6 +636,7 @@ module ActionDispatch def recognize_path(path, environment = {}) method = (environment[:method] || "GET").to_s.upcase path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://} + extras = environment[:extras] || {} begin env = Rack::MockRequest.env_for(path, {:method => method}) @@ -593,6 +646,7 @@ module ActionDispatch req = @request_class.new(env) @router.recognize(req) do |route, matches, params| + params.merge!(extras) params.each do |key, value| if value.is_a?(String) value = value.dup.force_encoding(Encoding::BINARY) diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index ee6616c5d3..94db36ce1f 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -102,6 +102,9 @@ module ActionDispatch super end + # Hook overriden in controller to add request information + # with `default_url_options`. Application logic should not + # go into url_options. def url_options default_url_options end @@ -152,6 +155,11 @@ module ActionDispatch protected + def optimize_routes_generation? + return @_optimized_routes if defined?(@_optimized_routes) + @_optimized_routes = _routes.optimize_routes_generation? && default_url_options.empty? + end + def _with_routes(routes) old_routes, @_routes = @_routes, routes yield diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index 1552676fbb..1f4b905d18 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -39,10 +39,9 @@ module ActionDispatch # # Test a custom route # assert_recognizes({:controller => 'items', :action => 'show', :id => '1'}, 'view/item1') def assert_recognizes(expected_options, path, extras={}, message=nil) - request = recognized_request_for(path) + request = recognized_request_for(path, extras) expected_options = expected_options.clone - extras.each_key { |key| expected_options.delete key } unless extras.nil? expected_options.stringify_keys! @@ -181,7 +180,7 @@ module ActionDispatch private # Recognizes the route for a given path. - def recognized_request_for(path) + def recognized_request_for(path, extras = {}) if path.is_a?(Hash) method = path[:method] path = path[:path] @@ -209,7 +208,7 @@ module ActionDispatch request.request_method = method if method - params = @routes.recognize_path(path, { :method => method }) + params = @routes.recognize_path(path, { :method => method, :extras => extras }) request.path_parameters = params.with_indifferent_access request diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 08b7ff49c2..69d54f6981 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -26,8 +26,8 @@ module ActionDispatch # object's <tt>@response</tt> instance variable will point to the same # response object. # - # You can also perform POST, PUT, DELETE, and HEAD requests with +#post+, - # +#put+, +#delete+, and +#head+. + # You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with + # +#post+, +#patch+, +#put+, +#delete+, and +#head+. def get(path, parameters = nil, headers = nil) process :get, path, parameters, headers end @@ -38,6 +38,12 @@ module ActionDispatch process :post, path, parameters, headers end + # Performs a PATCH request with the given parameters. See +#get+ for more + # details. + def patch(path, parameters = nil, headers = nil) + process :patch, path, parameters, headers + end + # Performs a PUT request with the given parameters. See +#get+ for more # details. def put(path, parameters = nil, headers = nil) @@ -56,13 +62,19 @@ module ActionDispatch process :head, path, parameters, headers end + # Performs a OPTIONS request with the given parameters. See +#get+ for + # more details. + def options(path, parameters = nil, headers = nil) + process :options, path, parameters, headers + end + # Performs an XMLHttpRequest request with the given parameters, mirroring # a request from the Prototype library. # - # The request_method is +:get+, +:post+, +:put+, +:delete+ or +:head+; the - # parameters are +nil+, a hash, or a url-encoded or multipart string; - # the headers are a hash. Keys are automatically upcased and prefixed - # with 'HTTP_' if not already. + # The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or + # +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart + # string; the headers are a hash. Keys are automatically upcased and + # prefixed with 'HTTP_' if not already. def xml_http_request(request_method, path, parameters = nil, headers = nil) headers ||= {} headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' @@ -102,6 +114,12 @@ module ActionDispatch request_via_redirect(:post, path, parameters, headers) end + # Performs a PATCH request, following any subsequent redirect. + # See +request_via_redirect+ for more information. + def patch_via_redirect(path, parameters = nil, headers = nil) + request_via_redirect(:patch, path, parameters, headers) + end + # Performs a PUT request, following any subsequent redirect. # See +request_via_redirect+ for more information. def put_via_redirect(path, parameters = nil, headers = nil) @@ -312,7 +330,7 @@ module ActionDispatch @integration_session = Integration::Session.new(app) end - %w(get post put head delete cookies assigns + %w(get post patch put head delete options cookies assigns xml_http_request xhr get_via_redirect post_via_redirect).each do |method| define_method(method) do |*args| reset! unless integration_session diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb index b08ff41950..3a6d081721 100644 --- a/actionpack/lib/action_dispatch/testing/test_process.rb +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -5,7 +5,8 @@ require 'active_support/core_ext/hash/indifferent_access' module ActionDispatch module TestProcess def assigns(key = nil) - assigns = @controller.view_assigns.with_indifferent_access + assigns = {}.with_indifferent_access + @controller.view_assigns.each {|k, v| assigns.regular_writer(k, v)} key.nil? ? assigns : assigns[key] end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 662adbe183..6dd52d8186 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -24,9 +24,10 @@ module ActionView # server by setting ActionController::Base.asset_host in the application # configuration, typically in <tt>config/environments/production.rb</tt>. # For example, you'd define <tt>assets.example.com</tt> to be your asset - # host this way: + # host this way, inside the <tt>configure</tt> block of your environment-specific + # configuration files or <tt>config/application.rb</tt>: # - # ActionController::Base.asset_host = "assets.example.com" + # config.action_controller.asset_host = "assets.example.com" # # Helpers take that into account: # diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 17bbfe2efd..278139cadb 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -215,7 +215,7 @@ module ActionView def flush_output_buffer #:nodoc: if output_buffer && !output_buffer.empty? response.body_parts << output_buffer - self.output_buffer = output_buffer[0,0] + self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0] nil end end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index bdfef920c5..53ae8b66da 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -250,7 +250,7 @@ module ActionView # # You can force the form to use the full array of HTTP verbs by setting # - # :method => (:get|:post|:put|:delete) + # :method => (:get|:post|:patch|:put|:delete) # # in the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the # form will be set to POST and a hidden input called _method will carry the intended verb for the server @@ -385,7 +385,7 @@ module ActionView object = convert_to_model(object) as = options[:as] - action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post] + action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post] options[:html].reverse_merge!( :class => as ? "#{action}_#{as}" : dom_class(object, action), :id => as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence, @@ -889,6 +889,24 @@ module ActionView end alias phone_field telephone_field + # Returns a text_field of type "date". + # + # date_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="date" /> + # + # The default value is generated by trying to call "to_date" + # on the object's value, which makes it behave as expected for instances + # of DateTime and ActiveSupport::TimeWithZone. You can still override that + # by passing the "value" option explicitly, e.g. + # + # @user.born_on = Date.new(1984, 1, 27) + # date_field("user", "born_on", value: "1984-05-12") + # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-05-12" /> + # + def date_field(object_name, method, options = {}) + Tags::DateField.new(object_name, method, self, options).render + end + # Returns a text_field of type "url". # # url_field("user", "homepage") @@ -1084,14 +1102,14 @@ module ActionView # <% end %> # # In the example above, if @post is a new record, it will use "Create Post" as - # submit button label, otherwise, it uses "Update Post". + # button label, otherwise, it uses "Update Post". # - # Those labels can be customized using I18n, under the helpers.submit key and accept - # the %{model} as translation interpolation: + # Those labels can be customized using I18n, under the helpers.submit key + # (the same as submit helper) and accept the %{model} as translation interpolation: # # en: # helpers: - # button: + # submit: # create: "Create a %{model}" # update: "Confirm changes to %{model}" # @@ -1099,7 +1117,7 @@ module ActionView # # en: # helpers: - # button: + # submit: # post: # create: "Add %{model}" # diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index bc03a1cf83..5be3da9b94 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -330,9 +330,12 @@ module ActionView container.map do |element| html_attributes = option_html_attributes(element) text, value = option_text_and_value(element).map { |item| item.to_s } - selected_attribute = ' selected="selected"' if option_value_selected?(value, selected) - disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled) - %(<option value="#{ERB::Util.html_escape(value)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{ERB::Util.html_escape(text)}</option>) + + html_attributes[:selected] = 'selected' if option_value_selected?(value, selected) + html_attributes[:disabled] = 'disabled' if disabled && option_value_selected?(value, disabled) + html_attributes[:value] = value + + content_tag(:option, text, html_attributes) end.join("\n").html_safe end @@ -472,16 +475,16 @@ module ActionView # <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to # wrap the output in an appropriate <tt><select></tt> tag. def grouped_options_for_select(grouped_options, selected_key = nil, prompt = nil) - body = '' - body << content_tag(:option, prompt, { :value => "" }, true) if prompt + body = "".html_safe + body.safe_concat content_tag(:option, prompt, :value => "") if prompt grouped_options = grouped_options.sort if grouped_options.is_a?(Hash) - grouped_options.each do |group| - body << content_tag(:optgroup, options_for_select(group[1], selected_key), :label => group[0]) + grouped_options.each do |label, container| + body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), :label => label) end - body.html_safe + body end # Returns a string of option tags for pretty much any time zone in the @@ -503,23 +506,24 @@ module ActionView # NOTE: Only the option tags are returned, you have to wrap this call in # a regular HTML select tag. def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone) - zone_options = "" + zone_options = "".html_safe zones = model.all convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } } if priority_zones if priority_zones.is_a?(Regexp) - priority_zones = model.all.find_all {|z| z =~ priority_zones} + priority_zones = zones.select { |z| z =~ priority_zones } end - zone_options += options_for_select(convert_zones[priority_zones], selected) - zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n" - zones = zones.reject { |z| priority_zones.include?( z ) } + zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected) + zone_options.safe_concat content_tag(:option, '-------------', :value => '', :disabled => 'disabled') + zone_options.safe_concat "\n" + + zones.reject! { |z| priority_zones.include?(z) } end - zone_options += options_for_select(convert_zones[zones], selected) - zone_options.html_safe + zone_options.safe_concat options_for_select(convert_zones[zones], selected) end # Returns radio button tags for the collection of existing return values @@ -574,9 +578,9 @@ module ActionView # b.label(:class => "radio_button") { b.radio_button(:class => "radio_button") } # end # - # There are also two special methods available: <tt>text</tt> and - # <tt>value</tt>, which are the current text and value methods for the - # item being rendered, respectively. You can use them like this: + # There are also three special methods available: <tt>object</tt>, <tt>text</tt> and + # <tt>value</tt>, which are the current item being rendered, its text and value methods, + # respectively. You can use them like this: # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b| # b.label(:"data-value" => b.value) { b.radio_button + b.text } # end @@ -637,9 +641,9 @@ module ActionView # b.label(:class => "check_box") { b.check_box(:class => "check_box") } # end # - # There are also two special methods available: <tt>text</tt> and - # <tt>value</tt>, which are the current text and value methods for the - # item being rendered, respectively. You can use them like this: + # There are also three special methods available: <tt>object</tt>, <tt>text</tt> and + # <tt>value</tt>, which are the current item being rendered, its text and value methods, + # respectively. You can use them like this: # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b| # b.label(:"data-value" => b.value) { b.check_box + b.text } # end @@ -649,20 +653,15 @@ module ActionView private def option_html_attributes(element) - return "" unless Array === element + return {} unless Array === element - element.select { |e| Hash === e }.reduce({}, :merge).map do |k, v| - " #{k}=\"#{ERB::Util.html_escape(v.to_s)}\"" - end.join + Hash[element.select { |e| Hash === e }.reduce({}, :merge).map { |k, v| [k, ERB::Util.html_escape(v.to_s)] }] end def option_text_and_value(option) # Options are [text, value] pairs or strings used for both. - case - when Array === option - option = option.reject { |e| Hash === e } - [option.first, option.last] - when !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last) + if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last) + option = option.reject { |e| Hash === e } if Array === option [option.first, option.last] else [option, option] @@ -670,11 +669,7 @@ module ActionView end def option_value_selected?(value, selected) - if selected.respond_to?(:include?) && !selected.is_a?(String) - selected.include? value - else - value == selected - end + Array(selected).include? value end def extract_selected_and_disabled(selected) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index e97f602728..9fad30a48f 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -313,7 +313,7 @@ module ActionView options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) end - escape = options.key?("escape") ? options.delete("escape") : true + escape = options.delete("escape") { true } content = ERB::Util.html_escape(content) if escape content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options) @@ -525,10 +525,9 @@ module ActionView # <% end %> # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset> def field_set_tag(legend = nil, options = nil, &block) - content = capture(&block) output = tag(:fieldset, options, true) output.safe_concat(content_tag(:legend, legend)) unless legend.blank? - output.concat(content) + output.concat(capture(&block)) if block_given? output.safe_concat("</fieldset>") end @@ -549,6 +548,14 @@ module ActionView end alias phone_field_tag telephone_field_tag + # Creates a text field of type "date". + # + # ==== Options + # * Accepts the same options as text_field_tag. + def date_field_tag(name, value = nil, options = {}) + text_field_tag(name, value, options.stringify_keys.update("type" => "date")) + end + # Creates a text field of type "url". # # ==== Options diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 309923490c..ac9e530f01 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -1,5 +1,4 @@ require 'action_view/helpers/tag_helper' -require 'active_support/core_ext/string/encoding' module ActionView module Helpers diff --git a/actionpack/lib/action_view/helpers/tags.rb b/actionpack/lib/action_view/helpers/tags.rb index c480799fe3..3cf762877f 100644 --- a/actionpack/lib/action_view/helpers/tags.rb +++ b/actionpack/lib/action_view/helpers/tags.rb @@ -8,6 +8,7 @@ module ActionView autoload :CollectionCheckBoxes autoload :CollectionRadioButtons autoload :CollectionSelect + autoload :DateField autoload :DateSelect autoload :DatetimeSelect autoload :EmailField diff --git a/actionpack/lib/action_view/helpers/tags/base.rb b/actionpack/lib/action_view/helpers/tags/base.rb index e22612ccd0..22c16de057 100644 --- a/actionpack/lib/action_view/helpers/tags/base.rb +++ b/actionpack/lib/action_view/helpers/tags/base.rb @@ -36,7 +36,7 @@ module ActionView object.respond_to?(method_before_type_cast) ? object.send(method_before_type_cast) : - object.send(@method_name) + value(object) end end @@ -75,14 +75,14 @@ module ActionView def add_default_name_and_id(options) if options.has_key?("index") - options["name"] ||= tag_name_with_index(options["index"]) + options["name"] ||= options.fetch("name"){ tag_name_with_index(options["index"]) } options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) } options.delete("index") elsif defined?(@auto_index) - options["name"] ||= tag_name_with_index(@auto_index) + options["name"] ||= options.fetch("name"){ tag_name_with_index(@auto_index) } options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) } else - options["name"] ||= options['multiple'] ? tag_name_multiple : tag_name + options["name"] ||= options.fetch("name"){ options['multiple'] ? tag_name_multiple : tag_name } options["id"] = options.fetch("id"){ tag_id } end options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence @@ -133,13 +133,13 @@ module ActionView def add_options(option_tags, options, value = nil) if options[:include_blank] - option_tags = "<option value=\"\">#{ERB::Util.html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags + option_tags = content_tag('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags end if value.blank? && options[:prompt] prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select') - option_tags = "<option value=\"\">#{ERB::Util.html_escape(prompt)}</option>\n" + option_tags + option_tags = content_tag('option', prompt, :value => '') + "\n" + option_tags end - option_tags.html_safe + option_tags end end end diff --git a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb index 5f1e9ec026..e23f5113fb 100644 --- a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb +++ b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb @@ -14,9 +14,9 @@ module ActionView end def render - rendered_collection = render_collection do |value, text, default_html_options| + rendered_collection = render_collection do |item, value, text, default_html_options| default_html_options[:multiple] = true - builder = instantiate_builder(CheckBoxBuilder, value, text, default_html_options) + builder = instantiate_builder(CheckBoxBuilder, item, value, text, default_html_options) if block_given? yield builder diff --git a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb index 1e2e77dde1..6a1479069f 100644 --- a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb +++ b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb @@ -3,13 +3,14 @@ module ActionView module Tags module CollectionHelpers class Builder - attr_reader :text, :value + attr_reader :object, :text, :value - def initialize(template_object, object_name, method_name, + def initialize(template_object, object_name, method_name, object, sanitized_attribute_name, text, value, input_html_options) @template_object = template_object @object_name = object_name @method_name = method_name + @object = object @sanitized_attribute_name = sanitized_attribute_name @text = text @value = value @@ -32,8 +33,8 @@ module ActionView private - def instantiate_builder(builder_class, value, text, html_options) - builder_class.new(@template_object, @object_name, @method_name, + def instantiate_builder(builder_class, item, value, text, html_options) + builder_class.new(@template_object, @object_name, @method_name, item, sanitize_attribute_name(value), text, value, html_options) end @@ -58,6 +59,7 @@ module ActionView end end + html_options[:object] = @object html_options end @@ -71,7 +73,7 @@ module ActionView text = value_for_collection(item, @text_method) default_html_options = default_html_options_for_collection(item, value) - yield value, text, default_html_options + yield item, value, text, default_html_options end.join.html_safe end end diff --git a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb index 8e7aeeed63..ba2035f074 100644 --- a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb +++ b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb @@ -14,8 +14,8 @@ module ActionView end def render - render_collection do |value, text, default_html_options| - builder = instantiate_builder(RadioButtonBuilder, value, text, default_html_options) + render_collection do |item, value, text, default_html_options| + builder = instantiate_builder(RadioButtonBuilder, item, value, text, default_html_options) if block_given? yield builder diff --git a/actionpack/lib/action_view/helpers/tags/date_field.rb b/actionpack/lib/action_view/helpers/tags/date_field.rb new file mode 100644 index 0000000000..bb968e9f39 --- /dev/null +++ b/actionpack/lib/action_view/helpers/tags/date_field.rb @@ -0,0 +1,15 @@ +module ActionView + module Helpers + module Tags + class DateField < TextField #:nodoc: + def render + options = @options.stringify_keys + options["value"] = @options.fetch("value") { value(object).try(:to_date) } + options["size"] = nil + @options = options + super + end + end + end + end +end diff --git a/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb b/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb index 507466a57a..507ba8835f 100644 --- a/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb +++ b/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb @@ -14,8 +14,13 @@ module ActionView end def render + option_tags_options = { + :selected => @options.fetch(:selected) { value(@object) }, + :disabled => @options[:disabled] + } + select_content_tag( - option_groups_from_collection_for_select(@collection, @group_method, @group_label_method, @option_key_method, @option_value_method, value(@object)), @options, @html_options + option_groups_from_collection_for_select(@collection, @group_method, @group_label_method, @option_key_method, @option_value_method, option_tags_options), @options, @html_options ) end end diff --git a/actionpack/lib/action_view/helpers/tags/text_area.rb b/actionpack/lib/action_view/helpers/tags/text_area.rb index a7db8eb437..461a049fc2 100644 --- a/actionpack/lib/action_view/helpers/tags/text_area.rb +++ b/actionpack/lib/action_view/helpers/tags/text_area.rb @@ -12,7 +12,7 @@ module ActionView options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) end - content_tag("textarea", ERB::Util.html_escape(options.delete('value') || value_before_type_cast(object)), options) + content_tag("textarea", "\n#{options.delete('value') || value_before_type_cast(object)}", options) end end end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index b5fc882e31..f4946e65b5 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -23,20 +23,25 @@ module ActionView include ActionDispatch::Routing::UrlFor include TagHelper - def _routes_context - controller - end + # We need to override url_optoins, _routes_context + # and optimize_routes_generation? to consider the controller. - # Need to map default url options to controller one. - # def default_url_options(*args) #:nodoc: - # controller.send(:default_url_options, *args) - # end - # - def url_options + def url_options #:nodoc: return super unless controller.respond_to?(:url_options) controller.url_options end + def _routes_context #:nodoc: + controller + end + protected :_routes_context + + def optimize_routes_generation? #:nodoc: + controller.respond_to?(:optimize_routes_generation?) ? + controller.optimize_routes_generation? : super + end + protected :optimize_routes_generation? + # Returns the URL for the set of +options+ provided. This takes the # same options as +url_for+ in Action Controller (see the # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default @@ -146,12 +151,12 @@ module ActionView # create an HTML form and immediately submit the form for processing using # the HTTP verb specified. Useful for having links perform a POST operation # in dangerous actions like deleting a record (which search bots can follow - # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt> and <tt>:put</tt>. + # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. # Note that if the user has JavaScript disabled, the request will fall back # to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript # disabled clicking the link will have no effect. If you are relying on the # POST behavior, you should check for it in your controller's action by using - # the request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>. + # the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>:patch</tt>, or <tt>put?</tt>. # * <tt>:remote => true</tt> - This will allow the unobtrusive JavaScript # driver to make an Ajax request to the URL in question instead of following # the link. The drivers each provide mechanisms for listening for the @@ -272,7 +277,7 @@ module ActionView # # There are a few special +html_options+: # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>, - # <tt>:delete</tt> and <tt>:put</tt>. By default it will be <tt>:post</tt>. + # <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>. # * <tt>:disabled</tt> - If set to true, it will generate a disabled button. # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to # prompt with the question specified. If the user accepts, the link is @@ -329,7 +334,7 @@ module ActionView remote = html_options.delete('remote') method = html_options.delete('method').to_s - method_tag = %w{put delete}.include?(method) ? method_tag(method) : "" + method_tag = %w{patch put delete}.include?(method) ? method_tag(method) : "" form_method = method == 'get' ? 'get' : 'post' form_options = html_options.delete('form') || {} diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index 7cca7d969a..8e9db634fb 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -147,15 +147,8 @@ # Default value for :prompt => true in FormOptionsHelper prompt: "Please select" - # Default translation keys for submit FormHelper + # Default translation keys for submit and button FormHelper submit: create: 'Create %{model}' update: 'Update %{model}' submit: 'Save %{model}' - - # Default translation keys for button FormHelper - button: - create: 'Create %{model}' - update: 'Update %{model}' - submit: 'Save %{model}' - diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index 90c4a2759a..b7945a23be 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -9,7 +9,7 @@ module ActionView # generate a key, given to view paths, used in the resolver cache lookup. Since # this key is generated just once during the request, it speeds up all cache accesses. class LookupContext #:nodoc: - attr_accessor :prefixes + attr_accessor :prefixes, :rendered_format mattr_accessor :fallbacks @@fallbacks = FallbackFileSystemResolver.instances @@ -170,23 +170,15 @@ module ActionView def initialize(view_paths, details = {}, prefixes = []) @details, @details_key = {}, nil - @frozen_formats, @skip_default_locale = false, false + @skip_default_locale = false @cache = true @prefixes = prefixes + @rendered_format = nil self.view_paths = view_paths initialize_details(details) end - # Freeze the current formats in the lookup context. By freezing them, you - # that next template lookups are not going to modify the formats. The con - # use this, to ensure that formats won't be further modified (as it does - def freeze_formats(formats, unless_frozen=false) #:nodoc: - return if unless_frozen && @frozen_formats - self.formats = formats - @frozen_formats = true - end - # Override formats= to expand ["*/*"] values and automatically # add :html as fallback to :js. def formats=(values) diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb index a588abcee3..52473cd222 100644 --- a/actionpack/lib/action_view/renderer/abstract_renderer.rb +++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb @@ -1,7 +1,7 @@ module ActionView class AbstractRenderer #:nodoc: delegate :find_template, :template_exists?, :with_fallbacks, :update_details, - :with_layout_format, :formats, :freeze_formats, :to => :@lookup_context + :with_layout_format, :formats, :to => :@lookup_context def initialize(lookup_context) @lookup_context = lookup_context diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 3033294883..3628b935b7 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -272,6 +272,8 @@ module ActionView @block = block @details = extract_details(options) + @lookup_context.rendered_format ||= formats.first + if String === partial @object = options[:object] @path = partial diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index f3abc6d533..20f75fba45 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -6,7 +6,8 @@ module ActionView @view = context @details = extract_details(options) template = determine_template(options) - freeze_formats(template.formats, true) + @lookup_context.rendered_format ||= template.formats.first + @lookup_context.formats = template.formats render_template(template, options[:layout], options[:locals]) end diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb index 67978ada7e..4e22bec6cc 100644 --- a/actionpack/lib/action_view/template/handlers.rb +++ b/actionpack/lib/action_view/template/handlers.rb @@ -23,6 +23,7 @@ module ActionView #:nodoc: # and should return the rendered template as a String. def register_template_handler(extension, handler) @@template_handlers[extension.to_sym] = handler + @@template_extensions = nil end def template_handler_extensions diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index 323df67c97..19b9112afd 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -44,10 +44,6 @@ module ActionView class_attribute :erb_trim_mode self.erb_trim_mode = '-' - # Default format used by ERB. - class_attribute :default_format - self.default_format = Mime::HTML - # Default implementation used. class_attribute :erb_implementation self.erb_implementation = Erubis diff --git a/actionpack/lib/sprockets/compressors.rb b/actionpack/lib/sprockets/compressors.rb index cb3e13314b..8b728d6570 100644 --- a/actionpack/lib/sprockets/compressors.rb +++ b/actionpack/lib/sprockets/compressors.rb @@ -1,38 +1,28 @@ module Sprockets module Compressors + extend self + @@css_compressors = {} @@js_compressors = {} @@default_css_compressor = nil @@default_js_compressor = nil - def self.register_css_compressor(name, klass, options = {}) + def register_css_compressor(name, klass, options = {}) @@default_css_compressor = name.to_sym if options[:default] || @@default_css_compressor.nil? - @@css_compressors[name.to_sym] = {:klass => klass.to_s, :require => options[:require]} + @@css_compressors[name.to_sym] = { :klass => klass.to_s, :require => options[:require] } end - def self.register_js_compressor(name, klass, options = {}) + def register_js_compressor(name, klass, options = {}) @@default_js_compressor = name.to_sym if options[:default] || @@default_js_compressor.nil? - @@js_compressors[name.to_sym] = {:klass => klass.to_s, :require => options[:require]} + @@js_compressors[name.to_sym] = { :klass => klass.to_s, :require => options[:require] } end - def self.registered_css_compressor(name) - if name.respond_to?(:to_sym) - compressor = @@css_compressors[name.to_sym] || @@css_compressors[@@default_css_compressor] - require compressor[:require] if compressor[:require] - compressor[:klass].constantize.new - else - name - end + def registered_css_compressor(name) + find_registered_compressor name, @@css_compressors, @@default_css_compressor end - def self.registered_js_compressor(name) - if name.respond_to?(:to_sym) - compressor = @@js_compressors[name.to_sym] || @@js_compressors[@@default_js_compressor] - require compressor[:require] if compressor[:require] - compressor[:klass].constantize.new - else - name - end + def registered_js_compressor(name) + find_registered_compressor name, @@js_compressors, @@default_js_compressor end # The default compressors must be registered in default plugins (ex. Sass-Rails) @@ -43,6 +33,18 @@ module Sprockets register_css_compressor(:yui, 'YUI::CssCompressor', :require => 'yui/compressor') register_js_compressor(:closure, 'Closure::Compiler', :require => 'closure-compiler') register_js_compressor(:yui, 'YUI::JavaScriptCompressor', :require => 'yui/compressor') + + private + + def find_registered_compressor(name, compressors_hash, default_compressor_name) + if name.respond_to?(:to_sym) + compressor = compressors_hash[name.to_sym] || compressors_hash[default_compressor_name] + require compressor[:require] if compressor[:require] + compressor[:klass].constantize.new + else + name + end + end end # An asset compressor which does nothing. diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb index 976ae5a76d..839ec7635f 100644 --- a/actionpack/lib/sprockets/helpers/rails_helper.rb +++ b/actionpack/lib/sprockets/helpers/rails_helper.rb @@ -19,9 +19,9 @@ module Sprockets def javascript_include_tag(*sources) options = sources.extract_options! - debug = options.key?(:debug) ? options.delete(:debug) : debug_assets? - body = options.key?(:body) ? options.delete(:body) : false - digest = options.key?(:digest) ? options.delete(:digest) : digest_assets? + debug = options.delete(:debug) { debug_assets? } + body = options.delete(:body) { false } + digest = options.delete(:digest) { digest_assets? } sources.collect do |source| if debug && asset = asset_paths.asset_for(source, 'js') @@ -36,9 +36,9 @@ module Sprockets def stylesheet_link_tag(*sources) options = sources.extract_options! - debug = options.key?(:debug) ? options.delete(:debug) : debug_assets? - body = options.key?(:body) ? options.delete(:body) : false - digest = options.key?(:digest) ? options.delete(:digest) : digest_assets? + debug = options.delete(:debug) { debug_assets? } + body = options.delete(:body) { false } + digest = options.delete(:digest) { digest_assets? } sources.collect do |source| if debug && asset = asset_paths.asset_for(source, 'css') diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index 44ddab0950..2bc482a39d 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -24,7 +24,7 @@ module Sprockets env.version = ::Rails.env + "-#{config.assets.version}" if config.assets.logger != false - env.logger = config.assets.logger || ::Rails.logger + env.logger = config.assets.logger || ::Rails.logger end if config.assets.cache_store != false diff --git a/actionpack/lib/sprockets/static_compiler.rb b/actionpack/lib/sprockets/static_compiler.rb index 719df0bd51..9bbb464474 100644 --- a/actionpack/lib/sprockets/static_compiler.rb +++ b/actionpack/lib/sprockets/static_compiler.rb @@ -8,8 +8,8 @@ module Sprockets @env = env @target = target @paths = paths - @digest = options.key?(:digest) ? options.delete(:digest) : true - @manifest = options.key?(:manifest) ? options.delete(:manifest) : true + @digest = options.fetch(:digest, true) + @manifest = options.fetch(:manifest, true) @manifest_path = options.delete(:manifest_path) || target @zip_files = options.delete(:zip_files) || /\.(?:css|html|js|svg|txt|xml)$/ end |