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 2324 def associations 2325 @associations ||= {} 2326 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 2331 def freeze 2332 associations 2333 super 2334 associations.freeze 2335 self 2336 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 2341 def _apply_association_options(opts, ds) 2342 unless ds.kind_of?(AssociationDatasetMethods) 2343 ds = opts.apply_dataset_changes(ds) 2344 end 2345 ds = ds.clone(:model_object => self) 2346 ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? 2347 # block method is private 2348 ds = send(opts[:block_method], ds) if opts[:block_method] 2349 ds 2350 end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb 2353 def _associated_dataset(opts, dynamic_opts) 2354 ds = public_send(opts.dataset_method) 2355 if callback = dynamic_opts[:callback] 2356 ds = callback.call(ds) 2357 end 2358 ds 2359 end
A placeholder literalizer that can be used to load the association, or nil to not use one.
# File lib/sequel/model/associations.rb 2362 def _associated_object_loader(opts, dynamic_opts) 2363 if !dynamic_opts[:callback] && (loader = opts.placeholder_loader) 2364 loader 2365 end 2366 end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb 2369 def _dataset(opts) 2370 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2371 ds = if opts[:dataset_opt_arity] == 1 2372 # dataset_opt_method is private 2373 send(opts[:dataset_opt_method], opts) 2374 else 2375 send(opts[:dataset_opt_method]) 2376 end 2377 _apply_association_options(opts, ds) 2378 end
Dataset
for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb 2381 def _join_table_dataset(opts) 2382 ds = model.db.from(opts.join_table_source) 2383 opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds 2384 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 2388 def _load_associated_object(opts, dynamic_opts) 2389 _load_associated_object_array(opts, dynamic_opts).first 2390 end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb 2399 def _load_associated_object_array(opts, dynamic_opts) 2400 if loader = _associated_object_loader(opts, dynamic_opts) 2401 loader.all(*opts.predicate_key_values(self)) 2402 else 2403 _associated_dataset(opts, dynamic_opts).all 2404 end 2405 end
Return the associated single object using a primary key lookup on the associated class.
# File lib/sequel/model/associations.rb 2393 def _load_associated_object_via_primary_key(opts) 2394 opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk))) 2395 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 2409 def _load_associated_objects(opts, dynamic_opts=OPTS) 2410 if opts.can_have_associated_objects?(self) 2411 if opts.returns_array? 2412 _load_associated_object_array(opts, dynamic_opts) 2413 elsif load_with_primary_key_lookup?(opts, dynamic_opts) 2414 _load_associated_object_via_primary_key(opts) 2415 else 2416 _load_associated_object(opts, dynamic_opts) 2417 end 2418 elsif opts.returns_array? 2419 [] 2420 end 2421 end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb 2424 def _refresh_set_values(hash) 2425 @associations.clear if @associations 2426 super 2427 end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb 2666 def _set_associated_object(opts, o) 2667 a = associations[opts[:name]] 2668 reciprocal = opts.reciprocal 2669 if set_associated_object_if_same? 2670 if reciprocal 2671 remove_reciprocal = a && (a != o || a.associations[reciprocal] != self) 2672 add_reciprocal = o && o.associations[reciprocal] != self 2673 end 2674 else 2675 return if a && a == o 2676 if reciprocal 2677 remove_reciprocal = a 2678 add_reciprocal = o 2679 end 2680 end 2681 run_association_callbacks(opts, :before_set, o) 2682 remove_reciprocal_object(opts, a) if remove_reciprocal 2683 # Allow calling private _setter method 2684 send(opts[:_setter_method], o) 2685 associations[opts[:name]] = o 2686 add_reciprocal_object(opts, o) if add_reciprocal 2687 run_association_callbacks(opts, :after_set, o) 2688 o 2689 end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb 2430 def add_associated_object(opts, o, *args) 2431 o = make_add_associated_object(opts, o) 2432 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2433 ensure_associated_primary_key(opts, o, *args) 2434 return if run_association_callbacks(opts, :before_add, o) == false 2435 # Allow calling private _add method 2436 return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure? 2437 if array = associations[opts[:name]] and !array.include?(o) 2438 array.push(o) 2439 end 2440 add_reciprocal_object(opts, o) 2441 run_association_callbacks(opts, :after_add, o) 2442 o 2443 end
Add/Set the current object to/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2449 def add_reciprocal_object(opts, o) 2450 return if o.frozen? 2451 return unless reciprocal = opts.reciprocal 2452 if opts.reciprocal_array? 2453 if array = o.associations[reciprocal] and !array.include?(self) 2454 array.push(self) 2455 end 2456 else 2457 o.associations[reciprocal] = self 2458 end 2459 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 2463 def array_uniq!(a) 2464 a.uniq! 2465 end
If a foreign key column value changes, clear the related cached associations.
# File lib/sequel/model/associations.rb 2469 def change_column_value(column, value) 2470 if assocs = model.autoreloading_associations[column] 2471 vals = @values 2472 if new? 2473 # Do deeper checking for new objects, so that associations are 2474 # not deleted when values do not change. This code is run at 2475 # a higher level for existing objects. 2476 if value == (c = vals[column]) && value.class == c.class 2477 # If the value is the same, there is no reason to delete 2478 # the related associations, so exit early in that case. 2479 return super 2480 end 2481 2482 only_delete_nil = c.nil? 2483 elsif vals[column].nil? 2484 only_delete_nil = true 2485 end 2486 2487 if only_delete_nil 2488 # If the current foreign key value is nil, but the association 2489 # is already present in the cache, it was probably added to the 2490 # cache for a reason, and we do not want to delete it in that case. 2491 # However, we still want to delete associations with nil values 2492 # to remove the cached false negative. 2493 assocs.each{|a| associations.delete(a) if associations[a].nil?} 2494 else 2495 assocs.each{|a| associations.delete(a)} 2496 end 2497 end 2498 super 2499 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 2504 def ensure_associated_primary_key(opts, o, *args) 2505 if opts.need_associated_primary_key? 2506 o.save(:validate=>opts[:validate]) if o.new? 2507 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk 2508 end 2509 end
Duplicate the associations hash when duplicating the object.
# File lib/sequel/model/associations.rb 2512 def initialize_copy(other) 2513 super 2514 @associations = Hash[@associations] if @associations 2515 self 2516 end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb 2529 def load_associated_objects(opts, dynamic_opts, &block) 2530 dynamic_opts = load_association_objects_options(dynamic_opts, &block) 2531 name = opts[:name] 2532 if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload] 2533 associations[name] 2534 else 2535 objs = _load_associated_objects(opts, dynamic_opts) 2536 if opts.set_reciprocal_to_self? 2537 if opts.returns_array? 2538 objs.each{|o| add_reciprocal_object(opts, o)} 2539 elsif objs 2540 add_reciprocal_object(opts, objs) 2541 end 2542 end 2543 2544 # If the current object is frozen, you can't update the associations 2545 # cache. This can cause issues for after_load procs that expect 2546 # the objects to be already cached in the associations, but 2547 # unfortunately that case cannot be handled. 2548 associations[name] = objs unless frozen? 2549 run_association_callbacks(opts, :after_load, objs) 2550 frozen? ? objs : associations[name] 2551 end 2552 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 2519 def load_association_objects_options(dynamic_opts, &block) 2520 if block 2521 dynamic_opts = Hash[dynamic_opts] 2522 dynamic_opts[:callback] = block 2523 end 2524 2525 dynamic_opts 2526 end
Whether to use a simple primary key lookup on the associated class when loading.
# File lib/sequel/model/associations.rb 2555 def load_with_primary_key_lookup?(opts, dynamic_opts) 2556 opts[:type] == :many_to_one && 2557 !dynamic_opts[:callback] && 2558 opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key} 2559 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 2565 def make_add_associated_object(opts, o) 2566 klass = opts.associated_class 2567 2568 case o 2569 when Hash 2570 klass.new(o) 2571 when Integer, String, Array 2572 klass.with_pk!(o) 2573 when klass 2574 o 2575 else 2576 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2577 end 2578 end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb 2581 def remove_all_associated_objects(opts, *args) 2582 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2583 # Allow calling private _remove_all method 2584 send(opts[:_remove_all_method], *args) 2585 ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) 2586 associations[opts[:name]] = [] 2587 ret 2588 end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb 2594 def remove_associated_object(opts, o, *args) 2595 klass = opts.associated_class 2596 if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) 2597 o = remove_check_existing_object_from_pk(opts, o, *args) 2598 elsif !o.is_a?(klass) 2599 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2600 elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty? 2601 raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") 2602 end 2603 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2604 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk 2605 return if run_association_callbacks(opts, :before_remove, o) == false 2606 # Allow calling private _remove method 2607 return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure? 2608 associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) 2609 remove_reciprocal_object(opts, o) 2610 run_association_callbacks(opts, :after_remove, o) 2611 o 2612 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 2620 def remove_check_existing_object_from_pk(opts, o, *args) 2621 key = o 2622 pkh = opts.associated_class.qualified_primary_key_hash(key) 2623 raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh) 2624 o 2625 end
Remove/unset the current object from/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2628 def remove_reciprocal_object(opts, o) 2629 return unless reciprocal = opts.reciprocal 2630 if opts.reciprocal_array? 2631 if array = o.associations[reciprocal] 2632 array.delete_if{|x| self === x} 2633 end 2634 else 2635 o.associations[reciprocal] = nil 2636 end 2637 end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb 2640 def run_association_callbacks(reflection, callback_type, object) 2641 return unless cbs = reflection[callback_type] 2642 2643 begin 2644 cbs.each do |cb| 2645 case cb 2646 when Symbol 2647 # Allow calling private methods in association callbacks 2648 send(cb, object) 2649 when Proc 2650 cb.call(self, object) 2651 else 2652 raise Error, "callbacks should either be Procs or Symbols" 2653 end 2654 end 2655 rescue HookFailed 2656 # The reason we automatically set raise_error for singular associations is that 2657 # assignment in ruby always returns the argument instead of the result of the 2658 # method, so we can't return nil to signal that the association callback prevented 2659 # the modification 2660 return false unless raise_on_save_failure || !reflection.returns_array? 2661 raise 2662 end 2663 end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb 2699 def set_associated_object(opts, o) 2700 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2701 _set_associated_object(opts, o) 2702 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 2694 def set_associated_object_if_same? 2695 @set_associated_object_if_same 2696 end
Set the given object as the associated object for the given one_through_one association reflection
# File lib/sequel/model/associations.rb 2705 def set_one_through_one_associated_object(opts, o) 2706 raise(Error, "object #{inspect} does not have a primary key") unless pk 2707 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2708 _set_associated_object(opts, o) 2709 end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb 2712 def set_one_to_one_associated_object(opts, o) 2713 raise(Error, "object #{inspect} does not have a primary key") unless pk 2714 _set_associated_object(opts, o) 2715 end