Compare commits

..

2 Commits

Author SHA1 Message Date
Michael Koziarski
bac6ba99b1 Prepare for the 3.0.1 release 2010-10-15 09:13:00 +13:00
Michael Koziarski
2d96bccb1e Revert 0c0b0aa0f2 which introduced a security vulnerability.
This addresses  CVE-2010-3933
2010-10-13 14:28:06 +13:00
673 changed files with 4742 additions and 18561 deletions

4
.gitignore vendored
View File

@@ -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

View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

@@ -1 +1 @@
3.0.20
3.0.1

View File

@@ -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"

View File

@@ -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)*

View File

@@ -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

View File

@@ -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

View File

@@ -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'

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -25,6 +25,7 @@ end
silence_warnings do
# These external dependencies have warnings :/
require 'text/format'
require 'mail'
end

View File

@@ -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

View File

@@ -1,4 +0,0 @@
Hello there,
Mr. <%= @recipient %>. Be greeted, new member!

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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>') # => "...&lt;script&gt;..."
*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]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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|

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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(*)

View File

@@ -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)

View File

@@ -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

View File

@@ -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?

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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'

View File

@@ -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

View File

@@ -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

View File

@@ -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>;

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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!

View File

@@ -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.

View File

@@ -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

View File

@@ -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"
#

View File

@@ -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

View File

@@ -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

View File

@@ -12,6 +12,7 @@ module ActionView #:nodoc:
autoload :CsrfHelper
autoload :DateHelper
autoload :DebugHelper
autoload :DeprecatedBlockHelpers
autoload :FormHelper
autoload :FormOptionsHelper
autoload :FormTagHelper

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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>
#

View File

@@ -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 => "&pound;", :separator => ",", :delimiter => "")
# # => &pound;1234567890,50
# number_to_currency(1234567890.50, :unit => "&pound;", :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

View File

@@ -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

View File

@@ -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.

View File

@@ -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>&lt;a href=\"http://example.com/\"&gt;Example&lt;/a&gt;</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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 }

View File

@@ -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

View File

@@ -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.

View File

@@ -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'

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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