familia 2.0.0.pre10 → 2.0.0.pre12

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +75 -12
  3. data/CLAUDE.md +4 -54
  4. data/Gemfile.lock +1 -1
  5. data/changelog.d/README.md +45 -34
  6. data/docs/archive/FAMILIA_RELATIONSHIPS.md +1 -1
  7. data/docs/archive/FAMILIA_UPDATE.md +1 -1
  8. data/docs/archive/README.md +15 -19
  9. data/docs/guides/Home.md +1 -1
  10. data/docs/guides/Implementation-Guide.md +1 -1
  11. data/docs/guides/relationships-methods.md +1 -1
  12. data/docs/migrating/.gitignore +2 -0
  13. data/docs/migrating/v2.0.0-pre.md +84 -0
  14. data/docs/migrating/v2.0.0-pre11.md +255 -0
  15. data/docs/migrating/v2.0.0-pre12.md +306 -0
  16. data/docs/migrating/v2.0.0-pre5.md +110 -0
  17. data/docs/migrating/v2.0.0-pre6.md +154 -0
  18. data/docs/migrating/v2.0.0-pre7.md +222 -0
  19. data/docs/overview.md +6 -7
  20. data/{examples/redis_command_validation_example.rb → docs/reference/auditing_database_commands.rb} +29 -32
  21. data/examples/{bit_encoding_integration.rb → permissions.rb} +30 -27
  22. data/examples/{relationships_basic.rb → relationships.rb} +2 -3
  23. data/examples/safe_dump.rb +281 -0
  24. data/familia.gemspec +4 -4
  25. data/lib/familia/base.rb +52 -0
  26. data/lib/familia/{encryption_request_cache.rb → encryption/request_cache.rb} +1 -1
  27. data/lib/familia/errors.rb +2 -0
  28. data/lib/familia/features/autoloader.rb +57 -0
  29. data/lib/familia/features/external_identifier.rb +310 -0
  30. data/lib/familia/features/object_identifier.rb +307 -0
  31. data/lib/familia/features/safe_dump.rb +66 -72
  32. data/lib/familia/features.rb +93 -5
  33. data/lib/familia/horreum/subclass/definition.rb +47 -3
  34. data/lib/familia/secure_identifier.rb +51 -75
  35. data/lib/familia/verifiable_identifier.rb +162 -0
  36. data/lib/familia/version.rb +1 -1
  37. data/lib/familia.rb +1 -0
  38. data/setup.cfg +1 -8
  39. data/try/core/secure_identifier_try.rb +47 -18
  40. data/try/core/verifiable_identifier_try.rb +171 -0
  41. data/try/features/{external_identifiers/external_identifiers_try.rb → external_identifier/external_identifier_try.rb} +25 -28
  42. data/try/features/feature_improvements_try.rb +126 -0
  43. data/try/features/{object_identifiers/object_identifiers_integration_try.rb → object_identifier/object_identifier_integration_try.rb} +28 -30
  44. data/try/features/{object_identifiers/object_identifiers_try.rb → object_identifier/object_identifier_try.rb} +13 -13
  45. data/try/features/real_feature_integration_try.rb +7 -6
  46. data/try/features/safe_dump/safe_dump_try.rb +8 -9
  47. data/try/helpers/test_helpers.rb +17 -17
  48. metadata +30 -22
  49. data/changelog.d/fragments/.keep +0 -0
  50. data/changelog.d/template.md.j2 +0 -29
  51. data/lib/familia/features/external_identifiers/external_identifier_field_type.rb +0 -120
  52. data/lib/familia/features/external_identifiers.rb +0 -111
  53. data/lib/familia/features/object_identifiers/object_identifier_field_type.rb +0 -91
  54. data/lib/familia/features/object_identifiers.rb +0 -194
@@ -1,15 +1,15 @@
1
- # try/features/object_identifiers_integration_try.rb
1
+ # try/features/object_identifier/object_identifier_integration_try.rb
2
2
 
3
3
  require_relative '../../helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
7
- # Integration test for ObjectIdentifiers and ExternalIdentifiers features together
7
+ # Integration test for ObjectIdentifier and ExternalIdentifiers features together
8
8
 
9
9
  # Class using both features with defaults
