diff options
Diffstat (limited to 'actionpack/lib')
44 files changed, 376 insertions, 311 deletions
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index 8f73e244d7..d6f75bded0 100644 --- a/actionpack/lib/abstract_controller/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -81,11 +81,12 @@ module AbstractController # class EmployeeController < BankController # layout nil # - # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites - # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. - # - # The TellerController uses +teller.html.erb+, and TillController inherits that layout and - # uses it as well. + # In these examples: + # * The InformationController uses the "bank_standard" layout, inherited from BankController. + # * The TellerController follows convention and uses +app/views/layouts/teller.html.erb+. + # * The TillController inherits the layout from TellerController and uses +teller.html.erb+ as well. + # * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method. + # * The EmployeeController does not use a layout at all. # # == Types of layouts # @@ -138,8 +139,8 @@ module AbstractController # # end # - # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout - # around the rendered view. + # This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will + # be rendered directly, without wrapping a layout around the rendered view. # # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so # #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>. @@ -158,7 +159,7 @@ module AbstractController # end # end # - # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout. + # This will override the controller-wide "weblog_standard" layout, and will render the help action with the "help" layout instead. module Layouts extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index c03c77cb4a..d14c5f940b 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -31,7 +31,7 @@ module ActionController # "302 Moved" HTTP response that takes the user to the index action. # # These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect. - # Most actions are variations of these themes. + # Most actions are variations on these themes. # # == Requests # @@ -116,8 +116,8 @@ module ActionController # # Title: <%= @post.title %> # - # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use - # the manual rendering methods: + # You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates + # will use the manual rendering methods: # # def search # @results = Search.find(params[:query]) @@ -132,9 +132,9 @@ module ActionController # # == Redirects # - # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to a database, - # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to) - # a <tt>show</tt> action that we'll assume has already been created. The code might look like this: + # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the + # database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're + # going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this: # # def create # @entry = Entry.new(params[:entry]) @@ -146,7 +146,9 @@ module ActionController # end # end # - # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed. + # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method, which is then executed. + # Note that this is an external HTTP-level redirection which will cause the browser to make a second request (a GET to the show action), + # and not some internal re-routing which calls both "create" and then "show" within one request. # # Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting. # diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 0be04b70a1..2bdb23c4d7 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -109,7 +109,6 @@ module ActionController #:nodoc: def expire_fragment(key, options = nil) return unless cache_configured? key = fragment_cache_key(key) unless key.is_a?(Regexp) - message = nil instrument_fragment_cache :expire_fragment, key do if key.is_a?(Regexp) diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb index e9db0d97b6..938a6ae81c 100644 --- a/actionpack/lib/action_controller/caching/sweeping.rb +++ b/actionpack/lib/action_controller/caching/sweeping.rb @@ -61,6 +61,7 @@ module ActionController #:nodoc: end def after(controller) + self.controller = controller callback(:after) if controller.perform_caching # Clean up, so that the controller can be collected after this request self.controller = nil diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index 8d813a8e38..35e29398e6 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -10,7 +10,7 @@ module ActionController format = payload[:format] format = format.to_s.upcase if format.is_a?(Symbol) - info " Processing by #{payload[:controller]}##{payload[:action]} as #{format}" + info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}" info " Parameters: #{params.inspect}" unless params.empty? end @@ -20,10 +20,11 @@ module ActionController status = payload[:status] if status.nil? && payload[:exception].present? - status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil - end + status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil + end message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration message << " (#{additions.join(" | ")})" unless additions.blank? + message << "\n" info(message) end @@ -59,4 +60,4 @@ module ActionController end end -ActionController::LogSubscriber.attach_to :action_controller
\ No newline at end of file +ActionController::LogSubscriber.attach_to :action_controller diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 2ad571b2b5..7420a5e7e9 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -106,7 +106,7 @@ module ActionController module ControllerMethods extend ActiveSupport::Concern - + module ClassMethods def http_basic_authenticate_with(options = {}) before_filter(options.except(:name, :password, :realm)) do @@ -116,7 +116,7 @@ module ActionController end end end - + def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure) authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm) end @@ -423,7 +423,7 @@ module ActionController # Returns an Array of [String, Hash] if a token is present. # Returns nil if no token is found. def token_and_options(request) - if header = request.authorization.to_s[/^Token (.*)/] + if request.authorization.to_s[/^Token (.*)/] values = Hash[$1.split(',').map do |value| value.strip! # remove any spaces between commas and values key, value = value.split(/\=\"?/) # split key=value pairs diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 5500f88930..2d8afc3a78 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -64,7 +64,7 @@ module ActionController # end # # will try to check if +Admin::User+ or +User+ model exists, and use it to - # determine the wrapper key respectively. If both of the model doesn't exists, + # determine the wrapper key respectively. If both models don't exist, # it will then fallback to use +user+ as the key. module ParamsWrapper extend ActiveSupport::Concern @@ -88,14 +88,14 @@ module ActionController # # wraps parameters into +params[:person]+ hash # # wrap_parameters Person - # # wraps parameters by determine the wrapper key from Person class + # # wraps parameters by determining the wrapper key from Person class # (+person+, in this case) and the list of attribute names # # wrap_parameters :include => [:username, :title] # # wraps only +:username+ and +:title+ attributes from parameters. # # wrap_parameters false - # # disable parameters wrapping for this controller altogether. + # # disables parameters wrapping for this controller altogether. # # ==== Options # * <tt>:format</tt> - The list of formats in which the parameters wrapper diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 55c650df6c..dee7eb1ec8 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -43,6 +43,7 @@ module ActionController # # The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an # integer, or a symbol representing the downcased, underscored and symbolized description. + # Note that the status code must be a 3xx HTTP code, or redirection will not occur. # # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names # +alert+ and +notice+ as well as a general purpose +flash+ bucket. diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 3892a12407..0bb436a476 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -260,4 +260,3 @@ module ActionController #:nodoc: end end end -
\ No newline at end of file diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index 2def78b51a..c11d676c5e 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -40,7 +40,7 @@ module ActionController # dom_class(post, :edit) # => "edit_post" # dom_class(Person, :edit) # => "edit_person" def dom_class(record_or_class, prefix = nil) - singular = ActiveModel::Naming.singular(record_or_class) + singular = ActiveModel::Naming.param_key(record_or_class) prefix ? "#{prefix}#{JOIN}#{singular}" : singular end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index bfb820fcdf..45bb641aee 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -175,10 +175,6 @@ module ActionController end def recycle! - write_cookies! - @env.delete('HTTP_COOKIE') if @cookies.blank? - @env.delete('action_dispatch.cookies') - @cookies = nil @formats = nil @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } @@ -186,6 +182,14 @@ module ActionController @method = @request_method = nil @fullpath = @ip = @remote_ip = nil @env['action_dispatch.request.query_parameters'] = {} + @set_cookies ||= {} + @set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }]) + deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies") + @set_cookies.reject!{ |k,v| deleted_cookies.include?(k) } + cookie_jar.update(rack_cookies) + cookie_jar.update(cookies) + cookie_jar.update(@set_cookies) + cookie_jar.recycle! end end @@ -206,7 +210,7 @@ module ActionController DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS def initialize(session = {}) - @env, @by = nil, nil + super(nil, nil) replace(session.stringify_keys) @loaded = true end @@ -301,18 +305,17 @@ module ActionController # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another # action call which can then be asserted against. # - # == Manipulating the request collections + # == Manipulating session and cookie variables # - # The collections described above link to the response, so you can test if what the actions were expected to do happened. But - # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions - # and cookies, though. For sessions, you just do: + # Sometimes you need to set up the session and cookie variables for a test. + # To do this just assign a value to the session or cookie collection: # - # @request.session[:key] = "value" - # @request.cookies[:key] = "value" + # session[:key] = "value" + # cookies[:key] = "value" # - # To clear the cookies for a test just clear the request's cookies hash: + # To clear the cookies for a test just clear the cookie collection: # - # @request.cookies.clear + # cookies.clear # # == \Testing named routes # @@ -450,7 +453,6 @@ module ActionController @controller.process_with_new_base_test(@request, @response) @assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {} @request.session.delete('flash') if @request.session['flash'].blank? - @request.cookies.merge!(@response.cookies) @response end diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb index 1e43104f0a..505d5560b1 100644 --- a/actionpack/lib/action_dispatch/http/headers.rb +++ b/actionpack/lib/action_dispatch/http/headers.rb @@ -3,9 +3,10 @@ require 'active_support/memoizable' module ActionDispatch module Http class Headers < ::Hash - extend ActiveSupport::Memoizable + @@env_cache = Hash.new { |h,k| h[k] = "HTTP_#{k.upcase.gsub(/-/, '_')}" } def initialize(*args) + if args.size == 1 && args[0].is_a?(Hash) super() update(args[0]) @@ -25,9 +26,8 @@ module ActionDispatch private # Converts a HTTP header name to an environment variable name. def env_name(header_name) - "HTTP_#{header_name.upcase.gsub(/-/, '_')}" + @@env_cache[header_name] end - memoize :env_name end end end diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb index 37effade4f..a15ad28f16 100644 --- a/actionpack/lib/action_dispatch/http/upload.rb +++ b/actionpack/lib/action_dispatch/http/upload.rb @@ -4,7 +4,7 @@ module ActionDispatch attr_accessor :original_filename, :content_type, :tempfile, :headers def initialize(hash) - @original_filename = hash[:filename] + @original_filename = encode_filename(hash[:filename]) @content_type = hash[:type] @headers = hash[:head] @tempfile = hash[:tempfile] @@ -30,6 +30,16 @@ module ActionDispatch def size @tempfile.size end + + private + def encode_filename(filename) + # Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8 + if "ruby".encoding_aware? && filename + filename.force_encoding("UTF-8").encode! + else + filename + end + end end module Upload diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 47c4bad489..8cee9ecdc4 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -1,4 +1,5 @@ -require "active_support/core_ext/object/blank" +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/hash/keys' module ActionDispatch class Request @@ -129,7 +130,7 @@ module ActionDispatch end def update(other_hash) - @cookies.update other_hash + @cookies.update other_hash.stringify_keys self end @@ -185,6 +186,11 @@ module ActionDispatch value end + # Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie + def clear(options = {}) + @cookies.each_key{ |k| delete(k, options) } + end + # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example: # # cookies.permanent[:prefers_open_id] = true @@ -222,6 +228,11 @@ module ActionDispatch @delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) } end + def recycle! #:nodoc: + @set_cookies.clear + @delete_cookies.clear + end + private def write_cookie?(cookie) diff --git a/actionpack/lib/action_dispatch/middleware/head.rb b/actionpack/lib/action_dispatch/middleware/head.rb index 56e2d2f2a8..f1906a3ab3 100644 --- a/actionpack/lib/action_dispatch/middleware/head.rb +++ b/actionpack/lib/action_dispatch/middleware/head.rb @@ -8,7 +8,7 @@ module ActionDispatch if env["REQUEST_METHOD"] == "HEAD" env["REQUEST_METHOD"] = "GET" env["rack.methodoverride.original_method"] = "HEAD" - status, headers, body = @app.call(env) + status, headers, _ = @app.call(env) [status, headers, []] else @app.call(env) diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 74c090f260..1dcd83ceb5 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -161,7 +161,7 @@ module ActionDispatch # Consider the following route, which you will find commented out at the # bottom of your generated <tt>config/routes.rb</tt>: # - # match ':controller(/:action(/:id(.:format)))' + # match ':controller(/:action(/:id))(.:format)' # # This route states that it expects requests to consist of a # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index ec76d1da1e..65895590bf 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -49,6 +49,9 @@ 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/]+$} + WILDCARD_PATH = %r{\*([^/]+)$} def initialize(set, scope, path, options) @set, @scope = set, scope @@ -77,7 +80,7 @@ module ActionDispatch # segment_keys.include?(k.to_s) || k == :controller next unless Regexp === requirement && !constraints[name] - if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z} + if requirement.source =~ ANCHOR_CHARACTERS_REGEX raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}" end if requirement.multiline? @@ -88,7 +91,7 @@ module ActionDispatch # match "account/overview" def using_match_shorthand?(path, options) - path && options.except(:via, :anchor, :to, :as).empty? && path =~ %r{^/[\w\/]+$} + path && options.except(:via, :anchor, :to, :as).empty? && path =~ SHORTHAND_REGEX end def normalize_path(path) @@ -107,7 +110,7 @@ module ActionDispatch # Add a constraint for wildcard route to make it non-greedy and match the # optional format part of the route by default - if path.match(/\*([^\/]+)$/) && @options[:format] != false + if path.match(WILDCARD_PATH) && @options[:format] != false @options.reverse_merge!(:"#{$1}" => /.+?/) end @@ -1102,9 +1105,9 @@ module ActionDispatch # # The +comments+ resource here will have the following routes generated for it: # - # post_comments GET /sekret/posts/:post_id/comments(.:format) - # post_comments POST /sekret/posts/:post_id/comments(.:format) - # new_post_comment GET /sekret/posts/:post_id/comments/new(.: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 PUT /sekret/comments/:id(.:format) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 9791ba58e2..aae5752c93 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -207,9 +207,6 @@ module ActionDispatch "*/*;q=0.5" unless defined? @named_routes_configured - # install the named routes in this session instance. - klass = singleton_class - # the helpers are made protected by default--we make them public for # easier access during testing and troubleshooting. @named_routes_configured = true diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb index 397bda41d5..f668b81b45 100644 --- a/actionpack/lib/action_dispatch/testing/test_process.rb +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -1,3 +1,4 @@ +require 'action_dispatch/middleware/cookies' require 'action_dispatch/middleware/flash' require 'active_support/core_ext/hash/indifferent_access' @@ -22,7 +23,7 @@ module ActionDispatch end def cookies - @request.cookies.merge(@response.cookies).with_indifferent_access + @request.cookie_jar end def redirect_to_url @@ -38,7 +39,7 @@ module ActionDispatch # # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary) def fixture_file_upload(path, mime_type = nil, binary = false) - fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path) + fixture_path = self.class.fixture_path if self.class.respond_to?(:fixture_path) Rack::Test::UploadedFile.new("#{fixture_path}#{path}", mime_type, binary) end end diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb index 822adb6a47..7280e9a93b 100644 --- a/actionpack/lib/action_dispatch/testing/test_request.rb +++ b/actionpack/lib/action_dispatch/testing/test_request.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/reverse_merge' require 'rack/utils' @@ -14,18 +15,11 @@ module ActionDispatch env = Rails.application.env_config.merge(env) if defined?(Rails.application) super(DEFAULT_ENV.merge(env)) - @cookies = nil self.host = 'test.host' self.remote_addr = '0.0.0.0' self.user_agent = 'Rails Testing' end - def env - write_cookies! - delete_nil_values! - super - end - def request_method=(method) @env['REQUEST_METHOD'] = method.to_s.upcase end @@ -71,23 +65,10 @@ module ActionDispatch @env['HTTP_ACCEPT'] = Array(mime_types).collect { |mime_type| mime_type.to_s }.join(",") end + alias :rack_cookies :cookies + def cookies - @cookies ||= super + @cookies ||= {}.with_indifferent_access end - - private - def write_cookies! - unless @cookies.blank? - @env['HTTP_COOKIE'] = @cookies.map { |name, value| escape_cookie(name, value) }.join('; ') - end - end - - def escape_cookie(name, value) - "#{Rack::Utils.escape(name)}=#{Rack::Utils.escape(value)}" - end - - def delete_nil_values! - @env.delete_if { |k, v| v.nil? } - end end end diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 78eddb7530..d7229419a9 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -30,6 +30,7 @@ module ActionView extend ActiveSupport::Autoload eager_autoload do + autoload :AssetPaths autoload :Base autoload :Context autoload :Helpers diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb new file mode 100644 index 0000000000..2b1fe545a6 --- /dev/null +++ b/actionpack/lib/action_view/asset_paths.rb @@ -0,0 +1,79 @@ +require 'active_support/core_ext/file' + +module ActionView + class AssetPaths #:nodoc: + attr_reader :config, :controller + + def initialize(config, controller) + @config = config + @controller = controller + end + + # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched. + # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL + # roots. Rewrite the asset path for cache-busting asset ids. Include + # asset host, if configured, with the correct request protocol. + def compute_public_path(source, dir, ext = nil, include_host = true) + source = source.to_s + return source if is_uri?(source) + + source = rewrite_extension(source, dir, ext) if ext + source = rewrite_asset_path(source, dir) + + if controller && include_host + has_request = controller.respond_to?(:request) + source = rewrite_host_and_protocol(source, has_request) + end + + source + end + + def is_uri?(path) + path =~ %r{^[-a-z]+://|^cid:|^//} + end + + private + + def rewrite_extension(source, dir, ext) + raise NotImplementedError + end + + def rewrite_asset_path(source, path = nil) + raise NotImplementedError + end + + def rewrite_relative_url_root(source, relative_url_root) + relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source + end + + def rewrite_host_and_protocol(source, has_request) + source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request + host = compute_asset_host(source) + if has_request && host && !is_uri?(host) + host = "#{controller.request.protocol}#{host}" + end + "#{host}#{source}" + end + + # Pick an asset host for this source. Returns +nil+ if no host is set, + # the host if no wildcard is set, the host interpolated with the + # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4), + # or the value returned from invoking call on an object responding to call + # (proc or otherwise). + def compute_asset_host(source) + if host = config.asset_host + if host.respond_to?(:call) + case host.is_a?(Proc) ? host.arity : host.method(:call).arity + when 2 + request = controller.respond_to?(:request) && controller.request + host.call(source, request) + else + host.call(source) + end + else + (host =~ /%d/) ? host % (source.hash % 4) : host + end + end + end + end +end diff --git a/actionpack/lib/action_view/buffers.rb b/actionpack/lib/action_view/buffers.rb index 089fc68706..be7f65c2ce 100644 --- a/actionpack/lib/action_view/buffers.rb +++ b/actionpack/lib/action_view/buffers.rb @@ -35,9 +35,9 @@ module ActionView def html_safe? true end - + def html_safe self end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb index 9a99c3cf52..fae2e4fc1c 100644 --- a/actionpack/lib/action_view/helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_paths.rb @@ -1,83 +1,7 @@ -require 'active_support/core_ext/file' +ActiveSupport::Deprecation.warn "ActionView::Helpers::AssetPaths is deprecated. Please use ActionView::AssetPaths instead." module ActionView module Helpers - - class AssetPaths #:nodoc: - attr_reader :config, :controller - - def initialize(config, controller) - @config = config - @controller = controller - end - - # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched. - # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL - # roots. Rewrite the asset path for cache-busting asset ids. Include - # asset host, if configured, with the correct request protocol. - def compute_public_path(source, dir, ext = nil, include_host = true) - source = source.to_s - return source if is_uri?(source) - - source = rewrite_extension(source, dir, ext) if ext - source = rewrite_asset_path(source, dir) - - if controller && include_host - has_request = controller.respond_to?(:request) - source = rewrite_host_and_protocol(source, has_request) - end - - source - end - - def is_uri?(path) - path =~ %r{^[-a-z]+://|^cid:|^//} - end - - private - - def rewrite_extension(source, dir, ext) - raise NotImplementedError - end - - def rewrite_asset_path(source, path = nil) - raise NotImplementedError - end - - def rewrite_relative_url_root(source, relative_url_root) - relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source - end - - def rewrite_host_and_protocol(source, has_request) - source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request - host = compute_asset_host(source) - if has_request && host && !is_uri?(host) - host = "#{controller.request.protocol}#{host}" - end - "#{host}#{source}" - end - - # Pick an asset host for this source. Returns +nil+ if no host is set, - # the host if no wildcard is set, the host interpolated with the - # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4), - # or the value returned from invoking call on an object responding to call - # (proc or otherwise). - def compute_asset_host(source) - if host = config.asset_host - if host.respond_to?(:call) - case host.is_a?(Proc) ? host.arity : host.method(:call).arity - when 2 - request = controller.respond_to?(:request) && controller.request - host.call(source, request) - else - host.call(source) - end - else - (host =~ /%d/) ? host % (source.hash % 4) : host - end - end - end - end - + AssetPaths = ::ActionView::AssetPaths end -end +end
\ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb index 2d49823412..12a304b395 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb @@ -1,11 +1,10 @@ require 'active_support/core_ext/file' -require 'action_view/helpers/asset_paths' module ActionView module Helpers module AssetTagHelper - class AssetPaths < ActionView::Helpers::AssetPaths #:nodoc: + class AssetPaths < ::ActionView::AssetPaths #:nodoc: # You can enable or disable the asset tag ids cache. # With the cache enabled, the asset tag helper methods will make fewer # expensive file system calls (the default implementation checks the file diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index e81d03b537..f81ce3e31c 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -51,7 +51,11 @@ module ActionView # This dance is needed because Builder can't use capture pos = output_buffer.length yield + output_safe = output_buffer.html_safe? fragment = output_buffer.slice!(pos..-1) + if output_safe + self.output_buffer = output_buffer.html_safe + end controller.write_fragment(name, fragment, options) end end diff --git a/actionpack/lib/action_view/helpers/controller_helper.rb b/actionpack/lib/action_view/helpers/controller_helper.rb index db59bca159..1a583e62ae 100644 --- a/actionpack/lib/action_view/helpers/controller_helper.rb +++ b/actionpack/lib/action_view/helpers/controller_helper.rb @@ -20,4 +20,4 @@ module ActionView end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 56e3af683b..6a724749f4 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -621,7 +621,6 @@ module ActionView end class DateTimeSelector #:nodoc: - extend ActiveSupport::Memoizable include ActionView::Helpers::TagHelper DEFAULT_PREFIX = 'date'.freeze @@ -786,11 +785,12 @@ module ActionView # Returns translated month names, but also ensures that a custom month # name array has a leading nil element. def month_names - month_names = @options[:use_month_names] || translated_month_names - month_names.unshift(nil) if month_names.size < 13 - month_names + @month_names ||= begin + month_names = @options[:use_month_names] || translated_month_names + month_names.unshift(nil) if month_names.size < 13 + month_names + end end - memoize :month_names # Returns translated month names. # => [nil, "January", "February", "March", @@ -825,9 +825,8 @@ module ActionView end def date_order - @options[:order] || translated_date_order + @date_order ||= @options[:order] || translated_date_order end - memoize :date_order def translated_date_order I18n.translate(:'date.order', :locale => @options[:locale]) || [] diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb index cd67851642..c0cc7d347c 100644 --- a/actionpack/lib/action_view/helpers/debug_helper.rb +++ b/actionpack/lib/action_view/helpers/debug_helper.rb @@ -30,7 +30,7 @@ module ActionView begin Marshal::dump(object) "<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", " ")}</pre>".html_safe - rescue Exception => e # errors from Marshal or YAML + rescue Exception # errors from Marshal or YAML # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback "<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index cb1c13912a..0ef2357368 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -202,13 +202,13 @@ module ActionView # # is equivalent to something like: # - # <%= form_for @post, :as => :post, :url => post_path(@post), :html => { :class => "new_post", :id => "new_post" } do |f| %> + # <%= form_for @post, :as => :post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %> # ... # <% end %> # # You can also overwrite the individual conventions, like this: # - # <%= form_for(@post, :url => super_post_path(@post)) do |f| %> + # <%= form_for(@post, :url => super_posts_path) do |f| %> # ... # <% end %> # @@ -219,9 +219,9 @@ module ActionView # <% end %> # # If you have an object that needs to be represented as a different - # parameter, like a Client that acts as a Person: + # parameter, like a Person that acts as a Client: # - # <%= form_for(@post, :as => :client) do |f| %> + # <%= form_for(@person, :as => :client) do |f| %> # ... # <% end %> # @@ -290,7 +290,7 @@ module ActionView # # Example: # - # <%= form(@post) do |f| %> + # <%= form_for(@post) do |f| %> # <% f.fields_for(:comments, :include_id => false) do |cf| %> # ... # <% end %> @@ -517,6 +517,18 @@ module ActionView # end # end # + # Note that the <tt>projects_attributes=</tt> writer method is in fact + # required for fields_for to correctly identify <tt>:projects</tt> as a + # collection, and the correct indices to be set in the form markup. + # + # When projects is already an association on Person you can use + # +accepts_nested_attributes_for+ to define the writer method for you: + # + # class Person < ActiveRecord::Base + # has_many :projects + # accepts_nested_attributes_for :projects + # end + # # This model can now be used with a nested fields_for. The block given to # the nested fields_for call will be repeated for each instance in the # collection: @@ -568,14 +580,6 @@ module ActionView # ... # <% end %> # - # When projects is already an association on Person you can use - # +accepts_nested_attributes_for+ to define the writer method for you: - # - # class Person < ActiveRecord::Base - # has_many :projects - # accepts_nested_attributes_for :projects - # end - # # If you want to destroy any of the associated models through the # form, you have to enable it first using the <tt>:allow_destroy</tt> # option for +accepts_nested_attributes_for+: @@ -1237,7 +1241,7 @@ module ActionView end def fields_for(record_name, record_object = nil, fields_options = {}, &block) - fields_options, record_object = record_object, nil if record_object.is_a?(Hash) + fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options? fields_options[:builder] ||= options[:builder] fields_options[:parent_builder] = self diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 794132637c..3dc6d65432 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -128,6 +128,28 @@ module ActionView # By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection # or <tt>:selected => nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option # tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled. + # + # ==== Gotcha + # + # The HTML specification says when +multiple+ parameter passed to select and all options got deselected + # web browsers do not send any value to server. Unfortunately this introduces a gotcha: + # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user + # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So, + # any mass-assignment idiom like + # + # @user.update_attributes(params[:user]) + # + # wouldn't update roles. + # + # To prevent this the helper generates an auxiliary hidden field before + # every multiple select. The hidden field has the same name as multiple select and blank value. + # + # This way, the client either sends only the hidden field (representing + # the deselected multiple select box), or both fields. Since the HTML specification + # says key/value pairs have to be sent in the same order they appear in the + # form, and parameters extraction gets the last occurrence of any repeated + # key in the query string, that works for ordinary forms. + # def select(object, method, choices, options = {}, html_options = {}) InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options) end @@ -427,7 +449,7 @@ module ActionView # # Sample usage (Hash): # grouped_options = { - # 'North America' => [['United States','US], 'Canada'], + # 'North America' => [['United States','US'], 'Canada'], # 'Europe' => ['Denmark','Germany','France'] # } # grouped_options_for_select(grouped_options) @@ -552,43 +574,26 @@ module ActionView include FormOptionsHelper def to_select_tag(choices, options, html_options) - html_options = html_options.stringify_keys - add_default_name_and_id(html_options) - value = value(object) - selected_value = options.has_key?(:selected) ? options[:selected] : value - disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil - content_tag("select", add_options(options_for_select(choices, :selected => selected_value, :disabled => disabled_value), options, selected_value), html_options) + selected_value = options.has_key?(:selected) ? options[:selected] : value(object) + select_content_tag(options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]), options, html_options) end def to_collection_select_tag(collection, value_method, text_method, options, html_options) - html_options = html_options.stringify_keys - add_default_name_and_id(html_options) - value = value(object) - disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil - selected_value = options.has_key?(:selected) ? options[:selected] : value - content_tag( - "select", add_options(options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => disabled_value), options, value), html_options + selected_value = options.has_key?(:selected) ? options[:selected] : value(object) + select_content_tag( + options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options ) end def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) - html_options = html_options.stringify_keys - add_default_name_and_id(html_options) - value = value(object) - content_tag( - "select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options + 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 ) end def to_time_zone_select_tag(priority_zones, options, html_options) - html_options = html_options.stringify_keys - add_default_name_and_id(html_options) - value = value(object) - content_tag("select", - add_options( - time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), - options, value - ), html_options + select_content_tag( + time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options ) end @@ -603,6 +608,17 @@ module ActionView end option_tags.html_safe end + + def select_content_tag(option_tags, options, html_options) + html_options = html_options.stringify_keys + add_default_name_and_id(html_options) + select = content_tag("select", add_options(option_tags, options, value(object)), html_options) + if html_options["multiple"] + tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select + else + select + end + end end class FormBuilder diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index a91e86f4db..72bc4510b5 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -597,6 +597,12 @@ module ActionView number_field_tag(name, value, options.stringify_keys.update("type" => "range")) end + # Creates the hidden UTF8 enforcer tag. Override this method in a helper + # to customize the tag. + def utf8_enforcer_tag + tag(:input, :type => "hidden", :name => "utf8", :value => "✓".html_safe) + end + private def html_options_for_form(url_for_options, options, *parameters_for_url) options.stringify_keys.tap do |html_options| @@ -611,9 +617,6 @@ module ActionView end def extra_tags_for_form(html_options) - snowman_tag = tag(:input, :type => "hidden", - :name => "utf8", :value => "✓".html_safe) - authenticity_token = html_options.delete("authenticity_token") method = html_options.delete("method").to_s @@ -629,7 +632,7 @@ module ActionView tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token) end - tags = snowman_tag << method_tag + tags = utf8_enforcer_tag << method_tag content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline') end diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index d7228bab67..4484390fde 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -18,7 +18,8 @@ module ActionView # $('some_element').replaceWith('<%=j render 'some/element_template' %>'); def escape_javascript(javascript) if javascript - javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] } + result = javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) {|match| JS_ESCAPE_MAP[match] } + javascript.html_safe? ? result.html_safe : result else '' end diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 63d13a0f0b..fe0288521f 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -211,7 +211,7 @@ module ActionView defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {}) options = options.reverse_merge(defaults) - parts = number.to_s.split('.') + parts = number.to_s.to_str.split('.') parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}") parts.join(options[:separator]).html_safe @@ -342,7 +342,7 @@ module ActionView options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros) storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true) - + base = options[:prefix] == :si ? 1000 : 1024 if number.to_i < base diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 75422a343d..ae71ade588 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -255,9 +255,11 @@ module ActionView # simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false) # # => "<p><span>I'm allowed!</span> It's true.</p>" def simple_format(text, html_options={}, options={}) - text = ''.html_safe if text.nil? + text = '' if text.nil? + text = text.dup if text.frozen? start_tag = tag('p', html_options, true) text = sanitize(text) unless options[:sanitize] == false + text = text.to_str text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br @@ -279,7 +281,7 @@ module ActionView # @items = [1,2,3,4] # <table> # <% @items.each do |item| %> - # <tr class="<%= cycle("even", "odd") -%>"> + # <tr class="<%= cycle("odd", "even") -%>"> # <td>item</td> # </tr> # <% end %> diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index fd8fe417d0..26b6e8b599 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -5,7 +5,7 @@ module I18n class ExceptionHandler include Module.new { def call(exception, locale, key, options) - exception.is_a?(MissingTranslation) ? super.html_safe : super + exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super end } end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 25411856cb..d70ae4196b 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -55,9 +55,9 @@ module ActionView # # ==== Relying on named routes # - # If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter, - # you'll trigger the named route for that record. The lookup will happen on the name of the class. So passing - # a Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as + # Passing a record (like an Active Record or Active Resource) instead of a Hash as the options parameter will + # trigger the named route for that record. The lookup will happen on the name of the class. So passing a + # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route). # # ==== Examples @@ -112,13 +112,13 @@ module ActionView end end - # Creates a link tag of the given +name+ using a URL created by the set - # of +options+. See the valid options in the documentation for - # +url_for+. It's also possible to pass a string instead - # of an options hash to get a link tag that uses the value of the string as the - # href for the link, or use <tt>:back</tt> to link to the referrer - a JavaScript back - # link will be used in place of a referrer if none exists. If +nil+ is passed as - # a name, the link itself will become the name. + # Creates a link tag of the given +name+ using a URL created by the set of +options+. + # See the valid options in the documentation for +url_for+. It's also possible to + # pass a String instead of an options hash, which generates a link tag that uses the + # value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead + # of an options hash will generate a link to the referrer (a JavaScript back link + # will be used in place of a referrer if none exists). If +nil+ is passed as the name + # the value of the link itself will become the name. # # ==== Signatures # @@ -497,14 +497,14 @@ module ActionView }.compact extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&')) - email_address_obfuscated = email_address.dup + email_address_obfuscated = email_address.to_str email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at") email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot") case encode when "javascript" string = '' html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)) - html = escape_javascript(html) + html = escape_javascript(html.to_str) "document.write('#{html}');".each_byte do |c| string << sprintf("%%%x", c) end @@ -619,7 +619,9 @@ module ActionView end def add_method_to_attributes!(html_options, method) - html_options["rel"] = "nofollow" if method.to_s.downcase != "get" + if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/ + html_options["rel"] = "#{html_options["rel"]} nofollow".strip + end html_options["data-method"] = method end diff --git a/actionpack/lib/action_view/log_subscriber.rb b/actionpack/lib/action_view/log_subscriber.rb index 29ffbd6fdd..bf90d012bf 100644 --- a/actionpack/lib/action_view/log_subscriber.rb +++ b/actionpack/lib/action_view/log_subscriber.rb @@ -4,7 +4,7 @@ module ActionView # Provides functionality so that Rails can output logs from Action View. class LogSubscriber < ActiveSupport::LogSubscriber def render_template(event) - message = "Rendered #{from_rails_root(event.payload[:identifier])}" + message = " Rendered #{from_rails_root(event.payload[:identifier])}" message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout] message << (" (%.1fms)" % event.duration) info(message) diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index f0ed3425de..560df15e82 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -10,7 +10,7 @@ module ActionView # this key is generated just once during the request, it speeds up all cache accesses. class LookupContext #:nodoc: attr_accessor :prefixes - + mattr_accessor :fallbacks @@fallbacks = FallbackFileSystemResolver.instances diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index a09cef8fef..d04c00fd40 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -64,7 +64,7 @@ module ActionView layout =~ /^\// ? with_fallbacks { find_template(layout, nil, false, keys) } : find_template(layout, nil, false, keys) end - rescue ActionView::MissingTemplate => e + rescue ActionView::MissingTemplate update_details(:formats => nil) do raise unless template_exists?(layout) end diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index d4448a7b33..587e37a84f 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -31,7 +31,6 @@ module ActionView def initialize(paths, path, prefixes, partial, details, *) @path = path prefixes = Array.wrap(prefixes) - display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ") template_type = if partial "partial" elsif path =~ /layouts/i diff --git a/actionpack/lib/action_view/testing/resolvers.rb b/actionpack/lib/action_view/testing/resolvers.rb index 773dfcbb1d..7afa2fa613 100644 --- a/actionpack/lib/action_view/testing/resolvers.rb +++ b/actionpack/lib/action_view/testing/resolvers.rb @@ -34,7 +34,7 @@ module ActionView #:nodoc: templates << Template.new(source, _path, handler, :virtual_path => path.virtual, :format => format, :updated_at => updated_at) end - + templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size } end end diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb index a99dcad81d..9f1f0f3b68 100644 --- a/actionpack/lib/sprockets/helpers/rails_helper.rb +++ b/actionpack/lib/sprockets/helpers/rails_helper.rb @@ -1,5 +1,4 @@ -require "action_view/helpers/asset_paths" -require "action_view/helpers/asset_tag_helper" +require "action_view/helpers" module Sprockets module Helpers @@ -15,42 +14,48 @@ module Sprockets end end - def javascript_include_tag(source, options = {}) + 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 - if debug && asset = asset_paths.asset_for(source, 'js') - asset.to_a.map { |dep| - javascript_include_tag(dep, :debug => false, :body => true) - }.join("\n").html_safe - else - options = { - 'type' => "text/javascript", - 'src' => asset_path(source, 'js', body) - }.merge(options.stringify_keys) - - content_tag 'script', "", options - end + sources.collect do |source| + if debug && asset = asset_paths.asset_for(source, 'js') + asset.to_a.map { |dep| + javascript_include_tag(dep, :debug => false, :body => true) + }.join("\n").html_safe + else + tag_options = { + 'type' => "text/javascript", + 'src' => asset_path(source, 'js', body) + }.merge(options.stringify_keys) + + content_tag 'script', "", tag_options + end + end.join("\n").html_safe end - def stylesheet_link_tag(source, options = {}) + 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 - if debug && asset = asset_paths.asset_for(source, 'css') - asset.to_a.map { |dep| - stylesheet_link_tag(dep, :debug => false, :body => true) - }.join("\n").html_safe - else - options = { - 'rel' => "stylesheet", - 'type' => "text/css", - 'media' => "screen", - 'href' => asset_path(source, 'css', body) - }.merge(options.stringify_keys) - - tag 'link', options - end + sources.collect do |source| + if debug && asset = asset_paths.asset_for(source, 'css') + asset.to_a.map { |dep| + stylesheet_link_tag(dep, :debug => false, :body => true) + }.join("\n").html_safe + else + tag_options = { + 'rel' => "stylesheet", + 'type' => "text/css", + 'media' => "screen", + 'href' => asset_path(source, 'css', body) + }.merge(options.stringify_keys) + + tag 'link', tag_options + end + end.join("\n").html_safe end private @@ -61,13 +66,13 @@ module Sprockets def asset_path(source, default_ext = nil, body = false) source = source.logical_path if source.respond_to?(:logical_path) - path = asset_paths.compute_public_path(source, 'assets', default_ext, true) + path = asset_paths.compute_public_path(source, Rails.application.config.assets.prefix, default_ext, true) body ? "#{path}?body=1" : path end - class AssetPaths < ActionView::Helpers::AssetPaths #:nodoc: + class AssetPaths < ::ActionView::AssetPaths #:nodoc: def compute_public_path(source, dir, ext=nil, include_host=true) - super(source, 'assets', ext, include_host) + super(source, Rails.application.config.assets.prefix, ext, include_host) end def asset_for(source, ext) diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index 7b8a7ad3bb..ab5101f6fc 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -46,43 +46,61 @@ module Sprockets end protected + def asset_environment(app) + require "sprockets" - def asset_environment(app) - require "sprockets" - assets = app.config.assets - env = Sprockets::Environment.new(app.root.to_s) - env.static_root = File.join(app.root.join("public"), assets.prefix) - env.paths.concat assets.paths - env.logger = Rails.logger - env.js_compressor = expand_js_compressor(assets.js_compressor) if assets.compress - env.css_compressor = expand_css_compressor(assets.css_compressor) if assets.compress - env - end + assets = app.config.assets + + env = Sprockets::Environment.new(app.root.to_s) + + env.static_root = File.join(app.root.join("public"), assets.prefix) + + if env.respond_to?(:append_path) + assets.paths.each { |path| env.append_path(path) } + else + env.paths.concat assets.paths + end + + env.logger = Rails.logger + + if env.respond_to?(:cache) + env.cache = Rails.cache + end - def expand_js_compressor(sym) - case sym - when :closure - require 'closure-compiler' - Closure::Compiler.new - when :uglifier - require 'uglifier' - Uglifier.new - when :yui - require 'yui/compressor' - YUI::JavaScriptCompressor.new - else - sym + if assets.compress + # temporarily hardcode default JS compressor to uglify. Soon, it will work + # the same as SCSS, where a default plugin sets the default. + env.js_compressor = expand_js_compressor(assets.js_compressor || :uglifier) + env.css_compressor = expand_css_compressor(assets.css_compressor) + end + + env + end + + def expand_js_compressor(sym) + case sym + when :closure + require 'closure-compiler' + Closure::Compiler.new + when :uglifier + require 'uglifier' + Uglifier.new + when :yui + require 'yui/compressor' + YUI::JavaScriptCompressor.new + else + sym + end end - end - def expand_css_compressor(sym) - case sym - when :yui - require 'yui/compressor' - YUI::CssCompressor.new - else - sym + def expand_css_compressor(sym) + case sym + when :yui + require 'yui/compressor' + YUI::CssCompressor.new + else + sym + end end - end end end |