mirror of
https://github.com/github/rails.git
synced 2026-01-13 16:48:06 -05:00
Compare commits
334 Commits
3-2-github
...
v3.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9891ca89c6 | ||
|
|
e38fa05781 | ||
|
|
c7106e827e | ||
|
|
3d0c92868c | ||
|
|
a8a9f00058 | ||
|
|
ca73b5ba65 | ||
|
|
9f9c50f917 | ||
|
|
b63b6c4033 | ||
|
|
647ed22aad | ||
|
|
851552bd80 | ||
|
|
829df8007d | ||
|
|
a2c547f592 | ||
|
|
15e9b53a57 | ||
|
|
90d4a19285 | ||
|
|
0f94ca31b1 | ||
|
|
ef01f8840b | ||
|
|
3edd3d052e | ||
|
|
331234e0ab | ||
|
|
cc5a9c642b | ||
|
|
bf35d1e7c0 | ||
|
|
7f83aefd38 | ||
|
|
0c87873455 | ||
|
|
8da45763b2 | ||
|
|
5502a78c3e | ||
|
|
6c84fd80b7 | ||
|
|
c144f50347 | ||
|
|
e2d73f01a9 | ||
|
|
ba03dd4774 | ||
|
|
dbbf198f5c | ||
|
|
3ba8e31005 | ||
|
|
e8a083ecc2 | ||
|
|
39f2f18679 | ||
|
|
983a5e2970 | ||
|
|
2af7f29203 | ||
|
|
a2d3e6e29f | ||
|
|
473feeb32d | ||
|
|
d806aa2b68 | ||
|
|
daf2f95830 | ||
|
|
52e962086d | ||
|
|
b78011314e | ||
|
|
9df512be94 | ||
|
|
ace0f87056 | ||
|
|
1c2a2f711d | ||
|
|
a3161096c2 | ||
|
|
217fb3e9b5 | ||
|
|
dcdb8bae38 | ||
|
|
27512c2161 | ||
|
|
60685475dc | ||
|
|
3ecbae0672 | ||
|
|
59cd141c81 | ||
|
|
d7f1226412 | ||
|
|
6a035099b2 | ||
|
|
3b3cfa44f6 | ||
|
|
908544c90e | ||
|
|
2722c82f6e | ||
|
|
d810854d42 | ||
|
|
e12ff5b3bd | ||
|
|
61292a1f87 | ||
|
|
8bad8ac758 | ||
|
|
241dfa3c90 | ||
|
|
9ba46cf15a | ||
|
|
3dc9880866 | ||
|
|
da7a28027a | ||
|
|
bfe956d785 | ||
|
|
e991f39709 | ||
|
|
aec084955d | ||
|
|
55c1f351c4 | ||
|
|
d887dbc2d6 | ||
|
|
37467bf0fc | ||
|
|
30dcac2926 | ||
|
|
5deeb43fca | ||
|
|
78f6f0dc4b | ||
|
|
174d5cd7ee | ||
|
|
2ae4f01650 | ||
|
|
3a831cb7d6 | ||
|
|
0d7b8f8c83 | ||
|
|
91916e6c3c | ||
|
|
6b54a6a8ff | ||
|
|
6d1e87b16b | ||
|
|
7830f8d9f8 | ||
|
|
d8196bf994 | ||
|
|
aa384de7dd | ||
|
|
78486cb9c5 | ||
|
|
6579173814 | ||
|
|
a32f46d0ce | ||
|
|
c857bd23c0 | ||
|
|
5ccdb362b4 | ||
|
|
7a7012c717 | ||
|
|
8feb2856ea | ||
|
|
5708412052 | ||
|
|
822fa17c6c | ||
|
|
04aa14f8d7 | ||
|
|
bc61196bf5 | ||
|
|
aafb29073a | ||
|
|
880eaa145e | ||
|
|
fc6db6226f | ||
|
|
8931dd17a9 | ||
|
|
c8b84a1c8c | ||
|
|
36fcc99cce | ||
|
|
a1ca2e0a38 | ||
|
|
6a1ea881cf | ||
|
|
1cc653f9b3 | ||
|
|
eeb9b379f9 | ||
|
|
ce04ea973c | ||
|
|
ab64dc9c20 | ||
|
|
f10fb1c4e9 | ||
|
|
2570fda5ab | ||
|
|
b97ad85d44 | ||
|
|
d125687b97 | ||
|
|
2c81a31039 | ||
|
|
bedf6a0061 | ||
|
|
6edae4553e | ||
|
|
8235c9288f | ||
|
|
6ca6ef2ab0 | ||
|
|
0579963a38 | ||
|
|
7cb44a5092 | ||
|
|
df0a7bfb8f | ||
|
|
2106782828 | ||
|
|
e88f8bee5e | ||
|
|
11e9883f19 | ||
|
|
b4e5da6bde | ||
|
|
7dbc99ef0d | ||
|
|
d0e3323d5b | ||
|
|
68e2d1e496 | ||
|
|
9011f8f49c | ||
|
|
b4a520874a | ||
|
|
47f6d8b880 | ||
|
|
612c233a28 | ||
|
|
8f72ddc12b | ||
|
|
47280f083a | ||
|
|
c6391e6676 | ||
|
|
20088f6fff | ||
|
|
d033b237c4 | ||
|
|
809a04ba8f | ||
|
|
ac66de4a82 | ||
|
|
e509d4afc9 | ||
|
|
ad063263bc | ||
|
|
8f2b2781b0 | ||
|
|
6b52a58f72 | ||
|
|
1031fe1478 | ||
|
|
758f01d49e | ||
|
|
21c9795c15 | ||
|
|
49e406efb7 | ||
|
|
1ef9b98a31 | ||
|
|
bef90f8449 | ||
|
|
64f4dc68f6 | ||
|
|
a16ec2f4cf | ||
|
|
6fb6ddb9a7 | ||
|
|
632a224bd1 | ||
|
|
6580c6df36 | ||
|
|
ddce48a355 | ||
|
|
06632578c2 | ||
|
|
2f6383e340 | ||
|
|
7e85b16518 | ||
|
|
12f7f7a714 | ||
|
|
82eff0ffe9 | ||
|
|
588ac71213 | ||
|
|
0a41ece3e3 | ||
|
|
c40856c46c | ||
|
|
23303d6ab7 | ||
|
|
017840beb8 | ||
|
|
92f4cca4a3 | ||
|
|
82a58abe05 | ||
|
|
90176a6f15 | ||
|
|
4b21dfe9a7 | ||
|
|
c11ba424e7 | ||
|
|
21063e5e27 | ||
|
|
22d242c2ca | ||
|
|
6f478b0698 | ||
|
|
fc43c62fc6 | ||
|
|
b9281e8e2c | ||
|
|
5f5c508444 | ||
|
|
020aeb6192 | ||
|
|
141634ddc6 | ||
|
|
14be1789b7 | ||
|
|
1d2e075bf1 | ||
|
|
d1480926e8 | ||
|
|
047e411fd2 | ||
|
|
452a56ad51 | ||
|
|
83cb532009 | ||
|
|
c330e96a6e | ||
|
|
a34dce9717 | ||
|
|
b8ec4eaac5 | ||
|
|
688368100a | ||
|
|
19fb031d84 | ||
|
|
dbe5ae488e | ||
|
|
1091a6e9b7 | ||
|
|
8520045200 | ||
|
|
ebf7447b34 | ||
|
|
10177d3a38 | ||
|
|
f224c66a91 | ||
|
|
88fc37ff03 | ||
|
|
bdeeca358b | ||
|
|
5f7bfb1c3a | ||
|
|
ae7732f957 | ||
|
|
df8a941a43 | ||
|
|
6d68cde2c5 | ||
|
|
0f1b9bbbf8 | ||
|
|
477a9d4d86 | ||
|
|
a036999ed1 | ||
|
|
707248a629 | ||
|
|
ecf59b4776 | ||
|
|
677564f8f7 | ||
|
|
f6f7ae4020 | ||
|
|
b3ece73114 | ||
|
|
76c91a237c | ||
|
|
05ba082c6a | ||
|
|
3270c58ebb | ||
|
|
ccd4364a13 | ||
|
|
6373dd466f | ||
|
|
59e63e76c3 | ||
|
|
433d7a26fe | ||
|
|
79e15f0340 | ||
|
|
3698da65e5 | ||
|
|
108179b068 | ||
|
|
bacf78150c | ||
|
|
e86b758592 | ||
|
|
abd973689d | ||
|
|
fb6edb1769 | ||
|
|
e4283007d6 | ||
|
|
91ae6e9933 | ||
|
|
fe2d65864e | ||
|
|
9df227983f | ||
|
|
d03a1249a0 | ||
|
|
9528aa9f86 | ||
|
|
4dcce5d06e | ||
|
|
ab68d4b52e | ||
|
|
1e6e868d8c | ||
|
|
198bffe3be | ||
|
|
06af291346 | ||
|
|
30ea923040 | ||
|
|
7325dd21b3 | ||
|
|
f2d22ecbb3 | ||
|
|
1c970b8394 | ||
|
|
c8509d5303 | ||
|
|
36cb62eb9d | ||
|
|
bfd728182c | ||
|
|
ff760dd6ce | ||
|
|
5352a89d50 | ||
|
|
a56ee4c9a2 | ||
|
|
dac2b37b03 | ||
|
|
8464ee0650 | ||
|
|
e1b85c3bda | ||
|
|
1fbcd5f5fc | ||
|
|
43b8722f4b | ||
|
|
d9b77ddecd | ||
|
|
6f88b82263 | ||
|
|
b2eaac24c3 | ||
|
|
5859f5eee1 | ||
|
|
63ffec85b7 | ||
|
|
ca3fc4b325 | ||
|
|
aaa52c6d1f | ||
|
|
68bed3a4ad | ||
|
|
8968eecb93 | ||
|
|
3c404c56eb | ||
|
|
cbf89a378c | ||
|
|
2d681838c0 | ||
|
|
b02751c961 | ||
|
|
3ccf3504d2 | ||
|
|
98384b1ce8 | ||
|
|
a263a8ffd5 | ||
|
|
dd7e872e85 | ||
|
|
f85b206e7a | ||
|
|
22cbc3f0fa | ||
|
|
efb2bd0409 | ||
|
|
195e891954 | ||
|
|
28d82bd2e9 | ||
|
|
4d4b865b11 | ||
|
|
5c109e243f | ||
|
|
f7996be0c7 | ||
|
|
d55491c844 | ||
|
|
4b18d3c210 | ||
|
|
783dc5207b | ||
|
|
78c7705b32 | ||
|
|
5aec933385 | ||
|
|
413c9c8235 | ||
|
|
91930dc30b | ||
|
|
001a574785 | ||
|
|
a897a1f4a3 | ||
|
|
8fb0c9f509 | ||
|
|
0bb8d0561a | ||
|
|
bed98b9bf2 | ||
|
|
148dd2eac6 | ||
|
|
06e4c48815 | ||
|
|
0a86cb5972 | ||
|
|
e34fb808db | ||
|
|
2005f8234a | ||
|
|
05a49c7718 | ||
|
|
902d732617 | ||
|
|
84d5461d43 | ||
|
|
18bcc548bf | ||
|
|
ba9602b9e7 | ||
|
|
906ef233e4 | ||
|
|
4da32babdf | ||
|
|
87365272e9 | ||
|
|
f316a851dd | ||
|
|
19c77a0d2b | ||
|
|
2498cdaf14 | ||
|
|
963638aac8 | ||
|
|
fbc40a4d94 | ||
|
|
32d840d98a | ||
|
|
bd1cf94a29 | ||
|
|
d599e94e45 | ||
|
|
91e4249c02 | ||
|
|
07c5e5416b | ||
|
|
8158afa47e | ||
|
|
9269e55b1f | ||
|
|
b46b5a6d54 | ||
|
|
46a1da7c79 | ||
|
|
8b7219b9d6 | ||
|
|
109dc3c39c | ||
|
|
af8e085190 | ||
|
|
146a013c42 | ||
|
|
84703be5ff | ||
|
|
43cc69cb65 | ||
|
|
01186652cc | ||
|
|
7a1bba4799 | ||
|
|
4474470ffd | ||
|
|
9ae7f04cd6 | ||
|
|
1318bf6e33 | ||
|
|
5987fd4c79 | ||
|
|
677e1e58b6 | ||
|
|
3b170b2e14 | ||
|
|
79583ca9b1 | ||
|
|
257e9c4ec4 | ||
|
|
a44779e9bb | ||
|
|
503931df05 | ||
|
|
e1142dfcae | ||
|
|
1466f312ba | ||
|
|
2c8a4a53a8 | ||
|
|
fb2b8fec24 | ||
|
|
db1c484c55 | ||
|
|
807239f5a1 | ||
|
|
f73e9d2df8 |
20
Gemfile
20
Gemfile
@@ -10,8 +10,8 @@ gem "rails", :path => File.dirname(__FILE__)
|
||||
|
||||
gem "rake", ">= 0.8.7"
|
||||
gem "mocha", ">= 0.9.8"
|
||||
gem "rdoc", ">= 2.5.9"
|
||||
gem "horo"
|
||||
gem "rdoc", ">= 2.5.10"
|
||||
gem "horo", ">= 1.0.2"
|
||||
|
||||
# AS
|
||||
gem "memcache-client", ">= 1.8.5"
|
||||
@@ -35,6 +35,7 @@ platforms :ruby do
|
||||
group :db do
|
||||
gem "pg", ">= 0.9.0"
|
||||
gem "mysql", ">= 2.8.1"
|
||||
gem "mysql2", ">= 0.2.3"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -49,11 +50,14 @@ platforms :jruby do
|
||||
end
|
||||
end
|
||||
|
||||
env 'CI' do
|
||||
gem "nokogiri", ">= 1.4.3.1"
|
||||
|
||||
platforms :ruby_18 do
|
||||
# fcgi gem doesn't compile on 1.9
|
||||
gem "fcgi", ">= 0.8.8"
|
||||
# gems that are necessary for ActiveRecord tests with Oracle database
|
||||
if ENV['ORACLE_ENHANCED_PATH'] || ENV['ORACLE_ENHANCED']
|
||||
platforms :ruby do
|
||||
gem 'ruby-oci8', ">= 2.0.4"
|
||||
end
|
||||
if ENV['ORACLE_ENHANCED_PATH']
|
||||
gem 'activerecord-oracle_enhanced-adapter', :path => ENV['ORACLE_ENHANCED_PATH']
|
||||
else
|
||||
gem "activerecord-oracle_enhanced-adapter", :git => "git://github.com/rsim/oracle-enhanced.git"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.0.0.rc
|
||||
3.0.0
|
||||
|
||||
33
README.rdoc
33
README.rdoc
@@ -1,6 +1,6 @@
|
||||
== Welcome to Rails
|
||||
== Welcome to \Rails
|
||||
|
||||
Rails is a web-application framework that includes everything needed to create
|
||||
\Rails is a web-application framework that includes everything needed to create
|
||||
database-backed web applications according to the Model-View-Control pattern.
|
||||
|
||||
This pattern splits the view (also called the presentation) into "dumb"
|
||||
@@ -11,30 +11,30 @@ persist themselves to a database. The controller handles the incoming requests
|
||||
(such as Save New Account, Update Product, Show Post) by manipulating the model
|
||||
and directing data to the view.
|
||||
|
||||
In Rails, the model is handled by what's called an object-relational mapping
|
||||
In \Rails, the model is handled by what's called an object-relational mapping
|
||||
layer entitled Active Record. This layer allows you to present the data from
|
||||
database rows as objects and embellish these data objects with business logic
|
||||
methods. You can read more about Active Record in
|
||||
link:files/vendor/rails/activerecord/README.html.
|
||||
methods. You can read more about Active Record in its
|
||||
{README}[link:files/activerecord/README_rdoc.html].
|
||||
|
||||
The controller and view are handled by the Action Pack, which handles both
|
||||
layers by its two parts: Action View and Action Controller. These two layers
|
||||
are bundled in a single package due to their heavy interdependence. This is
|
||||
unlike the relationship between the Active Record and Action Pack that is much
|
||||
more separate. Each of these packages can be used independently outside of
|
||||
Rails. You can read more about Action Pack in
|
||||
link:files/vendor/rails/actionpack/README.html.
|
||||
\Rails. You can read more about Action Pack in its
|
||||
{README}[link:files/actionpack/README_rdoc.html].
|
||||
|
||||
|
||||
== Getting Started
|
||||
|
||||
1. Install Rails at the command prompt if you haven't yet:
|
||||
1. Install \Rails at the command prompt if you haven't yet:
|
||||
|
||||
gem install rails
|
||||
|
||||
2. At the command prompt, create a new Rails application:
|
||||
2. At the command prompt, create a new \Rails application:
|
||||
|
||||
rails new myapp
|
||||
rails new myapp
|
||||
|
||||
where "myapp" is the application name.
|
||||
|
||||
@@ -48,20 +48,21 @@ link:files/vendor/rails/actionpack/README.html.
|
||||
|
||||
"Welcome aboard: You're riding Ruby on Rails!"
|
||||
|
||||
5. Follow the guidelines to start developing your application. You can find
|
||||
the following resources handy:
|
||||
5. Follow the guidelines to start developing your application. You can find the following resources handy:
|
||||
|
||||
* The README file created within your application.
|
||||
* The {Getting Started Guide}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {Ruby on Rails Tutorial Book}[http://railstutorial.org/book].
|
||||
* The {Getting Started with Rails}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {Ruby on Rails Tutorial}[http://railstutorial.org/book].
|
||||
* The {Ruby on Rails guides}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {API documentation}[http://api.rubyonrails.org].
|
||||
|
||||
|
||||
== Contributing
|
||||
|
||||
We encourage you to contribute to Ruby on Rails! Please check out the {Contributing to Rails
|
||||
We encourage you to contribute to Ruby on \Rails! Please check out the {Contributing to Rails
|
||||
guide}[http://edgeguides.rubyonrails.org/contributing_to_rails.html] for guidelines about how
|
||||
to proceed. {Join us}[http://contributors.rubyonrails.org]!
|
||||
|
||||
== License
|
||||
|
||||
Ruby on Rails is released under the MIT license.
|
||||
Ruby on \Rails is released under the MIT license.
|
||||
|
||||
36
Rakefile
36
Rakefile
@@ -1,10 +1,35 @@
|
||||
gem 'rdoc', '>= 2.5.9'
|
||||
gem 'rdoc', '>= 2.5.10'
|
||||
require 'rdoc'
|
||||
|
||||
require 'rake'
|
||||
require 'rdoc/task'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
# RDoc skips some files in the Rails tree due to its binary? predicate. This is a quick
|
||||
# hack for edge docs, until we decide which is the correct way to address this issue.
|
||||
# If not fixed in RDoc itself, via an option or something, we should probably move this
|
||||
# to railties and use it also in doc:rails.
|
||||
def hijack_rdoc!
|
||||
require "rdoc/parser"
|
||||
class << RDoc::Parser
|
||||
def binary?(file)
|
||||
s = File.read(file, 1024) or return false
|
||||
|
||||
if s[0, 2] == Marshal.dump('')[0, 2] then
|
||||
true
|
||||
elsif file =~ /erb\.rb$/ then
|
||||
false
|
||||
elsif s.index("\x00") then # ORIGINAL is s.scan(/<%|%>/).length >= 4 || s.index("\x00")
|
||||
true
|
||||
elsif 0.respond_to? :fdiv then
|
||||
s.count("^ -~\t\r\n").fdiv(s.size) > 0.3
|
||||
else # HACK 1.8.6
|
||||
(s.count("^ -~\t\r\n").to_f / s.size) > 0.3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties)
|
||||
|
||||
desc 'Run all tests by default'
|
||||
@@ -63,6 +88,8 @@ end
|
||||
|
||||
desc "Generate documentation for the Rails framework"
|
||||
RDoc::Task.new do |rdoc|
|
||||
hijack_rdoc!
|
||||
|
||||
rdoc.rdoc_dir = 'doc/rdoc'
|
||||
rdoc.title = "Ruby on Rails Documentation"
|
||||
|
||||
@@ -90,6 +117,7 @@ RDoc::Task.new do |rdoc|
|
||||
|
||||
rdoc.rdoc_files.include('actionpack/README.rdoc')
|
||||
rdoc.rdoc_files.include('actionpack/CHANGELOG')
|
||||
rdoc.rdoc_files.include('actionpack/lib/abstract_controller/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_view/**/*.rb')
|
||||
@@ -116,12 +144,6 @@ task :rdoc do
|
||||
FileUtils.copy "activerecord/examples/associations.png", "doc/rdoc/files/examples/associations.png"
|
||||
end
|
||||
|
||||
desc "Publish API docs for Rails as a whole and for each component"
|
||||
task :pdoc => :rdoc do
|
||||
require 'rake/contrib/sshpublisher'
|
||||
Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/api", "doc/rdoc").upload
|
||||
end
|
||||
|
||||
task :update_versions do
|
||||
require File.dirname(__FILE__) + "/version"
|
||||
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
*Rails 3.0.0 [release candidate] (July 26th, 2010)*
|
||||
|
||||
* No material changes
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
|
||||
*Rails 3.0.0 (August 29, 2010)*
|
||||
|
||||
* subject is automatically looked up on I18n using mailer_name and action_name as scope as in t(".subject") [JK]
|
||||
|
||||
@@ -11,16 +6,10 @@
|
||||
|
||||
* Added ability to pass Proc objects to the defaults hash [ML]
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 3] (April 13th, 2010)*
|
||||
|
||||
* Removed all quoting.rb type files from ActionMailer and put Mail 2.2.0 in instead [ML]
|
||||
|
||||
* Lot of updates to various test cases that now work better with the new Mail and so have different expectations
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
|
||||
|
||||
* Added interceptors and observers from Mail [ML]
|
||||
|
||||
ActionMailer::Base.register_interceptor calls Mail.register_interceptor
|
||||
@@ -38,9 +27,6 @@
|
||||
|
||||
* Whole new API added with tests. See base.rb for full details. Old API is deprecated.
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 1] (February 4, 2010)*
|
||||
|
||||
* The Mail::Message class has helped methods for all the field types that return 'common' defaults for the common use case, so to get the subject, mail.subject will give you a string, mail.date will give you a DateTime object, mail.from will give you an array of address specs (mikel@test.lindsaar.net) etc. If you want to access the field object itself, call mail[:field_name] which will return the field object you want, which you can then chain, like mail[:from].formatted
|
||||
|
||||
* Mail#content_type now returns the content_type field as a string. If you want the mime type of a mail, then you call Mail#mime_type (eg, text/plain), if you want the parameters of the content type field, you call Mail#content_type_parameters which gives you a hash, eg {'format' => 'flowed', 'charset' => 'utf-8'}
|
||||
@@ -181,7 +167,7 @@
|
||||
|
||||
* ActionMailer::Base documentation rewrite. Closes #4991 [Kevin Clark, Marcel Molina Jr.]
|
||||
|
||||
* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
|
||||
* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
|
||||
|
||||
* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
|
||||
|
||||
@@ -327,7 +313,7 @@
|
||||
|
||||
* Added that deliver_* will now return the email that was sent
|
||||
|
||||
* Added that quoting to UTF-8 only happens if the characters used are in that range #955 [Jamis Buck]
|
||||
* Added that quoting to UTF-8 only happens if the characters used are in that range #955 [Jamis Buck]
|
||||
|
||||
* Fixed quoting for all address headers, not just to #955 [Jamis Buck]
|
||||
|
||||
@@ -366,7 +352,7 @@
|
||||
@body = "Nothing to see here."
|
||||
@charset = "iso-8859-1"
|
||||
end
|
||||
|
||||
|
||||
def unencoded_subject(recipient)
|
||||
@recipients = recipient
|
||||
@subject = "testing unencoded subject"
|
||||
@@ -375,7 +361,7 @@
|
||||
@encode_subject = false
|
||||
@charset = "iso-8859-1"
|
||||
end
|
||||
|
||||
|
||||
|
||||
*0.6.1* (January 18th, 2005)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ are used to consolidate code for sending out forgotten passwords, welcome
|
||||
wishes on signup, invoices for billing, and any other use case that requires
|
||||
a written notification to either a person or another system.
|
||||
|
||||
Action Mailer is in essence a wrapper around Action Controller and the
|
||||
Action Mailer is in essence a wrapper around Action Controller and the
|
||||
Mail gem. It provides a way to make emails using templates in the same
|
||||
way that Action Controller renders views using templates.
|
||||
|
||||
@@ -23,7 +23,7 @@ This can be as simple as:
|
||||
|
||||
class Notifier < ActionMailer::Base
|
||||
delivers_from 'system@loudthinking.com'
|
||||
|
||||
|
||||
def welcome(recipient)
|
||||
@recipient = recipient
|
||||
mail(:to => recipient,
|
||||
@@ -36,13 +36,13 @@ ERb) that has the instance variables that are declared in the mailer action.
|
||||
|
||||
So the corresponding body template for the method above could look like this:
|
||||
|
||||
Hello there,
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>
|
||||
|
||||
Thank you for signing up!
|
||||
|
||||
And if the recipient was given as "david@loudthinking.com", the email
|
||||
|
||||
And if the recipient was given as "david@loudthinking.com", the email
|
||||
generated would look like this:
|
||||
|
||||
Date: Mon, 25 Jan 2010 22:48:09 +1100
|
||||
@@ -55,7 +55,7 @@ generated would look like this:
|
||||
charset="US-ASCII";
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Hello there,
|
||||
Hello there,
|
||||
|
||||
Mr. david@loudthinking.com
|
||||
|
||||
@@ -65,8 +65,8 @@ simply call the method and optionally call +deliver+ on the return value.
|
||||
|
||||
Calling the method returns a Mail Message object:
|
||||
|
||||
message = Notifier.welcome #=> Returns a Mail::Message object
|
||||
message.deliver #=> delivers the email
|
||||
message = Notifier.welcome # => Returns a Mail::Message object
|
||||
message.deliver # => delivers the email
|
||||
|
||||
Or you can just chain the methods together like:
|
||||
|
||||
@@ -75,7 +75,7 @@ Or you can just chain the methods together like:
|
||||
== Receiving emails
|
||||
|
||||
To receive emails, you need to implement a public instance method called <tt>receive</tt> that takes a
|
||||
tmail object as its single parameter. The Action Mailer framework has a corresponding class method,
|
||||
tmail object as its single parameter. The Action Mailer framework has a corresponding class method,
|
||||
which is also called <tt>receive</tt>, that accepts a raw, unprocessed email as a string, which it then turns
|
||||
into the tmail object and calls the receive instance method.
|
||||
|
||||
@@ -90,7 +90,7 @@ Example:
|
||||
|
||||
if email.has_attachments?
|
||||
for attachment in email.attachments
|
||||
page.attachments.create({
|
||||
page.attachments.create({
|
||||
:file => attachment, :description => email.subject
|
||||
})
|
||||
end
|
||||
@@ -98,13 +98,13 @@ Example:
|
||||
end
|
||||
end
|
||||
|
||||
This Mailman can be the target for Postfix or other MTAs. In Rails, you would use the runner in the
|
||||
This Mailman can be the target for Postfix or other MTAs. In Rails, you would use the runner in the
|
||||
trivial case like this:
|
||||
|
||||
rails runner 'Mailman.receive(STDIN.read)'
|
||||
|
||||
However, invoking Rails in the runner for each mail to be received is very resource intensive. A single
|
||||
instance of Rails should be run within a daemon if it is going to be utilized to process more than just
|
||||
However, invoking Rails in the runner for each mail to be received is very resource intensive. A single
|
||||
instance of Rails should be run within a daemon if it is going to be utilized to process more than just
|
||||
a limited number of email.
|
||||
|
||||
== Configuration
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
gem 'rdoc', '>= 2.5.9'
|
||||
require 'rdoc'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rdoc/task'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
@@ -25,19 +22,6 @@ namespace :test do
|
||||
end
|
||||
end
|
||||
|
||||
# Generate the RDoc documentation
|
||||
RDoc::Task.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Action Mailer -- Easy email delivery and testing"
|
||||
rdoc.options << '--charset' << 'utf-8'
|
||||
rdoc.options << '-f' << 'horo'
|
||||
rdoc.options << '--main' << 'README.rdoc'
|
||||
rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG')
|
||||
rdoc.rdoc_files.include('lib/action_mailer.rb')
|
||||
rdoc.rdoc_files.include('lib/action_mailer/*.rb')
|
||||
rdoc.rdoc_files.include('lib/action_mailer/delivery_method/*.rb')
|
||||
}
|
||||
|
||||
spec = eval(File.read('actionmailer.gemspec'))
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
|
||||
@@ -187,31 +187,31 @@ module ActionMailer #:nodoc:
|
||||
# with the filename +free_book.pdf+.
|
||||
#
|
||||
# = Inline Attachments
|
||||
#
|
||||
# You can also specify that a file should be displayed inline with other HTML. This is useful
|
||||
#
|
||||
# You can also specify that a file should be displayed inline with other HTML. This is useful
|
||||
# if you want to display a corporate logo or a photo.
|
||||
#
|
||||
#
|
||||
# class ApplicationMailer < ActionMailer::Base
|
||||
# def welcome(recipient)
|
||||
# attachments.inline['photo.png'] = File.read('path/to/photo.png')
|
||||
# mail(:to => recipient, :subject => "Here is what we look like")
|
||||
# end
|
||||
# end
|
||||
#
|
||||
#
|
||||
# And then to reference the image in the view, you create a <tt>welcome.html.erb</tt> file and
|
||||
# make a call to +image_tag+ passing in the attachment you want to display and then call
|
||||
# make a call to +image_tag+ passing in the attachment you want to display and then call
|
||||
# +url+ on the attachment to get the relative content id path for the image source:
|
||||
#
|
||||
#
|
||||
# <h1>Please Don't Cringe</h1>
|
||||
#
|
||||
#
|
||||
# <%= image_tag attachments['photo.png'].url -%>
|
||||
#
|
||||
#
|
||||
# As we are using Action View's +image_tag+ method, you can pass in any other options you want:
|
||||
#
|
||||
#
|
||||
# <h1>Please Don't Cringe</h1>
|
||||
#
|
||||
#
|
||||
# <%= image_tag attachments['photo.png'].url, :alt => 'Our Photo', :class => 'photo' -%>
|
||||
#
|
||||
#
|
||||
# = Observing and Intercepting Mails
|
||||
#
|
||||
# Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to
|
||||
@@ -446,6 +446,33 @@ module ActionMailer #:nodoc:
|
||||
super
|
||||
end
|
||||
|
||||
class DeprecatedHeaderProxy < ActiveSupport::BasicObject
|
||||
def initialize(message)
|
||||
@message = message
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
unless value.is_a?(::String)
|
||||
::ActiveSupport::Deprecation.warn("Using a non-String object for a header's value is deprecated. " \
|
||||
"You specified #{value.inspect} (a #{value.class}) for #{key}", caller)
|
||||
|
||||
value = value.to_s
|
||||
end
|
||||
|
||||
@message[key] = value
|
||||
end
|
||||
|
||||
def headers(hash = {})
|
||||
hash.each_pair do |k,v|
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(meth, *args, &block)
|
||||
@message.send(meth, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
# Allows you to pass random and unusual headers to the new +Mail::Message+ object
|
||||
# which will add them to itself.
|
||||
#
|
||||
@@ -462,9 +489,9 @@ module ActionMailer #:nodoc:
|
||||
# X-Special-Domain-Specific-Header: SecretValue
|
||||
def headers(args=nil)
|
||||
if args
|
||||
@_message.headers(args)
|
||||
DeprecatedHeaderProxy.new(@_message).headers(args)
|
||||
else
|
||||
@_message
|
||||
DeprecatedHeaderProxy.new(@_message)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ module ActionMailer
|
||||
# as alias and the default options supplied:
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
#
|
||||
# add_delivery_method :sendmail, Mail::Sendmail,
|
||||
# :location => '/usr/sbin/sendmail',
|
||||
# :arguments => '-i -t'
|
||||
#
|
||||
#
|
||||
def add_delivery_method(symbol, klass, default_options={})
|
||||
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
|
||||
send(:"#{symbol}_settings=", default_options)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/object/try'
|
||||
|
||||
module ActionMailer
|
||||
# This is the API which is deprecated and is going to be removed on Rails 3.1 release.
|
||||
# Part of the old API will be deprecated after 3.1, for a smoother deprecation process.
|
||||
@@ -94,6 +96,12 @@ module ActionMailer
|
||||
def render(*args)
|
||||
options = args.last.is_a?(Hash) ? args.last : {}
|
||||
|
||||
if file = options[:file] and !file.index("/")
|
||||
ActiveSupport::Deprecation.warn("render :file is deprecated except for absolute paths. " \
|
||||
"Please use render :template instead")
|
||||
options[:prefix] = _prefix
|
||||
end
|
||||
|
||||
if options[:body].is_a?(Hash)
|
||||
ActiveSupport::Deprecation.warn(':body in render deprecated. Please use instance ' <<
|
||||
'variables as assigns instead', caller[0,1])
|
||||
|
||||
@@ -15,11 +15,11 @@ module ActionMailer
|
||||
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
|
||||
).format
|
||||
}.join("\n")
|
||||
|
||||
|
||||
# Make list points stand on their own line
|
||||
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
|
||||
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
|
||||
|
||||
|
||||
formatted
|
||||
end
|
||||
|
||||
|
||||
@@ -116,36 +116,36 @@ module ActionMailer
|
||||
|
||||
def normalize_nonfile_hash(params)
|
||||
content_disposition = "attachment;"
|
||||
|
||||
|
||||
mime_type = params.delete(:mime_type)
|
||||
|
||||
|
||||
if content_type = params.delete(:content_type)
|
||||
content_type = "#{mime_type || content_type};"
|
||||
end
|
||||
|
||||
params[:body] = params.delete(:data) if params[:data]
|
||||
|
||||
{ :content_type => content_type,
|
||||
:content_disposition => content_disposition }.merge(params)
|
||||
end
|
||||
|
||||
def normalize_file_hash(params)
|
||||
filename = File.basename(params.delete(:filename))
|
||||
content_disposition = "attachment; filename=\"#{File.basename(filename)}\""
|
||||
|
||||
mime_type = params.delete(:mime_type)
|
||||
|
||||
if (content_type = params.delete(:content_type)) && (content_type !~ /filename=/)
|
||||
content_type = "#{mime_type || content_type}; filename=\"#{filename}\""
|
||||
end
|
||||
|
||||
params[:body] = params.delete(:data) if params[:data]
|
||||
|
||||
|
||||
{ :content_type => content_type,
|
||||
:content_disposition => content_disposition }.merge(params)
|
||||
end
|
||||
|
||||
def create_mail
|
||||
def normalize_file_hash(params)
|
||||
filename = File.basename(params.delete(:filename))
|
||||
content_disposition = "attachment; filename=\"#{File.basename(filename)}\""
|
||||
|
||||
mime_type = params.delete(:mime_type)
|
||||
|
||||
if (content_type = params.delete(:content_type)) && (content_type !~ /filename=/)
|
||||
content_type = "#{mime_type || content_type}; filename=\"#{filename}\""
|
||||
end
|
||||
|
||||
params[:body] = params.delete(:data) if params[:data]
|
||||
|
||||
{ :content_type => content_type,
|
||||
:content_disposition => content_disposition }.merge(params)
|
||||
end
|
||||
|
||||
def create_mail
|
||||
m = @_message
|
||||
|
||||
set_fields!({:subject => subject, :to => recipients, :from => from,
|
||||
@@ -178,14 +178,14 @@ module ActionMailer
|
||||
|
||||
wrap_delivery_behavior!
|
||||
m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii?
|
||||
|
||||
|
||||
@_message
|
||||
end
|
||||
|
||||
|
||||
# Set up the default values for the various instance variables of this
|
||||
# mailer. Subclasses may override this method to provide different
|
||||
# defaults.
|
||||
def initialize_defaults(method_name)
|
||||
def initialize_defaults(method_name)
|
||||
@charset ||= self.class.default[:charset].try(:dup)
|
||||
@content_type ||= self.class.default[:content_type].try(:dup)
|
||||
@implicit_parts_order ||= self.class.default[:parts_order].try(:dup)
|
||||
@@ -201,7 +201,7 @@ module ActionMailer
|
||||
@body ||= {}
|
||||
end
|
||||
|
||||
def create_parts
|
||||
def create_parts
|
||||
if String === @body
|
||||
@parts.unshift create_inline_part(@body)
|
||||
elsif @parts.empty? || @parts.all? { |p| p.content_disposition =~ /^attachment/ }
|
||||
@@ -220,7 +220,7 @@ module ActionMailer
|
||||
end
|
||||
end
|
||||
|
||||
def create_inline_part(body, mime_type=nil)
|
||||
def create_inline_part(body, mime_type=nil)
|
||||
ct = mime_type || "text/plain"
|
||||
main_type, sub_type = split_content_type(ct.to_s)
|
||||
|
||||
@@ -242,11 +242,11 @@ module ActionMailer
|
||||
m.reply_to ||= headers.delete(:reply_to) if headers[:reply_to]
|
||||
end
|
||||
|
||||
def split_content_type(ct)
|
||||
def split_content_type(ct)
|
||||
ct.to_s.split("/")
|
||||
end
|
||||
|
||||
def parse_content_type(defaults=nil)
|
||||
def parse_content_type(defaults=nil)
|
||||
if @content_type.blank?
|
||||
[ nil, {} ]
|
||||
else
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
module Mail
|
||||
class Message
|
||||
|
||||
|
||||
def set_content_type(*args)
|
||||
ActiveSupport::Deprecation.warn('Message#set_content_type is deprecated, please just call ' <<
|
||||
'Message#content_type with the same arguments', caller[0,2])
|
||||
content_type(*args)
|
||||
end
|
||||
|
||||
|
||||
alias :old_transfer_encoding :transfer_encoding
|
||||
def transfer_encoding(value = nil)
|
||||
if value
|
||||
@@ -29,6 +29,6 @@ module Mail
|
||||
'please call Message#filename', caller[0,2])
|
||||
filename
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
@@ -3,8 +3,7 @@ module ActionMailer
|
||||
MAJOR = 3
|
||||
MINOR = 0
|
||||
TINY = 0
|
||||
BUILD = "rc"
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,8 +11,18 @@ ensure
|
||||
$VERBOSE = old
|
||||
end
|
||||
|
||||
|
||||
require 'active_support/core_ext/kernel/reporting'
|
||||
|
||||
require 'active_support/core_ext/string/encoding'
|
||||
if "ruby".encoding_aware?
|
||||
# These are the normal settings that will be set up by Railties
|
||||
# TODO: Have these tests support other combinations of these values
|
||||
silence_warnings do
|
||||
Encoding.default_internal = "UTF-8"
|
||||
Encoding.default_external = "UTF-8"
|
||||
end
|
||||
end
|
||||
|
||||
silence_warnings do
|
||||
# These external dependencies have warnings :/
|
||||
require 'text/format'
|
||||
@@ -67,4 +77,4 @@ end
|
||||
|
||||
def restore_delivery_method
|
||||
ActionMailer::Base.delivery_method = @old_delivery_method
|
||||
end
|
||||
end
|
||||
|
||||
@@ -76,6 +76,11 @@ class BaseTest < ActiveSupport::TestCase
|
||||
assert_equal("Not SPAM", email['X-SPAM'].decoded)
|
||||
end
|
||||
|
||||
test "deprecated non-String custom headers" do
|
||||
email = assert_deprecated { BaseMailer.welcome_with_fixnum_header }
|
||||
assert_equal("2", email['X-SPAM-COUNT'].decoded)
|
||||
end
|
||||
|
||||
test "can pass random headers in as a hash to mail" do
|
||||
hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
|
||||
'In-Reply-To' => '1234@mikel.me.com' }
|
||||
@@ -148,7 +153,7 @@ class BaseTest < ActiveSupport::TestCase
|
||||
assert_equal("application/pdf", email.parts[1].mime_type)
|
||||
assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
|
||||
test "can embed an inline attachment" do
|
||||
email = BaseMailer.inline_attachment
|
||||
# Need to call #encoded to force the JIT sort on parts
|
||||
@@ -209,6 +214,12 @@ class BaseTest < ActiveSupport::TestCase
|
||||
assert_equal "New Subject!", email.subject
|
||||
end
|
||||
|
||||
test "translations are scoped properly" do
|
||||
I18n.backend.store_translations('en', :base_mailer => {:email_with_translations => {:greet_user => "Hello %{name}!"}})
|
||||
email = BaseMailer.email_with_translations
|
||||
assert_equal 'Hello lifo!', email.body.encoded
|
||||
end
|
||||
|
||||
# Implicit multipart
|
||||
test "implicit multipart" do
|
||||
email = BaseMailer.implicit_multipart
|
||||
@@ -413,7 +424,7 @@ class BaseTest < ActiveSupport::TestCase
|
||||
BaseMailer.welcome.deliver
|
||||
assert_equal(1, BaseMailer.deliveries.length)
|
||||
end
|
||||
|
||||
|
||||
test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
|
||||
mail = Mail::Message.new
|
||||
mail.expects(:do_delivery).once
|
||||
@@ -428,6 +439,13 @@ class BaseTest < ActiveSupport::TestCase
|
||||
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
|
||||
end
|
||||
|
||||
test "render :file uses render :template semantics and is deprecated" do
|
||||
mail = nil
|
||||
assert_deprecated { mail = BaseMailer.implicit_different_template_with_file('implicit_multipart').deliver }
|
||||
assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
|
||||
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
|
||||
end
|
||||
|
||||
test "you can specify a different template for explicit render" do
|
||||
mail = BaseMailer.explicit_different_template('explicit_multipart_templates').deliver
|
||||
assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
|
||||
@@ -447,7 +465,7 @@ class BaseTest < ActiveSupport::TestCase
|
||||
mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer']).deliver
|
||||
assert_equal("Welcome from another path", mail.body.encoded)
|
||||
end
|
||||
|
||||
|
||||
test "assets tags should use ActionMailer's asset_host settings" do
|
||||
ActionMailer::Base.config.asset_host = "http://global.com"
|
||||
ActionMailer::Base.config.assets_dir = "global/"
|
||||
@@ -456,7 +474,7 @@ class BaseTest < ActiveSupport::TestCase
|
||||
|
||||
assert_equal(%{<img alt="Dummy" src="http://global.com/images/dummy.png" />}, mail.body.to_s.strip)
|
||||
end
|
||||
|
||||
|
||||
test "assets tags should use a Mailer's asset_host settings when available" do
|
||||
ActionMailer::Base.config.asset_host = "global.com"
|
||||
ActionMailer::Base.config.assets_dir = "global/"
|
||||
@@ -469,12 +487,12 @@ class BaseTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
# Before and After hooks
|
||||
|
||||
|
||||
class MyObserver
|
||||
def self.delivered_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
test "you can register an observer to the mail object that gets informed on email delivery" do
|
||||
ActionMailer::Base.register_observer(MyObserver)
|
||||
mail = BaseMailer.welcome
|
||||
@@ -493,7 +511,7 @@ class BaseTest < ActiveSupport::TestCase
|
||||
MyInterceptor.expects(:delivering_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
|
||||
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
|
||||
mail1 = ProcMailer.welcome
|
||||
yesterday = 1.day.ago
|
||||
@@ -501,12 +519,24 @@ class BaseTest < ActiveSupport::TestCase
|
||||
mail2 = ProcMailer.welcome
|
||||
assert(mail1['X-Proc-Method'].to_s.to_i > mail2['X-Proc-Method'].to_s.to_i)
|
||||
end
|
||||
|
||||
|
||||
test "we can call other defined methods on the class as needed" do
|
||||
mail = ProcMailer.welcome
|
||||
assert_equal("Thanks for signing up this afternoon", mail.subject)
|
||||
end
|
||||
|
||||
test "action methods should be refreshed after defining new method" do
|
||||
class FooMailer < ActionMailer::Base
|
||||
# this triggers action_methods
|
||||
self.respond_to?(:foo)
|
||||
|
||||
def notify
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal ["notify"], FooMailer.action_methods
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Execute the block setting the given values and restoring old values after
|
||||
|
||||
@@ -128,7 +128,7 @@ class MailDeliveryTest < ActiveSupport::TestCase
|
||||
Mail::Message.any_instance.expects(:deliver!).never
|
||||
DeliveryMailer.welcome.deliver
|
||||
end
|
||||
|
||||
|
||||
test "does not append the deliveries collection if told not to perform the delivery" do
|
||||
DeliveryMailer.perform_deliveries = false
|
||||
DeliveryMailer.deliveries.clear
|
||||
@@ -160,7 +160,7 @@ class MailDeliveryTest < ActiveSupport::TestCase
|
||||
DeliveryMailer.welcome.deliver
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
test "does not increment the deliveries collection on bogus deliveries" do
|
||||
DeliveryMailer.delivery_method = BogusDelivery
|
||||
DeliveryMailer.raise_delivery_errors = false
|
||||
@@ -168,5 +168,5 @@ class MailDeliveryTest < ActiveSupport::TestCase
|
||||
DeliveryMailer.welcome.deliver
|
||||
assert_equal(0, DeliveryMailer.deliveries.length)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
1
actionmailer/test/fixtures/base_mailer/email_with_translations.html.erb
vendored
Normal file
1
actionmailer/test/fixtures/base_mailer/email_with_translations.html.erb
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<%= t('.greet_user', :name => 'lifo') %>
|
||||
2
actionmailer/test/fixtures/raw_email10
vendored
2
actionmailer/test/fixtures/raw_email10
vendored
@@ -15,6 +15,6 @@ Content-Type: text/plain; charset=X-UNKNOWN
|
||||
Test test. Hi. Waving. m
|
||||
|
||||
----------------------------------------------------------------
|
||||
Sent via Bell Mobility's Text Messaging service.
|
||||
Sent via Bell Mobility's Text Messaging service.
|
||||
Envoyé par le service de messagerie texte de Bell Mobilité.
|
||||
----------------------------------------------------------------
|
||||
|
||||
2
actionmailer/test/fixtures/raw_email2
vendored
2
actionmailer/test/fixtures/raw_email2
vendored
@@ -32,7 +32,7 @@ To: xxxxx xxxx <xxxxx@xxxxxxxxx.com>
|
||||
Subject: Fwd: Signed email causes file attachments
|
||||
In-Reply-To: <F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@mac.com>
|
||||
Mime-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="----=_Part_5028_7368284.1115579351471"
|
||||
References: <F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@mac.com>
|
||||
|
||||
|
||||
2
actionmailer/test/fixtures/raw_email3
vendored
2
actionmailer/test/fixtures/raw_email3
vendored
@@ -31,7 +31,7 @@ Reply-To: Test Tester <xxxx@xxxx.com>
|
||||
To: xxxx@xxxx.com, xxxx@xxxx.com
|
||||
Subject: Another PDF
|
||||
Mime-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="----=_Part_2192_32400445.1115745999735"
|
||||
X-Virus-Scanned: amavisd-new at textdrive.com
|
||||
|
||||
|
||||
2
actionmailer/test/fixtures/raw_email5
vendored
2
actionmailer/test/fixtures/raw_email5
vendored
@@ -14,6 +14,6 @@ Importance: normal
|
||||
Test test. Hi. Waving. m
|
||||
|
||||
----------------------------------------------------------------
|
||||
Sent via Bell Mobility's Text Messaging service.
|
||||
Sent via Bell Mobility's Text Messaging service.
|
||||
Envoyé par le service de messagerie texte de Bell Mobilité.
|
||||
----------------------------------------------------------------
|
||||
|
||||
2
actionmailer/test/fixtures/raw_email6
vendored
2
actionmailer/test/fixtures/raw_email6
vendored
@@ -15,6 +15,6 @@ Content-Type: text/plain; charset=us-ascii
|
||||
Test test. Hi. Waving. m
|
||||
|
||||
----------------------------------------------------------------
|
||||
Sent via Bell Mobility's Text Messaging service.
|
||||
Sent via Bell Mobility's Text Messaging service.
|
||||
Envoyé par le service de messagerie texte de Bell Mobilité.
|
||||
----------------------------------------------------------------
|
||||
|
||||
2
actionmailer/test/fixtures/raw_email8
vendored
2
actionmailer/test/fixtures/raw_email8
vendored
@@ -8,7 +8,7 @@ To: xxxxx xxxx <xxxxx@xxxxxxxxx.com>
|
||||
Subject: Fwd: Signed email causes file attachments
|
||||
In-Reply-To: <F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@mac.com>
|
||||
Mime-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="----=_Part_5028_7368284.1115579351471"
|
||||
References: <F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@mac.com>
|
||||
|
||||
|
||||
10
actionmailer/test/fixtures/raw_email9
vendored
10
actionmailer/test/fixtures/raw_email9
vendored
@@ -10,19 +10,19 @@ Date: Wed, 23 Feb 2005 18:20:17 -0400
|
||||
From: "xxx xxx" <xxx@xxx.xxx>
|
||||
Message-ID: <4D6AA7EB.6490534@xxx.xxx>
|
||||
To: xxx@xxx.com
|
||||
Subject: Stop adware/spyware once and for all.
|
||||
Subject: Stop adware/spyware once and for all.
|
||||
X-Scanned-By: MIMEDefang 2.11 (www dot roaringpenguin dot com slash mimedefang)
|
||||
|
||||
You are infected with:
|
||||
You are infected with:
|
||||
Ad Ware and Spy Ware
|
||||
|
||||
Get your free scan and removal download now,
|
||||
before it gets any worse.
|
||||
Get your free scan and removal download now,
|
||||
before it gets any worse.
|
||||
|
||||
http://xxx.xxx.info?aid=3D13&?stat=3D4327kdzt
|
||||
|
||||
|
||||
|
||||
|
||||
no more? (you will still be infected)
|
||||
no more? (you will still be infected)
|
||||
http://xxx.xxx.info/discon/?xxx@xxx.com
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Hello there,
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>
|
||||
@@ -1,6 +1,6 @@
|
||||
%p Hello there,
|
||||
%p Hello there,
|
||||
|
||||
%p
|
||||
%p
|
||||
Mr.
|
||||
= @recipient
|
||||
from haml
|
||||
@@ -1,6 +1,6 @@
|
||||
%p Hello there,
|
||||
%p Hello there,
|
||||
|
||||
%p
|
||||
%p
|
||||
Mr.
|
||||
= @recipient
|
||||
from haml
|
||||
@@ -1,3 +1,3 @@
|
||||
Hello there,
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>
|
||||
@@ -1,4 +1,4 @@
|
||||
Hello there,
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>. Please see our greeting at <%= @welcome_url %> <%= welcome_url %>
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ class BaseMailer < ActionMailer::Base
|
||||
mail({:subject => "The first email on new API!"}.merge!(hash))
|
||||
end
|
||||
|
||||
def welcome_with_fixnum_header(hash = {})
|
||||
headers['X-SPAM-COUNT'] = 2
|
||||
mail({:template_name => "welcome", :subject => "The first email on new API!"}.merge!(hash))
|
||||
end
|
||||
|
||||
def welcome_with_headers(hash = {})
|
||||
headers hash
|
||||
mail
|
||||
@@ -98,6 +103,13 @@ class BaseMailer < ActionMailer::Base
|
||||
mail(:template_name => template_name)
|
||||
end
|
||||
|
||||
def implicit_different_template_with_file(template_name='')
|
||||
mail do |format|
|
||||
format.text { render :file => template_name }
|
||||
format.html { render :file => template_name }
|
||||
end
|
||||
end
|
||||
|
||||
def explicit_different_template(template_name='')
|
||||
mail do |format|
|
||||
format.text { render :template => "#{mailer_name}/#{template_name}" }
|
||||
@@ -111,4 +123,8 @@ class BaseMailer < ActionMailer::Base
|
||||
format.html { render :layout => layout_name }
|
||||
end
|
||||
end
|
||||
|
||||
def email_with_translations
|
||||
body render("email_with_translations.html")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,11 +6,11 @@ class ProcMailer < ActionMailer::Base
|
||||
def welcome
|
||||
mail
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
|
||||
def give_a_greeting
|
||||
"Thanks for signing up this afternoon"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -112,7 +112,7 @@ class RenderHelperTest < Test::Unit::TestCase
|
||||
|
||||
def test_file_template
|
||||
mail = RenderMailer.file_template
|
||||
assert_equal "Hello there, \n\nMr. test@localhost", mail.body.to_s.strip
|
||||
assert_equal "Hello there,\n\nMr. test@localhost", mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_rxml_template
|
||||
|
||||
@@ -106,7 +106,7 @@ class TestMailer < ActionMailer::Base
|
||||
cc "Foo áëô îü <extended@example.net>"
|
||||
bcc "Foo áëô îü <extended@example.net>"
|
||||
charset "UTF-8"
|
||||
|
||||
|
||||
body "åœö blah"
|
||||
end
|
||||
|
||||
@@ -359,7 +359,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
assert_equal "text/plain", created.parts[0].parts[0].mime_type
|
||||
assert_equal "text/html", created.parts[0].parts[1].mime_type
|
||||
assert_equal "application/octet-stream", created.parts[1].mime_type
|
||||
|
||||
|
||||
end
|
||||
|
||||
def test_nested_parts_with_body
|
||||
@@ -392,14 +392,14 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
expected = new_mail
|
||||
expected.to = @recipient
|
||||
expected.subject = "[Signed up] Welcome #{@recipient}"
|
||||
expected.body = "Hello there, \n\nMr. #{@recipient}"
|
||||
expected.body = "Hello there,\n\nMr. #{@recipient}"
|
||||
expected.from = "system@loudthinking.com"
|
||||
expected.date = Time.now
|
||||
|
||||
created = nil
|
||||
assert_nothing_raised { created = TestMailer.signed_up(@recipient) }
|
||||
assert_not_nil created
|
||||
|
||||
|
||||
expected.message_id = '<123@456>'
|
||||
created.message_id = '<123@456>'
|
||||
|
||||
@@ -420,7 +420,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
expected = new_mail
|
||||
expected.to = @recipient
|
||||
expected.subject = "[Signed up] Welcome #{@recipient}"
|
||||
expected.body = "Hello there, \n\nMr. #{@recipient}"
|
||||
expected.body = "Hello there,\n\nMr. #{@recipient}"
|
||||
expected.from = "system@loudthinking.com"
|
||||
expected.date = Time.local(2004, 12, 12)
|
||||
|
||||
@@ -503,7 +503,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
delivered = ActionMailer::Base.deliveries.first
|
||||
expected.message_id = '<123@456>'
|
||||
delivered.message_id = '<123@456>'
|
||||
|
||||
|
||||
assert_equal expected.encoded, delivered.encoded
|
||||
end
|
||||
|
||||
@@ -546,10 +546,10 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
created = TestMailer.different_reply_to @recipient
|
||||
end
|
||||
assert_not_nil created
|
||||
|
||||
|
||||
expected.message_id = '<123@456>'
|
||||
created.message_id = '<123@456>'
|
||||
|
||||
|
||||
assert_equal expected.encoded, created.encoded
|
||||
|
||||
assert_nothing_raised do
|
||||
@@ -558,10 +558,10 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
|
||||
delivered = ActionMailer::Base.deliveries.first
|
||||
assert_not_nil delivered
|
||||
|
||||
|
||||
expected.message_id = '<123@456>'
|
||||
delivered.message_id = '<123@456>'
|
||||
|
||||
|
||||
assert_equal expected.encoded, delivered.encoded
|
||||
end
|
||||
|
||||
@@ -581,7 +581,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
created = TestMailer.iso_charset @recipient
|
||||
end
|
||||
assert_not_nil created
|
||||
|
||||
|
||||
expected.message_id = '<123@456>'
|
||||
created.message_id = '<123@456>'
|
||||
|
||||
@@ -596,7 +596,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
|
||||
expected.message_id = '<123@456>'
|
||||
delivered.message_id = '<123@456>'
|
||||
|
||||
|
||||
assert_equal expected.encoded, delivered.encoded
|
||||
end
|
||||
|
||||
@@ -631,7 +631,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
|
||||
expected.message_id = '<123@456>'
|
||||
delivered.message_id = '<123@456>'
|
||||
|
||||
|
||||
assert_equal expected.encoded, delivered.encoded
|
||||
end
|
||||
|
||||
@@ -761,10 +761,10 @@ EOF
|
||||
|
||||
delivered = ActionMailer::Base.deliveries.first
|
||||
assert_not_nil delivered
|
||||
|
||||
|
||||
expected.message_id = '<123@456>'
|
||||
delivered.message_id = '<123@456>'
|
||||
|
||||
|
||||
assert_equal expected.encoded, delivered.encoded
|
||||
end
|
||||
|
||||
@@ -887,7 +887,7 @@ EOF
|
||||
assert_equal "iso-8859-1", mail.parts[1].charset
|
||||
|
||||
assert_equal "image/jpeg", mail.parts[2].mime_type
|
||||
|
||||
|
||||
assert_equal "attachment", mail.parts[2][:content_disposition].disposition_type
|
||||
assert_equal "foo.jpg", mail.parts[2][:content_disposition].filename
|
||||
assert_equal "foo.jpg", mail.parts[2][:content_type].filename
|
||||
@@ -1005,7 +1005,7 @@ EOF
|
||||
attachment = mail.attachments.last
|
||||
|
||||
expected = "01 Quien Te Dij\212at. Pitbull.mp3"
|
||||
|
||||
|
||||
if expected.respond_to?(:force_encoding)
|
||||
result = attachment.filename.dup
|
||||
expected.force_encoding(Encoding::ASCII_8BIT)
|
||||
|
||||
@@ -31,5 +31,5 @@ class TmailCompatTest < ActiveSupport::TestCase
|
||||
end
|
||||
assert_equal mail.content_transfer_encoding, "base64"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -68,7 +68,7 @@ class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
expected = new_mail
|
||||
expected.to = @recipient
|
||||
expected.subject = "[Signed up] Welcome #{@recipient}"
|
||||
expected.body = "Hello there, \n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting http://www.basecamphq.com/welcome\n\n<img alt=\"Somelogo\" src=\"/images/somelogo.png\" />"
|
||||
expected.body = "Hello there,\n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting http://www.basecamphq.com/welcome\n\n<img alt=\"Somelogo\" src=\"/images/somelogo.png\" />"
|
||||
expected.from = "system@loudthinking.com"
|
||||
expected.date = Time.local(2004, 12, 12)
|
||||
|
||||
@@ -83,7 +83,7 @@ class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
assert_nothing_raised { UrlTestMailer.signed_up_with_url(@recipient).deliver }
|
||||
assert_not_nil ActionMailer::Base.deliveries.first
|
||||
delivered = ActionMailer::Base.deliveries.first
|
||||
|
||||
|
||||
delivered.message_id = '<123@456>'
|
||||
assert_equal expected.encoded, delivered.encoded
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ class TestHelperMailerTest < ActionMailer::TestCase
|
||||
self.class.determine_default_mailer("NotAMailerTest")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_charset_is_utf_8
|
||||
assert_equal "UTF-8", charset
|
||||
end
|
||||
@@ -44,14 +44,14 @@ class TestHelperMailerTest < ActionMailer::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_repeated_assert_emails_calls
|
||||
assert_nothing_raised do
|
||||
assert_emails 1 do
|
||||
TestHelperMailer.test.deliver
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
assert_nothing_raised do
|
||||
assert_emails 2 do
|
||||
TestHelperMailer.test.deliver
|
||||
@@ -59,20 +59,20 @@ class TestHelperMailerTest < ActionMailer::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_assert_emails_with_no_block
|
||||
assert_nothing_raised do
|
||||
TestHelperMailer.test.deliver
|
||||
assert_emails 1
|
||||
end
|
||||
|
||||
|
||||
assert_nothing_raised do
|
||||
TestHelperMailer.test.deliver
|
||||
TestHelperMailer.test.deliver
|
||||
assert_emails 3
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_assert_no_emails
|
||||
assert_nothing_raised do
|
||||
assert_no_emails do
|
||||
@@ -80,17 +80,17 @@ class TestHelperMailerTest < ActionMailer::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_assert_emails_too_few_sent
|
||||
error = assert_raise ActiveSupport::TestCase::Assertion do
|
||||
assert_emails 2 do
|
||||
TestHelperMailer.test.deliver
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
assert_match(/2 .* but 1/, error.message)
|
||||
end
|
||||
|
||||
|
||||
def test_assert_emails_too_many_sent
|
||||
error = assert_raise ActiveSupport::TestCase::Assertion do
|
||||
assert_emails 1 do
|
||||
@@ -98,17 +98,17 @@ class TestHelperMailerTest < ActionMailer::TestCase
|
||||
TestHelperMailer.test.deliver
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
assert_match(/1 .* but 2/, error.message)
|
||||
end
|
||||
|
||||
|
||||
def test_assert_no_emails_failure
|
||||
error = assert_raise ActiveSupport::TestCase::Assertion do
|
||||
assert_no_emails do
|
||||
TestHelperMailer.test.deliver
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
assert_match(/0 .* but 1/, error.message)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
*Rails 3.0.0 [release candidate] (July 26th, 2010)*
|
||||
*Rails 3.0.0 (August 29, 2010)*
|
||||
|
||||
* Symbols and strings in routes should yield the same behavior. Note this may break existing apps that were using symbols with the new routes API [José Valim]
|
||||
|
||||
* Add clear_helpers as a way to clean up all helpers added to this controller, maintaing just the helper with the same name as the controller. [José Valim]
|
||||
|
||||
* Support routing constraints in functional tests. [Andrew White]
|
||||
|
||||
* Add a header that tells Internet Explorer (all versions) to use the best available standards support. [Yehuda Katz]
|
||||
|
||||
* Allow stylesheet/javascript extensions to be changed through railties. [Josh Kalderimis]
|
||||
|
||||
@@ -26,16 +34,13 @@
|
||||
resources :comments
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
You can now use comment_path for /comments/1 instead of post_comment_path for /posts/1/comments/1.
|
||||
|
||||
* Add support for multi-subdomain session by setting cookie host in session cookie so you can share session between www.example.com, example.com and user.example.com. #4818 [Guillermo Álvarez]
|
||||
|
||||
* Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino]
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
|
||||
|
||||
* Remove middleware laziness [José Valim]
|
||||
|
||||
* Make session stores rely on request.cookie_jar and change set_session semantics to return the cookie value instead of a boolean. [José Valim]
|
||||
@@ -52,9 +57,6 @@
|
||||
|
||||
* Changed translate helper so that it doesn’t mark every translation as safe HTML. Only keys with a "_html" suffix and keys named "html" are considered to be safe HTML. All other translations are left untouched. [Craig Davey]
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 3] (April 13th, 2010)*
|
||||
|
||||
* New option :as added to form_for allows to change the object name. The old <% form_for :client, @post %> becomes <% form_for @post, :as => :client %> [spastorino]
|
||||
|
||||
* Removed verify method in controllers. [JV]
|
||||
@@ -89,9 +91,6 @@
|
||||
"HEAD" and #request_method returns "GET" in HEAD requests). This
|
||||
is for compatibility with Rack::Request [YK]
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
|
||||
|
||||
* #concat is now deprecated in favor of using <%= %> helpers [YK]
|
||||
|
||||
* Block helpers now return Strings, so you can use <%= form_for @foo do |f| %>.
|
||||
@@ -110,7 +109,7 @@
|
||||
* ActionDispatch::Request#content_type returns a String to be compatible with
|
||||
Rack::Request. Use #content_mime_type for the Mime::Type instance [YK]
|
||||
|
||||
* Updated Prototype to 1.6.1 and Scriptaculous to 1.8.3 [ML]
|
||||
* Updated Prototype to 1.6.1 and Scriptaculous to 1.8.3 [ML]
|
||||
|
||||
* Change the preferred way that URL helpers are included into a class[YK & CL]
|
||||
|
||||
@@ -120,9 +119,6 @@
|
||||
# for just url_for
|
||||
include Rails.application.router.url_for
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 1] (February 4, 2010)*
|
||||
|
||||
* Fixed that PrototypeHelper#update_page should return html_safe [DHH]
|
||||
|
||||
* Fixed that much of DateHelper wouldn't return html_safe? strings [DHH]
|
||||
@@ -144,7 +140,6 @@
|
||||
|
||||
* Added ActionController::Base#notice/= and ActionController::Base#alert/= as a convenience accessors in both the controller and the view for flash[:notice]/= and flash[:alert]/= [DHH]
|
||||
|
||||
|
||||
* Introduce grouped_collection_select helper. #1249 [Dan Codeape, Erik Ostrom]
|
||||
|
||||
* Make sure javascript_include_tag/stylesheet_link_tag does not append ".js" or ".css" onto external urls. #1664 [Matthew Rudy Jacobs]
|
||||
|
||||
@@ -102,10 +102,10 @@ A short rundown of some of the major features:
|
||||
class WeblogController < ActionController::Base
|
||||
# filters as methods
|
||||
before_filter :authenticate, :cache, :audit
|
||||
|
||||
|
||||
# filter as a proc
|
||||
after_filter { |c| c.response.body = Gzip::compress(c.response.body) }
|
||||
|
||||
|
||||
# class filter
|
||||
after_filter LocalizeFilter
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
== Running with Rake
|
||||
|
||||
The easiest way to run the unit tests is through Rake. The default task runs
|
||||
the entire test suite for all classes. For more information, checkout the
|
||||
the entire test suite for all classes. For more information, checkout the
|
||||
full array of rake tasks with "rake -T"
|
||||
|
||||
Rake can be found at http://rake.rubyforge.org
|
||||
@@ -16,7 +16,7 @@ you can do so with something like:
|
||||
== Dependency on ActiveRecord and database setup
|
||||
|
||||
Test cases in the test/controller/active_record/ directory depend on having
|
||||
activerecord and sqlite installed. If ActiveRecord is not in
|
||||
activerecord and sqlite installed. If ActiveRecord is not in
|
||||
actionpack/../activerecord directory, or the sqlite rubygem is not installed,
|
||||
these tests are skipped.
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
gem 'rdoc', '>= 2.5.9'
|
||||
require 'rdoc'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rdoc/task'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
@@ -36,24 +33,6 @@ Rake::TestTask.new(:test_active_record_integration) do |t|
|
||||
t.test_files = Dir.glob("test/activerecord/*_test.rb")
|
||||
end
|
||||
|
||||
# Genereate the RDoc documentation
|
||||
|
||||
RDoc::Task.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Action Pack -- On rails from request to response"
|
||||
rdoc.options << '--charset' << 'utf-8'
|
||||
rdoc.options << '-f' << 'horo'
|
||||
rdoc.options << '--main' << 'README.rdoc'
|
||||
if ENV['DOC_FILES']
|
||||
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
||||
else
|
||||
rdoc.rdoc_files.include('README.rdoc', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
|
||||
rdoc.rdoc_files.include(Dir['lib/**/*.rb'] -
|
||||
Dir['lib/*/vendor/**/*.rb'])
|
||||
rdoc.rdoc_files.exclude('lib/actionpack.rb')
|
||||
end
|
||||
}
|
||||
|
||||
spec = eval(File.read('actionpack.gemspec'))
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
|
||||
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
|
||||
s.add_dependency('i18n', '~> 0.4.1')
|
||||
s.add_dependency('rack', '~> 1.2.1')
|
||||
s.add_dependency('rack-test', '~> 0.5.4')
|
||||
s.add_dependency('rack-mount', '~> 0.6.9')
|
||||
s.add_dependency('tzinfo', '~> 0.3.22')
|
||||
s.add_dependency('rack-mount', '~> 0.6.12')
|
||||
s.add_dependency('tzinfo', '~> 0.3.23')
|
||||
s.add_dependency('erubis', '~> 2.6.6')
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
|
||||
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
|
||||
|
||||
require 'action_pack'
|
||||
require 'active_support/concern'
|
||||
require 'active_support/ruby/shim'
|
||||
require 'active_support/dependencies/autoload'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
@@ -6,6 +6,10 @@ module AbstractController
|
||||
class Error < StandardError; end
|
||||
class ActionNotFound < StandardError; end
|
||||
|
||||
# <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
|
||||
# using it directly, and subclasses (like ActionController::Base) are
|
||||
# expected to provide their own +render+ method, since rendering means
|
||||
# different things depending on the context.
|
||||
class Base
|
||||
attr_internal :response_body
|
||||
attr_internal :action_name
|
||||
@@ -36,13 +40,12 @@ module AbstractController
|
||||
controller.public_instance_methods(true)
|
||||
end
|
||||
|
||||
# The list of hidden actions to an empty Array. Defaults to an
|
||||
# empty Array. This can be modified by other modules or subclasses
|
||||
# The list of hidden actions to an empty array. Defaults to an
|
||||
# empty array. This can be modified by other modules or subclasses
|
||||
# to specify particular actions as hidden.
|
||||
#
|
||||
# ==== Returns
|
||||
# Array[String]:: An array of method names that should not be
|
||||
# considered actions.
|
||||
# * <tt>array</tt> - An array of method names that should not be considered actions.
|
||||
def hidden_actions
|
||||
[]
|
||||
end
|
||||
@@ -54,8 +57,7 @@ module AbstractController
|
||||
# itself. Finally, #hidden_actions are removed.
|
||||
#
|
||||
# ==== Returns
|
||||
# Array[String]:: A list of all methods that should be considered
|
||||
# actions.
|
||||
# * <tt>array</tt> - A list of all methods that should be considered actions.
|
||||
def action_methods
|
||||
@action_methods ||= begin
|
||||
# All public instance methods of this class, including ancestors
|
||||
@@ -72,15 +74,27 @@ module AbstractController
|
||||
end
|
||||
end
|
||||
|
||||
# action_methods are cached and there is sometimes need to refresh
|
||||
# them. clear_action_methods! allows you to do that, so next time
|
||||
# you run action_methods, they will be recalculated
|
||||
def clear_action_methods!
|
||||
@action_methods = nil
|
||||
end
|
||||
|
||||
# Returns the full controller name, underscored, without the ending Controller.
|
||||
# For instance, MyApp::MyPostsController would return "my_app/my_posts" for
|
||||
# controller_name.
|
||||
#
|
||||
# ==== Returns
|
||||
# String
|
||||
# * <tt>string</tt>
|
||||
def controller_path
|
||||
@controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
|
||||
end
|
||||
|
||||
def method_added(name)
|
||||
super
|
||||
clear_action_methods!
|
||||
end
|
||||
end
|
||||
|
||||
abstract!
|
||||
@@ -92,12 +106,12 @@ module AbstractController
|
||||
# ActionNotFound error is raised.
|
||||
#
|
||||
# ==== Returns
|
||||
# self
|
||||
# * <tt>self</tt>
|
||||
def process(action, *args)
|
||||
@_action_name = action_name = action.to_s
|
||||
|
||||
unless action_name = method_for_action(action_name)
|
||||
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
|
||||
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
|
||||
end
|
||||
|
||||
@_response_body = nil
|
||||
@@ -121,10 +135,10 @@ module AbstractController
|
||||
# can be considered an action.
|
||||
#
|
||||
# ==== Parameters
|
||||
# name<String>:: The name of an action to be tested
|
||||
# * <tt>name</tt> - The name of an action to be tested
|
||||
#
|
||||
# ==== Returns
|
||||
# TrueClass, FalseClass
|
||||
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
|
||||
def action_method?(name)
|
||||
self.class.action_methods.include?(name)
|
||||
end
|
||||
@@ -168,11 +182,11 @@ module AbstractController
|
||||
# returns nil, an ActionNotFound exception will be raised.
|
||||
#
|
||||
# ==== Parameters
|
||||
# action_name<String>:: An action name to find a method name for
|
||||
# * <tt>action_name</tt> - An action name to find a method name for
|
||||
#
|
||||
# ==== Returns
|
||||
# String:: The name of the method that handles the action
|
||||
# nil:: No method name could be found. Raise ActionNotFound.
|
||||
# * <tt>string</tt> - The name of the method that handles the action
|
||||
# * <tt>nil</tt> - No method name could be found. Raise ActionNotFound.
|
||||
def method_for_action(action_name)
|
||||
if action_method?(action_name) then action_name
|
||||
elsif respond_to?(:action_missing, true) then "_handle_action_missing"
|
||||
|
||||
@@ -28,9 +28,8 @@ module AbstractController
|
||||
# a Rails process.
|
||||
#
|
||||
# ==== Options
|
||||
# :only<#to_s>:: The callback should be run only for this action
|
||||
# :except<#to_s>:: The callback should be run for all actions
|
||||
# except 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
|
||||
def _normalize_callback_options(options)
|
||||
if only = options[:only]
|
||||
only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
|
||||
@@ -45,7 +44,7 @@ module AbstractController
|
||||
# Skip before, after, and around filters matching any of the names
|
||||
#
|
||||
# ==== Parameters
|
||||
# *names<Object>:: A list of valid names that could be used for
|
||||
# * <tt>names</tt> - A list of valid names that could be used for
|
||||
# callbacks. Note that skipping uses Ruby equality, so it's
|
||||
# impossible to skip a callback defined using an anonymous proc
|
||||
# using #skip_filter
|
||||
@@ -60,13 +59,13 @@ module AbstractController
|
||||
# the normalization across several methods that use it.
|
||||
#
|
||||
# ==== Parameters
|
||||
# callbacks<Array[*Object, Hash]>:: A list of callbacks, with an optional
|
||||
# * <tt>callbacks</tt> - An array of callbacks, with an optional
|
||||
# options hash as the last parameter.
|
||||
# block<Proc>:: A proc that should be added to the callbacks.
|
||||
# * <tt>block</tt> - A proc that should be added to the callbacks.
|
||||
#
|
||||
# ==== Block Parameters
|
||||
# name<Symbol>:: The callback to be added
|
||||
# options<Hash>:: A list of options to be used when adding the callback
|
||||
# * <tt>name</tt> - The callback to be added
|
||||
# * <tt>options</tt> - A hash of options to be used when adding the callback
|
||||
def _insert_callbacks(callbacks, block)
|
||||
options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
|
||||
_normalize_callback_options(options)
|
||||
@@ -82,27 +81,27 @@ module AbstractController
|
||||
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
||||
# Append a before, after or around filter. See _insert_callbacks
|
||||
# for details on the allowed parameters.
|
||||
def #{filter}_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options|
|
||||
set_callback(:process_action, :#{filter}, name, options)
|
||||
end
|
||||
end
|
||||
def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options}
|
||||
set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before_filter, name, options)
|
||||
end # end
|
||||
end # end
|
||||
|
||||
# Prepend a before, after or around filter. See _insert_callbacks
|
||||
# for details on the allowed parameters.
|
||||
def prepend_#{filter}_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options|
|
||||
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true))
|
||||
end
|
||||
end
|
||||
def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
|
||||
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
|
||||
end # end
|
||||
end # end
|
||||
|
||||
# Skip a before, after or around filter. See _insert_callbacks
|
||||
# for details on the allowed parameters.
|
||||
def skip_#{filter}_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options|
|
||||
skip_callback(:process_action, :#{filter}, name, options)
|
||||
end
|
||||
end
|
||||
def skip_#{filter}_filter(*names, &blk) # def skip_before_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
|
||||
skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
|
||||
end # end
|
||||
end # end
|
||||
|
||||
# *_filter is the same as append_*_filter
|
||||
alias_method :append_#{filter}_filter, :#{filter}_filter
|
||||
|
||||
@@ -9,6 +9,9 @@ module AbstractController
|
||||
included do
|
||||
class_attribute :_helpers
|
||||
self._helpers = Module.new
|
||||
|
||||
class_attribute :_helper_methods
|
||||
self._helper_methods = Array.new
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
@@ -40,10 +43,13 @@ module AbstractController
|
||||
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
|
||||
#
|
||||
# ==== Parameters
|
||||
# meths<Array[#to_s]>:: The name of a method on the controller
|
||||
# * <tt>method[, method]</tt> - A name or names of a method on the controller
|
||||
# to be made available on the view.
|
||||
def helper_method(*meths)
|
||||
meths.flatten.each do |meth|
|
||||
meths.flatten!
|
||||
self._helper_methods += meths
|
||||
|
||||
meths.each do |meth|
|
||||
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
|
||||
def #{meth}(*args, &blk)
|
||||
controller.send(%(#{meth}), *args, &blk)
|
||||
@@ -55,8 +61,8 @@ module AbstractController
|
||||
# The +helper+ class method can take a series of helper module names, a block, or both.
|
||||
#
|
||||
# ==== Parameters
|
||||
# *args<Array[Module, Symbol, String, :all]>
|
||||
# block<Block>:: A block defining helper methods
|
||||
# * <tt>*args</tt> - Module, Symbol, String, :all
|
||||
# * <tt>block</tt> - A block defining helper methods
|
||||
#
|
||||
# ==== Examples
|
||||
# When the argument is a module it will be included directly in the template class.
|
||||
@@ -95,12 +101,23 @@ module AbstractController
|
||||
_helpers.module_eval(&block) if block_given?
|
||||
end
|
||||
|
||||
# Clears up all existing helpers in this class, only keeping the helper
|
||||
# with the same name as this class.
|
||||
def clear_helpers
|
||||
inherited_helper_methods = _helper_methods
|
||||
self._helpers = Module.new
|
||||
self._helper_methods = Array.new
|
||||
|
||||
inherited_helper_methods.each { |meth| helper_method meth }
|
||||
default_helper_module! unless anonymous?
|
||||
end
|
||||
|
||||
private
|
||||
# Makes all the (instance) methods in the helper module available to templates
|
||||
# rendered through this controller.
|
||||
#
|
||||
# ==== Parameters
|
||||
# mod<Module>:: The module to include into the current helper module
|
||||
# * <tt>module</tt> - The module to include into the current helper module
|
||||
# for the class
|
||||
def add_template_helper(mod)
|
||||
_helpers.module_eval { include mod }
|
||||
@@ -118,10 +135,10 @@ module AbstractController
|
||||
# are returned.
|
||||
#
|
||||
# ==== Parameters
|
||||
# args<Array[String, Symbol, Module]>:: A list of helpers
|
||||
# * <tt>args</tt> - An array of helpers
|
||||
#
|
||||
# ==== Returns
|
||||
# Array[Module]:: A normalized list of modules for the list of
|
||||
# * <tt>Array</tt> - A normalized list of modules for the list of
|
||||
# helpers provided.
|
||||
def modules_for_helpers(args)
|
||||
args.flatten.map! do |arg|
|
||||
|
||||
@@ -114,11 +114,13 @@ module AbstractController
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
|
||||
# end
|
||||
#
|
||||
# Of course, the most common way of specifying a layout is still just as a plain template name:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# layout "weblog_standard"
|
||||
# end
|
||||
#
|
||||
# If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
|
||||
# Otherwise, it will be looked up relative to the template root.
|
||||
@@ -183,7 +185,7 @@ module AbstractController
|
||||
# layout.
|
||||
#
|
||||
# ==== Returns
|
||||
# Boolean:: True if the action has a layout, false otherwise.
|
||||
# * <tt> Boolean</tt> - True if the action has a layout, false otherwise.
|
||||
def action_has_layout?
|
||||
return unless super
|
||||
|
||||
@@ -209,11 +211,11 @@ module AbstractController
|
||||
# true:: raise an ArgumentError
|
||||
#
|
||||
# ==== Parameters
|
||||
# layout<String, Symbol, false)>:: The layout to use.
|
||||
# * <tt>String, Symbol, false</tt> - The layout to use.
|
||||
#
|
||||
# ==== Options (conditions)
|
||||
# :only<#to_s, Array[#to_s]>:: A list of actions to apply this layout to.
|
||||
# :except<#to_s, Array[#to_s]>:: Apply this layout to all actions but this one
|
||||
# * :only - A list of actions to apply this layout to.
|
||||
# * :except - Apply this layout to all actions but this one.
|
||||
def layout(layout, conditions = {})
|
||||
include LayoutConditions unless conditions.empty?
|
||||
|
||||
@@ -228,7 +230,7 @@ module AbstractController
|
||||
# value of this method.
|
||||
#
|
||||
# ==== Returns
|
||||
# String:: A template name
|
||||
# * <tt>String</tt> - A template name
|
||||
def _implied_layout_name
|
||||
controller_path
|
||||
end
|
||||
@@ -313,8 +315,8 @@ module AbstractController
|
||||
# the name type.
|
||||
#
|
||||
# ==== Parameters
|
||||
# name<String|TrueClass|FalseClass|Symbol>:: The name of the template
|
||||
# details<Hash{Symbol => Object}>:: A list of details to restrict
|
||||
# * <tt>name</tt> - The name of the template
|
||||
# * <tt>details</tt> - A list of details to restrict
|
||||
# the lookup to. By default, layout lookup is limited to the
|
||||
# formats specified for the current request.
|
||||
def _layout_for_option(name)
|
||||
@@ -333,14 +335,14 @@ module AbstractController
|
||||
# Optionally raises an exception if the layout could not be found.
|
||||
#
|
||||
# ==== Parameters
|
||||
# details<Hash>:: A list of details to restrict the search by. This
|
||||
# * <tt>details</tt> - A list of details to restrict the search by. This
|
||||
# might include details like the format or locale of the template.
|
||||
# require_layout<Boolean>:: If this is true, raise an ArgumentError
|
||||
# * <tt>require_logout</tt> - If this is true, raise an ArgumentError
|
||||
# with details about the fact that the exception could not be
|
||||
# found (defaults to false)
|
||||
#
|
||||
# ==== Returns
|
||||
# Template:: The template object for the default layout (or nil)
|
||||
# * <tt>template</tt> - The template object for the default layout (or nil)
|
||||
def _default_layout(require_layout = false)
|
||||
begin
|
||||
layout_name = _layout if action_has_layout?
|
||||
|
||||
@@ -34,9 +34,9 @@ module AbstractController
|
||||
# Append a path to the list of view paths for this controller.
|
||||
#
|
||||
# ==== Parameters
|
||||
# path<String, ViewPath>:: If a String is provided, it gets converted into
|
||||
# the default view path. You may also provide a custom view path
|
||||
# (see ActionView::ViewPathSet for more information)
|
||||
# * <tt>path</tt> - If a String is provided, it gets converted into
|
||||
# the default view path. You may also provide a custom view path
|
||||
# (see ActionView::ViewPathSet for more information)
|
||||
def append_view_path(path)
|
||||
self.view_paths = view_paths.dup + Array(path)
|
||||
end
|
||||
@@ -44,9 +44,9 @@ module AbstractController
|
||||
# Prepend a path to the list of view paths for this controller.
|
||||
#
|
||||
# ==== Parameters
|
||||
# path<String, ViewPath>:: If a String is provided, it gets converted into
|
||||
# the default view path. You may also provide a custom view path
|
||||
# (see ActionView::ViewPathSet for more information)
|
||||
# * <tt>path</tt> - If a String is provided, it gets converted into
|
||||
# the default view path. You may also provide a custom view path
|
||||
# (see ActionView::ViewPathSet for more information)
|
||||
def prepend_view_path(path)
|
||||
self.view_paths = Array(path) + view_paths.dup
|
||||
end
|
||||
@@ -59,7 +59,7 @@ module AbstractController
|
||||
# Set the view paths.
|
||||
#
|
||||
# ==== Parameters
|
||||
# paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
|
||||
# * <tt>paths</tt> - If a ViewPathSet is provided, use that;
|
||||
# otherwise, process the parameter into a ViewPathSet.
|
||||
def view_paths=(paths)
|
||||
self._view_paths = ActionView::Base.process_view_paths(paths)
|
||||
|
||||
@@ -35,10 +35,11 @@ module ActionController
|
||||
end
|
||||
|
||||
autoload :Dispatcher, 'action_controller/deprecated/dispatcher'
|
||||
autoload :UrlWriter, 'action_controller/deprecated/url_writer'
|
||||
autoload :UrlRewriter, 'action_controller/deprecated/url_writer'
|
||||
autoload :Integration, 'action_controller/deprecated/integration_test'
|
||||
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
|
||||
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
|
||||
autoload :UrlWriter, 'action_controller/deprecated'
|
||||
autoload :Routing, 'action_controller/deprecated'
|
||||
autoload :TestCase, 'action_controller/test_case'
|
||||
|
||||
|
||||
@@ -1,6 +1,169 @@
|
||||
require "action_controller/log_subscriber"
|
||||
|
||||
module ActionController
|
||||
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
|
||||
# on request and then either render a template or redirect to another action. An action is defined as a public method
|
||||
# on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
|
||||
#
|
||||
# By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
|
||||
# controllers in turn inherit from ApplicationController. This gives you one class to configure things such as
|
||||
# request forgery protection and filtering of sensitive request parameters.
|
||||
#
|
||||
# A sample controller could look like this:
|
||||
#
|
||||
# class PostsController < ApplicationController
|
||||
# def index
|
||||
# @posts = Post.all
|
||||
# end
|
||||
#
|
||||
# def create
|
||||
# @post = Post.create params[:post]
|
||||
# redirect_to posts_path
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
|
||||
# after executing code in the action. For example, the +index+ action of the PostsController would render the
|
||||
# template <tt>app/views/posts/index.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
|
||||
#
|
||||
# Unlike index, the create action will not render a template. After performing its main purpose (creating a
|
||||
# new post), it initiates a redirect instead. This redirect works by returning an external
|
||||
# "302 Moved" HTTP response that takes the user to the index action.
|
||||
#
|
||||
# These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
|
||||
# Most actions are variations of these themes.
|
||||
#
|
||||
# == Requests
|
||||
#
|
||||
# For every request, the router determines the value of the +controller+ and +action+ keys. These determine which controller
|
||||
# and action are called. The remaining request parameters, the session (if one is available), and the full request with
|
||||
# all the HTTP headers are made available to the action through accessor methods. Then the action is performed.
|
||||
#
|
||||
# The full request object is available via the request accessor and is primarily used to query for HTTP headers:
|
||||
#
|
||||
# def server_ip
|
||||
# location = request.env["SERVER_ADDR"]
|
||||
# render :text => "This server hosted at #{location}"
|
||||
# end
|
||||
#
|
||||
# == Parameters
|
||||
#
|
||||
# All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
|
||||
# which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
|
||||
# <tt>{ "category" => "All", "limit" => 5 }</tt> in params.
|
||||
#
|
||||
# It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
|
||||
#
|
||||
# <input type="text" name="post[name]" value="david">
|
||||
# <input type="text" name="post[address]" value="hyacintvej">
|
||||
#
|
||||
# A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
|
||||
# If the address input had been named "post[address][street]", the params would have included
|
||||
# <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
|
||||
#
|
||||
# == Sessions
|
||||
#
|
||||
# Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
|
||||
# such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
|
||||
# as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
|
||||
# they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
|
||||
#
|
||||
# You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
|
||||
#
|
||||
# session[:person] = Person.authenticate(user_name, password)
|
||||
#
|
||||
# And retrieved again through the same hash:
|
||||
#
|
||||
# Hello #{session[:person]}
|
||||
#
|
||||
# For removing objects from the session, you can either assign a single key to +nil+:
|
||||
#
|
||||
# # removes :person from session
|
||||
# session[:person] = nil
|
||||
#
|
||||
# or you can remove the entire session with +reset_session+.
|
||||
#
|
||||
# Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
|
||||
# This prevents the user from tampering with the session but also allows him to see its contents.
|
||||
#
|
||||
# Do not put secret information in cookie-based sessions!
|
||||
#
|
||||
# Other options for session storage:
|
||||
#
|
||||
# * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
|
||||
# unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
|
||||
#
|
||||
# config.action_controller.session_store = :active_record_store
|
||||
#
|
||||
# in your <tt>config/environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
|
||||
#
|
||||
# == Responses
|
||||
#
|
||||
# Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
|
||||
# object is generated automatically through the use of renders and redirects and requires no user intervention.
|
||||
#
|
||||
# == Renders
|
||||
#
|
||||
# Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
|
||||
# of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured.
|
||||
# The controller passes objects to the view by assigning instance variables:
|
||||
#
|
||||
# def show
|
||||
# @post = Post.find(params[:id])
|
||||
# end
|
||||
#
|
||||
# Which are then automatically available to the view:
|
||||
#
|
||||
# Title: <%= @post.title %>
|
||||
#
|
||||
# You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use
|
||||
# the manual rendering methods:
|
||||
#
|
||||
# def search
|
||||
# @results = Search.find(params[:query])
|
||||
# case @results
|
||||
# when 0 then render :action => "no_results"
|
||||
# when 1 then render :action => "show"
|
||||
# when 2..10 then render :action => "show_many"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Read more about writing ERb and Builder templates in ActionView::Base.
|
||||
#
|
||||
# == Redirects
|
||||
#
|
||||
# Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to a database,
|
||||
# we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to)
|
||||
# a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
|
||||
#
|
||||
# def create
|
||||
# @entry = Entry.new(params[:entry])
|
||||
# if @entry.save
|
||||
# # The entry was saved correctly, redirect to show
|
||||
# redirect_to :action => 'show', :id => @entry.id
|
||||
# else
|
||||
# # things didn't go so well, do something else
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed.
|
||||
#
|
||||
# == Calling multiple redirects or renders
|
||||
#
|
||||
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
|
||||
#
|
||||
# def do_something
|
||||
# redirect_to :action => "elsewhere"
|
||||
# render :action => "overthere" # raises DoubleRenderError
|
||||
# end
|
||||
#
|
||||
# If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
|
||||
#
|
||||
# def do_something
|
||||
# redirect_to(:action => "elsewhere") and return if monkeys.nil?
|
||||
# render :action => "overthere" # won't be called if monkeys is nil
|
||||
# end
|
||||
#
|
||||
class Base < Metal
|
||||
abstract!
|
||||
|
||||
@@ -60,11 +223,11 @@ module ActionController
|
||||
|
||||
def self.inherited(klass)
|
||||
super
|
||||
klass.helper :all
|
||||
klass.helper :all if klass.superclass == ActionController::Base
|
||||
end
|
||||
|
||||
require "action_controller/deprecated/base"
|
||||
ActiveSupport.run_load_hooks(:action_controller, self)
|
||||
end
|
||||
end
|
||||
|
||||
require "action_controller/deprecated/base"
|
||||
|
||||
@@ -81,6 +81,11 @@ module ActionController
|
||||
def session=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.session= is deprecated. " <<
|
||||
"Please configure it on your application with config.session_store :cookie_store, :key => '....'", caller
|
||||
|
||||
if secret = value.delete(:secret)
|
||||
Rails.application.config.secret_token = secret
|
||||
end
|
||||
|
||||
if value.delete(:disabled)
|
||||
Rails.application.config.session_store :disabled
|
||||
else
|
||||
@@ -120,9 +125,14 @@ module ActionController
|
||||
|
||||
# This was moved to a plugin
|
||||
def verify(*args)
|
||||
ActiveSupport::Deprecation.warn "verify was removed from Rails and is now available as a plugin. " <<
|
||||
ActiveSupport::Deprecation.warn "verify was removed from Rails and is now available as a plugin. " \
|
||||
"Please install it with `rails plugin install git://github.com/rails/verification.git`.", caller
|
||||
end
|
||||
|
||||
def exempt_from_layout(*)
|
||||
ActiveSupport::Deprecation.warn "exempt_from_layout is no longer needed, because layouts in Rails 3 " \
|
||||
"are restricted to the content-type of the template that was rendered.", caller
|
||||
end
|
||||
end
|
||||
|
||||
extend DeprecatedBehavior
|
||||
|
||||
14
actionpack/lib/action_controller/deprecated/url_writer.rb
Normal file
14
actionpack/lib/action_controller/deprecated/url_writer.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
module ActionController
|
||||
module UrlWriter
|
||||
def self.included(klass)
|
||||
ActiveSupport::Deprecation.warn "include ActionController::UrlWriter is deprecated. Instead, " \
|
||||
"include Rails.application.routes.url_helpers"
|
||||
klass.class_eval { include Rails.application.routes.url_helpers }
|
||||
end
|
||||
end
|
||||
|
||||
class UrlRewriter
|
||||
def initialize(*)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -392,11 +392,11 @@ module ActionController
|
||||
end
|
||||
end
|
||||
|
||||
# If token Authorization header is present, call the login procedure with
|
||||
# If token Authorization header is present, call the login procedure with
|
||||
# the present token and options.
|
||||
#
|
||||
# controller - ActionController::Base instance for the current request.
|
||||
# login_procedure - Proc to call if a token is present. The Proc should
|
||||
# login_procedure - Proc to call if a token is present. The Proc should
|
||||
# take 2 arguments:
|
||||
# authenticate(controller) { |token, options| ... }
|
||||
#
|
||||
|
||||
@@ -89,6 +89,8 @@ module ActionController #:nodoc:
|
||||
|
||||
def initialize(controller, resources, options={})
|
||||
@controller = controller
|
||||
@request = @controller.request
|
||||
@format = @controller.formats.first
|
||||
@resource = resources.last
|
||||
@resources = resources
|
||||
@options = options
|
||||
@@ -99,14 +101,6 @@ module ActionController #:nodoc:
|
||||
delegate :head, :render, :redirect_to, :to => :controller
|
||||
delegate :get?, :post?, :put?, :delete?, :to => :request
|
||||
|
||||
def request
|
||||
@request ||= @controller.request
|
||||
end
|
||||
|
||||
def format
|
||||
@format ||= @controller.formats.first
|
||||
end
|
||||
|
||||
# Undefine :to_json and :to_yaml since it's defined on Object
|
||||
undef_method(:to_json) if method_defined?(:to_json)
|
||||
undef_method(:to_yaml) if method_defined?(:to_yaml)
|
||||
|
||||
@@ -31,7 +31,7 @@ module ActionController
|
||||
super()
|
||||
@_app = app
|
||||
end
|
||||
|
||||
|
||||
def index
|
||||
call(env)
|
||||
end
|
||||
|
||||
@@ -167,6 +167,7 @@ module ActionController
|
||||
@formats = nil
|
||||
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
|
||||
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
|
||||
@symbolized_path_params = nil
|
||||
@method = @request_method = nil
|
||||
@fullpath = @ip = @remote_ip = nil
|
||||
@env['action_dispatch.request.query_parameters'] = {}
|
||||
@@ -311,7 +312,7 @@ module ActionController
|
||||
def tests(controller_class)
|
||||
self.controller_class = controller_class
|
||||
end
|
||||
|
||||
|
||||
def controller_class=(new_class)
|
||||
prepare_controller_class(new_class) if new_class
|
||||
write_inheritable_attribute(:controller_class, new_class)
|
||||
|
||||
@@ -48,7 +48,7 @@ EOF
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Search the tree for (and return) the first node that matches the given
|
||||
# conditions. The conditions are interpreted differently for different node
|
||||
# types, see HTML::Text#find and HTML::Tag#find.
|
||||
@@ -62,7 +62,7 @@ EOF
|
||||
def find_all(conditions)
|
||||
@root.find_all(conditions)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'strscan'
|
||||
|
||||
module HTML #:nodoc:
|
||||
|
||||
|
||||
class Conditions < Hash #:nodoc:
|
||||
def initialize(hash)
|
||||
super()
|
||||
@@ -57,17 +57,17 @@ module HTML #:nodoc:
|
||||
class Node #:nodoc:
|
||||
# The array of children of this node. Not all nodes have children.
|
||||
attr_reader :children
|
||||
|
||||
|
||||
# The parent node of this node. All nodes have a parent, except for the
|
||||
# root node.
|
||||
attr_reader :parent
|
||||
|
||||
|
||||
# The line number of the input where this node was begun
|
||||
attr_reader :line
|
||||
|
||||
|
||||
# The byte position in the input where this node was begun
|
||||
attr_reader :position
|
||||
|
||||
|
||||
# Create a new node as a child of the given parent.
|
||||
def initialize(parent, line=0, pos=0)
|
||||
@parent = parent
|
||||
@@ -92,7 +92,7 @@ module HTML #:nodoc:
|
||||
# returns non +nil+. Returns the result of the #find call that succeeded.
|
||||
def find(conditions)
|
||||
conditions = validate_conditions(conditions)
|
||||
@children.each do |child|
|
||||
@children.each do |child|
|
||||
node = child.find(conditions)
|
||||
return node if node
|
||||
end
|
||||
@@ -133,7 +133,7 @@ module HTML #:nodoc:
|
||||
|
||||
equivalent
|
||||
end
|
||||
|
||||
|
||||
class <<self
|
||||
def parse(parent, line, pos, content, strict=true)
|
||||
if content !~ /^<\S/
|
||||
@@ -160,11 +160,11 @@ module HTML #:nodoc:
|
||||
|
||||
return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
|
||||
end
|
||||
|
||||
|
||||
closing = ( scanner.scan(/\//) ? :close : nil )
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
|
||||
name.downcase!
|
||||
|
||||
|
||||
unless closing
|
||||
scanner.skip(/\s*/)
|
||||
attributes = {}
|
||||
@@ -191,13 +191,13 @@ module HTML #:nodoc:
|
||||
attributes[attr.downcase] = value
|
||||
scanner.skip(/\s*/)
|
||||
end
|
||||
|
||||
|
||||
closing = ( scanner.scan(/\//) ? :self : nil )
|
||||
end
|
||||
|
||||
|
||||
unless scanner.scan(/\s*>/)
|
||||
if strict
|
||||
raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
|
||||
raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
|
||||
else
|
||||
# throw away all text until we find what we're looking for
|
||||
scanner.skip_until(/>/) or scanner.terminate
|
||||
@@ -212,9 +212,9 @@ module HTML #:nodoc:
|
||||
|
||||
# A node that represents text, rather than markup.
|
||||
class Text < Node #:nodoc:
|
||||
|
||||
|
||||
attr_reader :content
|
||||
|
||||
|
||||
# Creates a new text node as a child of the given parent, with the given
|
||||
# content.
|
||||
def initialize(parent, line, pos, content)
|
||||
@@ -240,7 +240,7 @@ module HTML #:nodoc:
|
||||
def find(conditions)
|
||||
match(conditions) && self
|
||||
end
|
||||
|
||||
|
||||
# Returns non-+nil+ if this node meets the given conditions, or +nil+
|
||||
# otherwise. See the discussion of #find for the valid conditions.
|
||||
def match(conditions)
|
||||
@@ -268,7 +268,7 @@ module HTML #:nodoc:
|
||||
content == node.content
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# A CDATA node is simply a text node with a specialized way of displaying
|
||||
# itself.
|
||||
class CDATA < Text #:nodoc:
|
||||
@@ -281,16 +281,16 @@ module HTML #:nodoc:
|
||||
# closing tag, or a self-closing tag. It has a name, and may have a hash of
|
||||
# attributes.
|
||||
class Tag < Node #:nodoc:
|
||||
|
||||
|
||||
# Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
|
||||
attr_reader :closing
|
||||
|
||||
|
||||
# Either +nil+, or a hash of attributes for this node.
|
||||
attr_reader :attributes
|
||||
|
||||
# The name of this tag.
|
||||
attr_reader :name
|
||||
|
||||
|
||||
# Create a new node as a child of the given parent, using the given content
|
||||
# to describe the node. It will be parsed and the node name, attributes and
|
||||
# closing status extracted.
|
||||
@@ -344,7 +344,7 @@ module HTML #:nodoc:
|
||||
def tag?
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
# Returns +true+ if the node meets any of the given conditions. The
|
||||
# +conditions+ parameter must be a hash of any of the following keys
|
||||
# (all are optional):
|
||||
@@ -404,7 +404,7 @@ module HTML #:nodoc:
|
||||
# node.match :descendant => { :tag => "strong" }
|
||||
#
|
||||
# # test if the node has between 2 and 4 span tags as immediate children
|
||||
# node.match :children => { :count => 2..4, :only => { :tag => "span" } }
|
||||
# node.match :children => { :count => 2..4, :only => { :tag => "span" } }
|
||||
#
|
||||
# # get funky: test to see if the node is a "div", has a "ul" ancestor
|
||||
# # and an "li" parent (with "class" = "enum"), and whether or not it has
|
||||
@@ -439,7 +439,7 @@ module HTML #:nodoc:
|
||||
|
||||
# test children
|
||||
return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
|
||||
|
||||
|
||||
# test ancestors
|
||||
if conditions[:ancestor]
|
||||
return false unless catch :found do
|
||||
@@ -457,13 +457,13 @@ module HTML #:nodoc:
|
||||
child.match(:descendant => conditions[:descendant])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# count children
|
||||
if opts = conditions[:children]
|
||||
matches = children.select do |c|
|
||||
(c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
|
||||
end
|
||||
|
||||
|
||||
matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
|
||||
opts.each do |key, value|
|
||||
next if key == :only
|
||||
@@ -489,24 +489,24 @@ module HTML #:nodoc:
|
||||
self_index = siblings.index(self)
|
||||
|
||||
if conditions[:sibling]
|
||||
return false unless siblings.detect do |s|
|
||||
return false unless siblings.detect do |s|
|
||||
s != self && s.match(conditions[:sibling])
|
||||
end
|
||||
end
|
||||
|
||||
if conditions[:before]
|
||||
return false unless siblings[self_index+1..-1].detect do |s|
|
||||
return false unless siblings[self_index+1..-1].detect do |s|
|
||||
s != self && s.match(conditions[:before])
|
||||
end
|
||||
end
|
||||
|
||||
if conditions[:after]
|
||||
return false unless siblings[0,self_index].detect do |s|
|
||||
return false unless siblings[0,self_index].detect do |s|
|
||||
s != self && s.match(conditions[:after])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
@@ -515,7 +515,7 @@ module HTML #:nodoc:
|
||||
return false unless closing == node.closing && self.name == node.name
|
||||
attributes == node.attributes
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
# Match the given value to the given condition.
|
||||
def match_condition(value, condition)
|
||||
|
||||
@@ -7,11 +7,11 @@ module HTML
|
||||
return text unless sanitizeable?(text)
|
||||
tokenize(text, options).join
|
||||
end
|
||||
|
||||
|
||||
def sanitizeable?(text)
|
||||
!(text.nil? || text.empty? || !text.index("<"))
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def tokenize(text, options)
|
||||
tokenizer = HTML::Tokenizer.new(text)
|
||||
@@ -22,12 +22,12 @@ module HTML
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
|
||||
def process_node(node, result, options)
|
||||
result << node.to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class FullSanitizer < Sanitizer
|
||||
def sanitize(text, options = {})
|
||||
result = super
|
||||
@@ -37,12 +37,12 @@ module HTML
|
||||
# Recurse - handle all dirty nested tags
|
||||
result == text ? result : sanitize(result, options)
|
||||
end
|
||||
|
||||
|
||||
def process_node(node, result, options)
|
||||
result << node.to_s if node.class == HTML::Text
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class LinkSanitizer < FullSanitizer
|
||||
cattr_accessor :included_tags, :instance_writer => false
|
||||
self.included_tags = Set.new(%w(a href))
|
||||
@@ -50,13 +50,13 @@ module HTML
|
||||
def sanitizeable?(text)
|
||||
!(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def process_node(node, result, options)
|
||||
result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
|
||||
result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class WhiteListSanitizer < Sanitizer
|
||||
[:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
|
||||
:allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
|
||||
@@ -66,35 +66,35 @@ module HTML
|
||||
# A regular expression of the valid characters used to separate protocols like
|
||||
# the ':' in 'http://foo.com'
|
||||
self.protocol_separator = /:|(�*58)|(p)|(%|%)3A/
|
||||
|
||||
|
||||
# Specifies a Set of HTML attributes that can have URIs.
|
||||
self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
|
||||
|
||||
# Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
|
||||
# to just escaping harmless tags like <font>
|
||||
self.bad_tags = Set.new(%w(script))
|
||||
|
||||
|
||||
# Specifies the default Set of tags that the #sanitize helper will allow unscathed.
|
||||
self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
|
||||
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
|
||||
self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
|
||||
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
|
||||
acronym a img blockquote del ins))
|
||||
|
||||
# Specifies the default Set of html attributes that the #sanitize helper will leave
|
||||
# Specifies the default Set of html attributes that the #sanitize helper will leave
|
||||
# in the allowed tag.
|
||||
self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
|
||||
|
||||
|
||||
# Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
|
||||
self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
|
||||
self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
|
||||
feed svn urn aim rsync tag ssh sftp rtsp afs))
|
||||
|
||||
|
||||
# Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
|
||||
self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
|
||||
border-color border-left-color border-right-color border-top-color clear color cursor direction display
|
||||
self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
|
||||
border-color border-left-color border-right-color border-top-color clear color cursor direction display
|
||||
elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
|
||||
overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
|
||||
speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
|
||||
width))
|
||||
|
||||
|
||||
# Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
|
||||
self.allowed_css_keywords = Set.new(%w(auto aqua black block blue bold both bottom brown center
|
||||
collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
|
||||
@@ -118,9 +118,9 @@ module HTML
|
||||
style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
|
||||
if allowed_css_properties.include?(prop.downcase)
|
||||
clean << prop + ': ' + val + ';'
|
||||
elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
|
||||
elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
|
||||
unless val.split().any? do |keyword|
|
||||
!allowed_css_keywords.include?(keyword) &&
|
||||
!allowed_css_keywords.include?(keyword) &&
|
||||
keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
|
||||
end
|
||||
clean << prop + ': ' + val + ';'
|
||||
@@ -146,7 +146,7 @@ module HTML
|
||||
else
|
||||
options[:parent].unshift node.name
|
||||
end
|
||||
|
||||
|
||||
process_attributes_for node, options
|
||||
|
||||
options[:tags].include?(node.name) ? node : nil
|
||||
@@ -154,7 +154,7 @@ module HTML
|
||||
bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "<")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def process_attributes_for(node, options)
|
||||
return unless node.attributes
|
||||
node.attributes.keys.each do |attr_name|
|
||||
@@ -169,7 +169,7 @@ module HTML
|
||||
end
|
||||
|
||||
def contains_bad_protocols?(attr_name, value)
|
||||
uri_attributes.include?(attr_name) &&
|
||||
uri_attributes.include?(attr_name) &&
|
||||
(value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -182,7 +182,7 @@ module HTML
|
||||
# not another using <tt>:not</tt>. For example:
|
||||
# p:not(.post)
|
||||
# Matches all paragraphs that do not have the class <tt>.post</tt>.
|
||||
#
|
||||
#
|
||||
# === Substitution Values
|
||||
#
|
||||
# You can use substitution with identifiers, class names and element values.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'strscan'
|
||||
|
||||
module HTML #:nodoc:
|
||||
|
||||
|
||||
# A simple HTML tokenizer. It simply breaks a stream of text into tokens, where each
|
||||
# token is a string. Each string represents either "text", or an HTML element.
|
||||
#
|
||||
@@ -14,13 +14,13 @@ module HTML #:nodoc:
|
||||
# p token
|
||||
# end
|
||||
class Tokenizer #:nodoc:
|
||||
|
||||
|
||||
# The current (byte) position in the text
|
||||
attr_reader :position
|
||||
|
||||
|
||||
# The current line number
|
||||
attr_reader :line
|
||||
|
||||
|
||||
# Create a new Tokenizer for the given text.
|
||||
def initialize(text)
|
||||
text.encode! if text.encoding_aware?
|
||||
@@ -42,7 +42,7 @@ module HTML #:nodoc:
|
||||
update_current_line(scan_text)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
# Treat the text at the current position as a tag, and scan it. Supports
|
||||
@@ -69,13 +69,13 @@ module HTML #:nodoc:
|
||||
def scan_text
|
||||
"#{@scanner.getch}#{@scanner.scan(/[^<]*/)}"
|
||||
end
|
||||
|
||||
|
||||
# Counts the number of newlines in the text and updates the current line
|
||||
# accordingly.
|
||||
def update_current_line(text)
|
||||
text.scan(/\r?\n/) { @current_line += 1 }
|
||||
end
|
||||
|
||||
|
||||
# Skips over quoted strings, so that less-than and greater-than characters
|
||||
# within the strings are ignored.
|
||||
def consume_quoted_regions
|
||||
@@ -103,5 +103,5 @@ module HTML #:nodoc:
|
||||
text
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -113,10 +113,10 @@ module ActionDispatch
|
||||
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
|
||||
|
||||
def set_conditional_cache_control!
|
||||
control = @cache_control
|
||||
|
||||
return if self["Cache-Control"].present?
|
||||
|
||||
control = @cache_control
|
||||
|
||||
if control.empty?
|
||||
headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
|
||||
elsif @cache_control[:no_cache]
|
||||
|
||||
@@ -32,7 +32,7 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the Mime type for the \format used in the request.
|
||||
# Returns the MIME type for the \format used in the request.
|
||||
#
|
||||
# GET /posts/5.xml | request.format => Mime::XML
|
||||
# GET /posts/5.xhtml | request.format => Mime::HTML
|
||||
|
||||
@@ -109,10 +109,10 @@ module Mime
|
||||
else
|
||||
# keep track of creation order to keep the subsequent sort stable
|
||||
list = []
|
||||
accept_header.split(/,/).each_with_index do |header, index|
|
||||
params, q = header.split(/;\s*q=/)
|
||||
accept_header.split(/,/).each_with_index do |header, index|
|
||||
params, q = header.split(/;\s*q=/)
|
||||
if params
|
||||
params.strip!
|
||||
params.strip!
|
||||
list << AcceptItem.new(index, params, q) unless params.empty?
|
||||
end
|
||||
end
|
||||
@@ -161,20 +161,20 @@ module Mime
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(string, symbol = nil, synonyms = [])
|
||||
@symbol, @synonyms = symbol, synonyms
|
||||
@string = string
|
||||
end
|
||||
|
||||
|
||||
def to_s
|
||||
@string
|
||||
end
|
||||
|
||||
|
||||
def to_str
|
||||
to_s
|
||||
end
|
||||
|
||||
|
||||
def to_sym
|
||||
@symbol || @string.to_sym
|
||||
end
|
||||
@@ -186,11 +186,11 @@ module Mime
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def ==(mime_type)
|
||||
return false if mime_type.blank?
|
||||
(@synonyms + [ self ]).any? do |synonym|
|
||||
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
|
||||
(@synonyms + [ self ]).any? do |synonym|
|
||||
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -15,14 +15,14 @@ module ActionDispatch
|
||||
alias :params :parameters
|
||||
|
||||
def path_parameters=(parameters) #:nodoc:
|
||||
@env.delete("action_dispatch.request.symbolized_path_parameters")
|
||||
@symbolized_path_params = nil
|
||||
@env.delete("action_dispatch.request.parameters")
|
||||
@env["action_dispatch.request.path_parameters"] = parameters
|
||||
end
|
||||
|
||||
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
|
||||
def symbolized_path_parameters
|
||||
@env["action_dispatch.request.symbolized_path_parameters"] ||= path_parameters.symbolize_keys
|
||||
@symbolized_path_params ||= path_parameters.symbolize_keys
|
||||
end
|
||||
|
||||
# Returns a hash with the \parameters used to form the \path of the request.
|
||||
|
||||
@@ -15,6 +15,8 @@ module ActionDispatch
|
||||
include ActionDispatch::Http::Upload
|
||||
include ActionDispatch::Http::URL
|
||||
|
||||
LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, "::1", /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
|
||||
|
||||
%w[ AUTH_TYPE GATEWAY_INTERFACE
|
||||
PATH_TRANSLATED REMOTE_HOST
|
||||
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
|
||||
@@ -231,5 +233,10 @@ module ActionDispatch
|
||||
@env['X_HTTP_AUTHORIZATION'] ||
|
||||
@env['REDIRECT_X_HTTP_AUTHORIZATION']
|
||||
end
|
||||
|
||||
# True if the request came from localhost, 127.0.0.1.
|
||||
def local?
|
||||
LOCALHOST.any? { |local_ip| local_ip === remote_addr && local_ip === remote_ip }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
module ActionDispatch
|
||||
module Http
|
||||
module URL
|
||||
# Returns the complete URL used for this request.
|
||||
# Returns the complete \URL used for this request.
|
||||
def url
|
||||
protocol + host_with_port + fullpath
|
||||
end
|
||||
|
||||
# Returns 'https' if this is an SSL request and 'http' otherwise.
|
||||
def scheme
|
||||
ssl? ? 'https' : 'http'
|
||||
end
|
||||
|
||||
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
|
||||
def protocol
|
||||
ssl? ? 'https://' : 'http://'
|
||||
@@ -53,6 +58,11 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# Returns whether this request is using the standard port
|
||||
def standard_port?
|
||||
port == standard_port
|
||||
end
|
||||
|
||||
# Returns a \port suffix like ":8080" if the \port number of this request
|
||||
# is not the default HTTP \port 80 or HTTPS \port 443.
|
||||
def port_string
|
||||
@@ -86,7 +96,7 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
# Returns the request URI, accounting for server idiosyncrasies.
|
||||
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
|
||||
# WEBrick includes the full \URL. IIS leaves REQUEST_URI blank.
|
||||
def request_uri
|
||||
ActiveSupport::Deprecation.warn "Using #request_uri is deprecated. Use fullpath instead.", caller
|
||||
fullpath
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
module ActionDispatch
|
||||
class BestStandardsSupport
|
||||
def initialize(app)
|
||||
def initialize(app, type = true)
|
||||
@app = app
|
||||
|
||||
@header = case type
|
||||
when true
|
||||
"IE=Edge,chrome=1"
|
||||
when :builtin
|
||||
"IE=Edge"
|
||||
when false
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, body = @app.call(env)
|
||||
headers["X-UA-Compatible"] = "IE=Edge,chrome=1"
|
||||
headers["X-UA-Compatible"] = @header
|
||||
[status, headers, body]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# Cookies are read and written through ActionController#cookies.
|
||||
# \Cookies are read and written through ActionController#cookies.
|
||||
#
|
||||
# The cookies being read are the ones received along with the request, the cookies
|
||||
# being written will be sent out with the response. Reading a cookie does not get
|
||||
@@ -21,6 +21,15 @@ module ActionDispatch
|
||||
# # Sets a cookie that expires in 1 hour.
|
||||
# cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
|
||||
#
|
||||
# # Sets a signed cookie, which prevents a user from tampering with its value.
|
||||
# # You must specify a value in ActionController::Base.cookie_verifier_secret.
|
||||
# cookies.signed[:remember_me] = [current_user.id, current_user.salt]
|
||||
#
|
||||
# # Sets a "permanent" cookie (which expires in 20 years from now).
|
||||
# cookies.permanent[:login] = "XJ-122"
|
||||
# # You can also chain these methods:
|
||||
# cookies.permanent.signed[:login] = "XJ-122"
|
||||
#
|
||||
# Examples for reading:
|
||||
#
|
||||
# cookies[:user_name] # => "david"
|
||||
@@ -55,7 +64,7 @@ module ActionDispatch
|
||||
# :domain => :all # Allow the cookie for the top most level
|
||||
# domain and subdomains.
|
||||
#
|
||||
# * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
|
||||
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
|
||||
# * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
|
||||
# Default is +false+.
|
||||
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
|
||||
@@ -69,16 +78,26 @@ module ActionDispatch
|
||||
|
||||
class CookieJar < Hash #:nodoc:
|
||||
|
||||
# This regular expression is used to split the levels of a domain
|
||||
# So www.example.co.uk gives:
|
||||
# $1 => www.
|
||||
# $2 => example
|
||||
# $3 => co.uk
|
||||
DOMAIN_REGEXP = /^(.*\.)*(.*)\.(...|...\...|....|..\...|..)$/
|
||||
# This regular expression is used to split the levels of a domain.
|
||||
# The top level domain can be any string without a period or
|
||||
# **.**, ***.** style TLDs like co.uk or com.au
|
||||
#
|
||||
# www.example.co.uk gives:
|
||||
# $1 => example
|
||||
# $2 => co.uk
|
||||
#
|
||||
# example.com gives:
|
||||
# $1 => example
|
||||
# $2 => com
|
||||
#
|
||||
# lots.of.subdomains.example.local gives:
|
||||
# $1 => example
|
||||
# $2 => local
|
||||
DOMAIN_REGEXP = /([^.]*)\.([^.]*|..\...|...\...)$/
|
||||
|
||||
def self.build(request)
|
||||
secret = request.env[TOKEN_KEY]
|
||||
host = request.env["HTTP_HOST"]
|
||||
host = request.host
|
||||
|
||||
new(secret, host).tap do |hash|
|
||||
hash.update(request.cookies)
|
||||
@@ -104,7 +123,7 @@ module ActionDispatch
|
||||
|
||||
if options[:domain] == :all
|
||||
@host =~ DOMAIN_REGEXP
|
||||
options[:domain] = ".#{$2}.#{$3}"
|
||||
options[:domain] = ".#{$1}.#{$2}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ module ActionDispatch
|
||||
|
||||
# The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
|
||||
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
|
||||
# action that sets <tt>flash[:notice] = "Successfully created"</tt> before redirecting to a display action that can
|
||||
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
|
||||
# then expose the flash to its template. Actually, that exposure is automatically done. Example:
|
||||
#
|
||||
# class PostsController < ActionController::Base
|
||||
# def create
|
||||
# # save post
|
||||
# flash[:notice] = "Successfully created post"
|
||||
# flash[:notice] = "Post successfully created"
|
||||
# redirect_to posts_path(@post)
|
||||
# end
|
||||
#
|
||||
@@ -30,6 +30,11 @@ module ActionDispatch
|
||||
# <div class="notice"><%= flash[:notice] %></div>
|
||||
# <% end %>
|
||||
#
|
||||
# Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
|
||||
#
|
||||
# flash.alert = "You must be logged in"
|
||||
# flash.notice = "Post successfully created"
|
||||
#
|
||||
# This example just places a string in the flash, but you can put any object in there. And of course, you can put as
|
||||
# many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
|
||||
#
|
||||
|
||||
@@ -6,8 +6,6 @@ module ActionDispatch
|
||||
# This middleware rescues any exception returned by the application and renders
|
||||
# nice exception pages if it's being rescued locally.
|
||||
class ShowExceptions
|
||||
LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, "::1", /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
|
||||
|
||||
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
|
||||
|
||||
cattr_accessor :rescue_responses
|
||||
@@ -66,7 +64,7 @@ module ActionDispatch
|
||||
log_error(exception)
|
||||
|
||||
request = Request.new(env)
|
||||
if @consider_all_requests_local || local_request?(request)
|
||||
if @consider_all_requests_local || request.local?
|
||||
rescue_action_locally(request, exception)
|
||||
else
|
||||
rescue_action_in_public(exception)
|
||||
@@ -112,11 +110,6 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
# True if the request came from localhost, 127.0.0.1.
|
||||
def local_request?(request)
|
||||
LOCALHOST.any? { |local_ip| local_ip === request.remote_addr && local_ip === request.remote_ip }
|
||||
end
|
||||
|
||||
def status_code(exception)
|
||||
Rack::Utils.status_code(@@rescue_responses[exception.class.name])
|
||||
end
|
||||
@@ -134,7 +127,7 @@ module ActionDispatch
|
||||
|
||||
ActiveSupport::Deprecation.silence do
|
||||
message = "\n#{exception.class} (#{exception.message}):\n"
|
||||
message << exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
|
||||
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
|
||||
message << " " << application_trace(exception).join("\n ")
|
||||
logger.fatal("#{message}\n\n")
|
||||
end
|
||||
|
||||
@@ -69,7 +69,7 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def active
|
||||
ActiveSupport::Deprecation.warn "All middlewares in the chain are active since the laziness " <<
|
||||
ActiveSupport::Deprecation.warn "All middlewares in the chain are active since the laziness " <<
|
||||
"was removed from the middleware stack", caller
|
||||
end
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ module ActionDispatch
|
||||
# match ':controller(/:action(/:id(.:format)))'
|
||||
#
|
||||
# This route states that it expects requests to consist of a
|
||||
# <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
|
||||
# <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
|
||||
# turn is followed optionally by an <tt>:id</tt>, which in turn is followed
|
||||
# optionally by a <tt>:format</tt>
|
||||
#
|
||||
@@ -134,8 +134,8 @@ module ActionDispatch
|
||||
# == HTTP Methods
|
||||
#
|
||||
# Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
|
||||
# Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
|
||||
# If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
|
||||
# Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
|
||||
# If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
|
||||
# The default value is <tt>:any</tt> which means that the route will respond to any of the HTTP methods.
|
||||
#
|
||||
# Examples:
|
||||
@@ -144,7 +144,7 @@ module ActionDispatch
|
||||
# match 'post/:id' => "posts#create_comment', :via => :post
|
||||
#
|
||||
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
|
||||
# URL will route to the <tt>show</tt> action.
|
||||
# URL will route to the <tt>show</tt> action.
|
||||
#
|
||||
# === HTTP helper methods
|
||||
#
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/object/with_options'
|
||||
require 'active_support/core_ext/object/try'
|
||||
|
||||
module ActionDispatch
|
||||
module Routing
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
require 'erb'
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
@@ -25,21 +26,27 @@ module ActionDispatch
|
||||
@constraints.each { |constraint|
|
||||
if constraint.respond_to?(:matches?) && !constraint.matches?(req)
|
||||
return [ 404, {'X-Cascade' => 'pass'}, [] ]
|
||||
elsif constraint.respond_to?(:call) && !constraint.call(req)
|
||||
elsif constraint.respond_to?(:call) && !constraint.call(*constraint_args(constraint, req))
|
||||
return [ 404, {'X-Cascade' => 'pass'}, [] ]
|
||||
end
|
||||
}
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
private
|
||||
def constraint_args(constraint, request)
|
||||
constraint.arity == 1 ? [request] : [request.symbolized_path_parameters, request]
|
||||
end
|
||||
end
|
||||
|
||||
class Mapping #:nodoc:
|
||||
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
|
||||
|
||||
def initialize(set, scope, args)
|
||||
@set, @scope = set, scope
|
||||
@path, @options = extract_path_and_options(args)
|
||||
def initialize(set, scope, path, options)
|
||||
@set, @scope = set, scope
|
||||
@options = (@scope[:options] || {}).merge(options)
|
||||
@path = normalize_path(path)
|
||||
normalize_options!
|
||||
end
|
||||
|
||||
@@ -48,28 +55,6 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
private
|
||||
def extract_path_and_options(args)
|
||||
options = args.extract_options!
|
||||
|
||||
if using_to_shorthand?(args, options)
|
||||
path, to = options.find { |name, value| name.is_a?(String) }
|
||||
options.merge!(:to => to).delete(path) if path
|
||||
else
|
||||
path = args.first
|
||||
end
|
||||
|
||||
if path.match(':controller')
|
||||
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
|
||||
|
||||
# Add a default constraint for :controller path segments that matches namespaced
|
||||
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
||||
# GET /admin/products/show/1
|
||||
# => { :controller => 'admin/products', :action => 'show', :id => '1' }
|
||||
options.reverse_merge!(:controller => /.+?/)
|
||||
end
|
||||
|
||||
[ normalize_path(path), options ]
|
||||
end
|
||||
|
||||
def normalize_options!
|
||||
path_without_format = @path.sub(/\(\.:format\)$/, '')
|
||||
@@ -77,25 +62,39 @@ module ActionDispatch
|
||||
if using_match_shorthand?(path_without_format, @options)
|
||||
to_shorthand = @options[:to].blank?
|
||||
@options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1')
|
||||
@options[:as] ||= path_without_format[1..-1].gsub("/", "_")
|
||||
@options[:as] ||= Mapper.normalize_name(path_without_format)
|
||||
end
|
||||
|
||||
@options.merge!(default_controller_and_action(to_shorthand))
|
||||
end
|
||||
|
||||
# match "account" => "account#index"
|
||||
def using_to_shorthand?(args, options)
|
||||
args.empty? && options.present?
|
||||
end
|
||||
|
||||
# match "account/overview"
|
||||
def using_match_shorthand?(path, options)
|
||||
path && options.except(:via, :anchor, :to, :as).empty? && path =~ %r{^/[\w\/]+$}
|
||||
end
|
||||
|
||||
def normalize_path(path)
|
||||
raise ArgumentError, "path is required" if @scope[:path].blank? && path.blank?
|
||||
Mapper.normalize_path("#{@scope[:path]}/#{path}")
|
||||
raise ArgumentError, "path is required" if path.blank?
|
||||
path = Mapper.normalize_path(path)
|
||||
|
||||
if path.match(':controller')
|
||||
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
|
||||
|
||||
# Add a default constraint for :controller path segments that matches namespaced
|
||||
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
||||
# GET /admin/products/show/1
|
||||
# => { :controller => 'admin/products', :action => 'show', :id => '1' }
|
||||
@options.reverse_merge!(:controller => /.+?/)
|
||||
end
|
||||
|
||||
if @options[:format] == false
|
||||
@options.delete(:format)
|
||||
path
|
||||
elsif path.include?(":format")
|
||||
path
|
||||
else
|
||||
"#{path}(.:format)"
|
||||
end
|
||||
end
|
||||
|
||||
def app
|
||||
@@ -218,6 +217,10 @@ module ActionDispatch
|
||||
path
|
||||
end
|
||||
|
||||
def self.normalize_name(name)
|
||||
normalize_path(name)[1..-1].gsub("/", "_")
|
||||
end
|
||||
|
||||
module Base
|
||||
def initialize(set) #:nodoc:
|
||||
@set = set
|
||||
@@ -227,8 +230,8 @@ module ActionDispatch
|
||||
match '/', options.reverse_merge(:as => :root)
|
||||
end
|
||||
|
||||
def match(*args)
|
||||
mapping = Mapping.new(@set, @scope, args).to_route
|
||||
def match(path, options=nil)
|
||||
mapping = Mapping.new(@set, @scope, path, options || {}).to_route
|
||||
@set.add_route(*mapping)
|
||||
self
|
||||
end
|
||||
@@ -244,7 +247,7 @@ module ActionDispatch
|
||||
|
||||
raise "A rack application must be specified" unless path
|
||||
|
||||
match(path, options.merge(:to => app, :anchor => false))
|
||||
match(path, options.merge(:to => app, :anchor => false, :format => false))
|
||||
self
|
||||
end
|
||||
|
||||
@@ -277,7 +280,6 @@ module ActionDispatch
|
||||
path = args.shift || block
|
||||
path_proc = path.is_a?(Proc) ? path : proc { |params| path % params }
|
||||
status = options[:status] || 301
|
||||
body = 'Moved Permanently'
|
||||
|
||||
lambda do |env|
|
||||
req = Request.new(env)
|
||||
@@ -288,13 +290,16 @@ module ActionDispatch
|
||||
uri = URI.parse(path_proc.call(*params))
|
||||
uri.scheme ||= req.scheme
|
||||
uri.host ||= req.host
|
||||
uri.port ||= req.port unless req.port == 80
|
||||
uri.port ||= req.port unless req.standard_port?
|
||||
|
||||
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
|
||||
|
||||
headers = {
|
||||
'Location' => uri.to_s,
|
||||
'Content-Type' => 'text/html',
|
||||
'Content-Length' => body.length.to_s
|
||||
}
|
||||
|
||||
[ status, headers, [body] ]
|
||||
end
|
||||
end
|
||||
@@ -324,13 +329,7 @@ module ActionDispatch
|
||||
ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller
|
||||
end
|
||||
|
||||
case args.first
|
||||
when String
|
||||
options[:path] = args.first
|
||||
when Symbol
|
||||
options[:controller] = args.first
|
||||
end
|
||||
|
||||
options[:path] = args.first if args.first.is_a?(String)
|
||||
recover = {}
|
||||
|
||||
options[:constraints] ||= {}
|
||||
@@ -362,8 +361,9 @@ module ActionDispatch
|
||||
@scope[:blocks] = recover[:block]
|
||||
end
|
||||
|
||||
def controller(controller)
|
||||
scope(controller.to_sym) { yield }
|
||||
def controller(controller, options={})
|
||||
options[:controller] = controller
|
||||
scope(options) { yield }
|
||||
end
|
||||
|
||||
def namespace(path, options = {})
|
||||
@@ -381,21 +381,6 @@ module ActionDispatch
|
||||
scope(:defaults => defaults) { yield }
|
||||
end
|
||||
|
||||
def match(*args)
|
||||
options = args.extract_options!
|
||||
|
||||
options = (@scope[:options] || {}).merge(options)
|
||||
|
||||
if @scope[:as] && !options[:as].blank?
|
||||
options[:as] = "#{@scope[:as]}_#{options[:as]}"
|
||||
elsif @scope[:as] && options[:as] == ""
|
||||
options[:as] = @scope[:as].to_s
|
||||
end
|
||||
|
||||
args.push(options)
|
||||
super(*args)
|
||||
end
|
||||
|
||||
private
|
||||
def scope_options
|
||||
@scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
|
||||
@@ -459,9 +444,9 @@ module ActionDispatch
|
||||
module Resources
|
||||
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
|
||||
# a path appended since they fit properly in their scope level.
|
||||
VALID_ON_OPTIONS = [:new, :collection, :member]
|
||||
CANONICAL_ACTIONS = [:index, :create, :new, :show, :update, :destroy]
|
||||
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
|
||||
VALID_ON_OPTIONS = [:new, :collection, :member]
|
||||
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
|
||||
CANONICAL_ACTIONS = %w(index create new show update destroy)
|
||||
|
||||
class Resource #:nodoc:
|
||||
DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
|
||||
@@ -470,7 +455,7 @@ module ActionDispatch
|
||||
|
||||
def initialize(entities, options = {})
|
||||
@name = entities.to_s
|
||||
@path = options.delete(:path) || @name
|
||||
@path = (options.delete(:path) || @name).to_s
|
||||
@controller = (options.delete(:controller) || @name).to_s
|
||||
@as = options.delete(:as)
|
||||
@options = options
|
||||
@@ -495,16 +480,14 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def plural
|
||||
name.to_s.pluralize
|
||||
@plural ||= name.to_s
|
||||
end
|
||||
|
||||
def singular
|
||||
name.to_s.singularize
|
||||
@singular ||= name.to_s.singularize
|
||||
end
|
||||
|
||||
def member_name
|
||||
singular
|
||||
end
|
||||
alias :member_name :singular
|
||||
|
||||
# Checks for uncountable plurals, and appends "_index" if they're.
|
||||
def collection_name
|
||||
@@ -515,9 +498,7 @@ module ActionDispatch
|
||||
{ :controller => controller }
|
||||
end
|
||||
|
||||
def collection_scope
|
||||
path
|
||||
end
|
||||
alias :collection_scope :path
|
||||
|
||||
def member_scope
|
||||
"#{path}/:id"
|
||||
@@ -538,21 +519,25 @@ module ActionDispatch
|
||||
|
||||
def initialize(entities, options)
|
||||
@name = entities.to_s
|
||||
@path = options.delete(:path) || @name
|
||||
@path = (options.delete(:path) || @name).to_s
|
||||
@controller = (options.delete(:controller) || plural).to_s
|
||||
@as = options.delete(:as)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def member_name
|
||||
name
|
||||
def plural
|
||||
@plural ||= name.to_s.pluralize
|
||||
end
|
||||
alias :collection_name :member_name
|
||||
|
||||
def member_scope
|
||||
path
|
||||
def singular
|
||||
@singular ||= name.to_s
|
||||
end
|
||||
alias :nested_scope :member_scope
|
||||
|
||||
alias :member_name :singular
|
||||
alias :collection_name :singular
|
||||
|
||||
alias :member_scope :path
|
||||
alias :nested_scope :path
|
||||
end
|
||||
|
||||
def initialize(*args) #:nodoc:
|
||||
@@ -583,10 +568,10 @@ module ActionDispatch
|
||||
end if parent_resource.actions.include?(:new)
|
||||
|
||||
member_scope do
|
||||
get :edit if parent_resource.actions.include?(:edit)
|
||||
get :show if parent_resource.actions.include?(:show)
|
||||
put :update if parent_resource.actions.include?(:update)
|
||||
delete :destroy if parent_resource.actions.include?(:destroy)
|
||||
get :edit if parent_resource.actions.include?(:edit)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -613,10 +598,10 @@ module ActionDispatch
|
||||
end if parent_resource.actions.include?(:new)
|
||||
|
||||
member_scope do
|
||||
get :edit if parent_resource.actions.include?(:edit)
|
||||
get :show if parent_resource.actions.include?(:show)
|
||||
put :update if parent_resource.actions.include?(:update)
|
||||
delete :destroy if parent_resource.actions.include?(:destroy)
|
||||
get :edit if parent_resource.actions.include?(:edit)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -710,46 +695,31 @@ module ActionDispatch
|
||||
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
||||
end
|
||||
|
||||
if @scope[:scope_level] == :resource
|
||||
if @scope[:scope_level] == :resources
|
||||
args.push(options)
|
||||
return nested { match(*args) }
|
||||
elsif @scope[:scope_level] == :resource
|
||||
args.push(options)
|
||||
return member { match(*args) }
|
||||
end
|
||||
|
||||
path = options.delete(:path)
|
||||
action = args.first
|
||||
path = path_for_action(action, options.delete(:path))
|
||||
|
||||
if action.is_a?(Symbol)
|
||||
path = path_for_action(action, path)
|
||||
options[:to] ||= action
|
||||
options[:as] = name_for_action(action, options[:as])
|
||||
|
||||
with_exclusive_scope do
|
||||
return super(path, options)
|
||||
end
|
||||
elsif resource_method_scope?
|
||||
path = path_for_custom_action
|
||||
options[:as] = name_for_action(options[:as]) if options[:as]
|
||||
args.push(options)
|
||||
|
||||
with_exclusive_scope do
|
||||
scope(path) do
|
||||
return super
|
||||
end
|
||||
end
|
||||
if action.to_s =~ /^[\w\/]+$/
|
||||
options[:action] ||= action unless action.to_s.include?("/")
|
||||
options[:as] = name_for_action(action, options[:as])
|
||||
else
|
||||
options[:as] = name_for_action(options[:as])
|
||||
end
|
||||
|
||||
if resource_scope?
|
||||
raise ArgumentError, "can't define route directly in resource(s) scope"
|
||||
end
|
||||
|
||||
args.push(options)
|
||||
super
|
||||
super(path, options)
|
||||
end
|
||||
|
||||
def root(options={})
|
||||
if @scope[:scope_level] == :resources
|
||||
with_scope_level(:nested) do
|
||||
scope(parent_resource.path, :as => parent_resource.collection_name) do
|
||||
with_scope_level(:root) do
|
||||
scope(parent_resource.path) do
|
||||
super(options)
|
||||
end
|
||||
end
|
||||
@@ -770,6 +740,10 @@ module ActionDispatch
|
||||
return true
|
||||
end
|
||||
|
||||
options.keys.each do |k|
|
||||
(options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
|
||||
end
|
||||
|
||||
scope_options = options.slice!(*RESOURCE_OPTIONS)
|
||||
unless scope_options.empty?
|
||||
scope(scope_options) do
|
||||
@@ -882,7 +856,7 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def canonical_action?(action, flag)
|
||||
flag && CANONICAL_ACTIONS.include?(action)
|
||||
flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
|
||||
end
|
||||
|
||||
def shallow_scoping?
|
||||
@@ -893,18 +867,10 @@ module ActionDispatch
|
||||
prefix = shallow_scoping? ?
|
||||
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
|
||||
|
||||
if canonical_action?(action, path.blank?)
|
||||
"#{prefix}(.:format)"
|
||||
path = if canonical_action?(action, path.blank?)
|
||||
prefix.to_s
|
||||
else
|
||||
"#{prefix}/#{action_path(action, path)}(.:format)"
|
||||
end
|
||||
end
|
||||
|
||||
def path_for_custom_action
|
||||
if shallow_scoping?
|
||||
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id"
|
||||
else
|
||||
@scope[:path]
|
||||
"#{prefix}/#{action_path(action, path)}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -914,44 +880,61 @@ module ActionDispatch
|
||||
|
||||
def prefix_name_for_action(action, as)
|
||||
if as.present?
|
||||
"#{as}_"
|
||||
as.to_s
|
||||
elsif as
|
||||
""
|
||||
nil
|
||||
elsif !canonical_action?(action, @scope[:scope_level])
|
||||
"#{action}_"
|
||||
action.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def name_for_action(action, as=nil)
|
||||
prefix = prefix_name_for_action(action, as)
|
||||
prefix = Mapper.normalize_name(prefix) if prefix
|
||||
name_prefix = @scope[:as]
|
||||
|
||||
if parent_resource
|
||||
collection_name = parent_resource.collection_name
|
||||
member_name = parent_resource.member_name
|
||||
name_prefix = "#{name_prefix}_" if name_prefix.present?
|
||||
end
|
||||
|
||||
case @scope[:scope_level]
|
||||
name = case @scope[:scope_level]
|
||||
when :nested
|
||||
[member_name, prefix]
|
||||
when :collection
|
||||
"#{prefix}#{name_prefix}#{collection_name}"
|
||||
[prefix, name_prefix, collection_name]
|
||||
when :new
|
||||
"#{prefix}new_#{name_prefix}#{member_name}"
|
||||
[prefix, :new, name_prefix, member_name]
|
||||
when :member
|
||||
[prefix, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name]
|
||||
when :root
|
||||
[name_prefix, collection_name, prefix]
|
||||
else
|
||||
if shallow_scoping?
|
||||
shallow_prefix = "#{@scope[:shallow_prefix]}_" if @scope[:shallow_prefix].present?
|
||||
"#{prefix}#{shallow_prefix}#{member_name}"
|
||||
else
|
||||
"#{prefix}#{name_prefix}#{member_name}"
|
||||
end
|
||||
[name_prefix, member_name, prefix]
|
||||
end
|
||||
|
||||
name.select(&:present?).join("_").presence
|
||||
end
|
||||
end
|
||||
|
||||
module Shorthand
|
||||
def match(*args)
|
||||
if args.size == 1 && args.last.is_a?(Hash)
|
||||
options = args.pop
|
||||
path, to = options.find { |name, value| name.is_a?(String) }
|
||||
options.merge!(:to => to).delete(path)
|
||||
super(path, options)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
include Base
|
||||
include HttpHelpers
|
||||
include Scoping
|
||||
include Resources
|
||||
include Shorthand
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -148,29 +148,29 @@ module ActionDispatch
|
||||
def build_named_route_call(records, inflection, options = {})
|
||||
unless records.is_a?(Array)
|
||||
record = extract_record(records)
|
||||
route = ''
|
||||
route = []
|
||||
else
|
||||
record = records.pop
|
||||
route = records.inject("") do |string, parent|
|
||||
route = records.map do |parent|
|
||||
if parent.is_a?(Symbol) || parent.is_a?(String)
|
||||
string << "#{parent}_"
|
||||
parent
|
||||
else
|
||||
string << ActiveModel::Naming.plural(parent).singularize
|
||||
string << "_"
|
||||
ActiveModel::Naming.plural(parent).singularize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if record.is_a?(Symbol) || record.is_a?(String)
|
||||
route << "#{record}_"
|
||||
route << record
|
||||
else
|
||||
route << ActiveModel::Naming.plural(record)
|
||||
route = route.singularize if inflection == :singular
|
||||
route << "_"
|
||||
route << "index_" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
|
||||
route = [route.join("_").singularize] if inflection == :singular
|
||||
route << "index" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
|
||||
end
|
||||
|
||||
action_prefix(options) + route + routing_type(options).to_s
|
||||
route << routing_type(options)
|
||||
|
||||
action_prefix(options) + route.join("_")
|
||||
end
|
||||
|
||||
def extract_record(record_or_hash_or_array)
|
||||
|
||||
@@ -392,10 +392,9 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def generate
|
||||
error = ActionController::RoutingError.new("No route matches #{options.inspect}")
|
||||
path, params = @set.set.generate(:path_info, named_route, options, recall, opts)
|
||||
|
||||
raise error unless path
|
||||
raise_routing_error unless path
|
||||
|
||||
params.reject! {|k,v| !v }
|
||||
|
||||
@@ -404,7 +403,7 @@ module ActionDispatch
|
||||
path << "?#{params.to_query}" if params.any?
|
||||
"#{script_name}#{path}"
|
||||
rescue Rack::Mount::RoutingError
|
||||
raise error
|
||||
raise_routing_error
|
||||
end
|
||||
|
||||
def opts
|
||||
@@ -421,6 +420,10 @@ module ActionDispatch
|
||||
{:parameterize => parameterize}
|
||||
end
|
||||
|
||||
def raise_routing_error
|
||||
raise ActionController::RoutingError.new("No route matches #{options.inspect}")
|
||||
end
|
||||
|
||||
def different_controller?
|
||||
return false unless current_controller
|
||||
controller.to_param != current_controller.to_param
|
||||
@@ -491,7 +494,7 @@ module ActionDispatch
|
||||
|
||||
def recognize_path(path, environment = {})
|
||||
method = (environment[:method] || "GET").to_s.upcase
|
||||
path = Rack::Mount::Utils.normalize_path(path)
|
||||
path = Rack::Mount::Utils.normalize_path(path) unless path =~ %r{://}
|
||||
|
||||
begin
|
||||
env = Rack::MockRequest.env_for(path, {:method => method})
|
||||
@@ -499,7 +502,7 @@ module ActionDispatch
|
||||
raise ActionController::RoutingError, e.message
|
||||
end
|
||||
|
||||
req = Rack::Request.new(env)
|
||||
req = @request_class.new(env)
|
||||
@set.recognize(req) do |route, matches, params|
|
||||
params.each do |key, value|
|
||||
if value.is_a?(String)
|
||||
|
||||
@@ -3,7 +3,7 @@ require 'action_controller/vendor/html-scanner'
|
||||
module ActionDispatch
|
||||
module Assertions
|
||||
module DomAssertions
|
||||
# Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
|
||||
# \Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module ActionDispatch
|
||||
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
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -18,8 +18,8 @@ module ActionDispatch
|
||||
# * <tt>:missing</tt> - Status code was 404
|
||||
# * <tt>:error</tt> - Status code was in the 500-599 range
|
||||
#
|
||||
# You can also pass an explicit status number like assert_response(501)
|
||||
# or its symbolic equivalent assert_response(:not_implemented).
|
||||
# You can also pass an explicit status number like <tt>assert_response(501)</tt>
|
||||
# or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
|
||||
# See ActionDispatch::StatusCodes for a full list.
|
||||
#
|
||||
# ==== Examples
|
||||
@@ -45,8 +45,8 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
# Assert that the redirection options passed in match those of the redirect called in the latest action.
|
||||
# This match can be partial, such that assert_redirected_to(:controller => "weblog") will also
|
||||
# match the redirection of redirect_to(:controller => "weblog", :action => "show") and so on.
|
||||
# This match can be partial, such that <tt>assert_redirected_to(:controller => "weblog")</tt> will also
|
||||
# match the redirection of <tt>redirect_to(:controller => "weblog", :action => "show")</tt> and so on.
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
require 'uri'
|
||||
require 'active_support/core_ext/hash/diff'
|
||||
require 'active_support/core_ext/hash/indifferent_access'
|
||||
|
||||
module ActionDispatch
|
||||
module Assertions
|
||||
# Suite of assertions to test routes generated by Rails and the handling of requests made to them.
|
||||
# Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
|
||||
module RoutingAssertions
|
||||
# Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
|
||||
# match +path+. Basically, it asserts that Rails recognizes the route given by +expected_options+.
|
||||
# match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
|
||||
#
|
||||
# Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
|
||||
# requiring a specific HTTP method. The hash should contain a :path with the incoming request path
|
||||
@@ -40,14 +41,7 @@ module ActionDispatch
|
||||
# # Check a Simply RESTful generated route
|
||||
# assert_recognizes list_items_url, 'items/list'
|
||||
def assert_recognizes(expected_options, path, extras={}, message=nil)
|
||||
if path.is_a? Hash
|
||||
request_method = path[:method]
|
||||
path = path[:path]
|
||||
else
|
||||
request_method = nil
|
||||
end
|
||||
|
||||
request = recognized_request_for(path, request_method)
|
||||
request = recognized_request_for(path)
|
||||
|
||||
expected_options = expected_options.clone
|
||||
extras.each_key { |key| expected_options.delete key } unless extras.nil?
|
||||
@@ -77,7 +71,16 @@ module ActionDispatch
|
||||
# # Asserts that the generated route gives us our custom route
|
||||
# assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" }
|
||||
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
|
||||
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
|
||||
if expected_path =~ %r{://}
|
||||
begin
|
||||
uri = URI.parse(expected_path)
|
||||
expected_path = uri.path.to_s.empty? ? "/" : uri.path
|
||||
rescue URI::InvalidURIError => e
|
||||
raise ActionController::RoutingError, e.message
|
||||
end
|
||||
else
|
||||
expected_path = "/#{expected_path}" unless expected_path.first == '/'
|
||||
end
|
||||
# Load routes.rb if it hasn't been loaded.
|
||||
|
||||
generated_path, extra_keys = @routes.generate_extras(options, defaults)
|
||||
@@ -177,15 +180,35 @@ module ActionDispatch
|
||||
|
||||
private
|
||||
# Recognizes the route for a given path.
|
||||
def recognized_request_for(path, request_method = nil)
|
||||
path = "/#{path}" unless path.first == '/'
|
||||
def recognized_request_for(path)
|
||||
if path.is_a?(Hash)
|
||||
method = path[:method]
|
||||
path = path[:path]
|
||||
else
|
||||
method = :get
|
||||
end
|
||||
|
||||
# Assume given controller
|
||||
request = ActionController::TestRequest.new
|
||||
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
|
||||
request.path = path
|
||||
|
||||
params = @routes.recognize_path(path, { :method => request.method })
|
||||
if path =~ %r{://}
|
||||
begin
|
||||
uri = URI.parse(path)
|
||||
request.env["rack.url_scheme"] = uri.scheme || "http"
|
||||
request.host = uri.host if uri.host
|
||||
request.port = uri.port if uri.port
|
||||
request.path = uri.path.to_s.empty? ? "/" : uri.path
|
||||
rescue URI::InvalidURIError => e
|
||||
raise ActionController::RoutingError, e.message
|
||||
end
|
||||
else
|
||||
path = "/#{path}" unless path.first == "/"
|
||||
request.path = path
|
||||
end
|
||||
|
||||
request.request_method = method if method
|
||||
|
||||
params = @routes.recognize_path(path, { :method => method })
|
||||
request.path_parameters = params.with_indifferent_access
|
||||
|
||||
request
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require 'stringio'
|
||||
require 'uri'
|
||||
require 'active_support/core_ext/kernel/singleton_class'
|
||||
require 'active_support/core_ext/object/try'
|
||||
require 'rack/test'
|
||||
require 'test/unit/assertions'
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ module ActionPack
|
||||
MAJOR = 3
|
||||
MINOR = 0
|
||||
TINY = 0
|
||||
BUILD = "rc"
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -227,6 +227,7 @@ module ActionView #:nodoc:
|
||||
@lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
|
||||
lookup_context : ActionView::LookupContext.new(lookup_context)
|
||||
@lookup_context.formats = formats if formats
|
||||
@controller = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :controller)
|
||||
end
|
||||
|
||||
def controller_path
|
||||
|
||||
@@ -393,6 +393,18 @@ module ActionView
|
||||
@@stylesheet_expansions.merge!(expansions)
|
||||
end
|
||||
|
||||
def self.reset_javascript_include_default
|
||||
ActiveSupport::Deprecation.warn "reset_javascript_include_default is deprecated. Please manipulate " \
|
||||
"config.action_view.javascript_expansions[:defaults] directly", caller
|
||||
self.javascript_expansions[:defaults] = ['prototype', 'effects', 'dragdrop', 'controls', 'rails']
|
||||
end
|
||||
|
||||
def self.register_javascript_include_default(*args)
|
||||
ActiveSupport::Deprecation.warn "register_javascript_include_default is deprecated. Please " \
|
||||
"manipulate config.action_view.javascript_expansions[:defaults] directly", caller
|
||||
self.javascript_expansions[:defaults].concat args
|
||||
end
|
||||
|
||||
# Computes the path to a stylesheet asset in the public stylesheets directory.
|
||||
# If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
|
||||
# Full paths from the document root will be passed through.
|
||||
|
||||
@@ -106,7 +106,7 @@ module ActionView
|
||||
# <%= javascript_include_tag :defaults %>
|
||||
# <% end %>
|
||||
#
|
||||
# That will place <script> tags for Prototype, Scriptaculous, and application.js (if it exists)
|
||||
# That will place <tt>script</tt> tags for Prototype, Scriptaculous, and application.js (if it exists)
|
||||
# on the page; this technique is useful if you'll only be using these scripts in a few views.
|
||||
#
|
||||
# Note that content_for concatenates the blocks it is given for a particular
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require "date"
|
||||
require 'date'
|
||||
require 'action_view/helpers/tag_helper'
|
||||
require 'active_support/core_ext/hash/slice'
|
||||
require 'active_support/core_ext/object/with_options'
|
||||
|
||||
module ActionView
|
||||
module Helpers
|
||||
@@ -751,10 +752,8 @@ module ActionView
|
||||
# => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
# "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
||||
def translated_month_names
|
||||
begin
|
||||
key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
|
||||
I18n.translate(key, :locale => @options[:locale])
|
||||
end
|
||||
key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
|
||||
I18n.translate(key, :locale => @options[:locale])
|
||||
end
|
||||
|
||||
# Lookup month name for number
|
||||
@@ -781,9 +780,7 @@ module ActionView
|
||||
memoize :date_order
|
||||
|
||||
def translated_date_order
|
||||
begin
|
||||
I18n.translate(:'date.order', :locale => @options[:locale]) || []
|
||||
end
|
||||
I18n.translate(:'date.order', :locale => @options[:locale]) || []
|
||||
end
|
||||
|
||||
# Build full select tag from date type and options
|
||||
@@ -837,15 +834,14 @@ module ActionView
|
||||
# prompt_option_tag(:month, :prompt => 'Select month')
|
||||
# => "<option value="">Select month</option>"
|
||||
def prompt_option_tag(type, options)
|
||||
default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
|
||||
|
||||
case options
|
||||
when Hash
|
||||
prompt = default_options.merge(options)[type.to_sym]
|
||||
when String
|
||||
prompt = options
|
||||
else
|
||||
prompt = I18n.translate(('datetime.prompts.' + type.to_s).to_sym, :locale => @options[:locale])
|
||||
prompt = case options
|
||||
when Hash
|
||||
default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
|
||||
default_options.merge!(options)[type.to_sym]
|
||||
when String
|
||||
options
|
||||
else
|
||||
I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
|
||||
end
|
||||
|
||||
prompt ? content_tag(:option, prompt, :value => '') : ''
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module ActionView
|
||||
# = Action View Debug Helper
|
||||
#
|
||||
#
|
||||
# Provides a set of methods for making it easier to debug Rails objects.
|
||||
module Helpers
|
||||
module DebugHelper
|
||||
|
||||
@@ -624,16 +624,16 @@ module ActionView
|
||||
#
|
||||
# ==== Examples
|
||||
# password_field(:login, :pass, :size => 20)
|
||||
# # => <input type="text" id="login_pass" name="login[pass]" size="20" value="#{@login.pass}" />
|
||||
# # => <input type="password" id="login_pass" name="login[pass]" size="20" value="#{@login.pass}" />
|
||||
#
|
||||
# password_field(:account, :secret, :class => "form_input")
|
||||
# # => <input type="text" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
|
||||
# # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
|
||||
#
|
||||
# password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
|
||||
# # => <input type="text" id="user_password" name="user[password]" value="#{@user.password}" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
|
||||
# # => <input type="password" id="user_password" name="user[password]" value="#{@user.password}" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
|
||||
#
|
||||
# password_field(:account, :pin, :size => 20, :class => 'form_input')
|
||||
# # => <input type="text" id="account_pin" name="account[pin]" size="20" value="#{@account.pin}" class="form_input" />
|
||||
# # => <input type="password" id="account_pin" name="account[pin]" size="20" value="#{@account.pin}" class="form_input" />
|
||||
#
|
||||
def password_field(object_name, method, options = {})
|
||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("password", options)
|
||||
@@ -1006,9 +1006,14 @@ module ActionView
|
||||
|
||||
def value_before_type_cast(object, method_name)
|
||||
unless object.nil?
|
||||
object.respond_to?(method_name) ?
|
||||
object.send(method_name) :
|
||||
object.send(method_name + "_before_type_cast")
|
||||
if object.respond_to?(method_name)
|
||||
object.send(method_name)
|
||||
# FIXME: this is AR dependent
|
||||
elsif object.respond_to?(method_name + "_before_type_cast")
|
||||
object.send(method_name + "_before_type_cast")
|
||||
else
|
||||
raise NoMethodError, "Model #{object.class} does not respond to #{method_name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -53,16 +53,16 @@ module ActionView
|
||||
# <option value="2">Sam</option>
|
||||
# <option value="3">Tobias</option>
|
||||
# </select>
|
||||
#
|
||||
# Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
|
||||
#
|
||||
# Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
|
||||
# option to be in the +html_options+ parameter.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# select("album[]", "genre", %w[rap rock country], {}, { :index => nil })
|
||||
#
|
||||
#
|
||||
# becomes:
|
||||
#
|
||||
#
|
||||
# <select name="album[][genre]" id="album__genre">
|
||||
# <option value="rap">rap</option>
|
||||
# <option value="rock">rock</option>
|
||||
@@ -140,7 +140,7 @@ module ActionView
|
||||
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
|
||||
# of +collection+. The return values are used as the +value+ attribute and contents of each
|
||||
# <tt><option></tt> tag, respectively.
|
||||
#
|
||||
#
|
||||
# Example object structure for use with this method:
|
||||
# class Post < ActiveRecord::Base
|
||||
# belongs_to :author
|
||||
@@ -298,17 +298,18 @@ module ActionView
|
||||
return container if String === container
|
||||
|
||||
container = container.to_a if Hash === container
|
||||
selected, disabled = extract_selected_and_disabled(selected)
|
||||
|
||||
options_for_select = container.inject([]) do |options, element|
|
||||
html_attributes = option_html_attributes(element)
|
||||
text, value = option_text_and_value(element)
|
||||
selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
|
||||
disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
|
||||
options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{html_escape(text.to_s)}</option>)
|
||||
selected, disabled = extract_selected_and_disabled(selected).map do | r |
|
||||
Array.wrap(r).map(&:to_s)
|
||||
end
|
||||
|
||||
options_for_select.join("\n").html_safe
|
||||
container.map do |element|
|
||||
html_attributes = option_html_attributes(element)
|
||||
text, value = option_text_and_value(element).map(&:to_s)
|
||||
selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
|
||||
disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
|
||||
%(<option value="#{html_escape(value)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{html_escape(text)}</option>)
|
||||
end.join("\n").html_safe
|
||||
|
||||
end
|
||||
|
||||
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
|
||||
@@ -493,7 +494,7 @@ module ActionView
|
||||
end
|
||||
|
||||
zone_options += options_for_select(convert_zones[zones], selected)
|
||||
zone_options
|
||||
zone_options.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
@@ -528,10 +529,12 @@ module ActionView
|
||||
end
|
||||
|
||||
def extract_selected_and_disabled(selected)
|
||||
if selected.is_a?(Hash)
|
||||
[selected[:selected], selected[:disabled]]
|
||||
if selected.is_a?(Proc)
|
||||
[ selected, nil ]
|
||||
else
|
||||
[selected, nil]
|
||||
selected = Array.wrap(selected)
|
||||
options = selected.extract_options!.symbolize_keys
|
||||
[ options[:selected] || selected , options[:disabled] ]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ module ActionView
|
||||
# If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
|
||||
# is added to simulate the verb over post.
|
||||
# * A list of parameters to feed to the URL the form will be posted to.
|
||||
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
|
||||
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
|
||||
# submit behaviour. By default this behaviour is an ajax submit.
|
||||
#
|
||||
# ==== Examples
|
||||
@@ -42,10 +42,10 @@ module ActionView
|
||||
# <div><%= submit_tag 'Save' %></div>
|
||||
# <% end -%>
|
||||
# # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
|
||||
#
|
||||
#
|
||||
# <%= form_tag('/posts', :remote => true) %>
|
||||
# # => <form action="/posts" method="post" data-remote="true">
|
||||
#
|
||||
#
|
||||
def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
|
||||
html_options = html_options_for_form(url_for_options, options, *parameters_for_url)
|
||||
if block_given?
|
||||
@@ -351,12 +351,12 @@ module ActionView
|
||||
# Creates a submit button with the text <tt>value</tt> as the caption.
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>:confirm => 'question?'</tt> - If present the unobtrusive JavaScript
|
||||
# drivers will provide a prompt with the question specified. If the user accepts,
|
||||
# * <tt>:confirm => 'question?'</tt> - If present the unobtrusive JavaScript
|
||||
# drivers will provide a prompt with the question specified. If the user accepts,
|
||||
# the form is processed normally, otherwise no action is taken.
|
||||
# * <tt>:disabled</tt> - If true, the user will not be able to use this input.
|
||||
# * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
|
||||
# disabled version of the submit button when the form is submitted. This feature is
|
||||
# * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
|
||||
# disabled version of the submit button when the form is submitted. This feature is
|
||||
# provided by the unobtrusive JavaScript driver.
|
||||
# * Any other key creates standard HTML options for the tag.
|
||||
#
|
||||
@@ -383,7 +383,7 @@ module ActionView
|
||||
# # name="commit" type="submit" value="Edit" />
|
||||
#
|
||||
# submit_tag "Save", :confirm => "Are you sure?"
|
||||
# # => <input name='commit' type='submit' value='Save'
|
||||
# # => <input name='commit' type='submit' value='Save'
|
||||
# data-confirm="Are you sure?" />
|
||||
#
|
||||
def submit_tag(value = "Save changes", options = {})
|
||||
@@ -538,7 +538,7 @@ module ActionView
|
||||
|
||||
def extra_tags_for_form(html_options)
|
||||
snowman_tag = tag(:input, :type => "hidden",
|
||||
:name => "_snowman", :value => "☃".html_safe)
|
||||
:name => "utf8", :value => "✓".html_safe)
|
||||
|
||||
method = html_options.delete("method").to_s
|
||||
|
||||
|
||||
@@ -325,7 +325,7 @@ module ActionView
|
||||
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
|
||||
human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
|
||||
defaults = defaults.merge(human)
|
||||
|
||||
|
||||
options = options.reverse_merge(defaults)
|
||||
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
|
||||
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
|
||||
|
||||
@@ -2,7 +2,7 @@ module ActionView #:nodoc:
|
||||
# = Action View Raw Output Helper
|
||||
module Helpers #:nodoc:
|
||||
module RawOutputHelper
|
||||
# This method outputs without escaping a string. Since escaping tags is
|
||||
# This method outputs without escaping a string. Since escaping tags is
|
||||
# now default, this can be used when you don't want Rails to automatically
|
||||
# escape tags. This is not recommended if the data is coming from the user's
|
||||
# input.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
require 'active_support/core_ext/object/try'
|
||||
require 'action_controller/vendor/html-scanner'
|
||||
require 'action_view/helpers/tag_helper'
|
||||
|
||||
@@ -7,7 +8,8 @@ module ActionView
|
||||
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
|
||||
# These helper methods extend Action View making them callable within your template files.
|
||||
module SanitizeHelper
|
||||
# This +sanitize+ helper will html encode all tags and strip all attributes that
|
||||
extend ActiveSupport::Concern
|
||||
# This +sanitize+ helper will html encode all tags and strip all attributes that
|
||||
# aren't specifically allowed.
|
||||
#
|
||||
# It also strips href/src tags with invalid protocols, like javascript: especially.
|
||||
@@ -19,7 +21,7 @@ module ActionView
|
||||
#
|
||||
# You can add or remove tags/attributes if you want to customize it a bit.
|
||||
# See ActionView::Base for full docs on the available options. You can add
|
||||
# tags/attributes for single uses of +sanitize+ by passing either the
|
||||
# tags/attributes for single uses of +sanitize+ by passing either the
|
||||
# <tt>:attributes</tt> or <tt>:tags</tt> options:
|
||||
#
|
||||
# Normal Use
|
||||
|
||||
@@ -10,6 +10,9 @@ module ActionView
|
||||
# your views. These helper methods extend Action View making them callable
|
||||
# within your template files.
|
||||
module TextHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include SanitizeHelper
|
||||
# The preferred method of outputting text in your views is to use the
|
||||
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
|
||||
# do not operate as expected in an eRuby code block. If you absolutely must
|
||||
@@ -459,7 +462,7 @@ module ActionView
|
||||
text.gsub(AUTO_LINK_RE) do
|
||||
scheme, href = $1, $&
|
||||
punctuation = []
|
||||
|
||||
|
||||
if auto_linked?($`, $')
|
||||
# do not change string; URL is already linked
|
||||
href
|
||||
@@ -504,7 +507,7 @@ module ActionView
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Detects already linked context or position in the middle of a tag
|
||||
def auto_linked?(left, right)
|
||||
(left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
|
||||
|
||||
@@ -5,21 +5,21 @@ module ActionView
|
||||
module Helpers
|
||||
module TranslationHelper
|
||||
# Delegates to I18n#translate but also performs three additional functions.
|
||||
# First, it'll catch MissingTranslationData exceptions and turn them into
|
||||
# inline spans that contains the missing key, such that you can see in a
|
||||
# First, it'll catch MissingTranslationData exceptions and turn them into
|
||||
# inline spans that contains the missing key, such that you can see in a
|
||||
# view what is missing where.
|
||||
#
|
||||
# Second, it'll scope the key by the current partial if the key starts
|
||||
# with a period. So if you call <tt>translate(".foo")</tt> from the
|
||||
# <tt>people/index.html.erb</tt> template, you'll actually be calling
|
||||
# Second, it'll scope the key by the current partial if the key starts
|
||||
# with a period. So if you call <tt>translate(".foo")</tt> from the
|
||||
# <tt>people/index.html.erb</tt> template, you'll actually be calling
|
||||
# <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
|
||||
# to translate many keys within the same partials and gives you a simple framework
|
||||
# for scoping them consistently. If you don't prepend the key with a period,
|
||||
# for scoping them consistently. If you don't prepend the key with a period,
|
||||
# nothing is converted.
|
||||
#
|
||||
# Third, it'll mark the translation as safe HTML if the key has the suffix
|
||||
# "_html" or the last element of the key is the word "html". For example,
|
||||
# calling translate("footer_html") or translate("footer.html") will return
|
||||
# Third, it'll mark the translation as safe HTML if the key has the suffix
|
||||
# "_html" or the last element of the key is the word "html". For example,
|
||||
# calling translate("footer_html") or translate("footer.html") will return
|
||||
# a safe HTML string that won't be escaped by other HTML helper methods. This
|
||||
# naming convention helps to identify translations that include HTML tags so that
|
||||
# you know what kind of output to expect when you call translate in a template.
|
||||
|
||||
@@ -367,8 +367,8 @@ module ActionView
|
||||
# "Go Back" link instead of a link to the comments page, we could do something like this...
|
||||
#
|
||||
# <%=
|
||||
# link_to_unless_current("Comment", { :controller => 'comments', :action => 'new}) do
|
||||
# link_to("Go back", { :controller => 'posts', :action => 'index' })
|
||||
# link_to_unless_current("Comment", { :controller => "comments", :action => "new" }) do
|
||||
# link_to("Go back", { :controller => "posts", :action => "index" })
|
||||
# end
|
||||
# %>
|
||||
def link_to_unless_current(name, options = {}, html_options = {}, &block)
|
||||
|
||||
@@ -7,7 +7,7 @@ module ActionView
|
||||
message = "Rendered #{from_rails_root(event.payload[:identifier])}"
|
||||
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
||||
message << (" (%.1fms)" % event.duration)
|
||||
info(message)
|
||||
info(message)
|
||||
end
|
||||
alias :render_partial :render_template
|
||||
alias :render_collection :render_template
|
||||
|
||||
@@ -47,11 +47,15 @@ module ActionView
|
||||
# Hello David
|
||||
# </html>
|
||||
#
|
||||
def _layout_for(name = nil, &block) #:nodoc:
|
||||
if !block || name
|
||||
@_content_for[name || :layout].html_safe
|
||||
def _layout_for(*args, &block) #:nodoc:
|
||||
name = args.first
|
||||
|
||||
if name.is_a?(Symbol)
|
||||
@_content_for[name].html_safe
|
||||
elsif block
|
||||
capture(*args, &block)
|
||||
else
|
||||
capture(&block)
|
||||
@_content_for[:layout].html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ module ActionView
|
||||
# you're following its conventions for RecordIdentifier#partial_path. Examples:
|
||||
#
|
||||
# # @account is an Account instance, so it uses the RecordIdentifier to replace
|
||||
# # <%= render :partial => "accounts/account", :locals => { :account => @buyer } %>
|
||||
# # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
|
||||
# <%= render :partial => @account %>
|
||||
#
|
||||
# # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user