Module: Sequel::Plugins::ThroughAssociations::ClassMethods
- Defined in:
- lib/sequel/plugins/through_associations.rb
Instance Method Summary collapse
-
#associate_through(type, name, opts, &block) ⇒ Object
Associates a related model with the current model using another association as the intermediary.
-
#find_association_path(**opts) ⇒ Object
Recurses through associations until a path to the destination is completed.
Instance Method Details
#associate_through(type, name, opts, &block) ⇒ Object
Associates a related model with the current model using another association as the intermediary.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/sequel/plugins/through_associations.rb', line 44 def associate_through type, name, opts, &block unless assoc_type = Sequel.synchronize{ASSOCIATION_THROUGH_TYPES[type]} raise Error, "#{type} does not support through associations" end result = find_association_path(**opts, name: name, models: self, from_through: true) # Remove the last table if it matches the destination table dest_model = result[:models].pop result[:tables].pop if result[:tables].last == dest_model.table_name # Build the association path path = [] left_key = result[:keys].shift result[:tables].each do |table| path.push [table, result[:keys].shift, result[:keys].shift] end # Create the association if assoc_type.to_s.end_with? "_through_many" # *_through_many has a path argument self.send(assoc_type, name, path, left_primary_key: left_key, right_primary_key: result[:keys].shift, class: dest_model, **opts, originally_through: opts[:through], &block ) else # *_through_one does not have a path argument self.send(assoc_type, name, left_primary_key: left_key, right_primary_key: result[:keys].shift, class: dest_model, **opts, originally_through: opts[:through], &block ) end end |
#find_association_path(**opts) ⇒ Object
Recurses through associations until a path to the destination is completed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/sequel/plugins/through_associations.rb', line 91 def find_association_path **opts # Initialize arguments [:tables, :keys, :through, :models, :assocs].each do |k| opts[k] ||= [] opts[k] = [opts[k]] unless Array === opts[k] opts[k] = opts[k].dup end # Find the linked association assoc = \ opts[:models].last.association_reflection(opts[:through].last.to_s.pluralize.to_sym) \ || opts[:models].last.association_reflection(opts[:through].last.to_s.singularize.to_sym) # Short circuit if association does not exist unless assoc # Determine if finished or if the last relation is missing if opts[:from_through] m = opts[:models].pop t = opts[:through].pop path = [m] opts[:models].zip(opts[:through]).each do |model, through| path.push "#{model}.#{through}" end raise MissingAssociation, "#{m} is missing through association :#{t} from #{path.join " -> "}" else if opts[:assocs].last[:name].to_s.singularize != (opts[:using] || opts[:name]).to_s.singularize text = "#{opts[:models].first}.#{opts[:name]} could not be resolved through path #{opts[:models].zip(opts[:through]).map{|model, through| "#{model}.#{through}"}.join " -> "}" raise MissingAssociation, text end return opts end end # Store the association opts[:assocs].push assoc # Handle *_through_many associations if assoc[:type].to_s.end_with? "_through_many" opts[:through].push assoc[:originally_through] opts[:from_through] = true # Search through the existing model first, falling back to the associated model search = [ opts[:models].last, assoc[:class] || assoc[:class_name].constantize ] return begin model = search.shift raise NoAssociationPath, opts unless model self.find_association_path(**opts, models: opts[:models] + [model]) rescue MissingAssociation # Try the next model in the search path retry end end # Move to the new model opts[:models].push assoc[:class] || assoc[:class_name].constantize # Read the through association if present if assoc[:through] opts[:through].push assoc[:using] || assoc[:through] opts[:from_through] = true opts[:using] = nil return self.find_association_path(**opts) end # Otherwise, add the new table to the stack if opts[:from_through] && opts[:models].last.respond_to?(:cti_tables) opts[:tables].push opts[:models].last.cti_tables.first else opts[:tables].push opts[:models].last.table_name end # Left side case assoc[:type] when :one_to_many, :one_to_one # 1:_ opts[:keys].push assoc.primary_key when :many_to_one # n:_ opts[:keys].push assoc[:key] else raise end # Right side case assoc[:type] when :many_to_one, :one_to_one # _:1 opts[:keys].push assoc.primary_key when :one_to_many # _:n opts[:keys].push assoc[:key] else raise end # Check for a source association opts[:through].push opts[:using] || opts[:name] opts[:from_through] = false return self.find_association_path(**opts) end |