familia 2.0.0.pre12 → 2.0.0.pre13

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +2 -3
  3. data/CHANGELOG.rst +507 -0
  4. data/CLAUDE.md +1 -1
  5. data/Gemfile +1 -6
  6. data/Gemfile.lock +13 -7
  7. data/changelog.d/README.md +5 -5
  8. data/{setup.cfg → changelog.d/scriv.ini} +1 -1
  9. data/docs/guides/Feature-System-Autoloading.md +228 -0
  10. data/docs/guides/time-utilities.md +221 -0
  11. data/docs/migrating/v2.0.0-pre11.md +14 -16
  12. data/docs/migrating/v2.0.0-pre13.md +329 -0
  13. data/examples/autoloader/mega_customer/safe_dump_fields.rb +6 -0
  14. data/examples/autoloader/mega_customer.rb +17 -0
  15. data/familia.gemspec +1 -0
  16. data/lib/familia/autoloader.rb +53 -0
  17. data/lib/familia/base.rb +5 -0
  18. data/lib/familia/data_type.rb +4 -0
  19. data/lib/familia/encryption/encrypted_data.rb +4 -4
  20. data/lib/familia/encryption/manager.rb +6 -4
  21. data/lib/familia/encryption.rb +1 -1
  22. data/lib/familia/errors.rb +3 -0
  23. data/lib/familia/features/autoloadable.rb +113 -0
  24. data/lib/familia/features/encrypted_fields/concealed_string.rb +4 -2
  25. data/lib/familia/features/expiration.rb +4 -0
  26. data/lib/familia/features/quantization.rb +5 -0
  27. data/lib/familia/features/safe_dump.rb +7 -0
  28. data/lib/familia/features.rb +20 -16
  29. data/lib/familia/field_type.rb +2 -0
  30. data/lib/familia/horreum/core/serialization.rb +3 -3
  31. data/lib/familia/horreum/subclass/definition.rb +3 -4
  32. data/lib/familia/horreum.rb +2 -0
  33. data/lib/familia/json_serializer.rb +70 -0
  34. data/lib/familia/logging.rb +12 -10
  35. data/lib/familia/refinements/logger_trace.rb +57 -0
  36. data/lib/familia/refinements/snake_case.rb +40 -0
  37. data/lib/familia/refinements/time_utils.rb +248 -0
  38. data/lib/familia/refinements.rb +3 -49
  39. data/lib/familia/utils.rb +2 -0
  40. data/lib/familia/validation/{test_helpers.rb → validation_helpers.rb} +2 -2
  41. data/lib/familia/validation.rb +1 -1
  42. data/lib/familia/version.rb +1 -1
  43. data/lib/familia.rb +15 -3
  44. data/try/core/autoloader_try.rb +112 -0
  45. data/try/core/extensions_try.rb +38 -21
  46. data/try/core/familia_extended_try.rb +4 -3
  47. data/try/core/time_utils_try.rb +130 -0
  48. data/try/data_types/datatype_base_try.rb +3 -2
  49. data/try/features/autoloadable/autoloadable_try.rb +61 -0
  50. data/try/features/encrypted_fields/concealed_string_core_try.rb +8 -3
  51. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +59 -17
  52. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +36 -12
  53. data/try/features/feature_improvements_try.rb +2 -1
  54. data/try/features/real_feature_integration_try.rb +1 -1
  55. data/try/features/safe_dump/safe_dump_autoloading_try.rb +111 -0
  56. data/try/helpers/test_helpers.rb +24 -0
  57. data/try/integration/cross_component_try.rb +3 -1
  58. metadata +33 -6
  59. data/CHANGELOG.md +0 -247
  60. data/lib/familia/core_ext.rb +0 -135
  61. data/lib/familia/features/autoloader.rb +0 -57
@@ -188,18 +188,30 @@ rescue TypeError => e
188
188
  end
189
189
  #=> true
190
190
 
191
- ## JSON serialization is safe
192
- @user_json = {
193
- id: @user.id,
194
- username: @user.username,
195
- password: @user.password_hash
196
- }.to_json
197
-
198
- @user_json.include?("bcrypt")
199
- #=> false
191
+ ## JSON serialization prevents leakage by raising error
192
+ begin
193
+ user_json = {
194
+ id: @user.id,
195
+ username: @user.username,
196
+ password: @user.password_hash
197
+ }.to_json
198
+ false
199
+ rescue Familia::SerializerError
200
+ true
201
+ end
202
+ #=> true
200
203
 
