module Sequel::Plugins::ManyThroughMany::ClassMethods
Public Instance Methods
Source
# File lib/sequel/plugins/many_through_many.rb 226 def many_through_many(name, through, opts=OPTS, &block) 227 associate(:many_through_many, name, opts.merge(through.is_a?(Hash) ? through : {:through=>through}), &block) 228 end
Create a many_through_many
association. Arguments:
- name
-
Same as associate, the name of the association.
- through
-
The tables and keys to join between the current table and the associated table. Must be an array, with elements that are either 3 element arrays, or hashes with keys :table, :left, and :right. The required entries in the array/hash are:
- :table (first array element)
-
The name of the table to join.
- :left (middle array element)
-
The key joining the table to the previous table. Can use an array of symbols for a composite key association.
- :right (last array element)
-
The key joining the table to the next table. Can use an array of symbols for a composite key association.
If a hash is provided, the following keys are respected when using eager_graph:
- :db
-
The
Database
containing the table. This changes lookup to use a separate query for each join table. - :block
-
A proc to use as the block argument to join.
- :conditions
-
Extra conditions to add to the JOIN ON clause. Must be a hash or array of two pairs.
- :join_type
-
The join type to use for the join, defaults to :left_outer.
- :only_conditions
-
Conditions to use for the join instead of the ones specified by the keys.
- opts
-
The options for the associaion. Takes the same options as many_to_many.
Source
# File lib/sequel/plugins/many_through_many.rb 231 def one_through_many(name, through, opts=OPTS, &block) 232 associate(:one_through_many, name, opts.merge(through.is_a?(Hash) ? through : {:through=>through}), &block) 233 end
Creates a one_through_many
association. See many_through_many
for arguments.
Private Instance Methods
Source
# File lib/sequel/plugins/many_through_many.rb 238 def def_many_through_many(opts) 239 one_through_many = opts[:type] == :one_through_many 240 opts[:read_only] = true 241 if opts[:uniq] 242 opts[:after_load] ||= [] 243 opts[:after_load].unshift(:array_uniq!) 244 end 245 opts[:cartesian_product_number] ||= one_through_many ? 0 : 2 246 separate_query_per_table = false 247 through = opts[:through] = opts[:through].map do |e| 248 case e 249 when Array 250 raise(Error, "array elements of the through option/argument for many_through_many associations must have at least three elements") unless e.length == 3 251 {:table=>e[0], :left=>e[1], :right=>e[2]} 252 when Hash 253 raise(Error, "hash elements of the through option/argument for many_through_many associations must contain :table, :left, and :right keys") unless e[:table] && e[:left] && e[:right] 254 separate_query_per_table = true if e[:db] 255 e 256 else 257 raise(Error, "the through option/argument for many_through_many associations must be an enumerable of arrays or hashes") 258 end 259 end 260 opts[:separate_query_per_table] = separate_query_per_table 261 262 left_key = opts[:left_key] = opts[:through].first[:left] 263 opts[:left_keys] = Array(left_key) 264 uses_lcks = opts[:uses_left_composite_keys] = left_key.is_a?(Array) 265 left_pk = (opts[:left_primary_key] ||= self.primary_key) 266 raise(Error, "no primary key specified for #{inspect}") unless left_pk 267 opts[:eager_loader_key] = left_pk unless opts.has_key?(:eager_loader_key) 268 opts[:left_primary_keys] = Array(left_pk) 269 lpkc = opts[:left_primary_key_column] ||= left_pk 270 lpkcs = opts[:left_primary_key_columns] ||= Array(lpkc) 271 272 opts[:left_key_alias] ||= opts.default_associated_key_alias 273 if separate_query_per_table 274 opts[:use_placeholder_loader] = false 275 opts[:allow_eager_graph] = false 276 opts[:allow_filtering_by] = false 277 opts[:eager_limit_strategy] = nil 278 279 opts[:dataset] ||= proc do |r| 280 def_db = r.associated_class.db 281 vals = uses_lcks ? [lpkcs.map{|k| get_column_value(k)}] : get_column_value(left_pk) 282 283 has_results = through.each do |edge| 284 ds = (edge[:db] || def_db).from(edge[:table]).where(edge[:left]=>vals) 285 ds = ds.where(edge[:conditions]) if edge[:conditions] 286 right = edge[:right] 287 vals = ds.select_map(right) 288 if right.is_a?(Array) 289 vals.delete_if{|v| v.any?(&:nil?)} 290 else 291 vals.delete(nil) 292 end 293 break if vals.empty? 294 end 295 296 ds = r.associated_dataset.where(opts.right_primary_key=>vals) 297 ds = ds.clone(:no_results=>true) unless has_results 298 ds 299 end 300 opts[:eager_loader] ||= proc do |eo| 301 h = eo[:id_map] 302 assign_singular = opts.assign_singular? 303 uses_rcks = opts.right_primary_key.is_a?(Array) 304 rpk = uses_rcks ? opts.right_primary_keys : opts.right_primary_key 305 name = opts[:name] 306 def_db = opts.associated_class.db 307 join_map = h 308 309 run_query = through.each do |edge| 310 ds = (edge[:db] || def_db).from(edge[:table]) 311 ds = ds.where(edge[:conditions]) if edge[:conditions] 312 left = edge[:left] 313 right = edge[:right] 314 prev_map = join_map 315 join_map = ds.where(left=>join_map.keys).select_hash_groups(right, left) 316 if right.is_a?(Array) 317 join_map.delete_if{|v,| v.any?(&:nil?)} 318 else 319 join_map.delete(nil) 320 end 321 break if join_map.empty? 322 join_map.each_value do |vs| 323 vs.replace(vs.flat_map{|v| prev_map[v]}) 324 vs.uniq! 325 end 326 end 327 328 eo = Hash[eo] 329 330 if run_query 331 eo[:loader] = false 332 eo[:right_keys] = join_map.keys 333 else 334 eo[:no_results] = true 335 end 336 337 opts[:model].eager_load_results(opts, eo) do |assoc_record| 338 rpkv = if uses_rcks 339 assoc_record.values.values_at(*rpk) 340 else 341 assoc_record.values[rpk] 342 end 343 344 objects = join_map[rpkv] 345 346 if assign_singular 347 objects.each do |object| 348 object.associations[name] ||= assoc_record 349 end 350 else 351 objects.each do |object| 352 object.associations[name].push(assoc_record) 353 end 354 end 355 end 356 end 357 else 358 opts[:dataset] ||= opts.association_dataset_proc 359 opts[:eager_loader] ||= opts.method(:default_eager_loader) 360 end 361 362 join_type = opts[:graph_join_type] 363 select = opts[:graph_select] 364 graph_block = opts[:graph_block] 365 only_conditions = opts[:graph_only_conditions] 366 use_only_conditions = opts.include?(:graph_only_conditions) 367 conditions = opts[:graph_conditions] 368 opts[:eager_grapher] ||= proc do |eo| 369 ds = eo[:self] 370 iq = eo[:implicit_qualifier] 371 egls = eo[:limit_strategy] 372 if egls && egls != :ruby 373 associated_key_array = opts.associated_key_array 374 orig_egds = egds = eager_graph_dataset(opts, eo) 375 opts.reverse_edges.each{|t| egds = egds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias], :qualify=>:deep)} 376 ft = opts.final_reverse_edge 377 egds = egds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])), :table_alias=>ft[:alias], :qualify=>:deep). 378 select_all(egds.first_source). 379 select_append(*associated_key_array) 380 egds = opts.apply_eager_graph_limit_strategy(egls, egds) 381 ds.graph(egds, associated_key_array.map(&:alias).zip(Array(lpkcs)) + conditions, :qualify=>:deep, :table_alias=>eo[:table_alias], :implicit_qualifier=>iq, :join_type=>eo[:join_type]||join_type, :join_only=>eo[:join_only], :from_self_alias=>eo[:from_self_alias], :select=>select||orig_egds.columns, &graph_block) 382 else 383 opts.edges.each do |t| 384 ds = ds.graph(t[:table], t.fetch(:only_conditions, (Array(t[:right]).zip(Array(t[:left])) + t[:conditions])), :select=>false, :table_alias=>ds.unused_table_alias(t[:table]), :join_type=>eo[:join_type]||t[:join_type], :join_only=>eo[:join_only], :qualify=>:deep, :implicit_qualifier=>iq, :from_self_alias=>eo[:from_self_alias], &t[:block]) 385 iq = nil 386 end 387 fe = opts.final_edge 388 ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>eo[:table_alias], :qualify=>:deep, :join_type=>eo[:join_type]||join_type, :join_only=>eo[:join_only], &graph_block) 389 end 390 end 391 end
Create the association methods and :eager_loader and :eager_grapher procs.
Source
# File lib/sequel/plugins/many_through_many.rb 394 def def_one_through_many(opts) 395 def_many_through_many(opts) 396 end
Use def_many_through_many
, since they share pretty much the same code.