familia 2.0.0.pre26 → 2.1.0

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rst +94 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +12 -2
  5. data/README.md +1 -3
  6. data/docs/guides/feature-encrypted-fields.md +1 -1
  7. data/docs/guides/feature-expiration.md +1 -1
  8. data/docs/guides/feature-quantization.md +1 -1
  9. data/docs/guides/writing-migrations.md +345 -0
  10. data/docs/overview.md +7 -7
  11. data/docs/reference/api-technical.md +103 -7
  12. data/examples/migrations/v1_to_v2_serialization_migration.rb +374 -0
  13. data/examples/schemas/customer.json +33 -0
  14. data/examples/schemas/session.json +27 -0
  15. data/familia.gemspec +3 -2
  16. data/lib/familia/features/schema_validation.rb +139 -0
  17. data/lib/familia/migration/base.rb +447 -0
  18. data/lib/familia/migration/errors.rb +31 -0
  19. data/lib/familia/migration/model.rb +418 -0
  20. data/lib/familia/migration/pipeline.rb +226 -0
  21. data/lib/familia/migration/rake_tasks.rake +3 -0
  22. data/lib/familia/migration/rake_tasks.rb +160 -0
  23. data/lib/familia/migration/registry.rb +364 -0
  24. data/lib/familia/migration/runner.rb +311 -0
  25. data/lib/familia/migration/script.rb +234 -0
  26. data/lib/familia/migration.rb +43 -0
  27. data/lib/familia/schema_registry.rb +173 -0
  28. data/lib/familia/settings.rb +63 -1
  29. data/lib/familia/version.rb +1 -1
  30. data/lib/familia.rb +1 -0
  31. data/try/features/schema_registry_try.rb +193 -0
  32. data/try/features/schema_validation_feature_try.rb +218 -0
  33. data/try/migration/base_try.rb +226 -0
  34. data/try/migration/errors_try.rb +67 -0
  35. data/try/migration/integration_try.rb +451 -0
  36. data/try/migration/model_try.rb +431 -0
  37. data/try/migration/pipeline_try.rb +460 -0
  38. data/try/migration/rake_tasks_try.rb +61 -0
  39. data/try/migration/registry_try.rb +199 -0
  40. data/try/migration/runner_try.rb +311 -0
  41. data/try/migration/schema_validation_try.rb +201 -0
  42. data/try/migration/script_try.rb +192 -0
  43. data/try/migration/v1_to_v2_serialization_try.rb +513 -0
  44. data/try/performance/benchmarks_try.rb +11 -12
  45. metadata +45 -27
  46. data/docs/migrating/v2.0.0-pre.md +0 -84
  47. data/docs/migrating/v2.0.0-pre11.md +0 -253
  48. data/docs/migrating/v2.0.0-pre12.md +0 -306
  49. data/docs/migrating/v2.0.0-pre13.md +0 -95
  50. data/docs/migrating/v2.0.0-pre14.md +0 -37
  51. data/docs/migrating/v2.0.0-pre18.md +0 -58
  52. data/docs/migrating/v2.0.0-pre19.md +0 -197
  53. data/docs/migrating/v2.0.0-pre22.md +0 -241
  54. data/docs/migrating/v2.0.0-pre5.md +0 -131
  55. data/docs/migrating/v2.0.0-pre6.md +0 -154
  56. data/docs/migrating/v2.0.0-pre7.md +0 -222
