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
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/active_item/associations.rb', line 19 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
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/active_item/associations.rb', line 295 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
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/active_item/associations.rb', line 242 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.
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/active_item/associations.rb', line 268 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
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/active_item/associations.rb', line 318 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 |