10
10
  class IntegrationTest < Familia::Horreum
11
- feature :object_identifiers
12
- feature :external_identifiers # This depends on :object_identifiers
11
+ feature :object_identifier
12
+ feature :external_identifier # This depends on :object_identifier
13
13
  identifier_field :id
14
14
  field :id
15
15
  field :name
@@ -18,8 +18,8 @@ end
18
18
 
19
19
  # Class with custom configurations for both features
20
20
  class CustomIntegrationTest < Familia::Horreum
21
- feature :object_identifiers, generator: :hex
22
- feature :external_identifiers, prefix: 'custom'
21
+ feature :object_identifier, generator: :hex
22
+ feature :external_identifier, prefix: 'custom'
23
23
  identifier_field :id
24
24
  field :id
25
25
  field :name
@@ -27,8 +27,8 @@ end
27
27
 
28
28
  # Class testing full lifecycle with Redis persistence
29
29
  class PersistenceTest < Familia::Horreum
30
- feature :object_identifiers
31
- feature :external_identifiers
30
+ feature :object_identifier
31
+ feature :external_identifier
32
32
  identifier_field :id
33
33
  field :id
34
34
  field :name
@@ -39,12 +39,12 @@ end
39
39
  @integration_obj = IntegrationTest.new(id: 'integration_1', name: 'Integration Test', email: 'test@example.com')
40
40
  @custom_obj = CustomIntegrationTest.new(id: 'custom_1', name: 'Custom Test')
41
41
 
42
- ## Object identifiers feature is automatically included
43
- IntegrationTest.features_enabled.include?(:object_identifiers)
42
+ ## Object identifier feature is automatically included
43
+ IntegrationTest.features_enabled.include?(:object_identifier)
44
44
  #==> true
45
45
 
46
- ## External identifiers feature is included
47
- IntegrationTest.features_enabled.include?(:external_identifiers)
46
+ ## External identifier feature is included
47
+ IntegrationTest.features_enabled.include?(:external_identifier)
48
48
  #==> true
49
49
 
50
50
  ## Object responds to objid accessor
@@ -70,14 +70,12 @@ obj = IntegrationTest.new
70
70
  obj.objid != obj.extid
71
71
  #==> true
72
72
 
73
- ## extid is deterministically generated from objid
73
+ ## extid is deterministically generated from objid for same object
74
74
  obj = IntegrationTest.new
75
75
  original_objid = obj.objid
76
76
  original_extid = obj.extid
77
- # Create new object with same objid
78
- obj2 = IntegrationTest.new
79
- obj2.instance_variable_set(:@objid, original_objid)
80
- obj2.extid == original_extid
77
+ # Multiple calls on same object should return same extid
78
+ obj.extid == original_extid
81
79
  #==> true
82
80
 
83
81
  ## Custom objid uses hex format (64 chars for 256-bit)
@@ -145,41 +143,41 @@ lazy_obj2.instance_variable_get(:@objid)
145
143
  #=*> nil
146
144
 
147
145
  ## Check field types objid
148
- IntegrationTest.field_types[:objid].is_a?(Familia::Features::ObjectIdentifiers::ObjectIdentifierFieldType)
146
+ IntegrationTest.field_types[:objid].is_a?(Familia::Features::ObjectIdentifier::ObjectIdentifierFieldType)
149
147
  #==> true
150
148
 
151
149
  ## ObjectIdentifier fields have correct types in field registry
152
- IntegrationTest.field_types[:objid].class.ancestors.include?(Familia::Features::ObjectIdentifiers::ObjectIdentifierFieldType)
150
+ IntegrationTest.field_types[:objid].class.ancestors.include?(Familia::Features::ObjectIdentifier::ObjectIdentifierFieldType)
153
151
  #==> true
154
152
 
155
153
  ## ExternalIdentifier fields have correct types in field registry
156
- IntegrationTest.field_types[:extid].class.ancestors.include?(Familia::Features::ExternalIdentifiers::ExternalIdentifierFieldType)
154
+ IntegrationTest.field_types[:extid].class.ancestors.include?(Familia::Features::ExternalIdentifier::ExternalIdentifierFieldType)
157
155
  #==> true
158
156
 
