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,226 @@
1
+ # try/migration/base_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ require_relative '../support/helpers/test_helpers'
6
+ require_relative '../../lib/familia/migration'
7
+
8
+ Familia.debug = false
9
+
10
+ # Store initial migration count to detect auto-registration
11
+ @initial_count = Familia::Migration.migrations.size
12
+
13
+ # Define test migrations inline
14
+ class TestBaseMigration < Familia::Migration::Base
15
+ self.migration_id = 'test_20260131_base'
16
+ self.description = 'Test base migration'
17
+ self.dependencies = ['some_other_migration']
18
+
19
+ def migration_needed?
20
+ true
21
+ end
22
+
23
+ def migrate
24
+ track_stat(:items_processed, 5)
25
+ end
26
+ end
27
+
28
+ class ReversibleTestMigration < Familia::Migration::Base
29
+ self.migration_id = 'test_20260131_reversible'
30
+
31
+ def migration_needed?
32
+ true
33
+ end
34
+
35
+ def migrate
36
+ # do something
37
+ end
38
+
39
+ def down
40
+ # undo something
41
+ end
42
+ end
43
+
44
+ class CliTestMigration < Familia::Migration::Base
45
+ self.migration_id = 'cli_test_migration'
46
+
47
+ class << self
48
+ attr_accessor :migration_needed_value, :migrate_called
49
+ end
50
+ self.migration_needed_value = true
51
+ self.migrate_called = false
52
+
53
+ def migration_needed?
54
+ self.class.migration_needed_value
55
+ end
56
+
57
+ def migrate
58
+ self.class.migrate_called = true
59
+ true
60
+ end
61
+ end
62
+
63
+ ## migration_id class attribute works
64
+ TestBaseMigration.migration_id
65
+ #=> 'test_20260131_base'
66
+
67
+ ## description class attribute works
68
+ TestBaseMigration.description
69
+ #=> 'Test base migration'
70
+
71
+ ## dependencies class attribute works
72
+ TestBaseMigration.dependencies
73
+ #=> ['some_other_migration']
74
+
75
+ ## Subclassing auto-registers migration
76
+ Familia::Migration.migrations.size > @initial_count
77
+ #=> true
78
+
79
+ ## Subclass is in migrations list
80
+ Familia::Migration.migrations.include?(TestBaseMigration)
81
+ #=> true
82
+
83
+ ## reversible? returns false when down not overridden
84
+ TestBaseMigration.new.reversible?
85
+ #=> false
86
+
87
+ ## reversible? returns true when down is overridden
88
+ ReversibleTestMigration.new.reversible?
89
+ #=> true
90
+
91
+ ## dry_run? returns true by default (no :run option)
92
+ TestBaseMigration.new.dry_run?
93
+ #=> true
94
+
95
+ ## actual_run? returns falsy by default
96
+ TestBaseMigration.new.actual_run? ? true : false
97
+ #=> false
98
+
99
+ ## dry_run? returns false when run: true
100
+ TestBaseMigration.new(run: true).dry_run?
101
+ #=> false
102
+
103
+ ## actual_run? returns true when run: true
104
+ TestBaseMigration.new(run: true).actual_run?
105
+ #=> true
106
+
107
+ ## for_realsies_this_time? yields only in actual run
108
+ results = []
109
+ dry = TestBaseMigration.new(run: false)
110
+ dry.for_realsies_this_time? { results << :dry }
111
+ live = TestBaseMigration.new(run: true)
112
+ live.for_realsies_this_time? { results << :live }
113
+ results
114
+ #=> [:live]
115
+
116
+ ## track_stat increments counter
117
+ instance = TestBaseMigration.new
118
+ instance.track_stat(:foo)
119
+ instance.track_stat(:foo)
120
+ instance.track_stat(:bar, 5)
121
+ [instance.stats[:foo], instance.stats[:bar]]
122
+ #=> [2, 5]
123
+
124
+ ## stats hash defaults to 0
125
+ instance = TestBaseMigration.new
126
+ instance.stats[:nonexistent]
127
+ #=> 0
128
+
129
+ ## redis accessor returns connection (via send for protected method)
130
+ instance = TestBaseMigration.new
131
+ instance.send(:redis).respond_to?(:get)
132
+ #=> true
133
+
134
+ ## Base class migration_needed? raises NotImplementedError
135
+ begin
136
+ Familia::Migration::Base.new.migration_needed?
137
+ false
138
+ rescue NotImplementedError
139
+ true
140
+ end
141
+ #=> true
142
+
143
+ ## Base class migrate raises NotImplementedError
144
+ begin
145
+ Familia::Migration::Base.new.migrate
146
+ false
147
+ rescue NotImplementedError
148
+ true
149
+ end
150
+ #=> true
151
+
152
+ ## prepare can be called without error
153
+ instance = TestBaseMigration.new
154
+ instance.prepare
155
+ true
156
+ #=> true
157
+
158
+ ## Logging methods exist
159
+ instance = TestBaseMigration.new
160
+ [:info, :debug, :warn, :error, :header, :progress].all? { |m| instance.respond_to?(m) }
161
+ #=> true
162
+
163
+ ## cli_run returns 0 for dry run success
164
+ CliTestMigration.migrate_called = false
165
+ CliTestMigration.migration_needed_value = true
166
+ result = CliTestMigration.cli_run([])
167
+ [result, CliTestMigration.migrate_called]
168
+ #=> [0, true]
169
+
170
+ ## cli_run returns 0 for actual run success
171
+ CliTestMigration.migrate_called = false
172
+ CliTestMigration.migration_needed_value = true
173
+ result = CliTestMigration.cli_run(['--run'])
174
+ [result, CliTestMigration.migrate_called]
175
+ #=> [0, true]
176
+
177
+ ## cli_run with --check returns 1 when migration needed
178
+ CliTestMigration.migration_needed_value = true
179
+ result = CliTestMigration.cli_run(['--check'])
180
+ result
181
+ #=> 1
182
+
183
+ ## cli_run with --check returns 0 when migration not needed
184
+ CliTestMigration.migration_needed_value = false
185
+ result = CliTestMigration.cli_run(['--check'])
186
+ result
187
+ #=> 0
188
+
189
+ ## cli_run returns 0 when migration not needed
190
+ CliTestMigration.migration_needed_value = false
191
+ CliTestMigration.migrate_called = false
192
+ result = CliTestMigration.cli_run([])
193
+ [result, CliTestMigration.migrate_called]
194
+ #=> [0, false]
195
+
196
+ ## check_only returns 1 when migration needed
197
+ CliTestMigration.migration_needed_value = true
198
+ CliTestMigration.check_only
199
+ #=> 1
200
+
201
+ ## check_only returns 0 when migration not needed
202
+ CliTestMigration.migration_needed_value = false
203
+ CliTestMigration.check_only
204
+ #=> 0
205
+
206
+ ## dry_run_only? yields only in dry run mode
207
+ results = []
208
+ dry = TestBaseMigration.new(run: false)
209
+ dry.dry_run_only? { results << :dry }
210
+ live = TestBaseMigration.new(run: true)
211
+ live.dry_run_only? { results << :live }
212
+ results
213
+ #=> [:dry]
214
+
215
+ ## dry_run_only? returns true in dry run mode
216
+ TestBaseMigration.new(run: false).dry_run_only?
217
+ #=> true
218
+
219
+ ## dry_run_only? returns false in actual run mode
220
+ TestBaseMigration.new(run: true).dry_run_only?
221
+ #=> false
222
+
223
+ # Cleanup - remove test migrations from registry
224
+ Familia::Migration.migrations.delete(TestBaseMigration)
225
+ Familia::Migration.migrations.delete(ReversibleTestMigration)
226
+ Familia::Migration.migrations.delete(CliTestMigration)
@@ -0,0 +1,67 @@
1
+ # try/migration/errors_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ require_relative '../support/helpers/test_helpers'
6
+ require_relative '../../lib/familia/migration/errors'
7
+
8
+ ## MigrationError inherits from StandardError
9
+ Familia::Migration::Errors::MigrationError.superclass
10
+ #=> StandardError
11
+
12
+ ## NotReversible inherits from MigrationError
13
+ Familia::Migration::Errors::NotReversible.superclass
14
+ #=> Familia::Migration::Errors::MigrationError
15
+
16
+ ## NotApplied inherits from MigrationError
17
+ Familia::Migration::Errors::NotApplied.superclass
18
+ #=> Familia::Migration::Errors::MigrationError
19
+
20
+ ## NotFound inherits from MigrationError
21
+ Familia::Migration::Errors::NotFound.superclass
22
+ #=> Familia::Migration::Errors::MigrationError
23
+
24
+ ## DependencyNotMet inherits from MigrationError
25
+ Familia::Migration::Errors::DependencyNotMet.superclass
26
+ #=> Familia::Migration::Errors::MigrationError
27
+
28
+ ## HasDependents inherits from MigrationError
29
+ Familia::Migration::Errors::HasDependents.superclass
30
+ #=> Familia::Migration::Errors::MigrationError
31
+
32
+ ## CircularDependency inherits from MigrationError
33
+ Familia::Migration::Errors::CircularDependency.superclass
34
+ #=> Familia::Migration::Errors::MigrationError
35
+
36
+ ## PreconditionFailed inherits from MigrationError
37
+ Familia::Migration::Errors::PreconditionFailed.superclass
38
+ #=> Familia::Migration::Errors::MigrationError
39
+
40
+ ## All error classes are defined as constants
41
+ [
42
+ :MigrationError,
43
+ :NotReversible,
44
+ :NotApplied,
45
+ :NotFound,
46
+ :DependencyNotMet,
47
+ :HasDependents,
48
+ :CircularDependency,
49
+ :PreconditionFailed
50
+ ].all? { |name| Familia::Migration::Errors.const_defined?(name) }
51
+ #=> true
52
+
53
+ ## Errors can be raised with custom messages
54
+ begin
55
+ raise Familia::Migration::Errors::NotFound, "Migration xyz not found"
56
+ rescue Familia::Migration::Errors::MigrationError => e
57
+ e.message
58
+ end
59
+ #=> "Migration xyz not found"
60
+
61
+ ## Errors can be rescued by parent class
62
+ begin
63
+ raise Familia::Migration::Errors::CircularDependency, "A depends on B depends on A"
64
+ rescue Familia::Migration::Errors::MigrationError => e
65
+ e.class.name.split('::').last
66
+ end
67
+ #=> "CircularDependency"