Class: ActiveModelPersistence::Index

Inherits:
Object
  • Object
show all
Defined in:
lib/active_model_persistence/index.rb

Overview

An Index keeps a map from a key to zero of more objects

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, key_value_source: nil, unique: false) ⇒ Index

Create an Index

Examples:

An object that can be indexed must include Indexable (which includes PrimaryKey)

Employee = Struct.new(:id, :name, keyword_init: true)
  include ActiveModelPersistence::Indexable
end
e1 = Employee.new(id: 1, name: 'James')
e2 = Employee.new(id: 2, name: 'Frank')
e3 = Employee.new(id: 1, name: 'Margaret') # Note e1.id == e3.id
i = Index.new(name: 'id', key_source: :id, unique: true)
i.name # => 'id'
i.key_source # => :id -- get the key value by calling the 'id' method on object
i.unique # => true -- each key can only have one object associated with it

i.objects(e1.id) # => []
i.add(e1)
i.add(e2)
i.objects(e1.id) # => [e1]
i.objects(e2.id) # => [e2]
i.add(e3) # => raises a UniqueContraintError since e1.id == e3.id
i.add(e1) # => raises an ObjectAlreadyInIndexError

Parameters:

  • name (String)

    the name of the index

  • key_value_source (Symbol, Proc) (defaults to: nil)

    the attribute name or proc used to calculate the index key

  • unique (Boolean) (defaults to: false)

    when true the index will only allow one object per key



77
78
79
80
81
82
# File 'lib/active_model_persistence/index.rb', line 77

def initialize(name:, key_value_source: nil, unique: false)
  @name = name.to_s
  @key_value_source = determine_key_value_source(name, key_value_source)
  @unique = unique
  @key_to_objects_map = {}
end

Instance Attribute Details

#key_value_sourceSymbol, Proc (readonly)

Defines how the object's key value is calculated

If a proc is provided, it will be called with the object as an argument to get the key value.

If a symbol is provided, it will identify the method to call on the object to get the key value.

Examples:

i = Index.new(name: 'id', key_value_source: :id, unique: true)
i.key_value_source # => :id

Returns:

  • (Symbol, Proc)

    the method name or proc used to calculate the index key



30
31
32
# File 'lib/active_model_persistence/index.rb', line 30

def key_value_source
  @key_value_source
end

#nameString (readonly)

The name of the index

Examples:

i = Index.new(name: 'id', key_source: :id, unique: true)
i.name # => 'id'

Returns:

  • (String)

    The name of the index



16
17
18
# File 'lib/active_model_persistence/index.rb', line 16

def name
  @name
end

#uniqueBoolean (readonly) Also known as: unique?

Determines if a key value can index more than one object

The default value is false.

If true, if two objects have the same key, a UniqueContraintError will be raised when trying to add the second object.

Examples:

i = Index.new(name: 'id', key_value_source: :id, unique: true)
i.unique? # => true

Returns:

  • (Boolean)

    true if the index is unique



45
46
47
# File 'lib/active_model_persistence/index.rb', line 45

def unique
  @unique
end

Instance Method Details

#add_or_update(object) ⇒ Index Also known as: <<

Adds an object to the index

If the object was already in the index using a different key, remote the object from the index using the previous key before adding it again.

Examples:

i = Index.new(name: 'id', key_source: :id, unique: true)
e = Employee.new(id: 1, name: 'James')
i << e
i.objects(1) # => [e]
e.id = 2
i << e
i.objects(1) # => []
i.objects(2) # => [e]

Parameters:

  • object (Object)

    the object to add to the index

Returns:

  • (Index)

    self so calls can be chained

Raises:

  • (UniqueConstraintError)

    if the index is unique and there is already an index entry for the same key



143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/active_model_persistence/index.rb', line 143

def add_or_update(object)
  previous_key = object.previous_index_key(name)
  key = key_value_for(object)

  return if previous_key == key

  remove(object, previous_key) unless previous_key.nil?

  add(object, key) unless key.nil?

  self
end

#include?(key) ⇒ Boolean

Returns true if the index contains an object with the given key

Examples:

i = Index.new(name: 'id', key_source: :id, unique: true)
e = Employee.new(id: 1, name: 'James')
i.include?(e.id) # => false
i << e
i.include?(e.id) # => true

Parameters:

  • key (Object)

    the key to search for

Returns:

  • (Boolean)

    true if the index contains an object with the given key



117
118
119
# File 'lib/active_model_persistence/index.rb', line 117

def include?(key)
  key_to_objects_map.key?(key)
end

#objects(key) ⇒ Array<Object>

Returns the objects that match the key

A unique index will return an Array containing zero or one objects. A non-unique index will return an array containing zero or more objects.

Examples:

i = Index.new(name: 'id', key_source: :id, unique: true)
e = Employee.new(id: 1, name: 'James')
i.object(e.id) # => []
i.add(e.id, e)
i.object(e.id) # => [e]

Parameters:

  • key (Object)

    the key to search for

Returns:

  • (Array<Object>)

    the objects that match the key



100
101
102
# File 'lib/active_model_persistence/index.rb', line 100

def objects(key)
  key_to_objects_map[key] || []
end

#remove(object, key = nil) ⇒ void

This method returns an undefined value.

Removes an object from the index

Examples:

i = Index.new(name: 'id', key_source: :id, unique: true)
e = Employee.new(id: 1, name: 'James')
i << e
i.objects(1) # => [e]
i.remove(e)
i.objects(1) # => []

Parameters:

  • object (Object)

    the object to remove from the index

  • key (Object) (defaults to: nil)

    the object's key in the index If nil (the default), call the object.previous_index_key to get the key. `previous_index_key` is implemented by the Indexable concern.

Raises:



177
178
179
180
181
182
183
184
185
# File 'lib/active_model_persistence/index.rb', line 177

def remove(object, key = nil)
  key ||= object.previous_index_key(name)

  raise ActiveModelPersistence::ObjectNotInIndexError if key.nil?

  remove_object_from_index(object, key)

  nil
end

#remove_allvoid

This method returns an undefined value.

Removes all objects from the index

Examples:

i = Index.new(name: 'id', key_source: :id, unique: true)
e1 = Employee.new(id: 1, name: 'James')
e2 = Employee.new(id: 2, name: 'Frank')
i << e1 << e2
i.objects(1) # => [e1]
i.objects(2) # => [e2]
i.remove_all
i.objects(1) # => []
i.objects(2) # => []


202
203
204
205
206
207
208
209
210
# File 'lib/active_model_persistence/index.rb', line 202

def remove_all
  @key_to_objects_map.each_pair do |_key, objects|
    objects.each do |object|
      object.clear_index_key(name)
    end
  end
  @key_to_objects_map = {}
  nil
end