159
- ## Object identifiers options are preserved
157
+ ## Object identifier options are preserved
160
158
  opts = IntegrationTest.feature_options
161
- opts.key?(:object_identifiers)
159
+ opts.key?(:object_identifier)
162
160
  #==> true
163
161
 
164
- ## External identifiers options are preserved
162
+ ## External identifiersoptions are preserved
165
163
  opts = IntegrationTest.feature_options
166
- opts.key?(:external_identifiers)
164
+ opts.key?(:external_identifier)
167
165
  #==> true
168
166
 
169
167
  ## Generator default configuration is applied correctly
170
- IntegrationTest.feature_options(:object_identifiers)[:generator]
168
+ IntegrationTest.feature_options(:object_identifier)[:generator]
171
169
  #=> :uuid_v7
172
170
 
173
171
  ## Prefix default configuration is applied correctly
174
- IntegrationTest.feature_options(:external_identifiers)[:prefix]
172
+ IntegrationTest.feature_options(:external_identifier)[:prefix]
175
173
  #=> "ext"
176
174
 
177
175
  ## Custom generator configuration is applied correctly
178
- CustomIntegrationTest.feature_options(:object_identifiers)[:generator]
176
+ CustomIntegrationTest.feature_options(:object_identifier)[:generator]
179
177
  #=> :hex
180
178
 
181
179
  ## Custom prefix configuration is applied correctly
182
- CustomIntegrationTest.feature_options(:external_identifiers)[:prefix]
180
+ CustomIntegrationTest.feature_options(:external_identifier)[:prefix]
183
181
  #=> "custom"
184
182
 
185
183
  ## objid is URL-safe (UUID format)
@@ -265,9 +263,9 @@ second_extid = stability_obj.extid
265
263
  first_extid == second_extid
266
264
  #==> true
267
265
 
268
- ## Feature dependency is enforced (external_identifiers requires object_identifiers)
266
+ ## Feature dependency is enforced (external_identifier requires object_identifier)
269
267
  # This is automatically handled by the feature system
270
- IntegrationTest.features_enabled.include?(:object_identifiers)
268
+ IntegrationTest.features_enabled.include?(:object_identifier)
271
269
  #==> true
272
270
 
273
271
  ## Objects work with existing Horreum save pattern
@@ -1,14 +1,14 @@
1
- # try/features/object_identifiers_try.rb
1
+ # try/features/object_identifier/object_identifier_try.rb
2
2
 
3
3
  require_relative '../../helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
7
- # Test ObjectIdentifiers feature functionality
7
+ # Test ObjectIdentifier feature functionality
8
8
 
9
9
  # Basic class using default UUID v7 generator
10
10
  class BasicObjectTest < Familia::Horreum
11
- feature :object_identifiers
11
+ feature :object_identifier
12
12
  identifier_field :id
13
13
  field :id
14
14
  field :name
@@ -16,7 +16,7 @@ end
16
16
 
17
17
  # Class using UUID v4 generator
18
18
  class UuidV4Test < Familia::Horreum
19
- feature :object_identifiers, generator: :uuid_v4
19
+ feature :object_identifier, generator: :uuid_v4
20
20
  identifier_field :id
21
21
  field :id
22
22
  field :name
@@ -24,7 +24,7 @@ end
24
24
 
25
25
  # Class using hex generator
26
26
  class HexTest < Familia::Horreum
27
- feature :object_identifiers, generator: :hex
27
+ feature :object_identifier, generator: :hex
28
28
  identifier_field :id
29
29
  field :id
30
30
  field :name
@@ -32,7 +32,7 @@ end
32
32
 
33
33
  # Class using custom proc generator
34
34
  class CustomProcTest < Familia::Horreum
35
- feature :object_identifiers, generator: -> { "custom_#{SecureRandom.hex(4)}" }
35
+ feature :object_identifier, generator: -> { "custom_#{SecureRandom.hex(4)}" }
36
36
  identifier_field :id
37
37
  field :id
38
38
  field :name
@@ -40,7 +40,7 @@ end
40
40
 
41
41
  # Class testing data integrity preservation
42
42
  class DataIntegrityTest < Familia::Horreum
43
- feature :object_identifiers
43
+ feature :object_identifier
44
44
  identifier_field :id
45
45
  field :id
