Module: ActiveItem::Associations
- Extended by:
- ActiveSupport::Concern
- Includes:
- ModelLoader
- Included in:
- Base
- Defined in:
- lib/active_item/associations.rb
Overview
Provides has_many and belongs_to association macros with lazy loading, preloading support, and dependent record handling (destroy, nullify, restrict).
Instance Method Summary collapse
- #check_dependent_associations ⇒ Object
- #load_belongs_to_association(name) ⇒ Object
- #load_has_many_association(name) ⇒ Object
-
#load_has_many_through_association(name) ⇒ Object
Load a has_many :through association.
- #set_belongs_to_association(name, record) ⇒ Object
Methods included from ModelLoader
Instance Method Details
#check_dependent_associations ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/active_item/associations.rb', line 20 def check_dependent_associations self.class._associations.each do |name, config| next unless config[:type] == :has_many && config[:dependent] case config[:dependent] when :restrict_with_exception raise DeleteRestrictionError, name if send(name).limit(1).any? when :restrict_with_error if send(name).limit(1).any? = config[:message] || "Cannot delete #{self.class.name} because dependent #{name} exist" errors.add(:base, ) throw(:abort) end when :destroy send(name).each(&:destroy) when :delete_all send(name).each(&:delete) when :nullify foreign_key = config[:foreign_key] send(name).each { |record| record.update(foreign_key => nil) } end end end |
#load_belongs_to_association(name) ⇒ Object
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/active_item/associations.rb', line 354 def load_belongs_to_association(name) config = self.class._associations[name] return nil unless config cache_var = :"@_association_cache_#{name}" return instance_variable_get(cache_var) if instance_variable_defined?(cache_var) foreign_key_value = send(config[:foreign_key]) return nil if foreign_key_value.nil? klass = safe_constantize_model(config[:class_name]) begin record = klass.find(foreign_key_value) rescue ActiveItem::RecordNotFound raise unless config[:optional] record = nil end instance_variable_set(cache_var, record) record end |
#load_has_many_association(name) ⇒ Object
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/active_item/associations.rb', line 301 def load_has_many_association(name) config = self.class._associations[name] return Relation.new(Object, conditions: { _empty: true }) unless config return Relation.new(nil, preloaded_records: _preloaded_associations[name], class_name: config[:class_name]) if _preloaded_associations.key?(name) local_key_value = send(config[:primary_key]) return Relation.new(nil, conditions: { _empty: true }, class_name: config[:class_name]) if local_key_value.nil? conditions = { config[:foreign_key].to_sym => local_key_value } relation = Relation.new(nil, conditions: conditions, index_name: config[:index], class_name: config[:class_name], owner: self) if config[:scope] if config[:scope].arity.zero? relation.instance_exec(&config[:scope]) || relation else config[:scope].call(relation) end else relation end end |
#load_has_many_through_association(name) ⇒ Object
Load a has_many :through association. Two queries: GSI query on join table → batch_find on target table.
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/active_item/associations.rb', line 327 def load_has_many_through_association(name) config = self.class._associations[name] return Relation.new(Object, conditions: { _empty: true }) unless config # If preloaded (via includes), return cached records return Relation.new(nil, preloaded_records: _preloaded_associations[name], class_name: config[:class_name]) if _preloaded_associations.key?(name) through_name = config[:through] source_name = config[:source] target_class = safe_constantize_model(config[:class_name]) # Query 1: Load intermediate records via the through association intermediate_records = send(through_name).to_a # Determine the foreign key on the join model that points to the target # Convention: source_name + "_id" (e.g., :author → "author_id") target_foreign_key = "#{source_name}_id" target_ids = intermediate_records.filter_map { |r| r.send(target_foreign_key) }.uniq return Relation.new(nil, preloaded_records: [], class_name: config[:class_name]) if target_ids.empty? # Query 2: Batch load target records target_records = target_class.batch_find(target_ids) Relation.new(nil, preloaded_records: target_records, class_name: config[:class_name]) end |
#set_belongs_to_association(name, record) ⇒ Object
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
# File 'lib/active_item/associations.rb', line 377 def set_belongs_to_association(name, record) config = self.class._associations[name] return unless config cache_var = :"@_association_cache_#{name}" instance_variable_set(cache_var, record) if record.nil? send("#{config[:foreign_key]}=", nil) else pk = config[:primary_key] || record.class.primary_key send("#{config[:foreign_key]}=", record.send(pk)) end hook_method = :"after_set_#{name}_association" send(hook_method, record) if respond_to?(hook_method, true) end |