module Sequel::Plugins::PgArrayAssociations::ClassMethods

Public Instance Methods

many_to_pg_array(name, opts=OPTS, &block) click to toggle source

Create a #many_to_pg_array association, for the case where the associated table contains the array with foreign keys pointing to the current table. See associate for options.

# File lib/sequel/plugins/pg_array_associations.rb, line 182
def many_to_pg_array(name, opts=OPTS, &block)
  associate(:many_to_pg_array, name, opts, &block)
end
pg_array_to_many(name, opts=OPTS, &block) click to toggle source

Create a #pg_array_to_many association, for the case where the current table contains the array with foreign keys pointing to the associated table. See associate for options.

# File lib/sequel/plugins/pg_array_associations.rb, line 189
def pg_array_to_many(name, opts=OPTS, &block)
  associate(:pg_array_to_many, name, opts, &block)
end

Private Instance Methods

def_many_to_pg_array(opts) click to toggle source

Setup the many_to_pg_array-specific datasets, eager loaders, and modification methods.

# File lib/sequel/plugins/pg_array_associations.rb, line 196
def def_many_to_pg_array(opts)
  name = opts[:name]
  model = self
  pk = opts[:eager_loader_key] = opts[:primary_key] ||= model.primary_key
  opts[:key] = opts.default_key unless opts.has_key?(:key)
  key = opts[:key]
  key_column = opts[:key_column] ||= opts[:key]
  opts[:after_load].unshift(:array_uniq!) if opts[:uniq]
  slice_range = opts.slice_range
  opts[:dataset] ||= lambda do
    opts.associated_dataset.where(Sequel.pg_array_op(opts.predicate_key).contains([send(pk)]))
  end
  opts[:eager_loader] ||= proc do |eo|
    id_map = eo[:id_map]
    rows = eo[:rows]
    rows.each do |object|
      object.associations[name] = []
    end

    klass = opts.associated_class
    ds = model.eager_loading_dataset(opts, klass.where(Sequel.pg_array_op(opts.predicate_key).overlaps(id_map.keys)), nil, eo[:associations], eo)
    ds.all do |assoc_record|
      if pks ||= assoc_record.send(key)
        pks.each do |pkv|
          next unless objects = id_map[pkv]
          objects.each do |object| 
            object.associations[name].push(assoc_record)
          end
        end
      end
    end
    if slice_range
      rows.each{|o| o.associations[name] = o.associations[name][slice_range] || []}
    end
  end

  join_type = opts[:graph_join_type]
  select = opts[:graph_select]
  opts[:cartesian_product_number] ||= 1

  if opts.include?(:graph_only_conditions)
    conditions = opts[:graph_only_conditions]
    graph_block = opts[:graph_block]
  else
    conditions = opts[:graph_conditions]
    conditions = nil if conditions.empty?
    graph_block = proc do |j, lj, js|
      Sequel.pg_array_op(Sequel.deep_qualify(j, key_column)).contains([Sequel.deep_qualify(lj, opts.primary_key)])
    end

    if orig_graph_block = opts[:graph_block]
      pg_array_graph_block = graph_block
      graph_block = proc do |j, lj, js|
        Sequel.&(orig_graph_block.call(j,lj,js), pg_array_graph_block.call(j, lj, js))
      end
    end
  end

  opts[:eager_grapher] ||= proc do |eo|
    ds = eo[:self]
    ds = ds.graph(eager_graph_dataset(opts, eo), conditions, eo.merge(:select=>select, :join_type=>join_type, :qualify=>:deep, :from_self_alias=>ds.opts[:eager_graph][:master]), &graph_block)
    ds
  end

  def_association_dataset_methods(opts)

  unless opts[:read_only]
    validate = opts[:validate]

    array_type = opts[:array_type] ||= :integer
    adder = opts[:adder] || proc do |o|
      if array = o.send(key)
        array << send(pk)
      else
        o.send("#{key}=", Sequel.pg_array([send(pk)], array_type))
      end
      o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
    end
    association_module_private_def(opts._add_method, opts, &adder)
    
    remover = opts[:remover] || proc do |o|
      if (array = o.send(key)) && !array.empty?
        array.delete(send(pk))
        o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
      end
    end
    association_module_private_def(opts._remove_method, opts, &remover)

    clearer = opts[:clearer] || proc do
      opts.associated_dataset.where(Sequel.pg_array_op(key).contains([send(pk)])).update(key=>Sequel.function(:array_remove, key, send(pk)))
    end
    association_module_private_def(opts._remove_all_method, opts, &clearer)

    def_add_method(opts)
    def_remove_methods(opts)
  end
