tallty_duck_record 1.0.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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +41 -0
  3. data/README.md +82 -0
  4. data/Rakefile +28 -0
  5. data/lib/core_ext/array_without_blank.rb +46 -0
  6. data/lib/duck_record.rb +65 -0
  7. data/lib/duck_record/associations.rb +130 -0
  8. data/lib/duck_record/associations/association.rb +271 -0
  9. data/lib/duck_record/associations/belongs_to_association.rb +71 -0
  10. data/lib/duck_record/associations/builder/association.rb +127 -0
  11. data/lib/duck_record/associations/builder/belongs_to.rb +44 -0
  12. data/lib/duck_record/associations/builder/collection_association.rb +45 -0
  13. data/lib/duck_record/associations/builder/embeds_many.rb +9 -0
  14. data/lib/duck_record/associations/builder/embeds_one.rb +9 -0
  15. data/lib/duck_record/associations/builder/has_many.rb +11 -0
  16. data/lib/duck_record/associations/builder/has_one.rb +20 -0
  17. data/lib/duck_record/associations/builder/singular_association.rb +33 -0
  18. data/lib/duck_record/associations/collection_association.rb +476 -0
  19. data/lib/duck_record/associations/collection_proxy.rb +1160 -0
  20. data/lib/duck_record/associations/embeds_association.rb +92 -0
  21. data/lib/duck_record/associations/embeds_many_association.rb +203 -0
  22. data/lib/duck_record/associations/embeds_many_proxy.rb +892 -0
  23. data/lib/duck_record/associations/embeds_one_association.rb +48 -0
  24. data/lib/duck_record/associations/foreign_association.rb +11 -0
  25. data/lib/duck_record/associations/has_many_association.rb +17 -0
  26. data/lib/duck_record/associations/has_one_association.rb +39 -0
  27. data/lib/duck_record/associations/singular_association.rb +73 -0
  28. data/lib/duck_record/attribute.rb +213 -0
  29. data/lib/duck_record/attribute/user_provided_default.rb +30 -0
  30. data/lib/duck_record/attribute_assignment.rb +118 -0
  31. data/lib/duck_record/attribute_decorators.rb +89 -0
  32. data/lib/duck_record/attribute_methods.rb +325 -0
  33. data/lib/duck_record/attribute_methods/before_type_cast.rb +76 -0
  34. data/lib/duck_record/attribute_methods/dirty.rb +107 -0
  35. data/lib/duck_record/attribute_methods/read.rb +78 -0
  36. data/lib/duck_record/attribute_methods/serialization.rb +66 -0
  37. data/lib/duck_record/attribute_methods/write.rb +70 -0
  38. data/lib/duck_record/attribute_mutation_tracker.rb +108 -0
  39. data/lib/duck_record/attribute_set.rb +98 -0
  40. data/lib/duck_record/attribute_set/yaml_encoder.rb +41 -0
  41. data/lib/duck_record/attributes.rb +262 -0
  42. data/lib/duck_record/base.rb +300 -0
  43. data/lib/duck_record/callbacks.rb +324 -0
  44. data/lib/duck_record/coders/json.rb +13 -0
  45. data/lib/duck_record/coders/yaml_column.rb +48 -0
  46. data/lib/duck_record/core.rb +262 -0
  47. data/lib/duck_record/define_callbacks.rb +23 -0
  48. data/lib/duck_record/enum.rb +139 -0
  49. data/lib/duck_record/errors.rb +71 -0
  50. data/lib/duck_record/inheritance.rb +130 -0
  51. data/lib/duck_record/locale/en.yml +46 -0
  52. data/lib/duck_record/model_schema.rb +71 -0
  53. data/lib/duck_record/nested_attributes.rb +555 -0
  54. data/lib/duck_record/nested_validate_association.rb +262 -0
  55. data/lib/duck_record/persistence.rb +39 -0
  56. data/lib/duck_record/readonly_attributes.rb +36 -0
  57. data/lib/duck_record/reflection.rb +650 -0
  58. data/lib/duck_record/serialization.rb +26 -0
  59. data/lib/duck_record/translation.rb +22 -0
  60. data/lib/duck_record/type.rb +77 -0
  61. data/lib/duck_record/type/array.rb +36 -0
  62. data/lib/duck_record/type/array_without_blank.rb +36 -0
  63. data/lib/duck_record/type/date.rb +7 -0
  64. data/lib/duck_record/type/date_time.rb +7 -0
  65. data/lib/duck_record/type/decimal_without_scale.rb +13 -0
  66. data/lib/duck_record/type/internal/abstract_json.rb +33 -0
  67. data/lib/duck_record/type/internal/timezone.rb +15 -0
  68. data/lib/duck_record/type/json.rb +6 -0
  69. data/lib/duck_record/type/registry.rb +97 -0
  70. data/lib/duck_record/type/serialized.rb +63 -0
  71. data/lib/duck_record/type/text.rb +9 -0
  72. data/lib/duck_record/type/time.rb +19 -0
  73. data/lib/duck_record/type/unsigned_integer.rb +15 -0
  74. data/lib/duck_record/validations.rb +67 -0
  75. data/lib/duck_record/validations/subset.rb +74 -0
  76. data/lib/duck_record/validations/uniqueness_on_real_record.rb +248 -0
  77. data/lib/duck_record/version.rb +3 -0
  78. data/lib/tasks/acts_as_record_tasks.rake +4 -0
  79. metadata +181 -0