@@ -0,0 +1,513 @@
1
+ # try/migration/v1_to_v2_serialization_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Tests for V1 to V2 Serialization Migration
6
+ #
7
+ # Demonstrates migrating Familia Horreum objects from v1.x serialization
8
+ # (plain strings via distinguisher) to v2.0 serialization (universal JSON).
9
+
10
+ require_relative '../support/helpers/test_helpers'
11
+ require_relative '../../lib/familia/migration'
12
+ require_relative '../../examples/migrations/v1_to_v2_serialization_migration'
13
+
14
+ Familia.debug = false
15
+
16
+ @redis = Familia.dbclient
17
+ @test_id = "#{Process.pid}_#{Time.now.to_i}"
18
+ @prefix = "familia:test:v1v2:#{@test_id}"
19
+
20
+ @initial_migrations = Familia::Migration.migrations.dup
21
+
22
+ # Test model for migration testing
23
+ class V1V2TestRecord < Familia::Horreum
24
+ identifier_field :record_id
25
+ field :record_id
26
+ field :name # String field
27
+ field :age # Integer field
28
+ field :balance # Float field
29
+ field :active # Boolean field
30
+ field :verified # Boolean field (false)
31
+ field :settings # Hash field
32
+ field :tags # Array field
33
+ field :notes # String or nil
34
+ field :created_at # Timestamp (integer)
35
+ end
36
+
37
+ # Concrete migration for V1V2TestRecord
38
+ class V1V2TestMigration < V1ToV2SerializationMigration
39
+ self.migration_id = 'test_v1v2_migration'
40
+ self.description = 'Test migration for v1 to v2 serialization'
41
+
42
+ def prepare
43
+ @model_class = V1V2TestRecord
44
+ @batch_size = 10
45
+ super
46
+ end
47
+
48
+ def field_types_for_model
49
+ {
50
+ record_id: :string,
51
+ name: :string,
52
+ age: :integer,
53
+ balance: :float,
54
+ active: :boolean,
55
+ verified: :boolean,
56
+ settings: :hash,
57
+ tags: :array,
58
+ notes: :string,
59
+ created_at: :timestamp
60
+ }
61
+ end
62
+ end
63
+
64
+ # Helper to create v1.x format data directly in Redis
65
+ # Simulates how v1.x Familia stored values
66
+ def create_v1_record(suffix, data = {})
67
+ id = "#{@test_id}_#{suffix}"
68
+ # Use the model's actual prefix to match what the migration scans for
69
+ prefix = V1V2TestRecord.prefix
70
+ dbkey = "#{prefix}:#{id}:object"
71
+
72
+ # v1.x serialization: plain strings, no JSON for simple types
73
+ v1_data = {
74
+ 'record_id' => id,
75
+ 'name' => data[:name] || 'Test User',
76
+ 'age' => (data[:age] || 0).to_s, # v1: Integer as string
77
+ 'balance' => (data[:balance] || 0.0).to_s, # v1: Float as string
78
+ 'active' => (data[:active] || false).to_s, # v1: Boolean as "true"/"false"
79
+ 'verified' => (data[:verified] || false).to_s, # v1: Boolean as "true"/"false"
80
+ 'settings' => Familia::JsonSerializer.dump(data[:settings] || {}), # v1: Hash already JSON
81
+ 'tags' => Familia::JsonSerializer.dump(data[:tags] || []), # v1: Array already JSON
82
+ 'created_at' => (data[:created_at] || 0).to_s # v1: Timestamp as string
83
+ }
84
+
85
+ # Add notes only if present (v1 skipped nil values or stored as "")
86
+ v1_data['notes'] = data[:notes] || '' if data.key?(:notes)
87
+
88
+ @redis.hmset(dbkey, *v1_data.flatten)
89
+
90
+ # Register in instances sorted set (zset) for migration to find
91
+ # Familia uses zset for instances tracking with timestamp as score
92
+ @redis.zadd("#{prefix}:instances", Time.now.to_f, id)
93
+
94
+ { dbkey: dbkey, id: id }
95
+ end
96
+
97
+ # Helper to read raw Redis values (bypass Familia)
98
+ def read_raw_redis(dbkey)
99
+ @redis.hgetall(dbkey)
100
+ end
101
+
102
+ # Helper to load a record from the dbkey after migration
103
+ # Uses from_redis to load with v2 deserialization
104
+ def load_migrated_record(dbkey)
105
+ V1V2TestRecord.from_redis(dbkey)
106
+ end
107
+
108
+ # Helper to cleanup test records
109
+ def cleanup_records
110
+ prefix = V1V2TestRecord.prefix
111
+ pattern = "#{prefix}:#{@test_id}_*"
112
+ @redis.keys(pattern).each { |k| @redis.del(k) }
113
+ # Clean up instances zset entries for our test ids
114
+ @redis.zremrangebylex("#{prefix}:instances", "[#{@test_id}_", "[#{@test_id}_\xff")
115
+ end
116
+
117
+ cleanup_records
118
+
119
+ ## V1ToV2SerializationMigration is a subclass of Model
120
+ V1ToV2SerializationMigration < Familia::Migration::Model
121
+ #=> true
122
+
123
+ ## Base migration_id is set
124
+ V1ToV2SerializationMigration.migration_id
125
+ #=> '20260201_000000_v1_to_v2_serialization_base'
126
+
127
+ ## Test migration initializes correctly
128
+ migration = V1V2TestMigration.new
129
+ migration.is_a?(V1ToV2SerializationMigration)
130
+ #=> true
131
+
132
+ ## Test migration prepares with field types
133
+ migration = V1V2TestMigration.new
134
+ migration.prepare
135
+ migration.instance_variable_get(:@field_types)[:age]
136
+ #=> :integer
137
+
138
+ ## Test migration prepares with batch_size
139
+ migration = V1V2TestMigration.new
140
+ migration.prepare
141
+ migration.batch_size
142
+ #=> 10
143
+
144
+ ## Detect type correctly identifies integers
145
+ migration = V1V2TestMigration.new
146
+ migration.send(:detect_type, '42')
147
+ #=> :integer
148
+
149
+ ## Detect type correctly identifies negative integers
150
+ migration = V1V2TestMigration.new
151
+ migration.send(:detect_type, '-123')
152
+ #=> :integer
153
+
154
+ ## Detect type correctly identifies floats
155
+ migration = V1V2TestMigration.new
156
+ migration.send(:detect_type, '3.14')
157
+ #=> :float
158
+
159
+ ## Detect type correctly identifies booleans
160
+ migration = V1V2TestMigration.new
161
+ [migration.send(:detect_type, 'true'), migration.send(:detect_type, 'false')]
162
+ #=> [:boolean, :boolean]
163
+
164
+ ## Detect type correctly identifies hashes
165
+ migration = V1V2TestMigration.new
166
+ migration.send(:detect_type, '{"key":"value"}')
167
+ #=> :hash
168
+
169
+ ## Detect type correctly identifies arrays
170
+ migration = V1V2TestMigration.new
171
+ migration.send(:detect_type, '["a","b","c"]')
172
+ #=> :array
173
+
174
+ ## Detect type defaults to string for plain text
175
+ migration = V1V2TestMigration.new
176
+ migration.send(:detect_type, 'hello world')
177
+ #=> :string
178
+
179
+ ## Detect type recognizes v2.0 JSON-quoted strings
180
+ migration = V1V2TestMigration.new
181
+ migration.send(:detect_type, '"already quoted"')
182
+ #=> :string
183
+
184
+ ## Parse v1 string value returns string as-is
185
+ migration = V1V2TestMigration.new
186
+ migration.send(:parse_v1_value, 'hello', :string)
187
+ #=> 'hello'
188
+
189
+ ## Parse v1 integer value converts to Integer
190
+ migration = V1V2TestMigration.new
191
+ migration.send(:parse_v1_value, '42', :integer)
192
+ #=> 42
193
+
194
+ ## Parse v1 float value converts to Float
195
+ migration = V1V2TestMigration.new
196
+ migration.send(:parse_v1_value, '3.14', :float)
197
+ #=> 3.14
198
+
199
+ ## Parse v1 boolean true converts to TrueClass
200
+ migration = V1V2TestMigration.new
201
+ migration.send(:parse_v1_value, 'true', :boolean)
202
+ #=> true
203
+
204
+ ## Parse v1 boolean false converts to FalseClass
205
+ migration = V1V2TestMigration.new
206
+ migration.send(:parse_v1_value, 'false', :boolean)
207
+ #=> false
208
+
209
+ ## Parse v1 hash value returns Hash
210
+ migration = V1V2TestMigration.new
211
+ migration.send(:parse_v1_value, '{"theme":"dark"}', :hash)
212
+ #=> {"theme"=>"dark"}
213
+
214
+ ## Parse v1 array value returns Array
215
+ migration = V1V2TestMigration.new
216
+ migration.send(:parse_v1_value, '["a","b"]', :array)
217
+ #=> ["a", "b"]
218
+
219
+ ## Parse v1 timestamp value converts to Integer
220
+ migration = V1V2TestMigration.new
221
+ migration.send(:parse_v1_value, '1706745600', :timestamp)
222
+ #=> 1706745600
223
+
224
+ ## Convert value transforms v1 string to v2 JSON-quoted string
225
+ migration = V1V2TestMigration.new
226
+ migration.send(:convert_value, 'hello', :string)
227
+ #=> '"hello"'
228
+
229
+ ## Convert value transforms v1 integer string to v2 JSON integer
230
+ migration = V1V2TestMigration.new
231
+ migration.send(:convert_value, '42', :integer)
232
+ #=> '42'
233
+
234
+ ## Convert value transforms v1 float string to v2 JSON float
235
+ migration = V1V2TestMigration.new
236
+ migration.send(:convert_value, '3.14', :float)
237
+ #=> '3.14'
238
+
239
+ ## Convert value transforms v1 boolean string to v2 JSON boolean
240
+ migration = V1V2TestMigration.new
241
+ migration.send(:convert_value, 'true', :boolean)
242
+ #=> 'true'
243
+
244
+ ## Convert value transforms empty string (v1 nil) to v2 null
245
+ migration = V1V2TestMigration.new
246
+ migration.send(:convert_value, '', :string)
247
+ #=> 'null'
248
+
249
+ ## Already v2 format detects JSON-quoted strings
250
+ migration = V1V2TestMigration.new
251
+ migration.prepare
252
+ migration.send(:already_v2_format?, '"hello"', :string)
253
+ #=> true
254
+
255
+ ## Already v2 format rejects plain strings
256
+ migration = V1V2TestMigration.new
257
+ migration.prepare
258
+ migration.send(:already_v2_format?, 'hello', :string)
259
+ #=> false
260
+
261
+ ## Already v2 format accepts JSON hashes
262
+ migration = V1V2TestMigration.new
263
+ migration.prepare
264
+ migration.send(:already_v2_format?, '{"key":"value"}', :hash)
265
+ #=> true
266
+
267
+ ## Already v2 format accepts JSON arrays
268
+ migration = V1V2TestMigration.new
269
+ migration.prepare
270
+ migration.send(:already_v2_format?, '["a","b"]', :array)
271
+ #=> true
272
+
273
+ ## V1 data is created correctly for testing
274
+ cleanup_records
275
+ result = create_v1_record('basic', name: 'Alice', age: 30, balance: 99.99, active: true, verified: false)
276
+ raw = read_raw_redis(result[:dbkey])
277
+ raw['age']
278
+ #=> '30'
279
+
280
+ ## V1 data stores boolean as string
281
+ cleanup_records
282
+ result = create_v1_record('bool', active: true, verified: false)
283
+ raw = read_raw_redis(result[:dbkey])
284
+ [raw['active'], raw['verified']]
285
+ #=> ['true', 'false']
286
+
287
+ ## V1 data stores name as plain string (not JSON-quoted)
288
+ cleanup_records
289
+ result = create_v1_record('str', name: 'Bob Smith')
290
+ raw = read_raw_redis(result[:dbkey])
291
+ raw['name']
292
+ #=> 'Bob Smith'
293
+
294
+ ## Migration converts v1 integer field to v2 format
295
+ cleanup_records
296
+ result = create_v1_record('int_test', age: 25)
297
+ migration = V1V2TestMigration.new(run: true)
298
+ migration.prepare
299
+ migration.migrate
300
+ raw = read_raw_redis(result[:dbkey])
301
+ # age stays '25' (JSON number, which is the same string representation)
302
+ # but now it will be parsed correctly by v2 deserializer
303
+ raw['age']
304
+ #=> '25'
305
+
306
+ ## Migration converts v1 string field to v2 JSON-quoted format
307
+ cleanup_records
308
+ result = create_v1_record('str_test', name: 'Charlie')
309
+ migration = V1V2TestMigration.new(run: true)
310
+ migration.prepare
311
+ migration.migrate
312
+ raw = read_raw_redis(result[:dbkey])
313
+ raw['name']
314
+ #=> '"Charlie"'
315
+
316
+ ## Migration converts v1 boolean field correctly
317
+ cleanup_records
318
+ result = create_v1_record('bool_test', active: true)
319
+ migration = V1V2TestMigration.new(run: true)
320
+ migration.prepare
321
+ migration.migrate
322
+ raw = read_raw_redis(result[:dbkey])
323
+ # Boolean fields stay as 'true'/'false' (same JSON representation)
324
+ raw['active']
325
+ #=> 'true'
326
+
327
+ ## Migration converts v1 float field correctly
328
+ cleanup_records
329
+ result = create_v1_record('float_test', balance: 123.45)
330
+ migration = V1V2TestMigration.new(run: true)
331
+ migration.prepare
332
+ migration.migrate
333
+ raw = read_raw_redis(result[:dbkey])
334
+ # Float representation stays the same in JSON
335
+ raw['balance']
336
+ #=> '123.45'
337
+
338
+ ## Migration converts v1 empty string (nil) to v2 null
339
+ cleanup_records
340
+ result = create_v1_record('nil_test', notes: nil)
341
+ migration = V1V2TestMigration.new(run: true)
342
+ migration.prepare
343
+ migration.migrate
344
+ raw = read_raw_redis(result[:dbkey])
345
+ raw['notes']
346
+ #=> 'null'
347
+
348
+ ## Migration preserves v1 hash field (already JSON)
349
+ cleanup_records
350
+ settings = { 'theme' => 'dark', 'lang' => 'en' }
351
+ result = create_v1_record('hash_test', settings: settings)
352
+ migration = V1V2TestMigration.new(run: true)
353
+ migration.prepare
354
+ migration.migrate
355
+ raw = read_raw_redis(result[:dbkey])
356
+ # Hash stays as JSON object
357
+ Familia::JsonSerializer.parse(raw['settings'])
358
+ #=> {"theme"=>"dark", "lang"=>"en"}
359
+
360
+ ## Migration preserves v1 array field (already JSON)
361
+ cleanup_records
362
+ tags = ['ruby', 'redis', 'orm']
363
+ result = create_v1_record('array_test', tags: tags)
364
+ migration = V1V2TestMigration.new(run: true)
365
+ migration.prepare
366
+ migration.migrate
367
+ raw = read_raw_redis(result[:dbkey])
368
+ # Array stays as JSON array
369
+ Familia::JsonSerializer.parse(raw['tags'])
370
+ #=> ["ruby", "redis", "orm"]
371
+
372
+ ## Migrated data loads correctly with v2 deserializer
373
+ cleanup_records
374
+ result = create_v1_record('load_test', name: 'Diana', age: 28, active: true)
375
+ migration = V1V2TestMigration.new(run: true)
376
+ migration.prepare
377
+ migration.migrate
378
+ record = V1V2TestRecord.find_by_key(result[:dbkey])
379
+ [record.name, record.age, record.active]
380
+ #=> ['Diana', 28, true]
381
+
382
+ ## Migrated integer field returns Integer class
383
+ cleanup_records
384
+ result = create_v1_record('type_int', age: 35)
385
+ migration = V1V2TestMigration.new(run: true)
386
+ migration.prepare
387
+ migration.migrate
388
+ record = V1V2TestRecord.find_by_key(result[:dbkey])
389
+ record.age.class
390
+ #=> Integer
391
+
392
+ ## Migrated boolean field returns TrueClass
393
+ cleanup_records
394
+ result = create_v1_record('type_bool', active: true)
395
+ migration = V1V2TestMigration.new(run: true)
396
+ migration.prepare
397
+ migration.migrate
398
+ record = V1V2TestRecord.find_by_key(result[:dbkey])
399
+ record.active.class
400
+ #=> TrueClass
401
+
402
+ ## Migrated boolean false field returns FalseClass
403
+ cleanup_records
404
+ result = create_v1_record('type_bool_false', verified: false)
405
+ migration = V1V2TestMigration.new(run: true)
406
+ migration.prepare
407
+ migration.migrate
408
+ record = V1V2TestRecord.find_by_key(result[:dbkey])
409
+ record.verified.class
410
+ #=> FalseClass
411
+
412
+ ## Migrated float field returns Float class
413
+ cleanup_records
414
+ result = create_v1_record('type_float', balance: 99.99)
415
+ migration = V1V2TestMigration.new(run: true)
416
+ migration.prepare
417
+ migration.migrate
418
+ record = V1V2TestRecord.find_by_key(result[:dbkey])
419
+ record.balance.class
420
+ #=> Float
421
+
422
+ ## Migrated nil field returns NilClass
423
+ cleanup_records
424
+ result = create_v1_record('type_nil', notes: nil)
425
+ migration = V1V2TestMigration.new(run: true)
426
+ migration.prepare
427
+ migration.migrate
428
+ record = V1V2TestRecord.find_by_key(result[:dbkey])
429
+ record.notes.class
430
+ #=> NilClass
431
+
432
+ ## Migration respects dry_run mode (no changes made)
433
+ cleanup_records
434
+ result = create_v1_record('dry_run', name: 'Eve')
435
+ original_name = read_raw_redis(result[:dbkey])['name']
436
+ migration = V1V2TestMigration.new(run: false) # dry run
437
+ migration.prepare
438
+ migration.migrate
439
+ raw = read_raw_redis(result[:dbkey])
440
+ raw['name'] == original_name # Should be unchanged
441
+ #=> true
442
+
443
+ ## Migration tracks records_updated statistic
444
+ cleanup_records
445
+ create_v1_record('stat1', name: 'F1')
446
+ create_v1_record('stat2', name: 'F2')
447
+ migration = V1V2TestMigration.new(run: true)
448
+ migration.prepare
449
+ migration.migrate
450
+ migration.records_updated >= 2
451
+ #=> true
452
+
453
+ ## Migration tracks fields_converted statistic
454
+ # Note: Integer/float fields (age, balance) have same JSON representation in v1 and v2
455
+ # Only string fields (record_id, name) need actual conversion (adding JSON quotes)
456
+ cleanup_records
457
+ create_v1_record('conv1', name: 'G1', age: 20, balance: 10.5)
458
+ migration = V1V2TestMigration.new(run: true)
459
+ migration.prepare
460
+ migration.migrate
461
+ migration.stats[:fields_converted] >= 2
462
+ #=> true
463
+
464
+ ## Migration processes multiple records correctly
465
+ cleanup_records
466
+ create_v1_record('multi1', name: 'H1', age: 21)
467
+ create_v1_record('multi2', name: 'H2', age: 22)
468
+ create_v1_record('multi3', name: 'H3', age: 23)
469
+ migration = V1V2TestMigration.new(run: true)
470
+ migration.prepare
471
+ migration.migrate
472
+ migration.total_scanned >= 3
473
+ #=> true
474
+
475
+ ## Already migrated records are skipped on re-run
476
+ cleanup_records
477
+ result = create_v1_record('rerun', name: 'Iris', age: 30)
478
+ migration1 = V1V2TestMigration.new(run: true)
479
+ migration1.prepare
480
+ migration1.migrate
481
+ first_converted = migration1.stats[:fields_converted]
482
+ migration2 = V1V2TestMigration.new(run: true)
483
+ migration2.prepare
484
+ migration2.migrate
485
+ second_converted = migration2.stats[:fields_converted]
486
+ # Second run should convert zero fields (already v2 format)
487
+ second_converted
488
+ #=> 0
489
+
490
+ ## Complete round-trip: create v1 data, migrate, load with v2, save, reload
491
+ cleanup_records
492
+ result = create_v1_record('roundtrip',
493
+ name: 'Jack',
494
+ age: 40,
495
+ balance: 500.00,
496
+ active: true,
497
+ verified: false,
498
+ settings: { 'pref' => 'value' },
499
+ tags: ['tag1', 'tag2'],
500
+ created_at: 1706745600
501
+ )
502
+ migration = V1V2TestMigration.new(run: true)
503
+ migration.prepare
504
+ migration.migrate
505
+ record = V1V2TestRecord.find_by_key(result[:dbkey])
506
+ record.name = 'Jack Updated'
507
+ record.save
508
+ reloaded = V1V2TestRecord.find_by_key(result[:dbkey])
509
+ [reloaded.name, reloaded.age.class, reloaded.active.class]
510
+ #=> ['Jack Updated', Integer, TrueClass]
511
+
512
+ cleanup_records
513
+ Familia::Migration.migrations.replace(@initial_migrations)
@@ -8,12 +8,6 @@ require_relative '../support/helpers/test_helpers'
8
8
  require 'benchmark'
