anchormodel 0.3.1 → 0.4.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +14 -0
  4. data/EXAMPLES.md +408 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +43 -1
  7. data/VERSION +1 -1
  8. data/anchormodel.gemspec +3 -3
  9. data/bin/test +9 -0
  10. data/doc/Anchormodel/ActiveModelTypeValueMulti.html +204 -42
  11. data/doc/Anchormodel/ActiveModelTypeValueSingle.html +243 -54
  12. data/doc/Anchormodel/Attribute.html +60 -37
  13. data/doc/Anchormodel/ModelMixin.html +166 -16
  14. data/doc/Anchormodel/SimpleFormInputs/Helpers/AnchormodelInputsCommon.html +82 -21
  15. data/doc/Anchormodel/SimpleFormInputs/Helpers.html +2 -11
  16. data/doc/Anchormodel/SimpleFormInputs.html +2 -11
  17. data/doc/Anchormodel/Util.html +497 -38
  18. data/doc/Anchormodel/Version.html +2 -20
  19. data/doc/Anchormodel.html +536 -129
  20. data/doc/AnchormodelCheckBoxesInput.html +22 -1
  21. data/doc/AnchormodelGenerator.html +64 -13
  22. data/doc/AnchormodelInput.html +24 -1
  23. data/doc/AnchormodelRadioButtonsInput.html +22 -1
  24. data/doc/_index.html +16 -1
  25. data/doc/class_list.html +1 -1
  26. data/doc/file.README.html +40 -2
  27. data/doc/index.html +40 -2
  28. data/doc/method_list.html +57 -17
  29. data/doc/top-level-namespace.html +1 -1
  30. data/lib/anchormodel/active_model_type_value_multi.rb +31 -5
  31. data/lib/anchormodel/active_model_type_value_single.rb +34 -6
  32. data/lib/anchormodel/attribute.rb +20 -8
  33. data/lib/anchormodel/model_mixin.rb +43 -8
  34. data/lib/anchormodel/simple_form_inputs/anchormodel_check_boxes_input.rb +7 -0
  35. data/lib/anchormodel/simple_form_inputs/anchormodel_input.rb +9 -0
  36. data/lib/anchormodel/simple_form_inputs/anchormodel_radio_buttons_input.rb +7 -0
  37. data/lib/anchormodel/simple_form_inputs/helpers/anchormodel_inputs_common.rb +14 -0
  38. data/lib/anchormodel/util.rb +102 -17
  39. data/lib/anchormodel.rb +109 -15
  40. data/lib/generators/anchormodel/anchormodel_generator.rb +8 -0
  41. data/test/active_record_model/user_test.rb +221 -10
  42. data/test/dummy/app/anchormodels/animal.rb +1 -0
  43. metadata +3 -1
@@ -5,12 +5,14 @@ class UserTest < Minitest::Test
5
5
  User.destroy_all
6
6
  end
7
7
 
8
+ # `find` by Symbol and by String must return the same singleton instance.
8
9
  def test_retrieval
9
10
  assert_equal Role.find(:guest), Role.find('guest')
10
11
  end
11
12
 
13
+ # `Anchormodel.all` preserves declaration order and is isolated per subclass
14
+ # (no cross-class registry pollution).
12
15
  def test_collections
13
- # Order must fit as well
14
16
  assert_equal(
15
17
  %i[guest moderator admin the_chosen_one].map { |key| Role.find(key) },
16
18
  Role.all
@@ -21,15 +23,40 @@ class UserTest < Minitest::Test
21
23
  )
22
24
  end
23
25
 
26
+ # All three assignment forms are valid: String, Symbol, Anchormodel instance.
27
+ # Reader always returns the Anchormodel instance regardless of input form.
24
28
  def test_basic_setters_and_getters
25
29
  u = User.create!(role: 'guest', locale: 'de') # String assignment
26
30
  assert_equal Role.find(:guest), u.role
27
31
  assert_equal Locale.find(:de), u.locale
28
- u.update!(role: :admin, locale: Locale.find(:en)) # Symbol and Anchormodel assignemnt
32
+ u.update!(role: :admin, locale: Locale.find(:en)) # Symbol and Anchormodel assignment
29
33
  assert_equal Role.find(:admin), u.role
30
34
  assert_equal Locale.find(:en), u.locale
31
35
  end
32
36
 