end
def_pg_array_to_many(opts) click to toggle source

Setup the pg_array_to_many-specific datasets, eager loaders, and modification methods.

# File lib/sequel/plugins/pg_array_associations.rb, line 295
def def_pg_array_to_many(opts)
  name = opts[:name]
  model = self
  opts[:key] = opts.default_key unless opts.has_key?(:key)
  key = opts[:key]
  key_column = opts[:key_column] ||= key
  opts[:eager_loader_key] = nil
  opts[:after_load].unshift(:array_uniq!) if opts[:uniq]
  slice_range = opts.slice_range
  opts[:dataset] ||= lambda do
    opts.associated_dataset.where(opts.predicate_key=>send(key).to_a)
  end
  opts[:eager_loader] ||= proc do |eo|
    rows = eo[:rows]
    id_map = {}
    pkm = opts.primary_key_method
    rows.each do |object|
      object.associations[name] = []
      if associated_pks = object.send(key)
        associated_pks.each do |apk|
          (id_map[apk] ||= []) << object
        end
      end
    end

    klass = opts.associated_class
    ds = model.eager_loading_dataset(opts, klass.where(opts.predicate_key=>id_map.keys), nil, eo[:associations], eo)
    ds.all do |assoc_record|
      if objects = id_map[assoc_record.send(pkm)]
        objects.each do |object| 
          object.associations[name].push(assoc_record)
        end
      end
    end
    if slice_range
      rows.each{|o| o.associations[name] = o.associations[name][slice_range] || []}
    end
  end

  join_type = opts[:graph_join_type]
  select = opts[:graph_select]
  opts[:cartesian_product_number] ||= 1

  if opts.include?(:graph_only_conditions)
    conditions = opts[:graph_only_conditions]
    graph_block = opts[:graph_block]
  else
    conditions = opts[:graph_conditions]
    conditions = nil if conditions.empty?
    graph_block = proc do |j, lj, js|
      Sequel.pg_array_op(Sequel.deep_qualify(lj, key_column)).contains([Sequel.deep_qualify(j, opts.primary_key)])
    end

    if orig_graph_block = opts[:graph_block]
      pg_array_graph_block = graph_block
      graph_block = proc do |j, lj, js|
        Sequel.&(orig_graph_block.call(j,lj,js), pg_array_graph_block.call(j, lj, js))
      end
    end
  end

  opts[:eager_grapher] ||= proc do |eo|
    ds = eo[:self]
    ds = ds.graph(eager_graph_dataset(opts, eo), conditions, eo.merge(:select=>select, :join_type=>join_type, :qualify=>:deep, :from_self_alias=>ds.opts[:eager_graph][:master]), &graph_block)
    ds
  end

  def_association_dataset_methods(opts)

  unless opts[:read_only]
    validate = opts[:validate]
    array_type = opts[:array_type] ||= :integer
    if opts[:save_after_modify]
      save_after_modify = proc do |obj|
        obj.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
      end
    end

    adder = opts[:adder] || proc do |o|
      opk = o.send(opts.primary_key) 
      if array = send(key)
        modified!(key)
        array << opk
      else
        send("#{key}=", Sequel.pg_array([opk], array_type))
      end
      save_after_modify.call(self) if save_after_modify
    end
    association_module_private_def(opts._add_method, opts, &adder)
    
    remover = opts[:remover] || proc do |o|
      if (array = send(key)) && !array.empty?
        modified!(key)
        array.delete(o.send(opts.primary_key))
        save_after_modify.call(self) if save_after_modify
      end
    end
    association_module_private_def(opts._remove_method, opts, &remover)

    clearer = opts[:clearer] || proc do
      if (array = send(key)) && !array.empty?
        modified!(key)
        array.clear
        save_after_modify.call(self) if save_after_modify
      end
    end
    association_module_private_def(opts._remove_all_method, opts, &clearer)

    def_add_method(opts)
    def_remove_methods(opts)
  end
end