201
- ## JSON contains concealed marker
202
- @user_json.include?("[CONCEALED]")
204
+ ## JSON serialization with ConcealedString raises error
205
+ begin
206
+ user_json = {
207
+ id: @user.id,
208
+ username: @user.username,
209
+ password: @user.password_hash
210
+ }.to_json
211
+ false
212
+ rescue Familia::SerializerError => e
213
+ e.message.include?("ConcealedString")
214
+ end
203
215
  #=> true
204
216
 
205
217
  ## Bulk field operations are secure
@@ -281,16 +293,46 @@ api_response = {
281
293
  }
282
294
  }
283
295
 
284
- @response_json = api_response.to_json
285
- @response_json.include?("bcrypt")
286
- #=> false
296
+ begin
297
+ @response_json = api_response.to_json
298
+ false
299
+ rescue Familia::SerializerError
300
+ true
301
+ end
302
+ #=> true
287
303
 
288
304
  ## API response doesn't leak secrets
289
- @response_json.include?("sk-1234567890abcdef")
290
- #=> false
305
+ api_response = {
306
+ user_id: @user.id,
307
+ credentials: {
308
+ password: @user.password_hash,
309
+ api_key: @user.api_secret
310
+ }
311
+ }
312
+
313
+ begin
314
+ @response_json = api_response.to_json
315
+ false
316
+ rescue Familia::SerializerError
317
+ true
318
+ end
319
+ #=> true
291
320
 
292
321
  ## API response contains concealed markers
293
- @response_json.include?("[CONCEALED]")
322
+ api_response = {
323
+ user_id: @user.id,
324
+ credentials: {
325
+ password: @user.password_hash,
326
+ api_key: @user.api_secret
327
+ }
328
+ }
329
+
330
+ begin
331
+ @response_json = api_response.to_json
332
+ false
333
+ rescue Familia::SerializerError => e
334
+ e.message.include?("ConcealedString")
335
+ end
294
336
  #=> true
295
337
 
296
338
  ## Debug logging safety
@@ -67,9 +67,14 @@ hash_result.keys.include?("api_token")
67
67
  @record.api_token.inspect
68
68
  #=> "[CONCEALED]"
69
69
 
70
- ## JSON serialization - to_json
71
- @record.api_token.to_json
72
- #=> "\"[CONCEALED]\""
70
+ ## JSON serialization - to_json (fails for security)
71
+ begin
72
+ @record.api_token.to_json
73
+ raise "Should have raised SerializerError"
74
+ rescue Familia::SerializerError => e
75
+ e.class
76
+ end
77
+ #=> Familia::SerializerError
73
78
 
74
79
  ## JSON serialization - as_json
75
80
  @record.api_token.as_json
@@ -100,12 +105,21 @@ hash_result.keys.include?("api_token")
100
105
  }
101
106
  }
102
107
 
103
- @serialized = @nested_data.to_json
104
- @serialized.include?("token-abc123456789")
105
- #=> false
108
+ begin
109
+ @serialized = @nested_data.to_json
110
+ false
111
+ rescue Familia::SerializerError
112
+ true
113
+ end
114
+ #=> true
106
115
 
107
- ## Nested JSON contains concealed markers
108
- @nested_data.to_json.include?("[CONCEALED]")
116
+ ## Nested JSON with ConcealedString raises error
117
+ begin
118
+ @nested_data.to_json
119
+ false
120
+ rescue Familia::SerializerError => e
121
+ e.message.include?("ConcealedString cannot be serialized")
122
+ end
109
123
  #=> true
110
124
 
111
125
  ## Array of mixed field types safety
@@ -116,11 +130,21 @@ hash_result.keys.include?("api_token")
116
130
  @record.secret_notes
117
131
  ]
118
132
 
119
- @mixed_array.to_json.include?("token-abc123456789")
120
- #=> false
133
+ begin
134
+ @mixed_array.to_json
135
+ false
136
+ rescue Familia::SerializerError
137
+ true
138
+ end
139
+ #=> true
121
140
 
122
- ## Mixed array preserves public data
123
- @mixed_array.to_json.include?("Public Record")
141
+ ## Mixed array with ConcealedString raises error
142
+ begin
143
+ @mixed_array.to_json
144
+ false
145
+ rescue Familia::SerializerError => e
146
+ e.message.include?("ConcealedString")
147
+ end
124
148
  #=> true
125
149
 
126
150
  ## String interpolation safety
@@ -46,7 +46,8 @@ class ::TestModelWithSafeDump
46
46
  safe_dump_field :id
47
47
  safe_dump_field :name
48
48
  safe_dump_field :status, ->(obj) { obj.active? ? 'active' : 'inactive' }
