aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb10
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb2
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb224
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb27
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb125
8 files changed, 258 insertions, 135 deletions
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index aab2b9dc25..eba5e9377b 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -23,6 +23,7 @@ module ActionController
autoload :ImplicitRender
autoload :Instrumentation
autoload :MimeResponds
+ autoload :ParamsWrapper
autoload :RackDelegation
autoload :Redirecting
autoload :Renderers
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index ca0dccf575..c03c77cb4a 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -206,13 +206,17 @@ module ActionController
HttpAuthentication::Digest::ControllerMethods,
HttpAuthentication::Token::ControllerMethods,
+ # Before callbacks should also be executed the earliest as possible, so
+ # also include them at the bottom.
+ AbstractController::Callbacks,
+
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
Instrumentation,
- # Before callbacks should also be executed the earliest as possible, so
- # also include them at the bottom.
- AbstractController::Callbacks,
+ # Params wrapper should come before instrumentation so they are
+ # properly showed in logs
+ ParamsWrapper,
# The same with rescue, append it at the end to wrap as much as possible.
Rescue
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index dc3ea939e6..4e54c2ad88 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -14,7 +14,7 @@ module ActionController
attr_internal :view_runtime
- def process_action(action, *args)
+ def process_action(*args)
raw_payload = {
:controller => self.class.name,
:action => self.action_name,
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
new file mode 100644
index 0000000000..21bbe17dc3
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -0,0 +1,224 @@
+require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/hash/slice'
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/array/wrap'
+require 'action_dispatch/http/mime_types'
+
+module ActionController
+ # Wraps parameters hash into nested hash. This will allow client to submit
+ # POST request without having to specify a root element in it.
+ #
+ # By default this functionality won't be enabled. You can enable
+ # it globally by setting +ActionController::Base.wrap_parameters+:
+ #
+ # ActionController::Base.wrap_parameters = [:json]
+ #
+ # You could also turn it on per controller by setting the format array to
+ # non-empty array:
+ #
+ # class UsersController < ApplicationController
+ # wrap_parameters :format => [:json, :xml]
+ # end
+ #
+ # If you enable +ParamsWrapper+ for +:json+ format. Instead of having to
+ # send JSON parameters like this:
+ #
+ # {"user": {"name": "Konata"}}
+ #
+ # You can now just send a parameters like this:
+ #
+ # {"name": "Konata"}
+ #
+ # And it will be wrapped into a nested hash with the key name matching
+ # controller's name. For example, if you're posting to +UsersController+,
+ # your new +params+ hash will look like this:
+ #
+ # {"name" => "Konata", "user" => {"name" => "Konata"}}
+ #
+ # You can also specify the key in which the parameters should be wrapped to,
+ # and also the list of attributes it should wrap by using either +:only+ or
+ # +:except+ options like this:
+ #
+ # class UsersController < ApplicationController
+ # wrap_parameters :person, :only => [:username, :password]
+ # end
+ #
+ # If you're going to pass the parameters to an +ActiveModel+ object (such as
+ # +User.new(params[:user])+), you might consider passing the model class to
+ # the method instead. The +ParamsWrapper+ will actually try to determine the
+ # list of attribute names from the model and only wrap those attributes:
+ #
+ # class UsersController < ApplicationController
+ # wrap_parameters Person
+ # end
+ #
+ # You still could pass +:only+ and +:except+ to set the list of attributes
+ # you want to wrap.
+ #
+ # By default, if you don't specify the key in which the parameters would be
+ # wrapped to, +ParamsWrapper+ will actually try to determine if there's
+ # a model related to it or not. This controller, for example:
+ #
+ # class Admin::UsersController < ApplicationController
+ # end
+ #
+ # will try to check if +Admin::User+ or +User+ model exists, and use it to
+ # determine the wrapper key respectively. If both of the model doesn't exists,
+ # it will then fallback to use +user+ as the key.
+ module ParamsWrapper
+ extend ActiveSupport::Concern
+
+ EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
+
+ included do
+ class_attribute :_wrapper_options
+ self._wrapper_options = {:format => []}
+ end
+
+ module ClassMethods
+ # Sets the name of the wrapper key, or the model which +ParamsWrapper+
+ # would use to determine the attribute names from.
+ #
+ # ==== Examples
+ # wrap_parameters :format => :xml
+ # # enables the parmeter wrapper for XML format
+ #
+ # wrap_parameters :person
+ # # wraps parameters into +params[:person]+ hash
+ #
+ # wrap_parameters Person
+ # # wraps parameters by determine the wrapper key from Person class
+ # (+person+, in this case) and the list of attribute names
+ #
+ # wrap_parameters :only => [:username, :title]
+ # # wraps only +:username+ and +:title+ attributes from parameters.
+ #
+ # wrap_parameters false
+ # # disable parameters wrapping for this controller altogether.
+ #
+ # ==== Options
+ # * <tt>:format</tt> - The list of formats in which the parameters wrapper
+ # will be enabled.
+ # * <tt>:only</tt> - The list of attribute names which parameters wrapper
+ # will wrap into a nested hash.
+ # * <tt>:except</tt> - The list of attribute names which parameters wrapper
+ # will exclude from a nested hash.
+ def wrap_parameters(name_or_model_or_options, options = {})
+ model = nil
+
+ case name_or_model_or_options
+ when Hash
+ options = name_or_model_or_options
+ when false
+ options = options.merge(:format => [])
+ when Symbol, String
+ options = options.merge(:name => name_or_model_or_options)
+ else
+ model = name_or_model_or_options
+ end
+
+ _set_wrapper_defaults(_wrapper_options.slice(:format).merge(options), model)
+ end
+
+ # Sets the default wrapper key or model which will be used to determine
+ # wrapper key and attribute names. Will be called automatically when the
+ # module is inherited.
+ def inherited(klass)
+ if klass._wrapper_options[:format].present?
+ klass._set_wrapper_defaults(klass._wrapper_options)
+ end
+ super
+ end
+
+ protected
+
+ # Determine the wrapper model from the controller's name. By convention,
+ # this could be done by trying to find the defined model that has the
+ # same singularize name as the controller. For example, +UsersController+
+ # will try to find if the +User+ model exists.
+ def _default_wrap_model
+ model_name = self.name.sub(/Controller$/, '').singularize
+
+ begin
+ model_klass = model_name.constantize
+ rescue NameError => e
+ unscoped_model_name = model_name.split("::", 2).last
+ break if unscoped_model_name == model_name
+ model_name = unscoped_model_name
+ end until model_klass
+
+ model_klass
+ end
+
+ def _set_wrapper_defaults(options, model=nil)
+ options = options.dup
+
+ unless options[:only] || options[:except]
+ model ||= _default_wrap_model
+ if model.respond_to?(:column_names)
+ options[:only] = model.column_names
+ end
+ end
+
+ unless options[:name]
+ model ||= _default_wrap_model
+ options[:name] = model ? model.to_s.demodulize.underscore :
+ controller_name.singularize
+ end
+
+ options[:only] = Array.wrap(options[:only]).collect(&:to_s) if options[:only]
+ options[:except] = Array.wrap(options[:except]).collect(&:to_s) if options[:except]
+ options[:format] = Array.wrap(options[:format])
+
+ self._wrapper_options = options
+ end
+ end
+
+ # Performs parameters wrapping upon the request. Will be called automatically
+ # by the metal call stack.
+ def process_action(*args)
+ if _wrapper_enabled?
+ wrapped_hash = _wrap_parameters request.request_parameters
+ wrapped_filtered_hash = _wrap_parameters request.filtered_parameters
+
+ # This will make the wrapped hash accessible from controller and view
+ request.parameters.merge! wrapped_hash
+ request.request_parameters.merge! wrapped_hash
+
+ # This will make the wrapped hash displayed in the log file
+ request.filtered_parameters.merge! wrapped_filtered_hash
+ end
+ super
+ end
+
+ private
+
+ # Returns the wrapper key which will use to stored wrapped parameters.
+ def _wrapper_key
+ _wrapper_options[:name]
+ end
+
+ # Returns the list of enabled formats.
+ def _wrapper_formats
+ _wrapper_options[:format]
+ end
+
+ # Returns the list of parameters which will be selected for wrapped.
+ def _wrap_parameters(parameters)
+ value = if only = _wrapper_options[:only]
+ parameters.slice(*only)
+ else
+ except = _wrapper_options[:except] || []
+ parameters.except(*(except + EXCLUDE_PARAMETERS))
+ end
+
+ { _wrapper_key => value }
+ end
+
+ # Checks if we should perform parameters wrapping.
+ def _wrapper_enabled?
+ ref = request.content_mime_type.try(:ref)
+ _wrapper_formats.include?(ref) && !request.request_parameters[_wrapper_key]
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 1d27c3aa51..3892a12407 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -160,7 +160,7 @@ module ActionController #:nodoc:
# needs to inject contents in the HTML body.
#
# Also +Rack::Cache+ won't work with streaming as it does not support
- # streaming bodies yet. So, whenever streaming, Cache-Control is automatically
+ # streaming bodies yet. Whenever streaming Cache-Control is automatically
# set to "no-cache".
#
# == Errors
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 68ba1a81b5..980c658ab7 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -1,6 +1,13 @@
module ActionDispatch
module Http
module MimeNegotiation
+ extend ActiveSupport::Concern
+
+ included do
+ mattr_accessor :ignore_accept_header
+ self.ignore_accept_header = false
+ end
+
# The MIME type of the HTTP request, such as Mime::XML.
#
# For backward compatibility, the post \format is extracted from the
@@ -42,16 +49,14 @@ module ActionDispatch
formats.first
end
- BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
-
def formats
- accept = @env['HTTP_ACCEPT']
-
@env["action_dispatch.request.formats"] ||=
if parameters[:format]
Array(Mime[parameters[:format]])
- elsif xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS)
+ elsif use_accept_header && valid_accept_header
accepts
+ elsif xhr?
+ [Mime::JS]
else
[Mime::HTML]
end
@@ -87,6 +92,18 @@ module ActionDispatch
order.include?(Mime::ALL) ? formats.first : nil
end
+
+ protected
+
+ BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
+
+ def valid_accept_header
+ xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS)
+ end
+
+ def use_accept_header
+ !self.class.ignore_accept_header
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 0a3bd5fe40..f51cc3711b 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -9,10 +9,12 @@ module ActionDispatch
config.action_dispatch.show_exceptions = true
config.action_dispatch.best_standards_support = true
config.action_dispatch.tld_length = 1
+ config.action_dispatch.ignore_accept_header = false
config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => true}
initializer "action_dispatch.configure" do |app|
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
+ ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
end
end
end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 06e2b027da..ca09c77b5c 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -265,60 +265,6 @@ module ActionView
text.html_safe.safe_concat("</p>")
end
- # Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
- # will limit what should be linked. You can add HTML attributes to the links using
- # <tt>:html</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
- # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
- # e-mail address is yielded and the result is used as the link text.
- #
- # ==== Examples
- # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
- # # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
- # # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
- #
- # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
- # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
- # # or e-mail david@loudthinking.com"
- #
- # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
- # # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
- #
- # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
- # auto_link(post_body, :html => { :target => '_blank' }) do |text|
- # truncate(text, :length => 15)
- # end
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
- # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
- #
- #
- # You can still use <tt>auto_link</tt> with the old API that accepts the
- # +link+ as its optional second parameter and the +html_options+ hash
- # as its optional third parameter:
- # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
- # auto_link(post_body, :urls)
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\">http://www.myblog.com</a>.
- # Please e-mail me at me@email.com."
- #
- # auto_link(post_body, :all, :target => "_blank")
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
- # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
- def auto_link(text, *args, &block)#link = :all, html = {}, &block)
- return '' if text.blank?
-
- options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
- unless args.empty?
- options[:link] = args[0] || :all
- options[:html] = args[1] || {}
- end
- options.reverse_merge!(:link => :all, :html => {})
-
- case options[:link].to_sym
- when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], options, &block), options[:html], &block)
- when :email_addresses then auto_link_email_addresses(text, options[:html], &block)
- when :urls then auto_link_urls(text, options[:html], options, &block)
- end
- end
-
# Creates a Cycle object whose _to_s_ method cycles through elements of an
# array every time it is called. This can be used for example, to alternate
# classes for table rows. You can use named cycles to allow nesting in loops.
@@ -464,77 +410,6 @@ module ActionView
@_cycles = Hash.new unless defined?(@_cycles)
@_cycles[name] = cycle_object
end
-
- AUTO_LINK_RE = %r{
- (?: ([0-9A-Za-z+.:-]+:)// | www\. )
- [^\s<]+
- }x
-
- # regexps for determining context, used high-volume
- AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]
-
- AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/
-
- BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
-
- # Turns all urls into clickable links. If a block is given, each url
- # is yielded and the result is used as the link text.
- def auto_link_urls(text, html_options = {}, options = {})
- link_attributes = html_options.stringify_keys
- text.gsub(AUTO_LINK_RE) do
- scheme, href = $1, $&
- punctuation = []
-
- if auto_linked?($`, $')
- # do not change string; URL is already linked
- href
- else
- # don't include trailing punctuation character as part of the URL
- while href.sub!(/[^\w\/-]$/, '')
- punctuation.push $&
- if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
- href << punctuation.pop
- break
- end
- end
-
- link_text = block_given?? yield(href) : href
- href = 'http://' + href unless scheme
-
- unless options[:sanitize] == false
- link_text = sanitize(link_text)
- href = sanitize(href)
- end
- content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('')
- end
- end
- end
-
- # Turns all email addresses into clickable links. If a block is given,
- # each email is yielded and the result is used as the link text.
- def auto_link_email_addresses(text, html_options = {}, options = {})
- text.gsub(AUTO_EMAIL_RE) do
- text = $&
-
- if auto_linked?($`, $')
- text.html_safe
- else
- display_text = (block_given?) ? yield(text) : text
-
- unless options[:sanitize] == false
- text = sanitize(text)
- display_text = sanitize(display_text) unless text == display_text
- end
- mail_to text, display_text, html_options
- end
- end
- end
-
- # Detects already linked context or position in the middle of a tag
- def auto_linked?(left, right)
- (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
- (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
- end
end
end
end