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