Module: CouchFoo::Associations::ClassMethods
- Defined in:
- lib/couch_foo/associations.rb
Overview
Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like “Project has one Project Manager” or “Project belongs to a Portfolio”. Each macro adds a number of methods to the class which are specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby’s own attr*
methods. Example:
class Project < CouchFoo::Base
belongs_to :portfolio
has_one :project_manager
has_many :milestones
has_and_belongs_to_many :categories
end
The project class now has the following methods (and more) to ease the traversal and manipulation of its relationships:
-
Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?
-
Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,
-
Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),
Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),
Project#milestones.build, Project#milestones.create
-
Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),
Project#categories.delete(category1)
Note
The current CouchFoo implementation does not include has_and_belongs_to_many This will be added in a future release along with an option for using has_many in an inline context, so all the associated documents are stored in the parent itself rather than in separate records.
A word of warning
Don’t create associations that have the same name as instance methods of CouchFoo::Base. Since the association adds a method with that name to its model, it will override the inherited method and break things. For instance, attributes
and connection
would be bad choices for association names.
Auto-generated methods
Singular associations (one-to-one)
| | belongs_to |
generated methods | belongs_to | :polymorphic | has_one
----------------------------------+------------+--------------+---------
#other | X | X | X
#other=(other) | X | X | X
#build_other(attributes={}) | X | | X
#create_other(attributes={}) | X | | X
#other.create!(attributes={}) | | | X
#other.nil? | X | X |
Collection associations (one-to-many / many-to-many)
| | | has_many
generated methods | habtm | has_many | :through
----------------------------------+-------+----------+----------
#others | X | X | X
#others=(other,other,...) | X | X | X
#other_ids | X | X | X
#other_ids=(id,id,...) | X | X | X
#others<< | X | X | X
#others.push | X | X | X
#others.concat | X | X | X
#others.build(attributes={}) | X | X | X
#others.create(attributes={}) | X | X | X
#others.create!(attributes={}) | X | X | X
#others.size | X | X | X
#others.length | X | X | X
#others.count | X | X | X
#others.sum(args*,&block) | X | X | X
#others.empty? | X | X | X
#others.clear | X | X | X
#others.delete(other,other,...) | X | X | X
#others.delete_all | X | X |
#others.destroy_all | X | X | X
#others.find(*args) | X | X | X
#others.find_first | X | |
#others.uniq | X | X | X
#others.reset | X | X | X
Cardinality and associations
Couch Foo associations can be used to describe one-to-one, one-to-many and many-to-many relationships between models. Each model uses an association to describe its role in the relation. The belongs_to
association is always used in the model that has the foreign key.
One-to-one
Use has_one
in the base, and belongs_to
in the associated model.
class Employee < CouchFoo::Base
has_one :office
end
class Office < CouchFoo::Base
belongs_to :employee # foreign key - employee_id
end
One-to-many
Use has_many
in the base, and belongs_to
in the associated model.
class Manager < CouchFoo::Base
has_many :employees
end
class Employee < CouchFoo::Base
belongs_to :manager # foreign key - manager_id
end
Many-to-many
Not implement yet
Is it a belongs_to
or has_one
association?
Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the model for the class declaring the belongs_to
relationship. Example:
class User < CouchFoo::Base
# I reference an account.
belongs_to :account
end
class Account < CouchFoo::Base
# One user references me.
has_one :user
end
The properties definitions for these classes could look something like:
class User < CouchFoo::Base
property :account_id, Integer
property :name, String
end
class Account < CouchFoo::Base
property :name, String
end
Unsaved objects and associations
You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be aware of, mostly involving the saving of associated objects.
One-to-one associations
-
Assigning an object to a
has_one
association automatically saves that object and the object being replaced (if there is one), in order to update their primary keys - except if the parent object is unsaved (new_record? == true
). -
If either of these saves fail (due to one of the objects being invalid) the assignment statement returns
false
and the assignment is cancelled. -
If you wish to assign an object to a
has_one
association without saving it, use theassociation.build
method (documented below). -
Assigning an object to a
belongs_to
association does not save the object, since the foreign key field belongs on the parent. It does not save the parent either.
Collections
-
Adding an object to a collection (
has_many
orhas_and_belongs_to_many
) automatically saves that object, except if the parent object (the owner of the collection) is not yet stored in the database. -
If saving any of the objects being added to a collection (via
push
or similar) fails, thenpush
returnsfalse
. -
You can add an object to a collection without automatically saving it by using the
collection.build
method (documented below). -
All unsaved (
new_record? == true
) members of the collection are automatically saved when the parent is saved.
Association callbacks
Similar to the normal callbacks that hook into the lifecycle of an Couch Foo object, you can also define callbacks that get triggered when you add an object to or remove an object from an association collection. Example:
class Project
has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
def evaluate_velocity(developer)
...
end
end
It’s possible to stack callbacks by passing them as an array. Example:
class Project
has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
end
Possible callbacks are: before_add
, after_add
, before_remove
and after_remove
.
Should any of the before_add
callbacks throw an exception, the object does not get added to the collection. Same with the before_remove
callbacks; if an exception is thrown the object doesn’t get removed.
Association extensions
The proxy objects that control the access to associations can be extended through anonymous modules. This is especially beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association. Example:
class Account < CouchFoo::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"
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(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
class Account < CouchFoo::Base
has_many :people, :extend => FindOrCreateByNameExtension
end
class Company < CouchFoo::Base
has_many :people, :extend => FindOrCreateByNameExtension
end
If you need to use multiple named extension modules, you can specify an array of modules with the :extend
option. In the case of name conflicts between methods in the modules, methods in modules later in the array supercede those earlier in the array. Example:
class Account < CouchFoo::Base
has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
end
Some extensions can only be made to work with knowledge of the association proxy’s internals. Extensions can access relevant state using accessors on the association proxy:
-
proxy_owner
- Returns the object the association is part of. -
proxy_reflection
- Returns the reflection object that describes the association. -
proxy_target
- Returns the associated object forbelongs_to
andhas_one
, or the collection of associated objects forhas_many
andhas_and_belongs_to_many
.
Association Join Models
This is not supported yet
Polymorphic Associations
Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they specify an interface that a has_many
association must adhere to.
class Asset < CouchFoo::Base
belongs_to :attachable, :polymorphic => true
end
class Post < CouchFoo::Base
has_many :assets, :as => :attachable # The :as option specifies the polymorphic interface to use.
end
@asset.attachable = @post
This works by using a type property in addition to a foreign key to specify the associated record. In the Asset example, you’d need an attachable_id
key attribute and an attachable_type
string attribute.
Using polymorphic associations in combination with inheritance is a little tricky. In order for the associations to work as expected, ensure that you store the base model in the type property of the polymorphic association. To continue with the asset example above, suppose there are guest posts and member posts that use inheritence. In this case, there must be a type
property in the Post model.
class Asset < CouchFoo::Base
belongs_to :attachable, :polymorphic => true
def attachable_type=(sType)
super(sType.to_s.classify.constantize.class.to_s)
end
end
class Post < CouchFoo::Base
# because we store "Post" in attachable_type now :dependent => :destroy will work
has_many :assets, :as => :attachable, :dependent => :destroy
end
class GuestPost < Post
end
class MemberPost < Post
end
Caching
All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without worrying too much about performance at the first go. Example:
project.milestones # fetches milestones from the database
project.milestones.size # uses the milestone cache
project.milestones.empty? # uses the milestone cache
project.milestones(true).size # fetches milestones from the database
project.milestones # uses the milestone cache
Eager loading of associations
Not implemented yet
Modules
By default, associations will look for objects within the current module scope. Consider:
module MyApplication
module Business
class Firm < CouchFoo::Base
has_many :clients
end
class Company < CouchFoo::Base; end
end
end
When Firm#clients is called, it will in turn call MyApplication::Business::Company.find(firm.id)
. If you want to associate with a class in another module scope, this can be done by specifying the complete class name. Example:
module MyApplication
module Business
class Firm < CouchFoo::Base; end
end
module Billing
class Account < CouchFoo::Base
belongs_to :firm, :class_name => "MyApplication::Business::Firm"
end
end
end
Type safety with CouchFoo::AssociationTypeMismatch
If you attempt to assign an object to an association that doesn’t match the inferred or specified :class_name
, you’ll get an CouchFoo::AssociationTypeMismatch
.
Options
All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones possible.
Instance Method Summary collapse
-
#belongs_to(association_id, options = {}) ⇒ Object
Adds the following methods for retrieval and query for a single associated object for which this object holds an id:
association
is replaced with the symbol passed as the first argument, sobelongs_to :author
would add among othersauthor.nil?
. -
#has_many(association_id, options = {}, &extension) ⇒ Object
Adds the following methods for retrieval and query of collections of associated objects:
collection
is replaced with the symbol passed as the first argument, sohas_many :clients
would add among othersclients.empty?
. -
#has_one(association_id, options = {}) ⇒ Object
Adds the following methods for retrieval and query of a single associated object:
association
is replaced with the symbol passed as the first argument, sohas_one :manager
would add among othersmanager.nil?
.
Instance Method Details
#belongs_to(association_id, options = {}) ⇒ Object
Adds the following methods for retrieval and query for a single associated object for which this object holds an id: association
is replaced with the symbol passed as the first argument, so belongs_to :author
would add among others author.nil?
.
-
association(force_reload = false)
- Returns the associated object.nil
is returned if none is found. -
association=(associate)
- Assigns the associate object, extracts the primary key, and sets it as the foreign key. -
association.nil?
- Returnstrue
if there is no associated object. -
build_association(attributes = {})
- Returns a new object of the associated type that has been instantiated withattributes
and linked to this object through a foreign key, but has not yet been saved. -
create_association(attributes = {})
- Returns a new object of the associated type that has been instantiated withattributes
, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
Example: A Post class declares belongs_to :author
, which will add:
-
Post#author
(similar toAuthor.find(author_id)
) -
Post#author=(author)
(similar topost.author_id = author.id
) -
Post#author?
(similar topost.author == some_author
) -
Post#author.nil?
-
Post#build_author
(similar topost.author = Author.new
) -
Post#create_author
(similar topost.author = Author.new; post.author.save; post.author
)
The declaration can also include an options hash to specialize the behavior of the association.
Options are:
-
:class_name
- Specify the class name of the association. Use it only if that name can’t be inferred from the association name. Sohas_one :author
will by default be linked to the Author class, but if the real class name is Person, you’ll have to specify it with this option. -
:conditions
- Specify the conditions that the associated objects must meet in order to be included in the results. For examplehas_many :posts, :conditions => {:published => true}
. This will also create published posts with@blog.posts.create
or@blog.posts.build
. -
:foreign_key
- Specify the foreign key used for the association. By default this is guessed to be the name of the association with an “_id” suffix. So a class that defines abelongs_to :person
association will use “person_id” as the default:foreign_key
. Similarly,belongs_to :favorite_person, :class_name => "Person"
will use a foreign key of “favorite_person_id”. -
:dependent
- If set to:destroy
, the associated object is destroyed when this object is. If set to:delete
, the associated object is deleted without calling its destroy method. This option should not be specified whenbelongs_to
is used in conjunction with ahas_many
relationship on another class because of the potential to leave orphaned records behind. -
:counter_cache
- Caches the number of belonging objects on the associate class through the use ofincrement_counter
anddecrement_counter
. The counter cache is incremented when an object of this class is created and decremented when it’s destroyed. This requires that a property named#{document_name}_count
(such ascomments_count
for a belonging Comment class) is used on the associate class (such as a Post class). You can also specify a custom counter cache property by providing a property name instead of atrue
/false
value to this option (e.g.,:counter_cache => :my_custom_counter
.) When creating a counter cache property, the database statement or migration must specify a default value of0
, failing to do this results in a counter withNULL
value, which will never increment. Note: Specifying a counter cache will add it to that model’s list of readonly attributes usingattr_readonly
. -
:include
- Specify second-order associations that should be eager loaded when this object is loaded. -
:polymorphic
- Specify this association is a polymorphic association by passingtrue
. Note: If you’ve enabled the counter cache, then you may want to add the counter cache attribute to theattr_readonly
list in the associated classes (e.g.class Post; attr_readonly :comments_count; end
). -
:readonly
- If true, the associated object is readonly through the association. -
:validate
- If false, don’t validate the associated objects when saving the parent object.false
by default.
Option examples:
belongs_to :firm, :foreign_key => "client_of"
belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
:conditions => {discounts = #{payments_count}}
belongs_to :attachable, :polymorphic => true
belongs_to :project, :readonly => true
belongs_to :post, :counter_cache => true
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 |
# File 'lib/couch_foo/associations.rb', line 648 def belongs_to(association_id, = {}) reflection = create_belongs_to_reflection(association_id, ) ivar = "@#{reflection.name}" if reflection.[:polymorphic] association_accessor_methods(reflection, BelongsToPolymorphicAssociation) method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym define_method(method_name) do association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") if association && association.target if association.new_record? association.save(true) end if association.updated? self["#{reflection.primary_key_name}"] = association.id self["#{reflection.[:foreign_type]}"] = association.class.name.to_s end end end before_save method_name else association_accessor_methods(reflection, BelongsToAssociation) association_constructor_method(:build, reflection, BelongsToAssociation) association_constructor_method(:create, reflection, BelongsToAssociation) method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym define_method(method_name) do association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") if !association.nil? if association.new_record? association.save(true) end if association.updated? self["#{reflection.primary_key_name}"] = association.id end end end before_save method_name end # Create the callbacks to update counter cache if [:counter_cache] cache_property = [:counter_cache] == true ? "#{self.to_s.demodulize.underscore.pluralize}_count" : [:counter_cache] method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym define_method(method_name) do association = send("#{reflection.name}") association.class.increment_counter("#{cache_property}", send("#{reflection.primary_key_name}")) unless association.nil? end after_create method_name method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym define_method(method_name) do association = send("#{reflection.name}") association.class.decrement_counter("#{cache_property}", send("#{reflection.primary_key_name}")) unless association.nil? end before_destroy method_name module_eval( "#{reflection.class_name}.send(:attr_readonly,\"#{cache_property}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)" ) end add_single_associated_validation_callbacks(reflection.name) if [:validate] == true configure_dependency_for_belongs_to(reflection) end |
#has_many(association_id, options = {}, &extension) ⇒ Object
Adds the following methods for retrieval and query of collections of associated objects: collection
is replaced with the symbol passed as the first argument, so has_many :clients
would add among others clients.empty?
.
-
collection(force_reload = false)
- Returns an array of all the associated objects. An empty array is returned if none are found. -
collection<<(object, ...)
- Adds one or more objects to the collection by setting their foreign keys to the collection’s primary key. -
collection.delete(object, ...)
- Removes one or more objects from the collection by setting their foreign keys toNULL
. This will also destroy the objects if they’re declared asbelongs_to
and dependent on this model. -
collection=objects
- Replaces the collections content by deleting and adding objects as appropriate. -
collection_singular_ids
- Returns an array of the associated objects’ ids -
collection_singular_ids=ids
- Replace the collection with the objects identified by the primary keys inids
-
collection.clear
- Removes every object from the collection. This destroys the associated objects if they are associated with:dependent => :destroy
, deletes them directly from the database if:dependent => :delete_all
, otherwise sets their foreign keys toNULL
. -
collection.empty?
- Returnstrue
if there are no associated objects. -
collection.size
- Returns the number of associated objects. -
collection.find
- Finds an associated object according to the same rules as Base.find. -
collection.build(attributes = {}, ...)
- Returns one or more new objects of the collection type that have been instantiated withattributes
and linked to this object through a foreign key, but have not yet been saved. Note: This only works if an associated object already exists, not if it’snil
! -
collection.create(attributes = {})
- Returns a new object of the collection type that has been instantiated withattributes
, linked to this object through a foreign key, and that has already been saved (if it passed the validation). Note: This only works if an associated object already exists, not if it’snil
!
Example: A Firm class declares has_many :clients
, which will add:
-
Firm#clients
(similar toClients.find :all, :conditions => "firm_id = #{id}"
) -
Firm#clients<<
-
Firm#clients.delete
-
Firm#clients=
-
Firm#client_ids
-
Firm#client_ids=
-
Firm#clients.clear
-
Firm#clients.empty?
(similar tofirm.clients.size == 0
) -
Firm#clients.size
(similar toClient.count "firm_id = #{id}"
) -
Firm#clients.find
(similar toClient.find(id, :conditions => "firm_id = #{id}")
) -
Firm#clients.build
(similar toClient.new("firm_id" => id)
) -
Firm#clients.create
(similar toc = Client.new("firm_id" => id); c.save; c
)
The declaration can also include an options hash to specialize the behavior of the association.
Options are:
-
:class_name
- Specify the class name of the association. Use it only if that name can’t be inferred from the association name. Sohas_many :products
will by default be linked to the Product class, but if the real class name is SpecialProduct, you’ll have to specify it with this option. -
:conditions
- Specify the conditions that the associated objects must meet in order to be included in the results. For examplehas_many :posts, :conditions => {:published => true}
. This will also create published posts with@blog.posts.create
or@blog.posts.build
. -
:order
- Specify the order in which the associated objects are returned by a property to sort on, for example :order => :product_weight. See notes in CouchFoo#find when using with :limit -
:dependent
- If set to:destroy
all the associated objects are destroyed alongside this object by calling theirdestroy
method. If set to:delete_all
all associated objects are deleted without calling theirdestroy
method. If set to:nullify
all associated objects’ foreign keys are set toNULL
without calling theirsave
callbacks. Warning: This option is ignored when also using the:through
option. -
:extend
- Specify a named module for extending the proxy. See “Association extensions”. -
:include
- Specify second-order associations that should be eager loaded when the collection is loaded. -
:limit
- An integer determining the limit on the number of rows that should be returned. See notes in CouchFoo#find when using with :order -
:offset
- An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows. -
:as
- Specifies a polymorphic interface (Seebelongs_to
). -
:through
- Not implemented at the moment -
:source_type
- Specifies type of the source association used byhas_many :through
queries where the source association is a polymorphicbelongs_to
. -
:uniq
- If true, duplicates will be omitted from the collection. Useful in conjunction with:through
. -
:readonly
- If true, all the associated objects are readonly through the association. -
:validate
- If false, don’t validate the associated objects when saving the parent object. true by default.
Option examples:
has_many :comments, :order => :posted_on
has_many :comments, :include => :author
has_many :people, :class_name => "Person", :conditions => {deleted => 0}, :order => "name"
has_many :tracks, :order => :position, :dependent => :destroy
has_many :comments, :dependent => :nullify
has_many :tags, :as => :taggable
has_many :reports, :readonly => true
494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/couch_foo/associations.rb', line 494 def has_many(association_id, = {}, &extension) reflection = create_has_many_reflection(association_id, , &extension) configure_dependency_for_has_many(reflection) add_multiple_associated_validation_callbacks(reflection.name) unless [:validate] == false add_multiple_associated_save_callbacks(reflection.name) add_association_callbacks(reflection.name, reflection.) #if options[:through] # collection_accessor_methods(reflection, HasManyThroughAssociation) #else collection_accessor_methods(reflection, HasManyAssociation) #end end |
#has_one(association_id, options = {}) ⇒ Object
Adds the following methods for retrieval and query of a single associated object: association
is replaced with the symbol passed as the first argument, so has_one :manager
would add among others manager.nil?
.
-
association(force_reload = false)
- Returns the associated object.nil
is returned if none is found. -
association=(associate)
- Assigns the associate object, extracts the primary key, sets it as the foreign key, and saves the associate object. -
association.nil?
- Returnstrue
if there is no associated object. -
build_association(attributes = {})
- Returns a new object of the associated type that has been instantiated withattributes
and linked to this object through a foreign key, but has not yet been saved. Note: This ONLY works if an association already exists. It will NOT work if the association isnil
. -
create_association(attributes = {})
- Returns a new object of the associated type that has been instantiated withattributes
, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
Example: An Account class declares has_one :beneficiary
, which will add:
-
Account#beneficiary
(similar toBeneficiary.find(:first, :conditions => "account_id = #{id}")
) -
Account#beneficiary=(beneficiary)
(similar tobeneficiary.account_id = account.id; beneficiary.save
) -
Account#beneficiary.nil?
-
Account#build_beneficiary
(similar toBeneficiary.new("account_id" => id)
) -
Account#create_beneficiary
(similar tob = Beneficiary.new("account_id" => id); b.save; b
)
The declaration can also include an options hash to specialize the behavior of the association.
Options are:
-
:class_name
- Specify the class name of the association. Use it only if that name can’t be inferred from the association name. Sohas_one :manager
will by default be linked to the Manager class, but if the real class name is Person, you’ll have to specify it with this option. -
:conditions
- Specify the conditions that the associated objects must meet in order to be included in the results. For examplehas_many :posts, :conditions => {:published => true}
. This will also create published posts with@blog.posts.create
or@blog.posts.build
. -
:order
- Specify the order in which the associated objects are returned by a property to sort on, for example :order => :product_weight. See notes in CouchFoo#find when using with :limit -
:dependent
- If set to:destroy
, the associated object is destroyed when this object is. If set to:delete
, the associated object is deleted without calling its destroy method. If set to:nullify
, the associated object’s foreign key is set toNULL
. Also, association is assigned. -
:foreign_key
- Specify the foreign key used for the association. By default this is guessed to be the name of this class in lower-case and “_id” suffixed. So a Person class that makes ahas_one
association will use “person_id” as the default:foreign_key
. -
:include
- Specify second-order associations that should be eager loaded when this object is loaded. -
:as
- Specifies a polymorphic interface (Seebelongs_to
). -
:through
- Not implemented yet -
:source
- Not implemented yet -
:source_type
- Not implemented yet -
:readonly
- If true, the associated object is readonly through the association. -
:validate
- If false, don’t validate the associated object when saving the parent object.false
by default.
Option examples:
has_one :credit_card, :dependent => :destroy # destroys the associated credit card
has_one :credit_card, :dependent => :nullify # updates the associated records foreign key value to NULL rather than destroying it
has_one :last_comment, :class_name => "Comment", :order => :posted_on
has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'"
has_one :attachment, :as => :attachable
has_one :boss, :readonly => :true
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
# File 'lib/couch_foo/associations.rb', line 561 def has_one(association_id, = {}) #if options[:through] # reflection = create_has_one_through_reflection(association_id, options) # association_accessor_methods(reflection, CouchFoo::Associations::HasOneThroughAssociation) #else reflection = create_has_one_reflection(association_id, ) ivar = "@#{reflection.name}" method_name = "has_one_after_save_for_#{reflection.name}".to_sym define_method(method_name) do association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") if !association.nil? && (new_record? || association.new_record? || association["#{reflection.primary_key_name}"] != id) association["#{reflection.primary_key_name}"] = id association.save(true) end end after_save method_name add_single_associated_validation_callbacks(reflection.name) if [:validate] == true association_accessor_methods(reflection, HasOneAssociation) association_constructor_method(:build, reflection, HasOneAssociation) association_constructor_method(:create, reflection, HasOneAssociation) configure_dependency_for_has_one(reflection) #end end |