mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Made association extensions use simpler block syntax
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2895 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
0073a759f1
commit
c8dd66fdcd
6 changed files with 86 additions and 24 deletions
|
@ -34,14 +34,14 @@
|
||||||
* Added extension capabilities to has_many and has_and_belongs_to_many proxies [DHH]. Example:
|
* Added extension capabilities to has_many and has_and_belongs_to_many proxies [DHH]. Example:
|
||||||
|
|
||||||
class Account < ActiveRecord::Base
|
class Account < ActiveRecord::Base
|
||||||
has_many :people, :extend => Module.new {
|
has_many :people do
|
||||||
def find_or_create_by_name(name)
|
def find_or_create_by_name(name)
|
||||||
first_name, *last_name = name.split
|
first_name, *last_name = name.split
|
||||||
last_name = last_name.join " "
|
last_name = last_name.join " "
|
||||||
|
|
||||||
find_or_create_by_first_name_and_last_name(first_name, last_name)
|
find_or_create_by_first_name_and_last_name(first_name, last_name)
|
||||||
end
|
end
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
|
person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
|
||||||
|
|
|
@ -127,7 +127,7 @@ module ActiveRecord
|
||||||
# Example:
|
# Example:
|
||||||
#
|
#
|
||||||
# class Account < ActiveRecord::Base
|
# class Account < ActiveRecord::Base
|
||||||
# has_many :people, :extend => Module.new {
|
# has_many :people do
|
||||||
# def find_or_create_by_name(name)
|
# def find_or_create_by_name(name)
|
||||||
# first_name, *last_name = name.split
|
# first_name, *last_name = name.split
|
||||||
# last_name = last_name.join " "
|
# last_name = last_name.join " "
|
||||||
|
@ -135,14 +135,32 @@ module ActiveRecord
|
||||||
# find_by_first_name_and_last_name(first_name, last_name) ||
|
# find_by_first_name_and_last_name(first_name, last_name) ||
|
||||||
# create({ :first_name => first_name, :last_name => last_name })
|
# create({ :first_name => first_name, :last_name => last_name })
|
||||||
# end
|
# end
|
||||||
# }
|
# end
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
|
# person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
|
||||||
# person.first_name # => "David"
|
# person.first_name # => "David"
|
||||||
# person.last_name # => "Heinemeier Hansson"
|
# person.last_name # => "Heinemeier Hansson"
|
||||||
#
|
#
|
||||||
# Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
|
# If you need to share the same extensions between many associations, you can use a named extension module. Example:
|
||||||
|
#
|
||||||
|
# module FindOrCreateByNameExtension
|
||||||
|
# def find_or_create_by_name(name)
|
||||||
|
# first_name, *last_name = name.split
|
||||||
|
# last_name = last_name.join " "
|
||||||
|
#
|
||||||
|
# find_by_first_name_and_last_name(first_name, last_name) ||
|
||||||
|
# create({ :first_name => first_name, :last_name => last_name })
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# class Account < ActiveRecord::Base
|
||||||
|
# has_many :people, :extend => FindOrCreateByNameExtension
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# class Company < ActiveRecord::Base
|
||||||
|
# has_many :people, :extend => FindOrCreateByNameExtension
|
||||||
|
# end
|
||||||
#
|
#
|
||||||
# == Caching
|
# == Caching
|
||||||
#
|
#
|
||||||
|
@ -306,7 +324,7 @@ module ActiveRecord
|
||||||
# associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
|
# associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
|
||||||
# * <tt>:counter_sql</tt> - specify a complete SQL statement to fetch the size of the association. If +:finder_sql+ is
|
# * <tt>:counter_sql</tt> - specify a complete SQL statement to fetch the size of the association. If +:finder_sql+ is
|
||||||
# specified but +:counter_sql+, +:counter_sql+ will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM.
|
# specified but +:counter_sql+, +:counter_sql+ will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM.
|
||||||
# * <tt>:extend</tt> - anonymous module for extending the proxy, see "Association extensions".
|
# * <tt>:extend</tt> - specify a named module for extending the proxy, see "Association extensions".
|
||||||
#
|
#
|
||||||
# Option examples:
|
# Option examples:
|
||||||
# has_many :comments, :order => "posted_on"
|
# has_many :comments, :order => "posted_on"
|
||||||
|
@ -317,13 +335,15 @@ module ActiveRecord
|
||||||
# 'FROM people p, post_subscriptions ps ' +
|
# 'FROM people p, post_subscriptions ps ' +
|
||||||
# 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
|
# 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
|
||||||
# 'ORDER BY p.first_name'
|
# 'ORDER BY p.first_name'
|
||||||
def has_many(association_id, options = {})
|
def has_many(association_id, options = {}, &extension)
|
||||||
options.assert_valid_keys(
|
options.assert_valid_keys(
|
||||||
:foreign_key, :class_name, :exclusively_dependent, :dependent,
|
:foreign_key, :class_name, :exclusively_dependent, :dependent,
|
||||||
:conditions, :order, :finder_sql, :counter_sql,
|
:conditions, :order, :finder_sql, :counter_sql,
|
||||||
:before_add, :after_add, :before_remove, :after_remove, :extend
|
:before_add, :after_add, :before_remove, :after_remove, :extend
|
||||||
)
|
)
|
||||||
|
|
||||||
|
options[:extend] = create_extension_module(association_id, extension) if block_given?
|
||||||
|
|
||||||
association_name, association_class_name, association_class_primary_key_name =
|
association_name, association_class_name, association_class_primary_key_name =
|
||||||
associate_identification(association_id, options[:class_name], options[:foreign_key])
|
associate_identification(association_id, options[:class_name], options[:foreign_key])
|
||||||
|
|
||||||
|
@ -602,13 +622,15 @@ module ActiveRecord
|
||||||
# has_and_belongs_to_many :categories, :join_table => "prods_cats"
|
# has_and_belongs_to_many :categories, :join_table => "prods_cats"
|
||||||
# has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
|
# has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
|
||||||
# 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
|
# 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
|
||||||
def has_and_belongs_to_many(association_id, options = {})
|
def has_and_belongs_to_many(association_id, options = {}, &extension)
|
||||||
options.assert_valid_keys(
|
options.assert_valid_keys(
|
||||||
:class_name, :table_name, :foreign_key, :association_foreign_key, :conditions,
|
:class_name, :table_name, :foreign_key, :association_foreign_key, :conditions,
|
||||||
:join_table, :finder_sql, :delete_sql, :insert_sql, :order, :uniq, :before_add, :after_add,
|
:join_table, :finder_sql, :delete_sql, :insert_sql, :order, :uniq, :before_add, :after_add,
|
||||||
:before_remove, :after_remove, :extend
|
:before_remove, :after_remove, :extend
|
||||||
)
|
)
|
||||||
|
|
||||||
|
options[:extend] = create_extension_module(association_id, extension) if block_given?
|
||||||
|
|
||||||
association_name, association_class_name, association_class_primary_key_name =
|
association_name, association_class_name, association_class_primary_key_name =
|
||||||
associate_identification(association_id, options[:class_name], options[:foreign_key])
|
associate_identification(association_id, options[:class_name], options[:foreign_key])
|
||||||
|
|
||||||
|
@ -1004,7 +1026,16 @@ module ActiveRecord
|
||||||
def condition_word(sql)
|
def condition_word(sql)
|
||||||
sql =~ /where/i ? " AND " : "WHERE "
|
sql =~ /where/i ? " AND " : "WHERE "
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
def create_extension_module(association_id, extension)
|
||||||
|
extension_module_name = "#{self.to_s}#{association_id.to_s.camelize}AssociationExtension"
|
||||||
|
|
||||||
|
silence_warnings do
|
||||||
|
Object.const_set(extension_module_name, Module.new(&extension))
|
||||||
|
end
|
||||||
|
|
||||||
|
extension_module_name.constantize
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,12 +21,12 @@ module ActiveRecord
|
||||||
base.module_eval <<-"end_eval"
|
base.module_eval <<-"end_eval"
|
||||||
class << self
|
class << self
|
||||||
alias_method :#{association_type}_without_reflection, :#{association_type}
|
alias_method :#{association_type}_without_reflection, :#{association_type}
|
||||||
|
|
||||||
def #{association_type}_with_reflection(association_id, options = {})
|
def #{association_type}_with_reflection(association_id, options = {}, &block)
|
||||||
#{association_type}_without_reflection(association_id, options)
|
#{association_type}_without_reflection(association_id, options, &block)
|
||||||
reflect_on_all_associations << AssociationReflection.new(:#{association_type}, association_id, options, self)
|
reflect_on_all_associations << AssociationReflection.new(:#{association_type}, association_id, options, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :#{association_type}, :#{association_type}_with_reflection
|
alias_method :#{association_type}, :#{association_type}_with_reflection
|
||||||
end
|
end
|
||||||
end_eval
|
end_eval
|
||||||
|
|
|
@ -7,11 +7,31 @@ require 'fixtures/developer'
|
||||||
class AssociationsExtensionsTest < Test::Unit::TestCase
|
class AssociationsExtensionsTest < Test::Unit::TestCase
|
||||||
fixtures :projects, :developers, :comments, :posts
|
fixtures :projects, :developers, :comments, :posts
|
||||||
|
|
||||||
def test_extension_on_habtm
|
|
||||||
assert_equal projects(:action_controller), developers(:david).projects.find_most_recent
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_extension_on_has_many
|
def test_extension_on_has_many
|
||||||
assert_equal comments(:more_greetings), posts(:welcome).comments.find_most_recent
|
assert_equal comments(:more_greetings), posts(:welcome).comments.find_most_recent
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_extension_on_habtm
|
||||||
|
assert_equal projects(:action_controller), developers(:david).projects.find_most_recent
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_named_extension_on_habtm
|
||||||
|
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name.find_most_recent
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_marshalling_extensions
|
||||||
|
david = developers(:david)
|
||||||
|
assert_equal projects(:action_controller), david.projects.find_most_recent
|
||||||
|
|
||||||
|
david = Marshal.load(Marshal.dump(david))
|
||||||
|
assert_equal projects(:action_controller), david.projects.find_most_recent
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_marshalling_named_extensions
|
||||||
|
david = developers(:david)
|
||||||
|
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
|
||||||
|
|
||||||
|
david = Marshal.load(Marshal.dump(david))
|
||||||
|
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
|
||||||
|
end
|
||||||
end
|
end
|
17
activerecord/test/fixtures/developer.rb
vendored
17
activerecord/test/fixtures/developer.rb
vendored
|
@ -1,10 +1,21 @@
|
||||||
|
module DeveloperProjectsAssociationExtension
|
||||||
|
def find_most_recent
|
||||||
|
find(:first, :order => "id DESC")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class Developer < ActiveRecord::Base
|
class Developer < ActiveRecord::Base
|
||||||
has_and_belongs_to_many :projects, :extend => Module.new {
|
has_and_belongs_to_many :projects do
|
||||||
def find_most_recent
|
def find_most_recent
|
||||||
find(:first, :order => "id DESC")
|
find(:first, :order => "id DESC")
|
||||||
end
|
end
|
||||||
}
|
end
|
||||||
|
|
||||||
|
has_and_belongs_to_many :projects_extended_by_name,
|
||||||
|
:class_name => "Project",
|
||||||
|
:join_table => "developers_projects",
|
||||||
|
:association_foreign_key => "project_id",
|
||||||
|
:extend => DeveloperProjectsAssociationExtension
|
||||||
|
|
||||||
has_and_belongs_to_many :special_projects, :join_table => 'developers_projects', :association_foreign_key => 'project_id'
|
has_and_belongs_to_many :special_projects, :join_table => 'developers_projects', :association_foreign_key => 'project_id'
|
||||||
|
|
||||||
|
|
8
activerecord/test/fixtures/post.rb
vendored
8
activerecord/test/fixtures/post.rb
vendored
|
@ -1,15 +1,15 @@
|
||||||
class Post < ActiveRecord::Base
|
class Post < ActiveRecord::Base
|
||||||
belongs_to :author, :extend => Module.new {
|
belongs_to :author do
|
||||||
def greeting
|
def greeting
|
||||||
"hello"
|
"hello"
|
||||||
end
|
end
|
||||||
}
|
end
|
||||||
|
|
||||||
has_many :comments, :order => "body", :extend => Module.new {
|
has_many :comments, :order => "body" do
|
||||||
def find_most_recent
|
def find_most_recent
|
||||||
find(:first, :order => "id DESC")
|
find(:first, :order => "id DESC")
|
||||||
end
|
end
|
||||||
}
|
end
|
||||||
|
|
||||||
has_one :very_special_comment
|
has_one :very_special_comment
|
||||||
has_many :special_comments
|
has_many :special_comments
|
||||||
|
|
Loading…
Reference in a new issue