46
46
  field :name
@@ -50,7 +50,7 @@ end
50
50
  @existing_obj = DataIntegrityTest.new(id: 'test_id', objid: 'preset_id_123', name: 'Preset Object')
51
51
 
52
52
  ## Feature is available on class
53
- BasicObjectTest.features_enabled.include?(:object_identifiers)
53
+ BasicObjectTest.features_enabled.include?(:object_identifier)
54
54
  #==> true
55
55
 
56
56
  ## Class has objid field defined
@@ -162,22 +162,22 @@ basic_obj.objid.include?('-') && !hex_obj.objid.include?('-')
162
162
 
163
163
  ## objid field type is ObjectIdentifierFieldType
164
164
  BasicObjectTest.field_types[:objid]
165
- #=:> Familia::Features::ObjectIdentifiers::ObjectIdentifierFieldType
165
+ #=:> Familia::Features::ObjectIdentifier::ObjectIdentifierFieldType
166
166
 
167
167
  ## Generator configuration is accessible through feature options
168
- BasicObjectTest.feature_options(:object_identifiers)[:generator]
168
+ BasicObjectTest.feature_options(:object_identifier)[:generator]
169
169
  #=> :uuid_v7
170
170
 
171
171
  ## UUID v4 class has correct generator configured
172
- UuidV4Test.feature_options(:object_identifiers)[:generator]
172
+ UuidV4Test.feature_options(:object_identifier)[:generator]
173
173
  #=> :uuid_v4
174
174
 
175
175
  ## Hex class has correct generator configured
176
- HexTest.feature_options(:object_identifiers)[:generator]
176
+ HexTest.feature_options(:object_identifier)[:generator]
177
177
  #=> :hex
178
178
 
179
179
  ## Custom proc class has proc generator
180
- CustomProcTest.feature_options(:object_identifiers)[:generator]
180
+ CustomProcTest.feature_options(:object_identifier)[:generator]
181
181
  #=:> Proc
182
182
 
183
183
  ## Empty initialization preserves nil objid for lazy generation
@@ -22,11 +22,10 @@ class SafeDumpCategoryTest < Familia::Horreum
22
22
 
23
23
  feature :safe_dump
24
24
 
25
- @safe_dump_fields = [
26
- :id,
27
- :public_name,
28
- :email
29
- ]
25
+ # Use new SafeDump DSL
26
+ safe_dump_field :id
27
+ safe_dump_field :public_name
28
+ safe_dump_field :email
30
29
  end
31
30
 
32
31
  # Combined features work together
@@ -39,7 +38,9 @@ class CombinedFeaturesTest < Familia::Horreum
39
38
  feature :expiration
40
39
  feature :safe_dump
41
40
 
42
- @safe_dump_fields = [:id, :name]
41
+ # Use new SafeDump DSL
42
+ safe_dump_field :id
43
+ safe_dump_field :name
43
44
  end
44
45
 
45
46
  # Test that individual features can be queried
@@ -15,12 +15,11 @@ class SafeDumpTest < Familia::Horreum
15
15
  field :email
16
16
  field :secret_data
17
17
 
18
- @safe_dump_fields = [
19
- :id,
20
- :name,
21
- { display_name: ->(obj) { "#{obj.name} (#{obj.id})" } },
22
- { has_email: ->(obj) { !obj.email.nil? && !obj.email.empty? } }
23
- ]
18
+ # Use new DSL instead of @safe_dump_fields
19
+ safe_dump_field :id
20
+ safe_dump_field :name
21
+ safe_dump_field :display_name, ->(obj) { "#{obj.name} (#{obj.id})" }
22
+ safe_dump_field :has_email, ->(obj) { !obj.email.nil? && !obj.email.empty? }
24
23
 
25
24
  def active?
26
25
  true
@@ -47,9 +46,9 @@ SafeDumpTest.respond_to?(:safe_dump_field_map)
47
46
  #=> true
48
47
 
49
48
  ## safe_dump_fields returns field names only
50
- fields = SafeDumpTest.safe_dump_fields
51
- fields
52
- #=> [:id, :name, :display_name, :has_email]
49
+ fields = SafeDumpTest.safe_dump_field_names
50
+ fields.sort
51
+ #=> [:display_name, :has_email, :id, :name]
53
52
 