37
+ # `Anchormodel#==` defined class+key equality, but `hash` and `eql?` defaulted to
38
+ # object identity. Worked in practice only because instances are singletons via
39
+ # `entries_hash`. Any non-singleton copy (e.g. `dup`, Marshal round-trip, test doubles)
40
+ # broke `Set`/`Hash` membership and `==` comparison invariants.
41
+ def test_hash_and_eql_match_equality
42
+ admin1 = Role.find(:admin)
43
+ admin2 = admin1.dup
44
+
45
+ assert_equal admin1, admin2
46
+ assert_equal admin1.hash, admin2.hash
47
+ assert admin1.eql?(admin2)
48
+ assert admin2.eql?(admin1)
49
+
50
+ # Different keys must differ
51
+ refute_equal admin1.hash, Role.find(:guest).hash # rubocop:disable Rails/RefuteMethods
52
+
53
+ # Set/Hash membership now consistent with `==`
54
+ assert_equal 1, Set.new([admin1, admin2]).size
55
+ assert_equal 'a', ({ admin1 => 'a' }[admin2])
56
+ end
57
+
58
+ # Two users with the same locale yield equal locale instances; different locales differ.
59
+ # Verifies that `==` works through model accessors (not just direct `find` results).
33
60
  def test_comparison
34
61
  bob = User.create(locale: :en)
35
62
  alice = User.create(locale: :fr)
@@ -38,6 +65,8 @@ class UserTest < Minitest::Test
38
65
  assert bob.locale != alice.locale
39
66
  end
40
67
 
68
+ # Custom attributes declared on the Anchormodel subclass (e.g. `privilege_level` on Role)
69
+ # are accessible both standalone and through the model accessor.
41
70
  def test_attributes
42
71
  # Standalone
43
72
  assert_equal 0, Role.find(:guest).privilege_level
@@ -46,6 +75,8 @@ class UserTest < Minitest::Test
46
75
  assert_equal 42, u.role.privilege_level
47
76
  end
48
77
 
78
+ # User-defined `<=>` on the Anchormodel subclass (via `include Comparable`) powers
79
+ # `<`, `>`, `==`, and `<=>` between instances.
49
80
  def test_custom_comparison
50
81
  assert_equal(-1, Role.find(:moderator) <=> Role.find(:admin))
51
82
  assert_equal(1, Role.find(:moderator) <=> Role.find(:guest))
@@ -53,6 +84,8 @@ class UserTest < Minitest::Test
53
84
  assert Role.find(:moderator) < Role.find(:admin)
54
85
  end
55
86
 
87
+ # `belongs_to_anchormodel :col_name, AnchormodelClass` decouples the DB column name
88
+ # from the anchormodel class — here `:secondary_role` maps to `Role`.
56
89
  def test_alternative_column_name
