familia 2.0.0.pre18 → 2.0.0.pre19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.rst +58 -6
- data/CLAUDE.md +34 -9
- data/Gemfile +2 -2
- data/Gemfile.lock +9 -47
- data/README.md +39 -0
- data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +91 -0
- data/changelog.d/20251011_203905_delano_next.rst +30 -0
- data/changelog.d/20251011_212633_delano_next.rst +13 -0
- data/changelog.d/20251011_221253_delano_next.rst +26 -0
- data/docs/guides/feature-expiration.md +18 -18
- data/docs/migrating/v2.0.0-pre19.md +197 -0
- data/examples/datatype_standalone.rb +281 -0
- data/lib/familia/connection/behavior.rb +252 -0
- data/lib/familia/connection/handlers.rb +95 -0
- data/lib/familia/connection/operation_core.rb +1 -1
- data/lib/familia/connection/{pipeline_core.rb → pipelined_core.rb} +2 -2
- data/lib/familia/connection/transaction_core.rb +7 -9
- data/lib/familia/connection.rb +3 -2
- data/lib/familia/data_type/connection.rb +151 -7
- data/lib/familia/data_type/database_commands.rb +7 -4
- data/lib/familia/data_type/serialization.rb +4 -0
- data/lib/familia/data_type/types/hashkey.rb +1 -1
- data/lib/familia/errors.rb +51 -14
- data/lib/familia/features/expiration/extensions.rb +8 -10
- data/lib/familia/features/expiration.rb +19 -19
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +39 -38
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +115 -43
- data/lib/familia/features/relationships/indexing.rb +37 -42
- data/lib/familia/features/relationships/indexing_relationship.rb +14 -4
- data/lib/familia/field_type.rb +2 -1
- data/lib/familia/horreum/connection.rb +11 -35
- data/lib/familia/horreum/database_commands.rb +129 -10
- data/lib/familia/horreum/definition.rb +2 -1
- data/lib/familia/horreum/management.rb +21 -15
- data/lib/familia/horreum/persistence.rb +190 -66
- data/lib/familia/horreum/serialization.rb +3 -0
- data/lib/familia/horreum/utils.rb +0 -8
- data/lib/familia/horreum.rb +31 -12
- data/lib/familia/logging.rb +2 -5
- data/lib/familia/settings.rb +7 -7
- data/lib/familia/version.rb +1 -1
- data/lib/middleware/database_logger.rb +76 -5
- data/try/edge_cases/string_coercion_try.rb +4 -4
- data/try/features/expiration/expiration_try.rb +1 -1
- data/try/features/relationships/indexing_try.rb +28 -4
- data/try/features/relationships/relationships_api_changes_try.rb +4 -4
- data/try/integration/connection/fiber_context_preservation_try.rb +3 -3
- data/try/integration/connection/operation_mode_guards_try.rb +1 -1
- data/try/integration/connection/pipeline_fallback_integration_try.rb +12 -12
- data/try/integration/create_method_try.rb +22 -22
- data/try/integration/data_types/datatype_pipelines_try.rb +104 -0
- data/try/integration/data_types/datatype_transactions_try.rb +247 -0
- data/try/integration/models/customer_safe_dump_try.rb +5 -1
- data/try/integration/models/familia_object_try.rb +1 -1
- data/try/integration/persistence_operations_try.rb +162 -10
- data/try/unit/data_types/boolean_try.rb +1 -1
- data/try/unit/data_types/string_try.rb +1 -1
- data/try/unit/horreum/auto_indexing_on_save_try.rb +32 -16
- data/try/unit/horreum/automatic_index_validation_try.rb +253 -0
- data/try/unit/horreum/base_try.rb +1 -1
- data/try/unit/horreum/class_methods_try.rb +2 -2
- data/try/unit/horreum/initialization_try.rb +1 -1
- data/try/unit/horreum/relations_try.rb +4 -4
- data/try/unit/horreum/serialization_try.rb +2 -2
- data/try/unit/horreum/unique_index_edge_cases_try.rb +376 -0
- data/try/unit/horreum/unique_index_guard_validation_try.rb +281 -0
- metadata +14 -2
data/lib/familia/errors.rb
CHANGED
@@ -1,19 +1,52 @@
|
|
1
1
|
# lib/familia/errors.rb
|
2
2
|
#
|
3
3
|
module Familia
|
4
|
+
# Base exception class for all Familia errors
|
4
5
|
class Problem < RuntimeError; end
|
5
|
-
class NoIdentifier < Problem; end
|
6
|
-
class NonUniqueKey < Problem; end
|
7
6
|
|
8
|
-
class
|
9
|
-
class
|
7
|
+
# Base exception class for Redis/persistence-related errors
|
8
|
+
class PersistenceError < Problem; end
|
10
9
|
|
11
|
-
class
|
10
|
+
# Base exception class for Horreum models
|
11
|
+
class HorreumError < Problem; end
|
12
12
|
|
13
|
-
# Raised when
|
14
|
-
|
13
|
+
# Raised when an object creation fails (e.g. the identifier
|
14
|
+
# is already in use)
|
15
|
+
class CreationError < HorreumError; end
|
15
16
|
|
16
|
-
|
17
|
+
# Raised when an object lacks a required identifier
|
18
|
+
class NoIdentifier < HorreumError; end
|
19
|
+
|
20
|
+
# Raised when a key is expected to be unique but isn't
|
21
|
+
class NonUniqueKey < PersistenceError; end
|
22
|
+
|
23
|
+
# Raised when watch failed (e.g. key was modified), typically
|
24
|
+
# retry
|
25
|
+
class OptimisticLockError < PersistenceError; end
|
26
|
+
|
27
|
+
# Raised when a field type is invalid or unexpected
|
28
|
+
class FieldTypeError < HorreumError; end
|
29
|
+
|
30
|
+
# Raised when autoloading fails
|
31
|
+
class AutoloadError < HorreumError; end
|
32
|
+
|
33
|
+
# Raised when serialization or deserialization fails
|
34
|
+
class SerializerError < HorreumError; end
|
35
|
+
|
36
|
+
# Raised when attempting to start transactions or pipelines on
|
37
|
+
# connection types that don't support them
|
38
|
+
class OperationModeError < PersistenceError; end
|
39
|
+
|
40
|
+
# Raised when attempting to call a major method like save when
|
41
|
+
# inside a transaction or pipeline
|
42
|
+
class NestedTransactionError < OperationModeError; end
|
43
|
+
|
44
|
+
# Raised when attempting to reference a field that doesn't exist
|
45
|
+
class UnknownFieldError < HorreumError; end
|
46
|
+
|
47
|
+
# Raised when a value cannot be converted to a distinguishable
|
48
|
+
# string representation
|
49
|
+
class NotDistinguishableError < HorreumError
|
17
50
|
attr_reader :value
|
18
51
|
|
19
52
|
def initialize(value)
|
@@ -26,7 +59,8 @@ module Familia
|
|
26
59
|
end
|
27
60
|
end
|
28
61
|
|
29
|
-
|
62
|
+
# Raised when no connection is available for a given URI
|
63
|
+
class NotConnected < PersistenceError
|
30
64
|
attr_reader :uri
|
31
65
|
|
32
66
|
def initialize(uri)
|
@@ -39,13 +73,15 @@ module Familia
|
|
39
73
|
end
|
40
74
|
end
|
41
75
|
|
42
|
-
# UnsortedSet Familia.connection_provider or use middleware
|
43
|
-
|
76
|
+
# UnsortedSet Familia.connection_provider or use middleware
|
77
|
+
# to provide connections.
|
78
|
+
class NoConnectionAvailable < PersistenceError; end
|
44
79
|
|
45
80
|
# Raised when a load method fails to find the requested object
|
46
|
-
class NotFound <
|
81
|
+
class NotFound < PersistenceError; end
|
47
82
|
|
48
|
-
# Raised when attempting to refresh an object whose key
|
83
|
+
# Raised when attempting to refresh an object whose key
|
84
|
+
# doesn't exist in the database
|
49
85
|
class KeyNotFoundError < NonUniqueKey
|
50
86
|
attr_reader :key
|
51
87
|
|
@@ -59,7 +95,8 @@ module Familia
|
|
59
95
|
end
|
60
96
|
end
|
61
97
|
|
62
|
-
# Raised when attempting to create an object that already
|
98
|
+
# Raised when attempting to create an object that already
|
99
|
+
# exists in the database
|
63
100
|
class RecordExistsError < NonUniqueKey
|
64
101
|
attr_reader :key
|
65
102
|
|
@@ -10,25 +10,23 @@ module Familia
|
|
10
10
|
# with the :expiration feature's implementation.
|
11
11
|
#
|
12
12
|
# This is a no-op implementation that gets overridden by the :expiration
|
13
|
-
# feature.
|
14
|
-
#
|
13
|
+
# feature when it is enabled. This allows for calling this method on any
|
14
|
+
# horreum model regardless of the feature status. It accepts an optional
|
15
|
+
# expiration parameter to maintain interface compatibility with
|
16
|
+
# the overriding implementations.
|
15
17
|
#
|
16
|
-
# @param
|
18
|
+
# @param expiration [Numeric, nil] Time To Live in seconds
|
17
19
|
# @return [nil] Always returns nil for the base implementation
|
18
20
|
#
|
19
21
|
# @note This is a no-op implementation. Classes that need expiration
|
20
22
|
# functionality should include the :expiration feature.
|
21
23
|
#
|
22
|
-
# @example
|
23
|
-
# class MyModel < Familia::Horreum
|
24
|
-
# feature :expiration
|
25
|
-
# default_expiration 1.hour
|
26
|
-
# end
|
24
|
+
# @example MyModel.new.update_expiration(expiration: 3600) # => nothing happens
|
27
25
|
#
|
28
|
-
def update_expiration(
|
26
|
+
def update_expiration(expiration: nil)
|
29
27
|
Familia.ld <<~LOG
|
30
28
|
[update_expiration] Expiration feature not enabled for #{self.class}.
|
31
|
-
Key: #{dbkey} Arg: #{
|
29
|
+
Key: #{dbkey} Arg: #{expiration} (caller: #{caller(1..1)})
|
32
30
|
LOG
|
33
31
|
nil
|
34
32
|
end
|
@@ -36,7 +36,7 @@ module Familia
|
|
36
36
|
# session.ttl # => 1799
|
37
37
|
#
|
38
38
|
# # UnsortedSet custom expiration for new objects
|
39
|
-
# session.update_expiration(
|
39
|
+
# session.update_expiration(expiration: 2.hours)
|
40
40
|
#
|
41
41
|
# Class-Level Configuration:
|
42
42
|
#
|
@@ -89,7 +89,7 @@ module Familia
|
|
89
89
|
#
|
90
90
|
# Setting expiration to 0 (zero) disables TTL, making data persist indefinitely:
|
91
91
|
#
|
92
|
-
# session.update_expiration(
|
92
|
+
# session.update_expiration(expiration: 0) # No expiration
|
93
93
|
#
|
94
94
|
# TTL Querying:
|
95
95
|
#
|
@@ -115,7 +115,7 @@ module Familia
|
|
115
115
|
# when 'free'
|
116
116
|
# update_expiration(1.hour)
|
117
117
|
# else
|
118
|
-
# update_expiration(
|
118
|
+
# update_expiration(expiration)
|
119
119
|
# end
|
120
120
|
# end
|
121
121
|
# end
|
@@ -135,10 +135,10 @@ module Familia
|
|
135
135
|
#
|
136
136
|
# The feature validates expiration values and raises descriptive errors:
|
137
137
|
#
|
138
|
-
# session.update_expiration(
|
138
|
+
# session.update_expiration(expiration: "invalid")
|
139
139
|
# # => Familia::Problem: Default expiration must be a number
|
140
140
|
#
|
141
|
-
# session.update_expiration(
|
141
|
+
# session.update_expiration(expiration: -1)
|
142
142
|
# # => Familia::Problem: Default expiration must be non-negative
|
143
143
|
#
|
144
144
|
# Performance Considerations:
|
@@ -226,20 +226,20 @@ module Familia
|
|
226
226
|
# after which it will be automatically removed. The method also handles
|
227
227
|
# cascading expiration to related data structures when applicable.
|
228
228
|
#
|
229
|
-
# @param
|
229
|
+
# @param expiration [Numeric, nil] The Time To Live in seconds. If nil,
|
230
230
|
# the default TTL will be used.
|
231
231
|
#
|
232
232
|
# @return [Boolean] Returns true if the expiration was set successfully,
|
233
233
|
# false otherwise.
|
234
234
|
#
|
235
235
|
# @example Setting an expiration of one day
|
236
|
-
# object.update_expiration(
|
236
|
+
# object.update_expiration(expiration: 86400)
|
237
237
|
#
|
238
238
|
# @example Using default expiration
|
239
239
|
# object.update_expiration # Uses class default_expiration
|
240
240
|
#
|
241
241
|
# @example Removing expiration (persist indefinitely)
|
242
|
-
# object.update_expiration(
|
242
|
+
# object.update_expiration(expiration: 0)
|
243
243
|
#
|
244
244
|
# @note If default expiration is set to zero, the expiration will be removed,
|
245
245
|
# making the data persist indefinitely.
|
@@ -247,8 +247,8 @@ module Familia
|
|
247
247
|
# @raise [Familia::Problem] Raises an error if the default expiration is not
|
248
248
|
# a non-negative number.
|
249
249
|
#
|
250
|
-
def update_expiration(
|
251
|
-
|
250
|
+
def update_expiration(expiration: nil)
|
251
|
+
expiration ||= default_expiration
|
252
252
|
|
253
253
|
# Handle cascading expiration to related data structures
|
254
254
|
if self.class.relations?
|
@@ -258,8 +258,8 @@ module Familia
|
|
258
258
|
next if definition.opts[:default_expiration].nil?
|
259
259
|
|
260
260
|
obj = send(name)
|
261
|
-
Familia.ld "[update_expiration] Updating expiration for #{name} (#{obj.dbkey}) to #{
|
262
|
-
obj.update_expiration(
|
261
|
+
Familia.ld "[update_expiration] Updating expiration for #{name} (#{obj.dbkey}) to #{expiration}"
|
262
|
+
obj.update_expiration(expiration: expiration)
|
263
263
|
end
|
264
264
|
end
|
265
265
|
|
@@ -267,29 +267,29 @@ module Familia
|
|
267
267
|
# It's important to raise exceptions here and not just log warnings. We
|
268
268
|
# don't want to silently fail at setting expirations and cause data
|
269
269
|
# retention issues (e.g. not removed in a timely fashion).
|
270
|
-
unless
|
270
|
+
unless expiration.is_a?(Numeric)
|
271
271
|
raise Familia::Problem,
|
272
|
-
"Default expiration must be a number (#{
|
272
|
+
"Default expiration must be a number (#{expiration.class} given for #{self.class})"
|
273
273
|
end
|
274
274
|
|
275
|
-
unless
|
275
|
+
unless expiration >= 0
|
276
276
|
raise Familia::Problem,
|
277
|
-
"Default expiration must be non-negative (#{
|
277
|
+
"Default expiration must be non-negative (#{expiration} given for #{self.class})"
|
278
278
|
end
|
279
279
|
|
280
280
|
# If zero, simply skip setting an expiry for this key. If we were to set
|
281
281
|
# 0, Valkey/Redis would drop the key immediately.
|
282
|
-
if
|
282
|
+
if expiration.zero?
|
283
283
|
Familia.ld "[update_expiration] No expiration for #{self.class} (#{dbkey})"
|
284
284
|
return true
|
285
285
|
end
|
286
286
|
|
287
|
-
Familia.ld "[update_expiration] Expires #{dbkey} in #{
|
287
|
+
Familia.ld "[update_expiration] Expires #{dbkey} in #{expiration} seconds"
|
288
288
|
|
289
289
|
# The Valkey/Redis' EXPIRE command returns 1 if the timeout was set, 0
|
290
290
|
# if key does not exist or the timeout could not be set. Via redis-rb,
|
291
291
|
# it's a boolean.
|
292
|
-
expire(
|
292
|
+
expire(expiration)
|
293
293
|
end
|
294
294
|
|
295
295
|
# Get the remaining time to live for this object's data
|
@@ -32,29 +32,30 @@ module Familia
|
|
32
32
|
# @param indexed_class [Class] The class being indexed (e.g., Employee)
|
33
33
|
# @param field [Symbol] The field to index
|
34
34
|
# @param index_name [Symbol] Name of the index
|
35
|
-
# @param within [Class, Symbol]
|
35
|
+
# @param within [Class, Symbol] Scope class for instance-scoped index (required)
|
36
36
|
# @param query [Boolean] Whether to generate query methods
|
37
37
|
def setup(indexed_class:, field:, index_name:, within:, query:)
|
38
|
-
# Multi-index always requires a
|
39
|
-
|
40
|
-
resolved_class = Familia.resolve_class(
|
38
|
+
# Multi-index always requires a scope context
|
39
|
+
scope_class = within
|
40
|
+
resolved_class = Familia.resolve_class(scope_class)
|
41
41
|
|
42
42
|
# Store metadata for this indexing relationship
|
43
43
|
indexed_class.indexing_relationships << IndexingRelationship.new(
|
44
44
|
field: field,
|
45
|
-
|
45
|
+
scope_class: scope_class,
|
46
|
+
within: within,
|
46
47
|
index_name: index_name,
|
47
48
|
query: query,
|
48
49
|
cardinality: :multi,
|
49
50
|
)
|
50
51
|
|
51
52
|
# Always generate the factory method - required by mutation methods
|
52
|
-
if
|
53
|
+
if scope_class.is_a?(Class)
|
53
54
|
generate_factory_method(resolved_class, index_name)
|
54
55
|
end
|
55
56
|
|
56
|
-
# Generate query methods on the
|
57
|
-
if query &&
|
57
|
+
# Generate query methods on the scope class (optional)
|
58
|
+
if query && scope_class.is_a?(Class)
|
58
59
|
generate_query_methods_destination(indexed_class, field, resolved_class, index_name)
|
59
60
|
end
|
60
61
|
|
@@ -62,17 +63,17 @@ module Familia
|
|
62
63
|
generate_mutation_methods_self(indexed_class, field, resolved_class, index_name)
|
63
64
|
end
|
64
65
|
|
65
|
-
# Generates the factory method ON THE
|
66
|
+
# Generates the factory method ON THE SCOPE CLASS (Company when within: Company):
|
66
67
|
# - company.index_name_for(field_value) - DataType factory (always needed)
|
67
68
|
#
|
68
69
|
# This method is required by mutation methods even when query: false
|
69
70
|
#
|
70
|
-
# @param
|
71
|
+
# @param scope_class [Class] The scope class providing uniqueness context (e.g., Company)
|
71
72
|
# @param index_name [Symbol] Name of the index (e.g., :dept_index)
|
72
|
-
def generate_factory_method(
|
73
|
-
|
73
|
+
def generate_factory_method(scope_class, index_name)
|
74
|
+
actual_scope_class = Familia.resolve_class(scope_class)
|
74
75
|
|
75
|
-
|
76
|
+
actual_scope_class.class_eval do
|
76
77
|
# Helper method to get index set for a specific field value
|
77
78
|
# This acts as a factory for field-value-specific DataTypes
|
78
79
|
define_method(:"#{index_name}_for") do |field_value|
|
@@ -83,21 +84,21 @@ module Familia
|
|
83
84
|
end
|
84
85
|
end
|
85
86
|
|
86
|
-
# Generates query methods ON THE
|
87
|
+
# Generates query methods ON THE SCOPE CLASS (Company when within: Company):
|
87
88
|
# - company.sample_from_department(dept, count=1) - random sampling
|
88
89
|
# - company.find_all_by_department(dept) - all objects
|
89
90
|
# - company.rebuild_dept_index - rebuild index
|
90
91
|
#
|
91
92
|
# @param indexed_class [Class] The class being indexed (e.g., Employee)
|
92
93
|
# @param field [Symbol] The field to index (e.g., :department)
|
93
|
-
# @param
|
94
|
+
# @param scope_class [Class] The scope class providing uniqueness context (e.g., Company)
|
94
95
|
# @param index_name [Symbol] Name of the index (e.g., :dept_index)
|
95
|
-
def generate_query_methods_destination(indexed_class, field,
|
96
|
-
# Resolve
|
97
|
-
|
96
|
+
def generate_query_methods_destination(indexed_class, field, scope_class, index_name)
|
97
|
+
# Resolve scope class using Familia pattern
|
98
|
+
actual_scope_class = Familia.resolve_class(scope_class)
|
98
99
|
|
99
100
|
# Generate instance sampling method (e.g., company.sample_from_department)
|
100
|
-
|
101
|
+
actual_scope_class.class_eval do
|
101
102
|
|
102
103
|
define_method(:"sample_from_#{field}") do |field_value, count = 1|
|
103
104
|
index_set = send("#{index_name}_for", field_value) # i.e. UnsortedSet
|
@@ -133,62 +134,62 @@ module Familia
|
|
133
134
|
#
|
134
135
|
# @param indexed_class [Class] The class being indexed (e.g., Employee)
|
135
136
|
# @param field [Symbol] The field to index (e.g., :department)
|
136
|
-
# @param
|
137
|
+
# @param scope_class [Class] The scope class providing uniqueness context (e.g., Company)
|
137
138
|
# @param index_name [Symbol] Name of the index (e.g., :dept_index)
|
138
|
-
def generate_mutation_methods_self(indexed_class, field,
|
139
|
-
|
139
|
+
def generate_mutation_methods_self(indexed_class, field, scope_class, index_name)
|
140
|
+
scope_class_config = scope_class.config_name
|
140
141
|
indexed_class.class_eval do
|
141
|
-
method_name = :"add_to_#{
|
142
|
+
method_name = :"add_to_#{scope_class_config}_#{index_name}"
|
142
143
|
Familia.ld("[MultiIndexGenerators] #{name} method #{method_name}")
|
143
144
|
|
144
|
-
define_method(method_name) do |
|
145
|
-
return unless
|
145
|
+
define_method(method_name) do |scope_instance|
|
146
|
+
return unless scope_instance
|
146
147
|
|
147
148
|
field_value = send(field)
|
148
149
|
return unless field_value
|
149
150
|
|
150
|
-
# Use helper method on
|
151
|
-
index_set =
|
151
|
+
# Use helper method on scope instance instead of manual instantiation
|
152
|
+
index_set = scope_instance.send("#{index_name}_for", field_value)
|
152
153
|
|
153
154
|
# Use UnsortedSet DataType method (no scoring)
|
154
155
|
index_set.add(identifier)
|
155
156
|
end
|
156
157
|
|
157
|
-
method_name = :"remove_from_#{
|
158
|
+
method_name = :"remove_from_#{scope_class_config}_#{index_name}"
|
158
159
|
Familia.ld("[MultiIndexGenerators] #{name} method #{method_name}")
|
159
160
|
|
160
|
-
define_method(method_name) do |
|
161
|
-
return unless
|
161
|
+
define_method(method_name) do |scope_instance|
|
162
|
+
return unless scope_instance
|
162
163
|
|
163
164
|
field_value = send(field)
|
164
165
|
return unless field_value
|
165
166
|
|
166
|
-
# Use helper method on
|
167
|
-
index_set =
|
167
|
+
# Use helper method on scope instance instead of manual instantiation
|
168
|
+
index_set = scope_instance.send("#{index_name}_for", field_value)
|
168
169
|
|
169
170
|
# Remove using UnsortedSet DataType method
|
170
171
|
index_set.remove(identifier)
|
171
172
|
end
|
172
173
|
|
173
|
-
method_name = :"update_in_#{
|
174
|
+
method_name = :"update_in_#{scope_class_config}_#{index_name}"
|
174
175
|
Familia.ld("[MultiIndexGenerators] #{name} method #{method_name}")
|
175
176
|
|
176
|
-
define_method(method_name) do |
|
177
|
-
return unless
|
177
|
+
define_method(method_name) do |scope_instance, old_field_value = nil|
|
178
|
+
return unless scope_instance
|
178
179
|
|
179
180
|
new_field_value = send(field)
|
180
181
|
|
181
182
|
# Use Familia's transaction method for atomicity with DataType abstraction
|
182
|
-
|
183
|
+
scope_instance.transaction do |_tx|
|
183
184
|
# Remove from old index if provided - use helper method
|
184
185
|
if old_field_value
|
185
|
-
old_index_set =
|
186
|
+
old_index_set = scope_instance.send("#{index_name}_for", old_field_value)
|
186
187
|
old_index_set.remove(identifier)
|
187
188
|
end
|
188
189
|
|
189
190
|
# Add to new index if present - use helper method
|
190
191
|
if new_field_value
|
191
|
-
new_index_set =
|
192
|
+
new_index_set = scope_instance.send("#{index_name}_for", new_field_value)
|
192
193
|
new_index_set.add(identifier)
|
193
194
|
end
|
194
195
|
end
|