Module: ProductTaxonomy::Indexed

Included in:
Attribute, Category, Disclosure, ReturnReason, Value
Defined in:
lib/product_taxonomy/models/mixins/indexed.rb

Overview

Mixin providing indexing for a model, using hashes to support fast uniqueness checks and lookups by field value.

Defined Under Namespace

Classes: UniquenessValidator

Constant Summary collapse

NotFoundError =
Class.new(StandardError)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(indexed_model_class) ⇒ Object

Keep track of which model class used "extend Indexed" so that the model can be subclassed while still storing model objects in a shared index on the superclass.



11
12
13
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 11

def extended(indexed_model_class)
  indexed_model_class.instance_variable_set(:@is_indexed, true)
end

Instance Method Details

#add(model) ⇒ Object

Add a model to the index.

Parameters:

  • model (Object)

    The model to add to the index.



28
29
30
31
32
33
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 28

def add(model)
  hashed_models.keys.each do |field|
    hashed_models[field][model.send(field)] ||= []
    hashed_models[field][model.send(field)] << model
  end
end

#allArray<Object>

Get all models in the index.

Returns:

  • (Array<Object>)

    All models in the index.



100
101
102
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 100

def all
  hashed_models.first[1].values.flatten
end

#create_validate_and_add!Object

Convenience method to create a model, validate it, and add it to the index. If the model is not valid, raise an error.

Parameters:

  • args (Array)

    The arguments to pass to the model's constructor.

Returns:

  • (Object)

    The created model.



40
41
42
43
44
45
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 40

def create_validate_and_add!(...)
  model = new(...)
  model.validate!(:create)
  add(model)
  model
end

#duplicate?(model:, field:) ⇒ Boolean

Check if a field value is a duplicate of an existing model. A model is considered to be a duplicate if it was added after the first model with that value.

Parameters:

  • model (Object)

    The model to check for uniqueness.

  • field (Symbol)

    The field to check for uniqueness.

Returns:

  • (Boolean)

    Whether the value is unique.

Raises:

  • (ArgumentError)


53
54
55
56
57
58
59
60
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 53

def duplicate?(model:, field:)
  raise ArgumentError, "Field not hashed: #{field}" unless hashed_models.key?(field)

  existing_models = hashed_models[field][model.send(field)]
  return false if existing_models.nil?

  existing_models.first != model
end

#find_by(**conditions) ⇒ Object?

Find a model by field value. Returns the first matching record or nil. Only works for fields marked unique.

Parameters:

  • conditions (Hash)

    Hash of field-value pairs to search by

Returns:

  • (Object, nil)

    The matching model or nil if not found

Raises:

  • (ArgumentError)


66
67
68
69
70
71
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 66

def find_by(**conditions)
  field, value = conditions.first
  raise ArgumentError, "Field not hashed: #{field}" unless hashed_models.key?(field)

  hashed_models[field][value]&.first
end

#find_by!(**conditions) ⇒ Object

Find a model by field value. Returns the first matching record or raises an error if not found. Only works for fields marked unique.

Parameters:

  • conditions (Hash)

    Hash of field-value pairs to search by

Returns:

  • (Object)

    The matching model

Raises:



78
79
80
81
82
83
84
85
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 78

def find_by!(**conditions)
  record = find_by(**conditions)
  return record if record

  field, value = conditions.first
  field = field.to_s.humanize(capitalize: false, keep_id_suffix: true)
  raise NotFoundError, "#{self.name.demodulize} with #{field} \"#{value}\" not found"
end

#hashed_by(field) ⇒ Hash

Get the hash of models indexed by a given field. Only works for fields marked unique.

Parameters:

  • field (Symbol)

    The field to get the hash for.

Returns:

  • (Hash)

    The hash of models indexed by the given field.

Raises:

  • (ArgumentError)


91
92
93
94
95
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 91

def hashed_by(field)
  raise ArgumentError, "Field not hashed: #{field}" unless hashed_models.key?(field)

  hashed_models[field]
end

#hashed_modelsHash

Get the hash of hash of models, indexed by fields marked unique.

Returns:

  • (Hash)

    The hash of hash of models indexed by the fields marked unique.



114
115
116
117
118
119
120
121
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 114

def hashed_models
  # If we're a subclass of the model class that originally extended Indexed, use the parent class' hashed_models.
  return superclass.hashed_models unless @is_indexed

  @hashed_models ||= unique_fields.each_with_object({}) do |field, hash|
    hash[field] = {}
  end
end

#sizeInteger

Get the number of models in the index.

Returns:

  • (Integer)

    The number of models in the index.



107
108
109
# File 'lib/product_taxonomy/models/mixins/indexed.rb', line 107

def size
  all.size
end