diff options
Diffstat (limited to 'actionpack/lib')
21 files changed, 183 insertions, 83 deletions
diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb index 81fb514770..492329c401 100644 --- a/actionpack/lib/abstract_controller/collector.rb +++ b/actionpack/lib/abstract_controller/collector.rb @@ -4,7 +4,7 @@ module AbstractController module Collector def self.generate_method_for_mime(mime) sym = mime.is_a?(Symbol) ? mime : mime.to_sym - const = sym.to_s.upcase + const = sym.upcase class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{sym}(*args, &block) # def html(*args, &block) custom(Mime::#{const}, *args, &block) # custom(Mime::HTML, *args, &block) @@ -19,7 +19,7 @@ module AbstractController protected def method_missing(symbol, &block) - mime_constant = Mime.const_get(symbol.to_s.upcase) + mime_constant = Mime.const_get(symbol.upcase) if Mime::SET.include?(mime_constant) AbstractController::Collector.generate_method_for_mime(mime_constant) @@ -29,4 +29,4 @@ module AbstractController end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index 4e0672d590..d63d17f4c5 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -132,7 +132,11 @@ module AbstractController case arg when String, Symbol file_name = "#{arg.to_s.underscore}_helper" - require_dependency(file_name, "Missing helper file helpers/%s.rb") + begin + require_dependency(file_name) + rescue LoadError => e + raise MissingHelperError.new(e, file_name) + end file_name.camelize.constantize when Module arg @@ -142,6 +146,15 @@ module AbstractController end end + class MissingHelperError < LoadError + def initialize(error, path) + @error = error + @path = "helpers/#{path}.rb" + set_backtrace error.backtrace + super("Missing helper file helpers/%s.rb" % path) + end + end + private # Makes all the (instance) methods in the helper module available to templates # rendered through this controller. diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 112573a38d..9118806059 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -55,6 +55,9 @@ module ActionController #:nodoc: end end + include RackDelegation + include AbstractController::Callbacks + include ConfigMethods include Pages, Actions, Fragments include Sweeping if defined?(ActiveRecord) diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index dd4eddbe9a..73b8cd383c 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -110,7 +110,7 @@ module ActionController #:nodoc: gzip_level = options.fetch(:gzip, page_cache_compression) gzip_level = case gzip_level when Symbol - Zlib.const_get(gzip_level.to_s.upcase) + Zlib.const_get(gzip_level.upcase) when Fixnum gzip_level when false diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index ac12cbb625..77d799a38a 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -40,15 +40,23 @@ module ActionController def force_ssl(options = {}) host = options.delete(:host) before_filter(options) do - unless request.ssl? - redirect_options = {:protocol => 'https://', :status => :moved_permanently} - redirect_options.merge!(:host => host) if host - redirect_options.merge!(:params => request.query_parameters) - flash.keep if respond_to?(:flash) - redirect_to redirect_options - end + force_ssl_redirect(host) end end end + + # Redirect the existing request to use the HTTPS protocol. + # + # ==== Parameters + # * <tt>host</tt> - Redirect to a different host name + def force_ssl_redirect(host = nil) + unless request.ssl? + redirect_options = {:protocol => 'https://', :status => :moved_permanently} + redirect_options.merge!(:host => host) if host + redirect_options.merge!(:params => request.query_parameters) + flash.keep if respond_to?(:flash) + redirect_to redirect_options + end + end end end diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 57bb0e2a32..a0d1064094 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -401,16 +401,20 @@ module ActionController end end - # If token Authorization header is present, call the login procedure with - # the present token and options. + # If token Authorization header is present, call the login + # procedure with the present token and options. # - # controller - ActionController::Base instance for the current request. - # login_procedure - Proc to call if a token is present. The Proc should - # take 2 arguments: - # authenticate(controller) { |token, options| ... } + # [controller] + # ActionController::Base instance for the current request. # - # Returns the return value of `&login_procedure` if a token is found. - # Returns nil if no token is found. + # [login_procedure] + # Proc to call if a token is present. The Proc should take two arguments: + # + # authenticate(controller) { |token, options| ... } + # + # Returns the return value of <tt>login_procedure</tt> if a + # token is found. Returns <tt>nil</tt> if no token is found. + def authenticate(controller, &login_procedure) token, options = token_and_options(controller.request) unless token.blank? diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 95b0e99ed5..53534c0307 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -14,6 +14,20 @@ module ActionController #:nodoc: # authentication scheme there anyway). Also, GET requests are not protected as these # should be idempotent. # + # It's important to remember that XML or JSON requests are also affected and if + # you're building an API you'll need something like: + # + # class ApplicationController < ActionController::Base + # protect_from_forgery + # skip_before_filter :verify_authenticity_token, :if => :json_request? + # + # protected + # + # def json_request? + # request.format.json? + # end + # end + # # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method, # which checks the token and resets the session if it doesn't match what was expected. # A call to this method is generated for new \Rails applications by default. diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 0eaae80461..ee1913dbf9 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -99,9 +99,9 @@ module Mime end def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false) - Mime.const_set(symbol.to_s.upcase, Type.new(string, symbol, mime_type_synonyms)) + Mime.const_set(symbol.upcase, Type.new(string, symbol, mime_type_synonyms)) - SET << Mime.const_get(symbol.to_s.upcase) + SET << Mime.const_get(symbol.upcase) ([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup ([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = SET.last } @@ -194,7 +194,7 @@ module Mime # # Mime::Type.unregister(:mobile) def unregister(symbol) - symbol = symbol.to_s.upcase + symbol = symbol.upcase mime = Mime.const_get(symbol) Mime.instance_eval { remove_const(symbol) } diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index aa5ba3e8a5..8cea17c7a6 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -263,6 +263,27 @@ module ActionDispatch LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip end + protected + + # Remove nils from the params hash + def deep_munge(hash) + hash.each_value do |v| + case v + when Array + v.grep(Hash) { |x| deep_munge(x) } + v.compact! + when Hash + deep_munge(v) + end + end + + hash + end + + def parse_query(qs) + deep_munge(super) + end + private def check_method(name) diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb index 85b8d178bf..53bedaa40a 100644 --- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb @@ -1,5 +1,4 @@ module ActionDispatch - # A simple Rack application that renders exceptions in the given public path. class PublicExceptions attr_accessor :public_path @@ -8,23 +7,41 @@ module ActionDispatch end def call(env) - status = env["PATH_INFO"][1..-1] - locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale - path = "#{public_path}/#{status}.html" - - if locale_path && File.exist?(locale_path) - render(status, File.read(locale_path)) - elsif File.exist?(path) - render(status, File.read(path)) + exception = env["action_dispatch.exception"] + status = env["PATH_INFO"][1..-1] + request = ActionDispatch::Request.new(env) + content_type = request.formats.first + body = { :status => status, :error => exception.message } + + render(status, content_type, body) + end + + private + + def render(status, content_type, body) + format = content_type && "to_#{content_type.to_sym}" + if format && body.respond_to?(format) + render_format(status, content_type, body.public_send(format)) else - [404, { "X-Cascade" => "pass" }, []] + render_html(status) end end - private + def render_format(status, content_type, body) + [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}", + 'Content-Length' => body.bytesize.to_s}, [body]] + end + + def render_html(status) + found = false + path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale + path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path)) - def render(status, body) - [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]] + if found || File.exist?(path) + render_format(status, 'text/html', File.read(path)) + else + [404, { "X-Cascade" => "pass" }, []] + end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 38a0270151..29090882a5 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 require 'active_support/core_ext/object/to_param' require 'active_support/core_ext/regexp' @@ -218,6 +219,12 @@ module ActionDispatch # # match "/stories" => redirect("/posts") # + # == Unicode character routes + # + # You can specify unicode character routes in your router: + # + # match "こんにちは" => "welcome#index" + # # == Routing to Rack Applications # # Instead of a String, like <tt>posts#index</tt>, which corresponds to the diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index e43e897783..94242ad962 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1327,7 +1327,7 @@ module ActionDispatch msg += @draw_paths.map { |_path| " * #{_path}" }.join("\n") raise ArgumentError, msg end - + route_path = "#{path}/#{name}.rb" instance_eval(File.read(route_path), route_path.to_s) end @@ -1387,7 +1387,7 @@ module ActionDispatch options[:as] = name_for_action(options[:as], action) end - mapping = Mapping.new(@set, @scope, path, options) + mapping = Mapping.new(@set, @scope, URI.parser.escape(path), options) app, conditions, requirements, defaults, as, anchor = mapping.to_route @set.add_route(app, conditions, requirements, defaults, as, anchor) end diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 8fde667108..86ce7a83b9 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -95,7 +95,7 @@ module ActionDispatch end record = extract_record(record_or_hash_or_array) - record = record.to_model if record.respond_to?(:to_model) + record = convert_to_model(record) args = Array === record_or_hash_or_array ? record_or_hash_or_array.dup : @@ -122,6 +122,8 @@ module ActionDispatch args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options end + args.collect! { |a| convert_to_model(a) } + (proxy || self).send(named_route, *args) end @@ -152,6 +154,10 @@ module ActionDispatch options[:action] ? "#{options[:action]}_" : '' end + def convert_to_model(object) + object.respond_to?(:to_model) ? object.to_model : object + end + def routing_type(options) options[:routing_type] || :url end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 7872f4007e..64b1d58ae9 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -252,15 +252,11 @@ module ActionDispatch self.draw_paths = [] self.request_class = request_class - @valid_conditions = {} - + @valid_conditions = { :controller => true, :action => true } request_class.public_instance_methods.each { |m| - @valid_conditions[m.to_sym] = true + @valid_conditions[m] = true } - @valid_conditions[:controller] = true - @valid_conditions[:action] = true - - self.valid_conditions.delete(:id) + @valid_conditions.delete(:id) @append = [] @prepend = [] diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 3fdc6688c2..50ca28395b 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -193,8 +193,11 @@ module ActionDispatch # If the app is a Rails app, make url_helpers available on the session # This makes app.url_for and app.foo_path available in the console - if app.respond_to?(:routes) && app.routes.respond_to?(:url_helpers) - singleton_class.class_eval { include app.routes.url_helpers } + if app.respond_to?(:routes) + singleton_class.class_eval do + include app.routes.url_helpers if app.routes.respond_to?(:url_helpers) + include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers) + end end reset! diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 3823f87027..03dfa110e3 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -36,7 +36,7 @@ module ActionView autoload :LookupContext autoload :PathSet autoload :Template - autoload :TestCase + autoload_under "renderer" do autoload :Renderer @@ -73,6 +73,8 @@ module ActionView end end + autoload :TestCase + ENCODING_FLAG = '#.*coding[:=]\s*(\S+)[ \t]*' end diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb index 4ce41d51f1..81880d17ea 100644 --- a/actionpack/lib/action_view/asset_paths.rb +++ b/actionpack/lib/action_view/asset_paths.rb @@ -35,7 +35,13 @@ module ActionView # Return the filesystem path for the source def compute_source_path(source, dir, ext) source = rewrite_extension(source, dir, ext) if ext - File.join(config.assets_dir, dir, source) + + sources = [] + sources << config.assets_dir + sources << dir unless source[0] == ?/ + sources << source + + File.join(sources) end def is_uri?(path) diff --git a/actionpack/lib/action_view/helpers/controller_helper.rb b/actionpack/lib/action_view/helpers/controller_helper.rb index 1a583e62ae..74ef25f7c1 100644 --- a/actionpack/lib/action_view/helpers/controller_helper.rb +++ b/actionpack/lib/action_view/helpers/controller_helper.rb @@ -10,14 +10,16 @@ module ActionView delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers, :flash, :action_name, :controller_name, :controller_path, :to => :controller - delegate :logger, :to => :controller, :allow_nil => true - def assign_controller(controller) if @_controller = controller @_request = controller.request if controller.respond_to?(:request) @_config = controller.config.inheritable_copy if controller.respond_to?(:config) end end + + def logger + controller.logger if controller.respond_to?(:logger) + end end end end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index ac150882b1..7dd35f7357 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1311,10 +1311,21 @@ module ActionView # post: # create: "Add %{model}" # - def button(value=nil, options={}) + # ==== Examples + # button("Create a post") + # # => <button name='button' type='submit'>Create post</button> + # + # button do + # content_tag(:strong, 'Ask me!') + # end + # # => <button name='button' type='submit'> + # # <strong>Ask me!</strong> + # # </button> + # + def button(value = nil, options = {}, &block) value, options = nil, value if value.is_a?(Hash) value ||= submit_default_value - @template.button_tag(value, options) + @template.button_tag(value, options, &block) end def emitted_hidden_id? diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index a4b10bc68a..7f5b3c8a0f 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -233,25 +233,15 @@ module ActionView # # link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?") # # => <a href='http://www.example.com' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Destroy</a> - def link_to(*args, &block) - if block_given? - options = args.first || {} - html_options = args.second - link_to(capture(&block), options, html_options) - else - name = args[0] - options = args[1] || {} - html_options = args[2] - - html_options = convert_options_to_data_attributes(options, html_options) - url = url_for(options) + def link_to(name = nil, options = nil, html_options = nil, &block) + html_options, options = options, name if block_given? + options ||= {} + url = url_for(options) - href = html_options['href'] - tag_options = tag_options(html_options) + html_options = convert_options_to_data_attributes(options, html_options) + html_options['href'] ||= url - href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href - "<a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a>".html_safe - end + content_tag(:a, name || url, html_options, &block) end # Generates a form containing a single button that submits to the URL created @@ -341,15 +331,10 @@ module ActionView # # </div> # # </form>" # # - def button_to(*args, &block) - if block_given? - options = args[0] || {} - html_options = args[1] || {} - else - name = args[0] - options = args[1] || {} - html_options = args[2] || {} - end + def button_to(name = nil, options = nil, html_options = nil, &block) + html_options, options = options, name if block_given? + options ||= {} + html_options ||= {} html_options = html_options.stringify_keys convert_boolean_attributes!(html_options, %w(disabled)) @@ -374,7 +359,8 @@ module ActionView button = if block_given? content_tag('button', html_options, &block) else - tag('input', html_options.merge('value' => name || url)) + html_options['value'] = name || url + tag('input', html_options) end inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag) diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index fece499c94..53bde48e4d 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -229,7 +229,8 @@ module ActionView def method_missing(selector, *args) if @controller.respond_to?(:_routes) && - @controller._routes.named_routes.helpers.include?(selector) + ( @controller._routes.named_routes.helpers.include?(selector) || + @controller._routes.mounted_helpers.method_defined?(selector) ) @controller.__send__(selector, *args) else super |