aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb2
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb4
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb36
-rw-r--r--actionpack/lib/action_controller/base.rb2
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb58
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb2
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb6
-rw-r--r--actionpack/lib/action_controller/metal.rb61
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb4
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb12
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb19
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb6
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb7
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb21
-rw-r--r--actionpack/lib/action_dispatch/middleware/reloader.rb31
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb8
-rw-r--r--actionpack/lib/action_dispatch/routing.rb12
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb325
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb6
-rw-r--r--actionpack/lib/action_view/base.rb6
-rw-r--r--actionpack/lib/action_view/helpers.rb4
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb22
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb23
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb7
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb22
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb47
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb75
-rw-r--r--actionpack/lib/action_view/helpers/output_safety_helper.rb38
-rw-r--r--actionpack/lib/action_view/helpers/raw_output_helper.rb18
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb27
-rw-r--r--actionpack/lib/action_view/lookup_context.rb40
-rw-r--r--actionpack/lib/action_view/partials.rb22
-rw-r--r--actionpack/lib/action_view/path_set.rb12
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb10
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb8
-rw-r--r--actionpack/lib/action_view/template.rb4
-rw-r--r--actionpack/lib/action_view/template/error.rb13
-rw-r--r--actionpack/lib/action_view/template/resolver.rb23
-rw-r--r--actionpack/lib/action_view/test_case.rb5
-rw-r--r--actionpack/lib/action_view/testing/resolvers.rb6
54 files changed, 784 insertions, 364 deletions
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index f169ab7c3a..95992c2698 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -13,7 +13,7 @@ module AbstractController
# Override AbstractController::Base's process_action to run the
# process_action callbacks around the normal behavior.
- def process_action(method_name)
+ def process_action(method_name, *args)
run_callbacks(:process_action, method_name) do
super
end
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 606f7eedec..4ee54474cc 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -265,11 +265,11 @@ module AbstractController
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
when nil
if name
- _prefix = "layouts" unless _implied_layout_name =~ /\blayouts/
+ _prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def _layout
- if template_exists?("#{_implied_layout_name}", #{_prefix.inspect})
+ if template_exists?("#{_implied_layout_name}", #{_prefixes.inspect})
"#{_implied_layout_name}"
else
super
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 91b75273fa..691310d5d2 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -13,14 +13,15 @@ module AbstractController
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
# it will trigger the lookup_context and consequently expire the cache.
class I18nProxy < ::I18n::Config #:nodoc:
- attr_reader :i18n_config, :lookup_context
+ attr_reader :original_config, :lookup_context
- def initialize(i18n_config, lookup_context)
- @i18n_config, @lookup_context = i18n_config, lookup_context
+ def initialize(original_config, lookup_context)
+ original_config = original_config.original_config if original_config.respond_to?(:original_config)
+ @original_config, @lookup_context = original_config, lookup_context
end
def locale
- @i18n_config.locale
+ @original_config.locale
end
def locale=(value)
@@ -60,6 +61,20 @@ module AbstractController
end
end
end
+
+ def parent_prefixes
+ @parent_prefixes ||= begin
+ parent_controller = superclass
+ prefixes = []
+
+ until parent_controller.abstract?
+ prefixes << parent_controller.controller_path
+ parent_controller = parent_controller.superclass
+ end
+
+ prefixes
+ end
+ end
end
attr_writer :view_context_class
@@ -98,7 +113,7 @@ module AbstractController
def render_to_string(*args, &block)
options = _normalize_args(*args, &block)
_normalize_options(options)
- render_to_body(options)
+ render_to_body(options).tap { self.response_body = nil }
end
# Raw rendering of a template to a Rack-compatible body.
@@ -114,9 +129,12 @@ module AbstractController
view_context.render(options)
end
- # The prefix used in render "foo" shortcuts.
- def _prefix
- controller_path
+ # The prefixes used in render "foo" shortcuts.
+ def _prefixes
+ @_prefixes ||= begin
+ parent_prefixes = self.class.parent_prefixes
+ parent_prefixes.dup.unshift(controller_path)
+ end
end
private
@@ -156,7 +174,7 @@ module AbstractController
end
if (options.keys & [:partial, :file, :template, :once]).empty?
- options[:prefix] ||= _prefix
+ options[:prefixes] ||= _prefixes
end
options[:template] ||= (options[:action] || action_name).to_s
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 48308cbb60..81c0698fb8 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -24,7 +24,7 @@ module ActionController
#
# Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
# after executing code in the action. For example, the +index+ action of the PostsController would render the
- # template <tt>app/views/posts/index.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
+ # template <tt>app/views/posts/index.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
#
# Unlike index, the create action will not render a template. After performing its main purpose (creating a
# new post), it initiates a redirect instead. This redirect works by returning an external
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 37c155b9cd..0be04b70a1 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -1,52 +1,72 @@
module ActionController #:nodoc:
module Caching
- # Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
- # certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
- # parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like:
+ # Fragment caching is used for caching various blocks within
+ # views without caching the entire action as a whole. This is
+ # useful when certain elements of an action change frequently or
+ # depend on complicated state while other parts rarely change or
+ # can be shared amongst multiple parties. The caching is done using
+ # the <tt>cache</tt> helper available in the Action View. A
+ # template with fragment caching might look like:
#
# <b>Hello <%= @name %></b>
+ #
# <% cache do %>
# All the topics in the system:
# <%= render :partial => "topic", :collection => Topic.find(:all) %>
# <% end %>
#
- # This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would
- # be able to invalidate it using <tt>expire_fragment(:controller => "topics", :action => "list")</tt>.
+ # This cache will bind the name of the action that called it, so if
+ # this code was part of the view for the topics/list action, you
+ # would be able to invalidate it using:
+ #
+ # expire_fragment(:controller => "topics", :action => "list")
#
- # This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using
- # <tt>caches_action</tt>, so we also have the option to qualify the name of the cached fragment with something like:
+ # This default behavior is limited if you need to cache multiple
+ # fragments per action or if the action itself is cached using
+ # <tt>caches_action</tt>. To remedy this, there is an option to
+ # qualify the name of the cached fragment by using the
+ # <tt>:action_suffix</tt> option:
#
# <% cache(:action => "list", :action_suffix => "all_topics") do %>
#
- # That would result in a name such as <tt>/topics/list/all_topics</tt>, avoiding conflicts with the action cache and with any fragments that use a
- # different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique
- # cache names that we can refer to when we need to expire the cache.
+ # That would result in a name such as
+ # <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
+ # action cache and with any fragments that use a different suffix.
+ # Note that the URL doesn't have to really exist or be callable
+ # - the url_for system is just used to generate unique cache names
+ # that we can refer to when we need to expire the cache.
#
# The expiration call for this example is:
#
- # expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")
+ # expire_fragment(:controller => "topics",
+ # :action => "list",
+ # :action_suffix => "all_topics")
module Fragments
- # Given a key (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading,
- # writing, or expiring a cached fragment. If the key is a hash, the generated key is the return
- # value of url_for on that hash (without the protocol). All keys are prefixed with <tt>views/</tt> and uses
+ # Given a key (as described in <tt>expire_fragment</tt>), returns
+ # a key suitable for use in reading, writing, or expiring a
+ # cached fragment. If the key is a hash, the generated key is the
+ # return value of url_for on that hash (without the protocol).
+ # All keys are prefixed with <tt>views/</tt> and uses
# ActiveSupport::Cache.expand_cache_key for the expansion.
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
- # Writes <tt>content</tt> to the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
+ # Writes <tt>content</tt> to the location signified by
+ # <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats).
def write_fragment(key, content, options = nil)
return content unless cache_configured?
key = fragment_cache_key(key)
instrument_fragment_cache :write_fragment, key do
- content = content.html_safe.to_str if content.respond_to?(:html_safe)
+ content = content.to_str
cache_store.write(key, content, options)
end
content
end
- # Reads a cached fragment from the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
+ # Reads a cached fragment from the location signified by <tt>key</tt>
+ # (see <tt>expire_fragment</tt> for acceptable formats).
def read_fragment(key, options = nil)
return unless cache_configured?
@@ -57,7 +77,8 @@ module ActionController #:nodoc:
end
end
- # Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
+ # Check if a cached fragment from the location signified by
+ # <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
def fragment_exist?(key, options = nil)
return unless cache_configured?
key = fragment_cache_key(key)
@@ -70,6 +91,7 @@ module ActionController #:nodoc:
# Removes fragments from the cache.
#
# +key+ can take one of three forms:
+ #
# * String - This would normally take the form of a path, like
# <tt>pages/45/notes</tt>.
# * Hash - Treated as an implicit call to +url_for+, like
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 3e57d2c236..8c583c7ce0 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -106,7 +106,7 @@ module ActionController #:nodoc:
end
def page_cache_path(path, extension = nil)
- page_cache_directory + page_cache_file(path, extension)
+ page_cache_directory.to_s + page_cache_file(path, extension)
end
def instrument_page_cache(name, path)
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index 3b19310a69..3fae697cc3 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -16,7 +16,11 @@ module ActionController
payload = event.payload
additions = ActionController::Base.log_process_action(payload)
- message = "Completed #{payload[:status]} #{Rack::Utils::HTTP_STATUS_CODES[payload[:status]]} in %.0fms" % event.duration
+ status = payload[:status]
+ if status.nil? && payload[:exception].present?
+ status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil
+ end
+ message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
message << " (#{additions.join(" | ")})" unless additions.blank?
info(message)
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 329798e84f..b2c8053584 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -43,12 +43,61 @@ module ActionController
end
end
- # Provides a way to get a valid Rack application from a controller.
+ # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
+ # valid Rack interface without the additional niceties provided by
+ # <tt>ActionController::Base</tt>.
+ #
+ # A sample metal controller might look like this:
+ #
+ # class HelloController < ActionController::Metal
+ # def index
+ # self.response_body = "Hello World!"
+ # end
+ # end
+ #
+ # And then to route requests to your metal controller, you would add
+ # something like this to <tt>config/routes.rb</tt>:
+ #
+ # match 'hello', :to => HelloController.action(:index)
+ #
+ # The +action+ method returns a valid Rack application for the \Rails
+ # router to dispatch to.
+ #
+ # == Rendering Helpers
+ #
+ # <tt>ActionController::Metal</tt> by default provides no utilities for rendering
+ # views, partials, or other responses aside from explicitly calling of
+ # <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
+ # add the render helpers you're used to having in a normal controller, you
+ # can do the following:
+ #
+ # class HelloController < ActionController::Metal
+ # include ActionController::Rendering
+ # append_view_path "#{Rails.root}/app/views"
+ #
+ # def index
+ # render "hello/index"
+ # end
+ # end
+ #
+ # == Redirection Helpers
+ #
+ # To add redirection helpers to your metal controller, do the following:
+ #
+ # class HelloController < ActionController::Metal
+ # include ActionController::Redirecting
+ # include Rails.application.routes.url_helpers
+ #
+ # def index
+ # redirect_to root_url
+ # end
+ # end
+ #
+ # == Other Helpers
+ #
+ # You can refer to the modules included in <tt>ActionController::Base</tt> to see
+ # other features you can bring into your metal controller.
#
- # In AbstractController, dispatching is triggered directly by calling #process on a new controller.
- # <tt>ActionController::Metal</tt> provides an <tt>action</tt> method that returns a valid Rack application for a
- # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the \Rails router,
- # can dispatch directly to actions returned by controllers in your application.
class Metal < AbstractController::Base
abstract!
@@ -133,7 +182,7 @@ module ActionController
end
def response_body=(val)
- body = val.respond_to?(:each) ? val : [val]
+ body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
super body
end
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index 282dcf66b3..cfa7004048 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -12,10 +12,10 @@ module ActionController
def method_for_action(action_name)
super || begin
- if template_exists?(action_name.to_s, _prefix)
+ if template_exists?(action_name.to_s, _prefixes)
"default_render"
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 9ba37134b8..a2e06fe0a6 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -63,13 +63,13 @@ module ActionController #:nodoc:
# might look something like this:
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
# end
#
# Here's the same action, with web-service support baked in:
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
#
# respond_to do |format|
# format.html
@@ -155,7 +155,7 @@ module ActionController #:nodoc:
# Respond to also allows you to specify a common block for different formats by using any:
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
#
# respond_to do |format|
# format.html
@@ -178,7 +178,7 @@ module ActionController #:nodoc:
# respond_to :html, :xml, :json
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
# respond_with(@person)
# end
# end
@@ -208,8 +208,8 @@ module ActionController #:nodoc:
# It also accepts a block to be given. It's used to overwrite a default
# response:
#
- # def destroy
- # @user = User.find(params[:id])
+ # def create
+ # @user = User.new(params[:user])
# flash[:notice] = "User was successfully created." if @user.save
#
# respond_with(@user) do |format|
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 14cc547dd0..32d52c84c4 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -6,7 +6,7 @@ module ActionController
# Before processing, set the request formats in current controller formats.
def process_action(*) #:nodoc:
- self.formats = request.formats.map { |x| x.to_sym }
+ self.formats = request.formats.map { |x| x.ref }
super
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 148efbb081..b89e03bfb6 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -71,25 +71,24 @@ module ActionController #:nodoc:
end
protected
-
- def protect_from_forgery(options = {})
- self.request_forgery_protection_token ||= :authenticity_token
- before_filter :verify_authenticity_token, options
- end
-
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
- verified_request? || raise(ActionController::InvalidAuthenticityToken)
+ verified_request? || handle_unverified_request
+ end
+
+ def handle_unverified_request
+ reset_session
end
# Returns true or false if a request is verified. Checks:
#
- # * is the format restricted? By default, only HTML requests are checked.
# * is it a GET request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
+ # * Does the X-CSRF-Token header match the form_authenticity_token
def verified_request?
- !protect_against_forgery? || request.forgery_whitelisted? ||
- form_authenticity_token == params[request_forgery_protection_token]
+ !protect_against_forgery? || request.get? ||
+ form_authenticity_token == params[request_forgery_protection_token] ||
+ form_authenticity_token == request.headers['X-CSRF-Token']
end
# Sets the token value for the current session.
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index 38d32211cc..4b45413cf8 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -77,8 +77,6 @@ module ActionController #:nodoc:
#
# respond_with(@project, :manager, @task)
#
- # Check <code>polymorphic_url</code> documentation for more examples.
- #
class Responder
attr_reader :controller, :request, :format, :resource, :resources, :options
@@ -115,7 +113,7 @@ module ActionController #:nodoc:
# Main entry point for responder responsible to dispatch to the proper format.
#
def respond
- method = :"to_#{format}"
+ method = "to_#{format}"
respond_to?(method) ? send(method) : to_format
end
@@ -171,7 +169,7 @@ module ActionController #:nodoc:
# Checks whether the resource responds to the current format or not.
#
def resourceful?
- resource.respond_to?(:"to_#{format}")
+ resource.respond_to?("to_#{format}")
end
# Returns the resource location by retrieving it from the options or
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
index 3e5d23b5c1..09dd08898c 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -170,7 +170,7 @@ module HTML
def contains_bad_protocols?(attr_name, value)
uri_attributes.include?(attr_name) &&
- (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
+ (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase))
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 1d2f7e4f19..4f4cb96a74 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -43,7 +43,7 @@ module ActionDispatch
alias :etag? :etag
def initialize(*)
- status, header, body = super
+ super
@cache_control = {}
@etag = self["ETag"]
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 5b87a80c1b..7c9ebe7c7b 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -216,7 +216,11 @@ module Mime
end
def to_sym
- @symbol || @string.to_sym
+ @symbol
+ end
+
+ def ref
+ to_sym || to_s
end
def ===(list)
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 08f30e068d..f07ac44f7a 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -2,6 +2,7 @@ require 'tempfile'
require 'stringio'
require 'strscan'
+require 'active_support/core_ext/module/deprecation'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/string/access'
require 'active_support/inflector'
@@ -133,8 +134,9 @@ module ActionDispatch
end
def forgery_whitelisted?
- get? || xhr? || content_mime_type.nil? || !content_mime_type.verify_request?
+ get?
end
+ deprecate :forgery_whitelisted? => "it is just an alias for 'get?' now, update your code"
def media_type
content_mime_type.to_s
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 796cd8c09b..535ff42b90 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -28,8 +28,11 @@ module ActionDispatch
rewritten_url = ""
unless options[:only_path]
- rewritten_url << (options[:protocol] || "http")
- rewritten_url << "://" unless rewritten_url.match("://")
+ unless options[:protocol] == false
+ rewritten_url << (options[:protocol] || "http")
+ rewritten_url << ":" unless rewritten_url.match(%r{:|//})
+ end
+ 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]
diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
index 5776a7bb27..1bb2ad7f67 100644
--- a/actionpack/lib/action_dispatch/middleware/callbacks.rb
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module/delegation'
+
module ActionDispatch
# Provide callbacks to be executed before and after the request dispatch.
class Callbacks
@@ -5,10 +7,8 @@ module ActionDispatch
define_callbacks :call, :rescuable => true
- def self.to_prepare(*args, &block)
- ActiveSupport::Deprecation.warn "ActionDispatch::Callbacks.to_prepare is deprecated. " <<
- "Please use ActionDispatch::Reloader.to_prepare instead."
- ActionDispatch::Reloader.to_prepare(*args, &block)
+ class << self
+ delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
end
def self.before(*args, &block)
@@ -25,7 +25,7 @@ module ActionDispatch
end
def call(env)
- _run_call_callbacks do
+ run_callbacks :call do
@app.call(env)
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index f369d2d3c2..7ac608f0a8 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -90,17 +90,14 @@ module ActionDispatch
# **.**, ***.** style TLDs like co.uk or com.au
#
# www.example.co.uk gives:
- # $1 => example
- # $2 => co.uk
+ # $& => example.co.uk
#
# example.com gives:
- # $1 => example
- # $2 => com
+ # $& => example.com
#
# lots.of.subdomains.example.local gives:
- # $1 => example
- # $2 => local
- DOMAIN_REGEXP = /([^.]*)\.([^.]*|..\...|...\...)$/
+ # $& => example.local
+ DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
def self.build(request)
secret = request.env[TOKEN_KEY]
@@ -131,11 +128,17 @@ module ActionDispatch
options[:path] ||= "/"
if options[:domain] == :all
+ # if there is a provided tld length then we use it otherwise default domain regexp
+ domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
+
# if host is not ip and matches domain regexp
# (ip confirms to domain regexp so we explicitly check for ip)
- options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ DOMAIN_REGEXP)
- ".#{$1}.#{$2}"
+ options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ domain_regexp)
+ ".#{$&}"
end
+ elsif options[:domain].is_a? Array
+ # if host matches one of the supplied domains without a dot in front of it
+ options[:domain] = options[:domain].find {|domain| @host.include? domain[/^\.?(.*)$/, 1] }
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb
index 579b5d8a02..29289a76b4 100644
--- a/actionpack/lib/action_dispatch/middleware/reloader.rb
+++ b/actionpack/lib/action_dispatch/middleware/reloader.rb
@@ -1,9 +1,12 @@
module ActionDispatch
- # ActionDispatch::Reloader provides to_prepare and to_cleanup callbacks.
- # These are analogs of ActionDispatch::Callback's before and after
- # callbacks, with the difference that to_cleanup is not called until the
+ # ActionDispatch::Reloader provides prepare and cleanup callbacks,
+ # intended to assist with code reloading during development.
+ #
+ # Prepare callbacks are run before each request, and cleanup callbacks
+ # after each request. In this respect they are analogs of ActionDispatch::Callback's
+ # before and after callbacks. However, cleanup callbacks are not called until the
# request is fully complete -- that is, after #close has been called on
- # the request body. This is important for streaming responses such as the
+ # the response body. This is important for streaming responses such as the
# following:
#
# self.response_body = lambda { |response, output|
@@ -15,7 +18,10 @@ module ActionDispatch
# classes before they are unloaded.
#
# By default, ActionDispatch::Reloader is included in the middleware stack
- # only in the development environment.
+ # only in the development environment; specifically, when config.cache_classes
+ # is false. Callbacks may be registered even when it is not included in the
+ # middleware stack, but are executed only when +ActionDispatch::Reloader.prepare!+
+ # or +ActionDispatch::Reloader.cleanup!+ are called manually.
#
class Reloader
include ActiveSupport::Callbacks
@@ -23,8 +29,8 @@ module ActionDispatch
define_callbacks :prepare, :scope => :name
define_callbacks :cleanup, :scope => :name
- # Add a preparation callback. Preparation callbacks are run before each
- # request.
+ # Add a prepare callback. Prepare callbacks are run before each request, prior
+ # to ActionDispatch::Callback's before callbacks.
def self.to_prepare(*args, &block)
set_callback(:prepare, *args, &block)
end
@@ -35,12 +41,14 @@ module ActionDispatch
set_callback(:cleanup, *args, &block)
end
+ # Execute all prepare callbacks.
def self.prepare!
- new(nil).send(:_run_prepare_callbacks)
+ new(nil).run_callbacks :prepare
end
+ # Execute all cleanup callbacks.
def self.cleanup!
- new(nil).send(:_run_cleanup_callbacks)
+ new(nil).run_callbacks :cleanup
end
def initialize(app)
@@ -56,10 +64,13 @@ module ActionDispatch
end
def call(env)
- _run_prepare_callbacks
+ run_callbacks :prepare
response = @app.call(env)
response[2].extend(CleanupOnClose)
response
+ rescue Exception
+ run_callbacks :cleanup
+ raise
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 71e736ce9f..dbe3206808 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -43,20 +43,20 @@ module ActionDispatch
end
def call(env)
- status, headers, body = @app.call(env)
-
- # Only this middleware cares about RoutingError. So, let's just raise
- # it here.
- # TODO: refactor this middleware to handle the X-Cascade scenario without
- # having to raise an exception.
- if headers['X-Cascade'] == 'pass'
- raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}"
+ begin
+ status, headers, body = @app.call(env)
+ exception = nil
+
+ # Only this middleware cares about RoutingError. So, let's just raise
+ # it here.
+ if headers['X-Cascade'] == 'pass'
+ raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}"
+ end
+ rescue Exception => exception
+ raise exception if env['action_dispatch.show_exceptions'] == false
end
- [status, headers, body]
- rescue Exception => exception
- raise exception if env['action_dispatch.show_exceptions'] == false
- render_exception(env, exception)
+ exception ? render_exception(env, exception) : [status, headers, body]
end
private
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
index bd6ffbab5d..50d8ca9484 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
@@ -6,5 +6,5 @@
</h1>
<pre><%=h @exception.message %></pre>
-<%= render :file => "rescues/_trace.erb" %>
-<%= render :file => "rescues/_request_and_response.erb" %>
+<%= render :template => "rescues/_trace" %>
+<%= render :template => "rescues/_request_and_response" %>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
index 02fa18211d..c658559be9 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
@@ -13,9 +13,5 @@
<p><%=h @exception.sub_template_message %></p>
-<% @real_exception = @exception
- @exception = @exception.original_exception || @exception %>
-<%= render :file => "rescues/_trace.erb" %>
-<% @exception = @real_exception %>
-
-<%= render :file => "rescues/_request_and_response.erb" %>
+<%= render :template => "rescues/_trace" %>
+<%= render :template => "rescues/_request_and_response" %>
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 8810227a59..43fd93adf6 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -56,6 +56,18 @@ module ActionDispatch
# resources :posts, :comments
# end
#
+ # Alternately, you can add prefixes to your path without using a separate
+ # directory by using +scope+. +scope+ takes additional options which
+ # apply to all enclosed routes.
+ #
+ # scope :path => "/cpanel", :as => 'admin' do
+ # resources :posts, :comments
+ # end
+ #
+ # For more, see <tt>Routing::Mapper::Resources#resources</tt>,
+ # <tt>Routing::Mapper::Scoping#namespace</tt>, and
+ # <tt>Routing::Mapper::Scoping#scope</tt>.
+ #
# == Named routes
#
# Routes can be named by passing an <tt>:as</tt> option,
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index f65a294eca..589df218a8 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -22,18 +22,22 @@ module ActionDispatch
@app, @constraints, @request = app, constraints, request
end
- def call(env)
+ def matches?(env)
req = @request.new(env)
@constraints.each { |constraint|
if constraint.respond_to?(:matches?) && !constraint.matches?(req)
- return [ 404, {'X-Cascade' => 'pass'}, [] ]
+ return false
elsif constraint.respond_to?(:call) && !constraint.call(*constraint_args(constraint, req))
- return [ 404, {'X-Cascade' => 'pass'}, [] ]
+ return false
end
}
- @app.call(env)
+ return true
+ end
+
+ def call(env)
+ matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
end
private
@@ -247,7 +251,7 @@ module ActionDispatch
#
# root :to => 'pages#main'
#
- # For options, see the +match+ method's documentation, as +root+ uses it internally.
+ # For options, see +match+, as +root+ uses it internally.
#
# You should put the root route at the top of <tt>config/routes.rb</tt>,
# because this means it will be matched first. As this is the most popular route
@@ -256,15 +260,114 @@ module ActionDispatch
match '/', options.reverse_merge(:as => :root)
end
- # When you set up a regular route, you supply a series of symbols that
- # Rails maps to parts of an incoming HTTP request.
+ # Matches a url pattern to one or more routes. Any symbols in a pattern
+ # are interpreted as url query parameters and thus available as +params+
+ # in an action:
+ #
+ # # sets :controller, :action and :id in params
+ # match ':controller/:action/:id'
+ #
+ # Two of these symbols are special, +:controller+ maps to the controller
+ # and +:action+ to the controller's action. A pattern can also map
+ # wildcard segments (globs) to params:
+ #
+ # match 'songs/*category/:title' => 'songs#show'
+ #
+ # # 'songs/rock/classic/stairway-to-heaven' sets
+ # # params[:category] = 'rock/classic'
+ # # params[:title] = 'stairway-to-heaven'
+ #
+ # When a pattern points to an internal route, the route's +:action+ and
+ # +:controller+ should be set in options or hash shorthand. Examples:
+ #
+ # match 'photos/:id' => 'photos#show'
+ # match 'photos/:id', :to => 'photos#show'
+ # match 'photos/:id', :controller => 'photos', :action => 'show'
+ #
+ # A pattern can also point to a +Rack+ endpoint i.e. anything that
+ # responds to +call+:
+ #
+ # match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon" }
+ # match 'photos/:id' => PhotoRackApp
+ # # Yes, controller actions are just rack endpoints
+ # match 'photos/:id' => PhotosController.action(:show)
+ #
+ # === Options
+ #
+ # Any options not seen here are passed on as params with the url.
+ #
+ # [:controller]
+ # The route's controller.
+ #
+ # [:action]
+ # The route's action.
+ #
+ # [:path]
+ # The path prefix for the routes.
+ #
+ # [:module]
+ # The namespace for :controller.
+ #
+ # match 'path' => 'c#a', :module => 'sekret', :controller => 'posts'
+ # #=> Sekret::PostsController
+ #
+ # See <tt>Scoping#namespace</tt> for its scope equivalent.
+ #
+ # [:as]
+ # The name used to generate routing helpers.
+ #
+ # [:via]
+ # Allowed HTTP verb(s) for route.
+ #
+ # match 'path' => 'c#a', :via => :get
+ # match 'path' => 'c#a', :via => [:get, :post]
+ #
+ # [:to]
+ # Points to a +Rack+ endpoint. Can be an object that responds to
+ # +call+ or a string representing a controller's action.
+ #
+ # match 'path', :to => 'controller#action'
+ # match 'path', :to => lambda { [200, {}, "Success!"] }
+ # match 'path', :to => RackApp
+ #
+ # [:on]
+ # Shorthand for wrapping routes in a specific RESTful context. Valid
+ # values are :member, :collection, and :new. Only use within
+ # <tt>resource(s)</tt> block. For example:
+ #
+ # resource :bar do
+ # match 'foo' => 'c#a', :on => :member, :via => [:get, :post]
+ # end
+ #
+ # Is equivalent to:
#
- # match ':controller/:action/:id/:user_id'
+ # resource :bar do
+ # member do
+ # match 'foo' => 'c#a', :via => [:get, :post]
+ # end
+ # end
+ #
+ # [:constraints]
+ # Constrains parameters with a hash of regular expressions or an
+ # object that responds to #matches?
+ #
+ # match 'path/:id', :constraints => { :id => /[A-Z]\d{5}/ }
+ #
+ # class Blacklist
+ # def matches?(request) request.remote_ip == '1.2.3.4' end
+ # end
+ # match 'path' => 'c#a', :constraints => Blacklist.new
#
- # Two of these symbols are special: :controller maps to the name of a
- # controller in your application, and :action maps to the name of an
- # action within that controller. Anything other than :controller or
- # :action will be available to the action as part of params.
+ # See <tt>Scoping#constraints</tt> for more examples with its scope
+ # equivalent.
+ #
+ # [:defaults]
+ # Sets defaults for parameters
+ #
+ # # Sets params[:format] to 'jpg' by default
+ # match 'path' => 'c#a', :defaults => { :format => 'jpg' }
+ #
+ # See <tt>Scoping#defaults</tt> for its scope equivalent.
def match(path, options=nil)
mapping = Mapping.new(@set, @scope, path, options || {}).to_route
@set.add_route(*mapping)
@@ -279,6 +382,8 @@ module ActionDispatch
#
# mount(SomeRackApp => "some_route")
#
+ # For options, see +match+, as +mount+ uses it internally.
+ #
# All mounted applications come with routing helpers to access them.
# These are named after the class specified, so for the above example
# the helper is either +some_rack_app_path+ or +some_rack_app_url+.
@@ -349,7 +454,7 @@ module ActionDispatch
module HttpHelpers
# Define a route that only recognizes HTTP GET.
- # For supported arguments, see +match+.
+ # For supported arguments, see <tt>Base#match</tt>.
#
# Example:
#
@@ -359,7 +464,7 @@ module ActionDispatch
end
# Define a route that only recognizes HTTP POST.
- # For supported arguments, see +match+.
+ # For supported arguments, see <tt>Base#match</tt>.
#
# Example:
#
@@ -369,7 +474,7 @@ module ActionDispatch
end
# Define a route that only recognizes HTTP PUT.
- # For supported arguments, see +match+.
+ # For supported arguments, see <tt>Base#match</tt>.
#
# Example:
#
@@ -379,7 +484,7 @@ module ActionDispatch
end
# Define a route that only recognizes HTTP PUT.
- # For supported arguments, see +match+.
+ # For supported arguments, see <tt>Base#match</tt>.
#
# Example:
#
@@ -458,7 +563,7 @@ module ActionDispatch
super
end
- # Used to scope a set of routes to particular constraints.
+ # Scopes a set of routes to the given default options.
#
# Take the following route definition as an example:
#
@@ -470,51 +575,26 @@ module ActionDispatch
# The difference here being that the routes generated are like /rails/projects/2,
# rather than /accounts/rails/projects/2.
#
- # === Supported options
- # [:module]
- # If you want to route /posts (without the prefix /admin) to
- # Admin::PostsController, you could use
- #
- # scope :module => "admin" do
- # resources :posts
- # end
- #
- # [:path]
- # If you want to prefix the route, you could use
- #
- # scope :path => "/admin" do
- # resources :posts
- # end
+ # === Options
#
- # This will prefix all of the +posts+ resource's requests with '/admin'
+ # Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
#
- # [:as]
- # Prefixes the routing helpers in this scope with the specified label.
+ # === Examples
#
- # scope :as => "sekret" do
- # resources :posts
- # end
- #
- # Helpers such as +posts_path+ will now be +sekret_posts_path+
- #
- # [:shallow_path]
- #
- # Prefixes nested shallow routes with the specified path.
- #
- # scope :shallow_path => "sekret" do
- # resources :posts do
- # resources :comments, :shallow => true
- # end
+ # # route /posts (without the prefix /admin) to Admin::PostsController
+ # scope :module => "admin" do
+ # resources :posts
+ # end
#
- # The +comments+ resource here will have the following routes generated for it:
+ # # prefix the posts resource's requests with '/admin'
+ # scope :path => "/admin" do
+ # resources :posts
+ # end
#
- # post_comments GET /sekret/posts/:post_id/comments(.:format)
- # post_comments POST /sekret/posts/:post_id/comments(.:format)
- # new_post_comment GET /sekret/posts/:post_id/comments/new(.:format)
- # edit_comment GET /sekret/comments/:id/edit(.:format)
- # comment GET /sekret/comments/:id(.:format)
- # comment PUT /sekret/comments/:id(.:format)
- # comment DELETE /sekret/comments/:id(.:format)
+ # # prefix the routing helper name: sekret_posts_path instead of posts_path
+ # scope :as => "sekret" do
+ # resources :posts
+ # end
def scope(*args)
options = args.extract_options!
options = options.dup
@@ -577,43 +657,31 @@ module ActionDispatch
# admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
# admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
# admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
- # === Supported options
#
- # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ options all default to the name of the namespace.
+ # === Options
#
- # [:path]
- # The path prefix for the routes.
- #
- # namespace :admin, :path => "sekret" do
- # resources :posts
- # end
+ # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
+ # options all default to the name of the namespace.
#
- # All routes for the above +resources+ will be accessible through +/sekret/posts+, rather than +/admin/posts+
- #
- # [:module]
- # The namespace for the controllers.
- #
- # namespace :admin, :module => "sekret" do
- # resources :posts
- # end
+ # For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
+ # <tt>Resources#resources</tt>.
#
- # The +PostsController+ here should go in the +Sekret+ namespace and so it should be defined like this:
+ # === Examples
#
- # class Sekret::PostsController < ApplicationController
- # # code go here
- # end
- #
- # [:as]
- # Changes the name used in routing helpers for this namespace.
- #
- # namespace :admin, :as => "sekret" do
- # resources :posts
- # end
+ # # accessible through /sekret/posts rather than /admin/posts
+ # namespace :admin, :path => "sekret" do
+ # resources :posts
+ # end
#
- # Routing helpers such as +admin_posts_path+ will now be +sekret_posts_path+.
+ # # maps to Sekret::PostsController rather than Admin::PostsController
+ # namespace :admin, :module => "sekret" do
+ # resources :posts
+ # end
#
- # [:shallow_path]
- # See the +scope+ method.
+ # # generates sekret_posts_path rather than admin_posts_path
+ # namespace :admin, :as => "sekret" do
+ # resources :posts
+ # end
def namespace(path, options = {})
path = path.to_s
options = { :path => path, :as => path, :module => path,
@@ -680,9 +748,9 @@ module ActionDispatch
end
# Allows you to set default parameters for a route, such as this:
- # defaults :id => 'home' do
- # match 'scoped_pages/(:id)', :to => 'pages#show'
- # end
+ # defaults :id => 'home' do
+ # match 'scoped_pages/(:id)', :to => 'pages#show'
+ # end
# Using this, the +:id+ parameter here will default to 'home'.
def defaults(defaults = {})
scope(:defaults => defaults) { yield }
@@ -779,6 +847,14 @@ module ActionDispatch
# resources :posts, :comments
# end
#
+ # By default the :id parameter doesn't accept dots. If you need to
+ # use dots as part of the :id parameter add a constraint which
+ # overrides this restriction, e.g:
+ #
+ # resources :articles, :id => /[^\/]+/
+ #
+ # This allows any character other than a slash as part of your :id.
+ #
module Resources
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
# a path appended since they fit properly in their scope level.
@@ -827,7 +903,8 @@ module ActionDispatch
alias :member_name :singular
- # Checks for uncountable plurals, and appends "_index" if they're.
+ # Checks for uncountable plurals, and appends "_index" if the plural
+ # and singular form are the same.
def collection_name
singular == plural ? "#{plural}_index" : plural
end
@@ -906,6 +983,9 @@ module ActionDispatch
# GET /geocoder/edit
# PUT /geocoder
# DELETE /geocoder
+ #
+ # === Options
+ # Takes same options as +resources+.
def resource(*resources, &block)
options = resources.extract_options!
@@ -967,7 +1047,9 @@ module ActionDispatch
# PUT /photos/:id/comments/:id
# DELETE /photos/:id/comments/:id
#
- # === Supported options
+ # === Options
+ # Takes same options as <tt>Base#match</tt> as well as:
+ #
# [:path_names]
# Allows you to change the paths of the seven default actions.
# Paths not specified are not changed.
@@ -976,20 +1058,59 @@ module ActionDispatch
#
# The above example will now change /posts/new to /posts/brand_new
#
- # [:module]
- # Set the module where the controller can be found. Defaults to nothing.
+ # [:only]
+ # Only generate routes for the given actions.
#
- # resources :posts, :module => "admin"
+ # resources :cows, :only => :show
+ # resources :cows, :only => [:show, :index]
#
- # All requests to the posts resources will now go to +Admin::PostsController+.
+ # [:except]
+ # Generate all routes except for the given actions.
#
- # [:path]
+ # resources :cows, :except => :show
+ # resources :cows, :except => [:show, :index]
+ #
+ # [:shallow]
+ # Generates shallow routes for nested resource(s). When placed on a parent resource,
+ # generates shallow routes for all nested resources.
+ #
+ # resources :posts, :shallow => true do
+ # resources :comments
+ # end
+ #
+ # Is the same as:
+ #
+ # resources :posts do
+ # resources :comments
+ # end
+ # resources :comments
+ #
+ # [:shallow_path]
+ # Prefixes nested shallow routes with the specified path.
+ #
+ # scope :shallow_path => "sekret" do
+ # resources :posts do
+ # resources :comments, :shallow => true
+ # end
+ # end
+ #
+ # The +comments+ resource here will have the following routes generated for it:
+ #
+ # post_comments GET /sekret/posts/:post_id/comments(.:format)
+ # post_comments POST /sekret/posts/:post_id/comments(.:format)
+ # new_post_comment GET /sekret/posts/:post_id/comments/new(.:format)
+ # edit_comment GET /sekret/comments/:id/edit(.:format)
+ # comment GET /sekret/comments/:id(.:format)
+ # comment PUT /sekret/comments/:id(.:format)
+ # comment DELETE /sekret/comments/:id(.:format)
#
- # Set a path prefix for this resource.
+ # === Examples
#
- # resources :posts, :path => "admin"
+ # # routes call Admin::PostsController
+ # resources :posts, :module => "admin"
#
- # All actions for this resource will now be at +/admin/posts+.
+ # # resource actions are at /admin/posts.
+ # resources :posts, :path => "admin"
def resources(*resources, &block)
options = resources.extract_options!
@@ -1111,7 +1232,7 @@ module ActionDispatch
end
def shallow
- scope(:shallow => true) do
+ scope(:shallow => true, :shallow_path => @scope[:path]) do
yield
end
end
@@ -1321,7 +1442,7 @@ module ActionDispatch
name = case @scope[:scope_level]
when :nested
- [member_name, prefix]
+ [name_prefix, prefix]
when :collection
[prefix, name_prefix, collection_name]
when :new
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 03bfe178e5..4b4e9da173 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -450,7 +450,7 @@ module ActionDispatch
end
def raise_routing_error
- raise ActionController::RoutingError.new("No route matches #{options.inspect}")
+ raise ActionController::RoutingError, "No route matches #{options.inspect}"
end
def different_controller?
@@ -540,7 +540,9 @@ module ActionDispatch
end
dispatcher = route.app
- dispatcher = dispatcher.app while dispatcher.is_a?(Mapper::Constraints)
+ while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
+ dispatcher = dispatcher.app
+ end
if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
dispatcher.prepare_params!(params)
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 1558c3ae05..77a15f3e97 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -20,7 +20,7 @@ module ActionDispatch
#
# You can also pass an explicit status number like <tt>assert_response(501)</tt>
# or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
- # See ActionDispatch::StatusCodes for a full list.
+ # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
#
# ==== Examples
#
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 1390b74a95..11e8c63fa0 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -37,9 +37,6 @@ module ActionDispatch
#
# # Test a custom route
# assert_recognizes({:controller => 'items', :action => 'show', :id => '1'}, 'view/item1')
- #
- # # Check a Simply RESTful generated route
- # assert_recognizes list_items_url, 'items/list'
def assert_recognizes(expected_options, path, extras={}, message=nil)
request = recognized_request_for(path)
@@ -124,7 +121,8 @@ module ActionDispatch
options[:controller] = "/#{controller}"
end
- assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message)
+ generate_options = options.dup.delete_if{ |k,v| defaults.key?(k) }
+ assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
end
# A helper to make it easier to test different route configurations.
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 92ff3380b0..ab8c6259c5 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -18,7 +18,7 @@ module ActionView #:nodoc:
# following loop for names:
#
# <b>Names of all the people</b>
- # <% for person in @people %>
+ # <% @people.each do |person| %>
# Name: <%= person.name %><br/>
# <% end %>
#
@@ -232,8 +232,8 @@ module ActionView #:nodoc:
@controller_path ||= controller && controller.controller_path
end
- def controller_prefix
- @controller_prefix ||= controller && controller._prefix
+ def controller_prefixes
+ @controller_prefixes ||= controller && controller._prefixes
end
ActiveSupport.run_load_hooks(:action_view, self)
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index 41013c800c..d338ce616a 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -18,7 +18,7 @@ module ActionView #:nodoc:
autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
autoload :NumberHelper
autoload :PrototypeHelper
- autoload :RawOutputHelper
+ autoload :OutputSafetyHelper
autoload :RecordTagHelper
autoload :SanitizeHelper
autoload :ScriptaculousHelper
@@ -48,7 +48,7 @@ module ActionView #:nodoc:
include JavaScriptHelper
include NumberHelper
include PrototypeHelper
- include RawOutputHelper
+ include OutputSafetyHelper
include RecordTagHelper
include SanitizeHelper
include ScriptaculousHelper
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
index e52797042f..52eb43a1cd 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
@@ -11,7 +11,9 @@ module ActionView
attr_reader :config, :asset_paths
class_attribute :expansions
- self.expansions = { }
+ def self.inherited(base)
+ base.expansions = { }
+ end
def initialize(config, asset_paths)
@config = config
@@ -69,9 +71,21 @@ module ActionView
if sources.first == :all
collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}")
else
- sources.collect do |source|
- determine_source(source, expansions)
- end.flatten
+ sources.inject([]) do |list, source|
+ determined_source = determine_source(source, expansions)
+ update_source_list(list, determined_source)
+ end
+ end
+ end
+
+ def update_source_list(list, source)
+ case source
+ when String
+ list.delete(source)
+ list << source
+ when Array
+ updated_sources = source - list
+ list.concat(updated_sources)
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
index 6581e1d6f2..b9126af944 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
@@ -33,13 +33,21 @@ module ActionView
all_asset_files = (collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}") - ['application']) << 'application'
((determine_source(:defaults, expansions).dup & all_asset_files) + all_asset_files).uniq
else
- expanded_sources = sources.collect do |source|
- determine_source(source, expansions)
- end.flatten
- expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(custom_dir, "application.#{extension}"))
+ expanded_sources = sources.inject([]) do |list, source|
+ determined_source = determine_source(source, expansions)
+ update_source_list(list, determined_source)
+ end
+ add_application_js(expanded_sources, sources)
expanded_sources
end
end
+
+ def add_application_js(expanded_sources, sources)
+ if sources.include?(:defaults) && File.exist?(File.join(custom_dir, "application.#{extension}"))
+ expanded_sources.delete('application')
+ expanded_sources << "application"
+ end
+ end
end
@@ -59,7 +67,10 @@ module ActionView
# <script type="text/javascript" src="/javascripts/body.js"></script>
# <script type="text/javascript" src="/javascripts/tail.js"></script>
def register_javascript_expansion(expansions)
- JavascriptIncludeTag.expansions.merge!(expansions)
+ js_expansions = JavascriptIncludeTag.expansions
+ expansions.each do |key, values|
+ js_expansions[key] = (js_expansions[key] || []) | Array(values) if values
+ end
end
end
@@ -170,4 +181,4 @@ module ActionView
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
index d02b28d7f6..f3e041de95 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
@@ -44,7 +44,10 @@ module ActionView
# <link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />
# <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />
def register_stylesheet_expansion(expansions)
- StylesheetIncludeTag.expansions.merge!(expansions)
+ style_expansions = StylesheetIncludeTag.expansions
+ expansions.each do |key, values|
+ style_expansions[key] = (style_expansions[key] || []) | Array(values) if values
+ end
end
end
@@ -141,4 +144,4 @@ module ActionView
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index f544a9d147..385378ea29 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -2,31 +2,29 @@ module ActionView
# = Action View Cache Helper
module Helpers
module CacheHelper
- # This helper to exposes a method for caching of view fragments.
- # See ActionController::Caching::Fragments for usage instructions.
+ # This helper exposes a method for caching fragments of a view
+ # rather than an entire action or page. This technique is useful
+ # caching pieces like menus, lists of newstopics, static HTML
+ # fragments, and so on. This method takes a block that contains
+ # the content you wish to cache.
#
- # A method for caching fragments of a view rather than an entire
- # action or page. This technique is useful caching pieces like
- # menus, lists of news topics, static HTML fragments, and so on.
- # This method takes a block that contains the content you wish
- # to cache. See ActionController::Caching::Fragments for more
- # information.
+ # See ActionController::Caching::Fragments for usage instructions.
#
# ==== Examples
- # If you wanted to cache a navigation menu, you could do the
- # following.
+ # If you want to cache a navigation menu, you can do following:
#
# <% cache do %>
# <%= render :partial => "menu" %>
# <% end %>
#
- # You can also cache static content...
+ # You can also cache static content:
#
# <% cache do %>
# <p>Hello users! Welcome to our website!</p>
# <% end %>
#
- # ...and static content mixed with RHTML content.
+ # Static content with embedded ruby content can be cached as
+ # well:
#
# <% cache do %>
# Topics:
@@ -46,8 +44,8 @@ module ActionView
private
# TODO: Create an object that has caching read/write on it
def fragment_for(name = {}, options = nil, &block) #:nodoc:
- if controller.fragment_exist?(name, options)
- controller.read_fragment(name, options)
+ if fragment = controller.read_fragment(name, options)
+ fragment
else
# VIEW TODO: Make #capture usable outside of ERB
# This dance is needed because Builder can't use capture
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 875ec9b77b..dc8e4bc316 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -1,5 +1,6 @@
require 'date'
require 'action_view/helpers/tag_helper'
+require 'active_support/core_ext/date/conversions'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/object/with_options'
@@ -566,6 +567,27 @@ module ActionView
def select_year(date, options = {}, html_options = {})
DateTimeSelector.new(date, options, html_options).select_year
end
+
+ # Returns an html time tag for the given date or time.
+ #
+ # ==== Examples
+ # time_tag Date.today # =>
+ # <time datetime="2010-11-04">November 04, 2010</time>
+ # time_tag Time.now # =>
+ # <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
+ # time_tag Date.yesterday, 'Yesterday' # =>
+ # <time datetime="2010-11-03">Yesterday</time>
+ # time_tag Date.today, :pubdate => true # =>
+ # <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
+ #
+ def time_tag(date_or_time, *args)
+ options = args.extract_options!
+ format = options.delete(:format) || :long
+ content = args.first || I18n.l(date_or_time, :format => format)
+ datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.rfc3339
+
+ content_tag(:time, content, options.reverse_merge(:datetime => datetime))
+ end
end
class DateTimeSelector #:nodoc:
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 6f0e2c99ba..befaa3e8d9 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -298,6 +298,23 @@ module ActionView
#
# If you don't need to attach a form to a model instance, then check out
# FormTagHelper#form_tag.
+ #
+ # === Form to external resources
+ #
+ # When you build forms to external resources sometimes you need to set an authenticity token or just render a form
+ # without it, for example when you submit data to a payment gateway number and types of fields could be limited.
+ #
+ # To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
+ #
+ # <%= form_for @invoice, :url => external_url, :authenticity_token => 'external_token' do |f|
+ # ...
+ # <% end %>
+ #
+ # If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
+ #
+ # <%= form_for @invoice, :url => external_url, :authenticity_token => false do |f|
+ # ...
+ # <% end %>
def form_for(record, options = {}, &proc)
raise ArgumentError, "Missing block" unless block_given?
@@ -314,6 +331,8 @@ module ActionView
end
options[:html][:remote] = options.delete(:remote)
+ options[:html][:authenticity_token] = options.delete(:authenticity_token)
+
builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &proc)
fields_for = fields_for(object_name, object, options, &proc)
default_options = builder.multipart? ? { :multipart => true } : {}
@@ -530,8 +549,11 @@ module ActionView
# <% end %>
# ...
# <% end %>
- def fields_for(record, record_object = nil, options = nil, &block)
- capture(instantiate_builder(record, record_object, options, &block), &block)
+ def fields_for(record, record_object = nil, options = {}, &block)
+ builder = instantiate_builder(record, record_object, options, &block)
+ output = capture(builder, &block)
+ output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id?
+ output
end
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
@@ -858,8 +880,7 @@ module ActionView
end
end
- module InstanceTagMethods #:nodoc:
- extend ActiveSupport::Concern
+ class InstanceTag
include Helpers::CaptureHelper, Context, Helpers::TagHelper, Helpers::FormTagHelper
attr_reader :object, :method_name, :object_name
@@ -1025,7 +1046,7 @@ module ActionView
self.class.value_before_type_cast(object, @method_name)
end
- module ClassMethods
+ class << self
def value(object, method_name)
object.send method_name if object
end
@@ -1111,10 +1132,6 @@ module ActionView
end
end
- class InstanceTag
- include InstanceTagMethods
- end
-
class FormBuilder
# The methods which wrap a form helper call.
class_attribute :field_helpers
@@ -1248,7 +1265,7 @@ module ActionView
def submit(value=nil, options={})
value, options = nil, value if value.is_a?(Hash)
value ||= submit_default_value
- @template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
+ @template.submit_tag(value, options)
end
def emitted_hidden_id?
@@ -1309,14 +1326,8 @@ module ActionView
def fields_for_nested_model(name, object, options, block)
object = convert_to_model(object)
- if object.persisted?
- @template.fields_for(name, object, options) do |builder|
- block.call(builder)
- @template.concat builder.hidden_field(:id) unless builder.emitted_hidden_id?
- end
- else
- @template.fields_for(name, object, options, &block)
- end
+ options[:hidden_field_id] = object.persisted?
+ @template.fields_for(name, object, options, &block)
end
def nested_child_index(name)
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 6ac8577785..7698602022 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -533,7 +533,7 @@ module ActionView
else
selected = Array.wrap(selected)
options = selected.extract_options!.symbolize_keys
- [ options[:selected] || selected , options[:disabled] ]
+ [ options.include?(:selected) ? options[:selected] : selected, options[:disabled] ]
end
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 9500e85e8b..71f8534cbf 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -25,6 +25,9 @@ module ActionView
# * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
# If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
# is added to simulate the verb over post.
+ # * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
+ # pass custom authenticity token string, or to not add authenticity_token field at all
+ # (by passing <tt>false</tt>).
# * A list of parameters to feed to the URL the form will be posted to.
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
# submit behaviour. By default this behaviour is an ajax submit.
@@ -47,6 +50,12 @@ module ActionView
# <%= form_tag('/posts', :remote => true) %>
# # => <form action="/posts" method="post" data-remote="true">
#
+ # form_tag('http://far.away.com/form', :authenticity_token => false)
+ # # form without authenticity token
+ #
+ # form_tag('http://far.away.com/form', :authenticity_token => "cf50faa3fe97702ca1ae")
+ # # form with custom authenticity token
+ #
def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
html_options = html_options_for_form(url_for_options, options, *parameters_for_url)
if block_given?
@@ -401,6 +410,59 @@ module ActionView
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
end
+ # Creates a button element that defines a <tt>submit</tt> button,
+ # <tt>reset</tt>button or a generic button which can be used in
+ # JavaScript, for example. You can use the button tag as a regular
+ # submit tag but it isn't supported in legacy browsers. However,
+ # the button tag allows richer labels such as images and emphasis,
+ # so this helper will also accept a block.
+ #
+ # ==== Options
+ # * <tt>:confirm => 'question?'</tt> - If present, the
+ # unobtrusive JavaScript drivers will provide a prompt with
+ # the question specified. If the user accepts, the form is
+ # processed normally, otherwise no action is taken.
+ # * <tt>:disabled</tt> - If true, the user will not be able to
+ # use this input.
+ # * <tt>:disable_with</tt> - Value of this parameter will be
+ # used as the value for a disabled version of the submit
+ # button when the form is submitted. This feature is provided
+ # by the unobtrusive JavaScript driver.
+ # * Any other key creates standard HTML options for the tag.
+ #
+ # ==== Examples
+ # button_tag
+ # # => <button name="button" type="submit">Button</button>
+ #
+ # button_tag(:type => 'button') do
+ # content_tag(:strong, 'Ask me!')
+ # end
+ # # => <button name="button" type="button">
+ # <strong>Ask me!</strong>
+ # </button>
+ #
+ # button_tag "Checkout", :disable_with => "Please wait..."
+ # # => <button data-disable-with="Please wait..." name="button"
+ # type="submit">Checkout</button>
+ #
+ def button_tag(content_or_options = nil, options = nil, &block)
+ options = content_or_options if block_given? && content_or_options.is_a?(Hash)
+ options ||= {}
+ options.stringify_keys!
+
+ if disable_with = options.delete("disable_with")
+ options["data-disable-with"] = disable_with
+ end
+
+ if confirm = options.delete("confirm")
+ options["data-confirm"] = confirm
+ end
+
+ options.reverse_merge! 'name' => 'button', 'type' => 'submit'
+
+ content_tag :button, content_or_options || 'Button', options, &block
+ end
+
# Displays an image which when clicked will submit the form.
#
# <tt>source</tt> is passed to AssetTagHelper#path_to_image
@@ -534,6 +596,7 @@ module ActionView
html_options["action"] = url_for(url_for_options, *parameters_for_url)
html_options["accept-charset"] = "UTF-8"
html_options["data-remote"] = true if html_options.delete("remote")
+ html_options["authenticity_token"] = html_options.delete("authenticity_token") if html_options.has_key?("authenticity_token")
end
end
@@ -541,6 +604,7 @@ module ActionView
snowman_tag = tag(:input, :type => "hidden",
:name => "utf8", :value => "&#x2713;".html_safe)
+ authenticity_token = html_options.delete("authenticity_token")
method = html_options.delete("method").to_s
method_tag = case method
@@ -549,10 +613,10 @@ module ActionView
''
when /^post$/i, "", nil
html_options["method"] = "post"
- token_tag
+ token_tag(authenticity_token)
else
html_options["method"] = "post"
- tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag
+ tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
end
tags = snowman_tag << method_tag
@@ -572,11 +636,12 @@ module ActionView
output.safe_concat("</form>")
end
- def token_tag
- unless protect_against_forgery?
+ def token_tag(token)
+ if token == false || !protect_against_forgery?
''
else
- tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
+ token = form_authenticity_token if token.nil?
+ tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token)
end
end
diff --git a/actionpack/lib/action_view/helpers/output_safety_helper.rb b/actionpack/lib/action_view/helpers/output_safety_helper.rb
new file mode 100644
index 0000000000..a035dd70ad
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/output_safety_helper.rb
@@ -0,0 +1,38 @@
+require 'active_support/core_ext/string/output_safety'
+
+module ActionView #:nodoc:
+ # = Action View Raw Output Helper
+ module Helpers #:nodoc:
+ module OutputSafetyHelper
+ # This method outputs without escaping a string. Since escaping tags is
+ # now default, this can be used when you don't want Rails to automatically
+ # escape tags. This is not recommended if the data is coming from the user's
+ # input.
+ #
+ # For example:
+ #
+ # <%=raw @user.name %>
+ def raw(stringish)
+ stringish.to_s.html_safe
+ end
+
+ # This method returns a html safe string similar to what <tt>Array#join</tt>
+ # would return. All items in the array, including the supplied separator, are
+ # html escaped unless they are html safe, and the returned string is marked
+ # as html safe.
+ #
+ # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />")
+ # # => "<p>foo</p>&lt;br /&gt;&lt;p&gt;bar&lt;/p&gt;"
+ #
+ # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>".html_safe], "<br />".html_safe)
+ # # => "<p>foo</p><br /><p>bar</p>"
+ #
+ def safe_join(array, sep=$,)
+ sep ||= "".html_safe
+ sep = ERB::Util.html_escape(sep)
+
+ array.map { |i| ERB::Util.html_escape(i) }.join(sep).html_safe
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/raw_output_helper.rb b/actionpack/lib/action_view/helpers/raw_output_helper.rb
deleted file mode 100644
index 216683a2e0..0000000000
--- a/actionpack/lib/action_view/helpers/raw_output_helper.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module ActionView #:nodoc:
- # = Action View Raw Output Helper
- module Helpers #:nodoc:
- module RawOutputHelper
- # This method outputs without escaping a string. Since escaping tags is
- # now default, this can be used when you don't want Rails to automatically
- # escape tags. This is not recommended if the data is coming from the user's
- # input.
- #
- # For example:
- #
- # <%=raw @user.name %>
- def raw(stringish)
- stringish.to_s.html_safe
- end
- end
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index ee51617a2b..786af5ca58 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -14,7 +14,7 @@ module ActionView
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
autoplay controls loop selected hidden scoped async
defer reversed ismap seemless muted required
- autofocus novalidate formnovalidate open).to_set
+ autofocus novalidate formnovalidate open pubdate).to_set
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
# Returns an empty HTML tag of type +name+ which by default is XHTML
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 3d276000a1..4f7f5c454f 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -459,7 +459,7 @@ module ActionView
end
AUTO_LINK_RE = %r{
- (?: ([\w+.:-]+:)// | www\. )
+ (?: ([0-9A-Za-z+.:-]+:)// | www\. )
[^\s<]+
}x
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index c23315b344..2cd2dca711 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -253,8 +253,9 @@ module ActionView
# using the +link_to+ method with the <tt>:method</tt> modifier as described in
# the +link_to+ documentation.
#
- # The generated form element has a class name of <tt>button_to</tt>
- # to allow styling of the form itself and its children. You can control
+ # By default, the generated form element has a class name of <tt>button_to</tt>
+ # to allow styling of the form itself and its children. This can be changed
+ # using the <tt>:form_class</tt> modifier within +html_options+. You can control
# the form submission and input element behavior using +html_options+.
# This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
# described in the +link_to+ documentation. If no <tt>:method</tt> modifier
@@ -275,6 +276,8 @@ module ActionView
# processed normally, otherwise no action is taken.
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
# submit behaviour. By default this behaviour is an ajax submit.
+ # * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
+ # be placed
#
# ==== Examples
# <%= button_to "New", :action => "new" %>
@@ -283,6 +286,12 @@ module ActionView
# # </form>"
#
#
+ # <%= button_to "New", :action => "new", :form_class => "new-thing" %>
+ # # => "<form method="post" action="/controller/new" class="new-thing">
+ # # <div><input value="New" type="submit" /></div>
+ # # </form>"
+ #
+ #
# <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
# :confirm => "Are you sure?", :method => :delete %>
# # => "<form method="post" action="/images/delete/1" class="button_to">
@@ -312,6 +321,7 @@ module ActionView
end
form_method = method.to_s == 'get' ? 'get' : 'post'
+ form_class = html_options.delete('form_class') || 'button_to'
remote = html_options.delete('remote')
@@ -327,7 +337,7 @@ module ActionView
html_options.merge!("type" => "submit", "value" => name)
- ("<form method=\"#{form_method}\" action=\"#{ERB::Util.html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" +
+ ("<form method=\"#{form_method}\" action=\"#{ERB::Util.html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"#{ERB::Util.html_escape(form_class)}\"><div>" +
method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe
end
@@ -487,13 +497,14 @@ module ActionView
email_address_obfuscated = email_address.dup
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at")
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot")
-
case encode
when "javascript"
- string =
- "document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))}');".unpack('C*').map { |c|
- sprintf("%%%x", c)
- }.join
+ string = ''
+ html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))
+ html = escape_javascript(html)
+ "document.write('#{html}');".each_byte do |c|
+ string << sprintf("%%%x", c)
+ end
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe
when "hex"
email_address_encoded = email_address_obfuscated.unpack('C*').map {|c|
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index d524c68450..06c607931d 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -78,17 +78,17 @@ module ActionView
@view_paths = ActionView::Base.process_view_paths(paths)
end
- def find(name, prefix = nil, partial = false, keys = [])
- @view_paths.find(*args_for_lookup(name, prefix, partial, keys))
+ def find(name, prefixes = [], partial = false, keys = [])
+ @view_paths.find(*args_for_lookup(name, prefixes, partial, keys))
end
alias :find_template :find
- def find_all(name, prefix = nil, partial = false, keys = [])
- @view_paths.find_all(*args_for_lookup(name, prefix, partial, keys))
+ def find_all(name, prefixes = [], partial = false, keys = [])
+ @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys))
end
- def exists?(name, prefix = nil, partial = false, keys = [])
- @view_paths.exists?(*args_for_lookup(name, prefix, partial, keys))
+ def exists?(name, prefixes = [], partial = false, keys = [])
+ @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys))
end
alias :template_exists? :exists?
@@ -107,18 +107,26 @@ module ActionView
protected
- def args_for_lookup(name, prefix, partial, keys) #:nodoc:
- name, prefix = normalize_name(name, prefix)
- [name, prefix, partial || false, @details, details_key, keys]
+ def args_for_lookup(name, prefixes, partial, keys) #:nodoc:
+ name, prefixes = normalize_name(name, prefixes)
+ [name, prefixes, partial || false, @details, details_key, keys]
end
# Support legacy foo.erb names even though we now ignore .erb
# as well as incorrectly putting part of the path in the template
# name instead of the prefix.
- def normalize_name(name, prefix) #:nodoc:
+ def normalize_name(name, prefixes) #:nodoc:
name = name.to_s.gsub(handlers_regexp, '')
parts = name.split('/')
- return parts.pop, [prefix, *parts].compact.join("/")
+ name = parts.pop
+
+ prefixes = if prefixes.blank?
+ [parts.join('/')]
+ else
+ prefixes.map { |prefix| [prefix, *parts].compact.join('/') }
+ end
+
+ return name, prefixes
end
def default_handlers #:nodoc:
@@ -156,11 +164,11 @@ module ActionView
@frozen_formats = true
end
- # Overload formats= to reject [:"*/*"] values.
+ # Overload formats= to reject ["*/*"] values.
def formats=(values)
if values && values.size == 1
value = values.first
- values = nil if value == :"*/*"
+ values = nil if value == "*/*"
values << :html if value == :js
end
super(values)
@@ -178,11 +186,11 @@ module ActionView
end
# Overload locale= to also set the I18n.locale. If the current I18n.config object responds
- # to i18n_config, it means that it's has a copy of the original I18n configuration and it's
+ # to original_config, it means that it's has a copy of the original I18n configuration and it's
# acting as proxy, which we need to skip.
def locale=(value)
if value
- config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
+ config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config
config.locale = value
end
@@ -237,4 +245,4 @@ module ActionView
include Details
include ViewPaths
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index cd3f130dc4..c181689e62 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -12,19 +12,19 @@ module ActionView
#
# <%= render :partial => "account" %>
#
- # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable
+ # This would render "advertiser/_account.html.erb" and pass the instance variable @account in as a local variable
# +account+ to the template for display.
#
# In another template for Advertiser#buy, we could have:
#
# <%= render :partial => "account", :locals => { :account => @buyer } %>
#
- # <% for ad in @advertisements %>
+ # <% @advertisements.each do |ad| %>
# <%= render :partial => "ad", :locals => { :ad => ad } %>
# <% end %>
#
- # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then
- # render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
+ # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then
+ # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display.
#
# == The :as and :object options
#
@@ -40,16 +40,16 @@ module ActionView
# With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
# wanted it to be +agreement+ instead of +contract+ we'd do:
#
- # <%= render :partial => "contract", :as => :agreement %>
+ # <%= render :partial => "contract", :as => 'agreement' %>
#
# The <tt>:object</tt> option can be used to directly specify which object is rendered into the partial;
# useful when the template's object is elsewhere, in a different ivar or in a local variable for instance.
- #
+ #
# Revisiting a previous example we could have written this code:
- #
+ #
# <%= render :partial => "account", :object => @buyer %>
#
- # <% for ad in @advertisements %>
+ # <% @advertisements.each do |ad| %>
# <%= render :partial => "ad", :object => ad %>
# <% end %>
#
@@ -64,12 +64,12 @@ module ActionView
#
# <%= render :partial => "ad", :collection => @advertisements %>
#
- # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An
+ # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An
# iteration counter will automatically be made available to the template with a name of the form
# +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
#
# The <tt>:as</tt> option may be used when rendering partials.
- #
+ #
# You can specify a partial to be rendered between elements via the <tt>:spacer_template</tt> option.
# The following example will render <tt>advertiser/_ad_divider.html.erb</tt> between each ad partial:
#
@@ -89,7 +89,7 @@ module ActionView
#
# <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %>
#
- # This will render the partial "advertisement/_ad.erb" regardless of which controller this is being called from.
+ # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
#
# == Rendering objects with the RecordIdentifier
#
diff --git a/actionpack/lib/action_view/path_set.rb b/actionpack/lib/action_view/path_set.rb
index fa35120a0d..e3de3e1eac 100644
--- a/actionpack/lib/action_view/path_set.rb
+++ b/actionpack/lib/action_view/path_set.rb
@@ -11,13 +11,15 @@ module ActionView #:nodoc:
end
def find(*args)
- find_all(*args).first || raise(MissingTemplate.new(self, "#{args[1]}/#{args[0]}", args[3], args[2]))
+ find_all(*args).first || raise(MissingTemplate.new(self, *args))
end
- def find_all(*args)
- each do |resolver|
- templates = resolver.find_all(*args)
- return templates unless templates.empty?
+ def find_all(path, prefixes = [], *args)
+ prefixes.each do |prefix|
+ each do |resolver|
+ templates = resolver.find_all(path, prefix, *args)
+ return templates unless templates.empty?
+ end
end
[]
end
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 317479ad4c..94c0a8a8fb 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -108,11 +108,11 @@ module ActionView
locals << @variable_counter if @collection
find_template(path, locals)
end
- end
+ end
def find_template(path=@path, locals=@locals.keys)
- prefix = @view.controller_prefix unless path.include?(?/)
- @lookup_context.find_template(path, prefix, true, locals)
+ prefixes = path.include?(?/) ? [] : @view.controller_prefixes
+ @lookup_context.find_template(path, prefixes, true, locals)
end
def collection_with_template
@@ -152,14 +152,14 @@ module ActionView
object = object.to_model if object.respond_to?(:to_model)
object.class.model_name.partial_path.dup.tap do |partial|
- path = @view.controller_prefix
+ path = @view.controller_prefixes.first
partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
end
end
end
def retrieve_variable(path)
- variable = @options[:as] || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ variable = @options[:as].try(:to_sym) || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
variable_counter = :"#{variable}_counter" if @collection
[variable, variable_counter]
end
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index ece3f621b6..9ae1636131 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -22,14 +22,14 @@ module ActionView
def render_once(options)
paths, locals = options[:once], options[:locals] || {}
layout, keys = options[:layout], locals.keys
- prefix = options.fetch(:prefix, @view.controller_prefix)
+ prefixes = options.fetch(:prefixes, @view.controller_prefixes)
raise "render :once expects a String or an Array to be given" unless paths
render_with_layout(layout, locals) do
contents = []
Array.wrap(paths).each do |path|
- template = find_template(path, prefix, false, keys)
+ template = find_template(path, prefixes, false, keys)
contents << render_template(template, nil, locals) if @rendered.add?(template)
end
contents.join("\n")
@@ -43,13 +43,13 @@ module ActionView
if options.key?(:text)
Template::Text.new(options[:text], formats.try(:first))
elsif options.key?(:file)
- with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
+ with_fallbacks { find_template(options[:file], options[:prefixes], false, keys) }
elsif options.key?(:inline)
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) ?
- options[:template] : find_template(options[:template], options[:prefix], false, keys)
+ options[:template] : find_template(options[:template], options[:prefixes], false, keys)
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 0d8373ef14..96d506fac5 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -123,7 +123,7 @@ module ActionView
@locals = details[:locals] || []
@virtual_path = details[:virtual_path]
@updated_at = details[:updated_at] || Time.now
- @formats = Array.wrap(format).map(&:to_sym)
+ @formats = Array.wrap(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
end
# Render a template. If the template was not compiled yet, it is done
@@ -163,7 +163,7 @@ module ActionView
name = pieces.pop
partial = !!name.sub!(/^_/, "")
lookup.disable_cache do
- lookup.find_template(name, pieces.join('/'), partial, @locals)
+ lookup.find_template(name, [ pieces.join('/') ], partial, @locals)
end
end
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index ff256738a9..e246646963 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -27,7 +27,7 @@ module ActionView
class MissingTemplate < ActionViewError #:nodoc:
attr_reader :path
- def initialize(paths, path, details, partial)
+ def initialize(paths, path, prefixes, partial, details, *)
@path = path
display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ")
template_type = if partial
@@ -38,7 +38,11 @@ module ActionView
'template'
end
- super("Missing #{template_type} #{path} with #{details.inspect} in view paths #{display_paths}")
+ searched_paths = prefixes.map { |prefix| [prefix, path].join("/") }
+
+ out = "Missing #{template_type} #{searched_paths.join(", ")} with #{details.inspect}. Searched in:\n"
+ out += paths.compact.map { |p| " * #{p.to_s.inspect}\n" }.join
+ super out
end
end
@@ -52,6 +56,7 @@ module ActionView
attr_reader :original_exception, :backtrace
def initialize(template, assigns, original_exception)
+ super(original_exception.message)
@template, @assigns, @original_exception = template, assigns.dup, original_exception
@sub_templates = nil
@backtrace = original_exception.backtrace
@@ -61,10 +66,6 @@ module ActionView
@template.identifier
end
- def message
- ActiveSupport::Deprecation.silence { original_exception.message }
- end
-
def sub_template_message
if @sub_templates
"Trace of template inclusion: " +
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 0dccc99d14..4d999fb3b2 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -47,7 +47,7 @@ module ActionView
path
end
- # Hnadles templates caching. If a key is given and caching is on
+ # Handles templates caching. If a key is given and caching is on
# always check the cache before hitting the resolver. Otherwise,
# it always hits the resolver but check if the resolver is fresher
# before returning it.
@@ -109,18 +109,27 @@ module ActionView
def query(path, exts, formats)
query = File.join(@path, path)
- exts.each do |ext|
- query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << ',}'
- end
+ query << exts.map { |ext|
+ "{#{ext.compact.map { |e| ".#{e}" }.join(',')},}"
+ }.join
- Dir[query].reject { |p| File.directory?(p) }.map do |p|
- handler, format = extract_handler_and_format(p, formats)
+ query.gsub!(/\{\.html,/, "{.html,.text.html,")
+ query.gsub!(/\{\.text,/, "{.text,.text.plain,")
+
+ templates = []
+ sanitizer = Hash.new { |h,k| h[k] = Dir["#{File.dirname(k)}/*"] }
+ Dir[query].each do |p|
+ next if File.directory?(p) || !sanitizer[p].include?(p)
+
+ handler, format = extract_handler_and_format(p, formats)
contents = File.open(p, "rb") {|io| io.read }
- Template.new(contents, File.expand_path(p), handler,
+ templates << Template.new(contents, File.expand_path(p), handler,
:virtual_path => path, :format => format, :updated_at => mtime(p))
end
+
+ templates
end
# Returns the file mtime from the filesystem.
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 4026f7a40e..2ce109ea99 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -156,10 +156,8 @@ module ActionView
# The instance of ActionView::Base that is used by +render+.
def view
@view ||= begin
- view = ActionView::Base.new(ActionController::Base.view_paths, {}, @controller)
+ view = @controller.view_context
view.singleton_class.send :include, _helpers
- view.singleton_class.send :include, @controller._routes.url_helpers
- view.singleton_class.send :delegate, :alert, :notice, :to => "request.flash"
view.extend(Locals)
view.locals = self.locals
view.output_buffer = self.output_buffer
@@ -171,6 +169,7 @@ module ActionView
INTERNAL_IVARS = %w{
@__name__
+ @__io__
@_assertion_wrapped
@_assertions
@_result
diff --git a/actionpack/lib/action_view/testing/resolvers.rb b/actionpack/lib/action_view/testing/resolvers.rb
index 55583096e0..5c5cab7c7d 100644
--- a/actionpack/lib/action_view/testing/resolvers.rb
+++ b/actionpack/lib/action_view/testing/resolvers.rb
@@ -13,7 +13,11 @@ module ActionView #:nodoc:
@hash = hash
end
- private
+ def to_s
+ @hash.keys.join(', ')
+ end
+
+ private
def query(path, exts, formats)
query = ""