Merge branch 'master' of github.com:rails/rails
Conflicts: guides/source/security.md
This commit is contained in:
commit
4f252cddc1
@ -21,16 +21,12 @@ env:
|
|||||||
- "GEM=aj:integration"
|
- "GEM=aj:integration"
|
||||||
- "GEM=guides"
|
- "GEM=guides"
|
||||||
rvm:
|
rvm:
|
||||||
- 2.2.2
|
- 2.2.3
|
||||||
- ruby-head
|
- ruby-head
|
||||||
- rbx-2
|
|
||||||
- jruby-head
|
|
||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- env: "GEM=ar:mysql"
|
- env: "GEM=ar:mysql"
|
||||||
- rvm: ruby-head
|
- rvm: ruby-head
|
||||||
- rvm: rbx-2
|
|
||||||
- rvm: jruby-head
|
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
12
CODE_OF_CONDUCT.md
Normal file
12
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Contributor Code of Conduct
|
||||||
|
|
||||||
|
The Rails team is committed to fostering a welcoming community.
|
||||||
|
|
||||||
|
**Our Code of Conduct can be found here**:
|
||||||
|
|
||||||
|
http://rubyonrails.org/conduct/
|
||||||
|
|
||||||
|
For a history of updates, see the page history here:
|
||||||
|
|
||||||
|
https://github.com/rails/rails.github.com/commits/master/conduct/index.html
|
||||||
|
|
7
Gemfile
7
Gemfile
@ -7,6 +7,7 @@ gem 'rake', '>= 10.3'
|
|||||||
|
|
||||||
# Active Job depends on the URI::GID::MissingModelIDError, which isn't released yet.
|
# Active Job depends on the URI::GID::MissingModelIDError, which isn't released yet.
|
||||||
gem 'globalid', github: 'rails/globalid'
|
gem 'globalid', github: 'rails/globalid'
|
||||||
|
gem 'rack', github: 'rack/rack'
|
||||||
|
|
||||||
# This needs to be with require false as it is
|
# This needs to be with require false as it is
|
||||||
# loaded after loading the test library to
|
# loaded after loading the test library to
|
||||||
@ -20,8 +21,9 @@ gem 'turbolinks'
|
|||||||
gem 'arel', github: 'rails/arel', branch: 'master'
|
gem 'arel', github: 'rails/arel', branch: 'master'
|
||||||
gem 'mail', github: 'mikel/mail'
|
gem 'mail', github: 'mikel/mail'
|
||||||
|
|
||||||
gem 'sprockets', '~> 3.0.0.rc.1'
|
gem 'sprockets', github: 'rails/sprockets', branch: 'master'
|
||||||
gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
|
gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
|
||||||
|
gem 'sass-rails', github: 'rails/sass-rails', branch: 'master'
|
||||||
|
|
||||||
# require: false so bcrypt is loaded only when has_secure_password is used.
|
# require: false so bcrypt is loaded only when has_secure_password is used.
|
||||||
# This is to avoid ActiveModel (and by extension the entire framework)
|
# This is to avoid ActiveModel (and by extension the entire framework)
|
||||||
@ -31,6 +33,7 @@ gem 'bcrypt', '~> 3.1.10', require: false
|
|||||||
# This needs to be with require false to avoid
|
# This needs to be with require false to avoid
|
||||||
# it being automatically loaded by sprockets
|
# it being automatically loaded by sprockets
|
||||||
gem 'uglifier', '>= 1.3.0', require: false
|
gem 'uglifier', '>= 1.3.0', require: false
|
||||||
|
gem 'sass', '>= 3.3', require: false
|
||||||
|
|
||||||
group :doc do
|
group :doc do
|
||||||
gem 'sdoc', '~> 0.4.0'
|
gem 'sdoc', '~> 0.4.0'
|
||||||
@ -49,7 +52,7 @@ group :job do
|
|||||||
gem 'sidekiq', require: false
|
gem 'sidekiq', require: false
|
||||||
gem 'sucker_punch', require: false
|
gem 'sucker_punch', require: false
|
||||||
gem 'delayed_job', require: false
|
gem 'delayed_job', require: false
|
||||||
gem 'queue_classic', require: false, platforms: :ruby
|
gem 'queue_classic', github: "QueueClassic/queue_classic", require: false, platforms: :ruby
|
||||||
gem 'sneakers', require: false
|
gem 'sneakers', require: false
|
||||||
gem 'que', require: false
|
gem 'que', require: false
|
||||||
gem 'backburner', require: false
|
gem 'backburner', require: false
|
||||||
|
167
Gemfile.lock
167
Gemfile.lock
@ -1,3 +1,10 @@
|
|||||||
|
GIT
|
||||||
|
remote: git://github.com/QueueClassic/queue_classic.git
|
||||||
|
revision: d144db29f1436e9e8b3c7a1a1ecd4442316a9ecd
|
||||||
|
specs:
|
||||||
|
queue_classic (3.2.0.alpha)
|
||||||
|
pg (>= 0.17, < 0.19)
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/bkeepers/qu.git
|
remote: git://github.com/bkeepers/qu.git
|
||||||
revision: d098e2657c92e89a6413bebd9c033930759c061f
|
revision: d098e2657c92e89a6413bebd9c033930759c061f
|
||||||
@ -13,44 +20,70 @@ GIT
|
|||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/mikel/mail.git
|
remote: git://github.com/mikel/mail.git
|
||||||
revision: b159e0a542962fdd5e292a48cfffa560d7cf412e
|
revision: 64ef1a12efcdda53fd63e1456c2c564044bf82ce
|
||||||
specs:
|
specs:
|
||||||
mail (2.6.3.edge)
|
mail (2.6.3.edge)
|
||||||
mime-types (>= 1.16, < 3)
|
mime-types (>= 1.16, < 3)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/rack/rack.git
|
||||||
|
revision: c94e22401d4719b4d78378c7b63362cd692f9005
|
||||||
|
specs:
|
||||||
|
rack (2.0.0.alpha)
|
||||||
|
json
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/rails/arel.git
|
remote: git://github.com/rails/arel.git
|
||||||
revision: aac9da257f291ad8d2d4f914528881c240848bb2
|
revision: d5432b4616ff43fbb14540d351eed351e21bb20e
|
||||||
branch: master
|
branch: master
|
||||||
specs:
|
specs:
|
||||||
arel (7.0.0.alpha)
|
arel (7.0.0.alpha)
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/rails/globalid.git
|
remote: git://github.com/rails/globalid.git
|
||||||
revision: 4df66fb9e9f0c832d29119aa8bc30be55a614b71
|
revision: 8178ff2dc898a8f49dd71f6eb46f2a09712462de
|
||||||
specs:
|
specs:
|
||||||
globalid (0.3.5)
|
globalid (0.3.6)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/rails/jquery-rails.git
|
remote: git://github.com/rails/jquery-rails.git
|
||||||
revision: 272abdd319bb3381b23182b928b25320590096b0
|
revision: 38053f45402f1ccc4cce5dd8c7005ec707376db3
|
||||||
branch: master
|
branch: master
|
||||||
specs:
|
specs:
|
||||||
jquery-rails (4.0.3)
|
jquery-rails (4.0.4)
|
||||||
rails-dom-testing (~> 1.0)
|
rails-dom-testing (~> 1.0)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/rails/sprockets-rails.git
|
remote: git://github.com/rails/sass-rails.git
|
||||||
revision: 85b89c44ad40af3056899808475e6e4bf65c1f5a
|
revision: 805cb17722b8a13ff00dffe20283a6ba2c9a45dc
|
||||||
branch: master
|
branch: master
|
||||||
specs:
|
specs:
|
||||||
sprockets-rails (3.0.0.beta1)
|
sass-rails (6.0.0)
|
||||||
|
railties (>= 4.0.0, < 5.0)
|
||||||
|
sass (~> 3.3)
|
||||||
|
sprockets (>= 4.0)
|
||||||
|
sprockets-rails (< 4.0)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/rails/sprockets-rails.git
|
||||||
|
revision: e65e088575be4f6b994823b80a277affb0a57a5e
|
||||||
|
branch: master
|
||||||
|
specs:
|
||||||
|
sprockets-rails (3.0.0.beta2)
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0, < 4.0)
|
sprockets (>= 3.0.0)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/rails/sprockets.git
|
||||||
|
revision: a61550db7184fc83964fb983547ff09da9efa9be
|
||||||
|
branch: master
|
||||||
|
specs:
|
||||||
|
sprockets (4.0.0)
|
||||||
|
rack (~> 2.x)
|
||||||
|
|
||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
@ -64,7 +97,7 @@ PATH
|
|||||||
actionpack (5.0.0.alpha)
|
actionpack (5.0.0.alpha)
|
||||||
actionview (= 5.0.0.alpha)
|
actionview (= 5.0.0.alpha)
|
||||||
activesupport (= 5.0.0.alpha)
|
activesupport (= 5.0.0.alpha)
|
||||||
rack (~> 1.6)
|
rack (~> 2.x)
|
||||||
rack-test (~> 0.6.3)
|
rack-test (~> 0.6.3)
|
||||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
@ -85,8 +118,10 @@ PATH
|
|||||||
activesupport (= 5.0.0.alpha)
|
activesupport (= 5.0.0.alpha)
|
||||||
arel (= 7.0.0.alpha)
|
arel (= 7.0.0.alpha)
|
||||||
activesupport (5.0.0.alpha)
|
activesupport (5.0.0.alpha)
|
||||||
|
concurrent-ruby (~> 0.9.0)
|
||||||
i18n (~> 0.7)
|
i18n (~> 0.7)
|
||||||
json (~> 1.7, >= 1.7.7)
|
json (~> 1.7, >= 1.7.7)
|
||||||
|
method_source
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
thread_safe (~> 0.3, >= 0.3.4)
|
thread_safe (~> 0.3, >= 0.3.4)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
@ -111,83 +146,73 @@ PATH
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
amq-protocol (1.9.2)
|
amq-protocol (2.0.0)
|
||||||
backburner (0.4.6)
|
backburner (1.0.0)
|
||||||
beaneater (~> 0.3.1)
|
beaneater (~> 1.0)
|
||||||
dante (~> 0.1.5)
|
dante (> 0.1.5)
|
||||||
bcrypt (3.1.10)
|
bcrypt (3.1.10)
|
||||||
bcrypt (3.1.10-x64-mingw32)
|
bcrypt (3.1.10-x64-mingw32)
|
||||||
bcrypt (3.1.10-x86-mingw32)
|
bcrypt (3.1.10-x86-mingw32)
|
||||||
beaneater (0.3.3)
|
beaneater (1.0.0)
|
||||||
benchmark-ips (2.1.1)
|
benchmark-ips (2.3.0)
|
||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
bunny (1.7.0)
|
bunny (2.0.0)
|
||||||
amq-protocol (>= 1.9.2)
|
amq-protocol (>= 1.9.2)
|
||||||
byebug (4.0.5)
|
byebug (6.0.2)
|
||||||
columnize (= 0.9.0)
|
|
||||||
celluloid (0.16.0)
|
celluloid (0.16.0)
|
||||||
timers (~> 4.0.0)
|
timers (~> 4.0.0)
|
||||||
coffee-rails (4.1.0)
|
coffee-rails (4.1.0)
|
||||||
coffee-script (>= 2.2.0)
|
coffee-script (>= 2.2.0)
|
||||||
railties (>= 4.0.0, < 5.0)
|
railties (>= 4.0.0, < 5.0)
|
||||||
coffee-script (2.3.0)
|
coffee-script (2.4.1)
|
||||||
coffee-script-source
|
coffee-script-source
|
||||||
execjs
|
execjs
|
||||||
coffee-script-source (1.9.0)
|
coffee-script-source (1.9.1.1)
|
||||||
columnize (0.9.0)
|
concurrent-ruby (0.9.1)
|
||||||
connection_pool (2.1.1)
|
connection_pool (2.2.0)
|
||||||
dalli (2.7.2)
|
dalli (2.7.4)
|
||||||
dante (0.1.5)
|
dante (0.2.0)
|
||||||
delayed_job (4.0.6)
|
delayed_job (4.0.6)
|
||||||
activesupport (>= 3.0, < 5.0)
|
activesupport (>= 3.0, < 5.0)
|
||||||
delayed_job_active_record (4.0.3)
|
delayed_job_active_record (4.0.3)
|
||||||
activerecord (>= 3.0, < 5.0)
|
activerecord (>= 3.0, < 5.0)
|
||||||
delayed_job (>= 3.0, < 4.1)
|
delayed_job (>= 3.0, < 4.1)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
execjs (2.3.0)
|
execjs (2.6.0)
|
||||||
hitimes (1.2.2)
|
hitimes (1.2.2)
|
||||||
hitimes (1.2.2-x86-mingw32)
|
hitimes (1.2.2-x86-mingw32)
|
||||||
i18n (0.7.0)
|
i18n (0.7.0)
|
||||||
json (1.8.2)
|
json (1.8.3)
|
||||||
kindlerb (0.1.1)
|
kindlerb (0.1.1)
|
||||||
mustache
|
mustache
|
||||||
nokogiri
|
nokogiri
|
||||||
loofah (2.0.1)
|
loofah (2.0.3)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
metaclass (0.0.4)
|
metaclass (0.0.4)
|
||||||
method_source (0.8.2)
|
method_source (0.8.2)
|
||||||
mime-types (2.4.3)
|
mime-types (2.6.1)
|
||||||
mini_portile (0.6.2)
|
mini_portile (0.6.2)
|
||||||
minitest (5.3.3)
|
minitest (5.3.3)
|
||||||
mocha (0.14.0)
|
mocha (0.14.0)
|
||||||
metaclass (~> 0.0.1)
|
metaclass (~> 0.0.1)
|
||||||
mono_logger (1.1.0)
|
mono_logger (1.1.0)
|
||||||
multi_json (1.11.0)
|
multi_json (1.11.2)
|
||||||
mustache (1.0.0)
|
mustache (1.0.2)
|
||||||
mysql (2.9.1)
|
mysql (2.9.1)
|
||||||
mysql2 (0.3.18)
|
mysql2 (0.3.19)
|
||||||
nokogiri (1.6.6.2)
|
nokogiri (1.6.6.2)
|
||||||
mini_portile (~> 0.6.0)
|
mini_portile (~> 0.6.0)
|
||||||
nokogiri (1.6.6.2-x64-mingw32)
|
pg (0.18.2)
|
||||||
mini_portile (~> 0.6.0)
|
psych (2.0.15)
|
||||||
nokogiri (1.6.6.2-x86-mingw32)
|
que (0.10.0)
|
||||||
mini_portile (~> 0.6.0)
|
|
||||||
pg (0.18.1)
|
|
||||||
psych (2.0.13)
|
|
||||||
que (0.9.2)
|
|
||||||
queue_classic (3.1.0)
|
|
||||||
pg (>= 0.17, < 0.19)
|
|
||||||
racc (1.4.12)
|
racc (1.4.12)
|
||||||
rack (1.6.0)
|
|
||||||
rack-cache (1.2)
|
rack-cache (1.2)
|
||||||
rack (>= 0.4)
|
rack (>= 0.4)
|
||||||
rack-protection (1.5.3)
|
|
||||||
rack
|
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails-deprecated_sanitizer (1.0.3)
|
rails-deprecated_sanitizer (1.0.3)
|
||||||
activesupport (>= 4.2.0.alpha)
|
activesupport (>= 4.2.0.alpha)
|
||||||
rails-dom-testing (1.0.6)
|
rails-dom-testing (1.0.7)
|
||||||
activesupport (>= 4.2.0.beta, < 5.0)
|
activesupport (>= 4.2.0.beta, < 5.0)
|
||||||
nokogiri (~> 1.6.0)
|
nokogiri (~> 1.6.0)
|
||||||
rails-deprecated_sanitizer (>= 1.0.1)
|
rails-deprecated_sanitizer (>= 1.0.1)
|
||||||
@ -197,7 +222,7 @@ GEM
|
|||||||
rdoc (4.2.0)
|
rdoc (4.2.0)
|
||||||
redcarpet (3.2.3)
|
redcarpet (3.2.3)
|
||||||
redis (3.2.1)
|
redis (3.2.1)
|
||||||
redis-namespace (1.5.1)
|
redis-namespace (1.5.2)
|
||||||
redis (~> 3.0, >= 3.0.4)
|
redis (~> 3.0, >= 3.0.4)
|
||||||
resque (1.25.2)
|
resque (1.25.2)
|
||||||
mono_logger (~> 1.0)
|
mono_logger (~> 1.0)
|
||||||
@ -210,47 +235,42 @@ GEM
|
|||||||
redis (~> 3.0)
|
redis (~> 3.0)
|
||||||
resque (~> 1.25)
|
resque (~> 1.25)
|
||||||
rufus-scheduler (~> 3.0)
|
rufus-scheduler (~> 3.0)
|
||||||
rufus-scheduler (3.0.9)
|
rufus-scheduler (3.1.3)
|
||||||
tzinfo
|
sass (3.4.17)
|
||||||
sdoc (0.4.1)
|
sdoc (0.4.1)
|
||||||
json (~> 1.7, >= 1.7.7)
|
json (~> 1.7, >= 1.7.7)
|
||||||
rdoc (~> 4.0)
|
rdoc (~> 4.0)
|
||||||
sequel (4.19.0)
|
sequel (4.25.0)
|
||||||
serverengine (1.5.10)
|
serverengine (1.5.10)
|
||||||
sigdump (~> 0.2.2)
|
sigdump (~> 0.2.2)
|
||||||
sidekiq (3.3.2)
|
sidekiq (3.4.2)
|
||||||
celluloid (>= 0.16.0)
|
celluloid (~> 0.16.0)
|
||||||
connection_pool (>= 2.1.1)
|
connection_pool (~> 2.2, >= 2.2.0)
|
||||||
json
|
json (~> 1.0)
|
||||||
redis (>= 3.0.6)
|
redis (~> 3.2, >= 3.2.1)
|
||||||
redis-namespace (>= 1.3.1)
|
redis-namespace (~> 1.5, >= 1.5.2)
|
||||||
sigdump (0.2.2)
|
sigdump (0.2.3)
|
||||||
sinatra (1.4.5)
|
sinatra (1.0)
|
||||||
rack (~> 1.4)
|
rack (>= 1.0)
|
||||||
rack-protection (~> 1.4)
|
sneakers (1.1.1)
|
||||||
tilt (~> 1.3, >= 1.3.4)
|
bunny (>= 1.7.0, <= 2.0.0)
|
||||||
sneakers (1.0.4)
|
|
||||||
bunny (~> 1.7.0)
|
|
||||||
serverengine (~> 1.5.5)
|
serverengine (~> 1.5.5)
|
||||||
thor
|
thor
|
||||||
thread (~> 0.1.7)
|
thread (~> 0.1.7)
|
||||||
sprockets (3.0.2)
|
|
||||||
rack (~> 1.0)
|
|
||||||
sqlite3 (1.3.10)
|
sqlite3 (1.3.10)
|
||||||
stackprof (0.2.7)
|
stackprof (0.2.7)
|
||||||
sucker_punch (1.3.2)
|
sucker_punch (1.5.1)
|
||||||
celluloid (~> 0.16.0)
|
celluloid (= 0.16.0)
|
||||||
thor (0.19.1)
|
thor (0.19.1)
|
||||||
thread (0.1.7)
|
thread (0.1.7)
|
||||||
thread_safe (0.3.5)
|
thread_safe (0.3.5)
|
||||||
tilt (1.4.1)
|
|
||||||
timers (4.0.1)
|
timers (4.0.1)
|
||||||
hitimes
|
hitimes
|
||||||
turbolinks (2.5.3)
|
turbolinks (2.5.3)
|
||||||
coffee-rails
|
coffee-rails
|
||||||
tzinfo (1.2.2)
|
tzinfo (1.2.2)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
uglifier (2.7.0)
|
uglifier (2.7.1)
|
||||||
execjs (>= 0.3.0)
|
execjs (>= 0.3.0)
|
||||||
json (>= 1.8.0)
|
json (>= 1.8.0)
|
||||||
vegas (0.1.11)
|
vegas (0.1.11)
|
||||||
@ -292,19 +312,22 @@ DEPENDENCIES
|
|||||||
qu-rails!
|
qu-rails!
|
||||||
qu-redis
|
qu-redis
|
||||||
que
|
que
|
||||||
queue_classic
|
queue_classic!
|
||||||
racc (>= 1.4.6)
|
racc (>= 1.4.6)
|
||||||
|
rack!
|
||||||
rack-cache (~> 1.2)
|
rack-cache (~> 1.2)
|
||||||
rails!
|
rails!
|
||||||
rake (>= 10.3)
|
rake (>= 10.3)
|
||||||
redcarpet (~> 3.2.3)
|
redcarpet (~> 3.2.3)
|
||||||
resque
|
resque
|
||||||
resque-scheduler
|
resque-scheduler
|
||||||
|
sass (>= 3.3)
|
||||||
|
sass-rails!
|
||||||
sdoc (~> 0.4.0)
|
sdoc (~> 0.4.0)
|
||||||
sequel
|
sequel
|
||||||
sidekiq
|
sidekiq
|
||||||
sneakers
|
sneakers
|
||||||
sprockets (~> 3.0.0.rc.1)
|
sprockets!
|
||||||
sprockets-rails!
|
sprockets-rails!
|
||||||
sqlite3 (~> 1.3.6)
|
sqlite3 (~> 1.3.6)
|
||||||
stackprof
|
stackprof
|
||||||
@ -314,4 +337,4 @@ DEPENDENCIES
|
|||||||
w3c_validators
|
w3c_validators
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.10.5
|
1.10.6
|
||||||
|
@ -76,6 +76,8 @@ and may also be used independently outside Rails.
|
|||||||
We encourage you to contribute to Ruby on Rails! Please check out the
|
We encourage you to contribute to Ruby on Rails! Please check out the
|
||||||
[Contributing to Ruby on Rails guide](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html) for guidelines about how to proceed. [Join us!](http://contributors.rubyonrails.org)
|
[Contributing to Ruby on Rails guide](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html) for guidelines about how to proceed. [Join us!](http://contributors.rubyonrails.org)
|
||||||
|
|
||||||
|
Everyone interacting in Rails and its sub-project’s codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](http://rubyonrails.org/conduct/).
|
||||||
|
|
||||||
## Code Status
|
## Code Status
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/rails/rails.svg?branch=master)](https://travis-ci.org/rails/rails)
|
[![Build Status](https://travis-ci.org/rails/rails.svg?branch=master)](https://travis-ci.org/rails/rails)
|
||||||
|
@ -1,43 +1,47 @@
|
|||||||
= Releasing Rails
|
# Releasing Rails
|
||||||
|
|
||||||
In this document, we'll cover the steps necessary to release Rails. Each
|
In this document, we'll cover the steps necessary to release Rails. Each
|
||||||
section contains steps to take during that time before the release. The times
|
section contains steps to take during that time before the release. The times
|
||||||
suggested in each header are just that: suggestions. However, they should
|
suggested in each header are just that: suggestions. However, they should
|
||||||
really be considered as minimums.
|
really be considered as minimums.
|
||||||
|
|
||||||
== 10 Days before release
|
## 10 Days before release
|
||||||
|
|
||||||
Today is mostly coordination tasks. Here are the things you must do today:
|
Today is mostly coordination tasks. Here are the things you must do today:
|
||||||
|
|
||||||
=== Is the CI green? If not, make it green. (See "Fixing the CI")
|
### Is the CI green? If not, make it green. (See "Fixing the CI")
|
||||||
|
|
||||||
Do not release with a Red CI. You can find the CI status here:
|
Do not release with a Red CI. You can find the CI status here:
|
||||||
|
|
||||||
http://travis-ci.org/rails/rails
|
```
|
||||||
|
http://travis-ci.org/rails/rails
|
||||||
|
```
|
||||||
|
|
||||||
=== Is Sam Ruby happy? If not, make him happy.
|
### Is Sam Ruby happy? If not, make him happy.
|
||||||
|
|
||||||
Sam Ruby keeps a test suite that makes sure the code samples in his book (Agile
|
Sam Ruby keeps a test suite that makes sure the code samples in his book (Agile
|
||||||
Web Development with Rails) all work. These are valuable integration tests
|
Web Development with Rails) all work. These are valuable integration tests
|
||||||
for Rails. You can check the status of his tests here:
|
for Rails. You can check the status of his tests here:
|
||||||
|
|
||||||
http://intertwingly.net/projects/dashboard.html
|
```
|
||||||
|
http://intertwingly.net/projects/dashboard.html
|
||||||
|
```
|
||||||
|
|
||||||
Do not release with Red AWDwR tests.
|
Do not release with Red AWDwR tests.
|
||||||
|
|
||||||
=== Do we have any Git dependencies? If so, contact those authors.
|
### Do we have any Git dependencies? If so, contact those authors.
|
||||||
|
|
||||||
Having Git dependencies indicates that we depend on unreleased code.
|
Having Git dependencies indicates that we depend on unreleased code.
|
||||||
Obviously Rails cannot be released when it depends on unreleased code.
|
Obviously Rails cannot be released when it depends on unreleased code.
|
||||||
Contact the authors of those particular gems and work out a release date that
|
Contact the authors of those particular gems and work out a release date that
|
||||||
suits them.
|
suits them.
|
||||||
|
|
||||||
=== Contact the security team (either Koz or tenderlove)
|
### Contact the security team (either Koz or tenderlove)
|
||||||
|
|
||||||
Let them know of your plans to release. There may be security issues to be
|
Let them know of your plans to release. There may be security issues to be
|
||||||
addressed, and that can impact your release date.
|
addressed, and that can impact your release date.
|
||||||
|
|
||||||
=== Notify implementors.
|
### Notify implementors.
|
||||||
|
|
||||||
Ruby implementors have high stakes in making sure Rails works. Be kind and
|
Ruby implementors have high stakes in making sure Rails works. Be kind and
|
||||||
give them a heads up that Rails will be released soonish.
|
give them a heads up that Rails will be released soonish.
|
||||||
@ -54,27 +58,29 @@ lists:
|
|||||||
|
|
||||||
Implementors will love you and help you.
|
Implementors will love you and help you.
|
||||||
|
|
||||||
== 3 Days before release
|
### 3 Days before release
|
||||||
|
|
||||||
This is when you should release the release candidate. Here are your tasks
|
This is when you should release the release candidate. Here are your tasks
|
||||||
for today:
|
for today:
|
||||||
|
|
||||||
=== Is the CI green? If not, make it green.
|
### Is the CI green? If not, make it green.
|
||||||
|
|
||||||
=== Is Sam Ruby happy? If not, make him happy.
|
### Is Sam Ruby happy? If not, make him happy.
|
||||||
|
|
||||||
=== Contact the security team. CVE emails must be sent on this day.
|
### Contact the security team. CVE emails must be sent on this day.
|
||||||
|
|
||||||
=== Create a release branch.
|
### Create a release branch.
|
||||||
|
|
||||||
From the stable branch, create a release branch. For example, if you're
|
From the stable branch, create a release branch. For example, if you're
|
||||||
releasing Rails 3.0.10, do this:
|
releasing Rails 3.0.10, do this:
|
||||||
|
|
||||||
[aaron@higgins rails (3-0-stable)]$ git checkout -b 3-0-10
|
```
|
||||||
Switched to a new branch '3-0-10'
|
[aaron@higgins rails (3-0-stable)]$ git checkout -b 3-0-10
|
||||||
[aaron@higgins rails (3-0-10)]$
|
Switched to a new branch '3-0-10'
|
||||||
|
[aaron@higgins rails (3-0-10)]$
|
||||||
|
```
|
||||||
|
|
||||||
=== Update each CHANGELOG.
|
### Update each CHANGELOG.
|
||||||
|
|
||||||
Many times commits are made without the CHANGELOG being updated. You should
|
Many times commits are made without the CHANGELOG being updated. You should
|
||||||
review the commits since the last release, and fill in any missing information
|
review the commits since the last release, and fill in any missing information
|
||||||
@ -82,15 +88,17 @@ for each CHANGELOG.
|
|||||||
|
|
||||||
You can review the commits for the 3.0.10 release like this:
|
You can review the commits for the 3.0.10 release like this:
|
||||||
|
|
||||||
[aaron@higgins rails (3-0-10)]$ git log v3.0.9..
|
```
|
||||||
|
[aaron@higgins rails (3-0-10)]$ git log v3.0.9..
|
||||||
|
```
|
||||||
|
|
||||||
If you're doing a stable branch release, you should also ensure that all of
|
If you're doing a stable branch release, you should also ensure that all of
|
||||||
the CHANGELOG entries in the stable branch are also synced to the master
|
the CHANGELOG entries in the stable branch are also synced to the master
|
||||||
branch.
|
branch.
|
||||||
|
|
||||||
=== Update the RAILS_VERSION file to include the RC.
|
### Update the RAILS_VERSION file to include the RC.
|
||||||
|
|
||||||
=== Build and test the gem.
|
### Build and test the gem.
|
||||||
|
|
||||||
Run `rake install` to generate the gems and install them locally. Then try
|
Run `rake install` to generate the gems and install them locally. Then try
|
||||||
generating a new app and ensure that nothing explodes.
|
generating a new app and ensure that nothing explodes.
|
||||||
@ -98,7 +106,7 @@ generating a new app and ensure that nothing explodes.
|
|||||||
This will stop you from looking silly when you push an RC to rubygems.org and
|
This will stop you from looking silly when you push an RC to rubygems.org and
|
||||||
then realize it is broken.
|
then realize it is broken.
|
||||||
|
|
||||||
=== Release the gem.
|
### Release the gem.
|
||||||
|
|
||||||
IMPORTANT: Due to YAML parse problems on the rubygems.org server, it is safest
|
IMPORTANT: Due to YAML parse problems on the rubygems.org server, it is safest
|
||||||
to use Ruby 1.8 when releasing.
|
to use Ruby 1.8 when releasing.
|
||||||
@ -108,14 +116,16 @@ RAILS_VERSION, commit the changes, tag it, and push the gems to rubygems.org.
|
|||||||
Here are the commands that `rake release` should use, so you can understand
|
Here are the commands that `rake release` should use, so you can understand
|
||||||
what to do in case anything goes wrong:
|
what to do in case anything goes wrong:
|
||||||
|
|
||||||
$ rake all:build
|
```
|
||||||
$ git commit -am'updating RAILS_VERSION'
|
$ rake all:build
|
||||||
$ git tag -m 'v3.0.10.rc1 release' v3.0.10.rc1
|
$ git commit -am'updating RAILS_VERSION'
|
||||||
$ git push
|
$ git tag -m 'v3.0.10.rc1 release' v3.0.10.rc1
|
||||||
$ git push --tags
|
$ git push
|
||||||
$ for i in $(ls pkg); do gem push $i; done
|
$ git push --tags
|
||||||
|
$ for i in $(ls pkg); do gem push $i; done
|
||||||
|
```
|
||||||
|
|
||||||
=== Send Rails release announcements
|
### Send Rails release announcements
|
||||||
|
|
||||||
Write a release announcement that includes the version, changes, and links to
|
Write a release announcement that includes the version, changes, and links to
|
||||||
GitHub where people can find the specific commit list. Here are the mailing
|
GitHub where people can find the specific commit list. Here are the mailing
|
||||||
@ -132,16 +142,16 @@ IMPORTANT: If any users experience regressions when using the release
|
|||||||
candidate, you *must* postpone the release. Bugfix releases *should not*
|
candidate, you *must* postpone the release. Bugfix releases *should not*
|
||||||
break existing applications.
|
break existing applications.
|
||||||
|
|
||||||
=== Post the announcement to the Rails blog.
|
### Post the announcement to the Rails blog.
|
||||||
|
|
||||||
If you used Markdown format for your email, you can just paste it in to the
|
If you used Markdown format for your email, you can just paste it in to the
|
||||||
blog.
|
blog.
|
||||||
|
|
||||||
* http://weblog.rubyonrails.org
|
* http://weblog.rubyonrails.org
|
||||||
|
|
||||||
=== Post the announcement to the Rails Twitter account.
|
### Post the announcement to the Rails Twitter account.
|
||||||
|
|
||||||
== Time between release candidate and actual release
|
## Time between release candidate and actual release
|
||||||
|
|
||||||
Check the rails-core mailing list and the GitHub issue list for regressions in
|
Check the rails-core mailing list and the GitHub issue list for regressions in
|
||||||
the RC.
|
the RC.
|
||||||
@ -155,7 +165,7 @@ When you fix the regressions, do not create a new branch. Fix them on the
|
|||||||
stable branch, then cherry pick the commit to your release branch. No other
|
stable branch, then cherry pick the commit to your release branch. No other
|
||||||
commits should be added to the release branch besides regression fixing commits.
|
commits should be added to the release branch besides regression fixing commits.
|
||||||
|
|
||||||
== Day of release
|
## Day of release
|
||||||
|
|
||||||
Many of these steps are the same as for the release candidate, so if you need
|
Many of these steps are the same as for the release candidate, so if you need
|
||||||
more explanation on a particular step, see the RC steps.
|
more explanation on a particular step, see the RC steps.
|
||||||
@ -173,7 +183,7 @@ Today, do this stuff in this order:
|
|||||||
* Email security lists
|
* Email security lists
|
||||||
* Email general announcement lists
|
* Email general announcement lists
|
||||||
|
|
||||||
=== Emailing the Rails security announce list
|
### Emailing the Rails security announce list
|
||||||
|
|
||||||
Email the security announce list once for each vulnerability fixed.
|
Email the security announce list once for each vulnerability fixed.
|
||||||
|
|
||||||
@ -193,13 +203,13 @@ so we need to give them the security fixes in patch form.
|
|||||||
* Merge the release branch to the stable branch.
|
* Merge the release branch to the stable branch.
|
||||||
* Drink beer (or other cocktail)
|
* Drink beer (or other cocktail)
|
||||||
|
|
||||||
== Misc
|
## Misc
|
||||||
|
|
||||||
=== Fixing the CI
|
### Fixing the CI
|
||||||
|
|
||||||
There are two simple steps for fixing the CI:
|
There are two simple steps for fixing the CI:
|
||||||
|
|
||||||
1. Identify the problem
|
1. Identify the problem
|
||||||
2. Fix it
|
2. Fix it
|
||||||
|
|
||||||
Repeat these steps until the CI is green.
|
Repeat these steps until the CI is green.
|
@ -820,7 +820,7 @@ def mail(headers = {}, &block)
|
|||||||
# Set configure delivery behavior
|
# Set configure delivery behavior
|
||||||
wrap_delivery_behavior!(headers.delete(:delivery_method), headers.delete(:delivery_method_options))
|
wrap_delivery_behavior!(headers.delete(:delivery_method), headers.delete(:delivery_method_options))
|
||||||
|
|
||||||
# Assign all headers except parts_order, content_type and body
|
# Assign all headers except parts_order, content_type, body, template_name, and template_path
|
||||||
assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
|
assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
|
||||||
assignable.each { |k, v| m[k] = v }
|
assignable.each { |k, v| m[k] = v }
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
end
|
end
|
||||||
|
|
||||||
require 'active_support/testing/autorun'
|
require 'active_support/testing/autorun'
|
||||||
|
require 'active_support/testing/method_call_assertions'
|
||||||
require 'action_mailer'
|
require 'action_mailer'
|
||||||
require 'action_mailer/test_case'
|
require 'action_mailer/test_case'
|
||||||
|
|
||||||
@ -40,4 +41,6 @@ def jruby_skip(message = '')
|
|||||||
skip message if defined?(JRUBY_VERSION)
|
skip message if defined?(JRUBY_VERSION)
|
||||||
end
|
end
|
||||||
|
|
||||||
require 'mocha/setup' # FIXME: stop using mocha
|
class ActiveSupport::TestCase
|
||||||
|
include ActiveSupport::Testing::MethodCallAssertions
|
||||||
|
end
|
||||||
|
@ -505,9 +505,10 @@ def welcome
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "calling deliver on the action should deliver the mail object" do
|
test "calling deliver on the action should deliver the mail object" do
|
||||||
BaseMailer.expects(:deliver_mail).once
|
assert_called(BaseMailer, :deliver_mail) do
|
||||||
mail = BaseMailer.welcome.deliver_now
|
mail = BaseMailer.welcome.deliver_now
|
||||||
assert_equal 'The first email on new API!', mail.subject
|
assert_equal 'The first email on new API!', mail.subject
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calling deliver on the action should increment the deliveries collection if using the test mailer" do
|
test "calling deliver on the action should increment the deliveries collection if using the test mailer" do
|
||||||
@ -517,9 +518,11 @@ def welcome
|
|||||||
|
|
||||||
test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
|
test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
|
||||||
mail = Mail::Message.new
|
mail = Mail::Message.new
|
||||||
mail.expects(:do_delivery).once
|
assert_called(mail, :do_delivery) do
|
||||||
BaseMailer.expects(:welcome).returns(mail)
|
assert_called(BaseMailer, :welcome, returns: mail) do
|
||||||
BaseMailer.welcome.deliver
|
BaseMailer.welcome.deliver
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rendering
|
# Rendering
|
||||||
@ -607,8 +610,9 @@ def self.delivered_email(mail)
|
|||||||
mail_side_effects do
|
mail_side_effects do
|
||||||
ActionMailer::Base.register_observer(MyObserver)
|
ActionMailer::Base.register_observer(MyObserver)
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
MyObserver.expects(:delivered_email).with(mail)
|
assert_called_with(MyObserver, :delivered_email, [mail]) do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -616,8 +620,9 @@ def self.delivered_email(mail)
|
|||||||
mail_side_effects do
|
mail_side_effects do
|
||||||
ActionMailer::Base.register_observer("BaseTest::MyObserver")
|
ActionMailer::Base.register_observer("BaseTest::MyObserver")
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
MyObserver.expects(:delivered_email).with(mail)
|
assert_called_with(MyObserver, :delivered_email, [mail]) do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -625,8 +630,9 @@ def self.delivered_email(mail)
|
|||||||
mail_side_effects do
|
mail_side_effects do
|
||||||
ActionMailer::Base.register_observer(:"base_test/my_observer")
|
ActionMailer::Base.register_observer(:"base_test/my_observer")
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
MyObserver.expects(:delivered_email).with(mail)
|
assert_called_with(MyObserver, :delivered_email, [mail]) do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -634,9 +640,11 @@ def self.delivered_email(mail)
|
|||||||
mail_side_effects do
|
mail_side_effects do
|
||||||
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
|
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
MyObserver.expects(:delivered_email).with(mail)
|
assert_called_with(MyObserver, :delivered_email, [mail]) do
|
||||||
MySecondObserver.expects(:delivered_email).with(mail)
|
assert_called_with(MySecondObserver, :delivered_email, [mail]) do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -654,8 +662,9 @@ def self.previewing_email(mail); end
|
|||||||
mail_side_effects do
|
mail_side_effects do
|
||||||
ActionMailer::Base.register_interceptor(MyInterceptor)
|
ActionMailer::Base.register_interceptor(MyInterceptor)
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
MyInterceptor.expects(:delivering_email).with(mail)
|
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -663,8 +672,9 @@ def self.previewing_email(mail); end
|
|||||||
mail_side_effects do
|
mail_side_effects do
|
||||||
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
|
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
MyInterceptor.expects(:delivering_email).with(mail)
|
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -672,8 +682,9 @@ def self.previewing_email(mail); end
|
|||||||
mail_side_effects do
|
mail_side_effects do
|
||||||
ActionMailer::Base.register_interceptor(:"base_test/my_interceptor")
|
ActionMailer::Base.register_interceptor(:"base_test/my_interceptor")
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
MyInterceptor.expects(:delivering_email).with(mail)
|
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -681,18 +692,21 @@ def self.previewing_email(mail); end
|
|||||||
mail_side_effects do
|
mail_side_effects do
|
||||||
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
|
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
MyInterceptor.expects(:delivering_email).with(mail)
|
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
|
||||||
MySecondInterceptor.expects(:delivering_email).with(mail)
|
assert_called_with(MySecondInterceptor, :delivering_email, [mail]) do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
|
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
|
||||||
mail1 = ProcMailer.welcome['X-Proc-Method']
|
mail1 = ProcMailer.welcome['X-Proc-Method']
|
||||||
yesterday = 1.day.ago
|
yesterday = 1.day.ago
|
||||||
Time.stubs(:now).returns(yesterday)
|
Time.stub(:now, yesterday) do
|
||||||
mail2 = ProcMailer.welcome['X-Proc-Method']
|
mail2 = ProcMailer.welcome['X-Proc-Method']
|
||||||
assert(mail1.to_s.to_i > mail2.to_s.to_i)
|
assert(mail1.to_s.to_i > mail2.to_s.to_i)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'default values which have to_proc (e.g. symbols) should not be considered procs' do
|
test 'default values which have to_proc (e.g. symbols) should not be considered procs' do
|
||||||
@ -877,33 +891,50 @@ def self.previewing_email(mail); end
|
|||||||
test "you can register a preview interceptor to the mail object that gets passed the mail object before previewing" do
|
test "you can register a preview interceptor to the mail object that gets passed the mail object before previewing" do
|
||||||
ActionMailer::Base.register_preview_interceptor(MyInterceptor)
|
ActionMailer::Base.register_preview_interceptor(MyInterceptor)
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
|
stub_any_instance(BaseMailerPreview) do |instance|
|
||||||
MyInterceptor.expects(:previewing_email).with(mail)
|
instance.stub(:welcome, mail) do
|
||||||
BaseMailerPreview.call(:welcome)
|
assert_called_with(MyInterceptor, :previewing_email, [mail]) do
|
||||||
|
BaseMailerPreview.call(:welcome)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "you can register a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
|
test "you can register a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
|
||||||
ActionMailer::Base.register_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
|
ActionMailer::Base.register_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
|
stub_any_instance(BaseMailerPreview) do |instance|
|
||||||
MyInterceptor.expects(:previewing_email).with(mail)
|
instance.stub(:welcome, mail) do
|
||||||
BaseMailerPreview.call(:welcome)
|
assert_called_with(MyInterceptor, :previewing_email, [mail]) do
|
||||||
|
BaseMailerPreview.call(:welcome)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
|
test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
|
||||||
ActionMailer::Base.register_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
|
ActionMailer::Base.register_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
|
stub_any_instance(BaseMailerPreview) do |instance|
|
||||||
MyInterceptor.expects(:previewing_email).with(mail)
|
instance.stub(:welcome, mail) do
|
||||||
BaseMailerPreview.call(:welcome)
|
assert_called_with(MyInterceptor, :previewing_email, [mail]) do
|
||||||
|
BaseMailerPreview.call(:welcome)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "you can register multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
|
test "you can register multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
|
||||||
ActionMailer::Base.register_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
|
ActionMailer::Base.register_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
|
||||||
mail = BaseMailer.welcome
|
mail = BaseMailer.welcome
|
||||||
BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
|
stub_any_instance(BaseMailerPreview) do |instance|
|
||||||
MyInterceptor.expects(:previewing_email).with(mail)
|
instance.stub(:welcome, mail) do
|
||||||
MySecondInterceptor.expects(:previewing_email).with(mail)
|
assert_called_with(MyInterceptor, :previewing_email, [mail]) do
|
||||||
BaseMailerPreview.call(:welcome)
|
assert_called_with(MySecondInterceptor, :previewing_email, [mail]) do
|
||||||
|
BaseMailerPreview.call(:welcome)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -102,16 +102,21 @@ def welcome(hash={})
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "ActionMailer should be told when Mail gets delivered" do
|
test "ActionMailer should be told when Mail gets delivered" do
|
||||||
DeliveryMailer.expects(:deliver_mail).once
|
DeliveryMailer.delivery_method = :test
|
||||||
DeliveryMailer.welcome.deliver_now
|
assert_called(DeliveryMailer, :deliver_mail) do
|
||||||
|
DeliveryMailer.welcome.deliver_now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delivery method can be customized per instance" do
|
test "delivery method can be customized per instance" do
|
||||||
Mail::SMTP.any_instance.expects(:deliver!)
|
stub_any_instance(Mail::SMTP, instance: Mail::SMTP.new({})) do |instance|
|
||||||
email = DeliveryMailer.welcome.deliver_now
|
assert_called(instance, :deliver!) do
|
||||||
assert_instance_of Mail::SMTP, email.delivery_method
|
email = DeliveryMailer.welcome.deliver_now
|
||||||
email = DeliveryMailer.welcome(delivery_method: :test).deliver_now
|
assert_instance_of Mail::SMTP, email.delivery_method
|
||||||
assert_instance_of Mail::TestMailer, email.delivery_method
|
email = DeliveryMailer.welcome(delivery_method: :test).deliver_now
|
||||||
|
assert_instance_of Mail::TestMailer, email.delivery_method
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delivery method can be customized in subclasses not changing the parent" do
|
test "delivery method can be customized in subclasses not changing the parent" do
|
||||||
@ -176,8 +181,11 @@ def welcome(hash={})
|
|||||||
old_perform_deliveries = DeliveryMailer.perform_deliveries
|
old_perform_deliveries = DeliveryMailer.perform_deliveries
|
||||||
begin
|
begin
|
||||||
DeliveryMailer.perform_deliveries = false
|
DeliveryMailer.perform_deliveries = false
|
||||||
Mail::Message.any_instance.expects(:deliver!).never
|
stub_any_instance(Mail::Message) do |instance|
|
||||||
DeliveryMailer.welcome.deliver_now
|
assert_not_called(instance, :deliver!) do
|
||||||
|
DeliveryMailer.welcome.deliver_now
|
||||||
|
end
|
||||||
|
end
|
||||||
ensure
|
ensure
|
||||||
DeliveryMailer.perform_deliveries = old_perform_deliveries
|
DeliveryMailer.perform_deliveries = old_perform_deliveries
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
require 'abstract_unit'
|
require 'abstract_unit'
|
||||||
require 'action_view'
|
require 'action_view'
|
||||||
require 'action_controller'
|
require 'action_controller'
|
||||||
|
require 'active_support/deprecation'
|
||||||
|
|
||||||
class I18nTestMailer < ActionMailer::Base
|
class I18nTestMailer < ActionMailer::Base
|
||||||
configure do |c|
|
configure do |c|
|
||||||
@ -52,10 +53,15 @@ def app
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_send_mail
|
def test_send_mail
|
||||||
Mail::SMTP.any_instance.expects(:deliver!)
|
stub_any_instance(Mail::SMTP, instance: Mail::SMTP.new({})) do |instance|
|
||||||
with_translation 'de', email_subject: '[Anmeldung] Willkommen' do
|
assert_called(instance, :deliver!) do
|
||||||
get '/test/send_mail'
|
with_translation 'de', email_subject: '[Anmeldung] Willkommen' do
|
||||||
assert_equal "Mail sent - Subject: [Anmeldung] Willkommen", @response.body
|
ActiveSupport::Deprecation.silence do
|
||||||
|
get '/test/send_mail'
|
||||||
|
end
|
||||||
|
assert_equal "Mail sent - Subject: [Anmeldung] Willkommen", @response.body
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,3 +1,45 @@
|
|||||||
|
* Using strings or symbols for middleware class names is deprecated. Convert
|
||||||
|
things like this:
|
||||||
|
|
||||||
|
middleware.use "Foo::Bar"
|
||||||
|
|
||||||
|
to this:
|
||||||
|
|
||||||
|
middleware.use Foo::Bar
|
||||||
|
|
||||||
|
* ActionController::TestSession now accepts a default value as well as
|
||||||
|
a block for generating a default value based off the key provided.
|
||||||
|
|
||||||
|
This fixes calls to session#fetch in ApplicationController instances that
|
||||||
|
take more two arguments or a block from raising `ArgumentError: wrong
|
||||||
|
number of arguments (2 for 1)` when performing controller tests.
|
||||||
|
|
||||||
|
*Matthew Gerrior*
|
||||||
|
|
||||||
|
* Fix `ActionController::Parameters#fetch` overwriting `KeyError` returned by
|
||||||
|
default block.
|
||||||
|
|
||||||
|
*Jonas Schuber Erlandsson*, *Roque Pinel*
|
||||||
|
|
||||||
|
* `ActionController::Parameters` no longer inherits from
|
||||||
|
`HashWithIndifferentAccess`
|
||||||
|
|
||||||
|
Inheriting from `HashWithIndifferentAccess` allowed users to call any
|
||||||
|
enumerable methods on `Parameters` object, resulting in a risk of losing the
|
||||||
|
`permitted?` status or even getting back a pure `Hash` object instead of
|
||||||
|
a `Parameters` object with proper sanitization.
|
||||||
|
|
||||||
|
By not inheriting from `HashWithIndifferentAccess`, we are able to make
|
||||||
|
sure that all methods that are defined in `Parameters` object will return
|
||||||
|
a proper `Parameters` object with a correct `permitted?` flag.
|
||||||
|
|
||||||
|
*Prem Sichanugrist*
|
||||||
|
|
||||||
|
* Replaced `ActiveSupport::Concurrency::Latch` with `Concurrent::CountDownLatch`
|
||||||
|
from the concurrent-ruby gem.
|
||||||
|
|
||||||
|
*Jerry D'Antonio*
|
||||||
|
|
||||||
* Add ability to filter parameters based on parent keys.
|
* Add ability to filter parameters based on parent keys.
|
||||||
|
|
||||||
# matches {credit_card: {code: "xxxx"}}
|
# matches {credit_card: {code: "xxxx"}}
|
||||||
@ -164,7 +206,8 @@
|
|||||||
*arthurnn*
|
*arthurnn*
|
||||||
|
|
||||||
* `ActionController#translate` supports symbols as shortcuts.
|
* `ActionController#translate` supports symbols as shortcuts.
|
||||||
When shortcut is given it also lookups without action name.
|
When a shortcut is given it also performs the lookup without the action
|
||||||
|
name.
|
||||||
|
|
||||||
*Max Melentiev*
|
*Max Melentiev*
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
s.add_dependency 'activesupport', version
|
s.add_dependency 'activesupport', version
|
||||||
|
|
||||||
s.add_dependency 'rack', '~> 1.6'
|
s.add_dependency 'rack', '~> 2.x'
|
||||||
s.add_dependency 'rack-test', '~> 0.6.3'
|
s.add_dependency 'rack-test', '~> 0.6.3'
|
||||||
s.add_dependency 'rails-html-sanitizer', '~> 1.0', '>= 1.0.2'
|
s.add_dependency 'rails-html-sanitizer', '~> 1.0', '>= 1.0.2'
|
||||||
s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
|
s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
|
||||||
|
@ -96,7 +96,7 @@ def clear_action_methods!
|
|||||||
# ==== Returns
|
# ==== Returns
|
||||||
# * <tt>String</tt>
|
# * <tt>String</tt>
|
||||||
def controller_path
|
def controller_path
|
||||||
@controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
|
@controller_path ||= name.sub(/Controller$/, ''.freeze).underscore unless anonymous?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Refresh the cached action_methods when a new action_method is added.
|
# Refresh the cached action_methods when a new action_method is added.
|
||||||
|
@ -181,7 +181,7 @@ def add_template_helper(mod)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def default_helper_module!
|
def default_helper_module!
|
||||||
module_name = name.sub(/Controller$/, '')
|
module_name = name.sub(/Controller$/, ''.freeze)
|
||||||
module_path = module_name.underscore
|
module_path = module_name.underscore
|
||||||
helper module_path
|
helper module_path
|
||||||
rescue LoadError => e
|
rescue LoadError => e
|
||||||
|
@ -54,11 +54,11 @@ def rendered_format
|
|||||||
Mime::TEXT
|
Mime::TEXT
|
||||||
end
|
end
|
||||||
|
|
||||||
DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %w(
|
DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %i(
|
||||||
@_action_name @_response_body @_formats @_prefixes @_config
|
@_action_name @_response_body @_formats @_prefixes @_config
|
||||||
@_view_context_class @_view_renderer @_lookup_context
|
@_view_context_class @_view_renderer @_lookup_context
|
||||||
@_routes @_db_runtime
|
@_routes @_db_runtime
|
||||||
).map(&:to_sym)
|
)
|
||||||
|
|
||||||
# This method should return a hash with assigns.
|
# This method should return a hash with assigns.
|
||||||
# You can overwrite this configuration per controller.
|
# You can overwrite this configuration per controller.
|
||||||
|
@ -90,7 +90,7 @@ class API < Metal
|
|||||||
# Shortcut helper that returns all the ActionController::API modules except
|
# Shortcut helper that returns all the ActionController::API modules except
|
||||||
# the ones passed as arguments:
|
# the ones passed as arguments:
|
||||||
#
|
#
|
||||||
# class MetalController
|
# class MyAPIBaseController < ActionController::Metal
|
||||||
# ActionController::API.without_modules(:ForceSSL, :UrlFor).each do |left|
|
# ActionController::API.without_modules(:ForceSSL, :UrlFor).each do |left|
|
||||||
# include left
|
# include left
|
||||||
# end
|
# end
|
||||||
|
@ -183,7 +183,7 @@ class Base < Metal
|
|||||||
# Shortcut helper that returns all the modules included in
|
# Shortcut helper that returns all the modules included in
|
||||||
# ActionController::Base except the ones passed as arguments:
|
# ActionController::Base except the ones passed as arguments:
|
||||||
#
|
#
|
||||||
# class MetalController
|
# class MyBaseController < ActionController::Metal
|
||||||
# ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
|
# ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
|
||||||
# include left
|
# include left
|
||||||
# end
|
# end
|
||||||
@ -252,7 +252,7 @@ def self.without_modules(*modules)
|
|||||||
|
|
||||||
# Define some internal variables that should not be propagated to the view.
|
# Define some internal variables that should not be propagated to the view.
|
||||||
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
|
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
|
||||||
:@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
|
:@_status, :@_headers, :@_params, :@_response, :@_request,
|
||||||
:@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
|
:@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
|
||||||
|
|
||||||
def _protected_ivars # :nodoc:
|
def _protected_ivars # :nodoc:
|
||||||
|
@ -8,7 +8,7 @@ module ActionController
|
|||||||
#
|
#
|
||||||
# You can read more about each approach by clicking the modules below.
|
# You can read more about each approach by clicking the modules below.
|
||||||
#
|
#
|
||||||
# Note: To turn off all caching, set
|
# Note: To turn off all caching provided by Action Controller, set
|
||||||
# config.action_controller.perform_caching = false
|
# config.action_controller.perform_caching = false
|
||||||
#
|
#
|
||||||
# == \Caching stores
|
# == \Caching stores
|
||||||
|
@ -25,7 +25,7 @@ def process_action(event)
|
|||||||
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
||||||
end
|
end
|
||||||
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
||||||
message << " (#{additions.join(" | ")})" unless additions.blank?
|
message << " (#{additions.join(" | ".freeze)})" unless additions.blank?
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
require 'active_support/core_ext/array/extract_options'
|
require 'active_support/core_ext/array/extract_options'
|
||||||
require 'action_dispatch/middleware/stack'
|
require 'action_dispatch/middleware/stack'
|
||||||
|
require 'active_support/deprecation'
|
||||||
|
|
||||||
module ActionController
|
module ActionController
|
||||||
# Extend ActionDispatch middleware stack to make it aware of options
|
# Extend ActionDispatch middleware stack to make it aware of options
|
||||||
@ -11,22 +12,14 @@ module ActionController
|
|||||||
#
|
#
|
||||||
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
|
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
|
||||||
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
|
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
|
||||||
def initialize(klass, *args, &block)
|
def initialize(klass, args, actions, strategy, block)
|
||||||
options = args.extract_options!
|
@actions = actions
|
||||||
@only = Array(options.delete(:only)).map(&:to_s)
|
@strategy = strategy
|
||||||
@except = Array(options.delete(:except)).map(&:to_s)
|
super(klass, args, block)
|
||||||
args << options unless options.empty?
|
|
||||||
super
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid?(action)
|
def valid?(action)
|
||||||
if @only.present?
|
@strategy.call @actions, action
|
||||||
@only.include?(action)
|
|
||||||
elsif @except.present?
|
|
||||||
!@except.include?(action)
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -37,6 +30,32 @@ def build(action, app = Proc.new)
|
|||||||
middleware.valid?(action) ? middleware.build(a) : a
|
middleware.valid?(action) ? middleware.build(a) : a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
INCLUDE = ->(list, action) { list.include? action }
|
||||||
|
EXCLUDE = ->(list, action) { !list.include? action }
|
||||||
|
NULL = ->(list, action) { true }
|
||||||
|
|
||||||
|
def build_middleware(klass, args, block)
|
||||||
|
options = args.extract_options!
|
||||||
|
only = Array(options.delete(:only)).map(&:to_s)
|
||||||
|
except = Array(options.delete(:except)).map(&:to_s)
|
||||||
|
args << options unless options.empty?
|
||||||
|
|
||||||
|
strategy = NULL
|
||||||
|
list = nil
|
||||||
|
|
||||||
|
if only.any?
|
||||||
|
strategy = INCLUDE
|
||||||
|
list = only
|
||||||
|
elsif except.any?
|
||||||
|
strategy = EXCLUDE
|
||||||
|
list = except
|
||||||
|
end
|
||||||
|
|
||||||
|
Middleware.new(get_class(klass), args, list, strategy, block)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
|
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
|
||||||
@ -98,11 +117,10 @@ def build(action, app = Proc.new)
|
|||||||
class Metal < AbstractController::Base
|
class Metal < AbstractController::Base
|
||||||
abstract!
|
abstract!
|
||||||
|
|
||||||
attr_internal_writer :env
|
|
||||||
|
|
||||||
def env
|
def env
|
||||||
@_env ||= {}
|
@_request.env
|
||||||
end
|
end
|
||||||
|
deprecate :env
|
||||||
|
|
||||||
# Returns the last part of the controller's name, underscored, without the ending
|
# Returns the last part of the controller's name, underscored, without the ending
|
||||||
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
|
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
|
||||||
@ -197,8 +215,7 @@ def dispatch(name, request) #:nodoc:
|
|||||||
|
|
||||||
def set_request!(request) #:nodoc:
|
def set_request!(request) #:nodoc:
|
||||||
@_request = request
|
@_request = request
|
||||||
@_env = request.env
|
@_request.controller_instance = self
|
||||||
@_env['action_controller.instance'] = self
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_a #:nodoc:
|
def to_a #:nodoc:
|
||||||
@ -232,13 +249,13 @@ def self.call(env)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Returns a Rack endpoint for the given action name.
|
# Returns a Rack endpoint for the given action name.
|
||||||
def self.action(name, klass = ActionDispatch::Request)
|
def self.action(name)
|
||||||
if middleware_stack.any?
|
if middleware_stack.any?
|
||||||
middleware_stack.build(name) do |env|
|
middleware_stack.build(name) do |env|
|
||||||
new.dispatch(name, klass.new(env))
|
new.dispatch(name, ActionDispatch::Request.new(env))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
lambda { |env| new.dispatch(name, klass.new(env)) }
|
lambda { |env| new.dispatch(name, ActionDispatch::Request.new(env)) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -85,6 +85,10 @@ def initialize(path)
|
|||||||
@to_path = path
|
@to_path = path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
File.binread(to_path)
|
||||||
|
end
|
||||||
|
|
||||||
# Stream the file's contents if Rack::Sendfile isn't present.
|
# Stream the file's contents if Rack::Sendfile isn't present.
|
||||||
def each
|
def each
|
||||||
File.open(to_path, 'rb') do |file|
|
File.open(to_path, 'rb') do |file|
|
||||||
@ -126,7 +130,7 @@ def each
|
|||||||
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
||||||
def send_data(data, options = {}) #:doc:
|
def send_data(data, options = {}) #:doc:
|
||||||
send_file_headers! options
|
send_file_headers! options
|
||||||
render options.slice(:status, :content_type).merge(:text => data)
|
render options.slice(:status, :content_type).merge(body: data)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -55,10 +55,10 @@ module ClassMethods
|
|||||||
# You can pass any of the following options to affect the before_action callback
|
# You can pass any of the following options to affect the before_action callback
|
||||||
# * <tt>only</tt> - The callback should be run only for this action
|
# * <tt>only</tt> - The callback should be run only for this action
|
||||||
# * <tt>except</tt> - The callback should be run for all actions except this action
|
# * <tt>except</tt> - The callback should be run for all actions except this action
|
||||||
# * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
|
# * <tt>if</tt> - A symbol naming an instance method or a proc; the
|
||||||
# will be called only when it returns a true value.
|
# callback will be called only when it returns a true value.
|
||||||
# * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
|
# * <tt>unless</tt> - A symbol naming an instance method or a proc; the
|
||||||
# will be called only when it returns a false value.
|
# callback will be called only when it returns a false value.
|
||||||
def force_ssl(options = {})
|
def force_ssl(options = {})
|
||||||
action_options = options.slice(*ACTION_OPTIONS)
|
action_options = options.slice(*ACTION_OPTIONS)
|
||||||
redirect_options = options.except(*ACTION_OPTIONS)
|
redirect_options = options.except(*ACTION_OPTIONS)
|
||||||
@ -71,8 +71,8 @@ def force_ssl(options = {})
|
|||||||
# Redirect the existing request to use the HTTPS protocol.
|
# Redirect the existing request to use the HTTPS protocol.
|
||||||
#
|
#
|
||||||
# ==== Parameters
|
# ==== Parameters
|
||||||
# * <tt>host_or_options</tt> - Either a host name or any of the url & redirect options
|
# * <tt>host_or_options</tt> - Either a host name or any of the url &
|
||||||
# available to the <tt>force_ssl</tt> method.
|
# redirect options available to the <tt>force_ssl</tt> method.
|
||||||
def force_ssl_redirect(host_or_options = nil)
|
def force_ssl_redirect(host_or_options = nil)
|
||||||
unless request.ssl?
|
unless request.ssl?
|
||||||
options = {
|
options = {
|
||||||
|
@ -73,7 +73,7 @@ def helper_attr(*attrs)
|
|||||||
|
|
||||||
# Provides a proxy to access helpers methods from outside the view.
|
# Provides a proxy to access helpers methods from outside the view.
|
||||||
def helpers
|
def helpers
|
||||||
@helper_proxy ||= begin
|
@helper_proxy ||= begin
|
||||||
proxy = ActionView::Base.new
|
proxy = ActionView::Base.new
|
||||||
proxy.config = config.inheritable_copy
|
proxy.config = config.inheritable_copy
|
||||||
proxy.extend(_helpers)
|
proxy.extend(_helpers)
|
||||||
@ -100,7 +100,7 @@ def modules_for_helpers(args)
|
|||||||
def all_helpers_from_path(path)
|
def all_helpers_from_path(path)
|
||||||
helpers = Array(path).flat_map do |_path|
|
helpers = Array(path).flat_map do |_path|
|
||||||
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
|
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
|
||||||
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
|
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
|
||||||
names.sort!
|
names.sort!
|
||||||
end
|
end
|
||||||
helpers.uniq!
|
helpers.uniq!
|
||||||
|
@ -94,7 +94,7 @@ def authenticate(request, &login_procedure)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def has_basic_credentials?(request)
|
def has_basic_credentials?(request)
|
||||||
request.authorization.present? && (auth_scheme(request) == 'Basic')
|
request.authorization.present? && (auth_scheme(request).downcase == 'basic')
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_name_and_password(request)
|
def user_name_and_password(request)
|
||||||
@ -502,7 +502,7 @@ def encode_credentials(token, options = {})
|
|||||||
def authentication_request(controller, realm, message = nil)
|
def authentication_request(controller, realm, message = nil)
|
||||||
message ||= "HTTP Token: Access denied.\n"
|
message ||= "HTTP Token: Access denied.\n"
|
||||||
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}")
|
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}")
|
||||||
controller.__send__ :render, :text => message, :status => :unauthorized
|
controller.__send__ :render, plain: message, status: :unauthorized
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -156,16 +156,16 @@ module MimeResponds
|
|||||||
# It works for both inline:
|
# It works for both inline:
|
||||||
#
|
#
|
||||||
# respond_to do |format|
|
# respond_to do |format|
|
||||||
# format.html.any { render text: "any" }
|
# format.html.any { render html: "any" }
|
||||||
# format.html.phone { render text: "phone" }
|
# format.html.phone { render html: "phone" }
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# and block syntax:
|
# and block syntax:
|
||||||
#
|
#
|
||||||
# respond_to do |format|
|
# respond_to do |format|
|
||||||
# format.html do |variant|
|
# format.html do |variant|
|
||||||
# variant.any(:tablet, :phablet){ render text: "any" }
|
# variant.any(:tablet, :phablet){ render html: "any" }
|
||||||
# variant.phone { render text: "phone" }
|
# variant.phone { render html: "phone" }
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
require 'action_dispatch/http/mime_type'
|
require 'action_dispatch/http/mime_type'
|
||||||
|
|
||||||
module ActionController
|
module ActionController
|
||||||
# Wraps the parameters hash into a nested hash. This will allow clients to submit
|
# Wraps the parameters hash into a nested hash. This will allow clients to
|
||||||
# POST requests without having to specify any root elements.
|
# submit requests without having to specify any root elements.
|
||||||
#
|
#
|
||||||
# This functionality is enabled in +config/initializers/wrap_parameters.rb+
|
# This functionality is enabled in +config/initializers/wrap_parameters.rb+
|
||||||
# and can be customized.
|
# and can be customized.
|
||||||
@ -14,7 +14,7 @@ module ActionController
|
|||||||
# a non-empty array:
|
# a non-empty array:
|
||||||
#
|
#
|
||||||
# class UsersController < ApplicationController
|
# class UsersController < ApplicationController
|
||||||
# wrap_parameters format: [:json, :xml]
|
# wrap_parameters format: [:json, :xml, :url_encoded_form, :multipart_form]
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# If you enable +ParamsWrapper+ for +:json+ format, instead of having to
|
# If you enable +ParamsWrapper+ for +:json+ format, instead of having to
|
||||||
|
@ -94,7 +94,7 @@ def self.add(key, &block)
|
|||||||
|
|
||||||
# This method is the opposite of add method.
|
# This method is the opposite of add method.
|
||||||
#
|
#
|
||||||
# Usage:
|
# To remove a csv renderer:
|
||||||
#
|
#
|
||||||
# ActionController::Renderers.remove(:csv)
|
# ActionController::Renderers.remove(:csv)
|
||||||
def self.remove(key)
|
def self.remove(key)
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
require 'active_support/deprecation'
|
||||||
|
require 'active_support/core_ext/string/filters'
|
||||||
|
|
||||||
module ActionController
|
module ActionController
|
||||||
module Rendering
|
module Rendering
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
@ -74,6 +77,17 @@ def _normalize_args(action=nil, options={}, &blk) #:nodoc:
|
|||||||
def _normalize_options(options) #:nodoc:
|
def _normalize_options(options) #:nodoc:
|
||||||
_normalize_text(options)
|
_normalize_text(options)
|
||||||
|
|
||||||
|
if options[:text]
|
||||||
|
ActiveSupport::Deprecation.warn <<-WARNING.squish
|
||||||
|
`render :text` is deprecated because it does not actually render a
|
||||||
|
`text/plain` response. Switch to `render plain: 'plain text'` to
|
||||||
|
render as `text/plain`, `render html: '<strong>HTML</strong>'` to
|
||||||
|
render as `text/html`, or `render body: 'raw'` to match the deprecated
|
||||||
|
behavior and render with the default Content-Type, which is
|
||||||
|
`text/plain`.
|
||||||
|
WARNING
|
||||||
|
end
|
||||||
|
|
||||||
if options[:html]
|
if options[:html]
|
||||||
options[:html] = ERB::Util.html_escape(options[:html])
|
options[:html] = ERB::Util.html_escape(options[:html])
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,7 @@ class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
|
|||||||
# Since HTML and JavaScript requests are typically made from the browser, we
|
# Since HTML and JavaScript requests are typically made from the browser, we
|
||||||
# need to ensure to verify request authenticity for the web browser. We can
|
# need to ensure to verify request authenticity for the web browser. We can
|
||||||
# use session-oriented authentication for these types of requests, by using
|
# use session-oriented authentication for these types of requests, by using
|
||||||
# the `protect_form_forgery` method in our controllers.
|
# the `protect_from_forgery` method in our controllers.
|
||||||
#
|
#
|
||||||
# GET requests are not protected since they don't have side effects like writing
|
# GET requests are not protected since they don't have side effects like writing
|
||||||
# to the database and don't leak sensitive information. JavaScript requests are
|
# to the database and don't leak sensitive information. JavaScript requests are
|
||||||
@ -136,17 +136,17 @@ def initialize(controller)
|
|||||||
# This is the method that defines the application behavior when a request is found to be unverified.
|
# This is the method that defines the application behavior when a request is found to be unverified.
|
||||||
def handle_unverified_request
|
def handle_unverified_request
|
||||||
request = @controller.request
|
request = @controller.request
|
||||||
request.session = NullSessionHash.new(request.env)
|
request.session = NullSessionHash.new(request)
|
||||||
request.env['action_dispatch.request.flash_hash'] = nil
|
request.env['action_dispatch.request.flash_hash'] = nil
|
||||||
request.env['rack.session.options'] = { skip: true }
|
request.env['rack.session.options'] = { skip: true }
|
||||||
request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
|
request.cookie_jar = NullCookieJar.build(request, {})
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
|
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
|
||||||
def initialize(env)
|
def initialize(req)
|
||||||
super(nil, env)
|
super(nil, req)
|
||||||
@data = {}
|
@data = {}
|
||||||
@loaded = true
|
@loaded = true
|
||||||
end
|
end
|
||||||
@ -160,14 +160,6 @@ def exists?
|
|||||||
end
|
end
|
||||||
|
|
||||||
class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
|
class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
|
||||||
def self.build(request)
|
|
||||||
key_generator = request.env[ActionDispatch::Cookies::GENERATOR_KEY]
|
|
||||||
host = request.host
|
|
||||||
secure = request.ssl?
|
|
||||||
|
|
||||||
new(key_generator, host, secure, options_for_env({}))
|
|
||||||
end
|
|
||||||
|
|
||||||
def write(*)
|
def write(*)
|
||||||
# nothing
|
# nothing
|
||||||
end
|
end
|
||||||
|
@ -199,7 +199,7 @@ module Streaming
|
|||||||
def _process_options(options) #:nodoc:
|
def _process_options(options) #:nodoc:
|
||||||
super
|
super
|
||||||
if options[:stream]
|
if options[:stream]
|
||||||
if env["HTTP_VERSION"] == "HTTP/1.0"
|
if request.version == "HTTP/1.0"
|
||||||
options.delete(:stream)
|
options.delete(:stream)
|
||||||
else
|
else
|
||||||
headers["Cache-Control"] ||= "no-cache"
|
headers["Cache-Control"] ||= "no-cache"
|
||||||
|
@ -104,10 +104,12 @@ def initialize(params) # :nodoc:
|
|||||||
# params = ActionController::Parameters.new(key: 'value')
|
# params = ActionController::Parameters.new(key: 'value')
|
||||||
# params[:key] # => "value"
|
# params[:key] # => "value"
|
||||||
# params["key"] # => "value"
|
# params["key"] # => "value"
|
||||||
class Parameters < ActiveSupport::HashWithIndifferentAccess
|
class Parameters
|
||||||
cattr_accessor :permit_all_parameters, instance_accessor: false
|
cattr_accessor :permit_all_parameters, instance_accessor: false
|
||||||
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
|
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
|
||||||
|
|
||||||
|
delegate :keys, :key?, :has_key?, :empty?, :inspect, to: :@parameters
|
||||||
|
|
||||||
# By default, never raise an UnpermittedParameters exception if these
|
# By default, never raise an UnpermittedParameters exception if these
|
||||||
# params are present. The default includes both 'controller' and 'action'
|
# params are present. The default includes both 'controller' and 'action'
|
||||||
# because they are added by Rails and should be of no concern. One way
|
# because they are added by Rails and should be of no concern. One way
|
||||||
@ -144,11 +146,22 @@ def self.const_missing(const_name)
|
|||||||
# params = ActionController::Parameters.new(name: 'Francesco')
|
# params = ActionController::Parameters.new(name: 'Francesco')
|
||||||
# params.permitted? # => true
|
# params.permitted? # => true
|
||||||
# Person.new(params) # => #<Person id: nil, name: "Francesco">
|
# Person.new(params) # => #<Person id: nil, name: "Francesco">
|
||||||
def initialize(attributes = nil)
|
def initialize(parameters = {})
|
||||||
super(attributes)
|
@parameters = parameters.with_indifferent_access
|
||||||
@permitted = self.class.permit_all_parameters
|
@permitted = self.class.permit_all_parameters
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns true if another +Parameters+ object contains the same content and
|
||||||
|
# permitted flag, or other Hash-like object contains the same content. This
|
||||||
|
# override is in place so you can perform a comparison with `Hash`.
|
||||||
|
def ==(other_hash)
|
||||||
|
if other_hash.respond_to?(:permitted?)
|
||||||
|
super
|
||||||
|
else
|
||||||
|
@parameters == other_hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Returns a safe +Hash+ representation of this parameter with all
|
# Returns a safe +Hash+ representation of this parameter with all
|
||||||
# unpermitted keys removed.
|
# unpermitted keys removed.
|
||||||
#
|
#
|
||||||
@ -162,7 +175,7 @@ def initialize(attributes = nil)
|
|||||||
# safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
|
# safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
|
||||||
def to_h
|
def to_h
|
||||||
if permitted?
|
if permitted?
|
||||||
to_hash
|
@parameters.to_h
|
||||||
else
|
else
|
||||||
slice(*self.class.always_permitted_parameters).permit!.to_h
|
slice(*self.class.always_permitted_parameters).permit!.to_h
|
||||||
end
|
end
|
||||||
@ -170,20 +183,17 @@ def to_h
|
|||||||
|
|
||||||
# Returns an unsafe, unfiltered +Hash+ representation of this parameter.
|
# Returns an unsafe, unfiltered +Hash+ representation of this parameter.
|
||||||
def to_unsafe_h
|
def to_unsafe_h
|
||||||
to_hash
|
@parameters.to_h
|
||||||
end
|
end
|
||||||
alias_method :to_unsafe_hash, :to_unsafe_h
|
alias_method :to_unsafe_hash, :to_unsafe_h
|
||||||
|
|
||||||
# Convert all hashes in values into parameters, then yield each pair like
|
# Convert all hashes in values into parameters, then yield each pair like
|
||||||
# the same way as <tt>Hash#each_pair</tt>
|
# the same way as <tt>Hash#each_pair</tt>
|
||||||
def each_pair(&block)
|
def each_pair(&block)
|
||||||
super do |key, value|
|
@parameters.each_pair do |key, value|
|
||||||
convert_hashes_to_parameters(key, value)
|
yield key, convert_hashes_to_parameters(key, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
super
|
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :each, :each_pair
|
alias_method :each, :each_pair
|
||||||
|
|
||||||
# Attribute that keeps track of converted arrays, if any, to avoid double
|
# Attribute that keeps track of converted arrays, if any, to avoid double
|
||||||
@ -347,7 +357,13 @@ def permit(*filters)
|
|||||||
# params[:person] # => {"name"=>"Francesco"}
|
# params[:person] # => {"name"=>"Francesco"}
|
||||||
# params[:none] # => nil
|
# params[:none] # => nil
|
||||||
def [](key)
|
def [](key)
|
||||||
convert_hashes_to_parameters(key, super)
|
convert_hashes_to_parameters(key, @parameters[key])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Assigns a value to a given +key+. The given key may still get filtered out
|
||||||
|
# when +permit+ is called.
|
||||||
|
def []=(key, value)
|
||||||
|
@parameters[key] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a parameter for the given +key+. If the +key+
|
# Returns a parameter for the given +key+. If the +key+
|
||||||
@ -361,10 +377,16 @@ def [](key)
|
|||||||
# params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
|
# params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
|
||||||
# params.fetch(:none, 'Francesco') # => "Francesco"
|
# params.fetch(:none, 'Francesco') # => "Francesco"
|
||||||
# params.fetch(:none) { 'Francesco' } # => "Francesco"
|
# params.fetch(:none) { 'Francesco' } # => "Francesco"
|
||||||
def fetch(key, *args)
|
def fetch(key, *args, &block)
|
||||||
convert_hashes_to_parameters(key, super, false)
|
convert_value_to_parameters(
|
||||||
rescue KeyError
|
@parameters.fetch(key) {
|
||||||
raise ActionController::ParameterMissing.new(key)
|
if block_given?
|
||||||
|
yield
|
||||||
|
else
|
||||||
|
args.fetch(0) { raise ActionController::ParameterMissing.new(key) }
|
||||||
|
end
|
||||||
|
}
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a new <tt>ActionController::Parameters</tt> instance that
|
# Returns a new <tt>ActionController::Parameters</tt> instance that
|
||||||
@ -375,7 +397,24 @@ def fetch(key, *args)
|
|||||||
# params.slice(:a, :b) # => {"a"=>1, "b"=>2}
|
# params.slice(:a, :b) # => {"a"=>1, "b"=>2}
|
||||||
# params.slice(:d) # => {}
|
# params.slice(:d) # => {}
|
||||||
def slice(*keys)
|
def slice(*keys)
|
||||||
new_instance_with_inherited_permitted_status(super)
|
new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns current <tt>ActionController::Parameters</tt> instance which
|
||||||
|
# contains only the given +keys+.
|
||||||
|
def slice!(*keys)
|
||||||
|
@parameters.slice!(*keys)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a new <tt>ActionController::Parameters</tt> instance that
|
||||||
|
# filters out the given +keys+.
|
||||||
|
#
|
||||||
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
||||||
|
# params.except(:a, :b) # => {"c"=>3}
|
||||||
|
# params.except(:d) # => {"a"=>1,"b"=>2,"c"=>3}
|
||||||
|
def except(*keys)
|
||||||
|
new_instance_with_inherited_permitted_status(@parameters.except(*keys))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Removes and returns the key/value pairs matching the given keys.
|
# Removes and returns the key/value pairs matching the given keys.
|
||||||
@ -384,7 +423,7 @@ def slice(*keys)
|
|||||||
# params.extract!(:a, :b) # => {"a"=>1, "b"=>2}
|
# params.extract!(:a, :b) # => {"a"=>1, "b"=>2}
|
||||||
# params # => {"c"=>3}
|
# params # => {"c"=>3}
|
||||||
def extract!(*keys)
|
def extract!(*keys)
|
||||||
new_instance_with_inherited_permitted_status(super)
|
new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a new <tt>ActionController::Parameters</tt> with the results of
|
# Returns a new <tt>ActionController::Parameters</tt> with the results of
|
||||||
@ -393,36 +432,80 @@ def extract!(*keys)
|
|||||||
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
||||||
# params.transform_values { |x| x * 2 }
|
# params.transform_values { |x| x * 2 }
|
||||||
# # => {"a"=>2, "b"=>4, "c"=>6}
|
# # => {"a"=>2, "b"=>4, "c"=>6}
|
||||||
def transform_values
|
def transform_values(&block)
|
||||||
if block_given?
|
if block
|
||||||
new_instance_with_inherited_permitted_status(super)
|
new_instance_with_inherited_permitted_status(
|
||||||
|
@parameters.transform_values(&block)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
super
|
@parameters.transform_values
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This method is here only to make sure that the returned object has the
|
# Performs values transformation and returns the altered
|
||||||
# correct +permitted+ status. It should not matter since the parent of
|
# <tt>ActionController::Parameters</tt> instance.
|
||||||
# this object is +HashWithIndifferentAccess+
|
def transform_values!(&block)
|
||||||
def transform_keys # :nodoc:
|
@parameters.transform_values!(&block)
|
||||||
if block_given?
|
self
|
||||||
new_instance_with_inherited_permitted_status(super)
|
end
|
||||||
|
|
||||||
|
# Returns a new <tt>ActionController::Parameters</tt> instance with the
|
||||||
|
# results of running +block+ once for every key. The values are unchanged.
|
||||||
|
def transform_keys(&block)
|
||||||
|
if block
|
||||||
|
new_instance_with_inherited_permitted_status(
|
||||||
|
@parameters.transform_keys(&block)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
super
|
@parameters.transform_keys
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Performs keys transformation and returns the altered
|
||||||
|
# <tt>ActionController::Parameters</tt> instance.
|
||||||
|
def transform_keys!(&block)
|
||||||
|
@parameters.transform_keys!(&block)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
# Deletes and returns a key-value pair from +Parameters+ whose key is equal
|
# Deletes and returns a key-value pair from +Parameters+ whose key is equal
|
||||||
# to key. If the key is not found, returns the default value. If the
|
# to key. If the key is not found, returns the default value. If the
|
||||||
# optional code block is given and the key is not found, pass in the key
|
# optional code block is given and the key is not found, pass in the key
|
||||||
# and return the result of block.
|
# and return the result of block.
|
||||||
def delete(key, &block)
|
def delete(key, &block)
|
||||||
convert_hashes_to_parameters(key, super, false)
|
convert_value_to_parameters(@parameters.delete(key))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a new instance of <tt>ActionController::Parameters</tt> with only
|
||||||
|
# items that the block evaluates to true.
|
||||||
|
def select(&block)
|
||||||
|
new_instance_with_inherited_permitted_status(@parameters.select(&block))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Equivalent to Hash#keep_if, but returns nil if no changes were made.
|
# Equivalent to Hash#keep_if, but returns nil if no changes were made.
|
||||||
def select!(&block)
|
def select!(&block)
|
||||||
convert_value_to_parameters(super)
|
@parameters.select!(&block)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
alias_method :keep_if, :select!
|
||||||
|
|
||||||
|
# Returns a new instance of <tt>ActionController::Parameters</tt> with items
|
||||||
|
# that the block evaluates to true removed.
|
||||||
|
def reject(&block)
|
||||||
|
new_instance_with_inherited_permitted_status(@parameters.reject(&block))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Removes items that the block evaluates to true and returns self.
|
||||||
|
def reject!(&block)
|
||||||
|
@parameters.reject!(&block)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
alias_method :delete_if, :reject!
|
||||||
|
|
||||||
|
# Return values that were assigned to the given +keys+. Note that all the
|
||||||
|
# +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
|
||||||
|
def values_at(*keys)
|
||||||
|
convert_value_to_parameters(@parameters.values_at(*keys))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an exact copy of the <tt>ActionController::Parameters</tt>
|
# Returns an exact copy of the <tt>ActionController::Parameters</tt>
|
||||||
@ -439,11 +522,30 @@ def dup
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns a new <tt>ActionController::Parameters</tt> with all keys from
|
||||||
|
# +other_hash+ merges into current hash.
|
||||||
|
def merge(other_hash)
|
||||||
|
new_instance_with_inherited_permitted_status(
|
||||||
|
@parameters.merge(other_hash)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is required by ActiveModel attribute assignment, so that user can
|
||||||
|
# pass +Parameters+ to a mass assignment methods in a model. It should not
|
||||||
|
# matter as we are using +HashWithIndifferentAccess+ internally.
|
||||||
|
def stringify_keys # :nodoc:
|
||||||
|
dup
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
def permitted=(new_permitted)
|
def permitted=(new_permitted)
|
||||||
@permitted = new_permitted
|
@permitted = new_permitted
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fields_for_style?
|
||||||
|
@parameters.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def new_instance_with_inherited_permitted_status(hash)
|
def new_instance_with_inherited_permitted_status(hash)
|
||||||
self.class.new(hash).tap do |new_instance|
|
self.class.new(hash).tap do |new_instance|
|
||||||
@ -451,40 +553,41 @@ def new_instance_with_inherited_permitted_status(hash)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_hashes_to_parameters(key, value, assign_if_converted=true)
|
def convert_hashes_to_parameters(key, value)
|
||||||
converted = convert_value_to_parameters(value)
|
converted = convert_value_to_parameters(value)
|
||||||
self[key] = converted if assign_if_converted && !converted.equal?(value)
|
@parameters[key] = converted unless converted.equal?(value)
|
||||||
converted
|
converted
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_value_to_parameters(value)
|
def convert_value_to_parameters(value)
|
||||||
if value.is_a?(Array) && !converted_arrays.member?(value)
|
case value
|
||||||
|
when Array
|
||||||
|
return value if converted_arrays.member?(value)
|
||||||
converted = value.map { |_| convert_value_to_parameters(_) }
|
converted = value.map { |_| convert_value_to_parameters(_) }
|
||||||
converted_arrays << converted
|
converted_arrays << converted
|
||||||
converted
|
converted
|
||||||
elsif value.is_a?(Parameters) || !value.is_a?(Hash)
|
when Hash
|
||||||
value
|
|
||||||
else
|
|
||||||
self.class.new(value)
|
self.class.new(value)
|
||||||
|
else
|
||||||
|
value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def each_element(object)
|
def each_element(object)
|
||||||
if object.is_a?(Array)
|
case object
|
||||||
object.map { |el| yield el }.compact
|
when Array
|
||||||
elsif fields_for_style?(object)
|
object.grep(Parameters).map { |el| yield el }.compact
|
||||||
hash = object.class.new
|
when Parameters
|
||||||
object.each { |k,v| hash[k] = yield v }
|
if object.fields_for_style?
|
||||||
hash
|
hash = object.class.new
|
||||||
else
|
object.each { |k,v| hash[k] = yield v }
|
||||||
yield object
|
hash
|
||||||
|
else
|
||||||
|
yield object
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fields_for_style?(object)
|
|
||||||
object.is_a?(Hash) && object.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def unpermitted_parameters!(params)
|
def unpermitted_parameters!(params)
|
||||||
unpermitted_keys = unpermitted_keys(params)
|
unpermitted_keys = unpermitted_keys(params)
|
||||||
if unpermitted_keys.any?
|
if unpermitted_keys.any?
|
||||||
@ -546,14 +649,8 @@ def permitted_scalar_filter(params, key)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def array_of_permitted_scalars?(value)
|
def array_of_permitted_scalars?(value)
|
||||||
if value.is_a?(Array)
|
if value.is_a?(Array) && value.all? {|element| permitted_scalar?(element)}
|
||||||
value.all? {|element| permitted_scalar?(element)}
|
yield value
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def array_of_permitted_scalars_filter(params, key)
|
|
||||||
if has_key?(key) && array_of_permitted_scalars?(self[key])
|
|
||||||
params[key] = self[key]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -564,17 +661,17 @@ def hash_filter(params, filter)
|
|||||||
# Slicing filters out non-declared keys.
|
# Slicing filters out non-declared keys.
|
||||||
slice(*filter.keys).each do |key, value|
|
slice(*filter.keys).each do |key, value|
|
||||||
next unless value
|
next unless value
|
||||||
|
next unless has_key? key
|
||||||
|
|
||||||
if filter[key] == EMPTY_ARRAY
|
if filter[key] == EMPTY_ARRAY
|
||||||
# Declaration { comment_ids: [] }.
|
# Declaration { comment_ids: [] }.
|
||||||
array_of_permitted_scalars_filter(params, key)
|
array_of_permitted_scalars?(self[key]) do |val|
|
||||||
|
params[key] = val
|
||||||
|
end
|
||||||
else
|
else
|
||||||
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
|
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
|
||||||
params[key] = each_element(value) do |element|
|
params[key] = each_element(value) do |element|
|
||||||
if element.is_a?(Hash)
|
element.permit(*Array.wrap(filter[key]))
|
||||||
element = self.class.new(element) unless element.respond_to?(:permit)
|
|
||||||
element.permit(*Array.wrap(filter[key]))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -41,7 +41,11 @@ def url_options
|
|||||||
if original_script_name
|
if original_script_name
|
||||||
options[:original_script_name] = original_script_name
|
options[:original_script_name] = original_script_name
|
||||||
else
|
else
|
||||||
options[:script_name] = same_origin ? request.script_name.dup : script_name
|
if same_origin
|
||||||
|
options[:script_name] = request.script_name.empty? ? "".freeze : request.script_name.dup
|
||||||
|
else
|
||||||
|
options[:script_name] = script_name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
options.freeze
|
options.freeze
|
||||||
else
|
else
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
module ActionController
|
|
||||||
class Middleware < Metal
|
|
||||||
class ActionMiddleware
|
|
||||||
def initialize(controller, app)
|
|
||||||
@controller, @app = controller, app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
request = ActionDispatch::Request.new(env)
|
|
||||||
@controller.build(@app).dispatch(:index, request)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class << self
|
|
||||||
alias build new
|
|
||||||
|
|
||||||
def new(app)
|
|
||||||
ActionMiddleware.new(self, app)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_internal :app
|
|
||||||
|
|
||||||
def process(action)
|
|
||||||
response = super
|
|
||||||
self.status, self.headers, self.response_body = response if response.is_a?(Array)
|
|
||||||
response
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(app)
|
|
||||||
super()
|
|
||||||
@_app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def index
|
|
||||||
call(env)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,4 +1,5 @@
|
|||||||
require 'rack/session/abstract/id'
|
require 'rack/session/abstract/id'
|
||||||
|
require 'active_support/core_ext/hash/conversions'
|
||||||
require 'active_support/core_ext/object/to_query'
|
require 'active_support/core_ext/object/to_query'
|
||||||
require 'active_support/core_ext/module/anonymous'
|
require 'active_support/core_ext/module/anonymous'
|
||||||
require 'active_support/core_ext/hash/keys'
|
require 'active_support/core_ext/hash/keys'
|
||||||
@ -35,21 +36,23 @@ def initialize(env, session)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def query_string=(string)
|
def query_string=(string)
|
||||||
@env[Rack::QUERY_STRING] = string
|
set_header Rack::QUERY_STRING, string
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_parameters=(params)
|
def request_parameters=(params)
|
||||||
@env["action_dispatch.request.request_parameters"] = params
|
set_header "action_dispatch.request.request_parameters", params
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_parameters(routes, controller_path, action, parameters = {})
|
def content_type=(type)
|
||||||
parameters = parameters.symbolize_keys
|
set_header 'CONTENT_TYPE', type
|
||||||
generated_path, extra_keys = routes.generate_extras(parameters.merge(:controller => controller_path, :action => action))
|
end
|
||||||
|
|
||||||
|
def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
|
||||||
non_path_parameters = {}
|
non_path_parameters = {}
|
||||||
path_parameters = {}
|
path_parameters = {}
|
||||||
|
|
||||||
parameters.each do |key, value|
|
parameters.each do |key, value|
|
||||||
if extra_keys.include?(key) || key == :action || key == :controller
|
if query_string_keys.include?(key)
|
||||||
non_path_parameters[key] = value
|
non_path_parameters[key] = value
|
||||||
else
|
else
|
||||||
if value.is_a?(Array)
|
if value.is_a?(Array)
|
||||||
@ -68,10 +71,12 @@ def assign_parameters(routes, controller_path, action, parameters = {})
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
if ENCODER.should_multipart?(non_path_parameters)
|
if ENCODER.should_multipart?(non_path_parameters)
|
||||||
@env['CONTENT_TYPE'] = ENCODER.content_type
|
self.content_type = ENCODER.content_type
|
||||||
data = ENCODER.build_multipart non_path_parameters
|
data = ENCODER.build_multipart non_path_parameters
|
||||||
else
|
else
|
||||||
@env['CONTENT_TYPE'] ||= 'application/x-www-form-urlencoded'
|
get_header('CONTENT_TYPE') do |k|
|
||||||
|
set_header k, 'application/x-www-form-urlencoded'
|
||||||
|
end
|
||||||
|
|
||||||
# FIXME: setting `request_parametes` is normally handled by the
|
# FIXME: setting `request_parametes` is normally handled by the
|
||||||
# params parser middleware, and we should remove this roundtripping
|
# params parser middleware, and we should remove this roundtripping
|
||||||
@ -93,8 +98,8 @@ def assign_parameters(routes, controller_path, action, parameters = {})
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@env['CONTENT_LENGTH'] = data.length.to_s
|
set_header 'CONTENT_LENGTH', data.length.to_s
|
||||||
@env['rack.input'] = StringIO.new(data)
|
set_header 'rack.input', StringIO.new(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
@env["PATH_INFO"] ||= generated_path
|
@env["PATH_INFO"] ||= generated_path
|
||||||
@ -132,23 +137,13 @@ def content_type
|
|||||||
end.new
|
end.new
|
||||||
end
|
end
|
||||||
|
|
||||||
class TestResponse < ActionDispatch::TestResponse
|
|
||||||
end
|
|
||||||
|
|
||||||
class LiveTestResponse < Live::Response
|
class LiveTestResponse < Live::Response
|
||||||
def body
|
|
||||||
@body ||= super
|
|
||||||
end
|
|
||||||
|
|
||||||
# Was the response successful?
|
# Was the response successful?
|
||||||
alias_method :success?, :successful?
|
alias_method :success?, :successful?
|
||||||
|
|
||||||
# Was the URL not found?
|
# Was the URL not found?
|
||||||
alias_method :missing?, :not_found?
|
alias_method :missing?, :not_found?
|
||||||
|
|
||||||
# Were we redirected?
|
|
||||||
alias_method :redirect?, :redirection?
|
|
||||||
|
|
||||||
# Was there a server-side error?
|
# Was there a server-side error?
|
||||||
alias_method :error?, :server_error?
|
alias_method :error?, :server_error?
|
||||||
end
|
end
|
||||||
@ -181,6 +176,10 @@ def destroy
|
|||||||
clear
|
clear
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch(*args, &block)
|
||||||
|
@data.fetch(*args, &block)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def load!
|
def load!
|
||||||
@ -237,7 +236,7 @@ def load!
|
|||||||
# request. You can modify this object before sending the HTTP request. For example,
|
# request. You can modify this object before sending the HTTP request. For example,
|
||||||
# you might want to set some session properties before sending a GET request.
|
# you might want to set some session properties before sending a GET request.
|
||||||
# <b>@response</b>::
|
# <b>@response</b>::
|
||||||
# An ActionController::TestResponse object, representing the response
|
# An ActionDispatch::TestResponse object, representing the response
|
||||||
# of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
|
# of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
|
||||||
# after calling +post+. If the various assert methods are not sufficient, then you
|
# after calling +post+. If the various assert methods are not sufficient, then you
|
||||||
# may use this object to inspect the HTTP response in detail.
|
# may use this object to inspect the HTTP response in detail.
|
||||||
@ -457,7 +456,7 @@ def process(action, *args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if body.present?
|
if body.present?
|
||||||
@request.env['RAW_POST_DATA'] = body
|
@request.set_header 'RAW_POST_DATA', body
|
||||||
end
|
end
|
||||||
|
|
||||||
if http_method.present?
|
if http_method.present?
|
||||||
@ -479,44 +478,50 @@ def process(action, *args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.cookies.update @request.cookies
|
self.cookies.update @request.cookies
|
||||||
@request.env['HTTP_COOKIE'] = cookies.to_header
|
@request.set_header 'HTTP_COOKIE', cookies.to_header
|
||||||
@request.env['action_dispatch.cookies'] = nil
|
@request.delete_header 'action_dispatch.cookies'
|
||||||
|
|
||||||
@request = TestRequest.new scrub_env!(@request.env), @request.session
|
@request = TestRequest.new scrub_env!(@request.env), @request.session
|
||||||
@response = build_response @response_klass
|
@response = build_response @response_klass
|
||||||
@response.request = @request
|
@response.request = @request
|
||||||
@controller.recycle!
|
@controller.recycle!
|
||||||
|
|
||||||
@request.env['REQUEST_METHOD'] = http_method
|
@request.set_header 'REQUEST_METHOD', http_method
|
||||||
|
|
||||||
controller_class_name = @controller.class.anonymous? ?
|
parameters = parameters.symbolize_keys
|
||||||
"anonymous" :
|
|
||||||
@controller.class.controller_path
|
|
||||||
|
|
||||||
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
|
generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action.to_s))
|
||||||
|
generated_path = generated_path(generated_extras)
|
||||||
|
query_string_keys = query_parameter_names(generated_extras)
|
||||||
|
|
||||||
|
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters, generated_path, query_string_keys)
|
||||||
|
|
||||||
@request.session.update(session) if session
|
@request.session.update(session) if session
|
||||||
@request.flash.update(flash || {})
|
@request.flash.update(flash || {})
|
||||||
|
|
||||||
if xhr
|
if xhr
|
||||||
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
@request.set_header 'HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'
|
||||||
@request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
|
@request.get_header('HTTP_ACCEPT') do |k|
|
||||||
|
@request.set_header k, [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@controller.request = @request
|
@controller.request = @request
|
||||||
@controller.response = @response
|
@controller.response = @response
|
||||||
|
|
||||||
@request.env["SCRIPT_NAME"] ||= @controller.config.relative_url_root
|
@request.get_header("SCRIPT_NAME") do |k|
|
||||||
|
@request.set_header k, @controller.config.relative_url_root
|
||||||
|
end
|
||||||
|
|
||||||
@controller.recycle!
|
@controller.recycle!
|
||||||
@controller.process(action)
|
@controller.process(action)
|
||||||
|
|
||||||
@request.env.delete 'HTTP_COOKIE'
|
@request.delete_header 'HTTP_COOKIE'
|
||||||
|
|
||||||
if cookies = @request.env['action_dispatch.cookies']
|
if @request.have_cookie_jar?
|
||||||
unless @response.committed?
|
unless @response.committed?
|
||||||
cookies.write(@response)
|
@request.cookie_jar.write(@response)
|
||||||
self.cookies.update(cookies.instance_variable_get(:@cookies))
|
self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@response.prepare!
|
@response.prepare!
|
||||||
@ -528,18 +533,30 @@ def process(action, *args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if xhr
|
if xhr
|
||||||
@request.env.delete 'HTTP_X_REQUESTED_WITH'
|
@request.delete_header 'HTTP_X_REQUESTED_WITH'
|
||||||
@request.env.delete 'HTTP_ACCEPT'
|
@request.delete_header 'HTTP_ACCEPT'
|
||||||
end
|
end
|
||||||
@request.query_string = ''
|
@request.query_string = ''
|
||||||
|
|
||||||
@response
|
@response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def controller_class_name
|
||||||
|
@controller.class.anonymous? ? "anonymous" : @controller.class.controller_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def generated_path(generated_extras)
|
||||||
|
generated_extras[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
def query_parameter_names(generated_extras)
|
||||||
|
generated_extras[1] + [:controller, :action]
|
||||||
|
end
|
||||||
|
|
||||||
def setup_controller_request_and_response
|
def setup_controller_request_and_response
|
||||||
@controller = nil unless defined? @controller
|
@controller = nil unless defined? @controller
|
||||||
|
|
||||||
@response_klass = TestResponse
|
@response_klass = ActionDispatch::TestResponse
|
||||||
|
|
||||||
if klass = self.class.controller_class
|
if klass = self.class.controller_class
|
||||||
if klass < ActionController::Live
|
if klass < ActionController::Live
|
||||||
|
@ -50,13 +50,13 @@ def filtered_path
|
|||||||
protected
|
protected
|
||||||
|
|
||||||
def parameter_filter
|
def parameter_filter
|
||||||
parameter_filter_for @env.fetch("action_dispatch.parameter_filter") {
|
parameter_filter_for get_header("action_dispatch.parameter_filter") {
|
||||||
return NULL_PARAM_FILTER
|
return NULL_PARAM_FILTER
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def env_filter
|
def env_filter
|
||||||
user_key = @env.fetch("action_dispatch.parameter_filter") {
|
user_key = get_header("action_dispatch.parameter_filter") {
|
||||||
return NULL_ENV_FILTER
|
return NULL_ENV_FILTER
|
||||||
}
|
}
|
||||||
parameter_filter_for(Array(user_key) + ENV_MATCH)
|
parameter_filter_for(Array(user_key) + ENV_MATCH)
|
||||||
|
@ -5,8 +5,7 @@ module FilterRedirect
|
|||||||
FILTERED = '[FILTERED]'.freeze # :nodoc:
|
FILTERED = '[FILTERED]'.freeze # :nodoc:
|
||||||
|
|
||||||
def filtered_location # :nodoc:
|
def filtered_location # :nodoc:
|
||||||
filters = location_filter
|
if location_filter_match?
|
||||||
if !filters.empty? && location_filter_match?(filters)
|
|
||||||
FILTERED
|
FILTERED
|
||||||
else
|
else
|
||||||
location
|
location
|
||||||
@ -15,7 +14,7 @@ def filtered_location # :nodoc:
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def location_filter
|
def location_filters
|
||||||
if request
|
if request
|
||||||
request.env['action_dispatch.redirect_filter'] || []
|
request.env['action_dispatch.redirect_filter'] || []
|
||||||
else
|
else
|
||||||
@ -23,12 +22,12 @@ def location_filter
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def location_filter_match?(filters)
|
def location_filter_match?
|
||||||
filters.any? do |filter|
|
location_filters.any? do |filter|
|
||||||
if String === filter
|
if String === filter
|
||||||
location.include?(filter)
|
location.include?(filter)
|
||||||
elsif Regexp === filter
|
elsif Regexp === filter
|
||||||
location.match(filter)
|
location =~ filter
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -30,27 +30,32 @@ class Headers
|
|||||||
HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
|
HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
|
||||||
|
|
||||||
include Enumerable
|
include Enumerable
|
||||||
attr_reader :env
|
|
||||||
|
|
||||||
def initialize(env = {}) # :nodoc:
|
def self.from_hash(hash)
|
||||||
@env = env
|
new ActionDispatch::Request.new hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(request) # :nodoc:
|
||||||
|
@req = request
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the value for the given key mapped to @env.
|
# Returns the value for the given key mapped to @env.
|
||||||
def [](key)
|
def [](key)
|
||||||
@env[env_name(key)]
|
@req.get_header env_name(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets the given value for the key mapped to @env.
|
# Sets the given value for the key mapped to @env.
|
||||||
def []=(key, value)
|
def []=(key, value)
|
||||||
@env[env_name(key)] = value
|
@req.set_header env_name(key), value
|
||||||
end
|
end
|
||||||
|
|
||||||
def key?(key)
|
def key?(key)
|
||||||
@env.key? env_name(key)
|
@req.has_header? env_name(key)
|
||||||
end
|
end
|
||||||
alias :include? :key?
|
alias :include? :key?
|
||||||
|
|
||||||
|
DEFAULT = Object.new # :nodoc:
|
||||||
|
|
||||||
# Returns the value for the given key mapped to @env.
|
# Returns the value for the given key mapped to @env.
|
||||||
#
|
#
|
||||||
# If the key is not found and an optional code block is not provided,
|
# If the key is not found and an optional code block is not provided,
|
||||||
@ -58,18 +63,22 @@ def key?(key)
|
|||||||
#
|
#
|
||||||
# If the code block is provided, then it will be run and
|
# If the code block is provided, then it will be run and
|
||||||
# its result returned.
|
# its result returned.
|
||||||
def fetch(key, *args, &block)
|
def fetch(key, default = DEFAULT)
|
||||||
@env.fetch env_name(key), *args, &block
|
@req.get_header(env_name(key)) do
|
||||||
|
return default unless default == DEFAULT
|
||||||
|
return yield if block_given?
|
||||||
|
raise NameError, key
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def each(&block)
|
def each(&block)
|
||||||
@env.each(&block)
|
@req.each_header(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a new Http::Headers instance containing the contents of
|
# Returns a new Http::Headers instance containing the contents of
|
||||||
# <tt>headers_or_env</tt> and the original instance.
|
# <tt>headers_or_env</tt> and the original instance.
|
||||||
def merge(headers_or_env)
|
def merge(headers_or_env)
|
||||||
headers = Http::Headers.new(env.dup)
|
headers = @req.dup.headers
|
||||||
headers.merge!(headers_or_env)
|
headers.merge!(headers_or_env)
|
||||||
headers
|
headers
|
||||||
end
|
end
|
||||||
@ -79,11 +88,14 @@ def merge(headers_or_env)
|
|||||||
# <tt>headers_or_env</tt>.
|
# <tt>headers_or_env</tt>.
|
||||||
def merge!(headers_or_env)
|
def merge!(headers_or_env)
|
||||||
headers_or_env.each do |key, value|
|
headers_or_env.each do |key, value|
|
||||||
self[env_name(key)] = value
|
@req.set_header env_name(key), value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def env; @req.env.dup; end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Converts a HTTP header name to an environment variable name if it is
|
# Converts a HTTP header name to an environment variable name if it is
|
||||||
# not contained within the headers hash.
|
# not contained within the headers hash.
|
||||||
def env_name(key)
|
def env_name(key)
|
||||||
|
@ -15,12 +15,13 @@ module MimeNegotiation
|
|||||||
# For backward compatibility, the post \format is extracted from the
|
# For backward compatibility, the post \format is extracted from the
|
||||||
# X-Post-Data-Format HTTP header if present.
|
# X-Post-Data-Format HTTP header if present.
|
||||||
def content_mime_type
|
def content_mime_type
|
||||||
@env["action_dispatch.request.content_type"] ||= begin
|
get_header("action_dispatch.request.content_type") do |k|
|
||||||
if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
|
v = if get_header('CONTENT_TYPE') =~ /^([^,\;]*)/
|
||||||
Mime::Type.lookup($1.strip.downcase)
|
Mime::Type.lookup($1.strip.downcase)
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
set_header k, v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -30,14 +31,15 @@ def content_type
|
|||||||
|
|
||||||
# Returns the accepted MIME type for the request.
|
# Returns the accepted MIME type for the request.
|
||||||
def accepts
|
def accepts
|
||||||
@env["action_dispatch.request.accepts"] ||= begin
|
get_header("action_dispatch.request.accepts") do |k|
|
||||||
header = @env['HTTP_ACCEPT'].to_s.strip
|
header = get_header('HTTP_ACCEPT').to_s.strip
|
||||||
|
|
||||||
if header.empty?
|
v = if header.empty?
|
||||||
[content_mime_type]
|
[content_mime_type]
|
||||||
else
|
else
|
||||||
Mime::Type.parse(header)
|
Mime::Type.parse(header)
|
||||||
end
|
end
|
||||||
|
set_header k, v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -52,14 +54,14 @@ def format(view_path = [])
|
|||||||
end
|
end
|
||||||
|
|
||||||
def formats
|
def formats
|
||||||
@env["action_dispatch.request.formats"] ||= begin
|
get_header("action_dispatch.request.formats") do |k|
|
||||||
params_readable = begin
|
params_readable = begin
|
||||||
parameters[:format]
|
parameters[:format]
|
||||||
rescue ActionController::BadRequest
|
rescue ActionController::BadRequest
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
if params_readable
|
v = if params_readable
|
||||||
Array(Mime[parameters[:format]])
|
Array(Mime[parameters[:format]])
|
||||||
elsif use_accept_header && valid_accept_header
|
elsif use_accept_header && valid_accept_header
|
||||||
accepts
|
accepts
|
||||||
@ -68,6 +70,7 @@ def formats
|
|||||||
else
|
else
|
||||||
[Mime::HTML]
|
[Mime::HTML]
|
||||||
end
|
end
|
||||||
|
set_header k, v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -102,7 +105,7 @@ def variant
|
|||||||
# end
|
# end
|
||||||
def format=(extension)
|
def format=(extension)
|
||||||
parameters[:format] = extension.to_s
|
parameters[:format] = extension.to_s
|
||||||
@env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
|
set_header "action_dispatch.request.formats", [Mime::Type.lookup_by_extension(parameters[:format])]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets the \formats by string extensions. This differs from #format= by allowing you
|
# Sets the \formats by string extensions. This differs from #format= by allowing you
|
||||||
@ -121,9 +124,9 @@ def format=(extension)
|
|||||||
# end
|
# end
|
||||||
def formats=(extensions)
|
def formats=(extensions)
|
||||||
parameters[:format] = extensions.first.to_s
|
parameters[:format] = extensions.first.to_s
|
||||||
@env["action_dispatch.request.formats"] = extensions.collect do |extension|
|
set_header "action_dispatch.request.formats", extensions.collect { |extension|
|
||||||
Mime::Type.lookup_by_extension(extension)
|
Mime::Type.lookup_by_extension(extension)
|
||||||
end
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Receives an array of mimes and return the first user sent mime that
|
# Receives an array of mimes and return the first user sent mime that
|
||||||
|
@ -45,7 +45,7 @@ def fetch(type)
|
|||||||
#
|
#
|
||||||
# respond_to do |format|
|
# respond_to do |format|
|
||||||
# format.html
|
# format.html
|
||||||
# format.ics { render text: @post.to_ics, mime_type: Mime::Type.lookup("text/calendar") }
|
# format.ics { render body: @post.to_ics, mime_type: Mime::Type.lookup("text/calendar") }
|
||||||
# format.xml { render xml: @post }
|
# format.xml { render xml: @post }
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
@ -211,7 +211,7 @@ def parse_data_with_trailing_star(input)
|
|||||||
|
|
||||||
# This method is opposite of register method.
|
# This method is opposite of register method.
|
||||||
#
|
#
|
||||||
# Usage:
|
# To unregister a MIME type:
|
||||||
#
|
#
|
||||||
# Mime::Type.unregister(:mobile)
|
# Mime::Type.unregister(:mobile)
|
||||||
def unregister(symbol)
|
def unregister(symbol)
|
||||||
|
@ -34,11 +34,11 @@ def self.compile(filters)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.") }
|
deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) }
|
||||||
deep_strings, strings = strings.partition { |s| s.include?("\\.") }
|
deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) }
|
||||||
|
|
||||||
regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
|
regexps << Regexp.new(strings.join('|'.freeze), true) unless strings.empty?
|
||||||
deep_regexps << Regexp.new(deep_strings.join('|'), true) unless deep_strings.empty?
|
deep_regexps << Regexp.new(deep_strings.join('|'.freeze), true) unless deep_strings.empty?
|
||||||
|
|
||||||
new regexps, deep_regexps, blocks
|
new regexps, deep_regexps, blocks
|
||||||
end
|
end
|
||||||
|
@ -8,20 +8,23 @@ module Parameters
|
|||||||
|
|
||||||
# Returns both GET and POST \parameters in a single hash.
|
# Returns both GET and POST \parameters in a single hash.
|
||||||
def parameters
|
def parameters
|
||||||
@env["action_dispatch.request.parameters"] ||= begin
|
params = get_header("action_dispatch.request.parameters")
|
||||||
params = begin
|
return params if params
|
||||||
request_parameters.merge(query_parameters)
|
|
||||||
rescue EOFError
|
params = begin
|
||||||
query_parameters.dup
|
request_parameters.merge(query_parameters)
|
||||||
end
|
rescue EOFError
|
||||||
params.merge!(path_parameters)
|
query_parameters.dup
|
||||||
end
|
end
|
||||||
|
params.merge!(path_parameters)
|
||||||
|
set_header("action_dispatch.request.parameters", params)
|
||||||
|
params
|
||||||
end
|
end
|
||||||
alias :params :parameters
|
alias :params :parameters
|
||||||
|
|
||||||
def path_parameters=(parameters) #:nodoc:
|
def path_parameters=(parameters) #:nodoc:
|
||||||
@env.delete('action_dispatch.request.parameters')
|
delete_header('action_dispatch.request.parameters')
|
||||||
@env[PARAMETERS_KEY] = parameters
|
set_header PARAMETERS_KEY, parameters
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a hash with the \parameters used to form the \path of the request.
|
# Returns a hash with the \parameters used to form the \path of the request.
|
||||||
@ -29,7 +32,7 @@ def path_parameters=(parameters) #:nodoc:
|
|||||||
#
|
#
|
||||||
# {'action' => 'my_action', 'controller' => 'my_controller'}
|
# {'action' => 'my_action', 'controller' => 'my_controller'}
|
||||||
def path_parameters
|
def path_parameters
|
||||||
@env[PARAMETERS_KEY] ||= {}
|
get_header(PARAMETERS_KEY) || {}
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -37,22 +40,7 @@ def path_parameters
|
|||||||
# Convert nested Hash to HashWithIndifferentAccess.
|
# Convert nested Hash to HashWithIndifferentAccess.
|
||||||
#
|
#
|
||||||
def normalize_encode_params(params)
|
def normalize_encode_params(params)
|
||||||
case params
|
ActionDispatch::Request::Utils.normalize_encode_params params
|
||||||
when Hash
|
|
||||||
if params.has_key?(:tempfile)
|
|
||||||
UploadedFile.new(params)
|
|
||||||
else
|
|
||||||
params.each_with_object({}) do |(key, val), new_hash|
|
|
||||||
new_hash[key] = if val.is_a?(Array)
|
|
||||||
val.map! { |el| normalize_encode_params(el) }
|
|
||||||
else
|
|
||||||
normalize_encode_params(val)
|
|
||||||
end
|
|
||||||
end.with_indifferent_access
|
|
||||||
end
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -20,8 +20,6 @@ class Request < Rack::Request
|
|||||||
include ActionDispatch::Http::FilterParameters
|
include ActionDispatch::Http::FilterParameters
|
||||||
include ActionDispatch::Http::URL
|
include ActionDispatch::Http::URL
|
||||||
|
|
||||||
HTTP_X_REQUEST_ID = "HTTP_X_REQUEST_ID".freeze # :nodoc:
|
|
||||||
|
|
||||||
autoload :Session, 'action_dispatch/request/session'
|
autoload :Session, 'action_dispatch/request/session'
|
||||||
autoload :Utils, 'action_dispatch/request/utils'
|
autoload :Utils, 'action_dispatch/request/utils'
|
||||||
|
|
||||||
@ -31,15 +29,19 @@ class Request < Rack::Request
|
|||||||
PATH_TRANSLATED REMOTE_HOST
|
PATH_TRANSLATED REMOTE_HOST
|
||||||
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
|
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
|
||||||
SERVER_NAME SERVER_PROTOCOL
|
SERVER_NAME SERVER_PROTOCOL
|
||||||
|
ORIGINAL_SCRIPT_NAME
|
||||||
|
|
||||||
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
|
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
|
||||||
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
|
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
|
||||||
HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
|
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
|
||||||
|
HTTP_X_FORWARDED_FOR HTTP_VERSION
|
||||||
|
HTTP_X_REQUEST_ID
|
||||||
|
].freeze
|
||||||
|
|
||||||
ENV_METHODS.each do |env|
|
ENV_METHODS.each do |env|
|
||||||
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
||||||
def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
|
def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
|
||||||
@env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"]
|
get_header "#{env}".freeze # get_header "HTTP_ACCEPT_CHARSET".freeze
|
||||||
end # end
|
end # end
|
||||||
METHOD
|
METHOD
|
||||||
end
|
end
|
||||||
@ -65,8 +67,20 @@ def check_path_parameters!
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def controller_class
|
||||||
|
check_path_parameters!
|
||||||
|
params = path_parameters
|
||||||
|
controller_param = params[:controller].underscore if params.key?(:controller)
|
||||||
|
params[:action] ||= 'index'
|
||||||
|
|
||||||
|
yield unless controller_param
|
||||||
|
|
||||||
|
const_name = "#{controller_param.camelize}Controller"
|
||||||
|
ActiveSupport::Dependencies.constantize(const_name)
|
||||||
|
end
|
||||||
|
|
||||||
def key?(key)
|
def key?(key)
|
||||||
@env.key?(key)
|
has_header? key
|
||||||
end
|
end
|
||||||
|
|
||||||
# List of HTTP request methods from the following RFCs:
|
# List of HTTP request methods from the following RFCs:
|
||||||
@ -103,27 +117,46 @@ def key?(key)
|
|||||||
# the application should use), this \method returns the overridden
|
# the application should use), this \method returns the overridden
|
||||||
# value, not the original.
|
# value, not the original.
|
||||||
def request_method
|
def request_method
|
||||||
@request_method ||= check_method(env["REQUEST_METHOD"])
|
@request_method ||= check_method(super)
|
||||||
end
|
end
|
||||||
|
|
||||||
def routes # :nodoc:
|
def routes # :nodoc:
|
||||||
env["action_dispatch.routes".freeze]
|
get_header("action_dispatch.routes".freeze)
|
||||||
end
|
end
|
||||||
|
|
||||||
def original_script_name # :nodoc:
|
def routes=(routes) # :nodoc:
|
||||||
env['ORIGINAL_SCRIPT_NAME'.freeze]
|
set_header("action_dispatch.routes".freeze, routes)
|
||||||
end
|
end
|
||||||
|
|
||||||
def engine_script_name(_routes) # :nodoc:
|
def engine_script_name(_routes) # :nodoc:
|
||||||
env[_routes.env_key]
|
get_header(_routes.env_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def engine_script_name=(name) # :nodoc:
|
||||||
|
set_header(routes.env_key, name.dup)
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_method=(request_method) #:nodoc:
|
def request_method=(request_method) #:nodoc:
|
||||||
if check_method(request_method)
|
if check_method(request_method)
|
||||||
@request_method = env["REQUEST_METHOD"] = request_method
|
@request_method = set_header("REQUEST_METHOD", request_method)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def controller_instance # :nodoc:
|
||||||
|
get_header('action_controller.instance'.freeze)
|
||||||
|
end
|
||||||
|
|
||||||
|
def controller_instance=(controller) # :nodoc:
|
||||||
|
set_header('action_controller.instance'.freeze, controller)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_exceptions? # :nodoc:
|
||||||
|
# We're treating `nil` as "unset", and we want the default setting to be
|
||||||
|
# `true`. This logic should be extracted to `env_config` and calculated
|
||||||
|
# once.
|
||||||
|
!(get_header('action_dispatch.show_exceptions'.freeze) == false)
|
||||||
|
end
|
||||||
|
|
||||||
# Returns a symbol form of the #request_method
|
# Returns a symbol form of the #request_method
|
||||||
def request_method_symbol
|
def request_method_symbol
|
||||||
HTTP_METHOD_LOOKUP[request_method]
|
HTTP_METHOD_LOOKUP[request_method]
|
||||||
@ -133,7 +166,7 @@ def request_method_symbol
|
|||||||
# even if it was overridden by middleware. See #request_method for
|
# even if it was overridden by middleware. See #request_method for
|
||||||
# more information.
|
# more information.
|
||||||
def method
|
def method
|
||||||
@method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
|
@method ||= check_method(get_header("rack.methodoverride.original_method") || get_header('REQUEST_METHOD'))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a symbol form of the #method
|
# Returns a symbol form of the #method
|
||||||
@ -145,7 +178,7 @@ def method_symbol
|
|||||||
#
|
#
|
||||||
# request.headers["Content-Type"] # => "text/plain"
|
# request.headers["Content-Type"] # => "text/plain"
|
||||||
def headers
|
def headers
|
||||||
@headers ||= Http::Headers.new(@env)
|
@headers ||= Http::Headers.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a +String+ with the last requested path including their params.
|
# Returns a +String+ with the last requested path including their params.
|
||||||
@ -156,7 +189,7 @@ def headers
|
|||||||
# # get '/foo?bar'
|
# # get '/foo?bar'
|
||||||
# request.original_fullpath # => '/foo?bar'
|
# request.original_fullpath # => '/foo?bar'
|
||||||
def original_fullpath
|
def original_fullpath
|
||||||
@original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
|
@original_fullpath ||= (get_header("ORIGINAL_FULLPATH") || fullpath)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the +String+ full path including params of the last URL requested.
|
# Returns the +String+ full path including params of the last URL requested.
|
||||||
@ -195,7 +228,7 @@ def content_length
|
|||||||
# (case-insensitive), which may need to be manually added depending on the
|
# (case-insensitive), which may need to be manually added depending on the
|
||||||
# choice of JavaScript libraries and frameworks.
|
# choice of JavaScript libraries and frameworks.
|
||||||
def xml_http_request?
|
def xml_http_request?
|
||||||
@env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
|
get_header('HTTP_X_REQUESTED_WITH') =~ /XMLHttpRequest/i
|
||||||
end
|
end
|
||||||
alias :xhr? :xml_http_request?
|
alias :xhr? :xml_http_request?
|
||||||
|
|
||||||
@ -207,7 +240,11 @@ def ip
|
|||||||
# Returns the IP address of client as a +String+,
|
# Returns the IP address of client as a +String+,
|
||||||
# usually set by the RemoteIp middleware.
|
# usually set by the RemoteIp middleware.
|
||||||
def remote_ip
|
def remote_ip
|
||||||
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
|
@remote_ip ||= (get_header("action_dispatch.remote_ip") || ip).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_ip=(remote_ip)
|
||||||
|
set_header "action_dispatch.remote_ip".freeze, remote_ip
|
||||||
end
|
end
|
||||||
|
|
||||||
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
|
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
|
||||||
@ -219,43 +256,39 @@ def remote_ip
|
|||||||
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
|
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
|
||||||
# This relies on the rack variable set by the ActionDispatch::RequestId middleware.
|
# This relies on the rack variable set by the ActionDispatch::RequestId middleware.
|
||||||
def request_id
|
def request_id
|
||||||
env[ACTION_DISPATCH_REQUEST_ID]
|
get_header ACTION_DISPATCH_REQUEST_ID
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_id=(id) # :nodoc:
|
def request_id=(id) # :nodoc:
|
||||||
env[ACTION_DISPATCH_REQUEST_ID] = id
|
set_header ACTION_DISPATCH_REQUEST_ID, id
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :uuid, :request_id
|
alias_method :uuid, :request_id
|
||||||
|
|
||||||
def x_request_id # :nodoc:
|
|
||||||
@env[HTTP_X_REQUEST_ID]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the lowercase name of the HTTP server software.
|
# Returns the lowercase name of the HTTP server software.
|
||||||
def server_software
|
def server_software
|
||||||
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
|
(get_header('SERVER_SOFTWARE') && /^([a-zA-Z]+)/ =~ get_header('SERVER_SOFTWARE')) ? $1.downcase : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Read the request \body. This is useful for web services that need to
|
# Read the request \body. This is useful for web services that need to
|
||||||
# work with raw requests directly.
|
# work with raw requests directly.
|
||||||
def raw_post
|
def raw_post
|
||||||
unless @env.include? 'RAW_POST_DATA'
|
unless has_header? 'RAW_POST_DATA'
|
||||||
raw_post_body = body
|
raw_post_body = body
|
||||||
@env['RAW_POST_DATA'] = raw_post_body.read(content_length)
|
set_header('RAW_POST_DATA', raw_post_body.read(content_length))
|
||||||
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
|
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
|
||||||
end
|
end
|
||||||
@env['RAW_POST_DATA']
|
get_header 'RAW_POST_DATA'
|
||||||
end
|
end
|
||||||
|
|
||||||
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
||||||
# variable is already set, wrap it in a StringIO.
|
# variable is already set, wrap it in a StringIO.
|
||||||
def body
|
def body
|
||||||
if raw_post = @env['RAW_POST_DATA']
|
if raw_post = get_header('RAW_POST_DATA')
|
||||||
raw_post.force_encoding(Encoding::BINARY)
|
raw_post.force_encoding(Encoding::BINARY)
|
||||||
StringIO.new(raw_post)
|
StringIO.new(raw_post)
|
||||||
else
|
else
|
||||||
@env['rack.input']
|
body_stream
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -266,7 +299,7 @@ def form_data?
|
|||||||
end
|
end
|
||||||
|
|
||||||
def body_stream #:nodoc:
|
def body_stream #:nodoc:
|
||||||
@env['rack.input']
|
get_header('rack.input')
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO This should be broken apart into AD::Request::Session and probably
|
# TODO This should be broken apart into AD::Request::Session and probably
|
||||||
@ -277,20 +310,20 @@ def reset_session
|
|||||||
else
|
else
|
||||||
self.session = {}
|
self.session = {}
|
||||||
end
|
end
|
||||||
@env['action_dispatch.request.flash_hash'] = nil
|
set_header('action_dispatch.request.flash_hash', nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def session=(session) #:nodoc:
|
def session=(session) #:nodoc:
|
||||||
Session.set @env, session
|
Session.set self, session
|
||||||
end
|
end
|
||||||
|
|
||||||
def session_options=(options)
|
def session_options=(options)
|
||||||
Session::Options.set @env, options
|
Session::Options.set self, options
|
||||||
end
|
end
|
||||||
|
|
||||||
# Override Rack's GET method to support indifferent access
|
# Override Rack's GET method to support indifferent access
|
||||||
def GET
|
def GET
|
||||||
@env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
|
@env["action_dispatch.request.query_parameters"] ||= normalize_encode_params(super || {})
|
||||||
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
||||||
raise ActionController::BadRequest.new(:query, e)
|
raise ActionController::BadRequest.new(:query, e)
|
||||||
end
|
end
|
||||||
@ -298,7 +331,7 @@ def GET
|
|||||||
|
|
||||||
# Override Rack's POST method to support indifferent access
|
# Override Rack's POST method to support indifferent access
|
||||||
def POST
|
def POST
|
||||||
@env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
|
@env["action_dispatch.request.request_parameters"] ||= normalize_encode_params(super || {})
|
||||||
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
||||||
raise ActionController::BadRequest.new(:request, e)
|
raise ActionController::BadRequest.new(:request, e)
|
||||||
end
|
end
|
||||||
@ -307,10 +340,10 @@ def POST
|
|||||||
# Returns the authorization header regardless of whether it was specified directly or through one of the
|
# Returns the authorization header regardless of whether it was specified directly or through one of the
|
||||||
# proxy alternatives.
|
# proxy alternatives.
|
||||||
def authorization
|
def authorization
|
||||||
@env['HTTP_AUTHORIZATION'] ||
|
get_header('HTTP_AUTHORIZATION') ||
|
||||||
@env['X-HTTP_AUTHORIZATION'] ||
|
get_header('X-HTTP_AUTHORIZATION') ||
|
||||||
@env['X_HTTP_AUTHORIZATION'] ||
|
get_header('X_HTTP_AUTHORIZATION') ||
|
||||||
@env['REDIRECT_X_HTTP_AUTHORIZATION']
|
get_header('REDIRECT_X_HTTP_AUTHORIZATION')
|
||||||
end
|
end
|
||||||
|
|
||||||
# True if the request came from localhost, 127.0.0.1.
|
# True if the request came from localhost, 127.0.0.1.
|
||||||
@ -318,10 +351,13 @@ def local?
|
|||||||
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
|
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
def request_parameters=(params)
|
||||||
def parse_query(*)
|
set_header("action_dispatch.request.request_parameters".freeze, params)
|
||||||
Utils.deep_munge(super)
|
end
|
||||||
end
|
|
||||||
|
def logger
|
||||||
|
get_header("action_dispatch.logger".freeze)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def check_method(name)
|
def check_method(name)
|
||||||
|
@ -80,11 +80,21 @@ def initialize(response, buf)
|
|||||||
@response = response
|
@response = response
|
||||||
@buf = buf
|
@buf = buf
|
||||||
@closed = false
|
@closed = false
|
||||||
|
@str_body = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
@str_body ||= begin
|
||||||
|
buf = ''
|
||||||
|
each { |chunk| buf << chunk }
|
||||||
|
buf
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def write(string)
|
def write(string)
|
||||||
raise IOError, "closed stream" if closed?
|
raise IOError, "closed stream" if closed?
|
||||||
|
|
||||||
|
@str_body = nil
|
||||||
@response.commit!
|
@response.commit!
|
||||||
@buf.push string
|
@buf.push string
|
||||||
end
|
end
|
||||||
@ -187,13 +197,13 @@ def content_type=(content_type)
|
|||||||
@content_type = content_type.to_s
|
@content_type = content_type.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets the HTTP character set.
|
# Sets the HTTP character set. In case of nil parameter
|
||||||
|
# it sets the charset to utf-8.
|
||||||
|
#
|
||||||
|
# response.charset = 'utf-16' # => 'utf-16'
|
||||||
|
# response.charset = nil # => 'utf-8'
|
||||||
def charset=(charset)
|
def charset=(charset)
|
||||||
if nil == charset
|
@charset = charset.nil? ? self.class.default_charset : charset
|
||||||
@charset = self.class.default_charset
|
|
||||||
else
|
|
||||||
@charset = charset
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# The response code of the request.
|
# The response code of the request.
|
||||||
@ -222,9 +232,7 @@ def message
|
|||||||
# Returns the content of the response as a string. This contains the contents
|
# Returns the content of the response as a string. This contains the contents
|
||||||
# of any calls to <tt>render</tt>.
|
# of any calls to <tt>render</tt>.
|
||||||
def body
|
def body
|
||||||
strings = []
|
@stream.body
|
||||||
each { |part| strings << part.to_s }
|
|
||||||
strings.join
|
|
||||||
end
|
end
|
||||||
|
|
||||||
EMPTY = " "
|
EMPTY = " "
|
||||||
|
@ -28,7 +28,13 @@ def initialize(hash) # :nodoc:
|
|||||||
raise(ArgumentError, ':tempfile is required') unless @tempfile
|
raise(ArgumentError, ':tempfile is required') unless @tempfile
|
||||||
|
|
||||||
@original_filename = hash[:filename]
|
@original_filename = hash[:filename]
|
||||||
@original_filename &&= @original_filename.encode "UTF-8"
|
if @original_filename
|
||||||
|
begin
|
||||||
|
@original_filename.encode!(Encoding::UTF_8)
|
||||||
|
rescue EncodingError
|
||||||
|
@original_filename.force_encoding(Encoding::UTF_8)
|
||||||
|
end
|
||||||
|
end
|
||||||
@content_type = hash[:type]
|
@content_type = hash[:type]
|
||||||
@headers = hash[:head]
|
@headers = hash[:head]
|
||||||
end
|
end
|
||||||
|
@ -245,7 +245,7 @@ def raw_host_with_port
|
|||||||
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
||||||
# req.host # => "example.com"
|
# req.host # => "example.com"
|
||||||
def host
|
def host
|
||||||
raw_host_with_port.sub(/:\d+$/, '')
|
raw_host_with_port.sub(/:\d+$/, ''.freeze)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a \host:\port string for this request, such as "example.com" or
|
# Returns a \host:\port string for this request, such as "example.com" or
|
||||||
|
@ -14,7 +14,7 @@ def initialize(routes)
|
|||||||
|
|
||||||
def generate(name, options, path_parameters, parameterize = nil)
|
def generate(name, options, path_parameters, parameterize = nil)
|
||||||
constraints = path_parameters.merge(options)
|
constraints = path_parameters.merge(options)
|
||||||
missing_keys = []
|
missing_keys = nil # need for variable scope
|
||||||
|
|
||||||
match_route(name, constraints) do |route|
|
match_route(name, constraints) do |route|
|
||||||
parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
|
parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
|
||||||
@ -25,22 +25,22 @@ def generate(name, options, path_parameters, parameterize = nil)
|
|||||||
next unless name || route.dispatcher?
|
next unless name || route.dispatcher?
|
||||||
|
|
||||||
missing_keys = missing_keys(route, parameterized_parts)
|
missing_keys = missing_keys(route, parameterized_parts)
|
||||||
next unless missing_keys.empty?
|
next if missing_keys && !missing_keys.empty?
|
||||||
params = options.dup.delete_if do |key, _|
|
params = options.dup.delete_if do |key, _|
|
||||||
parameterized_parts.key?(key) || route.defaults.key?(key)
|
parameterized_parts.key?(key) || route.defaults.key?(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
defaults = route.defaults
|
defaults = route.defaults
|
||||||
required_parts = route.required_parts
|
required_parts = route.required_parts
|
||||||
parameterized_parts.delete_if do |key, value|
|
parameterized_parts.keep_if do |key, value|
|
||||||
value.to_s == defaults[key].to_s && !required_parts.include?(key)
|
defaults[key].nil? || value.to_s != defaults[key].to_s || required_parts.include?(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
return [route.format(parameterized_parts), params]
|
return [route.format(parameterized_parts), params]
|
||||||
end
|
end
|
||||||
|
|
||||||
message = "No route matches #{Hash[constraints.sort_by{|k,v| k.to_s}].inspect}"
|
message = "No route matches #{Hash[constraints.sort_by{|k,v| k.to_s}].inspect}"
|
||||||
message << " missing required keys: #{missing_keys.sort.inspect}" unless missing_keys.empty?
|
message << " missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
|
||||||
|
|
||||||
raise ActionController::UrlGenerationError, message
|
raise ActionController::UrlGenerationError, message
|
||||||
end
|
end
|
||||||
@ -54,12 +54,12 @@ def clear
|
|||||||
def extract_parameterized_parts(route, options, recall, parameterize = nil)
|
def extract_parameterized_parts(route, options, recall, parameterize = nil)
|
||||||
parameterized_parts = recall.merge(options)
|
parameterized_parts = recall.merge(options)
|
||||||
|
|
||||||
keys_to_keep = route.parts.reverse.drop_while { |part|
|
keys_to_keep = route.parts.reverse_each.drop_while { |part|
|
||||||
!options.key?(part) || (options[part] || recall[part]).nil?
|
!options.key?(part) || (options[part] || recall[part]).nil?
|
||||||
} | route.required_parts
|
} | route.required_parts
|
||||||
|
|
||||||
(parameterized_parts.keys - keys_to_keep).each do |bad_key|
|
parameterized_parts.delete_if do |bad_key, _|
|
||||||
parameterized_parts.delete(bad_key)
|
!keys_to_keep.include?(bad_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
if parameterize
|
if parameterize
|
||||||
@ -110,15 +110,36 @@ def non_recursive(cache, options)
|
|||||||
routes
|
routes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module RegexCaseComparator
|
||||||
|
DEFAULT_INPUT = /[-_.a-zA-Z0-9]+\/[-_.a-zA-Z0-9]+/
|
||||||
|
DEFAULT_REGEX = /\A#{DEFAULT_INPUT}\Z/
|
||||||
|
|
||||||
|
def self.===(regex)
|
||||||
|
DEFAULT_INPUT == regex
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Returns an array populated with missing keys if any are present.
|
# Returns an array populated with missing keys if any are present.
|
||||||
def missing_keys(route, parts)
|
def missing_keys(route, parts)
|
||||||
missing_keys = []
|
missing_keys = nil
|
||||||
tests = route.path.requirements
|
tests = route.path.requirements
|
||||||
route.required_parts.each { |key|
|
route.required_parts.each { |key|
|
||||||
if tests.key?(key)
|
case tests[key]
|
||||||
missing_keys << key unless /\A#{tests[key]}\Z/ === parts[key]
|
when nil
|
||||||
|
unless parts[key]
|
||||||
|
missing_keys ||= []
|
||||||
|
missing_keys << key
|
||||||
|
end
|
||||||
|
when RegexCaseComparator
|
||||||
|
unless RegexCaseComparator::DEFAULT_REGEX === parts[key]
|
||||||
|
missing_keys ||= []
|
||||||
|
missing_keys << key
|
||||||
|
end
|
||||||
else
|
else
|
||||||
missing_keys << key unless parts[key]
|
unless /\A#{tests[key]}\Z/ === parts[key]
|
||||||
|
missing_keys ||= []
|
||||||
|
missing_keys << key
|
||||||
|
end
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
missing_keys
|
missing_keys
|
||||||
@ -134,7 +155,7 @@ def possibles(cache, options, depth = 0)
|
|||||||
|
|
||||||
def build_cache
|
def build_cache
|
||||||
root = { ___routes: [] }
|
root = { ___routes: [] }
|
||||||
routes.each_with_index do |route, i|
|
routes.routes.each_with_index do |route, i|
|
||||||
leaf = route.required_defaults.inject(root) do |h, tuple|
|
leaf = route.required_defaults.inject(root) do |h, tuple|
|
||||||
h[tuple] ||= {}
|
h[tuple] ||= {}
|
||||||
end
|
end
|
||||||
|
@ -14,15 +14,15 @@ def initialize(left)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def each(&block)
|
def each(&block)
|
||||||
Visitors::Each.new(block).accept(self)
|
Visitors::Each::INSTANCE.accept(self, block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
Visitors::String.new.accept(self)
|
Visitors::String::INSTANCE.accept(self, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_dot
|
def to_dot
|
||||||
Visitors::Dot.new.accept(self)
|
Visitors::Dot::INSTANCE.accept(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_sym
|
def to_sym
|
||||||
@ -30,7 +30,7 @@ def to_sym
|
|||||||
end
|
end
|
||||||
|
|
||||||
def name
|
def name
|
||||||
left.tr '*:', ''
|
left.tr '*:'.freeze, ''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def type
|
def type
|
||||||
@ -39,10 +39,14 @@ def type
|
|||||||
|
|
||||||
def symbol?; false; end
|
def symbol?; false; end
|
||||||
def literal?; false; end
|
def literal?; false; end
|
||||||
|
def terminal?; false; end
|
||||||
|
def star?; false; end
|
||||||
|
def cat?; false; end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Terminal < Node # :nodoc:
|
class Terminal < Node # :nodoc:
|
||||||
alias :symbol :left
|
alias :symbol :left
|
||||||
|
def terminal?; true; end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Literal < Terminal # :nodoc:
|
class Literal < Terminal # :nodoc:
|
||||||
@ -69,11 +73,13 @@ def type; :#{t.upcase}; end
|
|||||||
class Symbol < Terminal # :nodoc:
|
class Symbol < Terminal # :nodoc:
|
||||||
attr_accessor :regexp
|
attr_accessor :regexp
|
||||||
alias :symbol :regexp
|
alias :symbol :regexp
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
DEFAULT_EXP = /[^\.\/\?]+/
|
DEFAULT_EXP = /[^\.\/\?]+/
|
||||||
def initialize(left)
|
def initialize(left)
|
||||||
super
|
super
|
||||||
@regexp = DEFAULT_EXP
|
@regexp = DEFAULT_EXP
|
||||||
|
@name = left.tr '*:'.freeze, ''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_regexp?
|
def default_regexp?
|
||||||
@ -92,6 +98,7 @@ def type; :GROUP; end
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Star < Unary # :nodoc:
|
class Star < Unary # :nodoc:
|
||||||
|
def star?; true; end
|
||||||
def type; :STAR; end
|
def type; :STAR; end
|
||||||
|
|
||||||
def name
|
def name
|
||||||
@ -111,6 +118,7 @@ def children; [left, right] end
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Cat < Binary # :nodoc:
|
class Cat < Binary # :nodoc:
|
||||||
|
def cat?; true; end
|
||||||
def type; :CAT; end
|
def type; :CAT; end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,6 +6,10 @@ module Journey # :nodoc:
|
|||||||
class Parser < Racc::Parser # :nodoc:
|
class Parser < Racc::Parser # :nodoc:
|
||||||
include Journey::Nodes
|
include Journey::Nodes
|
||||||
|
|
||||||
|
def self.parse(string)
|
||||||
|
new.parse string
|
||||||
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@scanner = Scanner.new
|
@scanner = Scanner.new
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
require 'action_dispatch/journey/router/strexp'
|
|
||||||
|
|
||||||
module ActionDispatch
|
module ActionDispatch
|
||||||
module Journey # :nodoc:
|
module Journey # :nodoc:
|
||||||
module Path # :nodoc:
|
module Path # :nodoc:
|
||||||
@ -7,14 +5,20 @@ class Pattern # :nodoc:
|
|||||||
attr_reader :spec, :requirements, :anchored
|
attr_reader :spec, :requirements, :anchored
|
||||||
|
|
||||||
def self.from_string string
|
def self.from_string string
|
||||||
new Journey::Router::Strexp.build(string, {}, ["/.?"], true)
|
build(string, {}, "/.?", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(strexp)
|
def self.build(path, requirements, separators, anchored)
|
||||||
@spec = strexp.ast
|
parser = Journey::Parser.new
|
||||||
@requirements = strexp.requirements
|
ast = parser.parse path
|
||||||
@separators = strexp.separators.join
|
new ast, requirements, separators, anchored
|
||||||
@anchored = strexp.anchor
|
end
|
||||||
|
|
||||||
|
def initialize(ast, requirements, separators, anchored)
|
||||||
|
@spec = ast
|
||||||
|
@requirements = requirements
|
||||||
|
@separators = separators
|
||||||
|
@anchored = anchored
|
||||||
|
|
||||||
@names = nil
|
@names = nil
|
||||||
@optional_names = nil
|
@optional_names = nil
|
||||||
@ -28,12 +32,12 @@ def build_formatter
|
|||||||
end
|
end
|
||||||
|
|
||||||
def ast
|
def ast
|
||||||
@spec.grep(Nodes::Symbol).each do |node|
|
@spec.find_all(&:symbol?).each do |node|
|
||||||
re = @requirements[node.to_sym]
|
re = @requirements[node.to_sym]
|
||||||
node.regexp = re if re
|
node.regexp = re if re
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec.grep(Nodes::Star).each do |node|
|
@spec.find_all(&:star?).each do |node|
|
||||||
node = node.left
|
node = node.left
|
||||||
node.regexp = @requirements[node.to_sym] || /(.+)/
|
node.regexp = @requirements[node.to_sym] || /(.+)/
|
||||||
end
|
end
|
||||||
@ -55,31 +59,6 @@ def optional_names
|
|||||||
}.map(&:name).uniq
|
}.map(&:name).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
|
|
||||||
attr_reader :offsets
|
|
||||||
|
|
||||||
def initialize(matchers)
|
|
||||||
@matchers = matchers
|
|
||||||
@capture_count = [0]
|
|
||||||
end
|
|
||||||
|
|
||||||
def visit(node)
|
|
||||||
super
|
|
||||||
@capture_count
|
|
||||||
end
|
|
||||||
|
|
||||||
def visit_SYMBOL(node)
|
|
||||||
node = node.to_sym
|
|
||||||
|
|
||||||
if @matchers.key?(node)
|
|
||||||
re = /#{@matchers[node]}|/
|
|
||||||
@capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
|
|
||||||
else
|
|
||||||
@capture_count << (@capture_count.last || 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
|
class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
|
||||||
def initialize(separator, matchers)
|
def initialize(separator, matchers)
|
||||||
@separator = separator
|
@separator = separator
|
||||||
@ -189,8 +168,20 @@ def regexp_visitor
|
|||||||
def offsets
|
def offsets
|
||||||
return @offsets if @offsets
|
return @offsets if @offsets
|
||||||
|
|
||||||
viz = RegexpOffsets.new(@requirements)
|
@offsets = [0]
|
||||||
@offsets = viz.accept(spec)
|
|
||||||
|
spec.find_all(&:symbol?).each do |node|
|
||||||
|
node = node.to_sym
|
||||||
|
|
||||||
|
if @requirements.key?(node)
|
||||||
|
re = /#{@requirements[node]}|/
|
||||||
|
@offsets.push((re.match('').length - 1) + @offsets.last)
|
||||||
|
else
|
||||||
|
@offsets << @offsets.last
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@offsets
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,36 +1,81 @@
|
|||||||
module ActionDispatch
|
module ActionDispatch
|
||||||
module Journey # :nodoc:
|
module Journey # :nodoc:
|
||||||
class Route # :nodoc:
|
class Route # :nodoc:
|
||||||
attr_reader :app, :path, :defaults, :name
|
attr_reader :app, :path, :defaults, :name, :precedence
|
||||||
|
|
||||||
attr_reader :constraints
|
attr_reader :constraints
|
||||||
alias :conditions :constraints
|
alias :conditions :constraints
|
||||||
|
|
||||||
attr_accessor :precedence
|
module VerbMatchers
|
||||||
|
VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
|
||||||
|
VERBS.each do |v|
|
||||||
|
class_eval <<-eoc
|
||||||
|
class #{v}
|
||||||
|
def self.verb; name.split("::").last; end
|
||||||
|
def self.call(req); req.#{v.downcase}?; end
|
||||||
|
end
|
||||||
|
eoc
|
||||||
|
end
|
||||||
|
|
||||||
|
class Unknown
|
||||||
|
attr_reader :verb
|
||||||
|
|
||||||
|
def initialize(verb)
|
||||||
|
@verb = verb
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(request); @verb === request.request_method; end
|
||||||
|
end
|
||||||
|
|
||||||
|
class All
|
||||||
|
def self.call(_); true; end
|
||||||
|
def self.verb; ''; end
|
||||||
|
end
|
||||||
|
|
||||||
|
VERB_TO_CLASS = VERBS.each_with_object({ :all => All }) do |verb, hash|
|
||||||
|
klass = const_get verb
|
||||||
|
hash[verb] = klass
|
||||||
|
hash[verb.downcase] = klass
|
||||||
|
hash[verb.downcase.to_sym] = klass
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.verb_matcher(verb)
|
||||||
|
VerbMatchers::VERB_TO_CLASS.fetch(verb) do
|
||||||
|
VerbMatchers::Unknown.new verb.to_s.dasherize.upcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.build(name, app, path, constraints, required_defaults, defaults)
|
||||||
|
request_method_match = verb_matcher(constraints.delete(:request_method))
|
||||||
|
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# +path+ is a path constraint.
|
# +path+ is a path constraint.
|
||||||
# +constraints+ is a hash of constraints to be applied to this route.
|
# +constraints+ is a hash of constraints to be applied to this route.
|
||||||
def initialize(name, app, path, constraints, required_defaults, defaults)
|
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence)
|
||||||
@name = name
|
@name = name
|
||||||
@app = app
|
@app = app
|
||||||
@path = path
|
@path = path
|
||||||
|
|
||||||
|
@request_method_match = request_method_match
|
||||||
@constraints = constraints
|
@constraints = constraints
|
||||||
@defaults = defaults
|
@defaults = defaults
|
||||||
@required_defaults = nil
|
@required_defaults = nil
|
||||||
@_required_defaults = required_defaults || []
|
@_required_defaults = required_defaults
|
||||||
@required_parts = nil
|
@required_parts = nil
|
||||||
@parts = nil
|
@parts = nil
|
||||||
@decorated_ast = nil
|
@decorated_ast = nil
|
||||||
@precedence = 0
|
@precedence = precedence
|
||||||
@path_formatter = @path.build_formatter
|
@path_formatter = @path.build_formatter
|
||||||
end
|
end
|
||||||
|
|
||||||
def ast
|
def ast
|
||||||
@decorated_ast ||= begin
|
@decorated_ast ||= begin
|
||||||
decorated_ast = path.ast
|
decorated_ast = path.ast
|
||||||
decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
|
decorated_ast.find_all(&:terminal?).each { |n| n.memo = self }
|
||||||
decorated_ast
|
decorated_ast
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -92,7 +137,8 @@ def dispatcher?
|
|||||||
end
|
end
|
||||||
|
|
||||||
def matches?(request)
|
def matches?(request)
|
||||||
constraints.all? do |method, value|
|
match_verb(request) &&
|
||||||
|
constraints.all? { |method, value|
|
||||||
case value
|
case value
|
||||||
when Regexp, String
|
when Regexp, String
|
||||||
value === request.send(method).to_s
|
value === request.send(method).to_s
|
||||||
@ -105,15 +151,28 @@ def matches?(request)
|
|||||||
else
|
else
|
||||||
value === request.send(method)
|
value === request.send(method)
|
||||||
end
|
end
|
||||||
end
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def ip
|
def ip
|
||||||
constraints[:ip] || //
|
constraints[:ip] || //
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def requires_matching_verb?
|
||||||
|
!@request_method_match.all? { |x| x == VerbMatchers::All }
|
||||||
|
end
|
||||||
|
|
||||||
def verb
|
def verb
|
||||||
constraints[:request_method] || //
|
%r[^#{verbs.join('|')}$]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def verbs
|
||||||
|
@request_method_match.map(&:verb)
|
||||||
|
end
|
||||||
|
|
||||||
|
def match_verb(request)
|
||||||
|
@request_method_match.any? { |m| m.call request }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
require 'action_dispatch/journey/router/utils'
|
require 'action_dispatch/journey/router/utils'
|
||||||
require 'action_dispatch/journey/router/strexp'
|
|
||||||
require 'action_dispatch/journey/routes'
|
require 'action_dispatch/journey/routes'
|
||||||
require 'action_dispatch/journey/formatter'
|
require 'action_dispatch/journey/formatter'
|
||||||
|
|
||||||
@ -102,7 +101,7 @@ def find_routes req
|
|||||||
}
|
}
|
||||||
|
|
||||||
routes =
|
routes =
|
||||||
if req.request_method == "HEAD"
|
if req.head?
|
||||||
match_head_routes(routes, req)
|
match_head_routes(routes, req)
|
||||||
else
|
else
|
||||||
match_routes(routes, req)
|
match_routes(routes, req)
|
||||||
@ -121,7 +120,7 @@ def find_routes req
|
|||||||
end
|
end
|
||||||
|
|
||||||
def match_head_routes(routes, req)
|
def match_head_routes(routes, req)
|
||||||
verb_specific_routes = routes.reject { |route| route.verb == // }
|
verb_specific_routes = routes.select(&:requires_matching_verb?)
|
||||||
head_routes = match_routes(verb_specific_routes, req)
|
head_routes = match_routes(verb_specific_routes, req)
|
||||||
|
|
||||||
if head_routes.empty?
|
if head_routes.empty?
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
module ActionDispatch
|
|
||||||
module Journey # :nodoc:
|
|
||||||
class Router # :nodoc:
|
|
||||||
class Strexp # :nodoc:
|
|
||||||
class << self
|
|
||||||
alias :compile :new
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :path, :requirements, :separators, :anchor, :ast
|
|
||||||
|
|
||||||
def self.build(path, requirements, separators, anchor = true)
|
|
||||||
parser = Journey::Parser.new
|
|
||||||
ast = parser.parse path
|
|
||||||
new ast, path, requirements, separators, anchor
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(ast, path, requirements, separators, anchor = true)
|
|
||||||
@ast = ast
|
|
||||||
@path = path
|
|
||||||
@requirements = requirements
|
|
||||||
@separators = separators
|
|
||||||
@anchor = anchor
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -14,10 +14,10 @@ class Utils # :nodoc:
|
|||||||
# normalize_path("/%ab") # => "/%AB"
|
# normalize_path("/%ab") # => "/%AB"
|
||||||
def self.normalize_path(path)
|
def self.normalize_path(path)
|
||||||
path = "/#{path}"
|
path = "/#{path}"
|
||||||
path.squeeze!('/')
|
path.squeeze!('/'.freeze)
|
||||||
path.sub!(%r{/+\Z}, '')
|
path.sub!(%r{/+\Z}, ''.freeze)
|
||||||
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
||||||
path = '/' if path == ''
|
path = '/' if path == ''.freeze
|
||||||
path
|
path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -5,11 +5,10 @@ module Journey # :nodoc:
|
|||||||
class Routes # :nodoc:
|
class Routes # :nodoc:
|
||||||
include Enumerable
|
include Enumerable
|
||||||
|
|
||||||
attr_reader :routes, :named_routes, :custom_routes, :anchored_routes
|
attr_reader :routes, :custom_routes, :anchored_routes
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@routes = []
|
@routes = []
|
||||||
@named_routes = {}
|
|
||||||
@ast = nil
|
@ast = nil
|
||||||
@anchored_routes = []
|
@anchored_routes = []
|
||||||
@custom_routes = []
|
@custom_routes = []
|
||||||
@ -37,7 +36,6 @@ def clear
|
|||||||
routes.clear
|
routes.clear
|
||||||
anchored_routes.clear
|
anchored_routes.clear
|
||||||
custom_routes.clear
|
custom_routes.clear
|
||||||
named_routes.clear
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def partition_route(route)
|
def partition_route(route)
|
||||||
@ -62,13 +60,9 @@ def simulator
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a route to the routing table.
|
def add_route(name, mapping)
|
||||||
def add_route(app, path, conditions, required_defaults, defaults, name = nil)
|
route = mapping.make_route name, routes.length
|
||||||
route = Route.new(name, app, path, conditions, required_defaults, defaults)
|
|
||||||
|
|
||||||
route.precedence = routes.length
|
|
||||||
routes << route
|
routes << route
|
||||||
named_routes[name] = route if name && !named_routes[name]
|
|
||||||
partition_route(route)
|
partition_route(route)
|
||||||
clear_cache!
|
clear_cache!
|
||||||
route
|
route
|
||||||
|
@ -92,6 +92,45 @@ def visit_DOT(n); terminal(n); end
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class FunctionalVisitor # :nodoc:
|
||||||
|
DISPATCH_CACHE = {}
|
||||||
|
|
||||||
|
def accept(node, seed)
|
||||||
|
visit(node, seed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def visit node, seed
|
||||||
|
send(DISPATCH_CACHE[node.type], node, seed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def binary(node, seed)
|
||||||
|
visit(node.right, visit(node.left, seed))
|
||||||
|
end
|
||||||
|
def visit_CAT(n, seed); binary(n, seed); end
|
||||||
|
|
||||||
|
def nary(node, seed)
|
||||||
|
node.children.inject(seed) { |s, c| visit(c, s) }
|
||||||
|
end
|
||||||
|
def visit_OR(n, seed); nary(n, seed); end
|
||||||
|
|
||||||
|
def unary(node, seed)
|
||||||
|
visit(node.left, seed)
|
||||||
|
end
|
||||||
|
def visit_GROUP(n, seed); unary(n, seed); end
|
||||||
|
def visit_STAR(n, seed); unary(n, seed); end
|
||||||
|
|
||||||
|
def terminal(node, seed); seed; end
|
||||||
|
def visit_LITERAL(n, seed); terminal(n, seed); end
|
||||||
|
def visit_SYMBOL(n, seed); terminal(n, seed); end
|
||||||
|
def visit_SLASH(n, seed); terminal(n, seed); end
|
||||||
|
def visit_DOT(n, seed); terminal(n, seed); end
|
||||||
|
|
||||||
|
instance_methods(false).each do |pim|
|
||||||
|
next unless pim =~ /^visit_(.*)$/
|
||||||
|
DISPATCH_CACHE[$1.to_sym] = pim
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class FormatBuilder < Visitor # :nodoc:
|
class FormatBuilder < Visitor # :nodoc:
|
||||||
def accept(node); Journey::Format.new(super); end
|
def accept(node); Journey::Format.new(super); end
|
||||||
def terminal(node); [node.left]; end
|
def terminal(node); [node.left]; end
|
||||||
@ -117,104 +156,110 @@ def visit_SYMBOL(n)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Loop through the requirements AST
|
# Loop through the requirements AST
|
||||||
class Each < Visitor # :nodoc:
|
class Each < FunctionalVisitor # :nodoc:
|
||||||
attr_reader :block
|
def visit(node, block)
|
||||||
|
|
||||||
def initialize(block)
|
|
||||||
@block = block
|
|
||||||
end
|
|
||||||
|
|
||||||
def visit(node)
|
|
||||||
block.call(node)
|
block.call(node)
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
|
INSTANCE = new
|
||||||
end
|
end
|
||||||
|
|
||||||
class String < Visitor # :nodoc:
|
class String < FunctionalVisitor # :nodoc:
|
||||||
private
|
private
|
||||||
|
|
||||||
def binary(node)
|
def binary(node, seed)
|
||||||
[visit(node.left), visit(node.right)].join
|
visit(node.right, visit(node.left, seed))
|
||||||
end
|
end
|
||||||
|
|
||||||
def nary(node)
|
def nary(node, seed)
|
||||||
node.children.map { |c| visit(c) }.join '|'
|
last_child = node.children.last
|
||||||
|
node.children.inject(seed) { |s, c|
|
||||||
|
string = visit(c, s)
|
||||||
|
string << "|".freeze unless last_child == c
|
||||||
|
string
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def terminal(node)
|
def terminal(node, seed)
|
||||||
node.left
|
seed + node.left
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_GROUP(node)
|
def visit_GROUP(node, seed)
|
||||||
"(#{visit(node.left)})"
|
visit(node.left, seed << "(".freeze) << ")".freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
|
INSTANCE = new
|
||||||
end
|
end
|
||||||
|
|
||||||
class Dot < Visitor # :nodoc:
|
class Dot < FunctionalVisitor # :nodoc:
|
||||||
def initialize
|
def initialize
|
||||||
@nodes = []
|
@nodes = []
|
||||||
@edges = []
|
@edges = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def accept(node)
|
def accept(node, seed = [[], []])
|
||||||
super
|
super
|
||||||
|
nodes, edges = seed
|
||||||
<<-eodot
|
<<-eodot
|
||||||
digraph parse_tree {
|
digraph parse_tree {
|
||||||
size="8,5"
|
size="8,5"
|
||||||
node [shape = none];
|
node [shape = none];
|
||||||
edge [dir = none];
|
edge [dir = none];
|
||||||
#{@nodes.join "\n"}
|
#{nodes.join "\n"}
|
||||||
#{@edges.join("\n")}
|
#{edges.join("\n")}
|
||||||
}
|
}
|
||||||
eodot
|
eodot
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def binary(node)
|
def binary(node, seed)
|
||||||
node.children.each do |c|
|
seed.last.concat node.children.map { |c|
|
||||||
@edges << "#{node.object_id} -> #{c.object_id};"
|
"#{node.object_id} -> #{c.object_id};"
|
||||||
end
|
}
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def nary(node)
|
def nary(node, seed)
|
||||||
node.children.each do |c|
|
seed.last.concat node.children.map { |c|
|
||||||
@edges << "#{node.object_id} -> #{c.object_id};"
|
"#{node.object_id} -> #{c.object_id};"
|
||||||
end
|
}
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def unary(node)
|
def unary(node, seed)
|
||||||
@edges << "#{node.object_id} -> #{node.left.object_id};"
|
seed.last << "#{node.object_id} -> #{node.left.object_id};"
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_GROUP(node)
|
def visit_GROUP(node, seed)
|
||||||
@nodes << "#{node.object_id} [label=\"()\"];"
|
seed.first << "#{node.object_id} [label=\"()\"];"
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_CAT(node)
|
def visit_CAT(node, seed)
|
||||||
@nodes << "#{node.object_id} [label=\"○\"];"
|
seed.first << "#{node.object_id} [label=\"○\"];"
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_STAR(node)
|
def visit_STAR(node, seed)
|
||||||
@nodes << "#{node.object_id} [label=\"*\"];"
|
seed.first << "#{node.object_id} [label=\"*\"];"
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_OR(node)
|
def visit_OR(node, seed)
|
||||||
@nodes << "#{node.object_id} [label=\"|\"];"
|
seed.first << "#{node.object_id} [label=\"|\"];"
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def terminal(node)
|
def terminal(node, seed)
|
||||||
value = node.left
|
value = node.left
|
||||||
|
|
||||||
@nodes << "#{node.object_id} [label=\"#{value}\"];"
|
seed.first << "#{node.object_id} [label=\"#{value}\"];"
|
||||||
|
seed
|
||||||
end
|
end
|
||||||
|
INSTANCE = new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -8,8 +8,52 @@
|
|||||||
module ActionDispatch
|
module ActionDispatch
|
||||||
class Request < Rack::Request
|
class Request < Rack::Request
|
||||||
def cookie_jar
|
def cookie_jar
|
||||||
env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(env, host, ssl?, cookies)
|
get_header('action_dispatch.cookies'.freeze) do |k|
|
||||||
|
self.cookie_jar = Cookies::CookieJar.build(self, cookies)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :stopdoc:
|
||||||
|
def have_cookie_jar?
|
||||||
|
has_header? 'action_dispatch.cookies'.freeze
|
||||||
|
end
|
||||||
|
|
||||||
|
def cookie_jar=(jar)
|
||||||
|
set_header 'action_dispatch.cookies'.freeze, jar
|
||||||
|
end
|
||||||
|
|
||||||
|
def key_generator
|
||||||
|
get_header Cookies::GENERATOR_KEY
|
||||||
|
end
|
||||||
|
|
||||||
|
def signed_cookie_salt
|
||||||
|
get_header Cookies::SIGNED_COOKIE_SALT
|
||||||
|
end
|
||||||
|
|
||||||
|
def encrypted_cookie_salt
|
||||||
|
get_header Cookies::ENCRYPTED_COOKIE_SALT
|
||||||
|
end
|
||||||
|
|
||||||
|
def encrypted_signed_cookie_salt
|
||||||
|
get_header Cookies::ENCRYPTED_SIGNED_COOKIE_SALT
|
||||||
|
end
|
||||||
|
|
||||||
|
def secret_token
|
||||||
|
get_header Cookies::SECRET_TOKEN
|
||||||
|
end
|
||||||
|
|
||||||
|
def secret_key_base
|
||||||
|
get_header Cookies::SECRET_KEY_BASE
|
||||||
|
end
|
||||||
|
|
||||||
|
def cookies_serializer
|
||||||
|
get_header Cookies::COOKIES_SERIALIZER
|
||||||
|
end
|
||||||
|
|
||||||
|
def cookies_digest
|
||||||
|
get_header Cookies::COOKIES_DIGEST
|
||||||
|
end
|
||||||
|
# :startdoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
# \Cookies are read and written through ActionController#cookies.
|
# \Cookies are read and written through ActionController#cookies.
|
||||||
@ -118,7 +162,7 @@ module ChainedCookieJars
|
|||||||
# cookies.permanent.signed[:remember_me] = current_user.id
|
# cookies.permanent.signed[:remember_me] = current_user.id
|
||||||
# # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
|
# # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
|
||||||
def permanent
|
def permanent
|
||||||
@permanent ||= PermanentCookieJar.new(self, @key_generator, @options)
|
@permanent ||= PermanentCookieJar.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
|
# Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
|
||||||
@ -138,10 +182,10 @@ def permanent
|
|||||||
# cookies.signed[:discount] # => 45
|
# cookies.signed[:discount] # => 45
|
||||||
def signed
|
def signed
|
||||||
@signed ||=
|
@signed ||=
|
||||||
if @options[:upgrade_legacy_signed_cookies]
|
if upgrade_legacy_signed_cookies?
|
||||||
UpgradeLegacySignedCookieJar.new(self, @key_generator, @options)
|
UpgradeLegacySignedCookieJar.new(self)
|
||||||
else
|
else
|
||||||
SignedCookieJar.new(self, @key_generator, @options)
|
SignedCookieJar.new(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -161,10 +205,10 @@ def signed
|
|||||||
# cookies.encrypted[:discount] # => 45
|
# cookies.encrypted[:discount] # => 45
|
||||||
def encrypted
|
def encrypted
|
||||||
@encrypted ||=
|
@encrypted ||=
|
||||||
if @options[:upgrade_legacy_signed_cookies]
|
if upgrade_legacy_signed_cookies?
|
||||||
UpgradeLegacyEncryptedCookieJar.new(self, @key_generator, @options)
|
UpgradeLegacyEncryptedCookieJar.new(self)
|
||||||
else
|
else
|
||||||
EncryptedCookieJar.new(self, @key_generator, @options)
|
EncryptedCookieJar.new(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -172,12 +216,26 @@ def encrypted
|
|||||||
# Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores.
|
# Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores.
|
||||||
def signed_or_encrypted
|
def signed_or_encrypted
|
||||||
@signed_or_encrypted ||=
|
@signed_or_encrypted ||=
|
||||||
if @options[:secret_key_base].present?
|
if request.secret_key_base.present?
|
||||||
encrypted
|
encrypted
|
||||||
else
|
else
|
||||||
signed
|
signed
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def request; @parent_jar.request; end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def upgrade_legacy_signed_cookies?
|
||||||
|
request.secret_token.present? && request.secret_key_base.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def key_generator
|
||||||
|
request.key_generator
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream
|
# Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream
|
||||||
@ -187,7 +245,7 @@ def signed_or_encrypted
|
|||||||
module VerifyAndUpgradeLegacySignedMessage # :nodoc:
|
module VerifyAndUpgradeLegacySignedMessage # :nodoc:
|
||||||
def initialize(*args)
|
def initialize(*args)
|
||||||
super
|
super
|
||||||
@legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
@legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_and_upgrade_legacy_signed_message(name, signed_message)
|
def verify_and_upgrade_legacy_signed_message(name, signed_message)
|
||||||
@ -216,34 +274,18 @@ class CookieJar #:nodoc:
|
|||||||
# $& => example.local
|
# $& => example.local
|
||||||
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
||||||
|
|
||||||
def self.options_for_env(env) #:nodoc:
|
def self.build(req, cookies)
|
||||||
{ signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '',
|
new(req).tap do |hash|
|
||||||
encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '',
|
|
||||||
encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '',
|
|
||||||
secret_token: env[SECRET_TOKEN],
|
|
||||||
secret_key_base: env[SECRET_KEY_BASE],
|
|
||||||
upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?,
|
|
||||||
serializer: env[COOKIES_SERIALIZER],
|
|
||||||
digest: env[COOKIES_DIGEST]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.build(env, host, secure, cookies)
|
|
||||||
key_generator = env[GENERATOR_KEY]
|
|
||||||
options = options_for_env env
|
|
||||||
|
|
||||||
new(key_generator, host, secure, options).tap do |hash|
|
|
||||||
hash.update(cookies)
|
hash.update(cookies)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(key_generator, host = nil, secure = false, options = {})
|
attr_reader :request
|
||||||
@key_generator = key_generator
|
|
||||||
|
def initialize(request)
|
||||||
@set_cookies = {}
|
@set_cookies = {}
|
||||||
@delete_cookies = {}
|
@delete_cookies = {}
|
||||||
@host = host
|
@request = request
|
||||||
@secure = secure
|
|
||||||
@options = options
|
|
||||||
@cookies = {}
|
@cookies = {}
|
||||||
@committed = false
|
@committed = false
|
||||||
end
|
end
|
||||||
@ -292,12 +334,12 @@ def handle_options(options) #:nodoc:
|
|||||||
|
|
||||||
# if host is not ip and matches domain regexp
|
# if host is not ip and matches domain regexp
|
||||||
# (ip confirms to domain regexp so we explicitly check for ip)
|
# (ip confirms to domain regexp so we explicitly check for ip)
|
||||||
options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ domain_regexp)
|
options[:domain] = if (request.host !~ /^[\d.]+$/) && (request.host =~ domain_regexp)
|
||||||
".#{$&}"
|
".#{$&}"
|
||||||
end
|
end
|
||||||
elsif options[:domain].is_a? Array
|
elsif options[:domain].is_a? Array
|
||||||
# if host matches one of the supplied domains without a dot in front of it
|
# if host matches one of the supplied domains without a dot in front of it
|
||||||
options[:domain] = options[:domain].find {|domain| @host.include? domain.sub(/^\./, '') }
|
options[:domain] = options[:domain].find {|domain| request.host.include? domain.sub(/^\./, '') }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -356,27 +398,20 @@ def write(headers)
|
|||||||
@delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
|
@delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def recycle! #:nodoc:
|
|
||||||
@set_cookies = {}
|
|
||||||
@delete_cookies = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
mattr_accessor :always_write_cookie
|
mattr_accessor :always_write_cookie
|
||||||
self.always_write_cookie = false
|
self.always_write_cookie = false
|
||||||
|
|
||||||
private
|
private
|
||||||
def write_cookie?(cookie)
|
def write_cookie?(cookie)
|
||||||
@secure || !cookie[:secure] || always_write_cookie
|
request.ssl? || !cookie[:secure] || always_write_cookie
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class PermanentCookieJar #:nodoc:
|
class PermanentCookieJar #:nodoc:
|
||||||
include ChainedCookieJars
|
include ChainedCookieJars
|
||||||
|
|
||||||
def initialize(parent_jar, key_generator, options = {})
|
def initialize(parent_jar)
|
||||||
@parent_jar = parent_jar
|
@parent_jar = parent_jar
|
||||||
@key_generator = key_generator
|
|
||||||
@options = options
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](name)
|
def [](name)
|
||||||
@ -410,7 +445,7 @@ module SerializedCookieJars # :nodoc:
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
def needs_migration?(value)
|
def needs_migration?(value)
|
||||||
@options[:serializer] == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
|
request.cookies_serializer == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize(value)
|
def serialize(value)
|
||||||
@ -430,7 +465,7 @@ def deserialize(name, value)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def serializer
|
def serializer
|
||||||
serializer = @options[:serializer] || :marshal
|
serializer = request.cookies_serializer || :marshal
|
||||||
case serializer
|
case serializer
|
||||||
when :marshal
|
when :marshal
|
||||||
Marshal
|
Marshal
|
||||||
@ -442,7 +477,7 @@ def serializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def digest
|
def digest
|
||||||
@options[:digest] || 'SHA1'
|
request.cookies_digest || 'SHA1'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -450,10 +485,9 @@ class SignedCookieJar #:nodoc:
|
|||||||
include ChainedCookieJars
|
include ChainedCookieJars
|
||||||
include SerializedCookieJars
|
include SerializedCookieJars
|
||||||
|
|
||||||
def initialize(parent_jar, key_generator, options = {})
|
def initialize(parent_jar)
|
||||||
@parent_jar = parent_jar
|
@parent_jar = parent_jar
|
||||||
@options = options
|
secret = key_generator.generate_key(request.signed_cookie_salt)
|
||||||
secret = key_generator.generate_key(@options[:signed_cookie_salt])
|
|
||||||
@verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
@verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -505,16 +539,16 @@ class EncryptedCookieJar #:nodoc:
|
|||||||
include ChainedCookieJars
|
include ChainedCookieJars
|
||||||
include SerializedCookieJars
|
include SerializedCookieJars
|
||||||
|
|
||||||
def initialize(parent_jar, key_generator, options = {})
|
def initialize(parent_jar)
|
||||||
|
@parent_jar = parent_jar
|
||||||
|
|
||||||
if ActiveSupport::LegacyKeyGenerator === key_generator
|
if ActiveSupport::LegacyKeyGenerator === key_generator
|
||||||
raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " +
|
raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " +
|
||||||
"Read the upgrade documentation to learn more about this new config option."
|
"Read the upgrade documentation to learn more about this new config option."
|
||||||
end
|
end
|
||||||
|
|
||||||
@parent_jar = parent_jar
|
secret = key_generator.generate_key(request.encrypted_cookie_salt || '')
|
||||||
@options = options
|
sign_secret = key_generator.generate_key(request.encrypted_signed_cookie_salt || '')
|
||||||
secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
|
|
||||||
sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
|
|
||||||
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -568,9 +602,12 @@ def initialize(app)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
|
request = ActionDispatch::Request.new env
|
||||||
|
|
||||||
status, headers, body = @app.call(env)
|
status, headers, body = @app.call(env)
|
||||||
|
|
||||||
if cookie_jar = env['action_dispatch.cookies']
|
if request.have_cookie_jar?
|
||||||
|
cookie_jar = request.cookie_jar
|
||||||
unless cookie_jar.committed?
|
unless cookie_jar.committed?
|
||||||
cookie_jar.write(headers)
|
cookie_jar.write(headers)
|
||||||
if headers[HTTP_HEADER].respond_to?(:join)
|
if headers[HTTP_HEADER].respond_to?(:join)
|
||||||
|
@ -44,6 +44,7 @@ def initialize(app, routes_app = nil)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
|
request = ActionDispatch::Request.new env
|
||||||
_, headers, body = response = @app.call(env)
|
_, headers, body = response = @app.call(env)
|
||||||
|
|
||||||
if headers['X-Cascade'] == 'pass'
|
if headers['X-Cascade'] == 'pass'
|
||||||
@ -53,18 +54,18 @@ def call(env)
|
|||||||
|
|
||||||
response
|
response
|
||||||
rescue Exception => exception
|
rescue Exception => exception
|
||||||
raise exception if env['action_dispatch.show_exceptions'] == false
|
raise exception unless request.show_exceptions?
|
||||||
render_exception(env, exception)
|
render_exception(request, exception)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def render_exception(env, exception)
|
def render_exception(request, exception)
|
||||||
wrapper = ExceptionWrapper.new(env, exception)
|
backtrace_cleaner = request.get_header('action_dispatch.backtrace_cleaner')
|
||||||
log_error(env, wrapper)
|
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
||||||
|
log_error(request, wrapper)
|
||||||
|
|
||||||
if env['action_dispatch.show_detailed_exceptions']
|
if request.get_header('action_dispatch.show_detailed_exceptions')
|
||||||
request = Request.new(env)
|
|
||||||
traces = wrapper.traces
|
traces = wrapper.traces
|
||||||
|
|
||||||
trace_to_show = 'Application Trace'
|
trace_to_show = 'Application Trace'
|
||||||
@ -106,8 +107,8 @@ def render(status, body, format)
|
|||||||
[status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
|
[status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
|
||||||
end
|
end
|
||||||
|
|
||||||
def log_error(env, wrapper)
|
def log_error(request, wrapper)
|
||||||
logger = logger(env)
|
logger = logger(request)
|
||||||
return unless logger
|
return unless logger
|
||||||
|
|
||||||
exception = wrapper.exception
|
exception = wrapper.exception
|
||||||
@ -123,8 +124,8 @@ def log_error(env, wrapper)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def logger(env)
|
def logger(request)
|
||||||
env['action_dispatch.logger'] || stderr_logger
|
request.logger || stderr_logger
|
||||||
end
|
end
|
||||||
|
|
||||||
def stderr_logger
|
def stderr_logger
|
||||||
|
@ -31,10 +31,10 @@ class ExceptionWrapper
|
|||||||
'ActionView::Template::Error' => 'template_error'
|
'ActionView::Template::Error' => 'template_error'
|
||||||
)
|
)
|
||||||
|
|
||||||
attr_reader :env, :exception, :line_number, :file
|
attr_reader :backtrace_cleaner, :exception, :line_number, :file
|
||||||
|
|
||||||
def initialize(env, exception)
|
def initialize(backtrace_cleaner, exception)
|
||||||
@env = env
|
@backtrace_cleaner = backtrace_cleaner
|
||||||
@exception = original_exception(exception)
|
@exception = original_exception(exception)
|
||||||
|
|
||||||
expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError)
|
expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError)
|
||||||
@ -125,10 +125,6 @@ def clean_backtrace(*args)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def backtrace_cleaner
|
|
||||||
@backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
|
|
||||||
end
|
|
||||||
|
|
||||||
def source_fragment(path, line)
|
def source_fragment(path, line)
|
||||||
return unless Rails.respond_to?(:root) && Rails.root
|
return unless Rails.respond_to?(:root) && Rails.root
|
||||||
full_path = Rails.root.join(path)
|
full_path = Rails.root.join(path)
|
||||||
|
@ -6,7 +6,17 @@ class Request < Rack::Request
|
|||||||
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
|
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
|
||||||
# to put a new one.
|
# to put a new one.
|
||||||
def flash
|
def flash
|
||||||
@env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
|
flash = flash_hash
|
||||||
|
return flash if flash
|
||||||
|
self.flash = Flash::FlashHash.from_session_value(session["flash"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def flash=(flash)
|
||||||
|
set_header Flash::KEY, flash
|
||||||
|
end
|
||||||
|
|
||||||
|
def flash_hash # :nodoc:
|
||||||
|
get_header Flash::KEY
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -263,14 +273,15 @@ def initialize(app)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
|
req = ActionDispatch::Request.new env
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
ensure
|
ensure
|
||||||
session = Request::Session.find(env) || {}
|
session = Request::Session.find(req) || {}
|
||||||
flash_hash = env[KEY]
|
flash_hash = req.flash_hash
|
||||||
|
|
||||||
if flash_hash && (flash_hash.present? || session.key?('flash'))
|
if flash_hash && (flash_hash.present? || session.key?('flash'))
|
||||||
session["flash"] = flash_hash.to_session_value
|
session["flash"] = flash_hash.to_session_value
|
||||||
env[KEY] = flash_hash.dup
|
req.flash = flash_hash.dup
|
||||||
end
|
end
|
||||||
|
|
||||||
if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
|
if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
require 'active_support/core_ext/hash/conversions'
|
|
||||||
require 'action_dispatch/http/request'
|
require 'action_dispatch/http/request'
|
||||||
require 'active_support/core_ext/hash/indifferent_access'
|
|
||||||
|
|
||||||
module ActionDispatch
|
module ActionDispatch
|
||||||
|
# ActionDispatch::ParamsParser works for all the requests having any Content-Length
|
||||||
|
# (like POST). It takes raw data from the request and puts it through the parser
|
||||||
|
# that is picked based on Content-Type header.
|
||||||
|
#
|
||||||
|
# In case of any error while parsing data ParamsParser::ParseError is raised.
|
||||||
class ParamsParser
|
class ParamsParser
|
||||||
|
# Raised when raw data from the request cannot be parsed by the parser
|
||||||
|
# defined for request's content mime type.
|
||||||
class ParseError < StandardError
|
class ParseError < StandardError
|
||||||
attr_reader :original_exception
|
attr_reader :original_exception
|
||||||
|
|
||||||
@ -17,39 +22,42 @@ def initialize(message, original_exception)
|
|||||||
Mime::JSON => lambda { |raw_post|
|
Mime::JSON => lambda { |raw_post|
|
||||||
data = ActiveSupport::JSON.decode(raw_post)
|
data = ActiveSupport::JSON.decode(raw_post)
|
||||||
data = {:_json => data} unless data.is_a?(Hash)
|
data = {:_json => data} unless data.is_a?(Hash)
|
||||||
Request::Utils.deep_munge(data).with_indifferent_access
|
Request::Utils.normalize_encode_params(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Create a new +ParamsParser+ middleware instance.
|
||||||
|
#
|
||||||
|
# The +parsers+ argument can take Hash of parsers where key is identifying
|
||||||
|
# content mime type, and value is a lambda that is going to process data.
|
||||||
def initialize(app, parsers = {})
|
def initialize(app, parsers = {})
|
||||||
@app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
|
@app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
default = env["action_dispatch.request.request_parameters"]
|
request = Request.new(env)
|
||||||
env["action_dispatch.request.request_parameters"] = parse_formatted_parameters(env, @parsers, default)
|
|
||||||
|
request.request_parameters = parse_formatted_parameters(request, @parsers)
|
||||||
|
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def parse_formatted_parameters(env, parsers, default)
|
def parse_formatted_parameters(request, parsers)
|
||||||
request = Request.new(env)
|
return if request.content_length.zero?
|
||||||
|
|
||||||
return default if request.content_length.zero?
|
strategy = parsers.fetch(request.content_mime_type) { return nil }
|
||||||
|
|
||||||
strategy = parsers.fetch(request.content_mime_type) { return default }
|
|
||||||
|
|
||||||
strategy.call(request.raw_post)
|
strategy.call(request.raw_post)
|
||||||
|
|
||||||
rescue => e # JSON or Ruby code block errors
|
rescue => e # JSON or Ruby code block errors
|
||||||
logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
|
logger(request).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
|
||||||
|
|
||||||
raise ParseError.new(e.message, e)
|
raise ParseError.new(e.message, e)
|
||||||
end
|
end
|
||||||
|
|
||||||
def logger(env)
|
def logger(request)
|
||||||
env['action_dispatch.logger'] || ActiveSupport::Logger.new($stderr)
|
request.logger || ActiveSupport::Logger.new($stderr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,8 +17,8 @@ def initialize(public_path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
status = env["PATH_INFO"][1..-1].to_i
|
|
||||||
request = ActionDispatch::Request.new(env)
|
request = ActionDispatch::Request.new(env)
|
||||||
|
status = request.path_info[1..-1].to_i
|
||||||
content_type = request.formats.first
|
content_type = request.formats.first
|
||||||
body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
|
body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
|
||||||
|
|
||||||
|
@ -74,16 +74,17 @@ def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
|
|||||||
# requests. For those requests that do need to know the IP, the
|
# requests. For those requests that do need to know the IP, the
|
||||||
# GetIp#calculate_ip method will calculate the memoized client IP address.
|
# GetIp#calculate_ip method will calculate the memoized client IP address.
|
||||||
def call(env)
|
def call(env)
|
||||||
env["action_dispatch.remote_ip"] = GetIp.new(env, check_ip, proxies)
|
req = ActionDispatch::Request.new env
|
||||||
@app.call(env)
|
req.remote_ip = GetIp.new(req, check_ip, proxies)
|
||||||
|
@app.call(req.env)
|
||||||
end
|
end
|
||||||
|
|
||||||
# The GetIp class exists as a way to defer processing of the request data
|
# The GetIp class exists as a way to defer processing of the request data
|
||||||
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
|
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
|
||||||
# is called, this class will calculate the value and then memoize it.
|
# is called, this class will calculate the value and then memoize it.
|
||||||
class GetIp
|
class GetIp
|
||||||
def initialize(env, check_ip, proxies)
|
def initialize(req, check_ip, proxies)
|
||||||
@env = env
|
@req = req
|
||||||
@check_ip = check_ip
|
@check_ip = check_ip
|
||||||
@proxies = proxies
|
@proxies = proxies
|
||||||
end
|
end
|
||||||
@ -108,11 +109,11 @@ def initialize(env, check_ip, proxies)
|
|||||||
# the last address left, which was presumably set by one of those proxies.
|
# the last address left, which was presumably set by one of those proxies.
|
||||||
def calculate_ip
|
def calculate_ip
|
||||||
# Set by the Rack web server, this is a single value.
|
# Set by the Rack web server, this is a single value.
|
||||||
remote_addr = ips_from('REMOTE_ADDR').last
|
remote_addr = ips_from(@req.remote_addr).last
|
||||||
|
|
||||||
# Could be a CSV list and/or repeated headers that were concatenated.
|
# Could be a CSV list and/or repeated headers that were concatenated.
|
||||||
client_ips = ips_from('HTTP_CLIENT_IP').reverse
|
client_ips = ips_from(@req.client_ip).reverse
|
||||||
forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR').reverse
|
forwarded_ips = ips_from(@req.x_forwarded_for).reverse
|
||||||
|
|
||||||
# +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
|
# +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
|
||||||
# If they are both set, it means that this request passed through two
|
# If they are both set, it means that this request passed through two
|
||||||
@ -123,8 +124,8 @@ def calculate_ip
|
|||||||
if should_check_ip && !forwarded_ips.include?(client_ips.last)
|
if should_check_ip && !forwarded_ips.include?(client_ips.last)
|
||||||
# We don't know which came from the proxy, and which from the user
|
# We don't know which came from the proxy, and which from the user
|
||||||
raise IpSpoofAttackError, "IP spoofing attack?! " +
|
raise IpSpoofAttackError, "IP spoofing attack?! " +
|
||||||
"HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} " +
|
"HTTP_CLIENT_IP=#{@req.client_ip.inspect} " +
|
||||||
"HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
|
"HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# We assume these things about the IP headers:
|
# We assume these things about the IP headers:
|
||||||
@ -147,8 +148,9 @@ def to_s
|
|||||||
protected
|
protected
|
||||||
|
|
||||||
def ips_from(header)
|
def ips_from(header)
|
||||||
|
return [] unless header
|
||||||
# Split the comma-separated list into an array of strings
|
# Split the comma-separated list into an array of strings
|
||||||
ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
|
ips = header.strip.split(/[,\s]+/)
|
||||||
ips.select do |ip|
|
ips.select do |ip|
|
||||||
begin
|
begin
|
||||||
# Only return IPs that are valid according to the IPAddr#new method
|
# Only return IPs that are valid according to the IPAddr#new method
|
||||||
|
@ -36,6 +36,11 @@ def initialize_sid
|
|||||||
@default_options.delete(:sidbits)
|
@default_options.delete(:sidbits)
|
||||||
@default_options.delete(:secure_random)
|
@default_options.delete(:secure_random)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def make_request(env)
|
||||||
|
ActionDispatch::Request.new env
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module StaleSessionCheck
|
module StaleSessionCheck
|
||||||
@ -65,8 +70,8 @@ def stale_session_check!
|
|||||||
end
|
end
|
||||||
|
|
||||||
module SessionObject # :nodoc:
|
module SessionObject # :nodoc:
|
||||||
def prepare_session(env)
|
def prepare_session(req)
|
||||||
Request::Session.create(self, env, @default_options)
|
Request::Session.create(self, req, @default_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def loaded_session?(session)
|
def loaded_session?(session)
|
||||||
@ -81,8 +86,7 @@ class AbstractStore < Rack::Session::Abstract::ID
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_cookie(env, session_id, cookie)
|
def set_cookie(request, session_id, cookie)
|
||||||
request = ActionDispatch::Request.new(env)
|
|
||||||
request.cookie_jar[key] = cookie
|
request.cookie_jar[key] = cookie
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -71,16 +71,16 @@ def initialize(app, options={})
|
|||||||
super(app, options.merge!(:cookie_only => true))
|
super(app, options.merge!(:cookie_only => true))
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_session(env, session_id, options)
|
def destroy_session(req, session_id, options)
|
||||||
new_sid = generate_sid unless options[:drop]
|
new_sid = generate_sid unless options[:drop]
|
||||||
# Reset hash and Assign the new session id
|
# Reset hash and Assign the new session id
|
||||||
env["action_dispatch.request.unsigned_session_cookie"] = new_sid ? { "session_id" => new_sid } : {}
|
req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid } : {})
|
||||||
new_sid
|
new_sid
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_session(env)
|
def load_session(req)
|
||||||
stale_session_check! do
|
stale_session_check! do
|
||||||
data = unpacked_cookie_data(env)
|
data = unpacked_cookie_data(req)
|
||||||
data = persistent_session_id!(data)
|
data = persistent_session_id!(data)
|
||||||
[data["session_id"], data]
|
[data["session_id"], data]
|
||||||
end
|
end
|
||||||
@ -88,20 +88,21 @@ def load_session(env)
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def extract_session_id(env)
|
def extract_session_id(req)
|
||||||
stale_session_check! do
|
stale_session_check! do
|
||||||
unpacked_cookie_data(env)["session_id"]
|
unpacked_cookie_data(req)["session_id"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unpacked_cookie_data(env)
|
def unpacked_cookie_data(req)
|
||||||
env["action_dispatch.request.unsigned_session_cookie"] ||= begin
|
req.get_header("action_dispatch.request.unsigned_session_cookie") do |k|
|
||||||
stale_session_check! do
|
v = stale_session_check! do
|
||||||
if data = get_cookie(env)
|
if data = get_cookie(req)
|
||||||
data.stringify_keys!
|
data.stringify_keys!
|
||||||
end
|
end
|
||||||
data || {}
|
data || {}
|
||||||
end
|
end
|
||||||
|
req.set_header k, v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -111,21 +112,20 @@ def persistent_session_id!(data, sid=nil)
|
|||||||
data
|
data
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_session(env, sid, session_data, options)
|
def set_session(req, sid, session_data, options)
|
||||||
session_data["session_id"] = sid
|
session_data["session_id"] = sid
|
||||||
session_data
|
session_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_cookie(env, session_id, cookie)
|
def set_cookie(request, session_id, cookie)
|
||||||
cookie_jar(env)[@key] = cookie
|
cookie_jar(request)[@key] = cookie
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_cookie(env)
|
def get_cookie(req)
|
||||||
cookie_jar(env)[@key]
|
cookie_jar(req)[@key]
|
||||||
end
|
end
|
||||||
|
|
||||||
def cookie_jar(env)
|
def cookie_jar(request)
|
||||||
request = ActionDispatch::Request.new(env)
|
|
||||||
request.cookie_jar.signed_or_encrypted
|
request.cookie_jar.signed_or_encrypted
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -27,24 +27,26 @@ def initialize(app, exceptions_app)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
|
request = ActionDispatch::Request.new env
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
rescue Exception => exception
|
rescue Exception => exception
|
||||||
if env['action_dispatch.show_exceptions'] == false
|
if request.show_exceptions?
|
||||||
raise exception
|
render_exception(request, exception)
|
||||||
else
|
else
|
||||||
render_exception(env, exception)
|
raise exception
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def render_exception(env, exception)
|
def render_exception(request, exception)
|
||||||
wrapper = ExceptionWrapper.new(env, exception)
|
backtrace_cleaner = request.get_header 'action_dispatch.backtrace_cleaner'
|
||||||
|
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
||||||
status = wrapper.status_code
|
status = wrapper.status_code
|
||||||
env["action_dispatch.exception"] = wrapper.exception
|
request.set_header "action_dispatch.exception", wrapper.exception
|
||||||
env["action_dispatch.original_path"] = env["PATH_INFO"]
|
request.set_header "action_dispatch.original_path", request.path_info
|
||||||
env["PATH_INFO"] = "/#{status}"
|
request.path_info = "/#{status}"
|
||||||
response = @exceptions_app.call(env)
|
response = @exceptions_app.call(request.env)
|
||||||
response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
|
response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
|
||||||
rescue Exception => failsafe_error
|
rescue Exception => failsafe_error
|
||||||
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
|
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
|
||||||
|
@ -4,36 +4,15 @@
|
|||||||
module ActionDispatch
|
module ActionDispatch
|
||||||
class MiddlewareStack
|
class MiddlewareStack
|
||||||
class Middleware
|
class Middleware
|
||||||
attr_reader :args, :block, :name, :classcache
|
attr_reader :args, :block, :klass
|
||||||
|
|
||||||
def initialize(klass_or_name, *args, &block)
|
def initialize(klass, args, block)
|
||||||
@klass = nil
|
@klass = klass
|
||||||
|
@args = args
|
||||||
if klass_or_name.respond_to?(:name)
|
@block = block
|
||||||
@klass = klass_or_name
|
|
||||||
@name = @klass.name
|
|
||||||
else
|
|
||||||
@name = klass_or_name.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
@classcache = ActiveSupport::Dependencies::Reference
|
|
||||||
@args, @block = args, block
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def klass
|
def name; klass.name; end
|
||||||
@klass || classcache[@name]
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(middleware)
|
|
||||||
case middleware
|
|
||||||
when Middleware
|
|
||||||
klass == middleware.klass
|
|
||||||
when Class
|
|
||||||
klass == middleware
|
|
||||||
else
|
|
||||||
normalize(@name) == normalize(middleware)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def inspect
|
def inspect
|
||||||
klass.to_s
|
klass.to_s
|
||||||
@ -42,12 +21,6 @@ def inspect
|
|||||||
def build(app)
|
def build(app)
|
||||||
klass.new(app, *args, &block)
|
klass.new(app, *args, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def normalize(object)
|
|
||||||
object.to_s.strip.sub(/^::/, '')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
include Enumerable
|
include Enumerable
|
||||||
@ -75,19 +48,17 @@ def [](i)
|
|||||||
middlewares[i]
|
middlewares[i]
|
||||||
end
|
end
|
||||||
|
|
||||||
def unshift(*args, &block)
|
def unshift(klass, *args, &block)
|
||||||
middleware = self.class::Middleware.new(*args, &block)
|
middlewares.unshift(build_middleware(klass, args, block))
|
||||||
middlewares.unshift(middleware)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize_copy(other)
|
def initialize_copy(other)
|
||||||
self.middlewares = other.middlewares.dup
|
self.middlewares = other.middlewares.dup
|
||||||
end
|
end
|
||||||
|
|
||||||
def insert(index, *args, &block)
|
def insert(index, klass, *args, &block)
|
||||||
index = assert_index(index, :before)
|
index = assert_index(index, :before)
|
||||||
middleware = self.class::Middleware.new(*args, &block)
|
middlewares.insert(index, build_middleware(klass, args, block))
|
||||||
middlewares.insert(index, middleware)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :insert_before, :insert
|
alias_method :insert_before, :insert
|
||||||
@ -104,26 +75,48 @@ def swap(target, *args, &block)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def delete(target)
|
def delete(target)
|
||||||
middlewares.delete target
|
target = get_class target
|
||||||
|
middlewares.delete_if { |m| m.klass == target }
|
||||||
end
|
end
|
||||||
|
|
||||||
def use(*args, &block)
|
def use(klass, *args, &block)
|
||||||
middleware = self.class::Middleware.new(*args, &block)
|
middlewares.push(build_middleware(klass, args, block))
|
||||||
middlewares.push(middleware)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build(app = nil, &block)
|
def build(app = Proc.new)
|
||||||
app ||= block
|
|
||||||
raise "MiddlewareStack#build requires an app" unless app
|
|
||||||
middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
|
middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def assert_index(index, where)
|
def assert_index(index, where)
|
||||||
i = index.is_a?(Integer) ? index : middlewares.index(index)
|
index = get_class index
|
||||||
|
i = index.is_a?(Integer) ? index : middlewares.index { |m| m.klass == index }
|
||||||
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
|
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
|
||||||
i
|
i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def get_class(klass)
|
||||||
|
if klass.is_a?(String) || klass.is_a?(Symbol)
|
||||||
|
classcache = ActiveSupport::Dependencies::Reference
|
||||||
|
converted_klass = classcache[klass.to_s]
|
||||||
|
ActiveSupport::Deprecation.warn <<-eowarn
|
||||||
|
Passing strings or symbols to the middleware builder is deprecated, please change
|
||||||
|
them to actual class references. For example:
|
||||||
|
|
||||||
|
"#{klass}" => #{converted_klass}
|
||||||
|
|
||||||
|
eowarn
|
||||||
|
converted_klass
|
||||||
|
else
|
||||||
|
klass
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_middleware(klass, args, block)
|
||||||
|
Middleware.new(get_class(klass), args, block)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -35,7 +35,7 @@ def match?(path)
|
|||||||
paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"]
|
paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"]
|
||||||
|
|
||||||
if match = paths.detect { |p|
|
if match = paths.detect { |p|
|
||||||
path = File.join(@root, p.force_encoding('UTF-8'))
|
path = File.join(@root, p.force_encoding('UTF-8'.freeze))
|
||||||
begin
|
begin
|
||||||
File.file?(path) && File.readable?(path)
|
File.file?(path) && File.readable?(path)
|
||||||
rescue SystemCallError
|
rescue SystemCallError
|
||||||
@ -48,26 +48,30 @@ def match?(path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
path = env['PATH_INFO']
|
serve ActionDispatch::Request.new env
|
||||||
|
end
|
||||||
|
|
||||||
|
def serve(request)
|
||||||
|
path = request.path_info
|
||||||
gzip_path = gzip_file_path(path)
|
gzip_path = gzip_file_path(path)
|
||||||
|
|
||||||
if gzip_path && gzip_encoding_accepted?(env)
|
if gzip_path && gzip_encoding_accepted?(request)
|
||||||
env['PATH_INFO'] = gzip_path
|
request.path_info = gzip_path
|
||||||
status, headers, body = @file_server.call(env)
|
status, headers, body = @file_server.call(request.env)
|
||||||
if status == 304
|
if status == 304
|
||||||
return [status, headers, body]
|
return [status, headers, body]
|
||||||
end
|
end
|
||||||
headers['Content-Encoding'] = 'gzip'
|
headers['Content-Encoding'] = 'gzip'
|
||||||
headers['Content-Type'] = content_type(path)
|
headers['Content-Type'] = content_type(path)
|
||||||
else
|
else
|
||||||
status, headers, body = @file_server.call(env)
|
status, headers, body = @file_server.call(request.env)
|
||||||
end
|
end
|
||||||
|
|
||||||
headers['Vary'] = 'Accept-Encoding' if gzip_path
|
headers['Vary'] = 'Accept-Encoding' if gzip_path
|
||||||
|
|
||||||
return [status, headers, body]
|
return [status, headers, body]
|
||||||
ensure
|
ensure
|
||||||
env['PATH_INFO'] = path
|
request.path_info = path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -76,11 +80,11 @@ def ext
|
|||||||
end
|
end
|
||||||
|
|
||||||
def content_type(path)
|
def content_type(path)
|
||||||
::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
|
::Rack::Mime.mime_type(::File.extname(path), 'text/plain'.freeze)
|
||||||
end
|
end
|
||||||
|
|
||||||
def gzip_encoding_accepted?(env)
|
def gzip_encoding_accepted?(request)
|
||||||
env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/i
|
request.accept_encoding =~ /\bgzip\b/i
|
||||||
end
|
end
|
||||||
|
|
||||||
def gzip_file_path(path)
|
def gzip_file_path(path)
|
||||||
@ -110,16 +114,17 @@ def initialize(app, path, cache_control = nil, index: 'index')
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
case env['REQUEST_METHOD']
|
req = ActionDispatch::Request.new env
|
||||||
when 'GET', 'HEAD'
|
|
||||||
path = env['PATH_INFO'].chomp('/')
|
if req.get? || req.head?
|
||||||
|
path = req.path_info.chomp('/'.freeze)
|
||||||
if match = @file_handler.match?(path)
|
if match = @file_handler.match?(path)
|
||||||
env['PATH_INFO'] = match
|
req.path_info = match
|
||||||
return @file_handler.call(env)
|
return @file_handler.serve(req)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@app.call(env)
|
@app.call(req.env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -4,38 +4,38 @@ module ActionDispatch
|
|||||||
class Request < Rack::Request
|
class Request < Rack::Request
|
||||||
# Session is responsible for lazily loading the session from store.
|
# Session is responsible for lazily loading the session from store.
|
||||||
class Session # :nodoc:
|
class Session # :nodoc:
|
||||||
ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc:
|
ENV_SESSION_KEY = Rack::RACK_SESSION # :nodoc:
|
||||||
ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc:
|
ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS # :nodoc:
|
||||||
|
|
||||||
# Singleton object used to determine if an optional param wasn't specified
|
# Singleton object used to determine if an optional param wasn't specified
|
||||||
Unspecified = Object.new
|
Unspecified = Object.new
|
||||||
|
|
||||||
# Creates a session hash, merging the properties of the previous session if any
|
# Creates a session hash, merging the properties of the previous session if any
|
||||||
def self.create(store, env, default_options)
|
def self.create(store, req, default_options)
|
||||||
session_was = find env
|
session_was = find req
|
||||||
session = Request::Session.new(store, env)
|
session = Request::Session.new(store, req)
|
||||||
session.merge! session_was if session_was
|
session.merge! session_was if session_was
|
||||||
|
|
||||||
set(env, session)
|
set(req, session)
|
||||||
Options.set(env, Request::Session::Options.new(store, default_options))
|
Options.set(req, Request::Session::Options.new(store, default_options))
|
||||||
session
|
session
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find(env)
|
def self.find(req)
|
||||||
env[ENV_SESSION_KEY]
|
req.get_header ENV_SESSION_KEY
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.set(env, session)
|
def self.set(req, session)
|
||||||
env[ENV_SESSION_KEY] = session
|
req.set_header ENV_SESSION_KEY, session
|
||||||
end
|
end
|
||||||
|
|
||||||
class Options #:nodoc:
|
class Options #:nodoc:
|
||||||
def self.set(env, options)
|
def self.set(req, options)
|
||||||
env[ENV_SESSION_OPTIONS_KEY] = options
|
req.set_header ENV_SESSION_OPTIONS_KEY, options
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find(env)
|
def self.find(req)
|
||||||
env[ENV_SESSION_OPTIONS_KEY]
|
req.get_header ENV_SESSION_OPTIONS_KEY
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(by, default_options)
|
def initialize(by, default_options)
|
||||||
@ -47,9 +47,9 @@ def [](key)
|
|||||||
@delegate[key]
|
@delegate[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
def id(env)
|
def id(req)
|
||||||
@delegate.fetch(:id) {
|
@delegate.fetch(:id) {
|
||||||
@by.send(:extract_session_id, env)
|
@by.send(:extract_session_id, req)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -58,26 +58,26 @@ def to_hash; @delegate.dup; end
|
|||||||
def values_at(*args); @delegate.values_at(*args); end
|
def values_at(*args); @delegate.values_at(*args); end
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(by, env)
|
def initialize(by, req)
|
||||||
@by = by
|
@by = by
|
||||||
@env = env
|
@req = req
|
||||||
@delegate = {}
|
@delegate = {}
|
||||||
@loaded = false
|
@loaded = false
|
||||||
@exists = nil # we haven't checked yet
|
@exists = nil # we haven't checked yet
|
||||||
end
|
end
|
||||||
|
|
||||||
def id
|
def id
|
||||||
options.id(@env)
|
options.id(@req)
|
||||||
end
|
end
|
||||||
|
|
||||||
def options
|
def options
|
||||||
Options.find @env
|
Options.find @req
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
clear
|
clear
|
||||||
options = self.options || {}
|
options = self.options || {}
|
||||||
@by.send(:destroy_session, @env, options.id(@env), options)
|
@by.send(:destroy_session, @req, options.id(@req), options)
|
||||||
|
|
||||||
# Load the new sid to be written with the response
|
# Load the new sid to be written with the response
|
||||||
@loaded = false
|
@loaded = false
|
||||||
@ -181,7 +181,7 @@ def inspect
|
|||||||
|
|
||||||
def exists?
|
def exists?
|
||||||
return @exists unless @exists.nil?
|
return @exists unless @exists.nil?
|
||||||
@exists = @by.send(:session_exists?, @env)
|
@exists = @by.send(:session_exists?, @req)
|
||||||
end
|
end
|
||||||
|
|
||||||
def loaded?
|
def loaded?
|
||||||
@ -209,7 +209,7 @@ def load_for_write!
|
|||||||
end
|
end
|
||||||
|
|
||||||
def load!
|
def load!
|
||||||
id, session = @by.load_session @env
|
id, session = @by.load_session @req
|
||||||
options[:id] = id
|
options[:id] = id
|
||||||
@delegate.replace(stringify_keys(session))
|
@delegate.replace(stringify_keys(session))
|
||||||
@loaded = true
|
@loaded = true
|
||||||
|
@ -5,24 +5,45 @@ class Utils # :nodoc:
|
|||||||
mattr_accessor :perform_deep_munge
|
mattr_accessor :perform_deep_munge
|
||||||
self.perform_deep_munge = true
|
self.perform_deep_munge = true
|
||||||
|
|
||||||
class << self
|
def self.normalize_encode_params(params)
|
||||||
# Remove nils from the params hash
|
if perform_deep_munge
|
||||||
def deep_munge(hash, keys = [])
|
NoNilParamEncoder.normalize_encode_params params
|
||||||
return hash unless perform_deep_munge
|
else
|
||||||
|
ParamEncoder.normalize_encode_params params
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
hash.each do |k, v|
|
class ParamEncoder # :nodoc:
|
||||||
keys << k
|
# Convert nested Hash to HashWithIndifferentAccess.
|
||||||
case v
|
#
|
||||||
when Array
|
def self.normalize_encode_params(params)
|
||||||
v.grep(Hash) { |x| deep_munge(x, keys) }
|
case params
|
||||||
v.compact!
|
when Array
|
||||||
when Hash
|
handle_array params
|
||||||
deep_munge(v, keys)
|
when Hash
|
||||||
|
if params.has_key?(:tempfile)
|
||||||
|
ActionDispatch::Http::UploadedFile.new(params)
|
||||||
|
else
|
||||||
|
params.each_with_object({}) do |(key, val), new_hash|
|
||||||
|
new_hash[key] = normalize_encode_params(val)
|
||||||
|
end.with_indifferent_access
|
||||||
end
|
end
|
||||||
keys.pop
|
else
|
||||||
|
params
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
hash
|
def self.handle_array(params)
|
||||||
|
params.map! { |el| normalize_encode_params(el) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove nils from the params hash
|
||||||
|
class NoNilParamEncoder < ParamEncoder # :nodoc:
|
||||||
|
def self.handle_array(params)
|
||||||
|
list = super
|
||||||
|
list.compact!
|
||||||
|
list
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -24,7 +24,7 @@ def call(env)
|
|||||||
def serve(req)
|
def serve(req)
|
||||||
req.check_path_parameters!
|
req.check_path_parameters!
|
||||||
uri = URI.parse(path(req.path_parameters, req))
|
uri = URI.parse(path(req.path_parameters, req))
|
||||||
|
|
||||||
unless uri.host
|
unless uri.host
|
||||||
if relative_path?(uri.path)
|
if relative_path?(uri.path)
|
||||||
uri.path = "#{req.script_name}/#{uri.path}"
|
uri.path = "#{req.script_name}/#{uri.path}"
|
||||||
@ -32,7 +32,7 @@ def serve(req)
|
|||||||
uri.path = req.script_name.empty? ? "/" : req.script_name
|
uri.path = req.script_name.empty? ? "/" : req.script_name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
uri.scheme ||= req.scheme
|
uri.scheme ||= req.scheme
|
||||||
uri.host ||= req.host
|
uri.host ||= req.host
|
||||||
uri.port ||= req.port unless req.standard_port?
|
uri.port ||= req.port unless req.standard_port?
|
||||||
@ -124,7 +124,7 @@ def path(params, request)
|
|||||||
url_options[:script_name] = request.script_name
|
url_options[:script_name] = request.script_name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ActionDispatch::Http::URL.url_for url_options
|
ActionDispatch::Http::URL.url_for url_options
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
require 'action_dispatch/journey'
|
require 'action_dispatch/journey'
|
||||||
require 'forwardable'
|
require 'forwardable'
|
||||||
require 'thread_safe'
|
|
||||||
require 'active_support/concern'
|
require 'active_support/concern'
|
||||||
require 'active_support/core_ext/object/to_query'
|
require 'active_support/core_ext/object/to_query'
|
||||||
require 'active_support/core_ext/hash/slice'
|
require 'active_support/core_ext/hash/slice'
|
||||||
@ -21,64 +20,35 @@ class RouteSet
|
|||||||
alias inspect to_s
|
alias inspect to_s
|
||||||
|
|
||||||
class Dispatcher < Routing::Endpoint
|
class Dispatcher < Routing::Endpoint
|
||||||
def initialize(defaults)
|
def initialize(raise_on_name_error)
|
||||||
@defaults = defaults
|
@raise_on_name_error = raise_on_name_error
|
||||||
@controller_class_names = ThreadSafe::Cache.new
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def dispatcher?; true; end
|
def dispatcher?; true; end
|
||||||
|
|
||||||
def serve(req)
|
def serve(req)
|
||||||
req.check_path_parameters!
|
|
||||||
params = req.path_parameters
|
params = req.path_parameters
|
||||||
|
controller = controller_reference(req) do
|
||||||
prepare_params!(params)
|
|
||||||
|
|
||||||
# Just raise undefined constant errors if a controller was specified as default.
|
|
||||||
unless controller = controller(params, @defaults.key?(:controller))
|
|
||||||
return [404, {'X-Cascade' => 'pass'}, []]
|
return [404, {'X-Cascade' => 'pass'}, []]
|
||||||
end
|
end
|
||||||
|
dispatch(controller, params[:action], req)
|
||||||
dispatch(controller, params[:action], req.env)
|
|
||||||
end
|
|
||||||
|
|
||||||
def prepare_params!(params)
|
|
||||||
normalize_controller!(params)
|
|
||||||
merge_default_action!(params)
|
|
||||||
end
|
|
||||||
|
|
||||||
# If this is a default_controller (i.e. a controller specified by the user)
|
|
||||||
# we should raise an error in case it's not found, because it usually means
|
|
||||||
# a user error. However, if the controller was retrieved through a dynamic
|
|
||||||
# segment, as in :controller(/:action), we should simply return nil and
|
|
||||||
# delegate the control back to Rack cascade. Besides, if this is not a default
|
|
||||||
# controller, it means we should respect the @scope[:module] parameter.
|
|
||||||
def controller(params, default_controller=true)
|
|
||||||
if params && params.key?(:controller)
|
|
||||||
controller_param = params[:controller]
|
|
||||||
controller_reference(controller_param)
|
|
||||||
end
|
|
||||||
rescue NameError => e
|
rescue NameError => e
|
||||||
raise ActionController::RoutingError, e.message, e.backtrace if default_controller
|
if @raise_on_name_error
|
||||||
|
raise ActionController::RoutingError, e.message, e.backtrace
|
||||||
|
else
|
||||||
|
return [404, {'X-Cascade' => 'pass'}, []]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
def controller_reference(req, &block)
|
||||||
|
req.controller_class(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def controller_reference(controller_param)
|
def dispatch(controller, action, req)
|
||||||
const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
|
controller.action(action).call(req.env)
|
||||||
ActiveSupport::Dependencies.constantize(const_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def dispatch(controller, action, env)
|
|
||||||
controller.action(action).call(env)
|
|
||||||
end
|
|
||||||
|
|
||||||
def normalize_controller!(params)
|
|
||||||
params[:controller] = params[:controller].underscore if params.key?(:controller)
|
|
||||||
end
|
|
||||||
|
|
||||||
def merge_default_action!(params)
|
|
||||||
params[:action] ||= 'index'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -88,6 +58,7 @@ def merge_default_action!(params)
|
|||||||
class NamedRouteCollection
|
class NamedRouteCollection
|
||||||
include Enumerable
|
include Enumerable
|
||||||
attr_reader :routes, :url_helpers_module, :path_helpers_module
|
attr_reader :routes, :url_helpers_module, :path_helpers_module
|
||||||
|
private :routes
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@routes = {}
|
@routes = {}
|
||||||
@ -142,6 +113,7 @@ def get(name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def key?(name)
|
def key?(name)
|
||||||
|
return unless name
|
||||||
routes.key? name.to_sym
|
routes.key? name.to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -267,9 +239,13 @@ def handle_positional_args(controller_options, inner_options, args, result, path
|
|||||||
path_params -= controller_options.keys
|
path_params -= controller_options.keys
|
||||||
path_params -= result.keys
|
path_params -= result.keys
|
||||||
end
|
end
|
||||||
path_params -= inner_options.keys
|
inner_options.each_key do |key|
|
||||||
path_params.take(args.size).each do |param|
|
path_params.delete(key)
|
||||||
result[param] = args.shift
|
end
|
||||||
|
|
||||||
|
args.each_with_index do |arg, index|
|
||||||
|
param = path_params[index]
|
||||||
|
result[param] = arg if param
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -309,7 +285,7 @@ def define_url_helper(mod, route, name, opts, route_key, url_strategy)
|
|||||||
|
|
||||||
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
|
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
|
||||||
attr_accessor :disable_clear_and_finalize, :resources_path_names
|
attr_accessor :disable_clear_and_finalize, :resources_path_names
|
||||||
attr_accessor :default_url_options
|
attr_accessor :default_url_options, :dispatcher_class
|
||||||
attr_reader :env_key
|
attr_reader :env_key
|
||||||
|
|
||||||
alias :routes :set
|
alias :routes :set
|
||||||
@ -351,7 +327,8 @@ def initialize(config = DEFAULT_CONFIG)
|
|||||||
|
|
||||||
@set = Journey::Routes.new
|
@set = Journey::Routes.new
|
||||||
@router = Journey::Router.new @set
|
@router = Journey::Router.new @set
|
||||||
@formatter = Journey::Formatter.new @set
|
@formatter = Journey::Formatter.new self
|
||||||
|
@dispatcher_class = Routing::RouteSet::Dispatcher
|
||||||
end
|
end
|
||||||
|
|
||||||
def relative_url_root
|
def relative_url_root
|
||||||
@ -409,8 +386,8 @@ def clear!
|
|||||||
@prepend.each { |blk| eval_block(blk) }
|
@prepend.each { |blk| eval_block(blk) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def dispatcher(defaults)
|
def dispatcher(raise_on_name_error)
|
||||||
Routing::RouteSet::Dispatcher.new(defaults)
|
dispatcher_class.new(raise_on_name_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
module MountedHelpers
|
module MountedHelpers
|
||||||
@ -508,7 +485,7 @@ def empty?
|
|||||||
routes.empty?
|
routes.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
|
def add_route(mapping, path_ast, name, anchor)
|
||||||
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
|
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
|
||||||
|
|
||||||
if name && named_routes[name]
|
if name && named_routes[name]
|
||||||
@ -519,74 +496,17 @@ def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil
|
|||||||
"http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
|
"http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
|
||||||
end
|
end
|
||||||
|
|
||||||
path = conditions.delete :path_info
|
route = @set.add_route(name, mapping)
|
||||||
ast = conditions.delete :parsed_path_info
|
|
||||||
required_defaults = conditions.delete :required_defaults
|
|
||||||
path = build_path(path, ast, requirements, anchor)
|
|
||||||
conditions = build_conditions(conditions)
|
|
||||||
|
|
||||||
route = @set.add_route(app, path, conditions, required_defaults, defaults, name)
|
|
||||||
named_routes[name] = route if name
|
named_routes[name] = route if name
|
||||||
route
|
route
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_path(path, ast, requirements, anchor)
|
|
||||||
strexp = Journey::Router::Strexp.new(
|
|
||||||
ast,
|
|
||||||
path,
|
|
||||||
requirements,
|
|
||||||
SEPARATORS,
|
|
||||||
anchor)
|
|
||||||
|
|
||||||
pattern = Journey::Path::Pattern.new(strexp)
|
|
||||||
|
|
||||||
builder = Journey::GTG::Builder.new pattern.spec
|
|
||||||
|
|
||||||
# Get all the symbol nodes followed by literals that are not the
|
|
||||||
# dummy node.
|
|
||||||
symbols = pattern.spec.grep(Journey::Nodes::Symbol).find_all { |n|
|
|
||||||
builder.followpos(n).first.literal?
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get all the symbol nodes preceded by literals.
|
|
||||||
symbols.concat pattern.spec.find_all(&:literal?).map { |n|
|
|
||||||
builder.followpos(n).first
|
|
||||||
}.find_all(&:symbol?)
|
|
||||||
|
|
||||||
symbols.each { |x|
|
|
||||||
x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern
|
|
||||||
end
|
|
||||||
private :build_path
|
|
||||||
|
|
||||||
def build_conditions(current_conditions)
|
|
||||||
conditions = current_conditions.dup
|
|
||||||
|
|
||||||
# Rack-Mount requires that :request_method be a regular expression.
|
|
||||||
# :request_method represents the HTTP verb that matches this route.
|
|
||||||
#
|
|
||||||
# Here we munge values before they get sent on to rack-mount.
|
|
||||||
verbs = conditions[:request_method] || []
|
|
||||||
unless verbs.empty?
|
|
||||||
conditions[:request_method] = %r[^#{verbs.join('|')}$]
|
|
||||||
end
|
|
||||||
|
|
||||||
conditions.keep_if do |k, _|
|
|
||||||
request_class.public_method_defined?(k)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
private :build_conditions
|
|
||||||
|
|
||||||
class Generator
|
class Generator
|
||||||
PARAMETERIZE = lambda do |name, value|
|
PARAMETERIZE = lambda do |name, value|
|
||||||
if name == :controller
|
if name == :controller
|
||||||
value
|
value
|
||||||
elsif value.is_a?(Array)
|
else
|
||||||
value.map(&:to_param).join('/')
|
value.to_param
|
||||||
elsif param = value.to_param
|
|
||||||
param
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -594,8 +514,8 @@ class Generator
|
|||||||
|
|
||||||
def initialize(named_route, options, recall, set)
|
def initialize(named_route, options, recall, set)
|
||||||
@named_route = named_route
|
@named_route = named_route
|
||||||
@options = options.dup
|
@options = options
|
||||||
@recall = recall.dup
|
@recall = recall
|
||||||
@set = set
|
@set = set
|
||||||
|
|
||||||
normalize_recall!
|
normalize_recall!
|
||||||
@ -617,7 +537,7 @@ def current_controller
|
|||||||
def use_recall_for(key)
|
def use_recall_for(key)
|
||||||
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
|
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
|
||||||
if !named_route_exists? || segment_keys.include?(key)
|
if !named_route_exists? || segment_keys.include?(key)
|
||||||
@options[key] = @recall.delete(key)
|
@options[key] = @recall[key]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -671,12 +591,18 @@ def use_relative_controller!
|
|||||||
|
|
||||||
# Remove leading slashes from controllers
|
# Remove leading slashes from controllers
|
||||||
def normalize_controller!
|
def normalize_controller!
|
||||||
@options[:controller] = controller.sub(%r{^/}, '') if controller
|
if controller
|
||||||
|
if controller.start_with?("/".freeze)
|
||||||
|
@options[:controller] = controller[1..-1]
|
||||||
|
else
|
||||||
|
@options[:controller] = controller
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Move 'index' action from options to recall
|
# Move 'index' action from options to recall
|
||||||
def normalize_action!
|
def normalize_action!
|
||||||
if @options[:action] == 'index'
|
if @options[:action] == 'index'.freeze
|
||||||
@recall[:action] = @options.delete(:action)
|
@recall[:action] = @options.delete(:action)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -803,14 +729,13 @@ def recognize_path(path, environment = {})
|
|||||||
req.path_parameters = old_params.merge params
|
req.path_parameters = old_params.merge params
|
||||||
app = route.app
|
app = route.app
|
||||||
if app.matches?(req) && app.dispatcher?
|
if app.matches?(req) && app.dispatcher?
|
||||||
dispatcher = app.app
|
begin
|
||||||
|
req.controller_class
|
||||||
if dispatcher.controller(params, false)
|
rescue NameError
|
||||||
dispatcher.prepare_params!(params)
|
|
||||||
return params
|
|
||||||
else
|
|
||||||
raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
|
raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return req.path_parameters
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -171,6 +171,10 @@ def url_for(options = nil)
|
|||||||
route_name = options.delete :use_route
|
route_name = options.delete :use_route
|
||||||
_routes.url_for(options.symbolize_keys.reverse_merge!(url_options),
|
_routes.url_for(options.symbolize_keys.reverse_merge!(url_options),
|
||||||
route_name)
|
route_name)
|
||||||
|
when ActionController::Parameters
|
||||||
|
route_name = options.delete :use_route
|
||||||
|
_routes.url_for(options.to_unsafe_h.symbolize_keys.
|
||||||
|
reverse_merge!(url_options), route_name)
|
||||||
when String
|
when String
|
||||||
options
|
options
|
||||||
when Symbol
|
when Symbol
|
||||||
|
@ -3,6 +3,13 @@ module ActionDispatch
|
|||||||
module Assertions
|
module Assertions
|
||||||
# A small suite of assertions that test responses from \Rails applications.
|
# A small suite of assertions that test responses from \Rails applications.
|
||||||
module ResponseAssertions
|
module ResponseAssertions
|
||||||
|
RESPONSE_PREDICATES = { # :nodoc:
|
||||||
|
success: :successful?,
|
||||||
|
missing: :not_found?,
|
||||||
|
redirect: :redirection?,
|
||||||
|
error: :server_error?,
|
||||||
|
}
|
||||||
|
|
||||||
# Asserts that the response is one of the following types:
|
# Asserts that the response is one of the following types:
|
||||||
#
|
#
|
||||||
# * <tt>:success</tt> - Status code was in the 200-299 range
|
# * <tt>:success</tt> - Status code was in the 200-299 range
|
||||||
@ -20,11 +27,9 @@ module ResponseAssertions
|
|||||||
# # assert that the response code was status code 401 (unauthorized)
|
# # assert that the response code was status code 401 (unauthorized)
|
||||||
# assert_response 401
|
# assert_response 401
|
||||||
def assert_response(type, message = nil)
|
def assert_response(type, message = nil)
|
||||||
message ||= "Expected response to be a <#{type}>, but was <#{@response.response_code}>"
|
|
||||||
|
|
||||||
if Symbol === type
|
if Symbol === type
|
||||||
if [:success, :missing, :redirect, :error].include?(type)
|
if [:success, :missing, :redirect, :error].include?(type)
|
||||||
assert @response.send("#{type}?"), message
|
assert_predicate @response, RESPONSE_PREDICATES[type], message
|
||||||
else
|
else
|
||||||
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
|
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
|
||||||
if code.nil?
|
if code.nil?
|
||||||
|
@ -86,8 +86,8 @@ def assert_generates(expected_path, options, defaults={}, extras={}, message=nil
|
|||||||
end
|
end
|
||||||
# Load routes.rb if it hasn't been loaded.
|
# Load routes.rb if it hasn't been loaded.
|
||||||
|
|
||||||
generated_path, extra_keys = @routes.generate_extras(options, defaults)
|
generated_path, query_string_keys = @routes.generate_extras(options, defaults)
|
||||||
found_extras = options.reject { |k, _| ! extra_keys.include? k }
|
found_extras = options.reject { |k, _| ! query_string_keys.include? k }
|
||||||
|
|
||||||
msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
|
msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
|
||||||
assert_equal(extras, found_extras, msg)
|
assert_equal(extras, found_extras, msg)
|
||||||
@ -165,7 +165,7 @@ def with_routing
|
|||||||
|
|
||||||
# ROUTES TODO: These assertions should really work in an integration context
|
# ROUTES TODO: These assertions should really work in an integration context
|
||||||
def method_missing(selector, *args, &block)
|
def method_missing(selector, *args, &block)
|
||||||
if defined?(@controller) && @controller && @routes && @routes.named_routes.route_defined?(selector)
|
if defined?(@controller) && @controller && defined?(@routes) && @routes && @routes.named_routes.route_defined?(selector)
|
||||||
@controller.send(selector, *args, &block)
|
@controller.send(selector, *args, &block)
|
||||||
else
|
else
|
||||||
super
|
super
|
||||||
|
@ -325,7 +325,11 @@ def process(method, path, params: nil, headers: nil, env: nil, xhr: false)
|
|||||||
if path =~ %r{://}
|
if path =~ %r{://}
|
||||||
location = URI.parse(path)
|
location = URI.parse(path)
|
||||||
https! URI::HTTPS === location if location.scheme
|
https! URI::HTTPS === location if location.scheme
|
||||||
host! "#{location.host}:#{location.port}" if location.host
|
if url_host = location.host
|
||||||
|
default = Rack::Request::DEFAULT_PORTS[location.scheme]
|
||||||
|
url_host += ":#{location.port}" if default != location.port
|
||||||
|
host! url_host
|
||||||
|
end
|
||||||
path = location.query ? "#{location.path}?#{location.query}" : location.path
|
path = location.query ? "#{location.path}?#{location.query}" : location.path
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -355,10 +359,10 @@ def process(method, path, params: nil, headers: nil, env: nil, xhr: false)
|
|||||||
|
|
||||||
# this modifies the passed request_env directly
|
# this modifies the passed request_env directly
|
||||||
if headers.present?
|
if headers.present?
|
||||||
Http::Headers.new(request_env).merge!(headers)
|
Http::Headers.from_hash(request_env).merge!(headers)
|
||||||
end
|
end
|
||||||
if env.present?
|
if env.present?
|
||||||
Http::Headers.new(request_env).merge!(env)
|
Http::Headers.from_hash(request_env).merge!(env)
|
||||||
end
|
end
|
||||||
|
|
||||||
session = Rack::Test::Session.new(_mock_session)
|
session = Rack::Test::Session.new(_mock_session)
|
||||||
@ -374,7 +378,7 @@ def process(method, path, params: nil, headers: nil, env: nil, xhr: false)
|
|||||||
@html_document = nil
|
@html_document = nil
|
||||||
@url_options = nil
|
@url_options = nil
|
||||||
|
|
||||||
@controller = session.last_request.env['action_controller.instance']
|
@controller = @request.controller_instance
|
||||||
|
|
||||||
response.status
|
response.status
|
||||||
end
|
end
|
||||||
@ -391,7 +395,7 @@ module Runner
|
|||||||
|
|
||||||
attr_reader :app
|
attr_reader :app
|
||||||
|
|
||||||
def before_setup
|
def before_setup # :nodoc:
|
||||||
@app = nil
|
@app = nil
|
||||||
@integration_session = nil
|
@integration_session = nil
|
||||||
super
|
super
|
||||||
|
@ -19,7 +19,7 @@ def flash
|
|||||||
end
|
end
|
||||||
|
|
||||||
def cookies
|
def cookies
|
||||||
@cookie_jar ||= Cookies::CookieJar.build(@request.env, @request.host, @request.ssl?, @request.cookies)
|
@cookie_jar ||= Cookies::CookieJar.build(@request, @request.cookies)
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirect_to_url
|
def redirect_to_url
|
||||||
|
@ -16,9 +16,6 @@ def self.from_response(response)
|
|||||||
# Was the URL not found?
|
# Was the URL not found?
|
||||||
alias_method :missing?, :not_found?
|
alias_method :missing?, :not_found?
|
||||||
|
|
||||||
# Were we redirected?
|
|
||||||
alias_method :redirect?, :redirection?
|
|
||||||
|
|
||||||
# Was there a server-side error?
|
# Was there a server-side error?
|
||||||
alias_method :error?, :server_error?
|
alias_method :error?, :server_error?
|
||||||
end
|
end
|
||||||
|
@ -63,6 +63,10 @@ def env
|
|||||||
|
|
||||||
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
|
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
|
||||||
|
|
||||||
|
SharedTestRoutes.draw do
|
||||||
|
get ':controller(/:action)'
|
||||||
|
end
|
||||||
|
|
||||||
module ActionDispatch
|
module ActionDispatch
|
||||||
module SharedRoutes
|
module SharedRoutes
|
||||||
def before_setup
|
def before_setup
|
||||||
@ -70,35 +74,10 @@ def before_setup
|
|||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Hold off drawing routes until all the possible controller classes
|
|
||||||
# have been loaded.
|
|
||||||
module DrawOnce
|
|
||||||
class << self
|
|
||||||
attr_accessor :drew
|
|
||||||
end
|
|
||||||
self.drew = false
|
|
||||||
|
|
||||||
def before_setup
|
|
||||||
super
|
|
||||||
return if DrawOnce.drew
|
|
||||||
|
|
||||||
SharedTestRoutes.draw do
|
|
||||||
get ':controller(/:action)'
|
|
||||||
end
|
|
||||||
|
|
||||||
ActionDispatch::IntegrationTest.app.routes.draw do
|
|
||||||
get ':controller(/:action)'
|
|
||||||
end
|
|
||||||
|
|
||||||
DrawOnce.drew = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module ActiveSupport
|
module ActiveSupport
|
||||||
class TestCase
|
class TestCase
|
||||||
include ActionDispatch::DrawOnce
|
|
||||||
if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0
|
if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0
|
||||||
parallelize_me!
|
parallelize_me!
|
||||||
end
|
end
|
||||||
@ -119,29 +98,31 @@ def call(env)
|
|||||||
end
|
end
|
||||||
|
|
||||||
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
|
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
|
||||||
include ActionDispatch::SharedRoutes
|
|
||||||
|
|
||||||
def self.build_app(routes = nil)
|
def self.build_app(routes = nil)
|
||||||
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
|
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
|
||||||
middleware.use "ActionDispatch::ShowExceptions", ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
|
middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
|
||||||
middleware.use "ActionDispatch::DebugExceptions"
|
middleware.use ActionDispatch::DebugExceptions
|
||||||
middleware.use "ActionDispatch::Callbacks"
|
middleware.use ActionDispatch::Callbacks
|
||||||
middleware.use "ActionDispatch::ParamsParser"
|
middleware.use ActionDispatch::ParamsParser
|
||||||
middleware.use "ActionDispatch::Cookies"
|
middleware.use ActionDispatch::Cookies
|
||||||
middleware.use "ActionDispatch::Flash"
|
middleware.use ActionDispatch::Flash
|
||||||
middleware.use "Rack::Head"
|
middleware.use Rack::Head
|
||||||
yield(middleware) if block_given?
|
yield(middleware) if block_given?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.app = build_app
|
self.app = build_app
|
||||||
|
|
||||||
|
app.routes.draw do
|
||||||
|
get ':controller(/:action)'
|
||||||
|
end
|
||||||
|
|
||||||
# Stub Rails dispatcher so it does not get controller references and
|
# Stub Rails dispatcher so it does not get controller references and
|
||||||
# simply return the controller#action as Rack::Body.
|
# simply return the controller#action as Rack::Body.
|
||||||
class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
|
class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
|
||||||
protected
|
protected
|
||||||
def controller_reference(controller_param)
|
def controller_reference(controller_param)
|
||||||
controller_param
|
controller_param.params[:controller]
|
||||||
end
|
end
|
||||||
|
|
||||||
def dispatch(controller, action, env)
|
def dispatch(controller, action, env)
|
||||||
@ -149,14 +130,10 @@ def dispatch(controller, action, env)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.stub_controllers
|
def self.stub_controllers(config = nil)
|
||||||
old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher
|
route_set = ActionDispatch::Routing::RouteSet.new(*[config].compact)
|
||||||
ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
|
route_set.dispatcher_class = StubDispatcher
|
||||||
ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher }
|
yield route_set
|
||||||
yield ActionDispatch::Routing::RouteSet.new
|
|
||||||
ensure
|
|
||||||
ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
|
|
||||||
ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_routing(&block)
|
def with_routing(&block)
|
||||||
|
@ -7,7 +7,7 @@ class ResponseAssertionsTest < ActiveSupport::TestCase
|
|||||||
include ResponseAssertions
|
include ResponseAssertions
|
||||||
|
|
||||||
FakeResponse = Struct.new(:response_code) do
|
FakeResponse = Struct.new(:response_code) do
|
||||||
[:success, :missing, :redirect, :error].each do |sym|
|
[:successful, :not_found, :redirection, :server_error].each do |sym|
|
||||||
define_method("#{sym}?") do
|
define_method("#{sym}?") do
|
||||||
sym == response_code
|
sym == response_code
|
||||||
end
|
end
|
||||||
@ -16,7 +16,7 @@ class ResponseAssertionsTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
def test_assert_response_predicate_methods
|
def test_assert_response_predicate_methods
|
||||||
[:success, :missing, :redirect, :error].each do |sym|
|
[:success, :missing, :redirect, :error].each do |sym|
|
||||||
@response = FakeResponse.new sym
|
@response = FakeResponse.new RESPONSE_PREDICATES[sym].to_s.sub(/\?/, '').to_sym
|
||||||
assert_response sym
|
assert_response sym
|
||||||
|
|
||||||
assert_raises(Minitest::Assertion) {
|
assert_raises(Minitest::Assertion) {
|
||||||
|
@ -43,12 +43,12 @@ def response599() head '599 Whoah!' end
|
|||||||
|
|
||||||
def flash_me
|
def flash_me
|
||||||
flash['hello'] = 'my name is inigo montoya...'
|
flash['hello'] = 'my name is inigo montoya...'
|
||||||
render :text => "Inconceivable!"
|
render plain: "Inconceivable!"
|
||||||
end
|
end
|
||||||
|
|
||||||
def flash_me_naked
|
def flash_me_naked
|
||||||
flash.clear
|
flash.clear
|
||||||
render :text => "wow!"
|
render plain: "wow!"
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_this
|
def assign_this
|
||||||
@ -57,30 +57,30 @@ def assign_this
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_based_on_parameters
|
def render_based_on_parameters
|
||||||
render :text => "Mr. #{params[:name]}"
|
render plain: "Mr. #{params[:name]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_url
|
def render_url
|
||||||
render :text => "<div>#{url_for(:action => 'flash_me', :only_path => true)}</div>"
|
render html: "<div>#{url_for(action: 'flash_me', only_path: true)}</div>"
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_text_with_custom_content_type
|
def render_text_with_custom_content_type
|
||||||
render :text => "Hello!", :content_type => Mime::RSS
|
render body: "Hello!", content_type: Mime::RSS
|
||||||
end
|
end
|
||||||
|
|
||||||
def session_stuffing
|
def session_stuffing
|
||||||
session['xmas'] = 'turkey'
|
session['xmas'] = 'turkey'
|
||||||
render :text => "ho ho ho"
|
render plain: "ho ho ho"
|
||||||
end
|
end
|
||||||
|
|
||||||
def raise_exception_on_get
|
def raise_exception_on_get
|
||||||
raise "get" if request.get?
|
raise "get" if request.get?
|
||||||
render :text => "request method: #{request.env['REQUEST_METHOD']}"
|
render plain: "request method: #{request.env['REQUEST_METHOD']}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def raise_exception_on_post
|
def raise_exception_on_post
|
||||||
raise "post" if request.post?
|
raise "post" if request.post?
|
||||||
render :text => "request method: #{request.env['REQUEST_METHOD']}"
|
render plain: "request method: #{request.env['REQUEST_METHOD']}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_file_absolute_path
|
def render_file_absolute_path
|
||||||
@ -101,7 +101,7 @@ def index
|
|||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render :text => "Boom", :status => 500
|
render plain: "Boom", status: 500
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -318,7 +318,7 @@ def test_server_error_response_code
|
|||||||
|
|
||||||
def test_missing_response_code
|
def test_missing_response_code
|
||||||
process :response404
|
process :response404
|
||||||
assert @response.missing?
|
assert @response.not_found?
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_client_error_response_code
|
def test_client_error_response_code
|
||||||
@ -346,12 +346,12 @@ def test_redirection
|
|||||||
|
|
||||||
def test_successful_response_code
|
def test_successful_response_code
|
||||||
process :nothing
|
process :nothing
|
||||||
assert @response.success?
|
assert @response.successful?
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_response_object
|
def test_response_object
|
||||||
process :nothing
|
process :nothing
|
||||||
assert_kind_of ActionController::TestResponse, @response
|
assert_kind_of ActionDispatch::TestResponse, @response
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_based_on_parameters
|
def test_render_based_on_parameters
|
||||||
|
@ -7,12 +7,12 @@ class ConditionalGetApiController < ActionController::API
|
|||||||
|
|
||||||
def one
|
def one
|
||||||
if stale?(last_modified: Time.now.utc.beginning_of_day, etag: [:foo, 123])
|
if stale?(last_modified: Time.now.utc.beginning_of_day, etag: [:foo, 123])
|
||||||
render text: "Hi!"
|
render plain: "Hi!"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def two
|
def two
|
||||||
render text: "Hi!"
|
render plain: "Hi!"
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -7,7 +7,7 @@ class UsersController < ActionController::API
|
|||||||
wrap_parameters :person, format: [:json]
|
wrap_parameters :person, format: [:json]
|
||||||
|
|
||||||
def test
|
def test
|
||||||
self.last_parameters = params.except(:controller, :action)
|
self.last_parameters = params.except(:controller, :action).to_unsafe_h
|
||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -53,7 +53,7 @@ class RecordIdentifierIncludedController < ActionController::Base
|
|||||||
|
|
||||||
class ActionMissingController < ActionController::Base
|
class ActionMissingController < ActionController::Base
|
||||||
def action_missing(action)
|
def action_missing(action)
|
||||||
render :text => "Response for #{action}"
|
render plain: "Response for #{action}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -4,29 +4,29 @@ class OldContentTypeController < ActionController::Base
|
|||||||
# :ported:
|
# :ported:
|
||||||
def render_content_type_from_body
|
def render_content_type_from_body
|
||||||
response.content_type = Mime::RSS
|
response.content_type = Mime::RSS
|
||||||
render :text => "hello world!"
|
render body: "hello world!"
|
||||||
end
|
end
|
||||||
|
|
||||||
# :ported:
|
# :ported:
|
||||||
def render_defaults
|
def render_defaults
|
||||||
render :text => "hello world!"
|
render body: "hello world!"
|
||||||
end
|
end
|
||||||
|
|
||||||
# :ported:
|
# :ported:
|
||||||
def render_content_type_from_render
|
def render_content_type_from_render
|
||||||
render :text => "hello world!", :content_type => Mime::RSS
|
render body: "hello world!", :content_type => Mime::RSS
|
||||||
end
|
end
|
||||||
|
|
||||||
# :ported:
|
# :ported:
|
||||||
def render_charset_from_body
|
def render_charset_from_body
|
||||||
response.charset = "utf-16"
|
response.charset = "utf-16"
|
||||||
render :text => "hello world!"
|
render body: "hello world!"
|
||||||
end
|
end
|
||||||
|
|
||||||
# :ported:
|
# :ported:
|
||||||
def render_nil_charset_from_body
|
def render_nil_charset_from_body
|
||||||
response.charset = nil
|
response.charset = nil
|
||||||
render :text => "hello world!"
|
render body: "hello world!"
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_default_for_erb
|
def render_default_for_erb
|
||||||
@ -42,10 +42,10 @@ def render_change_for_builder
|
|||||||
|
|
||||||
def render_default_content_types_for_respond_to
|
def render_default_content_types_for_respond_to
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :text => "hello world!" }
|
format.html { render body: "hello world!" }
|
||||||
format.xml { render :action => "render_default_content_types_for_respond_to" }
|
format.xml { render action: "render_default_content_types_for_respond_to" }
|
||||||
format.js { render :text => "hello world!" }
|
format.js { render body: "hello world!" }
|
||||||
format.rss { render :text => "hello world!", :content_type => Mime::XML }
|
format.rss { render body: "hello world!", content_type: Mime::XML }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -64,14 +64,14 @@ def setup
|
|||||||
def test_render_defaults
|
def test_render_defaults
|
||||||
get :render_defaults
|
get :render_defaults
|
||||||
assert_equal "utf-8", @response.charset
|
assert_equal "utf-8", @response.charset
|
||||||
assert_equal Mime::HTML, @response.content_type
|
assert_equal Mime::TEXT, @response.content_type
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_changed_charset_default
|
def test_render_changed_charset_default
|
||||||
with_default_charset "utf-16" do
|
with_default_charset "utf-16" do
|
||||||
get :render_defaults
|
get :render_defaults
|
||||||
assert_equal "utf-16", @response.charset
|
assert_equal "utf-16", @response.charset
|
||||||
assert_equal Mime::HTML, @response.content_type
|
assert_equal Mime::TEXT, @response.content_type
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -92,14 +92,14 @@ def test_content_type_from_render
|
|||||||
# :ported:
|
# :ported:
|
||||||
def test_charset_from_body
|
def test_charset_from_body
|
||||||
get :render_charset_from_body
|
get :render_charset_from_body
|
||||||
assert_equal Mime::HTML, @response.content_type
|
assert_equal Mime::TEXT, @response.content_type
|
||||||
assert_equal "utf-16", @response.charset
|
assert_equal "utf-16", @response.charset
|
||||||
end
|
end
|
||||||
|
|
||||||
# :ported:
|
# :ported:
|
||||||
def test_nil_charset_from_body
|
def test_nil_charset_from_body
|
||||||
get :render_nil_charset_from_body
|
get :render_nil_charset_from_body
|
||||||
assert_equal Mime::HTML, @response.content_type
|
assert_equal Mime::TEXT, @response.content_type
|
||||||
assert_equal "utf-8", @response.charset, @response.headers.inspect
|
assert_equal "utf-8", @response.charset, @response.headers.inspect
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ class ControllerWithBeforeActionAndDefaultUrlOptions < ActionController::Base
|
|||||||
after_action { I18n.locale = "en" }
|
after_action { I18n.locale = "en" }
|
||||||
|
|
||||||
def target
|
def target
|
||||||
render :text => "final response"
|
render plain: "final response"
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirect
|
def redirect
|
||||||
|
@ -40,7 +40,7 @@ class ChangingTheRequirementsController < TestController
|
|||||||
before_action :ensure_login, :except => [:go_wild]
|
before_action :ensure_login, :except => [:go_wild]
|
||||||
|
|
||||||
def go_wild
|
def go_wild
|
||||||
render :text => "gobble"
|
render plain: "gobble"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ class TestMultipleFiltersController < ActionController::Base
|
|||||||
|
|
||||||
(1..3).each do |i|
|
(1..3).each do |i|
|
||||||
define_method "fail_#{i}" do
|
define_method "fail_#{i}" do
|
||||||
render :text => i.to_s
|
render plain: i.to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -222,7 +222,7 @@ class SkipFilterUsingOnlyAndIf < ConditionalFilterController
|
|||||||
skip_before_action :clean_up_tmp, only: :login, if: -> { true }
|
skip_before_action :clean_up_tmp, only: :login, if: -> { true }
|
||||||
|
|
||||||
def login
|
def login
|
||||||
render text: 'ok'
|
render plain: 'ok'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ class SkipFilterUsingIfAndExcept < ConditionalFilterController
|
|||||||
skip_before_action :clean_up_tmp, if: -> { true }, except: :login
|
skip_before_action :clean_up_tmp, if: -> { true }, except: :login
|
||||||
|
|
||||||
def login
|
def login
|
||||||
render text: 'ok'
|
render plain: 'ok'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -258,11 +258,11 @@ class SkippingAndLimitedController < TestController
|
|||||||
before_action :ensure_login, :only => :index
|
before_action :ensure_login, :only => :index
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render :text => 'ok'
|
render plain: 'ok'
|
||||||
end
|
end
|
||||||
|
|
||||||
def public
|
def public
|
||||||
render :text => 'ok'
|
render plain: 'ok'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -272,7 +272,7 @@ class SkippingAndReorderingController < TestController
|
|||||||
before_action :ensure_login
|
before_action :ensure_login
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render :text => 'ok'
|
render plain: 'ok'
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -383,7 +383,7 @@ class AuditController < ActionController::Base
|
|||||||
before_action(AuditFilter)
|
before_action(AuditFilter)
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render :text => "hello"
|
render plain: "hello"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -421,11 +421,11 @@ class OutOfOrder < StandardError; end
|
|||||||
before_action :second, :only => :foo
|
before_action :second, :only => :foo
|
||||||
|
|
||||||
def foo
|
def foo
|
||||||
render :text => 'foo'
|
render plain: 'foo'
|
||||||
end
|
end
|
||||||
|
|
||||||
def bar
|
def bar
|
||||||
render :text => 'bar'
|
render plain: 'bar'
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
@ -442,7 +442,7 @@ class DynamicDispatchController < ActionController::Base
|
|||||||
before_action :choose
|
before_action :choose
|
||||||
|
|
||||||
%w(foo bar baz).each do |action|
|
%w(foo bar baz).each do |action|
|
||||||
define_method(action) { render :text => action }
|
define_method(action) { render plain: action }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -471,7 +471,7 @@ def between_before_all_and_after_all
|
|||||||
@ran_filter << 'between_before_all_and_after_all'
|
@ran_filter << 'between_before_all_and_after_all'
|
||||||
end
|
end
|
||||||
def show
|
def show
|
||||||
render :text => 'hello'
|
render plain: 'hello'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -481,7 +481,7 @@ class RescuingAroundFilterWithBlock
|
|||||||
def around(controller)
|
def around(controller)
|
||||||
yield
|
yield
|
||||||
rescue ErrorToRescue => ex
|
rescue ErrorToRescue => ex
|
||||||
controller.__send__ :render, :text => "I rescued this: #{ex.inspect}"
|
controller.__send__ :render, plain: "I rescued this: #{ex.inspect}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -819,7 +819,7 @@ def test_a_rescuing_around_action
|
|||||||
response = test_process(RescuedController)
|
response = test_process(RescuedController)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert response.success?
|
assert response.successful?
|
||||||
assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body)
|
assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -329,7 +329,7 @@ def with_test_route_set
|
|||||||
@app = self.class.build_app(set) do |middleware|
|
@app = self.class.build_app(set) do |middleware|
|
||||||
middleware.use ActionDispatch::Session::CookieStore, :key => SessionKey
|
middleware.use ActionDispatch::Session::CookieStore, :key => SessionKey
|
||||||
middleware.use ActionDispatch::Flash
|
middleware.use ActionDispatch::Flash
|
||||||
middleware.delete "ActionDispatch::ShowExceptions"
|
middleware.delete ActionDispatch::ShowExceptions
|
||||||
end
|
end
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
class ForceSSLController < ActionController::Base
|
class ForceSSLController < ActionController::Base
|
||||||
def banana
|
def banana
|
||||||
render :text => "monkey"
|
render plain: "monkey"
|
||||||
end
|
end
|
||||||
|
|
||||||
def cheeseburger
|
def cheeseburger
|
||||||
render :text => "sikachu"
|
render plain: "sikachu"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ class ForceSSLCustomOptions < ForceSSLController
|
|||||||
force_ssl :notice => 'Foo, Bar!', :only => :redirect_notice
|
force_ssl :notice => 'Foo, Bar!', :only => :redirect_notice
|
||||||
|
|
||||||
def force_ssl_action
|
def force_ssl_action
|
||||||
render :text => action_name
|
render plain: action_name
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :redirect_host, :force_ssl_action
|
alias_method :redirect_host, :force_ssl_action
|
||||||
@ -40,15 +40,15 @@ def force_ssl_action
|
|||||||
alias_method :redirect_notice, :force_ssl_action
|
alias_method :redirect_notice, :force_ssl_action
|
||||||
|
|
||||||
def use_flash
|
def use_flash
|
||||||
render :text => flash[:message]
|
render plain: flash[:message]
|
||||||
end
|
end
|
||||||
|
|
||||||
def use_alert
|
def use_alert
|
||||||
render :text => flash[:alert]
|
render plain: flash[:alert]
|
||||||
end
|
end
|
||||||
|
|
||||||
def use_notice
|
def use_notice
|
||||||
render :text => flash[:notice]
|
render plain: flash[:notice]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -85,10 +85,10 @@ def use_flash
|
|||||||
|
|
||||||
class RedirectToSSL < ForceSSLController
|
class RedirectToSSL < ForceSSLController
|
||||||
def banana
|
def banana
|
||||||
force_ssl_redirect || render(:text => 'monkey')
|
force_ssl_redirect || render(plain: 'monkey')
|
||||||
end
|
end
|
||||||
def cheeseburger
|
def cheeseburger
|
||||||
force_ssl_redirect('secure.cheeseburger.host') || render(:text => 'ihaz')
|
force_ssl_redirect('secure.cheeseburger.host') || render(plain: 'ihaz')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ def test_helper_for_acronym_controller
|
|||||||
assert_equal "test: baz", call_controller(Fun::PdfController, "test").last.body
|
assert_equal "test: baz", call_controller(Fun::PdfController, "test").last.body
|
||||||
#
|
#
|
||||||
# request = ActionController::TestRequest.new
|
# request = ActionController::TestRequest.new
|
||||||
# response = ActionController::TestResponse.new
|
# response = ActionDispatch::TestResponse.new
|
||||||
# request.action = 'test'
|
# request.action = 'test'
|
||||||
#
|
#
|
||||||
# assert_equal 'test: baz', Fun::PdfController.process(request, response).body
|
# assert_equal 'test: baz', Fun::PdfController.process(request, response).body
|
||||||
|
@ -9,19 +9,19 @@ class DummyController < ActionController::Base
|
|||||||
http_basic_authenticate_with :name => "David", :password => "Goliath", :only => :search
|
http_basic_authenticate_with :name => "David", :password => "Goliath", :only => :search
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render :text => "Hello Secret"
|
render plain: "Hello Secret"
|
||||||
end
|
end
|
||||||
|
|
||||||
def display
|
def display
|
||||||
render :text => 'Definitely Maybe' if @logged_in
|
render plain: 'Definitely Maybe' if @logged_in
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render :text => 'Only for loooooong credentials'
|
render plain: 'Only for loooooong credentials'
|
||||||
end
|
end
|
||||||
|
|
||||||
def search
|
def search
|
||||||
render :text => 'All inline'
|
render plain: 'All inline'
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -100,6 +100,14 @@ def test_encode_credentials_has_no_newline
|
|||||||
assert_no_match(/\n/, result)
|
assert_no_match(/\n/, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "succesful authentication with uppercase authorization scheme" do
|
||||||
|
@request.env['HTTP_AUTHORIZATION'] = "BASIC #{::Base64.encode64("lifo:world")}"
|
||||||
|
get :index
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_equal 'Hello Secret', @response.body, 'Authentication failed when authorization scheme BASIC'
|
||||||
|
end
|
||||||
|
|
||||||
test "authentication request without credential" do
|
test "authentication request without credential" do
|
||||||
get :display
|
get :display
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@ class DummyDigestController < ActionController::Base
|
|||||||
'dhh' => ::Digest::MD5::hexdigest(["dhh","SuperSecret","secret"].join(":"))}
|
'dhh' => ::Digest::MD5::hexdigest(["dhh","SuperSecret","secret"].join(":"))}
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render :text => "Hello Secret"
|
render plain: "Hello Secret"
|
||||||
end
|
end
|
||||||
|
|
||||||
def display
|
def display
|
||||||
render :text => 'Definitely Maybe' if @logged_in
|
render plain: 'Definitely Maybe' if @logged_in
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -7,15 +7,15 @@ class DummyController < ActionController::Base
|
|||||||
before_action :authenticate_long_credentials, only: :show
|
before_action :authenticate_long_credentials, only: :show
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render :text => "Hello Secret"
|
render plain: "Hello Secret"
|
||||||
end
|
end
|
||||||
|
|
||||||
def display
|
def display
|
||||||
render :text => 'Definitely Maybe'
|
render plain: 'Definitely Maybe'
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render :text => 'Only for loooooong credentials'
|
render plain: 'Only for loooooong credentials'
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -359,28 +359,28 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
|
|||||||
class IntegrationController < ActionController::Base
|
class IntegrationController < ActionController::Base
|
||||||
def get
|
def get
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :text => "OK", :status => 200 }
|
format.html { render plain: "OK", status: 200 }
|
||||||
format.js { render :text => "JS OK", :status => 200 }
|
format.js { render plain: "JS OK", status: 200 }
|
||||||
format.xml { render :xml => "<root></root>", :status => 200 }
|
format.xml { render :xml => "<root></root>", :status => 200 }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_with_params
|
def get_with_params
|
||||||
render :text => "foo: #{params[:foo]}", :status => 200
|
render plain: "foo: #{params[:foo]}", status: 200
|
||||||
end
|
end
|
||||||
|
|
||||||
def post
|
def post
|
||||||
render :text => "Created", :status => 201
|
render plain: "Created", status: 201
|
||||||
end
|
end
|
||||||
|
|
||||||
def method
|
def method
|
||||||
render :text => "method: #{request.method.downcase}"
|
render plain: "method: #{request.method.downcase}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def cookie_monster
|
def cookie_monster
|
||||||
cookies["cookie_1"] = nil
|
cookies["cookie_1"] = nil
|
||||||
cookies["cookie_3"] = "chocolate"
|
cookies["cookie_3"] = "chocolate"
|
||||||
render :text => "Gone", :status => 410
|
render plain: "Gone", status: 410
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_cookie
|
def set_cookie
|
||||||
@ -389,7 +389,7 @@ def set_cookie
|
|||||||
end
|
end
|
||||||
|
|
||||||
def get_cookie
|
def get_cookie
|
||||||
render :text => cookies["foo"]
|
render plain: cookies["foo"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirect
|
def redirect
|
||||||
@ -755,12 +755,31 @@ def test_pass_env
|
|||||||
assert_equal "http://test.com/", @request.env["HTTP_REFERER"]
|
assert_equal "http://test.com/", @request.env["HTTP_REFERER"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_ignores_common_ports_in_host
|
||||||
|
get "http://test.com"
|
||||||
|
assert_equal "test.com", @request.env["HTTP_HOST"]
|
||||||
|
|
||||||
|
get "https://test.com"
|
||||||
|
assert_equal "test.com", @request.env["HTTP_HOST"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_keeps_uncommon_ports_in_host
|
||||||
|
get "http://test.com:123"
|
||||||
|
assert_equal "test.com:123", @request.env["HTTP_HOST"]
|
||||||
|
|
||||||
|
get "http://test.com:443"
|
||||||
|
assert_equal "test.com:443", @request.env["HTTP_HOST"]
|
||||||
|
|
||||||
|
get "https://test.com:80"
|
||||||
|
assert_equal "test.com:80", @request.env["HTTP_HOST"]
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class ApplicationIntegrationTest < ActionDispatch::IntegrationTest
|
class ApplicationIntegrationTest < ActionDispatch::IntegrationTest
|
||||||
class TestController < ActionController::Base
|
class TestController < ActionController::Base
|
||||||
def index
|
def index
|
||||||
render :text => "index"
|
render plain: "index"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -847,7 +866,7 @@ def app
|
|||||||
class EnvironmentFilterIntegrationTest < ActionDispatch::IntegrationTest
|
class EnvironmentFilterIntegrationTest < ActionDispatch::IntegrationTest
|
||||||
class TestController < ActionController::Base
|
class TestController < ActionController::Base
|
||||||
def post
|
def post
|
||||||
render :text => "Created", :status => 201
|
render plain: "Created", status: 201
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -880,15 +899,15 @@ def app
|
|||||||
class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest
|
class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest
|
||||||
class FooController < ActionController::Base
|
class FooController < ActionController::Base
|
||||||
def index
|
def index
|
||||||
render :text => "foo#index"
|
render plain: "foo#index"
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render :text => "foo#show"
|
render plain: "foo#show"
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
render :text => "foo#show"
|
render plain: "foo#show"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -898,7 +917,7 @@ def default_url_options
|
|||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render :text => "foo#index"
|
render plain: "foo#index"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
require 'abstract_unit'
|
require 'abstract_unit'
|
||||||
require 'active_support/concurrency/latch'
|
require 'concurrent/atomics'
|
||||||
Thread.abort_on_exception = true
|
Thread.abort_on_exception = true
|
||||||
|
|
||||||
module ActionController
|
module ActionController
|
||||||
@ -125,7 +125,7 @@ def set_cookie
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_text
|
def render_text
|
||||||
render :text => 'zomg'
|
render plain: 'zomg'
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_header
|
def default_header
|
||||||
@ -145,7 +145,7 @@ def blocking_stream
|
|||||||
response.headers['Content-Type'] = 'text/event-stream'
|
response.headers['Content-Type'] = 'text/event-stream'
|
||||||
%w{ hello world }.each do |word|
|
%w{ hello world }.each do |word|
|
||||||
response.stream.write word
|
response.stream.write word
|
||||||
latch.await
|
latch.wait
|
||||||
end
|
end
|
||||||
response.stream.close
|
response.stream.close
|
||||||
end
|
end
|
||||||
@ -162,7 +162,7 @@ def thread_locals
|
|||||||
end
|
end
|
||||||
|
|
||||||
def with_stale
|
def with_stale
|
||||||
render text: 'stale' if stale?(etag: "123", template: false)
|
render plain: 'stale' if stale?(etag: "123", template: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def exception_in_view
|
def exception_in_view
|
||||||
@ -212,7 +212,7 @@ def overfill_buffer_and_die
|
|||||||
# .. plus one more, because the #each frees up a slot:
|
# .. plus one more, because the #each frees up a slot:
|
||||||
response.stream.write '.'
|
response.stream.write '.'
|
||||||
|
|
||||||
latch.release
|
latch.count_down
|
||||||
|
|
||||||
# This write will block, and eventually raise
|
# This write will block, and eventually raise
|
||||||
response.stream.write 'x'
|
response.stream.write 'x'
|
||||||
@ -233,7 +233,7 @@ def ignore_client_disconnect
|
|||||||
end
|
end
|
||||||
|
|
||||||
logger.info 'Work complete'
|
logger.info 'Work complete'
|
||||||
latch.release
|
latch.count_down
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ def test_write_to_stream
|
|||||||
def test_async_stream
|
def test_async_stream
|
||||||
rubinius_skip "https://github.com/rubinius/rubinius/issues/2934"
|
rubinius_skip "https://github.com/rubinius/rubinius/issues/2934"
|
||||||
|
|
||||||
@controller.latch = ActiveSupport::Concurrency::Latch.new
|
@controller.latch = Concurrent::CountDownLatch.new
|
||||||
parts = ['hello', 'world']
|
parts = ['hello', 'world']
|
||||||
|
|
||||||
@controller.request = @request
|
@controller.request = @request
|
||||||
@ -289,8 +289,8 @@ def test_async_stream
|
|||||||
resp.stream.each do |part|
|
resp.stream.each do |part|
|
||||||
assert_equal parts.shift, part
|
assert_equal parts.shift, part
|
||||||
ol = @controller.latch
|
ol = @controller.latch
|
||||||
@controller.latch = ActiveSupport::Concurrency::Latch.new
|
@controller.latch = Concurrent::CountDownLatch.new
|
||||||
ol.release
|
ol.count_down
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,23 +300,23 @@ def test_async_stream
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_abort_with_full_buffer
|
def test_abort_with_full_buffer
|
||||||
@controller.latch = ActiveSupport::Concurrency::Latch.new
|
@controller.latch = Concurrent::CountDownLatch.new
|
||||||
|
|
||||||
@request.parameters[:format] = 'plain'
|
@request.parameters[:format] = 'plain'
|
||||||
@controller.request = @request
|
@controller.request = @request
|
||||||
@controller.response = @response
|
@controller.response = @response
|
||||||
|
|
||||||
got_error = ActiveSupport::Concurrency::Latch.new
|
got_error = Concurrent::CountDownLatch.new
|
||||||
@response.stream.on_error do
|
@response.stream.on_error do
|
||||||
ActionController::Base.logger.warn 'Error while streaming'
|
ActionController::Base.logger.warn 'Error while streaming'
|
||||||
got_error.release
|
got_error.count_down
|
||||||
end
|
end
|
||||||
|
|
||||||
t = Thread.new(@response) { |resp|
|
t = Thread.new(@response) { |resp|
|
||||||
resp.await_commit
|
resp.await_commit
|
||||||
_, _, body = resp.to_a
|
_, _, body = resp.to_a
|
||||||
body.each do |part|
|
body.each do |part|
|
||||||
@controller.latch.await
|
@controller.latch.wait
|
||||||
body.close
|
body.close
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@ -325,13 +325,13 @@ def test_abort_with_full_buffer
|
|||||||
capture_log_output do |output|
|
capture_log_output do |output|
|
||||||
@controller.process :overfill_buffer_and_die
|
@controller.process :overfill_buffer_and_die
|
||||||
t.join
|
t.join
|
||||||
got_error.await
|
got_error.wait
|
||||||
assert_match 'Error while streaming', output.rewind && output.read
|
assert_match 'Error while streaming', output.rewind && output.read
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_ignore_client_disconnect
|
def test_ignore_client_disconnect
|
||||||
@controller.latch = ActiveSupport::Concurrency::Latch.new
|
@controller.latch = Concurrent::CountDownLatch.new
|
||||||
|
|
||||||
@controller.request = @request
|
@controller.request = @request
|
||||||
@controller.response = @response
|
@controller.response = @response
|
||||||
@ -349,7 +349,7 @@ def test_ignore_client_disconnect
|
|||||||
@controller.process :ignore_client_disconnect
|
@controller.process :ignore_client_disconnect
|
||||||
t.join
|
t.join
|
||||||
Timeout.timeout(3) do
|
Timeout.timeout(3) do
|
||||||
@controller.latch.await
|
@controller.latch.wait
|
||||||
end
|
end
|
||||||
assert_match 'Work complete', output.rewind && output.read
|
assert_match 'Work complete', output.rewind && output.read
|
||||||
end
|
end
|
||||||
|
@ -13,41 +13,41 @@ class RespondToController < ActionController::Base
|
|||||||
|
|
||||||
def html_xml_or_rss
|
def html_xml_or_rss
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => "HTML" }
|
type.html { render body: "HTML" }
|
||||||
type.xml { render :text => "XML" }
|
type.xml { render body: "XML" }
|
||||||
type.rss { render :text => "RSS" }
|
type.rss { render body: "RSS" }
|
||||||
type.all { render :text => "Nothing" }
|
type.all { render body: "Nothing" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def js_or_html
|
def js_or_html
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => "HTML" }
|
type.html { render body: "HTML" }
|
||||||
type.js { render :text => "JS" }
|
type.js { render body: "JS" }
|
||||||
type.all { render :text => "Nothing" }
|
type.all { render body: "Nothing" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def json_or_yaml
|
def json_or_yaml
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.json { render :text => "JSON" }
|
type.json { render body: "JSON" }
|
||||||
type.yaml { render :text => "YAML" }
|
type.yaml { render body: "YAML" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def html_or_xml
|
def html_or_xml
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => "HTML" }
|
type.html { render body: "HTML" }
|
||||||
type.xml { render :text => "XML" }
|
type.xml { render body: "XML" }
|
||||||
type.all { render :text => "Nothing" }
|
type.all { render body: "Nothing" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def json_xml_or_html
|
def json_xml_or_html
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.json { render :text => 'JSON' }
|
type.json { render body: 'JSON' }
|
||||||
type.xml { render :xml => 'XML' }
|
type.xml { render :xml => 'XML' }
|
||||||
type.html { render :text => 'HTML' }
|
type.html { render body: 'HTML' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -56,14 +56,14 @@ def forced_xml
|
|||||||
request.format = :xml
|
request.format = :xml
|
||||||
|
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => "HTML" }
|
type.html { render body: "HTML" }
|
||||||
type.xml { render :text => "XML" }
|
type.xml { render body: "XML" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def just_xml
|
def just_xml
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.xml { render :text => "XML" }
|
type.xml { render body: "XML" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -81,52 +81,52 @@ def using_defaults_with_type_list
|
|||||||
def using_defaults_with_all
|
def using_defaults_with_all
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html
|
type.html
|
||||||
type.all{ render text: "ALL" }
|
type.all{ render body: "ALL" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def made_for_content_type
|
def made_for_content_type
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.rss { render :text => "RSS" }
|
type.rss { render body: "RSS" }
|
||||||
type.atom { render :text => "ATOM" }
|
type.atom { render body: "ATOM" }
|
||||||
type.all { render :text => "Nothing" }
|
type.all { render body: "Nothing" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_type_handling
|
def custom_type_handling
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => "HTML" }
|
type.html { render body: "HTML" }
|
||||||
type.custom("application/crazy-xml") { render :text => "Crazy XML" }
|
type.custom("application/crazy-xml") { render body: "Crazy XML" }
|
||||||
type.all { render :text => "Nothing" }
|
type.all { render body: "Nothing" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def custom_constant_handling
|
def custom_constant_handling
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => "HTML" }
|
type.html { render body: "HTML" }
|
||||||
type.mobile { render :text => "Mobile" }
|
type.mobile { render body: "Mobile" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_constant_handling_without_block
|
def custom_constant_handling_without_block
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => "HTML" }
|
type.html { render body: "HTML" }
|
||||||
type.mobile
|
type.mobile
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_any
|
def handle_any
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => "HTML" }
|
type.html { render body: "HTML" }
|
||||||
type.any(:js, :xml) { render :text => "Either JS or XML" }
|
type.any(:js, :xml) { render body: "Either JS or XML" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_any_any
|
def handle_any_any
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render :text => 'HTML' }
|
type.html { render body: 'HTML' }
|
||||||
type.any { render :text => 'Whatever you ask for, I got it' }
|
type.any { render body: 'Whatever you ask for, I got it' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -167,15 +167,15 @@ def variant_with_format_and_custom_render
|
|||||||
request.variant = :mobile
|
request.variant = :mobile
|
||||||
|
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html { render text: "mobile" }
|
type.html { render body: "mobile" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def multiple_variants_for_format
|
def multiple_variants_for_format
|
||||||
respond_to do |type|
|
respond_to do |type|
|
||||||
type.html do |html|
|
type.html do |html|
|
||||||
html.tablet { render text: "tablet" }
|
html.tablet { render body: "tablet" }
|
||||||
html.phone { render text: "phone" }
|
html.phone { render body: "phone" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -183,7 +183,7 @@ def multiple_variants_for_format
|
|||||||
def variant_plus_none_for_format
|
def variant_plus_none_for_format
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do |variant|
|
format.html do |variant|
|
||||||
variant.phone { render text: "phone" }
|
variant.phone { render body: "phone" }
|
||||||
variant.none
|
variant.none
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -191,9 +191,9 @@ def variant_plus_none_for_format
|
|||||||
|
|
||||||
def variant_inline_syntax
|
def variant_inline_syntax
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.js { render text: "js" }
|
format.js { render body: "js" }
|
||||||
format.html.none { render text: "none" }
|
format.html.none { render body: "none" }
|
||||||
format.html.phone { render text: "phone" }
|
format.html.phone { render body: "phone" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -208,8 +208,8 @@ def variant_inline_syntax_without_block
|
|||||||
def variant_any
|
def variant_any
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do |variant|
|
format.html do |variant|
|
||||||
variant.any(:tablet, :phablet){ render text: "any" }
|
variant.any(:tablet, :phablet){ render body: "any" }
|
||||||
variant.phone { render text: "phone" }
|
variant.phone { render body: "phone" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -217,23 +217,23 @@ def variant_any
|
|||||||
def variant_any_any
|
def variant_any_any
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do |variant|
|
format.html do |variant|
|
||||||
variant.any { render text: "any" }
|
variant.any { render body: "any" }
|
||||||
variant.phone { render text: "phone" }
|
variant.phone { render body: "phone" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def variant_inline_any
|
def variant_inline_any
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html.any(:tablet, :phablet){ render text: "any" }
|
format.html.any(:tablet, :phablet){ render body: "any" }
|
||||||
format.html.phone { render text: "phone" }
|
format.html.phone { render body: "phone" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def variant_inline_any_any
|
def variant_inline_any_any
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html.phone { render text: "phone" }
|
format.html.phone { render body: "phone" }
|
||||||
format.html.any { render text: "any" }
|
format.html.any { render body: "any" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -246,16 +246,16 @@ def variant_any_implicit_render
|
|||||||
|
|
||||||
def variant_any_with_none
|
def variant_any_with_none
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html.any(:none, :phone){ render text: "none or phone" }
|
format.html.any(:none, :phone){ render body: "none or phone" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_any_variant_any
|
def format_any_variant_any
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render text: "HTML" }
|
format.html { render body: "HTML" }
|
||||||
format.any(:js, :xml) do |variant|
|
format.any(:js, :xml) do |variant|
|
||||||
variant.phone{ render text: "phone" }
|
variant.phone{ render body: "phone" }
|
||||||
variant.any(:tablet, :phablet){ render text: "tablet" }
|
variant.any(:tablet, :phablet){ render body: "tablet" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -780,7 +780,7 @@ def test_variant_negotiation_without_block
|
|||||||
class RespondToWithBlockOnDefaultRenderController < ActionController::Base
|
class RespondToWithBlockOnDefaultRenderController < ActionController::Base
|
||||||
def show
|
def show
|
||||||
default_render do
|
default_render do
|
||||||
render text: 'default_render yielded'
|
render body: 'default_render yielded'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -794,6 +794,6 @@ def setup
|
|||||||
def test_default_render_uses_block_when_no_template_exists
|
def test_default_render_uses_block_when_no_template_exists
|
||||||
get :show
|
get :show
|
||||||
assert_equal "default_render yielded", @response.body
|
assert_equal "default_render yielded", @response.body
|
||||||
assert_equal "text/html", @response.content_type
|
assert_equal "text/plain", @response.content_type
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,7 +6,7 @@ class SimpleController < ActionController::Base
|
|||||||
before_action :authenticate
|
before_action :authenticate
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render :text => "success"
|
render body: "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
def modify_response_body
|
def modify_response_body
|
||||||
@ -22,7 +22,7 @@ def modify_response_headers
|
|||||||
end
|
end
|
||||||
|
|
||||||
def show_actions
|
def show_actions
|
||||||
render :text => "actions: #{action_methods.to_a.sort.join(', ')}"
|
render body: "actions: #{action_methods.to_a.sort.join(', ')}"
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
@ -51,7 +51,7 @@ class BaseTest < Rack::TestCase
|
|||||||
|
|
||||||
assert_body "success"
|
assert_body "success"
|
||||||
assert_status 200
|
assert_status 200
|
||||||
assert_content_type "text/html; charset=utf-8"
|
assert_content_type "text/plain; charset=utf-8"
|
||||||
end
|
end
|
||||||
|
|
||||||
# :api: plugin
|
# :api: plugin
|
||||||
|
@ -9,7 +9,7 @@ class BasicController < ActionController::Base
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
def all
|
def all
|
||||||
render :text => self.formats.inspect
|
render plain: self.formats.inspect
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
module ContentType
|
module ContentType
|
||||||
class BaseController < ActionController::Base
|
class BaseController < ActionController::Base
|
||||||
def index
|
def index
|
||||||
render :text => "Hello world!"
|
render body: "Hello world!"
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_on_response_obj
|
def set_on_response_obj
|
||||||
response.content_type = Mime::RSS
|
response.content_type = Mime::RSS
|
||||||
render :text => "Hello world!"
|
render body: "Hello world!"
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_on_render
|
def set_on_render
|
||||||
render :text => "Hello world!", :content_type => Mime::RSS
|
render body: "Hello world!", content_type: Mime::RSS
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -30,17 +30,17 @@ class ImpliedController < ActionController::Base
|
|||||||
class CharsetController < ActionController::Base
|
class CharsetController < ActionController::Base
|
||||||
def set_on_response_obj
|
def set_on_response_obj
|
||||||
response.charset = "utf-16"
|
response.charset = "utf-16"
|
||||||
render :text => "Hello world!"
|
render body: "Hello world!"
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_as_nil_on_response_obj
|
def set_as_nil_on_response_obj
|
||||||
response.charset = nil
|
response.charset = nil
|
||||||
render :text => "Hello world!"
|
render body: "Hello world!"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class ExplicitContentTypeTest < Rack::TestCase
|
class ExplicitContentTypeTest < Rack::TestCase
|
||||||
test "default response is HTML and UTF8" do
|
test "default response is text/plain and UTF8" do
|
||||||
with_routing do |set|
|
with_routing do |set|
|
||||||
set.draw do
|
set.draw do
|
||||||
get ':controller', :action => 'index'
|
get ':controller', :action => 'index'
|
||||||
@ -49,7 +49,7 @@ class ExplicitContentTypeTest < Rack::TestCase
|
|||||||
get "/content_type/base"
|
get "/content_type/base"
|
||||||
|
|
||||||
assert_body "Hello world!"
|
assert_body "Hello world!"
|
||||||
assert_header "Content-Type", "text/html; charset=utf-8"
|
assert_header "Content-Type", "text/plain; charset=utf-8"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -99,14 +99,14 @@ class ExplicitCharsetTest < Rack::TestCase
|
|||||||
get "/content_type/charset/set_on_response_obj"
|
get "/content_type/charset/set_on_response_obj"
|
||||||
|
|
||||||
assert_body "Hello world!"
|
assert_body "Hello world!"
|
||||||
assert_header "Content-Type", "text/html; charset=utf-16"
|
assert_header "Content-Type", "text/plain; charset=utf-16"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "setting the charset of the response as nil directly on the response object" do
|
test "setting the charset of the response as nil directly on the response object" do
|
||||||
get "/content_type/charset/set_as_nil_on_response_obj"
|
get "/content_type/charset/set_as_nil_on_response_obj"
|
||||||
|
|
||||||
assert_body "Hello world!"
|
assert_body "Hello world!"
|
||||||
assert_header "Content-Type", "text/html; charset=utf-8"
|
assert_header "Content-Type", "text/plain; charset=utf-8"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user