49
- safe_dump_fields :email, { computed_field: ->(obj) { "#{obj.name}-computed" } }
49
+ safe_dump_field :email
50
+ safe_dump_field :computed_field, ->(obj) { "#{obj.name}-computed" }
50
51
  end
51
52
 
52
53
  # Test field definitions in feature modules
@@ -101,7 +101,7 @@ SafeDumpCategoryTest.features_enabled.include?(:safe_dump)
101
101
  @safedump_result.keys.sort
102
102
  #=> [:email, :id, :public_name]
103
103
 
104
- ## Safe dump respects safe_dump_fields configuration
104
+ ## Safe dump respects safe_dump_field configuration
105
105
  @safedump_result.key?(:tryouts_cache_data)
106
106
  #=> false
107
107
 
@@ -0,0 +1,111 @@
1
+ # try/features/safe_dump/safe_dump_autoloading_try.rb
2
+
3
+ require_relative '../../../lib/familia'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+
7
+ # Create test directory structure for SafeDump autoloading testing
8
+ @test_dir = Dir.mktmpdir('familia_safe_dump_autoload_test')
9
+ @model_file = File.join(@test_dir, 'test_safe_dump_model.rb')
10
+ @extension_file = File.join(@test_dir, 'test_safe_dump_model', 'safe_dump_extensions.rb')
11
+ @extension_dir = File.join(@test_dir, 'test_safe_dump_model')
12
+
13
+ # Create directory structure
14
+ FileUtils.mkdir_p(@extension_dir)
15
+
16
+ # Write test model file that uses SafeDump
17
+ File.write(@model_file, <<~RUBY)
18
+ class TestSafeDumpModel < Familia::Horreum
19
+ field :name
20
+ field :email
21
+ field :secret
22
+
23
+ feature :safe_dump
24
+ end
25
+ RUBY
26
+
27
+ # Write extension file (pattern: model_name/safe_dump_*.rb)
28
+ File.write(@extension_file, <<~RUBY)
29
+ class TestSafeDumpModel
30
+ # Define safe dump fields
31
+ safe_dump_fields :name, :email
32
+
33
+ # Add method to verify autoloading worked
34
+ def extension_loaded?
35
+ true
36
+ end
37
+ end
38
+ RUBY
39
+
40
+ ## Test that SafeDump includes Autoloadable
41
+ Familia::Features::SafeDump.ancestors.include?(Familia::Features::Autoloadable)
42
+ #=> true
43
+
44
+ ## Test that SafeDump has post_inclusion_autoload capability
45
+ Familia::Features::SafeDump.respond_to?(:post_inclusion_autoload)
46
+ #=> true
47
+
48
+ ## Test SafeDump autoloading by loading model file
49
+ @model_instance = nil
50
+
51
+ begin
52
+ require @model_file
53
+ @model_instance = TestSafeDumpModel.new(
54
+ name: 'John Doe',
55
+ email: 'john@example.com',
56
+ secret: 'hidden data'
57
+ )
58
+ true
59
+ rescue => e
60
+ false
61
+ end
62
+ #=> true
63
+
64
+ ## Test that autoloaded extension method is available
65
+ @model_instance.respond_to?(:extension_loaded?)
66
+ #=> true
67
+
68
+ ## Test autoloaded extension method works
69
+ @model_instance.extension_loaded?
70
+ #=> true
71
+
72
+ ## Test that model was created successfully
73
+ @model_instance.class.name
74
+ #=> "TestSafeDumpModel"
75
+
76
+ ## Test that feature_options were set up correctly
77
+ TestSafeDumpModel.respond_to?(:feature_options)
78
+ #=> true
79
+
80
+ ## Test that safe_dump fields were loaded from extension file
81
+ TestSafeDumpModel.safe_dump_field_names.sort
82
+ #=> [:email, :name]
83
+
84
+ ## Test that safe_dump functionality works with autoloaded fields
85
+ @dump_result = @model_instance.safe_dump
86
+ @dump_result.keys.sort
87
+ #=> [:email, :name]
88
+
89
+ ## Test that only safe fields are dumped
90
+ @dump_result[:name]
91
+ #=> "John Doe"
92
+
93
+ ## Test that email field is included
94
+ @dump_result[:email]
95
+ #=> "john@example.com"
96
+
97
+ ## Test that secret field is excluded (not in safe_dump_fields)
98
+ @dump_result.key?(:secret)
99
+ #=> false
100
+
101
+ ## Test that feature_options can be retrieved
102
+ @options = TestSafeDumpModel.feature_options(:safe_dump)
103
+ @options.is_a?(Hash)
104
+ #=> true
105
+
106
+ ## Test safe_dump feature is recognized
107
+ TestSafeDumpModel.features_enabled.include?(:safe_dump)
108
+ #=> true
109
+
110
+ # Cleanup test files and directories
111
+ FileUtils.rm_rf(@test_dir)
@@ -12,6 +12,8 @@ Familia.enable_database_logging = true
12
12
  Familia.enable_database_counter = true
