diff options
Diffstat (limited to 'actionpack')
54 files changed, 689 insertions, 437 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index e5bd21ea7b..9db30c8cf7 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,58 @@ ## Rails 4.0.0 (unreleased) ## +* Rename all action callbacks from *_filter to *_action to avoid the misconception that these + callbacks are only suited for transforming or halting the response. With the new style, + it's more inviting to use them as they were intended, like setting shared ivars for views. + + Example: + + class PeopleController < ActionController::Base + before_action :set_person, except: [ :index, :new, :create ] + before_action :ensure_permission, only: [ :edit, :update ] + + ... + + private + def set_person + @person = current_account.people.find(params[:id]) + end + + def ensure_permission + current_person.can_change?(@person) + end + end + + The old *_filter methods still work with no deprecation notice. + + *DHH* + +* Add :if / :unless conditions to fragment cache: + + <%= cache @model, if: some_condition(@model) do %> + + *Stephen Ausman + Fabrizio Regini* + +* Add filter capability to ActionController logs for redirect locations: + + config.filter_redirect << 'http://please.hide.it/' + + *Fabrizio Regini* + +* Fixed a bug that ignores constraints on a glob route. This was caused because the constraint + regular expression is overwritten when the `routes.rb` file is processed. Fixes #7924 + + *Maura Fitzgerald* + +* More descriptive error messages when calling `render :partial` with + an invalid `:layout` argument. + #8376 + + render :partial => 'partial', :layout => true + + # results in ActionView::MissingTemplate: Missing partial /true + + *Yves Senn* + * Sweepers was extracted from Action Controller as `rails-observers` gem. *Rafael Mendonça França* diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index 02ac111392..599fff81c2 100644 --- a/actionpack/lib/abstract_controller/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb @@ -40,19 +40,22 @@ module AbstractController end end - # Skip before, after, and around filters matching any of the names + # Skip before, after, and around action callbacks matching any of the names + # Aliased as skip_filter. # # ==== Parameters # * <tt>names</tt> - A list of valid names that could be used for # callbacks. Note that skipping uses Ruby equality, so it's # impossible to skip a callback defined using an anonymous proc # using #skip_filter - def skip_filter(*names) - skip_before_filter(*names) - skip_after_filter(*names) - skip_around_filter(*names) + def skip_action_callback(*names) + skip_before_action(*names) + skip_after_action(*names) + skip_around_action(*names) end + alias_method :skip_filter, :skip_action_callback + # Take callback names and an optional callback proc, normalize them, # then call the block with each callback. This allows us to abstract # the normalization across several methods that use it. @@ -75,119 +78,138 @@ module AbstractController end ## - # :method: before_filter + # :method: before_action # - # :call-seq: before_filter(names, block) + # :call-seq: before_action(names, block) # - # Append a before filter. See _insert_callbacks for parameter details. + # Append a callback before actions. See _insert_callbacks for parameter details. + # Aliased as before_filter. ## - # :method: prepend_before_filter + # :method: prepend_before_action # - # :call-seq: prepend_before_filter(names, block) + # :call-seq: prepend_before_action(names, block) # - # Prepend a before filter. See _insert_callbacks for parameter details. + # Prepend a callback before actions. See _insert_callbacks for parameter details. + # Aliased as prepend_before_filter. ## - # :method: skip_before_filter + # :method: skip_before_action # - # :call-seq: skip_before_filter(names) + # :call-seq: skip_before_action(names) # - # Skip a before filter. See _insert_callbacks for parameter details. + # Skip a callback before actions. See _insert_callbacks for parameter details. + # Aliased as skip_before_filter. ## - # :method: append_before_filter + # :method: append_before_action # - # :call-seq: append_before_filter(names, block) + # :call-seq: append_before_action(names, block) # - # Append a before filter. See _insert_callbacks for parameter details. + # Append a callback before actions. See _insert_callbacks for parameter details. + # Aliased as append_before_filter. ## - # :method: after_filter + # :method: after_action # - # :call-seq: after_filter(names, block) + # :call-seq: after_action(names, block) # - # Append an after filter. See _insert_callbacks for parameter details. + # Append a callback after actions. See _insert_callbacks for parameter details. + # Aliased as after_filter. ## - # :method: prepend_after_filter + # :method: prepend_after_action # - # :call-seq: prepend_after_filter(names, block) + # :call-seq: prepend_after_action(names, block) # - # Prepend an after filter. See _insert_callbacks for parameter details. + # Prepend a callback after actions. See _insert_callbacks for parameter details. + # Aliased as prepend_after_filter. ## - # :method: skip_after_filter + # :method: skip_after_action # - # :call-seq: skip_after_filter(names) + # :call-seq: skip_after_action(names) # - # Skip an after filter. See _insert_callbacks for parameter details. + # Skip a callback after actions. See _insert_callbacks for parameter details. + # Aliased as skip_after_filter. ## - # :method: append_after_filter + # :method: append_after_action # - # :call-seq: append_after_filter(names, block) + # :call-seq: append_after_action(names, block) # - # Append an after filter. See _insert_callbacks for parameter details. + # Append a callback after actions. See _insert_callbacks for parameter details. + # Aliased as append_after_filter. ## - # :method: around_filter + # :method: around_action # - # :call-seq: around_filter(names, block) + # :call-seq: around_action(names, block) # - # Append an around filter. See _insert_callbacks for parameter details. + # Append a callback around actions. See _insert_callbacks for parameter details. + # Aliased as around_filter. ## - # :method: prepend_around_filter + # :method: prepend_around_action # - # :call-seq: prepend_around_filter(names, block) + # :call-seq: prepend_around_action(names, block) # - # Prepend an around filter. See _insert_callbacks for parameter details. + # Prepend a callback around actions. See _insert_callbacks for parameter details. + # Aliased as prepend_around_filter. ## - # :method: skip_around_filter + # :method: skip_around_action # - # :call-seq: skip_around_filter(names) + # :call-seq: skip_around_action(names) # - # Skip an around filter. See _insert_callbacks for parameter details. + # Skip a callback around actions. See _insert_callbacks for parameter details. + # Aliased as skip_around_filter. ## - # :method: append_around_filter + # :method: append_around_action # - # :call-seq: append_around_filter(names, block) + # :call-seq: append_around_action(names, block) # - # Append an around filter. See _insert_callbacks for parameter details. + # Append a callback around actions. See _insert_callbacks for parameter details. + # Aliased as append_around_filter. - # set up before_filter, prepend_before_filter, skip_before_filter, etc. + # set up before_action, prepend_before_action, skip_before_action, etc. # for each of before, after, and around. - [:before, :after, :around].each do |filter| + [:before, :after, :around].each do |callback| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - # Append a before, after or around filter. See _insert_callbacks + # Append a before, after or around callback. See _insert_callbacks # for details on the allowed parameters. - def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk) - _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| - set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options) - end # end - end # end + def #{callback}_action(*names, &blk) # def before_action(*names, &blk) + _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| + set_callback(:process_action, :#{callback}, name, options) # set_callback(:process_action, :before, name, options) + end # end + end # end + + alias_method :#{callback}_filter, :#{callback}_action - # Prepend a before, after or around filter. See _insert_callbacks + # Prepend a before, after or around callback. See _insert_callbacks # for details on the allowed parameters. - def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk) - _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| - set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true)) - end # end - end # end + def prepend_#{callback}_action(*names, &blk) # def prepend_before_action(*names, &blk) + _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| + set_callback(:process_action, :#{callback}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true)) + end # end + end # end + + alias_method :prepend_#{callback}_filter, :prepend_#{callback}_action - # Skip a before, after or around filter. See _insert_callbacks + # Skip a before, after or around callback. See _insert_callbacks # for details on the allowed parameters. - def skip_#{filter}_filter(*names) # def skip_before_filter(*names) - _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options| - skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options) - end # end - end # end - - # *_filter is the same as append_*_filter - alias_method :append_#{filter}_filter, :#{filter}_filter # alias_method :append_before_filter, :before_filter + def skip_#{callback}_action(*names) # def skip_before_action(*names) + _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options| + skip_callback(:process_action, :#{callback}, name, options) # skip_callback(:process_action, :before, name, options) + end # end + end # end + + alias_method :skip_#{callback}_filter, :skip_#{callback}_action + + # *_action is the same as append_*_action + alias_method :append_#{callback}_action, :#{callback}_action # alias_method :append_before_action, :before_action + alias_method :append_#{callback}_filter, :#{callback}_action # alias_method :append_before_filter, :before_action RUBY_EVAL end end diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index d3929b685c..d4e73bf257 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -58,11 +58,10 @@ module AbstractController # The +helper+ class method can take a series of helper module names, a block, or both. # - # ==== Parameters + # ==== Options # * <tt>*args</tt> - Module, Symbol, String, :all # * <tt>block</tt> - A block defining helper methods # - # ==== Examples # When the argument is a module it will be included directly in the template class. # helper FooHelper # => includes FooHelper # diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 177da1c8a0..2892e093af 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -6,8 +6,7 @@ module ActionController # \Caching is a cheap way of speeding up slow applications by keeping the result of # calculations, renderings, and database calls around for subsequent requests. # - # You can read more about each approach and the by clicking the - # modules below. + # You can read more about each approach by clicking the modules below. # # Note: To turn off all caching, set # config.action_controller.perform_caching = false. diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 426adfe675..3f9b382a11 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -42,7 +42,7 @@ module ActionController # * <tt>:public</tt> By default the Cache-Control header is private, set this to # +true+ if you want your application to be cachable by other devices (proxy caches). # - # === Example: + # === Example: # # def show # @article = Article.find(params[:id]) diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index c38d8ccef3..f1e8714a86 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -32,14 +32,14 @@ module ActionController # ==== Options # * <tt>host</tt> - Redirect to a different host name # * <tt>only</tt> - The callback should be run only for this action - # * <tt>except</tt> - The callback should be run for all actions except this action + # * <tt>except</tt> - The callback should be run for all actions except this action # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback # will be called only when it returns a true value. # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback # will be called only when it returns a false value. def force_ssl(options = {}) host = options.delete(:host) - before_filter(options) do + before_action(options) do force_ssl_redirect(host) end end diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index d3b5bafee1..283f6413ec 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -25,7 +25,7 @@ module ActionController # the regular HTML interface is protected by a session approach: # # class ApplicationController < ActionController::Base - # before_filter :set_account, :authenticate + # before_action :set_account, :authenticate # # protected # def set_account @@ -68,7 +68,7 @@ module ActionController module ClassMethods def http_basic_authenticate_with(options = {}) - before_filter(options.except(:name, :password, :realm)) do + before_action(options.except(:name, :password, :realm)) do authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password| name == options[:name] && password == options[:password] end @@ -124,7 +124,7 @@ module ActionController # USERS = {"dhh" => "secret", #plain text password # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password # - # before_filter :authenticate, except: [:index] + # before_action :authenticate, except: [:index] # # def index # render text: "Everyone can see me!" @@ -317,7 +317,7 @@ module ActionController # class PostsController < ApplicationController # TOKEN = "secret" # - # before_filter :authenticate, except: [ :index ] + # before_action :authenticate, except: [ :index ] # # def index # render text: "Everyone can see me!" @@ -340,7 +340,7 @@ module ActionController # the regular HTML interface is protected by a session approach: # # class ApplicationController < ActionController::Base - # before_filter :set_account, :authenticate + # before_action :set_account, :authenticate # # protected # def set_account diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index ca4ae532ca..d3aa8f90c5 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -60,7 +60,7 @@ module ActionController ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload| result = super payload[:status] = response.status - payload[:location] = response.location + payload[:location] = response.filtered_location result 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 265ce5d6f3..c5db0cb0d4 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -19,7 +19,7 @@ module ActionController #:nodoc: # # class ApplicationController < ActionController::Base # protect_from_forgery - # skip_before_filter :verify_authenticity_token, if: :json_request? + # skip_before_action :verify_authenticity_token, if: :json_request? # # protected # @@ -66,15 +66,15 @@ module ActionController #:nodoc: # # You can disable csrf protection on controller-by-controller basis: # - # skip_before_filter :verify_authenticity_token + # skip_before_action :verify_authenticity_token # # It can also be disabled for specific controller actions: # - # skip_before_filter :verify_authenticity_token, except: [:create] + # skip_before_action :verify_authenticity_token, except: [:create] # # Valid Options: # - # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified. + # * <tt>:only/:except</tt> - Passed to the <tt>before_action</tt> call. Set which actions are verified. # * <tt>:with</tt> - Set the method to handle unverified request. # # Valid unverified request handling methods are: @@ -84,7 +84,7 @@ module ActionController #:nodoc: def protect_from_forgery(options = {}) include protection_method_module(options[:with] || :null_session) self.request_forgery_protection_token ||= :authenticity_token - prepend_before_filter :verify_authenticity_token, options + prepend_before_action :verify_authenticity_token, options end private @@ -152,7 +152,7 @@ module ActionController #:nodoc: end protected - # The actual before_filter that is used. Modify this to change how you handle unverified requests. + # The actual before_action that is used. Modify this to change how you handle unverified requests. def verify_authenticity_token unless verified_request? logger.warn "Can't verify CSRF token authenticity" if logger diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 4eb582648e..0b3c438ec2 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -21,8 +21,6 @@ module ActionController #:nodoc: # supports fibers (fibers are supported since version 1.9.2 of the main # Ruby implementation). # - # == Examples - # # Streaming can be added to a given template easily, all you need to do is # to pass the :stream option. # diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 386075bd30..25e72adbe0 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -163,7 +163,7 @@ module ActionController # } # }) # - # permitted = params.permit(person: [ :name, { pets: :name } ]) + # permitted = params.permit(person: [ :name, { pets: :name } ]) # permitted.permitted? # => true # permitted[:person][:name] # => "Francesco" # permitted[:person][:age] # => nil @@ -228,7 +228,7 @@ module ActionController # Returns a parameter for the given +key+. If not found, # returns +nil+. # - # params = ActionController::Parameters.new(person: { name: 'Francesco' }) + # params = ActionController::Parameters.new(person: { name: 'Francesco' }) # params[:person] # => {"name"=>"Francesco"} # params[:none] # => nil def [](key) diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 1d716a3248..d002babee3 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -75,6 +75,7 @@ module ActionDispatch autoload :Parameters autoload :ParameterFilter autoload :FilterParameters + autoload :FilterRedirect autoload :Upload autoload :UploadedFile, 'action_dispatch/http/upload' autoload :URL diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb new file mode 100644 index 0000000000..900ce1c646 --- /dev/null +++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb @@ -0,0 +1,37 @@ +module ActionDispatch + module Http + module FilterRedirect + + FILTERED = '[FILTERED]'.freeze # :nodoc: + + def filtered_location + if !location_filter.empty? && location_filter_match? + FILTERED + else + location + end + end + + private + + def location_filter + if request.present? + request.env['action_dispatch.redirect_filter'] || [] + else + [] + end + end + + def location_filter_match? + location_filter.any? do |filter| + if String === filter + location.include?(filter) + elsif Regexp === filter + location.match(filter) + end + end + end + + end + end +end diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 0f98e84788..57660e93c4 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -68,7 +68,7 @@ module ActionDispatch # that are not controlled by the extension. # # class ApplicationController < ActionController::Base - # before_filter :adjust_format_for_iphone + # before_action :adjust_format_for_iphone # # private # def adjust_format_for_iphone @@ -87,7 +87,7 @@ module ActionDispatch # to the :html format. # # class ApplicationController < ActionController::Base - # before_filter :adjust_format_for_iphone_with_html_fallback + # before_action :adjust_format_for_iphone_with_html_fallback # # private # def adjust_format_for_iphone_with_html_fallback diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 11b7534ea4..0f808ac9cf 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -61,6 +61,7 @@ module ActionDispatch # :nodoc: cattr_accessor(:default_headers) include Rack::Response::Helpers + include ActionDispatch::Http::FilterRedirect include ActionDispatch::Http::Cache::Response include MonitorMixin diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 7b18c57420..f24e9b8e18 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -105,7 +105,7 @@ module ActionDispatch super end - def []=(k, v) #:nodoc: + def []=(k, v) @discard.delete k @flashes[k] = v end diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 98c87d9b2d..5a835ae439 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -1,7 +1,7 @@ require "action_dispatch" module ActionDispatch - class Railtie < Rails::Railtie + class Railtie < Rails::Railtie # :nodoc: config.action_dispatch = ActiveSupport::OrderedOptions.new config.action_dispatch.x_sendfile_header = nil config.action_dispatch.ip_spoofing_check = true diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 4417cb841a..d55eb8109a 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -191,8 +191,6 @@ module ActionDispatch # <tt>:any</tt> which means that the route will respond to any of the HTTP # methods. # - # Examples: - # # match 'post/:id' => 'posts#show', via: :get # match 'post/:id' => 'posts#create_comment', via: :post # @@ -204,8 +202,6 @@ module ActionDispatch # An alternative method of specifying which HTTP method a route should respond to is to use the helper # methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>. # - # Examples: - # # get 'post/:id' => 'posts#show' # post 'post/:id' => 'posts#create_comment' # diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 0c19b493ab..3c99932e72 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -158,7 +158,7 @@ module ActionDispatch def requirements @requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements| requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints] - @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) } + @options.each { |k, v| requirements[k] ||= v if v.is_a?(Regexp) } end end @@ -624,8 +624,6 @@ module ActionDispatch # # Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>. # - # === Examples - # # # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt> # scope module: "admin" do # resources :posts @@ -706,8 +704,6 @@ module ActionDispatch # For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see # <tt>Resources#resources</tt>. # - # === Examples - # # # accessible through /sekret/posts rather than /admin/posts # namespace :admin, path: "sekret" do # resources :posts @@ -1052,15 +1048,7 @@ module ActionDispatch get :new end if parent_resource.actions.include?(:new) - member do - get :edit if parent_resource.actions.include?(:edit) - get :show if parent_resource.actions.include?(:show) - if parent_resource.actions.include?(:update) - patch :update - put :update - end - delete :destroy if parent_resource.actions.include?(:destroy) - end + set_member_mappings_for_resource end self @@ -1219,15 +1207,7 @@ module ActionDispatch get :new end if parent_resource.actions.include?(:new) - member do - get :edit if parent_resource.actions.include?(:edit) - get :show if parent_resource.actions.include?(:show) - if parent_resource.actions.include?(:update) - patch :update - put :update - end - delete :destroy if parent_resource.actions.include?(:destroy) - end + set_member_mappings_for_resource end self @@ -1578,6 +1558,18 @@ module ActionDispatch end end end + + def set_member_mappings_for_resource + member do + get :edit if parent_resource.actions.include?(:edit) + get :show if parent_resource.actions.include?(:show) + if parent_resource.actions.include?(:update) + patch :update + put :update + end + delete :destroy if parent_resource.actions.include?(:destroy) + end + end end # Routing Concerns allow you to declare common routes that can be reused diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 497ac3d545..6d3f8da932 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -74,8 +74,6 @@ module ActionDispatch # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>. # Default is <tt>:url</tt>. # - # ==== Examples - # # # an Article record # polymorphic_url(record) # same as article_url(record) # diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb index 2207a43afc..e481f3b245 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb @@ -155,8 +155,6 @@ module ActionDispatch # If the method is called with a block, once all equality tests are # evaluated the block is called with an array of all matched elements. # - # ==== Examples - # # # At least one form element # assert_select "form" # diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 29a5ccedc1..cf2a117966 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -32,6 +32,9 @@ module ActionView # You can modify the HTML attributes of the script tag by passing a hash as the # last argument. # + # When the Asset Pipeline is enabled, you can pass the name of your manifest as + # source, and include other JavaScript or CoffeeScript files inside the manifest. + # # javascript_include_tag "xmlhr" # # => <script src="/assets/xmlhr.js?1284139606"></script> # @@ -106,19 +109,18 @@ module ActionView # * <tt>:type</tt> - Override the auto-generated mime type # * <tt>:title</tt> - Specify the title of the link, defaults to the +type+ # - # ==== Examples - # auto_discovery_link_tag - # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" /> - # auto_discovery_link_tag(:atom) - # # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" /> - # auto_discovery_link_tag(:rss, {action: "feed"}) - # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" /> - # auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"}) - # # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" /> - # auto_discovery_link_tag(:rss, {controller: "news", action: "feed"}) - # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" /> - # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"}) - # # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" /> + # auto_discovery_link_tag + # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" /> + # auto_discovery_link_tag(:atom) + # # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" /> + # auto_discovery_link_tag(:rss, {action: "feed"}) + # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" /> + # auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"}) + # # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" /> + # auto_discovery_link_tag(:rss, {controller: "news", action: "feed"}) + # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" /> + # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"}) + # # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" /> def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {}) if !(type == :rss || type == :atom) && tag_options[:type].blank? message = "You have passed type other than :rss or :atom to auto_discovery_link_tag and haven't supplied " + @@ -137,27 +139,24 @@ module ActionView ) end - # <%= favicon_link_tag %> - # - # generates - # - # <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" /> - # - # You may specify a different file in the first argument: + # Returns a link loading a favicon file. You may specify a different file + # in the first argument. The helper accepts an additional options hash where + # you can override "rel" and "type". # - # <%= favicon_link_tag '/myicon.ico' %> - # - # That's passed to +path_to_image+ as is, so it gives - # - # <link href="/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" /> + # ==== Options + # * <tt>:rel</tt> - Specify the relation of this link, defaults to 'shortcut icon' + # * <tt>:type</tt> - Override the auto-generated mime type, defaults to 'image/vnd.microsoft.icon' # - # The helper accepts an additional options hash where you can override "rel" and "type". + # favicon_link_tag '/myicon.ico' + # # => <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" /> # - # For example, Mobile Safari looks for a different LINK tag, pointing to an image that + # Mobile Safari looks for a different <link> tag, pointing to an image that # will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad. # The following call would generate such a tag: # - # <%= favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png' %> + # favicon_link_tag '/mb-icon.png', rel: 'apple-touch-icon', type: 'image/png' + # # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" /> + # def favicon_link_tag(source='favicon.ico', options={}) tag('link', { :rel => 'shortcut icon', @@ -166,7 +165,7 @@ module ActionView }.merge(options.symbolize_keys)) end - # Returns an html image tag for the +source+. The +source+ can be a full + # Returns an HTML image tag for the +source+. The +source+ can be a full # path or a file. # # ==== Options @@ -179,18 +178,18 @@ module ActionView # width="30" and height="45", and "50" becomes width="50" and height="50". # <tt>:size</tt> will be ignored if the value is not in the correct format. # - # image_tag("icon") - # # => <img alt="Icon" src="/assets/icon" /> - # image_tag("icon.png") - # # => <img alt="Icon" src="/assets/icon.png" /> - # image_tag("icon.png", size: "16x10", alt: "Edit Entry") - # # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" /> - # image_tag("/icons/icon.gif", size: "16") - # # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" /> - # image_tag("/icons/icon.gif", height: '32', width: '32') - # # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" /> - # image_tag("/icons/icon.gif", class: "menu_icon") - # # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" /> + # image_tag("icon") + # # => <img alt="Icon" src="/assets/icon" /> + # image_tag("icon.png") + # # => <img alt="Icon" src="/assets/icon.png" /> + # image_tag("icon.png", size: "16x10", alt: "Edit Entry") + # # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" /> + # image_tag("/icons/icon.gif", size: "16") + # # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" /> + # image_tag("/icons/icon.gif", height: '32', width: '32') + # # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" /> + # image_tag("/icons/icon.gif", class: "menu_icon") + # # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" /> def image_tag(source, options={}) options = options.symbolize_keys @@ -208,6 +207,9 @@ module ActionView tag("img", options) end + # Returns a string suitable for an html image tag alt attribute. + # +src+ is meant to be an image file path. + # It removes the basename of the file path and the digest, if any. def image_alt(src) File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize end @@ -228,24 +230,24 @@ module ActionView # width="30" and height="45". <tt>:size</tt> will be ignored if the # value is not in the correct format. # - # video_tag("trailer") - # # => <video src="/videos/trailer" /> - # video_tag("trailer.ogg") - # # => <video src="/videos/trailer.ogg" /> - # video_tag("trailer.ogg", controls: true, autobuffer: true) - # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" /> - # video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png") - # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" /> - # video_tag("/trailers/hd.avi", size: "16x16") - # # => <video src="/trailers/hd.avi" width="16" height="16" /> - # video_tag("/trailers/hd.avi", height: '32', width: '32') - # # => <video height="32" src="/trailers/hd.avi" width="32" /> - # video_tag("trailer.ogg", "trailer.flv") - # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video> - # video_tag(["trailer.ogg", "trailer.flv"]) - # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video> - # video_tag(["trailer.ogg", "trailer.flv"], size: "160x120") - # # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video> + # video_tag("trailer") + # # => <video src="/videos/trailer" /> + # video_tag("trailer.ogg") + # # => <video src="/videos/trailer.ogg" /> + # video_tag("trailer.ogg", controls: true, autobuffer: true) + # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" /> + # video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png") + # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" /> + # video_tag("/trailers/hd.avi", size: "16x16") + # # => <video src="/trailers/hd.avi" width="16" height="16" /> + # video_tag("/trailers/hd.avi", height: '32', width: '32') + # # => <video height="32" src="/trailers/hd.avi" width="32" /> + # video_tag("trailer.ogg", "trailer.flv") + # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video> + # video_tag(["trailer.ogg", "trailer.flv"]) + # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video> + # video_tag(["trailer.ogg", "trailer.flv"], size: "160x120") + # # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video> def video_tag(*sources) multiple_sources_tag('video', sources) do |options| options[:poster] = path_to_image(options[:poster]) if options[:poster] @@ -256,18 +258,18 @@ module ActionView end end - # Returns an html audio tag for the +source+. + # Returns an HTML audio tag for the +source+. # The +source+ can be full path or file that exists in # your public audios directory. # - # audio_tag("sound") # => - # <audio src="/audios/sound" /> - # audio_tag("sound.wav") # => - # <audio src="/audios/sound.wav" /> - # audio_tag("sound.wav", autoplay: true, controls: true) # => - # <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" /> - # audio_tag("sound.wav", "sound.mid") # => - # <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio> + # audio_tag("sound") + # # => <audio src="/audios/sound" /> + # audio_tag("sound.wav") + # # => <audio src="/audios/sound.wav" /> + # audio_tag("sound.wav", autoplay: true, controls: true) + # # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" /> + # audio_tag("sound.wav", "sound.mid") + # # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio> def audio_tag(*sources) multiple_sources_tag('audio', sources) end diff --git a/actionpack/lib/action_view/helpers/asset_url_helper.rb b/actionpack/lib/action_view/helpers/asset_url_helper.rb index 0bb5e739bb..0affac41e8 100644 --- a/actionpack/lib/action_view/helpers/asset_url_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_url_helper.rb @@ -2,7 +2,7 @@ require 'zlib' module ActionView # = Action View Asset URL Helpers - module Helpers #:nodoc: + module Helpers # This module provides methods for generating asset paths and # urls. # diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index f5ac455208..42b1dd8933 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -2,7 +2,7 @@ require 'set' module ActionView # = Action View Atom Feed Helpers - module Helpers #:nodoc: + module Helpers module AtomFeedHelper # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other # template languages). @@ -124,7 +124,7 @@ module ActionView end end - class AtomBuilder + class AtomBuilder #:nodoc: XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set def initialize(xml) @@ -158,7 +158,7 @@ module ActionView end end - class AtomFeedBuilder < AtomBuilder + class AtomFeedBuilder < AtomBuilder #:nodoc: def initialize(xml, view, feed_options = {}) @xml, @view, @feed_options = xml, view, feed_options end diff --git a/actionpack/lib/action_view/helpers/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb index dfdd5a786d..87fbf8f1a8 100644 --- a/actionpack/lib/action_view/helpers/benchmark_helper.rb +++ b/actionpack/lib/action_view/helpers/benchmark_helper.rb @@ -2,7 +2,7 @@ require 'active_support/benchmarkable' module ActionView module Helpers - module BenchmarkHelper + module BenchmarkHelper #:nodoc: include ActiveSupport::Benchmarkable def benchmark(*) diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index db920ae7a4..8693f4f0e4 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -110,8 +110,15 @@ module ActionView # <%= some_helper_method(person) %> # # Now all you'll have to do is change that timestamp when the helper method changes. + # + # ==== Conditional caching + # + # You can pass :if and :unless options, to conditionally perform or skip the cache. + # + # <%= cache @model, if: some_condition(@model) do %> + # def cache(name = {}, options = nil, &block) - if controller.perform_caching + if controller.perform_caching && conditions_match?(options) safe_concat(fragment_for(cache_fragment_name(name, options), options, &block)) else yield @@ -136,6 +143,11 @@ module ActionView end private + + def conditions_match?(options) + !(options && (!options.fetch(:if, true) || options.fetch(:unless, false))) + end + def fragment_name_with_digest(name) #:nodoc: if @virtual_path [ diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 85e398e559..4ec860d69a 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -42,14 +42,12 @@ module ActionView end # Calling content_for stores a block of markup in an identifier for later use. - # You can make subsequent calls to the stored content in other templates, helper modules - # or the layout by passing the identifier as an argument to <tt>content_for</tt>. + # In order to access this stored content in other templates, helper modules + # or the layout, you would pass the identifier as an argument to <tt>content_for</tt>. # # Note: <tt>yield</tt> can still be used to retrieve the stored content, but calling # <tt>yield</tt> doesn't work in helper modules, while <tt>content_for</tt> does. # - # ==== Examples - # # <% content_for :not_authorized do %> # alert('You are not authorized to do that!') # <% end %> @@ -74,7 +72,8 @@ module ActionView # # <%= stored_content %> # - # You can use the <tt>yield</tt> syntax alongside an existing call to <tt>yield</tt> in a layout. For example: + # You can also use the <tt>yield</tt> syntax alongside an existing call to + # <tt>yield</tt> in a layout. For example: # # <%# This is the layout %> # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> @@ -115,7 +114,7 @@ module ActionView # <li><%= link_to 'Home', action: 'index' %></li> # <% end %> # - # <%# Add some other content, or use a different template: %> + # And in other place: # # <% content_for :navigation do %> # <li><%= link_to 'Login', action: 'login' %></li> @@ -145,8 +144,7 @@ module ActionView # # <% content_for :script, javascript_include_tag(:defaults) %> # - # WARNING: content_for is ignored in caches. So you shouldn't use it - # for elements that will be fragment cached. + # 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, options = {}, &block) if content || block_given? if block_given? @@ -173,13 +171,9 @@ module ActionView result unless content end - # content_for? simply checks whether any content has been captured yet using content_for + # content_for? checks whether any content has been captured yet using `content_for`. # Useful to render parts of your layout differently based on what is in your views. # - # ==== Examples - # - # Perhaps you will use different css in you layout if no content_for :right_column - # # <%# This is the layout %> # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> # <head> diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 6e51ba66a5..1fbf61a5a9 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -45,7 +45,6 @@ module ActionView # 40-59 secs # => less than a minute # 60-89 secs # => 1 minute # - # ==== Examples # from_time = Time.now # distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour @@ -166,7 +165,6 @@ module ActionView # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based # attribute (identified by +method+) on an object assigned to the template (identified by +object+). # - # # ==== Options # * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g. # "2" instead of "February"). diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index c79d30ea88..17386a57b8 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -460,8 +460,6 @@ module ActionView # doesn't create the form tags themselves. This makes fields_for suitable # for specifying additional model objects in the same form. # - # === Generic Examples - # # Although the usage and purpose of +field_for+ is similar to +form_for+'s, # its method signature is slightly different. Like +form_for+, it yields # a FormBuilder object associated with a particular model object to a block, diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 9310a90f0f..c0e7ee1f8d 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -7,13 +7,11 @@ module ActionView # = Action View Form Option Helpers module Helpers # Provides a number of methods for turning different kinds of containers into a set of option tags. - # == Options + # # The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash: # # * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element. # - # For example, - # # select("post", "category", Post::CATEGORIES, {include_blank: true}) # # could become: @@ -24,7 +22,7 @@ module ActionView # <option>poem</option> # </select> # - # Another common case is a select tag for an <tt>belongs_to</tt>-associated object. + # Another common case is a select tag for a <tt>belongs_to</tt>-associated object. # # Example with @post.person_id => 2: # @@ -41,8 +39,6 @@ module ActionView # # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string. # - # Example: - # # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'}) # # could become: @@ -57,8 +53,6 @@ module ActionView # Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this # option to be in the +html_options+ parameter. # - # Example: - # # select("album[]", "genre", %w[rap rock country], {}, { index: nil }) # # becomes: @@ -71,8 +65,6 @@ module ActionView # # * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output. # - # Example: - # # select("post", "category", Post::CATEGORIES, {disabled: 'restricted'}) # # could become: @@ -86,8 +78,6 @@ module ActionView # # When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled. # - # Example: - # # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }}) # # If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return: @@ -152,7 +142,8 @@ module ActionView # form, and parameters extraction gets the last occurrence of any repeated # key in the query string, that works for ordinary forms. # - # In case if you don't want the helper to generate this hidden field you can specify <tt>include_hidden: false</tt> option. + # In case if you don't want the helper to generate this hidden field you can specify + # <tt>include_hidden: false</tt> option. # def select(object, method, choices, options = {}, html_options = {}) Tags::Select.new(object, method, self, choices, options, html_options).render @@ -170,9 +161,11 @@ module ActionView # retrieve the value/text. # # Example object structure for use with this method: + # # class Post < ActiveRecord::Base # belongs_to :author # end + # # class Author < ActiveRecord::Base # has_many :posts # def name_with_initial @@ -181,6 +174,7 @@ module ActionView # end # # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>): + # # collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true) # # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return: @@ -213,23 +207,28 @@ module ActionView # +collection+, returns a value to be used as the contents of its <tt><option></tt> tag. # # Example object structure for use with this method: + # # class Continent < ActiveRecord::Base # has_many :countries # # attribs: id, name # end + # # class Country < ActiveRecord::Base # belongs_to :continent # # attribs: id, name, continent_id # end + # # class City < ActiveRecord::Base # belongs_to :country # # attribs: id, name, country_id # end # # Sample usage: + # # grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name) # # Possible output: + # # <select name="city[country_id]"> # <optgroup label="Africa"> # <option value="1">South Africa</option> @@ -284,57 +283,54 @@ module ActionView # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+ # may also be an array of values to be selected when using a multiple select. # - # Examples (call, result): # options_for_select([["Dollar", "$"], ["Kroner", "DKK"]]) - # # <option value="$">Dollar</option> - # # <option value="DKK">Kroner</option> + # # => <option value="$">Dollar</option> + # # => <option value="DKK">Kroner</option> # # options_for_select([ "VISA", "MasterCard" ], "MasterCard") - # # <option>VISA</option> - # # <option selected="selected">MasterCard</option> + # # => <option>VISA</option> + # # => <option selected="selected">MasterCard</option> # # options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40") - # # <option value="$20">Basic</option> - # # <option value="$40" selected="selected">Plus</option> + # # => <option value="$20">Basic</option> + # # => <option value="$40" selected="selected">Plus</option> # # options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"]) - # # <option selected="selected">VISA</option> - # # <option>MasterCard</option> - # # <option selected="selected">Discover</option> + # # => <option selected="selected">VISA</option> + # # => <option>MasterCard</option> + # # => <option selected="selected">Discover</option> # # You can optionally provide html attributes as the last element of the array. # - # Examples: # options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"]) - # # <option value="Denmark">Denmark</option> - # # <option value="USA" class="bold" selected="selected">USA</option> - # # <option value="Sweden" selected="selected">Sweden</option> + # # => <option value="Denmark">Denmark</option> + # # => <option value="USA" class="bold" selected="selected">USA</option> + # # => <option value="Sweden" selected="selected">Sweden</option> # # options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]]) - # # <option value="$" class="bold">Dollar</option> - # # <option value="DKK" onclick="alert('HI');">Kroner</option> + # # => <option value="$" class="bold">Dollar</option> + # # => <option value="DKK" onclick="alert('HI');">Kroner</option> # # If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value # or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags. # - # Examples: # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: "Super Platinum") - # # <option value="Free">Free</option> - # # <option value="Basic">Basic</option> - # # <option value="Advanced">Advanced</option> - # # <option value="Super Platinum" disabled="disabled">Super Platinum</option> + # # => <option value="Free">Free</option> + # # => <option value="Basic">Basic</option> + # # => <option value="Advanced">Advanced</option> + # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option> # # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: ["Advanced", "Super Platinum"]) - # # <option value="Free">Free</option> - # # <option value="Basic">Basic</option> - # # <option value="Advanced" disabled="disabled">Advanced</option> - # # <option value="Super Platinum" disabled="disabled">Super Platinum</option> + # # => <option value="Free">Free</option> + # # => <option value="Basic">Basic</option> + # # => <option value="Advanced" disabled="disabled">Advanced</option> + # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option> # # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], selected: "Free", disabled: "Super Platinum") - # # <option value="Free" selected="selected">Free</option> - # # <option value="Basic">Basic</option> - # # <option value="Advanced">Advanced</option> - # # <option value="Super Platinum" disabled="disabled">Super Platinum</option> + # # => <option value="Free" selected="selected">Free</option> + # # => <option value="Basic">Basic</option> + # # => <option value="Advanced">Advanced</option> + # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option> # # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. def options_for_select(container, selected = nil) @@ -358,12 +354,12 @@ module ActionView # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning # the result of a call to the +value_method+ as the option value and the +text_method+ as the option text. - # Example: + # # options_from_collection_for_select(@people, 'id', 'name') - # This will output the same HTML as if you did this: - # <option value="#{person.id}">#{person.name}</option> + # # => <option value="#{person.id}">#{person.name}</option> # # This is more often than not used inside a #select_tag like this example: + # # select_tag 'person', options_from_collection_for_select(@people, 'id', 'name') # # If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+ @@ -412,10 +408,12 @@ module ActionView # to be specified. # # Example object structure for use with this method: + # # class Continent < ActiveRecord::Base # has_many :countries # # attribs: id, name # end + # # class Country < ActiveRecord::Base # belongs_to :continent # # attribs: id, name, continent_id @@ -465,7 +463,6 @@ module ActionView # prepends an option with a generic prompt - "Please select" - or the given prompt string. # * <tt>:divider</tt> - the divider for the options groups. # - # Sample usage (Array): # grouped_options = [ # ['North America', # [['United States','US'],'Canada']], @@ -474,7 +471,6 @@ module ActionView # ] # grouped_options_for_select(grouped_options) # - # Sample usage (Hash): # grouped_options = { # 'North America' => [['United States','US'], 'Canada'], # 'Europe' => ['Denmark','Germany','France'] @@ -492,7 +488,6 @@ module ActionView # <option value="France">France</option> # </optgroup> # - # Sample usage (divider): # grouped_options = [ # [['United States','US'], 'Canada'], # ['Denmark','Germany','France'] diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 1a99fc7091..cfdd7c77d8 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -18,7 +18,8 @@ module ActionView # 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: + # 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) @@ -43,12 +44,14 @@ module ActionView # </script> # # +html_options+ may be a hash of attributes for the <tt>\<script></tt> - # tag. Example: + # tag. + # # javascript_tag "alert('All is good')", defer: 'defer' # # => <script defer="defer">alert('All is good')</script> # # Instead of passing the content as an argument, you can also use a block # in which case, you pass your +html_options+ as the first parameter. + # # <%= javascript_tag defer: 'defer' do -%> # alert('All is good') # <% end -%> diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 82340171af..9e1be65b1a 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -28,8 +28,6 @@ module ActionView # Formats a +number+ into a US phone number (e.g., (555) # 123-9876). You can customize the format in the +options+ hash. # - # ==== Options - # # * <tt>:area_code</tt> - Adds parentheses around the area code. # * <tt>:delimiter</tt> - Specifies the delimiter to use # (defaults to "-"). @@ -40,21 +38,18 @@ module ActionView # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # - # ==== Examples - # - # number_to_phone(5551234) # => 555-1234 - # number_to_phone("5551234") # => 555-1234 - # number_to_phone(1235551234) # => 123-555-1234 - # number_to_phone(1235551234, area_code: true) # => (123) 555-1234 - # number_to_phone(1235551234, delimiter: " ") # => 123 555 1234 - # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555 - # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234 - # number_to_phone("123a456") # => 123a456 - # - # number_to_phone("1234a567", raise: true) # => InvalidNumberError - # - # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".") - # # => +1.123.555.1234 x 1343 + # number_to_phone(5551234) # => 555-1234 + # number_to_phone("5551234") # => 555-1234 + # number_to_phone(1235551234) # => 123-555-1234 + # number_to_phone(1235551234, area_code: true) # => (123) 555-1234 + # number_to_phone(1235551234, delimiter: " ") # => 123 555 1234 + # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555 + # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234 + # number_to_phone("123a456") # => 123a456 + # number_to_phone("1234a567", raise: true) # => InvalidNumberError + # + # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".") + # # => +1.123.555.1234 x 1343 def number_to_phone(number, options = {}) return unless number options = options.symbolize_keys @@ -66,8 +61,6 @@ module ActionView # Formats a +number+ into a currency string (e.g., $13.65). You # can customize the format in the +options+ hash. # - # ==== Options - # # * <tt>:locale</tt> - Sets the locale to be used for formatting # (defaults to current locale). # * <tt>:precision</tt> - Sets the level of precision (defaults @@ -89,22 +82,20 @@ module ActionView # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # - # ==== Examples - # - # number_to_currency(1234567890.50) # => $1,234,567,890.50 - # number_to_currency(1234567890.506) # => $1,234,567,890.51 - # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506 - # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 € - # number_to_currency("123a456") # => $123a456 + # number_to_currency(1234567890.50) # => $1,234,567,890.50 + # number_to_currency(1234567890.506) # => $1,234,567,890.51 + # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506 + # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 € + # number_to_currency("123a456") # => $123a456 # - # number_to_currency("123a456", raise: true) # => InvalidNumberError + # number_to_currency("123a456", raise: true) # => InvalidNumberError # - # number_to_currency(-1234567890.50, negative_format: "(%u%n)") - # # => ($1,234,567,890.50) - # number_to_currency(1234567890.50, unit: "£", separator: ",", delimiter: "") - # # => £1234567890,50 - # number_to_currency(1234567890.50, unit: "£", separator: ",", delimiter: "", format: "%n %u") - # # => 1234567890,50 £ + # number_to_currency(-1234567890.50, negative_format: "(%u%n)") + # # => ($1,234,567,890.50) + # number_to_currency(1234567890.50, unit: "£", separator: ",", delimiter: "") + # # => £1234567890,50 + # number_to_currency(1234567890.50, unit: "£", separator: ",", delimiter: "", format: "%n %u") + # # => 1234567890,50 £ def number_to_currency(number, options = {}) return unless number options = escape_unsafe_delimiters_and_separators(options.symbolize_keys) @@ -117,7 +108,6 @@ module ActionView # Formats a +number+ as a percentage string (e.g., 65%). You can # customize the format in the +options+ hash. # - # ==== Options # # * <tt>:locale</tt> - Sets the locale to be used for formatting # (defaults to current locale). @@ -138,18 +128,16 @@ module ActionView # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # - # ==== Examples + # number_to_percentage(100) # => 100.000% + # number_to_percentage("98") # => 98.000% + # number_to_percentage(100, precision: 0) # => 100% + # number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000% + # number_to_percentage(302.24398923423, precision: 5) # => 302.24399% + # number_to_percentage(1000, locale: :fr) # => 1 000,000% + # number_to_percentage("98a") # => 98a% + # number_to_percentage(100, format: "%n %") # => 100 % # - # number_to_percentage(100) # => 100.000% - # number_to_percentage("98") # => 98.000% - # number_to_percentage(100, precision: 0) # => 100% - # number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000% - # number_to_percentage(302.24398923423, precision: 5) # => 302.24399% - # number_to_percentage(1000, locale: :fr) # => 1 000,000% - # number_to_percentage("98a") # => 98a% - # number_to_percentage(100, format: "%n %") # => 100 % - # - # number_to_percentage("98a", raise: true) # => InvalidNumberError + # number_to_percentage("98a", raise: true) # => InvalidNumberError def number_to_percentage(number, options = {}) return unless number options = escape_unsafe_delimiters_and_separators(options.symbolize_keys) @@ -163,8 +151,6 @@ module ActionView # (e.g., 12,324). You can customize the format in the +options+ # hash. # - # ==== Options - # # * <tt>:locale</tt> - Sets the locale to be used for formatting # (defaults to current locale). # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults @@ -174,20 +160,18 @@ module ActionView # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # - # ==== Examples - # - # number_with_delimiter(12345678) # => 12,345,678 - # number_with_delimiter("123456") # => 123,456 - # number_with_delimiter(12345678.05) # => 12,345,678.05 - # number_with_delimiter(12345678, delimiter: ".") # => 12.345.678 - # number_with_delimiter(12345678, delimiter: ",") # => 12,345,678 - # number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05 - # number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05 - # number_with_delimiter("112a") # => 112a - # number_with_delimiter(98765432.98, delimiter: " ", separator: ",") - # # => 98 765 432,98 - # - # number_with_delimiter("112a", raise: true) # => raise InvalidNumberError + # number_with_delimiter(12345678) # => 12,345,678 + # number_with_delimiter("123456") # => 123,456 + # number_with_delimiter(12345678.05) # => 12,345,678.05 + # number_with_delimiter(12345678, delimiter: ".") # => 12.345.678 + # number_with_delimiter(12345678, delimiter: ",") # => 12,345,678 + # number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05 + # number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05 + # number_with_delimiter("112a") # => 112a + # number_with_delimiter(98765432.98, delimiter: " ", separator: ",") + # # => 98 765 432,98 + # + # number_with_delimiter("112a", raise: true) # => raise InvalidNumberError def number_with_delimiter(number, options = {}) options = escape_unsafe_delimiters_and_separators(options.symbolize_keys) @@ -201,8 +185,6 @@ module ActionView # +:significant+ is +false+, and 5 if +:significant+ is +true+). # You can customize the format in the +options+ hash. # - # ==== Options - # # * <tt>:locale</tt> - Sets the locale to be used for formatting # (defaults to current locale). # * <tt>:precision</tt> - Sets the precision of the number @@ -220,23 +202,21 @@ module ActionView # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # - # ==== Examples - # - # number_with_precision(111.2345) # => 111.235 - # number_with_precision(111.2345, precision: 2) # => 111.23 - # number_with_precision(13, precision: 5) # => 13.00000 - # number_with_precision(389.32314, precision: 0) # => 389 - # number_with_precision(111.2345, significant: true) # => 111 - # number_with_precision(111.2345, precision: 1, significant: true) # => 100 - # number_with_precision(13, precision: 5, significant: true) # => 13.000 - # number_with_precision(111.234, locale: :fr) # => 111,234 - # - # number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true) - # # => 13 - # - # number_with_precision(389.32314, precision: 4, significant: true) # => 389.3 - # number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.') - # # => 1.111,23 + # number_with_precision(111.2345) # => 111.235 + # number_with_precision(111.2345, precision: 2) # => 111.23 + # number_with_precision(13, precision: 5) # => 13.00000 + # number_with_precision(389.32314, precision: 0) # => 389 + # number_with_precision(111.2345, significant: true) # => 111 + # number_with_precision(111.2345, precision: 1, significant: true) # => 100 + # number_with_precision(13, precision: 5, significant: true) # => 13.000 + # number_with_precision(111.234, locale: :fr) # => 111,234 + # + # number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true) + # # => 13 + # + # number_with_precision(389.32314, precision: 4, significant: true) # => 389.3 + # number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.') + # # => 1.111,23 def number_with_precision(number, options = {}) options = escape_unsafe_delimiters_and_separators(options.symbolize_keys) @@ -245,7 +225,6 @@ module ActionView } end - # Formats the bytes in +number+ into a more understandable # representation (e.g., giving it 1500 yields 1.5 KB). This # method is useful for reporting file sizes to users. You can @@ -254,8 +233,6 @@ module ActionView # See <tt>number_to_human</tt> if you want to pretty-print a # generic number. # - # ==== Options - # # * <tt>:locale</tt> - Sets the locale to be used for formatting # (defaults to current locale). # * <tt>:precision</tt> - Sets the precision of the number @@ -275,24 +252,23 @@ module ActionView # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # - # ==== Examples - # - # number_to_human_size(123) # => 123 Bytes - # number_to_human_size(1234) # => 1.21 KB - # number_to_human_size(12345) # => 12.1 KB - # number_to_human_size(1234567) # => 1.18 MB - # number_to_human_size(1234567890) # => 1.15 GB - # number_to_human_size(1234567890123) # => 1.12 TB - # number_to_human_size(1234567, precision: 2) # => 1.2 MB - # number_to_human_size(483989, precision: 2) # => 470 KB - # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB + # number_to_human_size(123) # => 123 Bytes + # number_to_human_size(1234) # => 1.21 KB + # number_to_human_size(12345) # => 12.1 KB + # number_to_human_size(1234567) # => 1.18 MB + # number_to_human_size(1234567890) # => 1.15 GB + # number_to_human_size(1234567890123) # => 1.12 TB + # number_to_human_size(1234567, precision: 2) # => 1.2 MB + # number_to_human_size(483989, precision: 2) # => 470 KB + # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB # # Non-significant zeros after the fractional separator are # stripped out by default (set # <tt>:strip_insignificant_zeros</tt> to +false+ to change # that): - # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB" - # number_to_human_size(524288000, precision: 5) # => "500 MB" + # + # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB" + # number_to_human_size(524288000, precision: 5) # => "500 MB" def number_to_human_size(number, options = {}) options = escape_unsafe_delimiters_and_separators(options.symbolize_keys) @@ -348,29 +324,27 @@ module ActionView # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # - # ==== Examples - # - # number_to_human(123) # => "123" - # number_to_human(1234) # => "1.23 Thousand" - # number_to_human(12345) # => "12.3 Thousand" - # number_to_human(1234567) # => "1.23 Million" - # number_to_human(1234567890) # => "1.23 Billion" - # number_to_human(1234567890123) # => "1.23 Trillion" - # number_to_human(1234567890123456) # => "1.23 Quadrillion" - # number_to_human(1234567890123456789) # => "1230 Quadrillion" - # number_to_human(489939, precision: 2) # => "490 Thousand" - # number_to_human(489939, precision: 4) # => "489.9 Thousand" - # number_to_human(1234567, precision: 4, - # significant: false) # => "1.2346 Million" - # number_to_human(1234567, precision: 1, + # number_to_human(123) # => "123" + # number_to_human(1234) # => "1.23 Thousand" + # number_to_human(12345) # => "12.3 Thousand" + # number_to_human(1234567) # => "1.23 Million" + # number_to_human(1234567890) # => "1.23 Billion" + # number_to_human(1234567890123) # => "1.23 Trillion" + # number_to_human(1234567890123456) # => "1.23 Quadrillion" + # number_to_human(1234567890123456789) # => "1230 Quadrillion" + # number_to_human(489939, precision: 2) # => "490 Thousand" + # number_to_human(489939, precision: 4) # => "489.9 Thousand" + # number_to_human(1234567, precision: 4, + # significant: false) # => "1.2346 Million" + # number_to_human(1234567, precision: 1, # separator: ',', - # significant: false) # => "1,2 Million" + # significant: false) # => "1,2 Million" # # Non-significant zeros after the decimal separator are stripped # out by default (set <tt>:strip_insignificant_zeros</tt> to # +false+ to change that): - # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion" - # number_to_human(500000000, precision: 5) # => "500 Million" + # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion" + # number_to_human(500000000, precision: 5) # => "500 Million" # # ==== Custom Unit Quantifiers # @@ -392,12 +366,12 @@ module ActionView # # Then you could do: # - # number_to_human(543934, units: :distance) # => "544 kilometers" - # number_to_human(54393498, units: :distance) # => "54400 kilometers" - # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance" - # number_to_human(343, units: :distance, precision: 1) # => "300 meters" - # number_to_human(1, units: :distance) # => "1 meter" - # number_to_human(0.34, units: :distance) # => "34 centimeters" + # number_to_human(543934, units: :distance) # => "544 kilometers" + # number_to_human(54393498, units: :distance) # => "54400 kilometers" + # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance" + # number_to_human(343, units: :distance, precision: 1) # => "300 meters" + # number_to_human(1, units: :distance) # => "1 meter" + # number_to_human(0.34, units: :distance) # => "34 centimeters" # def number_to_human(number, options = {}) options = escape_unsafe_delimiters_and_separators(options.symbolize_keys) diff --git a/actionpack/lib/action_view/helpers/output_safety_helper.rb b/actionpack/lib/action_view/helpers/output_safety_helper.rb index 2e7e9dc50c..60a4478c26 100644 --- a/actionpack/lib/action_view/helpers/output_safety_helper.rb +++ b/actionpack/lib/action_view/helpers/output_safety_helper.rb @@ -11,7 +11,8 @@ module ActionView #:nodoc: # # For example: # - # <%=raw @user.name %> + # raw @user.name + # # => 'Jimmy <alert>Tables</alert>' def raw(stringish) stringish.to_s.html_safe end diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index e6f61d269c..e5cb843670 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -3,7 +3,7 @@ require 'action_view/vendor/html-scanner' module ActionView # = Action View Sanitize Helpers - module Helpers #:nodoc: + module Helpers # The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements. # These helper methods extend Action View making them callable within your template files. module SanitizeHelper diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb index 3875d88a9f..e80e0ed9b0 100644 --- a/actionpack/lib/action_view/railtie.rb +++ b/actionpack/lib/action_view/railtie.rb @@ -3,7 +3,7 @@ require "rails" module ActionView # = Action View Railtie - class Railtie < Rails::Railtie + class Railtie < Rails::Railtie # :nodoc: config.action_view = ActiveSupport::OrderedOptions.new config.action_view.embed_authenticity_token_in_remote_forms = false diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index f5fdf766ad..8fb9b6ff18 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -293,7 +293,7 @@ module ActionView object, as = @object, @variable if !block && (layout = @options[:layout]) - layout = find_template(layout, @template_keys) + layout = find_template(layout.to_s, @template_keys) end object ||= locals[as] diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index 731d8f9dab..afbbece90f 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -14,6 +14,17 @@ module ActionView src << "@output_buffer.safe_concat('" << escape_text(text) << "');" end + # Erubis toggles <%= and <%== behavior when escaping is enabled. + # We override to always treat <%== as escaped. + def add_expr(src, code, indicator) + case indicator + when '==' + add_expr_escaped(src, code) + else + super + end + end + BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/ def add_expr_literal(src, code) diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb index 5d1a703c55..1090af3060 100644 --- a/actionpack/test/abstract/callbacks_test.rb +++ b/actionpack/test/abstract/callbacks_test.rb @@ -28,9 +28,9 @@ module AbstractController end class Callback2 < ControllerWithCallbacks - before_filter :first - after_filter :second - around_filter :aroundz + before_action :first + after_action :second + around_action :aroundz def first @text = "Hello world" @@ -53,7 +53,7 @@ module AbstractController end class Callback2Overwrite < Callback2 - before_filter :first, :except => :index + before_action :first, except: :index end class TestCallbacks2 < ActiveSupport::TestCase @@ -61,22 +61,22 @@ module AbstractController @controller = Callback2.new end - test "before_filter works" do + test "before_action works" do @controller.process(:index) assert_equal "Hello world", @controller.response_body end - test "after_filter works" do + test "after_action works" do @controller.process(:index) assert_equal "Goodbye", @controller.instance_variable_get("@second") end - test "around_filter works" do + test "around_action works" do @controller.process(:index) assert_equal "FIRSTSECOND", @controller.instance_variable_get("@aroundz") end - test "before_filter with overwritten condition" do + test "before_action with overwritten condition" do @controller = Callback2Overwrite.new @controller.process(:index) assert_equal "", @controller.response_body @@ -84,11 +84,11 @@ module AbstractController end class Callback3 < ControllerWithCallbacks - before_filter do |c| + before_action do |c| c.instance_variable_set("@text", "Hello world") end - after_filter do |c| + after_action do |c| c.instance_variable_set("@second", "Goodbye") end @@ -102,20 +102,20 @@ module AbstractController @controller = Callback3.new end - test "before_filter works with procs" do + test "before_action works with procs" do @controller.process(:index) assert_equal "Hello world", @controller.response_body end - test "after_filter works with procs" do + test "after_action works with procs" do @controller.process(:index) assert_equal "Goodbye", @controller.instance_variable_get("@second") end end class CallbacksWithConditions < ControllerWithCallbacks - before_filter :list, :only => :index - before_filter :authenticate, :except => :index + before_action :list, :only => :index + before_action :authenticate, :except => :index def index self.response_body = @list.join(", ") @@ -141,25 +141,25 @@ module AbstractController @controller = CallbacksWithConditions.new end - test "when :only is specified, a before filter is triggered on that action" do + test "when :only is specified, a before action is triggered on that action" do @controller.process(:index) assert_equal "Hello, World", @controller.response_body end - test "when :only is specified, a before filter is not triggered on other actions" do + test "when :only is specified, a before action is not triggered on other actions" do @controller.process(:sekrit_data) assert_equal "true", @controller.response_body end - test "when :except is specified, an after filter is not triggered on that action" do + test "when :except is specified, an after action is not triggered on that action" do @controller.process(:index) assert !@controller.instance_variable_defined?("@authenticated") end end class CallbacksWithArrayConditions < ControllerWithCallbacks - before_filter :list, :only => [:index, :listy] - before_filter :authenticate, :except => [:index, :listy] + before_action :list, only: [:index, :listy] + before_action :authenticate, except: [:index, :listy] def index self.response_body = @list.join(", ") @@ -185,24 +185,24 @@ module AbstractController @controller = CallbacksWithArrayConditions.new end - test "when :only is specified with an array, a before filter is triggered on that action" do + test "when :only is specified with an array, a before action is triggered on that action" do @controller.process(:index) assert_equal "Hello, World", @controller.response_body end - test "when :only is specified with an array, a before filter is not triggered on other actions" do + test "when :only is specified with an array, a before action is not triggered on other actions" do @controller.process(:sekrit_data) assert_equal "true", @controller.response_body end - test "when :except is specified with an array, an after filter is not triggered on that action" do + test "when :except is specified with an array, an after action is not triggered on that action" do @controller.process(:index) assert !@controller.instance_variable_defined?("@authenticated") end end class ChangedConditions < Callback2 - before_filter :first, :only => :index + before_action :first, :only => :index def not_index @text ||= nil @@ -227,7 +227,7 @@ module AbstractController end class SetsResponseBody < ControllerWithCallbacks - before_filter :set_body + before_action :set_body def index self.response_body = "Fail" @@ -266,6 +266,50 @@ module AbstractController end end + class AliasedCallbacks < ControllerWithCallbacks + before_filter :first + after_filter :second + around_filter :aroundz + + def first + @text = "Hello world" + end + + def second + @second = "Goodbye" + end + + def aroundz + @aroundz = "FIRST" + yield + @aroundz << "SECOND" + end + def index + @text ||= nil + self.response_body = @text.to_s + end + end + + class TestAliasedCallbacks < ActiveSupport::TestCase + def setup + @controller = AliasedCallbacks.new + end + + test "before_filter works" do + @controller.process(:index) + assert_equal "Hello world", @controller.response_body + end + + test "after_filter works" do + @controller.process(:index) + assert_equal "Goodbye", @controller.instance_variable_get("@second") + end + + test "around_filter works" do + @controller.process(:index) + assert_equal "FIRSTSECOND", @controller.instance_variable_get("@aroundz") + end + end end end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 6414ba3994..9d4356f546 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -53,8 +53,8 @@ class FlashTest < ActionController::TestCase render :inline => "hello" end - # methods for test_sweep_after_halted_filter_chain - before_filter :halt_and_redir, :only => "filter_halting_action" + # methods for test_sweep_after_halted_action_chain + before_action :halt_and_redir, only: 'filter_halting_action' def std_action @flash_copy = {}.update(flash) @@ -159,7 +159,7 @@ class FlashTest < ActionController::TestCase assert_nil session["flash"] end - def test_sweep_after_halted_filter_chain + def test_sweep_after_halted_action_chain get :std_action assert_nil assigns["flash_copy"]["foo"] get :filter_halting_action diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 2dcfda02a7..90548d4294 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -2,9 +2,9 @@ require 'abstract_unit' class HttpBasicAuthenticationTest < ActionController::TestCase class DummyController < ActionController::Base - before_filter :authenticate, :only => :index - before_filter :authenticate_with_request, :only => :display - before_filter :authenticate_long_credentials, :only => :show + before_action :authenticate, only: :index + before_action :authenticate_with_request, only: :display + before_action :authenticate_long_credentials, only: :show http_basic_authenticate_with :name => "David", :password => "Goliath", :only => :search diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index c4a94264c3..537de7a2dd 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -4,8 +4,8 @@ require 'active_support/key_generator' class HttpDigestAuthenticationTest < ActionController::TestCase class DummyDigestController < ActionController::Base - before_filter :authenticate, :only => :index - before_filter :authenticate_with_request, :only => :display + before_action :authenticate, only: :index + before_action :authenticate_with_request, only: :display USERS = { 'lifo' => 'world', 'pretty' => 'please', 'dhh' => ::Digest::MD5::hexdigest(["dhh","SuperSecret","secret"].join(":"))} diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index ad4e743be8..8a409d6ed2 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -2,9 +2,9 @@ require 'abstract_unit' class HttpTokenAuthenticationTest < ActionController::TestCase class DummyController < ActionController::Base - before_filter :authenticate, :only => :index - before_filter :authenticate_with_request, :only => :display - before_filter :authenticate_long_credentials, :only => :show + before_action :authenticate, only: :index + before_action :authenticate_with_request, only: :display + before_action :authenticate_long_credentials, only: :show def index render :text => "Hello Secret" diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 9efb6ab95f..929545fc10 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -13,7 +13,7 @@ module Another head :status => 406 end - before_filter :redirector, :only => :never_executed + before_action :redirector, only: :never_executed def never_executed end @@ -26,6 +26,10 @@ module Another redirect_to "http://foo.bar/" end + def filterable_redirector + redirect_to "http://secret.foo.bar/" + end + def data_sender send_data "cool data", :filename => "file.txt" end @@ -42,6 +46,22 @@ module Another render :inline => "<%= cache('foo%bar'){ 'Contains % sign in key' } %>" end + def with_fragment_cache_and_if_true_condition + render :inline => "<%= cache('foo', :if => true) { 'bar' } %>" + end + + def with_fragment_cache_and_if_false_condition + render :inline => "<%= cache('foo', :if => false) { 'bar' } %>" + end + + def with_fragment_cache_and_unless_false_condition + render :inline => "<%= cache('foo', :unless => false) { 'bar' } %>" + end + + def with_fragment_cache_and_unless_true_condition + render :inline => "<%= cache('foo', :unless => true) { 'bar' } %>" + end + def with_exception raise Exception end @@ -152,6 +172,24 @@ class ACLogSubscriberTest < ActionController::TestCase assert_equal "Redirected to http://foo.bar/", logs[1] end + def test_filter_redirect_url_by_string + @request.env['action_dispatch.redirect_filter'] = ['secret'] + get :filterable_redirector + wait + + assert_equal 3, logs.size + assert_equal "Redirected to [FILTERED]", logs[1] + end + + def test_filter_redirect_url_by_regexp + @request.env['action_dispatch.redirect_filter'] = [/secret\.foo.+/] + get :filterable_redirector + wait + + assert_equal 3, logs.size + assert_equal "Redirected to [FILTERED]", logs[1] + end + def test_send_data get :data_sender wait @@ -181,6 +219,54 @@ class ACLogSubscriberTest < ActionController::TestCase @controller.config.perform_caching = true end + def test_with_fragment_cache_and_if_true + @controller.config.perform_caching = true + get :with_fragment_cache_and_if_true_condition + wait + + assert_equal 4, logs.size + assert_match(/Read fragment views\/foo/, logs[1]) + assert_match(/Write fragment views\/foo/, logs[2]) + ensure + @controller.config.perform_caching = true + end + + def test_with_fragment_cache_and_if_false + @controller.config.perform_caching = true + get :with_fragment_cache_and_if_false_condition + wait + + assert_equal 2, logs.size + assert_no_match(/Read fragment views\/foo/, logs[1]) + assert_no_match(/Write fragment views\/foo/, logs[2]) + ensure + @controller.config.perform_caching = true + end + + def test_with_fragment_cache_and_unless_true + @controller.config.perform_caching = true + get :with_fragment_cache_and_unless_true_condition + wait + + assert_equal 2, logs.size + assert_no_match(/Read fragment views\/foo/, logs[1]) + assert_no_match(/Write fragment views\/foo/, logs[2]) + ensure + @controller.config.perform_caching = true + end + + def test_with_fragment_cache_and_unless_false + @controller.config.perform_caching = true + get :with_fragment_cache_and_unless_false_condition + wait + + assert_equal 4, logs.size + assert_match(/Read fragment views\/foo/, logs[1]) + assert_match(/Write fragment views\/foo/, logs[2]) + ensure + @controller.config.perform_caching = true + end + def test_with_fragment_cache_and_percent_in_key @controller.config.perform_caching = true get :with_fragment_cache_and_percent_in_key diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index d183b0be17..ed013e2185 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -1139,7 +1139,7 @@ end # For testing layouts which are set automatically class PostController < AbstractPostController - around_filter :with_iphone + around_action :with_iphone def index respond_to(:html, :iphone, :js) diff --git a/actionpack/test/controller/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb index ed244513a5..964f22eb03 100644 --- a/actionpack/test/controller/new_base/base_test.rb +++ b/actionpack/test/controller/new_base/base_test.rb @@ -3,7 +3,7 @@ require 'abstract_unit' # Tests the controller dispatching happy path module Dispatching class SimpleController < ActionController::Base - before_filter :authenticate + before_action :authenticate def index render :text => "success" diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb index f41b14d5d6..177a1c088d 100644 --- a/actionpack/test/controller/new_base/render_context_test.rb +++ b/actionpack/test/controller/new_base/render_context_test.rb @@ -14,7 +14,7 @@ module RenderContext include ActionView::Context # 2) Call _prepare_context that will do the required initialization - before_filter :_prepare_context + before_action :_prepare_context def hello_world @value = "Hello" diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index d0be4f66d1..6b2ae2b2a9 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -9,7 +9,8 @@ module RenderTemplate "locals.html.erb" => "The secret is <%= secret %>", "xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend", "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>", - "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %>", + "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a html template", + "with_implicit_raw.text.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a text template", "test/with_json.html.erb" => "<%= render :template => 'test/with_json', :formats => [:json] %>", "test/with_json.json.erb" => "<%= render :template => 'test/final', :formats => [:json] %>", "test/final.json.erb" => "{ final: json }", @@ -113,7 +114,12 @@ module RenderTemplate get :with_implicit_raw - assert_body "Hello <strong>this is also raw</strong>" + assert_body "Hello <strong>this is also raw</strong> in a html template" + assert_status 200 + + get :with_implicit_raw, format: 'text' + + assert_body "Hello <strong>this is also raw</strong> in a text template" assert_status 200 end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 859ed1466b..7640bc12a2 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -37,7 +37,7 @@ end class TestController < ActionController::Base protect_from_forgery - before_filter :set_variable_for_layout + before_action :set_variable_for_layout class LabellingFormBuilder < ActionView::Helpers::FormBuilder end @@ -137,7 +137,7 @@ class TestController < ActionController::Base def conditional_hello_with_bangs render :action => 'hello_world' end - before_filter :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs + before_action :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs def handle_last_modified_and_etags fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ]) @@ -710,7 +710,7 @@ class TestController < ActionController::Base render :action => "calling_partial_with_layout", :layout => "layouts/partial_with_layout" end - before_filter :only => :render_with_filters do + before_action only: :render_with_filters do request.format = :xml end diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index 48e2d6491e..4898b0c57f 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -68,9 +68,9 @@ class RescueController < ActionController::Base render :text => 'io error' end - before_filter(:only => :before_filter_raises) { raise 'umm nice' } + before_action(only: :before_action_raises) { raise 'umm nice' } - def before_filter_raises + def before_action_raises end def raises diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 718d06ef38..888791b874 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -5,7 +5,7 @@ module ShowExceptions use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public") use ActionDispatch::DebugExceptions - before_filter :only => :another_boom do + before_action only: :another_boom do request.env["action_dispatch.show_detailed_exceptions"] = true end diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb index 40f6dc6f0f..c6e7a523b9 100644 --- a/actionpack/test/controller/view_paths_test.rb +++ b/actionpack/test/controller/view_paths_test.rb @@ -4,7 +4,7 @@ class ViewLoadPathsTest < ActionController::TestCase class TestController < ActionController::Base def self.controller_path() "test" end - before_filter :add_view_path, :only => :hello_world_at_request_time + before_action :add_view_path, only: :hello_world_at_request_time def hello_world() end def hello_world_at_request_time() render(:action => 'hello_world') end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 4f5d8fdb7c..cb5299e8d3 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -3065,6 +3065,35 @@ class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest end end +class TestGlobRoutingMapper < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } + + get "/*id" => redirect("/not_cars"), :constraints => {id: /dummy/} + get "/cars" => ok + end + end + + #include Routes.url_helpers + def app; Routes end + + def test_glob_constraint + get "/dummy" + assert_equal "301", @response.code + assert_equal "/not_cars", @response.header['Location'].match('/[^/]+$')[0] + end + + def test_glob_constraint_skip_route + get "/cars" + assert_equal "200", @response.code + end + def test_glob_constraint_skip_all + get "/missing" + assert_equal "404", @response.code + end +end + class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| app.draw do diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 4e6a676fc6..9fb26e32b1 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -437,6 +437,11 @@ module RenderTestCases @view.render(:partial => 'test/partial_with_layout_block_content', :layout => 'test/layout_for_partial', :locals => { :name => 'Foo!'}) end + def test_render_partial_with_layout_raises_descriptive_error + e = assert_raises(ActionView::MissingTemplate) { @view.render(partial: 'test/partial', layout: true) } + assert_match "Missing partial /true with", e.message + end + def test_render_with_nested_layout assert_equal %(<title>title</title>\n\n<div id="column">column</div>\n<div id="content">content</div>\n), @view.render(:file => "test/nested_layout", :layout => "layouts/yield") diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb index ed9d303158..8d32205fb8 100644 --- a/actionpack/test/template/template_test.rb +++ b/actionpack/test/template/template_test.rb @@ -82,8 +82,8 @@ class TestERBTemplate < ActiveSupport::TestCase end def test_text_template_does_not_html_escape - @template = new_template("<%= apostrophe %>", format: :text) - assert_equal "l'apostrophe", render + @template = new_template("<%= apostrophe %> <%== apostrophe %>", format: :text) + assert_equal "l'apostrophe l'apostrophe", render end def test_raw_template |