54
53
  ## safe_dump_field_map returns callable map
55
54
  field_map = SafeDumpTest.safe_dump_field_map
@@ -33,6 +33,11 @@ class Blone < Familia::Horreum
33
33
  string :value, default: 'GREAT!'
34
34
  end
35
35
 
36
+ class Bourne < Familia::Horreum
37
+ feature :object_identifier
38
+ feature :external_identifier
39
+ end
40
+
36
41
  class Customer < Familia::Horreum
37
42
  logical_database 15 # Use something other than the default DB
38
43
  default_expiration 5.years
@@ -41,24 +46,19 @@ class Customer < Familia::Horreum
41
46
  # feature :expiration
42
47
  # feature :api_version
43
48
 
44
- # NOTE: The SafeDump mixin caches the safe_dump_field_map so updating this list
45
- # with hot reloading in dev mode will not work. You will need to restart the
46
- # server to see the changes.
47
- @safe_dump_fields = [
48
- :custid,
49
- :role,
50
- :verified,
51
- :updated,
52
- :created,
53
-
54
- # NOTE: The secrets_created incrementer is null until the first secret
55
- # is created. See CreateSecret for where the incrementer is called.
56
- #
57
- { secrets_created: ->(cust) { cust.secrets_created.value || 0 } },
49
+ # Use new SafeDump DSL instead of @safe_dump_fields
50
+ safe_dump_field :custid
51
+ safe_dump_field :role
52
+ safe_dump_field :verified
53
+ safe_dump_field :updated
54
+ safe_dump_field :created
55
+
56
+ # NOTE: The secrets_created incrementer is null until the first secret
57
+ # is created. See CreateSecret for where the incrementer is called.
58
+ safe_dump_field :secrets_created, ->(cust) { cust.secrets_created.value || 0 }
58
59
 
59
- # We use the hash syntax here since `:active?` is not a valid symbol.
60
- { active: ->(cust) { cust.active? } }
61
- ]
60
+ # We use a callable here since `:active?` is not a valid method symbol.
61
+ safe_dump_field :active, ->(cust) { cust.active? }
62
62
 
63
63
  class_sorted_set :values, key: 'onetime:customer'
64
64
  class_hashkey :domains
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: familia
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre10
4
+ version: 2.0.0.pre12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -15,56 +15,56 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.1'
18
+ version: '0.4'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0.1'
25
+ version: '0.4'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: connection_pool
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '2.4'
32
+ version: '2.5'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.4'
39
+ version: '2.5'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: csv
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '3.1'
46
+ version: '3.3'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '3.1'
53
+ version: '3.3'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: logger
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '1.6'
60
+ version: '1.7'
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '1.6'
67
+ version: '1.7'
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: redis
70
70
  requirement: !ruby/object:Gem::Requirement
@@ -137,8 +137,6 @@ files:
137
137
  - README.md
138
138
  - bin/irb
139
139
  - changelog.d/README.md
140
- - changelog.d/fragments/.keep
141
- - changelog.d/template.md.j2
142
140
  - docs/archive/.gitignore
143
141
  - docs/archive/FAMILIA_RELATIONSHIPS.md
144
142
  - docs/archive/FAMILIA_TECHNICAL.md
@@ -160,10 +158,18 @@ files:
160
158
  - docs/guides/Security-Model.md
161
159
  - docs/guides/Transient-Fields-Guide.md
162
160
  - docs/guides/relationships-methods.md
161
+ - docs/migrating/.gitignore
162
+ - docs/migrating/v2.0.0-pre.md
163
+ - docs/migrating/v2.0.0-pre11.md
164
+ - docs/migrating/v2.0.0-pre12.md
165
+ - docs/migrating/v2.0.0-pre5.md
166
+ - docs/migrating/v2.0.0-pre6.md
167
+ - docs/migrating/v2.0.0-pre7.md
163
168
  - docs/overview.md
164
- - examples/bit_encoding_integration.rb
165
- - examples/redis_command_validation_example.rb
166
- - examples/relationships_basic.rb
169
+ - docs/reference/auditing_database_commands.rb
170
+ - examples/permissions.rb
171
+ - examples/relationships.rb
172
+ - examples/safe_dump.rb
167
173
  - familia.gemspec