13
13
 
14
14
  class Bone < Familia::Horreum
15
+ using Familia::Refinements::TimeUtils
16
+
15
17
  identifier_field :token
16
18
  field :token
17
19
  field :name
@@ -39,6 +41,9 @@ class Bourne < Familia::Horreum
39
41
  end
40
42
 
41
43
  class Customer < Familia::Horreum
44
+
45
+ using Familia::Refinements::TimeUtils
46
+
42
47
  logical_database 15 # Use something other than the default DB
43
48
  default_expiration 5.years
44
49
 
@@ -101,6 +106,8 @@ end
101
106
  @c.custid = 'd@example.com'
102
107
 
103
108
  class Session < Familia::Horreum
109
+ using Familia::Refinements::TimeUtils
110
+
104
111
  logical_database 14 # don't use Onetime's default DB
105
112
  default_expiration 180.minutes
106
113
 
@@ -123,6 +130,8 @@ end
123
130
  @s = Session.new
124
131
 
125
132
  class CustomDomain < Familia::Horreum
133
+ using Familia::Refinements::TimeUtils
134
+
126
135
  feature :expiration
127
136
 
128
137
  class_sorted_set :values
@@ -152,6 +161,8 @@ end
152
161
  @d.custid = @c.custid
153
162
 
154
163
  class Limiter < Familia::Horreum
164
+ using Familia::Refinements::TimeUtils
165
+
155
166
  feature :expiration
156
167
  feature :quantization
157
168
 
@@ -229,3 +240,16 @@ module ConcealedStringTestHelper
229
240
  end
230
241
  end
231
242
  end
243
+
244
+ # Helper module for testing refinements in tryouts
245
+ module RefinedContext
246
+ using Familia::Refinements::TimeUtils
247
+
248
+ def self.eval_in_refined_context(code)
249
+ eval(code)
250
+ end
251
+
252
+ def self.instance_eval_in_refined_context(code)
253
+ instance_eval(code)
254
+ end
255
+ end
@@ -2,7 +2,9 @@
2
2
 
3
3
  require_relative '../helpers/test_helpers'
4
4
 
5
- TestUser = Class.new(Familia::Horreum) do
5
+ class TestUser < Familia::Horreum
6
+ using Familia::Refinements::SnakeCase
7
+
6
8
  identifier_field :email
7
9
  field :email
8
10
  field :name
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.pre12
4
+ version: 2.0.0.pre13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -65,6 +65,20 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '1.7'
68
+ - !ruby/object:Gem::Dependency
69
+ name: oj
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.16'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.16'
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: redis
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -129,7 +143,7 @@ files:
129
143
  - ".rubocop.yml"
130
144
  - ".rubocop_todo.yml"
131
145
  - ".yardopts"
132
- - CHANGELOG.md
146
+ - CHANGELOG.rst
133
147
  - CLAUDE.md
134
148
  - Gemfile
135
149
  - Gemfile.lock
@@ -137,6 +151,7 @@ files:
137
151
  - README.md
138
152
  - bin/irb
139
153
  - changelog.d/README.md
154
+ - changelog.d/scriv.ini
140
155
  - docs/archive/.gitignore
141
156
  - docs/archive/FAMILIA_RELATIONSHIPS.md
142
157
  - docs/archive/FAMILIA_TECHNICAL.md
@@ -148,6 +163,7 @@ files:
148
163
  - docs/guides/Connection-Pooling-Guide.md
149
164
  - docs/guides/Encrypted-Fields-Overview.md
150
165
  - docs/guides/Expiration-Feature-Guide.md
166
+ - docs/guides/Feature-System-Autoloading.md
151
167
  - docs/guides/Feature-System-Guide.md
152
168
  - docs/guides/Features-System-Developer-Guide.md
153
169
  - docs/guides/Field-System-Guide.md
@@ -158,23 +174,27 @@ files:
158
174
  - docs/guides/Security-Model.md
159
175
  - docs/guides/Transient-Fields-Guide.md
160
176
  - docs/guides/relationships-methods.md
177
+ - docs/guides/time-utilities.md
161
178
  - docs/migrating/.gitignore
162
179
  - docs/migrating/v2.0.0-pre.md
163
180
  - docs/migrating/v2.0.0-pre11.md
