mirror of
https://github.com/github/rails.git
synced 2026-01-09 14:48:01 -05:00
Merge remote branch 'rails/master' into identity_map
Conflicts: activerecord/examples/performance.rb activerecord/lib/active_record/association_preload.rb activerecord/lib/active_record/associations.rb activerecord/lib/active_record/associations/association_proxy.rb activerecord/lib/active_record/autosave_association.rb activerecord/lib/active_record/base.rb activerecord/lib/active_record/nested_attributes.rb activerecord/test/cases/relations_test.rb
This commit is contained in:
21
Gemfile
21
Gemfile
@@ -9,23 +9,20 @@ else
|
||||
end
|
||||
|
||||
gem "rack", :git => "git://github.com/rack/rack.git"
|
||||
gem "rack-test", :git => "git://github.com/brynary/rack-test.git"
|
||||
|
||||
gem "rake", ">= 0.8.7"
|
||||
gem "mocha", ">= 0.9.8"
|
||||
gem "rdoc", ">= 2.5.10"
|
||||
gem "horo", ">= 1.0.2"
|
||||
|
||||
# for perf tests
|
||||
gem "faker"
|
||||
gem "rbench"
|
||||
gem "addressable"
|
||||
group :doc do
|
||||
gem "rdoc", "~> 3.4"
|
||||
gem "horo", "= 1.0.3"
|
||||
gem "RedCloth", "~> 4.2" if RUBY_VERSION < "1.9.3"
|
||||
end
|
||||
|
||||
# AS
|
||||
gem "memcache-client", ">= 1.8.5"
|
||||
|
||||
# AM
|
||||
gem "text-format", "~> 1.0.0"
|
||||
|
||||
platforms :mri_18 do
|
||||
gem "system_timer"
|
||||
gem "ruby-debug", ">= 0.10.3"
|
||||
@@ -34,7 +31,7 @@ end
|
||||
|
||||
platforms :mri_19 do
|
||||
# TODO: Remove the conditional when ruby-debug19 supports Ruby >= 1.9.3
|
||||
gem "ruby-debug19" if RUBY_VERSION < "1.9.3"
|
||||
gem "ruby-debug19", :require => 'ruby-debug' if RUBY_VERSION < "1.9.3"
|
||||
end
|
||||
|
||||
platforms :ruby do
|
||||
@@ -43,12 +40,12 @@ platforms :ruby do
|
||||
gem "nokogiri", ">= 1.4.4"
|
||||
|
||||
# AR
|
||||
gem "sqlite3-ruby", "~> 1.3.1", :require => 'sqlite3'
|
||||
gem "sqlite3", "~> 1.3.3"
|
||||
|
||||
group :db do
|
||||
gem "pg", ">= 0.9.0"
|
||||
gem "mysql", ">= 2.8.1"
|
||||
gem "mysql2", ">= 0.2.6"
|
||||
gem "mysql2", :git => "git://github.com/brianmario/mysql2.git"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ more separate. Each of these packages can be used independently outside of
|
||||
* The README file created within your application.
|
||||
* The {Getting Started with Rails}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {Ruby on Rails Tutorial}[http://railstutorial.org/book].
|
||||
* The {Ruby on Rails guides}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {API documentation}[http://api.rubyonrails.org].
|
||||
* The {Ruby on Rails Guides}[http://guides.rubyonrails.org].
|
||||
* The {API Documentation}[http://api.rubyonrails.org].
|
||||
|
||||
|
||||
== Contributing
|
||||
|
||||
29
Rakefile
29
Rakefile
@@ -1,6 +1,4 @@
|
||||
#!/usr/bin/env rake
|
||||
gem 'rdoc', '>= 2.5.10'
|
||||
require 'rdoc'
|
||||
|
||||
require 'rdoc/task'
|
||||
require 'net/http'
|
||||
@@ -14,31 +12,6 @@ task :build => "all:build"
|
||||
desc "Release all gems to gemcutter and create a tag"
|
||||
task :release => "all:release"
|
||||
|
||||
# RDoc skips some files in the Rails tree due to its binary? predicate. This is a quick
|
||||
# hack for edge docs, until we decide which is the correct way to address this issue.
|
||||
# If not fixed in RDoc itself, via an option or something, we should probably move this
|
||||
# to railties and use it also in doc:rails.
|
||||
def hijack_rdoc!
|
||||
require "rdoc/parser"
|
||||
class << RDoc::Parser
|
||||
def binary?(file)
|
||||
s = File.read(file, 1024) or return false
|
||||
|
||||
if s[0, 2] == Marshal.dump('')[0, 2] then
|
||||
true
|
||||
elsif file =~ /erb\.rb$/ then
|
||||
false
|
||||
elsif s.index("\x00") then # ORIGINAL is s.scan(/<%|%>/).length >= 4 || s.index("\x00")
|
||||
true
|
||||
elsif 0.respond_to? :fdiv then
|
||||
s.count("^ -~\t\r\n").fdiv(s.size) > 0.3
|
||||
else # HACK 1.8.6
|
||||
(s.count("^ -~\t\r\n").to_f / s.size) > 0.3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties)
|
||||
|
||||
desc 'Run all tests by default'
|
||||
@@ -76,8 +49,6 @@ end
|
||||
|
||||
desc "Generate documentation for the Rails framework"
|
||||
RDoc::Task.new do |rdoc|
|
||||
hijack_rdoc!
|
||||
|
||||
rdoc.rdoc_dir = 'doc/rdoc'
|
||||
rdoc.title = "Ruby on Rails Documentation"
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ generated would look like this:
|
||||
|
||||
Mr. david@loudthinking.com
|
||||
|
||||
In previous version of rails you would call <tt>create_method_name</tt> and
|
||||
In previous version of Rails you would call <tt>create_method_name</tt> and
|
||||
<tt>deliver_method_name</tt>. Rails 3.0 has a much simpler interface, you
|
||||
simply call the method and optionally call +deliver+ on the return value.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace :test do
|
||||
task :isolated do
|
||||
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
|
||||
Dir.glob("test/**/*_test.rb").all? do |file|
|
||||
system(ruby, '-Ilib:test', file)
|
||||
sh(ruby, '-Ilib:test', file)
|
||||
end or raise "Failures"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,5 +20,5 @@ Gem::Specification.new do |s|
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('actionpack', version)
|
||||
s.add_dependency('mail', '~> 2.2.9')
|
||||
s.add_dependency('mail', '~> 2.2.15')
|
||||
end
|
||||
|
||||
@@ -246,7 +246,7 @@ module ActionMailer #:nodoc:
|
||||
# but Action Mailer translates them appropriately and sets the correct values.
|
||||
#
|
||||
# As you can pass in any header, you need to either quote the header as a string, or pass it in as
|
||||
# an underscorised symbol, so the following will work:
|
||||
# an underscored symbol, so the following will work:
|
||||
#
|
||||
# class Notifier < ActionMailer::Base
|
||||
# default 'Content-Transfer-Encoding' => '7bit',
|
||||
@@ -298,7 +298,7 @@ module ActionMailer #:nodoc:
|
||||
#
|
||||
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
|
||||
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
|
||||
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@addres</tt>
|
||||
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@address</tt>
|
||||
# added automatically before the message is sent.
|
||||
#
|
||||
# * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
|
||||
@@ -404,7 +404,7 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
def respond_to?(method, *args) #:nodoc:
|
||||
def respond_to?(method, include_private = false) #:nodoc:
|
||||
super || action_methods.include?(method.to_s)
|
||||
end
|
||||
|
||||
@@ -693,15 +693,8 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
|
||||
def each_template(paths, name, &block) #:nodoc:
|
||||
Array.wrap(paths).each do |path|
|
||||
templates = lookup_context.find_all(name, path)
|
||||
templates = templates.uniq_by { |t| t.formats }
|
||||
|
||||
unless templates.empty?
|
||||
templates.each(&block)
|
||||
return
|
||||
end
|
||||
end
|
||||
templates = lookup_context.find_all(name, Array.wrap(paths))
|
||||
templates.uniq_by { |t| t.formats }.each(&block)
|
||||
end
|
||||
|
||||
def create_parts_from_responses(m, responses) #:nodoc:
|
||||
|
||||
@@ -3,17 +3,8 @@ module ActionMailer
|
||||
# Uses Text::Format to take the text and format it, indented two spaces for
|
||||
# each line, and wrapped at 72 columns.
|
||||
def block_format(text)
|
||||
begin
|
||||
require 'text/format'
|
||||
rescue LoadError => e
|
||||
$stderr.puts "You don't have text-format installed in your application. Please add it to your Gemfile and run bundle install"
|
||||
raise e
|
||||
end unless defined?(Text::Format)
|
||||
|
||||
formatted = text.split(/\n\r\n/).collect { |paragraph|
|
||||
Text::Format.new(
|
||||
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
|
||||
).format
|
||||
simple_format(paragraph)
|
||||
}.join("\n")
|
||||
|
||||
# Make list points stand on their own line
|
||||
@@ -37,5 +28,22 @@ module ActionMailer
|
||||
def attachments
|
||||
@_message.attachments
|
||||
end
|
||||
|
||||
private
|
||||
def simple_format(text, len = 72, indent = 2)
|
||||
sentences = [[]]
|
||||
|
||||
text.split.each do |word|
|
||||
if (sentences.last + [word]).join(' ').length > len
|
||||
sentences << [word]
|
||||
else
|
||||
sentences.last << word
|
||||
end
|
||||
end
|
||||
|
||||
sentences.map { |sentence|
|
||||
"#{" " * indent}#{sentence.join(' ')}"
|
||||
}.join "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -201,7 +201,7 @@ module ActionMailer
|
||||
if String === @body
|
||||
@parts.unshift create_inline_part(@body)
|
||||
elsif @parts.empty? || @parts.all? { |p| p.content_disposition =~ /^attachment/ }
|
||||
lookup_context.find_all(@template, @mailer_name).each do |template|
|
||||
lookup_context.find_all(@template, [@mailer_name]).each do |template|
|
||||
self.formats = template.formats
|
||||
@parts << create_inline_part(render(:template => template), template.mime_type)
|
||||
end
|
||||
@@ -242,12 +242,12 @@ module ActionMailer
|
||||
ct.to_s.split("/")
|
||||
end
|
||||
|
||||
def parse_content_type(defaults=nil)
|
||||
def parse_content_type
|
||||
if @content_type.blank?
|
||||
[ nil, {} ]
|
||||
else
|
||||
ctype, *attrs = @content_type.split(/;\s*/)
|
||||
attrs = Hash[attrs.map { |attr| attr.split(/\=/, 2) }]
|
||||
attrs = Hash[attrs.map { |attr| attr.split(/=/, 2) }]
|
||||
[ctype, {"charset" => @charset}.merge!(attrs)]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,16 +2,18 @@ module Mail
|
||||
class Message
|
||||
|
||||
def set_content_type(*args)
|
||||
ActiveSupport::Deprecation.warn('Message#set_content_type is deprecated, please just call ' <<
|
||||
'Message#content_type with the same arguments', caller[0,2])
|
||||
message = 'Message#set_content_type is deprecated, please just call ' <<
|
||||
'Message#content_type with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
content_type(*args)
|
||||
end
|
||||
|
||||
alias :old_transfer_encoding :transfer_encoding
|
||||
def transfer_encoding(value = nil)
|
||||
if value
|
||||
ActiveSupport::Deprecation.warn('Message#transfer_encoding is deprecated, please call ' <<
|
||||
'Message#content_transfer_encoding with the same arguments', caller[0,2])
|
||||
message = 'Message#transfer_encoding is deprecated, ' <<
|
||||
'please call Message#content_transfer_encoding with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
content_transfer_encoding(value)
|
||||
else
|
||||
old_transfer_encoding
|
||||
@@ -19,16 +21,17 @@ module Mail
|
||||
end
|
||||
|
||||
def transfer_encoding=(value)
|
||||
ActiveSupport::Deprecation.warn('Message#transfer_encoding= is deprecated, please call ' <<
|
||||
'Message#content_transfer_encoding= with the same arguments', caller[0,2])
|
||||
message = 'Message#transfer_encoding= is deprecated, ' <<
|
||||
'please call Message#content_transfer_encoding= with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
self.content_transfer_encoding = value
|
||||
end
|
||||
|
||||
def original_filename
|
||||
ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' <<
|
||||
'please call Message#filename', caller[0,2])
|
||||
message = 'Message#original_filename is deprecated, please call Message#filename'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
filename
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,7 +25,6 @@ end
|
||||
|
||||
silence_warnings do
|
||||
# These external dependencies have warnings :/
|
||||
require 'text/format'
|
||||
require 'mail'
|
||||
end
|
||||
|
||||
@@ -79,4 +78,4 @@ def restore_delivery_method
|
||||
ActionMailer::Base.delivery_method = @old_delivery_method
|
||||
end
|
||||
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
|
||||
4
actionmailer/test/fixtures/i18n_test_mailer/mail_with_i18n_subject.erb
vendored
Normal file
4
actionmailer/test/fixtures/i18n_test_mailer/mail_with_i18n_subject.erb
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>. Be greeted, new member!
|
||||
|
||||
46
actionmailer/test/i18n_with_controller_test.rb
Normal file
46
actionmailer/test/i18n_with_controller_test.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
require 'abstract_unit'
|
||||
require 'action_controller'
|
||||
|
||||
class I18nTestMailer < ActionMailer::Base
|
||||
configure do |c|
|
||||
c.assets_dir = ''
|
||||
end
|
||||
|
||||
def mail_with_i18n_subject(recipient)
|
||||
@recipient = recipient
|
||||
I18n.locale = :de
|
||||
mail(:to => recipient, :subject => "#{I18n.t :email_subject} #{recipient}",
|
||||
:from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
|
||||
end
|
||||
end
|
||||
|
||||
class TestController < ActionController::Base
|
||||
def send_mail
|
||||
I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver
|
||||
render :text => 'Mail sent'
|
||||
end
|
||||
end
|
||||
|
||||
class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
|
||||
Routes = ActionDispatch::Routing::RouteSet.new
|
||||
Routes.draw do
|
||||
match ':controller(/:action(/:id))'
|
||||
end
|
||||
|
||||
def app
|
||||
Routes
|
||||
end
|
||||
|
||||
def setup
|
||||
I18n.backend.store_translations('de', :email_subject => '[Signed up] Welcome')
|
||||
end
|
||||
|
||||
def teardown
|
||||
I18n.locale = :en
|
||||
end
|
||||
|
||||
def test_send_mail
|
||||
get '/test/send_mail'
|
||||
assert_equal "Mail sent", @response.body
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,14 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
class TmailCompatTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@silence = ActiveSupport::Deprecation.silenced
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = @silence
|
||||
end
|
||||
|
||||
def test_set_content_type_raises_deprecation_warning
|
||||
mail = Mail.new
|
||||
@@ -31,5 +39,4 @@ class TmailCompatTest < ActiveSupport::TestCase
|
||||
end
|
||||
assert_equal mail.content_transfer_encoding, "base64"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
*Rails 3.1.0 (unreleased)*
|
||||
|
||||
* brought back config.action_view.cache_template_loading, which allows to decide whether templates should be cached or not [Piotr Sarnacki]
|
||||
* Add an :authenticity_token option to form_tag for custom handling or to omit the token (pass :authenticity_token => false). [Jakub Kuźma, Igor Wiedler]
|
||||
|
||||
* url_for and named url helpers now accept :subdomain and :domain as options [Josh Kalderimis]
|
||||
* HTML5 button_tag helper. [Rizwan Reza]
|
||||
|
||||
* Template lookup now searches further up in the inheritance chain. [Artemave]
|
||||
|
||||
* Brought back config.action_view.cache_template_loading, which allows to decide whether templates should be cached or not. [Piotr Sarnacki]
|
||||
|
||||
* url_for and named url helpers now accept :subdomain and :domain as options, [Josh Kalderimis]
|
||||
|
||||
* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused (check the documentation for examples). [Josh Kalderimis]
|
||||
|
||||
@@ -31,23 +37,24 @@
|
||||
|
||||
* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply to the browser only. [Yehuda Katz, Carl Lerche]
|
||||
|
||||
|
||||
*Rails 3.0.2 (unreleased)*
|
||||
|
||||
* The helper number_to_currency accepts a new :negative_format option to be able to configure how to render negative amounts. [Don Wilson]
|
||||
|
||||
|
||||
*Rails 3.0.1 (October 15, 2010)*
|
||||
|
||||
* No Changes, just a version bump.
|
||||
|
||||
|
||||
*Rails 3.0.0 (August 29, 2010)*
|
||||
|
||||
* password_field renders with nil value by default making the use of passwords secure by default, if you want to render you should do for instance f.password_field(:password, :value => @user.password) [Santiago Pastorino]
|
||||
|
||||
* Symbols and strings in routes should yield the same behavior. Note this may break existing apps that were using symbols with the new routes API. [José Valim]
|
||||
|
||||
* Add clear_helpers as a way to clean up all helpers added to this controller, maintaing just the helper with the same name as the controller. [José Valim]
|
||||
|
||||
* See http://github.com/rails/rails/compare/v3.0.0_RC...v3.0.0_RC2 for gory details
|
||||
* Add clear_helpers as a way to clean up all helpers added to this controller, maintaining just the helper with the same name as the controller. [José Valim]
|
||||
|
||||
* Support routing constraints in functional tests. [Andrew White]
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ methods:
|
||||
layout "weblog/layout"
|
||||
|
||||
def index
|
||||
@posts = Post.find(:all)
|
||||
@posts = Post.all
|
||||
end
|
||||
|
||||
def show
|
||||
|
||||
@@ -21,11 +21,11 @@ Gem::Specification.new do |s|
|
||||
|
||||
s.add_dependency('activesupport', version)
|
||||
s.add_dependency('activemodel', version)
|
||||
s.add_dependency('rack-cache', '~> 0.5.3')
|
||||
s.add_dependency('rack-cache', '~> 1.0.0')
|
||||
s.add_dependency('builder', '~> 3.0.0')
|
||||
s.add_dependency('i18n', '~> 0.5.0')
|
||||
s.add_dependency('rack', '~> 1.2.1')
|
||||
s.add_dependency('rack-test', '~> 0.5.6')
|
||||
s.add_dependency('rack-test', '~> 0.5.7')
|
||||
s.add_dependency('rack-mount', '~> 0.6.13')
|
||||
s.add_dependency('tzinfo', '~> 0.3.23')
|
||||
s.add_dependency('erubis', '~> 2.6.6')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
end
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -170,7 +170,7 @@ module HTML
|
||||
|
||||
def contains_bad_protocols?(attr_name, value)
|
||||
uri_attributes.include?(attr_name) &&
|
||||
(value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
|
||||
(value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,7 +43,7 @@ module ActionDispatch
|
||||
alias :etag? :etag
|
||||
|
||||
def initialize(*)
|
||||
status, header, body = super
|
||||
super
|
||||
|
||||
@cache_control = {}
|
||||
@etag = self["ETag"]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -43,20 +43,20 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, body = @app.call(env)
|
||||
begin
|
||||
status, headers, body = @app.call(env)
|
||||
exception = nil
|
||||
|
||||
# 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}"
|
||||
# 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
|
||||
|
||||
@@ -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" %>
|
||||
|
||||
@@ -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" %>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
#
|
||||
# match ':controller/:action/:id/:user_id'
|
||||
# # sets :controller, :action and :id in params
|
||||
# match ':controller/:action/:id'
|
||||
#
|
||||
# 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.
|
||||
# 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:
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# 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
|
||||
# === Options
|
||||
#
|
||||
# scope :module => "admin" do
|
||||
# resources :posts
|
||||
# end
|
||||
# Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
|
||||
#
|
||||
# [:path]
|
||||
# If you want to prefix the route, you could use
|
||||
# === Examples
|
||||
#
|
||||
# scope :path => "/admin" do
|
||||
# resources :posts
|
||||
# end
|
||||
# # route /posts (without the prefix /admin) to Admin::PostsController
|
||||
# scope :module => "admin" do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# This will prefix all of the +posts+ resource's requests with '/admin'
|
||||
# # prefix the posts resource's requests with '/admin'
|
||||
# scope :path => "/admin" do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# [:as]
|
||||
# Prefixes the routing helpers in this scope with the specified label.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# 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)
|
||||
# # 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.
|
||||
# The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
|
||||
# options all default to the name of the namespace.
|
||||
#
|
||||
# namespace :admin, :path => "sekret" do
|
||||
# resources :posts
|
||||
# end
|
||||
# For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
|
||||
# <tt>Resources#resources</tt>.
|
||||
#
|
||||
# All routes for the above +resources+ will be accessible through +/sekret/posts+, rather than +/admin/posts+
|
||||
# === Examples
|
||||
#
|
||||
# [:module]
|
||||
# The namespace for the controllers.
|
||||
# # accessible through /sekret/posts rather than /admin/posts
|
||||
# namespace :admin, :path => "sekret" do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# namespace :admin, :module => "sekret" do
|
||||
# resources :posts
|
||||
# end
|
||||
# # maps to Sekret::PostsController rather than Admin::PostsController
|
||||
# namespace :admin, :module => "sekret" do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# The +PostsController+ here should go in the +Sekret+ namespace and so it should be defined like this:
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# Routing helpers such as +admin_posts_path+ will now be +sekret_posts_path+.
|
||||
#
|
||||
# [: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]
|
||||
#
|
||||
# Set a path prefix for this resource.
|
||||
# [:shallow]
|
||||
# Generates shallow routes for nested resource(s). When placed on a parent resource,
|
||||
# generates shallow routes for all nested resources.
|
||||
#
|
||||
# resources :posts, :path => "admin"
|
||||
# resources :posts, :shallow => true do
|
||||
# resources :comments
|
||||
# end
|
||||
#
|
||||
# All actions for this resource will now be at +/admin/posts+.
|
||||
# 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)
|
||||
#
|
||||
# === Examples
|
||||
#
|
||||
# # routes call Admin::PostsController
|
||||
# resources :posts, :module => "admin"
|
||||
#
|
||||
# # 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
end
|
||||
|
||||
@@ -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
|
||||
end
|
||||
|
||||
@@ -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.
|
||||
# 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.
|
||||
#
|
||||
# See ActionController::Caching::Fragments for usage instructions.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# ==== 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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 => "✓".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
|
||||
|
||||
|
||||
38
actionpack/lib/action_view/helpers/output_safety_helper.rb
Normal file
38
actionpack/lib/action_view/helpers/output_safety_helper.rb
Normal file
@@ -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><br /><p>bar</p>"
|
||||
#
|
||||
# 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -459,7 +459,7 @@ module ActionView
|
||||
end
|
||||
|
||||
AUTO_LINK_RE = %r{
|
||||
(?: ([\w+.:-]+:)// | www\. )
|
||||
(?: ([0-9A-Za-z+.:-]+:)// | www\. )
|
||||
[^\s<]+
|
||||
}x
|
||||
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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
|
||||
end
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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: " +
|
||||
|
||||
@@ -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
|
||||
|
||||
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)
|
||||
|
||||
Dir[query].reject { |p| File.directory?(p) }.map do |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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
@@ -30,14 +30,14 @@ module AbstractController
|
||||
class RenderingController < AbstractController::Base
|
||||
include ::AbstractController::Rendering
|
||||
|
||||
def _prefix() end
|
||||
def _prefixes
|
||||
[]
|
||||
end
|
||||
|
||||
def render(options = {})
|
||||
if options.is_a?(String)
|
||||
options = {:_template_name => options}
|
||||
end
|
||||
|
||||
options[:_prefix] = _prefix
|
||||
super
|
||||
end
|
||||
|
||||
@@ -116,8 +116,8 @@ module AbstractController
|
||||
name.underscore
|
||||
end
|
||||
|
||||
def _prefix
|
||||
self.class.prefix
|
||||
def _prefixes
|
||||
[self.class.prefix]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -157,10 +157,10 @@ module AbstractController
|
||||
private
|
||||
def self.layout(formats)
|
||||
begin
|
||||
find_template(name.underscore, {:formats => formats}, :_prefix => "layouts")
|
||||
find_template(name.underscore, {:formats => formats}, :_prefixes => ["layouts"])
|
||||
rescue ActionView::MissingTemplate
|
||||
begin
|
||||
find_template("application", {:formats => formats}, :_prefix => "layouts")
|
||||
find_template("application", {:formats => formats}, :_prefixes => ["layouts"])
|
||||
rescue ActionView::MissingTemplate
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,7 +22,7 @@ module AbstractController
|
||||
class TestCallbacks1 < ActiveSupport::TestCase
|
||||
test "basic callbacks work" do
|
||||
controller = Callback1.new
|
||||
result = controller.process(:index)
|
||||
controller.process(:index)
|
||||
assert_equal "Hello world", controller.response_body
|
||||
end
|
||||
end
|
||||
@@ -62,7 +62,7 @@ module AbstractController
|
||||
end
|
||||
|
||||
test "before_filter works" do
|
||||
result = @controller.process(:index)
|
||||
@controller.process(:index)
|
||||
assert_equal "Hello world", @controller.response_body
|
||||
end
|
||||
|
||||
@@ -78,7 +78,7 @@ module AbstractController
|
||||
|
||||
test "before_filter with overwritten condition" do
|
||||
@controller = Callback2Overwrite.new
|
||||
result = @controller.process(:index)
|
||||
@controller.process(:index)
|
||||
assert_equal "", @controller.response_body
|
||||
end
|
||||
end
|
||||
@@ -103,12 +103,12 @@ module AbstractController
|
||||
end
|
||||
|
||||
test "before_filter works with procs" do
|
||||
result = @controller.process(:index)
|
||||
@controller.process(:index)
|
||||
assert_equal "Hello world", @controller.response_body
|
||||
end
|
||||
|
||||
test "after_filter works with procs" do
|
||||
result = @controller.process(:index)
|
||||
@controller.process(:index)
|
||||
assert_equal "Goodbye", @controller.instance_variable_get("@second")
|
||||
end
|
||||
end
|
||||
@@ -152,7 +152,7 @@ module AbstractController
|
||||
end
|
||||
|
||||
test "when :except is specified, an after filter is not triggered on that action" do
|
||||
result = @controller.process(:index)
|
||||
@controller.process(:index)
|
||||
assert !@controller.instance_variable_defined?("@authenticated")
|
||||
end
|
||||
end
|
||||
@@ -186,17 +186,17 @@ module AbstractController
|
||||
end
|
||||
|
||||
test "when :only is specified with an array, a before filter is triggered on that action" do
|
||||
result = @controller.process(:index)
|
||||
@controller.process(:index)
|
||||
assert_equal "Hello, World", @controller.response_body
|
||||
end
|
||||
|
||||
test "when :only is specified with an array, a before filter is not triggered on other actions" do
|
||||
result = @controller.process(:sekrit_data)
|
||||
@controller.process(:sekrit_data)
|
||||
assert_equal "true", @controller.response_body
|
||||
end
|
||||
|
||||
test "when :except is specified with an array, an after filter is not triggered on that action" do
|
||||
result = @controller.process(:index)
|
||||
@controller.process(:index)
|
||||
assert !@controller.instance_variable_defined?("@authenticated")
|
||||
end
|
||||
end
|
||||
@@ -216,12 +216,12 @@ module AbstractController
|
||||
end
|
||||
|
||||
test "when a callback is modified in a child with :only, it works for the :only action" do
|
||||
result = @controller.process(:index)
|
||||
@controller.process(:index)
|
||||
assert_equal "Hello world", @controller.response_body
|
||||
end
|
||||
|
||||
test "when a callback is modified in a child with :only, it does not work for other actions" do
|
||||
result = @controller.process(:not_index)
|
||||
@controller.process(:not_index)
|
||||
assert_equal "", @controller.response_body
|
||||
end
|
||||
end
|
||||
@@ -246,5 +246,26 @@ module AbstractController
|
||||
end
|
||||
end
|
||||
|
||||
class CallbacksWithArgs < ControllerWithCallbacks
|
||||
set_callback :process_action, :before, :first
|
||||
|
||||
def first
|
||||
@text = "Hello world"
|
||||
end
|
||||
|
||||
def index(text)
|
||||
self.response_body = @text + text
|
||||
end
|
||||
end
|
||||
|
||||
class TestCallbacksWithArgs < ActiveSupport::TestCase
|
||||
test "callbacks still work when invoking process with multiple args" do
|
||||
controller = CallbacksWithArgs.new
|
||||
controller.process(:index, " Howdy!")
|
||||
assert_equal "Hello world Howdy!", controller.response_body
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,8 +6,8 @@ module AbstractController
|
||||
class ControllerRenderer < AbstractController::Base
|
||||
include AbstractController::Rendering
|
||||
|
||||
def _prefix
|
||||
"renderer"
|
||||
def _prefixes
|
||||
%w[renderer]
|
||||
end
|
||||
|
||||
self.view_paths = [ActionView::FixtureResolver.new(
|
||||
|
||||
@@ -156,6 +156,17 @@ class PageCachingTest < ActionController::TestCase
|
||||
assert_page_not_cached :ok
|
||||
end
|
||||
|
||||
def test_page_caching_directory_set_as_pathname
|
||||
begin
|
||||
ActionController::Base.page_cache_directory = Pathname.new(FILE_STORE_PATH)
|
||||
get :ok
|
||||
assert_response :ok
|
||||
assert_page_cached :ok
|
||||
ensure
|
||||
ActionController::Base.page_cache_directory = FILE_STORE_PATH
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def assert_page_cached(action, message = "#{action} should have been cached")
|
||||
assert page_cached?(action), message
|
||||
@@ -257,7 +268,6 @@ class ActionCachingMockController
|
||||
end
|
||||
|
||||
def request
|
||||
mocked_path = @mock_path
|
||||
Object.new.instance_eval(<<-EVAL)
|
||||
def path; '#{@mock_path}' end
|
||||
def format; 'all' end
|
||||
@@ -416,7 +426,6 @@ class ActionCacheTest < ActionController::TestCase
|
||||
|
||||
get :index
|
||||
assert_response :success
|
||||
new_cached_time = content_to_cache
|
||||
assert_not_equal cached_time, @response.body
|
||||
end
|
||||
|
||||
|
||||
@@ -664,7 +664,7 @@ class FilterTest < ActionController::TestCase
|
||||
end
|
||||
|
||||
def test_prepending_and_appending_around_filter
|
||||
controller = test_process(MixedFilterController)
|
||||
test_process(MixedFilterController)
|
||||
assert_equal " before aroundfilter before procfilter before appended aroundfilter " +
|
||||
" after appended aroundfilter after procfilter after aroundfilter ",
|
||||
MixedFilterController.execution_log
|
||||
@@ -677,26 +677,26 @@ class FilterTest < ActionController::TestCase
|
||||
end
|
||||
|
||||
def test_before_filter_rendering_breaks_filtering_chain_for_after_filter
|
||||
response = test_process(RenderingController)
|
||||
test_process(RenderingController)
|
||||
assert_equal %w( before_filter_rendering ), assigns["ran_filter"]
|
||||
assert !assigns["ran_action"]
|
||||
end
|
||||
|
||||
def test_before_filter_redirects_breaks_filtering_chain_for_after_filter
|
||||
response = test_process(BeforeFilterRedirectionController)
|
||||
test_process(BeforeFilterRedirectionController)
|
||||
assert_response :redirect
|
||||
assert_equal "http://test.host/filter_test/before_filter_redirection/target_of_redirection", redirect_to_url
|
||||
assert_equal %w( before_filter_redirects ), assigns["ran_filter"]
|
||||
end
|
||||
|
||||
def test_before_filter_rendering_breaks_filtering_chain_for_preprend_after_filter
|
||||
response = test_process(RenderingForPrependAfterFilterController)
|
||||
test_process(RenderingForPrependAfterFilterController)
|
||||
assert_equal %w( before_filter_rendering ), assigns["ran_filter"]
|
||||
assert !assigns["ran_action"]
|
||||
end
|
||||
|
||||
def test_before_filter_redirects_breaks_filtering_chain_for_preprend_after_filter
|
||||
response = test_process(BeforeFilterRedirectionForPrependAfterFilterController)
|
||||
test_process(BeforeFilterRedirectionForPrependAfterFilterController)
|
||||
assert_response :redirect
|
||||
assert_equal "http://test.host/filter_test/before_filter_redirection_for_prepend_after_filter/target_of_redirection", redirect_to_url
|
||||
assert_equal %w( before_filter_redirects ), assigns["ran_filter"]
|
||||
|
||||
@@ -32,6 +32,11 @@ module Another
|
||||
cache_page("Super soaker", "/index.html")
|
||||
render :nothing => true
|
||||
end
|
||||
|
||||
def with_exception
|
||||
raise Exception
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -139,20 +144,20 @@ class ACLogSubscriberTest < ActionController::TestCase
|
||||
wait
|
||||
|
||||
assert_equal 4, logs.size
|
||||
assert_match(/Exist fragment\? views\/foo/, logs[1])
|
||||
assert_match(/Read fragment views\/foo/, logs[1])
|
||||
assert_match(/Write fragment views\/foo/, logs[2])
|
||||
ensure
|
||||
@controller.config.perform_caching = true
|
||||
end
|
||||
|
||||
|
||||
def test_with_fragment_cache_and_percent_in_key
|
||||
@controller.config.perform_caching = true
|
||||
get :with_fragment_cache_and_percent_in_key
|
||||
wait
|
||||
|
||||
assert_equal 4, logs.size
|
||||
assert_match(/Exist fragment\? views\/foo%bar/, logs[1])
|
||||
assert_match(/Write fragment views\/foo%bar/, logs[2])
|
||||
assert_match(/Read fragment views\/foo/, logs[1])
|
||||
assert_match(/Write fragment views\/foo/, logs[2])
|
||||
ensure
|
||||
@controller.config.perform_caching = true
|
||||
end
|
||||
@@ -169,6 +174,16 @@ class ACLogSubscriberTest < ActionController::TestCase
|
||||
@controller.config.perform_caching = true
|
||||
end
|
||||
|
||||
def test_process_action_with_exception_includes_http_status_code
|
||||
begin
|
||||
get :with_exception
|
||||
wait
|
||||
rescue Exception
|
||||
end
|
||||
assert_equal 2, logs.size
|
||||
assert_match(/Completed 500/, logs.last)
|
||||
end
|
||||
|
||||
def logs
|
||||
@logs ||= @logger.logged(:info)
|
||||
end
|
||||
|
||||
@@ -565,7 +565,7 @@ class RespondWithController < ActionController::Base
|
||||
|
||||
def using_resource_with_action
|
||||
respond_with(resource, :action => :foo) do |format|
|
||||
format.html { raise ActionView::MissingTemplate.new([], "foo/bar", {}, false) }
|
||||
format.html { raise ActionView::MissingTemplate.new([], "bar", ["foo"], {}, false) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -658,7 +658,7 @@ class RespondWithControllerTest < ActionController::TestCase
|
||||
@request.accept = "application/json"
|
||||
get :using_hash_resource
|
||||
assert_equal "application/json", @response.content_type
|
||||
assert_equal %Q[{"result":["david",13]}], @response.body
|
||||
assert_equal %Q[{"result":{"name":"david","id":13}}], @response.body
|
||||
end
|
||||
|
||||
def test_using_resource_with_block
|
||||
|
||||
@@ -35,7 +35,7 @@ module BareMetalTest
|
||||
|
||||
class HeadTest < ActiveSupport::TestCase
|
||||
test "head works on its own" do
|
||||
status, headers, body = HeadController.action(:index).call(Rack::MockRequest.env_for("/"))
|
||||
status = HeadController.action(:index).call(Rack::MockRequest.env_for("/")).first
|
||||
assert_equal 404, status
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,6 +7,10 @@ module ContentNegotiation
|
||||
self.view_paths = [ActionView::FixtureResolver.new(
|
||||
"content_negotiation/basic/hello.html.erb" => "Hello world <%= request.formats.first.to_s %>!"
|
||||
)]
|
||||
|
||||
def all
|
||||
render :text => self.formats.inspect
|
||||
end
|
||||
end
|
||||
|
||||
class TestContentNegotiation < Rack::TestCase
|
||||
@@ -14,5 +18,10 @@ module ContentNegotiation
|
||||
get "/content_negotiation/basic/hello", {}, "HTTP_ACCEPT" => "*/*"
|
||||
assert_body "Hello world */*!"
|
||||
end
|
||||
|
||||
test "Not all mimes are converted to symbol" do
|
||||
get "/content_negotiation/basic/all", {}, "HTTP_ACCEPT" => "text/plain, mime/another"
|
||||
assert_body '[:text, "mime/another"]'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,8 +18,8 @@ module RenderTemplate
|
||||
|
||||
self.view_paths = [RESOLVER]
|
||||
|
||||
def _prefix
|
||||
"test"
|
||||
def _prefixes
|
||||
%w(test)
|
||||
end
|
||||
|
||||
def multiple
|
||||
@@ -39,11 +39,11 @@ module RenderTemplate
|
||||
end
|
||||
|
||||
def with_prefix
|
||||
render :once => "result", :prefix => "other"
|
||||
render :once => "result", :prefixes => %w(other)
|
||||
end
|
||||
|
||||
def with_nil_prefix
|
||||
render :once => "test/result", :prefix => nil
|
||||
render :once => "test/result", :prefixes => []
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ module RenderPartial
|
||||
"render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>",
|
||||
"render_partial/basic/with_json.html.erb" => "<%= render 'with_json.json' %>",
|
||||
"render_partial/basic/_with_json.json.erb" => "<%= render 'final' %>",
|
||||
"render_partial/basic/_final.json.erb" => "{ final: json }"
|
||||
"render_partial/basic/_final.json.erb" => "{ final: json }",
|
||||
"render_partial/basic/overriden.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'overriden' %><%= @test_unchanged %>",
|
||||
"render_partial/basic/_overriden.html.erb" => "ParentPartial!",
|
||||
"render_partial/child/_overriden.html.erb" => "OverridenPartial!"
|
||||
)]
|
||||
|
||||
def html_with_json_inside_json
|
||||
@@ -20,7 +23,13 @@ module RenderPartial
|
||||
@test_unchanged = 'hello'
|
||||
render :action => "basic"
|
||||
end
|
||||
|
||||
def overriden
|
||||
@test_unchanged = 'hello'
|
||||
end
|
||||
end
|
||||
|
||||
class ChildController < BasicController; end
|
||||
|
||||
class TestPartial < Rack::TestCase
|
||||
testing BasicController
|
||||
@@ -37,4 +46,18 @@ module RenderPartial
|
||||
end
|
||||
end
|
||||
|
||||
class TestInheritedPartial < Rack::TestCase
|
||||
testing ChildController
|
||||
|
||||
test "partial from parent controller gets picked if missing in child one" do
|
||||
get :changing
|
||||
assert_response("goodbyeBasicPartial!goodbye")
|
||||
end
|
||||
|
||||
test "partial from child controller gets picked" do
|
||||
get :overriden
|
||||
assert_response("goodbyeOverridenPartial!goodbye")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -6,7 +6,11 @@ module Render
|
||||
"render/blank_render/index.html.erb" => "Hello world!",
|
||||
"render/blank_render/access_request.html.erb" => "The request: <%= request.method.to_s.upcase %>",
|
||||
"render/blank_render/access_action_name.html.erb" => "Action Name: <%= action_name %>",
|
||||
"render/blank_render/access_controller_name.html.erb" => "Controller Name: <%= controller_name %>"
|
||||
"render/blank_render/access_controller_name.html.erb" => "Controller Name: <%= controller_name %>",
|
||||
"render/blank_render/overriden_with_own_view_paths_appended.html.erb" => "parent content",
|
||||
"render/blank_render/overriden_with_own_view_paths_prepended.html.erb" => "parent content",
|
||||
"render/blank_render/overriden.html.erb" => "parent content",
|
||||
"render/child_render/overriden.html.erb" => "child content"
|
||||
)]
|
||||
|
||||
def index
|
||||
@@ -21,6 +25,15 @@ module Render
|
||||
render :action => "access_action_name"
|
||||
end
|
||||
|
||||
def overriden_with_own_view_paths_appended
|
||||
end
|
||||
|
||||
def overriden_with_own_view_paths_prepended
|
||||
end
|
||||
|
||||
def overriden
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def secretz
|
||||
@@ -35,6 +48,11 @@ module Render
|
||||
end
|
||||
end
|
||||
|
||||
class ChildRenderController < BlankRenderController
|
||||
append_view_path ActionView::FixtureResolver.new("render/child_render/overriden_with_own_view_paths_appended.html.erb" => "child content")
|
||||
prepend_view_path ActionView::FixtureResolver.new("render/child_render/overriden_with_own_view_paths_prepended.html.erb" => "child content")
|
||||
end
|
||||
|
||||
class RenderTest < Rack::TestCase
|
||||
test "render with blank" do
|
||||
with_routing do |set|
|
||||
@@ -94,4 +112,26 @@ module Render
|
||||
assert_body "Controller Name: blank_render"
|
||||
end
|
||||
end
|
||||
|
||||
class TestViewInheritance < Rack::TestCase
|
||||
test "Template from child controller gets picked over parent one" do
|
||||
get "/render/child_render/overriden"
|
||||
assert_body "child content"
|
||||
end
|
||||
|
||||
test "Template from child controller with custom view_paths prepended gets picked over parent one" do
|
||||
get "/render/child_render/overriden_with_own_view_paths_prepended"
|
||||
assert_body "child content"
|
||||
end
|
||||
|
||||
test "Template from child controller with custom view_paths appended gets picked over parent one" do
|
||||
get "/render/child_render/overriden_with_own_view_paths_appended"
|
||||
assert_body "child content"
|
||||
end
|
||||
|
||||
test "Template from parent controller gets picked if missing in child controller" do
|
||||
get "/render/child_render/index"
|
||||
assert_body "Hello world!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,6 +26,10 @@ class RenderJsonTest < ActionController::TestCase
|
||||
render :json => nil
|
||||
end
|
||||
|
||||
def render_json_render_to_string
|
||||
render :text => render_to_string(:json => '[]')
|
||||
end
|
||||
|
||||
def render_json_hello_world
|
||||
render :json => ActiveSupport::JSON.encode(:hello => 'world')
|
||||
end
|
||||
@@ -76,6 +80,12 @@ class RenderJsonTest < ActionController::TestCase
|
||||
assert_equal 'application/json', @response.content_type
|
||||
end
|
||||
|
||||
def test_render_json_render_to_string
|
||||
get :render_json_render_to_string
|
||||
assert_equal '[]', @response.body
|
||||
end
|
||||
|
||||
|
||||
def test_render_json
|
||||
get :render_json_hello_world
|
||||
assert_equal '{"hello":"world"}', @response.body
|
||||
|
||||
@@ -125,6 +125,10 @@ class TestController < ActionController::Base
|
||||
render :action => "hello_world"
|
||||
end
|
||||
|
||||
def render_action_upcased_hello_world
|
||||
render :action => "Hello_world"
|
||||
end
|
||||
|
||||
def render_action_hello_world_as_string
|
||||
render "hello_world"
|
||||
end
|
||||
@@ -742,6 +746,12 @@ class RenderTest < ActionController::TestCase
|
||||
assert_template "test/hello_world"
|
||||
end
|
||||
|
||||
def test_render_action_upcased
|
||||
assert_raise ActionView::MissingTemplate do
|
||||
get :render_action_upcased_hello_world
|
||||
end
|
||||
end
|
||||
|
||||
# :ported:
|
||||
def test_render_action_hello_world_as_string
|
||||
get :render_action_hello_world_as_string
|
||||
|
||||
@@ -29,8 +29,7 @@ class ActionController::TestRequestTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
def test_session_id_different_on_each_call
|
||||
prev_id =
|
||||
assert_not_equal(@request.session_options[:id], ActionController::TestRequest.new.session_options[:id])
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,6 +12,14 @@ module RequestForgeryProtectionActions
|
||||
render :inline => "<%= button_to('New', '/') {} %>"
|
||||
end
|
||||
|
||||
def external_form
|
||||
render :inline => "<%= form_tag('http://farfar.away/form', :authenticity_token => 'external_token') {} %>"
|
||||
end
|
||||
|
||||
def external_form_without_protection
|
||||
render :inline => "<%= form_tag('http://farfar.away/form', :authenticity_token => false) {} %>"
|
||||
end
|
||||
|
||||
def unsafe
|
||||
render :text => 'pwn'
|
||||
end
|
||||
@@ -20,6 +28,14 @@ module RequestForgeryProtectionActions
|
||||
render :inline => "<%= csrf_meta_tags %>"
|
||||
end
|
||||
|
||||
def external_form_for
|
||||
render :inline => "<%= form_for(:some_resource, :authenticity_token => 'external_token') {} %>"
|
||||
end
|
||||
|
||||
def form_for_without_protection
|
||||
render :inline => "<%= form_for(:some_resource, :authenticity_token => false ) {} %>"
|
||||
end
|
||||
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
@@ -29,6 +45,16 @@ class RequestForgeryProtectionController < ActionController::Base
|
||||
protect_from_forgery :only => %w(index meta)
|
||||
end
|
||||
|
||||
class RequestForgeryProtectionControllerUsingOldBehaviour < ActionController::Base
|
||||
include RequestForgeryProtectionActions
|
||||
protect_from_forgery :only => %w(index meta)
|
||||
|
||||
def handle_unverified_request
|
||||
raise(ActionController::InvalidAuthenticityToken)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class FreeCookieController < RequestForgeryProtectionController
|
||||
self.allow_forgery_protection = false
|
||||
|
||||
@@ -51,152 +77,92 @@ end
|
||||
# common test methods
|
||||
|
||||
module RequestForgeryProtectionTests
|
||||
def teardown
|
||||
ActionController::Base.request_forgery_protection_token = nil
|
||||
def setup
|
||||
@token = "cf50faa3fe97702ca1ae"
|
||||
|
||||
ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
|
||||
ActionController::Base.request_forgery_protection_token = :authenticity_token
|
||||
end
|
||||
|
||||
|
||||
def test_should_render_form_with_token_tag
|
||||
get :index
|
||||
assert_not_blocked do
|
||||
get :index
|
||||
end
|
||||
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
|
||||
end
|
||||
|
||||
def test_should_render_button_to_with_token_tag
|
||||
get :show_button
|
||||
assert_not_blocked do
|
||||
get :show_button
|
||||
end
|
||||
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
|
||||
end
|
||||
|
||||
def test_should_allow_get
|
||||
get :index
|
||||
assert_response :success
|
||||
assert_not_blocked { get :index }
|
||||
end
|
||||
|
||||
def test_should_allow_post_without_token_on_unsafe_action
|
||||
post :unsafe
|
||||
assert_response :success
|
||||
assert_not_blocked { post :unsafe }
|
||||
end
|
||||
|
||||
def test_should_not_allow_html_post_without_token
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) { post :index, :format => :html }
|
||||
def test_should_not_allow_post_without_token
|
||||
assert_blocked { post :index }
|
||||
end
|
||||
|
||||
def test_should_not_allow_html_put_without_token
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) { put :index, :format => :html }
|
||||
def test_should_not_allow_post_without_token_irrespective_of_format
|
||||
assert_blocked { post :index, :format=>'xml' }
|
||||
end
|
||||
|
||||
def test_should_not_allow_html_delete_without_token
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html }
|
||||
def test_should_not_allow_put_without_token
|
||||
assert_blocked { put :index }
|
||||
end
|
||||
|
||||
def test_should_allow_api_formatted_post_without_token
|
||||
assert_nothing_raised do
|
||||
post :index, :format => 'xml'
|
||||
end
|
||||
def test_should_not_allow_delete_without_token
|
||||
assert_blocked { delete :index }
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_put_without_token
|
||||
assert_nothing_raised do
|
||||
put :index, :format => 'xml'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_api_formatted_delete_without_token
|
||||
assert_nothing_raised do
|
||||
delete :index, :format => 'xml'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_post_sent_as_url_encoded_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
post :index, :format => 'xml'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_put_sent_as_url_encoded_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
put :index, :format => 'xml'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_delete_sent_as_url_encoded_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
delete :index, :format => 'xml'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_post_sent_as_multipart_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
|
||||
post :index, :format => 'xml'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_put_sent_as_multipart_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
|
||||
put :index, :format => 'xml'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_token
|
||||
assert_raise(ActionController::InvalidAuthenticityToken) do
|
||||
@request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
|
||||
delete :index, :format => 'xml'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_xhr_post_without_token
|
||||
assert_nothing_raised { xhr :post, :index }
|
||||
end
|
||||
|
||||
def test_should_allow_xhr_put_without_token
|
||||
assert_nothing_raised { xhr :put, :index }
|
||||
end
|
||||
|
||||
def test_should_allow_xhr_delete_without_token
|
||||
assert_nothing_raised { xhr :delete, :index }
|
||||
end
|
||||
|
||||
def test_should_allow_xhr_post_with_encoded_form_content_type_without_token
|
||||
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
|
||||
assert_nothing_raised { xhr :post, :index }
|
||||
def test_should_not_allow_xhr_post_without_token
|
||||
assert_blocked { xhr :post, :index }
|
||||
end
|
||||
|
||||
def test_should_allow_post_with_token
|
||||
post :index, :authenticity_token => @token
|
||||
assert_response :success
|
||||
assert_not_blocked { post :index, :authenticity_token => @token }
|
||||
end
|
||||
|
||||
def test_should_allow_put_with_token
|
||||
put :index, :authenticity_token => @token
|
||||
assert_response :success
|
||||
assert_not_blocked { put :index, :authenticity_token => @token }
|
||||
end
|
||||
|
||||
def test_should_allow_delete_with_token
|
||||
delete :index, :authenticity_token => @token
|
||||
assert_not_blocked { delete :index, :authenticity_token => @token }
|
||||
end
|
||||
|
||||
def test_should_allow_post_with_token_in_header
|
||||
@request.env['HTTP_X_CSRF_TOKEN'] = @token
|
||||
assert_not_blocked { post :index }
|
||||
end
|
||||
|
||||
def test_should_allow_delete_with_token_in_header
|
||||
@request.env['HTTP_X_CSRF_TOKEN'] = @token
|
||||
assert_not_blocked { delete :index }
|
||||
end
|
||||
|
||||
def test_should_allow_put_with_token_in_header
|
||||
@request.env['HTTP_X_CSRF_TOKEN'] = @token
|
||||
assert_not_blocked { put :index }
|
||||
end
|
||||
|
||||
def assert_blocked
|
||||
session[:something_like_user_id] = 1
|
||||
yield
|
||||
assert_nil session[:something_like_user_id], "session values are still present"
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_allow_post_with_xml
|
||||
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
|
||||
post :index, :format => 'xml'
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_allow_put_with_xml
|
||||
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
|
||||
put :index, :format => 'xml'
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_allow_delete_with_xml
|
||||
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
|
||||
delete :index, :format => 'xml'
|
||||
def assert_not_blocked
|
||||
assert_nothing_raised { yield }
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
@@ -205,16 +171,6 @@ end
|
||||
|
||||
class RequestForgeryProtectionControllerTest < ActionController::TestCase
|
||||
include RequestForgeryProtectionTests
|
||||
def setup
|
||||
@controller = RequestForgeryProtectionController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@request.format = :html
|
||||
@response = ActionController::TestResponse.new
|
||||
@token = "cf50faa3fe97702ca1ae"
|
||||
|
||||
ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
|
||||
ActionController::Base.request_forgery_protection_token = :authenticity_token
|
||||
end
|
||||
|
||||
test 'should emit a csrf-token meta tag' do
|
||||
ActiveSupport::SecureRandom.stubs(:base64).returns(@token + '<=?')
|
||||
@@ -226,6 +182,15 @@ class RequestForgeryProtectionControllerTest < ActionController::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
class RequestForgeryProtectionControllerUsingOldBehaviourTest < ActionController::TestCase
|
||||
include RequestForgeryProtectionTests
|
||||
def assert_blocked
|
||||
assert_raises(ActionController::InvalidAuthenticityToken) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class FreeCookieControllerTest < ActionController::TestCase
|
||||
def setup
|
||||
@controller = FreeCookieController.new
|
||||
@@ -258,13 +223,23 @@ class FreeCookieControllerTest < ActionController::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CustomAuthenticityParamControllerTest < ActionController::TestCase
|
||||
def setup
|
||||
ActionController::Base.request_forgery_protection_token = :custom_token_name
|
||||
super
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActionController::Base.request_forgery_protection_token = :authenticity_token
|
||||
super
|
||||
end
|
||||
|
||||
def test_should_allow_custom_token
|
||||
post :index, :authenticity_token => 'foobar'
|
||||
post :index, :custom_token_name => 'foobar'
|
||||
assert_response :ok
|
||||
end
|
||||
end
|
||||
|
||||
@@ -701,7 +701,7 @@ class RouteSetTest < ActiveSupport::TestCase
|
||||
set.draw do
|
||||
match '/users/index' => 'users#index'
|
||||
end
|
||||
params = set.recognize_path('/users/index', :method => :get)
|
||||
set.recognize_path('/users/index', :method => :get)
|
||||
assert_equal 1, set.routes.size
|
||||
end
|
||||
|
||||
@@ -980,7 +980,7 @@ class RouteSetTest < ActiveSupport::TestCase
|
||||
match '/profile' => 'profile#index'
|
||||
end
|
||||
|
||||
params = set.recognize_path("/profile") rescue nil
|
||||
set.recognize_path("/profile") rescue nil
|
||||
|
||||
assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded"
|
||||
end
|
||||
|
||||
@@ -95,16 +95,29 @@ module AbstractController
|
||||
)
|
||||
end
|
||||
|
||||
def test_protocol_with_and_without_separator
|
||||
def test_protocol_with_and_without_separators
|
||||
add_host!
|
||||
assert_equal('https://www.basecamphq.com/c/a/i',
|
||||
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
|
||||
)
|
||||
assert_equal('https://www.basecamphq.com/c/a/i',
|
||||
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https:')
|
||||
)
|
||||
assert_equal('https://www.basecamphq.com/c/a/i',
|
||||
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https://')
|
||||
)
|
||||
end
|
||||
|
||||
def test_without_protocol
|
||||
add_host!
|
||||
assert_equal('//www.basecamphq.com/c/a/i',
|
||||
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => '//')
|
||||
)
|
||||
assert_equal('//www.basecamphq.com/c/a/i',
|
||||
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => false)
|
||||
)
|
||||
end
|
||||
|
||||
def test_trailing_slash
|
||||
add_host!
|
||||
options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'}
|
||||
|
||||
@@ -216,7 +216,7 @@ class WebServiceTest < ActionDispatch::IntegrationTest
|
||||
def test_typecast_as_yaml
|
||||
with_test_route_set do
|
||||
with_params_parsers Mime::YAML => :yaml do
|
||||
yaml = <<-YAML
|
||||
yaml = (<<-YAML).strip
|
||||
---
|
||||
data:
|
||||
a: 15
|
||||
|
||||
@@ -29,14 +29,16 @@ class DispatcherTest < ActiveSupport::TestCase
|
||||
assert_equal 4, Foo.b
|
||||
end
|
||||
|
||||
def test_to_prepare_deprecation
|
||||
prepared = false
|
||||
assert_deprecated do
|
||||
ActionDispatch::Callbacks.to_prepare { prepared = true }
|
||||
end
|
||||
def test_to_prepare_and_cleanup_delegation
|
||||
prepared = cleaned = false
|
||||
ActionDispatch::Callbacks.to_prepare { prepared = true }
|
||||
ActionDispatch::Callbacks.to_prepare { cleaned = true }
|
||||
|
||||
ActionDispatch::Reloader.prepare!
|
||||
assert prepared
|
||||
|
||||
ActionDispatch::Reloader.cleanup!
|
||||
assert cleaned
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -95,6 +95,26 @@ class CookiesTest < ActionController::TestCase
|
||||
head :ok
|
||||
end
|
||||
|
||||
def set_cookie_with_domain_and_tld
|
||||
cookies[:user_name] = {:value => "rizwanreza", :domain => :all, :tld_length => 2}
|
||||
head :ok
|
||||
end
|
||||
|
||||
def delete_cookie_with_domain_and_tld
|
||||
cookies.delete(:user_name, :domain => :all, :tld_length => 2)
|
||||
head :ok
|
||||
end
|
||||
|
||||
def set_cookie_with_domains
|
||||
cookies[:user_name] = {:value => "rizwanreza", :domain => %w(example1.com example2.com .example3.com)}
|
||||
head :ok
|
||||
end
|
||||
|
||||
def delete_cookie_with_domains
|
||||
cookies.delete(:user_name, :domain => %w(example1.com example2.com .example3.com))
|
||||
head :ok
|
||||
end
|
||||
|
||||
def symbol_key
|
||||
cookies[:user_name] = "david"
|
||||
head :ok
|
||||
@@ -322,6 +342,67 @@ class CookiesTest < ActionController::TestCase
|
||||
assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
|
||||
end
|
||||
|
||||
def test_cookie_with_all_domain_option_and_tld_length
|
||||
get :set_cookie_with_domain_and_tld
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=rizwanreza; domain=.nextangle.com; path=/"
|
||||
end
|
||||
|
||||
def test_cookie_with_all_domain_option_using_a_non_standard_tld_and_tld_length
|
||||
@request.host = "two.subdomains.nextangle.local"
|
||||
get :set_cookie_with_domain_and_tld
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/"
|
||||
end
|
||||
|
||||
def test_cookie_with_all_domain_option_using_host_with_port_and_tld_length
|
||||
@request.host = "nextangle.local:3000"
|
||||
get :set_cookie_with_domain_and_tld
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/"
|
||||
end
|
||||
|
||||
def test_deleting_cookie_with_all_domain_option_and_tld_length
|
||||
get :delete_cookie_with_domain_and_tld
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
|
||||
end
|
||||
|
||||
def test_cookie_with_several_preset_domains_using_one_of_these_domains
|
||||
@request.host = "example1.com"
|
||||
get :set_cookie_with_domains
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=rizwanreza; domain=example1.com; path=/"
|
||||
end
|
||||
|
||||
def test_cookie_with_several_preset_domains_using_other_domain
|
||||
@request.host = "other-domain.com"
|
||||
get :set_cookie_with_domains
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=rizwanreza; path=/"
|
||||
end
|
||||
|
||||
def test_cookie_with_several_preset_domains_using_shared_domain
|
||||
@request.host = "example3.com"
|
||||
get :set_cookie_with_domains
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=rizwanreza; domain=.example3.com; path=/"
|
||||
end
|
||||
|
||||
def test_deletings_cookie_with_several_preset_domains_using_one_of_these_domains
|
||||
@request.host = "example2.com"
|
||||
get :delete_cookie_with_domains
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=; domain=example2.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
|
||||
end
|
||||
|
||||
def test_deletings_cookie_with_several_preset_domains_using_other_domain
|
||||
@request.host = "other-domain.com"
|
||||
get :delete_cookie_with_domains
|
||||
assert_response :success
|
||||
assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
|
||||
end
|
||||
|
||||
def test_cookies_hash_is_indifferent_access
|
||||
[:symbol_key, :string_key].each do |cookie_key|
|
||||
get cookie_key
|
||||
|
||||
@@ -131,6 +131,13 @@ class MimeTypeTest < ActiveSupport::TestCase
|
||||
assert unverified.each { |type| assert !Mime.const_get(type.to_s.upcase).verify_request?, "Nonverifiable Mime Type is verified: #{type.inspect}" }
|
||||
end
|
||||
|
||||
test "references gives preference to symbols before strings" do
|
||||
assert_equal :html, Mime::HTML.ref
|
||||
another = Mime::Type.lookup("foo/bar")
|
||||
assert_nil another.to_sym
|
||||
assert_equal "foo/bar", another.ref
|
||||
end
|
||||
|
||||
test "regexp matcher" do
|
||||
assert Mime::JS =~ "text/javascript"
|
||||
assert Mime::JS =~ "application/javascript"
|
||||
|
||||
@@ -116,6 +116,20 @@ class ReloaderTest < Test::Unit::TestCase
|
||||
assert cleaned
|
||||
end
|
||||
|
||||
def test_cleanup_callbacks_are_called_on_exceptions
|
||||
cleaned = false
|
||||
Reloader.to_cleanup { cleaned = true }
|
||||
|
||||
begin
|
||||
call_and_return_body do
|
||||
raise "error"
|
||||
end
|
||||
rescue
|
||||
end
|
||||
|
||||
assert cleaned
|
||||
end
|
||||
|
||||
private
|
||||
def call_and_return_body(&block)
|
||||
@reloader ||= Reloader.new(block || proc {[200, {}, 'response']})
|
||||
|
||||
@@ -427,7 +427,7 @@ class RequestTest < ActiveSupport::TestCase
|
||||
begin
|
||||
request = stub_request(mock_rack_env)
|
||||
request.parameters
|
||||
rescue TypeError => e
|
||||
rescue TypeError
|
||||
# rack will raise a TypeError when parsing this query string
|
||||
end
|
||||
assert_equal({}, request.parameters)
|
||||
|
||||
@@ -18,7 +18,7 @@ class ResponseTest < ActiveSupport::TestCase
|
||||
body.each { |part| parts << part }
|
||||
assert_equal ["Hello, World!"], parts
|
||||
end
|
||||
|
||||
|
||||
test "status handled properly in initialize" do
|
||||
assert_equal 200, ActionDispatch::Response.new('200 OK').status
|
||||
end
|
||||
@@ -26,7 +26,7 @@ class ResponseTest < ActiveSupport::TestCase
|
||||
test "utf8 output" do
|
||||
@response.body = [1090, 1077, 1089, 1090].pack("U*")
|
||||
|
||||
status, headers, body = @response.to_a
|
||||
status, headers, _ = @response.to_a
|
||||
assert_equal 200, status
|
||||
assert_equal({
|
||||
"Content-Type" => "text/html; charset=utf-8"
|
||||
@@ -52,20 +52,20 @@ class ResponseTest < ActiveSupport::TestCase
|
||||
test "content type" do
|
||||
[204, 304].each do |c|
|
||||
@response.status = c.to_s
|
||||
status, headers, body = @response.to_a
|
||||
_, headers, _ = @response.to_a
|
||||
assert !headers.has_key?("Content-Type"), "#{c} should not have Content-Type header"
|
||||
end
|
||||
|
||||
[200, 302, 404, 500].each do |c|
|
||||
@response.status = c.to_s
|
||||
status, headers, body = @response.to_a
|
||||
_, headers, _ = @response.to_a
|
||||
assert headers.has_key?("Content-Type"), "#{c} did not have Content-Type header"
|
||||
end
|
||||
end
|
||||
|
||||
test "does not include Status header" do
|
||||
@response.status = "200 OK"
|
||||
status, headers, body = @response.to_a
|
||||
_, headers, _ = @response.to_a
|
||||
assert !headers.has_key?('Status')
|
||||
end
|
||||
|
||||
|
||||
103
actionpack/test/dispatch/routing_assertions_test.rb
Normal file
103
actionpack/test/dispatch/routing_assertions_test.rb
Normal file
@@ -0,0 +1,103 @@
|
||||
require 'abstract_unit'
|
||||
require 'controller/fake_controllers'
|
||||
|
||||
class SecureArticlesController < ArticlesController; end
|
||||
class BlockArticlesController < ArticlesController; end
|
||||
|
||||
class RoutingAssertionsTest < ActionController::TestCase
|
||||
|
||||
def setup
|
||||
@routes = ActionDispatch::Routing::RouteSet.new
|
||||
@routes.draw do
|
||||
resources :articles
|
||||
|
||||
scope 'secure', :constraints => { :protocol => 'https://' } do
|
||||
resources :articles, :controller => 'secure_articles'
|
||||
end
|
||||
|
||||
scope 'block', :constraints => lambda { |r| r.ssl? } do
|
||||
resources :articles, :controller => 'block_articles'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_generates
|
||||
assert_generates('/articles', { :controller => 'articles', :action => 'index' })
|
||||
assert_generates('/articles/1', { :controller => 'articles', :action => 'show', :id => '1' })
|
||||
end
|
||||
|
||||
def test_assert_generates_with_defaults
|
||||
assert_generates('/articles/1/edit', { :controller => 'articles', :action => 'edit' }, { :id => '1' })
|
||||
end
|
||||
|
||||
def test_assert_generates_with_extras
|
||||
assert_generates('/articles', { :controller => 'articles', :action => 'index', :page => '1' }, {}, { :page => '1' })
|
||||
end
|
||||
|
||||
def test_assert_recognizes
|
||||
assert_recognizes({ :controller => 'articles', :action => 'index' }, '/articles')
|
||||
assert_recognizes({ :controller => 'articles', :action => 'show', :id => '1' }, '/articles/1')
|
||||
end
|
||||
|
||||
def test_assert_recognizes_with_extras
|
||||
assert_recognizes({ :controller => 'articles', :action => 'index', :page => '1' }, '/articles', { :page => '1' })
|
||||
end
|
||||
|
||||
def test_assert_recognizes_with_method
|
||||
assert_recognizes({ :controller => 'articles', :action => 'create' }, { :path => '/articles', :method => :post })
|
||||
assert_recognizes({ :controller => 'articles', :action => 'update', :id => '1' }, { :path => '/articles/1', :method => :put })
|
||||
end
|
||||
|
||||
def test_assert_recognizes_with_hash_constraint
|
||||
assert_raise(ActionController::RoutingError) do
|
||||
assert_recognizes({ :controller => 'secure_articles', :action => 'index' }, 'http://test.host/secure/articles')
|
||||
end
|
||||
assert_recognizes({ :controller => 'secure_articles', :action => 'index' }, 'https://test.host/secure/articles')
|
||||
end
|
||||
|
||||
def test_assert_recognizes_with_block_constraint
|
||||
assert_raise(ActionController::RoutingError) do
|
||||
assert_recognizes({ :controller => 'block_articles', :action => 'index' }, 'http://test.host/block/articles')
|
||||
end
|
||||
assert_recognizes({ :controller => 'block_articles', :action => 'index' }, 'https://test.host/block/articles')
|
||||
end
|
||||
|
||||
def test_assert_routing
|
||||
assert_routing('/articles', :controller => 'articles', :action => 'index')
|
||||
end
|
||||
|
||||
def test_assert_routing_with_defaults
|
||||
assert_routing('/articles/1/edit', { :controller => 'articles', :action => 'edit', :id => '1' }, { :id => '1' })
|
||||
end
|
||||
|
||||
def test_assert_routing_with_extras
|
||||
assert_routing('/articles', { :controller => 'articles', :action => 'index', :page => '1' }, { }, { :page => '1' })
|
||||
end
|
||||
|
||||
def test_assert_routing_with_hash_constraint
|
||||
assert_raise(ActionController::RoutingError) do
|
||||
assert_routing('http://test.host/secure/articles', { :controller => 'secure_articles', :action => 'index' })
|
||||
end
|
||||
assert_routing('https://test.host/secure/articles', { :controller => 'secure_articles', :action => 'index' })
|
||||
end
|
||||
|
||||
def test_assert_routing_with_block_constraint
|
||||
assert_raise(ActionController::RoutingError) do
|
||||
assert_routing('http://test.host/block/articles', { :controller => 'block_articles', :action => 'index' })
|
||||
end
|
||||
assert_routing('https://test.host/block/articles', { :controller => 'block_articles', :action => 'index' })
|
||||
end
|
||||
|
||||
def test_with_routing
|
||||
with_routing do |routes|
|
||||
routes.draw do
|
||||
resources :articles, :path => 'artikel'
|
||||
end
|
||||
|
||||
assert_routing('/artikel', :controller => 'articles', :action => 'index')
|
||||
assert_raise(ActionController::RoutingError) do
|
||||
assert_routing('/articles', { :controller => 'articles', :action => 'index' })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -187,7 +187,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
resources :posts, :only => [:index, :show] do
|
||||
resources :comments, :except => :destroy
|
||||
namespace :admin do
|
||||
root :to => "index#index"
|
||||
end
|
||||
resources :comments, :except => :destroy do
|
||||
get "views" => "comments#views", :as => :views
|
||||
end
|
||||
end
|
||||
|
||||
resource :past, :only => :destroy
|
||||
@@ -205,6 +210,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
scope '/hello' do
|
||||
shallow do
|
||||
resources :notes do
|
||||
resources :trackbacks
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
resources :threads, :shallow => true do
|
||||
resource :owner
|
||||
resources :messages do
|
||||
@@ -1676,6 +1689,60 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_shallow_nested_resources_within_scope
|
||||
with_test_routes do
|
||||
|
||||
get '/hello/notes/1/trackbacks'
|
||||
assert_equal 'trackbacks#index', @response.body
|
||||
assert_equal '/hello/notes/1/trackbacks', note_trackbacks_path(:note_id => 1)
|
||||
|
||||
get '/hello/notes/1/edit'
|
||||
assert_equal 'notes#edit', @response.body
|
||||
assert_equal '/hello/notes/1/edit', edit_note_path(:id => '1')
|
||||
|
||||
get '/hello/notes/1/trackbacks/new'
|
||||
assert_equal 'trackbacks#new', @response.body
|
||||
assert_equal '/hello/notes/1/trackbacks/new', new_note_trackback_path(:note_id => 1)
|
||||
|
||||
get '/hello/trackbacks/1'
|
||||
assert_equal 'trackbacks#show', @response.body
|
||||
assert_equal '/hello/trackbacks/1', trackback_path(:id => '1')
|
||||
|
||||
get '/hello/trackbacks/1/edit'
|
||||
assert_equal 'trackbacks#edit', @response.body
|
||||
assert_equal '/hello/trackbacks/1/edit', edit_trackback_path(:id => '1')
|
||||
|
||||
put '/hello/trackbacks/1'
|
||||
assert_equal 'trackbacks#update', @response.body
|
||||
|
||||
post '/hello/notes/1/trackbacks'
|
||||
assert_equal 'trackbacks#create', @response.body
|
||||
|
||||
delete '/hello/trackbacks/1'
|
||||
assert_equal 'trackbacks#destroy', @response.body
|
||||
|
||||
get '/hello/notes'
|
||||
assert_equal 'notes#index', @response.body
|
||||
|
||||
post '/hello/notes'
|
||||
assert_equal 'notes#create', @response.body
|
||||
|
||||
get '/hello/notes/new'
|
||||
assert_equal 'notes#new', @response.body
|
||||
assert_equal '/hello/notes/new', new_note_path
|
||||
|
||||
get '/hello/notes/1'
|
||||
assert_equal 'notes#show', @response.body
|
||||
assert_equal '/hello/notes/1', note_path(:id => 1)
|
||||
|
||||
put '/hello/notes/1'
|
||||
assert_equal 'notes#update', @response.body
|
||||
|
||||
delete '/hello/notes/1'
|
||||
assert_equal 'notes#destroy', @response.body
|
||||
end
|
||||
end
|
||||
|
||||
def test_custom_resource_routes_are_scoped
|
||||
with_test_routes do
|
||||
assert_equal '/customers/recent', recent_customers_path
|
||||
@@ -2246,6 +2313,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_route_in_nested_resource
|
||||
get "/posts/1/comments/2/views"
|
||||
assert_equal "comments#views", @response.body
|
||||
assert_equal "/posts/1/comments/2/views", post_comment_views_path(:post_id => '1', :comment_id => '2')
|
||||
end
|
||||
|
||||
def test_root_in_deeply_nested_scope
|
||||
get "/posts/1/admin"
|
||||
assert_equal "admin/index#index", @response.body
|
||||
assert_equal "/posts/1/admin", post_admin_root_path(:post_id => '1')
|
||||
end
|
||||
|
||||
private
|
||||
def with_test_routes
|
||||
yield
|
||||
|
||||
@@ -194,7 +194,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
|
||||
with_test_route_set do
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
session_payload = response.body
|
||||
assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
|
||||
headers['Set-Cookie']
|
||||
|
||||
|
||||
4
actionpack/test/fixtures/test/_layout_with_partial_and_yield.html.erb
vendored
Normal file
4
actionpack/test/fixtures/test/_layout_with_partial_and_yield.html.erb
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
Before
|
||||
<%= render :partial => "test/partial.html.erb" %>
|
||||
<%= yield %>
|
||||
After
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user