提交 d71b0935 编写于 作者: V Vijay Dev

Merge branch 'master' of github.com:lifo/docrails

...@@ -151,9 +151,9 @@ module ActionMailer ...@@ -151,9 +151,9 @@ module ActionMailer
# #
# For example, if the following templates exist: # For example, if the following templates exist:
# * signup_notification.text.erb # * signup_notification.text.erb
# * signup_notification.text.html.erb # * signup_notification.html.erb
# * signup_notification.text.xml.builder # * signup_notification.xml.builder
# * signup_notification.text.yaml.erb # * signup_notification.yaml.erb
# #
# Each would be rendered and added as a separate part to the message, with the corresponding content # Each would be rendered and added as a separate part to the message, with the corresponding content
# type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>, # type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>,
...@@ -175,7 +175,7 @@ module ActionMailer ...@@ -175,7 +175,7 @@ module ActionMailer
# end # end
# end # end
# #
# Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.text.html.erb</tt> # Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.html.erb</tt>
# template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts, # template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
# the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside, # the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
# and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book # and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
...@@ -720,6 +720,15 @@ def mail(headers = {}, &block) ...@@ -720,6 +720,15 @@ def mail(headers = {}, &block)
protected protected
# Used by #mail to set the content type of the message.
#
# It will use the given +user_content_type+, or multipart if the mail
# message has any attachments. If the attachments are inline, the content
# type will be "multipart/related", otherwise "multipart/mixed".
#
# If there is no content type passed in via headers, and there are no
# attachments, or the message is multipart, then the default content type is
# used.
def set_content_type(m, user_content_type, class_default) def set_content_type(m, user_content_type, class_default)
params = m.content_type_parameters || {} params = m.content_type_parameters || {}
case case
......
...@@ -38,6 +38,7 @@ module DeliveryMethods ...@@ -38,6 +38,7 @@ module DeliveryMethods
add_delivery_method :test, Mail::TestMailer add_delivery_method :test, Mail::TestMailer
end end
# Helpers for creating and wrapping delivery behavior, used by DeliveryMethods.
module ClassMethods module ClassMethods
# Provides a list of emails that have been delivered by Mail::TestMailer # Provides a list of emails that have been delivered by Mail::TestMailer
delegate :deliveries, :deliveries=, to: Mail::TestMailer delegate :deliveries, :deliveries=, to: Mail::TestMailer
......
module ActionMailer module ActionMailer
# Implements the ActiveSupport::LogSubscriber for logging notifications when
# email is delivered and received.
class LogSubscriber < ActiveSupport::LogSubscriber class LogSubscriber < ActiveSupport::LogSubscriber
# An email was delivered.
def deliver(event) def deliver(event)
return unless logger.info? return unless logger.info?
recipients = Array(event.payload[:to]).join(', ') recipients = Array(event.payload[:to]).join(', ')
...@@ -7,12 +10,14 @@ def deliver(event) ...@@ -7,12 +10,14 @@ def deliver(event)
debug(event.payload[:mail]) debug(event.payload[:mail])
end end
# An email was received.
def receive(event) def receive(event)
return unless logger.info? return unless logger.info?
info("\nReceived mail (#{event.duration.round(1)}ms)") info("\nReceived mail (#{event.duration.round(1)}ms)")
debug(event.payload[:mail]) debug(event.payload[:mail])
end end
# Use the logger configured for ActionMailer::Base
def logger def logger
ActionMailer::Base.logger ActionMailer::Base.logger
end end
......
module ActionMailer module ActionMailer
# Provides helper methods for ActionMailer::Base that can be used for easily
# formatting messages, accessing mailer or message instances, and the
# attachments list.
module MailHelper module MailHelper
# Take the text and format it, indented two spaces for each line, and # Take the text and format it, indented two spaces for each line, and
# wrapped at 72 columns. # wrapped at 72 columns.
......
# This is the parent Association class which defines the variables
# used by all associations.
#
# The hierarchy is defined as follows:
# Association
# - SingularAssociation
# - BelongsToAssociation
# - HasOneAssociation
# - CollectionAssociation
# - HasManyAssociation
# - HasAndBelongsToManyAssociation
module ActiveRecord::Associations::Builder module ActiveRecord::Associations::Builder
class Association #:nodoc: class Association #:nodoc:
class << self class << self
...@@ -58,6 +70,13 @@ def valid_options ...@@ -58,6 +70,13 @@ def valid_options
def validate_options def validate_options
options.assert_valid_keys(valid_options) options.assert_valid_keys(valid_options)
end end
# Defines the setter and getter methods for the association
# class Post < ActiveRecord::Base
# has_many :comments
# end
#
# Post.first.comments and Post.first.comments= methods are defined by this method...
def define_accessors def define_accessors
define_readers define_readers
......
# This class is inherited by the has_many and has_many_and_belongs_to_many association classes
require 'active_record/associations' require 'active_record/associations'
module ActiveRecord::Associations::Builder module ActiveRecord::Associations::Builder
...@@ -66,6 +68,8 @@ def define_callback(callback_name) ...@@ -66,6 +68,8 @@ def define_callback(callback_name)
model.send("#{full_callback_name}=", Array(options[callback_name.to_sym])) model.send("#{full_callback_name}=", Array(options[callback_name.to_sym]))
end end
# Defines the setter and getter methods for the collection_singular_ids.
def define_readers def define_readers
super super
......
# This class is inherited by the has_one and belongs_to association classes
module ActiveRecord::Associations::Builder module ActiveRecord::Associations::Builder
class SingularAssociation < Association #:nodoc: class SingularAssociation < Association #:nodoc:
def valid_options def valid_options
...@@ -13,6 +15,8 @@ def define_accessors ...@@ -13,6 +15,8 @@ def define_accessors
define_constructors if constructable? define_constructors if constructable?
end end
# Defines the (build|create)_association methods for belongs_to or has_one association
def define_constructors def define_constructors
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1 mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
def build_#{name}(*args, &block) def build_#{name}(*args, &block)
......
...@@ -237,11 +237,11 @@ def delete(*records) ...@@ -237,11 +237,11 @@ def delete(*records)
end end
end end
# Destroy +records+ and remove them from this association calling # Deletes the +records+ and removes them from this association calling
# +before_remove+ and +after_remove+ callbacks. # +before_remove+ , +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
# #
# Note that this method will _always_ remove records from the database # Note that this method removes records from the database ignoring the
# ignoring the +:dependent+ option. # +:dependent+ option.
def destroy(*records) def destroy(*records)
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) } records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
delete_or_destroy(records, :destroy) delete_or_destroy(records, :destroy)
......
...@@ -422,9 +422,9 @@ def delete_all ...@@ -422,9 +422,9 @@ def delete_all
@association.delete_all @association.delete_all
end end
# Deletes the records of the collection directly from the database. # Deletes the records of the collection directly from the database
# This will _always_ remove the records ignoring the +:dependent+ # ignoring the +:dependent+ option. It invokes +before_remove+,
# option. # +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
# #
# class Person < ActiveRecord::Base # class Person < ActiveRecord::Base
# has_many :pets # has_many :pets
......
...@@ -14,7 +14,7 @@ module Querying ...@@ -14,7 +14,7 @@ module Querying
# Executes a custom SQL query against your database and returns all the results. The results will # Executes a custom SQL query against your database and returns all the results. The results will
# be returned as an array with columns requested encapsulated as attributes of the model you call # be returned as an array with columns requested encapsulated as attributes of the model you call
# this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
# a Product object with the attributes you specified in the SQL query. # a +Product+ object with the attributes you specified in the SQL query.
# #
# If you call a complicated SQL query which spans multiple tables the columns specified by the # If you call a complicated SQL query which spans multiple tables the columns specified by the
# SELECT will be attributes of the model, whether or not they are columns of the corresponding # SELECT will be attributes of the model, whether or not they are columns of the corresponding
...@@ -29,9 +29,10 @@ module Querying ...@@ -29,9 +29,10 @@ module Querying
# Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id" # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
# # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...] # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
# #
# # You can use the same string replacement techniques as you can with ActiveRecord#find # You can use the same string replacement techniques as you can with <tt>ActiveRecord::QueryMethods#where</tt>:
#
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
# # => [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...] # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
def find_by_sql(sql, binds = []) def find_by_sql(sql, binds = [])
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds) result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
column_types = {} column_types = {}
......
...@@ -12,6 +12,9 @@ class ModelGenerator < Base # :nodoc: ...@@ -12,6 +12,9 @@ class ModelGenerator < Base # :nodoc:
class_option :parent, :type => :string, :desc => "The parent class for the generated model" class_option :parent, :type => :string, :desc => "The parent class for the generated model"
class_option :indexes, :type => :boolean, :default => true, :desc => "Add indexes for references and belongs_to columns" class_option :indexes, :type => :boolean, :default => true, :desc => "Add indexes for references and belongs_to columns"
# creates the migration file for the model.
def create_migration_file def create_migration_file
return unless options[:migration] && options[:parent].nil? return unless options[:migration] && options[:parent].nil?
attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false
...@@ -39,6 +42,7 @@ def accessible_attributes ...@@ -39,6 +42,7 @@ def accessible_attributes
protected protected
# Used by the migration template to determine the parent name of the model
def parent_class_name def parent_class_name
options[:parent] || "ActiveRecord::Base" options[:parent] || "ActiveRecord::Base"
end end
......
...@@ -162,8 +162,8 @@ Person.create(name: nil).valid? # => false ...@@ -162,8 +162,8 @@ Person.create(name: nil).valid? # => false
``` ```
After Active Record has performed validations, any errors found can be accessed After Active Record has performed validations, any errors found can be accessed
through the `errors` instance method, which returns a collection of errors. By through the `errors.messages` instance method, which returns a collection of errors.
definition, an object is valid if this collection is empty after running By definition, an object is valid if this collection is empty after running
validations. validations.
Note that an object instantiated with `new` will not report errors even if it's Note that an object instantiated with `new` will not report errors even if it's
...@@ -176,17 +176,17 @@ end ...@@ -176,17 +176,17 @@ end
>> p = Person.new >> p = Person.new
#=> #<Person id: nil, name: nil> #=> #<Person id: nil, name: nil>
>> p.errors >> p.errors.messages
#=> {} #=> {}
>> p.valid? >> p.valid?
#=> false #=> false
>> p.errors >> p.errors.messages
#=> {name:["can't be blank"]} #=> {name:["can't be blank"]}
>> p = Person.create >> p = Person.create
#=> #<Person id: nil, name: nil> #=> #<Person id: nil, name: nil>
>> p.errors >> p.errors.messages
#=> {name:["can't be blank"]} #=> {name:["can't be blank"]}
>> p.save >> p.save
...@@ -993,12 +993,12 @@ end ...@@ -993,12 +993,12 @@ end
person = Person.new person = Person.new
person.valid? # => false person.valid? # => false
person.errors person.errors.messages
# => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]} # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]}
person = Person.new(name: "John Doe") person = Person.new(name: "John Doe")
person.valid? # => true person.valid? # => true
person.errors # => [] person.errors.messages # => {}
``` ```
### `errors[]` ### `errors[]`
......
...@@ -852,7 +852,7 @@ end ...@@ -852,7 +852,7 @@ end
# app/models/product.rb # app/models/product.rb
class Product < ActiveRecord::Base class Product < ActiveRecord::Base
validates :flag, :inclusion => { :in => [true, false] } validates :flag, inclusion: { in: [true, false] }
end end
``` ```
...@@ -877,7 +877,7 @@ end ...@@ -877,7 +877,7 @@ end
# app/models/product.rb # app/models/product.rb
class Product < ActiveRecord::Base class Product < ActiveRecord::Base
validates :flag, :inclusion => { :in => [true, false] } validates :flag, inclusion: { in: [true, false] }
validates :fuzz, presence: true validates :fuzz, presence: true
end end
``` ```
...@@ -1065,8 +1065,8 @@ with foreign key constraints in the database. ...@@ -1065,8 +1065,8 @@ with foreign key constraints in the database.
Although Active Record does not provide any tools for working directly with Although Active Record does not provide any tools for working directly with
such features, the `execute` method can be used to execute arbitrary SQL. You such features, the `execute` method can be used to execute arbitrary SQL. You
could also use some gem like can also use a gem like
[foreigner](https://github.com/matthuhiggins/foreigner) which add foreign key [foreigner](https://github.com/matthuhiggins/foreigner) which adds foreign key
support to Active Record (including support for dumping foreign keys in support to Active Record (including support for dumping foreign keys in
`db/schema.rb`). `db/schema.rb`).
......
...@@ -18,6 +18,8 @@ def initialize(args, *options) #:nodoc: ...@@ -18,6 +18,8 @@ def initialize(args, *options) #:nodoc:
parse_attributes! if respond_to?(:attributes) parse_attributes! if respond_to?(:attributes)
end end
# Defines the template that would be used for the migration file.
# The arguments include the source template file, the migration filename etc.
no_tasks do no_tasks do
def template(source, *args, &block) def template(source, *args, &block)
inside_template do inside_template do
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册