164
181
  - docs/migrating/v2.0.0-pre12.md
182
+ - docs/migrating/v2.0.0-pre13.md
165
183
  - docs/migrating/v2.0.0-pre5.md
166
184
  - docs/migrating/v2.0.0-pre6.md
167
185
  - docs/migrating/v2.0.0-pre7.md
168
186
  - docs/overview.md
169
187
  - docs/reference/auditing_database_commands.rb
188
+ - examples/autoloader/mega_customer.rb
189
+ - examples/autoloader/mega_customer/safe_dump_fields.rb
170
190
  - examples/permissions.rb
171
191
  - examples/relationships.rb
172
192
  - examples/safe_dump.rb
173
193
  - familia.gemspec
174
194
  - lib/familia.rb
195
+ - lib/familia/autoloader.rb
175
196
  - lib/familia/base.rb
176
197
  - lib/familia/connection.rb
177
- - lib/familia/core_ext.rb
178
198
  - lib/familia/data_type.rb
179
199
  - lib/familia/data_type/commands.rb
180
200
  - lib/familia/data_type/serialization.rb
@@ -196,7 +216,7 @@ files:
196
216
  - lib/familia/encryption/request_cache.rb
197
217
  - lib/familia/errors.rb
198
218
  - lib/familia/features.rb
199
- - lib/familia/features/autoloader.rb
219
+ - lib/familia/features/autoloadable.rb
200
220
  - lib/familia/features/encrypted_fields.rb
201
221
  - lib/familia/features/encrypted_fields/concealed_string.rb
202
222
  - lib/familia/features/encrypted_fields/encrypted_field_type.rb
@@ -229,22 +249,26 @@ files:
229
249
  - lib/familia/horreum/subclass/definition.rb
230
250
  - lib/familia/horreum/subclass/management.rb
231
251
  - lib/familia/horreum/subclass/related_fields_management.rb
252
+ - lib/familia/json_serializer.rb
232
253
  - lib/familia/logging.rb
233
254
  - lib/familia/multi_result.rb
234
255
  - lib/familia/refinements.rb
256
+ - lib/familia/refinements/logger_trace.rb
257
+ - lib/familia/refinements/snake_case.rb
258
+ - lib/familia/refinements/time_utils.rb
235
259
  - lib/familia/secure_identifier.rb
236
260
  - lib/familia/settings.rb
237
261
  - lib/familia/utils.rb
238
262
  - lib/familia/validation.rb
239
263
  - lib/familia/validation/command_recorder.rb
240
264
  - lib/familia/validation/expectations.rb
241
- - lib/familia/validation/test_helpers.rb
265
+ - lib/familia/validation/validation_helpers.rb
242
266
  - lib/familia/validation/validator.rb
243
267
  - lib/familia/verifiable_identifier.rb
244
268
  - lib/familia/version.rb
245
269
  - lib/middleware/database_middleware.rb
246
- - setup.cfg
247
270
  - try/configuration/scenarios_try.rb
271
+ - try/core/autoloader_try.rb
248
272
  - try/core/base_enhancements_try.rb
249
273
  - try/core/connection_try.rb
250
274
  - try/core/create_method_try.rb
@@ -258,6 +282,7 @@ files:
258
282
  - try/core/pools_try.rb
259
283
  - try/core/secure_identifier_try.rb
260
284
  - try/core/settings_try.rb
285
+ - try/core/time_utils_try.rb
261
286
  - try/core/tools_try.rb
262
287
  - try/core/utils_try.rb
263
288
  - try/core/verifiable_identifier_try.rb
@@ -310,6 +335,7 @@ files:
310
335
  - try/encryption/providers/xchacha20_poly1305_provider_try.rb
311
336
  - try/encryption/roundtrip_validation_try.rb
312
337
  - try/encryption/secure_memory_handling_try.rb
338
+ - try/features/autoloadable/autoloadable_try.rb
313
339
  - try/features/encrypted_fields/aad_protection_try.rb
314
340
  - try/features/encrypted_fields/concealed_string_core_try.rb
315
341
  - try/features/encrypted_fields/context_isolation_try.rb
@@ -344,6 +370,7 @@ files:
344
370
  - try/features/relationships/relationships_performance_working_try.rb
345
371
  - try/features/relationships/relationships_try.rb
346
372
  - try/features/safe_dump/safe_dump_advanced_try.rb
373
+ - try/features/safe_dump/safe_dump_autoloading_try.rb
347
374
  - try/features/safe_dump/safe_dump_try.rb
348
375
  - try/features/transient_fields/redacted_string_try.rb
349
376
  - try/features/transient_fields/refresh_reset_try.rb