aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/MIT-LICENSE2
-rw-r--r--actionmailer/lib/action_mailer.rb2
-rw-r--r--actionpack/MIT-LICENSE2
-rw-r--r--actionpack/lib/action_dispatch.rb2
-rw-r--r--actionpack/lib/action_pack.rb2
-rw-r--r--actionpack/lib/action_view.rb2
-rw-r--r--activemodel/MIT-LICENSE2
-rw-r--r--activemodel/README211
-rw-r--r--activemodel/lib/active_model.rb2
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb140
-rw-r--r--activemodel/lib/active_model/errors.rb107
-rw-r--r--activemodel/lib/active_model/lint.rb16
-rw-r--r--activemodel/lib/active_model/naming.rb15
-rw-r--r--activemodel/lib/active_model/observing.rb23
-rw-r--r--activemodel/lib/active_model/serialization.rb57
-rw-r--r--activemodel/lib/active_model/translation.rb19
-rw-r--r--activemodel/lib/active_model/validations.rb39
-rw-r--r--activemodel/lib/active_model/validator.rb5
-rw-r--r--activerecord/MIT-LICENSE2
-rw-r--r--activerecord/lib/active_record.rb2
-rw-r--r--activeresource/MIT-LICENSE2
-rw-r--r--activesupport/MIT-LICENSE2
-rw-r--r--activesupport/lib/active_support/core_ext/string/interpolation.rb2
-rw-r--r--railties/MIT-LICENSE2
-rw-r--r--railties/guides/source/3_0_release_notes.textile488
-rw-r--r--railties/guides/source/action_mailer_basics.textile391
-rw-r--r--railties/lib/rails/dispatcher.rb2
27 files changed, 1267 insertions, 274 deletions
diff --git a/actionmailer/MIT-LICENSE b/actionmailer/MIT-LICENSE
index e7accc5ea1..a345a2419d 100644
--- a/actionmailer/MIT-LICENSE
+++ b/actionmailer/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2009 David Heinemeier Hansson
+Copyright (c) 2004-2010 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 0265e6e222..7f5bcad922 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2009 David Heinemeier Hansson
+# Copyright (c) 2004-2010 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/actionpack/MIT-LICENSE b/actionpack/MIT-LICENSE
index e7accc5ea1..a345a2419d 100644
--- a/actionpack/MIT-LICENSE
+++ b/actionpack/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2009 David Heinemeier Hansson
+Copyright (c) 2004-2010 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index c6eb097ee5..479ea959e6 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2009 David Heinemeier Hansson
+# Copyright (c) 2004-2010 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/actionpack/lib/action_pack.rb b/actionpack/lib/action_pack.rb
index b90f89be39..1a1497385a 100644
--- a/actionpack/lib/action_pack.rb
+++ b/actionpack/lib/action_pack.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2009 David Heinemeier Hansson
+# Copyright (c) 2004-2010 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 483be47adf..f5035fe45a 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2009 David Heinemeier Hansson
+# Copyright (c) 2004-2010 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/activemodel/MIT-LICENSE b/activemodel/MIT-LICENSE
index e7accc5ea1..a345a2419d 100644
--- a/activemodel/MIT-LICENSE
+++ b/activemodel/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2009 David Heinemeier Hansson
+Copyright (c) 2004-2010 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activemodel/README b/activemodel/README
index 7c9c754a8e..0d6fd1f21c 100644
--- a/activemodel/README
+++ b/activemodel/README
@@ -1,45 +1,68 @@
= Active Model - defined interfaces for Rails
-
+
Prior to Rails 3.0, if a plugin or gem developer wanted to be able to have
an object interact with Action Pack helpers, it was required to either
copy chunks of code from Rails, or monkey patch entire helpers to make them
handle objects that did not look like Active Record. This generated code
duplication and fragile applications that broke on upgrades.
-
+
Active Model is a solution for this problem.
-
+
Active Model provides a known set of interfaces that your objects can implement
to then present a common interface to the Action Pack helpers. You can include
functionality from the following modules:
-
-* Adding callbacks to your class
-
- class MyClass
- extend ActiveModel::Callbacks
- define_model_callbacks :create
-
- def create
- _run_create_callbacks do
- # Your create action methods here
- end
- end
- end
-
- ...gives you before_create, around_create and after_create class methods that
- wrap your create method.
+
+* Adding attribute magic to your objects
+
+ Add prefixes and suffixes to defined attribute methods...
+
+ class Person
+ include ActiveModel::AttributeMethods
+
+ attribute_method_prefix 'clear_'
+ define_attribute_methods [:name, :age]
+
+ attr_accessor :name, :age
+
+ def clear_attribute(attr)
+ send("#{attr}=", nil)
+ end
+ end
+
+ ...gives you clear_name, clear_age.
+
+ {Learn more}[link:classes/ActiveModel/AttributeMethods.html]
+
+* Adding callbacks to your objects
+
+ class Person
+ extend ActiveModel::Callbacks
+ define_model_callbacks :create
+
+ def create
+ _run_create_callbacks do
+ # Your create action methods here
+ end
+ end
+ end
+
+ ...gives you before_create, around_create and after_create class methods that
+ wrap your create method.
{Learn more}[link:classes/ActiveModel/CallBacks.html]
-
+
* For classes that already look like an Active Record object
-
- class MyClass
+
+ class Person
include ActiveModel::Conversion
end
...returns the class itself when sent :to_model
-
+
+ {Learn more}[link:classes/ActiveModel/Conversion.html]
+
* Tracking changes in your object
-
+
Provides all the value tracking features implemented by ActiveRecord...
person = Person.new
@@ -52,6 +75,142 @@ functionality from the following modules:
person.name = 'robert'
person.save
person.previous_changes # => {'name' => ['bob, 'robert']}
-
+
{Learn more}[link:classes/ActiveModel/Dirty.html]
-
+
+* Adding +errors+ support to your object
+
+ Provides the error messages to allow your object to interact with Action Pack
+ helpers seamlessly...
+
+ class Person
+
+ def initialize
+ @errors = ActiveModel::Errors.new(self)
+ end
+
+ attr_accessor :name
+ attr_reader :errors
+
+ def validate!
+ errors.add(:name, "can not be nil") if name == nil
+ end
+
+ def ErrorsPerson.human_attribute_name(attr, options = {})
+ "Name"
+ end
+
+ end
+
+ ... gives you...
+
+ person.errors.full_messages
+ # => ["Name Can not be nil"]
+ person.errors.full_messages
+ # => ["Name Can not be nil"]
+
+ {Learn more}[link:classes/ActiveModel/Errors.html]
+
+* Testing the compliance of your object
+
+ Use ActiveModel::Lint to test the compliance of your object to the
+ basic ActiveModel API...
+
+ {Learn more}[link:classes/ActiveModel/Lint/Tests.html]
+
+* Providing a human face to your object
+
+ ActiveModel::Naming provides your model with the model_name convention
+ and a human_name attribute...
+
+ class NamedPerson
+ extend ActiveModel::Naming
+ end
+
+ ...gives you...
+
+ NamedPerson.model_name #=> "NamedPerson"
+ NamedPerson.model_name.human #=> "Named person"
+
+ {Learn more}[link:classes/ActiveModel/Naming.html]
+
+* Adding observer support to your objects
+
+ ActiveModel::Observers allows your object to implement the Observer
+ pattern in a Rails App and take advantage of all the standard observer
+ functions.
+
+ {Learn more}[link:classes/ActiveModel/Observer.html]
+
+* Making your object serializable
+
+ ActiveModel::Serialization provides a standard interface for your object
+ to provide to_json or to_xml serialization...
+
+ s = SerialPerson.new
+ s.serializable_hash # => {"name"=>nil}
+ s.to_json # => "{\"name\":null}"
+ s.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
+
+ {Learn more}[link:classes/ActiveModel/Serialization.html]
+
+
+* Turning your object into a finite State Machine
+
+ ActiveModel::StateMachine provides a clean way to include all the methods
+ you need to transform your object into a finite State Machine...
+
+ light = TrafficLight.new
+ light.current_state #=> :red
+ light.change_color! #=> true
+ light.current_state #=> :green
+
+ {Learn more}[link:classes/ActiveModel/StateMachine.html]
+
+* Integrating with Rail's internationalization (i18n) handling through
+ ActiveModel::Translations...
+
+ class Person
+ extend ActiveModel::Translation
+ end
+
+ {Learn more}[link:classes/ActiveModel/Translation.html]
+
+* Providing a full Validation stack for your objects...
+
+ class Person
+ include ActiveModel::Validations
+
+ attr_accessor :first_name, :last_name
+
+ validates_each :first_name, :last_name do |record, attr, value|
+ record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
+ end
+ end
+
+ person = Person.new(:first_name => 'zoolander')
+ person.valid? #=> false
+
+ {Learn more}[link:classes/ActiveModel/Validations.html]
+
+* Make custom validators
+
+ class Person
+ include ActiveModel::Validations
+ validates_with HasNameValidator
+ attr_accessor :name
+ end
+
+ class HasNameValidator < ActiveModel::Validator
+ def validate(record)
+ record.errors[:name] = "must exist" if record.name.blank?
+ end
+ end
+
+ p = ValidatorPerson.new
+ p.valid? #=> false
+ p.errors.full_messages #=> ["Name must exist"]
+ p.name = "Bob"
+ p.valid? #=> true
+
+ {Learn more}[link:classes/ActiveModel/Validator.html]
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 1609075f7e..026430fee3 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2009 David Heinemeier Hansson
+# Copyright (c) 2004-2010 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 977a101277..200a6afbf0 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -5,6 +5,48 @@ module ActiveModel
class MissingAttributeError < NoMethodError
end
+ # <tt>ActiveModel::AttributeMethods</tt> provides a way to add prefixes and suffixes
+ # to your methods as well as handling the creation of Active Record like class methods
+ # such as +table_name+.
+ #
+ # The requirements to implement ActiveModel::AttributeMethods are:
+ #
+ # * <tt>include ActiveModel::AttributeMethods</tt> in your object
+ # * Call each Attribute Method module method you want to add, such as
+ # attribute_method_suffix or attribute_method_prefix
+ # * Call <tt>define_attribute_methods</tt> after the other methods are
+ # called.
+ # * Define the various generic +_attribute+ methods that you have declared
+ #
+ # A minimal implementation could be:
+ #
+ # class Person
+ #
+ # include ActiveModel::AttributeMethods
+ #
+ # attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
+ # attribute_method_suffix '_contrived?'
+ # attribute_method_prefix 'clear_'
+ # define_attribute_methods ['name']
+ #
+ # attr_accessor :name
+ #
+ # private
+ #
+ # def attribute_contrived?(attr)
+ # true
+ # end
+ #
+ # def clear_attribute(attr)
+ # send("#{attr}=", nil)
+ # end
+ #
+ # def reset_attribute_to_default!(attr)
+ # send("#{attr}=", "Default Name")
+ # end
+ #
+ # end
+ #
module AttributeMethods
extend ActiveSupport::Concern
@@ -22,12 +64,27 @@ module ActiveModel
#
# Example:
#
- # class A < ActiveRecord::Base
+ # class Person
+ #
+ # include ActiveModel::AttributeMethods
+ #
+ # cattr_accessor :primary_key
+ # cattr_accessor :inheritance_column
+ #
# define_attr_method :primary_key, "sysid"
# define_attr_method( :inheritance_column ) do
# original_inheritance_column + "_id"
# end
+ #
# end
+ #
+ # Provivdes you with:
+ #
+ # AttributePerson.primary_key
+ # # => "sysid"
+ # AttributePerson.inheritance_column = 'address'
+ # AttributePerson.inheritance_column
+ # # => 'address_id'
def define_attr_method(name, value=nil, &block)
sing = metaclass
sing.send :alias_method, "original_#{name}", name
@@ -54,19 +111,25 @@ module ActiveModel
#
# For example:
#
- # class Person < ActiveRecord::Base
+ # class Person
+ #
+ # include ActiveModel::AttributeMethods
+ # attr_accessor :name
# attribute_method_prefix 'clear_'
+ # define_attribute_methods [:name]
#
# private
- # def clear_attribute(attr)
- # ...
- # end
+ #
+ # def clear_attribute(attr)
+ # send("#{attr}=", nil)
+ # end
# end
#
- # person = Person.find(1)
- # person.name # => 'Gem'
+ # person = Person.new
+ # person.name = "Bob"
+ # person.name # => "Bob"
# person.clear_name
- # person.name # => ''
+ # person.name # => nil
def attribute_method_prefix(*prefixes)
attribute_method_matchers.concat(prefixes.map { |prefix| AttributeMethodMatcher.new :prefix => prefix })
undefine_attribute_methods
@@ -86,18 +149,24 @@ module ActiveModel
#
# For example:
#
- # class Person < ActiveRecord::Base
+ # class Person
+ #
+ # include ActiveModel::AttributeMethods
+ # attr_accessor :name
# attribute_method_suffix '_short?'
+ # define_attribute_methods [:name]
#
# private
- # def attribute_short?(attr)
- # ...
- # end
+ #
+ # def attribute_short?(attr)
+ # send(attr).length < 5
+ # end
# end
#
- # person = Person.find(1)
- # person.name # => 'Gem'
- # person.name_short? # => true
+ # person = Person.new
+ # person.name = "Bob"
+ # person.name # => "Bob"
+ # person.name_short? # => true
def attribute_method_suffix(*suffixes)
attribute_method_matchers.concat(suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix })
undefine_attribute_methods
@@ -118,16 +187,21 @@ module ActiveModel
#
# For example:
#
- # class Person < ActiveRecord::Base
+ # class Person
+ #
+ # include ActiveModel::AttributeMethods
+ # attr_accessor :name
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
+ # define_attribute_methods [:name]
#
# private
- # def reset_attribute_to_default!(attr)
- # ...
- # end
+ #
+ # def reset_attribute_to_default!(attr)
+ # ...
+ # end
# end
#
- # person = Person.find(1)
+ # person = Person.new
# person.name # => 'Gem'
# person.reset_name_to_default!
# person.name # => 'Gemma'
@@ -146,6 +220,30 @@ module ActiveModel
end
end
+ # Declares a the attributes that should be prefixed and suffixed by
+ # ActiveModel::AttributeMethods.
+ #
+ # To use, pass in an array of attribute names (as strings or symbols),
+ # be sure to declare +define_attribute_methods+ after you define any
+ # prefix, suffix or affix methods, or they will not hook in.
+ #
+ # class Person
+ #
+ # include ActiveModel::AttributeMethods
+ # attr_accessor :name, :age, :address
+ # attribute_method_prefix 'clear_'
+ #
+ # # Call to define_attribute_methods must appear after the
+ # # attribute_method_prefix, attribute_method_suffix or
+ # # attribute_method_affix declares.
+ # define_attribute_methods [:name, :age, :address]
+ #
+ # private
+ #
+ # def clear_attribute(attr)
+ # ...
+ # end
+ # end
def define_attribute_methods(attr_names)
return if attribute_methods_generated?
attr_names.each do |attr_name|
@@ -168,6 +266,7 @@ module ActiveModel
@attribute_methods_generated = true
end
+ # Removes all the preiously dynamically defined methods from the class
def undefine_attribute_methods
generated_attribute_methods.module_eval do
instance_methods.each { |m| undef_method(m) }
@@ -175,6 +274,7 @@ module ActiveModel
@attribute_methods_generated = nil
end
+ # Returns true if the attribute methods defined have been generated.
def generated_attribute_methods #:nodoc:
@generated_attribute_methods ||= begin
mod = Module.new
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index ff11ddc605..d8320275df 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -2,9 +2,66 @@ require 'active_support/core_ext/string/inflections'
require 'active_support/ordered_hash'
module ActiveModel
+ # Provides a modified +OrderedHash+ that you can include in your object
+ # for handling error messages and interacting with Action Pack helpers.
+ #
+ # A minimal implementation could be:
+ #
+ # class Person
+ #
+ # # Required dependency for ActiveModel::Errors
+ # extend ActiveModel::Naming
+ #
+ # def initialize
+ # @errors = ActiveModel::Errors.new(self)
+ # end
+ #
+ # attr_accessor :name
+ # attr_reader :errors
+ #
+ # def validate!
+ # errors.add(:name, "can not be nil") if name == nil
+ # end
+ #
+ # # The following methods are needed to be minimally implemented
+ #
+ # def read_attribute_for_validation(attr)
+ # send(attr)
+ # end
+ #
+ # def ErrorsPerson.human_attribute_name(attr, options = {})
+ # attr
+ # end
+ #
+ # def ErrorsPerson.lookup_ancestors
+ # [self]
+ # end
+ #
+ # end
+ #
+ # The last three methods are required in your object for Errors to be
+ # able to generate error messages correctly and also handle multiple
+ # languages. Of course, if you extend your object with ActiveModel::Translations
+ # you will not need to implement the last two. Likewise, using
+ # ActiveModel::Validations will handle the validation related methods
+ # for you.
+ #
+ # The above allows you to do:
+ #
+ # p = Person.new
+ # p.validate! # => ["can not be nil"]
+ # p.errors.full_messages # => ["name can not be nil"]
+ # # etc..
class Errors < ActiveSupport::OrderedHash
include DeprecatedErrorMethods
+ # Pass in the instance of the object that is using the errors object.
+ #
+ # class Person
+ # def initialize
+ # @errors = ActiveModel::Errors.new(self)
+ # end
+ # end
def initialize(base)
@base = base
super()
@@ -13,6 +70,10 @@ module ActiveModel
alias_method :get, :[]
alias_method :set, :[]=
+ # When passed a symbol or a name of a method, returns an array of errors for the method.
+ #
+ # p.errors[:name] #=> ["can not be nil"]
+ # p.errors['name'] #=> ["can not be nil"]
def [](attribute)
if errors = get(attribute.to_sym)
errors
@@ -21,28 +82,73 @@ module ActiveModel
end
end
+ # Adds to the supplied attribute the supplied error message.
+ #
+ # p.errors[:name] = "must be set"
+ # p.errors[:name] #=> ['must be set']
def []=(attribute, error)
self[attribute.to_sym] << error
end
+ # Iterates through each error key, value pair in the error messages hash.
+ # Yields the attribute and the error for that attribute. If the attribute
+ # has more than one error message, yields once for each error message.
+ #
+ # p.errors.add(:name, "can't be blank")
+ # p.errors.each do |attribute, errors_array|
+ # # Will yield :name and "can't be blank"
+ # end
+ #
+ # p.errors.add(:name, "must be specified")
+ # p.errors.each do |attribute, errors_array|
+ # # Will yield :name and "can't be blank"
+ # # then yield :name and "must be specified"
+ # end
def each
each_key do |attribute|
self[attribute].each { |error| yield attribute, error }
end
end
+ # Returns the number of error messages.
+ #
+ # p.errors.add(:name, "can't be blank")
+ # p.errors.size #=> 1
+ # p.errors.add(:name, "must be specified")
+ # p.errors.size #=> 2
def size
values.flatten.size
end
+ # Returns an array of error messages, with the attribute name included
+ #
+ # p.errors.add(:name, "can't be blank")
+ # p.errors.add(:name, "must be specified")
+ # p.errors.to_a #=> ["name can't be blank", "name must be specified"]
def to_a
full_messages
end
+ # Returns the number of error messages.
+ # p.errors.add(:name, "can't be blank")
+ # p.errors.count #=> 1
+ # p.errors.add(:name, "must be specified")
+ # p.errors.count #=> 2
def count
to_a.size
end
+ # Returns an xml formatted representation of the Errors hash.
+ #
+ # p.errors.add(:name, "can't be blank")
+ # p.errors.add(:name, "must be specified")
+ # p.errors.to_xml #=> Produces:
+ #
+ # # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
+ # # <errors>
+ # # <error>name can't be blank</error>
+ # # <error>name must be specified</error>
+ # # </errors>
def to_xml(options={})
require 'builder' unless defined? ::Builder
options[:root] ||= "errors"
@@ -59,6 +165,7 @@ module ActiveModel
# for the same attribute and ensure that this error object returns false when asked if <tt>empty?</tt>. More than one
# error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
# If no +messsage+ is supplied, :invalid is assumed.
+ #
# If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
# If +message+ is a Proc, it will be called, allowing for things like Time.now to be used within an error
def add(attribute, message = nil, options = {})
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index 1330bf7042..7bf0ad712d 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -13,8 +13,7 @@
module ActiveModel
module Lint
module Tests
- # valid?
- # ------
+ # == Responds to <tt>valid?</tt>
#
# Returns a boolean that specifies whether the object is in a valid or invalid
# state.
@@ -23,8 +22,7 @@ module ActiveModel
assert_boolean model.valid?, "valid?"
end
- # new_record?
- # -----------
+ # == Responds to <tt>new_record?</tt>
#
# Returns a boolean that specifies whether the object has been persisted yet.
# This is used when calculating the URL for an object. If the object is
@@ -41,8 +39,7 @@ module ActiveModel
assert_boolean model.destroyed?, "destroyed?"
end
- # naming
- # ------
+ # == Naming
#
# Model.model_name must returns a string with some convenience methods as
# :human and :partial_path. Check ActiveModel::Naming for more information.
@@ -57,12 +54,11 @@ module ActiveModel
assert_kind_of String, model_name.plural
end
- # errors
- # ------
- #
+ # == Errors Testing
+ #
# Returns an object that has :[] and :full_messages defined on it. See below
# for more details.
-
+ #
# Returns an Array of Strings that are the errors for the attribute in
# question. If localization is used, the Strings should be localized
# for the current locale. If no error is present, this method should
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 4cd68a0c89..89e8f8b1ea 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -35,6 +35,21 @@ module ActiveModel
end
end
+ # ActiveModel::Naming is a module that creates a +model_name+ method on your
+ # object.
+ #
+ # To implement, just extend ActiveModel::Naming in your object:
+ #
+ # class BookCover
+ # exten ActiveModel::Naming
+ # end
+ #
+ # BookCover.model_name #=> "BookCover"
+ # BookCover.model_name.human #=> "Book cover"
+ #
+ # Providing the functionality that ActiveModel::Naming provides in your object
+ # is required to pass the ActiveModel Lint test. So either extending the provided
+ # method below, or rolling your own is required..
module Naming
# Returns an ActiveModel::Name object for module. It can be
# used to retrieve all kinds of naming-related information.
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
index d9d1ab8967..ed6fb47c7e 100644
--- a/activemodel/lib/active_model/observing.rb
+++ b/activemodel/lib/active_model/observing.rb
@@ -24,8 +24,9 @@ module ActiveModel
# # Same as above, just using explicit class references
# ActiveRecord::Base.observers = Cacher, GarbageCollector
#
- # Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
- # called during startup, and before each development request.
+ # Note: Setting this does not instantiate the observers yet.
+ # +instantiate_observers+ is called during startup, and before
+ # each development request.
def observers=(*values)
@observers = values.flatten
end
@@ -102,10 +103,12 @@ module ActiveModel
#
# == Observing a class that can't be inferred
#
- # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
- # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
- # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
- # either the concrete class (Product) or a symbol for that class (:product):
+ # Observers will by default be mapped to the class with which they share a
+ # name. So CommentObserver will be tied to observing Comment, ProductManagerObserver
+ # to ProductManager, and so on. If you want to name your observer differently than
+ # the class you're interested in observing, you can use the Observer.observe class
+ # method which takes either the concrete class (Product) or a symbol for that
+ # class (:product):
#
# class AuditObserver < ActiveModel::Observer
# observe :account
@@ -115,7 +118,8 @@ module ActiveModel
# end
# end
#
- # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
+ # If the audit observer needs to watch more than one kind of object, this can be
+ # specified with multiple arguments:
#
# class AuditObserver < ActiveModel::Observer
# observe :account, :balance
@@ -125,7 +129,8 @@ module ActiveModel
# end
# end
#
- # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
+ # The AuditObserver will now act on both updates to Account and Balance by treating
+ # them both as records.
#
class Observer
include Singleton
@@ -144,7 +149,7 @@ module ActiveModel
#
# class AuditObserver < ActiveModel::Observer
# def self.observed_classes
- # [AccountObserver, BalanceObserver]
+ # [Account, Balance]
# end
# end
def observed_classes
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index 4c0073f687..1c48d4613a 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -2,6 +2,63 @@ require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/slice'
module ActiveModel
+ # Provides a basic serialization to a serializable_hash for your object.
+ #
+ # A minimal implementation could be:
+ #
+ # class Person
+ #
+ # include ActiveModel::Serialization
+ #
+ # attr_accessor :name
+ #
+ # def attributes
+ # @attributes ||= {'name' => 'nil'}
+ # end
+ #
+ # end
+ #
+ # Which would provide you with:
+ #
+ # person = Person.new
+ # person.serializable_hash # => {"name"=>nil}
+ # person.name = "Bob"
+ # person.serializable_hash # => {"name"=>"Bob"}
+ #
+ # You need to declare some sort of attributes hash which contains the attributes
+ # you want to serialize and their current value.
+ #
+ # Most of the time though, you will want to include the JSON or XML
+ # serializations. Both of these modules automatically include the
+ # ActiveModel::Serialization module, so there is no need to explicitly
+ # include it.
+ #
+ # So a minimal implementation including XML and JSON would be:
+ #
+ # class Person
+ #
+ # include ActiveModel::Serializers::JSON
+ # include ActiveModel::Serializers::Xml
+ #
+ # attr_accessor :name
+ #
+ # def attributes
+ # @attributes ||= {'name' => 'nil'}
+ # end
+ #
+ # end
+ #
+ # Which would provide you with:
+ #
+ # person = Person.new
+ # person.serializable_hash # => {"name"=>nil}
+ # person.to_json # => "{\"name\":null}"
+ # person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
+ #
+ # person.name = "Bob"
+ # person.serializable_hash # => {"name"=>"Bob"}
+ # person.to_json # => "{\"name\":\"Bob\"}"
+ # person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
module Serialization
def serializable_hash(options = nil)
options ||= {}
diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb
index 2d2df269d0..2ab342ffac 100644
--- a/activemodel/lib/active_model/translation.rb
+++ b/activemodel/lib/active_model/translation.rb
@@ -1,6 +1,23 @@
require 'active_support/core_ext/hash/reverse_merge'
module ActiveModel
+
+ # ActiveModel::Translation provides integration between your object and
+ # the Rails internationalization (i18n) framework.
+ #
+ # A minimal implementation could be:
+ #
+ # class TranslatedPerson
+ # extend ActiveModel::Translation
+ # end
+ #
+ # TranslatedPerson.human_attribute_name('my_attribue')
+ # #=> "My attribute"
+ #
+ # This also provides the required class methods for hooking into the
+ # Rails internationalization API, including being able to define a
+ # class based i18n_scope and lookup_ancestors to find translations in
+ # parent classes.
module Translation
include ActiveModel::Naming
@@ -18,8 +35,6 @@ module ActiveModel
# Transforms attributes names into a more human format, such as "First name" instead of "first_name".
#
- # Example:
- #
# Person.human_attribute_name("first_name") # => "First name"
#
# Specify +options+ with additional translating options.
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 276472ea46..03733a9c89 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -3,6 +3,41 @@ require 'active_support/core_ext/hash/keys'
require 'active_model/errors'
module ActiveModel
+
+ # Provides a full validation framework to your objects.
+ #
+ # A minimal implementation could be:
+ #
+ # class Person
+ # include ActiveModel::Validations
+ #
+ # attr_accessor :first_name, :last_name
+ #
+ # validates_each :first_name, :last_name do |record, attr, value|
+ # record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
+ # end
+ # end
+ #
+ # Which provides you with the full standard validation stack that you
+ # know from ActiveRecord.
+ #
+ # person = Person.new
+ # person.valid?
+ # #=> true
+ # person.invalid?
+ # #=> false
+ # person.first_name = 'zoolander'
+ # person.valid?
+ # #=> false
+ # person.invalid?
+ # #=> true
+ # person.errors
+ # #=> #<OrderedHash {:first_name=>["starts with z."]}>
+ #
+ # Note that ActiveModel::Validations automatically adds an +errors+ method
+ # to your instances initialized with a new ActiveModel::Errors object, so
+ # there is no need for you to add this manually.
+ #
module Validations
extend ActiveSupport::Concern
include ActiveSupport::Callbacks
@@ -18,8 +53,10 @@ module ActiveModel
# class Person
# include ActiveModel::Validations
#
+ # attr_accessor :first_name, :last_name
+ #
# validates_each :first_name, :last_name do |record, attr, value|
- # record.errors.add attr, 'starts with z.' if value[0] == ?z
+ # record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
# end
# end
#
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 382a4cc98d..ad9729de00 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -1,5 +1,6 @@
module ActiveModel #:nodoc:
- # A simple base class that can be used along with ActiveModel::Validations::ClassMethods.validates_with
+ # A simple base class that can be used along with
+ # +ActiveModel::Validations::ClassMethods.validates_with+
#
# class Person
# include ActiveModel::Validations
@@ -28,7 +29,7 @@ module ActiveModel #:nodoc:
# end
#
# class MyValidator < ActiveModel::Validator
- # def validate
+ # def validate(record)
# record # => The person instance being validated
# options # => Any non-standard options passed to validates_with
# end
diff --git a/activerecord/MIT-LICENSE b/activerecord/MIT-LICENSE
index e6df48772f..86bcb23b7c 100644
--- a/activerecord/MIT-LICENSE
+++ b/activerecord/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2009 David Heinemeier Hansson
+Copyright (c) 2004-2010 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 0c4cc6a554..9535c576be 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2009 David Heinemeier Hansson
+# Copyright (c) 2004-2010 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/activeresource/MIT-LICENSE b/activeresource/MIT-LICENSE
index 5eb8c94758..1bf965ff1e 100644
--- a/activeresource/MIT-LICENSE
+++ b/activeresource/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2006-2009 David Heinemeier Hansson
+Copyright (c) 2006-2010 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activesupport/MIT-LICENSE b/activesupport/MIT-LICENSE
index d6fdf21596..cd928b856d 100644
--- a/activesupport/MIT-LICENSE
+++ b/activesupport/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2005-2009 David Heinemeier Hansson
+Copyright (c) 2005-2010 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activesupport/lib/active_support/core_ext/string/interpolation.rb b/activesupport/lib/active_support/core_ext/string/interpolation.rb
index 2048d35091..06d3505c60 100644
--- a/activesupport/lib/active_support/core_ext/string/interpolation.rb
+++ b/activesupport/lib/active_support/core_ext/string/interpolation.rb
@@ -1,7 +1,7 @@
=begin
heavily based on Masao Mutoh's gettext String interpolation extension
http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
- Copyright (C) 2005-2009 Masao Mutoh
+ Copyright (C) 2005-2010 Masao Mutoh
You may redistribute it and/or modify it under the same license terms as Ruby.
=end
diff --git a/railties/MIT-LICENSE b/railties/MIT-LICENSE
index e6df48772f..86bcb23b7c 100644
--- a/railties/MIT-LICENSE
+++ b/railties/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2009 David Heinemeier Hansson
+Copyright (c) 2004-2010 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile
new file mode 100644
index 0000000000..afc88af643
--- /dev/null
+++ b/railties/guides/source/3_0_release_notes.textile
@@ -0,0 +1,488 @@
+h2. Ruby on Rails 3.0 Release Notes
+
+Rails 3.0 is a landmark release as it delivers on the Merb/Rails merge promise made in December 2008. Rails 3.0 provides major upgrades to all of the components of Rails, including a complete overhaul of the router and query APIs.
+
+One of the main achievements of this release, is that while there are loads of new features, old APIs have been deprecated with warnings wherever possible, so that you can implement the new features and conventions at your own pace. There is a backwards compatibility layer that will be supported during 3.0.x and removed in 3.1.
+
+Rails 3.0 adds Active Model ORM abstraction, Abstract Controller generic controller abstraction as well as a consistent Plugin API giving developers full access to all the Rails internals that make Action Mailer, Action Controller, Action View, Active Record and Active Resource work.
+
+These release notes cover the major upgrades, but don't include every little bug fix and change. If you want to see everything, check out the "list of commits":http://github.com/rails/rails/commits/master in the main Rails repository on GitHub.
+
+endprologue.
+
+h3. Upgrading from Rails 2.3.5 to Rails 3.0
+
+As always, having a high coverage, passing test suite is your friend when upgrading. You should also first upgrade to Rails 2.3.5 and make sure your application still runs as expected before attempting to update to Rails 3.0. In general, the upgrade from Rails 2.x to 3.0 centers around three big changes:
+
+h4. New Ruby Version Requirement
+
+WARNING: Rails only runs on version 1.8.7 of Ruby or later. Support for previous versions of Ruby has been dropped and Rails 3.0 will no longer boot on any of these versions.
+
+h4. The new boot process
+
+As part of the shift to treating Rails apps as Rack endpoints, you are now required to have a +config/application.rb+ file, which takes over much of the work +config/environment.rb+ used to handle. Along with that comes a lot of internal change to the boot process, but those changes are mostly internal.
+
+h4. Gems and gems and gems
+
+The +config.gem+ method is gone and has been replaced by using +bundler+ and a +Gemfile+, see "Vendoring Gems":#vendoring-gems below.
+
+h4. New APIs
+
+Both the router and query interface have seen significant, breaking changes. There is a backwards compatibility layer that is in place and will be supported until the 3.1 release.
+
+h4. Upgrade Process
+
+To help with the upgrade process, a plugin named "Rails Upgrade":http://github.com/rails/rails_upgrade has been created to automate part of it.
+
+Simply install the plugin, then run +rake rails:upgrade:check+ to check your app for pieces that need to be updated (with links to information on how to update them). It also offers a task to generate a +Gemfile+ based on your current +config.gem+ calls and a task to generate a new routes file from your current one. To get the plugin, simply run the following:
+
+<shell>
+script/plugin install git://github.com/rails/rails_upgrade.git
+</shell>
+
+You can see an example of how that works at "Rails Upgrade is now an Official Plugin":http://omgbloglol.com/post/364624593/rails-upgrade-is-now-an-official-plugin
+
+Aside from Rails Upgrade tool, if you need more help, there are people on IRC and "rubyonrails-talk":http://groups.google.com/group/rubyonrails-talk that are probably doing the same thing, possibly hitting the same issues. Be sure to blog your own experiences when upgrading so others can benefit from your knowledge!
+
+More information - "The Path to Rails 3: Approaching the upgrade":http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade
+
+h3. Application Creation
+
+As stated above, you must be on Ruby 1.8.7 or later to boot up a Rails application. Rails will no longer boot on Ruby 1.8.6 or earlier.
+
+Rails 3.0 is designed to run on 1.8.7 and also support Ruby 1.9.
+
+There have been a few changes to the +rails+ script that's used to generate Rails applications:
+
+* The application name, <tt>rails my_app</tt>, can now optionally be a path instead <tt>rails ~/code/my_app</tt>, your rails application will be name spaced under the application name you pass the +rails+ command.
+* Additionally, any flags you need to generate the application now need to come after the application path, for example:
+
+<shell>
+$ rails myapp --database=mysql
+</shell>
+
+h4. Vendoring Gems
+
+Rails now uses a +Gemfile+ in the application root to determine the gems you require for your application to start. This +Gemfile+ is then read and acted on by the new "Bundler":http://github.com/wycats/bundler gem, which then vendors all your gems into the vendor directory, making your Rails application isolated from system gems.
+
+More information: - "Using bundler":http://yehudakatz.com/2009/11/03/using-the-new-gem-bundler-today/
+
+h4. Living on the Edge
+
+Due to the use of +Gemfile+, the concept of freezing Rails was dropped, because it's always bundled/frozen inside your application. By default, it uses your system gems when bundling; however, if you want to bundle straight from the Git repository, you can pass the edge flag:
+
+<shell>
+$ rails myapp --edge
+</shell>
+
+More information:
+* "Spinning up a new Rails app":http://yehudakatz.com/2009/12/31/spinning-up-a-new-rails-app/
+* "Rails 3 and Passenger":http://cakebaker.42dh.com/2010/01/17/rails-3-and-passenger/
+
+h3. Rails Architectural Changes
+
+There are six major changes in the architecture of Rails.
+
+h4. Railties Restrung
+
+Railties was updated to provide a consistent plugin API for the entire Rails framework as well as a total rewrite of generators and the Rails bindings, the result is that developers can now hook into any significant stage of the generators and application framework in a consistent, defined manner.
+
+h4. All Rails core components are decoupled
+
+With the merge of Merb and Rails, one of the big jobs was to remove the tight coupling between Rails core components. This has now been achieved, and all Rails core components are now using the same API that you can use for developing plugins. This means any plugin you make, or any core component replacement (like DataMapper or Sequel) can access all the functionality that the Rails core components have access to and extend and enhance at will.
+
+More information: - "The Great Decoupling":http://yehudakatz.com/2009/07/19/rails-3-the-great-decoupling/
+
+h4. Active Model Abstraction
+
+Part of decoupling the core components was extracting all ties to Active Record from Action Pack. This has now been completed. All new ORM plugins now just need to implement Active Model interfaces to work seamlessly with Action Pack.
+
+More information: - "Make Any Ruby Object Feel Like ActiveRecord":http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/
+
+h4. Controller Abstraction
+
+Another big part of decoupling the core components was creating a base superclass that is separated from the notions of HTTP in order to handle rendering of views etc. This creation of +AbstractController+ allowed +ActionController+ and +ActionMailer+ to be greatly simplified with common code removed from all these libraries and put into Abstract Controller.
+
+More Information: - "Rails Edge Architecture":http://yehudakatz.com/2009/06/11/rails-edge-architecture/
+
+h4. Arel Integration
+
+"Arel":http://github.com/brynary/arel (or Active Relation) has been taken on as the underpinnings of Active Record and is now required for Rails (it is installed for you when you do a <tt>gem bundle</tt>). Arel provides an SQL abstraction that simplifies out Active Record and provides the underpinnings for the relation functionality in Active Record.
+
+More information: - "Why I wrote Arel":http://magicscalingsprinkles.wordpress.com/2010/01/28/why-i-wrote-arel/.
+
+h4. Mail Extraction
+
+Action Mailer ever since its beginnings has had monkey patches, pre parsers and even delivery and receiver agents, all in addition to having TMail vendored in the source tree. Version 3 changes that with all email message related functionality abstracted out to the "Mail":http://github.com/mikel/mail gem. This again reduces code duplication and helps create definable boundaries between Action Mailer and the email parser.
+
+More information: - "New Action Mailer API in Rails 3":http://lindsaar.net/2010/1/26/new-actionmailer-api-in-rails-3
+
+h3. Documentation
+
+The documentation in the Rails tree is being updated with all the API changes, additionally, the "Rails Edge guides":http://guides.rails.info/ are being updated one by one to reflect the changes in Rails 3.0. The guides at "guides.rubyonrails.org":http://guides.rubyonrails.org/ however will continue to contain only the stable version of Rails (at this point, version 2.3.5, until 3.0 is released).
+
+More Information: - "Rails Documentation Projects":http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects.
+
+h3. Railties
+
+With the decoupling of the main Rails frameworks, Railties got a huge overhaul so as to make linking up frameworks, engines or plugins as painless and extensible as possible:
+
+* Each application now has it's own name space, application is started with <tt>YourAppName.boot</tt> for example, makes interacting with other applications a lot easier.
+* You now have access to <tt>Rails.config</tt> which provides huge amount of configuration settings for your application.
+* Anything under <tt>Rails.root/app</tt> is now added to the load path, so you can make <tt>app/observers/user_observer.rb</tt> and Rails will load it without any modifications.
+* Rails 3.0 now provides a <tt>Rails.config</tt> object, which provides a central repository of all sorts of Rails wide configuration options.
+
+Application generation has received extra flags allowing you to skip the installation of test-unit, Active Record, Prototype and Git. Also a new <tt>--dev</tt> flag has been added which sets the application up with the +Gemfile+ pointing to your Rails checkout (which is determined by the path to the +rails+ binary). See <tt>rails --help</tt> for more info.
+
+Railties generators got a huge amount of attention in Rails 3.0, basically:
+
+* Generators were completely rewritten and are backwards incompatible.
+* Rails templates API and generators API were merged (they are the same as the former).
+* Generators are no longer loaded from special paths anymore, they are just found in the Ruby load path, so calling <tt>script/generate foo</tt> will look for <tt>generators/foo_generator</tt>.
+* New generators provide hooks, so any template engine, ORM, test framework can easily hook in.
+* New generators allow you to override the templates by placing a copy at <tt>RAILS_ROOT/lib/templates</tt>.
+* <tt>Rails::Generators::TestCase</tt> is also supplied so you can create your own generators and test them.
+
+Also, the views generated by Railties generators had some overhaul:
+
+* Views now use +div+ tags instead of +p+ tags.
+* Scaffolds generated now make use of <tt>_form</tt> partials, instead of duplicated code in the edit and new views.
+* Scaffold forms now use <tt>f.submit</tt> which returns "Create ModelName" or "Update ModelName" depending on the state of the object passed in.
+
+Railties now deprecates:
+
+* <tt>RAILS_ROOT</tt> in favour of <tt>Rails.root</tt>,
+* <tt>RAILS_ENV</tt> in favour of <tt>Rails.env</tt>, and
+* <tt>RAILS_DEFAULT_LOGGER</tt> in favour of <tt>Rails.logger</tt>.
+
+<tt>PLUGIN/rails/tasks</tt>, and <tt>PLUGIN/tasks</tt> are no longer loaded all tasks now must be in <tt>PLUGIN/lib/tasks</tt>.
+
+More information:
+* "Discovering Rails 3 generators":http://blog.plataformatec.com.br/2010/01/discovering-rails-3-generators
+* "Making Generators for Rails 3 with Thor":http://caffeinedd.com/guides/331-making-generators-for-rails-3-with-thor
+
+h3. Action Pack
+
+There have been significant internal and external changes in Action Pack.
+
+h4. Abstract Controller
+
+Abstract Controller pulls out the generic parts of Action Controller into a reusable module that any library can use to render templates, render partials, helpers, translations, logging, any part of the request response cycle. This abstraction allowed <tt>ActionMailer::Base</tt> to now just inherit from +AbstractController+ and just wrap the Rails DSL onto the Mail gem.
+
+It also provided an opportunity to clean up Action Controller, abstracting out what could to simplify the code.
+
+Note however that Abstract Controller is not a user facing API, you will not run into it in your day to day use of Rails.
+
+More Information: - "Rails Edge Architecture":http://yehudakatz.com/2009/06/11/rails-edge-architecture/
+
+h4. Action Controller
+
+* <tt>application_controller.rb</tt> now has <tt>protect_from_forgery</tt> on by default.
+* The <tt>cookie_verifier_secret</tt> has been moved to <tt>initializers/cookie_verification_secret.rb</tt>.
+* The <tt>session_store</tt> configuration has moved to <tt>initializers/session_store.rb</tt>.
+* <tt>cookies.secure</tt> allowing you to set encrypted values in cookies with <tt>cookie.secure[:key] => value</tt>.
+* <tt>cookies.permanent</tt> allowing you to set permanent values in the cookie hash <tt>cookie.permanent[:key] => value</tt> that raise exceptions on signed values if verification failures.
+* You can now pass <tt>:notice => 'This is a flash message'</tt> or <tt>:alert => 'Something went wrong'</tt> to the <tt>format</tt> call inside a +respond_to+ block. The <tt>flash[]</tt> hash still works as previously.
+* <tt>respond_with</tt> method has now been added to your controllers simplifying the venerable +format+ blocks.
+* <tt>ActionController::Responder</tt> added allowing you flexibility in how your responses get generated.
+
+Deprecations:
+
+* <tt>filter_parameter_logging</tt> is deprecated in favour of <tt>config.filter_parameters << :password</tt>.
+
+More Information:
+* "Render Options in Rails 3":http://www.engineyard.com/blog/2010/render-options-in-rails-3/
+* "Three reasons to love ActionController::Responder":http://weblog.rubyonrails.org/2009/8/31/three-reasons-love-responder
+
+h4. Action Dispatch
+
+Action Dispatch is new in Rails 3.0 and provides a new, cleaner implementation for routing.
+
+* Big clean up and re-write of the router, the Rails router is now +rack_mount+ with a Rails DSL on top, it is a stand alone piece of software.
+* Routes defined by each application are now name spaced within your Application module, that is:
+
+<ruby>
+# Instead of:
+
+ActionController::Routing::Routes.draw do
+ map.resources :posts
+end
+
+# You do:
+
+AppName::Application.routes do
+ resources :posts
+end
+</ruby>
+
+* Added +match+ method to the router, you can also pass any Rack application to the matched route.
+* Added +constraints+ method to the router, allowing you to guard routers with defined constraints.
+* Added +scope+ method to the router, allowing you to namespace routes for different languages or different actions, for example:
+
+<ruby>
+scope 'es' { resources :projects,
+ :path_names => { :edit => 'cambiar' },
+ :as => 'projeto' }
+
+# Gives you the edit action with /es/projeto/1/cambiar
+</ruby>
+
+* Added +root+ method to the router as a short cut for <tt>match '/', :to => path</tt>.
+* You can pass optional segments into the match, for example <tt>match "/:controller(/:action(/:id))(.:format)"</tt>, each parenthesized segment is optional.
+* Routes can be expressed via blocks, for example you can call <tt>controller :home { match '/:action' }</tt>.
+
+NOTE. The old style <tt>map</tt> commands still work as before with a backwards compatibility layer, however this will be removed in the 3.1 release.
+
+Deprecations
+
+* The catch all route for non-REST applications (<tt>/:controller/:action/:id</tt>) is now commented out.
+* Routes :path_prefix no longer exists and :name_prefix now automatically adds "_" at the end of the given value.
+
+More Information:
+* "The Rails 3 Router: Rack it Up":http://yehudakatz.com/2009/12/26/the-rails-3-router-rack-it-up/
+* "Revamped Routes in Rails 3":http://rizwanreza.com/2009/12/20/revamped-routes-in-rails-3
+* "Generic Actions in Rails 3":http://yehudakatz.com/2009/12/20/generic-actions-in-rails-3/
+
+h4. Action View
+
+Major re-write was done in the Action View helpers, implementing Unobtrusive JavaScript (UJS) hooks and removing the old inline AJAX commands. This enables Rails to use any compliant UJS driver to implement the UJS hooks in the helpers.
+
+What this means is that all previous <tt>remote_<method></tt> helpers have been removed from Rails core and put into the "Prototype Legacy Helper":http://github.com/rails/prototype_legacy_helper. To get UJS hooks into your HTML, you now pass <tt>:remote => true</tt> instead. For example:
+
+<ruby>
+form_for @post, :remote => true
+</ruby>
+
+Produces:
+
+<html>
+<form action="http://host.com" id="create-post" method="post" data-remote="true">
+</html>
+
+* You no longer need to call <tt>h(string)</tt> to escape HTML output, it is on by default in all view templates. If you want the unescaped string, call <tt>raw(string)</tt>.
+* Helpers now output HTML 5 by default.
+* Form label helper now pulls values from I18n with a single value, so <tt>f.label :name</tt> will pull the <tt>:name</tt> translation.
+* I18n select label on should now be :en.helpers.select instead of :en.support.select.
+* You no longer need to place a minus sign at the end of a ruby interpolation inside an ERb template to remove the trailing carriage return in the HTML output.
+
+h3. Active Model
+
+Active Model is new in Rails 3.0. It provides an abstraction layer for any ORM libraries to use to interact with Rails by implementing an Active Model interface.
+
+h4. ORM Abstraction and Action Pack Interface
+
+Part of decoupling the core components was extracting all ties to Active Record from Action Pack. This has now been completed. All new ORM plugins now just need to implement Active Model interfaces to work seamlessly with Action Pack.
+
+More Information: - "Make Any Ruby Object Feel Like ActiveRecord":http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/
+
+h4. Validations
+
+Validations have been moved from Active Record into Active Model, providing an interface to validations that works across ORM libraries in Rails 3.
+
+* There is now a <tt>validates :attribute, options_hash</tt> shortcut method that allows you to pass options for all the validates class methods, you can pass more than one option to a validate method.
+* The +validates+ method has the following options:
+ * <tt>:acceptance => Boolean</tt>.
+ * <tt>:confirmation => Boolean</tt>.
+ * <tt>:exclusion => { :in => Ennumerable }</tt>.
+ * <tt>:inclusion => { :in => Ennumerable }</tt>.
+ * <tt>:format => { :with => Regexp, :on => :create }</tt>.
+ * <tt>:length => { :maximum => Fixnum }</tt>.
+ * <tt>:numericality => Boolean</tt>.
+ * <tt>:presence => Boolean</tt>.
+ * <tt>:uniqueness => Boolean</tt>.
+
+NOTE: All the Rails version 2.3 style validation methods are still supported in Rails 3.0, the new validates method is designed as an additional aid in your model validations, not a replacement for the existing API.
+
+You can also pass in a validator object, which you can then reuse between objects that use Active Model:
+
+<ruby>
+class TitleValidator < ActiveModel::EachValidator
+ Titles = ['Mr.', 'Mrs.', 'Dr.']
+ def validate_each(record, attribute, value)
+ unless Titles.include?(value)
+ record.errors[attribute] << 'must be a valid title'
+ end
+ end
+end
+</ruby>
+
+<ruby>
+class Person
+ include ActiveModel::Validations
+ attr_accessor :title
+ validates :title, :presence => true, :title => true
+end
+
+# Or for Active Record
+
+class Person < ActiveRecord::Base
+ validates :title, :presence => true, :title => true
+end
+</ruby>
+
+More Information:
+* "Sexy Validation in Rails 3":http://thelucid.com/2010/01/08/sexy-validation-in-edge-rails-rails-3/
+* "Rails 3 Validations Explained":http://lindsaar.net/2010/1/31/validates_rails_3_awesome_is_true
+
+h3. Active Record
+
+h4. Query Interface
+
+Active Record, through the use of Arel, now returns relations on it's core methods. The existing API in Rails 2.3.x is still supported and will not be deprecated until Rails 3.1 and not removed until Rails 3.2, however, the new API provides the following new methods that all return relations allowing them to be chained together:
+
+* <tt>where</tt> - provides conditions on the relation, what gets returned.
+* <tt>select</tt> - choose what attributes of the models you wish to have returned from the database.
+* <tt>group</tt> - groups the relation on the attribute supplied.
+* <tt>having</tt> - provides an expression limiting group relations (GROUP BY constraint).
+* <tt>joins</tt> - joins the relation to another table.
+* <tt>clause</tt> - provides an expression limiting join relations (JOIN constraint).
+* <tt>includes</tt> - includes other relations pre-loaded.
+* <tt>order</tt> - orders the relation based on the expression supplied.
+* <tt>limit</tt> - limits the relation to the number of records specified.
+* <tt>lock</tt> - locks the records returned from the table.
+* <tt>readonly</tt> - returns an read only copy of the data.
+* <tt>from</tt> - provides a way to select relationships from more than one table.
+* <tt>scope</tt> - (previously +named_scope+) return relations and can be chained together with the other relation methods.
+* <tt>with_scope</tt> - and +with_exclusive_scope+ now also return relations and so can be chained.
+* <tt>default_scope</tt> - also works with relations.
+
+More Information:
+* "Active Record Query Interface":http://m.onkey.org/2010/1/22/active-record-query-interface
+* "Let your SQL Growl in Rails 3":http://hasmanyquestions.wordpress.com/2010/01/17/let-your-sql-growl-in-rails-3/
+
+h4. Patches and Deprecations
+
+Additionally, many fixes in the Active Record branch:
+
+* SQLite 2 support has been dropped in favour of SQLite 3.
+* MySQL support for column order.
+* PostgreSQL adapter has had it's +TIME ZONE+ support fixed so it no longer inserts incorrect values.
+* PostgreSQL support for the XML data type column.
+* +table_name+ is now cached.
+
+As well as the following deprecations:
+
+* +named_scope+ in an Active Record class is deprecated and has been renamed to just +scope+.
+* In +scope+ methods, you should move to using the relation methods, instead of a <tt>:conditions => {}</tt> finder method, for example <tt>scope :since, lambda {|time| where("created_at > ?", time) }</tt>.
+* <tt>save(false)</tt> is deprecated, in favour of <tt>save(:validate => false)</tt>.
+* I18n error messages for ActiveRecord should be changed from :en.activerecord.errors to <tt>:en.errors</tt>.
+* <tt>model.errors.on</tt> is deprecated in favour of <tt>model.errors[]</tt>
+* validates_presence_of => validates... :presence => true
+* <tt>ActiveRecord::Base.colorize_logging</tt> and <tt>config.active_record.colorize_logging</tt> are deprecated in favour of <tt>Rails::Subscriber.colorize_logging</tt> or <tt>config.colorize_logging</tt>
+
+h3. Active Resource
+
+Active Resource was also extracted out to Active Model allowing you to use Active Resource objects with Action Pack seamlessly.
+
+* Added validations through Active Model.
+* Added observing hooks.
+* HTTP proxy support.
+* Added support for digest authentication.
+* Moved model naming into Active Model.
+* Changed Active Resource attributes to a Hash with indifferent access.
+* Added +first+, +last+ and +all+ aliases for equivalent find scopes.
+* <tt>find_every</tt> now does not return a +ResourceNotFound+ error if nothing returned.
+* Added <tt>save!</tt> which raises <tt>ResourceInvalid</tt> unless the object is <tt>valid?</tt>.
+* <tt>update_attribute</tt> and <tt>update_attributes</tt> added to Active Resource models.
+* Added <tt>exists?</tt>.
+* Renamed <tt>SchemaDefinition</tt> to <tt>Schema</tt> and <tt>define_schema</tt> to <tt>schema</tt>.
+* Use the <tt>format</tt> of Active Resources rather than the <tt>content-type</tt> of remote errors to load errors.
+* Use <tt>instance_eval</tt> for schema block.
+* Fix <tt>ActiveResource::ConnectionError#to_s</tt> when +@response+ does not respond to #code or #message, handles Ruby 1.9 compat.
+* Add support for errors in JSON format.
+* Ensure <tt>load</tt> works with numeric arrays.
+* Recognises a 410 response from remote resource as the resource has been deleted.
+* Add ability to set SSL options on Active Resource connections.
+* Setting connection timeout also affects +Net::HTTP+ <tt>open_timeout</tt>.
+
+Deprecations:
+
+* <tt>save(false)</tt> is deprecated, in favour of <tt>save(:validate => false)</tt>.
+* Ruby 1.9.2: <tt>URI.parse</tt> and <tt>.decode</tt> are deprecated and are no longer used in the library.
+
+h3. Active Support
+
+A large effort was made in Active Support to make it cherry pickable, that is, you no longer have to require the entire Active Support library to get pieces of it. This allows the various core components of Rails to run slimmer.
+
+These are the main changes in Active Support:
+
+* Large clean up of the library removing unused methods throughout.
+* Active Support no longer provides vendored versions of "TZInfo":http://tzinfo.rubyforge.org/, "Memcache Client":http://deveiate.org/projects/RMemCache/ and "Builder":http://builder.rubyforge.org/, these are all included as dependencies and installed via the <tt>gem bundle</tt> command.
+* Safe buffers are implemented in <tt>ActiveSupport::SafeBuffer</tt>.
+* Added <tt>Array.uniq_by</tt> and <tt>Array.uniq_by!</tt>.
+* Fixed bug on +TimeZone.seconds_to_utc_offset+ returning wrong value.
+* Added <tt>ActiveSupport::Notifications</tt> middleware.
+* <tt>ActiveSupport.use_standard_json_time_format</tt> now defaults to true.
+* <tt>ActiveSupport.escape_html_entities_in_json</tt> now defaults to false.
+* <tt>Integer#multiple_of?</tt> accepts zero as an argument, returns false unless the receiver is zero.
+* +string.chars+ has been renamed to +string.mb_chars+.
+* +ActiveSupport::OrderedHash+ now can de-serialize through YAML.
+* Added SAX-based parser for XmlMini, using LibXML and Nokogiri.
+* Added <tt>Object#presence</tt> that returns the object if it's <tt>#present?</tt> otherwise returns +nil+.
+* Added <tt>String#exclude?</tt> core extension that returns the inverse of <tt>#include?</tt>.
+* Added <tt>to_i</tt> to +DateTime+ in +ActiveSupport+ so <tt>to_yaml</tt> works correctly on models with +DateTime+ attributes.
+* Added <tt>Enumerable#exclude?</tt> to bring parity to <tt>Enumerable#include?</tt> and avoid if <tt>!x.include?</tt>.
+* Switch to on-by-default XSS escaping for rails.
+* Support deep-merging in +ActiveSupport::HashWithIndifferentAccess+.
+* <tt>Enumerable#sum</tt> now works will all enumerables, even if they don't respond to <tt>:size</tt>.
+* <tt>inspect</tt> on a zero length duration returns '0 seconds' instead of empty string.
+* Add <tt>element</tt> and <tt>collection</tt> to <tt>ModelName</tt>.
+* <tt>String#to_time</tt> and <tt>String#to_datetime</tt> handle fractional seconds.
+* Added support to new callbacks for around filter object that respond to <tt>:before</tt> and <tt>:after</tt> used in before and after callbacks.
+* The <tt>ActiveSupport::OrderedHash#to_a</tt> method returns an ordered set of arrays. Matches Ruby 1.9's <tt>Hash#to_a</tt>.
+* <tt>MissingSourceFile</tt> exists as a constant but it is now just equals to <tt>LoadError</tt>
+* Added <tt>Class#class_attribute</tt>, to be able to declare a class-level attribute whose value is inheritable and overwritable by subclasses.
+* Finally removed +DeprecatedCallbacks+ in <tt>ActiveRecord::Associations</tt>.
+
+The following methods have been removed because they are now available in Ruby 1.8.7 and 1.9.
+
+* <tt>Integer#even?</tt> and <tt>Integer#odd?</tt>
+* <tt>String#each_char</tt>
+* <tt>String#start_with?</tt> and <tt>String#end_with?</tt> (plural aliases still kept)
+* <tt>String#bytesize</tt>
+* <tt>Object#tap</tt>
+* <tt>Symbol#to_proc</tt>
+* <tt>Object#instance_variable_defined?</tt>
+* <tt>Enumerable#none?</tt>
+
+The security patch for REXML remains in Active Support because early patchlevels of Ruby 1.8.7 still need it. Active Support knows whether it has to apply it or not.
+
+The following methods have been removed because they are no longer used in the framework:
+
+* <tt>Class#subclasses</tt>, <tt>Class#reachable?</tt>, <tt>Class#remove_class</tt>
+* <tt>Object#remove_subclasses_of</tt>, <tt>Object#subclasses_of</tt>, <tt>Object#extend_with_included_modules_from</tt>, <tt>Object#extended_by</tt>
+* <tt>Regexp#number_of_captures</tt>
+* <tt>Regexp.unoptionalize</tt>, <tt>Regexp.optionalize</tt>, <tt>Regexp#number_of_captures</tt>
+
+
+h3. Action Mailer
+
+Action Mailer has been given a new API with TMail being replaced out with the new "Mail":http://github.com/mikel/mail as the Email library. Action Mailer itself has been given an almost complete re-write with pretty much every line of code touched. The result is that Action Mailer now simply inherits from Abstract Controller and wraps the Mail gem in a Rails DSL. This reduces the amount of code and duplication of other libraries in Action Mailer considerably.
+
+* All mailers are now in <tt>app/mailers</tt> by default.
+* Can now send email using new API with three methods: +attachments+, +headers+ and +mail+.
+* ActionMailer emailing methods now return Mail::Message objects, which can then be sent the +deliver+ message to send itself.
+* All delivery methods are now abstracted out to the Mail gem.
+* The mail delivery method can accept a hash of all valid mail header fields with their value pair.
+* The mail delivery method acts in a similar way to Action Controller's respond_to block, and you can explicitly or implicitly render templates. Action Mailer will turn the email into a multipart email as needed.
+* You can pass a proc to the <tt>format.mime_type</tt> calls within the mail block and explicitly render specific types of text, or add layouts or different templates. The +render+ call inside the proc is from Abstract Controller, so all the same options are available as they are in Action Controller.
+* What were mailer unit tests have been moved to functional tests.
+
+Deprecations:
+
+* <tt>:charset</tt>, <tt>:content_type</tt>, <tt>:mime_version</tt>, <tt>:implicit_parts_order</tt> are all deprecated in favour of <tt>ActionMailer.default :key => value</tt> style declarations.
+* Mailer dynamic <tt>create_method_name</tt> and <tt>deliver_method_name</tt> are deprecated, just call <tt>method_name</tt> which now returns a <tt>Mail::Message</tt> object.
+* <tt>ActionMailer.deliver(message)</tt> is deprecated, just call <tt>message.deliver</tt>.
+* <tt>template_root</tt> is deprecated, pass options to a render call inside a proc from the <tt>format.mime_type</tt> method inside the <tt>mail</tt> generation block
+* The body method to define instance variables is deprecated (<tt>body {:ivar => value}</tt>), just declare instance variables in the method directly and they will be available in the view.
+* Mailers being in <tt>app/models</tt> is deprecated, use <tt>app/mailers</tt> instead.
+
+More Information:
+* "New Action Mailer API in Rails 3":http://lindsaar.net/2010/1/26/new-actionmailer-api-in-rails-3
+* "New Mail Gem for Ruby":http://lindsaar.net/2010/1/23/mail-gem-version-2-released
+
+h3. Credits
+
+See the "full list of contributors to Rails":http://contributors.rubyonrails.org/, many people have spent many hours making Rails 3 what it is. Kudos to all of them.
+
+Rails 3.0 Release Notes were compiled by "Mikel Lindsaar":http://lindsaar.net, who can be found "feeding":http://feeds.feedburner.com/lindsaar-net and "tweeting":http://twitter.com/raasdnil. \ No newline at end of file
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 931ebe8a34..2405b8f28c 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -1,12 +1,14 @@
h2. Action Mailer Basics
-This guide should provide you with all you need to get started in sending and receiving emails from/to your application, and many internals of Action Mailer. It also covers how to test your mailers.
+This guide should provide you with all you need to get started in sending and receiving emails from and to your application, and many internals of Action Mailer. It also covers how to test your mailers.
endprologue.
+WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in other versions of Rails.
+
h3. Introduction
-Action Mailer allows you to send emails from your application using a mailer model and views. So, in Rails, emails are used by creating models that inherit from +ActionMailer::Base+ that live alongside other models in +app/models+. Those models have associated views that appear alongside controller views in +app/views+.
+Action Mailer allows you to send emails from your application using a mailer model and views. So, in Rails, emails are used by creating mailers that inherit from +ActionMailer::Base+ and live in +app/mailers+. Those mailers have associated views that appear alongside controller views in +app/views+.
h3. Sending Emails
@@ -18,22 +20,22 @@ h5. Create the Mailer
<shell>
./script/generate mailer UserMailer
-exists app/models/
-create app/views/user_mailer
-exists test/unit/
-create test/fixtures/user_mailer
-create app/models/user_mailer.rb
-create test/unit/user_mailer_test.rb
+create app/mailers/user_mailer.rb
+invoke erb
+create app/views/user_mailer
+invoke test_unit
+create test/functional/user_mailer_test.rb
</shell>
-So we got the model, the fixtures, and the tests.
+So we got the mailer, the fixtures, and the tests.
-h5. Edit the Model
+h5. Edit the Mailer
-+app/models/user_mailer.rb+ contains an empty mailer:
++app/mailers/user_mailer.rb+ contains an empty mailer:
<ruby>
class UserMailer < ActionMailer::Base
+ default :from => "from@example.com"
end
</ruby>
@@ -41,268 +43,297 @@ Let's add a method called +welcome_email+, that will send an email to the user's
<ruby>
class UserMailer < ActionMailer::Base
+ default :from => "notifications@example.com"
+
def welcome_email(user)
- recipients user.email
- from "My Awesome Site Notifications <notifications@example.com>"
- subject "Welcome to My Awesome Site"
- sent_on Time.now
- body( {:user => user, :url => "http://example.com/login"})
+ @user = user
+ @url = "http://example.com/login"
+ mail(:to => user.email,
+ :subject => "Welcome to My Awesome Site")
end
+
end
</ruby>
-Here is a quick explanation of the options presented in the preceding method. For a full list of all available options, please have a look further down at the Complete List of ActionMailer user-settable attributes section.
+Here is a quick explanation of the items presented in the preceding method. For a full list of all available options, please have a look further down at the Complete List of ActionMailer user-settable attributes section.
-|recipients| The recipients of the email. It can be a string or, if there are multiple recipients, an array of strings|
-|from| The from address of the email|
-|subject| The subject of the email|
-|sent_on| The timestamp for the email|
+* <tt>default Hash</tt> - This is a hash of default values for any email you send, in this case we are setting the <tt>:from</tt> header to a value for all messages in this class, this can be overridden on a per email basis
+* +mail+ - The actual email message, we are passing the <tt>:to</tt> and <tt>:subject</tt> headers in|
-The keys of the hash passed to +body+ become instance variables in the view. Thus, in our example the mailer view will have a +@user+ and a +@url+ instance variables available.
+And instance variables we define in the method become available for use in the view.
h5. Create a Mailer View
-Create a file called +welcome_email.text.html.erb+ in +app/views/user_mailer/+. This will be the template used for the email, formatted in HTML:
+Create a file called +welcome_email.html.erb+ in +app/views/user_mailer/+. This will be the template used for the email, formatted in HTML:
<erb>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
</head>
<body>
- <h1>Welcome to example.com, <%=h @user.first_name %></h1>
+ <h1>Welcome to example.com, <%= @user.name %></h1>
<p>
- You have successfully signed up to example.com, and your username is: <%= @user.login %>.<br/>
- To login to the site, just follow this link: <%=h @url %>.
+ You have successfully signed up to example.com,
+ your username is: <%= @user.login %>.<br/>
+ </p>
+ <p>
+ To login to the site, just follow this link: <%= @url %>.
</p>
<p>Thanks for joining and have a great day!</p>
</body>
</html>
</erb>
-Had we wanted to send text-only emails, the file would have been called +welcome_email.text.plain.erb+. Rails sets the content type of the email to be the one in the filename.
+It is also a good idea to make a text part for this email, to do this, create a file called +welcome_email.text.erb+ in +app/views/user_mailer/+:
+
+<erb>
+Welcome to example.com, <%= @user.name %>
+===============================================
+
+You have successfully signed up to example.com,
+your username is: <%= @user.login %>.
+
+To login to the site, just follow this link: <%= @url %>.
+
+Thanks for joining and have a great day!
+</erb>
+
+When you call the +mail+ method now, Action Mailer will detect the two templates (text and HTML) and automatically generate a <tt>multipart/alternative</tt> email.
h5. Wire It Up So That the System Sends the Email When a User Signs Up
There are three ways to achieve this. One is to send the email from the controller that sends the email, another is to put it in a +before_create+ callback in the user model, and the last one is to use an observer on the user model. Whether you use the second or third methods is up to you, but staying away from the first is recommended. Not because it's wrong, but because it keeps your controller clean, and keeps all logic related to the user model within the user model. This way, whichever way a user is created (from a web form, or from an API call, for example), we are guaranteed that the email will be sent.
-Let's see how we would go about wiring it up using an observer:
+Let's see how we would go about wiring it up using an observer.
-In +config/environment.rb+:
+First off, we need to create a simple +User+ scaffold:
+
+<shell>
+$ script/generate scaffold user name:string email:string login:string
+$ rake db:migrate
+</shell>
+
+Now that we have a user model to play with, edit +config/application.rb+ and register the observer:
<ruby>
-Rails::Initializer.run do |config|
- # ...
- config.active_record.observers = :user_observer
+module MailerGuideCode
+ class Application < Rails::Application
+ # ...
+ config.active_record.observers = :user_observer
+ end
end
</ruby>
-You can place the observer in +app/models+ where it will be loaded automatically by Rails.
+You can make a +app/observers+ directory and Rails will automatically load it for you (Rails will automatically load anything in the +app+ directory as of version 3.0)
-Now create a file called +user_observer.rb+ in +app/models+ depending on where you stored it, and make it look like:
+Now create a file called +user_observer.rb+ in +app/observers+ and make it look like:
<ruby>
class UserObserver < ActiveRecord::Observer
def after_create(user)
- UserMailer.deliver_welcome_email(user)
+ UserMailer.welcome_email(user).deliver
end
end
</ruby>
-Notice how we call +deliver_welcome_email+? In Action Mailer we send emails by calling +deliver_&lt;method_name&gt;+. In UserMailer, we defined a method called +welcome_email+, and so we deliver the email by calling +deliver_welcome_email+. The next section will go through how Action Mailer achieves this.
+Notice how we call <tt>UserMailer.welcome_email(user)</tt>? Even though in the <tt>user_mailer.rb</tt> file we defined an instance method, we are calling the method_name +welcome_email(user)+ on the class. This is a peculiarity of Action Mailer.
+
+NOTE: In previous versions of Rails, you would call +deliver_welcome_email+ or +create_welcome_email+ however in Rails 3.0 this has been deprecated in favour of just calling the method name itself.
+
+The method +welcome_email+ returns a Mail::Message object which can then just be told +deliver+ to send itself out.
-h4. Action Mailer and Dynamic +deliver_&lt;method_name&gt;+ methods
-So how does Action Mailer understand this +deliver_welcome_email+ call? If you read the documentation (http://api.rubyonrails.org/files/vendor/rails/actionmailer/README.html), you will find this in the "Sending Emails" section:
+h4. Complete List of Action Mailer Methods
-You never instantiate your mailer class. Rather, your delivery instance methods are automatically wrapped in class methods that start with the word +deliver_+ followed by the name of the mailer method that you would like to deliver.
+There are just three methods that you need to send pretty much any email message:
-So, how exactly does this work?
+* <tt>headers</tt> - Specifies any header on the email you want, you can pass a hash of header field names and value pairs, or you can call <tt>headers[:field_name] = 'value'</tt>
+* <tt>attachments</tt> - Allows you to add attachments to your email, for example <tt>attachments['file-name.jpg'] = File.read('file-name.jpg')</tt>
+* <tt>mail</tt> - Sends the actual email itself. You can pass in headers as a hash to the mail method as a parameter, mail will then create an email, either plain text, or multipart, depending on what email templates you have defined.
-Looking at the +ActionMailer::Base+ source, you will find this:
+h5. Custom Headers
+
+Defining custom headers are simple, you can do it one of three ways:
+
+* Defining a header field as a parameter to the +mail+ method:
<ruby>
-def method_missing(method_symbol, *parameters)#:nodoc:
- case method_symbol.id2name
- when /^create_([_a-z]\w*)/ then new($1, *parameters).mail
- when /^deliver_([_a-z]\w*)/ then new($1, *parameters).deliver!
- when "new" then nil
- else super
- end
-end
+mail(:x_spam => value)
+</ruby>
+
+* Passing in a key value assignment to the +headers+ method:
+
+<ruby>
+headers[:x_spam] = value
</ruby>
-Hence, if the method name starts with +deliver_+ followed by any combination of lowercase letters or underscore, +method_missing+ calls +new+ on your mailer class (+UserMailer+ in our example above), sending the combination of lower case letters or underscore, along with the parameters. The resulting object is then sent the +deliver!+ method, which well... delivers it.
-
-h4. Complete List of Action Mailer User-Settable Attributes
-
-|bcc| The BCC addresses of the email, either as a string (for a single address) or an array of strings (for multiple addresses)|
-|body| The body of the email. 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 body of the message|
-|cc| The CC addresses for the email, either as a string (for a single address) or an array of strings (for multiple addresses)|
-|charset| The charset to use for the email. This defaults to the +default_charset+ specified for ActionMailer::Base.|
-|content_type| The content type for the email. This defaults to "text/plain" but the filename may specify it|
-|from| The from address of the email|
-|reply_to| The address (if different than the "from" address) to direct replies to this email|
-|headers| Additional headers to be added to the email|
-|implicit_parts_order| The order in which parts should be sorted, based on the content type. This defaults to the value of +default_implicit_parts_order+|
-|mime_version| Defaults to "1.0", but may be explicitly given if needed|
-|recipient| The recipient addresses of the email, either as a string (for a single address) or an array of strings (for multiple addresses)|
-|sent_on| The timestamp on which the message was sent. If unset, the header will be set by the delivery agent|
-|subject| The subject of the email|
-|template| The template to use. This is the "base" template name, without the extension or directory, and may be used to have multiple mailer methods share the same template|
+* Passing a hash of key value pairs to the +headers+ method:
+
+<ruby>
+headers {:x_spam => value, :x_special => another_value}
+</ruby>
+
+h5. Adding Attachments
+
+Adding attachments has been simplified in Action Mailer 3.0.
+
+* Pass the file name and content and Action Mailer and the Mail gem will automatically guess the mime_type, set the encoding and create the attachment.
+
+<ruby>
+attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
+</ruby>
+
+NOTE: Mail will automatically Base64 encode an attachment, if you want something different, pre encode your content and pass in the encoded content and encoding in a +Hash+ to the +attachments+ method.
+
+* Pass the file name and specify headers and content and Action Mailer and Mail will use the settings you pass in.
+
+<ruby>
+encoded_content = SpecialEncode(File.read('/path/to/filename.jpg'))
+attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
+ :encoding => 'SpecialEncoding',
+ :content => encoded_content }
+</ruby>
+
+NOTE: If you specify an encoding, Mail will assume that your content is already encoded and not try to Base64 encode it.
h4. Mailer Views
-Mailer views are located in the +app/views/name_of_mailer_class+ directory. The specific mailer view is known to the class because it's name is the same as the mailer method. So for example, in our example from above, our mailer view for the +welcome_email+ method will be in +app/views/user_mailer/welcome_email.text.html.erb+ for the HTML version and +welcome_email.text.plain.erb+ for the plain text version.
+Mailer views are located in the +app/views/name_of_mailer_class+ directory. The specific mailer view is known to the class because it's name is the same as the mailer method. So for example, in our example from above, our mailer view for the +welcome_email+ method will be in +app/views/user_mailer/welcome_email.html.erb+ for the HTML version and +welcome_email.text.erb+ for the plain text version.
To change the default mailer view for your action you do something like:
<ruby>
class UserMailer < ActionMailer::Base
+ default :from => "notifications@example.com"
+
def welcome_email(user)
- recipients user.email
- from "My Awesome Site Notifications<notifications@example.com>"
- subject "Welcome to My Awesome Site"
- sent_on Time.now
- body( {:user => user, :url => "http://example.com/login"})
- content_type "text/html"
- # use some_other_template.text.(html|plain).erb instead
- template "some_other_template"
+ @user = user
+ @url = "http://example.com/login"
+ mail(:to => user.email,
+ :subject => "Welcome to My Awesome Site") do |format|
+ format.html { render 'another_template' }
+ format.text { render 'another_template' }
+ end
end
+
end
</ruby>
+Will render 'another_template.text.erb' and 'another_template.html.erb'. The render command is the same one used inside of Action Controller, so you can use all the same options, such as <tt>:text</tt> etc.
+
h4. Action Mailer Layouts
-Just like controller views, you can also have mailer layouts. The layout name needs to end in "_mailer" to be automatically recognized by your mailer as a layout. So in our UserMailer example, we need to call our layout +user_mailer.text.(html|plain).erb+. In order to use a different file just use:
+Just like controller views, you can also have mailer layouts. The layout name needs to be the same as your mailer, such as +user_mailer.html.erb+ and +user_mailer.text.erb+ to be automatically recognized by your mailer as a layout.
+
+In order to use a different file just use:
<ruby>
class UserMailer < ActionMailer::Base
- layout 'awesome' # use awesome.text.(html|plain).erb as the layout
+ layout 'awesome' # use awesome.(html|text).erb as the layout
end
</ruby>
Just like with controller views, use +yield+ to render the view inside the layout.
+You can also pass in a <tt>:layout => 'layout_name'</tt> option to the render call inside the format block to specify different layouts for different actions:
+
+<ruby>
+class UserMailer < ActionMailer::Base
+ def welcome_email(user)
+ mail(:to => user.email) do |format|
+ format.html { render :layout => 'my_layout' }
+ format.text
+ end
+ end
+end
+</ruby>
+
+Will render the HTML part using the <tt>my_layout.html.erb</tt> file and the text part with the usual <tt>user_mailer.text.erb</tt> file if it exists.
+
h4. Generating URLs in Action Mailer Views
URLs can be generated in mailer views using +url_for+ or named routes.
+
Unlike controllers, the mailer instance doesn't have any context about the incoming request so you'll need to provide the +:host+, +:controller+, and +:action+:
<erb>
-<%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
+<%= url_for(:host => "example.com",
+ :controller => "welcome",
+ :action => "greeting") %>
</erb>
When using named routes you only need to supply the +:host+:
<erb>
-<%= users_url(:host => "example.com") %>
+<%= user_url(@user, :host => "example.com") %>
</erb>
Email clients have no web context and so paths have no base URL to form complete web addresses. Thus, when using named routes only the "_url" variant makes sense.
-It is also possible to set a default host that will be used in all mailers by setting the +:host+ option in
-the +ActionMailer::Base.default_url_options+ hash as follows:
-
-<erb>
-ActionMailer::Base.default_url_options[:host] = "example.com"
-</erb>
-
-This can also be set as a configuration option in +config/environment.rb+:
-
-<erb>
-config.action_mailer.default_url_options = { :host => "example.com" }
-</erb>
-
-If you set a default +:host+ for your mailers you need to pass +:only_path => false+ to +url_for+. Otherwise it doesn't get included.
-
-h4. Sending Multipart Emails
-
-Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have +welcome_email.text.plain.erb+ and +welcome_email.text.html.erb+ in +app/views/user_mailer+, Action Mailer will automatically send a multipart email with the HTML and text versions setup as different parts.
-
-To explicitly specify multipart messages, you can do something like:
+It is also possible to set a default host that will be used in all mailers by setting the +:host+ option in the +ActionMailer::Base.default_url_options+ hash as follows:
<ruby>
class UserMailer < ActionMailer::Base
- def welcome_email(user)
- recipients user.email_address
- subject "New account information"
- from "system@example.com"
- content_type "multipart/alternative"
-
- part :content_type => "text/html",
- :body => "<p>html content, can also be the name of an action that you call<p>"
+ default_url_options[:host] = "example.com"
- part "text/plain" do |p|
- p.body = "text content, can also be the name of an action that you call"
- end
+ def welcome_email(user)
+ @user = user
+ @url = user_url(@user)
+ mail(:to => user.email,
+ :subject => "Welcome to My Awesome Site")
end
end
</ruby>
-h4. Sending Emails with Attachments
+h4. Sending Multipart Emails
-Attachments can be added by using the +attachment+ method:
+Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have +welcome_email.text.erb+ and +welcome_email.html.erb+ in +app/views/user_mailer+, Action Mailer will automatically send a multipart email with the HTML and text versions setup as different parts.
+
+The order of the parts getting inserted is determined by the <tt>:parts_order</tt> inside of the <tt>ActionMailer::Base.default</tt> method. If you want to explicitly alter the order, you can either change the <tt>:parts_order</tt> or explicitly render the parts in a different order:
<ruby>
class UserMailer < ActionMailer::Base
def welcome_email(user)
- recipients user.email_address
- subject "New account information"
- from "system@example.com"
- content_type "multipart/alternative"
-
- attachment :content_type => "image/jpeg",
- :body => File.read("an-image.jpg")
-
- attachment "application/pdf" do |a|
- a.body = generate_your_pdf_here()
+ @user = user
+ @url = user_url(@user)
+ mail(:to => user.email,
+ :subject => "Welcome to My Awesome Site") do |format|
+ format.html
+ format.text
end
end
end
</ruby>
-h4. Sending Multipart Emails with Attachments
+Will put the HTML part first, and the plain text part second.
-Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename, nor will it properly order the alternative parts. You must declare which template you are using for each content type via the +part+ method. And you must declare these templates in the proper order.
+h4. Sending Emails with Attachments
-In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder. The +text/plain+ part must be listed first for full compatibility with email clients. If +text/plain+ is listed after +text/html+, some clients may display both the HTML and plain text versions of the email. The text alternatives alone must be enclosed in a +multipart/alternative+ part. Do not set the entire message's +content_type+ to +multipart/alternative+ or some email clients may ignore the display of attachments such as PDF's.
+Attachments can be added by using the +attachment+ method:
<ruby>
class UserMailer < ActionMailer::Base
def welcome_email(user)
- recipients user.email_address
- subject "New account information"
- from "system@example.com"
-
- part "multipart/alternative" do |pt|
- pt.part "text/plain" do |p|
- p.body = render_message("welcome_email_plain", :message => "text content")
- end
-
- pt.part "text/html" do |p|
- p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>")
- end
- end
-
- attachment :content_type => "image/jpeg",
- :body => File.read("an-image.jpg")
-
- attachment "application/pdf" do |a|
- a.body = generate_your_pdf_here()
- end
+ @user = user
+ @url = user_url(@user)
+ attachments['terms.pdf'] = File.read('/path/terms.pdf')
+ mail(:to => user.email,
+ :subject => "Please see the Terms and Conditions attached")
end
end
</ruby>
+The above will send a multipart email with an attachment, properly nested with the top level being <tt>mixed/multipart</tt> and the first part being a <tt>mixed/alternative</tt> containing the plain text and HTML email messages.
+
h3. Receiving Emails
Receiving and parsing emails with Action Mailer can be a rather complex endeavour. Before your email reaches your Rails app, you would have had to configure your system to somehow forward emails to your app, which needs to be listening for that. So, to receive emails in your Rails app you'll need:
-1. Implement a +receive+ method in your mailer.
+* Implement a +receive+ method in your mailer.
-2. Configure your email server to forward emails from the address(es) you would like your app to receive to +/path/to/app/script/runner 'UserMailer.receive(STDIN.read)'+.
+* Configure your email server to forward emails from the address(es) you would like your app to receive to +/path/to/app/script/runner 'UserMailer.receive(STDIN.read)'+.
Once a method called +receive+ is defined in any mailer, Action Mailer will parse the raw incoming email into an email object, decode it, instantiate a new mailer, and pass the email object to the mailer +receive+ instance method. Here's an example:
@@ -329,12 +360,7 @@ end
h3. Using Action Mailer Helpers
-Action Mailer classes have 4 helper methods available to them:
-
-|add_template_helper(helper_module)|Makes all the (instance) methods in the helper module available to templates rendered through this controller.|
-|helper(*args, &block)| Declare a helper: helper :foo requires 'foo_helper' and includes FooHelper in the template class. helper FooHelper includes FooHelper in the template class. helper { def foo() "#{bar} is the very best" end } evaluates the block in the template class, adding method foo. helper(:three, BlindHelper) { def mice() 'mice' end } does all three. |
-|helper_method| Declare a controller method as a helper. For example, helper_method :link_to def link_to(name, options) ... end makes the link_to controller method available in the view.|
-|helper_attr| Declare a controller attribute as a helper. For example, helper_attr :name attr_accessor :name makes the name and name= controller methods available in the view. The is a convenience wrapper for helper_method.|
+Action Mailer now just inherits from Abstract Controller, so you have access to the same generic helpers as you do in Action Controller.
h3. Action Mailer Configuration
@@ -348,50 +374,36 @@ The following configuration options are best made in one of the environment file
|delivery_method|Defines a delivery method. Possible values are :smtp (default), :sendmail, and :test.|
|perform_deliveries|Determines whether deliver_* methods are actually carried out. By default they are, but this can be turned off to help functional testing.|
|deliveries|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
-|default_charset|The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also pick a different charset from inside a method with charset.|
-|default_content_type|The default content type used for the main part of the message. Defaults to "text/plain". You can also pick a different content type from inside a method with content_type.|
-|default_mime_version|The default mime version used for the message. Defaults to 1.0. You can also pick a different value from inside a method with mime_version.|
-|default_implicit_parts_order|When a message is built implicitly (i.e. multiple parts are assembled from templates which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to ["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client and appear last in the mime encoded message. You can also pick a different order from inside a method with implicit_parts_order.|
-
h4. Example Action Mailer Configuration
-An example would be:
+An example would be adding the following to your appropriate <tt>config/environments/env.rb</tt> file:
<ruby>
-ActionMailer::Base.delivery_method = :sendmail
-ActionMailer::Base.sendmail_settings = {
- :location => '/usr/sbin/sendmail',
- :arguments => '-i -t'
-}
-ActionMailer::Base.perform_deliveries = true
-ActionMailer::Base.raise_delivery_errors = true
-ActionMailer::Base.default_charset = "iso-8859-1"
+config.action_mailer.delivery_method = :sendmail
+# Defaults to:
+# config.action_mailer.sendmail_settings = {
+# :location => '/usr/sbin/sendmail',
+# :arguments => '-i -t'
+# }
+config.action_mailer.perform_deliveries = true
+config.action_mailer.raise_delivery_errors = true
</ruby>
h4. Action Mailer Configuration for GMail
-Instructions copied from "this blog entry":http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html by Robert Zotter.
-
-First you must install the "action_mailer_tls":http://github.com/openrain/action_mailer_tls plugin, then all you have to do is configure Action Mailer:
-
-<ruby>
-ActionMailer::Base.smtp_settings = {
- :address => "smtp.gmail.com",
- :port => 587,
- :domain => "domain.com",
- :user_name => "user@domain.com",
- :password => "password",
- :authentication => :plain
-}
-</ruby>
-
-h4. Configure Action Mailer to Recognize HAML Templates
-
-In +config/environment.rb+, add the following line:
+As Action Mailer now uses the Mail gem, this becomes as simple as adding to your <tt>config/environments/env.rb</tt> file:
<ruby>
-ActionMailer::Base.register_template_extension('haml')
+config.action_mailer.delivery_method = :smtp
+config.action_mailer.smtp_settings = {
+ :address => "smtp.gmail.com",
+ :port => 587,
+ :domain => 'baci.lindsaar.net',
+ :user_name => '<username>',
+ :password => '<password>',
+ :authentication => 'plain',
+ :enable_starttls_auto => true }
</ruby>
h3. Mailer Testing
@@ -412,7 +424,8 @@ class UserMailerTest < ActionMailer::TestCase
# Test the body of the sent email contains what we expect it to
assert_equal [user.email], email.to
assert_equal "Welcome to My Awesome Site", email.subject
- assert_match /Welcome to example.com, #{user.first_name}/, email.body
+ assert_match /<h1>Welcome to example.com, #{user.name}<\/h1>/, email.encoded
+ assert_match /Welcome to example.com, #{user.name}/, email.encoded
end
end
</ruby>
diff --git a/railties/lib/rails/dispatcher.rb b/railties/lib/rails/dispatcher.rb
index 5d383eacd1..75ee4de140 100644
--- a/railties/lib/rails/dispatcher.rb
+++ b/railties/lib/rails/dispatcher.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2009 David Heinemeier Hansson
+# Copyright (c) 2004-2010 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the