aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile4
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb1
-rw-r--r--actionpack/lib/abstract_controller/asset_paths.rb2
-rw-r--r--actionpack/lib/action_controller/railtie.rb1
-rw-r--r--actionpack/lib/action_view.rb1
-rw-r--r--actionpack/lib/action_view/asset_paths.rb143
-rw-r--r--actionpack/lib/action_view/helpers.rb4
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb93
-rw-r--r--actionpack/lib/action_view/helpers/asset_url_helper.rb259
-rw-r--r--actionpack/lib/action_view/railtie.rb8
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb279
-rw-r--r--actionpack/test/template/sanitize_helper_test.rb6
-rw-r--r--activerecord/CHANGELOG.md7
-rw-r--r--activerecord/lib/active_record/relation.rb8
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb16
-rw-r--r--activerecord/test/cases/calculations_test.rb4
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/object/conversions.rb4
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb1
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb1
-rw-r--r--guides/source/4_0_release_notes.md4
-rw-r--r--railties/lib/rails/application/configuration.rb6
-rw-r--r--railties/test/application/configuration_test.rb20
25 files changed, 351 insertions, 536 deletions
diff --git a/Gemfile b/Gemfile
index 0cb4cad48d..6e0f43c526 100644
--- a/Gemfile
+++ b/Gemfile
@@ -41,8 +41,8 @@ instance_eval File.read local_gemfile if File.exists? local_gemfile
platforms :mri do
group :test do
- gem 'ruby-prof', '~> 0.11.2'
- gem 'debugger' if !ENV['TRAVIS'] && RUBY_VERSION < "2.0" && RUBY_PATCHLEVEL < 286
+ gem 'ruby-prof', '~> 0.11.2' if RUBY_VERSION < '2.0'
+ gem 'debugger' if !ENV['TRAVIS'] && RUBY_VERSION < '2.0' && RUBY_PATCHLEVEL < 286
end
end
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index abf6ad80cf..59dc26841f 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -22,7 +22,6 @@ module ActionMailer
options.queue ||= app.queue
# make sure readers methods get compiled
- options.asset_path ||= app.config.asset_path
options.asset_host ||= app.config.asset_host
options.relative_url_root ||= app.config.relative_url_root
diff --git a/actionpack/lib/abstract_controller/asset_paths.rb b/actionpack/lib/abstract_controller/asset_paths.rb
index 822254b1a4..e6170228d9 100644
--- a/actionpack/lib/abstract_controller/asset_paths.rb
+++ b/actionpack/lib/abstract_controller/asset_paths.rb
@@ -3,7 +3,7 @@ module AbstractController
extend ActiveSupport::Concern
included do
- config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir,
+ config_accessor :asset_host, :assets_dir, :javascripts_dir,
:stylesheets_dir, :default_asset_host_protocol, :relative_url_root
end
end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index ee0f053bad..3e44155f73 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -34,7 +34,6 @@ module ActionController
options.stylesheets_dir ||= paths["public/stylesheets"].first
# Ensure readers methods get compiled
- options.asset_path ||= app.config.asset_path
options.asset_host ||= app.config.asset_host
options.relative_url_root ||= app.config.relative_url_root
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 091b0d8cd2..8bbf52382a 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -29,7 +29,6 @@ module ActionView
extend ActiveSupport::Autoload
eager_autoload do
- autoload :AssetPaths
autoload :Base
autoload :Context
autoload :CompiledTemplates, "action_view/context"
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb
deleted file mode 100644
index 4bbb31b3ee..0000000000
--- a/actionpack/lib/action_view/asset_paths.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-require 'zlib'
-require 'active_support/core_ext/file'
-
-module ActionView
- class AssetPaths #:nodoc:
- URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
-
- attr_reader :config, :controller
-
- def initialize(config, controller = nil)
- @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.
- #
- # When :relative (default), the protocol will be determined by the client using current protocol
- # When :request, the protocol will be the request protocol
- # Otherwise, the protocol is used (E.g. :http, :https, etc)
- def compute_public_path(source, dir, options = {})
- source = source.to_s
- return source if is_uri?(source)
-
- source = rewrite_extension(source, dir, options[:ext]) if options[:ext]
- source = rewrite_asset_path(source, dir, options)
- source = rewrite_relative_url_root(source, relative_url_root)
- source = rewrite_host_and_protocol(source, options[:protocol])
- source
- end
-
- # Return the filesystem path for the source
- def compute_source_path(source, dir, ext)
- source = rewrite_extension(source, dir, ext) if ext
-
- sources = []
- sources << config.assets_dir
- sources << dir unless source[0] == ?/
- sources << source
-
- File.join(sources)
- end
-
- def is_uri?(path)
- path =~ URI_REGEXP
- 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 has_request?
- controller.respond_to?(:request)
- end
-
- def rewrite_host_and_protocol(source, protocol = nil)
- host = compute_asset_host(source)
- if host && !is_uri?(host)
- if (protocol || default_protocol) == :request && !has_request?
- host = nil
- else
- host = "#{compute_protocol(protocol)}#{host}"
- end
- end
- host ? "#{host}#{source}" : source
- end
-
- def compute_protocol(protocol)
- protocol ||= default_protocol
- case protocol
- when :relative
- "//"
- when :request
- unless @controller
- invalid_asset_host!("The protocol requested was :request. Consider using :relative instead.")
- end
- @controller.request.protocol
- else
- "#{protocol}://"
- end
- end
-
- def default_protocol
- @config.default_asset_host_protocol || (has_request? ? :request : :relative)
- end
-
- def invalid_asset_host!(help_message)
- raise ActionView::MissingRequestError, "This asset host cannot be computed without a request in scope. #{help_message}"
- 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 = asset_host_config
- if host.respond_to?(:call)
- args = [source]
- arity = arity_of(host)
- if (arity > 1 || arity < -2) && !has_request?
- invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request, or make it optional.")
- end
- args << current_request if (arity > 1 || arity < 0) && has_request?
- host.call(*args)
- else
- (host =~ /%d/) ? host % (Zlib.crc32(source) % 4) : host
- end
- end
- end
-
- def relative_url_root
- config.relative_url_root || current_request.try(:script_name)
- end
-
- def asset_host_config
- config.asset_host
- end
-
- # Returns the current request if one exists.
- def current_request
- controller.request if has_request?
- end
-
- # Returns the arity of a callable
- def arity_of(callable)
- callable.respond_to?(:arity) ? callable.arity : callable.method(:call).arity
- end
-
- end
-end
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index dad50a58a1..269e78a021 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -29,10 +29,6 @@ module ActionView #:nodoc:
extend ActiveSupport::Concern
- included do
- extend SanitizeHelper::ClassMethods
- end
-
include ActiveModelHelper
include AssetTagHelper
include AssetUrlHelper
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
deleted file mode 100644
index 35f91cec18..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-require 'thread'
-require 'active_support/core_ext/file'
-require 'active_support/core_ext/module/attribute_accessors'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- 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
- # system timestamp). However this prevents you from modifying any asset
- # files while the server is running.
- #
- # ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
- mattr_accessor :cache_asset_ids
-
- # Add or change an asset id in the asset id cache. This can be used
- # for SASS on Heroku.
- # :api: public
- def add_to_asset_ids_cache(source, asset_id)
- self.asset_ids_cache_guard.synchronize do
- self.asset_ids_cache[source] = asset_id
- end
- end
-
- private
-
- def rewrite_extension(source, dir, ext)
- source_ext = File.extname(source)
-
- source_with_ext = if source_ext.empty?
- "#{source}.#{ext}"
- elsif ext != source_ext[1..-1]
- with_ext = "#{source}.#{ext}"
- with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext))
- end
-
- source_with_ext || source
- end
-
- # Break out the asset path rewrite in case plugins wish to put the asset id
- # someplace other than the query string.
- def rewrite_asset_path(source, dir, options = nil)
- source = "/#{dir}/#{source}" unless source[0] == ?/
- path = config.asset_path
-
- if path && path.respond_to?(:call)
- return path.call(source)
- elsif path && path.is_a?(String)
- return path % [source]
- end
-
- asset_id = rails_asset_id(source)
- if asset_id.empty?
- source
- else
- "#{source}?#{asset_id}"
- end
- end
-
- mattr_accessor :asset_ids_cache
- self.asset_ids_cache = {}
-
- mattr_accessor :asset_ids_cache_guard
- self.asset_ids_cache_guard = Mutex.new
-
- # Use the RAILS_ASSET_ID environment variable or the source's
- # modification time as its cache-busting asset id.
- def rails_asset_id(source)
- if asset_id = ENV["RAILS_ASSET_ID"]
- asset_id
- else
- if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source])
- asset_id
- else
- path = File.join(config.assets_dir, source)
- asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
-
- if self.cache_asset_ids
- add_to_asset_ids_cache(source, asset_id)
- end
-
- asset_id
- end
- end
- end
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_url_helper.rb b/actionpack/lib/action_view/helpers/asset_url_helper.rb
index 4554c0c473..0bb5e739bb 100644
--- a/actionpack/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_url_helper.rb
@@ -1,4 +1,4 @@
-require 'action_view/helpers/asset_tag_helpers/asset_paths'
+require 'zlib'
module ActionView
# = Action View Asset URL Helpers
@@ -104,92 +104,122 @@ module ActionView
# "http://asset%d.example.com", "https://asset1.example.com"
# )
#
- # === Customizing the asset path
- #
- # By default, Rails appends asset's timestamps to all asset paths. This allows
- # you to set a cache-expiration date for the asset far into the future, but
- # still be able to instantly invalidate it by simply updating the file (and
- # hence updating the timestamp, which then updates the URL as the timestamp
- # is part of that, which in turn busts the cache).
- #
- # It's the responsibility of the web server you use to set the far-future
- # expiration date on cache assets that you need to take advantage of this
- # feature. Here's an example for Apache:
- #
- # # Asset Expiration
- # ExpiresActive On
- # <FilesMatch "\.(ico|gif|jpe?g|png|js|css)$">
- # ExpiresDefault "access plus 1 year"
- # </FilesMatch>
- #
- # Also note that in order for this to work, all your application servers must
- # return the same timestamps. This means that they must have their clocks
- # synchronized. If one of them drifts out of sync, you'll see different
- # timestamps at random and the cache won't work. In that case the browser
- # will request the same assets over and over again even thought they didn't
- # change. You can use something like Live HTTP Headers for Firefox to verify
- # that the cache is indeed working.
- #
- # This strategy works well enough for most server setups and requires the
- # least configuration, but if you deploy several application servers at
- # different times - say to handle a temporary spike in load - then the
- # asset time stamps will be out of sync. In a setup like this you may want
- # to set the way that asset paths are generated yourself.
- #
- # Altering the asset paths that Rails generates can be done in two ways.
- # The easiest is to define the RAILS_ASSET_ID environment variable. The
- # contents of this variable will always be used in preference to
- # calculated timestamps. A more complex but flexible way is to set
- # <tt>ActionController::Base.config.asset_path</tt> to a proc
- # that takes the unmodified asset path and returns the path needed for
- # your asset caching to work. Typically you'd do something like this in
- # <tt>config/environments/production.rb</tt>:
- #
- # # Normally you'd calculate RELEASE_NUMBER at startup.
- # RELEASE_NUMBER = 12345
- # config.action_controller.asset_path = proc { |asset_path|
- # "/release-#{RELEASE_NUMBER}#{asset_path}"
- # }
- #
- # This example would cause the following behavior on all servers no
- # matter when they were deployed:
- #
- # image_tag("rails.png")
- # # => <img alt="Rails" src="/release-12345/images/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="/release-12345/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" />
- #
- # Changing the asset_path does require that your web servers have
- # knowledge of the asset template paths that you rewrite to so it's not
- # suitable for out-of-the-box use. To use the example given above you
- # could use something like this in your Apache VirtualHost configuration:
- #
- # <LocationMatch "^/release-\d+/(images|javascripts|stylesheets)/.*$">
- # # Some browsers still send conditional-GET requests if there's a
- # # Last-Modified header or an ETag header even if they haven't
- # # reached the expiry date sent in the Expires header.
- # Header unset Last-Modified
- # Header unset ETag
- # FileETag None
- #
- # # Assets requested using a cache-busting filename should be served
- # # only once and then cached for a really long time. The HTTP/1.1
- # # spec frowns on hugely-long expiration times though and suggests
- # # that assets which never expire be served with an expiration date
- # # 1 year from access.
- # ExpiresActive On
- # ExpiresDefault "access plus 1 year"
- # </LocationMatch>
- #
- # # We use cached-busting location names with the far-future expires
- # # headers to ensure that if a file does change it can force a new
- # # request. The actual asset filenames are still the same though so we
- # # need to rewrite the location from the cache-busting location to the
- # # real asset location so that we can serve it.
- # RewriteEngine On
- # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L]
- #
module AssetUrlHelper
+ URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
+
+ # Computes the path to asset in public directory. If :type
+ # options is set, a file extension will be appended and scoped
+ # to the corresponding public directory.
+ #
+ # All other asset *_path helpers delegate through this method.
+ #
+ # asset_path "application.js" # => /application.js
+ # asset_path "application", type: :javascript # => /javascripts/application.js
+ # asset_path "application", type: :stylesheet # => /stylesheets/application.css
+ # asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
+ def asset_path(source, options = {})
+ source = source.to_s
+ return "" unless source.present?
+ return source if source =~ URI_REGEXP
+
+ tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
+
+ if extname = compute_asset_extname(source, options)
+ source = "#{source}#{extname}"
+ end
+
+ if source[0] != ?/
+ source = compute_asset_path(source, options)
+ end
+
+ relative_url_root = (defined?(config.relative_url_root) && config.relative_url_root) ||
+ (respond_to?(:request) && request.try(:script_name))
+ if relative_url_root
+ source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/")
+ end
+
+ if host = compute_asset_host(source, options)
+ source = "#{host}#{source}"
+ end
+
+ "#{source}#{tail}"
+ end
+ alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route
+
+ # Computes the full URL to a asset in the public directory. This
+ # will use +asset_path+ internally, so most of their behaviors
+ # will be the same.
+ def asset_url(source, options = {})
+ path_to_asset(source, options.merge(:protocol => :request))
+ end
+ alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route
+
+ ASSET_EXTENSIONS = {
+ javascript: '.js',
+ stylesheet: '.css'
+ }
+
+ # Compute extname to append to asset path. Returns nil if
+ # nothing should be added.
+ def compute_asset_extname(source, options = {})
+ return if options[:extname] == false
+ extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
+ extname if extname && File.extname(source) != extname
+ end
+
+ # Maps asset types to public directory.
+ ASSET_PUBLIC_DIRECTORIES = {
+ audio: '/audios',
+ font: '/fonts',
+ image: '/images',
+ javascript: '/javascripts',
+ stylesheet: '/stylesheets',
+ video: '/videos'
+ }
+
+ # Computes asset path to public directory. Plugins and
+ # extensions can override this method to point to custom assets
+ # or generate digested paths or query strings.
+ def compute_asset_path(source, options = {})
+ dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
+ File.join(dir, 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 = "", options = {})
+ request = self.request if respond_to?(:request)
+ host = config.asset_host if defined? config.asset_host
+ host ||= request.base_url if request && options[:protocol] == :request
+ return unless host
+
+ if host.respond_to?(:call)
+ arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
+ args = [source]
+ args << request if request && (arity > 1 || arity < 0)
+ host = host.call(*args)
+ elsif host =~ /%d/
+ host = host % (Zlib.crc32(source) % 4)
+ end
+
+ if host =~ URI_REGEXP
+ host
+ else
+ protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
+ case protocol
+ when :relative
+ "//#{host}"
+ when :request
+ "#{request.protocol}#{host}"
+ else
+ "#{protocol}://#{host}"
+ end
+ end
+ end
+
# Computes the path to a javascript asset in the public javascripts directory.
# If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
# Full paths from the document root will be passed through.
@@ -200,15 +230,15 @@ module ActionView
# javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
# javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
# javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
- def javascript_path(source)
- asset_paths.compute_public_path(source, 'javascripts', :ext => 'js')
+ def javascript_path(source, options = {})
+ path_to_asset(source, {type: :javascript}.merge!(options))
end
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
# Computes the full URL to a javascript asset in the public javascripts directory.
# This will use +javascript_path+ internally, so most of their behaviors will be the same.
- def javascript_url(source)
- URI.join(current_host, path_to_javascript(source)).to_s
+ def javascript_url(source, options = {})
+ url_to_asset(source, {type: :javascript}.merge!(options))
end
alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
@@ -222,15 +252,15 @@ module ActionView
# stylesheet_path "/dir/style.css" # => /dir/style.css
# stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
# stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
- def stylesheet_path(source)
- asset_paths.compute_public_path(source, 'stylesheets', :ext => 'css', :protocol => :request)
+ def stylesheet_path(source, options = {})
+ path_to_asset(source, {type: :stylesheet}.merge!(options))
end
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
# Computes the full URL to a stylesheet asset in the public stylesheets directory.
# This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
- def stylesheet_url(source)
- URI.join(current_host, path_to_stylesheet(source)).to_s
+ def stylesheet_url(source, options = {})
+ url_to_asset(source, {type: :stylesheet}.merge!(options))
end
alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
@@ -247,15 +277,15 @@ module ActionView
# If you have images as application resources this method may conflict with their named routes.
# The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
# plugin authors are encouraged to do so.
- def image_path(source)
- source.present? ? asset_paths.compute_public_path(source, 'images') : ""
+ def image_path(source, options = {})
+ path_to_asset(source, {type: :image}.merge!(options))
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
# Computes the full URL to an image asset.
# This will use +image_path+ internally, so most of their behaviors will be the same.
- def image_url(source)
- URI.join(current_host, path_to_image(source)).to_s
+ def image_url(source, options = {})
+ url_to_asset(source, {type: :image}.merge!(options))
end
alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
@@ -268,15 +298,15 @@ module ActionView
# video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
# video_path("/trailers/hd.avi") # => /trailers/hd.avi
# video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
- def video_path(source)
- asset_paths.compute_public_path(source, 'videos')
+ def video_path(source, options = {})
+ path_to_asset(source, {type: :video}.merge!(options))
end
alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
# Computes the full URL to a video asset in the public videos directory.
# This will use +video_path+ internally, so most of their behaviors will be the same.
- def video_url(source)
- URI.join(current_host, path_to_video(source)).to_s
+ def video_url(source, options = {})
+ url_to_asset(source, {type: :video}.merge!(options))
end
alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
@@ -289,15 +319,15 @@ module ActionView
# audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
# audio_path("/sounds/horse.wav") # => /sounds/horse.wav
# audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
- def audio_path(source)
- asset_paths.compute_public_path(source, 'audios')
+ def audio_path(source, options = {})
+ path_to_asset(source, {type: :audio}.merge!(options))
end
alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
# Computes the full URL to an audio asset in the public audios directory.
# This will use +audio_path+ internally, so most of their behaviors will be the same.
- def audio_url(source)
- URI.join(current_host, path_to_audio(source)).to_s
+ def audio_url(source, options = {})
+ url_to_asset(source, {type: :audio}.merge!(options))
end
alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
@@ -309,26 +339,17 @@ module ActionView
# font_path("dir/font.ttf") # => /assets/dir/font.ttf
# font_path("/dir/font.ttf") # => /dir/font.ttf
# font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
- def font_path(source)
- asset_paths.compute_public_path(source, 'fonts')
+ def font_path(source, options = {})
+ path_to_asset(source, {type: :font}.merge!(options))
end
alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
# Computes the full URL to a font asset.
# This will use +font_path+ internally, so most of their behaviors will be the same.
- def font_url(source)
- URI.join(current_host, path_to_font(source)).to_s
+ def font_url(source, options = {})
+ url_to_asset(source, {type: :font}.merge!(options))
end
alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
-
- private
- def asset_paths
- @asset_paths ||= AssetTagHelper::AssetPaths.new(config, controller)
- end
-
- def current_host
- url_for(:only_path => false)
- end
end
end
end
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 55f6ea5522..3875d88a9f 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -20,14 +20,6 @@ module ActionView
ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
end
- initializer "action_view.cache_asset_ids" do |app|
- unless app.config.cache_classes
- ActiveSupport.on_load(:action_view) do
- ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
- end
- end
- end
-
initializer "action_view.set_configs" do |app|
ActiveSupport.on_load(:action_view) do
app.config.action_view.each do |k,v|
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 754622c883..eb1a54a81f 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -13,27 +13,10 @@ end
class AssetTagHelperTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
+ attr_reader :request
+
def setup
super
- silence_warnings do
- ActionView::Helpers::AssetTagHelper.send(
- :const_set,
- :JAVASCRIPTS_DIR,
- File.dirname(__FILE__) + "/../fixtures/public/javascripts"
- )
-
- ActionView::Helpers::AssetTagHelper.send(
- :const_set,
- :STYLESHEETS_DIR,
- File.dirname(__FILE__) + "/../fixtures/public/stylesheets"
- )
-
- ActionView::Helpers::AssetTagHelper.send(
- :const_set,
- :ASSETS_DIR,
- File.dirname(__FILE__) + "/../fixtures/public"
- )
- end
@controller = BasicController.new
@@ -42,6 +25,7 @@ class AssetTagHelperTest < ActionView::TestCase
def protocol() 'http://' end
def ssl?() false end
def host_with_port() 'localhost' end
+ def base_url() 'http://www.example.com' end
end.new
@controller.request = @request
@@ -51,9 +35,23 @@ class AssetTagHelperTest < ActionView::TestCase
"http://www.example.com"
end
- def teardown
- ENV.delete('RAILS_ASSET_ID')
- end
+ AssetPathToTag = {
+ %(asset_path("foo")) => %(/foo),
+ %(asset_path("style.css")) => %(/style.css),
+ %(asset_path("xmlhr.js")) => %(/xmlhr.js),
+ %(asset_path("xml.png")) => %(/xml.png),
+ %(asset_path("dir/xml.png")) => %(/dir/xml.png),
+ %(asset_path("/dir/xml.png")) => %(/dir/xml.png),
+
+ %(asset_path("script.min")) => %(/script.min),
+ %(asset_path("script.min.js")) => %(/script.min.js),
+ %(asset_path("style.min")) => %(/style.min),
+ %(asset_path("style.min.css")) => %(/style.min.css),
+
+ %(asset_path("style", type: :stylesheet)) => %(/stylesheets/style.css),
+ %(asset_path("xmlhr", type: :javascript)) => %(/javascripts/xmlhr.js),
+ %(asset_path("xml.png", type: :image)) => %(/images/xml.png)
+ }
AutoDiscoveryToTag = {
%(auto_discovery_link_tag) => %(<link href="http://www.example.com" rel="alternate" title="RSS" type="application/rss+xml" />),
@@ -73,7 +71,14 @@ class AssetTagHelperTest < ActionView::TestCase
JavascriptPathToTag = {
%(javascript_path("xmlhr")) => %(/javascripts/xmlhr.js),
%(javascript_path("super/xmlhr")) => %(/javascripts/super/xmlhr.js),
- %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js)
+ %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js),
+ %(javascript_path("xmlhr.min")) => %(/javascripts/xmlhr.min.js),
+ %(javascript_path("xmlhr.min.js")) => %(/javascripts/xmlhr.min.js),
+
+ %(javascript_path("xmlhr.js?123")) => %(/javascripts/xmlhr.js?123),
+ %(javascript_path("xmlhr.js?body=1")) => %(/javascripts/xmlhr.js?body=1),
+ %(javascript_path("xmlhr.js#hash")) => %(/javascripts/xmlhr.js#hash),
+ %(javascript_path("xmlhr.js?123#hash")) => %(/javascripts/xmlhr.js?123#hash)
}
PathToJavascriptToTag = {
@@ -98,7 +103,6 @@ class AssetTagHelperTest < ActionView::TestCase
%(javascript_include_tag("bank")) => %(<script src="/javascripts/bank.js" ></script>),
%(javascript_include_tag("bank.js")) => %(<script src="/javascripts/bank.js" ></script>),
%(javascript_include_tag("bank", :lang => "vbscript")) => %(<script lang="vbscript" src="/javascripts/bank.js" ></script>),
- %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(<script src="/javascripts/common.javascript" ></script>\n<script src="/elsewhere/cools.js" ></script>),
%(javascript_include_tag("http://example.com/all")) => %(<script src="http://example.com/all"></script>),
%(javascript_include_tag("http://example.com/all.js")) => %(<script src="http://example.com/all.js"></script>),
@@ -109,14 +113,17 @@ class AssetTagHelperTest < ActionView::TestCase
%(stylesheet_path("bank")) => %(/stylesheets/bank.css),
%(stylesheet_path("bank.css")) => %(/stylesheets/bank.css),
%(stylesheet_path('subdir/subdir')) => %(/stylesheets/subdir/subdir.css),
- %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css)
+ %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css),
+ %(stylesheet_path("style.min")) => %(/stylesheets/style.min.css),
+ %(stylesheet_path("style.min.css")) => %(/stylesheets/style.min.css)
}
PathToStyleToTag = {
%(path_to_stylesheet("style")) => %(/stylesheets/style.css),
%(path_to_stylesheet("style.css")) => %(/stylesheets/style.css),
%(path_to_stylesheet('dir/file')) => %(/stylesheets/dir/file.css),
- %(path_to_stylesheet('/dir/file.rcss')) => %(/dir/file.rcss)
+ %(path_to_stylesheet('/dir/file.rcss', :extname => false)) => %(/dir/file.rcss),
+ %(path_to_stylesheet('/dir/file', :extname => '.rcss')) => %(/dir/file.rcss)
}
StyleUrlToTag = {
@@ -130,7 +137,8 @@ class AssetTagHelperTest < ActionView::TestCase
%(url_to_stylesheet("style")) => %(http://www.example.com/stylesheets/style.css),
%(url_to_stylesheet("style.css")) => %(http://www.example.com/stylesheets/style.css),
%(url_to_stylesheet('dir/file')) => %(http://www.example.com/stylesheets/dir/file.css),
- %(url_to_stylesheet('/dir/file.rcss')) => %(http://www.example.com/dir/file.rcss)
+ %(url_to_stylesheet('/dir/file.rcss', :extname => false)) => %(http://www.example.com/dir/file.rcss),
+ %(url_to_stylesheet('/dir/file', :extname => '.rcss')) => %(http://www.example.com/dir/file.rcss)
}
StyleLinkToTag = {
@@ -139,7 +147,6 @@ class AssetTagHelperTest < ActionView::TestCase
%(stylesheet_link_tag("/elsewhere/file")) => %(<link href="/elsewhere/file.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("subdir/subdir")) => %(<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("bank", :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" />),
- %(stylesheet_link_tag("random.styles", "/elsewhere/file")) => %(<link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />\n<link href="/elsewhere/file.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("http://www.example.com/styles/style")) => %(<link href="http://www.example.com/styles/style" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("http://www.example.com/styles/style.css")) => %(<link href="http://www.example.com/styles/style.css" media="screen" rel="stylesheet" />),
@@ -283,6 +290,14 @@ class AssetTagHelperTest < ActionView::TestCase
%(audio_tag(["audio.mp3", "audio.ogg"], :autobuffer => true, :controls => true)) => %(<audio autobuffer="autobuffer" controls="controls"><source src="/audios/audio.mp3" /><source src="/audios/audio.ogg" /></audio>)
}
+ FontPathToTag = {
+ %(font_path("font.eot")) => %(/fonts/font.eot),
+ %(font_path("font.eot#iefix")) => %(/fonts/font.eot#iefix),
+ %(font_path("font.woff")) => %(/fonts/font.woff),
+ %(font_path("font.ttf")) => %(/fonts/font.ttf),
+ %(font_path("font.ttf?123")) => %(/fonts/font.ttf?123)
+ }
+
def test_autodiscovery_link_tag_deprecated_types
result = nil
assert_deprecated do
@@ -293,6 +308,18 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal expected, result
end
+ def test_asset_path_tag
+ AssetPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_compute_asset_public_path
+ assert_equal "/robots.txt", compute_asset_path("robots.txt")
+ assert_equal "/robots.txt", compute_asset_path("/robots.txt")
+ assert_equal "/javascripts/foo.js", compute_asset_path("foo.js", :type => :javascript)
+ assert_equal "/javascripts/foo.js", compute_asset_path("/foo.js", :type => :javascript)
+ assert_equal "/stylesheets/foo.css", compute_asset_path("foo.css", :type => :stylesheet)
+ end
+
def test_auto_discovery_link_tag
AutoDiscoveryToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -313,8 +340,7 @@ class AssetTagHelperTest < ActionView::TestCase
UrlToJavascriptToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
- def test_javascript_include_tag_with_blank_asset_id
- ENV["RAILS_ASSET_ID"] = ""
+ def test_javascript_include_tag
JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -332,17 +358,7 @@ class AssetTagHelperTest < ActionView::TestCase
assert javascript_include_tag("prototype").html_safe?
end
- def test_all_javascript_expansion_not_include_application_js_if_not_exists
- FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'),
- File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'))
- assert_no_match(/application\.js/, javascript_include_tag(:all))
- ensure
- FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'),
- File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'))
- end
-
def test_stylesheet_path
- ENV["RAILS_ASSET_ID"] = ""
StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -351,7 +367,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_stylesheet_url
- ENV["RAILS_ASSET_ID"] = ""
StyleUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -360,7 +375,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_stylesheet_link_tag
- ENV["RAILS_ASSET_ID"] = ""
StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -375,7 +389,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_stylesheet_link_tag_is_html_safe
- ENV["RAILS_ASSET_ID"] = ""
assert stylesheet_link_tag('dir/file').html_safe?
assert stylesheet_link_tag('dir/other/file', 'dir/file2').html_safe?
end
@@ -385,7 +398,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_stylesheet_link_tag_should_not_output_the_same_asset_twice
- ENV["RAILS_ASSET_ID"] = ""
assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('wellington', 'wellington', 'amsterdam')
end
@@ -427,21 +439,6 @@ class AssetTagHelperTest < ActionView::TestCase
FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
- def test_image_tag_windows_behaviour
- old_asset_id, ENV["RAILS_ASSET_ID"] = ENV["RAILS_ASSET_ID"], "1"
- # This simulates the behavior of File#exist? on windows when testing a file ending in "."
- # If the file "rails.png" exists, windows will return true when asked if "rails.png." exists (notice trailing ".")
- # OS X, linux etc will return false in this case.
- File.stubs(:exist?).with('template/../fixtures/public/images/rails.png.').returns(true)
- assert_equal '<img alt="Rails" src="/images/rails.png?1" />', image_tag('rails.png')
- ensure
- if old_asset_id
- ENV["RAILS_ASSET_ID"] = old_asset_id
- else
- ENV.delete("RAILS_ASSET_ID")
- end
- end
-
def test_video_path
VideoPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -482,6 +479,10 @@ class AssetTagHelperTest < ActionView::TestCase
AudioLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_font_path
+ FontPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_video_audio_tag_does_not_modify_options
options = {:autoplay => true}
video_tag('video', options)
@@ -490,27 +491,6 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal({:autoplay => true}, options)
end
- def test_timebased_asset_id
- expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
- assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
- end
-
- def test_string_asset_id
- @controller.config.asset_path = "/assets.v12345%s"
-
- expected_path = "/assets.v12345/images/rails.png"
- assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
- end
-
- def test_proc_asset_id
- @controller.config.asset_path = Proc.new do |asset_path|
- "/assets.v12345#{asset_path}"
- end
-
- expected_path = "/assets.v12345/images/rails.png"
- assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
- end
-
def test_image_tag_interpreting_email_cid_correctly
# An inline image has no need for an alt tag to be automatically generated from the cid:
assert_equal '<img src="cid:thi%25%25sis@acontentid" />', image_tag("cid:thi%25%25sis@acontentid")
@@ -520,37 +500,6 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal '<img alt="Image" src="cid:thi%25%25sis@acontentid" />', image_tag("cid:thi%25%25sis@acontentid", :alt => "Image")
end
- def test_timebased_asset_id_with_relative_url_root
- @controller.config.relative_url_root = "/collaboration/hieraki"
- expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
- assert_equal %(<img alt="Rails" src="#{@controller.config.relative_url_root}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
- end
-
- # Same as above, but with script_name
- def test_timebased_asset_id_with_script_name
- @request.script_name = "/collaboration/hieraki"
- expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
- assert_equal %(<img alt="Rails" src="#{@request.script_name}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
- end
-
- def test_should_skip_asset_id_on_complete_url
- assert_equal %(<img alt="Rails" src="http://www.example.com/rails.png" />), image_tag("http://www.example.com/rails.png")
- end
-
- def test_should_skip_asset_id_on_scheme_relative_url
- assert_equal %(<img alt="Rails" src="//www.example.com/rails.png" />), image_tag("//www.example.com/rails.png")
- end
-
- def test_should_use_preset_asset_id
- ENV["RAILS_ASSET_ID"] = "4500"
- assert_equal %(<img alt="Rails" src="/images/rails.png?4500" />), image_tag("rails.png")
- end
-
- def test_preset_empty_asset_id
- ENV["RAILS_ASSET_ID"] = ""
- assert_equal %(<img alt="Rails" src="/images/rails.png" />), image_tag("rails.png")
- end
-
def test_should_not_modify_source_string
source = '/images/rails.png'
copy = source.dup
@@ -559,7 +508,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_caching_image_path_with_caching_and_proc_asset_host_using_request
- ENV['RAILS_ASSET_ID'] = ''
@controller.config.asset_host = Proc.new do |source, request|
if request.ssl?
"#{request.protocol}#{request.host_with_port}"
@@ -579,12 +527,14 @@ end
class AssetTagHelperNonVhostTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
+ attr_reader :request
+
def setup
super
@controller = BasicController.new
@controller.config.relative_url_root = "/collaboration/hieraki"
- @request = Struct.new(:protocol).new("gopher://")
+ @request = Struct.new(:protocol, :base_url).new("gopher://", "gopher://www.example.com")
@controller.request = @request
end
@@ -599,10 +549,33 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(/collaboration/hieraki/images/xml.png), image_path("xml.png"))
end
+ def test_should_return_nothing_if_asset_host_isnt_configured
+ assert_equal nil, compute_asset_host("foo")
+ end
+
+ def test_should_current_request_host_is_always_returned_for_request
+ assert_equal "gopher://www.example.com", compute_asset_host("foo", :protocol => :request)
+ end
+
def test_should_ignore_relative_root_path_on_complete_url
assert_dom_equal(%(http://www.example.com/images/xml.png), image_path("http://www.example.com/images/xml.png"))
end
+ def test_should_return_simple_string_asset_host
+ @controller.config.asset_host = "assets.example.com"
+ assert_equal "gopher://assets.example.com", compute_asset_host("foo")
+ end
+
+ def test_should_return_relative_asset_host
+ @controller.config.asset_host = "assets.example.com"
+ assert_equal "//assets.example.com", compute_asset_host("foo", :protocol => :relative)
+ end
+
+ def test_should_return_custom_protocol_asset_host
+ @controller.config.asset_host = "assets.example.com"
+ assert_equal "ftp://assets.example.com", compute_asset_host("foo", :protocol => "ftp")
+ end
+
def test_should_compute_proper_path_with_asset_host
@controller.config.asset_host = "assets.example.com"
assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
@@ -635,6 +608,11 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_url("xml.png"))
end
+ def test_should_return_asset_host_with_protocol
+ @controller.config.asset_host = "http://assets.example.com"
+ assert_equal "http://assets.example.com", compute_asset_host("foo")
+ end
+
def test_should_ignore_asset_host_on_complete_url
@controller.config.asset_host = "http://assets.example.com"
assert_dom_equal(%(<link href="http://bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" />), stylesheet_link_tag("http://bar.example.com/stylesheets/style.css"))
@@ -645,6 +623,11 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(<link href="//bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" />), stylesheet_link_tag("//bar.example.com/stylesheets/style.css"))
end
+ def test_should_wildcard_asset_host
+ @controller.config.asset_host = 'http://a%d.example.com'
+ assert_match(%r(http://a[0123].example.com), compute_asset_host("foo"))
+ end
+
def test_should_wildcard_asset_host_between_zero_and_four
@controller.config.asset_host = 'http://a%d.example.com'
assert_match(%r(http://a[0123].example.com/collaboration/hieraki/images/xml.png), image_path('xml.png'))
@@ -668,3 +651,73 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(/collaboration/hieraki/stylesheets/foo.css), stylesheet_path("foo"))
end
end
+
+class AssetUrlHelperControllerTest < ActionView::TestCase
+ tests ActionView::Helpers::AssetUrlHelper
+
+ def setup
+ super
+
+ @controller = BasicController.new
+ @controller.extend ActionView::Helpers::AssetUrlHelper
+
+ @request = Class.new do
+ attr_accessor :script_name
+ def protocol() 'http://' end
+ def ssl?() false end
+ def host_with_port() 'www.example.com' end
+ def base_url() 'http://www.example.com' end
+ end.new
+
+ @controller.request = @request
+ end
+
+ def test_asset_path
+ assert_equal "/foo", @controller.asset_path("foo")
+ end
+
+ def test_asset_url
+ assert_equal "http://www.example.com/foo", @controller.asset_url("foo")
+ end
+end
+
+class AssetUrlHelperEmptyModuleTest < ActionView::TestCase
+ tests ActionView::Helpers::AssetUrlHelper
+
+ def setup
+ super
+
+ @module = Module.new
+ @module.extend ActionView::Helpers::AssetUrlHelper
+ end
+
+ def test_asset_path
+ assert_equal "/foo", @module.asset_path("foo")
+ end
+
+ def test_asset_url
+ assert_equal "/foo", @module.asset_url("foo")
+ end
+
+ def test_asset_url_with_request
+ @module.instance_eval do
+ def request
+ Struct.new(:base_url, :script_name).new("http://www.example.com", nil)
+ end
+ end
+
+ assert @module.request
+ assert_equal "http://www.example.com/foo", @module.asset_url("foo")
+ end
+
+ def test_asset_url_with_config_asset_host
+ @module.instance_eval do
+ def config
+ Struct.new(:asset_host).new("http://www.example.com")
+ end
+ end
+
+ assert @module.config.asset_host
+ assert_equal "http://www.example.com/foo", @module.asset_url("foo")
+ end
+end
diff --git a/actionpack/test/template/sanitize_helper_test.rb b/actionpack/test/template/sanitize_helper_test.rb
index 7626cdf386..12d5260a9d 100644
--- a/actionpack/test/template/sanitize_helper_test.rb
+++ b/actionpack/test/template/sanitize_helper_test.rb
@@ -17,7 +17,7 @@ class SanitizeHelperTest < ActionView::TestCase
end
def test_sanitize_form
- assert_sanitized "<form action=\"/foo/bar\" method=\"post\"><input></form>", ''
+ assert_equal '', sanitize("<form action=\"/foo/bar\" method=\"post\"><input></form>")
end
def test_should_sanitize_illegal_style_properties
@@ -48,8 +48,4 @@ class SanitizeHelperTest < ActionView::TestCase
def test_sanitize_is_marked_safe
assert sanitize("<html><script></script></html>").html_safe?
end
-
- def assert_sanitized(text, expected = nil)
- assert_equal((expected || text), sanitize(text))
- end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f3362f81a3..dd029540cf 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,10 @@
## Rails 4.0.0 (unreleased) ##
+* Fix bug where sum(expression) returns string '0' for no matching records
+ Fixes #7439
+
+ *Tim Macfarlane*
+
* PostgreSQL adapter correctly fetches default values when using multiple schemas and domains in a db. Fixes #7914
*Arturo Pie*
@@ -8,7 +13,7 @@
When symbol or hash passed we convert it to Arel::Nodes::Ordering.
If we pass invalid direction(like name: :DeSc) ActiveRecord::QueryMethods#order will raise an exception
-
+
User.order(:name, email: :desc)
# SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ed80422336..ecce7c703b 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -91,8 +91,10 @@ module ActiveRecord
end
def initialize_copy(other)
- @values = @values.dup
- @values[:bind] = @values[:bind].dup if @values[:bind]
+ # This method is a hot spot, so for now, use Hash[] to dup the hash.
+ # https://bugs.ruby-lang.org/issues/7166
+ @values = Hash[@values]
+ @values[:bind] = @values[:bind].dup if @values.key? :bind
reset
end
@@ -540,7 +542,7 @@ module ActiveRecord
end
def values
- @values.dup
+ Hash[@values]
end
def inspect
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 7c43d844d0..6317004631 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -349,7 +349,7 @@ module ActiveRecord
def type_cast_calculated_value(value, column, operation = nil)
case operation
when 'count' then value.to_i
- when 'sum' then type_cast_using_column(value || '0', column)
+ when 'sum' then type_cast_using_column(value || 0, column)
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
else type_cast_using_column(value, column)
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index a9ace10093..14bcb337e9 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -738,22 +738,22 @@ module ActiveRecord
buckets = joins.group_by do |join|
case join
when String
- 'string_join'
+ :string_join
when Hash, Symbol, Array
- 'association_join'
+ :association_join
when ActiveRecord::Associations::JoinDependency::JoinAssociation
- 'stashed_join'
+ :stashed_join
when Arel::Nodes::Join
- 'join_node'
+ :join_node
else
raise 'unknown class: %s' % join.class.name
end
end
- association_joins = buckets['association_join'] || []
- stashed_association_joins = buckets['stashed_join'] || []
- join_nodes = (buckets['join_node'] || []).uniq
- string_joins = (buckets['string_join'] || []).map { |x|
+ association_joins = buckets[:association_join] || []
+ stashed_association_joins = buckets[:stashed_join] || []
+ join_nodes = (buckets[:join_node] || []).uniq
+ string_joins = (buckets[:string_join] || []).map { |x|
x.strip
}.uniq
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 6cb6c469d2..de4902f89a 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -378,6 +378,10 @@ class CalculationsTest < ActiveRecord::TestCase
end
end
+ def test_sum_expression_returns_zero_when_no_records_to_sum
+ assert_equal 0, Account.where('1 = 2').sum("2 * credit_limit")
+ end
+
def test_count_with_from_option
assert_equal Company.count(:all), Company.from('companies').count(:all)
assert_equal Account.where("credit_limit = 50").count(:all),
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 80f46c6b08..5f13124e5b 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -2,13 +2,9 @@ require "cases/helper"
class SchemaDumperTest < ActiveRecord::TestCase
- def initialize(*)
- super
- ActiveRecord::SchemaMigration.create_table
- end
-
def setup
super
+ ActiveRecord::SchemaMigration.create_table
@stream = StringIO.new
end
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index ab01d7787a..ec2157221f 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -1,11 +1,14 @@
require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/object/deep_dup'
require 'active_support/core_ext/object/duplicable'
+require 'active_support/core_ext/object/deep_dup'
+require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/inclusion'
+
+require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/instance_variables'
+
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/object/to_query'
-require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/with_options'
diff --git a/activesupport/lib/active_support/core_ext/object/conversions.rb b/activesupport/lib/active_support/core_ext/object/conversions.rb
new file mode 100644
index 0000000000..540f7aadb0
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/conversions.rb
@@ -0,0 +1,4 @@
+require 'active_support/core_ext/object/to_param'
+require 'active_support/core_ext/object/to_query'
+require 'active_support/core_ext/array/conversions'
+require 'active_support/core_ext/hash/conversions'
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index 0b33a63460..9dfa2cbf11 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/array'
require 'active_support/core_ext/big_decimal'
+require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext' # FIXME: pulling in all to_xml extensions
require 'active_support/hash_with_indifferent_access'
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index b208767490..7cfe7b0ea7 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -3,6 +3,7 @@ require 'active_support/core_ext/hash'
require 'bigdecimal'
require 'active_support/core_ext/string/access'
require 'active_support/ordered_hash'
+require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/deep_dup'
require 'active_support/inflections'
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index c909ef1496..6b3680aa2d 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -810,6 +810,10 @@ Active Support
* Add `Time#prev_quarter` and `Time#next_quarter` short-hands for `months_ago(3)` and `months_since(3)`.
+* Add `Time#last_week`, `Time#last_month`, `Time#last_year` as aliases for `Time#prev_week`, `Time#prev_month`, and `Time#prev_year`.
+
+* Add `Date#last_week`, `Date#last_month`, `Date#last_year` as aliases for `Date#prev_week`, `Date#prev_month`, and `Date#prev_year`.
+
* Remove obsolete and unused `require_association` method from dependencies.
* Add `:instance_accessor` option for `config_accessor`.
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index c3e75a643b..9ada195dd3 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -6,7 +6,7 @@ require 'rails/engine/configuration'
module Rails
class Application
class Configuration < ::Rails::Engine::Configuration
- attr_accessor :asset_host, :asset_path, :assets, :autoflush_log,
+ attr_accessor :asset_host, :assets, :autoflush_log,
:cache_classes, :cache_store, :consider_all_requests_local, :console,
:eager_load, :exceptions_app, :file_watcher, :filter_parameters,
:force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
@@ -64,10 +64,6 @@ module Rails
@assets.logger = nil
end
- def compiled_asset_path
- "/"
- end
-
def encoding=(value)
@encoding = value
silence_warnings do
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 7a120410ba..c4c1100f19 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -154,11 +154,6 @@ module ApplicationTests
assert AppTemplate::Application, AppTemplate::Application.config.eager_load_namespaces
end
- test "asset_path defaults to nil for application" do
- require "#{app_path}/config/environment"
- assert_equal nil, AppTemplate::Application.config.asset_path
- end
-
test "the application can be eager loaded even when there are no frameworks" do
FileUtils.rm_rf("#{app_path}/config/environments")
add_to_config <<-RUBY
@@ -433,21 +428,6 @@ module ApplicationTests
end
end
- test "config.asset_path is not passed through env" do
- make_basic_app do |app|
- app.config.asset_path = "/omg%s"
- end
-
- class ::OmgController < ActionController::Base
- def index
- render inline: "<%= image_path('foo.jpg') %>"
- end
- end
-
- get "/"
- assert_equal "/omg/images/foo.jpg", last_response.body
- end
-
test "config.action_view.cache_template_loading with cache_classes default" do
add_to_config "config.cache_classes = true"
require "#{app_path}/config/environment"