diff options
Diffstat (limited to 'actionpack/lib')
28 files changed, 296 insertions, 173 deletions
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 2bdb23c4d7..abeb49d16f 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -12,13 +12,13 @@ module ActionController #:nodoc: # # <% cache do %> # All the topics in the system: - # <%= render :partial => "topic", :collection => Topic.find(:all) %> + # <%= render :partial => "topic", :collection => Topic.all %> # <% end %> # # This cache will bind the name of the action that called it, so if # this code was part of the view for the topics/list action, you # would be able to invalidate it using: - # + # # expire_fragment(:controller => "topics", :action => "list") # # This default behavior is limited if you need to cache multiple diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index 8c583c7ce0..496390402b 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -16,9 +16,10 @@ module ActionController #:nodoc: # caches_page :show, :new # end # - # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, - # which match the URLs used to trigger the dynamic generation. This is how the web server is able - # pick up a cache file when it exists and otherwise let the request pass on to Action Pack to generate it. + # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used + # that would normally trigger dynamic page generation. Page caching works by configuring a web server to first check for the + # existence of files on disk, and to serve them directly when found, without passing the request through to Action Pack. + # This is much faster than handling the full dynamic request in the usual way. # # Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache # is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends: @@ -132,8 +133,8 @@ module ActionController #:nodoc: end end - # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used - # If no options are provided, the requested url is used. Example: + # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used. + # If no options are provided, the url of the current request being handled is used. Example: # cache_page "I'm the cached content", :controller => "lists", :action => "show" def cache_page(content = nil, options = nil) return unless self.class.perform_caching && caching_allowed? 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/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb index 623a9873fc..50827d8107 100644 --- a/actionpack/lib/action_controller/metal/data_streaming.rb +++ b/actionpack/lib/action_controller/metal/data_streaming.rb @@ -26,8 +26,11 @@ module ActionController #:nodoc: # Options: # * <tt>:filename</tt> - suggests a filename for the browser to use. # Defaults to <tt>File.basename(path)</tt>. - # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify - # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json + # * <tt>:type</tt> - specifies an HTTP content type. + # You can specify either a string or a symbol for a registered type register with + # <tt>Mime::Type.register</tt>, for example :json + # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>. + # If no content type is registered for the extension, default type 'application/octet-stream' will be used. # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded. # Valid values are 'inline' and 'attachment' (default). # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'. @@ -84,6 +87,8 @@ module ActionController #:nodoc: # * <tt>:filename</tt> - suggests a filename for the browser to use. # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json + # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>. + # If no content type is registered for the extension, default type 'application/octet-stream' will be used. # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded. # Valid values are 'inline' and 'attachment' (default). # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'. @@ -108,6 +113,8 @@ module ActionController #:nodoc: private def send_file_headers!(options) + type_provided = options.has_key?(:type) + options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options)) [:type, :disposition].each do |arg| raise ArgumentError, ":#{arg} option required" if options[arg].nil? @@ -123,6 +130,10 @@ module ActionController #:nodoc: raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension self.content_type = extension else + if !type_provided && options[:filename] + # If type wasn't provided, try guessing from file extension. + content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.tr('.','')) || content_type + end self.content_type = content_type end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 75757db564..2df0e9422c 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -29,7 +29,7 @@ module ActionController # class EventsController < ActionController::Base # helper FormattedTimeHelper # def index - # @events = Event.find(:all) + # @events = Event.all # end # end # diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 2080e9b5b9..2271470334 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -7,17 +7,16 @@ module ActionController #:nodoc: # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks # by including a token in the rendered html for your application. This token is # stored as a random string in the session, to which an attacker does not have - # access. When a request reaches your application, \Rails then verifies the received - # token with the token in the session. Only HTML and javascript requests are checked, + # access. When a request reaches your application, \Rails verifies the received + # token with the token in the session. Only HTML and JavaScript requests are checked, # so this will not protect your XML API (presumably you'll have a different # authentication scheme there anyway). Also, GET requests are not protected as these # should be idempotent. # # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method, - # which will check the token and raise an ActionController::InvalidAuthenticityToken - # if it doesn't match what was expected. A call to this method is generated for new - # \Rails applications by default. You can customize the error message by editing - # public/422.html. + # which checks the token and resets the session if it doesn't match what was expected. + # A call to this method is generated for new \Rails applications by default. + # You can customize the error message by editing public/422.html. # # The token parameter is named <tt>authenticity_token</tt> by default. The name and # value of this token must be added to every layout that renders forms by including @@ -79,6 +78,8 @@ module ActionController #:nodoc: end end + # This is the method that defines the application behaviour when a request is found to be unverified. + # By default, \Rails resets the session when it finds an unverified request. def handle_unverified_request reset_session end diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index ebadb29ea7..3794e277f6 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -9,7 +9,7 @@ module ActionController #:nodoc: # respond_to :html, :xml, :json # # def index - # @people = Person.find(:all) + # @people = Person.all # respond_with(@people) # end # end @@ -162,6 +162,11 @@ module ActionController #:nodoc: navigation_behavior(e) end + # to_js simply tries to render a template. If no template is found, raises the error. + def to_js + default_render + end + # All other formats follow the procedure below. First we try to render a # template, if the template is not available, we verify if the resource # responds to :to_format and display it. diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 0bb436a476..5fe5334458 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -24,20 +24,8 @@ module ActionController #:nodoc: # # == Examples # - # Streaming can be added to a controller easily, all you need to do is - # call +stream+ in the controller class: - # - # class PostsController - # stream - # end - # - # The +stream+ method accepts the same options as +before_filter+ and friends: - # - # class PostsController - # stream :only => :index - # end - # - # You can also selectively turn on streaming for specific actions: + # Streaming can be added to a given template easily, all you need to do is + # to pass the :stream option. # # class PostsController # def index @@ -72,6 +60,9 @@ module ActionController #:nodoc: # render :stream => true # end # + # Notice that :stream only works with templates. Rendering :json + # or :xml with :stream won't work. + # # == Communication between layout and template # # When streaming, rendering happens top-down instead of inside-out. @@ -209,33 +200,9 @@ module ActionController #:nodoc: extend ActiveSupport::Concern include AbstractController::Rendering - attr_internal :stream - - module ClassMethods - # Render streaming templates. It accepts :only, :except, :if and :unless as options - # to specify when to stream, as in ActionController filters. - def stream(options={}) - if defined?(Fiber) - before_filter :_stream_filter, options - else - raise "You cannot use streaming if Fiber is not available." - end - end - end protected - # Mark following render calls as streaming. - def _stream_filter #:nodoc: - self.stream = true - end - - # Consider the stream option when normalazing options. - def _normalize_options(options) #:nodoc: - super - options[:stream] = self.stream unless options.key?(:stream) - end - # Set proper cache control and transfer encoding when streaming def _process_options(options) #:nodoc: super diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index d2ba052c8d..f0c29825ba 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -4,7 +4,6 @@ require "action_dispatch/railtie" require "action_view/railtie" require "abstract_controller/railties/routes_helpers" require "action_controller/railties/paths" -require "sprockets/railtie" module ActionController class Railtie < Rails::Railtie diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index 68f37d2f65..3da4f91051 100644 --- a/actionpack/lib/action_dispatch/http/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb @@ -7,6 +7,15 @@ Mime::Type.register "text/javascript", :js, %w( application/javascript applicati Mime::Type.register "text/css", :css Mime::Type.register "text/calendar", :ics Mime::Type.register "text/csv", :csv + +Mime::Type.register "image/png", :png, [], %w(png) +Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe) +Mime::Type.register "image/gif", :gif, [], %w(gif) +Mime::Type.register "image/bmp", :bmp, [], %w(bmp) +Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff) + +Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe) + Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml ) Mime::Type.register "application/rss+xml", :rss Mime::Type.register "application/atom+xml", :atom @@ -19,5 +28,8 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form # http://www.json.org/JSONRequest.html Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest ) +Mime::Type.register "application/pdf", :pdf, [], %w(pdf) +Mime::Type.register "application/zip", :zip, [], %w(zip) + # Create Mime::ALL but do not add it to the SET. Mime::ALL = Mime::Type.new("*/*", :all, []) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 8cee9ecdc4..1c312f2587 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -129,6 +129,11 @@ module ActionDispatch @cookies[name.to_s] end + def key?(name) + @cookies.key?(name.to_s) + end + alias :has_key? :key? + def update(other_hash) @cookies.update other_hash.stringify_keys self diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 82c4fadb50..49aef0bf72 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -182,10 +182,12 @@ module ActionDispatch if record.is_a?(Symbol) || record.is_a?(String) route << record - else + elsif record route << ActiveModel::Naming.route_key(record) route = [route.join("_").singularize] if inflection == :singular route << "index" if ActiveModel::Naming.uncountable?(record) && inflection == :plural + else + raise ArgumentError, "Nil location provided. Can't build URI." end route << routing_type(options) diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb index 367c295cf5..f668b81b45 100644 --- a/actionpack/lib/action_dispatch/testing/test_process.rb +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -39,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_pack/version.rb b/actionpack/lib/action_pack/version.rb index fcf0eb9565..add6b56425 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -1,9 +1,9 @@ module ActionPack module VERSION #:nodoc: MAJOR = 3 - MINOR = 1 + MINOR = 2 TINY = 0 - PRE = "rc1" + PRE = "beta" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb index 2b1fe545a6..96d8fd0dfe 100644 --- a/actionpack/lib/action_view/asset_paths.rb +++ b/actionpack/lib/action_view/asset_paths.rb @@ -1,10 +1,11 @@ +require 'zlib' require 'active_support/core_ext/file' module ActionView class AssetPaths #:nodoc: attr_reader :config, :controller - def initialize(config, controller) + def initialize(config, controller = nil) @config = config @controller = controller end @@ -13,21 +14,29 @@ module ActionView # 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) + # + # When include_host is true and the asset host does not specify the protocol + # the protocol parameter specifies how the protocol will be added. + # 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, ext = nil, include_host = true, protocol = nil) 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 = rewrite_relative_url_root(source, relative_url_root) if has_request? + source = rewrite_host_and_protocol(source, protocol) if include_host source end + # Return the filesystem path for the source + def compute_source_path(source, dir, ext) + source = rewrite_extension(source, dir, ext) if ext + File.join(config.assets_dir, dir, source) + end + def is_uri?(path) path =~ %r{^[-a-z]+://|^cid:|^//} end @@ -46,13 +55,45 @@ module ActionView 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 + def has_request? + controller.respond_to?(:request) + end + + def rewrite_host_and_protocol(source, protocol = nil) host = compute_asset_host(source) - if has_request && host && !is_uri?(host) - host = "#{controller.request.protocol}#{host}" + if host && !is_uri?(host) + if (protocol || default_protocol) == :request && !has_request? + host = nil + else + host = "#{compute_protocol(protocol)}#{host}" + end + end + host.nil? ? source : "#{host}#{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 - "#{host}#{source}" + end + + def default_protocol + protocol = @config.action_controller.default_asset_host_protocol if @config.action_controller.present? + protocol ||= @config.default_asset_host_protocol + protocol || (has_request? ? :request : :relative) + end + + def invalid_asset_host!(help_message) + raise ActionController::RoutingError, "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, @@ -61,19 +102,45 @@ module ActionView # 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 = asset_host_config 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) + args = [source] + arity = arity_of(host) + if arity > 1 && !has_request? + invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request.") end + args << current_request if (arity > 1 || arity < 0) && has_request? + host.call(*args) else - (host =~ /%d/) ? host % (source.hash % 4) : host + (host =~ /%d/) ? host % (Zlib.crc32(source) % 4) : host end end end + + def relative_url_root + config = controller.config if controller.respond_to?(:config) + config ||= config.action_controller if config.action_controller.present? + config ||= config + config.relative_url_root + end + + def asset_host_config + if config.action_controller.present? + config.action_controller.asset_host + else + config.asset_host + end + 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/base.rb b/actionpack/lib/action_view/base.rb index fd2970b8e2..36a0a20066 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -194,7 +194,7 @@ module ActionView #:nodoc: end def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc: - @_config = {} + @_config = ActiveSupport::InheritableOptions.new # Handle all these for backwards compatibility. # TODO Provide a new API for AV::Base and deprecate this one. diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 7970176d37..0c3f011c92 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -347,7 +347,7 @@ module ActionView src = options[:src] = path_to_image(source) unless src =~ /^cid:/ - options[:alt] = options.fetch(:alt){ File.basename(src, '.*').capitalize } + options[:alt] = options.fetch(:alt){ image_alt(src) } end if size = options.delete(:size) @@ -362,6 +362,10 @@ module ActionView tag("img", options) end + def image_alt(src) + File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize + end + # Returns an html video tag for the +sources+. If +sources+ is a string, # a single video tag will be returned. If +sources+ is an array, a video # tag with nested source tags for each source will be returned. The diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb index e4662a2919..3c05173a1b 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb @@ -60,12 +60,16 @@ module ActionView private - def path_to_asset(source, include_host = true) - asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host) + def path_to_asset(source, include_host = true, protocol = nil) + asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host, protocol) + end + + def path_to_asset_source(source) + asset_paths.compute_source_path(source, asset_name.to_s.pluralize, extension) end def compute_paths(*args) - expand_sources(*args).collect { |source| asset_paths.compute_public_path(source, asset_name.pluralize, extension, false) } + expand_sources(*args).collect { |source| path_to_asset_source(source) } end def expand_sources(sources, recursive) @@ -92,7 +96,7 @@ module ActionView def ensure_sources!(sources) sources.each do |source| - asset_file_path!(path_to_asset(source, false)) + asset_file_path!(path_to_asset_source(source)) end end @@ -123,19 +127,14 @@ module ActionView # Set mtime to the latest of the combined files to allow for # consistent ETag without a shared filesystem. - mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max + mt = asset_paths.map { |p| File.mtime(asset_file_path!(p)) }.max File.utime(mt, mt, joined_asset_path) end - def asset_file_path(path) - File.join(config.assets_dir, path.split('?').first) - end - - def asset_file_path!(path, error_if_file_is_uri = false) - if asset_paths.is_uri?(path) + def asset_file_path!(absolute_path, error_if_file_is_uri = false) + if asset_paths.is_uri?(absolute_path) raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri else - absolute_path = asset_file_path(path) raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path) return absolute_path end 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 12a304b395..8b35aa8896 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,3 +1,4 @@ +require 'thread' require 'active_support/core_ext/file' module ActionView diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb index e4f11c9bc7..8c25d38bbd 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb @@ -16,7 +16,8 @@ module ActionView end def asset_tag(source, options) - tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source)) }.merge(options), false, false) + # We force the :request protocol here to avoid a double-download bug in IE7 and IE8 + tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source, true, :request)) }.merge(options), false, false) end def custom_dir @@ -52,7 +53,7 @@ module ActionView # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs). # Full paths from the document root will be passed through. # Used internally by +stylesheet_link_tag+ to build the stylesheet path. - # + # # ==== Examples # stylesheet_path "style" # => /stylesheets/style.css # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css @@ -60,11 +61,7 @@ module ActionView # 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) - if config.use_sprockets - asset_path(source, 'css') - else - asset_paths.compute_public_path(source, 'stylesheets', 'css') - end + asset_paths.compute_public_path(source, 'stylesheets', 'css', true, :request) end alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index a087688a2c..39c37b25dc 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -20,7 +20,7 @@ module ActionView # # GET /posts.html # # GET /posts.atom # def index - # @posts = Post.find(:all) + # @posts = Post.all # # respond_to do |format| # format.html diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 0ef2357368..3a30263b49 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -4,6 +4,7 @@ require 'action_view/helpers/tag_helper' require 'action_view/helpers/form_tag_helper' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' +require 'active_support/core_ext/module/method_names' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/array/extract_options' @@ -567,18 +568,13 @@ module ActionView # ... # <% end %> # - # In addition, you may want to have access to the current iteration index. - # In that case, you can use a similar method called fields_for_with_index - # which receives a block with an extra parameter: + # When projects is already an association on Person you can use + # +accepts_nested_attributes_for+ to define the writer method for you: # - # <%= form_for @person do |person_form| %> - # ... - # <%= person_form.fields_for_with_index :projects do |project_fields, index| %> - # Position: <%= index %> - # Name: <%= project_fields.text_field :name %> - # <% end %> - # ... - # <% end %> + # 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> @@ -1153,7 +1149,7 @@ module ActionView options["name"] ||= tag_name_with_index(@auto_index) options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) } else - options["name"] ||= tag_name + (options.has_key?('multiple') ? '[]' : '') + options["name"] ||= tag_name + (options['multiple'] ? '[]' : '') options["id"] = options.fetch("id"){ tag_id } end end @@ -1233,13 +1229,6 @@ module ActionView RUBY_EVAL end - # Check +fields_for+ for docs and examples. - def fields_for_with_index(record_name, record_object = nil, fields_options = {}, &block) - index = fields_options[:index] || options[:child_index] || nested_child_index(@object_name) - block_with_index = Proc.new{ |obj| block.call(obj, index) } - fields_for(record_name, record_object, fields_options, &block_with_index) - 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) && record_object.extractable_options? fields_options[:builder] ||= options[:builder] 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/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index a351fbc04f..c6098fc7e0 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -12,8 +12,7 @@ module ActionView # # <%= render :partial => "account" %> # - # This would render "advertiser/_account.html.erb" and pass the instance variable @account in as a local variable - # +account+ to the template for display. + # This would render "advertiser/_account.html.erb". # # In another template for Advertiser#buy, we could have: # @@ -28,32 +27,24 @@ module ActionView # # == The :as and :object options # - # By default <tt>ActionView::Partials::PartialRenderer</tt> has its object in a local variable with the same - # name as the template. So, given + # By default <tt>ActionView::Partials::PartialRenderer</tt> doesn't have any local variables. + # The <tt>:object</tt> option can be used to pass an object to the partial. For instance: # - # <%= render :partial => "contract" %> + # <%= render :partial => "account", :object => @buyer %> # - # within contract we'll get <tt>@contract</tt> in the local variable +contract+, as if we had written + # would provide the +@buyer+ object to the partial, available under the local variable +account+ and is + # equivalent to: # - # <%= render :partial => "contract", :locals => { :contract => @contract } %> + # <%= render :partial => "account", :locals => { :account => @buyer } %> # # With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we - # wanted it to be +agreement+ instead of +contract+ we'd do: - # - # <%= render :partial => "contract", :as => 'agreement' %> - # - # The <tt>:object</tt> option can be used to directly specify which object is rendered into the partial; - # useful when the template's object is elsewhere, in a different ivar or in a local variable for instance. + # wanted it to be +user+ instead of +account+ we'd do: # - # Revisiting a previous example we could have written this code: + # <%= render :partial => "account", :object => @buyer, :as => 'user' %> # - # <%= render :partial => "account", :object => @buyer %> - # - # <% @advertisements.each do |ad| %> - # <%= render :partial => "ad", :object => ad %> - # <% end %> + # This is equivalent to # - # The <tt>:object</tt> and <tt>:as</tt> options can be used together. + # <%= render :partial => "account", :locals => { :user => @buyer } %> # # == Rendering a collection of partials # diff --git a/actionpack/lib/sprockets/assets.rake b/actionpack/lib/sprockets/assets.rake new file mode 100644 index 0000000000..0236350576 --- /dev/null +++ b/actionpack/lib/sprockets/assets.rake @@ -0,0 +1,26 @@ +namespace :assets do + desc "Compile all the assets named in config.assets.precompile" + task :precompile do + if ENV["RAILS_GROUPS"].to_s.empty? + ENV["RAILS_GROUPS"] = "assets" + Kernel.exec $0, *ARGV + else + Rake::Task["environment"].invoke + Sprockets::Helpers::RailsHelper + + assets = Rails.application.config.assets.precompile + Rails.application.assets.precompile(*assets) + end + end + + desc "Remove compiled assets" + task :clean => :environment do + assets = Rails.application.config.assets + public_asset_path = Rails.public_path + assets.prefix + file_list = FileList.new("#{public_asset_path}/**/*") + file_list.each do |file| + rm_rf file + rm_rf "#{file}.gz" + end + end +end diff --git a/actionpack/lib/sprockets/compressors.rb b/actionpack/lib/sprockets/compressors.rb new file mode 100644 index 0000000000..6544953df4 --- /dev/null +++ b/actionpack/lib/sprockets/compressors.rb @@ -0,0 +1,21 @@ +module Sprockets + class NullCompressor + def compress(content) + content + end + end + + class LazyCompressor + def initialize(&block) + @block = block + end + + def compressor + @compressor ||= @block.call || NullCompressor.new + end + + def compress(content) + compressor.compress(content) + end + end +end
\ No newline at end of file diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb index 9f1f0f3b68..a95e7c0649 100644 --- a/actionpack/lib/sprockets/helpers/rails_helper.rb +++ b/actionpack/lib/sprockets/helpers/rails_helper.rb @@ -1,4 +1,4 @@ -require "action_view/helpers" +require "action_view" module Sprockets module Helpers @@ -9,8 +9,12 @@ module Sprockets def asset_paths @asset_paths ||= begin config = self.config if respond_to?(:config) + config ||= Rails.application.config controller = self.controller if respond_to?(:controller) - RailsHelper::AssetPaths.new(config, controller) + paths = RailsHelper::AssetPaths.new(config, controller) + paths.asset_environment = asset_environment + paths.asset_prefix = asset_prefix + paths end end @@ -50,7 +54,7 @@ module Sprockets 'rel' => "stylesheet", 'type' => "text/css", 'media' => "screen", - 'href' => asset_path(source, 'css', body) + 'href' => asset_path(source, 'css', body, :request) }.merge(options.stringify_keys) tag 'link', tag_options @@ -58,35 +62,59 @@ module Sprockets end.join("\n").html_safe end + def asset_path(source, default_ext = nil, body = false, protocol = nil) + source = source.logical_path if source.respond_to?(:logical_path) + path = asset_paths.compute_public_path(source, 'assets', default_ext, true, protocol) + body ? "#{path}?body=1" : path + end + private def debug_assets? params[:debug_assets] == '1' || params[:debug_assets] == 'true' end - 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, Rails.application.config.assets.prefix, default_ext, true) - body ? "#{path}?body=1" : path + # Override to specify an alternative prefix for asset path generation. + # When combined with a custom +asset_environment+, this can be used to + # implement themes that can take advantage of the asset pipeline. + # + # If you only want to change where the assets are mounted, refer to + # +config.assets.prefix+ instead. + def asset_prefix + Rails.application.config.assets.prefix + end + + # Override to specify an alternative asset environment for asset + # path generation. The environment should already have been mounted + # at the prefix returned by +asset_prefix+. + def asset_environment + Rails.application.assets end class AssetPaths < ::ActionView::AssetPaths #:nodoc: - def compute_public_path(source, dir, ext=nil, include_host=true) - super(source, Rails.application.config.assets.prefix, ext, include_host) + attr_accessor :asset_environment, :asset_prefix + + def compute_public_path(source, dir, ext=nil, include_host=true, protocol=nil) + super(source, asset_prefix, ext, include_host, protocol) + end + + # Return the filesystem path for the source + def compute_source_path(source, ext) + asset_for(source, ext) end def asset_for(source, ext) source = source.to_s return nil if is_uri?(source) source = rewrite_extension(source, nil, ext) - assets[source] + asset_environment[source] end def rewrite_asset_path(source, dir) if source[0] == ?/ source else - assets.path(source, performing_caching?, dir) + asset_environment.path(source, performing_caching?, dir) end end @@ -98,13 +126,9 @@ module Sprockets end end - def assets - Rails.application.assets - end - # When included in Sprockets::Context, we need to ask the top-level config as the controller is not available def performing_caching? - @config ? @config.perform_caching : Rails.application.config.action_controller.perform_caching + config.action_controller.present? ? config.action_controller.perform_caching : config.perform_caching end end end diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index ab5101f6fc..c28bdc3061 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -1,15 +1,15 @@ module Sprockets autoload :Helpers, "sprockets/helpers" + autoload :LazyCompressor, "sprockets/compressors" + autoload :NullCompressor, "sprockets/compressors" + # TODO: Get rid of config.assets.enabled class Railtie < ::Rails::Railtie - def self.using_coffee? - require 'coffee-script' - defined?(CoffeeScript) - rescue LoadError - false - end + config.default_asset_host_protocol = :relative - config.app_generators.javascript_engine :coffee if using_coffee? + rake_tasks do + load "sprockets/assets.rake" + end # Configure ActionController to use sprockets. initializer "sprockets.set_configs", :after => "action_controller.set_configs" do |app| @@ -64,14 +64,14 @@ module Sprockets env.logger = Rails.logger if env.respond_to?(:cache) - env.cache = Rails.cache + env.cache = assets.cache_store || Rails.cache end 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) + env.js_compressor = LazyCompressor.new { expand_js_compressor(assets.js_compressor || :uglifier) } + env.css_compressor = LazyCompressor.new { expand_css_compressor(assets.css_compressor) } end env |