57
90
  ben = User.create!(
58
91
  role: Role.find(:moderator),
@@ -64,11 +97,14 @@ class UserTest < Minitest::Test
64
97
  assert_equal(Locale.find(:de), ben.locale)
65
98
  end
66
99
 
100
+ # `optional: true` permits NULL in the column; reader returns nil rather than raising.
67
101
  def test_optional_attribute
68
102
  jenny = User.create!(role: :admin, locale: :en)
69
103
  assert_nil jenny.secondary_role
70
104
  end
71
105
 
106
+ # Auto-generated per-key readers (`pia.admin?`) and writers (`pia.admin!`) installed
107
+ # by `belongs_to_anchormodel` for every key in the anchormodel.
72
108
  def test_model_readers_and_writers
73
109
  pia = User.new
74
110
  pia.admin!
@@ -77,6 +113,8 @@ class UserTest < Minitest::Test
77
113
  assert_equal Role.find(:admin), pia.role
78
114
  end
79
115
 
116
+ # Auto-generated per-key class scopes (`User.admin`, `User.moderator`, etc.) installed
117
+ # by `belongs_to_anchormodel`.
80
118
  def test_model_scopes
81
119
  User.create!(role: :admin, locale: :en)
82
120
  User.create!(role: :admin, locale: :en)
@@ -87,7 +125,8 @@ class UserTest < Minitest::Test
87
125
  end
88
126
 
89
127
  # Regression: `where(anchormodel_col: %w[a b])` used to collapse to `IN (NULL)`
90
- # because Single#serialize lacked Array handling. See memory `array-where-collapses-to-null`.
128
+ # because `Single#serializable?` was inverted, causing AR's `HomogeneousIn` to
129
+ # filter out all valid keys before binding.
91
130
  def test_where_with_array_of_keys
92
131
  User.create!(role: :admin, locale: :en)
93
132
  User.create!(role: :moderator, locale: :en)
@@ -107,24 +146,158 @@ class UserTest < Minitest::Test
107
146
  assert_equal 1, User.where.not(role: %w[admin moderator]).count
108
147
  end
109
148
 
149
+ # Bulk `where` with any unknown key raises immediately rather than silently filtering it out.
110
150
  def test_where_with_array_of_invalid_keys_raises
111
151
  assert_raises(RuntimeError) { User.where(role: %w[admin nope]).to_a }
112
152
  end
113
153
 
114
- # Direct probe on `serializable?` guards against re-introducing the inverted
154
+ # Single-value anchormodel attributes must reject Array assignment with a clear RuntimeError.
155
+ # Regression: when Array handling was added to `Single#serialize` for the `where(col: array)`
156
+ # fix, it began silently accepting Array writes (e.g. `u.role = %w[admin guest]`) and only
157
+ # blew up later with `NoMethodError: undefined method 'to_sym' for an instance of Array`.
158
+ def test_single_attr_rejects_array_assignment
159
+ u = User.new(role: 'admin', locale: 'de')
160
+ assert_raises(RuntimeError) { u.role = %w[admin guest] }
161
+ assert_raises(RuntimeError) { User.new(role: %w[admin guest], locale: 'de') }
162
+ end
163
+
164
+ # Direct probe on `Single#serializable?` — guards against re-introducing the inverted
115
165
  # `exclude?` logic that silently dropped valid keys from `HomogeneousIn` binds.
166
+ # Single-value attributes do NOT accept Array values (those are for `belongs_to_anchormodels`),
167
+ # so `serializable?(array)` must return false.
116
168
  def test_serializable_predicate
117
169
  type = User.type_for_attribute(:role)
118
170
  assert type.serializable?('admin')
119
171
  assert type.serializable?(:admin)
120
172
  assert type.serializable?(Role.find(:admin))
121
173
  assert type.serializable?(nil)
122
- assert type.serializable?(%w[admin guest])
123
- assert type.serializable?([:admin, Role.find(:guest)])
124
174
  refute type.serializable?(42) # rubocop:disable Rails/RefuteMethods
175
+ refute type.serializable?(%w[admin guest]) # rubocop:disable Rails/RefuteMethods
176
+ refute type.serializable?([:admin, Role.find(:guest)]) # rubocop:disable Rails/RefuteMethods
125
177
  refute type.serializable?([42]) # rubocop:disable Rails/RefuteMethods
126
178
  end
127
179
 
180
+ # `Multi#serialize` must validate String inputs against valid keys, not pass through verbatim.
181
+ # Old impl returned the raw String unchecked, which corrupted DB rows and deferred errors
182
+ # to the next read.
183
+ def test_multi_serialize_validates_string_input
184
+ type = User.type_for_attribute(:animals)
185
+ assert_equal 'cat', type.serialize('cat')
186
+ assert_equal 'cat,dog', type.serialize('cat,dog')
187
+ assert_equal '', type.serialize('')
188
+ assert_raises(RuntimeError) { type.serialize('bogus') }
189
+ assert_raises(RuntimeError) { type.serialize('bogus,nonsense') }
190
+ assert_raises(RuntimeError) { type.serialize('cat,bogus') }
191
+ end
192
+
193
+ # Same String-input validation must apply through normal model assignment, not just
194
+ # the type object directly. Verifies constructor, writer, and DB round-trip.
195
+ def test_multi_string_assignment_validates
196
+ # Constructor with valid String
197
+ u = User.create!(role: 'guest', locale: 'de', animals: 'cat,dog')
198
+ assert_equal(Set.new([Animal.find(:cat), Animal.find(:dog)]), u.animals)
199
+ assert_equal(Set.new([Animal.find(:cat), Animal.find(:dog)]), User.first.animals) # round-trip via DB
200
+
201
+ # Writer with valid single-key String
202
+ u.animals = 'horse'
203
+ assert_equal(Set.new([Animal.find(:horse)]), u.animals)
204
+
205
+ # Writer with invalid String raises immediately, not on next read
206
+ assert_raises(RuntimeError) { u.animals = 'bogus' }
207
+ assert_raises(RuntimeError) { u.animals = 'cat,bogus' }
208
+
209
+ # Constructor with invalid String raises immediately
210
+ assert_raises(RuntimeError) { User.new(role: 'guest', locale: 'de', animals: 'bogus,nonsense') }
211
+ end
212
+
213
+ # SQL LIKE treats `_` as a single-character wildcard. Keys containing `_` (e.g. `:big_cat`)
214
+ # must be escaped in the per-key scope and in the `with_any_<attr>` helper, or they
215
+ # cross-match arbitrary column values with one character in place of the underscore.
216
+ def test_multi_scope_escapes_underscore_wildcard_in_keys
217
+ # Raw row whose `animals` CSV does NOT contain `big_cat` but does contain a string
218
+ # that an unescaped LIKE pattern (`%big_cat,%`) would match: `bigXcat,foo`.
219
+ ActiveRecord::Base.connection.execute(<<~SQL.squish)
220
+ INSERT INTO users (role, locale, preferred_locale, animals, created_at, updated_at)
221
+ VALUES ('guest', 'de', 'de', 'bigXcat,foo', 'now', 'now')
222
+ SQL
223
+ # Baseline row that actually contains :big_cat.
224
+ User.create!(role: 'guest', locale: 'fr', animals: %w[big_cat])
225
+
226
+ # Both scope styles must match only the real row, not the look-alike.
227
+ assert_equal 1, User.big_cat.count
228
+ assert_equal 1, User.with_any_animals(:big_cat).count
229
+ assert_equal 1, User.with_all_animals(:big_cat).count
230
+ end
231
+
232
+ # `where(multi_col: array)` cannot match CSV-in-column storage. Helper scopes
233
+ # `with_any_<attr>` (OR semantics) and `with_all_<attr>` (AND semantics) provide
234
+ # a working idiom for bulk-key queries.
235
+ def test_multi_helper_scopes_with_any_and_with_all
236
+ u_cat_dog = User.create!(role: 'guest', locale: 'fr', animals: %w[cat dog])
237
+ u_dog_horse = User.create!(role: 'guest', locale: 'it', animals: %w[dog horse])
238
+ u_cat = User.create!(role: 'guest', locale: 'de', animals: %w[cat])
239
+
240
+ # Plain `where(animals: array)` cannot reliably match CSV-in-column storage —
241
+ # it only catches rows whose entire column value equals one of the given keys.
242
+ # Use `with_any_<attr>` / `with_all_<attr>` instead.
243
+ plain_count = User.where(animals: %w[cat dog]).count
244
+ helper_count = User.with_any_animals(:cat, :dog).count
245
+ refute_equal plain_count, helper_count, # rubocop:disable Rails/RefuteMethods
246
+ 'plain where(col: array) cannot match CSV-in-column storage; use with_any_<col>'
247
+
248
+ # `with_any_<attr>` — users that hold at least one of the given keys.
249
+ assert_equal 3, User.with_any_animals(:cat, :dog).count
250
+ assert_equal 2, User.with_any_animals(:cat).count
251
+ assert_equal 1, User.with_any_animals(:horse).count
252
+ assert_equal 0, User.with_any_animals(:rat).count
253
+ assert_equal 0, User.with_any_animals.count # empty arg list
254
+
255
+ # Accepts Strings, Symbols, and Anchormodel instances interchangeably.
256
+ assert_equal 2, User.with_any_animals('cat').count
257
+ assert_equal 2, User.with_any_animals(Animal.find(:cat)).count
258
+ assert_equal 3, User.with_any_animals([:cat, 'dog']).count # flattened
259
+
260
+ # `with_all_<attr>` — users that hold every given key.
261
+ assert_equal 1, User.with_all_animals(:cat, :dog).count
262
+ assert_equal 2, User.with_all_animals(:dog).count
263
+ assert_equal 0, User.with_all_animals(:cat, :dog, :rat).count
264
+
265
+ # Invalid keys raise immediately.
266
+ assert_raises(RuntimeError) { User.with_any_animals(:bogus).to_a }
267
+ assert_raises(RuntimeError) { User.with_all_animals(:cat, :bogus).to_a }
268
+
269
+ # Returned IDs check
270
+ assert_equal [u_cat_dog.id, u_cat.id].sort, User.with_any_animals(:cat).pluck(:id).sort
271
+ assert_equal [u_cat_dog.id, u_dog_horse.id].sort, User.with_any_animals(:dog).pluck(:id).sort
272
+ assert_equal [u_cat_dog.id], User.with_all_animals(:cat, :dog).pluck(:id)
273
+ end
274
+
275
+ # `Multi#cast` must tolerate nil — e.g. NULL stored in DB from a migration that did
276
+ # not backfill, or column with no default. Also covers `deserialize(nil)` which falls
277
+ # back to `cast` from `ActiveModel::Type::Value`.
278
+ def test_multi_cast_nil_returns_empty_set
279
+ type = User.type_for_attribute(:animals)
280
+ assert_equal Set.new, type.cast(nil)
281
+ assert_equal Set.new, type.deserialize(nil)
282
+ end
283
+
284
+ # `Multi#serializable?` must return strict Boolean (true/false), not a truthy String/Array.
285
+ # Old impl: `values.map { super }.compact.join(',')` etc — returned non-Boolean and was
286
+ # always truthy regardless of element validity.
287
+ def test_multi_serializable_predicate_returns_boolean
288
+ type = User.type_for_attribute(:animals)
289
+ assert_equal true, type.serializable?(%w[cat dog])
290
+ assert_equal true, type.serializable?([:cat, Animal.find(:dog)])
291
+ assert_equal true, type.serializable?(Set.new(%w[cat]))
292
+ assert_equal true, type.serializable?('cat,dog')
293
+ assert_equal true, type.serializable?(nil)
294
+ assert_equal false, type.serializable?(42)
295
+ assert_equal false, type.serializable?([42])
296
+ assert_equal false, type.serializable?([:cat, 42])
297
+ end
298
+
299
+ # Per-key readers/writers use the anchormodel's own keys (`:de`, `:fr`) even when the
300
+ # model attribute name differs (`preferred_locale` → `Locale`).
128
301
  def test_model_readers_writers_with_different_class_name
129
302
  pia = User.new(locale: :en)
130
303
  pia.de!
@@ -134,6 +307,8 @@ class UserTest < Minitest::Test
134
307
  assert_equal Locale.find(:en), pia.locale
135
308
  end
136
309
 
310
+ # Per-key scopes use the anchormodel's own keys (`User.de`, `User.fr`) even when the
311
+ # model attribute name differs (`preferred_locale` → `Locale`).
137
312
  def test_model_scopes_with_different_class_name
138
313
  User.create!(role: :admin, locale: :en, preferred_locale: :de)
139
314
  User.create!(role: :admin, locale: :en, preferred_locale: :de)
@@ -143,6 +318,8 @@ class UserTest < Minitest::Test
143
318
  assert_equal 0, User.en.count
144
319
  end
145
320
 
321
+ # Empty String is treated as nil — supports default Rails form submissions that send
322
+ # `""` for unset selects on optional attributes.
146
323
  def test_rails_blank_assignment
147
324
  u = User.new(role: :admin, secondary_role: :admin, locale: :en, preferred_locale: :en)
148
325
  u.secondary_role = ''
@@ -153,26 +330,53 @@ class UserTest < Minitest::Test
153
330
  # Testing failures
154
331
  ###---
155
332
 
333
+ # A required (non-optional) anchormodel attribute that is unset triggers Rails presence
334
+ # validation on save.
156
335
  def test_presence_validation
157
336
  valentine = User.new
158
337
  assert_raises(ActiveRecord::RecordInvalid) { valentine.save! }
159
338
  end
160
339
 
340
+ # `find` with an unregistered key raises `Anchormodel::InvalidKey`.
161
341
  def test_missing_key
162
- assert_raises { Role.find(:does_not_exist) }
342
+ assert_raises(Anchormodel::InvalidKey) { Role.find(:does_not_exist) }
343
+ end
344
+
345
+ # Callers want to `rescue` invalid-key errors specifically without matching exception
346
+ # message strings. `Anchormodel::InvalidKey` is the dedicated class, raised from every
347
+ # entry point that could encounter an unknown key.
348
+ def test_invalid_key_error_class
349
+ # `find` with unknown key
350
+ assert_raises(Anchormodel::InvalidKey) { Role.find(:nope) }
351
+
352
+ # Assignment with unknown key
353
+ u = User.new(locale: 'de')
354
+ assert_raises(Anchormodel::InvalidKey) { u.role = :nope }
355
+
356
+ # Bulk where with mixed valid/invalid array
357
+ assert_raises(Anchormodel::InvalidKey) { User.where(role: %w[admin nope]).to_a }
358
+
359
+ # Multi attribute assignment with unknown key
360
+ assert_raises(Anchormodel::InvalidKey) { User.new(role: 'admin', locale: 'de', animals: %w[cat nope]) }
361
+
362
+ # Helper scope with unknown key
363
+ assert_raises(Anchormodel::InvalidKey) { User.with_any_animals(:nope).to_a }
364
+
365
+ # InvalidKey must inherit from StandardError so generic `rescue` catches it
366
+ assert_operator Anchormodel::InvalidKey, :<, StandardError
163
367
  end
164
368
 
165
- # Attempting to create a model with an invalid constant name should fail
369
+ # Attempting to create a model with an invalid anchormodel key should fail.
166
370
  def test_invalid_key_update
167
371
  assert_raises(RuntimeError) { User.create!(role: :admin, locale: :de, preferred_locale: :invalid) }
168
372
  end
169
373
 
170
- # Attempting to assign an invalid constant name to a model should fail
374
+ # Attempting to assign an invalid anchormodel key to a model attribute should fail.
171
375
  def test_invalid_key_assignment
172
376
  assert_raises(RuntimeError) { User.new(role: :invalid) }
173
377
  end
174
378
 
175
- # An invalid constant name into the DB should raise when reading
379
+ # An invalid anchormodel key sneaked into the DB raises on read (via `cast` → `find`).
176
380
  def test_invalid_db_read
177
381
  sql = <<~SQL.squish
178
382
  INSERT INTO users (role, locale, preferred_locale, created_at, updated_at) VALUES ('invalid', 'de', 'de', 'now', 'now')
@@ -185,6 +389,8 @@ class UserTest < Minitest::Test
185
389
  # Testing multiple anchormodel associations
186
390
  ###---
187
391
 
392
+ # Multi attribute full lifecycle: empty default, `<<`, `add` with String/Symbol/instance,
393
+ # `delete` with String/Symbol/instance, mass `=`, and `clear`.
188
394
  def test_multi_basics
189
395
  u = User.create!(role: 'guest', locale: 'de')
190
396
  assert_equal(Set.new, u.animals)
@@ -210,6 +416,7 @@ class UserTest < Minitest::Test
210
416
  assert_equal(false, u.animals.any?)
211
417
  end
212
418
 
419
+ # Round-trip through the database preserves the Set of anchormodels (write CSV → read CSV → Set).
213
420
  def test_multi_save_load
214
421
  u = User.create!(role: 'guest', locale: 'de')
215
422
  u.animals = %i[cat dog]
@@ -218,6 +425,8 @@ class UserTest < Minitest::Test
218
425
  assert_equal(Set.new([Animal.find(:cat), Animal.find(:dog)]), freshly_loaded_u.animals)
219
426
  end
220
427
 
428
+ # Per-key readers (`u.cat?`) and writers (`u.cat!`) work for multi attributes.
429
+ # `cat!` is idempotent — Set-based storage prevents duplicates.
221
430
  def test_multi_model_readers_and_writers
222
431
  u = User.create!(role: 'guest', locale: 'de')
223
432
  u.cat!
@@ -230,6 +439,8 @@ class UserTest < Minitest::Test
230
439
  assert_equal(false, u.horse?)
231
440
  end
232
441
 
442
+ # Per-key scopes (`User.cat`, `User.dog`) work for multi attributes via the
443
+ # CSV-contains LIKE predicate (`Anchormodel::Util.csv_contains_like`).
233
444
  def test_multi_model_scopes
234
445
  u = User.create!(role: 'guest', locale: 'fr', animals: %w[dog cat])
235
446
  v = User.create!(role: 'guest', locale: 'it', animals: %w[dog horse])
@@ -3,4 +3,5 @@ class Animal < Anchormodel
3
3
  new :dog
4
4
  new :horse
5
5
  new :rat
6
+ new :big_cat # key with `_` — used to verify LIKE wildcard escaping
6
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anchormodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Kalbermatter
@@ -157,6 +157,7 @@ files:
157
157
  - ".ruby-version"
158
158
  - ".yardopts"
159
159
  - CHANGELOG.md
160
+ - EXAMPLES.md
160
161
  - Gemfile
161
162
  - Gemfile.lock
162
163
  - LICENSE
@@ -165,6 +166,7 @@ files:
165
166
  - VERSION
166
167
  - anchormodel.gemspec
167
168
  - bin/rails
169
+ - bin/test
168
170
  - doc/Anchormodel.html
169
171
  - doc/Anchormodel/ActiveModelTypeValue.html
170
172
  - doc/Anchormodel/ActiveModelTypeValueMulti.html