mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Merge branch 'master' of https://github.com/rails/rails
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,12 +10,15 @@ activerecord/doc
|
||||
actionpack/doc
|
||||
actionmailer/doc
|
||||
activesupport/doc
|
||||
activesupport/test/tmp
|
||||
activemodel/test/fixtures/fixture_database.sqlite3
|
||||
actionpack/test/tmp
|
||||
activesupport/test/fixtures/isolation_test
|
||||
dist
|
||||
railties/test/500.html
|
||||
railties/test/fixtures/tmp
|
||||
railties/test/initializer/root/log
|
||||
railties/doc
|
||||
railties/guides/output
|
||||
railties/tmp
|
||||
.rvmrc
|
||||
|
||||
4
.yardopts
Normal file
4
.yardopts
Normal file
@@ -0,0 +1,4 @@
|
||||
--exclude /templates/
|
||||
--quiet
|
||||
act*/lib/**/*.rb
|
||||
railties/lib/**/*.rb
|
||||
52
Gemfile
52
Gemfile
@@ -1,41 +1,55 @@
|
||||
source 'http://rubygems.org'
|
||||
|
||||
gemspec
|
||||
|
||||
if ENV['AREL']
|
||||
gem "arel", :path => ENV['AREL']
|
||||
else
|
||||
gem "arel", :git => "git://github.com/rails/arel.git"
|
||||
end
|
||||
|
||||
gem "rails", :path => File.dirname(__FILE__)
|
||||
gem "rack", :git => "git://github.com/rack/rack.git"
|
||||
gem "rack-test", :git => "git://github.com/brynary/rack-test.git"
|
||||
|
||||
gem "rake", ">= 0.8.7"
|
||||
gem "mocha", ">= 0.9.8"
|
||||
gem "rdoc", ">= 2.5.9"
|
||||
gem "horo", ">= 1.0.1"
|
||||
|
||||
group :doc do
|
||||
gem "rdoc", "~> 3.4"
|
||||
gem "horo", "= 1.0.3"
|
||||
gem "RedCloth", "~> 4.2" if RUBY_VERSION < "1.9.3"
|
||||
end
|
||||
|
||||
# AS
|
||||
gem "memcache-client", ">= 1.8.5"
|
||||
|
||||
# AM
|
||||
gem "text-format", "~> 1.0.0"
|
||||
gem "fssm", "~> 0.2.5"
|
||||
|
||||
platforms :mri_18 do
|
||||
gem "system_timer"
|
||||
gem "ruby-debug", ">= 0.10.3"
|
||||
gem 'ruby-prof'
|
||||
end
|
||||
|
||||
platforms :mri_19 do
|
||||
# TODO: Remove the conditional when ruby-debug19 supports Ruby >= 1.9.3
|
||||
gem "ruby-debug19", :require => 'ruby-debug' if RUBY_VERSION < "1.9.3"
|
||||
end
|
||||
|
||||
platforms :ruby do
|
||||
if ENV["RB_FSEVENT"]
|
||||
gem 'rb-fsevent'
|
||||
end
|
||||
gem 'json'
|
||||
gem 'yajl-ruby'
|
||||
gem "nokogiri", ">= 1.4.3.1"
|
||||
gem "nokogiri", ">= 1.4.4"
|
||||
|
||||
# AR
|
||||
gem "sqlite3-ruby", "~> 1.3.1", :require => 'sqlite3'
|
||||
gem "sqlite3", "~> 1.3.3"
|
||||
|
||||
group :db do
|
||||
gem "pg", ">= 0.9.0"
|
||||
gem "mysql", ">= 2.8.1"
|
||||
gem "mysql2", :git => 'git://github.com/brianmario/mysql2.git'
|
||||
gem "mysql2", :git => "git://github.com/brianmario/mysql2.git"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,17 +58,25 @@ platforms :jruby do
|
||||
|
||||
gem "activerecord-jdbcsqlite3-adapter"
|
||||
|
||||
# This is needed by now to let tests work on JRuby
|
||||
# TODO: When the JRuby guys merge jruby-openssl in
|
||||
# jruby this will be removed
|
||||
gem "jruby-openssl"
|
||||
|
||||
group :db do
|
||||
gem "activerecord-jdbcmysql-adapter"
|
||||
gem "activerecord-jdbcpostgresql-adapter"
|
||||
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.1.0.beta
|
||||
|
||||
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].
|
||||
* 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.
|
||||
|
||||
91
Rakefile
Normal file → Executable file
91
Rakefile
Normal file → Executable file
@@ -1,34 +1,16 @@
|
||||
gem 'rdoc', '>= 2.5.9'
|
||||
require 'rdoc'
|
||||
#!/usr/bin/env rake
|
||||
|
||||
require 'rake'
|
||||
require 'rdoc/task'
|
||||
require 'rake/gempackagetask'
|
||||
require 'net/http'
|
||||
|
||||
# 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
|
||||
$:.unshift File.expand_path('..', __FILE__)
|
||||
require "tasks/release"
|
||||
|
||||
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
|
||||
desc "Build gem files for all projects"
|
||||
task :build => "all:build"
|
||||
|
||||
desc "Release all gems to gemcutter and create a tag"
|
||||
task :release => "all:release"
|
||||
|
||||
PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties)
|
||||
|
||||
@@ -54,27 +36,6 @@ task :smoke do
|
||||
system %(cd activerecord && #{$0} sqlite3:isolated_test)
|
||||
end
|
||||
|
||||
spec = eval(File.read('rails.gemspec'))
|
||||
Rake::GemPackageTask.new(spec) do |pkg|
|
||||
pkg.gem_spec = spec
|
||||
end
|
||||
|
||||
desc "Release all gems to gemcutter. Package rails, package & push components, then push rails"
|
||||
task :release => :release_projects do
|
||||
require 'rake/gemcutter'
|
||||
Rake::Gemcutter::Tasks.new(spec).define
|
||||
Rake::Task['gem:push'].invoke
|
||||
end
|
||||
|
||||
desc "Release all components to gemcutter."
|
||||
task :release_projects => :package do
|
||||
errors = []
|
||||
PROJECTS.each do |project|
|
||||
system(%(cd #{project} && #{$0} release)) || errors << project
|
||||
end
|
||||
fail("Errors in #{errors.join(', ')}") unless errors.empty?
|
||||
end
|
||||
|
||||
desc "Install gems for all projects."
|
||||
task :install => :gem do
|
||||
version = File.read("RAILS_VERSION").strip
|
||||
@@ -88,8 +49,6 @@ end
|
||||
|
||||
desc "Generate documentation for the Rails framework"
|
||||
RDoc::Task.new do |rdoc|
|
||||
hijack_rdoc!
|
||||
|
||||
rdoc.rdoc_dir = 'doc/rdoc'
|
||||
rdoc.title = "Ruby on Rails Documentation"
|
||||
|
||||
@@ -126,6 +85,7 @@ RDoc::Task.new do |rdoc|
|
||||
rdoc.rdoc_files.include('actionmailer/README.rdoc')
|
||||
rdoc.rdoc_files.include('actionmailer/CHANGELOG')
|
||||
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/base.rb')
|
||||
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/mail_helper.rb')
|
||||
rdoc.rdoc_files.exclude('actionmailer/lib/action_mailer/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('activesupport/README.rdoc')
|
||||
@@ -144,12 +104,7 @@ 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
|
||||
|
||||
desc 'Bump all versions to match version.rb'
|
||||
task :update_versions do
|
||||
require File.dirname(__FILE__) + "/version"
|
||||
|
||||
@@ -177,3 +132,27 @@ task :update_versions do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# We have a webhook configured in Github that gets invoked after pushes.
|
||||
# This hook triggers the following tasks:
|
||||
#
|
||||
# * updates the local checkout
|
||||
# * updates Rails Contributors
|
||||
# * generates and publishes edge docs
|
||||
# * if there's a new stable tag, generates and publishes stable docs
|
||||
#
|
||||
# Everything is automated and you do NOT need to run this task normally.
|
||||
#
|
||||
# We publish a new version by tagging, and pushing a tag does not trigger
|
||||
# that webhook. Stable docs would be updated by any subsequent regular
|
||||
# push, but if you want that to happen right away just run this.
|
||||
#
|
||||
desc 'Publishes docs, run this AFTER a new stable tag has been pushed'
|
||||
task :publish_docs do
|
||||
Net::HTTP.new('rails-hooks.hashref.com').start do |http|
|
||||
request = Net::HTTP::Post.new('/rails-master-hook')
|
||||
response = http.request(request)
|
||||
puts response.body
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
*Rails 3.0.0 [release candidate] (July 26th, 2010)*
|
||||
*Rails 3.1.0 (unreleased)*
|
||||
|
||||
* No material changes
|
||||
* No changes
|
||||
|
||||
*Rails 3.0.2 (unreleased)*
|
||||
|
||||
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
|
||||
* No changes
|
||||
|
||||
*Rails 3.0.1 (October 15, 2010)*
|
||||
|
||||
* No Changes, just a version bump.
|
||||
|
||||
*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 +18,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 +39,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 +179,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 +325,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 +364,7 @@
|
||||
@body = "Nothing to see here."
|
||||
@charset = "iso-8859-1"
|
||||
end
|
||||
|
||||
|
||||
def unencoded_subject(recipient)
|
||||
@recipients = recipient
|
||||
@subject = "testing unencoded subject"
|
||||
@@ -375,7 +373,7 @@
|
||||
@encode_subject = false
|
||||
@charset = "iso-8859-1"
|
||||
end
|
||||
|
||||
|
||||
|
||||
*0.6.1* (January 18th, 2005)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
Copyright (c) 2004-2011 David Heinemeier Hansson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -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,18 +55,18 @@ generated would look like this:
|
||||
charset="US-ASCII";
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Hello there,
|
||||
Hello there,
|
||||
|
||||
Mr. david@loudthinking.com
|
||||
|
||||
In previous version of rails you would call <tt>create_method_name</tt> and
|
||||
In previous version of Rails you would call <tt>create_method_name</tt> and
|
||||
<tt>deliver_method_name</tt>. Rails 3.0 has a much simpler interface, you
|
||||
simply call the method and optionally call +deliver+ on the return value.
|
||||
|
||||
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:
|
||||
|
||||
@@ -74,10 +74,10 @@ 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,
|
||||
To receive emails, you need to implement a public instance method called <tt>receive</tt> that takes an
|
||||
email 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.
|
||||
into the email object and calls the receive instance method.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -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
|
||||
@@ -128,7 +128,7 @@ The latest version of Action Mailer can be installed with Rubygems:
|
||||
|
||||
Source code can be downloaded as part of the Rails project on GitHub
|
||||
|
||||
* http://github.com/rails/rails/tree/master/actionmailer/
|
||||
* https://github.com/rails/rails/tree/master/actionmailer/
|
||||
|
||||
|
||||
== License
|
||||
@@ -145,3 +145,4 @@ API documentation is at
|
||||
Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
|
||||
|
||||
* https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets
|
||||
|
||||
|
||||
20
actionmailer/Rakefile
Normal file → Executable file
20
actionmailer/Rakefile
Normal file → Executable file
@@ -1,8 +1,5 @@
|
||||
gem 'rdoc', '>= 2.5.9'
|
||||
require 'rdoc'
|
||||
require 'rake'
|
||||
#!/usr/bin/env rake
|
||||
require 'rake/testtask'
|
||||
require 'rdoc/task'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
@@ -20,24 +17,11 @@ namespace :test do
|
||||
task :isolated do
|
||||
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
|
||||
Dir.glob("test/**/*_test.rb").all? do |file|
|
||||
system(ruby, '-Ilib:test', file)
|
||||
sh(ruby, '-Ilib:test', file)
|
||||
end or raise "Failures"
|
||||
end
|
||||
end
|
||||
|
||||
# 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|
|
||||
|
||||
@@ -20,5 +20,5 @@ Gem::Specification.new do |s|
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('actionpack', version)
|
||||
s.add_dependency('mail', '~> 2.2.5')
|
||||
s.add_dependency('mail', '~> 2.2.15')
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#--
|
||||
# Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
# Copyright (c) 2004-2011 David Heinemeier Hansson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
@@ -26,6 +26,7 @@ $:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(
|
||||
|
||||
require 'abstract_controller'
|
||||
require 'action_view'
|
||||
require 'action_mailer/version'
|
||||
|
||||
# Common Active Support usage in Action Mailer
|
||||
require 'active_support/core_ext/class'
|
||||
@@ -43,7 +44,6 @@ module ActionMailer
|
||||
autoload :Collector
|
||||
autoload :Base
|
||||
autoload :DeliveryMethods
|
||||
autoload :DeprecatedApi
|
||||
autoload :MailHelper
|
||||
autoload :OldApi
|
||||
autoload :TestCase
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
module ActionMailer
|
||||
module AdvAttrAccessor #:nodoc:
|
||||
def adv_attr_accessor(*names)
|
||||
names.each do |name|
|
||||
ivar = "@#{name}"
|
||||
def adv_attr_accessor(name, deprecation=nil)
|
||||
ivar = "@#{name}"
|
||||
deprecation ||= "Please pass :#{name} as hash key to mail() instead"
|
||||
|
||||
class_eval <<-ACCESSORS, __FILE__, __LINE__ + 1
|
||||
def #{name}=(value)
|
||||
#{ivar} = value
|
||||
class_eval <<-ACCESSORS, __FILE__, __LINE__ + 1
|
||||
def #{name}=(value)
|
||||
ActiveSupport::Deprecation.warn "#{name}= is deprecated. #{deprecation}"
|
||||
#{ivar} = value
|
||||
end
|
||||
|
||||
def #{name}(*args)
|
||||
raise ArgumentError, "expected 0 or 1 parameters" unless args.length <= 1
|
||||
if args.empty?
|
||||
ActiveSupport::Deprecation.warn "#{name}() is deprecated and will be removed in future versions."
|
||||
#{ivar} if instance_variable_names.include?(#{ivar.inspect})
|
||||
else
|
||||
ActiveSupport::Deprecation.warn "#{name}(value) is deprecated. #{deprecation}"
|
||||
#{ivar} = args.first
|
||||
end
|
||||
end
|
||||
ACCESSORS
|
||||
|
||||
def #{name}(*args)
|
||||
raise ArgumentError, "expected 0 or 1 parameters" unless args.length <= 1
|
||||
if args.empty?
|
||||
#{ivar} if instance_variable_names.include?(#{ivar.inspect})
|
||||
else
|
||||
#{ivar} = args.first
|
||||
end
|
||||
end
|
||||
ACCESSORS
|
||||
|
||||
self.protected_instance_variables << ivar if self.respond_to?(:protected_instance_variables)
|
||||
end
|
||||
self.protected_instance_variables << ivar if self.respond_to?(:protected_instance_variables)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -222,7 +222,7 @@ module ActionMailer #:nodoc:
|
||||
#
|
||||
# An interceptor object must implement the <tt>:delivering_email(message)</tt> method which will be
|
||||
# called before the email is sent, allowing you to make modifications to the email before it hits
|
||||
# the delivery agents. Your object should make and needed modifications directly to the passed
|
||||
# the delivery agents. Your object should make any needed modifications directly to the passed
|
||||
# in Mail::Message instance.
|
||||
#
|
||||
# = Default Hash
|
||||
@@ -234,8 +234,8 @@ module ActionMailer #:nodoc:
|
||||
# default :sender => 'system@example.com'
|
||||
# end
|
||||
#
|
||||
# You can pass in any header value that a <tt>Mail::Message</tt>, out of the box, <tt>ActionMailer::Base</tt>
|
||||
# sets the following:
|
||||
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
|
||||
# <tt>ActionMailer::Base</tt> sets the following:
|
||||
#
|
||||
# * <tt>:mime_version => "1.0"</tt>
|
||||
# * <tt>:charset => "UTF-8",</tt>
|
||||
@@ -246,7 +246,7 @@ module ActionMailer #:nodoc:
|
||||
# but Action Mailer translates them appropriately and sets the correct values.
|
||||
#
|
||||
# As you can pass in any header, you need to either quote the header as a string, or pass it in as
|
||||
# an underscorised symbol, so the following will work:
|
||||
# an underscored symbol, so the following will work:
|
||||
#
|
||||
# class Notifier < ActionMailer::Base
|
||||
# default 'Content-Transfer-Encoding' => '7bit',
|
||||
@@ -273,7 +273,7 @@ module ActionMailer #:nodoc:
|
||||
# = Configuration options
|
||||
#
|
||||
# These options are specified on the class level, like
|
||||
# <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
|
||||
# <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
|
||||
#
|
||||
# * <tt>default</tt> - You can pass this in at a class level as well as within the class itself as
|
||||
# per the above section.
|
||||
@@ -290,13 +290,15 @@ module ActionMailer #:nodoc:
|
||||
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
|
||||
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
|
||||
# authentication type here.
|
||||
# This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
|
||||
# This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
|
||||
# send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
|
||||
# information and a cryptographic Message Digest 5 algorithm to hash important information)
|
||||
# * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
|
||||
# and starts to use it.
|
||||
#
|
||||
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
|
||||
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
|
||||
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@addres</tt>
|
||||
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@address</tt>
|
||||
# added automatically before the message is sent.
|
||||
#
|
||||
# * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
|
||||
@@ -341,10 +343,11 @@ module ActionMailer #:nodoc:
|
||||
include AbstractController::Translation
|
||||
include AbstractController::AssetPaths
|
||||
|
||||
helper ActionMailer::MailHelper
|
||||
cattr_reader :protected_instance_variables
|
||||
@@protected_instance_variables = []
|
||||
|
||||
helper ActionMailer::MailHelper
|
||||
include ActionMailer::OldApi
|
||||
include ActionMailer::DeprecatedApi
|
||||
|
||||
delegate :register_observer, :to => Mail
|
||||
delegate :register_interceptor, :to => Mail
|
||||
@@ -360,7 +363,6 @@ module ActionMailer #:nodoc:
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
|
||||
def mailer_name
|
||||
@mailer_name ||= name.underscore
|
||||
end
|
||||
@@ -402,14 +404,14 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
def respond_to?(method, *args) #:nodoc:
|
||||
def respond_to?(method, include_private = false) #:nodoc:
|
||||
super || action_methods.include?(method.to_s)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_payload_for_mail(payload, mail) #:nodoc:
|
||||
payload[:mailer] = self.name
|
||||
payload[:mailer] = name
|
||||
payload[:message_id] = mail.message_id
|
||||
payload[:subject] = mail.subject
|
||||
payload[:to] = mail.to
|
||||
@@ -421,11 +423,8 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
|
||||
def method_missing(method, *args) #:nodoc:
|
||||
if action_methods.include?(method.to_s)
|
||||
new(method, *args).message
|
||||
else
|
||||
super
|
||||
end
|
||||
return super unless respond_to?(method)
|
||||
new(method, *args).message
|
||||
end
|
||||
end
|
||||
|
||||
@@ -446,6 +445,10 @@ module ActionMailer #:nodoc:
|
||||
super
|
||||
end
|
||||
|
||||
def mailer_name
|
||||
self.class.mailer_name
|
||||
end
|
||||
|
||||
# Allows you to pass random and unusual headers to the new +Mail::Message+ object
|
||||
# which will add them to itself.
|
||||
#
|
||||
@@ -690,15 +693,8 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
|
||||
def each_template(paths, name, &block) #:nodoc:
|
||||
Array.wrap(paths).each do |path|
|
||||
templates = lookup_context.find_all(name, path)
|
||||
templates = templates.uniq_by { |t| t.formats }
|
||||
|
||||
unless templates.empty?
|
||||
templates.each(&block)
|
||||
return
|
||||
end
|
||||
end
|
||||
templates = lookup_context.find_all(name, Array.wrap(paths))
|
||||
templates.uniq_by { |t| t.formats }.each(&block)
|
||||
end
|
||||
|
||||
def create_parts_from_responses(m, responses) #:nodoc:
|
||||
@@ -720,28 +716,6 @@ module ActionMailer #:nodoc:
|
||||
container.add_part(part)
|
||||
end
|
||||
|
||||
module DeprecatedUrlOptions
|
||||
def default_url_options
|
||||
deprecated_url_options
|
||||
end
|
||||
|
||||
def default_url_options=(val)
|
||||
deprecated_url_options
|
||||
end
|
||||
|
||||
def deprecated_url_options
|
||||
raise "You can no longer call ActionMailer::Base.default_url_options " \
|
||||
"directly. You need to set config.action_mailer.default_url_options. " \
|
||||
"If you are using ActionMailer standalone, you need to include the " \
|
||||
"routing url_helpers directly."
|
||||
end
|
||||
end
|
||||
|
||||
# This module will complain if the user tries to set default_url_options
|
||||
# directly instead of through the config object. In Action Mailer's Railtie,
|
||||
# we include the router's url_helpers, which will override this module.
|
||||
extend DeprecatedUrlOptions
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_mailer, self)
|
||||
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,141 +0,0 @@
|
||||
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.
|
||||
# Check those in OldApi instead.
|
||||
module DeprecatedApi #:nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
[:charset, :content_type, :mime_version, :implicit_parts_order].each do |method|
|
||||
class_eval <<-FILE, __FILE__, __LINE__ + 1
|
||||
def self.default_#{method}
|
||||
@@default_#{method}
|
||||
end
|
||||
|
||||
def self.default_#{method}=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionMailer::Base.default_#{method}=value is deprecated, " <<
|
||||
"use default :#{method} => value instead"
|
||||
@@default_#{method} = value
|
||||
end
|
||||
|
||||
@@default_#{method} = nil
|
||||
FILE
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Deliver the given mail object directly. This can be used to deliver
|
||||
# a preconstructed mail object, like:
|
||||
#
|
||||
# email = MyMailer.create_some_mail(parameters)
|
||||
# email.set_some_obscure_header "frobnicate"
|
||||
# MyMailer.deliver(email)
|
||||
def deliver(mail, show_warning=true)
|
||||
if show_warning
|
||||
ActiveSupport::Deprecation.warn "#{self}.deliver is deprecated, call " <<
|
||||
"deliver in the mailer instance instead", caller[0,2]
|
||||
end
|
||||
|
||||
raise "no mail object available for delivery!" unless mail
|
||||
wrap_delivery_behavior(mail)
|
||||
mail.deliver
|
||||
mail
|
||||
end
|
||||
|
||||
def template_root
|
||||
self.view_paths && self.view_paths.first
|
||||
end
|
||||
|
||||
def template_root=(root)
|
||||
ActiveSupport::Deprecation.warn "template_root= is deprecated, use prepend_view_path instead", caller[0,2]
|
||||
self.view_paths = ActionView::Base.process_view_paths(root)
|
||||
end
|
||||
|
||||
def respond_to?(method_symbol, include_private = false)
|
||||
matches_dynamic_method?(method_symbol) || super
|
||||
end
|
||||
|
||||
def method_missing(method_symbol, *parameters)
|
||||
if match = matches_dynamic_method?(method_symbol)
|
||||
case match[1]
|
||||
when 'create'
|
||||
ActiveSupport::Deprecation.warn "#{self}.create_#{match[2]} is deprecated, " <<
|
||||
"use #{self}.#{match[2]} instead", caller[0,2]
|
||||
new(match[2], *parameters).message
|
||||
when 'deliver'
|
||||
ActiveSupport::Deprecation.warn "#{self}.deliver_#{match[2]} is deprecated, " <<
|
||||
"use #{self}.#{match[2]}.deliver instead", caller[0,2]
|
||||
new(match[2], *parameters).message.deliver
|
||||
else super
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def matches_dynamic_method?(method_name)
|
||||
method_name = method_name.to_s
|
||||
/^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name)
|
||||
end
|
||||
end
|
||||
|
||||
# Delivers a Mail object. By default, it delivers the cached mail
|
||||
# object (from the <tt>create!</tt> method). If no cached mail object exists, and
|
||||
# no alternate has been given as the parameter, this will fail.
|
||||
def deliver!(mail = @_message)
|
||||
ActiveSupport::Deprecation.warn "Calling deliver in the AM::Base object is deprecated, " <<
|
||||
"please call deliver in the Mail instance", caller[0,2]
|
||||
self.class.deliver(mail, false)
|
||||
end
|
||||
alias :deliver :deliver!
|
||||
|
||||
def render(*args)
|
||||
options = args.last.is_a?(Hash) ? args.last : {}
|
||||
|
||||
if options[:body].is_a?(Hash)
|
||||
ActiveSupport::Deprecation.warn(':body in render deprecated. Please use instance ' <<
|
||||
'variables as assigns instead', caller[0,1])
|
||||
|
||||
options[:body].each { |k,v| instance_variable_set(:"@#{k}", v) }
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
# Render a message but does not set it as mail body. Useful for rendering
|
||||
# data for part and attachments.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# render_message "special_message"
|
||||
# render_message :template => "special_message"
|
||||
# render_message :inline => "<%= 'Hi!' %>"
|
||||
#
|
||||
def render_message(*args)
|
||||
ActiveSupport::Deprecation.warn "render_message is deprecated, use render instead", caller[0,2]
|
||||
render(*args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_defaults(*)
|
||||
@charset ||= self.class.default_charset.try(:dup)
|
||||
@content_type ||= self.class.default_content_type.try(:dup)
|
||||
@implicit_parts_order ||= self.class.default_implicit_parts_order.try(:dup)
|
||||
@mime_version ||= self.class.default_mime_version.try(:dup)
|
||||
super
|
||||
end
|
||||
|
||||
def create_parts
|
||||
if @body.is_a?(Hash) && !@body.empty?
|
||||
ActiveSupport::Deprecation.warn "Giving a hash to body is deprecated, please use instance variables instead", caller[0,2]
|
||||
@body.each { |k, v| instance_variable_set(:"@#{k}", v) }
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -3,23 +3,14 @@ module ActionMailer
|
||||
# Uses Text::Format to take the text and format it, indented two spaces for
|
||||
# each line, and wrapped at 72 columns.
|
||||
def block_format(text)
|
||||
begin
|
||||
require 'text/format'
|
||||
rescue LoadError => e
|
||||
$stderr.puts "You don't have text-format installed in your application. Please add it to your Gemfile and run bundle install"
|
||||
raise e
|
||||
end unless defined?(Text::Format)
|
||||
|
||||
formatted = text.split(/\n\r\n/).collect { |paragraph|
|
||||
Text::Format.new(
|
||||
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
|
||||
).format
|
||||
format_paragraph(paragraph)
|
||||
}.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
|
||||
|
||||
@@ -37,5 +28,29 @@ module ActionMailer
|
||||
def attachments
|
||||
@_message.attachments
|
||||
end
|
||||
|
||||
# Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
|
||||
#
|
||||
# === Examples
|
||||
#
|
||||
# my_text = "Here is a sample text with more than 40 characters"
|
||||
#
|
||||
# format_paragraph(my_text, 25, 4)
|
||||
# # => " Here is a sample text with\n more than 40 characters"
|
||||
def format_paragraph(text, len = 72, indent = 2)
|
||||
sentences = [[]]
|
||||
|
||||
text.split.each do |word|
|
||||
if (sentences.last + [word]).join(' ').length > len
|
||||
sentences << [word]
|
||||
else
|
||||
sentences.last << word
|
||||
end
|
||||
end
|
||||
|
||||
sentences.map { |sentence|
|
||||
"#{" " * indent}#{sentence.join(' ')}"
|
||||
}.join "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,9 +8,7 @@ module ActionMailer
|
||||
|
||||
included do
|
||||
extend ActionMailer::AdvAttrAccessor
|
||||
|
||||
@@protected_instance_variables = %w(@parts)
|
||||
cattr_reader :protected_instance_variables
|
||||
self.protected_instance_variables.concat %w(@parts @mail_was_called)
|
||||
|
||||
# Specify the BCC addresses for the message
|
||||
adv_attr_accessor :bcc
|
||||
@@ -42,11 +40,11 @@ module ActionMailer
|
||||
|
||||
# The recipient addresses for the message, either as a string (for a single
|
||||
# address) or an array (for multiple addresses).
|
||||
adv_attr_accessor :recipients
|
||||
adv_attr_accessor :recipients, "Please pass :to as hash key to mail() instead"
|
||||
|
||||
# The date on which the message was sent. If not set (the default), the
|
||||
# header will be set by the delivery agent.
|
||||
adv_attr_accessor :sent_on
|
||||
adv_attr_accessor :sent_on, "Please pass :date as hash key to mail() instead"
|
||||
|
||||
# Specify the subject of the message.
|
||||
adv_attr_accessor :subject
|
||||
@@ -54,20 +52,12 @@ module ActionMailer
|
||||
# Specify the template name to use for current message. This is the "base"
|
||||
# template name, without the extension or directory, and may be used to
|
||||
# have multiple mailer methods share the same template.
|
||||
adv_attr_accessor :template
|
||||
|
||||
# Override the mailer name, which defaults to an inflected version of the
|
||||
# mailer's class name. If you want to use a template in a non-standard
|
||||
# location, you can use this to specify that location.
|
||||
adv_attr_accessor :mailer_name
|
||||
adv_attr_accessor :template, "Please pass :template_name or :template_path as hash key to mail() instead"
|
||||
|
||||
# Define the body of the message. This is either a Hash (in which case it
|
||||
# specifies the variables to pass to the template when it is rendered),
|
||||
# or a string, in which case it specifies the actual text of the message.
|
||||
adv_attr_accessor :body
|
||||
|
||||
# Alias controller_path to mailer_name so render :partial in views work.
|
||||
alias :controller_path :mailer_name
|
||||
end
|
||||
|
||||
def process(method_name, *args)
|
||||
@@ -84,6 +74,8 @@ module ActionMailer
|
||||
# part itself is yielded to the block so that other properties (charset,
|
||||
# body, headers, etc.) can be set on it.
|
||||
def part(params)
|
||||
ActiveSupport::Deprecation.warn "part() is deprecated and will be removed in future versions. " <<
|
||||
"Please pass a block to mail() instead."
|
||||
params = {:content_type => params} if String === params
|
||||
|
||||
if custom_headers = params.delete(:headers)
|
||||
@@ -99,6 +91,8 @@ module ActionMailer
|
||||
# Add an attachment to a multipart message. This is simply a part with the
|
||||
# content-disposition set to "attachment".
|
||||
def attachment(params, &block)
|
||||
ActiveSupport::Deprecation.warn "attachment() is deprecated and will be removed in future versions. " <<
|
||||
"Please use the attachments[] API instead."
|
||||
params = { :content_type => params } if String === params
|
||||
|
||||
params[:content] ||= params.delete(:data) || params.delete(:body)
|
||||
@@ -116,43 +110,43 @@ 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 create_mail
|
||||
m = @_message
|
||||
|
||||
set_fields!({:subject => subject, :to => recipients, :from => from,
|
||||
:bcc => bcc, :cc => cc, :reply_to => reply_to}, charset)
|
||||
set_fields!({:subject => @subject, :to => @recipients, :from => @from,
|
||||
:bcc => @bcc, :cc => @cc, :reply_to => @reply_to}, @charset)
|
||||
|
||||
m.mime_version = mime_version unless mime_version.nil?
|
||||
m.date = sent_on.to_time rescue sent_on if sent_on
|
||||
m.mime_version = @mime_version if @mime_version
|
||||
m.date = @sent_on.to_time rescue @sent_on if @sent_on
|
||||
|
||||
@headers.each { |k, v| m[k] = v }
|
||||
|
||||
@@ -178,19 +172,21 @@ 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)
|
||||
@mime_version ||= self.class.default[:mime_version].try(:dup)
|
||||
|
||||
@cc, @bcc, @reply_to, @subject, @from, @recipients = nil, nil, nil, nil, nil, nil
|
||||
|
||||
@mailer_name ||= self.class.mailer_name.dup
|
||||
@template ||= method_name
|
||||
@mail_was_called = false
|
||||
@@ -201,11 +197,11 @@ 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/ }
|
||||
lookup_context.find_all(@template, @mailer_name).each do |template|
|
||||
lookup_context.find_all(@template, [@mailer_name]).each do |template|
|
||||
self.formats = template.formats
|
||||
@parts << create_inline_part(render(:template => template), template.mime_type)
|
||||
end
|
||||
@@ -216,11 +212,11 @@ module ActionMailer
|
||||
|
||||
# If this is a multipart e-mail add the mime_version if it is not
|
||||
# already set.
|
||||
@mime_version ||= "1.0" if !@parts.empty?
|
||||
@mime_version ||= "1.0" unless @parts.empty?
|
||||
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,17 +238,17 @@ 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
|
||||
if @content_type.blank?
|
||||
[ nil, {} ]
|
||||
else
|
||||
ctype, *attrs = @content_type.split(/;\s*/)
|
||||
attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h }
|
||||
[ctype, {"charset" => @charset}.merge(attrs)]
|
||||
attrs = Hash[attrs.map { |attr| attr.split(/=/, 2) }]
|
||||
[ctype, {"charset" => @charset}.merge!(attrs)]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
require "action_mailer"
|
||||
require "rails"
|
||||
require "abstract_controller/railties/routes_helpers"
|
||||
|
||||
module ActionMailer
|
||||
class Railtie < Rails::Railtie
|
||||
@@ -13,14 +14,26 @@ module ActionMailer
|
||||
paths = app.config.paths
|
||||
options = app.config.action_mailer
|
||||
|
||||
options.assets_dir ||= paths.public.to_a.first
|
||||
options.javascripts_dir ||= paths.public.javascripts.to_a.first
|
||||
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
|
||||
options.assets_dir ||= paths["public"].first
|
||||
options.javascripts_dir ||= paths["public/javascripts"].first
|
||||
options.stylesheets_dir ||= paths["public/stylesheets"].first
|
||||
|
||||
# make sure readers methods get compiled
|
||||
options.asset_path ||= app.config.asset_path
|
||||
options.asset_host ||= app.config.asset_host
|
||||
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
include app.routes.url_helpers
|
||||
include AbstractController::UrlFor
|
||||
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
||||
include app.routes.mounted_helpers
|
||||
options.each { |k,v| send("#{k}=", v) }
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_mailer.compile_config_methods" do
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
config.compile_methods! if config.respond_to?(:compile_methods!)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module ActionMailer
|
||||
class NonInferrableMailerError < ::StandardError
|
||||
def initialize(name)
|
||||
@@ -15,11 +17,11 @@ module ActionMailer
|
||||
|
||||
module ClassMethods
|
||||
def tests(mailer)
|
||||
write_inheritable_attribute(:mailer_class, mailer)
|
||||
self._mailer_class = mailer
|
||||
end
|
||||
|
||||
def mailer_class
|
||||
if mailer = read_inheritable_attribute(:mailer_class)
|
||||
if mailer = self._mailer_class
|
||||
mailer
|
||||
else
|
||||
tests determine_default_mailer(name)
|
||||
@@ -65,6 +67,7 @@ module ActionMailer
|
||||
end
|
||||
|
||||
included do
|
||||
class_attribute :_mailer_class
|
||||
setup :initialize_test_deliveries
|
||||
setup :set_expected_mail
|
||||
end
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
module Mail
|
||||
class Message
|
||||
|
||||
|
||||
def set_content_type(*args)
|
||||
ActiveSupport::Deprecation.warn('Message#set_content_type is deprecated, please just call ' <<
|
||||
'Message#content_type with the same arguments', caller[0,2])
|
||||
message = 'Message#set_content_type is deprecated, please just call ' <<
|
||||
'Message#content_type with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
content_type(*args)
|
||||
end
|
||||
|
||||
|
||||
alias :old_transfer_encoding :transfer_encoding
|
||||
def transfer_encoding(value = nil)
|
||||
if value
|
||||
ActiveSupport::Deprecation.warn('Message#transfer_encoding is deprecated, please call ' <<
|
||||
'Message#content_transfer_encoding with the same arguments', caller[0,2])
|
||||
message = 'Message#transfer_encoding is deprecated, ' <<
|
||||
'please call Message#content_transfer_encoding with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
content_transfer_encoding(value)
|
||||
else
|
||||
old_transfer_encoding
|
||||
@@ -19,16 +21,17 @@ module Mail
|
||||
end
|
||||
|
||||
def transfer_encoding=(value)
|
||||
ActiveSupport::Deprecation.warn('Message#transfer_encoding= is deprecated, please call ' <<
|
||||
'Message#content_transfer_encoding= with the same arguments', caller[0,2])
|
||||
message = 'Message#transfer_encoding= is deprecated, ' <<
|
||||
'please call Message#content_transfer_encoding= with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
self.content_transfer_encoding = value
|
||||
end
|
||||
|
||||
def original_filename
|
||||
ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' <<
|
||||
'please call Message#filename', caller[0,2])
|
||||
message = 'Message#original_filename is deprecated, please call Message#filename'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
filename
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module ActionMailer
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 3
|
||||
MINOR = 0
|
||||
MINOR = 1
|
||||
TINY = 0
|
||||
BUILD = "rc"
|
||||
PRE = "beta"
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
|
||||
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
Description:
|
||||
============
|
||||
Stubs out a new mailer and its views. Pass the mailer name, either
|
||||
CamelCased or under_scored, and an optional list of emails as arguments.
|
||||
|
||||
@@ -6,10 +7,12 @@ Description:
|
||||
engine and test framework generators.
|
||||
|
||||
Example:
|
||||
`rails generate mailer Notifications signup forgot_password invoice`
|
||||
========
|
||||
rails generate mailer Notifications signup forgot_password invoice
|
||||
|
||||
creates a Notifications mailer class, views, test, and fixtures:
|
||||
Mailer: app/mailers/notifications.rb
|
||||
Views: app/views/notifications/signup.erb [...]
|
||||
Test: test/functional/notifications_test.rb
|
||||
Fixtures: test/fixtures/notifications/signup [...]
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<% module_namespacing do -%>
|
||||
class <%= class_name %> < ActionMailer::Base
|
||||
default :from => "from@example.com"
|
||||
<% for action in actions -%>
|
||||
@@ -5,7 +6,7 @@ class <%= class_name %> < ActionMailer::Base
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.<%= file_name %>.<%= action %>.subject
|
||||
# en.<%= file_path.gsub("/",".") %>.<%= action %>.subject
|
||||
#
|
||||
def <%= action %>
|
||||
@greeting = "Hi"
|
||||
@@ -14,3 +15,4 @@ class <%= class_name %> < ActionMailer::Base
|
||||
end
|
||||
<% end -%>
|
||||
end
|
||||
<% end -%>
|
||||
|
||||
@@ -11,11 +11,20 @@ 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'
|
||||
require 'mail'
|
||||
end
|
||||
|
||||
@@ -67,4 +76,6 @@ end
|
||||
|
||||
def restore_delivery_method
|
||||
ActionMailer::Base.delivery_method = @old_delivery_method
|
||||
end
|
||||
end
|
||||
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
|
||||
@@ -3,9 +3,9 @@ require 'action_controller'
|
||||
|
||||
class AssetHostMailer < ActionMailer::Base
|
||||
def email_with_asset
|
||||
recipients 'test@localhost'
|
||||
subject "testing email containing asset path while asset_host is set"
|
||||
from "tester@example.com"
|
||||
mail :to => 'test@localhost',
|
||||
:subject => 'testing email containing asset path while asset_host is set',
|
||||
:from => 'tester@example.com'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,7 +29,7 @@ class AssetHostTest < Test::Unit::TestCase
|
||||
assert_equal %Q{<img alt="Somelogo" src="http://www.example.com/images/somelogo.png" />}, mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_asset_host_as_one_arguement_proc
|
||||
def test_asset_host_as_one_argument_proc
|
||||
AssetHostMailer.config.asset_host = Proc.new { |source|
|
||||
if source.starts_with?('/images')
|
||||
"http://images.example.com"
|
||||
@@ -41,7 +41,7 @@ class AssetHostTest < Test::Unit::TestCase
|
||||
assert_equal %Q{<img alt="Somelogo" src="http://images.example.com/images/somelogo.png" />}, mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_asset_host_as_two_arguement_proc
|
||||
def test_asset_host_as_two_argument_proc
|
||||
ActionController::Base.config.asset_host = Proc.new {|source,request|
|
||||
if request && request.ssl?
|
||||
"https://www.example.com"
|
||||
@@ -7,9 +7,6 @@ require 'mailers/proc_mailer'
|
||||
require 'mailers/asset_mailer'
|
||||
|
||||
class BaseTest < ActiveSupport::TestCase
|
||||
# TODO Add some tests for implicity layout render and url helpers
|
||||
# so we can get rid of old base tests altogether with old base.
|
||||
|
||||
def teardown
|
||||
ActionMailer::Base.asset_host = nil
|
||||
ActionMailer::Base.assets_dir = nil
|
||||
@@ -35,21 +32,21 @@ class BaseTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do
|
||||
@time = Time.now.beginning_of_day.to_datetime
|
||||
time = Time.now.beginning_of_day.to_datetime
|
||||
email = BaseMailer.welcome(:bcc => 'bcc@test.lindsaar.net',
|
||||
:cc => 'cc@test.lindsaar.net',
|
||||
:content_type => 'multipart/mixed',
|
||||
:charset => 'iso-8559-1',
|
||||
:mime_version => '2.0',
|
||||
:reply_to => 'reply-to@test.lindsaar.net',
|
||||
:date => @time)
|
||||
:date => time)
|
||||
assert_equal(['bcc@test.lindsaar.net'], email.bcc)
|
||||
assert_equal(['cc@test.lindsaar.net'], email.cc)
|
||||
assert_equal('multipart/mixed; charset=iso-8559-1', email.content_type)
|
||||
assert_equal('iso-8559-1', email.charset)
|
||||
assert_equal('2.0', email.mime_version)
|
||||
assert_equal(['reply-to@test.lindsaar.net'], email.reply_to)
|
||||
assert_equal(@time, email.date)
|
||||
assert_equal(time, email.date)
|
||||
end
|
||||
|
||||
test "mail() renders the template using the method being processed" do
|
||||
@@ -148,7 +145,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
|
||||
@@ -156,8 +153,8 @@ class BaseTest < ActiveSupport::TestCase
|
||||
assert_equal(2, email.parts.length)
|
||||
assert_equal("multipart/related", email.mime_type)
|
||||
assert_equal("multipart/alternative", email.parts[0].mime_type)
|
||||
assert_equal("text/plain", email.parts[0].parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[0].parts[1].mime_type)
|
||||
assert_equal("text/plain", email.parts[0].parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[0].parts[1].mime_type)
|
||||
assert_equal("logo.png", email.parts[1].filename)
|
||||
end
|
||||
|
||||
@@ -209,6 +206,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 +416,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
|
||||
@@ -447,7 +450,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 +459,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 +472,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 +496,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,7 +504,7 @@ 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)
|
||||
|
||||
@@ -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') %>
|
||||
4
actionmailer/test/fixtures/i18n_test_mailer/mail_with_i18n_subject.erb
vendored
Normal file
4
actionmailer/test/fixtures/i18n_test_mailer/mail_with_i18n_subject.erb
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>. Be greeted, new member!
|
||||
|
||||
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 %>
|
||||
|
||||
|
||||
46
actionmailer/test/i18n_with_controller_test.rb
Normal file
46
actionmailer/test/i18n_with_controller_test.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
require 'abstract_unit'
|
||||
require 'action_controller'
|
||||
|
||||
class I18nTestMailer < ActionMailer::Base
|
||||
configure do |c|
|
||||
c.assets_dir = ''
|
||||
end
|
||||
|
||||
def mail_with_i18n_subject(recipient)
|
||||
@recipient = recipient
|
||||
I18n.locale = :de
|
||||
mail(:to => recipient, :subject => "#{I18n.t :email_subject} #{recipient}",
|
||||
:from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
|
||||
end
|
||||
end
|
||||
|
||||
class TestController < ActionController::Base
|
||||
def send_mail
|
||||
I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver
|
||||
render :text => 'Mail sent'
|
||||
end
|
||||
end
|
||||
|
||||
class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
|
||||
Routes = ActionDispatch::Routing::RouteSet.new
|
||||
Routes.draw do
|
||||
match ':controller(/:action(/:id))'
|
||||
end
|
||||
|
||||
def app
|
||||
Routes
|
||||
end
|
||||
|
||||
def setup
|
||||
I18n.backend.store_translations('de', :email_subject => '[Signed up] Welcome')
|
||||
end
|
||||
|
||||
def teardown
|
||||
I18n.locale = :en
|
||||
end
|
||||
|
||||
def test_send_mail
|
||||
get '/test/send_mail'
|
||||
assert_equal "Mail sent", @response.body
|
||||
end
|
||||
end
|
||||
@@ -14,6 +14,14 @@ class HelperMailer < ActionMailer::Base
|
||||
end
|
||||
end
|
||||
|
||||
def use_format_paragraph
|
||||
@text = "But soft! What light through yonder window breaks?"
|
||||
|
||||
mail_with_defaults do |format|
|
||||
format.html { render(:inline => "<%= format_paragraph @text, 15, 1 %>") }
|
||||
end
|
||||
end
|
||||
|
||||
def use_mailer
|
||||
mail_with_defaults do |format|
|
||||
format.html { render(:inline => "<%= mailer.message.subject %>") }
|
||||
@@ -50,5 +58,10 @@ class MailerHelperTest < ActionMailer::TestCase
|
||||
mail = HelperMailer.use_message
|
||||
assert_match "using helpers", mail.body.encoded
|
||||
end
|
||||
|
||||
def test_use_format_paragraph
|
||||
mail = HelperMailer.use_format_paragraph
|
||||
assert_match " But soft! What\r\n light through\r\n yonder window\r\n breaks?", mail.body.encoded
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,53 +1,45 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
class AutoLayoutMailer < ActionMailer::Base
|
||||
default :to => 'test@localhost',
|
||||
:subject => "You have a mail",
|
||||
:from => "tester@example.com"
|
||||
|
||||
def hello
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
mail()
|
||||
end
|
||||
|
||||
def spam
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
|
||||
@world = "Earth"
|
||||
body render(:inline => "Hello, <%= @world %>", :layout => 'spam')
|
||||
mail(:body => render(:inline => "Hello, <%= @world %>", :layout => 'spam'))
|
||||
end
|
||||
|
||||
def nolayout
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
|
||||
@world = "Earth"
|
||||
body render(:inline => "Hello, <%= @world %>", :layout => false)
|
||||
mail(:body => render(:inline => "Hello, <%= @world %>", :layout => false))
|
||||
end
|
||||
|
||||
def multipart(type = nil)
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
|
||||
content_type(type) if type
|
||||
mail(:content_type => type) do |format|
|
||||
format.text { render }
|
||||
format.html { render }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ExplicitLayoutMailer < ActionMailer::Base
|
||||
layout 'spam', :except => [:logout]
|
||||
|
||||
default :to => 'test@localhost',
|
||||
:subject => "You have a mail",
|
||||
:from => "tester@example.com"
|
||||
|
||||
def signup
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
mail()
|
||||
end
|
||||
|
||||
def logout
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
mail()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -91,19 +83,6 @@ class LayoutMailerTest < Test::Unit::TestCase
|
||||
assert_equal "Hello from layout text/html multipart", mail.parts.last.body.to_s
|
||||
end
|
||||
|
||||
def test_should_fix_multipart_layout
|
||||
mail = AutoLayoutMailer.multipart("text/plain")
|
||||
assert_equal "multipart/alternative", mail.mime_type
|
||||
assert_equal 2, mail.parts.size
|
||||
|
||||
assert_equal 'text/plain', mail.parts.first.mime_type
|
||||
assert_equal "text/plain layout - text/plain multipart", mail.parts.first.body.to_s
|
||||
|
||||
assert_equal 'text/html', mail.parts.last.mime_type
|
||||
assert_equal "Hello from layout text/html multipart", mail.parts.last.body.to_s
|
||||
end
|
||||
|
||||
|
||||
def test_should_pickup_layout_given_to_render
|
||||
mail = AutoLayoutMailer.spam
|
||||
assert_equal "Spammer layout Hello, Earth", mail.body.to_s.strip
|
||||
@@ -111,4 +111,8 @@ class BaseMailer < ActionMailer::Base
|
||||
format.html { render :layout => layout_name }
|
||||
end
|
||||
end
|
||||
|
||||
def email_with_translations
|
||||
mail :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
|
||||
|
||||
@@ -11,9 +11,14 @@ class AdvAttrTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
def setup
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
@person = Person.new
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
end
|
||||
|
||||
def test_adv_attr
|
||||
assert_nil @person.name
|
||||
@person.name 'Bob'
|
||||
|
||||
@@ -19,18 +19,6 @@ class RenderMailer < ActionMailer::Base
|
||||
body render(:file => "templates/signed_up")
|
||||
end
|
||||
|
||||
def rxml_template
|
||||
recipients 'test@localhost'
|
||||
subject "rendering rxml template"
|
||||
from "tester@example.com"
|
||||
end
|
||||
|
||||
def included_subtemplate
|
||||
recipients 'test@localhost'
|
||||
subject "Including another template in the one being rendered"
|
||||
from "tester@example.com"
|
||||
end
|
||||
|
||||
def no_instance_variable
|
||||
recipients 'test@localhost'
|
||||
subject "No Instance Variable"
|
||||
@@ -41,11 +29,6 @@ class RenderMailer < ActionMailer::Base
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_defaults(method_name)
|
||||
super
|
||||
mailer_name "test_mailer"
|
||||
end
|
||||
|
||||
def multipart_alternative
|
||||
recipients 'test@localhost'
|
||||
subject 'multipart/alternative'
|
||||
@@ -97,11 +80,13 @@ class RenderHelperTest < Test::Unit::TestCase
|
||||
set_delivery_method :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
|
||||
@recipient = 'test@localhost'
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
@@ -112,38 +97,19 @@ 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
|
||||
end
|
||||
|
||||
def test_rxml_template
|
||||
mail = RenderMailer.rxml_template.deliver
|
||||
assert_equal %(<?xml version="1.0" encoding="UTF-8"?>\n<test/>), mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_included_subtemplate
|
||||
mail = RenderMailer.included_subtemplate.deliver
|
||||
assert_equal "Hey Ho, let's go!", mail.body.to_s.strip
|
||||
assert_equal "Hello there,\n\nMr. test@localhost", mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_no_instance_variable
|
||||
mail = RenderMailer.no_instance_variable.deliver
|
||||
assert_equal "Look, subject.nil? is true!", mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_legacy_multipart_alternative
|
||||
mail = RenderMailer.multipart_alternative.deliver
|
||||
assert_equal(2, mail.parts.size)
|
||||
assert_equal("multipart/alternative", mail.mime_type)
|
||||
assert_equal("text/plain", mail.parts[0].mime_type)
|
||||
assert_equal("foo: bar", mail.parts[0].body.encoded)
|
||||
assert_equal("text/html", mail.parts[1].mime_type)
|
||||
assert_equal("<strong>foo</strong> bar", mail.parts[1].body.encoded)
|
||||
end
|
||||
end
|
||||
|
||||
class FirstSecondHelperTest < Test::Unit::TestCase
|
||||
def setup
|
||||
set_delivery_method :test
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
||||
@@ -151,6 +117,7 @@ class FirstSecondHelperTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class TestMailer < ActionMailer::Base
|
||||
cc "Foo áëô îü <extended@example.net>"
|
||||
bcc "Foo áëô îü <extended@example.net>"
|
||||
charset "UTF-8"
|
||||
|
||||
|
||||
body "åœö blah"
|
||||
end
|
||||
|
||||
@@ -328,21 +328,20 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
mail
|
||||
end
|
||||
|
||||
# Replacing logger work around for mocha bug. Should be fixed in mocha 0.3.3
|
||||
def setup
|
||||
set_delivery_method :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.raise_delivery_errors = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
|
||||
@original_logger = TestMailer.logger
|
||||
@recipient = 'test@localhost'
|
||||
|
||||
TestMailer.delivery_method = :test
|
||||
end
|
||||
|
||||
def teardown
|
||||
TestMailer.logger = @original_logger
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
@@ -359,7 +358,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 +391,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 +419,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 +502,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 +545,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 +557,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 +580,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 +595,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 +630,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 +760,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 +886,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 +1004,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)
|
||||
@@ -1096,111 +1095,3 @@ EOF
|
||||
TestMailer.smtp_settings.merge!(:enable_starttls_auto => true)
|
||||
end
|
||||
end
|
||||
|
||||
class InheritableTemplateRootTest < ActiveSupport::TestCase
|
||||
def test_attr
|
||||
expected = File.expand_path("#{File.dirname(__FILE__)}/../fixtures/path.with.dots")
|
||||
assert_equal expected, FunkyPathMailer.template_root.to_s
|
||||
|
||||
sub = Class.new(FunkyPathMailer)
|
||||
assert_deprecated do
|
||||
sub.template_root = 'test/path'
|
||||
end
|
||||
|
||||
assert_equal File.expand_path('test/path'), sub.template_root.to_s
|
||||
assert_equal expected, FunkyPathMailer.template_root.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class MethodNamingTest < ActiveSupport::TestCase
|
||||
include ActionMailer::TestHelper
|
||||
|
||||
class TestMailer < ActionMailer::Base
|
||||
def send
|
||||
body 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
set_delivery_method :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
def teardown
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
def test_send_method
|
||||
assert_nothing_raised do
|
||||
assert_emails 1 do
|
||||
assert_deprecated do
|
||||
TestMailer.deliver_send
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
class RespondToTest < Test::Unit::TestCase
|
||||
class RespondToMailer < ActionMailer::Base; end
|
||||
|
||||
def setup
|
||||
set_delivery_method :test
|
||||
end
|
||||
|
||||
def teardown
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
def test_should_respond_to_new
|
||||
assert_respond_to RespondToMailer, :new
|
||||
end
|
||||
|
||||
def test_should_respond_to_create_with_template_suffix
|
||||
assert_respond_to RespondToMailer, :create_any_old_template
|
||||
end
|
||||
|
||||
def test_should_respond_to_deliver_with_template_suffix
|
||||
assert_respond_to RespondToMailer, :deliver_any_old_template
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_new_with_template_suffix
|
||||
assert !RespondToMailer.respond_to?(:new_any_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_create_with_template_suffix_unless_it_is_separated_by_an_underscore
|
||||
assert !RespondToMailer.respond_to?(:createany_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_deliver_with_template_suffix_unless_it_is_separated_by_an_underscore
|
||||
assert !RespondToMailer.respond_to?(:deliverany_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_create_with_template_suffix_if_it_begins_with_a_uppercase_letter
|
||||
assert !RespondToMailer.respond_to?(:create_Any_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_deliver_with_template_suffix_if_it_begins_with_a_uppercase_letter
|
||||
assert !RespondToMailer.respond_to?(:deliver_Any_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_create_with_template_suffix_if_it_begins_with_a_digit
|
||||
assert !RespondToMailer.respond_to?(:create_1_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_deliver_with_template_suffix_if_it_begins_with_a_digit
|
||||
assert !RespondToMailer.respond_to?(:deliver_1_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_method_where_deliver_is_not_a_suffix
|
||||
assert !RespondToMailer.respond_to?(:foo_deliver_template)
|
||||
end
|
||||
|
||||
def test_should_still_raise_exception_with_expected_message_when_calling_an_undefined_method
|
||||
error = assert_raise NoMethodError do
|
||||
RespondToMailer.not_a_method
|
||||
end
|
||||
|
||||
assert_match(/method.*not_a_method/, error.message)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
class TmailCompatTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@silence = ActiveSupport::Deprecation.silenced
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = @silence
|
||||
end
|
||||
|
||||
def test_set_content_type_raises_deprecation_warning
|
||||
mail = Mail.new
|
||||
@@ -31,5 +39,4 @@ class TmailCompatTest < ActiveSupport::TestCase
|
||||
end
|
||||
assert_equal mail.content_transfer_encoding, "base64"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -2,11 +2,10 @@ require 'abstract_unit'
|
||||
|
||||
class TestHelperMailer < ActionMailer::Base
|
||||
def test
|
||||
recipients "test@example.com"
|
||||
from "tester@example.com"
|
||||
|
||||
@world = "Earth"
|
||||
render(:inline => "Hello, <%= @world %>")
|
||||
mail :body => render(:inline => "Hello, <%= @world %>"),
|
||||
:to => "test@example.com",
|
||||
:from => "tester@example.com"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,7 +31,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 +43,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 +58,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 +79,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 +97,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
|
||||
|
||||
@@ -18,13 +18,10 @@ class UrlTestMailer < ActionMailer::Base
|
||||
end
|
||||
|
||||
def signed_up_with_url(recipient)
|
||||
@recipients = recipient
|
||||
@subject = "[Signed up] Welcome #{recipient}"
|
||||
@from = "system@loudthinking.com"
|
||||
@sent_on = Time.local(2004, 12, 12)
|
||||
|
||||
@recipient = recipient
|
||||
@welcome_url = url_for :host => "example.com", :controller => "welcome", :action => "greeting"
|
||||
mail(:to => recipient, :subject => "[Signed up] Welcome #{recipient}",
|
||||
:from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -47,6 +44,7 @@ class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
set_delivery_method :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
|
||||
@recipient = 'test@localhost'
|
||||
end
|
||||
@@ -58,19 +56,18 @@ class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
def test_signed_up_with_url
|
||||
UrlTestMailer.delivery_method = :test
|
||||
|
||||
assert_deprecated do
|
||||
AppRoutes.draw do |map|
|
||||
map.connect ':controller/:action/:id'
|
||||
map.welcome 'welcome', :controller=>"foo", :action=>"bar"
|
||||
end
|
||||
AppRoutes.draw do
|
||||
match ':controller(/:action(/:id))'
|
||||
match '/welcome' => "foo#bar", :as => "welcome"
|
||||
end
|
||||
|
||||
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)
|
||||
expected.content_type = "text/html"
|
||||
|
||||
created = nil
|
||||
assert_nothing_raised { created = UrlTestMailer.signed_up_with_url(@recipient) }
|
||||
@@ -83,7 +80,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
|
||||
@@ -1,4 +1,74 @@
|
||||
*Rails 3.0.0 [release candidate] (July 26th, 2010)*
|
||||
*Rails 3.1.0 (unreleased)*
|
||||
|
||||
* Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. [Prem Sichanugrist, fxn]
|
||||
|
||||
* URL parameters which return false for to_param now appear in the query string (previously they were removed) [Andrew White]
|
||||
|
||||
* URL parameters which return nil for to_param are now removed from the query string [Andrew White]
|
||||
|
||||
* ActionDispatch::MiddlewareStack now uses composition over inheritance. It is
|
||||
no longer an array which means there may be methods missing that were not
|
||||
tested.
|
||||
|
||||
* Add an :authenticity_token option to form_tag for custom handling or to omit the token (pass :authenticity_token => false). [Jakub Kuźma, Igor Wiedler]
|
||||
|
||||
* HTML5 button_tag helper. [Rizwan Reza]
|
||||
|
||||
* Template lookup now searches further up in the inheritance chain. [Artemave]
|
||||
|
||||
* Brought back config.action_view.cache_template_loading, which allows to decide whether templates should be cached or not. [Piotr Sarnacki]
|
||||
|
||||
* url_for and named url helpers now accept :subdomain and :domain as options, [Josh Kalderimis]
|
||||
|
||||
* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused (check the documentation for examples). [Josh Kalderimis]
|
||||
|
||||
* Added config.action_controller.include_all_helpers. By default 'helper :all' is done in ActionController::Base, which includes all the helpers by default. Setting include_all_helpers to false will result in including only application_helper and helper corresponding to controller (like foo_helper for foo_controller). [Piotr Sarnacki]
|
||||
|
||||
* Added a convenience idiom to generate HTML5 data-* attributes in tag helpers from a :data hash of options:
|
||||
|
||||
tag("div", :data => {:name => 'Stephen', :city_state => %w(Chicago IL)})
|
||||
# => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
|
||||
|
||||
Keys are dasherized. Values are JSON-encoded, except for strings and symbols. [Stephen Celis]
|
||||
|
||||
* Added render :once. You can pass either a string or an array of strings and Rails will ensure they each of them are rendered just once. [José Valim]
|
||||
|
||||
* Deprecate old template handler API. The new API simply requires a template handler to respond to call. [José Valim]
|
||||
|
||||
* :rhtml and :rxml were finally removed as template handlers. [José Valim]
|
||||
|
||||
* Moved etag responsibility from ActionDispatch::Response to the middleware stack. [José Valim]
|
||||
|
||||
* Rely on Rack::Session stores API for more compatibility across the Ruby world. This is backwards incompatible since Rack::Session expects #get_session to accept 4 arguments and requires #destroy_session instead of simply #destroy. [José Valim]
|
||||
|
||||
* file_field automatically adds :multipart => true to the enclosing form. [Santiago Pastorino]
|
||||
|
||||
* Renames csrf_meta_tag -> csrf_meta_tags, and aliases csrf_meta_tag for backwards compatibility. [fxn]
|
||||
|
||||
* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply to the browser only. [Yehuda Katz, Carl Lerche]
|
||||
|
||||
|
||||
*Rails 3.0.2 (unreleased)*
|
||||
|
||||
* The helper number_to_currency accepts a new :negative_format option to be able to configure how to render negative amounts. [Don Wilson]
|
||||
|
||||
|
||||
*Rails 3.0.1 (October 15, 2010)*
|
||||
|
||||
* No Changes, just a version bump.
|
||||
|
||||
|
||||
*Rails 3.0.0 (August 29, 2010)*
|
||||
|
||||
* password_field renders with nil value by default making the use of passwords secure by default, if you want to render you should do for instance f.password_field(:password, :value => @user.password) [Santiago Pastorino]
|
||||
|
||||
* Symbols and strings in routes should yield the same behavior. Note this may break existing apps that were using symbols with the new routes API. [José Valim]
|
||||
|
||||
* Add clear_helpers as a way to clean up all helpers added to this controller, maintaining 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 +96,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 +119,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 +153,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 +171,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 +181,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]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
Copyright (c) 2004-2011 David Heinemeier Hansson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -262,7 +262,7 @@ methods:
|
||||
layout "weblog/layout"
|
||||
|
||||
def index
|
||||
@posts = Post.find(:all)
|
||||
@posts = Post.all
|
||||
end
|
||||
|
||||
def show
|
||||
@@ -323,7 +323,7 @@ The latest version of Action Pack can be installed with Rubygems:
|
||||
|
||||
Source code can be downloaded as part of the Rails project on GitHub
|
||||
|
||||
* http://github.com/rails/rails/tree/master/actionpack/
|
||||
* https://github.com/rails/rails/tree/master/actionpack/
|
||||
|
||||
|
||||
== License
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
== 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
|
||||
|
||||
== Running by hand
|
||||
|
||||
If you only want to run a single test suite, or don't want to bother with Rake,
|
||||
you can do so with something like:
|
||||
To run a single test suite
|
||||
|
||||
ruby -Itest test/controller/base_tests.rb
|
||||
rake test TEST=path/to/test.rb
|
||||
|
||||
== Dependency on ActiveRecord and database setup
|
||||
which can be further narrowed down to one test:
|
||||
|
||||
rake test TEST=path/to/test.rb TESTOPTS="--name=test_something"
|
||||
|
||||
== Dependency on Active Record 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 Active Record is not in
|
||||
actionpack/../activerecord directory, or the sqlite rubygem is not installed,
|
||||
these tests are skipped.
|
||||
|
||||
|
||||
26
actionpack/Rakefile
Normal file → Executable file
26
actionpack/Rakefile
Normal file → Executable file
@@ -1,8 +1,5 @@
|
||||
gem 'rdoc', '>= 2.5.9'
|
||||
require 'rdoc'
|
||||
require 'rake'
|
||||
#!/usr/bin/env rake
|
||||
require 'rake/testtask'
|
||||
require 'rdoc/task'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
@@ -21,7 +18,8 @@ Rake::TestTask.new(:test_action_pack) do |t|
|
||||
# this will not happen automatically and the tests (as a whole) will error
|
||||
t.test_files = Dir.glob('test/{abstract,controller,dispatch,template}/**/*_test.rb').sort
|
||||
|
||||
# t.warning = true
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
namespace :test do
|
||||
@@ -36,24 +34,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|
|
||||
|
||||
@@ -19,13 +19,14 @@ Gem::Specification.new do |s|
|
||||
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('activesupport', version)
|
||||
s.add_dependency('activemodel', version)
|
||||
s.add_dependency('builder', '~> 2.1.2')
|
||||
s.add_dependency('i18n', '~> 0.4.1')
|
||||
s.add_dependency('rack', '~> 1.2.1')
|
||||
s.add_dependency('rack-test', '~> 0.5.4')
|
||||
s.add_dependency('rack-mount', '~> 0.6.9')
|
||||
s.add_dependency('tzinfo', '~> 0.3.22')
|
||||
s.add_dependency('erubis', '~> 2.6.6')
|
||||
s.add_dependency('activesupport', version)
|
||||
s.add_dependency('activemodel', version)
|
||||
s.add_dependency('rack-cache', '~> 1.0.0')
|
||||
s.add_dependency('builder', '~> 3.0.0')
|
||||
s.add_dependency('i18n', '~> 0.5.0')
|
||||
s.add_dependency('rack', '~> 1.2.1')
|
||||
s.add_dependency('rack-test', '~> 0.5.7')
|
||||
s.add_dependency('rack-mount', '~> 0.6.13')
|
||||
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'
|
||||
@@ -23,4 +24,5 @@ module AbstractController
|
||||
autoload :Translation
|
||||
autoload :AssetPaths
|
||||
autoload :ViewPaths
|
||||
autoload :UrlFor
|
||||
end
|
||||
|
||||
@@ -6,9 +6,14 @@ 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
|
||||
attr_internal :formats
|
||||
|
||||
include ActiveSupport::Configurable
|
||||
extend ActiveSupport::DescendantsTracker
|
||||
@@ -26,23 +31,21 @@ module AbstractController
|
||||
# A list of all internal methods for a controller. This finds the first
|
||||
# abstract superclass of a controller, and gets a list of all public
|
||||
# instance methods on that abstract class. Public instance methods of
|
||||
# a controller would normally be considered action methods, so we
|
||||
# are removing those methods on classes declared as abstract
|
||||
# (ActionController::Metal and ActionController::Base are defined
|
||||
# as abstract)
|
||||
# a controller would normally be considered action methods, so methods
|
||||
# declared on abstract classes are being removed.
|
||||
# (ActionController::Metal and ActionController::Base are defined as abstract)
|
||||
def internal_methods
|
||||
controller = self
|
||||
controller = controller.superclass until controller.abstract?
|
||||
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,18 +57,17 @@ 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
|
||||
methods = public_instance_methods(true).map { |m| m.to_s }.to_set -
|
||||
methods = (public_instance_methods(true) -
|
||||
# Except for public instance methods of Base and its ancestors
|
||||
internal_methods.map { |m| m.to_s } +
|
||||
internal_methods +
|
||||
# Be sure to include shadowed public instance methods of this class
|
||||
public_instance_methods(false).map { |m| m.to_s } -
|
||||
public_instance_methods(false)).uniq.map { |x| x.to_s } -
|
||||
# And always exclude explicitly hidden actions
|
||||
hidden_actions
|
||||
hidden_actions.to_a
|
||||
|
||||
# Clear out AS callback method pollution
|
||||
methods.reject { |method| method =~ /_one_time_conditions/ }
|
||||
@@ -84,7 +86,7 @@ module AbstractController
|
||||
# controller_name.
|
||||
#
|
||||
# ==== Returns
|
||||
# String
|
||||
# * <tt>string</tt>
|
||||
def controller_path
|
||||
@controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
|
||||
end
|
||||
@@ -104,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
|
||||
@@ -133,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
|
||||
@@ -180,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"
|
||||
|
||||
@@ -13,8 +13,8 @@ module AbstractController
|
||||
|
||||
# Override AbstractController::Base's process_action to run the
|
||||
# process_action callbacks around the normal behavior.
|
||||
def process_action(method_name)
|
||||
run_callbacks(:process_action, method_name) do
|
||||
def process_action(method_name, *args)
|
||||
run_callbacks(:process_action, action_name) do
|
||||
super
|
||||
end
|
||||
end
|
||||
@@ -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)
|
||||
@@ -84,6 +83,7 @@ module AbstractController
|
||||
# for details on the allowed parameters.
|
||||
def #{filter}_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options|
|
||||
options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
||||
set_callback(:process_action, :#{filter}, name, options)
|
||||
end
|
||||
end
|
||||
@@ -92,6 +92,7 @@ module AbstractController
|
||||
# for details on the allowed parameters.
|
||||
def prepend_#{filter}_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options|
|
||||
options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
||||
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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,18 +230,15 @@ module AbstractController
|
||||
# value of this method.
|
||||
#
|
||||
# ==== Returns
|
||||
# String:: A template name
|
||||
# * <tt>String</tt> - A template name
|
||||
def _implied_layout_name
|
||||
controller_path
|
||||
end
|
||||
|
||||
# Takes the specified layout and creates a _layout method to be called
|
||||
# by _default_layout
|
||||
# Creates a _layout method to be called by _default_layout .
|
||||
#
|
||||
# If there is no explicit layout specified:
|
||||
# If a layout is found in the view paths with the controller's
|
||||
# name, return that string. Otherwise, use the superclass'
|
||||
# layout (which might also be implied)
|
||||
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
|
||||
# if nothing is found then try same procedure to find super class's layout.
|
||||
def _write_layout_method
|
||||
remove_possible_method(:_layout)
|
||||
|
||||
@@ -266,11 +265,11 @@ module AbstractController
|
||||
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
|
||||
when nil
|
||||
if name
|
||||
_prefix = "layouts" unless _implied_layout_name =~ /\blayouts/
|
||||
_prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
|
||||
|
||||
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def _layout
|
||||
if template_exists?("#{_implied_layout_name}", #{_prefix.inspect})
|
||||
if template_exists?("#{_implied_layout_name}", #{_prefixes.inspect})
|
||||
"#{_implied_layout_name}"
|
||||
else
|
||||
super
|
||||
@@ -313,8 +312,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,20 +332,19 @@ 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?
|
||||
rescue NameError => e
|
||||
raise NoMethodError,
|
||||
"You specified #{@_layout.inspect} as the layout, but no such method was found"
|
||||
raise e, "Could not render layout: #{e.message}"
|
||||
end
|
||||
|
||||
if require_layout && action_has_layout? && !layout_name
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
module AbstractController
|
||||
module Railties
|
||||
module RoutesHelpers
|
||||
def self.with(routes)
|
||||
Module.new do
|
||||
define_method(:inherited) do |klass|
|
||||
super(klass)
|
||||
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
|
||||
klass.send(:include, namespace._railtie.routes.url_helpers)
|
||||
else
|
||||
klass.send(:include, routes.url_helpers)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,16 +12,16 @@ module AbstractController
|
||||
|
||||
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
|
||||
# it will trigger the lookup_context and consequently expire the cache.
|
||||
# TODO Add some deprecation warnings to remove I18n.locale from controllers
|
||||
class I18nProxy < ::I18n::Config #:nodoc:
|
||||
attr_reader :i18n_config, :lookup_context
|
||||
attr_reader :original_config, :lookup_context
|
||||
|
||||
def initialize(i18n_config, lookup_context)
|
||||
@i18n_config, @lookup_context = i18n_config, lookup_context
|
||||
def initialize(original_config, lookup_context)
|
||||
original_config = original_config.original_config if original_config.respond_to?(:original_config)
|
||||
@original_config, @lookup_context = original_config, lookup_context
|
||||
end
|
||||
|
||||
def locale
|
||||
@i18n_config.locale
|
||||
@original_config.locale
|
||||
end
|
||||
|
||||
def locale=(value)
|
||||
@@ -47,19 +47,34 @@ module AbstractController
|
||||
@view_context_class ||= begin
|
||||
controller = self
|
||||
Class.new(ActionView::Base) do
|
||||
if controller.respond_to?(:_routes) && controller._routes
|
||||
include controller._routes.url_helpers
|
||||
include controller._routes.mounted_helpers
|
||||
end
|
||||
|
||||
if controller.respond_to?(:_helpers)
|
||||
include controller._helpers
|
||||
|
||||
if controller.respond_to?(:_routes)
|
||||
include controller._routes.url_helpers
|
||||
end
|
||||
|
||||
# TODO: Fix RJS to not require this
|
||||
self.helpers = controller._helpers
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parent_prefixes
|
||||
@parent_prefixes ||= begin
|
||||
parent_controller = superclass
|
||||
prefixes = []
|
||||
|
||||
until parent_controller.abstract?
|
||||
prefixes << parent_controller.controller_path
|
||||
parent_controller = parent_controller.superclass
|
||||
end
|
||||
|
||||
prefixes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_writer :view_context_class
|
||||
@@ -98,7 +113,7 @@ module AbstractController
|
||||
def render_to_string(*args, &block)
|
||||
options = _normalize_args(*args, &block)
|
||||
_normalize_options(options)
|
||||
render_to_body(options)
|
||||
render_to_body(options).tap { self.response_body = nil }
|
||||
end
|
||||
|
||||
# Raw rendering of a template to a Rack-compatible body.
|
||||
@@ -114,12 +129,15 @@ module AbstractController
|
||||
view_context.render(options)
|
||||
end
|
||||
|
||||
# The prefix used in render "foo" shortcuts.
|
||||
def _prefix
|
||||
controller_path
|
||||
# The prefixes used in render "foo" shortcuts.
|
||||
def _prefixes
|
||||
@_prefixes ||= begin
|
||||
parent_prefixes = self.class.parent_prefixes
|
||||
parent_prefixes.dup.unshift(controller_path)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# This method should return a hash with assigns.
|
||||
# You can overwrite this configuration per controller.
|
||||
@@ -128,7 +146,7 @@ module AbstractController
|
||||
hash = {}
|
||||
variables = instance_variable_names
|
||||
variables -= protected_instance_variables if respond_to?(:protected_instance_variables)
|
||||
variables.each { |name| hash[name.to_s[1..-1]] = instance_variable_get(name) }
|
||||
variables.each { |name| hash[name.to_s[1, name.length]] = instance_variable_get(name) }
|
||||
hash
|
||||
end
|
||||
|
||||
@@ -138,13 +156,13 @@ module AbstractController
|
||||
case action
|
||||
when NilClass
|
||||
when Hash
|
||||
options, action = action, nil
|
||||
options = action
|
||||
when String, Symbol
|
||||
action = action.to_s
|
||||
key = action.include?(?/) ? :file : :action
|
||||
options[key] = action
|
||||
else
|
||||
options.merge!(:partial => action)
|
||||
options[:partial] = action
|
||||
end
|
||||
|
||||
options
|
||||
@@ -155,8 +173,8 @@ module AbstractController
|
||||
options[:partial] = action_name
|
||||
end
|
||||
|
||||
if (options.keys & [:partial, :file, :template]).empty?
|
||||
options[:prefix] ||= _prefix
|
||||
if (options.keys & [:partial, :file, :template, :once]).empty?
|
||||
options[:prefixes] ||= _prefixes
|
||||
end
|
||||
|
||||
options[:template] ||= (options[:action] || action_name).to_s
|
||||
|
||||
27
actionpack/lib/abstract_controller/url_for.rb
Normal file
27
actionpack/lib/abstract_controller/url_for.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
module AbstractController
|
||||
module UrlFor
|
||||
extend ActiveSupport::Concern
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
|
||||
def _routes
|
||||
raise "In order to use #url_for, you must include routing helpers explicitly. " \
|
||||
"For instance, `include Rails.application.routes.url_helpers"
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def _routes
|
||||
nil
|
||||
end
|
||||
|
||||
def action_methods
|
||||
@action_methods ||= begin
|
||||
if _routes
|
||||
super - _routes.named_routes.helper_names
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -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::PathSet 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::PathSet for more information)
|
||||
def prepend_view_path(path)
|
||||
self.view_paths = Array(path) + view_paths.dup
|
||||
end
|
||||
@@ -59,8 +59,8 @@ module AbstractController
|
||||
# Set the view paths.
|
||||
#
|
||||
# ==== Parameters
|
||||
# paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
|
||||
# otherwise, process the parameter into a ViewPathSet.
|
||||
# * <tt>paths</tt> - If a PathSet is provided, use that;
|
||||
# otherwise, process the parameter into a PathSet.
|
||||
def view_paths=(paths)
|
||||
self._view_paths = ActionView::Base.process_view_paths(paths)
|
||||
self._view_paths.freeze
|
||||
|
||||
@@ -34,7 +34,6 @@ module ActionController
|
||||
autoload :UrlFor
|
||||
end
|
||||
|
||||
autoload :Dispatcher, 'action_controller/deprecated/dispatcher'
|
||||
autoload :Integration, 'action_controller/deprecated/integration_test'
|
||||
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
|
||||
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
|
||||
@@ -73,4 +72,5 @@ require 'active_support/core_ext/load_error'
|
||||
require 'active_support/core_ext/module/attr_internal'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/name_error'
|
||||
require 'active_support/core_ext/uri'
|
||||
require 'active_support/inflector'
|
||||
|
||||
@@ -1,6 +1,171 @@
|
||||
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.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
|
||||
#
|
||||
# Unlike index, the create action will not render a template. After performing its main purpose (creating a
|
||||
# new post), it initiates a redirect instead. This redirect works by returning an external
|
||||
# "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
|
||||
#
|
||||
# MyApplication::Application.config.session_store :active_record_store
|
||||
#
|
||||
# in your <tt>config/initializers/session_store.rb</tt> and run <tt>script/rails g session_migration</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.
|
||||
#
|
||||
# Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
|
||||
#
|
||||
# == Calling multiple redirects or renders
|
||||
#
|
||||
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
|
||||
#
|
||||
# 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!
|
||||
|
||||
@@ -58,13 +223,6 @@ module ActionController
|
||||
# Rails 2.x compatibility
|
||||
include ActionController::Compatibility
|
||||
|
||||
def self.inherited(klass)
|
||||
super
|
||||
klass.helper :all
|
||||
end
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_controller, self)
|
||||
end
|
||||
end
|
||||
|
||||
require "action_controller/deprecated/base"
|
||||
|
||||
@@ -3,7 +3,7 @@ require 'uri'
|
||||
require 'set'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
# Caching is a cheap way of speeding up slow applications by keeping the result of
|
||||
# \Caching is a cheap way of speeding up slow applications by keeping the result of
|
||||
# calculations, renderings, and database calls around for subsequent requests.
|
||||
# Action Controller affords you three approaches in varying levels of granularity:
|
||||
# Page, Action, Fragment.
|
||||
@@ -14,7 +14,7 @@ module ActionController #:nodoc:
|
||||
# Note: To turn off all caching and sweeping, set
|
||||
# config.action_controller.perform_caching = false.
|
||||
#
|
||||
# == Caching stores
|
||||
# == \Caching stores
|
||||
#
|
||||
# All the caching stores from ActiveSupport::Cache are available to be used as backends
|
||||
# for Action Controller caching. This setting only affects action and fragment caching
|
||||
|
||||
@@ -4,53 +4,58 @@ module ActionController #:nodoc:
|
||||
module Caching
|
||||
# Action caching is similar to page caching by the fact that the entire
|
||||
# output of the response is cached, but unlike page caching, every
|
||||
# request still goes through the Action Pack. The key benefit
|
||||
# of this is that filters are run before the cache is served, which
|
||||
# allows for authentication and other restrictions on whether someone
|
||||
# is allowed to see the cache. Example:
|
||||
# request still goes through Action Pack. The key benefit of this is
|
||||
# that filters run before the cache is served, which allows for
|
||||
# authentication and other restrictions on whether someone is allowed
|
||||
# to execute such action. Example:
|
||||
#
|
||||
# class ListsController < ApplicationController
|
||||
# before_filter :authenticate, :except => :public
|
||||
#
|
||||
# caches_page :public
|
||||
# caches_action :index, :show, :feed
|
||||
# caches_action :index, :show
|
||||
# end
|
||||
#
|
||||
# In this example, the public action doesn't require authentication,
|
||||
# so it's possible to use the faster page caching method. But both
|
||||
# the show and feed action are to be shielded behind the authenticate
|
||||
# filter, so we need to implement those as action caches.
|
||||
# In this example, the +public+ action doesn't require authentication
|
||||
# so it's possible to use the faster page caching. On the other hand
|
||||
# +index+ and +show+ require authentication. They can still be cached,
|
||||
# but we need action caching for them.
|
||||
#
|
||||
# Action caching internally uses the fragment caching and an around
|
||||
# filter to do the job. The fragment cache is named according to both
|
||||
# the current host and the path. So a page that is accessed at
|
||||
# http://david.somewhere.com/lists/show/1 will result in a fragment named
|
||||
# "david.somewhere.com/lists/show/1". This allows the cacher to
|
||||
# differentiate between "david.somewhere.com/lists/" and
|
||||
# "jamis.somewhere.com/lists/" -- which is a helpful way of assisting
|
||||
# Action caching uses fragment caching internally and an around
|
||||
# filter to do the job. The fragment cache is named according to
|
||||
# the host and path of the request. A page that is accessed at
|
||||
# <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
|
||||
# <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
|
||||
# differentiate between <tt>david.example.com/lists/</tt> and
|
||||
# <tt>jamis.example.com/lists/</tt> -- which is a helpful way of assisting
|
||||
# the subdomain-as-account-key pattern.
|
||||
#
|
||||
# Different representations of the same resource, e.g.
|
||||
# <tt>http://david.somewhere.com/lists</tt> and
|
||||
# <tt>http://david.somewhere.com/lists.xml</tt>
|
||||
# <tt>http://david.example.com/lists</tt> and
|
||||
# <tt>http://david.example.com/lists.xml</tt>
|
||||
# are treated like separate requests and so are cached separately.
|
||||
# Keep in mind when expiring an action cache that
|
||||
# <tt>:action => 'lists'</tt> is not the same as
|
||||
# <tt>:action => 'list', :format => :xml</tt>.
|
||||
#
|
||||
# You can set modify the default action cache path by passing a
|
||||
# :cache_path option. This will be passed directly to
|
||||
# ActionCachePath.path_for. This is handy for actions with multiple
|
||||
# possible routes that should be cached differently. If a block is
|
||||
# given, it is called with the current controller instance.
|
||||
# <tt>:cache_path</tt> option. This will be passed directly to
|
||||
# <tt>ActionCachePath.path_for</tt>. This is handy for actions with
|
||||
# multiple possible routes that should be cached differently. If a
|
||||
# block is given, it is called with the current controller instance.
|
||||
#
|
||||
# And you can also use :if (or :unless) to pass a Proc that
|
||||
# specifies when the action should be cached.
|
||||
# And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
|
||||
# proc that specifies when the action should be cached.
|
||||
#
|
||||
# Finally, if you are using memcached, you can also pass :expires_in.
|
||||
# Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
|
||||
#
|
||||
# The following example depicts some of the points made above:
|
||||
#
|
||||
# class ListsController < ApplicationController
|
||||
# before_filter :authenticate, :except => :public
|
||||
# caches_page :public
|
||||
#
|
||||
# caches_page :public
|
||||
#
|
||||
# caches_action :index, :if => proc do |c|
|
||||
# !c.request.format.json? # cache if is not a JSON request
|
||||
# end
|
||||
@@ -58,19 +63,28 @@ module ActionController #:nodoc:
|
||||
# caches_action :show, :cache_path => { :project => 1 },
|
||||
# :expires_in => 1.hour
|
||||
#
|
||||
# caches_action :feed, :cache_path => proc do |controller|
|
||||
# if controller.params[:user_id]
|
||||
# controller.send(:user_list_url,
|
||||
# controller.params[:user_id], controller.params[:id])
|
||||
# caches_action :feed, :cache_path => proc do |c|
|
||||
# if c.params[:user_id]
|
||||
# c.send(:user_list_url,
|
||||
# c.params[:user_id], c.params[:id])
|
||||
# else
|
||||
# controller.send(:list_url, controller.params[:id])
|
||||
# c.send(:list_url, c.params[:id])
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# If you pass :layout => false, it will only cache your action
|
||||
# content. It is useful when your layout has dynamic information.
|
||||
# If you pass <tt>:layout => false</tt>, it will only cache your action
|
||||
# content. That's useful when your layout has dynamic information.
|
||||
#
|
||||
# Warning: If the format of the request is determined by the Accept HTTP
|
||||
# header the Content-Type of the cached response could be wrong because
|
||||
# no information about the MIME type is stored in the cache key. So, if
|
||||
# you first ask for MIME type M in the Accept header, a cache entry is
|
||||
# created, and then perform a second request to the same resource asking
|
||||
# for a different MIME type, you'd get the content cached for M.
|
||||
#
|
||||
# The <tt>:format</tt> parameter is taken into account though. The safest
|
||||
# way to cache by MIME type is to pass the format in the route.
|
||||
module Actions
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -89,12 +103,14 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def _save_fragment(name, options)
|
||||
return unless caching_allowed?
|
||||
|
||||
content = response_body
|
||||
content = content.join if content.is_a?(Array)
|
||||
|
||||
write_fragment(name, content, options)
|
||||
if caching_allowed?
|
||||
write_fragment(name, content, options)
|
||||
else
|
||||
content
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
@@ -144,7 +160,7 @@ module ActionController #:nodoc:
|
||||
attr_reader :path, :extension
|
||||
|
||||
# If +infer_extension+ is true, the cache path extension is looked up from the request's
|
||||
# path & format. This is desirable when reading and writing the cache, but not when
|
||||
# path and format. This is desirable when reading and writing the cache, but not when
|
||||
# expiring the cache - expire_action should expire the same files regardless of the
|
||||
# request format.
|
||||
def initialize(controller, options = {}, infer_extension = true)
|
||||
@@ -161,7 +177,7 @@ module ActionController #:nodoc:
|
||||
def normalize!(path)
|
||||
path << 'index' if path[-1] == ?/
|
||||
path << ".#{extension}" if extension and !path.ends_with?(extension)
|
||||
URI.unescape(path)
|
||||
URI.parser.unescape(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,52 +1,72 @@
|
||||
module ActionController #:nodoc:
|
||||
module Caching
|
||||
# Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
|
||||
# certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
|
||||
# parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like:
|
||||
# Fragment caching is used for caching various blocks within
|
||||
# views without caching the entire action as a whole. This is
|
||||
# useful when certain elements of an action change frequently or
|
||||
# depend on complicated state while other parts rarely change or
|
||||
# can be shared amongst multiple parties. The caching is done using
|
||||
# the <tt>cache</tt> helper available in the Action View. A
|
||||
# template with fragment caching might look like:
|
||||
#
|
||||
# <b>Hello <%= @name %></b>
|
||||
#
|
||||
# <% cache do %>
|
||||
# All the topics in the system:
|
||||
# <%= render :partial => "topic", :collection => Topic.find(:all) %>
|
||||
# <% end %>
|
||||
#
|
||||
# This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would
|
||||
# be able to invalidate it using <tt>expire_fragment(:controller => "topics", :action => "list")</tt>.
|
||||
# This cache will bind the name of the action that called it, so if
|
||||
# this code was part of the view for the topics/list action, you
|
||||
# would be able to invalidate it using:
|
||||
#
|
||||
# expire_fragment(:controller => "topics", :action => "list")
|
||||
#
|
||||
# This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using
|
||||
# <tt>caches_action</tt>, so we also have the option to qualify the name of the cached fragment with something like:
|
||||
# This default behavior is limited if you need to cache multiple
|
||||
# fragments per action or if the action itself is cached using
|
||||
# <tt>caches_action</tt>. To remedy this, there is an option to
|
||||
# qualify the name of the cached fragment by using the
|
||||
# <tt>:action_suffix</tt> option:
|
||||
#
|
||||
# <% cache(:action => "list", :action_suffix => "all_topics") do %>
|
||||
#
|
||||
# That would result in a name such as "/topics/list/all_topics", avoiding conflicts with the action cache and with any fragments that use a
|
||||
# different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique
|
||||
# cache names that we can refer to when we need to expire the cache.
|
||||
# That would result in a name such as
|
||||
# <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
|
||||
# action cache and with any fragments that use a different suffix.
|
||||
# Note that the URL doesn't have to really exist or be callable
|
||||
# - the url_for system is just used to generate unique cache names
|
||||
# that we can refer to when we need to expire the cache.
|
||||
#
|
||||
# The expiration call for this example is:
|
||||
#
|
||||
# expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")
|
||||
# expire_fragment(:controller => "topics",
|
||||
# :action => "list",
|
||||
# :action_suffix => "all_topics")
|
||||
module Fragments
|
||||
# Given a key (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading,
|
||||
# writing, or expiring a cached fragment. If the key is a hash, the generated key is the return
|
||||
# value of url_for on that hash (without the protocol). All keys are prefixed with "views/" and uses
|
||||
# Given a key (as described in <tt>expire_fragment</tt>), returns
|
||||
# a key suitable for use in reading, writing, or expiring a
|
||||
# cached fragment. If the key is a hash, the generated key is the
|
||||
# return value of url_for on that hash (without the protocol).
|
||||
# All keys are prefixed with <tt>views/</tt> and uses
|
||||
# ActiveSupport::Cache.expand_cache_key for the expansion.
|
||||
def fragment_cache_key(key)
|
||||
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
|
||||
end
|
||||
|
||||
# Writes <tt>content</tt> to the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
|
||||
# Writes <tt>content</tt> to the location signified by
|
||||
# <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats).
|
||||
def write_fragment(key, content, options = nil)
|
||||
return content unless cache_configured?
|
||||
|
||||
key = fragment_cache_key(key)
|
||||
instrument_fragment_cache :write_fragment, key do
|
||||
content = content.html_safe.to_str if content.respond_to?(:html_safe)
|
||||
content = content.to_str
|
||||
cache_store.write(key, content, options)
|
||||
end
|
||||
content
|
||||
end
|
||||
|
||||
# Reads a cached fragment from the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
|
||||
# Reads a cached fragment from the location signified by <tt>key</tt>
|
||||
# (see <tt>expire_fragment</tt> for acceptable formats).
|
||||
def read_fragment(key, options = nil)
|
||||
return unless cache_configured?
|
||||
|
||||
@@ -57,7 +77,8 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
|
||||
# Check if a cached fragment from the location signified by
|
||||
# <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
|
||||
def fragment_exist?(key, options = nil)
|
||||
return unless cache_configured?
|
||||
key = fragment_cache_key(key)
|
||||
@@ -70,8 +91,9 @@ module ActionController #:nodoc:
|
||||
# Removes fragments from the cache.
|
||||
#
|
||||
# +key+ can take one of three forms:
|
||||
#
|
||||
# * String - This would normally take the form of a path, like
|
||||
# <tt>"pages/45/notes"</tt>.
|
||||
# <tt>pages/45/notes</tt>.
|
||||
# * Hash - Treated as an implicit call to +url_for+, like
|
||||
# <tt>{:controller => "pages", :action => "notes", :id => 45}</tt>
|
||||
# * Regexp - Will remove any fragment that matches, so
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
require 'fileutils'
|
||||
require 'uri'
|
||||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
@@ -71,9 +70,9 @@ module ActionController #:nodoc:
|
||||
|
||||
# Manually cache the +content+ in the key determined by +path+. Example:
|
||||
# cache_page "I'm the cached content", "/lists/show"
|
||||
def cache_page(content, path)
|
||||
def cache_page(content, path, extension = nil)
|
||||
return unless perform_caching
|
||||
path = page_cache_path(path)
|
||||
path = page_cache_path(path, extension)
|
||||
|
||||
instrument_page_cache :write_page, path do
|
||||
FileUtils.makedirs(File.dirname(path))
|
||||
@@ -98,14 +97,16 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
def page_cache_file(path)
|
||||
name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
|
||||
name << page_cache_extension unless (name.split('/').last || name).include? '.'
|
||||
def page_cache_file(path, extension)
|
||||
name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
|
||||
unless (name.split('/').last || name).include? '.'
|
||||
name << (extension || self.page_cache_extension)
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
def page_cache_path(path)
|
||||
page_cache_directory + page_cache_file(path)
|
||||
def page_cache_path(path, extension = nil)
|
||||
page_cache_directory.to_s + page_cache_file(path, extension)
|
||||
end
|
||||
|
||||
def instrument_page_cache(name, path)
|
||||
@@ -135,7 +136,7 @@ module ActionController #:nodoc:
|
||||
# If no options are provided, the requested url is used. Example:
|
||||
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
|
||||
def cache_page(content = nil, options = nil)
|
||||
return unless self.class.perform_caching && caching_allowed
|
||||
return unless self.class.perform_caching && caching_allowed?
|
||||
|
||||
path = case options
|
||||
when Hash
|
||||
@@ -146,13 +147,13 @@ module ActionController #:nodoc:
|
||||
request.path
|
||||
end
|
||||
|
||||
self.class.cache_page(content || response.body, path)
|
||||
if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
|
||||
extension = ".#{type_symbol}"
|
||||
end
|
||||
|
||||
self.class.cache_page(content || response.body, path, extension)
|
||||
end
|
||||
|
||||
private
|
||||
def caching_allowed
|
||||
request.get? && response.status.to_i == 200
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
|
||||
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
|
||||
ActionController::Routing = ActionDispatch::Routing
|
||||
ActionController::Routing = ActionDispatch::Routing
|
||||
@@ -1,133 +0,0 @@
|
||||
module ActionController
|
||||
class Base
|
||||
# Deprecated methods. Wrap them in a module so they can be overwritten by plugins
|
||||
# (like the verify method.)
|
||||
module DeprecatedBehavior #:nodoc:
|
||||
def relative_url_root
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root is ineffective. " <<
|
||||
"Please stop using it.", caller
|
||||
end
|
||||
|
||||
def relative_url_root=
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root= is ineffective. " <<
|
||||
"Please stop using it.", caller
|
||||
end
|
||||
|
||||
def consider_all_requests_local
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local is deprecated, " <<
|
||||
"use Rails.application.config.consider_all_requests_local instead", caller
|
||||
Rails.application.config.consider_all_requests_local
|
||||
end
|
||||
|
||||
def consider_all_requests_local=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local= is deprecated. " <<
|
||||
"Please configure it on your application with config.consider_all_requests_local=", caller
|
||||
Rails.application.config.consider_all_requests_local = value
|
||||
end
|
||||
|
||||
def allow_concurrency
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency is deprecated, " <<
|
||||
"use Rails.application.config.allow_concurrency instead", caller
|
||||
Rails.application.config.allow_concurrency
|
||||
end
|
||||
|
||||
def allow_concurrency=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency= is deprecated. " <<
|
||||
"Please configure it on your application with config.allow_concurrency=", caller
|
||||
Rails.application.config.allow_concurrency = value
|
||||
end
|
||||
|
||||
def ip_spoofing_check=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check= is deprecated. " <<
|
||||
"Please configure it on your application with config.action_dispatch.ip_spoofing_check=", caller
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check = value
|
||||
end
|
||||
|
||||
def ip_spoofing_check
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check is deprecated. " <<
|
||||
"Configuring ip_spoofing_check on the application configures a middleware.", caller
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check
|
||||
end
|
||||
|
||||
def cookie_verifier_secret=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret= is deprecated. " <<
|
||||
"Please configure it on your application with config.secret_token=", caller
|
||||
end
|
||||
|
||||
def cookie_verifier_secret
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret is deprecated.", caller
|
||||
end
|
||||
|
||||
def trusted_proxies=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies= is deprecated. " <<
|
||||
"Please configure it on your application with config.action_dispatch.trusted_proxies=", caller
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check = value
|
||||
end
|
||||
|
||||
def trusted_proxies
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies is deprecated. " <<
|
||||
"Configuring trusted_proxies on the application configures a middleware.", caller
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check = value
|
||||
end
|
||||
|
||||
def session(*args)
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"Disabling sessions for a single controller has been deprecated. " +
|
||||
"Sessions are now lazy loaded. So if you don't access them, " +
|
||||
"consider them off. You can still modify the session cookie " +
|
||||
"options with request.session_options.", caller)
|
||||
end
|
||||
|
||||
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 value.delete(:disabled)
|
||||
Rails.application.config.session_store :disabled
|
||||
else
|
||||
store = Rails.application.config.session_store
|
||||
Rails.application.config.session_store store, value
|
||||
end
|
||||
end
|
||||
|
||||
# Controls the resource action separator
|
||||
def resource_action_separator
|
||||
@resource_action_separator ||= "/"
|
||||
end
|
||||
|
||||
def resource_action_separator=(val)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.resource_action_separator is deprecated and only " \
|
||||
"works with the deprecated router DSL."
|
||||
@resource_action_separator = val
|
||||
end
|
||||
|
||||
def use_accept_header
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.use_accept_header doesn't do anything anymore. " \
|
||||
"The accept header is always taken into account."
|
||||
end
|
||||
|
||||
def use_accept_header=(val)
|
||||
use_accept_header
|
||||
end
|
||||
|
||||
# This method has been moved to ActionDispatch::Request.filter_parameters
|
||||
def filter_parameter_logging(*args, &block)
|
||||
ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller)
|
||||
filter = Rails.application.config.filter_parameters
|
||||
filter.concat(args)
|
||||
filter << block if block
|
||||
filter
|
||||
end
|
||||
|
||||
# This was moved to a plugin
|
||||
def verify(*args)
|
||||
ActiveSupport::Deprecation.warn "verify was removed from Rails and is now available as a plugin. " <<
|
||||
"Please install it with `rails plugin install git://github.com/rails/verification.git`.", caller
|
||||
end
|
||||
end
|
||||
|
||||
extend DeprecatedBehavior
|
||||
|
||||
delegate :consider_all_requests_local, :consider_all_requests_local=,
|
||||
:allow_concurrency, :allow_concurrency=, :to => :"self.class"
|
||||
end
|
||||
end
|
||||
@@ -1,28 +0,0 @@
|
||||
module ActionController
|
||||
class Dispatcher
|
||||
class << self
|
||||
def before_dispatch(*args, &block)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.before_dispatch is deprecated. " <<
|
||||
"Please use ActionDispatch::Callbacks.before instead.", caller
|
||||
ActionDispatch::Callbacks.before(*args, &block)
|
||||
end
|
||||
|
||||
def after_dispatch(*args, &block)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.after_dispatch is deprecated. " <<
|
||||
"Please use ActionDispatch::Callbacks.after instead.", caller
|
||||
ActionDispatch::Callbacks.after(*args, &block)
|
||||
end
|
||||
|
||||
def to_prepare(*args, &block)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.to_prepare is deprecated. " <<
|
||||
"Please use config.to_prepare instead", caller
|
||||
ActionDispatch::Callbacks.after(*args, &block)
|
||||
end
|
||||
|
||||
def new
|
||||
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.new is deprecated, use Rails.application instead."
|
||||
Rails.application
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -16,7 +16,11 @@ module ActionController
|
||||
payload = event.payload
|
||||
additions = ActionController::Base.log_process_action(payload)
|
||||
|
||||
message = "Completed #{payload[:status]} #{Rack::Utils::HTTP_STATUS_CODES[payload[:status]]} in %.0fms" % event.duration
|
||||
status = payload[:status]
|
||||
if status.nil? && payload[:exception].present?
|
||||
status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil
|
||||
end
|
||||
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
|
||||
message << " (#{additions.join(" | ")})" unless additions.blank?
|
||||
|
||||
info(message)
|
||||
@@ -42,7 +46,7 @@ module ActionController
|
||||
def #{method}(event)
|
||||
key_or_path = event.payload[:key] || event.payload[:path]
|
||||
human_name = #{method.to_s.humanize.inspect}
|
||||
info("\#{human_name} \#{key_or_path} (%.1fms)" % event.duration)
|
||||
info("\#{human_name} \#{key_or_path} \#{"(%.1fms)" % event.duration}")
|
||||
end
|
||||
METHOD
|
||||
end
|
||||
|
||||
@@ -12,7 +12,7 @@ module ActionController
|
||||
#
|
||||
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
|
||||
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
|
||||
def initialize(klass, *args)
|
||||
def initialize(klass, *args, &block)
|
||||
options = args.extract_options!
|
||||
@only = Array(options.delete(:only)).map(&:to_s)
|
||||
@except = Array(options.delete(:except)).map(&:to_s)
|
||||
@@ -36,35 +36,88 @@ module ActionController
|
||||
action = action.to_s
|
||||
raise "MiddlewareStack#build requires an app" unless app
|
||||
|
||||
reverse.inject(app) do |a, middleware|
|
||||
middlewares.reverse.inject(app) do |a, middleware|
|
||||
middleware.valid?(action) ?
|
||||
middleware.build(a) : a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ActionController::Metal provides a way to get a valid Rack application from a controller.
|
||||
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
|
||||
# valid Rack interface without the additional niceties provided by
|
||||
# <tt>ActionController::Base</tt>.
|
||||
#
|
||||
# A sample metal controller might look like this:
|
||||
#
|
||||
# class HelloController < ActionController::Metal
|
||||
# def index
|
||||
# self.response_body = "Hello World!"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# And then to route requests to your metal controller, you would add
|
||||
# something like this to <tt>config/routes.rb</tt>:
|
||||
#
|
||||
# match 'hello', :to => HelloController.action(:index)
|
||||
#
|
||||
# The +action+ method returns a valid Rack application for the \Rails
|
||||
# router to dispatch to.
|
||||
#
|
||||
# == Rendering Helpers
|
||||
#
|
||||
# <tt>ActionController::Metal</tt> by default provides no utilities for rendering
|
||||
# views, partials, or other responses aside from explicitly calling of
|
||||
# <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
|
||||
# add the render helpers you're used to having in a normal controller, you
|
||||
# can do the following:
|
||||
#
|
||||
# class HelloController < ActionController::Metal
|
||||
# include ActionController::Rendering
|
||||
# append_view_path "#{Rails.root}/app/views"
|
||||
#
|
||||
# def index
|
||||
# render "hello/index"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# == Redirection Helpers
|
||||
#
|
||||
# To add redirection helpers to your metal controller, do the following:
|
||||
#
|
||||
# class HelloController < ActionController::Metal
|
||||
# include ActionController::Redirecting
|
||||
# include Rails.application.routes.url_helpers
|
||||
#
|
||||
# def index
|
||||
# redirect_to root_url
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# == Other Helpers
|
||||
#
|
||||
# You can refer to the modules included in <tt>ActionController::Base</tt> to see
|
||||
# other features you can bring into your metal controller.
|
||||
#
|
||||
# In AbstractController, dispatching is triggered directly by calling #process on a new controller.
|
||||
# ActionController::Metal provides an #action method that returns a valid Rack application for a
|
||||
# given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router,
|
||||
# can dispatch directly to the action returned by FooController.action(:index).
|
||||
class Metal < AbstractController::Base
|
||||
abstract!
|
||||
|
||||
attr_internal :env
|
||||
attr_internal_writer :env
|
||||
|
||||
def env
|
||||
@_env ||= {}
|
||||
end
|
||||
|
||||
# Returns the last part of the controller's name, underscored, without the ending
|
||||
# "Controller". For instance, MyApp::MyPostsController would return "my_posts" for
|
||||
# controller_name
|
||||
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
|
||||
# Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
|
||||
#
|
||||
# ==== Returns
|
||||
# String
|
||||
# * <tt>string</tt>
|
||||
def self.controller_name
|
||||
@controller_name ||= self.name.demodulize.sub(/Controller$/, '').underscore
|
||||
end
|
||||
|
||||
# Delegates to the class' #controller_name
|
||||
# Delegates to the class' <tt>controller_name</tt>
|
||||
def controller_name
|
||||
self.class.controller_name
|
||||
end
|
||||
@@ -81,6 +134,9 @@ module ActionController
|
||||
def initialize(*)
|
||||
@_headers = {"Content-Type" => "text/html"}
|
||||
@_status = 200
|
||||
@_request = nil
|
||||
@_response = nil
|
||||
@_routes = nil
|
||||
super
|
||||
end
|
||||
|
||||
@@ -112,6 +168,11 @@ module ActionController
|
||||
headers["Location"] = url
|
||||
end
|
||||
|
||||
# basic url_for that can be overridden for more robust functionality
|
||||
def url_for(string)
|
||||
string
|
||||
end
|
||||
|
||||
def status
|
||||
@_status
|
||||
end
|
||||
@@ -121,12 +182,11 @@ module ActionController
|
||||
end
|
||||
|
||||
def response_body=(val)
|
||||
body = val.respond_to?(:each) ? val : [val]
|
||||
body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
|
||||
super body
|
||||
end
|
||||
|
||||
# :api: private
|
||||
def dispatch(name, request)
|
||||
def dispatch(name, request) #:nodoc:
|
||||
@_request = request
|
||||
@_env = request.env
|
||||
@_env['action_controller.instance'] = self
|
||||
@@ -134,8 +194,7 @@ module ActionController
|
||||
to_a
|
||||
end
|
||||
|
||||
# :api: private
|
||||
def to_a
|
||||
def to_a #:nodoc:
|
||||
response ? response.to_a : [status, headers, response_body]
|
||||
end
|
||||
|
||||
@@ -147,8 +206,8 @@ module ActionController
|
||||
super
|
||||
end
|
||||
|
||||
def self.use(*args)
|
||||
middleware_stack.use(*args)
|
||||
def self.use(*args, &block)
|
||||
middleware_stack.use(*args, &block)
|
||||
end
|
||||
|
||||
def self.middleware
|
||||
@@ -164,10 +223,10 @@ module ActionController
|
||||
# for the same action.
|
||||
#
|
||||
# ==== Parameters
|
||||
# action<#to_s>:: An action name
|
||||
# * <tt>action</tt> - An action name
|
||||
#
|
||||
# ==== Returns
|
||||
# Proc:: A rack application
|
||||
# * <tt>proc</tt> - A rack application
|
||||
def self.action(name, klass = ActionDispatch::Request)
|
||||
middleware_stack.build(name.to_s) do |env|
|
||||
new.dispatch(name, klass.new(env))
|
||||
|
||||
@@ -5,9 +5,6 @@ module ActionController
|
||||
class ::ActionController::ActionControllerError < StandardError #:nodoc:
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
end
|
||||
|
||||
# Temporary hax
|
||||
included do
|
||||
::ActionController::UnknownAction = ::AbstractController::ActionNotFound
|
||||
@@ -39,12 +36,6 @@ module ActionController
|
||||
def assign_shortcuts(*) end
|
||||
|
||||
def _normalize_options(options)
|
||||
if options[:action] && options[:action].to_s.include?(?/)
|
||||
ActiveSupport::Deprecation.warn "Giving a path to render :action is deprecated. " <<
|
||||
"Please use render :template instead", caller
|
||||
options[:template] = options.delete(:action)
|
||||
end
|
||||
|
||||
options[:text] = nil if options.delete(:nothing) == true
|
||||
options[:text] = " " if options.key?(:text) && options[:text].nil?
|
||||
super
|
||||
|
||||
@@ -6,7 +6,7 @@ module ActionController
|
||||
include Head
|
||||
|
||||
# Sets the etag, last_modified, or both on the response and renders a
|
||||
# "304 Not Modified" response if the request is already fresh.
|
||||
# <tt>304 Not Modified</tt> response if the request is already fresh.
|
||||
#
|
||||
# Parameters:
|
||||
# * <tt>:etag</tt>
|
||||
@@ -17,11 +17,11 @@ module ActionController
|
||||
#
|
||||
# def show
|
||||
# @article = Article.find(params[:id])
|
||||
# fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true)
|
||||
# fresh_when(:etag => @article, :last_modified => @article.created_at, :public => true)
|
||||
# end
|
||||
#
|
||||
# This will render the show template if the request isn't sending a matching etag or
|
||||
# If-Modified-Since header and just a "304 Not Modified" response if there's a match.
|
||||
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
|
||||
#
|
||||
def fresh_when(options)
|
||||
options.assert_valid_keys(:etag, :last_modified, :public)
|
||||
@@ -36,7 +36,7 @@ module ActionController
|
||||
# Sets the etag and/or last_modified on the response and checks it against
|
||||
# the client request. If the request doesn't match the options provided, the
|
||||
# request is considered stale and should be generated from scratch. Otherwise,
|
||||
# it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
|
||||
# it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
|
||||
#
|
||||
# Parameters:
|
||||
# * <tt>:etag</tt>
|
||||
@@ -48,7 +48,7 @@ module ActionController
|
||||
# def show
|
||||
# @article = Article.find(params[:id])
|
||||
#
|
||||
# if stale?(:etag => @article, :last_modified => @article.created_at.utc)
|
||||
# if stale?(:etag => @article, :last_modified => @article.created_at)
|
||||
# @statistics = @article.really_expensive_call
|
||||
# respond_to do |format|
|
||||
# # all the supported formats
|
||||
@@ -60,13 +60,13 @@ module ActionController
|
||||
!request.fresh?(response)
|
||||
end
|
||||
|
||||
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
|
||||
# intermediate caches shouldn't cache the response.
|
||||
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a <tt>private</tt> instruction, so that
|
||||
# intermediate caches must not cache the response.
|
||||
#
|
||||
# Examples:
|
||||
# expires_in 20.minutes
|
||||
# expires_in 3.hours, :public => true
|
||||
# expires in 3.hours, 'max-stale' => 5.hours, :public => true
|
||||
# expires_in 3.hours, 'max-stale' => 5.hours, :public => true
|
||||
#
|
||||
# This method will overwrite an existing Cache-Control header.
|
||||
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
||||
@@ -77,7 +77,7 @@ module ActionController
|
||||
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
|
||||
end
|
||||
|
||||
# Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
|
||||
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or
|
||||
# intermediate caches (like caching proxy servers).
|
||||
def expires_now #:doc:
|
||||
response.cache_control.replace(:no_cache => true)
|
||||
|
||||
@@ -2,8 +2,6 @@ module ActionController
|
||||
module Head
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionController::UrlFor
|
||||
|
||||
# Return a response that has no content (merely headers). The options
|
||||
# argument is interpreted to be a hash of header names and values.
|
||||
# This allows you to easily return a response that consists only of
|
||||
@@ -22,13 +20,13 @@ module ActionController
|
||||
location = options.delete(:location)
|
||||
|
||||
options.each do |key, value|
|
||||
headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
|
||||
headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
|
||||
end
|
||||
|
||||
self.status = status
|
||||
self.location = url_for(location) if location
|
||||
self.content_type = Mime[formats.first]
|
||||
self.content_type = Mime[formats.first] if formats
|
||||
self.response_body = " "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,21 +2,21 @@ require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module ActionController
|
||||
# The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
|
||||
# +numbers+ and model objects, to name a few. These helpers are available to all templates
|
||||
# The \Rails framework provides a large number of helpers for working with assets, dates, forms,
|
||||
# numbers and model objects, to name a few. These helpers are available to all templates
|
||||
# by default.
|
||||
#
|
||||
# In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
|
||||
# In addition to using the standard template helpers provided, creating custom helpers to
|
||||
# extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
|
||||
# include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
|
||||
# include <tt>MyHelper</tt>.
|
||||
#
|
||||
# Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
|
||||
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
|
||||
# controller which inherits from it.
|
||||
#
|
||||
# ==== Examples
|
||||
# The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
|
||||
# the Time object is blank:
|
||||
# The +to_s+ method from the \Time class can be wrapped in a helper method to display a custom message if
|
||||
# a \Time object is blank:
|
||||
#
|
||||
# module FormattedTimeHelper
|
||||
# def format_time(time, format=:long, blank_message=" ")
|
||||
@@ -53,30 +53,20 @@ module ActionController
|
||||
include AbstractController::Helpers
|
||||
|
||||
included do
|
||||
config_accessor :helpers_path
|
||||
config_accessor :helpers_path, :include_all_helpers
|
||||
self.helpers_path ||= []
|
||||
self.include_all_helpers = true
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def helpers_dir
|
||||
ActiveSupport::Deprecation.warn "helpers_dir is deprecated, use helpers_path instead", caller
|
||||
self.helpers_path
|
||||
end
|
||||
|
||||
def helpers_dir=(value)
|
||||
ActiveSupport::Deprecation.warn "helpers_dir= is deprecated, use helpers_path= instead", caller
|
||||
self.helpers_path = Array.wrap(value)
|
||||
end
|
||||
|
||||
# Declares helper accessors for controller attributes. For example, the
|
||||
# following adds new +name+ and <tt>name=</tt> instance methods to a
|
||||
# controller and makes them available to the view:
|
||||
# helper_attr :name
|
||||
# attr_accessor :name
|
||||
# helper_attr :name
|
||||
#
|
||||
# ==== Parameters
|
||||
# *attrs<Array[String, Symbol]>:: Names of attributes to be converted
|
||||
# into helpers.
|
||||
# * <tt>attrs</tt> - Names of attributes to be converted into helpers.
|
||||
def helper_attr(*attrs)
|
||||
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
|
||||
end
|
||||
@@ -88,25 +78,28 @@ module ActionController
|
||||
|
||||
private
|
||||
# Overwrite modules_for_helpers to accept :all as argument, which loads
|
||||
# all helpers in helpers_dir.
|
||||
# all helpers in helpers_path.
|
||||
#
|
||||
# ==== Parameters
|
||||
# args<Array[String, Symbol, Module, all]>:: A list of helpers
|
||||
# * <tt>args</tt> - A list of helpers
|
||||
#
|
||||
# ==== Returns
|
||||
# Array[Module]:: A normalized list of modules for the list of
|
||||
# helpers provided.
|
||||
# * <tt>array</tt> - A normalized list of modules for the list of helpers provided.
|
||||
def modules_for_helpers(args)
|
||||
args += all_application_helpers if args.delete(:all)
|
||||
super(args)
|
||||
end
|
||||
|
||||
# Extract helper names from files in app/helpers/**/*_helper.rb
|
||||
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
|
||||
def all_application_helpers
|
||||
all_helpers_from_path(helpers_path)
|
||||
end
|
||||
|
||||
def all_helpers_from_path(path)
|
||||
helpers = []
|
||||
Array.wrap(helpers_path).each do |path|
|
||||
extract = /^#{Regexp.quote(path.to_s)}\/?(.*)_helper.rb$/
|
||||
helpers += Dir["#{path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
|
||||
Array.wrap(path).each do |_path|
|
||||
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
|
||||
helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
|
||||
end
|
||||
helpers.sort!
|
||||
helpers.uniq!
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module ActionController
|
||||
# ActionController::HideActions adds the ability to prevent public methods on a controller
|
||||
# to be called as actions.
|
||||
# Adds the ability to prevent public methods on a controller to be called as actions.
|
||||
module HideActions
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -23,7 +22,7 @@ module ActionController
|
||||
# Sets all of the actions passed in as hidden actions.
|
||||
#
|
||||
# ==== Parameters
|
||||
# *args<#to_s>:: A list of actions
|
||||
# * <tt>args</tt> - A list of actions
|
||||
def hide_action(*args)
|
||||
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
|
||||
end
|
||||
|
||||
@@ -3,9 +3,9 @@ require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionController
|
||||
module HttpAuthentication
|
||||
# Makes it dead easy to do HTTP Basic authentication.
|
||||
# Makes it dead easy to do HTTP \Basic and \Digest authentication.
|
||||
#
|
||||
# Simple Basic example:
|
||||
# === Simple \Basic example
|
||||
#
|
||||
# class PostsController < ApplicationController
|
||||
# USER_NAME, PASSWORD = "dhh", "secret"
|
||||
@@ -29,7 +29,9 @@ module ActionController
|
||||
# end
|
||||
#
|
||||
#
|
||||
# Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
|
||||
# === Advanced \Basic example
|
||||
#
|
||||
# Here is a more advanced \Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
|
||||
# the regular HTML interface is protected by a session approach:
|
||||
#
|
||||
# class ApplicationController < ActionController::Base
|
||||
@@ -69,7 +71,7 @@ module ActionController
|
||||
# assert_equal 200, status
|
||||
# end
|
||||
#
|
||||
# Simple Digest example:
|
||||
# === Simple \Digest example
|
||||
#
|
||||
# require 'digest/md5'
|
||||
# class PostsController < ApplicationController
|
||||
@@ -95,18 +97,20 @@ module ActionController
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately
|
||||
# hash to check the user's credentials. Returning +nil+ will cause authentication to fail.
|
||||
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
|
||||
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
|
||||
# authenticate as the user at this +realm+, but would not have the user's password to try using at
|
||||
# other sites.
|
||||
# === Notes
|
||||
#
|
||||
# On shared hosts, Apache sometimes doesn't pass authentication headers to
|
||||
# FCGI instances. If your environment matches this description and you cannot
|
||||
# authenticate, try this rule in your Apache setup:
|
||||
# The +authenticate_or_request_with_http_digest+ block must return the user's password
|
||||
# or the ha1 digest hash so the framework can appropriately hash to check the user's
|
||||
# credentials. Returning +nil+ will cause authentication to fail.
|
||||
#
|
||||
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
|
||||
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
|
||||
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
|
||||
# authenticate as the user at this +realm+, but would not have the user's password to try using at
|
||||
# other sites.
|
||||
#
|
||||
# In rare instances, web servers or front proxies strip authorization headers before
|
||||
# they reach your application. You can debug this situation by logging all environment
|
||||
# variables, and check for HTTP_AUTHORIZATION, amongst others.
|
||||
module Basic
|
||||
extend self
|
||||
|
||||
@@ -210,7 +214,7 @@ module ActionController
|
||||
|
||||
def encode_credentials(http_method, credentials, password, password_is_ha1)
|
||||
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
|
||||
"Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
|
||||
"Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
|
||||
end
|
||||
|
||||
def decode_credentials_header(request)
|
||||
@@ -218,11 +222,10 @@ module ActionController
|
||||
end
|
||||
|
||||
def decode_credentials(header)
|
||||
header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair|
|
||||
Hash[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
|
||||
key, value = pair.split('=', 2)
|
||||
hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')
|
||||
hash
|
||||
end
|
||||
[key.strip.to_sym, value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')]
|
||||
end]
|
||||
end
|
||||
|
||||
def authentication_header(controller, realm)
|
||||
@@ -392,11 +395,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| ... }
|
||||
#
|
||||
@@ -404,7 +407,7 @@ module ActionController
|
||||
# Returns nil if no token is found.
|
||||
def authenticate(controller, &login_procedure)
|
||||
token, options = token_and_options(controller.request)
|
||||
if !token.blank?
|
||||
unless token.blank?
|
||||
login_procedure.call(token, options)
|
||||
end
|
||||
end
|
||||
@@ -414,20 +417,19 @@ module ActionController
|
||||
# Authorization: Token token="abc", nonce="def"
|
||||
# Then the returned token is "abc", and the options is {:nonce => "def"}
|
||||
#
|
||||
# request - ActionController::Request instance with the current headers.
|
||||
# request - ActionDispatch::Request instance with the current headers.
|
||||
#
|
||||
# Returns an Array of [String, Hash] if a token is present.
|
||||
# Returns nil if no token is found.
|
||||
def token_and_options(request)
|
||||
if header = request.authorization.to_s[/^Token (.*)/]
|
||||
values = $1.split(',').
|
||||
inject({}) do |memo, value|
|
||||
value.strip! # remove any spaces between commas and values
|
||||
key, value = value.split(/\=\"?/) # split key=value pairs
|
||||
value.chomp!('"') # chomp trailing " in value
|
||||
value.gsub!(/\\\"/, '"') # unescape remaining quotes
|
||||
memo.update(key => value)
|
||||
end
|
||||
values = Hash[$1.split(',').map do |value|
|
||||
value.strip! # remove any spaces between commas and values
|
||||
key, value = value.split(/\=\"?/) # split key=value pairs
|
||||
value.chomp!('"') # chomp trailing " in value
|
||||
value.gsub!(/\\\"/, '"') # unescape remaining quotes
|
||||
[key, value]
|
||||
end]
|
||||
[values.delete("token"), values.with_indifferent_access]
|
||||
end
|
||||
end
|
||||
@@ -439,9 +441,8 @@ module ActionController
|
||||
#
|
||||
# Returns String.
|
||||
def encode_credentials(token, options = {})
|
||||
values = ["token=#{token.to_s.inspect}"]
|
||||
options.each do |key, value|
|
||||
values << "#{key}=#{value.to_s.inspect}"
|
||||
values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
|
||||
"#{key}=#{value.to_s.inspect}"
|
||||
end
|
||||
"Token #{values * ", "}"
|
||||
end
|
||||
|
||||
@@ -12,10 +12,10 @@ module ActionController
|
||||
|
||||
def method_for_action(action_name)
|
||||
super || begin
|
||||
if template_exists?(action_name.to_s, _prefix)
|
||||
if template_exists?(action_name.to_s, _prefixes)
|
||||
"default_render"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -78,7 +78,7 @@ module ActionController
|
||||
yield
|
||||
end
|
||||
|
||||
# Everytime after an action is processed, this method is invoked
|
||||
# Every time after an action is processed, this method is invoked
|
||||
# with the payload, so you can add more information.
|
||||
# :api: plugin
|
||||
def append_info_to_payload(payload) #:nodoc:
|
||||
|
||||
@@ -63,13 +63,13 @@ module ActionController #:nodoc:
|
||||
# might look something like this:
|
||||
#
|
||||
# def index
|
||||
# @people = Person.find(:all)
|
||||
# @people = Person.all
|
||||
# end
|
||||
#
|
||||
# Here's the same action, with web-service support baked in:
|
||||
#
|
||||
# def index
|
||||
# @people = Person.find(:all)
|
||||
# @people = Person.all
|
||||
#
|
||||
# respond_to do |format|
|
||||
# format.html
|
||||
@@ -155,7 +155,7 @@ module ActionController #:nodoc:
|
||||
# Respond to also allows you to specify a common block for different formats by using any:
|
||||
#
|
||||
# def index
|
||||
# @people = Person.find(:all)
|
||||
# @people = Person.all
|
||||
#
|
||||
# respond_to do |format|
|
||||
# format.html
|
||||
@@ -178,7 +178,7 @@ module ActionController #:nodoc:
|
||||
# respond_to :html, :xml, :json
|
||||
#
|
||||
# def index
|
||||
# @people = Person.find(:all)
|
||||
# @people = Person.all
|
||||
# respond_with(@person)
|
||||
# end
|
||||
# end
|
||||
@@ -208,8 +208,8 @@ module ActionController #:nodoc:
|
||||
# It also accepts a block to be given. It's used to overwrite a default
|
||||
# response:
|
||||
#
|
||||
# def destroy
|
||||
# @user = User.find(params[:id])
|
||||
# def create
|
||||
# @user = User.new(params[:user])
|
||||
# flash[:notice] = "User was successfully created." if @user.save
|
||||
#
|
||||
# respond_with(@user) do |format|
|
||||
@@ -227,7 +227,7 @@ module ActionController #:nodoc:
|
||||
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
|
||||
|
||||
if response = retrieve_response_from_mimes(&block)
|
||||
options = resources.extract_options!
|
||||
options = resources.size == 1 ? {} : resources.extract_options!
|
||||
options.merge!(:default_response => response)
|
||||
(options.delete(:responder) || self.class.responder).call(self, resources, options)
|
||||
end
|
||||
@@ -258,9 +258,8 @@ module ActionController #:nodoc:
|
||||
# nil if :not_acceptable was sent to the client.
|
||||
#
|
||||
def retrieve_response_from_mimes(mimes=nil, &block)
|
||||
collector = Collector.new { default_render }
|
||||
mimes ||= collect_mimes_from_class_level
|
||||
mimes.each { |mime| collector.send(mime) }
|
||||
collector = Collector.new(mimes) { default_render }
|
||||
block.call(collector) if block_given?
|
||||
|
||||
if format = request.negotiate_mime(collector.order)
|
||||
@@ -277,8 +276,9 @@ module ActionController #:nodoc:
|
||||
include AbstractController::Collector
|
||||
attr_accessor :order
|
||||
|
||||
def initialize(&block)
|
||||
def initialize(mimes, &block)
|
||||
@order, @responses, @default_response = [], {}, block
|
||||
mimes.each { |mime| send(mime) }
|
||||
end
|
||||
|
||||
def any(*args, &block)
|
||||
@@ -291,7 +291,7 @@ module ActionController #:nodoc:
|
||||
alias :all :any
|
||||
|
||||
def custom(mime_type, &block)
|
||||
mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
|
||||
mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
|
||||
@order << mime_type
|
||||
@responses[mime_type] ||= block
|
||||
end
|
||||
|
||||
@@ -20,6 +20,7 @@ module ActionController
|
||||
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
|
||||
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
|
||||
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
|
||||
# * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
|
||||
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
|
||||
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
|
||||
#
|
||||
@@ -30,6 +31,7 @@ module ActionController
|
||||
# redirect_to "/images/screenshot.jpg"
|
||||
# redirect_to articles_url
|
||||
# redirect_to :back
|
||||
# redirect_to proc { edit_post_url(@post) }
|
||||
#
|
||||
# The redirection happens as a "302 Moved" header unless otherwise specified.
|
||||
#
|
||||
@@ -39,6 +41,9 @@ module ActionController
|
||||
# redirect_to post_url(@post), :status => 301
|
||||
# redirect_to :action=>'atom', :status => 302
|
||||
#
|
||||
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
|
||||
# integer, or a symbol representing the downcased, underscored and symbolized description.
|
||||
#
|
||||
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
|
||||
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
|
||||
#
|
||||
@@ -48,8 +53,7 @@ module ActionController
|
||||
# redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
|
||||
# redirect_to { :action=>'atom' }, :alert => "Something serious happened"
|
||||
#
|
||||
# When using <tt>redirect_to :back</tt>, if there is no referrer,
|
||||
# RedirectBackError will be raised. You may specify some fallback
|
||||
# When using <tt>redirect_to :back</tt>, if there is no referrer, RedirectBackError will be raised. You may specify some fallback
|
||||
# behavior for this case by rescuing RedirectBackError.
|
||||
def redirect_to(options = {}, response_status = {}) #:doc:
|
||||
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
|
||||
@@ -83,6 +87,8 @@ module ActionController
|
||||
when :back
|
||||
raise RedirectBackError unless refer = request.headers["Referer"]
|
||||
refer
|
||||
when Proc
|
||||
_compute_redirect_to_location options.call
|
||||
else
|
||||
url_for(options)
|
||||
end.gsub(/[\r\n]/, '')
|
||||
|
||||
@@ -2,6 +2,7 @@ require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionController
|
||||
# See <tt>Renderers.add</tt>
|
||||
def self.add_renderer(key, &block)
|
||||
Renderers.add(key, &block)
|
||||
end
|
||||
@@ -15,30 +16,12 @@ module ActionController
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def _write_render_options
|
||||
renderers = _renderers.map do |name, value|
|
||||
<<-RUBY_EVAL
|
||||
if options.key?(:#{name})
|
||||
_process_options(options)
|
||||
return _render_option_#{name}(options.delete(:#{name}), options)
|
||||
end
|
||||
RUBY_EVAL
|
||||
end
|
||||
|
||||
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
||||
def _handle_render_options(options)
|
||||
#{renderers.join}
|
||||
end
|
||||
RUBY_EVAL
|
||||
end
|
||||
|
||||
def use_renderers(*args)
|
||||
new = _renderers.dup
|
||||
args.each do |key|
|
||||
new[key] = RENDERERS[key]
|
||||
end
|
||||
self._renderers = new.freeze
|
||||
_write_render_options
|
||||
end
|
||||
alias use_renderer use_renderers
|
||||
end
|
||||
@@ -47,31 +30,69 @@ module ActionController
|
||||
_handle_render_options(options) || super
|
||||
end
|
||||
|
||||
def _handle_render_options(options)
|
||||
_renderers.each do |name, value|
|
||||
if options.key?(name.to_sym)
|
||||
_process_options(options)
|
||||
return send("_render_option_#{name}", options.delete(name.to_sym), options)
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Hash of available renderers, mapping a renderer name to its proc.
|
||||
# Default keys are :json, :js, :xml and :update.
|
||||
RENDERERS = {}
|
||||
|
||||
# Adds a new renderer to call within controller actions.
|
||||
# A renderer is invoked by passing its name as an option to
|
||||
# <tt>AbstractController::Rendering#render</tt>. To create a renderer
|
||||
# pass it a name and a block. The block takes two arguments, the first
|
||||
# is the value paired with its key and the second is the remaining
|
||||
# hash of options passed to +render+.
|
||||
#
|
||||
# === Example
|
||||
# Create a csv renderer:
|
||||
#
|
||||
# ActionController::Renderers.add :csv do |obj, options|
|
||||
# filename = options[:filename] || 'data'
|
||||
# str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
|
||||
# send_data str, :type => Mime::CSV,
|
||||
# :disposition => "attachment; filename=#{filename}.csv"
|
||||
# end
|
||||
#
|
||||
# Note that we used Mime::CSV for the csv mime type as it comes with Rails.
|
||||
# For a custom renderer, you'll need to register a mime type with
|
||||
# <tt>Mime::Type.register</tt>.
|
||||
#
|
||||
# To use the csv renderer in a controller action:
|
||||
#
|
||||
# def show
|
||||
# @csvable = Csvable.find(params[:id])
|
||||
# respond_to do |format|
|
||||
# format.html
|
||||
# format.csv { render :csv => @csvable, :filename => @csvable.name }
|
||||
# }
|
||||
# end
|
||||
# To use renderers and their mime types in more concise ways, see
|
||||
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
|
||||
# <tt>ActionController::MimeResponds#respond_with</tt>
|
||||
def self.add(key, &block)
|
||||
define_method("_render_option_#{key}", &block)
|
||||
RENDERERS[key] = block
|
||||
All._write_render_options
|
||||
end
|
||||
|
||||
module All
|
||||
extend ActiveSupport::Concern
|
||||
include Renderers
|
||||
|
||||
INCLUDED = []
|
||||
included do
|
||||
self._renderers = RENDERERS
|
||||
_write_render_options
|
||||
INCLUDED << self
|
||||
end
|
||||
|
||||
def self._write_render_options
|
||||
INCLUDED.each(&:_write_render_options)
|
||||
end
|
||||
end
|
||||
|
||||
add :json do |json, options|
|
||||
json = ActiveSupport::JSON.encode(json, options) unless json.respond_to?(:to_str)
|
||||
json = json.to_json(options) unless json.kind_of?(String)
|
||||
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
|
||||
self.content_type ||= Mime::JSON
|
||||
self.response_body = json
|
||||
|
||||
@@ -2,12 +2,11 @@ module ActionController
|
||||
module Rendering
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionController::RackDelegation
|
||||
include AbstractController::Rendering
|
||||
|
||||
# Before processing, set the request formats in current controller formats.
|
||||
def process_action(*) #:nodoc:
|
||||
self.formats = request.formats.map { |x| x.to_sym }
|
||||
self.formats = request.formats.map { |x| x.ref }
|
||||
super
|
||||
end
|
||||
|
||||
@@ -21,36 +20,35 @@ module ActionController
|
||||
|
||||
private
|
||||
|
||||
# Normalize arguments by catching blocks and setting them on :update.
|
||||
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
|
||||
options = super
|
||||
options[:update] = blk if block_given?
|
||||
options
|
||||
# Normalize arguments by catching blocks and setting them on :update.
|
||||
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
|
||||
options = super
|
||||
options[:update] = blk if block_given?
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize both text and status options.
|
||||
def _normalize_options(options) #:nodoc:
|
||||
if options.key?(:text) && options[:text].respond_to?(:to_text)
|
||||
options[:text] = options[:text].to_text
|
||||
end
|
||||
|
||||
# Normalize both text and status options.
|
||||
def _normalize_options(options) #:nodoc:
|
||||
if options.key?(:text) && options[:text].respond_to?(:to_text)
|
||||
options[:text] = options[:text].to_text
|
||||
end
|
||||
|
||||
if options[:status]
|
||||
options[:status] = Rack::Utils.status_code(options[:status])
|
||||
end
|
||||
|
||||
super
|
||||
if options[:status]
|
||||
options[:status] = Rack::Utils.status_code(options[:status])
|
||||
end
|
||||
|
||||
# Process controller specific options, as status, content-type and location.
|
||||
def _process_options(options) #:nodoc:
|
||||
status, content_type, location = options.values_at(:status, :content_type, :location)
|
||||
super
|
||||
end
|
||||
|
||||
self.status = status if status
|
||||
self.content_type = content_type if content_type
|
||||
self.headers["Location"] = url_for(location) if location
|
||||
# Process controller specific options, as status, content-type and location.
|
||||
def _process_options(options) #:nodoc:
|
||||
status, content_type, location = options.values_at(:status, :content_type, :location)
|
||||
|
||||
super
|
||||
end
|
||||
self.status = status if status
|
||||
self.content_type = content_type if content_type
|
||||
self.headers["Location"] = url_for(location) if location
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,45 +4,27 @@ module ActionController #:nodoc:
|
||||
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
|
||||
end
|
||||
|
||||
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current
|
||||
# web application, not a forged link from another site, is done by embedding a token based on a random
|
||||
# string stored in the session (which an attacker wouldn't know) in all forms and Ajax requests generated
|
||||
# by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript
|
||||
# requests are checked, so this will not protect your XML API (presumably you'll have a different
|
||||
# authentication scheme there anyway). Also, GET requests are not protected as these should be
|
||||
# idempotent anyway.
|
||||
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
|
||||
# by including a token in the rendered html for your application. This token is
|
||||
# stored as a random string in the session, to which an attacker does not have
|
||||
# access. When a request reaches your application, \Rails then verifies the received
|
||||
# token with the token in the session. Only HTML and javascript requests are checked,
|
||||
# so this will not protect your XML API (presumably you'll have a different
|
||||
# authentication scheme there anyway). Also, GET requests are not protected as these
|
||||
# should be idempotent.
|
||||
#
|
||||
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
|
||||
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the
|
||||
# error message in production by editing public/422.html. A call to this method in ApplicationController is
|
||||
# generated by default in post-Rails 2.0 applications.
|
||||
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
|
||||
# which will check the token and raise an ActionController::InvalidAuthenticityToken
|
||||
# if it doesn't match what was expected. A call to this method is generated for new
|
||||
# \Rails applications by default. You can customize the error message by editing
|
||||
# public/422.html.
|
||||
#
|
||||
# The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form
|
||||
# manually (without the use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to
|
||||
# include a hidden field named like that and set its value to what is returned by
|
||||
# <tt>form_authenticity_token</tt>.
|
||||
#
|
||||
# Request forgery protection is disabled by default in test environment. If you are upgrading from Rails
|
||||
# 1.x, add this to config/environments/test.rb:
|
||||
#
|
||||
# # Disable request forgery protection in test environment
|
||||
# config.action_controller.allow_forgery_protection = false
|
||||
#
|
||||
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
|
||||
#
|
||||
# Here are some resources:
|
||||
# * http://isc.sans.org/diary.html?storyid=1750
|
||||
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
||||
#
|
||||
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
||||
# There are a few guidelines you should follow:
|
||||
#
|
||||
# * Keep your GET requests safe and idempotent. More reading material:
|
||||
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
||||
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
||||
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look
|
||||
# for "Expires: at end of session"
|
||||
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
|
||||
# value of this token must be added to every layout that renders forms by including
|
||||
# <tt>csrf_meta_tags</tt> in the html +head+.
|
||||
#
|
||||
# Learn more about CSRF attacks and securing your application in the
|
||||
# {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
|
||||
module RequestForgeryProtection
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -71,39 +53,42 @@ module ActionController #:nodoc:
|
||||
# class FooController < ApplicationController
|
||||
# protect_from_forgery :except => :index
|
||||
#
|
||||
# # you can disable csrf protection on controller-by-controller basis:
|
||||
# skip_before_filter :verify_authenticity_token
|
||||
# end
|
||||
# You can disable csrf protection on controller-by-controller basis:
|
||||
#
|
||||
# skip_before_filter :verify_authenticity_token
|
||||
#
|
||||
# It can also be disabled for specific controller actions:
|
||||
#
|
||||
# skip_before_filter :verify_authenticity_token, :except => [:create]
|
||||
#
|
||||
# Valid Options:
|
||||
#
|
||||
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
|
||||
def protect_from_forgery(options = {})
|
||||
self.request_forgery_protection_token ||= :authenticity_token
|
||||
before_filter :verify_authenticity_token, options
|
||||
prepend_before_filter :verify_authenticity_token, options
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def protect_from_forgery(options = {})
|
||||
self.request_forgery_protection_token ||= :authenticity_token
|
||||
before_filter :verify_authenticity_token, options
|
||||
end
|
||||
|
||||
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
||||
def verify_authenticity_token
|
||||
verified_request? || raise(ActionController::InvalidAuthenticityToken)
|
||||
verified_request? || handle_unverified_request
|
||||
end
|
||||
|
||||
def handle_unverified_request
|
||||
reset_session
|
||||
end
|
||||
|
||||
# Returns true or false if a request is verified. Checks:
|
||||
#
|
||||
# * is the format restricted? By default, only HTML requests are checked.
|
||||
# * is it a GET request? Gets should be safe and idempotent
|
||||
# * Does the form_authenticity_token match the given token value from the params?
|
||||
# * Does the X-CSRF-Token header match the form_authenticity_token
|
||||
def verified_request?
|
||||
!protect_against_forgery? || request.forgery_whitelisted? ||
|
||||
form_authenticity_token == params[request_forgery_protection_token]
|
||||
!protect_against_forgery? || request.get? ||
|
||||
form_authenticity_token == params[request_forgery_protection_token] ||
|
||||
form_authenticity_token == request.headers['X-CSRF-Token']
|
||||
end
|
||||
|
||||
# Sets the token value for the current session.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'active_support/json'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
# Responder is responsible for exposing a resource to different mime requests,
|
||||
# Responsible for exposing a resource to different mime requests,
|
||||
# usually depending on the HTTP verb. The responder is triggered when
|
||||
# <code>respond_with</code> is called. The simplest case to study is a GET request:
|
||||
#
|
||||
@@ -24,10 +24,10 @@ module ActionController #:nodoc:
|
||||
#
|
||||
# === Builtin HTTP verb semantics
|
||||
#
|
||||
# The default Rails responder holds semantics for each HTTP verb. Depending on the
|
||||
# The default \Rails responder holds semantics for each HTTP verb. Depending on the
|
||||
# content type, verb and the resource status, it will behave differently.
|
||||
#
|
||||
# Using Rails default responder, a POST request for creating an object could
|
||||
# Using \Rails default responder, a POST request for creating an object could
|
||||
# be written as:
|
||||
#
|
||||
# def create
|
||||
@@ -77,8 +77,6 @@ module ActionController #:nodoc:
|
||||
#
|
||||
# respond_with(@project, :manager, @task)
|
||||
#
|
||||
# Check <code>polymorphic_url</code> documentation for more examples.
|
||||
#
|
||||
class Responder
|
||||
attr_reader :controller, :request, :format, :resource, :resources, :options
|
||||
|
||||
@@ -89,6 +87,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 +99,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)
|
||||
@@ -121,7 +113,7 @@ module ActionController #:nodoc:
|
||||
# Main entry point for responder responsible to dispatch to the proper format.
|
||||
#
|
||||
def respond
|
||||
method = :"to_#{format}"
|
||||
method = "to_#{format}"
|
||||
respond_to?(method) ? send(method) : to_format
|
||||
end
|
||||
|
||||
@@ -146,7 +138,7 @@ module ActionController #:nodoc:
|
||||
|
||||
protected
|
||||
|
||||
# This is the common behavior for "navigation" requests, like :html, :iphone and so forth.
|
||||
# This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
|
||||
def navigation_behavior(error)
|
||||
if get?
|
||||
raise error
|
||||
@@ -157,7 +149,7 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# This is the common behavior for "API" requests, like :xml and :json.
|
||||
# This is the common behavior for formats associated with APIs, such as :xml and :json.
|
||||
def api_behavior(error)
|
||||
raise error unless resourceful?
|
||||
|
||||
@@ -167,6 +159,8 @@ module ActionController #:nodoc:
|
||||
display resource.errors, :status => :unprocessable_entity
|
||||
elsif post?
|
||||
display resource, :status => :created, :location => api_location
|
||||
elsif has_empty_resource_definition?
|
||||
display empty_resource, :status => :ok
|
||||
else
|
||||
head :ok
|
||||
end
|
||||
@@ -175,7 +169,7 @@ module ActionController #:nodoc:
|
||||
# Checks whether the resource responds to the current format or not.
|
||||
#
|
||||
def resourceful?
|
||||
resource.respond_to?(:"to_#{format}")
|
||||
resource.respond_to?("to_#{format}")
|
||||
end
|
||||
|
||||
# Returns the resource location by retrieving it from the options or
|
||||
@@ -227,5 +221,23 @@ module ActionController #:nodoc:
|
||||
def default_action
|
||||
@action ||= ACTIONS_FOR_VERBS[request.request_method_symbol]
|
||||
end
|
||||
|
||||
# Check whether resource needs a specific definition of empty resource to be valid
|
||||
#
|
||||
def has_empty_resource_definition?
|
||||
respond_to?("empty_#{format}_resource")
|
||||
end
|
||||
|
||||
# Delegate to proper empty resource method
|
||||
#
|
||||
def empty_resource
|
||||
send("empty_#{format}_resource")
|
||||
end
|
||||
|
||||
# Return a valid empty JSON resource
|
||||
#
|
||||
def empty_json_resource
|
||||
"{}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -69,10 +69,6 @@ module ActionController #:nodoc:
|
||||
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
|
||||
send_file_headers! options
|
||||
|
||||
if options[:x_sendfile]
|
||||
ActiveSupport::Deprecation.warn(":x_sendfile is no longer needed in send_file", caller)
|
||||
end
|
||||
|
||||
self.status = options[:status] || 200
|
||||
self.content_type = options[:content_type] if options.key?(:content_type)
|
||||
self.response_body = File.open(path, "rb")
|
||||
@@ -105,10 +101,6 @@ module ActionController #:nodoc:
|
||||
# send_data image.data, :type => image.content_type, :disposition => 'inline'
|
||||
#
|
||||
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
||||
#
|
||||
# <b>Tip:</b> if you want to stream large amounts of on-the-fly generated
|
||||
# data to the browser, then use <tt>render :text => proc { ... }</tt>
|
||||
# instead. See ActionController::Base#render for more information.
|
||||
def send_data(data, options = {}) #:doc:
|
||||
send_file_headers! options.dup
|
||||
render options.slice(:status, :content_type).merge(:text => data)
|
||||
@@ -121,10 +113,6 @@ module ActionController #:nodoc:
|
||||
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
|
||||
end
|
||||
|
||||
if options.key?(:length)
|
||||
ActiveSupport::Deprecation.warn("You do not need to provide the file's length", caller)
|
||||
end
|
||||
|
||||
disposition = options[:disposition]
|
||||
disposition += %(; filename="#{options[:filename]}") if options[:filename]
|
||||
|
||||
|
||||
@@ -14,18 +14,9 @@ module ActionController
|
||||
cookies.write(@_response)
|
||||
end
|
||||
@_response.prepare!
|
||||
set_test_assigns
|
||||
ret
|
||||
end
|
||||
|
||||
def set_test_assigns
|
||||
@assigns = {}
|
||||
(instance_variable_names - self.class.protected_instance_variables).each do |var|
|
||||
name, value = var[1..-1], instance_variable_get(var)
|
||||
@assigns[name] = value
|
||||
end
|
||||
end
|
||||
|
||||
# TODO : Rewrite tests using controller.headers= to use Rack env
|
||||
def headers=(new_headers)
|
||||
@_response ||= ActionDispatch::Response.new
|
||||
|
||||
@@ -2,27 +2,25 @@ module ActionController
|
||||
module UrlFor
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
include AbstractController::UrlFor
|
||||
|
||||
def url_options
|
||||
super.reverse_merge(
|
||||
:host => request.host_with_port,
|
||||
@_url_options ||= super.reverse_merge(
|
||||
:host => request.host,
|
||||
:port => request.optional_port,
|
||||
:protocol => request.protocol,
|
||||
:_path_segments => request.symbolized_path_parameters
|
||||
).merge(:script_name => request.script_name)
|
||||
end
|
||||
).freeze
|
||||
|
||||
def _routes
|
||||
raise "In order to use #url_for, you must include routing helpers explicitly. " \
|
||||
"For instance, `include Rails.application.routes.url_helpers"
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def action_methods
|
||||
@action_methods ||= begin
|
||||
super - _routes.named_routes.helper_names
|
||||
if _routes.equal?(env["action_dispatch.routes"])
|
||||
@_url_options.dup.tap do |options|
|
||||
options[:script_name] = request.script_name.dup
|
||||
options.freeze
|
||||
end
|
||||
else
|
||||
@_url_options
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,7 +31,7 @@ module ActionController
|
||||
super()
|
||||
@_app = app
|
||||
end
|
||||
|
||||
|
||||
def index
|
||||
call(env)
|
||||
end
|
||||
|
||||
@@ -2,35 +2,13 @@ require "rails"
|
||||
require "action_controller"
|
||||
require "action_dispatch/railtie"
|
||||
require "action_view/railtie"
|
||||
require "active_support/deprecation/proxy_wrappers"
|
||||
require "active_support/deprecation"
|
||||
require "abstract_controller/railties/routes_helpers"
|
||||
require "action_controller/railties/paths"
|
||||
|
||||
module ActionController
|
||||
class Railtie < Rails::Railtie
|
||||
config.action_controller = ActiveSupport::OrderedOptions.new
|
||||
|
||||
config.action_controller.singleton_class.tap do |d|
|
||||
d.send(:define_method, :session) do
|
||||
ActiveSupport::Deprecation.warn "config.action_controller.session has been deprecated. " <<
|
||||
"Please use Rails.application.config.session_store instead.", caller
|
||||
end
|
||||
|
||||
d.send(:define_method, :session=) do |val|
|
||||
ActiveSupport::Deprecation.warn "config.action_controller.session= has been deprecated. " <<
|
||||
"Please use config.session_store(name, options) instead.", caller
|
||||
end
|
||||
|
||||
d.send(:define_method, :session_store) do
|
||||
ActiveSupport::Deprecation.warn "config.action_controller.session_store has been deprecated. " <<
|
||||
"Please use Rails.application.config.session_store instead.", caller
|
||||
end
|
||||
|
||||
d.send(:define_method, :session_store=) do |val|
|
||||
ActiveSupport::Deprecation.warn "config.action_controller.session_store= has been deprecated. " <<
|
||||
"Please use config.session_store(name, options) instead.", caller
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_controller.logger" do
|
||||
ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger }
|
||||
end
|
||||
@@ -43,24 +21,27 @@ module ActionController
|
||||
paths = app.config.paths
|
||||
options = app.config.action_controller
|
||||
|
||||
options.assets_dir ||= paths.public.to_a.first
|
||||
options.javascripts_dir ||= paths.public.javascripts.to_a.first
|
||||
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
|
||||
options.page_cache_directory ||= paths.public.to_a.first
|
||||
options.helpers_path ||= paths.app.helpers.to_a
|
||||
options.assets_dir ||= paths["public"].first
|
||||
options.javascripts_dir ||= paths["public/javascripts"].first
|
||||
options.stylesheets_dir ||= paths["public/stylesheets"].first
|
||||
options.page_cache_directory ||= paths["public"].first
|
||||
|
||||
# make sure readers methods get compiled
|
||||
options.asset_path ||= app.config.asset_path
|
||||
options.asset_host ||= app.config.asset_host
|
||||
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
include app.routes.url_helpers
|
||||
include app.routes.mounted_helpers
|
||||
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
||||
extend ::ActionController::Railties::Paths.with(app)
|
||||
options.each { |k,v| send("#{k}=", v) }
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_controller.deprecated_routes" do |app|
|
||||
message = "ActionController::Routing::Routes is deprecated. " \
|
||||
"Instead, use Rails.application.routes"
|
||||
|
||||
proxy = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(app.routes, message)
|
||||
ActionController::Routing::Routes = proxy
|
||||
initializer "action_controller.compile_config_methods" do
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
config.compile_methods! if config.respond_to?(:compile_methods!)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
32
actionpack/lib/action_controller/railties/paths.rb
Normal file
32
actionpack/lib/action_controller/railties/paths.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
module ActionController
|
||||
module Railties
|
||||
module Paths
|
||||
def self.with(app)
|
||||
Module.new do
|
||||
define_method(:inherited) do |klass|
|
||||
super(klass)
|
||||
|
||||
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
|
||||
paths = namespace._railtie.paths["app/helpers"].existent
|
||||
else
|
||||
paths = app.config.helpers_paths
|
||||
end
|
||||
|
||||
klass.helpers_path = paths
|
||||
if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
|
||||
klass.helper :all
|
||||
end
|
||||
|
||||
if app.config.serve_static_assets && namespace
|
||||
paths = namespace._railtie.config.paths
|
||||
|
||||
klass.config.assets_dir = paths["public"].first
|
||||
klass.config.javascripts_dir = paths["public/javascripts"].first
|
||||
klass.config.stylesheets_dir = paths["public/stylesheets"].first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,7 @@
|
||||
require 'rack/session/abstract/id'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/object/to_query'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module ActionController
|
||||
module TemplateAssertions
|
||||
@@ -40,6 +41,13 @@ module ActionController
|
||||
ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
|
||||
end
|
||||
|
||||
def process(*args)
|
||||
@partials = Hash.new(0)
|
||||
@templates = Hash.new(0)
|
||||
@layouts = Hash.new(0)
|
||||
super
|
||||
end
|
||||
|
||||
# Asserts that the request was rendered with the appropriate template file or partials.
|
||||
#
|
||||
# ==== Examples
|
||||
@@ -127,7 +135,7 @@ module ActionController
|
||||
class Result < ::Array #:nodoc:
|
||||
def to_s() join '/' end
|
||||
def self.new_escaped(strings)
|
||||
new strings.collect {|str| URI.unescape str}
|
||||
new strings.collect {|str| uri_parser.unescape str}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -164,9 +172,14 @@ module ActionController
|
||||
end
|
||||
|
||||
def recycle!
|
||||
write_cookies!
|
||||
@env.delete('HTTP_COOKIE') if @cookies.blank?
|
||||
@env.delete('action_dispatch.cookies')
|
||||
@cookies = nil
|
||||
@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'] = {}
|
||||
@@ -186,20 +199,23 @@ module ActionController
|
||||
end
|
||||
end
|
||||
|
||||
class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc:
|
||||
DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS
|
||||
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
|
||||
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
|
||||
|
||||
def initialize(session = {})
|
||||
@env, @by = nil, nil
|
||||
replace(session.stringify_keys)
|
||||
@loaded = true
|
||||
end
|
||||
|
||||
def exists?; true; end
|
||||
def exists?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Superclass for ActionController functional tests. Functional tests allow you to
|
||||
# test a single controller action per test method. This should not be confused with
|
||||
# integration tests (see ActionController::IntegrationTest), which are more like
|
||||
# integration tests (see ActionDispatch::IntegrationTest), which are more like
|
||||
# "stories" that can involve multiple controllers and multiple actions (i.e. multiple
|
||||
# different HTTP requests).
|
||||
#
|
||||
@@ -244,7 +260,7 @@ module ActionController
|
||||
# after calling +post+. If the various assert methods are not sufficient, then you
|
||||
# may use this object to inspect the HTTP response in detail.
|
||||
#
|
||||
# (Earlier versions of Rails required each functional test to subclass
|
||||
# (Earlier versions of \Rails required each functional test to subclass
|
||||
# Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
|
||||
#
|
||||
# == Controller is automatically inferred
|
||||
@@ -257,7 +273,7 @@ module ActionController
|
||||
# tests WidgetController
|
||||
# end
|
||||
#
|
||||
# == Testing controller internals
|
||||
# == \Testing controller internals
|
||||
#
|
||||
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
|
||||
# can be used against. These collections are:
|
||||
@@ -265,7 +281,7 @@ module ActionController
|
||||
# * assigns: Instance variables assigned in the action that are available for the view.
|
||||
# * session: Objects being saved in the session.
|
||||
# * flash: The flash objects currently in the session.
|
||||
# * cookies: Cookies being sent to the user on this request.
|
||||
# * cookies: \Cookies being sent to the user on this request.
|
||||
#
|
||||
# These collections can be used just like any other hash:
|
||||
#
|
||||
@@ -289,9 +305,13 @@ module ActionController
|
||||
# and cookies, though. For sessions, you just do:
|
||||
#
|
||||
# @request.session[:key] = "value"
|
||||
# @request.cookies["key"] = "value"
|
||||
# @request.cookies[:key] = "value"
|
||||
#
|
||||
# == Testing named routes
|
||||
# To clear the cookies for a test just clear the request's cookies hash:
|
||||
#
|
||||
# @request.cookies.clear
|
||||
#
|
||||
# == \Testing named routes
|
||||
#
|
||||
# If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
|
||||
# Example:
|
||||
@@ -311,14 +331,14 @@ 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)
|
||||
self._controller_class = new_class
|
||||
end
|
||||
|
||||
def controller_class
|
||||
if current_controller_class = read_inheritable_attribute(:controller_class)
|
||||
if current_controller_class = self._controller_class
|
||||
current_controller_class
|
||||
else
|
||||
self.controller_class = determine_default_controller_class(name)
|
||||
@@ -393,16 +413,18 @@ module ActionController
|
||||
parameters ||= {}
|
||||
@request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
|
||||
|
||||
@request.session = ActionController::TestSession.new(session) unless session.nil?
|
||||
@request.session = ActionController::TestSession.new(session) if session
|
||||
@request.session["flash"] = @request.flash.update(flash || {})
|
||||
@request.session["flash"].sweep
|
||||
|
||||
@controller.request = @request
|
||||
@controller.params.merge!(parameters)
|
||||
build_request_uri(action, parameters)
|
||||
Base.class_eval { include Testing }
|
||||
@controller.class.class_eval { include Testing }
|
||||
@controller.process_with_new_base_test(@request, @response)
|
||||
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
|
||||
@request.session.delete('flash') if @request.session['flash'].blank?
|
||||
@request.cookies.merge!(@response.cookies)
|
||||
@response
|
||||
end
|
||||
|
||||
@@ -416,7 +438,7 @@ module ActionController
|
||||
|
||||
@request.env.delete('PATH_INFO')
|
||||
|
||||
if @controller
|
||||
if defined?(@controller) && @controller
|
||||
@controller.request = @request
|
||||
@controller.params = {}
|
||||
end
|
||||
@@ -430,6 +452,7 @@ module ActionController
|
||||
included do
|
||||
include ActionController::TemplateAssertions
|
||||
include ActionDispatch::Assertions
|
||||
class_attribute :_controller_class
|
||||
setup :setup_controller_request_and_response
|
||||
end
|
||||
|
||||
@@ -437,7 +460,7 @@ module ActionController
|
||||
|
||||
def build_request_uri(action, parameters)
|
||||
unless @request.env["PATH_INFO"]
|
||||
options = @controller.__send__(:url_options).merge(parameters)
|
||||
options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
|
||||
options.update(
|
||||
:only_path => true,
|
||||
:action => action,
|
||||
@@ -461,9 +484,11 @@ module ActionController
|
||||
# The exception is stored in the exception accessor for further inspection.
|
||||
module RaiseActionExceptions
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
attr_accessor :exception
|
||||
protected :exception, :exception=
|
||||
unless base.method_defined?(:exception) && base.method_defined?(:exception=)
|
||||
base.class_eval do
|
||||
attr_accessor :exception
|
||||
protected :exception, :exception=
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user