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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +75 -12
- data/CLAUDE.md +4 -54
- data/Gemfile.lock +1 -1
- data/changelog.d/README.md +45 -34
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +1 -1
- data/docs/archive/FAMILIA_UPDATE.md +1 -1
- data/docs/archive/README.md +15 -19
- data/docs/guides/Home.md +1 -1
- data/docs/guides/Implementation-Guide.md +1 -1
- data/docs/guides/relationships-methods.md +1 -1
- data/docs/migrating/.gitignore +2 -0
- data/docs/migrating/v2.0.0-pre.md +84 -0
- data/docs/migrating/v2.0.0-pre11.md +255 -0
- data/docs/migrating/v2.0.0-pre12.md +306 -0
- data/docs/migrating/v2.0.0-pre5.md +110 -0
- data/docs/migrating/v2.0.0-pre6.md +154 -0
- data/docs/migrating/v2.0.0-pre7.md +222 -0
- data/docs/overview.md +6 -7
- data/{examples/redis_command_validation_example.rb → docs/reference/auditing_database_commands.rb} +29 -32
- data/examples/{bit_encoding_integration.rb → permissions.rb} +30 -27
- data/examples/{relationships_basic.rb → relationships.rb} +2 -3
- data/examples/safe_dump.rb +281 -0
- data/familia.gemspec +4 -4
- data/lib/familia/base.rb +52 -0
- data/lib/familia/{encryption_request_cache.rb → encryption/request_cache.rb} +1 -1
- data/lib/familia/errors.rb +2 -0
- data/lib/familia/features/autoloader.rb +57 -0
- data/lib/familia/features/external_identifier.rb +310 -0
- data/lib/familia/features/object_identifier.rb +307 -0
- data/lib/familia/features/safe_dump.rb +66 -72
- data/lib/familia/features.rb +93 -5
- data/lib/familia/horreum/subclass/definition.rb +47 -3
- data/lib/familia/secure_identifier.rb +51 -75
- data/lib/familia/verifiable_identifier.rb +162 -0
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +1 -0
- data/setup.cfg +1 -8
- data/try/core/secure_identifier_try.rb +47 -18
- data/try/core/verifiable_identifier_try.rb +171 -0
- data/try/features/{external_identifiers/external_identifiers_try.rb → external_identifier/external_identifier_try.rb} +25 -28
- data/try/features/feature_improvements_try.rb +126 -0
- data/try/features/{object_identifiers/object_identifiers_integration_try.rb → object_identifier/object_identifier_integration_try.rb} +28 -30
- data/try/features/{object_identifiers/object_identifiers_try.rb → object_identifier/object_identifier_try.rb} +13 -13
- data/try/features/real_feature_integration_try.rb +7 -6
- data/try/features/safe_dump/safe_dump_try.rb +8 -9
- data/try/helpers/test_helpers.rb +17 -17
- metadata +30 -22
- data/changelog.d/fragments/.keep +0 -0
- data/changelog.d/template.md.j2 +0 -29
- data/lib/familia/features/external_identifiers/external_identifier_field_type.rb +0 -120
- data/lib/familia/features/external_identifiers.rb +0 -111
- data/lib/familia/features/object_identifiers/object_identifier_field_type.rb +0 -91
- data/lib/familia/features/object_identifiers.rb +0 -194
@@ -1,15 +1,15 @@
|
|
1
|
-
# try/features/
|
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
|
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 :
|
12
|
-
feature :
|
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 :
|
22
|
-
feature :
|
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 :
|
31
|
-
feature :
|
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
|
43
|
-
IntegrationTest.features_enabled.include?(:
|
42
|
+
## Object identifier feature is automatically included
|
43
|
+
IntegrationTest.features_enabled.include?(:object_identifier)
|
44
44
|
#==> true
|
45
45
|
|
46
|
-
## External
|
47
|
-
IntegrationTest.features_enabled.include?(:
|
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
|
-
#
|
78
|
-
|
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::
|
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::
|
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::
|
154
|
+
IntegrationTest.field_types[:extid].class.ancestors.include?(Familia::Features::ExternalIdentifier::ExternalIdentifierFieldType)
|
157
155
|
#==> true
|
158
156
|
|
159
|
-
## Object
|
157
|
+
## Object identifier options are preserved
|
160
158
|
opts = IntegrationTest.feature_options
|
161
|
-
opts.key?(:
|
159
|
+
opts.key?(:object_identifier)
|
162
160
|
#==> true
|
163
161
|
|
164
|
-
## External
|
162
|
+
## External identifiersoptions are preserved
|
165
163
|
opts = IntegrationTest.feature_options
|
166
|
-
opts.key?(:
|
164
|
+
opts.key?(:external_identifier)
|
167
165
|
#==> true
|
168
166
|
|
169
167
|
## Generator default configuration is applied correctly
|
170
|
-
IntegrationTest.feature_options(:
|
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(:
|
172
|
+
IntegrationTest.feature_options(:external_identifier)[:prefix]
|
175
173
|
#=> "ext"
|
176
174
|
|
177
175
|
## Custom generator configuration is applied correctly
|
178
|
-
CustomIntegrationTest.feature_options(:
|
176
|
+
CustomIntegrationTest.feature_options(:object_identifier)[:generator]
|
179
177
|
#=> :hex
|
180
178
|
|
181
179
|
## Custom prefix configuration is applied correctly
|
182
|
-
CustomIntegrationTest.feature_options(:
|
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 (
|
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?(:
|
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/
|
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
|
7
|
+
# Test ObjectIdentifier feature functionality
|
8
8
|
|
9
9
|
# Basic class using default UUID v7 generator
|
10
10
|
class BasicObjectTest < Familia::Horreum
|
11
|
-
feature :
|
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 :
|
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 :
|
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 :
|
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 :
|
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?(:
|
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::
|
165
|
+
#=:> Familia::Features::ObjectIdentifier::ObjectIdentifierFieldType
|
166
166
|
|
167
167
|
## Generator configuration is accessible through feature options
|
168
|
-
BasicObjectTest.feature_options(:
|
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(:
|
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(:
|
176
|
+
HexTest.feature_options(:object_identifier)[:generator]
|
177
177
|
#=> :hex
|
178
178
|
|
179
179
|
## Custom proc class has proc generator
|
180
|
-
CustomProcTest.feature_options(:
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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.
|
51
|
-
fields
|
52
|
-
#=> [:
|
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
|
data/try/helpers/test_helpers.rb
CHANGED
@@ -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
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
60
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
-
|
165
|
-
- examples/
|
166
|
-
- examples/
|
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/
|
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/
|
198
|
-
- lib/familia/features/
|
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/
|
331
|
+
- try/features/external_identifier/external_identifier_try.rb
|
325
332
|
- try/features/feature_dependencies_try.rb
|
326
|
-
- try/features/
|
327
|
-
- try/features/
|
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
|
data/changelog.d/fragments/.keep
DELETED
File without changes
|
data/changelog.d/template.md.j2
DELETED
@@ -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
|