lib/sequel/plugins/many_through_many.rb in sequel-3.34.1 vs lib/sequel/plugins/many_through_many.rb in sequel-3.35.0

- old
+ new

@@ -45,46 +45,29 @@ module ManyThroughMany # The AssociationReflection subclass for many_through_many associations. class ManyThroughManyAssociationReflection < Sequel::Model::Associations::ManyToManyAssociationReflection Sequel::Model::Associations::ASSOCIATION_TYPES[:many_through_many] = self - # The table containing the column to use for the associated key when eagerly loading - def associated_key_table - self[:associated_key_table] = self[:final_reverse_edge][:alias] - end - # The default associated key alias(es) to use when eager loading # associations via eager. def default_associated_key_alias self[:uses_left_composite_keys] ? (0...self[:through].first[:left].length).map{|i| :"x_foreign_key_#{i}_x"} : :x_foreign_key_x end - # The hash key to use for the eager loading predicate (left side of IN (1, 2, 3)) - def eager_loading_predicate_key - self[:eager_loading_predicate_key] ||= begin - calculate_edges - e = self[:edges].first - f = self[:final_reverse_edge] - qualify(f[:alias], e[:right]) - end + %w'associated_key_table eager_loading_predicate_key edges final_edge final_reverse_edge reverse_edges'.each do |meth| + class_eval(<<-END, __FILE__, __LINE__+1) + def #{meth} + cached_fetch(:#{meth}){calculate_edges[:#{meth}]} + end + END end - - # The list of joins to use when eager graphing - def edges - self[:edges] || calculate_edges || self[:edges] - end # Many through many associations don't have a reciprocal def reciprocal nil end - # The list of joins to use when lazy loading or eager loading - def reverse_edges - self[:reverse_edges] || calculate_edges || self[:reverse_edges] - end - private # Make sure to use unique table aliases when lazy loading or eager loading def calculate_reverse_edge_aliases(reverse_edges) aliases = [associated_class.table_name] @@ -117,15 +100,22 @@ h end reverse_edges = es.reverse.map{|e| {:table=>e[:left_table], :left=>e[:left_key], :right=>e[:right_key]}} reverse_edges.pop calculate_reverse_edge_aliases(reverse_edges) - self[:final_edge] = edges.pop - self[:final_reverse_edge] = reverse_edges.pop - self[:edges] = edges - self[:reverse_edges] = reverse_edges - nil + final_reverse_edge = reverse_edges.pop + final_reverse_alias = final_reverse_edge[:alias] + + h = {:final_edge=>edges.pop, + :final_reverse_edge=>final_reverse_edge, + :edges=>edges, + :reverse_edges=>reverse_edges, + :eager_loading_predicate_key=>qualify(final_reverse_alias, edges.first[:right]), + :associated_key_table=>final_reverse_edge[:alias], + } + h.each{|k, v| cached_set(k, v)} + h end end module ClassMethods # Create a many_through_many association. Arguments: @@ -183,22 +173,22 @@ left_pk = (opts[:left_primary_key] ||= self.primary_key) left_pks = opts[:left_primary_keys] = Array(left_pk) opts[:dataset] ||= lambda do ds = opts.associated_class opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])} - ft = opts[:final_reverse_edge] + ft = opts.final_reverse_edge ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + left_keys.zip(left_pks.map{|k| send(k)}), :table_alias=>ft[:alias]) end left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias opts[:eager_loader] ||= lambda do |eo| h = eo[:key_hash][left_pk] rows = eo[:rows] rows.each{|object| object.associations[name] = []} ds = opts.associated_class opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])} - ft = opts[:final_reverse_edge] + ft = opts.final_reverse_edge ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + [[opts.eager_loading_predicate_key, h.keys]], :table_alias=>ft[:alias]) ds = model.eager_loading_dataset(opts, ds, nil, eo[:associations], eo) case opts.eager_limit_strategy when :window_function delete_rn = true @@ -238,11 +228,11 @@ iq = eo[:implicit_qualifier] opts.edges.each do |t| 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=>t[:join_type], :implicit_qualifier=>iq, &t[:block]) iq = nil end - fe = opts[:final_edge] + fe = opts.final_edge ds.graph(opts.associated_class, use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>eo[:table_alias], :join_type=>join_type, &graph_block) end def_association_dataset_methods(opts) end @@ -267,10 +257,10 @@ last_join = ds.opts[:join].last last_join.table_alias || last_join.table end meths = ref.right_primary_keys meths = ref.qualify(obj.model.table_name, meths) if obj.is_a?(Sequel::Dataset) - exp = association_filter_key_expression(ref.qualify(last_alias, Array(ref[:final_edge][:left])), meths, obj) + exp = association_filter_key_expression(ref.qualify(last_alias, Array(ref.final_edge[:left])), meths, obj) if exp == SQL::Constants::FALSE association_filter_handle_inversion(op, exp, Array(lpks)) else ds = ds.where(exp).exclude(SQL::BooleanExpression.from_value_pairs(ds.opts[:select].zip([]), :OR)) association_filter_handle_inversion(op, SQL::BooleanExpression.from_value_pairs(lpks=>ds), Array(lpks))