168
174
  - lib/familia.rb
169
175
  - lib/familia/base.rb
@@ -187,17 +193,16 @@ files:
187
193
  - lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb
188
194
  - lib/familia/encryption/providers/xchacha20_poly1305_provider.rb
189
195
  - lib/familia/encryption/registry.rb
190
- - lib/familia/encryption_request_cache.rb
196
+ - lib/familia/encryption/request_cache.rb
191
197
  - lib/familia/errors.rb
192
198
  - lib/familia/features.rb
199
+ - lib/familia/features/autoloader.rb
193
200
  - lib/familia/features/encrypted_fields.rb
194
201
  - lib/familia/features/encrypted_fields/concealed_string.rb
195
202
  - lib/familia/features/encrypted_fields/encrypted_field_type.rb
196
203
  - lib/familia/features/expiration.rb
197
- - lib/familia/features/external_identifiers.rb
198
- - lib/familia/features/external_identifiers/external_identifier_field_type.rb
199
- - lib/familia/features/object_identifiers.rb
200
- - lib/familia/features/object_identifiers/object_identifier_field_type.rb
204
+ - lib/familia/features/external_identifier.rb
205
+ - lib/familia/features/object_identifier.rb
201
206
  - lib/familia/features/quantization.rb
202
207
  - lib/familia/features/relationships.rb
203
208
  - lib/familia/features/relationships/cascading.rb
@@ -235,6 +240,7 @@ files:
235
240
  - lib/familia/validation/expectations.rb
236
241
  - lib/familia/validation/test_helpers.rb
237
242
  - lib/familia/validation/validator.rb
243
+ - lib/familia/verifiable_identifier.rb
238
244
  - lib/familia/version.rb
239
245
  - lib/middleware/database_middleware.rb
240
246
  - setup.cfg
@@ -254,6 +260,7 @@ files:
254
260
  - try/core/settings_try.rb
255
261
  - try/core/tools_try.rb
256
262
  - try/core/utils_try.rb
263
+ - try/core/verifiable_identifier_try.rb
257
264
  - try/data_types/boolean_try.rb
258
265
  - try/data_types/counter_try.rb
259
266
  - try/data_types/datatype_base_try.rb
@@ -321,10 +328,11 @@ files:
321
328
  - try/features/encrypted_fields/thread_safety_try.rb
322
329
  - try/features/encrypted_fields/universal_serialization_safety_try.rb
323
330
  - try/features/expiration/expiration_try.rb
324
- - try/features/external_identifiers/external_identifiers_try.rb
331
+ - try/features/external_identifier/external_identifier_try.rb
325
332
  - try/features/feature_dependencies_try.rb
326
- - try/features/object_identifiers/object_identifiers_integration_try.rb
327
- - try/features/object_identifiers/object_identifiers_try.rb
333
+ - try/features/feature_improvements_try.rb
334
+ - try/features/object_identifier/object_identifier_integration_try.rb
335
+ - try/features/object_identifier/object_identifier_try.rb
328
336
  - try/features/quantization/quantization_try.rb
329
337
  - try/features/real_feature_integration_try.rb
330
338
  - try/features/relationships/categorical_permissions_try.rb