9
9
 
10
10
  ## serialization performance comparison
11
- user_class = Class.new(Familia::Horreum) do
12
- identifier_field :email
13
- field :name
14
- field :data
15
- end
16
-
17
11
  large_data = { metadata: "x" * 1000, items: (1..1000).to_a }
18
12
 
19
13
  json_time = Benchmark.realtime do
@@ -25,11 +19,13 @@ familia_time = Benchmark.realtime do
25
19
  end
26
20
 
27
21
  json_time > 0 && familia_time > 0
28
- #=!> StandardError
22
+ #=> true
29
23
 
30
24
  ## bulk operations vs individual saves
31
25
  user_class = Class.new(Familia::Horreum) do
26
+ prefix :benchuser1
32
27
  identifier_field :email
28
+ field :email
33
29
  field :name
34
30
  field :data
35
31
  end
@@ -46,23 +42,26 @@ end
46
42
  users.each(&:delete!)
47
43
 
48
44
  individual_time > 0
49
- #=!> StandardError
45
+ #=> true
50
46
 
51
47
  ## Valkey/Redis type access performance
52
- user_class = Class.new(Familia::Horreum) do
48
+ user_class2 = Class.new(Familia::Horreum) do
49
+ prefix :benchuser2
53
50
  identifier_field :email
51
+ field :email
54
52
  field :name
55
53
  field :data
54
+ set :tags
56
55
  end
57
56
 
58
- user = user_class.new(email: "perf@example.com")
57
+ user = user_class2.new(email: "perf@example.com")
59
58
  user.save
60
59
 
61
60
  access_time = Benchmark.realtime do
62
- 1000.times { user.set(:tags) }
61
+ 1000.times { user.tags }
63
62
  end
64
63
 
65
64
  result = access_time > 0
66
65
  user.delete!
67
66
  result
68
- #=!> StandardError
67
+ #=> true