mirror of
https://github.com/github/rails.git
synced 2026-01-14 17:17:58 -05:00
Compare commits
2 Commits
3-0-github
...
v3.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bac6ba99b1 | ||
|
|
2d96bccb1e |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,18 +1,18 @@
|
||||
*.gem
|
||||
pkg
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
debug.log
|
||||
doc/rdoc
|
||||
activemodel/doc
|
||||
activeresource/doc
|
||||
activerecord/doc
|
||||
activerecord/sqlnet.log
|
||||
actionpack/doc
|
||||
actionmailer/doc
|
||||
activesupport/doc
|
||||
activemodel/test/fixtures/fixture_database.sqlite3
|
||||
actionpack/test/tmp
|
||||
activesupport/test/fixtures/isolation_test
|
||||
dist
|
||||
railties/test/500.html
|
||||
railties/test/fixtures/tmp
|
||||
railties/test/initializer/root/log
|
||||
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,25 +0,0 @@
|
||||
script: 'ci/travis.rb'
|
||||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- 1.9.3
|
||||
env:
|
||||
- "GEM=railties"
|
||||
- "GEM=ap,am,amo,ares,as"
|
||||
- "GEM=ar:mysql"
|
||||
- "GEM=ar:mysql2"
|
||||
- "GEM=ar:sqlite3"
|
||||
- "GEM=ar:postgresql"
|
||||
notifications:
|
||||
email: false
|
||||
irc:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
channels:
|
||||
- "irc.freenode.org#rails-contrib"
|
||||
campfire:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
rooms:
|
||||
- secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ="
|
||||
bundler_args: --path vendor/bundle
|
||||
50
Gemfile
50
Gemfile
@@ -1,43 +1,53 @@
|
||||
source 'http://rubygems.org'
|
||||
|
||||
gemspec
|
||||
|
||||
gem "rake", ">= 0.8.7"
|
||||
gem 'mocha', '>= 0.13.0', :require => false
|
||||
|
||||
gem "pry"
|
||||
|
||||
group :doc do
|
||||
gem "rdoc", "~> 3.4"
|
||||
gem "horo", "= 1.0.3"
|
||||
gem "RedCloth", "~> 4.2" if RUBY_VERSION < "1.9.3"
|
||||
if ENV['AREL']
|
||||
gem "arel", :path => ENV['AREL']
|
||||
else
|
||||
gem "arel", :git => "git://github.com/rails/arel.git"
|
||||
end
|
||||
|
||||
# for perf tests
|
||||
gem "faker"
|
||||
gem "rbench"
|
||||
gem "addressable"
|
||||
gem "rails", :path => File.dirname(__FILE__)
|
||||
|
||||
gem "rake", ">= 0.8.7"
|
||||
gem "mocha", ">= 0.9.8"
|
||||
gem "rdoc", ">= 2.5.10"
|
||||
gem "horo", ">= 1.0.2"
|
||||
|
||||
# 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"
|
||||
end
|
||||
|
||||
platforms :ruby do
|
||||
gem 'json'
|
||||
gem 'yajl-ruby'
|
||||
gem "nokogiri", ">= 1.4.4"
|
||||
gem "nokogiri", ">= 1.4.3.1"
|
||||
|
||||
# AR
|
||||
gem "sqlite3", "~> 1.3.3"
|
||||
gem "sqlite3-ruby", "~> 1.3.1", :require => 'sqlite3'
|
||||
|
||||
group :db do
|
||||
gem "pg", ">= 0.9.0"
|
||||
gem "mysql", ">= 2.8.1"
|
||||
gem "mysql2", :git => "git://github.com/brianmario/mysql2.git", :branch => "0.2.x"
|
||||
gem "mysql2", ">= 0.2.3"
|
||||
end
|
||||
end
|
||||
|
||||
env :AREL do
|
||||
gem "arel", :path => ENV['AREL']
|
||||
platforms :jruby do
|
||||
gem "ruby-debug", ">= 0.10.3"
|
||||
|
||||
gem "activerecord-jdbcsqlite3-adapter"
|
||||
|
||||
group :db do
|
||||
gem "activerecord-jdbcmysql-adapter"
|
||||
gem "activerecord-jdbcpostgresql-adapter"
|
||||
end
|
||||
end
|
||||
|
||||
# gems that are necessary for ActiveRecord tests with Oracle database
|
||||
|
||||
124
Gemfile.lock
124
Gemfile.lock
@@ -1,124 +0,0 @@
|
||||
GIT
|
||||
remote: git://github.com/brianmario/mysql2.git
|
||||
revision: 3c7548851f5bf124eb23307286ef95d61172ac4b
|
||||
branch: 0.2.x
|
||||
specs:
|
||||
mysql2 (0.2.22)
|
||||
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
actionmailer (3.0.20)
|
||||
actionpack (= 3.0.20)
|
||||
mail (~> 2.2)
|
||||
actionpack (3.0.20)
|
||||
activemodel (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
builder (~> 3.2.0)
|
||||
erubis (~> 2.7.0)
|
||||
i18n (~> 0.6.0)
|
||||
rack (~> 1.4.1)
|
||||
rack-mount (~> 0.6.14)
|
||||
rack-test (~> 0.6.1)
|
||||
tzinfo (~> 0.3.23)
|
||||
activemodel (3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
builder (~> 3.2.0)
|
||||
i18n (~> 0.6.0)
|
||||
activerecord (3.0.20)
|
||||
activemodel (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
arel (~> 2.0.10)
|
||||
tzinfo (~> 0.3.23)
|
||||
activeresource (3.0.20)
|
||||
activemodel (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
activesupport (3.0.20)
|
||||
rails (3.0.20)
|
||||
actionmailer (= 3.0.20)
|
||||
actionpack (= 3.0.20)
|
||||
activerecord (= 3.0.20)
|
||||
activeresource (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.0.20)
|
||||
railties (3.0.20)
|
||||
actionpack (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (~> 0.18)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
addressable (2.3.6)
|
||||
arel (2.0.10)
|
||||
builder (3.2.2)
|
||||
coderay (1.1.0)
|
||||
erubis (2.7.0)
|
||||
faker (1.3.0)
|
||||
i18n (~> 0.5)
|
||||
horo (1.0.3)
|
||||
rdoc (>= 2.5)
|
||||
i18n (0.6.9)
|
||||
json (1.8.1)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
memcache-client (1.8.5)
|
||||
metaclass (0.0.4)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.5.3)
|
||||
mocha (1.0.0)
|
||||
metaclass (~> 0.0.1)
|
||||
mysql (2.9.1)
|
||||
nokogiri (1.6.1)
|
||||
mini_portile (~> 0.5.0)
|
||||
pg (0.17.1)
|
||||
polyglot (0.3.4)
|
||||
pry (0.9.12.6)
|
||||
coderay (~> 1.0)
|
||||
method_source (~> 0.8)
|
||||
slop (~> 3.4)
|
||||
rack (1.4.5)
|
||||
rack-mount (0.6.14)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
rake (10.2.2)
|
||||
rbench (0.2.3)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
slop (3.5.0)
|
||||
sqlite3 (1.3.9)
|
||||
thor (0.19.1)
|
||||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.39)
|
||||
yajl-ruby (1.2.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
addressable
|
||||
arel
|
||||
faker
|
||||
horo (= 1.0.3)
|
||||
json
|
||||
memcache-client (>= 1.8.5)
|
||||
mocha (>= 0.13.0)
|
||||
mysql (>= 2.8.1)
|
||||
mysql2!
|
||||
nokogiri (>= 1.4.4)
|
||||
pg (>= 0.9.0)
|
||||
pry
|
||||
rails!
|
||||
rake (>= 0.8.7)
|
||||
rbench
|
||||
rdoc (~> 3.4)
|
||||
sqlite3 (~> 1.3.3)
|
||||
yajl-ruby
|
||||
@@ -1 +1 @@
|
||||
3.0.20
|
||||
3.0.1
|
||||
|
||||
32
Rakefile
32
Rakefile
@@ -3,15 +3,7 @@ require 'rdoc'
|
||||
|
||||
require 'rake'
|
||||
require 'rdoc/task'
|
||||
|
||||
$:.unshift File.expand_path('..', __FILE__)
|
||||
require "tasks/release"
|
||||
|
||||
desc "Build gem files for all projects"
|
||||
task :build => "all:build"
|
||||
|
||||
desc "Release all gems to gemcutter and create a tag"
|
||||
task :release => "all:release"
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
# 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.
|
||||
@@ -62,6 +54,27 @@ task :smoke do
|
||||
system %(cd activerecord && #{$0} sqlite3:isolated_test)
|
||||
end
|
||||
|
||||
spec = eval(File.read('rails.gemspec'))
|
||||
Rake::GemPackageTask.new(spec) do |pkg|
|
||||
pkg.gem_spec = spec
|
||||
end
|
||||
|
||||
desc "Release all gems to gemcutter. Package rails, package & push components, then push rails"
|
||||
task :release => :release_projects do
|
||||
require 'rake/gemcutter'
|
||||
Rake::Gemcutter::Tasks.new(spec).define
|
||||
Rake::Task['gem:push'].invoke
|
||||
end
|
||||
|
||||
desc "Release all components to gemcutter."
|
||||
task :release_projects => :package do
|
||||
errors = []
|
||||
PROJECTS.each do |project|
|
||||
system(%(cd #{project} && #{$0} release)) || errors << project
|
||||
end
|
||||
fail("Errors in #{errors.join(', ')}") unless errors.empty?
|
||||
end
|
||||
|
||||
desc "Install gems for all projects."
|
||||
task :install => :gem do
|
||||
version = File.read("RAILS_VERSION").strip
|
||||
@@ -131,7 +144,6 @@ task :rdoc do
|
||||
FileUtils.copy "activerecord/examples/associations.png", "doc/rdoc/files/examples/associations.png"
|
||||
end
|
||||
|
||||
desc 'Bump all versions to match version.rb'
|
||||
task :update_versions do
|
||||
require File.dirname(__FILE__) + "/version"
|
||||
|
||||
|
||||
@@ -1,78 +1,6 @@
|
||||
## Rails 3.0.20 (unreleased)
|
||||
|
||||
## Rails 3.0.19 (Jan 8, 2013)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.18 (Jan 2, 2013)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.17 (Aug 9, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.16 (Jul 26, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.14 (Jun 12, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
* Rails 3.0.13 (May 31, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
*Rails 3.0.10 (August 16, 2011)*
|
||||
|
||||
*No changes.
|
||||
|
||||
|
||||
*Rails 3.0.9 (June 16, 2011)*
|
||||
|
||||
*No changes.
|
||||
|
||||
|
||||
*Rails 3.0.8 (June 7, 2011)*
|
||||
|
||||
* Mail dependency increased to 2.2.19
|
||||
|
||||
|
||||
*Rails 3.0.7 (April 18, 2011)*
|
||||
|
||||
* remove AM delegating register_observer and register_interceptor to Mail [Josh Kalderimis]
|
||||
|
||||
|
||||
*Rails 3.0.6 (April 5, 2011)
|
||||
|
||||
* Don't allow i18n to change the minor version, version now set to ~> 0.5.0 [Santiago Pastorino]
|
||||
|
||||
|
||||
*Rails 3.0.5 (February 26, 2011)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.4 (February 8, 2011)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.3 (November 16, 2010)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.2 (November 15, 2010)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.1 (October 15, 2010)*
|
||||
|
||||
* No Changes.
|
||||
|
||||
* No Changes, just a version bump.
|
||||
|
||||
*Rails 3.0.0 (August 29, 2010)*
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/packagetask'
|
||||
require 'rubygems/package_task'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
desc "Default Task"
|
||||
task :default => [ :test ]
|
||||
@@ -17,14 +17,14 @@ 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|
|
||||
sh(ruby, '-Ilib:test', file)
|
||||
system(ruby, '-Ilib:test', file)
|
||||
end or raise "Failures"
|
||||
end
|
||||
end
|
||||
|
||||
spec = eval(File.read('actionmailer.gemspec'))
|
||||
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
end
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ Gem::Specification.new do |s|
|
||||
s.require_path = 'lib'
|
||||
s.requirements << 'none'
|
||||
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('actionpack', version)
|
||||
s.add_dependency('mail', '~> 2.2')
|
||||
s.add_dependency('mail', '~> 2.2.5')
|
||||
end
|
||||
|
||||
@@ -26,7 +26,6 @@ $:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(
|
||||
|
||||
require 'abstract_controller'
|
||||
require 'action_view'
|
||||
require 'action_mailer/version'
|
||||
|
||||
# Common Active Support usage in Action Mailer
|
||||
require 'active_support/core_ext/class'
|
||||
|
||||
@@ -4,7 +4,6 @@ require 'action_mailer/collector'
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/proc'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'action_mailer/log_subscriber'
|
||||
|
||||
module ActionMailer #:nodoc:
|
||||
@@ -235,8 +234,8 @@ module ActionMailer #:nodoc:
|
||||
# default :sender => 'system@example.com'
|
||||
# end
|
||||
#
|
||||
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
|
||||
# <tt>ActionMailer::Base</tt> sets the following:
|
||||
# You can pass in any header value that a <tt>Mail::Message</tt>, out of the box, <tt>ActionMailer::Base</tt>
|
||||
# sets the following:
|
||||
#
|
||||
# * <tt>:mime_version => "1.0"</tt>
|
||||
# * <tt>:charset => "UTF-8",</tt>
|
||||
@@ -274,7 +273,7 @@ module ActionMailer #:nodoc:
|
||||
# = Configuration options
|
||||
#
|
||||
# These options are specified on the class level, like
|
||||
# <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
|
||||
# <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
|
||||
#
|
||||
# * <tt>default</tt> - You can pass this in at a class level as well as within the class itself as
|
||||
# per the above section.
|
||||
@@ -291,9 +290,7 @@ module ActionMailer #:nodoc:
|
||||
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
|
||||
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
|
||||
# authentication type here.
|
||||
# This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
|
||||
# send password BASE64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
|
||||
# information and a cryptographic Message Digest 5 algorithm to hash important information)
|
||||
# This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
|
||||
# * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
|
||||
# and starts to use it.
|
||||
#
|
||||
@@ -349,6 +346,9 @@ module ActionMailer #:nodoc:
|
||||
include ActionMailer::OldApi
|
||||
include ActionMailer::DeprecatedApi
|
||||
|
||||
delegate :register_observer, :to => Mail
|
||||
delegate :register_interceptor, :to => Mail
|
||||
|
||||
private_class_method :new #:nodoc:
|
||||
|
||||
class_attribute :default_params
|
||||
@@ -360,31 +360,6 @@ module ActionMailer #:nodoc:
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
# Register one or more Observers which will be notified when mail is delivered.
|
||||
def register_observers(*observers)
|
||||
observers.flatten.compact.each { |observer| register_observer(observer) }
|
||||
end
|
||||
|
||||
# Register one or more Interceptors which will be called before mail is sent.
|
||||
def register_interceptors(*interceptors)
|
||||
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
|
||||
end
|
||||
|
||||
# Register an Observer which will be notified when mail is delivered.
|
||||
# Either a class or a string can be passed in as the Observer. If a string is passed in
|
||||
# it will be <tt>constantize</tt>d.
|
||||
def register_observer(observer)
|
||||
delivery_observer = (observer.is_a?(String) ? observer.constantize : observer)
|
||||
Mail.register_observer(delivery_observer)
|
||||
end
|
||||
|
||||
# Register an Inteceptor which will be called before mail is sent.
|
||||
# Either a class or a string can be passed in as the Observer. If a string is passed in
|
||||
# it will be <tt>constantize</tt>d.
|
||||
def register_interceptor(interceptor)
|
||||
delivery_interceptor = (interceptor.is_a?(String) ? interceptor.constantize : interceptor)
|
||||
Mail.register_interceptor(delivery_interceptor)
|
||||
end
|
||||
|
||||
def mailer_name
|
||||
@mailer_name ||= name.underscore
|
||||
|
||||
@@ -3,8 +3,17 @@ 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|
|
||||
simple_format(paragraph)
|
||||
Text::Format.new(
|
||||
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
|
||||
).format
|
||||
}.join("\n")
|
||||
|
||||
# Make list points stand on their own line
|
||||
@@ -28,22 +37,5 @@ 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
|
||||
|
||||
@@ -19,10 +19,6 @@ module ActionMailer
|
||||
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
include app.routes.url_helpers
|
||||
|
||||
register_interceptors(options.delete(:interceptors))
|
||||
register_observers(options.delete(:observers))
|
||||
|
||||
options.each { |k,v| send("#{k}=", v) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,9 +2,8 @@ module ActionMailer
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 3
|
||||
MINOR = 0
|
||||
TINY = 20
|
||||
PRE = nil
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ class <%= class_name %> < ActionMailer::Base
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.<%= file_path.gsub("/",".") %>.<%= action %>.subject
|
||||
# en.<%= file_name %>.<%= action %>.subject
|
||||
#
|
||||
def <%= action %>
|
||||
@greeting = "Hi"
|
||||
|
||||
@@ -25,6 +25,7 @@ end
|
||||
|
||||
silence_warnings do
|
||||
# These external dependencies have warnings :/
|
||||
require 'text/format'
|
||||
require 'mail'
|
||||
end
|
||||
|
||||
|
||||
@@ -111,7 +111,6 @@ class BaseTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test "attachment with hash" do
|
||||
skip "failed already"
|
||||
email = BaseMailer.attachment_with_hash
|
||||
assert_equal(1, email.attachments.length)
|
||||
assert_equal('invoice.jpg', email.attachments[0].filename)
|
||||
@@ -494,11 +493,6 @@ class BaseTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
class MySecondObserver
|
||||
def self.delivered_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
test "you can register an observer to the mail object that gets informed on email delivery" do
|
||||
ActionMailer::Base.register_observer(MyObserver)
|
||||
mail = BaseMailer.welcome
|
||||
@@ -506,31 +500,11 @@ class BaseTest < ActiveSupport::TestCase
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register an observer using its stringified name to the mail object that gets informed on email delivery" do
|
||||
ActionMailer::Base.register_observer("BaseTest::MyObserver")
|
||||
mail = BaseMailer.welcome
|
||||
MyObserver.expects(:delivered_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register multiple observers to the mail object that both get informed on email delivery" do
|
||||
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
|
||||
mail = BaseMailer.welcome
|
||||
MyObserver.expects(:delivered_email).with(mail)
|
||||
MySecondObserver.expects(:delivered_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
class MyInterceptor
|
||||
def self.delivering_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
class MySecondInterceptor
|
||||
def self.delivering_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
test "you can register an interceptor to the mail object that gets passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptor(MyInterceptor)
|
||||
mail = BaseMailer.welcome
|
||||
@@ -538,21 +512,6 @@ class BaseTest < ActiveSupport::TestCase
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
|
||||
mail = BaseMailer.welcome
|
||||
MyInterceptor.expects(:delivering_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register multiple interceptors to the mail object that both get passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
|
||||
mail = BaseMailer.welcome
|
||||
MyInterceptor.expects(:delivering_email).with(mail)
|
||||
MySecondInterceptor.expects(:delivering_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
|
||||
mail1 = ProcMailer.welcome
|
||||
yesterday = 1.day.ago
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>. Be greeted, new member!
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
require 'abstract_unit'
|
||||
require 'action_controller'
|
||||
require 'action_dispatch/testing/integration'
|
||||
|
||||
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
|
||||
Routes = ActionDispatch::Routing::RouteSet.new
|
||||
|
||||
Routes.draw do
|
||||
match ':controller(/:action(/:id))'
|
||||
end
|
||||
|
||||
def self._routes
|
||||
Routes
|
||||
end
|
||||
|
||||
def send_mail
|
||||
I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver
|
||||
render :text => 'Mail sent'
|
||||
end
|
||||
end
|
||||
|
||||
class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
|
||||
def app
|
||||
TestController::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
|
||||
@@ -659,8 +659,6 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_performs_delivery_via_sendmail
|
||||
skip "failed already"
|
||||
|
||||
IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t -f "system@loudthinking.com" test@localhost', 'w+')
|
||||
TestMailer.delivery_method = :sendmail
|
||||
TestMailer.signed_up(@recipient).deliver
|
||||
@@ -715,7 +713,7 @@ Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
The=3Dbody
|
||||
EOF
|
||||
mail = Mail.new(msg.strip)
|
||||
mail = Mail.new(msg)
|
||||
assert_equal "The=body", mail.body.to_s.strip
|
||||
assert_equal "The=3Dbody=", mail.body.encoded.strip
|
||||
end
|
||||
@@ -992,10 +990,10 @@ EOF
|
||||
|
||||
def test_recursive_multipart_processing
|
||||
fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email7")
|
||||
mail = Mail.new(fixture.strip)
|
||||
mail = Mail.new(fixture)
|
||||
assert_equal(2, mail.parts.length)
|
||||
assert_equal(4, mail.parts.first.parts.length)
|
||||
assert_equal("This is the first part.\n", mail.parts.first.parts.first.body.to_s)
|
||||
assert_equal("This is the first part.", mail.parts.first.parts.first.body.to_s)
|
||||
assert_equal("test.rb", mail.parts.first.parts.second.filename)
|
||||
assert_equal("flowed", mail.parts.first.parts.fourth.content_type_parameters[:format])
|
||||
assert_equal('smime.p7s', mail.parts.second.filename)
|
||||
@@ -1134,8 +1132,6 @@ class MethodNamingTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
def test_send_method
|
||||
skip "failed already"
|
||||
|
||||
assert_nothing_raised do
|
||||
assert_emails 1 do
|
||||
assert_deprecated do
|
||||
|
||||
@@ -58,12 +58,12 @@ class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
def test_signed_up_with_url
|
||||
UrlTestMailer.delivery_method = :test
|
||||
|
||||
# assert_deprecated do
|
||||
assert_deprecated do
|
||||
AppRoutes.draw do |map|
|
||||
map.connect ':controller/:action/:id'
|
||||
map.welcome 'welcome', :controller=>"foo", :action=>"bar"
|
||||
end
|
||||
# end
|
||||
end
|
||||
|
||||
expected = new_mail
|
||||
expected.to = @recipient
|
||||
|
||||
@@ -1,202 +1,6 @@
|
||||
## Rails 3.0.20 (unreleased)
|
||||
|
||||
* Fixed JSON params parsing regression for non-object JSON content.
|
||||
|
||||
## Rails 3.0.19 (Jan 8, 2013)
|
||||
|
||||
* Strip nils from collections on JSON and XML posts. [CVE-2013-0155]
|
||||
|
||||
## Rails 3.0.18 (Jan 2, 2013)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.17 (Aug 9, 2012)
|
||||
|
||||
* There is an XSS vulnerability in the strip_tags helper in Ruby on Rails, the
|
||||
helper doesn't correctly handle malformed html. As a result an attacker can
|
||||
execute arbitrary javascript through the use of specially crafted malformed
|
||||
html.
|
||||
|
||||
*Marek from Nethemba (www.nethemba.com) & Santiago Pastorino*
|
||||
|
||||
* When an "include_blank" value is supplied to the `select_tag` helper, the "include_blank" value is not escaped. If untrusted data is not escaped, and is supplied as the prompt value, there is a potential for XSS attacks.
|
||||
Vulnerable code will look something like this:
|
||||
select_tag("name", options, :include_blank => UNTRUSTED_INPUT)
|
||||
|
||||
*Santiago Pastorino*
|
||||
|
||||
## Rails 3.0.16 (Jul 26, 2012)
|
||||
|
||||
* Do not convert digest auth strings to symbols. CVE-2012-3424
|
||||
|
||||
## Rails 3.0.14 (Jun 12, 2012)
|
||||
|
||||
* nil is removed from array parameter values
|
||||
|
||||
CVE-2012-2694
|
||||
|
||||
* Rails 3.0.13 (May 31, 2012)
|
||||
|
||||
* Strip null bytes from Location header
|
||||
|
||||
* load the encoding converter to work around [ruby-core:41556] when switching
|
||||
encodings
|
||||
|
||||
* Avoid inspecting the whole route set, closes #1525
|
||||
|
||||
* whitelist protocols for auto_link
|
||||
|
||||
* Strip [nil] from parameters hash. Thanks to Ben Murphy for reporting this!
|
||||
CVE-2012-2660
|
||||
|
||||
*Rails 3.0.12 (unreleased)*
|
||||
|
||||
* Fix using `tranlate` helper with a html translation which uses the `:count` option for
|
||||
pluralization.
|
||||
|
||||
*Jon Leighton*
|
||||
|
||||
*Rails 3.0.11 (unreleased)*
|
||||
|
||||
* Fix XSS security vulnerability in the `translate` helper method. When using interpolation
|
||||
in combination with HTML-safe translations, the interpolated input would not get HTML
|
||||
escaped. *GH 3664*
|
||||
|
||||
Before:
|
||||
|
||||
translate('foo_html', :something => '<script>') # => "...<script>..."
|
||||
|
||||
After:
|
||||
|
||||
translate('foo_html', :something => '<script>') # => "...<script>..."
|
||||
|
||||
*Sergey Nartimov*
|
||||
|
||||
* Implement a workaround for a bug in ruby-1.9.3p0 where an error would be
|
||||
raised while attempting to convert a template from one encoding to another.
|
||||
|
||||
Please see http://redmine.ruby-lang.org/issues/5564 for details of the bug.
|
||||
|
||||
The workaround is to load all conversions into memory ahead of time, and will
|
||||
only happen if the ruby version is exactly 1.9.3p0. The hope is obviously
|
||||
that the underlying problem will be resolved in the next patchlevel release
|
||||
of 1.9.3.
|
||||
|
||||
* Fix assert_select_email to work on multipart and non-multipart emails as the method stopped working correctly in Rails 3.x due to changes in the new mail gem.
|
||||
|
||||
* Fix url_for when passed a hash to prevent additional options (eg. :host, :protocol) from being added to the hash after calling it.
|
||||
|
||||
|
||||
*Rails 3.0.10 (August 16, 2011)*
|
||||
|
||||
* Fixes an issue where cache sweepers with only after filters would have no
|
||||
controller object, it would raise undefined method controller_name for nil [jeroenj]
|
||||
|
||||
* Ensure status codes are logged when exceptions are raised.
|
||||
|
||||
* Subclasses of OutputBuffer are respected.
|
||||
|
||||
* Fixed ActionView::FormOptionsHelper#select with :multiple => false
|
||||
|
||||
* Avoid extra call to Cache#read in case of a fragment cache hit
|
||||
|
||||
*Rails 3.0.9 (June 16, 2011)*
|
||||
|
||||
* json_escape will now return a SafeBuffer string if it receives SafeBuffer string [tenderlove]
|
||||
|
||||
* Make sure escape_js returns SafeBuffer string if it receives SafeBuffer string [Prem Sichanugrist]
|
||||
|
||||
* Fix text helpers to work correctly with the new SafeBuffer restriction [Paul Gallagher, Arun Agrawal, Prem Sichanugrist]
|
||||
|
||||
|
||||
*Rails 3.0.8 (June 7, 2011)*
|
||||
|
||||
* It is prohibited to perform a in-place SafeBuffer mutation [tenderlove]
|
||||
|
||||
The old behavior of SafeBuffer allowed you to mutate string in place via
|
||||
method like `sub!`. These methods can add unsafe strings to a safe buffer,
|
||||
and the safe buffer will continue to be marked as safe.
|
||||
|
||||
An example problem would be something like this:
|
||||
|
||||
<%= link_to('hello world', @user).sub!(/hello/, params[:xss]) %>
|
||||
|
||||
In the above example, an untrusted string (`params[:xss]`) is added to the
|
||||
safe buffer returned by `link_to`, and the untrusted content is successfully
|
||||
sent to the client without being escaped. To prevent this from happening
|
||||
`sub!` and other similar methods will now raise an exception when they are called on a safe buffer.
|
||||
|
||||
In addition to the in-place versions, some of the versions of these methods which return a copy of the string will incorrectly mark strings as safe. For example:
|
||||
|
||||
<%= link_to('hello world', @user).sub(/hello/, params[:xss]) %>
|
||||
|
||||
The new versions will now ensure that *all* strings returned by these methods on safe buffers are marked unsafe.
|
||||
|
||||
You can read more about this change in http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2e516e7acc96c4fb
|
||||
|
||||
* Fixed github issue #342 with asset paths and relative roots.
|
||||
|
||||
|
||||
*Rails 3.0.7 (April 18, 2011)*
|
||||
|
||||
*No changes.
|
||||
|
||||
|
||||
*Rails 3.0.6 (April 5, 2011)
|
||||
|
||||
* Fixed XSS vulnerability in `auto_link`. `auto_link` no longer marks input as
|
||||
html safe. Please make sure that calls to auto_link() are wrapped in a
|
||||
sanitize(), or a raw() depending on the type of input passed to auto_link().
|
||||
For example:
|
||||
|
||||
<%= sanitize(auto_link(some_user_input)) %>
|
||||
|
||||
Thanks to Torben Schulz for reporting this. The fix can be found here:
|
||||
61ee3449674c591747db95f9b3472c5c3bd9e84d
|
||||
|
||||
* Fixes the output of `rake routes` to be correctly match to the behavior of the application, as the regular expression used to match the path is greedy and won't capture the format part by default [Prem Sichanugrist]
|
||||
|
||||
* Fixes an issue with number_to_human when converting values which are less than 1 but greater than -1 [Josh Kalderimis]
|
||||
|
||||
* Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. [Prem Sichanugrist, fxn]
|
||||
|
||||
* URL parameters which return nil for to_param are now removed from the query string [Andrew White]
|
||||
|
||||
* Don't allow i18n to change the minor version, version now set to ~> 0.5.0 [Santiago Pastorino]
|
||||
|
||||
* Make TranslationHelper#translate use the :rescue_format option in I18n 0.5.0 [Sven Fuchs]
|
||||
|
||||
* Fix regression: javascript_include_tag shouldn't raise if you register an expansion key with nil or [] value [Santiago Pastorino]
|
||||
|
||||
* Fix Action caching bug where an action that has a non-cacheable response always renders a nil response body. It now correctly renders the response body. [Cheah Chu Yeow]
|
||||
|
||||
|
||||
*Rails 3.0.5 (February 26, 2011)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.4 (February 8, 2011)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.3 (November 16, 2010)*
|
||||
|
||||
* When ActiveRecord::Base objects are sent to predicate methods, the id of the object should be sent to ARel, not the ActiveRecord::Base object.
|
||||
|
||||
* :constraints routing should only do sanity checks against regular expressions. String arguments are OK.
|
||||
|
||||
|
||||
*Rails 3.0.2 (November 15, 2010)*
|
||||
|
||||
* 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.
|
||||
|
||||
* No Changes, just a version bump.
|
||||
|
||||
*Rails 3.0.0 (August 29, 2010)*
|
||||
|
||||
@@ -2204,7 +2008,7 @@ superclass' view_paths. [Rick Olson]
|
||||
|
||||
* Update documentation for erb trim syntax. #5651 [matt@mattmargolis.net]
|
||||
|
||||
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com]
|
||||
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com, sebastien@goetzilla.info]
|
||||
|
||||
* Reset @html_document between requests so assert_tag works. #4810 [Jarkko Laine, easleydp@gmail.com]
|
||||
|
||||
@@ -2801,7 +2605,7 @@ superclass' view_paths. [Rick Olson]
|
||||
|
||||
* Provide support for decimal columns to form helpers. Closes #5672. [Dave Thomas]
|
||||
|
||||
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com]
|
||||
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com, sebastien@goetzilla.info]
|
||||
|
||||
* Reset @html_document between requests so assert_tag works. #4810 [Jarkko Laine, easleydp@gmail.com]
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/packagetask'
|
||||
require 'rubygems/package_task'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
desc "Default Task"
|
||||
task :default => :test
|
||||
@@ -35,7 +35,7 @@ end
|
||||
|
||||
spec = eval(File.read('actionpack.gemspec'))
|
||||
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
end
|
||||
|
||||
|
||||
@@ -17,13 +17,15 @@ Gem::Specification.new do |s|
|
||||
s.require_path = 'lib'
|
||||
s.requirements << 'none'
|
||||
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('activesupport', version)
|
||||
s.add_dependency('activemodel', version)
|
||||
s.add_dependency('builder', '~> 3.2.0')
|
||||
s.add_dependency('i18n', '~> 0.6.0')
|
||||
s.add_dependency('rack', '~> 1.4.1')
|
||||
s.add_dependency('rack-test', '~> 0.6.1')
|
||||
s.add_dependency('rack-mount', '~> 0.6.14')
|
||||
s.add_dependency('builder', '~> 2.1.2')
|
||||
s.add_dependency('i18n', '~> 0.4.1')
|
||||
s.add_dependency('rack', '~> 1.2.1')
|
||||
s.add_dependency('rack-test', '~> 0.5.4')
|
||||
s.add_dependency('rack-mount', '~> 0.6.12')
|
||||
s.add_dependency('tzinfo', '~> 0.3.23')
|
||||
s.add_dependency('erubis', '~> 2.7.0')
|
||||
s.add_dependency('erubis', '~> 2.6.6')
|
||||
end
|
||||
|
||||
@@ -9,11 +9,10 @@ module AbstractController
|
||||
# <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
|
||||
# using it directly, and subclasses (like ActionController::Base) are
|
||||
# expected to provide their own +render+ method, since rendering means
|
||||
# different things depending on the context.
|
||||
# different things depending on the context.
|
||||
class Base
|
||||
attr_internal :response_body
|
||||
attr_internal :action_name
|
||||
attr_internal :formats
|
||||
|
||||
include ActiveSupport::Configurable
|
||||
extend ActiveSupport::DescendantsTracker
|
||||
@@ -31,9 +30,10 @@ module AbstractController
|
||||
# A list of all internal methods for a controller. This finds the first
|
||||
# abstract superclass of a controller, and gets a list of all public
|
||||
# instance methods on that abstract class. Public instance methods of
|
||||
# a controller would normally be considered action methods, so methods
|
||||
# declared on abstract classes are being removed.
|
||||
# (ActionController::Metal and ActionController::Base are defined as abstract)
|
||||
# a controller would normally be considered action methods, so we
|
||||
# are removing those methods on classes declared as abstract
|
||||
# (ActionController::Metal and ActionController::Base are defined
|
||||
# as abstract)
|
||||
def internal_methods
|
||||
controller = self
|
||||
controller = controller.superclass until controller.abstract?
|
||||
@@ -164,8 +164,6 @@ module AbstractController
|
||||
action_missing(@_action_name)
|
||||
end
|
||||
|
||||
CVE_2014_0130 = Class.new(StandardError)
|
||||
|
||||
# Takes an action name and returns the name of the method that will
|
||||
# handle the action. In normal cases, this method returns the same
|
||||
# name as it receives. By default, if #method_for_action receives
|
||||
@@ -190,10 +188,6 @@ module AbstractController
|
||||
# * <tt>string</tt> - The name of the method that handles the action
|
||||
# * <tt>nil</tt> - No method name could be found. Raise ActionNotFound.
|
||||
def method_for_action(action_name)
|
||||
if action_name.include?("/")
|
||||
raise CVE_2014_0130
|
||||
end
|
||||
|
||||
if action_method?(action_name) then action_name
|
||||
elsif respond_to?(:action_missing, true) then "_handle_action_missing"
|
||||
end
|
||||
|
||||
@@ -13,8 +13,8 @@ module AbstractController
|
||||
|
||||
# Override AbstractController::Base's process_action to run the
|
||||
# process_action callbacks around the normal behavior.
|
||||
def process_action(method_name, *args)
|
||||
run_callbacks(:process_action, action_name) do
|
||||
def process_action(method_name)
|
||||
run_callbacks(:process_action, method_name) do
|
||||
super
|
||||
end
|
||||
end
|
||||
@@ -83,7 +83,6 @@ module AbstractController
|
||||
# for details on the allowed parameters.
|
||||
def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options}
|
||||
options[:if]=(Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
||||
set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before_filter, name, options)
|
||||
end # end
|
||||
end # end
|
||||
@@ -92,7 +91,6 @@ module AbstractController
|
||||
# for details on the allowed parameters.
|
||||
def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
|
||||
options[:if]=(Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
||||
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
|
||||
end # end
|
||||
end # end
|
||||
|
||||
@@ -235,10 +235,13 @@ module AbstractController
|
||||
controller_path
|
||||
end
|
||||
|
||||
# Creates a _layout method to be called by _default_layout .
|
||||
# Takes the specified layout and creates a _layout method to be called
|
||||
# by _default_layout
|
||||
#
|
||||
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
|
||||
# if nothing is found then try same procedure to find super class's layout.
|
||||
# If there is no explicit layout specified:
|
||||
# If a layout is found in the view paths with the controller's
|
||||
# name, return that string. Otherwise, use the superclass'
|
||||
# layout (which might also be implied)
|
||||
def _write_layout_method
|
||||
remove_possible_method(:_layout)
|
||||
|
||||
|
||||
@@ -14,15 +14,14 @@ module AbstractController
|
||||
# it will trigger the lookup_context and consequently expire the cache.
|
||||
# TODO Add some deprecation warnings to remove I18n.locale from controllers
|
||||
class I18nProxy < ::I18n::Config #:nodoc:
|
||||
attr_reader :original_config, :lookup_context
|
||||
attr_reader :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
|
||||
def initialize(i18n_config, lookup_context)
|
||||
@i18n_config, @lookup_context = i18n_config, lookup_context
|
||||
end
|
||||
|
||||
def locale
|
||||
@original_config.locale
|
||||
@i18n_config.locale
|
||||
end
|
||||
|
||||
def locale=(value)
|
||||
@@ -48,13 +47,13 @@ module AbstractController
|
||||
@view_context_class ||= begin
|
||||
controller = self
|
||||
Class.new(ActionView::Base) do
|
||||
if controller.respond_to?(:_routes)
|
||||
include controller._routes.url_helpers
|
||||
end
|
||||
|
||||
if controller.respond_to?(:_helpers)
|
||||
include controller._helpers
|
||||
|
||||
if controller.respond_to?(:_routes)
|
||||
include controller._routes.url_helpers
|
||||
end
|
||||
|
||||
# TODO: Fix RJS to not require this
|
||||
self.helpers = controller._helpers
|
||||
end
|
||||
|
||||
@@ -148,8 +148,6 @@ module ActionController
|
||||
#
|
||||
# In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed.
|
||||
#
|
||||
# Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
|
||||
#
|
||||
# == Calling multiple redirects or renders
|
||||
#
|
||||
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
|
||||
@@ -204,16 +202,16 @@ module ActionController
|
||||
HttpAuthentication::Digest::ControllerMethods,
|
||||
HttpAuthentication::Token::ControllerMethods,
|
||||
|
||||
# Add instrumentations hooks at the bottom, to ensure they instrument
|
||||
# all the methods properly.
|
||||
Instrumentation,
|
||||
|
||||
# Before callbacks should also be executed the earliest as possible, so
|
||||
# also include them at the bottom.
|
||||
AbstractController::Callbacks,
|
||||
|
||||
# The same with rescue, append it at the end to wrap as much as possible.
|
||||
Rescue,
|
||||
|
||||
# Add instrumentations hooks at the bottom, to ensure they instrument
|
||||
# all the methods properly.
|
||||
Instrumentation
|
||||
Rescue
|
||||
]
|
||||
|
||||
MODULES.each do |mod|
|
||||
|
||||
@@ -4,30 +4,29 @@ module ActionController #:nodoc:
|
||||
module Caching
|
||||
# Action caching is similar to page caching by the fact that the entire
|
||||
# output of the response is cached, but unlike page caching, every
|
||||
# request still goes through Action Pack. The key benefit of this is
|
||||
# that filters run before the cache is served, which allows for
|
||||
# authentication and other restrictions on whether someone is allowed
|
||||
# to execute such action. Example:
|
||||
# request still goes through the Action Pack. The key benefit
|
||||
# of this is that filters are run before the cache is served, which
|
||||
# allows for authentication and other restrictions on whether someone
|
||||
# is allowed to see the cache. Example:
|
||||
#
|
||||
# class ListsController < ApplicationController
|
||||
# before_filter :authenticate, :except => :public
|
||||
#
|
||||
# caches_page :public
|
||||
# caches_action :index, :show
|
||||
# caches_action :index, :show, :feed
|
||||
# end
|
||||
#
|
||||
# In this example, the +public+ action doesn't require authentication
|
||||
# so it's possible to use the faster page caching. On the other hand
|
||||
# +index+ and +show+ require authentication. They can still be cached,
|
||||
# but we need action caching for them.
|
||||
# In this example, the public action doesn't require authentication,
|
||||
# so it's possible to use the faster page caching method. But both
|
||||
# the show and feed action are to be shielded behind the authenticate
|
||||
# filter, so we need to implement those as action caches.
|
||||
#
|
||||
# Action caching uses fragment caching internally and an around
|
||||
# filter to do the job. The fragment cache is named according to
|
||||
# the host and path of the request. A page that is accessed at
|
||||
# <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
|
||||
# <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
|
||||
# differentiate between <tt>david.example.com/lists/</tt> and
|
||||
# <tt>jamis.example.com/lists/</tt> -- which is a helpful way of assisting
|
||||
# Action caching internally uses the fragment caching and an around
|
||||
# filter to do the job. The fragment cache is named according to both
|
||||
# the current host and the path. So a page that is accessed at
|
||||
# http://david.somewhere.com/lists/show/1 will result in a fragment named
|
||||
# "david.somewhere.com/lists/show/1". This allows the cacher to
|
||||
# differentiate between "david.somewhere.com/lists/" and
|
||||
# "jamis.somewhere.com/lists/" -- which is a helpful way of assisting
|
||||
# the subdomain-as-account-key pattern.
|
||||
#
|
||||
# Different representations of the same resource, e.g.
|
||||
@@ -39,23 +38,19 @@ module ActionController #:nodoc:
|
||||
# <tt>:action => 'list', :format => :xml</tt>.
|
||||
#
|
||||
# You can set modify the default action cache path by passing a
|
||||
# <tt>:cache_path</tt> option. This will be passed directly to
|
||||
# <tt>ActionCachePath.path_for</tt>. This is handy for actions with
|
||||
# multiple possible routes that should be cached differently. If a
|
||||
# block is given, it is called with the current controller instance.
|
||||
# :cache_path option. This will be passed directly to
|
||||
# ActionCachePath.path_for. This is handy for actions with multiple
|
||||
# possible routes that should be cached differently. If a block is
|
||||
# given, it is called with the current controller instance.
|
||||
#
|
||||
# And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
|
||||
# proc that specifies when the action should be cached.
|
||||
# And you can also use :if (or :unless) to pass a Proc that
|
||||
# specifies when the action should be cached.
|
||||
#
|
||||
# Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
|
||||
#
|
||||
# The following example depicts some of the points made above:
|
||||
# Finally, if you are using memcached, you can also pass :expires_in.
|
||||
#
|
||||
# class ListsController < ApplicationController
|
||||
# before_filter :authenticate, :except => :public
|
||||
#
|
||||
# caches_page :public
|
||||
#
|
||||
# caches_page :public
|
||||
# caches_action :index, :if => proc do |c|
|
||||
# !c.request.format.json? # cache if is not a JSON request
|
||||
# end
|
||||
@@ -63,28 +58,19 @@ module ActionController #:nodoc:
|
||||
# caches_action :show, :cache_path => { :project => 1 },
|
||||
# :expires_in => 1.hour
|
||||
#
|
||||
# caches_action :feed, :cache_path => proc do |c|
|
||||
# if c.params[:user_id]
|
||||
# c.send(:user_list_url,
|
||||
# c.params[:user_id], c.params[:id])
|
||||
# caches_action :feed, :cache_path => proc do |controller|
|
||||
# if controller.params[:user_id]
|
||||
# controller.send(:user_list_url,
|
||||
# controller.params[:user_id], controller.params[:id])
|
||||
# else
|
||||
# c.send(:list_url, c.params[:id])
|
||||
# controller.send(:list_url, controller.params[:id])
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# If you pass <tt>:layout => false</tt>, it will only cache your action
|
||||
# content. That's useful when your layout has dynamic information.
|
||||
# If you pass :layout => false, it will only cache your action
|
||||
# content. It is useful when your layout has dynamic information.
|
||||
#
|
||||
# Warning: If the format of the request is determined by the Accept HTTP
|
||||
# header the Content-Type of the cached response could be wrong because
|
||||
# no information about the MIME type is stored in the cache key. So, if
|
||||
# you first ask for MIME type M in the Accept header, a cache entry is
|
||||
# created, and then perform a second resquest to the same resource asking
|
||||
# for a different MIME type, you'd get the content cached for M.
|
||||
#
|
||||
# The <tt>:format</tt> parameter is taken into account though. The safest
|
||||
# way to cache by MIME type is to pass the format in the route.
|
||||
module Actions
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -103,14 +89,12 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def _save_fragment(name, options)
|
||||
return unless caching_allowed?
|
||||
|
||||
content = response_body
|
||||
content = content.join if content.is_a?(Array)
|
||||
|
||||
if caching_allowed?
|
||||
write_fragment(name, content, options)
|
||||
else
|
||||
content
|
||||
end
|
||||
write_fragment(name, content, options)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
@@ -71,9 +71,9 @@ module ActionController #:nodoc:
|
||||
|
||||
# Manually cache the +content+ in the key determined by +path+. Example:
|
||||
# cache_page "I'm the cached content", "/lists/show"
|
||||
def cache_page(content, path, extension = nil)
|
||||
def cache_page(content, path)
|
||||
return unless perform_caching
|
||||
path = page_cache_path(path, extension)
|
||||
path = page_cache_path(path)
|
||||
|
||||
instrument_page_cache :write_page, path do
|
||||
FileUtils.makedirs(File.dirname(path))
|
||||
@@ -98,16 +98,14 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
def page_cache_file(path, extension)
|
||||
def page_cache_file(path)
|
||||
name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
|
||||
unless (name.split('/').last || name).include? '.'
|
||||
name << (extension || self.page_cache_extension)
|
||||
end
|
||||
name << page_cache_extension unless (name.split('/').last || name).include? '.'
|
||||
return name
|
||||
end
|
||||
|
||||
def page_cache_path(path, extension = nil)
|
||||
page_cache_directory + page_cache_file(path, extension)
|
||||
def page_cache_path(path)
|
||||
page_cache_directory + page_cache_file(path)
|
||||
end
|
||||
|
||||
def instrument_page_cache(name, path)
|
||||
@@ -148,12 +146,7 @@ module ActionController #:nodoc:
|
||||
request.path
|
||||
end
|
||||
|
||||
|
||||
if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
|
||||
extension = ".#{type_symbol}"
|
||||
end
|
||||
|
||||
self.class.cache_page(content || response.body, path, extension)
|
||||
self.class.cache_page(content || response.body, path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -61,7 +61,6 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def after(controller)
|
||||
self.controller = controller
|
||||
callback(:after) if controller.perform_caching
|
||||
# Clean up, so that the controller can be collected after this request
|
||||
self.controller = nil
|
||||
|
||||
@@ -8,7 +8,7 @@ module ActionController
|
||||
"Please stop using it.", caller
|
||||
end
|
||||
|
||||
def relative_url_root=(value)
|
||||
def relative_url_root=
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root= is ineffective. " <<
|
||||
"Please stop using it.", caller
|
||||
end
|
||||
@@ -126,7 +126,7 @@ module ActionController
|
||||
# This was moved to a plugin
|
||||
def verify(*args)
|
||||
ActiveSupport::Deprecation.warn "verify was removed from Rails and is now available as a plugin. " \
|
||||
"Please install it with `rails plugin install git://github.com/sikachu/verification.git`.", caller
|
||||
"Please install it with `rails plugin install git://github.com/rails/verification.git`.", caller
|
||||
end
|
||||
|
||||
def exempt_from_layout(*)
|
||||
|
||||
@@ -16,11 +16,7 @@ module ActionController
|
||||
payload = event.payload
|
||||
additions = ActionController::Base.log_process_action(payload)
|
||||
|
||||
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 = "Completed #{payload[:status]} #{Rack::Utils::HTTP_STATUS_CODES[payload[:status]]} in %.0fms" % event.duration
|
||||
message << " (#{additions.join(" | ")})" unless additions.blank?
|
||||
|
||||
info(message)
|
||||
|
||||
@@ -12,7 +12,7 @@ module ActionController
|
||||
#
|
||||
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
|
||||
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
|
||||
def initialize(klass, *args, &block)
|
||||
def initialize(klass, *args)
|
||||
options = args.extract_options!
|
||||
@only = Array(options.delete(:only)).map(&:to_s)
|
||||
@except = Array(options.delete(:except)).map(&:to_s)
|
||||
@@ -112,11 +112,6 @@ module ActionController
|
||||
headers["Location"] = url
|
||||
end
|
||||
|
||||
# basic url_for that can be overridden for more robust functionality
|
||||
def url_for(string)
|
||||
string
|
||||
end
|
||||
|
||||
def status
|
||||
@_status
|
||||
end
|
||||
@@ -152,8 +147,8 @@ module ActionController
|
||||
super
|
||||
end
|
||||
|
||||
def self.use(*args, &block)
|
||||
middleware_stack.use(*args, &block)
|
||||
def self.use(*args)
|
||||
middleware_stack.use(*args)
|
||||
end
|
||||
|
||||
def self.middleware
|
||||
|
||||
@@ -60,7 +60,7 @@ module ActionController
|
||||
end
|
||||
|
||||
def method_for_action(action_name)
|
||||
super || (defined?(self.method_missing) && "_handle_method_missing")
|
||||
super || (respond_to?(:method_missing) && "_handle_method_missing")
|
||||
end
|
||||
|
||||
def performed?
|
||||
|
||||
@@ -2,6 +2,8 @@ module ActionController
|
||||
module Head
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionController::UrlFor
|
||||
|
||||
# Return a response that has no content (merely headers). The options
|
||||
# argument is interpreted to be a hash of header names and values.
|
||||
# This allows you to easily return a response that consists only of
|
||||
@@ -25,8 +27,8 @@ module ActionController
|
||||
|
||||
self.status = status
|
||||
self.location = url_for(location) if location
|
||||
self.content_type = Mime[formats.first] if formats
|
||||
self.content_type = Mime[formats.first]
|
||||
self.response_body = " "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -95,11 +95,12 @@ module ActionController
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# === Notes
|
||||
#
|
||||
# The +authenticate_or_request_with_http_digest+ block must return the user's password
|
||||
# or the ha1 digest hash so the framework can appropriately hash to check the user's
|
||||
# credentials. Returning +nil+ will cause authentication to fail.
|
||||
# NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately
|
||||
# hash to check the user's credentials. Returning +nil+ will cause authentication to fail.
|
||||
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
|
||||
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
|
||||
# authenticate as the user at this +realm+, but would not have the user's password to try using at
|
||||
# other sites.
|
||||
#
|
||||
# On shared hosts, Apache sometimes doesn't pass authentication headers to
|
||||
# FCGI instances. If your environment matches this description and you cannot
|
||||
@@ -217,10 +218,11 @@ module ActionController
|
||||
end
|
||||
|
||||
def decode_credentials(header)
|
||||
HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
|
||||
header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair|
|
||||
key, value = pair.split('=', 2)
|
||||
[key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
|
||||
end]
|
||||
hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
def authentication_header(controller, realm)
|
||||
|
||||
@@ -227,7 +227,7 @@ module ActionController #:nodoc:
|
||||
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
|
||||
|
||||
if response = retrieve_response_from_mimes(&block)
|
||||
options = resources.size == 1 ? {} : resources.extract_options!
|
||||
options = resources.extract_options!
|
||||
options.merge!(:default_response => response)
|
||||
(options.delete(:responder) || self.class.responder).call(self, resources, options)
|
||||
end
|
||||
|
||||
@@ -38,9 +38,6 @@ module ActionController
|
||||
# redirect_to :action=>'atom', :status => :moved_permanently
|
||||
# redirect_to post_url(@post), :status => 301
|
||||
# redirect_to :action=>'atom', :status => 302
|
||||
#
|
||||
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
|
||||
# integer, or a symbol representing the downcased, underscored and symbolized description.
|
||||
#
|
||||
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
|
||||
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
|
||||
@@ -51,7 +48,8 @@ module ActionController
|
||||
# redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
|
||||
# redirect_to { :action=>'atom' }, :alert => "Something serious happened"
|
||||
#
|
||||
# When using <tt>redirect_to :back</tt>, if there is no referrer, RedirectBackError will be raised. You may specify some fallback
|
||||
# When using <tt>redirect_to :back</tt>, if there is no referrer,
|
||||
# RedirectBackError will be raised. You may specify some fallback
|
||||
# behavior for this case by rescuing RedirectBackError.
|
||||
def redirect_to(options = {}, response_status = {}) #:doc:
|
||||
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
|
||||
@@ -87,7 +85,7 @@ module ActionController
|
||||
refer
|
||||
else
|
||||
url_for(options)
|
||||
end.gsub(/[\0\r\n]/, '')
|
||||
end.gsub(/[\r\n]/, '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -71,7 +71,7 @@ module ActionController
|
||||
end
|
||||
|
||||
add :json do |json, options|
|
||||
json = json.to_json(options) unless json.kind_of?(String)
|
||||
json = ActiveSupport::JSON.encode(json, options) unless json.respond_to?(:to_str)
|
||||
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
|
||||
self.content_type ||= Mime::JSON
|
||||
self.response_body = json
|
||||
|
||||
@@ -7,7 +7,7 @@ module ActionController
|
||||
|
||||
# Before processing, set the request formats in current controller formats.
|
||||
def process_action(*) #:nodoc:
|
||||
self.formats = request.formats.map { |x| x.ref }
|
||||
self.formats = request.formats.map { |x| x.to_sym }
|
||||
super
|
||||
end
|
||||
|
||||
|
||||
@@ -71,42 +71,39 @@ module ActionController #:nodoc:
|
||||
# class FooController < ApplicationController
|
||||
# protect_from_forgery :except => :index
|
||||
#
|
||||
# You can disable csrf protection on controller-by-controller basis:
|
||||
#
|
||||
# skip_before_filter :verify_authenticity_token
|
||||
#
|
||||
# It can also be disabled for specific controller actions:
|
||||
#
|
||||
# skip_before_filter :verify_authenticity_token, :except => [:create]
|
||||
# # you can disable csrf protection on controller-by-controller basis:
|
||||
# skip_before_filter :verify_authenticity_token
|
||||
# end
|
||||
#
|
||||
# Valid Options:
|
||||
#
|
||||
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
|
||||
def protect_from_forgery(options = {})
|
||||
self.request_forgery_protection_token ||= :authenticity_token
|
||||
prepend_before_filter :verify_authenticity_token, options
|
||||
before_filter :verify_authenticity_token, options
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
||||
def verify_authenticity_token
|
||||
verified_request? || handle_unverified_request
|
||||
|
||||
def protect_from_forgery(options = {})
|
||||
self.request_forgery_protection_token ||= :authenticity_token
|
||||
before_filter :verify_authenticity_token, options
|
||||
end
|
||||
|
||||
def handle_unverified_request
|
||||
reset_session
|
||||
# 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)
|
||||
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.get? ||
|
||||
form_authenticity_token == params[request_forgery_protection_token] ||
|
||||
form_authenticity_token == request.headers['X-CSRF-Token']
|
||||
!protect_against_forgery? || request.forgery_whitelisted? ||
|
||||
form_authenticity_token == params[request_forgery_protection_token]
|
||||
end
|
||||
|
||||
# Sets the token value for the current session.
|
||||
|
||||
@@ -161,8 +161,6 @@ module ActionController #:nodoc:
|
||||
display resource.errors, :status => :unprocessable_entity
|
||||
elsif post?
|
||||
display resource, :status => :created, :location => api_location
|
||||
elsif has_empty_resource_definition?
|
||||
display empty_resource, :status => :ok
|
||||
else
|
||||
head :ok
|
||||
end
|
||||
@@ -223,23 +221,5 @@ module ActionController #:nodoc:
|
||||
def default_action
|
||||
@action ||= ACTIONS_FOR_VERBS[request.request_method_symbol]
|
||||
end
|
||||
|
||||
# Check whether resource needs a specific definition of empty resource to be valid
|
||||
#
|
||||
def has_empty_resource_definition?
|
||||
respond_to?("empty_#{format}_resource", true)
|
||||
end
|
||||
|
||||
# Delegate to proper empty resource method
|
||||
#
|
||||
def empty_resource
|
||||
send("empty_#{format}_resource")
|
||||
end
|
||||
|
||||
# Return a valid empty JSON resource
|
||||
#
|
||||
def empty_json_resource
|
||||
"{}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,13 +40,6 @@ module ActionController
|
||||
ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
|
||||
end
|
||||
|
||||
def process(*args)
|
||||
@partials = Hash.new(0)
|
||||
@templates = Hash.new(0)
|
||||
@layouts = Hash.new(0)
|
||||
super
|
||||
end
|
||||
|
||||
# Asserts that the request was rendered with the appropriate template file or partials.
|
||||
#
|
||||
# ==== Examples
|
||||
@@ -99,11 +92,11 @@ module ActionController
|
||||
elsif options.key?(:layout)
|
||||
msg = build_message(message,
|
||||
"expecting layout <?> but action rendered <?>",
|
||||
options[:layout], @layouts.keys)
|
||||
expected_layout, @layouts.keys)
|
||||
|
||||
case layout = options[:layout]
|
||||
when String
|
||||
assert(@layouts.include?(layout), msg)
|
||||
assert(@layouts.include?(expected_layout), msg)
|
||||
when Regexp
|
||||
assert(@layouts.any? {|l| l =~ layout }, msg)
|
||||
when nil
|
||||
@@ -146,9 +139,7 @@ module ActionController
|
||||
if value.is_a? Fixnum
|
||||
value = value.to_s
|
||||
elsif value.is_a? Array
|
||||
value = Result.new(value.map { |v| v.is_a?(String) ? v.dup : v })
|
||||
elsif value.is_a? String
|
||||
value = value.dup
|
||||
value = Result.new(value)
|
||||
end
|
||||
|
||||
if extra_keys.include?(key.to_sym)
|
||||
|
||||
@@ -38,14 +38,18 @@ module HTML #:nodoc:
|
||||
private
|
||||
|
||||
def keys_to_strings(hash)
|
||||
Hash[hash.keys.map {|k| [k.to_s, hash[k]]}]
|
||||
hash.keys.inject({}) do |h,k|
|
||||
h[k.to_s] = hash[k]
|
||||
h
|
||||
end
|
||||
end
|
||||
|
||||
def keys_to_symbols(hash)
|
||||
Hash[hash.keys.map do |k|
|
||||
hash.keys.inject({}) do |h,k|
|
||||
raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
|
||||
[k.to_sym, hash[k]]
|
||||
end]
|
||||
h[k.to_sym] = hash[k]
|
||||
h
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -73,7 +77,9 @@ module HTML #:nodoc:
|
||||
|
||||
# Return a textual representation of the node.
|
||||
def to_s
|
||||
@children.join()
|
||||
s = ""
|
||||
@children.each { |child| s << child.to_s }
|
||||
s
|
||||
end
|
||||
|
||||
# Return false (subclasses must override this to provide specific matching
|
||||
@@ -156,7 +162,7 @@ module HTML #:nodoc:
|
||||
end
|
||||
|
||||
closing = ( scanner.scan(/\//) ? :close : nil )
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[^\s!>\/]+/)
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
|
||||
name.downcase!
|
||||
|
||||
unless closing
|
||||
|
||||
@@ -5,10 +5,10 @@ require 'active_support/core_ext/object/duplicable'
|
||||
module ActionDispatch
|
||||
module Http
|
||||
# Allows you to specify sensitive parameters which will be replaced from
|
||||
# the request log by looking in the query string of the request and all
|
||||
# subhashes of the params hash to filter. If a block is given, each key and
|
||||
# value of the params hash and all subhashes is passed to it, the value
|
||||
# or key can be replaced using String#replace or similar method.
|
||||
# the request log by looking in all subhashes of the param hash for keys
|
||||
# to filter. If a block is given, each key and value of the parameter
|
||||
# hash and all subhashes is passed to it, the value or key can be replaced
|
||||
# using String#replace or similar method.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
@@ -26,6 +26,8 @@ module ActionDispatch
|
||||
module FilterParameters
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@parameter_filter_for = {}
|
||||
|
||||
# Return a hash of parameters with all sensitive data replaced.
|
||||
def filtered_parameters
|
||||
@filtered_parameters ||= parameter_filter.filter(parameters)
|
||||
@@ -36,11 +38,6 @@ module ActionDispatch
|
||||
@filtered_env ||= env_filter.filter(@env)
|
||||
end
|
||||
|
||||
# Reconstructed a path with all sensitive GET parameters replaced.
|
||||
def filtered_path
|
||||
@filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def parameter_filter
|
||||
@@ -52,15 +49,7 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def parameter_filter_for(filters)
|
||||
ParameterFilter.new(filters)
|
||||
end
|
||||
|
||||
KV_RE = '[^&;=]+'
|
||||
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
|
||||
def filtered_query_string
|
||||
query_string.gsub(PAIR_RE) do |_|
|
||||
parameter_filter.filter([[$1, $2]]).first.join("=")
|
||||
end
|
||||
@@parameter_filter_for[filters] ||= ParameterFilter.new(filters)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -36,7 +36,7 @@ module ActionDispatch
|
||||
#
|
||||
# GET /posts/5.xml | request.format => Mime::XML
|
||||
# GET /posts/5.xhtml | request.format => Mime::HTML
|
||||
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
|
||||
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
|
||||
#
|
||||
def format(view_path = [])
|
||||
formats.first
|
||||
|
||||
@@ -176,11 +176,7 @@ module Mime
|
||||
end
|
||||
|
||||
def to_sym
|
||||
@symbol
|
||||
end
|
||||
|
||||
def ref
|
||||
to_sym || to_s
|
||||
@symbol || @string.to_sym
|
||||
end
|
||||
|
||||
def ===(list)
|
||||
|
||||
@@ -2,10 +2,8 @@ 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'
|
||||
require 'action_dispatch/http/headers'
|
||||
|
||||
module ActionDispatch
|
||||
@@ -46,24 +44,8 @@ module ActionDispatch
|
||||
@env.key?(key)
|
||||
end
|
||||
|
||||
# List of HTTP request methods from the following RFCs:
|
||||
# Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)
|
||||
# HTTP Extensions for Distributed Authoring -- WEBDAV (http://www.ietf.org/rfc/rfc2518.txt)
|
||||
# Versioning Extensions to WebDAV (http://www.ietf.org/rfc/rfc3253.txt)
|
||||
# Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
|
||||
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
|
||||
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
|
||||
# PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
|
||||
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
|
||||
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
|
||||
RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
|
||||
RFC3648 = %w(ORDERPATCH)
|
||||
RFC3744 = %w(ACL)
|
||||
RFC5323 = %w(SEARCH)
|
||||
RFC5789 = %w(PATCH)
|
||||
|
||||
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
|
||||
HTTP_METHOD_LOOKUP = Hash.new { |h, m| h[m] = m.underscore.to_sym if HTTP_METHODS.include?(m) }
|
||||
HTTP_METHODS = %w(get head put post delete options)
|
||||
HTTP_METHOD_LOOKUP = HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h }
|
||||
|
||||
# Returns the HTTP \method that the application should see.
|
||||
# In the case where the \method was overridden by a middleware
|
||||
@@ -142,9 +124,8 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def forgery_whitelisted?
|
||||
get?
|
||||
get? || xhr? || content_mime_type.nil? || !content_mime_type.verify_request?
|
||||
end
|
||||
deprecate :forgery_whitelisted? => "it is just an alias for 'get?' now, update your code"
|
||||
|
||||
def media_type
|
||||
content_mime_type.to_s
|
||||
@@ -218,7 +199,7 @@ module ActionDispatch
|
||||
# TODO This should be broken apart into AD::Request::Session and probably
|
||||
# be included by the session middleware.
|
||||
def reset_session
|
||||
session.destroy if session && session.respond_to?(:destroy)
|
||||
session.destroy if session
|
||||
self.session = {}
|
||||
@env['action_dispatch.request.flash_hash'] = nil
|
||||
end
|
||||
@@ -257,30 +238,5 @@ module ActionDispatch
|
||||
def local?
|
||||
LOCALHOST.any? { |local_ip| local_ip === remote_addr && local_ip === remote_ip }
|
||||
end
|
||||
|
||||
# Remove nils from the params hash
|
||||
def deep_munge(hash)
|
||||
hash.each do |k, v|
|
||||
case v
|
||||
when Array
|
||||
if v.size > 0 && v.all?(&:nil?)
|
||||
hash[k] = nil
|
||||
next
|
||||
end
|
||||
v.grep(Hash) { |x| deep_munge(x) }
|
||||
v.compact!
|
||||
when Hash
|
||||
deep_munge(v)
|
||||
end
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def parse_query(qs)
|
||||
deep_munge(super)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,26 +4,27 @@ require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
|
||||
module ActionDispatch # :nodoc:
|
||||
# Represents an HTTP response generated by a controller action. Use it to
|
||||
# retrieve the current state of the response, or customize the response. It can
|
||||
# either represent a real HTTP response (i.e. one that is meant to be sent
|
||||
# back to the web browser) or a TestResponse (i.e. one that is generated
|
||||
# from integration tests).
|
||||
# Represents an HTTP response generated by a controller action. One can use
|
||||
# an ActionDispatch::Response object to retrieve the current state
|
||||
# of the response, or customize the response. An Response object can
|
||||
# either represent a "real" HTTP response (i.e. one that is meant to be sent
|
||||
# back to the web browser) or a test response (i.e. one that is generated
|
||||
# from integration tests). See CgiResponse and TestResponse, respectively.
|
||||
#
|
||||
# \Response is mostly a Ruby on \Rails framework implementation detail, and
|
||||
# Response is mostly a Ruby on Rails framework implement detail, and
|
||||
# should never be used directly in controllers. Controllers should use the
|
||||
# methods defined in ActionController::Base instead. For example, if you want
|
||||
# to set the HTTP response's content MIME type, then use
|
||||
# ActionControllerBase#headers instead of Response#headers.
|
||||
#
|
||||
# Nevertheless, integration tests may want to inspect controller responses in
|
||||
# more detail, and that's when \Response can be useful for application
|
||||
# more detail, and that's when Response can be useful for application
|
||||
# developers. Integration test methods such as
|
||||
# ActionDispatch::Integration::Session#get and
|
||||
# ActionDispatch::Integration::Session#post return objects of type
|
||||
# TestResponse (which are of course also of type \Response).
|
||||
# TestResponse (which are of course also of type Response).
|
||||
#
|
||||
# For example, the following demo integration test prints the body of the
|
||||
# For example, the following demo integration "test" prints the body of the
|
||||
# controller response to the console:
|
||||
#
|
||||
# class DemoControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
@@ -1,45 +1,33 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionDispatch
|
||||
module Http
|
||||
class UploadedFile
|
||||
attr_accessor :original_filename, :content_type, :tempfile, :headers
|
||||
|
||||
def initialize(hash)
|
||||
@original_filename = encode_filename(hash[:filename])
|
||||
@content_type = hash[:type]
|
||||
@headers = hash[:head]
|
||||
@tempfile = hash[:tempfile]
|
||||
raise(ArgumentError, ':tempfile is required') unless @tempfile
|
||||
end
|
||||
|
||||
def open
|
||||
@tempfile.open
|
||||
end
|
||||
|
||||
def path
|
||||
@tempfile.path
|
||||
end
|
||||
|
||||
def read(*args)
|
||||
@tempfile.read(*args)
|
||||
end
|
||||
|
||||
def rewind
|
||||
@tempfile.rewind
|
||||
end
|
||||
|
||||
def size
|
||||
@tempfile.size
|
||||
end
|
||||
|
||||
private
|
||||
def encode_filename(filename)
|
||||
# Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
|
||||
if "ruby".encoding_aware? && filename
|
||||
filename.force_encoding("UTF-8").encode!
|
||||
else
|
||||
filename
|
||||
module UploadedFile
|
||||
def self.extended(object)
|
||||
object.class_eval do
|
||||
attr_accessor :original_path, :content_type
|
||||
alias_method :local_path, :path if method_defined?(:path)
|
||||
end
|
||||
end
|
||||
|
||||
# Take the basename of the upload's original filename.
|
||||
# This handles the full Windows paths given by Internet Explorer
|
||||
# (and perhaps other broken user agents) without affecting
|
||||
# those which give the lone filename.
|
||||
# The Windows regexp is adapted from Perl's File::Basename.
|
||||
def original_filename
|
||||
unless defined? @original_filename
|
||||
@original_filename =
|
||||
unless original_path.blank?
|
||||
if original_path =~ /^(?:.*[:\\\/])?(.*)/m
|
||||
$1
|
||||
else
|
||||
File.basename original_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@original_filename
|
||||
end
|
||||
end
|
||||
|
||||
module Upload
|
||||
@@ -47,7 +35,11 @@ module ActionDispatch
|
||||
# file upload hash with UploadedFile objects
|
||||
def normalize_parameters(value)
|
||||
if Hash === value && value.has_key?(:tempfile)
|
||||
UploadedFile.new(value)
|
||||
upload = value[:tempfile]
|
||||
upload.extend(UploadedFile)
|
||||
upload.original_path = value[:filename]
|
||||
upload.content_type = value[:type]
|
||||
upload
|
||||
else
|
||||
super
|
||||
end
|
||||
@@ -55,4 +47,4 @@ module ActionDispatch
|
||||
private :normalize_parameters
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -16,23 +16,17 @@ module ActionDispatch
|
||||
# Examples for writing:
|
||||
#
|
||||
# # Sets a simple session cookie.
|
||||
# # This cookie will be deleted when the user's browser is closed.
|
||||
# cookies[:user_name] = "david"
|
||||
#
|
||||
# # Assign an array of values to a cookie.
|
||||
# cookies[:lat_lon] = [47.68, -122.37]
|
||||
#
|
||||
# # Sets a cookie that expires in 1 hour.
|
||||
# cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
|
||||
#
|
||||
# # Sets a signed cookie, which prevents a user from tampering with its value.
|
||||
# # The cookie is signed by your app's <tt>config.secret_token</tt> value.
|
||||
# # Rails generates this value by default when you create a new Rails app.
|
||||
# cookies.signed[:user_id] = current_user.id
|
||||
# # You must specify a value in ActionController::Base.cookie_verifier_secret.
|
||||
# cookies.signed[:remember_me] = [current_user.id, current_user.salt]
|
||||
#
|
||||
# # Sets a "permanent" cookie (which expires in 20 years from now).
|
||||
# cookies.permanent[:login] = "XJ-122"
|
||||
#
|
||||
# # You can also chain these methods:
|
||||
# cookies.permanent.signed[:login] = "XJ-122"
|
||||
#
|
||||
@@ -40,7 +34,6 @@ module ActionDispatch
|
||||
#
|
||||
# cookies[:user_name] # => "david"
|
||||
# cookies.size # => 2
|
||||
# cookies[:lat_lon] # => [47.68, -122.37]
|
||||
#
|
||||
# Example for deleting:
|
||||
#
|
||||
@@ -105,19 +98,17 @@ module ActionDispatch
|
||||
def self.build(request)
|
||||
secret = request.env[TOKEN_KEY]
|
||||
host = request.host
|
||||
secure = request.ssl?
|
||||
|
||||
new(secret, host, secure).tap do |hash|
|
||||
new(secret, host).tap do |hash|
|
||||
hash.update(request.cookies)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(secret = nil, host = nil, secure = false)
|
||||
def initialize(secret = nil, host = nil)
|
||||
@secret = secret
|
||||
@set_cookies = {}
|
||||
@delete_cookies = {}
|
||||
@host = host
|
||||
@secure = secure
|
||||
|
||||
super()
|
||||
end
|
||||
@@ -202,15 +193,9 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def write(headers)
|
||||
@set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) }
|
||||
@set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) }
|
||||
@delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def write_cookie?(cookie)
|
||||
@secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development?
|
||||
end
|
||||
end
|
||||
|
||||
class PermanentCookieJar < CookieJar #:nodoc:
|
||||
@@ -282,7 +267,7 @@ module ActionDispatch
|
||||
"integrity hash for cookie session data. Use " +
|
||||
"config.secret_token = \"some secret phrase of at " +
|
||||
"least #{SECRET_MIN_LENGTH} characters\"" +
|
||||
"in config/initializers/secret_token.rb"
|
||||
"in config/application.rb"
|
||||
end
|
||||
|
||||
if secret.length < SECRET_MIN_LENGTH
|
||||
|
||||
@@ -38,7 +38,7 @@ module ActionDispatch
|
||||
when Proc
|
||||
strategy.call(request.raw_post)
|
||||
when :xml_simple, :xml_node
|
||||
data = request.deep_munge(Hash.from_xml(request.body.read) || {})
|
||||
data = Hash.from_xml(request.body.read) || {}
|
||||
request.body.rewind if request.body.respond_to?(:rewind)
|
||||
data.with_indifferent_access
|
||||
when :yaml
|
||||
@@ -47,7 +47,7 @@ module ActionDispatch
|
||||
data = ActiveSupport::JSON.decode(request.body)
|
||||
request.body.rewind if request.body.respond_to?(:rewind)
|
||||
data = {:_json => data} unless data.is_a?(Hash)
|
||||
request.deep_munge(data).with_indifferent_access
|
||||
data.with_indifferent_access
|
||||
else
|
||||
false
|
||||
end
|
||||
|
||||
@@ -43,20 +43,20 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def call(env)
|
||||
begin
|
||||
status, headers, body = @app.call(env)
|
||||
exception = nil
|
||||
status, headers, body = @app.call(env)
|
||||
|
||||
# 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
|
||||
# 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}"
|
||||
end
|
||||
|
||||
exception ? render_exception(env, exception) : [status, headers, body]
|
||||
[status, headers, body]
|
||||
rescue Exception => exception
|
||||
raise exception if env['action_dispatch.show_exceptions'] == false
|
||||
render_exception(env, exception)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<h1>
|
||||
<%=h @exception.class.to_s %>
|
||||
<% if @request.parameters['controller'] %>
|
||||
in <%=h @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
|
||||
in <%=h @request.parameters['controller'].humanize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
|
||||
<% end %>
|
||||
</h1>
|
||||
<pre><%=h @exception.message %></pre>
|
||||
|
||||
@@ -8,5 +8,10 @@ module ActionDispatch
|
||||
config.action_dispatch.ip_spoofing_check = true
|
||||
config.action_dispatch.show_exceptions = true
|
||||
config.action_dispatch.best_standards_support = true
|
||||
|
||||
# Prepare dispatcher callbacks and run 'prepare' callbacks
|
||||
initializer "action_dispatch.prepare_dispatcher" do |app|
|
||||
ActionDispatch::Callbacks.to_prepare { app.routes_reloader.execute_if_updated }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,11 +2,31 @@ require 'active_support/core_ext/object/to_param'
|
||||
require 'active_support/core_ext/regexp'
|
||||
|
||||
module ActionDispatch
|
||||
# = Routing
|
||||
#
|
||||
# The routing module provides URL rewriting in native Ruby. It's a way to
|
||||
# redirect incoming requests to controllers and actions. This replaces
|
||||
# mod_rewrite rules. Best of all, Rails' \Routing works with any web server.
|
||||
# mod_rewrite rules. Best of all, Rails' Routing works with any web server.
|
||||
# Routes are defined in <tt>config/routes.rb</tt>.
|
||||
#
|
||||
# Consider the following route, which you will find commented out at the
|
||||
# bottom of your generated <tt>config/routes.rb</tt>:
|
||||
#
|
||||
# match ':controller(/:action(/:id(.:format)))'
|
||||
#
|
||||
# This route states that it expects requests to consist of a
|
||||
# <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
|
||||
# turn is followed optionally by an <tt>:id</tt>, which in turn is followed
|
||||
# optionally by a <tt>:format</tt>
|
||||
#
|
||||
# Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end
|
||||
# up with:
|
||||
#
|
||||
# params = { :controller => 'blog',
|
||||
# :action => 'edit',
|
||||
# :id => '22'
|
||||
# }
|
||||
#
|
||||
# Think of creating routes as drawing a map for your requests. The map tells
|
||||
# them where to go based on some predefined pattern:
|
||||
#
|
||||
@@ -23,39 +43,6 @@ module ActionDispatch
|
||||
#
|
||||
# Other names simply map to a parameter as in the case of <tt>:id</tt>.
|
||||
#
|
||||
# == Resources
|
||||
#
|
||||
# Resource routing allows you to quickly declare all of the common routes
|
||||
# for a given resourceful controller. Instead of declaring separate routes
|
||||
# for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
|
||||
# actions, a resourceful route declares them in a single line of code:
|
||||
#
|
||||
# resources :photos
|
||||
#
|
||||
# Sometimes, you have a resource that clients always look up without
|
||||
# referencing an ID. A common example, /profile always shows the profile of
|
||||
# the currently logged in user. In this case, you can use a singular resource
|
||||
# to map /profile (rather than /profile/:id) to the show action.
|
||||
#
|
||||
# resource :profile
|
||||
#
|
||||
# It's common to have resources that are logically children of other
|
||||
# resources:
|
||||
#
|
||||
# resources :magazines do
|
||||
# resources :ads
|
||||
# end
|
||||
#
|
||||
# You may wish to organize groups of controllers under a namespace. Most
|
||||
# commonly, you might group a number of administrative controllers under
|
||||
# an +admin+ namespace. You would place these controllers under the
|
||||
# app/controllers/admin directory, and you can group them together in your
|
||||
# router:
|
||||
#
|
||||
# namespace "admin" do
|
||||
# resources :posts, :comments
|
||||
# end
|
||||
#
|
||||
# == Named routes
|
||||
#
|
||||
# Routes can be named by passing an <tt>:as</tt> option,
|
||||
@@ -144,30 +131,6 @@ module ActionDispatch
|
||||
# Encoding regular expression modifiers are silently ignored. The
|
||||
# match will always use the default encoding or ASCII.
|
||||
#
|
||||
# == Default route
|
||||
#
|
||||
# Consider the following route, which you will find commented out at the
|
||||
# bottom of your generated <tt>config/routes.rb</tt>:
|
||||
#
|
||||
# match ':controller(/:action(/:id(.:format)))'
|
||||
#
|
||||
# This route states that it expects requests to consist of a
|
||||
# <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
|
||||
# turn is followed optionally by an <tt>:id</tt>, which in turn is followed
|
||||
# optionally by a <tt>:format</tt>.
|
||||
#
|
||||
# Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end
|
||||
# up with:
|
||||
#
|
||||
# params = { :controller => 'blog',
|
||||
# :action => 'edit',
|
||||
# :id => '22'
|
||||
# }
|
||||
#
|
||||
# By not relying on default routes, you improve the security of your
|
||||
# application since not all controller actions, which includes actions you
|
||||
# might add at a later time, are exposed by default.
|
||||
#
|
||||
# == HTTP Methods
|
||||
#
|
||||
# Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
|
||||
@@ -197,20 +160,6 @@ module ActionDispatch
|
||||
# however if your route needs to respond to more than one HTTP method (or all methods) then using the
|
||||
# <tt>:via</tt> option on <tt>match</tt> is preferable.
|
||||
#
|
||||
# == External redirects
|
||||
#
|
||||
# You can redirect any path to another path using the redirect helper in your router:
|
||||
#
|
||||
# match "/stories" => redirect("/posts")
|
||||
#
|
||||
# == Routing to Rack Applications
|
||||
#
|
||||
# Instead of a String, like <tt>posts#index</tt>, which corresponds to the
|
||||
# index action in the PostsController, you can specify any Rack application
|
||||
# as the endpoint for a matcher:
|
||||
#
|
||||
# match "/application.js" => Sprockets
|
||||
#
|
||||
# == Reloading routes
|
||||
#
|
||||
# You can reload routes if you feel you must:
|
||||
@@ -259,9 +208,7 @@ module ActionDispatch
|
||||
#
|
||||
# == View a list of all your routes
|
||||
#
|
||||
# rake routes
|
||||
#
|
||||
# Target specific controllers by prefixing the command with <tt>CONTROLLER=x</tt>.
|
||||
# Run <tt>rake routes</tt>.
|
||||
#
|
||||
module Routing
|
||||
autoload :DeprecatedMapper, 'action_dispatch/routing/deprecated_mapper'
|
||||
|
||||
@@ -31,8 +31,8 @@ module ActionDispatch
|
||||
|
||||
class DeprecatedMapper #:nodoc:
|
||||
def initialize(set) #:nodoc:
|
||||
# ActiveSupport::Deprecation.warn "You are using the old router DSL which will be removed in Rails 3.1. " <<
|
||||
# "Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
|
||||
ActiveSupport::Deprecation.warn "You are using the old router DSL which will be removed in Rails 3.1. " <<
|
||||
"Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
|
||||
@set = set
|
||||
end
|
||||
|
||||
@@ -41,7 +41,6 @@ module ActionDispatch
|
||||
|
||||
if conditions = options.delete(:conditions)
|
||||
conditions = conditions.dup
|
||||
subdomain = conditions.delete(:subdomain)
|
||||
method = [conditions.delete(:method)].flatten.compact
|
||||
method.map! { |m|
|
||||
m = m.to_s.upcase
|
||||
@@ -124,7 +123,6 @@ module ActionDispatch
|
||||
conditions = {}
|
||||
conditions[:request_method] = method if method
|
||||
conditions[:path_info] = path if path
|
||||
conditions[:subdomain] = subdomain if subdomain
|
||||
|
||||
@set.add_route(app, conditions, requirements, defaults, name)
|
||||
end
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
require 'erb'
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/inflector'
|
||||
|
||||
module ActionDispatch
|
||||
module Routing
|
||||
@@ -21,22 +20,18 @@ module ActionDispatch
|
||||
@app, @constraints, @request = app, constraints, request
|
||||
end
|
||||
|
||||
def matches?(env)
|
||||
def call(env)
|
||||
req = @request.new(env)
|
||||
|
||||
@constraints.each { |constraint|
|
||||
if constraint.respond_to?(:matches?) && !constraint.matches?(req)
|
||||
return false
|
||||
return [ 404, {'X-Cascade' => 'pass'}, [] ]
|
||||
elsif constraint.respond_to?(:call) && !constraint.call(*constraint_args(constraint, req))
|
||||
return false
|
||||
return [ 404, {'X-Cascade' => 'pass'}, [] ]
|
||||
end
|
||||
}
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def call(env)
|
||||
matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -67,21 +62,10 @@ module ActionDispatch
|
||||
if using_match_shorthand?(path_without_format, @options)
|
||||
to_shorthand = @options[:to].blank?
|
||||
@options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1')
|
||||
@options[:as] ||= Mapper.normalize_name(path_without_format)
|
||||
end
|
||||
|
||||
@options.merge!(default_controller_and_action(to_shorthand))
|
||||
|
||||
requirements.each do |name, requirement|
|
||||
# segment_keys.include?(k.to_s) || k == :controller
|
||||
next unless Regexp === requirement && !constraints[name]
|
||||
|
||||
if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
|
||||
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
|
||||
end
|
||||
if requirement.multiline?
|
||||
raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# match "account/overview"
|
||||
@@ -106,7 +90,7 @@ module ActionDispatch
|
||||
if @options[:format] == false
|
||||
@options.delete(:format)
|
||||
path
|
||||
elsif path.include?(":format") || path.match(/\*[^\/]+$/)
|
||||
elsif path.include?(":format")
|
||||
path
|
||||
else
|
||||
"#{path}(.:format)"
|
||||
@@ -156,10 +140,6 @@ module ActionDispatch
|
||||
controller = [@scope[:module], controller].compact.join("/").presence
|
||||
end
|
||||
|
||||
if controller.is_a?(String) && controller =~ %r{\A/}
|
||||
raise ArgumentError, "controller name should not start with a slash"
|
||||
end
|
||||
|
||||
controller = controller.to_s unless controller.is_a?(Regexp)
|
||||
action = action.to_s unless action.is_a?(Regexp)
|
||||
|
||||
@@ -194,8 +174,8 @@ module ActionDispatch
|
||||
|
||||
def request_method_condition
|
||||
if via = @options[:via]
|
||||
via = Array(via).map { |m| m.to_s.dasherize.upcase }
|
||||
{ :request_method => %r[^#{via.join('|')}$] }
|
||||
via = Array(via).map { |m| m.to_s.upcase }
|
||||
{ :request_method => Regexp.union(*via) }
|
||||
else
|
||||
{ }
|
||||
end
|
||||
@@ -246,51 +226,16 @@ module ActionDispatch
|
||||
@set = set
|
||||
end
|
||||
|
||||
# You can specify what Rails should route "/" to with the root method:
|
||||
#
|
||||
# root :to => 'pages#main'
|
||||
#
|
||||
# For options, see the +match+ method's documentation, 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
|
||||
# of most Rails applications, this is beneficial.
|
||||
def root(options = {})
|
||||
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.
|
||||
#
|
||||
# match ':controller/:action/:id/:user_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.
|
||||
def match(path, options=nil)
|
||||
mapping = Mapping.new(@set, @scope, path, options || {}).to_route
|
||||
@set.add_route(*mapping)
|
||||
self
|
||||
end
|
||||
|
||||
# Mount a Rack-based application to be used within the application.
|
||||
#
|
||||
# mount SomeRackApp, :at => "some_route"
|
||||
#
|
||||
# Alternatively:
|
||||
#
|
||||
# mount(SomeRackApp => "some_route")
|
||||
#
|
||||
# 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+.
|
||||
# To customize this helper's name, use the +:as+ option:
|
||||
#
|
||||
# mount(SomeRackApp => "some_route", :as => "exciting")
|
||||
#
|
||||
# This will generate the +exciting_path+ and +exciting_url+ helpers
|
||||
# which can be used to navigate to this mounted app.
|
||||
def mount(app, options = nil)
|
||||
if options
|
||||
path = options.delete(:at)
|
||||
@@ -313,49 +258,22 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
module HttpHelpers
|
||||
# Define a route that only recognizes HTTP GET.
|
||||
# For supported arguments, see +match+.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# get 'bacon', :to => 'food#bacon'
|
||||
def get(*args, &block)
|
||||
map_method(:get, *args, &block)
|
||||
end
|
||||
|
||||
# Define a route that only recognizes HTTP POST.
|
||||
# For supported arguments, see +match+.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# post 'bacon', :to => 'food#bacon'
|
||||
def post(*args, &block)
|
||||
map_method(:post, *args, &block)
|
||||
end
|
||||
|
||||
# Define a route that only recognizes HTTP PUT.
|
||||
# For supported arguments, see +match+.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# put 'bacon', :to => 'food#bacon'
|
||||
def put(*args, &block)
|
||||
map_method(:put, *args, &block)
|
||||
end
|
||||
|
||||
# Define a route that only recognizes HTTP PUT.
|
||||
# For supported arguments, see +match+.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# delete 'broccoli', :to => 'food#broccoli'
|
||||
def delete(*args, &block)
|
||||
map_method(:delete, *args, &block)
|
||||
end
|
||||
|
||||
# Redirect any path to another path:
|
||||
#
|
||||
# match "/stories" => redirect("/posts")
|
||||
def redirect(*args, &block)
|
||||
options = args.last.is_a?(Hash) ? args.pop : {}
|
||||
|
||||
@@ -396,123 +314,12 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# You may wish to organize groups of controllers under a namespace.
|
||||
# Most commonly, you might group a number of administrative controllers
|
||||
# under an +admin+ namespace. You would place these controllers under
|
||||
# the app/controllers/admin directory, and you can group them together
|
||||
# in your router:
|
||||
#
|
||||
# namespace "admin" do
|
||||
# resources :posts, :comments
|
||||
# end
|
||||
#
|
||||
# This will create a number of routes for each of the posts and comments
|
||||
# controller. For Admin::PostsController, Rails will create:
|
||||
#
|
||||
# GET /admin/photos
|
||||
# GET /admin/photos/new
|
||||
# POST /admin/photos
|
||||
# GET /admin/photos/1
|
||||
# GET /admin/photos/1/edit
|
||||
# PUT /admin/photos/1
|
||||
# DELETE /admin/photos/1
|
||||
#
|
||||
# If you want to route /photos (without the prefix /admin) to
|
||||
# Admin::PostsController, you could use
|
||||
#
|
||||
# scope :module => "admin" do
|
||||
# resources :posts, :comments
|
||||
# end
|
||||
#
|
||||
# or, for a single case
|
||||
#
|
||||
# resources :posts, :module => "admin"
|
||||
#
|
||||
# If you want to route /admin/photos to PostsController
|
||||
# (without the Admin:: module prefix), you could use
|
||||
#
|
||||
# scope "/admin" do
|
||||
# resources :posts, :comments
|
||||
# end
|
||||
#
|
||||
# or, for a single case
|
||||
#
|
||||
# resources :posts, :path => "/admin/posts"
|
||||
#
|
||||
# In each of these cases, the named routes remain the same as if you did
|
||||
# not use scope. In the last case, the following paths map to
|
||||
# PostsController:
|
||||
#
|
||||
# GET /admin/photos
|
||||
# GET /admin/photos/new
|
||||
# POST /admin/photos
|
||||
# GET /admin/photos/1
|
||||
# GET /admin/photos/1/edit
|
||||
# PUT /admin/photos/1
|
||||
# DELETE /admin/photos/1
|
||||
module Scoping
|
||||
def initialize(*args) #:nodoc:
|
||||
@scope = {}
|
||||
super
|
||||
end
|
||||
|
||||
# Used to scope a set of routes to particular constraints.
|
||||
#
|
||||
# Take the following route definition as an example:
|
||||
#
|
||||
# scope :path => ":account_id", :as => "account" do
|
||||
# resources :projects
|
||||
# end
|
||||
#
|
||||
# This generates helpers such as +account_projects_path+, just like +resources+ does.
|
||||
# The difference here being that the routes generated are like /rails/projects/2,
|
||||
# rather than /accounts/rails/projects/2.
|
||||
#
|
||||
# === Supported options
|
||||
# [:module]
|
||||
# If you want to route /posts (without the prefix /admin) to
|
||||
# Admin::PostsController, you could use
|
||||
#
|
||||
# scope :module => "admin" do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# [:path]
|
||||
# If you want to prefix the route, you could use
|
||||
#
|
||||
# scope :path => "/admin" do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# This will prefix all of the +posts+ resource's requests with '/admin'
|
||||
#
|
||||
# [: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)
|
||||
def scope(*args)
|
||||
options = args.extract_options!
|
||||
options = options.dup
|
||||
@@ -554,139 +361,22 @@ module ActionDispatch
|
||||
@scope[:blocks] = recover[:block]
|
||||
end
|
||||
|
||||
# Scopes routes to a specific controller
|
||||
#
|
||||
# Example:
|
||||
# controller "food" do
|
||||
# match "bacon", :action => "bacon"
|
||||
# end
|
||||
def controller(controller, options={})
|
||||
options[:controller] = controller
|
||||
scope(options) { yield }
|
||||
end
|
||||
|
||||
# Scopes routes to a specific namespace. For example:
|
||||
#
|
||||
# namespace :admin do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# This generates the following routes:
|
||||
#
|
||||
# admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"}
|
||||
# admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"}
|
||||
# new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
|
||||
# edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
|
||||
# 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.
|
||||
#
|
||||
# [:path]
|
||||
# The path prefix for the routes.
|
||||
#
|
||||
# namespace :admin, :path => "sekret" do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# All routes for the above +resources+ will be accessible through +/sekret/posts+, rather than +/admin/posts+
|
||||
#
|
||||
# [:module]
|
||||
# The namespace for the controllers.
|
||||
#
|
||||
# namespace :admin, :module => "sekret" do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# 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.
|
||||
def namespace(path, options = {})
|
||||
path = path.to_s
|
||||
options = { :path => path, :as => path, :module => path,
|
||||
:shallow_path => path, :shallow_prefix => path }.merge!(options)
|
||||
scope(options) { yield }
|
||||
end
|
||||
|
||||
# === Parameter Restriction
|
||||
# Allows you to constrain the nested routes based on a set of rules.
|
||||
# For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
|
||||
#
|
||||
# constraints(:id => /\d+\.\d+) do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
|
||||
# The +id+ parameter must match the constraint passed in for this example.
|
||||
#
|
||||
# You may use this to also resrict other parameters:
|
||||
#
|
||||
# resources :posts do
|
||||
# constraints(:post_id => /\d+\.\d+) do
|
||||
# resources :comments
|
||||
# end
|
||||
#
|
||||
# === Restricting based on IP
|
||||
#
|
||||
# Routes can also be constrained to an IP or a certain range of IP addresses:
|
||||
#
|
||||
# constraints(:ip => /192.168.\d+.\d+/) do
|
||||
# resources :posts
|
||||
# end
|
||||
#
|
||||
# Any user connecting from the 192.168.* range will be able to see this resource,
|
||||
# where as any user connecting outside of this range will be told there is no such route.
|
||||
#
|
||||
# === Dynamic request matching
|
||||
#
|
||||
# Requests to routes can be constrained based on specific critera:
|
||||
#
|
||||
# constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
|
||||
# resources :iphones
|
||||
# end
|
||||
#
|
||||
# You are able to move this logic out into a class if it is too complex for routes.
|
||||
# This class must have a +matches?+ method defined on it which either returns +true+
|
||||
# if the user should be given access to that route, or +false+ if the user should not.
|
||||
#
|
||||
# class Iphone
|
||||
# def self.matches(request)
|
||||
# request.env["HTTP_USER_AGENT"] =~ /iPhone/
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# An expected place for this code would be +lib/constraints+.
|
||||
#
|
||||
# This class is then used like this:
|
||||
#
|
||||
# constraints(Iphone) do
|
||||
# resources :iphones
|
||||
# end
|
||||
|
||||
def constraints(constraints = {})
|
||||
scope(:constraints => constraints) { yield }
|
||||
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
|
||||
# Using this, the +:id+ parameter here will default to 'home'.
|
||||
def defaults(defaults = {})
|
||||
scope(:defaults => defaults) { yield }
|
||||
end
|
||||
@@ -751,45 +441,6 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# Resource routing allows you to quickly declare all of the common routes
|
||||
# for a given resourceful controller. Instead of declaring separate routes
|
||||
# for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
|
||||
# actions, a resourceful route declares them in a single line of code:
|
||||
#
|
||||
# resources :photos
|
||||
#
|
||||
# Sometimes, you have a resource that clients always look up without
|
||||
# referencing an ID. A common example, /profile always shows the profile of
|
||||
# the currently logged in user. In this case, you can use a singular resource
|
||||
# to map /profile (rather than /profile/:id) to the show action.
|
||||
#
|
||||
# resource :profile
|
||||
#
|
||||
# It's common to have resources that are logically children of other
|
||||
# resources:
|
||||
#
|
||||
# resources :magazines do
|
||||
# resources :ads
|
||||
# end
|
||||
#
|
||||
# You may wish to organize groups of controllers under a namespace. Most
|
||||
# commonly, you might group a number of administrative controllers under
|
||||
# an +admin+ namespace. You would place these controllers under the
|
||||
# app/controllers/admin directory, and you can group them together in your
|
||||
# router:
|
||||
#
|
||||
# namespace "admin" do
|
||||
# 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.
|
||||
@@ -898,24 +549,6 @@ module ActionDispatch
|
||||
@scope[:path_names].merge!(options)
|
||||
end
|
||||
|
||||
# Sometimes, you have a resource that clients always look up without
|
||||
# referencing an ID. A common example, /profile always shows the
|
||||
# profile of the currently logged in user. In this case, you can use
|
||||
# a singular resource to map /profile (rather than /profile/:id) to
|
||||
# the show action:
|
||||
#
|
||||
# resource :geocoder
|
||||
#
|
||||
# creates six different routes in your application, all mapping to
|
||||
# the GeoCoders controller (note that the controller is named after
|
||||
# the plural):
|
||||
#
|
||||
# GET /geocoder/new
|
||||
# POST /geocoder
|
||||
# GET /geocoder
|
||||
# GET /geocoder/edit
|
||||
# PUT /geocoder
|
||||
# DELETE /geocoder
|
||||
def resource(*resources, &block)
|
||||
options = resources.extract_options!
|
||||
|
||||
@@ -926,15 +559,15 @@ module ActionDispatch
|
||||
resource_scope(SingletonResource.new(resources.pop, options)) do
|
||||
yield if block_given?
|
||||
|
||||
collection do
|
||||
collection_scope do
|
||||
post :create
|
||||
end if parent_resource.actions.include?(:create)
|
||||
|
||||
new do
|
||||
new_scope do
|
||||
get :new
|
||||
end if parent_resource.actions.include?(:new)
|
||||
|
||||
member do
|
||||
member_scope do
|
||||
get :edit if parent_resource.actions.include?(:edit)
|
||||
get :show if parent_resource.actions.include?(:show)
|
||||
put :update if parent_resource.actions.include?(:update)
|
||||
@@ -945,61 +578,6 @@ module ActionDispatch
|
||||
self
|
||||
end
|
||||
|
||||
# In Rails, a resourceful route provides a mapping between HTTP verbs
|
||||
# and URLs and controller actions. By convention, each action also maps
|
||||
# to particular CRUD operations in a database. A single entry in the
|
||||
# routing file, such as
|
||||
#
|
||||
# resources :photos
|
||||
#
|
||||
# creates seven different routes in your application, all mapping to
|
||||
# the Photos controller:
|
||||
#
|
||||
# GET /photos/new
|
||||
# POST /photos
|
||||
# GET /photos/:id
|
||||
# GET /photos/:id/edit
|
||||
# PUT /photos/:id
|
||||
# DELETE /photos/:id
|
||||
#
|
||||
# Resources can also be nested infinitely by using this block syntax:
|
||||
#
|
||||
# resources :photos do
|
||||
# resources :comments
|
||||
# end
|
||||
#
|
||||
# This generates the following comments routes:
|
||||
#
|
||||
# GET /photos/:id/comments/new
|
||||
# POST /photos/:id/comments
|
||||
# GET /photos/:id/comments/:id
|
||||
# GET /photos/:id/comments/:id/edit
|
||||
# PUT /photos/:id/comments/:id
|
||||
# DELETE /photos/:id/comments/:id
|
||||
#
|
||||
# === Supported options
|
||||
# [:path_names]
|
||||
# Allows you to change the paths of the seven default actions.
|
||||
# Paths not specified are not changed.
|
||||
#
|
||||
# resources :posts, :path_names => { :new => "brand_new" }
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# resources :posts, :module => "admin"
|
||||
#
|
||||
# All requests to the posts resources will now go to +Admin::PostsController+.
|
||||
#
|
||||
# [:path]
|
||||
#
|
||||
# Set a path prefix for this resource.
|
||||
#
|
||||
# resources :posts, :path => "admin/posts"
|
||||
#
|
||||
# All actions for this resource will now be at +/admin/posts+.
|
||||
def resources(*resources, &block)
|
||||
options = resources.extract_options!
|
||||
|
||||
@@ -1010,16 +588,16 @@ module ActionDispatch
|
||||
resource_scope(Resource.new(resources.pop, options)) do
|
||||
yield if block_given?
|
||||
|
||||
collection do
|
||||
collection_scope do
|
||||
get :index if parent_resource.actions.include?(:index)
|
||||
post :create if parent_resource.actions.include?(:create)
|
||||
end
|
||||
|
||||
new do
|
||||
new_scope do
|
||||
get :new
|
||||
end if parent_resource.actions.include?(:new)
|
||||
|
||||
member do
|
||||
member_scope do
|
||||
get :edit if parent_resource.actions.include?(:edit)
|
||||
get :show if parent_resource.actions.include?(:show)
|
||||
put :update if parent_resource.actions.include?(:update)
|
||||
@@ -1030,50 +608,23 @@ module ActionDispatch
|
||||
self
|
||||
end
|
||||
|
||||
# To add a route to the collection:
|
||||
#
|
||||
# resources :photos do
|
||||
# collection do
|
||||
# get 'search'
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# This will enable Rails to recognize paths such as <tt>/photos/search</tt>
|
||||
# with GET, and route to the search action of PhotosController. It will also
|
||||
# create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
|
||||
# route helpers.
|
||||
def collection
|
||||
unless resource_scope?
|
||||
raise ArgumentError, "can't use collection outside resource(s) scope"
|
||||
unless @scope[:scope_level] == :resources
|
||||
raise ArgumentError, "can't use collection outside resources scope"
|
||||
end
|
||||
|
||||
with_scope_level(:collection) do
|
||||
scope(parent_resource.collection_scope) do
|
||||
yield
|
||||
end
|
||||
collection_scope do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
# To add a member route, add a member block into the resource block:
|
||||
#
|
||||
# resources :photos do
|
||||
# member do
|
||||
# get 'preview'
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
|
||||
# preview action of PhotosController. It will also create the
|
||||
# <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
|
||||
def member
|
||||
unless resource_scope?
|
||||
raise ArgumentError, "can't use member outside resource(s) scope"
|
||||
end
|
||||
|
||||
with_scope_level(:member) do
|
||||
scope(parent_resource.member_scope) do
|
||||
yield
|
||||
end
|
||||
member_scope do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1082,10 +633,8 @@ module ActionDispatch
|
||||
raise ArgumentError, "can't use new outside resource(s) scope"
|
||||
end
|
||||
|
||||
with_scope_level(:new) do
|
||||
scope(parent_resource.new_scope(action_path(:new))) do
|
||||
yield
|
||||
end
|
||||
new_scope do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1111,7 +660,6 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# See ActionDispatch::Routing::Mapper::Scoping#namespace
|
||||
def namespace(path, options = {})
|
||||
if resource_scope?
|
||||
nested { super }
|
||||
@@ -1160,14 +708,9 @@ module ActionDispatch
|
||||
|
||||
if action.to_s =~ /^[\w\/]+$/
|
||||
options[:action] ||= action unless action.to_s.include?("/")
|
||||
options[:as] = name_for_action(action, options[:as])
|
||||
else
|
||||
action = nil
|
||||
end
|
||||
|
||||
if options.key?(:as) && !options[:as]
|
||||
options.delete(:as)
|
||||
else
|
||||
options[:as] = name_for_action(options[:as], action)
|
||||
options[:as] = name_for_action(options[:as])
|
||||
end
|
||||
|
||||
super(path, options)
|
||||
@@ -1197,11 +740,6 @@ module ActionDispatch
|
||||
return true
|
||||
end
|
||||
|
||||
if resource_scope?
|
||||
nested { send(method, resources.pop, options, &block) }
|
||||
return true
|
||||
end
|
||||
|
||||
options.keys.each do |k|
|
||||
(options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
|
||||
end
|
||||
@@ -1218,6 +756,13 @@ module ActionDispatch
|
||||
options.merge!(scope_action_options) if scope_action_options?
|
||||
end
|
||||
|
||||
if resource_scope?
|
||||
nested do
|
||||
send(method, resources.pop, options, &block)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
@@ -1271,6 +816,30 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
def new_scope
|
||||
with_scope_level(:new) do
|
||||
scope(parent_resource.new_scope(action_path(:new))) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def collection_scope
|
||||
with_scope_level(:collection) do
|
||||
scope(parent_resource.collection_scope) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def member_scope
|
||||
with_scope_level(:member) do
|
||||
scope(parent_resource.member_scope) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def nested_options
|
||||
{}.tap do |options|
|
||||
options[:as] = parent_resource.member_name
|
||||
@@ -1279,11 +848,11 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def id_constraint?
|
||||
@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp)
|
||||
@scope[:id].is_a?(Regexp) || (@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp))
|
||||
end
|
||||
|
||||
def id_constraint
|
||||
@scope[:constraints][:id]
|
||||
@scope[:id] || @scope[:constraints][:id]
|
||||
end
|
||||
|
||||
def canonical_action?(action, flag)
|
||||
@@ -1309,29 +878,29 @@ module ActionDispatch
|
||||
path || @scope[:path_names][name.to_sym] || name.to_s
|
||||
end
|
||||
|
||||
def prefix_name_for_action(as, action)
|
||||
if as
|
||||
def prefix_name_for_action(action, as)
|
||||
if as.present?
|
||||
as.to_s
|
||||
elsif as
|
||||
nil
|
||||
elsif !canonical_action?(action, @scope[:scope_level])
|
||||
action.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def name_for_action(as, action)
|
||||
prefix = prefix_name_for_action(as, action)
|
||||
def name_for_action(action, as=nil)
|
||||
prefix = prefix_name_for_action(action, as)
|
||||
prefix = Mapper.normalize_name(prefix) if prefix
|
||||
name_prefix = @scope[:as]
|
||||
|
||||
if parent_resource
|
||||
return nil if as.nil? && action.nil?
|
||||
|
||||
collection_name = parent_resource.collection_name
|
||||
member_name = parent_resource.member_name
|
||||
end
|
||||
|
||||
name = case @scope[:scope_level]
|
||||
when :nested
|
||||
[name_prefix, prefix]
|
||||
[member_name, prefix]
|
||||
when :collection
|
||||
[prefix, name_prefix, collection_name]
|
||||
when :new
|
||||
@@ -1344,8 +913,7 @@ module ActionDispatch
|
||||
[name_prefix, member_name, prefix]
|
||||
end
|
||||
|
||||
candidate = name.select(&:present?).join("_").presence
|
||||
candidate unless as.nil? && @set.routes.find { |r| r.name == candidate }
|
||||
name.select(&:present?).join("_").presence
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ module ActionDispatch
|
||||
#
|
||||
# == Usage within the framework
|
||||
#
|
||||
# Polymorphic URL helpers are used in a number of places throughout the \Rails framework:
|
||||
# Polymorphic URL helpers are used in a number of places throughout the Rails framework:
|
||||
#
|
||||
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
|
||||
# <tt>url_for(@article)</tt>;
|
||||
|
||||
@@ -34,8 +34,7 @@ module ActionDispatch
|
||||
if method = conditions[:request_method]
|
||||
case method
|
||||
when Regexp
|
||||
source = method.source.upcase
|
||||
source =~ /\A\^[-A-Z|]+\$\Z/ ? source[1..-2] : source
|
||||
method.source.upcase
|
||||
else
|
||||
method.to_s.upcase
|
||||
end
|
||||
|
||||
@@ -6,12 +6,6 @@ require 'action_dispatch/routing/deprecated_mapper'
|
||||
module ActionDispatch
|
||||
module Routing
|
||||
class RouteSet #:nodoc:
|
||||
# Since the router holds references to many parts of the system
|
||||
# like engines, controllers and the application itself, inspecting
|
||||
# the route set can actually be really slow, therefore we default
|
||||
# alias inspect to to_s.
|
||||
alias inspect to_s
|
||||
|
||||
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
|
||||
|
||||
class Dispatcher #:nodoc:
|
||||
@@ -301,7 +295,6 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
|
||||
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
|
||||
route = Route.new(self, app, conditions, requirements, defaults, name, anchor)
|
||||
@set.add_route(*route)
|
||||
named_routes[name] = route if name
|
||||
@@ -403,7 +396,7 @@ module ActionDispatch
|
||||
|
||||
raise_routing_error unless path
|
||||
|
||||
params.reject! {|k,v| !v.to_param}
|
||||
params.reject! {|k,v| !v }
|
||||
|
||||
return [path, params.keys] if @extras
|
||||
|
||||
@@ -428,7 +421,7 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def raise_routing_error
|
||||
raise ActionController::RoutingError, "No route matches #{options.inspect}"
|
||||
raise ActionController::RoutingError.new("No route matches #{options.inspect}")
|
||||
end
|
||||
|
||||
def different_controller?
|
||||
@@ -504,7 +497,7 @@ module ActionDispatch
|
||||
path = Rack::Mount::Utils.normalize_path(path) unless path =~ %r{://}
|
||||
|
||||
begin
|
||||
env = Rack::MockRequest.env_for(path, {:method => method, :params => environment[:extras]})
|
||||
env = Rack::MockRequest.env_for(path, {:method => method})
|
||||
rescue URI::InvalidURIError => e
|
||||
raise ActionController::RoutingError, e.message
|
||||
end
|
||||
@@ -519,9 +512,7 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
dispatcher = route.app
|
||||
while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
|
||||
dispatcher = dispatcher.app
|
||||
end
|
||||
dispatcher = dispatcher.app while dispatcher.is_a?(Mapper::Constraints)
|
||||
|
||||
if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
|
||||
dispatcher.prepare_params!(params)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module ActionDispatch
|
||||
module Routing
|
||||
# In <tt>config/routes.rb</tt> you define URL-to-controller mappings, but the reverse
|
||||
# In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
|
||||
# is also possible: an URL can be generated from one of your routing definitions.
|
||||
# URL generation functionality is centralized in this module.
|
||||
#
|
||||
@@ -12,14 +12,15 @@ module ActionDispatch
|
||||
#
|
||||
# == URL generation from parameters
|
||||
#
|
||||
# As you may know, some functions, such as ActionController::Base#url_for
|
||||
# As you may know, some functions - such as ActionController::Base#url_for
|
||||
# and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
|
||||
# of parameters. For example, you've probably had the chance to write code
|
||||
# like this in one of your views:
|
||||
#
|
||||
# <%= link_to('Click here', :controller => 'users',
|
||||
# :action => 'new', :message => 'Welcome!') %>
|
||||
# # => "/users/new?message=Welcome%21"
|
||||
#
|
||||
# # Generates a link to /users/new?message=Welcome%21
|
||||
#
|
||||
# link_to, and all other functions that require URL generation functionality,
|
||||
# actually use ActionController::UrlFor under the hood. And in particular,
|
||||
@@ -60,7 +61,7 @@ module ActionDispatch
|
||||
#
|
||||
# UrlFor also allows one to access methods that have been auto-generated from
|
||||
# named routes. For example, suppose that you have a 'users' resource in your
|
||||
# <tt>config/routes.rb</tt>:
|
||||
# <b>routes.rb</b>:
|
||||
#
|
||||
# resources :users
|
||||
#
|
||||
@@ -128,7 +129,7 @@ module ActionDispatch
|
||||
when String
|
||||
options
|
||||
when nil, Hash
|
||||
_routes.url_for((options || {}).reverse_merge(url_options).symbolize_keys)
|
||||
_routes.url_for((options || {}).reverse_merge!(url_options).symbolize_keys)
|
||||
else
|
||||
polymorphic_url(options)
|
||||
end
|
||||
|
||||
@@ -94,7 +94,7 @@ module ActionDispatch
|
||||
refer
|
||||
else
|
||||
@controller.url_for(fragment)
|
||||
end.gsub(/[\0\r\n]/, '')
|
||||
end.gsub(/[\r\n]/, '')
|
||||
end
|
||||
|
||||
def validate_request!
|
||||
|
||||
@@ -37,6 +37,9 @@ 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)
|
||||
|
||||
@@ -121,8 +124,7 @@ module ActionDispatch
|
||||
options[:controller] = "/#{controller}"
|
||||
end
|
||||
|
||||
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)
|
||||
assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message)
|
||||
end
|
||||
|
||||
# A helper to make it easier to test different route configurations.
|
||||
|
||||
@@ -569,9 +569,9 @@ module ActionDispatch
|
||||
assert !deliveries.empty?, "No e-mail in delivery list"
|
||||
|
||||
for delivery in deliveries
|
||||
for part in (delivery.parts.empty? ? [delivery] : delivery.parts)
|
||||
for part in delivery.parts
|
||||
if part["Content-Type"].to_s =~ /^text\/html\W/
|
||||
root = HTML::Document.new(part.body.to_s).root
|
||||
root = HTML::Document.new(part.body).root
|
||||
assert_select root, ":root", &block
|
||||
end
|
||||
end
|
||||
|
||||
@@ -115,8 +115,8 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# An instance of this class represents a set of requests and responses
|
||||
# performed sequentially by a test process. Because you can instantiate
|
||||
# An integration Session instance represents a set of requests and responses
|
||||
# performed sequentially by some virtual user. Because you can instantiate
|
||||
# multiple sessions and run them side-by-side, you can also mimic (to some
|
||||
# limited extent) multiple simultaneous users interacting with your system.
|
||||
#
|
||||
@@ -257,14 +257,12 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
hostname, port = host.split(':')
|
||||
|
||||
env = {
|
||||
:method => method,
|
||||
:params => parameters,
|
||||
|
||||
"SERVER_NAME" => hostname,
|
||||
"SERVER_PORT" => port || (https? ? "443" : "80"),
|
||||
"SERVER_NAME" => host.split(':')[0],
|
||||
"SERVER_PORT" => (https? ? "443" : "80"),
|
||||
"HTTPS" => https? ? "on" : "off",
|
||||
"rack.url_scheme" => https? ? "https" : "http",
|
||||
|
||||
@@ -375,12 +373,12 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# An test that spans multiple controllers and actions,
|
||||
# An IntegrationTest is one that spans multiple controllers and actions,
|
||||
# tying them all together to ensure they work together as expected. It tests
|
||||
# more completely than either unit or functional tests do, exercising the
|
||||
# entire stack, from the dispatcher to the database.
|
||||
#
|
||||
# At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
|
||||
# At its simplest, you simply extend IntegrationTest and write your tests
|
||||
# using the get/post methods:
|
||||
#
|
||||
# require "test_helper"
|
||||
@@ -405,7 +403,7 @@ module ActionDispatch
|
||||
# However, you can also have multiple session instances open per test, and
|
||||
# even extend those instances with assertions and methods to create a very
|
||||
# powerful testing DSL that is specific for your application. You can even
|
||||
# reference any named routes you happen to have defined.
|
||||
# reference any named routes you happen to have defined!
|
||||
#
|
||||
# require "test_helper"
|
||||
#
|
||||
|
||||
@@ -2,9 +2,8 @@ module ActionPack
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 3
|
||||
MINOR = 0
|
||||
TINY = 20
|
||||
PRE = nil
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -79,8 +79,8 @@ module ActionView #:nodoc:
|
||||
#
|
||||
# === Template caching
|
||||
#
|
||||
# By default, Rails will compile each template to a method in order to render it. When you alter a template,
|
||||
# Rails will check the file's modification time and recompile it in development mode.
|
||||
# By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will
|
||||
# check the file's modification time and recompile it.
|
||||
#
|
||||
# == Builder
|
||||
#
|
||||
@@ -156,7 +156,7 @@ module ActionView #:nodoc:
|
||||
#
|
||||
# This refreshes the sidebar, removes a person element and highlights the user list.
|
||||
#
|
||||
# See the ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods documentation for more details.
|
||||
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
|
||||
class Base
|
||||
module Subclasses
|
||||
end
|
||||
@@ -165,12 +165,8 @@ module ActionView #:nodoc:
|
||||
|
||||
# Specify whether RJS responses should be wrapped in a try/catch block
|
||||
# that alert()s the caught exception (and then re-raises it).
|
||||
cattr_reader :debug_rjs
|
||||
cattr_accessor :debug_rjs
|
||||
@@debug_rjs = false
|
||||
def self.debug_rjs=(new_value)
|
||||
ActiveSupport::Deprecation.warn("config.action_view.debug_rjs will be removed in 3.1, from 3.1 onwards you will need to install prototype-rails to continue to use RJS templates ")
|
||||
@@debug_rjs = new_value
|
||||
end
|
||||
|
||||
# Specify the proc used to decorate input tags that refer to attributes with errors.
|
||||
cattr_accessor :field_error_proc
|
||||
|
||||
@@ -12,6 +12,7 @@ module ActionView #:nodoc:
|
||||
autoload :CsrfHelper
|
||||
autoload :DateHelper
|
||||
autoload :DebugHelper
|
||||
autoload :DeprecatedBlockHelpers
|
||||
autoload :FormHelper
|
||||
autoload :FormOptionsHelper
|
||||
autoload :FormTagHelper
|
||||
|
||||
@@ -375,9 +375,7 @@ module ActionView
|
||||
# <script type="text/javascript" src="/javascripts/body.js"></script>
|
||||
# <script type="text/javascript" src="/javascripts/tail.js"></script>
|
||||
def self.register_javascript_expansion(expansions)
|
||||
expansions.each do |key, values|
|
||||
@@javascript_expansions[key] = (@@javascript_expansions[key] || []) | Array(values)
|
||||
end
|
||||
@@javascript_expansions.merge!(expansions)
|
||||
end
|
||||
|
||||
# Register one or more stylesheet files to be included when <tt>symbol</tt>
|
||||
@@ -392,9 +390,7 @@ 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 self.register_stylesheet_expansion(expansions)
|
||||
expansions.each do |key, values|
|
||||
@@stylesheet_expansions[key] = (@@stylesheet_expansions[key] || []) | Array(values)
|
||||
end
|
||||
@@stylesheet_expansions.merge!(expansions)
|
||||
end
|
||||
|
||||
def self.reset_javascript_include_default
|
||||
@@ -742,11 +738,11 @@ module ActionView
|
||||
return source if is_uri?(source)
|
||||
|
||||
source += ".#{ext}" if rewrite_extension?(source, dir, ext)
|
||||
source = "/#{dir}/#{source}" unless source.start_with? '/'
|
||||
source = "/#{dir}/#{source}" unless source[0] == ?/
|
||||
source = rewrite_asset_path(source, config.asset_path)
|
||||
|
||||
has_request = controller.respond_to?(:request)
|
||||
if has_request && include_host && controller.config.relative_url_root && !source.start_with?(controller.config.relative_url_root)
|
||||
if has_request && include_host && source !~ %r{^#{controller.config.relative_url_root}/}
|
||||
source = "#{controller.config.relative_url_root}#{source}"
|
||||
end
|
||||
source = rewrite_host_and_protocol(source, has_request) if include_host
|
||||
@@ -840,7 +836,7 @@ module ActionView
|
||||
|
||||
def expand_javascript_sources(sources, recursive = false)
|
||||
if sources.include?(:all)
|
||||
all_javascript_files = (collect_asset_files(config.javascripts_dir, ('**' if recursive), '*.js') - ['application']) << 'application'
|
||||
all_javascript_files = collect_asset_files(config.javascripts_dir, ('**' if recursive), '*.js')
|
||||
((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
|
||||
else
|
||||
expanded_sources = sources.collect do |source|
|
||||
@@ -872,14 +868,14 @@ module ActionView
|
||||
|
||||
def ensure_stylesheet_sources!(sources)
|
||||
sources.each do |source|
|
||||
asset_file_path!(compute_public_path(source, 'stylesheets', 'css', false))
|
||||
asset_file_path!(path_to_stylesheet(source))
|
||||
end
|
||||
return sources
|
||||
end
|
||||
|
||||
def ensure_javascript_sources!(sources)
|
||||
sources.each do |source|
|
||||
asset_file_path!(compute_public_path(source, 'javascripts', 'js', false))
|
||||
asset_file_path!(path_to_javascript(source))
|
||||
end
|
||||
return sources
|
||||
end
|
||||
|
||||
@@ -46,22 +46,14 @@ module ActionView
|
||||
private
|
||||
# TODO: Create an object that has caching read/write on it
|
||||
def fragment_for(name = {}, options = nil, &block) #:nodoc:
|
||||
if fragment = controller.read_fragment(name, options)
|
||||
fragment
|
||||
if controller.fragment_exist?(name, options)
|
||||
controller.read_fragment(name, options)
|
||||
else
|
||||
# VIEW TODO: Make #capture usable outside of ERB
|
||||
# This dance is needed because Builder can't use capture
|
||||
pos = output_buffer.bytesize
|
||||
pos = output_buffer.length
|
||||
yield
|
||||
if output_buffer.html_safe?
|
||||
safe_output_buffer = output_buffer.to_str
|
||||
fragment = safe_output_buffer.byteslice(pos..-1)
|
||||
safe_output_buffer = safe_output_buffer.byteslice(0...pos)
|
||||
self.output_buffer = output_buffer.class.new(safe_output_buffer)
|
||||
else
|
||||
fragment = output_buffer.byteslice(pos..-1)
|
||||
self.output_buffer = output_buffer.byteslice(0...pos)
|
||||
end
|
||||
fragment = output_buffer.slice!(pos..-1)
|
||||
controller.write_fragment(name, fragment, options)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/string/output_safety'
|
||||
|
||||
module ActionView
|
||||
# = Action View Capture Helper
|
||||
@@ -39,7 +38,7 @@ module ActionView
|
||||
value = nil
|
||||
buffer = with_output_buffer { value = yield(*args) }
|
||||
if string = buffer.presence || value and string.is_a?(String)
|
||||
NonConcattingString.new(ERB::Util.html_escape(string))
|
||||
NonConcattingString.new(string)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -179,7 +178,7 @@ module ActionView
|
||||
def flush_output_buffer #:nodoc:
|
||||
if output_buffer && !output_buffer.empty?
|
||||
response.body_parts << output_buffer
|
||||
self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0]
|
||||
self.output_buffer = output_buffer[0,0]
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -893,8 +893,6 @@ module ActionView
|
||||
# Returns the separator for a given datetime component
|
||||
def separator(type)
|
||||
case type
|
||||
when :year
|
||||
@options[:discard_year] ? "" : @options[:date_separator]
|
||||
when :month
|
||||
@options[:discard_month] ? "" : @options[:date_separator]
|
||||
when :day
|
||||
|
||||
@@ -202,12 +202,6 @@ module ActionView
|
||||
# ...
|
||||
# <% end %>
|
||||
#
|
||||
# You can also set the answer format, like this:
|
||||
#
|
||||
# <%= form_for(@post, :format => :json) do |f| %>
|
||||
# ...
|
||||
# <% end %>
|
||||
#
|
||||
# If you have an object that needs to be represented as a different
|
||||
# parameter, like a Client that acts as a Person:
|
||||
#
|
||||
@@ -338,9 +332,7 @@ module ActionView
|
||||
|
||||
options[:html] ||= {}
|
||||
options[:html].reverse_merge!(html_options)
|
||||
options[:url] ||= options[:format] ? \
|
||||
polymorphic_path(object_or_array, :format => options.delete(:format)) : \
|
||||
polymorphic_path(object_or_array)
|
||||
options[:url] ||= polymorphic_path(object_or_array)
|
||||
end
|
||||
|
||||
# Creates a scope around a specific model object like form_for, but
|
||||
@@ -541,10 +533,7 @@ module ActionView
|
||||
end
|
||||
|
||||
builder = options[:builder] || ActionView::Base.default_form_builder
|
||||
builder = builder.new(object_name, object, self, options, block)
|
||||
output = capture(builder, &block)
|
||||
output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id?
|
||||
output
|
||||
capture(builder.new(object_name, object, self, options, block), &block)
|
||||
end
|
||||
|
||||
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
|
||||
@@ -814,7 +803,7 @@ module ActionView
|
||||
options["incremental"] = true unless options.has_key?("incremental")
|
||||
end
|
||||
|
||||
InstanceTag.new(object_name, method, self, options.delete("object")).to_input_field_tag("search", options)
|
||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("search", options)
|
||||
end
|
||||
|
||||
# Returns a text_field of type "tel".
|
||||
@@ -1017,9 +1006,14 @@ module ActionView
|
||||
|
||||
def value_before_type_cast(object, method_name)
|
||||
unless object.nil?
|
||||
object.respond_to?(method_name + "_before_type_cast") ?
|
||||
object.send(method_name + "_before_type_cast") :
|
||||
object.send(method_name)
|
||||
if object.respond_to?(method_name)
|
||||
object.send(method_name)
|
||||
# FIXME: this is AR dependent
|
||||
elsif object.respond_to?(method_name + "_before_type_cast")
|
||||
object.send(method_name + "_before_type_cast")
|
||||
else
|
||||
raise NoMethodError, "Model #{object.class} does not respond to #{method_name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1066,7 +1060,7 @@ module ActionView
|
||||
options["name"] ||= tag_name_with_index(@auto_index)
|
||||
options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
|
||||
else
|
||||
options["name"] ||= tag_name + (options['multiple'] ? '[]' : '')
|
||||
options["name"] ||= tag_name + (options.has_key?('multiple') ? '[]' : '')
|
||||
options["id"] = options.fetch("id"){ tag_id }
|
||||
end
|
||||
end
|
||||
@@ -1100,7 +1094,7 @@ module ActionView
|
||||
include InstanceTagMethods
|
||||
end
|
||||
|
||||
class FormBuilder
|
||||
class FormBuilder #:nodoc:
|
||||
# The methods which wrap a form helper call.
|
||||
class_inheritable_accessor :field_helpers
|
||||
self.field_helpers = (FormHelper.instance_method_names - ['form_for'])
|
||||
@@ -1264,11 +1258,11 @@ module ActionView
|
||||
|
||||
if association.respond_to?(:persisted?)
|
||||
association = [association] if @object.send(association_name).is_a?(Array)
|
||||
elsif !association.respond_to?(:to_ary)
|
||||
elsif !association.is_a?(Array)
|
||||
association = @object.send(association_name)
|
||||
end
|
||||
|
||||
if association.respond_to?(:to_ary)
|
||||
if association.is_a?(Array)
|
||||
explicit_child_index = options[:child_index]
|
||||
output = ActiveSupport::SafeBuffer.new
|
||||
association.each do |child|
|
||||
@@ -1283,8 +1277,14 @@ module ActionView
|
||||
def fields_for_nested_model(name, object, options, block)
|
||||
object = object.to_model if object.respond_to?(:to_model)
|
||||
|
||||
options[:hidden_field_id] = object.persisted?
|
||||
@template.fields_for(name, object, options, &block)
|
||||
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
|
||||
end
|
||||
|
||||
def nested_child_index(name)
|
||||
|
||||
@@ -299,12 +299,12 @@ module ActionView
|
||||
|
||||
container = container.to_a if Hash === container
|
||||
selected, disabled = extract_selected_and_disabled(selected).map do | r |
|
||||
Array.wrap(r).map { |item| item.to_s }
|
||||
Array.wrap(r).map(&:to_s)
|
||||
end
|
||||
|
||||
container.map do |element|
|
||||
html_attributes = option_html_attributes(element)
|
||||
text, value = option_text_and_value(element).map { |item| item.to_s }
|
||||
text, value = option_text_and_value(element).map(&:to_s)
|
||||
selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
|
||||
disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
|
||||
%(<option value="#{html_escape(value)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{html_escape(text)}</option>)
|
||||
@@ -395,12 +395,12 @@ module ActionView
|
||||
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
|
||||
# wrap the output in an appropriate <tt><select></tt> tag.
|
||||
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
|
||||
collection.map do |group|
|
||||
collection.inject("") do |options_for_select, group|
|
||||
group_label_string = eval("group.#{group_label_method}")
|
||||
"<optgroup label=\"#{html_escape(group_label_string)}\">" +
|
||||
options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key) +
|
||||
'</optgroup>'
|
||||
end.join.html_safe
|
||||
options_for_select += "<optgroup label=\"#{html_escape(group_label_string)}\">"
|
||||
options_for_select += options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key)
|
||||
options_for_select += '</optgroup>'
|
||||
end.html_safe
|
||||
end
|
||||
|
||||
# Returns a string of <tt><option></tt> tags, like <tt>options_for_select</tt>, but
|
||||
@@ -534,7 +534,7 @@ module ActionView
|
||||
else
|
||||
selected = Array.wrap(selected)
|
||||
options = selected.extract_options!.symbolize_keys
|
||||
[ options.include?(:selected) ? options[:selected] : selected, options[:disabled] ]
|
||||
[ options[:selected] || selected , options[:disabled] ]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -596,13 +596,13 @@ module ActionView
|
||||
private
|
||||
def add_options(option_tags, options, value = nil)
|
||||
if options[:include_blank]
|
||||
option_tags = content_tag('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
|
||||
option_tags = "<option value=\"\">#{html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
|
||||
end
|
||||
if value.blank? && options[:prompt]
|
||||
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
|
||||
option_tags = content_tag('option', prompt, :value => '') + "\n" + option_tags
|
||||
option_tags = "<option value=\"\">#{html_escape(prompt)}</option>\n" + option_tags
|
||||
end
|
||||
option_tags
|
||||
option_tags.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ module ActionView
|
||||
# form_tag('/upload', :multipart => true)
|
||||
# # => <form action="/upload" method="post" enctype="multipart/form-data">
|
||||
#
|
||||
# <%= form_tag('/posts') do -%>
|
||||
# <%= form_tag('/posts')do -%>
|
||||
# <div><%= submit_tag 'Save' %></div>
|
||||
# <% end -%>
|
||||
# # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
|
||||
@@ -46,8 +46,8 @@ module ActionView
|
||||
# <%= form_tag('/posts', :remote => true) %>
|
||||
# # => <form action="/posts" method="post" data-remote="true">
|
||||
#
|
||||
def form_tag(url_for_options = {}, options = {}, &block)
|
||||
html_options = html_options_for_form(url_for_options, options)
|
||||
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?
|
||||
form_tag_in_block(html_options, &block)
|
||||
else
|
||||
@@ -67,7 +67,7 @@ module ActionView
|
||||
# * Any other key creates standard HTML attributes for the tag.
|
||||
#
|
||||
# ==== Examples
|
||||
# select_tag "people", options_from_collection_for_select(@people, "id", "name")
|
||||
# select_tag "people", options_from_collection_for_select(@people, "name", "id")
|
||||
# # <select id="people" name="people"><option value="1">David</option></select>
|
||||
#
|
||||
# select_tag "people", "<option>David</option>"
|
||||
@@ -100,9 +100,9 @@ module ActionView
|
||||
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
|
||||
if blank = options.delete(:include_blank)
|
||||
if blank.kind_of?(String)
|
||||
option_tags = content_tag(:option, blank, :value => '').safe_concat(option_tags)
|
||||
option_tags = "<option value=\"\">#{blank}</option>".html_safe + option_tags
|
||||
else
|
||||
option_tags = content_tag(:option, '', :value => '').safe_concat(option_tags)
|
||||
option_tags = "<option value=\"\"></option>".html_safe + option_tags
|
||||
end
|
||||
end
|
||||
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
|
||||
@@ -115,7 +115,6 @@ module ActionView
|
||||
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
|
||||
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
|
||||
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
|
||||
# * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
|
||||
# * Any other key creates standard HTML attributes for the tag.
|
||||
#
|
||||
# ==== Examples
|
||||
@@ -125,9 +124,6 @@ module ActionView
|
||||
# text_field_tag 'query', 'Enter your search query here'
|
||||
# # => <input id="query" name="query" type="text" value="Enter your search query here" />
|
||||
#
|
||||
# text_field_tag 'search', nil, :placeholder => 'Enter search term...'
|
||||
# # => <input id="search" name="search" placeholder="Enter search term..." type="text" />
|
||||
#
|
||||
# text_field_tag 'request', nil, :class => 'special_input'
|
||||
# # => <input class="special_input" id="request" name="request" type="text" />
|
||||
#
|
||||
@@ -529,12 +525,12 @@ module ActionView
|
||||
end
|
||||
|
||||
private
|
||||
def html_options_for_form(url_for_options, options)
|
||||
def html_options_for_form(url_for_options, options, *parameters_for_url)
|
||||
options.stringify_keys.tap do |html_options|
|
||||
html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
|
||||
# The following URL is unescaped, this is just a hash of options, and it is the
|
||||
# responsability of the caller to escape all the values.
|
||||
html_options["action"] = url_for(url_for_options)
|
||||
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")
|
||||
end
|
||||
|
||||
@@ -49,8 +49,7 @@ module ActionView
|
||||
# Escape carrier returns and single and double quotes for JavaScript segments.
|
||||
def escape_javascript(javascript)
|
||||
if javascript
|
||||
result = javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) {|match| JS_ESCAPE_MAP[match] }
|
||||
javascript.html_safe? ? result.html_safe : result
|
||||
javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] }
|
||||
else
|
||||
''
|
||||
end
|
||||
@@ -66,8 +65,7 @@ module ActionView
|
||||
# //]]>
|
||||
# </script>
|
||||
#
|
||||
# +html_options+ may be a hash of attributes for the <tt>\<script></tt> tag.
|
||||
# Example:
|
||||
# +html_options+ may be a hash of attributes for the <script> tag. Example:
|
||||
# javascript_tag "alert('All is good')", :defer => 'defer'
|
||||
# # => <script defer="defer" type="text/javascript">alert('All is good')</script>
|
||||
#
|
||||
|
||||
@@ -14,7 +14,7 @@ module ActionView
|
||||
# unchanged if can't be converted into a valid number.
|
||||
module NumberHelper
|
||||
|
||||
DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :negative_format => "-%u%n", :unit => "$", :separator => ".", :delimiter => ",",
|
||||
DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :unit => "$", :separator => ".", :delimiter => ",",
|
||||
:precision => 2, :significant => false, :strip_insignificant_zeros => false }
|
||||
|
||||
# Raised when argument +number+ param given to the helpers is invalid and
|
||||
@@ -83,18 +83,15 @@ module ActionView
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
|
||||
# * <tt>:precision</tt> - Sets the level of precision (defaults to 2).
|
||||
# * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
|
||||
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
|
||||
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
|
||||
# * <tt>:format</tt> - Sets the format for non-negative numbers (defaults to "%u%n").
|
||||
# Fields are <tt>%u</tt> for the currency, and <tt>%n</tt>
|
||||
# for the number.
|
||||
# * <tt>:negative_format</tt> - Sets the format for negative numbers (defaults to prepending
|
||||
# an hyphen to the formatted number given by <tt>:format</tt>).
|
||||
# Accepts the same fields than <tt>:format</tt>, except
|
||||
# <tt>%n</tt> is here the absolute value of the number.
|
||||
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
|
||||
# * <tt>:precision</tt> - Sets the level of precision (defaults to 2).
|
||||
# * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
|
||||
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
|
||||
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
|
||||
# * <tt>:format</tt> - Sets the format of the output string (defaults to "%u%n"). The field types are:
|
||||
#
|
||||
# %u The currency unit
|
||||
# %n The number
|
||||
#
|
||||
# ==== Examples
|
||||
# number_to_currency(1234567890.50) # => $1,234,567,890.50
|
||||
@@ -102,8 +99,6 @@ module ActionView
|
||||
# number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506
|
||||
# number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,506 €
|
||||
#
|
||||
# number_to_currency(1234567890.50, :negative_format => "(%u%n)")
|
||||
# # => ($1,234,567,890.51)
|
||||
# number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "")
|
||||
# # => £1234567890,50
|
||||
# number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "", :format => "%n %u")
|
||||
@@ -117,17 +112,11 @@ module ActionView
|
||||
currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :default => {})
|
||||
|
||||
defaults = DEFAULT_CURRENCY_VALUES.merge(defaults).merge!(currency)
|
||||
defaults[:negative_format] = "-" + options[:format] if options[:format]
|
||||
options = defaults.merge!(options)
|
||||
|
||||
unit = options.delete(:unit)
|
||||
format = options.delete(:format)
|
||||
|
||||
if number.to_f < 0
|
||||
format = options.delete(:negative_format)
|
||||
number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
|
||||
end
|
||||
|
||||
begin
|
||||
value = number_with_precision(number, options.merge(:raise => true))
|
||||
format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
|
||||
@@ -213,7 +202,7 @@ module ActionView
|
||||
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
|
||||
options = options.reverse_merge(defaults)
|
||||
|
||||
parts = number.to_s.to_str.split('.')
|
||||
parts = number.to_s.split('.')
|
||||
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
||||
parts.join(options[:separator]).html_safe
|
||||
|
||||
@@ -271,14 +260,13 @@ module ActionView
|
||||
if number == 0
|
||||
digits, rounded_number = 1, 0
|
||||
else
|
||||
digits = (Math.log10(number.abs) + 1).floor
|
||||
rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
|
||||
digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
|
||||
digits = (Math.log10(number) + 1).floor
|
||||
rounded_number = BigDecimal.new((number / 10 ** (digits - precision)).to_s).round.to_f * 10 ** (digits - precision)
|
||||
end
|
||||
precision = precision - digits
|
||||
precision = precision > 0 ? precision : 0 #don't let it be negative
|
||||
else
|
||||
rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
|
||||
rounded_number = BigDecimal.new((number * (10 ** precision)).to_s).round.to_f / 10 ** precision
|
||||
end
|
||||
formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options)
|
||||
if strip_insignificant_zeros
|
||||
@@ -471,8 +459,8 @@ module ActionView
|
||||
raise ArgumentError, ":units must be a Hash or String translation scope."
|
||||
end.keys.map{|e_name| DECIMAL_UNITS.invert[e_name] }.sort_by{|e| -e}
|
||||
|
||||
number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
|
||||
display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
|
||||
number_exponent = Math.log10(number).floor
|
||||
display_exponent = unit_exponents.find{|e| number_exponent >= e }
|
||||
number /= 10 ** display_exponent
|
||||
|
||||
unit = case units
|
||||
|
||||
@@ -130,6 +130,7 @@ module ActionView
|
||||
"new Ajax.Updater(#{update}, "
|
||||
|
||||
url_options = options[:url]
|
||||
url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
|
||||
function << "'#{html_escape(escape_javascript(url_for(url_options)))}'"
|
||||
function << ", #{javascript_options})"
|
||||
|
||||
@@ -160,7 +161,7 @@ module ActionView
|
||||
|
||||
# JavaScriptGenerator generates blocks of JavaScript code that allow you
|
||||
# to change the content and presentation of multiple DOM elements. Use
|
||||
# this in your Ajax response bodies, either in a <tt>\<script></tt> tag or as plain
|
||||
# this in your Ajax response bodies, either in a <script> tag or as plain
|
||||
# JavaScript sent with a Content-type of "text/javascript".
|
||||
#
|
||||
# Create new instances with PrototypeHelper#update_page or with
|
||||
@@ -223,7 +224,7 @@ module ActionView
|
||||
#
|
||||
# You can also use PrototypeHelper#update_page_tag instead of
|
||||
# PrototypeHelper#update_page to wrap the generated JavaScript in a
|
||||
# <tt>\<script></tt> tag.
|
||||
# <script> tag.
|
||||
module GeneratorMethods
|
||||
def to_s #:nodoc:
|
||||
(@lines * $/).tap do |javascript|
|
||||
@@ -581,11 +582,11 @@ module ActionView
|
||||
JavaScriptGenerator.new(view_context, &block).to_s.html_safe
|
||||
end
|
||||
|
||||
# Works like update_page but wraps the generated JavaScript in a <tt>\<script></tt>
|
||||
# Works like update_page but wraps the generated JavaScript in a <script>
|
||||
# tag. Use this to include generated JavaScript in an ERb template.
|
||||
# See JavaScriptGenerator for more information.
|
||||
#
|
||||
# +html_options+ may be a hash of <tt>\<script></tt> attributes to be passed
|
||||
# +html_options+ may be a hash of <script> attributes to be passed
|
||||
# to ActionView::Helpers::JavaScriptHelper#javascript_tag.
|
||||
def update_page_tag(html_options = {}, &block)
|
||||
javascript_tag update_page(&block), html_options
|
||||
|
||||
@@ -81,7 +81,7 @@ module ActionView
|
||||
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
|
||||
# # => Welcome to my website!
|
||||
def strip_tags(html)
|
||||
self.class.full_sanitizer.sanitize(html)
|
||||
self.class.full_sanitizer.sanitize(html).try(:html_safe)
|
||||
end
|
||||
|
||||
# Strips all link tags from +text+ leaving just the link text.
|
||||
|
||||
@@ -9,24 +9,6 @@ module ActionView
|
||||
# and transforming strings, which can reduce the amount of inline Ruby code in
|
||||
# your views. These helper methods extend Action View making them callable
|
||||
# within your template files.
|
||||
#
|
||||
# ==== Sanitization
|
||||
#
|
||||
# Most text helpers by default sanitize the given content, but do not escape it.
|
||||
# This means HTML tags will appear in the page but all malicious code will be removed.
|
||||
# Let's look at some examples using the +simple_format+ method:
|
||||
#
|
||||
# simple_format('<a href="http://example.com/">Example</a>')
|
||||
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
|
||||
#
|
||||
# simple_format('<a href="javascript:alert('no!')">Example</a>')
|
||||
# # => "<p><a>Example</a></p>"
|
||||
#
|
||||
# If you want to escape all content, you should invoke the +h+ method before
|
||||
# calling the text helper.
|
||||
#
|
||||
# simple_format h('<a href="http://example.com/">Example</a>')
|
||||
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
|
||||
module TextHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -115,12 +97,13 @@ module ActionView
|
||||
end
|
||||
options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
|
||||
|
||||
if text.present? && phrases.present?
|
||||
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
|
||||
text = text.to_str.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
|
||||
end
|
||||
text = sanitize(text) unless options[:sanitize] == false
|
||||
text
|
||||
if text.blank? || phrases.blank?
|
||||
text
|
||||
else
|
||||
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
|
||||
text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
|
||||
end.html_safe
|
||||
end
|
||||
|
||||
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
|
||||
@@ -250,16 +233,14 @@ module ActionView
|
||||
# simple_format("Look ma! A class!", :class => 'description')
|
||||
# # => "<p class='description'>Look ma! A class!</p>"
|
||||
def simple_format(text, html_options={}, options={})
|
||||
text = text ? text.to_str : ''
|
||||
text = text.dup if text.frozen?
|
||||
text = ''.html_safe if text.nil?
|
||||
start_tag = tag('p', html_options, true)
|
||||
text = sanitize(text) unless options[:sanitize] == false
|
||||
text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
|
||||
text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
|
||||
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
|
||||
text.insert 0, start_tag
|
||||
text.concat("</p>")
|
||||
text = sanitize(text) unless options[:sanitize] == false
|
||||
text
|
||||
text.html_safe.safe_concat("</p>")
|
||||
end
|
||||
|
||||
# Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
|
||||
@@ -300,7 +281,7 @@ module ActionView
|
||||
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
|
||||
# Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
|
||||
def auto_link(text, *args, &block)#link = :all, html = {}, &block)
|
||||
return '' if text.blank?
|
||||
return ''.html_safe if text.blank?
|
||||
|
||||
options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
|
||||
unless args.empty?
|
||||
@@ -463,7 +444,7 @@ module ActionView
|
||||
end
|
||||
|
||||
AUTO_LINK_RE = %r{
|
||||
(?: ((?:ed2k|ftp|http|https|irc|mailto|news|gopher|nntp|telnet|webcal|xmpp|callto|feed|svn|urn|aim|rsync|tag|ssh|sftp|rtsp|afs):)// | www\. )
|
||||
(?: ([\w+.:-]+:)// | www\. )
|
||||
[^\s<]+
|
||||
}x
|
||||
|
||||
@@ -478,7 +459,7 @@ module ActionView
|
||||
# is yielded and the result is used as the link text.
|
||||
def auto_link_urls(text, html_options = {}, options = {})
|
||||
link_attributes = html_options.stringify_keys
|
||||
text.to_str.gsub(AUTO_LINK_RE) do
|
||||
text.gsub(AUTO_LINK_RE) do
|
||||
scheme, href = $1, $&
|
||||
punctuation = []
|
||||
|
||||
@@ -495,26 +476,33 @@ module ActionView
|
||||
end
|
||||
end
|
||||
|
||||
link_text = block_given? ? yield(href) : href
|
||||
link_text = block_given?? yield(href) : href
|
||||
href = 'http://' + href unless scheme
|
||||
|
||||
sanitize_link = options[:sanitize] != false
|
||||
content_tag(:a, link_text, link_attributes.merge('href' => href), sanitize_link) + punctuation.reverse.join('')
|
||||
unless options[:sanitize] == false
|
||||
link_text = sanitize(link_text)
|
||||
href = sanitize(href)
|
||||
end
|
||||
content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('')
|
||||
end
|
||||
end
|
||||
end.html_safe
|
||||
end
|
||||
|
||||
# Turns all email addresses into clickable links. If a block is given,
|
||||
# each email is yielded and the result is used as the link text.
|
||||
def auto_link_email_addresses(text, html_options = {}, options = {})
|
||||
text.to_str.gsub(AUTO_EMAIL_RE) do
|
||||
text.gsub(AUTO_EMAIL_RE) do
|
||||
text = $&
|
||||
|
||||
if auto_linked?($`, $')
|
||||
text.html_safe
|
||||
else
|
||||
display_text = block_given? ? yield(text) : text
|
||||
display_text = sanitize(display_text) unless options[:sanitize] == false
|
||||
display_text = (block_given?) ? yield(text) : text
|
||||
|
||||
unless options[:sanitize] == false
|
||||
text = sanitize(text)
|
||||
display_text = sanitize(display_text) unless text == display_text
|
||||
end
|
||||
mail_to text, display_text, html_options
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,33 +1,13 @@
|
||||
require 'action_view/helpers/tag_helper'
|
||||
require 'i18n/exceptions'
|
||||
|
||||
module I18n
|
||||
class ExceptionHandler
|
||||
include Module.new {
|
||||
def call(exception, locale, key, options)
|
||||
exception.is_a?(MissingTranslationData) && options[:rescue_format] == :html ? super.html_safe : super
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
module ActionView
|
||||
# = Action View Translation Helpers
|
||||
module Helpers
|
||||
module TranslationHelper
|
||||
# Delegates to I18n#translate but also performs three additional functions.
|
||||
#
|
||||
# First, it'll pass the :rescue_format => :html option to I18n so that any caught
|
||||
# MissingTranslationData exceptions will be turned into inline spans that
|
||||
#
|
||||
# * have a "translation-missing" class set,
|
||||
# * contain the missing key as a title attribute and
|
||||
# * a titleized version of the last key segment as a text.
|
||||
#
|
||||
# E.g. the value returned for a missing translation key :"blog.post.title" will be
|
||||
# <span class="translation_missing" title="translation missing: blog.post.title">Title</span>.
|
||||
# This way your views will display rather reasonableful strings but it will still
|
||||
# be easy to spot missing translations.
|
||||
# First, it'll catch MissingTranslationData exceptions and turn them into
|
||||
# inline spans that contains the missing key, such that you can see in a
|
||||
# view what is missing where.
|
||||
#
|
||||
# Second, it'll scope the key by the current partial if the key starts
|
||||
# with a period. So if you call <tt>translate(".foo")</tt> from the
|
||||
@@ -44,20 +24,15 @@ module ActionView
|
||||
# naming convention helps to identify translations that include HTML tags so that
|
||||
# you know what kind of output to expect when you call translate in a template.
|
||||
def translate(key, options = {})
|
||||
options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
|
||||
if html_safe_translation_key?(key)
|
||||
html_safe_options = options.dup
|
||||
options.except(*I18n::RESERVED_KEYS).each do |name, value|
|
||||
unless name == :count && value.is_a?(Numeric)
|
||||
html_safe_options[name] = ERB::Util.html_escape(value.to_s)
|
||||
end
|
||||
end
|
||||
translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
|
||||
|
||||
translation.respond_to?(:html_safe) ? translation.html_safe : translation
|
||||
translation = I18n.translate(scope_key_by_partial(key), options.merge!(:raise => true))
|
||||
if html_safe_translation_key?(key) && translation.respond_to?(:html_safe)
|
||||
translation.html_safe
|
||||
else
|
||||
I18n.translate(scope_key_by_partial(key), options)
|
||||
translation
|
||||
end
|
||||
rescue I18n::MissingTranslationData => e
|
||||
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
|
||||
content_tag('span', keys.join(', '), :class => 'translation_missing')
|
||||
end
|
||||
alias :t :translate
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ module ActionView
|
||||
module UrlHelper
|
||||
# This helper may be included in any class that includes the
|
||||
# URL helpers of a routes (routes.url_helpers). Some methods
|
||||
# provided here will only work in the context of a request
|
||||
# provided here will only work in the4 context of a request
|
||||
# (link_to_unless_current, for instance), which must be provided
|
||||
# as a method called #request on the context.
|
||||
|
||||
@@ -235,8 +235,13 @@ module ActionView
|
||||
html_options = convert_options_to_data_attributes(options, html_options)
|
||||
url = url_for(options)
|
||||
|
||||
href = html_options['href']
|
||||
tag_options = tag_options(html_options)
|
||||
if html_options
|
||||
html_options = html_options.stringify_keys
|
||||
href = html_options['href']
|
||||
tag_options = tag_options(html_options)
|
||||
else
|
||||
tag_options = nil
|
||||
end
|
||||
|
||||
href_attr = "href=\"#{html_escape(url)}\"" unless href
|
||||
"<a #{href_attr}#{tag_options}>#{html_escape(name || url)}</a>".html_safe
|
||||
@@ -264,9 +269,8 @@ module ActionView
|
||||
# The +options+ hash accepts the same options as url_for.
|
||||
#
|
||||
# There are a few special +html_options+:
|
||||
# * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
|
||||
# <tt>:delete</tt> and <tt>:put</tt>. By default it will be <tt>:post</tt>.
|
||||
# * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
|
||||
# * <tt>:method</tt> - Specifies the anchor name to be appended to the path.
|
||||
# * <tt>:disabled</tt> - Specifies the anchor name to be appended to the path.
|
||||
# * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
|
||||
# prompt with the question specified. If the user accepts, the link is
|
||||
# processed normally, otherwise no action is taken.
|
||||
@@ -483,16 +487,14 @@ module ActionView
|
||||
extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}" unless subject.nil?
|
||||
extras = extras.empty? ? '' : '?' + html_escape(extras.join('&'))
|
||||
|
||||
email_address_obfuscated = email_address.to_str
|
||||
email_address_obfuscated = email_address.dup
|
||||
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
|
||||
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
|
||||
|
||||
string = ''
|
||||
|
||||
if encode == "javascript"
|
||||
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|
|
||||
"document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))}');".each_byte do |c|
|
||||
string << sprintf("%%%x", c)
|
||||
end
|
||||
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe
|
||||
@@ -591,7 +593,6 @@ module ActionView
|
||||
html_options['data-remote'] = 'true'
|
||||
end
|
||||
|
||||
disable_with = html_options.delete("disable_with")
|
||||
confirm = html_options.delete("confirm")
|
||||
|
||||
if html_options.key?("popup")
|
||||
@@ -600,7 +601,6 @@ module ActionView
|
||||
|
||||
method, href = html_options.delete("method"), html_options['href']
|
||||
|
||||
add_disable_with_to_attributes!(html_options, disable_with) if disable_with
|
||||
add_confirm_to_attributes!(html_options, confirm) if confirm
|
||||
add_method_to_attributes!(html_options, method) if method
|
||||
|
||||
@@ -611,10 +611,6 @@ module ActionView
|
||||
html_options["data-confirm"] = confirm if confirm
|
||||
end
|
||||
|
||||
def add_disable_with_to_attributes!(html_options, disable_with)
|
||||
html_options["data-disable-with"] = disable_with if disable_with
|
||||
end
|
||||
|
||||
def add_method_to_attributes!(html_options, method)
|
||||
html_options["rel"] = "nofollow" if method && method.to_s.downcase != "get"
|
||||
html_options["data-method"] = method if method
|
||||
|
||||
@@ -145,11 +145,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)
|
||||
@@ -167,11 +167,11 @@ module ActionView
|
||||
end
|
||||
|
||||
# Overload locale= to also set the I18n.locale. If the current I18n.config object responds
|
||||
# to original_config, it means that it's has a copy of the original I18n configuration and it's
|
||||
# to i18n_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?(:original_config) ? I18n.config.original_config : I18n.config
|
||||
config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
|
||||
config.locale = value
|
||||
end
|
||||
|
||||
@@ -226,4 +226,4 @@ module ActionView
|
||||
include Details
|
||||
include ViewPaths
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -26,36 +26,6 @@ module ActionView
|
||||
# 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.
|
||||
#
|
||||
# == The :as and :object options
|
||||
#
|
||||
# By default PartialRenderer uses the template name for the local name of the object passed into the template.
|
||||
# These examples are effectively the same:
|
||||
#
|
||||
# <%= render :partial => "contract", :locals => { :contract => @contract } %>
|
||||
#
|
||||
# <%= render :partial => "contract" %>
|
||||
#
|
||||
# By specifying the :as option we can change the way the local variable is namedin the template.
|
||||
# These examples are effectively the same:
|
||||
#
|
||||
# <%= render :partial => "contract", :as => :agreement
|
||||
#
|
||||
# <%= render :partial => "contract", :locals => { :agreement => @contract }
|
||||
#
|
||||
# The :object option can be used to directly specify which object is rendered into the partial.
|
||||
#
|
||||
# Revisiting a previous example we could have written this code.
|
||||
#
|
||||
# <%= render :partial => "account", :object => @buyer %>
|
||||
#
|
||||
# <% for ad in @advertisements %>
|
||||
# <%= render :partial => "ad", :object => ad %>
|
||||
# <% end %>
|
||||
#
|
||||
# The :object and :as options can be used together. We might have a partial which we have named genericly,
|
||||
# such as 'form'. Using :object and :as together helps us.
|
||||
#
|
||||
# <%= render :partial => "form", :object => @contract, :as => :contract %>
|
||||
# == Rendering a collection of partials
|
||||
#
|
||||
# The example of partial use describes a familiar pattern where a template needs to iterate over an array and
|
||||
@@ -69,13 +39,6 @@ module ActionView
|
||||
# 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 :as option may be used when rendering partials.
|
||||
#
|
||||
# Also, you can specify a partial which will be render as a spacer between each element by passing partial name to
|
||||
# +:spacer_template+. The following example will render "advertiser/_ad_divider.erb" between each ad partial.
|
||||
#
|
||||
# <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %>
|
||||
#
|
||||
# NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
|
||||
# just keep domain objects, like Active Records, in there.
|
||||
#
|
||||
@@ -237,7 +200,7 @@ module ActionView
|
||||
else
|
||||
@object = partial
|
||||
|
||||
if @collection = collection_from_object || collection
|
||||
if @collection = collection
|
||||
paths = @collection_paths = @collection.map { |o| partial_path(o) }
|
||||
@path = paths.uniq.size == 1 ? paths.first : nil
|
||||
else
|
||||
@@ -255,15 +218,13 @@ module ActionView
|
||||
render_collection
|
||||
end
|
||||
else
|
||||
block, options, locals = @block, @options, @locals
|
||||
|
||||
content = ActiveSupport::Notifications.instrument("render_partial.action_view",
|
||||
:identifier => identifier) do
|
||||
render_partial
|
||||
end
|
||||
|
||||
if !block && (layout = options[:layout])
|
||||
content = @view._render_layout(find_template(layout), locals){ content }
|
||||
if !@block && (layout = @options[:layout])
|
||||
content = @view._render_layout(find_template(layout), @locals){ content }
|
||||
end
|
||||
|
||||
content
|
||||
@@ -325,28 +286,23 @@ module ActionView
|
||||
end
|
||||
|
||||
def render_partial(object = @object)
|
||||
locals, view, template, block = @locals, @view, @template, @block
|
||||
locals, view, template = @locals, @view, @template
|
||||
|
||||
object ||= locals[template.variable_name]
|
||||
locals[@options[:as] || template.variable_name] = object
|
||||
|
||||
template.render(view, locals) do |*name|
|
||||
view._layout_for(*name, &block)
|
||||
view._layout_for(*name, &@block)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collection
|
||||
if @options.key?(:collection)
|
||||
collection = @options[:collection]
|
||||
collection.respond_to?(:to_ary) ? collection.to_ary : []
|
||||
end
|
||||
end
|
||||
|
||||
def collection_from_object
|
||||
if @object.respond_to?(:to_ary)
|
||||
@object.to_ary
|
||||
@object
|
||||
elsif @options.key?(:collection)
|
||||
@options[:collection] || []
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ module ActionView
|
||||
case options
|
||||
when Hash
|
||||
if block_given?
|
||||
_render_partial(options.merge(:partial => options.delete(:layout)), &block)
|
||||
_render_partial(options.merge(:partial => options[:layout]), &block)
|
||||
elsif options.key?(:partial)
|
||||
_render_partial(options)
|
||||
else
|
||||
|
||||
@@ -117,7 +117,7 @@ module ActionView
|
||||
@method_names = {}
|
||||
|
||||
format = details[:format] || :html
|
||||
@formats = Array.wrap(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
|
||||
@formats = Array.wrap(format).map(&:to_sym)
|
||||
@virtual_path = details[:virtual_path].try(:sub, ".#{format}", "")
|
||||
end
|
||||
|
||||
@@ -192,9 +192,6 @@ module ActionView
|
||||
locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join
|
||||
|
||||
if source.encoding_aware?
|
||||
# Avoid performing in-place mutation for SafeBuffer
|
||||
@source = source.to_str if source.html_safe?
|
||||
|
||||
# Look for # encoding: *. If we find one, we'll encode the
|
||||
# String in that encoding, otherwise, we'll use the
|
||||
# default external encoding.
|
||||
|
||||
@@ -14,7 +14,6 @@ module ActionView
|
||||
super(value.to_s)
|
||||
end
|
||||
alias :append= :<<
|
||||
alias :safe_append= :safe_concat
|
||||
|
||||
def append_if_string=(value)
|
||||
if value.is_a?(String) && !value.is_a?(NonConcattingString)
|
||||
@@ -28,28 +27,17 @@ module ActionView
|
||||
module Handlers
|
||||
class Erubis < ::Erubis::Eruby
|
||||
def add_preamble(src)
|
||||
@newline_pending = 0
|
||||
src << "@output_buffer = ActionView::OutputBuffer.new;"
|
||||
end
|
||||
|
||||
def add_text(src, text)
|
||||
return if text.empty?
|
||||
if text == "\n"
|
||||
@newline_pending += 1
|
||||
else
|
||||
src << "@output_buffer.safe_concat('"
|
||||
src << "\n" * @newline_pending
|
||||
src << escape_text(text)
|
||||
src << "'.freeze);"
|
||||
@newline_pending = 0
|
||||
end
|
||||
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
|
||||
end
|
||||
|
||||
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
||||
|
||||
def add_expr_literal(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
|
||||
if code =~ BLOCK_EXPR
|
||||
src << '@output_buffer.append= ' << code
|
||||
else
|
||||
@@ -58,8 +46,6 @@ module ActionView
|
||||
end
|
||||
|
||||
def add_stmt(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
|
||||
if code =~ BLOCK_EXPR
|
||||
src << '@output_buffer.append_if_string= ' << code
|
||||
else
|
||||
@@ -68,29 +54,12 @@ module ActionView
|
||||
end
|
||||
|
||||
def add_expr_escaped(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
|
||||
if code =~ BLOCK_EXPR
|
||||
src << "@output_buffer.safe_append= " << code
|
||||
else
|
||||
src << "@output_buffer.safe_concat((" << code << ").to_s);"
|
||||
end
|
||||
src << '@output_buffer.append= ' << escaped_expr(code) << ';'
|
||||
end
|
||||
|
||||
def add_postamble(src)
|
||||
flush_newline_if_pending(src)
|
||||
|
||||
src << '@output_buffer.to_s'
|
||||
end
|
||||
|
||||
def flush_newline_if_pending(src)
|
||||
if @newline_pending > 0
|
||||
src << "@output_buffer.safe_concat('"
|
||||
src << "\n" * @newline_pending
|
||||
src << "'.freeze);"
|
||||
@newline_pending = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ERB < Handler
|
||||
|
||||
@@ -63,7 +63,7 @@ module ActionView
|
||||
end
|
||||
|
||||
def query(path, exts, formats)
|
||||
query = escape_entry File.join(@path, path)
|
||||
query = File.join(@path, path)
|
||||
|
||||
exts.each do |ext|
|
||||
query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << ',}'
|
||||
@@ -72,24 +72,14 @@ module ActionView
|
||||
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 }
|
||||
|
||||
templates << Template.new(contents, File.expand_path(p), handler,
|
||||
Template.new(contents, File.expand_path(p), handler,
|
||||
:virtual_path => path, :format => format)
|
||||
end
|
||||
|
||||
templates
|
||||
end
|
||||
|
||||
def escape_entry(entry)
|
||||
entry.gsub(/(\*|\[|\]|\{|\}|\?)/, "\\\\\\1")
|
||||
end
|
||||
|
||||
# Extract handler and formats from path. If a format cannot be a found neither
|
||||
|
||||
@@ -74,11 +74,6 @@ module ActionView
|
||||
@helper_class ||= determine_default_helper_class(name)
|
||||
end
|
||||
|
||||
def new(*)
|
||||
include_helper_modules!
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def include_helper_modules!
|
||||
@@ -94,6 +89,7 @@ module ActionView
|
||||
@output_buffer = ActiveSupport::SafeBuffer.new
|
||||
@rendered = ''
|
||||
|
||||
self.class.send(:include_helper_modules!)
|
||||
make_test_case_available_to_view!
|
||||
say_no_to_protect_against_forgery!
|
||||
end
|
||||
@@ -103,7 +99,7 @@ module ActionView
|
||||
end
|
||||
|
||||
def render(options = {}, local_assigns = {}, &block)
|
||||
view.assign(view_assigns)
|
||||
view.assign(_assigns)
|
||||
@rendered << output = view.render(options, local_assigns, &block)
|
||||
output
|
||||
end
|
||||
@@ -153,8 +149,10 @@ module ActionView
|
||||
# The instance of ActionView::Base that is used by +render+.
|
||||
def view
|
||||
@view ||= begin
|
||||
view = @controller.view_context
|
||||
view = ActionView::Base.new(ActionController::Base.view_paths, {}, @controller)
|
||||
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
|
||||
@@ -164,48 +162,34 @@ module ActionView
|
||||
|
||||
alias_method :_view, :view
|
||||
|
||||
INTERNAL_IVARS = %w{
|
||||
@__name__
|
||||
@__io__
|
||||
EXCLUDE_IVARS = %w{
|
||||
@_assertion_wrapped
|
||||
@_assertions
|
||||
@_result
|
||||
@_routes
|
||||
@controller
|
||||
@layouts
|
||||
@locals
|
||||
@method_name
|
||||
@output_buffer
|
||||
@partials
|
||||
@passed
|
||||
@rendered
|
||||
@request
|
||||
@routes
|
||||
@templates
|
||||
@options
|
||||
@test_passed
|
||||
@view
|
||||
@view_context_class
|
||||
}
|
||||
|
||||
def _user_defined_ivars
|
||||
instance_variables.map(&:to_s) - INTERNAL_IVARS
|
||||
end
|
||||
|
||||
# Returns a Hash of instance variables and their values, as defined by
|
||||
# the user in the test case, which are then assigned to the view being
|
||||
# rendered. This is generally intended for internal use and extension
|
||||
# frameworks.
|
||||
def view_assigns
|
||||
Hash[_user_defined_ivars.map do |var|
|
||||
[var[1..-1].to_sym, instance_variable_get(var)]
|
||||
end]
|
||||
def _instance_variables
|
||||
instance_variables.map(&:to_s) - EXCLUDE_IVARS
|
||||
end
|
||||
|
||||
def _assigns
|
||||
ActiveSupport::Deprecation.warn "ActionView::TestCase#_assigns is deprecated and will be removed in future versions. " <<
|
||||
"Please use view_assigns instead."
|
||||
view_assigns
|
||||
_instance_variables.inject({}) do |hash, var|
|
||||
name = var[1..-1].to_sym
|
||||
hash[name] = instance_variable_get(var)
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
def _routes
|
||||
|
||||
@@ -22,11 +22,10 @@ module ActionView #:nodoc:
|
||||
end
|
||||
|
||||
templates = []
|
||||
@hash.each do |_path, source|
|
||||
next unless _path =~ /^#{query}$/
|
||||
handler, format = extract_handler_and_format(_path, formats)
|
||||
templates << Template.new(source, _path, handler,
|
||||
:virtual_path => _path, :format => format)
|
||||
@hash.select { |k,v| k =~ /^#{query}$/ }.each do |path, source|
|
||||
handler, format = extract_handler_and_format(path, formats)
|
||||
templates << Template.new(source, path, handler,
|
||||
:virtual_path => path, :format => format)
|
||||
end
|
||||
|
||||
templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
|
||||
|
||||
@@ -243,27 +243,6 @@ module AbstractController
|
||||
assert_equal "Success", controller.response_body
|
||||
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
|
||||
result = controller.process(:index, " Howdy!")
|
||||
assert_equal "Hello world Howdy!", controller.response_body
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,14 +36,17 @@ require 'active_record'
|
||||
require 'action_controller/caching'
|
||||
require 'action_controller/caching/sweeping'
|
||||
|
||||
begin
|
||||
require 'ruby-debug'
|
||||
Debugger.settings[:autoeval] = true
|
||||
Debugger.start
|
||||
rescue LoadError
|
||||
# Debugging disabled. `gem install ruby-debug` to enable.
|
||||
end
|
||||
|
||||
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
|
||||
|
||||
module Rails
|
||||
class << self
|
||||
def env
|
||||
@_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Monkey patch the old routes initialization to be silenced.
|
||||
|
||||
@@ -23,7 +23,6 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
|
||||
def call_reset_session
|
||||
session[:foo]
|
||||
reset_session
|
||||
reset_session if params[:twice]
|
||||
session[:foo] = "baz"
|
||||
head :ok
|
||||
end
|
||||
@@ -75,17 +74,6 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_calling_reset_session_twice_does_not_raise_errors
|
||||
with_test_route_set do
|
||||
get '/call_reset_session', :twice => "true"
|
||||
assert_response :success
|
||||
|
||||
get '/get_session_value'
|
||||
assert_response :success
|
||||
assert_equal 'foo: "baz"', response.body
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_session_value_after_session_reset
|
||||
with_test_route_set do
|
||||
get '/set_session_value'
|
||||
|
||||
@@ -234,14 +234,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
def test_string_constraint
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
match "photos", :to => 'action_pack_assertions#nothing', :constraints => {:subdomain => "admin"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_redirect_to_named_route_failure
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
@@ -555,14 +547,6 @@ class AssertTemplateTest < ActionController::TestCase
|
||||
assert_template :hello_planet
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_template_reset_between_requests
|
||||
get :hello_world
|
||||
assert_template 'test/hello_world'
|
||||
|
||||
get :nothing
|
||||
assert_template nil
|
||||
end
|
||||
end
|
||||
|
||||
class ActionPackHeaderTest < ActionController::TestCase
|
||||
|
||||
@@ -22,15 +22,6 @@ class AssertSelectTest < ActionController::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
class AssertMultipartSelectMailer < ActionMailer::Base
|
||||
def test(options)
|
||||
mail :subject => "Test e-mail", :from => "test@test.host", :to => "test <test@test.host>" do |format|
|
||||
format.text { render :text => options[:text] }
|
||||
format.html { render :text => options[:html] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class AssertSelectController < ActionController::Base
|
||||
def response_with=(content)
|
||||
@content = content
|
||||
@@ -470,8 +461,8 @@ class AssertSelectTest < ActionController::TestCase
|
||||
|
||||
assert_select_rjs :remove, "test1"
|
||||
|
||||
rescue Assertion => e
|
||||
assert_equal "No RJS statement that removes 'test1' was rendered.", e.message
|
||||
rescue Assertion
|
||||
assert_equal "No RJS statement that removes 'test1' was rendered.", $!.message
|
||||
end
|
||||
|
||||
def test_assert_select_rjs_for_remove_ignores_block
|
||||
@@ -502,8 +493,8 @@ class AssertSelectTest < ActionController::TestCase
|
||||
|
||||
assert_select_rjs :show, "test1"
|
||||
|
||||
rescue Assertion => e
|
||||
assert_equal "No RJS statement that shows 'test1' was rendered.", e.message
|
||||
rescue Assertion
|
||||
assert_equal "No RJS statement that shows 'test1' was rendered.", $!.message
|
||||
end
|
||||
|
||||
def test_assert_select_rjs_for_show_ignores_block
|
||||
@@ -534,8 +525,8 @@ class AssertSelectTest < ActionController::TestCase
|
||||
|
||||
assert_select_rjs :hide, "test1"
|
||||
|
||||
rescue Assertion => e
|
||||
assert_equal "No RJS statement that hides 'test1' was rendered.", e.message
|
||||
rescue Assertion
|
||||
assert_equal "No RJS statement that hides 'test1' was rendered.", $!.message
|
||||
end
|
||||
|
||||
def test_assert_select_rjs_for_hide_ignores_block
|
||||
@@ -566,8 +557,8 @@ class AssertSelectTest < ActionController::TestCase
|
||||
|
||||
assert_select_rjs :toggle, "test1"
|
||||
|
||||
rescue Assertion => e
|
||||
assert_equal "No RJS statement that toggles 'test1' was rendered.", e.message
|
||||
rescue Assertion
|
||||
assert_equal "No RJS statement that toggles 'test1' was rendered.", $!.message
|
||||
end
|
||||
|
||||
def test_assert_select_rjs_for_toggle_ignores_block
|
||||
@@ -733,16 +724,6 @@ EOF
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_select_email_multipart
|
||||
AssertMultipartSelectMailer.test(:html => "<div><p>foo</p><p>bar</p></div>", :text => 'foo bar').deliver
|
||||
assert_select_email do
|
||||
assert_select "div:root" do
|
||||
assert_select "p:first-child", "foo"
|
||||
assert_select "p:last-child", "bar"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def render_html(html)
|
||||
@controller.response_with = html
|
||||
|
||||
@@ -16,14 +16,7 @@ end
|
||||
class PageCachingTestController < CachingController
|
||||
caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
|
||||
caches_page :found, :not_found
|
||||
caches_page :about_me
|
||||
|
||||
def about_me
|
||||
respond_to do |format|
|
||||
format.html {render :text => 'I am html'}
|
||||
format.xml {render :text => 'I am xml'}
|
||||
end
|
||||
end
|
||||
|
||||
def ok
|
||||
head :ok
|
||||
@@ -81,13 +74,6 @@ class PageCachingTest < ActionController::TestCase
|
||||
@controller.perform_caching = false
|
||||
end
|
||||
|
||||
def test_should_obey_http_accept_attribute
|
||||
@request.env['HTTP_ACCEPT'] = 'text/xml'
|
||||
get :about_me
|
||||
assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/about_me.xml")
|
||||
assert_equal 'I am xml', @response.body
|
||||
end
|
||||
|
||||
def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
@@ -547,11 +533,6 @@ class ActionCacheTest < ActionController::TestCase
|
||||
assert_response 404
|
||||
end
|
||||
|
||||
def test_four_oh_four_renders_content
|
||||
get :four_oh_four
|
||||
assert_equal "404'd!", @response.body
|
||||
end
|
||||
|
||||
def test_simple_runtime_error_returns_500_for_multiple_requests
|
||||
get :simple_runtime_error
|
||||
assert_response 500
|
||||
@@ -785,53 +766,3 @@ CACHED
|
||||
assert_equal " <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
|
||||
end
|
||||
end
|
||||
|
||||
class CacheHelperOutputBufferTest < ActionController::TestCase
|
||||
|
||||
class MockController
|
||||
def read_fragment(name, options)
|
||||
return false
|
||||
end
|
||||
|
||||
def write_fragment(name, fragment, options)
|
||||
fragment
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
end
|
||||
|
||||
def test_output_buffer
|
||||
output_buffer = ActionView::OutputBuffer.new
|
||||
controller = MockController.new
|
||||
cache_helper = Object.new
|
||||
cache_helper.extend(ActionView::Helpers::CacheHelper)
|
||||
cache_helper.expects(:controller).returns(controller).at_least(0)
|
||||
cache_helper.expects(:output_buffer).returns(output_buffer).at_least(0)
|
||||
# if the output_buffer is changed, the new one should be html_safe and of the same type
|
||||
cache_helper.expects(:output_buffer=).with(responds_with(:html_safe?, true)).with(instance_of(output_buffer.class)).at_least(0)
|
||||
|
||||
assert_nothing_raised do
|
||||
cache_helper.send :fragment_for, 'Test fragment name', 'Test fragment', &Proc.new{ nil }
|
||||
assert output_buffer.html_safe?, "Output buffer should stay html_safe"
|
||||
end
|
||||
end
|
||||
|
||||
def test_safe_buffer
|
||||
output_buffer = ActiveSupport::SafeBuffer.new
|
||||
controller = MockController.new
|
||||
cache_helper = Object.new
|
||||
cache_helper.extend(ActionView::Helpers::CacheHelper)
|
||||
cache_helper.expects(:controller).returns(controller).at_least(0)
|
||||
cache_helper.expects(:output_buffer).returns(output_buffer).at_least(0)
|
||||
# if the output_buffer is changed, the new one should be html_safe and of the same type
|
||||
cache_helper.expects(:output_buffer=).with(responds_with(:html_safe?, true)).with(instance_of(output_buffer.class)).at_least(0)
|
||||
|
||||
assert_nothing_raised do
|
||||
cache_helper.send :fragment_for, 'Test fragment name', 'Test fragment', &Proc.new{ nil }
|
||||
assert output_buffer.html_safe?, "Output buffer should stay html_safe"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -78,8 +78,7 @@ class FilterTest < ActionController::TestCase
|
||||
end
|
||||
|
||||
class RenderingController < ActionController::Base
|
||||
before_filter :before_filter_rendering
|
||||
after_filter :unreached_after_filter
|
||||
before_filter :render_something_else
|
||||
|
||||
def show
|
||||
@ran_action = true
|
||||
@@ -87,59 +86,9 @@ class FilterTest < ActionController::TestCase
|
||||
end
|
||||
|
||||
private
|
||||
def before_filter_rendering
|
||||
@ran_filter ||= []
|
||||
@ran_filter << "before_filter_rendering"
|
||||
def render_something_else
|
||||
render :inline => "something else"
|
||||
end
|
||||
|
||||
def unreached_after_filter
|
||||
@ran_filter << "unreached_after_filter_after_render"
|
||||
end
|
||||
end
|
||||
|
||||
class RenderingForPrependAfterFilterController < RenderingController
|
||||
prepend_after_filter :unreached_prepend_after_filter
|
||||
|
||||
private
|
||||
def unreached_prepend_after_filter
|
||||
@ran_filter << "unreached_preprend_after_filter_after_render"
|
||||
end
|
||||
end
|
||||
|
||||
class BeforeFilterRedirectionController < ActionController::Base
|
||||
before_filter :before_filter_redirects
|
||||
after_filter :unreached_after_filter
|
||||
|
||||
def show
|
||||
@ran_action = true
|
||||
render :inline => "ran show action"
|
||||
end
|
||||
|
||||
def target_of_redirection
|
||||
@ran_target_of_redirection = true
|
||||
render :inline => "ran target_of_redirection action"
|
||||
end
|
||||
|
||||
private
|
||||
def before_filter_redirects
|
||||
@ran_filter ||= []
|
||||
@ran_filter << "before_filter_redirects"
|
||||
redirect_to(:action => 'target_of_redirection')
|
||||
end
|
||||
|
||||
def unreached_after_filter
|
||||
@ran_filter << "unreached_after_filter_after_redirection"
|
||||
end
|
||||
end
|
||||
|
||||
class BeforeFilterRedirectionForPrependAfterFilterController < BeforeFilterRedirectionController
|
||||
prepend_after_filter :unreached_prepend_after_filter_after_redirection
|
||||
|
||||
private
|
||||
def unreached_prepend_after_filter_after_redirection
|
||||
@ran_filter << "unreached_prepend_after_filter_after_redirection"
|
||||
end
|
||||
end
|
||||
|
||||
class ConditionalFilterController < ActionController::Base
|
||||
@@ -503,22 +452,6 @@ class FilterTest < ActionController::TestCase
|
||||
render :text => 'hello world'
|
||||
end
|
||||
end
|
||||
|
||||
class ImplicitActionsController < ActionController::Base
|
||||
before_filter :find_only, :only => :edit
|
||||
before_filter :find_except, :except => :edit
|
||||
|
||||
private
|
||||
|
||||
def find_only
|
||||
@only = 'Only'
|
||||
end
|
||||
|
||||
def find_except
|
||||
@except = 'Except'
|
||||
end
|
||||
end
|
||||
|
||||
def test_sweeper_should_not_block_rendering
|
||||
response = test_process(SweeperTestController)
|
||||
assert_equal 'hello world', response.body
|
||||
@@ -529,11 +462,6 @@ class FilterTest < ActionController::TestCase
|
||||
assert sweeper.before(TestController.new)
|
||||
end
|
||||
|
||||
def test_after_method_of_sweeper_should_always_return_nil
|
||||
sweeper = ActionController::Caching::Sweeper.send(:new)
|
||||
assert_nil sweeper.after(TestController.new)
|
||||
end
|
||||
|
||||
def test_non_yielding_around_filters_not_returning_false_do_not_raise
|
||||
controller = NonYieldingAroundFilterController.new
|
||||
controller.instance_variable_set "@filter_return_value", true
|
||||
@@ -695,32 +623,6 @@ class FilterTest < ActionController::TestCase
|
||||
assert !assigns["ran_action"]
|
||||
end
|
||||
|
||||
def test_before_filter_rendering_breaks_filtering_chain_for_after_filter
|
||||
response = 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)
|
||||
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)
|
||||
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)
|
||||
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"]
|
||||
end
|
||||
|
||||
def test_filters_with_mixed_specialization_run_in_order
|
||||
assert_nothing_raised do
|
||||
response = test_process(MixedSpecializationController, 'bar')
|
||||
@@ -802,19 +704,6 @@ class FilterTest < ActionController::TestCase
|
||||
assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body)
|
||||
end
|
||||
|
||||
def test_filters_obey_only_and_except_for_implicit_actions
|
||||
test_process(ImplicitActionsController, 'show')
|
||||
assert_nil assigns(:user)
|
||||
assert_equal 'Except', assigns(:except)
|
||||
assert_nil assigns(:only)
|
||||
assert_equal 'show', response.body
|
||||
|
||||
test_process(ImplicitActionsController, 'edit')
|
||||
assert_equal 'Only', assigns(:only)
|
||||
assert_nil assigns(:except)
|
||||
assert_equal 'edit', response.body
|
||||
end
|
||||
|
||||
private
|
||||
def test_process(controller, action = "show")
|
||||
@controller = controller.is_a?(Class) ? controller.new : controller
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user