module Sequel::Model::Associations::InstanceMethods
Instance methods used to implement the associations support.
Public Instance Methods
The currently cached associations. A hash with the keys being the association name symbols and the values being the associated object or nil (many_to_one), or the array of associated objects (*_to_many).
# File lib/sequel/model/associations.rb 2321 def associations 2322 @associations ||= {} 2323 end
Freeze the associations cache when freezing the object. Note that retrieving associations after freezing will still work in most cases, but the associations will not be cached in the association cache.
# File lib/sequel/model/associations.rb 2328 def freeze 2329 associations 2330 super 2331 associations.freeze 2332 self 2333 end
Private Instance Methods
Apply the association options such as :order and :limit to the given dataset, returning a modified dataset.
# File lib/sequel/model/associations.rb 2338 def _apply_association_options(opts, ds) 2339 unless ds.kind_of?(AssociationDatasetMethods) 2340 ds = opts.apply_dataset_changes(ds) 2341 end 2342 ds = ds.clone(:model_object => self) 2343 ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? 2344 # block method is private 2345 ds = send(opts[:block_method], ds) if opts[:block_method] 2346 ds 2347 end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb 2350 def _associated_dataset(opts, dynamic_opts) 2351 ds = public_send(opts.dataset_method) 2352 if callback = dynamic_opts[:callback] 2353 ds = callback.call(ds) 2354 end 2355 ds 2356 end
A placeholder literalizer that can be used to load the association, or nil to not use one.
# File lib/sequel/model/associations.rb 2359 def _associated_object_loader(opts, dynamic_opts) 2360 if !dynamic_opts[:callback] && (loader = opts.placeholder_loader) 2361 loader 2362 end 2363 end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb 2366 def _dataset(opts) 2367 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2368 ds = if opts[:dataset_opt_arity] == 1 2369 # dataset_opt_method is private 2370 send(opts[:dataset_opt_method], opts) 2371 else 2372 send(opts[:dataset_opt_method]) 2373 end 2374 _apply_association_options(opts, ds) 2375 end
Dataset
for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb 2378 def _join_table_dataset(opts) 2379 ds = model.db.from(opts.join_table_source) 2380 opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds 2381 end
Return the associated single object for the given association reflection and dynamic options (or nil if no associated object).
# File lib/sequel/model/associations.rb 2385 def _load_associated_object(opts, dynamic_opts) 2386 _load_associated_object_array(opts, dynamic_opts).first 2387 end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb 2396 def _load_associated_object_array(opts, dynamic_opts) 2397 if loader = _associated_object_loader(opts, dynamic_opts) 2398 loader.all(*opts.predicate_key_values(self)) 2399 else 2400 _associated_dataset(opts, dynamic_opts).all 2401 end 2402 end
Return the associated single object using a primary key lookup on the associated class.
# File lib/sequel/model/associations.rb 2390 def _load_associated_object_via_primary_key(opts) 2391 opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk))) 2392 end
Return the associated objects from the dataset, without association callbacks, reciprocals, and caching. Still apply the dynamic callback if present.
# File lib/sequel/model/associations.rb 2406 def _load_associated_objects(opts, dynamic_opts=OPTS) 2407 if opts.can_have_associated_objects?(self) 2408 if opts.returns_array? 2409 _load_associated_object_array(opts, dynamic_opts) 2410 elsif load_with_primary_key_lookup?(opts, dynamic_opts) 2411 _load_associated_object_via_primary_key(opts) 2412 else 2413 _load_associated_object(opts, dynamic_opts) 2414 end 2415 elsif opts.returns_array? 2416 [] 2417 end 2418 end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb 2421 def _refresh_set_values(hash) 2422 @associations.clear if @associations 2423 super 2424 end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb 2663 def _set_associated_object(opts, o) 2664 a = associations[opts[:name]] 2665 reciprocal = opts.reciprocal 2666 if set_associated_object_if_same? 2667 if reciprocal 2668 remove_reciprocal = a && (a != o || a.associations[reciprocal] != self) 2669 add_reciprocal = o && o.associations[reciprocal] != self 2670 end 2671 else 2672 return if a && a == o 2673 if reciprocal 2674 remove_reciprocal = a 2675 add_reciprocal = o 2676 end 2677 end 2678 run_association_callbacks(opts, :before_set, o) 2679 remove_reciprocal_object(opts, a) if remove_reciprocal 2680 # Allow calling private _setter method 2681 send(opts[:_setter_method], o) 2682 associations[opts[:name]] = o 2683 add_reciprocal_object(opts, o) if add_reciprocal 2684 run_association_callbacks(opts, :after_set, o) 2685 o 2686 end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb 2427 def add_associated_object(opts, o, *args) 2428 o = make_add_associated_object(opts, o) 2429 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2430 ensure_associated_primary_key(opts, o, *args) 2431 return if run_association_callbacks(opts, :before_add, o) == false 2432 # Allow calling private _add method 2433 return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure? 2434 if array = associations[opts[:name]] and !array.include?(o) 2435 array.push(o) 2436 end 2437 add_reciprocal_object(opts, o) 2438 run_association_callbacks(opts, :after_add, o) 2439 o 2440 end
Add/Set the current object to/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2446 def add_reciprocal_object(opts, o) 2447 return if o.frozen? 2448 return unless reciprocal = opts.reciprocal 2449 if opts.reciprocal_array? 2450 if array = o.associations[reciprocal] and !array.include?(self) 2451 array.push(self) 2452 end 2453 else 2454 o.associations[reciprocal] = self 2455 end 2456 end
Call uniq! on the given array. This is used by the :uniq option, and is an actual method for memory reasons.
# File lib/sequel/model/associations.rb 2460 def array_uniq!(a) 2461 a.uniq! 2462 end
If a foreign key column value changes, clear the related cached associations.
# File lib/sequel/model/associations.rb 2466 def change_column_value(column, value) 2467 if assocs = model.autoreloading_associations[column] 2468 vals = @values 2469 if new? 2470 # Do deeper checking for new objects, so that associations are 2471 # not deleted when values do not change. This code is run at 2472 # a higher level for existing objects. 2473 if value == (c = vals[column]) && value.class == c.class 2474 # If the value is the same, there is no reason to delete 2475 # the related associations, so exit early in that case. 2476 return super 2477 end 2478 2479 only_delete_nil = c.nil? 2480 elsif vals[column].nil? 2481 only_delete_nil = true 2482 end 2483 2484 if only_delete_nil 2485 # If the current foreign key value is nil, but the association 2486 # is already present in the cache, it was probably added to the 2487 # cache for a reason, and we do not want to delete it in that case. 2488 # However, we still want to delete associations with nil values 2489 # to remove the cached false negative. 2490 assocs.each{|a| associations.delete(a) if associations[a].nil?} 2491 else 2492 assocs.each{|a| associations.delete(a)} 2493 end 2494 end 2495 super 2496 end
Save the associated object if the associated object needs a primary key and the associated object is new and does not have one. Raise an error if the object still does not have a primary key
# File lib/sequel/model/associations.rb 2501 def ensure_associated_primary_key(opts, o, *args) 2502 if opts.need_associated_primary_key? 2503 o.save(:validate=>opts[:validate]) if o.new? 2504 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk 2505 end 2506 end
Duplicate the associations hash when duplicating the object.
# File lib/sequel/model/associations.rb 2509 def initialize_copy(other) 2510 super 2511 @associations = Hash[@associations] if @associations 2512 self 2513 end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb 2526 def load_associated_objects(opts, dynamic_opts, &block) 2527 dynamic_opts = load_association_objects_options(dynamic_opts, &block) 2528 name = opts[:name] 2529 if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload] 2530 associations[name] 2531 else 2532 objs = _load_associated_objects(opts, dynamic_opts) 2533 if opts.set_reciprocal_to_self? 2534 if opts.returns_array? 2535 objs.each{|o| add_reciprocal_object(opts, o)} 2536 elsif objs 2537 add_reciprocal_object(opts, objs) 2538 end 2539 end 2540 2541 # If the current object is frozen, you can't update the associations 2542 # cache. This can cause issues for after_load procs that expect 2543 # the objects to be already cached in the associations, but 2544 # unfortunately that case cannot be handled. 2545 associations[name] = objs unless frozen? 2546 run_association_callbacks(opts, :after_load, objs) 2547 frozen? ? objs : associations[name] 2548 end 2549 end
If a block is given, assign it as the :callback option in the hash, and return the hash.
# File lib/sequel/model/associations.rb 2516 def load_association_objects_options(dynamic_opts, &block) 2517 if block 2518 dynamic_opts = Hash[dynamic_opts] 2519 dynamic_opts[:callback] = block 2520 end 2521 2522 dynamic_opts 2523 end
Whether to use a simple primary key lookup on the associated class when loading.
# File lib/sequel/model/associations.rb 2552 def load_with_primary_key_lookup?(opts, dynamic_opts) 2553 opts[:type] == :many_to_one && 2554 !dynamic_opts[:callback] && 2555 opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key} 2556 end
Convert the input of the add_* association method into an associated object. For hashes, this creates a new object using the hash. For integers, strings, and arrays, assume the value specifies a primary key, and lookup an existing object with that primary key. Otherwise, if the object is not already an instance of the class, raise an exception.
# File lib/sequel/model/associations.rb 2562 def make_add_associated_object(opts, o) 2563 klass = opts.associated_class 2564 2565 case o 2566 when Hash 2567 klass.new(o) 2568 when Integer, String, Array 2569 klass.with_pk!(o) 2570 when klass 2571 o 2572 else 2573 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2574 end 2575 end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb 2578 def remove_all_associated_objects(opts, *args) 2579 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2580 # Allow calling private _remove_all method 2581 send(opts[:_remove_all_method], *args) 2582 ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) 2583 associations[opts[:name]] = [] 2584 ret 2585 end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb 2591 def remove_associated_object(opts, o, *args) 2592 klass = opts.associated_class 2593 if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) 2594 o = remove_check_existing_object_from_pk(opts, o, *args) 2595 elsif !o.is_a?(klass) 2596 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2597 elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty? 2598 raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") 2599 end 2600 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2601 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk 2602 return if run_association_callbacks(opts, :before_remove, o) == false 2603 # Allow calling private _remove method 2604 return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure? 2605 associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) 2606 remove_reciprocal_object(opts, o) 2607 run_association_callbacks(opts, :after_remove, o) 2608 o 2609 end
Check that the object from the associated table specified by the primary key is currently associated to the receiver. If it is associated, return the object, otherwise raise an error.
# File lib/sequel/model/associations.rb 2617 def remove_check_existing_object_from_pk(opts, o, *args) 2618 key = o 2619 pkh = opts.associated_class.qualified_primary_key_hash(key) 2620 raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh) 2621 o 2622 end
Remove/unset the current object from/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2625 def remove_reciprocal_object(opts, o) 2626 return unless reciprocal = opts.reciprocal 2627 if opts.reciprocal_array? 2628 if array = o.associations[reciprocal] 2629 array.delete_if{|x| self === x} 2630 end 2631 else 2632 o.associations[reciprocal] = nil 2633 end 2634 end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb 2637 def run_association_callbacks(reflection, callback_type, object) 2638 return unless cbs = reflection[callback_type] 2639 2640 begin 2641 cbs.each do |cb| 2642 case cb 2643 when Symbol 2644 # Allow calling private methods in association callbacks 2645 send(cb, object) 2646 when Proc 2647 cb.call(self, object) 2648 else 2649 raise Error, "callbacks should either be Procs or Symbols" 2650 end 2651 end 2652 rescue HookFailed 2653 # The reason we automatically set raise_error for singular associations is that 2654 # assignment in ruby always returns the argument instead of the result of the 2655 # method, so we can't return nil to signal that the association callback prevented 2656 # the modification 2657 return false unless raise_on_save_failure || !reflection.returns_array? 2658 raise 2659 end 2660 end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb 2696 def set_associated_object(opts, o) 2697 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2698 _set_associated_object(opts, o) 2699 end
Whether run the associated object setter code if passed the same object as the one already cached in the association. Usually not set (so nil), can be set on a per-object basis if necessary.
# File lib/sequel/model/associations.rb 2691 def set_associated_object_if_same? 2692 @set_associated_object_if_same 2693 end
Set the given object as the associated object for the given one_through_one association reflection
# File lib/sequel/model/associations.rb 2702 def set_one_through_one_associated_object(opts, o) 2703 raise(Error, "object #{inspect} does not have a primary key") unless pk 2704 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2705 _set_associated_object(opts, o) 2706 end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb 2709 def set_one_to_one_associated_object(opts, o) 2710 raise(Error, "object #{inspect} does not have a primary key") unless pk 2711 _set_associated_object(opts, o) 2712 end