/usr/lib/ruby/vendor_ruby/sequel/plugins/association_pks.rb is in ruby-sequel 4.15.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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 89 90 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 | module Sequel
module Plugins
# The association_pks plugin adds the association_pks and association_pks=
# instance methods to the model class for each association added. These
# methods allow for easily returning the primary keys of the associated
# objects, and easily modifying the associated objects to set the primary
# keys to just the ones given:
#
# Artist.one_to_many :albums
# artist = Artist[1]
# artist.album_pks # [1, 2, 3]
# artist.album_pks = [2, 4]
# artist.album_pks # [2, 4]
#
# Note that it uses the singular form of the association name. Also note
# that the setter both associates to new primary keys not in the assocation
# and disassociates from primary keys not provided to the method.
#
# This plugin makes modifications directly to the underlying tables,
# it does not create or return any model objects, and therefore does
# not call any callbacks. If you have any association callbacks,
# you probably should not use the setter methods.
#
# Usage:
#
# # Make all model subclass *_to_many associations have association_pks
# # methods (called before loading subclasses)
# Sequel::Model.plugin :association_pks
#
# # Make the Album *_to_many associations have association_pks
# # methods (called before the association methods)
# Album.plugin :association_pks
module AssociationPks
module ClassMethods
private
# Define a association_pks method using the block for the association reflection
def def_association_pks_getter(opts, &block)
association_module_def(:"#{singularize(opts[:name])}_pks", opts, &block)
end
# Define a association_pks= method using the block for the association reflection,
# if the association is not read only.
def def_association_pks_setter(opts, &block)
association_module_def(:"#{singularize(opts[:name])}_pks=", opts, &block) unless opts[:read_only]
end
# Add a getter that checks the join table for matching records and
# a setter that deletes from or inserts into the join table.
def def_many_to_many(opts)
super
return if opts[:type] == :one_through_one
# Grab values from the reflection so that the hash lookup only needs to be
# done once instead of inside ever method call.
lk, lpk, rk = opts.values_at(:left_key, :left_primary_key, :right_key)
clpk = lpk.is_a?(Array)
crk = rk.is_a?(Array)
if clpk
def_association_pks_getter(opts) do
h = {}
lk.zip(lpk).each{|k, pk| h[k] = send(pk)}
_join_table_dataset(opts).filter(h).select_map(rk)
end
else
def_association_pks_getter(opts) do
_join_table_dataset(opts).filter(lk=>send(lpk)).select_map(rk)
end
end
def_association_pks_setter(opts) do |pks|
pks = send(crk ? :convert_cpk_array : :convert_pk_array, opts, pks)
checked_transaction do
if clpk
lpkv = lpk.map{|k| send(k)}
cond = lk.zip(lpkv)
else
lpkv = send(lpk)
cond = {lk=>lpkv}
end
ds = _join_table_dataset(opts).filter(cond)
ds.exclude(rk=>pks).delete
pks -= ds.select_map(rk)
lpkv = Array(lpkv)
key_array = crk ? pks.map{|pk| lpkv + pk} : pks.map{|pk| lpkv + [pk]}
key_columns = Array(lk) + Array(rk)
ds.import(key_columns, key_array)
end
end
end
# Add a getter that checks the association dataset and a setter
# that updates the associated table.
def def_one_to_many(opts)
super
return if opts[:type] == :one_to_one
key = opts[:key]
def_association_pks_getter(opts) do
send(opts.dataset_method).select_map(opts.associated_class.primary_key)
end
def_association_pks_setter(opts) do |pks|
primary_key = opts.associated_class.primary_key
pks = if primary_key.is_a?(Array)
convert_cpk_array(opts, pks)
else
convert_pk_array(opts, pks)
end
pkh = {primary_key=>pks}
if key.is_a?(Array)
h = {}
nh = {}
key.zip(pk).each do|k, v|
h[k] = v
nh[k] = nil
end
else
h = {key=>pk}
nh = {key=>nil}
end
checked_transaction do
ds = send(opts.dataset_method)
ds.unfiltered.filter(pkh).update(h)
ds.exclude(pkh).update(nh)
end
end
end
end
module InstanceMethods
private
# If any of associated class's composite primary key column types is integer,
# typecast the appropriate values to integer before using them.
def convert_cpk_array(opts, cpks)
if klass = opts.associated_class and sch = klass.db_schema and (cols = sch.values_at(*klass.primary_key)).all? and (convs = cols.map{|c| c[:type] == :integer}).any?
cpks.map do |cpk|
cpk.zip(convs).map do |pk, conv|
conv ? model.db.typecast_value(:integer, pk) : pk
end
end
else
cpks
end
end
# If the associated class's primary key column type is integer,
# typecast all provided values to integer before using them.
def convert_pk_array(opts, pks)
if klass = opts.associated_class and sch = klass.db_schema and col = sch[klass.primary_key] and col[:type] == :integer
pks.map{|pk| model.db.typecast_value(:integer, pk)}
else
pks
end
end
end
end
end
end
|