diff options
Diffstat (limited to 'actionpack')
36 files changed, 447 insertions, 134 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index b1286d04cc..82dfc625a6 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,21 @@ *Rails 3.2.0 (unreleased)* +* content_tag_for and div_for can now take the collection of records. It will also yield the record as the first argument if you set a receiving argument in your block [Prem Sichanugrist] + + So instead of having to do this: + + @items.each do |item| + content_tag_for(:li, item) do + Title: <%= item.title %> + end + end + + You can now do this: + + content_tag_for(:li, @items) do |item| + Title: <%= item.title %> + end + * send_file now guess the mime type [Esad Hajdarevic] * Mime type entries for PDF, ZIP and other formats were added [Esad Hajdarevic] @@ -28,7 +44,23 @@ for your test you need to do it before the cookie jar is created. -*Rails 3.1.0 (unreleased)* +*Rails 3.1.1 (unreleased)* + +* Fixed the behavior of asset pipeline when config.assets.digest and config.assets.compile are false and requested asset isn't precompiled. + Before the requested asset were compiled anyway ignoring that the config.assets.compile flag is false. [Guillermo Iguaran] + +* CookieJar is now Enumerable. Fixes #2795 + +* Fixed AssetNotPrecompiled error raised when rake assets:precompile is compiling certain .erb files. See GH #2763 #2765 #2805 [Guillermo Iguaran] + +* Manifest is correctly placed in assets path when default assets prefix is changed. Fixes #2776 [Guillermo Iguaran] + +* Fixed stylesheet_link_tag and javascript_include_tag to respect additional options passed by the users when debug is on. [Guillermo Iguaran] + + +*Rails 3.1.0 (August 30, 2011)* + +* Param values are `paramified` in controller tests. [David Chelimsky] * x_sendfile_header now defaults to nil and config/environments/production.rb doesn't set a particular value for it. This allows servers to set it through X-Sendfile-Type. [Santiago Pastorino] diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 620fdc4a72..65b364f872 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -18,13 +18,13 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) - s.add_dependency('rack-cache', '~> 1.0.2') + s.add_dependency('rack-cache', '~> 1.0.3') s.add_dependency('builder', '~> 3.0.0') s.add_dependency('i18n', '~> 0.6') s.add_dependency('rack', '~> 1.3.2') - s.add_dependency('rack-test', '~> 0.6.0') - s.add_dependency('rack-mount', '~> 0.8.1') - s.add_dependency('sprockets', '~> 2.0.0.beta.12') + s.add_dependency('rack-test', '~> 0.6.1') + s.add_dependency('rack-mount', '~> 0.8.2') + s.add_dependency('sprockets', '~> 2.0.0') s.add_dependency('erubis', '~> 2.7.0') s.add_development_dependency('tzinfo', '~> 0.3.29') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ce56d8bc71..da93c988c4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -63,7 +63,7 @@ module ActionController # # == Sessions # - # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted, + # Sessions allow you to store objects in between requests. This is useful for objects that are not yet ready to be persisted, # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at. diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb index 938a6ae81c..49cf70ec21 100644 --- a/actionpack/lib/action_controller/caching/sweeping.rb +++ b/actionpack/lib/action_controller/caching/sweeping.rb @@ -88,7 +88,7 @@ module ActionController #:nodoc: end def method_missing(method, *arguments, &block) - return if @controller.nil? + return unless @controller @controller.__send__(method, *arguments, &block) end end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 2df0e9422c..bd515bba82 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -7,9 +7,12 @@ module ActionController # by default. # # In addition to using the standard template helpers provided, creating custom helpers to - # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will - # include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically - # include <tt>MyHelper</tt>. + # extract complicated logic or reusable functionality is strongly encouraged. By default, each controller + # will include all helpers. + # + # In previous versions of \Rails the controller will include a helper whose + # name matches that of the controller, e.g., <tt>MyController</tt> will automatically + # include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+. # # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any # controller which inherits from it. diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 2d8afc3a78..6acbb23907 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -6,31 +6,30 @@ require 'active_support/core_ext/module/anonymous' require 'action_dispatch/http/mime_types' module ActionController - # Wraps parameters hash into nested hash. This will allow client to submit - # POST request without having to specify a root element in it. + # Wraps the parameters hash into a nested hash. This will allow clients to submit + # POST requests without having to specify any root elements. # - # By default this functionality won't be enabled. You can enable - # it globally by setting +ActionController::Base.wrap_parameters+: - # - # ActionController::Base.wrap_parameters = [:json] + # This functionality is enabled in +config/initializers/wrap_parameters.rb+ + # and can be customized. If you are upgrading to \Rails 3.1, this file will + # need to be created for the functionality to be enabled. # # You could also turn it on per controller by setting the format array to - # non-empty array: + # a non-empty array: # # class UsersController < ApplicationController # wrap_parameters :format => [:json, :xml] # end # - # If you enable +ParamsWrapper+ for +:json+ format. Instead of having to + # If you enable +ParamsWrapper+ for +:json+ format, instead of having to # send JSON parameters like this: # # {"user": {"name": "Konata"}} # - # You can now just send a parameters like this: + # You can send parameters like this: # # {"name": "Konata"} # - # And it will be wrapped into a nested hash with the key name matching + # And it will be wrapped into a nested hash with the key name matching the # controller's name. For example, if you're posting to +UsersController+, # your new +params+ hash will look like this: # @@ -82,7 +81,7 @@ module ActionController # # ==== Examples # wrap_parameters :format => :xml - # # enables the parmeter wrapper for XML format + # # enables the parameter wrapper for XML format # # wrap_parameters :person # # wraps parameters into +params[:person]+ hash diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index dee7eb1ec8..f2dfb3833b 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -45,7 +45,7 @@ module ActionController # integer, or a symbol representing the downcased, underscored and symbolized description. # Note that the status code must be a 3xx HTTP code, or redirection will not occur. # - # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names + # It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names # +alert+ and +notice+ as well as a general purpose +flash+ bucket. # # Examples: @@ -57,7 +57,7 @@ module ActionController # When using <tt>redirect_to :back</tt>, if there is no referrer, RedirectBackError will be raised. You may specify some fallback # behavior for this case by rescuing RedirectBackError. def redirect_to(options = {}, response_status = {}) #:doc: - raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? + raise ActionControllerError.new("Cannot redirect to nil!") unless options raise AbstractController::DoubleRenderError if response_body self.status = _extract_redirect_to_status(options, response_status) diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 08132b1900..0b40b1fc4c 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -18,7 +18,7 @@ # @url = root_path # named route from the application. # end # end -# => +# module ActionController module UrlFor extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index c8cf04bb69..a83fa74795 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -79,10 +79,10 @@ module ActionController "expecting <?> but rendering with <?>", options, rendered.keys.join(', ')) assert_block(msg) do - if options.nil? - @templates.blank? - else + if options rendered.any? { |t,num| t.match(options) } + else + @templates.blank? end end when Hash @@ -180,7 +180,7 @@ module ActionController @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } @symbolized_path_params = nil @method = @request_method = nil - @fullpath = @ip = @remote_ip = nil + @fullpath = @ip = @remote_ip = @protocol = nil @env['action_dispatch.request.query_parameters'] = {} @set_cookies ||= {} @set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }]) @@ -401,9 +401,7 @@ module ActionController def paramify_values(hash_or_array_or_value) case hash_or_array_or_value when Hash - hash_or_array_or_value.each do |key, value| - hash_or_array_or_value[key] = paramify_values(value) - end + Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }] when Array hash_or_array_or_value.map {|i| paramify_values(i)} when Rack::Test::UploadedFile @@ -416,7 +414,7 @@ module ActionController def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') # Ensure that numbers and symbols passed as params are converted to # proper params, as is the case when engaging rack. - paramify_values(parameters) + parameters = paramify_values(parameters) # Sanity check for required instance variables so we can give an # understandable error message. @@ -450,7 +448,7 @@ module ActionController @controller.params.merge!(parameters) build_request_uri(action, parameters) @controller.class.class_eval { include Testing } - @controller.recycle! + @controller.recycle! @controller.process_with_new_base_test(@request, @response) @assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {} @request.session.delete('flash') if @request.session['flash'].blank? diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb index a15ad28f16..94fa747a79 100644 --- a/actionpack/lib/action_dispatch/http/upload.rb +++ b/actionpack/lib/action_dispatch/http/upload.rb @@ -11,24 +11,13 @@ module ActionDispatch raise(ArgumentError, ':tempfile is required') unless @tempfile end - def open - @tempfile.open - end - - def path - @tempfile.path - end - def read(*args) @tempfile.read(*args) end - def rewind - @tempfile.rewind - end - - def size - @tempfile.size + # Delegate these methods to the tempfile. + [:open, :path, :rewind, :size].each do |method| + class_eval "def #{method}; @tempfile.#{method}; end" end private diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 1c312f2587..8c4615c0c1 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -85,6 +85,7 @@ module ActionDispatch class CookieOverflow < StandardError; end class CookieJar #:nodoc: + include Enumerable # This regular expression is used to split the levels of a domain. # The top level domain can be any string without a period or @@ -124,6 +125,10 @@ module ActionDispatch alias :closed? :closed def close!; @closed = true end + def each(&block) + @cookies.each(&block) + end + # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. def [](name) @cookies[name.to_s] diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index a70d814749..6bcf099d2c 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -59,7 +59,10 @@ module ActionDispatch # Note that the regexp does not allow $1 to end with a ':' $1.constantize rescue LoadError, NameError => const_error - raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n" + raise ActionDispatch::Session::SessionRestoreError, + "Session contains objects whose class definition isn't available.\n" + + "Remember to require the classes for all objects kept in the session.\n" + + "(Original exception: #{const_error.message} [#{const_error.class}])\n" end retry else diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index fbda1f8442..1af89858d1 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -1,5 +1,4 @@ require "action_dispatch" -require "rails" module ActionDispatch class Railtie < Rails::Railtie diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 8be0ce44f1..4d65173f61 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1038,12 +1038,12 @@ module ActionDispatch # # This generates the following comments routes: # - # GET /photos/:id/comments/new - # POST /photos/:id/comments - # GET /photos/:id/comments/:id - # GET /photos/:id/comments/:id/edit - # PUT /photos/:id/comments/:id - # DELETE /photos/:id/comments/:id + # GET /photos/:photo_id/comments/new + # POST /photos/:photo_id/comments + # GET /photos/:photo_id/comments/:id + # GET /photos/:photo_id/comments/:id/edit + # PUT /photos/:photo_id/comments/:id + # DELETE /photos/:photo_id/comments/:id # # === Options # Takes same options as <tt>Base#match</tt> as well as: @@ -1438,7 +1438,7 @@ module ActionDispatch name_prefix = @scope[:as] if parent_resource - return nil if as.nil? && action.nil? + return nil unless as || action collection_name = parent_resource.collection_name member_name = parent_resource.member_name diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index de14113c51..30048cd48a 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -131,10 +131,14 @@ module ActionDispatch # # Examples: # - # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :port => '8080' # => 'http://somehost.org:8080/tasks/testing' - # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok' - # url_for :controller => 'tasks', :action => 'testing', :trailing_slash => true # => 'http://somehost.org/tasks/testing/' - # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33' + # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :port => '8080' + # # => 'http://somehost.org:8080/tasks/testing' + # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :anchor => 'ok', :only_path => true + # # => '/tasks/testing#ok' + # url_for :controller => 'tasks', :action => 'testing', :trailing_slash => true + # # => 'http://somehost.org/tasks/testing/' + # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :number => '33' + # # => 'http://somehost.org/tasks/testing?number=33' def url_for(options = nil) case options when String diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb index aae8377f8a..73f4f8ee5f 100644 --- a/actionpack/lib/action_view/asset_paths.rb +++ b/actionpack/lib/action_view/asset_paths.rb @@ -69,7 +69,7 @@ module ActionView host = "#{compute_protocol(protocol)}#{host}" end end - host.nil? ? source : "#{host}#{source}" + host ? "#{host}#{source}" : source end def compute_protocol(protocol) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 43d67f2032..36c49d9c91 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -116,7 +116,7 @@ module ActionView #:nodoc: # xml.language "en-us" # xml.ttl "40" # - # for item in @recent_items + # @recent_items.each do |item| # xml.item do # xml.title(item_title(item)) # xml.description(item_description(item)) if item_description(item) diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 62f95379cd..8abd85c3a3 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -134,9 +134,9 @@ module ActionView # WARNING: content_for is ignored in caches. So you shouldn't use it # for elements that will be fragment cached. def content_for(name, content = nil, &block) - content = capture(&block) if block_given? - if content - @view_flow.append(name, content) + if content || block_given? + content = capture(&block) if block_given? + @view_flow.append(name, content) if content nil else @view_flow.get(name) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 9ed4611123..13b9dc8553 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -177,9 +177,12 @@ module ActionView # label_tag 'name', nil, :class => 'small_label' # # => <label for="name" class="small_label">Name</label> def label_tag(name = nil, content_or_options = nil, options = nil, &block) - options = content_or_options if block_given? && content_or_options.is_a?(Hash) - options ||= {} - options.stringify_keys! + if block_given? && content_or_options.is_a?(Hash) + options = content_or_options = content_or_options.stringify_keys + else + options ||= {} + options = options.stringify_keys + end options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for") content_tag :label, content_or_options || name.to_s.humanize, options, &block end @@ -653,7 +656,7 @@ module ActionView if token == false || !protect_against_forgery? '' else - token = form_authenticity_token if token.nil? + token ||= form_authenticity_token tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token) end end diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 4484390fde..1adcd716f8 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -1,4 +1,5 @@ require 'action_view/helpers/tag_helper' +require 'active_support/core_ext/string/encoding' module ActionView module Helpers @@ -10,15 +11,23 @@ module ActionView "\n" => '\n', "\r" => '\n', '"' => '\\"', - "'" => "\\'" } + "'" => "\\'" + } - # Escape carrier returns and single and double quotes for JavaScript segments. + if "ruby".encoding_aware? + JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
' + else + JS_ESCAPE_MAP["\342\200\250"] = '
' + end + + # Escapes carriage returns and single and double quotes for JavaScript segments. + # # Also available through the alias j(). This is particularly helpful in JavaScript responses, like: # # $('some_element').replaceWith('<%=j render 'some/element_template' %>'); def escape_javascript(javascript) if javascript - result = javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) {|match| JS_ESCAPE_MAP[match] } + result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] } javascript.html_safe? ? result.html_safe : result else '' diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 142a25f118..cbee517adc 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -17,6 +17,19 @@ module ActionView # # <div id="person_123" class="person foo"> Joe Bloggs </div> # + # You can also pass an array of Active Record objects, which will then + # get iterates over and yield each record as an argument for the block. + # For example: + # + # <%= div_for(@people, :class => "foo") do |person| %> + # <%= person.name %> + # <% end %> + # + # produces: + # + # <div id="person_123" class="person foo"> Joe Bloggs </div> + # <div id="person_124" class="person foo"> Jane Bloggs </div> + # def div_for(record, *args, &block) content_tag_for(:div, record, *args, &block) end @@ -42,6 +55,21 @@ module ActionView # # <tr id="foo_person_123" class="person">... # + # You can also pass an array of objects which this method will loop through + # and yield the current object to the supplied block, reduce the need for + # having to iterate through the object (using <tt>each</tt>) beforehand. + # For example (assuming @people is an array of Person objects): + # + # <%= content_tag_for(:tr, @people) do |person| %> + # <td><%= person.first_name %></td> + # <td><%= person.last_name %></td> + # <% end %> + # + # produces: + # + # <tr id="person_123" class="person">...</tr> + # <tr id="person_124" class="person">...</tr> + # # content_tag_for also accepts a hash of options, which will be converted to # additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined # with the default class name for your object. For example: @@ -52,12 +80,30 @@ module ActionView # # <li id="person_123" class="person bar">... # - def content_tag_for(tag_name, record, prefix = nil, options = nil, &block) - options, prefix = prefix, nil if prefix.is_a?(Hash) - options ||= {} - options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) }) - content_tag(tag_name, options, &block) + def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options = nil, &block) + if single_or_multiple_records.respond_to?(:to_ary) + single_or_multiple_records.to_ary.map do |single_record| + capture { content_tag_for_single_record(tag_name, single_record, prefix, options, &block) } + end.join("\n").html_safe + else + content_tag_for_single_record(tag_name, single_or_multiple_records, prefix, options, &block) + end end + + private + + # Called by <tt>content_tag_for</tt> internally to render a content tag + # for each record. + def content_tag_for_single_record(tag_name, record, prefix, options, &block) + options, prefix = prefix, nil if prefix.is_a?(Hash) + options ||= {} + options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) }) + if block.arity == 0 + content_tag(tag_name, capture(&block), options) + else + content_tag(tag_name, capture(record, &block), options) + end + end end end end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 51baca8e03..acd5e46e33 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -268,7 +268,7 @@ module ActionView # to change the HTTP verb used to submit the form. # # ==== Options - # The +options+ hash accepts the same options as url_for. + # The +options+ hash accepts the same options as +url_for+. # # There are a few special +html_options+: # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>, @@ -569,6 +569,12 @@ module ActionView # # current_page?(:controller => 'library', :action => 'checkout') # # => false + # + # Let's say we're in the <tt>/products</tt> action with method POST in case of invalid product. + # + # current_page?(:controller => 'product', :action => 'index') + # # => false + # def current_page?(options) unless request raise "You cannot use helpers that need to determine the current " \ @@ -576,6 +582,8 @@ module ActionView "in a #request method" end + return false unless request.get? + url_string = url_for(options) # We ignore any extra parameters in the request_uri if the @@ -596,9 +604,7 @@ module ActionView private def convert_options_to_data_attributes(options, html_options) - if html_options.nil? - link_to_remote_options?(options) ? {'data-remote' => 'true'} : {} - else + if html_options html_options = html_options.stringify_keys html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options) @@ -611,6 +617,8 @@ module ActionView add_method_to_attributes!(html_options, method) if method html_options + else + link_to_remote_options?(options) ? {'data-remote' => 'true'} : {} end end diff --git a/actionpack/lib/sprockets/assets.rake b/actionpack/lib/sprockets/assets.rake index 4cf5caab91..a8128d9a82 100644 --- a/actionpack/lib/sprockets/assets.rake +++ b/actionpack/lib/sprockets/assets.rake @@ -13,17 +13,45 @@ namespace :assets do # Ensure that action view is loaded and the appropriate sprockets hooks get executed ActionView::Base - assets = Rails.application.config.assets.precompile - # Always perform caching so that asset_path appends the timestamps to file references. - Rails.application.config.action_controller.perform_caching = true - Rails.application.assets.precompile(*assets) + # Always compile files + Rails.application.config.assets.compile = true + + config = Rails.application.config + env = Rails.application.assets + target = Pathname.new(File.join(Rails.public_path, config.assets.prefix)) + manifest = {} + manifest_path = config.assets.manifest || target + + config.assets.precompile.each do |path| + env.each_logical_path do |logical_path| + if path.is_a?(Regexp) + next unless path.match(logical_path) + else + next unless File.fnmatch(path.to_s, logical_path) + end + + if asset = env.find_asset(logical_path) + asset_path = config.assets.digest ? asset.digest_path : logical_path + manifest[logical_path] = asset_path + filename = target.join(asset_path) + + mkdir_p filename.dirname + asset.write_to(filename) + asset.write_to("#{filename}.gz") if filename.to_s =~ /\.(css|js)$/ + end + end + end + + File.open("#{manifest_path}/manifest.yml", 'w') do |f| + YAML.dump(manifest, f) + end end end desc "Remove compiled assets" task :clean => [:environment, 'tmp:cache:clear'] do - assets = Rails.application.config.assets - public_asset_path = Rails.public_path + assets.prefix + config = Rails.application.config + public_asset_path = File.join(Rails.public_path, config.assets.prefix) rm_rf public_asset_path, :secure => true end end diff --git a/actionpack/lib/sprockets/compressors.rb b/actionpack/lib/sprockets/compressors.rb index 6544953df4..351eff1085 100644 --- a/actionpack/lib/sprockets/compressors.rb +++ b/actionpack/lib/sprockets/compressors.rb @@ -1,21 +1,37 @@ module Sprockets - class NullCompressor + # An asset compressor which does nothing. + # + # This compressor simply returns the asset as-is, without any compression + # whatsoever. It is useful in development mode, when compression isn't + # needed but using the same asset pipeline as production is desired. + class NullCompressor #:nodoc: def compress(content) content end end - class LazyCompressor + # An asset compressor which only initializes the underlying compression + # engine when needed. + # + # This postpones the initialization of the compressor until + # <code>#compress</code> is called the first time. + class LazyCompressor #:nodoc: + # Initializes a new LazyCompressor. + # + # The block should return a compressor when called, i.e. an object + # which responds to <code>#compress</code>. def initialize(&block) @block = block end - def compressor - @compressor ||= @block.call || NullCompressor.new - end - def compress(content) compressor.compress(content) end + + private + + def compressor + @compressor ||= (@block.call || NullCompressor.new) + end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb index ec3d36d5ad..3987e6e17f 100644 --- a/actionpack/lib/sprockets/helpers/rails_helper.rb +++ b/actionpack/lib/sprockets/helpers/rails_helper.rb @@ -14,6 +14,9 @@ module Sprockets paths = RailsHelper::AssetPaths.new(config, controller) paths.asset_environment = asset_environment paths.asset_prefix = asset_prefix + paths.asset_digests = asset_digests + paths.compile_assets = compile_assets? + paths.digest_assets = digest_assets? paths end end @@ -26,15 +29,10 @@ module Sprockets sources.collect do |source| if debug && asset = asset_paths.asset_for(source, 'js') asset.to_a.map { |dep| - javascript_include_tag(dep, :debug => false, :body => true) - }.join("\n").html_safe + super(dep.to_s, { :src => asset_path(dep, 'js', true) }.merge!(options)) + } else - tag_options = { - 'type' => "text/javascript", - 'src' => asset_path(source, 'js', body) - }.merge(options.stringify_keys) - - content_tag 'script', "", tag_options + super(source.to_s, { :src => asset_path(source, 'js', body) }.merge!(options)) end end.join("\n").html_safe end @@ -47,17 +45,10 @@ module Sprockets sources.collect do |source| if debug && asset = asset_paths.asset_for(source, 'css') asset.to_a.map { |dep| - stylesheet_link_tag(dep, :debug => false, :body => true) - }.join("\n").html_safe + super(dep.to_s, { :href => asset_path(dep, 'css', true, :request) }.merge!(options)) + } else - tag_options = { - 'rel' => "stylesheet", - 'type' => "text/css", - 'media' => "screen", - 'href' => asset_path(source, 'css', body, :request) - }.merge(options.stringify_keys) - - tag 'link', tag_options + super(source.to_s, { :href => asset_path(source, 'css', body, :request) }.merge!(options)) end end.join("\n").html_safe end @@ -70,8 +61,7 @@ module Sprockets private def debug_assets? - params[:debug_assets] == '1' || - params[:debug_assets] == 'true' + compile_assets? && (Rails.application.config.assets.debug || params[:debug_assets]) rescue NoMethodError false end @@ -86,6 +76,18 @@ module Sprockets Rails.application.config.assets.prefix end + def asset_digests + Rails.application.config.assets.digests + end + + def compile_assets? + Rails.application.config.assets.compile + end + + def digest_assets? + Rails.application.config.assets.digest + 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+. @@ -94,7 +96,9 @@ module Sprockets end class AssetPaths < ::ActionView::AssetPaths #:nodoc: - attr_accessor :asset_environment, :asset_prefix + attr_accessor :asset_environment, :asset_prefix, :asset_digests, :compile_assets, :digest_assets + + class AssetNotPrecompiledError < StandardError; end def compute_public_path(source, dir, ext=nil, include_host=true, protocol=nil) super(source, asset_prefix, ext, include_host, protocol) @@ -112,11 +116,29 @@ module Sprockets asset_environment[source] end + def digest_for(logical_path) + if asset_digests && (digest = asset_digests[logical_path]) + return digest + end + + if compile_assets + if digest_assets && asset = asset_environment[logical_path] + return asset.digest_path + end + return logical_path + else + raise AssetNotPrecompiledError.new("#{logical_path} isn't precompiled") + end + end + def rewrite_asset_path(source, dir) if source[0] == ?/ source else - asset_environment.path(source, performing_caching?, dir) + source = digest_for(source) + source = File.join(dir, source) + source = "/#{source}" unless source =~ /^\// + source end end @@ -127,11 +149,6 @@ module Sprockets source end end - - # When included in Sprockets::Context, we need to ask the top-level config as the controller is not available - def performing_caching? - config.action_controller.present? ? config.action_controller.perform_caching : config.perform_caching - end end end end diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index 0a2c8c1ea3..dc991636a1 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -18,15 +18,24 @@ module Sprockets require 'sprockets' app.assets = Sprockets::Environment.new(app.root.to_s) do |env| - env.static_root = File.join(app.root.join('public'), config.assets.prefix) - env.logger = ::Rails.logger - env.version = ::Rails.env + "#{'-' + config.assets.version if config.assets.version.present?}" + env.logger = ::Rails.logger + env.version = ::Rails.env + "-#{config.assets.version}" if config.assets.cache_store != false env.cache = ActiveSupport::Cache.lookup_store(config.assets.cache_store) || ::Rails.cache end end + if config.assets.manifest + path = File.join(config.assets.manifest, "manifest.yml") + else + path = File.join(Rails.public_path, config.assets.prefix, "manifest.yml") + end + + if File.exist?(path) + config.assets.digests = YAML.load_file(path) + end + ActiveSupport.on_load(:action_view) do include ::Sprockets::Helpers::RailsHelper @@ -62,7 +71,7 @@ module Sprockets mount app.assets => config.assets.prefix end - if config.action_controller.perform_caching + if config.assets.digest app.assets = app.assets.index end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 6bcd606bf4..c46755417f 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -410,7 +410,7 @@ class TestController < ActionController::Base end def render_with_explicit_escaped_template - render :template => "test/hello_w*rld" + render :template => "test/hello,world" end def render_with_explicit_string_template diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 043d44500a..cba3aded2f 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -50,6 +50,10 @@ class TestTest < ActionController::TestCase render :text => request.query_string end + def test_protocol + render :text => request.protocol + end + def test_html_output render :text => <<HTML <html> @@ -515,6 +519,12 @@ XML ) end + def test_params_passing_doesnt_modify_in_place + page = {:name => "Page name", :month => 4, :year => 2004, :day => 6} + get :test_params, :page => page + assert_equal 2004, page[:year] + end + def test_id_converted_to_string get :test_params, :id => 20, :foo => Object.new assert_kind_of String, @request.path_parameters['id'] @@ -592,6 +602,19 @@ XML assert_nil @request.symbolized_path_parameters[:id] end + def test_request_protocol_is_reset_after_request + get :test_protocol + assert_equal "http://", @response.body + + @request.env["HTTPS"] = "on" + get :test_protocol + assert_equal "https://", @response.body + + @request.env.delete("HTTPS") + get :test_protocol + assert_equal "http://", @response.body + end + def test_should_have_knowledge_of_client_side_cookie_state_even_if_they_are_not_set cookies['foo'] = 'bar' get :no_op diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index fb67ecb07d..49da448001 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -148,6 +148,22 @@ class CookiesTest < ActionController::TestCase @request.host = "www.nextangle.com" end + def test_each + request.cookie_jar['foo'] = :bar + list = [] + request.cookie_jar.each do |k,v| + list << [k, v] + end + + assert_equal [['foo', :bar]], list + end + + def test_enumerable + request.cookie_jar['foo'] = :bar + actual = request.cookie_jar.map { |k,v| [k.to_s, v.to_s] } + assert_equal [['foo', 'bar']], actual + end + def test_key_methods assert !request.cookie_jar.key?(:foo) assert !request.cookie_jar.has_key?("foo") diff --git a/actionpack/test/fixtures/test/hello_w*rld.erb b/actionpack/test/fixtures/test/hello,world.erb index bc8fa5e0ca..bc8fa5e0ca 100644 --- a/actionpack/test/fixtures/test/hello_w*rld.erb +++ b/actionpack/test/fixtures/test/hello,world.erb diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb index a9157e711c..13e2d5b595 100644 --- a/actionpack/test/template/capture_helper_test.rb +++ b/actionpack/test/template/capture_helper_test.rb @@ -46,6 +46,42 @@ class CaptureHelperTest < ActionView::TestCase assert_equal "bar", content_for(:bar) end + def test_content_for_with_multiple_calls + assert ! content_for?(:title) + content_for :title, 'foo' + content_for :title, 'bar' + assert_equal 'foobar', content_for(:title) + end + + def test_content_for_with_block + assert ! content_for?(:title) + content_for :title do + output_buffer << 'foo' + output_buffer << 'bar' + nil + end + assert_equal 'foobar', content_for(:title) + end + + def test_content_for_with_whitespace_block + assert ! content_for?(:title) + content_for :title, 'foo' + content_for :title do + output_buffer << " \n " + nil + end + content_for :title, 'bar' + assert_equal 'foobar', content_for(:title) + end + + def test_content_for_returns_nil_when_writing + assert ! content_for?(:title) + assert_equal nil, content_for(:title, 'foo') + assert_equal nil, content_for(:title) { output_buffer << 'bar'; nil } + assert_equal nil, content_for(:title) { output_buffer << " \n "; nil } + assert_equal 'foobar', content_for(:title) + end + def test_content_for_question_mark assert ! content_for?(:title) content_for :title, 'title' diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index eb569c7308..6eae9bf846 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -530,6 +530,12 @@ class FormTagHelperTest < ActionView::TestCase assert_equal options, { :option => "random_option" } end + def test_image_label_tag_options_symbolize_keys_side_effects + options = { :option => "random_option" } + label_tag "submit source", "title", options + assert_equal options, { :option => "random_option" } + end + def protect_against_forgery? false end diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index dd8b7b7cd5..4b9c3c97b1 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/string/encoding' class JavaScriptHelperTest < ActionView::TestCase tests ActionView::Helpers::JavaScriptHelper @@ -27,6 +28,11 @@ class JavaScriptHelperTest < ActionView::TestCase assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos')) assert_equal %(backslash\\\\test), escape_javascript( %(backslash\\test) ) assert_equal %(dont <\\/close> tags), escape_javascript(%(dont </close> tags)) + if "ruby".encoding_aware? + assert_equal %(unicode 
 newline), escape_javascript(%(unicode \342\200\250 newline).force_encoding('UTF-8').encode!) + else + assert_equal %(unicode 
 newline), escape_javascript(%(unicode \342\200\250 newline)) + end assert_equal %(dont <\\/close> tags), j(%(dont </close> tags)) end diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 1ba14e8bc9..7f23629e05 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -4,11 +4,20 @@ require 'controller/fake_models' class Post extend ActiveModel::Naming include ActiveModel::Conversion + attr_writer :id, :body + + def initialize + @id = nil + @body = nil + super + end + def id - 45 + @id || 45 end + def body - super || "What a wonderful world!" + super || @body || "What a wonderful world!" end end @@ -58,4 +67,32 @@ class RecordTagHelperTest < ActionView::TestCase actual = div_for(@post, :class => "bar") { concat @post.body } assert_dom_equal expected, actual end + + def test_content_tag_for_collection + post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true } + post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true } + expected = %(<li class="post" id="post_101">Hello!</li>\n<li class="post" id="post_102">World!</li>) + actual = content_tag_for(:li, [post_1, post_2]) { |post| concat post.body } + assert_dom_equal expected, actual + end + + def test_div_for_collection + post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true } + post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true } + expected = %(<div class="post" id="post_101">Hello!</div>\n<div class="post" id="post_102">World!</div>) + actual = div_for([post_1, post_2]) { |post| concat post.body } + assert_dom_equal expected, actual + end + + def test_content_tag_for_single_record_is_html_safe + result = div_for(@post, :class => "bar") { concat @post.body } + assert result.html_safe? + end + + def test_content_tag_for_collection_is_html_safe + post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true } + post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true } + result = content_tag_for(:li, [post_1, post_2]) { |post| concat post.body } + assert result.html_safe? + end end diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb index dfa635335e..ae4cb1f0aa 100644 --- a/actionpack/test/template/sprockets_helper_test.rb +++ b/actionpack/test/template/sprockets_helper_test.rb @@ -30,6 +30,8 @@ class SprocketsHelperTest < ActionView::TestCase @config = config @config.action_controller ||= ActiveSupport::InheritableOptions.new @config.perform_caching = true + @config.assets.digest = true + @config.assets.compile = true end def url_for(*args) @@ -151,11 +153,16 @@ class SprocketsHelperTest < ActionView::TestCase assert_equal '<script src="http://www.example.com/xmlhr" type="text/javascript"></script>', javascript_include_tag("http://www.example.com/xmlhr") + assert_match %r{<script src=\"/assets/xmlhr-[0-9a-f]+.js" type=\"text/javascript\"></script>\n<script src=\"/assets/extra-[0-9a-f]+.js" type=\"text/javascript\"></script>}, + javascript_include_tag("xmlhr", "extra") + assert_match %r{<script src="/assets/xmlhr-[0-9a-f]+.js\?body=1" type="text/javascript"></script>\n<script src="/assets/application-[0-9a-f]+.js\?body=1" type="text/javascript"></script>}, javascript_include_tag(:application, :debug => true) - assert_match %r{<script src=\"/assets/xmlhr-[0-9a-f]+.js\" type=\"text/javascript\"></script>\n<script src=\"/assets/extra-[0-9a-f]+.js\" type=\"text/javascript\"></script>}, - javascript_include_tag("xmlhr", "extra") + @config.assets.compile = true + @config.assets.debug = true + assert_match %r{<script src="/assets/xmlhr-[0-9a-f]+.js\?body=1" type="text/javascript"></script>\n<script src="/assets/application-[0-9a-f]+.js\?body=1" type="text/javascript"></script>}, + javascript_include_tag(:application) end test "stylesheet path" do @@ -187,11 +194,19 @@ class SprocketsHelperTest < ActionView::TestCase assert_match %r{<link href="/assets/style-[0-9a-f]+.css" media="print" rel="stylesheet" type="text/css" />}, stylesheet_link_tag("style", :media => "print") + assert_match %r{<link href="/assets/style-[0-9a-f]+.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/assets/extra-[0-9a-f]+.css" media="screen" rel="stylesheet" type="text/css" />}, + stylesheet_link_tag("style", "extra") + assert_match %r{<link href="/assets/style-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />\n<link href="/assets/application-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />}, stylesheet_link_tag(:application, :debug => true) - assert_match %r{<link href="/assets/style-[0-9a-f]+.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/assets/extra-[0-9a-f]+.css" media="screen" rel="stylesheet" type="text/css" />}, - stylesheet_link_tag("style", "extra") + @config.assets.compile = true + @config.assets.debug = true + assert_match %r{<link href="/assets/style-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />\n<link href="/assets/application-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />}, + stylesheet_link_tag(:application) + + assert_match %r{<link href="/assets/style-[0-9a-f]+.css\?body=1" media="print" rel="stylesheet" type="text/css" />\n<link href="/assets/application-[0-9a-f]+.css\?body=1" media="print" rel="stylesheet" type="text/css" />}, + stylesheet_link_tag(:application, :media => "print") end test "alternate asset prefix" do diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 78245c1f95..dbac2e1fc0 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -304,8 +304,8 @@ class UrlHelperTest < ActiveSupport::TestCase assert_equal "Showing", link_to_if(false, "Showing", url_hash) end - def request_for_url(url) - env = Rack::MockRequest.env_for("http://www.example.com#{url}") + def request_for_url(url, opts = {}) + env = Rack::MockRequest.env_for("http://www.example.com#{url}", opts) ActionDispatch::Request.new(env) end @@ -329,6 +329,12 @@ class UrlHelperTest < ActiveSupport::TestCase assert current_page?("http://www.example.com/?order=desc&page=1") end + def test_current_page_with_not_get_verb + @request = request_for_url("/events", :method => :post) + + assert !current_page?('/events') + end + def test_link_unless_current @request = request_for_url("/") |