| Module | Sequel::Model::Associations::ClassMethods |
| In: |
lib/sequel/model/associations.rb
|
Each kind of association adds a number of instance methods to the model class which are specialized according to the association type and optional parameters given in the definition. Example:
class Project < Sequel::Model
many_to_one :portfolio
# or: one_to_one :portfolio
one_to_many :milestones
# or: many_to_many :milestones
end
The project class now has the following instance methods:
| portfolio : | Returns the associated portfolio. |
| portfolio=(obj) : | Sets the associated portfolio to the object, but the change is not persisted until you save the record (for many_to_one associations). |
| portfolio_dataset : | Returns a dataset that would return the associated portfolio, only useful in fairly specific circumstances. |
| milestones : | Returns an array of associated milestones |
| add_milestone(obj) : | Associates the passed milestone with this object. |
| remove_milestone(obj) : | Removes the association with the passed milestone. |
| remove_all_milestones : | Removes associations with all associated milestones. |
| milestones_dataset : | Returns a dataset that would return the associated milestones, allowing for further filtering/limiting/etc. |
If you want to override the behavior of the add_/remove_/remove_all_/ methods or the association setter method, there are private instance methods created that are prepended with an underscore (e.g. _add_milestone or _portfolio=). The private instance methods can be easily overridden, but you shouldn‘t override the public instance methods without calling super, as they deal with callbacks and caching.
By default the classes for the associations are inferred from the association name, so for example the Project#portfolio will return an instance of Portfolio, and Project#milestones will return an array of Milestone instances. You can use the :class option to change which class is used.
Association definitions are also reflected by the class, e.g.:
Project.associations
=> [:portfolio, :milestones]
Project.association_reflection(:portfolio)
=> {:type => :many_to_one, :name => :portfolio, ...}
For a more in depth general overview, as well as a reference guide, see the Association Basics guide. For examples of advanced usage, see the Advanced Associations guide.
| association_reflections | [R] | All association reflections defined for this model (default: {}). |
Associates a related model with the current model. The following types are supported:
The following options can be supplied:
# File lib/sequel/model/associations.rb, line 630
630: def associate(type, name, opts = {}, &block)
631: raise(Error, 'one_to_many association type with :one_to_one option removed, used one_to_one association type') if opts[:one_to_one] && type == :one_to_many
632: raise(Error, 'invalid association type') unless assoc_class = ASSOCIATION_TYPES[type]
633: raise(Error, 'Model.associate name argument must be a symbol') unless Symbol === name
634: raise(Error, ':eager_loader option must have an arity of 1 or 3') if opts[:eager_loader] && ![1, 3].include?(opts[:eager_loader].arity)
635: raise(Error, ':eager_grapher option must have an arity of 1 or 3') if opts[:eager_grapher] && ![1, 3].include?(opts[:eager_grapher].arity)
636:
637: # dup early so we don't modify opts
638: orig_opts = opts.dup
639: orig_opts = association_reflection(opts[:clone])[:orig_opts].merge(orig_opts) if opts[:clone]
640: opts = orig_opts.merge(:type => type, :name => name, :cache => true, :model => self)
641: opts[:block] = block if block
642: opts = assoc_class.new.merge!(opts)
643: opts[:eager_block] = block unless opts.include?(:eager_block)
644: opts[:graph_join_type] ||= :left_outer
645: opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
646: conds = opts[:conditions]
647: opts[:graph_conditions] = conds if !opts.include?(:graph_conditions) and Sequel.condition_specifier?(conds)
648: opts[:graph_conditions] = opts.fetch(:graph_conditions, []).to_a
649: opts[:graph_select] = Array(opts[:graph_select]) if opts[:graph_select]
650: [:before_add, :before_remove, :after_add, :after_remove, :after_load, :before_set, :after_set, :extend].each do |cb_type|
651: opts[cb_type] = Array(opts[cb_type])
652: end
653: late_binding_class_option(opts, opts.returns_array? ? singularize(name) : name)
654:
655: send("def_#{type}""def_#{type}", opts)
656:
657: orig_opts.delete(:clone)
658: orig_opts.merge!(:class_name=>opts[:class_name], :class=>opts[:class], :block=>block)
659: opts[:orig_opts] = orig_opts
660: # don't add to association_reflections until we are sure there are no errors
661: association_reflections[name] = opts
662: end
The association reflection hash for the association of the given name.
# File lib/sequel/model/associations.rb, line 665
665: def association_reflection(name)
666: association_reflections[name]
667: end
Modify and return eager loading dataset based on association options.
# File lib/sequel/model/associations.rb, line 675
675: def eager_loading_dataset(opts, ds, select, associations, eager_options={})
676: ds = ds.select(*select) if select
677: if c = opts[:conditions]
678: ds = (c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
679: end
680: ds = ds.order(*opts[:order]) if opts[:order]
681: ds = ds.eager(opts[:eager]) if opts[:eager]
682: ds = ds.distinct if opts[:distinct]
683: if opts[:eager_graph]
684: ds = ds.eager_graph(opts[:eager_graph])
685: ds = ds.add_graph_aliases(opts.associated_key_alias=>[opts.associated_class.table_name, opts.associated_key_alias, SQL::QualifiedIdentifier.new(opts.associated_key_table, opts.associated_key_column)]) if opts.eager_loading_use_associated_key?
686: end
687: ds = ds.eager(associations) unless Array(associations).empty?
688: ds = opts[:eager_block].call(ds) if opts[:eager_block]
689: ds = eager_options[:eager_block].call(ds) if eager_options[:eager_block]
690: if !opts[:eager_graph] && opts.eager_loading_use_associated_key?
691: ds = if opts[:uses_left_composite_keys]
692: t = opts.associated_key_table
693: ds.select_append(*opts.associated_key_alias.zip(opts.associated_key_column).map{|a, c| SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(t, c), a)})
694: else
695: ds.select_append(SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(opts.associated_key_table, opts.associated_key_column), opts.associated_key_alias))
696: end
697: end
698: ds
699: end
Copy the association reflections to the subclass
# File lib/sequel/model/associations.rb, line 702
702: def inherited(subclass)
703: super
704: subclass.instance_variable_set(:@association_reflections, @association_reflections.dup)
705: end
Shortcut for adding a many_to_many association, see associate
# File lib/sequel/model/associations.rb, line 708
708: def many_to_many(name, opts={}, &block)
709: associate(:many_to_many, name, opts, &block)
710: end
Shortcut for adding a many_to_one association, see associate
# File lib/sequel/model/associations.rb, line 713
713: def many_to_one(name, opts={}, &block)
714: associate(:many_to_one, name, opts, &block)
715: end
Shortcut for adding a one_to_many association, see associate
# File lib/sequel/model/associations.rb, line 718
718: def one_to_many(name, opts={}, &block)
719: associate(:one_to_many, name, opts, &block)
720: end
Shortcut for adding a one_to_one association, see associate.
# File lib/sequel/model/associations.rb, line 723
723: def one_to_one(name, opts={}, &block)
724: associate(:one_to_one, name, opts, &block)
725: end