Module | Sequel::Plugins::IdentityMap::ClassMethods
In: | lib/sequel/plugins/identity_map.rb

### Methods

associate call identity_map identity_map_key with_identity_map

### Public Instance methods

associate(type, name, opts = {}, &block)

Override the default :eager_loader option for many_*_many associations to work with an identity_map. If the :eager_graph association option is used, you‘ll probably have to use :uniq=>true on the current association amd :cartesian_product_number=>2 on the association mentioned by :eager_graph, otherwise you‘ll end up with duplicates because the row proc will be getting called multiple times for the same object. If you do have duplicates and you use :eager_graph, they‘ll probably be lost. Making that work correctly would require changing a lot of the core architecture, such as how graphing and eager graphing work.

[Source]

         # File lib/sequel/plugins/identity_map.rb, line 44
     44:         def associate(type, name, opts = {}, &block)
     45:           if opts[:eager_loader]
     46:             super
     47:           elsif type == :many_to_many
     48:             opts = super
     49:             el = opts[:eager_loader]
     50:             model = self
     51:             left_pk = opts[:left_primary_key]
     52:             uses_lcks = opts[:uses_left_composite_keys]
     53:             uses_rcks = opts[:uses_right_composite_keys]
     54:             right = opts[:right_key]
     55:             join_table = opts[:join_table]
     56:             left = opts[:left_key]
     57:             lcks = opts[:left_keys]
     58:             left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
     59:             opts[:eager_loader] = lambda do |eo|
     60:               return el.call(eo) unless model.identity_map
     61:               h = eo[:key_hash][left_pk]
     62:               eo[:rows].each{|object| object.associations[name] = []}
     63:               r = uses_rcks ? rcks.zip(opts.right_primary_keys) : [[right, opts.right_primary_key]]
     64:               l = uses_lcks ? [[lcks.map{|k| SQL::QualifiedIdentifier.new(join_table, k)}, h.keys]] : [[left, h.keys]]
     65:
     66:               # Replace the row proc to remove the left key alias before calling the previous row proc.
     67:               # Associate the value of the left key alias with the associated object (through its object_id).
     68:               # When loading the associated objects, lookup the left key alias value and associate the
     69:               # associated objects to the main objects if the left key alias value matches the left primary key
     70:               # value of the main object.
     71:               #
     72:               # The deleting of the left key alias from the hash before calling the previous row proc
     73:               # is necessary when an identity map is used, otherwise if the same associated object is returned more than
     74:               # once for the association, it won't know which of current objects to associate it to.
     75:               ds = opts.associated_class.inner_join(join_table, r + l)
     76:               pr = ds.row_proc
     77:               h2 = {}
     78:               ds.row_proc = proc do |hash|
     79:                 hash_key = if uses_lcks
     80:                   left_key_alias.map{|k| hash.delete(k)}
     81:                 else
     82:                   hash.delete(left_key_alias)
     83:                 end
     84:                 obj = pr.call(hash)
     85:                 (h2[obj.object_id] ||= []) << hash_key
     86:                 obj
     87:               end
     88:               model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo) .all do |assoc_record|
     89:                 if hash_keys = h2.delete(assoc_record.object_id)
     90:                   hash_keys.each do |hash_key|
     91:                     if objects = h[hash_key]
     92:                       objects.each{|object| object.associations[name].push(assoc_record)}
     93:                     end
     94:                   end
     95:                 end
     96:               end
     97:             end
     98:             opts
     99:           elsif type == :many_through_many
    100:             opts = super
    101:             el = opts[:eager_loader]
    102:             model = self
    103:             left_pk = opts[:left_primary_key]
    104:             left_key = opts[:left_key]
    105:             uses_lcks = opts[:uses_left_composite_keys]
    106:             left_keys = Array(left_key)
    107:             left_key_alias = opts[:left_key_alias]
    108:             opts[:eager_loader] = lambda do |eo|
    109:               return el.call(eo) unless model.identity_map
    110:               h = eo[:key_hash][left_pk]
    111:               eo[:rows].each{|object| object.associations[name] = []}
    112:               ds = opts.associated_class
    113:               opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
    114:               ft = opts[:final_reverse_edge]
    115:               conds = uses_lcks ? [[left_keys.map{|k| SQL::QualifiedIdentifier.new(ft[:table], k)}, h.keys]] : [[left_key, h.keys]]
    116:
    117:               # See above comment in many_to_many eager_loader
    118:               ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + conds, :table_alias=>ft[:alias])
    119:               pr = ds.row_proc
    120:               h2 = {}
    121:               ds.row_proc = proc do |hash|
    122:                 hash_key = if uses_lcks
    123:                   left_key_alias.map{|k| hash.delete(k)}
    124:                 else
    125:                   hash.delete(left_key_alias)
    126:                 end
    127:                 obj = pr.call(hash)
    128:                 (h2[obj.object_id] ||= []) << hash_key
    129:                 obj
    130:               end
    131:               model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo).all do |assoc_record|
    132:                 if hash_keys = h2.delete(assoc_record.object_id)
    133:                   hash_keys.each do |hash_key|
    134:                     if objects = h[hash_key]
    135:                       objects.each{|object| object.associations[name].push(assoc_record)}
    136:                     end
    137:                   end
    138:                 end
    139:               end
    140:             end
    141:             opts
    142:           else
    143:             super
    144:           end
    145:         end

call(row)

If the identity map is in use, check it for a current copy of the object. If a copy does not exist, create a new object and add it to the identity map. If a copy exists, add any values in the given row that aren‘t currently in the object to the object‘s values. This allows you to only request certain fields in an initial query, make modifications to some of those fields and request other, potentially overlapping fields in a new query, and not have the second query override fields you modified.

[Source]

         # File lib/sequel/plugins/identity_map.rb, line 166
    166:         def call(row)
    167:           return super unless idm = identity_map
    168:           if o = idm[identity_map_key(Array(primary_key).map{|x| row[x]})]
    169:             o.merge_db_update(row)
    170:           else
    171:             o = super
    172:             idm[identity_map_key(o.pk)] = o
    173:           end
    174:           o
    175:         end

identity_map()

Returns the current thread-local identity map. Should be a hash if there is an active identity map, and nil otherwise.

[Source]

         # File lib/sequel/plugins/identity_map.rb, line 149
    149:         def identity_map
    150:           Thread.current[:sequel_identity_map]
    151:         end

identity_map_key(pk)

The identity map key for an object of the current class with the given pk. May not always be correct for a class which uses STI.

[Source]

         # File lib/sequel/plugins/identity_map.rb, line 155
    155:         def identity_map_key(pk)
    156:           "#{self}:#{pk ? Array(pk).join(',') : "nil:#{rand}"}"
    157:         end

with_identity_map() {|if identity_map| ...}

Take a block and inside that block use an identity map to ensure a 1-1 correspondence of objects to the database row they represent.

[Source]

         # File lib/sequel/plugins/identity_map.rb, line 179
    179:         def with_identity_map
    180:           return yield if identity_map
    181:           begin
    182:             self.identity_map = {}
    183:             yield
    184:           ensure
    185:             self.identity_map = nil
    186:           end
    187:         end

[Validate]