aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb33
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb79
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb39
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb110
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb62
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb4
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb32
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb2
-rw-r--r--actionpack/lib/action_view/template/handlers.rb8
-rw-r--r--actionpack/lib/action_view/template/resolver.rb2
10 files changed, 250 insertions, 121 deletions
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index c85aa64572..5b87a80c1b 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -115,15 +115,20 @@ module Mime
end
else
# keep track of creation order to keep the subsequent sort stable
- list = []
- accept_header.split(/,/).each_with_index do |header, index|
+ list, index = [], 0
+ accept_header.split(/,/).each do |header|
params, q = header.split(/;\s*q=/)
- if params
+ if params.present?
params.strip!
+
if params =~ TRAILING_STAR_REGEXP
- parse_data_with_trailing_star($1).each { |m| list << AcceptItem.new(index, m.to_s, q) }
+ parse_data_with_trailing_star($1).each do |m|
+ list << AcceptItem.new(index, m.to_s, q)
+ index += 1
+ end
else
- list << AcceptItem.new(index, params, q) unless params.empty?
+ list << AcceptItem.new(index, params, q)
+ index += 1
end
end
end
@@ -178,20 +183,22 @@ module Mime
# input: 'application'
# returned value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM]
def parse_data_with_trailing_star(input)
- keys = Mime::LOOKUP.keys.select{|k| k.include?(input)}
- Mime::LOOKUP.values_at(*keys).uniq
+ Mime::SET.select { |m| m =~ input }
end
# This method is opposite of register method.
#
# Usage:
#
- # Mime::Type.unregister("text/x-mobile", :mobile)
- def unregister(string, symbol)
- EXTENSION_LOOKUP.delete(symbol.to_s)
- LOOKUP.delete(string)
- symbol = symbol.to_s.upcase.intern
- Mime.module_eval { remove_const(symbol) if const_defined?(symbol) }
+ # Mime::Type.unregister(:mobile)
+ def unregister(symbol)
+ symbol = symbol.to_s.upcase
+ mime = Mime.const_get(symbol)
+ Mime.instance_eval { remove_const(symbol) }
+
+ SET.delete_if { |v| v.eql?(mime) }
+ LOOKUP.delete_if { |k,v| v.eql?(mime) }
+ EXTENSION_LOOKUP.delete_if { |k,v| v.eql?(mime) }
end
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 1e7054f381..796cd8c09b 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -4,26 +4,74 @@ module ActionDispatch
mattr_accessor :tld_length
self.tld_length = 1
- def self.extract_domain(host, tld_length = @@tld_length)
- return nil unless named_host?(host)
+ class << self
+ def extract_domain(host, tld_length = @@tld_length)
+ return nil unless named_host?(host)
+ host.split('.').last(1 + tld_length).join('.')
+ end
- host.split('.').last(1 + tld_length).join('.')
- end
+ def extract_subdomains(host, tld_length = @@tld_length)
+ return [] unless named_host?(host)
+ parts = host.split('.')
+ parts[0..-(tld_length+2)]
+ end
- def self.extract_subdomains(host, tld_length = @@tld_length)
- return [] unless named_host?(host)
- parts = host.split('.')
- parts[0..-(tld_length+2)]
- end
+ def extract_subdomain(host, tld_length = @@tld_length)
+ extract_subdomains(host, tld_length).join('.')
+ end
- def self.extract_subdomain(host, tld_length = @@tld_length)
- extract_subdomains(host, tld_length).join('.')
- end
+ def url_for(options = {})
+ unless options[:host].present? || options[:only_path].present?
+ raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
+ end
- def self.named_host?(host)
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
- end
+ rewritten_url = ""
+
+ unless options[:only_path]
+ rewritten_url << (options[:protocol] || "http")
+ rewritten_url << "://" unless rewritten_url.match("://")
+ rewritten_url << rewrite_authentication(options)
+ rewritten_url << host_or_subdomain_and_domain(options)
+ rewritten_url << ":#{options.delete(:port)}" if options[:port]
+ end
+
+ path = options.delete(:path) || ''
+
+ params = options[:params] || {}
+ params.reject! {|k,v| !v }
+
+ rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
+ rewritten_url << "?#{params.to_query}" unless params.empty?
+ rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
+ rewritten_url
+ end
+
+ private
+
+ def named_host?(host)
+ !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
+ end
+ def rewrite_authentication(options)
+ if options[:user] && options[:password]
+ "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
+ else
+ ""
+ end
+ end
+
+ def host_or_subdomain_and_domain(options)
+ return options[:host] unless options[:subdomain] || options[:domain]
+
+ tld_length = options[:tld_length] || @@tld_length
+
+ host = ""
+ host << (options[:subdomain] || extract_subdomain(options[:host], tld_length))
+ host << "."
+ host << (options[:domain] || extract_domain(options[:host], tld_length))
+ host
+ end
+ end
# Returns the complete URL used for this request.
def url
@@ -116,7 +164,6 @@ module ActionDispatch
def subdomain(tld_length = @@tld_length)
subdomains(tld_length)
end
-
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 01826fcede..5a38158e9f 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -2,6 +2,7 @@ require 'erb'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/object/blank'
require 'active_support/inflector'
+require 'action_dispatch/routing/redirection'
module ActionDispatch
module Routing
@@ -383,39 +384,6 @@ module ActionDispatch
map_method(:delete, *args, &block)
end
- # Redirect any path to another path:
- #
- # match "/stories" => redirect("/posts")
- def redirect(*args)
- options = args.last.is_a?(Hash) ? args.pop : {}
-
- path = args.shift || Proc.new
- path_proc = path.is_a?(Proc) ? path : proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
- status = options[:status] || 301
-
- lambda do |env|
- req = Request.new(env)
-
- params = [req.symbolized_path_parameters]
- params << req if path_proc.arity > 1
-
- uri = URI.parse(path_proc.call(*params))
- uri.scheme ||= req.scheme
- uri.host ||= req.host
- uri.port ||= req.port unless req.standard_port?
-
- body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
-
- headers = {
- 'Location' => uri.to_s,
- 'Content-Type' => 'text/html',
- 'Content-Length' => body.length.to_s
- }
-
- [ status, headers, [body] ]
- end
- end
-
private
def map_method(method, *args, &block)
options = args.extract_options!
@@ -636,7 +604,7 @@ module ActionDispatch
:shallow_path => path, :shallow_prefix => path }.merge!(options)
scope(options) { yield }
end
-
+
# === Parameter Restriction
# Allows you to constrain the nested routes based on a set of rules.
# For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
@@ -647,7 +615,7 @@ module ActionDispatch
#
# Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
# The +id+ parameter must match the constraint passed in for this example.
- #
+ #
# You may use this to also resrict other parameters:
#
# resources :posts do
@@ -1369,6 +1337,7 @@ module ActionDispatch
include Base
include HttpHelpers
+ include Redirection
include Scoping
include Resources
include Shorthand
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
new file mode 100644
index 0000000000..804991ad5f
--- /dev/null
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -0,0 +1,110 @@
+require 'action_dispatch/http/request'
+
+module ActionDispatch
+ module Routing
+ module Redirection
+
+ # Redirect any path to another path:
+ #
+ # match "/stories" => redirect("/posts")
+ #
+ # You can also use interpolation in the supplied redirect argument:
+ #
+ # match 'docs/:article', :to => redirect('/wiki/%{article}')
+ #
+ # Alternatively you can use one of the other syntaxes:
+ #
+ # The block version of redirect allows for the easy encapsulation of any logic associated with
+ # the redirect in question. Either the params and request are supplied as arguments, or just
+ # params, depending of how many arguments your block accepts. A string is required as a
+ # return value.
+ #
+ # match 'jokes/:number', :to => redirect do |params, request|
+ # path = (params[:number].to_i.even? ? "/wheres-the-beef" : "/i-love-lamp")
+ # "http://#{request.host_with_port}/#{path}"
+ # end
+ #
+ # The options version of redirect allows you to supply only the parts of the url which need
+ # to change, it also supports interpolation of the path similar to the first example.
+ #
+ # match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
+ # match 'stores/:name(*all)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{all}')
+ #
+ # Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
+ # common redirect routes. The call method must accept two arguments, params and request, and return
+ # a string.
+ #
+ # match 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
+ #
+ def redirect(*args, &block)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ status = options.delete(:status) || 301
+
+ path = args.shift
+
+ path_proc = if path.is_a?(String)
+ proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
+ elsif options.any?
+ options_proc(options)
+ elsif path.respond_to?(:call)
+ proc { |params, request| path.call(params, request) }
+ elsif block
+ block
+ else
+ raise ArgumentError, "redirection argument not supported"
+ end
+
+ redirection_proc(status, path_proc)
+ end
+
+ private
+
+ def options_proc(options)
+ proc do |params, request|
+ path = if options[:path].nil?
+ request.path
+ elsif params.empty? || !options[:path].match(/%\{\w*\}/)
+ options.delete(:path)
+ else
+ (options.delete(:path) % params)
+ end
+
+ default_options = {
+ :protocol => request.protocol,
+ :host => request.host,
+ :port => request.optional_port,
+ :path => path,
+ :params => request.query_parameters
+ }
+
+ ActionDispatch::Http::URL.url_for(options.reverse_merge(default_options))
+ end
+ end
+
+ def redirection_proc(status, path_proc)
+ lambda do |env|
+ req = Request.new(env)
+
+ params = [req.symbolized_path_parameters]
+ params << req if path_proc.arity > 1
+
+ uri = URI.parse(path_proc.call(*params))
+ uri.scheme ||= req.scheme
+ uri.host ||= req.host
+ uri.port ||= req.port unless req.standard_port?
+
+ body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
+
+ headers = {
+ 'Location' => uri.to_s,
+ 'Content-Type' => 'text/html',
+ 'Content-Length' => body.length.to_s
+ }
+
+ [ status, headers, [body] ]
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index ebced9cabe..03bfe178e5 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -442,12 +442,9 @@ module ActionDispatch
raise_routing_error unless path
- params.reject! {|k,v| !v }
-
return [path, params.keys] if @extras
- path << "?#{params.to_query}" unless params.empty?
- path
+ [path, params]
rescue Rack::Mount::RoutingError
raise_routing_error
end
@@ -486,7 +483,7 @@ module ActionDispatch
end
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
- :trailing_slash, :script_name, :anchor, :params, :only_path ]
+ :trailing_slash, :anchor, :params, :only_path, :script_name]
def _generate_prefix(options = {})
nil
@@ -498,29 +495,24 @@ module ActionDispatch
handle_positional_args(options)
- rewritten_url = ""
-
- path_segments = options.delete(:_path_segments)
- unless options[:only_path]
- rewritten_url << (options[:protocol] || "http")
- rewritten_url << "://" unless rewritten_url.match("://")
- rewritten_url << rewrite_authentication(options)
- rewritten_url << host_from_options(options)
- rewritten_url << ":#{options.delete(:port)}" if options[:port]
- end
+ user, password = extract_authentication(options)
+ path_segments = options.delete(:_path_segments)
+ script_name = options.delete(:script_name)
- script_name = options.delete(:script_name)
path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
path_options = options.except(*RESERVED_OPTIONS)
path_options = yield(path_options) if block_given?
- path << generate(path_options, path_segments || {})
- # ROUTES TODO: This can be called directly, so script_name should probably be set in the routes
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
- rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
+ path_addition, params = generate(path_options, path_segments || {})
+ path << path_addition
- rewritten_url
+ ActionDispatch::Http::URL.url_for(options.merge({
+ :path => path,
+ :params => params,
+ :user => user,
+ :password => password
+ }))
end
def call(env)
@@ -561,23 +553,12 @@ module ActionDispatch
private
- def host_from_options(options)
- computed_host = subdomain_and_domain(options) || options[:host]
- unless computed_host
- raise ArgumentError, "Missing host to link to! Please provide :host parameter or set default_url_options[:host]"
+ def extract_authentication(options)
+ if options[:user] && options[:password]
+ [options.delete(:user), options.delete(:password)]
+ else
+ nil
end
- computed_host
- end
-
- def subdomain_and_domain(options)
- return nil unless options[:subdomain] || options[:domain]
- tld_length = options[:tld_length] || ActionDispatch::Http::URL.tld_length
-
- host = ""
- host << (options[:subdomain] || ActionDispatch::Http::URL.extract_subdomain(options[:host], tld_length))
- host << "."
- host << (options[:domain] || ActionDispatch::Http::URL.extract_domain(options[:host], tld_length))
- host
end
def handle_positional_args(options)
@@ -590,13 +571,6 @@ module ActionDispatch
options.merge!(Hash[args.zip(keys).map { |v, k| [k, v] }])
end
- def rewrite_authentication(options)
- if options[:user] && options[:password]
- "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
- else
- ""
- end
- end
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 8fe74c3c80..5c6416a19e 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -235,9 +235,7 @@ module ActionDispatch
# Set the host name to use in the next request.
#
# session.host! "www.example.com"
- def host!(name)
- @host = name
- end
+ alias :host! :host=
private
def _mock_session
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 8574ca6595..e7ec1df2c8 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -1,13 +1,33 @@
require 'action_view/helpers/tag_helper'
+require 'i18n/exceptions'
+
+module I18n
+ class ExceptionHandler
+ include Module.new {
+ def call(exception, locale, key, options)
+ exception.is_a?(MissingTranslationData) ? super.html_safe : super
+ end
+ }
+ end
+end
module ActionView
# = Action View Translation Helpers
module Helpers
module TranslationHelper
# Delegates to I18n#translate but also performs three additional functions.
- # First, it'll catch MissingTranslationData exceptions and turn them into
- # inline spans that contains the missing key, such that you can see in a
- # view what is missing where.
+ #
+ # First, it'll pass the :rescue_format => :html option to I18n so that any caught
+ # MissingTranslationData exceptions will be turned into inline spans that
+ #
+ # * have a "translation-missing" class set,
+ # * contain the missing key as a title attribute and
+ # * a titleized version of the last key segment as a text.
+ #
+ # E.g. the value returned for a missing translation key :"blog.post.title" will be
+ # <span class="translation_missing" title="translation missing: blog.post.title">Title</span>.
+ # This way your views will display rather reasonableful strings but it will still
+ # be easy to spot missing translations.
#
# Second, it'll scope the key by the current partial if the key starts
# with a period. So if you call <tt>translate(".foo")</tt> from the
@@ -24,15 +44,13 @@ module ActionView
# naming convention helps to identify translations that include HTML tags so that
# you know what kind of output to expect when you call translate in a template.
def translate(key, options = {})
- translation = I18n.translate(scope_key_by_partial(key), options.merge!(:raise => true))
+ options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
+ translation = I18n.translate(scope_key_by_partial(key), options)
if html_safe_translation_key?(key) && translation.respond_to?(:html_safe)
translation.html_safe
else
translation
end
- rescue I18n::MissingTranslationData => e
- keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
- content_tag('span', keys.join(', '), :class => 'translation_missing')
end
alias :t :translate
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index 6912acee31..ece3f621b6 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -45,7 +45,7 @@ module ActionView
elsif options.key?(:file)
with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
elsif options.key?(:inline)
- handler = Template.handler_class_for_extension(options[:type] || "erb")
+ handler = Template.handler_for_extension(options[:type] || "erb")
Template.new(options[:inline], "inline template", handler, :locals => keys)
elsif options.key?(:template)
options[:template].respond_to?(:render) ?
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
index 60347e2a95..4438199497 100644
--- a/actionpack/lib/action_view/template/handlers.rb
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -44,7 +44,13 @@ module ActionView #:nodoc:
end
def handler_class_for_extension(extension)
- (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers
+ ActiveSupport::Deprecation.warn "handler_class_for_extension is deprecated. " <<
+ "Please use handler_for_extension instead", caller
+ handler_for_extension(extension)
+ end
+
+ def handler_for_extension(extension)
+ registered_template_handler(extension) || @@default_template_handlers
end
end
end
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 7707dbcf98..9f15661669 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -129,7 +129,7 @@ module ActionView
def extract_handler_and_format(path, default_formats)
pieces = File.basename(path).split(".")
pieces.shift
- handler = Template.handler_class_for_extension(pieces.pop)
+ handler = Template.handler_for_extension(pieces.pop)
format = pieces.last && Mime[pieces.last]
[handler, format]
end