File without changes
@@ -1,29 +0,0 @@
1
- <!--
2
- A new scriv fragment. Uncomment sections as needed.
3
- Reference issues or pull requests in parentheses at the end of the entry.
4
- Example: - Fixed a bug in the connection pool. (Closes #123)
5
- -->
6
-
7
- ### Added
8
- <!-- - New features or capabilities -->
9
-
10
- ### Changed
11
- <!-- - Changes to existing functionality -->
12
-
13
- ### Deprecated
14
- <!-- - Soon-to-be removed features -->
15
-
16
- ### Removed
17
- <!-- - Now removed features -->
18
-
19
- ### Fixed
20
- <!-- - Bug fixes -->
21
-
22
- ### Security
23
- <!-- - Security-related improvements -->
24
-
25
- ### Documentation
26
- <!-- - Documentation improvements -->
27
-
28
- ### AI Assistance
29
- <!-- - Briefly describe how AI was used for this change (e.g., "Refactoring and documentation by Gemini"). -->
@@ -1,120 +0,0 @@
1
- # lib/familia/features/external_identifiers/external_identifier_field_type.rb
2
-
3
- require 'familia/field_type'
4
-
5
- module Familia
6
- module Features
7
- module ExternalIdentifiers
8
- # ExternalIdentifierFieldType - Fields that generate deterministic external identifiers
9
- #
10
- # External identifier fields generate shorter, public-facing identifiers that are
11
- # deterministically derived from object identifiers. These IDs are safe for use
12
- # in URLs, APIs, and other external contexts where shorter IDs are preferred.
13
- #
14
- # Key characteristics:
15
- # - Deterministic generation from objid ensures consistency
16
- # - Shorter than objid (128-bit vs 256-bit) for external use
17
- # - Base-36 encoding for URL-safe identifiers
18
- # - 'ext_' prefix for clear identification as external IDs
19
- # - Lazy generation preserves values from initialization
20
- #
21
- # @example Using external identifier fields
22
- # class User < Familia::Horreum
23
- # feature :object_identifiers
24
- # feature :external_identifiers
25
- # field :email
26
- # end
27
- #
28
- # user = User.new(email: 'user@example.com')
29
- # user.objid # => "01234567-89ab-7def-8000-123456789abc"
30
- # user.extid # => "ext_abc123def456ghi789" (deterministic from objid)
31
- #
32
- # # Same objid always produces same extid
33
- # user2 = User.new(objid: user.objid, email: 'user@example.com')
34
- # user2.extid # => "ext_abc123def456ghi789" (identical to user.extid)
35
- #
36
- class ExternalIdentifierFieldType < FieldType
37
- # Override getter to provide lazy generation from objid
38
- #
39
- # Generates the external identifier deterministically from the object's
40
- # objid. This ensures consistency - the same objid will always produce
41
- # the same extid. Only generates when objid is available.
42
- #
43
- # @param klass [Class] The class to define the method on
44
- #
45
- def define_getter(klass)
46
- field_name = @name
47
- method_name = @method_name
48
-
49
- handle_method_conflict(klass, method_name) do
50
- klass.define_method method_name do
51
- # Check if we already have a value (from initialization or previous generation)
52
- existing_value = instance_variable_get(:"@#{field_name}")
53
- return existing_value unless existing_value.nil?
54
-
55
- # Generate external identifier from objid if available
56
- generated_extid = generate_external_identifier
57
- return unless generated_extid
58
-
59
- instance_variable_set(:"@#{field_name}", generated_extid)
60
-
61
- # Update mapping if we have an identifier
62
- if respond_to?(:identifier) && identifier
63
- self.class.extid_lookup[generated_extid] = identifier
64
- end
65
-
66
- generated_extid
67
- end
68
- end
69
- end
70
-
71
- # Override setter to preserve values during initialization
72
- #
73
- # This ensures that values passed during object initialization
74
- # (e.g., when loading from Redis) are preserved and not overwritten
75
- # by the lazy generation logic.
76
- #
77
- # @param klass [Class] The class to define the method on
78
- #
79
- def define_setter(klass)
80
- field_name = @name
81
- method_name = @method_name
82
-
83
- handle_method_conflict(klass, :"#{method_name}=") do
84
- klass.define_method :"#{method_name}=" do |value|
85
- # Remove old mapping if extid is changing
86
- old_value = instance_variable_get(:"@#{field_name}")
87
- if old_value && old_value != value && respond_to?(:identifier)
88
- self.class.extid_lookup.del(old_value)
89
- end
90
-
91
- # Set the new value
92
- instance_variable_set(:"@#{field_name}", value)
93
-
94
- # Update mapping if we have both extid and identifier
95
- if value && respond_to?(:identifier) && identifier
96
- self.class.extid_lookup[value] = identifier
97
- end
98
- end
99
- end
100
- end
101
-
102
- # External identifier fields are persisted to database
103
- #
104
- # @return [Boolean] true - external identifiers are always persisted
105
- #
106
- def persistent?
107
- true
108
- end
109
-
110
- # Category for external identifier fields
111
- #
112
- # @return [Symbol] :external_identifier
113
- #
114
- def category
115
- :external_identifier
116
- end
117
- end
118
- end
119
- end
120
- end