@@ -0,0 +1,1160 @@
1
+ module DuckRecord
2
+ module Associations
3
+ # Association proxies in Active Record are middlemen between the object that
4
+ # holds the association, known as the <tt>@owner</tt>, and the actual associated
5
+ # object, known as the <tt>@target</tt>. The kind of association any proxy is
6
+ # about is available in <tt>@reflection</tt>. That's an instance of the class
7
+ # ActiveRecord::Reflection::AssociationReflection.
8
+ #
9
+ # For example, given
10
+ #
11
+ # class Blog < ActiveRecord::Base
12
+ # has_many :posts
13
+ # end
14
+ #
15
+ # blog = Blog.first
16
+ #
17
+ # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
18
+ # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
19
+ # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
20
+ #
21
+ # This class delegates unknown methods to <tt>@target</tt> via
22
+ # <tt>method_missing</tt>.
23
+ #
24
+ # The <tt>@target</tt> object is not \loaded until needed. For example,
25
+ #
26
+ # blog.posts.count
27
+ #
28
+ # is computed directly through SQL and does not trigger by itself the
29
+ # instantiation of the actual post records.
30
+ class CollectionProxy < ActiveRecord::Relation
31
+ def initialize(klass, association) #:nodoc:
32
+ @association = association
33
+ super klass, klass.arel_table, klass.predicate_builder
34
+
35
+ extensions = association.extensions
36
+ extend(*extensions) if extensions.any?
37
+ end
38
+
39
+ def target
40
+ @association.target
41
+ end
42
+
43
+ def load_target
44
+ @association.load_target
45
+ end
46
+
47
+ # Returns +true+ if the association has been loaded, otherwise +false+.
48
+ #
49
+ # person.pets.loaded? # => false
50
+ # person.pets
51
+ # person.pets.loaded? # => true
52
+ def loaded?
53
+ @association.loaded?
54
+ end
55
+
56
+ ##
57
+ # :method: select
58
+ #
59
+ # :call-seq:
60
+ # select(*fields, &block)
61
+ #
62
+ # Works in two ways.
63
+ #
64
+ # *First:* Specify a subset of fields to be selected from the result set.
65
+ #
66
+ # class Person < ActiveRecord::Base
67
+ # has_many :pets
68
+ # end
69
+ #
70
+ # person.pets
71
+ # # => [
72
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
73
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
74
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
75
+ # # ]
76
+ #
77
+ # person.pets.select(:name)
78
+ # # => [
79
+ # # #<Pet id: nil, name: "Fancy-Fancy">,
80
+ # # #<Pet id: nil, name: "Spook">,
81
+ # # #<Pet id: nil, name: "Choo-Choo">
82
+ # # ]
83
+ #
84
+ # person.pets.select(:id, :name)
85
+ # # => [
86
+ # # #<Pet id: 1, name: "Fancy-Fancy">,
87
+ # # #<Pet id: 2, name: "Spook">,
88
+ # # #<Pet id: 3, name: "Choo-Choo">
89
+ # # ]
90
+ #
91
+ # Be careful because this also means you're initializing a model
92
+ # object with only the fields that you've selected. If you attempt
93
+ # to access a field except +id+ that is not in the initialized record you'll
94
+ # receive:
95
+ #
96
+ # person.pets.select(:name).first.person_id
97
+ # # => ActiveModel::MissingAttributeError: missing attribute: person_id
98
+ #
99
+ # *Second:* You can pass a block so it can be used just like Array#select.
100
+ # This builds an array of objects from the database for the scope,
101
+ # converting them into an array and iterating through them using
102
+ # Array#select.
103
+ #
104
+ # person.pets.select { |pet| pet.name =~ /oo/ }
105
+ # # => [
106
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
107
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
108
+ # # ]
109
+
110
+ # Finds an object in the collection responding to the +id+. Uses the same
111
+ # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
112
+ # error if the object cannot be found.
113
+ #
114
+ # class Person < ActiveRecord::Base
115
+ # has_many :pets
116
+ # end
117
+ #
118
+ # person.pets
119
+ # # => [
120
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
121
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
122
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
123
+ # # ]
124
+ #
125
+ # person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
126
+ # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
127
+ #
128
+ # person.pets.find(2) { |pet| pet.name.downcase! }
129
+ # # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
130
+ #
131
+ # person.pets.find(2, 3)
132
+ # # => [
133
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
134
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
135
+ # # ]
136
+ def find(*args, &block)
137
+ @association.find(*args, &block)
138
+ end
139
+
140
+ ##
141
+ # :method: first
142
+ #
143
+ # :call-seq:
144
+ # first(limit = nil)
145
+ #
146
+ # Returns the first record, or the first +n+ records, from the collection.
147
+ # If the collection is empty, the first form returns +nil+, and the second
148
+ # form returns an empty array.
149
+ #
150
+ # class Person < ActiveRecord::Base
151
+ # has_many :pets
152
+ # end
153
+ #
154
+ # person.pets
155
+ # # => [
156
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
157
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
158
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
159
+ # # ]
160
+ #
161
+ # person.pets.first # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
162
+ #
163
+ # person.pets.first(2)
164
+ # # => [
165
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
166
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
167
+ # # ]
168
+ #
169
+ # another_person_without.pets # => []
170
+ # another_person_without.pets.first # => nil
171
+ # another_person_without.pets.first(3) # => []
172
+
173
+ ##
174
+ # :method: second
175
+ #
176
+ # :call-seq:
177
+ # second()
178
+ #
179
+ # Same as #first except returns only the second record.
180
+
181
+ ##
182
+ # :method: third
183
+ #
184
+ # :call-seq:
185
+ # third()
186
+ #
187
+ # Same as #first except returns only the third record.
188
+
189
+ ##
190
+ # :method: fourth
191
+ #
192
+ # :call-seq:
193
+ # fourth()
194
+ #
195
+ # Same as #first except returns only the fourth record.
196
+
197
+ ##
198
+ # :method: fifth
199
+ #
200
+ # :call-seq:
201
+ # fifth()
202
+ #
203
+ # Same as #first except returns only the fifth record.
204
+
205
+ ##
206
+ # :method: forty_two
207
+ #
208
+ # :call-seq:
209
+ # forty_two()
210
+ #
211
+ # Same as #first except returns only the forty second record.
212
+ # Also known as accessing "the reddit".
213
+
214
+ ##
215
+ # :method: third_to_last
216
+ #
217
+ # :call-seq:
218
+ # third_to_last()
219
+ #
220
+ # Same as #first except returns only the third-to-last record.
221
+
222
+ ##
223
+ # :method: second_to_last
224
+ #
225
+ # :call-seq:
226
+ # second_to_last()
227
+ #
228
+ # Same as #first except returns only the second-to-last record.
229
+
230
+ # Returns the last record, or the last +n+ records, from the collection.
231
+ # If the collection is empty, the first form returns +nil+, and the second
232
+ # form returns an empty array.
233
+ #
234
+ # class Person < ActiveRecord::Base
235
+ # has_many :pets
236
+ # end
237
+ #
238
+ # person.pets
239
+ # # => [
240
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
241
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
242
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
243
+ # # ]
244
+ #
245
+ # person.pets.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1>
246
+ #
247
+ # person.pets.last(2)
248
+ # # => [
249
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
250
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
251
+ # # ]
252
+ #
253
+ # another_person_without.pets # => []
254
+ # another_person_without.pets.last # => nil
255
+ # another_person_without.pets.last(3) # => []
256
+ def last(limit = nil)
257
+ load_target if find_from_target?
258
+ super
259
+ end
260
+
261
+ # Gives a record (or N records if a parameter is supplied) from the collection
262
+ # using the same rules as <tt>ActiveRecord::Base.take</tt>.
263
+ #
264
+ # class Person < ActiveRecord::Base
265
+ # has_many :pets
266
+ # end
267
+ #
268
+ # person.pets
269
+ # # => [
270
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
271
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
272
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
273
+ # # ]
274
+ #
275
+ # person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
276
+ #
277
+ # person.pets.take(2)
278
+ # # => [
279
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
280
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
281
+ # # ]
282
+ #
283
+ # another_person_without.pets # => []
284
+ # another_person_without.pets.take # => nil
285
+ # another_person_without.pets.take(2) # => []
286
+ def take(limit = nil)
287
+ load_target if find_from_target?
288
+ super
289
+ end
290
+
291
+ # Returns a new object of the collection type that has been instantiated
292
+ # with +attributes+ and linked to this object, but have not yet been saved.
293
+ # You can pass an array of attributes hashes, this will return an array
294
+ # with the new objects.
295
+ #
296
+ # class Person
297
+ # has_many :pets
298
+ # end
299
+ #
300
+ # person.pets.build
301
+ # # => #<Pet id: nil, name: nil, person_id: 1>
302
+ #
303
+ # person.pets.build(name: 'Fancy-Fancy')
304
+ # # => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
305
+ #
306
+ # person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
307
+ # # => [
308
+ # # #<Pet id: nil, name: "Spook", person_id: 1>,
309
+ # # #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
310
+ # # #<Pet id: nil, name: "Brain", person_id: 1>
311
+ # # ]
312
+ #
313
+ # person.pets.size # => 5 # size of the collection
314
+ # person.pets.count # => 0 # count from database
315
+ def build(attributes = {}, &block)
316
+ @association.build(attributes, &block)
317
+ end
318
+ alias_method :new, :build
319
+
320
+ # Returns a new object of the collection type that has been instantiated with
321
+ # attributes, linked to this object and that has already been saved (if it
322
+ # passes the validations).
323
+ #
324
+ # class Person
325
+ # has_many :pets
326
+ # end
327
+ #
328
+ # person.pets.create(name: 'Fancy-Fancy')
329
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
330
+ #
331
+ # person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}])
332
+ # # => [
333
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
334
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
335
+ # # ]
336
+ #
337
+ # person.pets.size # => 3
338
+ # person.pets.count # => 3
339
+ #
340
+ # person.pets.find(1, 2, 3)
341
+ # # => [
342
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
343
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
344
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
345
+ # # ]
346
+ def create(attributes = {}, &block)
347
+ @association.create(attributes, &block)
348
+ end
349
+
350
+ # Like #create, except that if the record is invalid, raises an exception.
351
+ #
352
+ # class Person
353
+ # has_many :pets
354
+ # end
355
+ #
356
+ # class Pet
357
+ # validates :name, presence: true
358
+ # end
359
+ #
360
+ # person.pets.create!(name: nil)
361
+ # # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
362
+ def create!(attributes = {}, &block)
363
+ @association.create!(attributes, &block)
364
+ end
365
+
366
+ # Add one or more records to the collection by setting their foreign keys
367
+ # to the association's primary key. Since #<< flattens its argument list and
368
+ # inserts each record, +push+ and #concat behave identically. Returns +self+
369
+ # so method calls may be chained.
370
+ #
371
+ # class Person < ActiveRecord::Base
372
+ # has_many :pets
373
+ # end
374
+ #
375
+ # person.pets.size # => 0
376
+ # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
377
+ # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
378
+ # person.pets.size # => 3
379
+ #
380
+ # person.id # => 1
381
+ # person.pets
382
+ # # => [
383
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
384
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
385
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
386
+ # # ]
387
+ #
388
+ # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
389
+ # person.pets.size # => 5
390
+ def concat(*records)
391
+ @association.concat(*records)
392
+ end
393
+
394
+ # Replaces this collection with +other_array+. This will perform a diff
395
+ # and delete/add only records that have changed.
396
+ #
397
+ # class Person < ActiveRecord::Base
398
+ # has_many :pets
399
+ # end
400
+ #
401
+ # person.pets
402
+ # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
403
+ #
404
+ # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
405
+ #
406
+ # person.pets.replace(other_pets)
407
+ #
408
+ # person.pets
409
+ # # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
410
+ #
411
+ # If the supplied array has an incorrect association type, it raises
412
+ # an <tt>ActiveRecord::AssociationTypeMismatch</tt> error:
413
+ #
414
+ # person.pets.replace(["doo", "ggie", "gaga"])
415
+ # # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
416
+ def replace(other_array)
417
+ @association.replace(other_array)
418
+ end
419
+
420
+ # Deletes all the records from the collection according to the strategy
421
+ # specified by the +:dependent+ option. If no +:dependent+ option is given,
422
+ # then it will follow the default strategy.
423
+ #
424
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
425
+ # +:delete_all+.
426
+ #
427
+ # For +has_many+ associations, the default deletion strategy is +:nullify+.
428
+ # This sets the foreign keys to +NULL+.
429
+ #
430
+ # class Person < ActiveRecord::Base
431
+ # has_many :pets # dependent: :nullify option by default
432
+ # end
433
+ #
434
+ # person.pets.size # => 3
435
+ # person.pets
436
+ # # => [
437
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
438
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
439
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
440
+ # # ]
441
+ #
442
+ # person.pets.delete_all
443
+ # # => [
444
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
445
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
446
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
447
+ # # ]
448
+ #
449
+ # person.pets.size # => 0
450
+ # person.pets # => []
451
+ #
452
+ # Pet.find(1, 2, 3)
453
+ # # => [
454
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>,
455
+ # # #<Pet id: 2, name: "Spook", person_id: nil>,
456
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
457
+ # # ]
458
+ #
459
+ # Both +has_many+ and <tt>has_many :through</tt> dependencies default to the
460
+ # +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
461
+ # Records are not instantiated and callbacks will not be fired.
462
+ #
463
+ # class Person < ActiveRecord::Base
464
+ # has_many :pets, dependent: :destroy
465
+ # end
466
+ #
467
+ # person.pets.size # => 3
468
+ # person.pets
469
+ # # => [
470
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
471
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
472
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
473
+ # # ]
474
+ #
475
+ # person.pets.delete_all
476
+ #
477
+ # Pet.find(1, 2, 3)
478
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
479
+ #
480
+ # If it is set to <tt>:delete_all</tt>, all the objects are deleted
481
+ # *without* calling their +destroy+ method.
482
+ #
483
+ # class Person < ActiveRecord::Base
484
+ # has_many :pets, dependent: :delete_all
485
+ # end
486
+ #
487
+ # person.pets.size # => 3
488
+ # person.pets
489
+ # # => [
490
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
491
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
492
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
493
+ # # ]
494
+ #
495
+ # person.pets.delete_all
496
+ #
497
+ # Pet.find(1, 2, 3)
498
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
499
+ def delete_all
500
+ @association.delete_all
501
+ end
502
+
503
+ # Deletes the records of the collection directly from the database
504
+ # ignoring the +:dependent+ option. Records are instantiated and it
505
+ # invokes +before_remove+, +after_remove+ , +before_destroy+ and
506
+ # +after_destroy+ callbacks.
507
+ #
508
+ # class Person < ActiveRecord::Base
509
+ # has_many :pets
510
+ # end
511
+ #
512
+ # person.pets.size # => 3
513
+ # person.pets
514
+ # # => [
515
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
516
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
517
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
518
+ # # ]
519
+ #
520
+ # person.pets.destroy_all
521
+ #
522
+ # person.pets.size # => 0
523
+ # person.pets # => []
524
+ #
525
+ # Pet.find(1) # => Couldn't find Pet with id=1
526
+ def destroy_all
527
+ @association.destroy_all
528
+ end
529
+
530
+ # Deletes the +records+ supplied from the collection according to the strategy
531
+ # specified by the +:dependent+ option. If no +:dependent+ option is given,
532
+ # then it will follow the default strategy. Returns an array with the
533
+ # deleted records.
534
+ #
535
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
536
+ # +:delete_all+.
537
+ #
538
+ # For +has_many+ associations, the default deletion strategy is +:nullify+.
539
+ # This sets the foreign keys to +NULL+.
540
+ #
541
+ # class Person < ActiveRecord::Base
542
+ # has_many :pets # dependent: :nullify option by default
543
+ # end
544
+ #
545
+ # person.pets.size # => 3
546
+ # person.pets
547
+ # # => [
548
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
549
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
550
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
551
+ # # ]
552
+ #
553
+ # person.pets.delete(Pet.find(1))
554
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
555
+ #
556
+ # person.pets.size # => 2
557
+ # person.pets
558
+ # # => [
559
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
560
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
561
+ # # ]
562
+ #
563
+ # Pet.find(1)
564
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
565
+ #
566
+ # If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
567
+ # their +destroy+ method. See +destroy+ for more information.
568
+ #
569
+ # class Person < ActiveRecord::Base
570
+ # has_many :pets, dependent: :destroy
571
+ # end
572
+ #
573
+ # person.pets.size # => 3
574
+ # person.pets
575
+ # # => [
576
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
577
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
578
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
579
+ # # ]
580
+ #
581
+ # person.pets.delete(Pet.find(1), Pet.find(3))
582
+ # # => [
583
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
584
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
585
+ # # ]
586
+ #
587
+ # person.pets.size # => 1
588
+ # person.pets
589
+ # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
590
+ #
591
+ # Pet.find(1, 3)
592
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
593
+ #
594
+ # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
595
+ # *without* calling their +destroy+ method.
596
+ #
597
+ # class Person < ActiveRecord::Base
598
+ # has_many :pets, dependent: :delete_all
599
+ # end
600
+ #
601
+ # person.pets.size # => 3
602
+ # person.pets
603
+ # # => [
604
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
605
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
606
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
607
+ # # ]
608
+ #
609
+ # person.pets.delete(Pet.find(1))
610
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
611
+ #
612
+ # person.pets.size # => 2
613
+ # person.pets
614
+ # # => [
615
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
616
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
617
+ # # ]
618
+ #
619
+ # Pet.find(1)
620
+ # # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
621
+ #
622
+ # You can pass +Integer+ or +String+ values, it finds the records
623
+ # responding to the +id+ and executes delete on them.
624
+ #
625
+ # class Person < ActiveRecord::Base
626
+ # has_many :pets
627
+ # end
628
+ #
629
+ # person.pets.size # => 3
630
+ # person.pets
631
+ # # => [
632
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
633
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
634
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
635
+ # # ]
636
+ #
637
+ # person.pets.delete("1")
638
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
639
+ #
640
+ # person.pets.delete(2, 3)
641
+ # # => [
642
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
643
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
644
+ # # ]
645
+ def delete(*records)
646
+ @association.delete(*records)
647
+ end
648
+
649
+ # Destroys the +records+ supplied and removes them from the collection.
650
+ # This method will _always_ remove record from the database ignoring
651
+ # the +:dependent+ option. Returns an array with the removed records.
652
+ #
653
+ # class Person < ActiveRecord::Base
654
+ # has_many :pets
655
+ # end
656
+ #
657
+ # person.pets.size # => 3
658
+ # person.pets
659
+ # # => [
660
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
661
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
662
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
663
+ # # ]
664
+ #
665
+ # person.pets.destroy(Pet.find(1))
666
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
667
+ #
668
+ # person.pets.size # => 2
669
+ # person.pets
670
+ # # => [
671
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
672
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
673
+ # # ]
674
+ #
675
+ # person.pets.destroy(Pet.find(2), Pet.find(3))
676
+ # # => [
677
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
678
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
679
+ # # ]
680
+ #
681
+ # person.pets.size # => 0
682
+ # person.pets # => []
683
+ #
684
+ # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
685
+ #
686
+ # You can pass +Integer+ or +String+ values, it finds the records
687
+ # responding to the +id+ and then deletes them from the database.
688
+ #
689
+ # person.pets.size # => 3
690
+ # person.pets
691
+ # # => [
692
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
693
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
694
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
695
+ # # ]
696
+ #
697
+ # person.pets.destroy("4")
698
+ # # => #<Pet id: 4, name: "Benny", person_id: 1>
699
+ #
700
+ # person.pets.size # => 2
701
+ # person.pets
702
+ # # => [
703
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
704
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
705
+ # # ]
706
+ #
707
+ # person.pets.destroy(5, 6)
708
+ # # => [
709
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
710
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
711
+ # # ]
712
+ #
713
+ # person.pets.size # => 0
714
+ # person.pets # => []
715
+ #
716
+ # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
717
+ def destroy(*records)
718
+ @association.destroy(*records)
719
+ end
720
+
721
+ ##
722
+ # :method: distinct
723
+ #
724
+ # :call-seq:
725
+ # distinct(value = true)
726
+ #
727
+ # Specifies whether the records should be unique or not.
728
+ #
729
+ # class Person < ActiveRecord::Base
730
+ # has_many :pets
731
+ # end
732
+ #
733
+ # person.pets.select(:name)
734
+ # # => [
735
+ # # #<Pet name: "Fancy-Fancy">,
736
+ # # #<Pet name: "Fancy-Fancy">
737
+ # # ]
738
+ #
739
+ # person.pets.select(:name).distinct
740
+ # # => [#<Pet name: "Fancy-Fancy">]
741
+ #
742
+ # person.pets.select(:name).distinct.distinct(false)
743
+ # # => [
744
+ # # #<Pet name: "Fancy-Fancy">,
745
+ # # #<Pet name: "Fancy-Fancy">
746
+ # # ]
747
+
748
+ #--
749
+ def uniq
750
+ load_target.uniq
751
+ end
752
+
753
+ def calculate(operation, column_name)
754
+ null_scope? ? scope.calculate(operation, column_name) : super
755
+ end
756
+
757
+ def pluck(*column_names)
758
+ null_scope? ? scope.pluck(*column_names) : super
759
+ end
760
+
761
+ ##
762
+ # :method: count
763
+ #
764
+ # :call-seq:
765
+ # count(column_name = nil, &block)
766
+ #
767
+ # Count all records.
768
+ #
769
+ # class Person < ActiveRecord::Base
770
+ # has_many :pets
771
+ # end
772
+ #
773
+ # # This will perform the count using SQL.
774
+ # person.pets.count # => 3
775
+ # person.pets
776
+ # # => [
777
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
778
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
779
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
780
+ # # ]
781
+ #
782
+ # Passing a block will select all of a person's pets in SQL and then
783
+ # perform the count using Ruby.
784
+ #
785
+ # person.pets.count { |pet| pet.name.include?('-') } # => 2
786
+
787
+ # Returns the size of the collection. If the collection hasn't been loaded,
788
+ # it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
789
+ #
790
+ # If the collection has been already loaded +size+ and +length+ are
791
+ # equivalent. If not and you are going to need the records anyway
792
+ # +length+ will take one less query. Otherwise +size+ is more efficient.
793
+ #
794
+ # class Person < ActiveRecord::Base
795
+ # has_many :pets
796
+ # end
797
+ #
798
+ # person.pets.size # => 3
799
+ # # executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1
800
+ #
801
+ # person.pets # This will execute a SELECT * FROM query
802
+ # # => [
803
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
804
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
805
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
806
+ # # ]
807
+ #
808
+ # person.pets.size # => 3
809
+ # # Because the collection is already loaded, this will behave like
810
+ # # collection.size and no SQL count query is executed.
811
+ def size
812
+ @association.size
813
+ end
814
+
815
+ ##
816
+ # :method: length
817
+ #
818
+ # :call-seq:
819
+ # length()
820
+ #
821
+ # Returns the size of the collection calling +size+ on the target.
822
+ # If the collection has been already loaded, +length+ and +size+ are
823
+ # equivalent. If not and you are going to need the records anyway this
824
+ # method will take one less query. Otherwise +size+ is more efficient.
825
+ #
826
+ # class Person < ActiveRecord::Base
827
+ # has_many :pets
828
+ # end
829
+ #
830
+ # person.pets.length # => 3
831
+ # # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1
832
+ #
833
+ # # Because the collection is loaded, you can
834
+ # # call the collection with no additional queries:
835
+ # person.pets
836
+ # # => [
837
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
838
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
839
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
840
+ # # ]
841
+
842
+ # Returns +true+ if the collection is empty. If the collection has been
843
+ # loaded it is equivalent
844
+ # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
845
+ # it is equivalent to <tt>!collection.exists?</tt>. If the collection has
846
+ # not already been loaded and you are going to fetch the records anyway it
847
+ # is better to check <tt>collection.length.zero?</tt>.
848
+ #
849
+ # class Person < ActiveRecord::Base
850
+ # has_many :pets
851
+ # end
852
+ #
853
+ # person.pets.count # => 1
854
+ # person.pets.empty? # => false
855
+ #
856
+ # person.pets.delete_all
857
+ #
858
+ # person.pets.count # => 0
859
+ # person.pets.empty? # => true
860
+ def empty?
861
+ @association.empty?
862
+ end
863
+
864
+ ##
865
+ # :method: any?
866
+ #
867
+ # :call-seq:
868
+ # any?()
869
+ #
870
+ # Returns +true+ if the collection is not empty.
871
+ #
872
+ # class Person < ActiveRecord::Base
873
+ # has_many :pets
874
+ # end
875
+ #
876
+ # person.pets.count # => 0
877
+ # person.pets.any? # => false
878
+ #
879
+ # person.pets << Pet.new(name: 'Snoop')
880
+ # person.pets.count # => 1
881
+ # person.pets.any? # => true
882
+ #
883
+ # You can also pass a +block+ to define criteria. The behavior
884
+ # is the same, it returns true if the collection based on the
885
+ # criteria is not empty.
886
+ #
887
+ # person.pets
888
+ # # => [#<Pet name: "Snoop", group: "dogs">]
889
+ #
890
+ # person.pets.any? do |pet|
891
+ # pet.group == 'cats'
892
+ # end
893
+ # # => false
894
+ #
895
+ # person.pets.any? do |pet|
896
+ # pet.group == 'dogs'
897
+ # end
898
+ # # => true
899
+
900
+ ##
901
+ # :method: many?
902
+ #
903
+ # :call-seq:
904
+ # many?()
905
+ #
906
+ # Returns true if the collection has more than one record.
907
+ # Equivalent to <tt>collection.size > 1</tt>.
908
+ #
909
+ # class Person < ActiveRecord::Base
910
+ # has_many :pets
911
+ # end
912
+ #
913
+ # person.pets.count # => 1
914
+ # person.pets.many? # => false
915
+ #
916
+ # person.pets << Pet.new(name: 'Snoopy')
917
+ # person.pets.count # => 2
918
+ # person.pets.many? # => true
919
+ #
920
+ # You can also pass a +block+ to define criteria. The
921
+ # behavior is the same, it returns true if the collection
922
+ # based on the criteria has more than one record.
923
+ #
924
+ # person.pets
925
+ # # => [
926
+ # # #<Pet name: "Gorby", group: "cats">,
927
+ # # #<Pet name: "Puff", group: "cats">,
928
+ # # #<Pet name: "Snoop", group: "dogs">
929
+ # # ]
930
+ #
931
+ # person.pets.many? do |pet|
932
+ # pet.group == 'dogs'
933
+ # end
934
+ # # => false
935
+ #
936
+ # person.pets.many? do |pet|
937
+ # pet.group == 'cats'
938
+ # end
939
+ # # => true
940
+
941
+ # Returns +true+ if the given +record+ is present in the collection.
942
+ #
943
+ # class Person < ActiveRecord::Base
944
+ # has_many :pets
945
+ # end
946
+ #
947
+ # person.pets # => [#<Pet id: 20, name: "Snoop">]
948
+ #
949
+ # person.pets.include?(Pet.find(20)) # => true
950
+ # person.pets.include?(Pet.find(21)) # => false
951
+ def include?(record)
952
+ !!@association.include?(record)
953
+ end
954
+
955
+ def proxy_association
956
+ @association
957
+ end
958
+
959
+ # Returns a <tt>Relation</tt> object for the records in this association
960
+ def scope
961
+ @scope ||= @association.scope
962
+ end
963
+
964
+ # Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
965
+ # contain the same number of elements and if each element is equal
966
+ # to the corresponding element in the +other+ array, otherwise returns
967
+ # +false+.
968
+ #
969
+ # class Person < ActiveRecord::Base
970
+ # has_many :pets
971
+ # end
972
+ #
973
+ # person.pets
974
+ # # => [
975
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
976
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
977
+ # # ]
978
+ #
979
+ # other = person.pets.to_ary
980
+ #
981
+ # person.pets == other
982
+ # # => true
983
+ #
984
+ # other = [Pet.new(id: 1), Pet.new(id: 2)]
985
+ #
986
+ # person.pets == other
987
+ # # => false
988
+ def ==(other)
989
+ load_target == other
990
+ end
991
+
992
+ # Returns a new array of objects from the collection. If the collection
993
+ # hasn't been loaded, it fetches the records from the database.
994
+ #
995
+ # class Person < ActiveRecord::Base
996
+ # has_many :pets
997
+ # end
998
+ #
999
+ # person.pets
1000
+ # # => [
1001
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
1002
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
1003
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
1004
+ # # ]
1005
+ #
1006
+ # other_pets = person.pets.to_ary
1007
+ # # => [
1008
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
1009
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
1010
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
1011
+ # # ]
1012
+ #
1013
+ # other_pets.replace([Pet.new(name: 'BooGoo')])
1014
+ #
1015
+ # other_pets
1016
+ # # => [#<Pet id: nil, name: "BooGoo", person_id: 1>]
1017
+ #
1018
+ # person.pets
1019
+ # # This is not affected by replace
1020
+ # # => [
1021
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
1022
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
1023
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
1024
+ # # ]
1025
+ def to_ary
1026
+ load_target.dup
1027
+ end
1028
+ alias_method :to_a, :to_ary
1029
+
1030
+ def records # :nodoc:
1031
+ load_target
1032
+ end
1033
+
1034
+ # Adds one or more +records+ to the collection by setting their foreign keys
1035
+ # to the association's primary key. Returns +self+, so several appends may be
1036
+ # chained together.
1037
+ #
1038
+ # class Person < ActiveRecord::Base
1039
+ # has_many :pets
1040
+ # end
1041
+ #
1042
+ # person.pets.size # => 0
1043
+ # person.pets << Pet.new(name: 'Fancy-Fancy')
1044
+ # person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')]
1045
+ # person.pets.size # => 3
1046
+ #
1047
+ # person.id # => 1
1048
+ # person.pets
1049
+ # # => [
1050
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
1051
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
1052
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
1053
+ # # ]
1054
+ def <<(*records)
1055
+ proxy_association.concat(records) && self
1056
+ end
1057
+ alias_method :push, :<<
1058
+ alias_method :append, :<<
1059
+
1060
+ def prepend(*args)
1061
+ raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
1062
+ end
1063
+
1064
+ # Equivalent to +delete_all+. The difference is that returns +self+, instead
1065
+ # of an array with the deleted objects, so methods can be chained. See
1066
+ # +delete_all+ for more information.
1067
+ # Note that because +delete_all+ removes records by directly
1068
+ # running an SQL query into the database, the +updated_at+ column of
1069
+ # the object is not changed.
1070
+ def clear
1071
+ delete_all
1072
+ self
1073
+ end
1074
+
1075
+ # Reloads the collection from the database. Returns +self+.
1076
+ # Equivalent to <tt>collection(true)</tt>.
1077
+ #
1078
+ # class Person < ActiveRecord::Base
1079
+ # has_many :pets
1080
+ # end
1081
+ #
1082
+ # person.pets # fetches pets from the database
1083
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1084
+ #
1085
+ # person.pets # uses the pets cache
1086
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1087
+ #
1088
+ # person.pets.reload # fetches pets from the database
1089
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1090
+ #
1091
+ # person.pets(true) # fetches pets from the database
1092
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1093
+ def reload
1094
+ proxy_association.reload
1095
+ reset_scope
1096
+ end
1097
+
1098
+ # Unloads the association. Returns +self+.
1099
+ #
1100
+ # class Person < ActiveRecord::Base
1101
+ # has_many :pets
1102
+ # end
1103
+ #
1104
+ # person.pets # fetches pets from the database
1105
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1106
+ #
1107
+ # person.pets # uses the pets cache
1108
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1109
+ #
1110
+ # person.pets.reset # clears the pets cache
1111
+ #
1112
+ # person.pets # fetches pets from the database
1113
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1114
+ def reset
1115
+ proxy_association.reset
1116
+ proxy_association.reset_scope
1117
+ reset_scope
1118
+ end
1119
+
1120
+ def reset_scope # :nodoc:
1121
+ @offsets = {}
1122
+ @scope = nil
1123
+ self
1124
+ end
1125
+
1126
+ delegate_methods = [
1127
+ ActiveRecord::QueryMethods,
1128
+ ActiveRecord::SpawnMethods,
1129
+ ].flat_map { |klass|
1130
+ klass.public_instance_methods(false)
1131
+ } - self.public_instance_methods(false) - [:select] + [:scoping]
1132
+
1133
+ delegate(*delegate_methods, to: :scope)
1134
+
1135
+ private
1136
+
1137
+ def find_nth_with_limit(index, limit)
1138
+ load_target if find_from_target?
1139
+ super
1140
+ end
1141
+
1142
+ def find_nth_from_last(index)
1143
+ load_target if find_from_target?
1144
+ super
1145
+ end
1146
+
1147
+ def null_scope?
1148
+ @association.null_scope?
1149
+ end
1150
+
1151
+ def find_from_target?
1152
+ @association.find_from_target?
1153
+ end
1154
+
1155
+ def exec_queries
1156
+ load_target
1157
+ end
1158
+ end
1159
+ end
1160
+ end