This commit is contained in:
Gonçalo Silva
2011-03-24 17:21:17 +00:00
1245 changed files with 56807 additions and 27487 deletions

3
.gitignore vendored
View File

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

@@ -0,0 +1,4 @@
--exclude /templates/
--quiet
act*/lib/**/*.rb
railties/lib/**/*.rb

52
Gemfile
View File

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

View File

@@ -1 +1 @@
3.0.0.rc
3.1.0.beta

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
<%= t('.greet_user', :name => 'lifo') %>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,3 @@
Hello there,
Hello there,
Mr. <%= @recipient %>

View File

@@ -1,6 +1,6 @@
%p Hello there,
%p Hello there,
%p
%p
Mr.
= @recipient
from haml

View File

@@ -1,6 +1,6 @@
%p Hello there,
%p Hello there,
%p
%p
Mr.
= @recipient
from haml

View File

@@ -1,3 +1,3 @@
Hello there,
Hello there,
Mr. <%= @recipient %>

View File

@@ -1,4 +1,4 @@
Hello there,
Hello there,
Mr. <%= @recipient %>. Please see our greeting at <%= @welcome_url %> <%= welcome_url %>

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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="[&quot;Chicago&quot;,&quot;IL&quot;]" />
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 doesnt 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]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,8 +13,8 @@ module AbstractController
# Override AbstractController::Base's process_action to run the
# process_action callbacks around the normal behavior.
def process_action(method_name)
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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,3 @@
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
ActionController::Routing = ActionDispatch::Routing
ActionController::Routing = ActionDispatch::Routing

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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="&nbsp;")
@@ -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!

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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]/, '')

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -31,7 +31,7 @@ module ActionController
super()
@_app = app
end
def index
call(env)
end

View File

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

View 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

View File

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