passive_record 0.4.14 → 0.4.15
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.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/lib/passive_record/associations.rb +20 -15
- data/lib/passive_record/associations/belongs_to.rb +5 -3
- data/lib/passive_record/associations/has_many_through.rb +8 -61
- data/lib/passive_record/associations/has_one.rb +5 -3
- data/lib/passive_record/core/query.rb +22 -5
- data/lib/passive_record/instance_methods.rb +7 -0
- data/lib/passive_record/version.rb +1 -1
- data/spec/passive_record_spec.rb +85 -19
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e72e0564a1b8661c945d597f7419a24c9b00eb8
|
4
|
+
data.tar.gz: 1ff3f8c8430a145d19d6ea2c7c17e69a067a15b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f0fabaab580b4b110f49b16367f945180e8da5c5228d151caae2eb1bdbe9c85e8f85b2aab3d44fcb6136ac062d4a41d0e12f5699059b2b421484b14c1a30acd
|
7
|
+
data.tar.gz: 4d5cc36aa5a22c8a6573f7e653c5cfdfff04668ed3f150277f33546de1c3936bdab49e02886bdf979e63ca34e825c67c7363fffc834acb42dbfddb9086c12ffe
|
data/README.md
CHANGED
@@ -44,6 +44,7 @@ PassiveRecord may be right for you!
|
|
44
44
|
end
|
45
45
|
|
46
46
|
class Dog < Model
|
47
|
+
attr_accessor :breed
|
47
48
|
belongs_to :child
|
48
49
|
end
|
49
50
|
|
@@ -64,26 +65,25 @@ PassiveRecord may be right for you!
|
|
64
65
|
child = parent.create_child
|
65
66
|
=> Child (id: 1, dog_id: nil, parent_id: 1)
|
66
67
|
|
67
|
-
dog = child.create_dog
|
68
|
-
=> Dog (id: 1, child_id: 1)
|
68
|
+
dog = child.create_dog(breed: "Pug")
|
69
|
+
=> Dog (id: 1, child_id: 1, breed: "Pug")
|
69
70
|
|
70
71
|
# Inverse relationships
|
71
72
|
dog.child
|
72
|
-
=> Child (id: 1, dog_id: 1, parent_id: 1)
|
73
73
|
|
74
74
|
Dog.find_by child: child
|
75
|
-
=> Dog (id: 1, child_id: 1)
|
75
|
+
=> Dog (id: 1, child_id: 1, breed: "Pug")
|
76
76
|
|
77
77
|
# Has many through
|
78
78
|
parent.dogs
|
79
79
|
=> [ ...has_many :through relation... ]
|
80
80
|
|
81
81
|
parent.dogs.all
|
82
|
-
=> [Dog (id: 1, child_id: 1)]
|
82
|
+
=> [Dog (id: 1, child_id: 1, breed: "Pug")]
|
83
83
|
|
84
84
|
# Nested queries
|
85
85
|
Dog.find_all_by(child: { parent: parent })
|
86
|
-
=> [Dog (id: 1, child_id: 1)]
|
86
|
+
=> [Dog (id: 1, child_id: 1, breed: "Pug")]
|
87
87
|
````
|
88
88
|
|
89
89
|
## PassiveRecord API
|
@@ -32,7 +32,7 @@ module PassiveRecord
|
|
32
32
|
end
|
33
33
|
|
34
34
|
define_method(parent_name_sym) do
|
35
|
-
relation =
|
35
|
+
relation = detect_relation(association)
|
36
36
|
association.parent_class.find(relation.parent_model_id)
|
37
37
|
end
|
38
38
|
|
@@ -41,7 +41,7 @@ module PassiveRecord
|
|
41
41
|
end
|
42
42
|
|
43
43
|
define_method(:"#{parent_name_sym}_id=") do |new_parent_id|
|
44
|
-
relation =
|
44
|
+
relation = detect_relation(association)
|
45
45
|
relation.parent_model_id = new_parent_id
|
46
46
|
end
|
47
47
|
end
|
@@ -57,12 +57,12 @@ module PassiveRecord
|
|
57
57
|
end
|
58
58
|
|
59
59
|
define_method(child_name_sym) do
|
60
|
-
relation =
|
60
|
+
relation = detect_relation(association)
|
61
61
|
relation.lookup
|
62
62
|
end
|
63
63
|
|
64
64
|
define_method(:"create_#{child_name_sym}") do |attrs={}|
|
65
|
-
relation =
|
65
|
+
relation = detect_relation(association)
|
66
66
|
relation.create(attrs)
|
67
67
|
end
|
68
68
|
|
@@ -71,7 +71,7 @@ module PassiveRecord
|
|
71
71
|
end
|
72
72
|
|
73
73
|
define_method(:"#{child_name_sym}_id=") do |new_child_id|
|
74
|
-
relation = relata.detect { |rel| rel.association == association }
|
74
|
+
relation = detect_relation(association) #relata.detect { |rel| rel.association == association }
|
75
75
|
rel = relation.lookup
|
76
76
|
rel && rel.send(:"#{relation.parent_model_id_field}=", nil)
|
77
77
|
|
@@ -102,7 +102,7 @@ module PassiveRecord
|
|
102
102
|
end
|
103
103
|
|
104
104
|
define_method(:"#{collection_name_sym.to_s.singularize}_ids=") do |new_collection_ids|
|
105
|
-
relation = relata.detect { |rel| rel.association == association }
|
105
|
+
relation = detect_relation(association) # relata.detect { |rel| rel.association == association }
|
106
106
|
|
107
107
|
intermediary = relation.intermediary_relation
|
108
108
|
|
@@ -113,16 +113,16 @@ module PassiveRecord
|
|
113
113
|
|
114
114
|
# add in new ones...
|
115
115
|
singular_target = collection_name_sym.to_s.singularize
|
116
|
-
if !(relation.nested_association.is_a?(BelongsToAssociation))
|
116
|
+
if !(relation.nested_association.is_a?(BelongsToAssociation))
|
117
117
|
intermediary.create(
|
118
118
|
singular_target + "_ids" => new_collection_ids,
|
119
|
-
relation.parent_model_id_field => relation.id
|
119
|
+
relation.parent_model_id_field => relation.id
|
120
120
|
)
|
121
121
|
else
|
122
122
|
new_collection_ids.each do |child_id|
|
123
123
|
intermediary.create(
|
124
|
-
singular_target + "_id" => child_id,
|
125
|
-
relation.parent_model_id_field => relation.id
|
124
|
+
singular_target + "_id" => child_id,
|
125
|
+
relation.parent_model_id_field => relation.id
|
126
126
|
)
|
127
127
|
end
|
128
128
|
end
|
@@ -132,7 +132,7 @@ module PassiveRecord
|
|
132
132
|
associate!(association)
|
133
133
|
|
134
134
|
define_method(:"#{collection_name_sym}=") do |new_collection|
|
135
|
-
relation =
|
135
|
+
relation = detect_relation(association)
|
136
136
|
|
137
137
|
# detach existing children...
|
138
138
|
relation.all.each do |child|
|
@@ -146,21 +146,26 @@ module PassiveRecord
|
|
146
146
|
end
|
147
147
|
|
148
148
|
define_method(:"#{collection_name_sym.to_s.singularize}_ids=") do |new_collection_ids|
|
149
|
-
relation = relata.detect { |rel| rel.association == association }
|
149
|
+
relation = detect_relation(association) #@ relata.detect { |rel| rel.association == association }
|
150
150
|
send(:"#{collection_name_sym}=", relation.child_class.find(new_collection_ids))
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
154
|
define_method(collection_name_sym) do
|
155
|
-
|
155
|
+
detect_relation(association)
|
156
|
+
# relata.detect { |rel| rel.association == association }
|
156
157
|
end
|
157
158
|
|
158
159
|
define_method(:"#{collection_name_sym.to_s.singularize}_ids") do
|
159
|
-
|
160
|
+
begin
|
161
|
+
send(collection_name_sym).map(&:id)
|
162
|
+
rescue
|
163
|
+
binding.pry
|
164
|
+
end
|
160
165
|
end
|
161
166
|
|
162
167
|
define_method(:"create_#{collection_name_sym.to_s.singularize}") do |attrs={}|
|
163
|
-
relation = relata.detect { |rel| rel.association == association }
|
168
|
+
relation = detect_relation(association) # relata.detect { |rel| rel.association == association }
|
164
169
|
relation.create(attrs)
|
165
170
|
end
|
166
171
|
end
|
@@ -6,9 +6,11 @@ module PassiveRecord
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def parent_class
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
@parent_class ||= (
|
10
|
+
module_name = child_class.name.deconstantize
|
11
|
+
module_name = "Object" if module_name.empty?
|
12
|
+
(module_name.constantize).const_get(parent_class_name)
|
13
|
+
)
|
12
14
|
end
|
13
15
|
|
14
16
|
def child_class_name
|
@@ -4,11 +4,6 @@ module PassiveRecord
|
|
4
4
|
def to_relation(parent_model)
|
5
5
|
HasManyThroughRelation.new(self, parent_model)
|
6
6
|
end
|
7
|
-
|
8
|
-
def nested_association
|
9
|
-
thru_klass = base_association.child_class_name.singularize.constantize
|
10
|
-
thru_klass.associations.detect { |assn| assn.child_class_name == child_class_name }
|
11
|
-
end
|
12
7
|
end
|
13
8
|
|
14
9
|
class HasManyThroughRelation < HasManyRelation
|
@@ -69,9 +64,9 @@ module PassiveRecord
|
|
69
64
|
if intermediate_results && !join_results.empty?
|
70
65
|
final_results = join_results.flat_map(&nested_association.target_name_symbol)
|
71
66
|
if final_results.first.is_a?(Associations::Relation)
|
72
|
-
final_results.flat_map(&:all)
|
67
|
+
final_results.flat_map(&:all).compact
|
73
68
|
else
|
74
|
-
Array(final_results)
|
69
|
+
Array(final_results.compact)
|
75
70
|
end
|
76
71
|
else
|
77
72
|
[]
|
@@ -90,61 +85,13 @@ module PassiveRecord
|
|
90
85
|
end
|
91
86
|
end
|
92
87
|
|
93
|
-
def intermediary_conditions
|
94
|
-
if intermediary_relation.is_a?(HasManyThroughRelation)
|
95
|
-
conds = intermediary_relation.intermediary_conditions
|
96
|
-
|
97
|
-
if nested_association.habtm || nested_association.is_a?(HasManyThroughAssociation)
|
98
|
-
{ association.through_class => conds }
|
99
|
-
else
|
100
|
-
{ association.through_class.to_s.singularize.to_sym => conds }
|
101
|
-
end
|
102
|
-
elsif intermediary_relation.association.is_a?(HasManyAssociation) # normal has many?
|
103
|
-
intermediary_key = if association.is_a?(HasManyThroughAssociation)
|
104
|
-
ch = association.child_class_name.constantize
|
105
|
-
inverse_assn = ch.associations.detect { |assn|
|
106
|
-
if assn.is_a?(HasManyAssociation) || assn.is_a?(HasManyThroughAssociation)
|
107
|
-
assn.child_class_name == association.base_association.child_class_name
|
108
|
-
else # belongs to...
|
109
|
-
assn.parent_class_name == association.base_association.child_class_name
|
110
|
-
end
|
111
|
-
}
|
112
|
-
|
113
|
-
if inverse_assn.nil?
|
114
|
-
association.through_class.to_s.singularize.to_sym
|
115
|
-
elsif inverse_assn.is_a?(HasManyAssociation) || inverse_assn.is_a?(HasManyThroughAssociation)
|
116
|
-
inverse_assn.children_name_sym
|
117
|
-
else
|
118
|
-
inverse_assn.target_name_symbol
|
119
|
-
end
|
120
|
-
elsif association.habtm
|
121
|
-
association.base_association.children_name_sym
|
122
|
-
else
|
123
|
-
association.base_association.children_name_sym.to_s.singularize.to_sym
|
124
|
-
end
|
125
|
-
|
126
|
-
nested_conds = { intermediary_key => { parent_model_id_field.to_sym => parent_model.id } }
|
127
|
-
|
128
|
-
if nested_association.is_a?(HasManyThroughAssociation)
|
129
|
-
n = nested_association
|
130
|
-
hash = nested_conds
|
131
|
-
|
132
|
-
until !n.is_a?(HasManyThroughAssociation)
|
133
|
-
key = n.through_class.to_s.singularize.to_sym
|
134
|
-
hash = {key => hash}
|
135
|
-
n = n.nested_association
|
136
|
-
end
|
137
|
-
|
138
|
-
hash
|
139
|
-
else
|
140
|
-
nested_conds
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
88
|
def where(conditions={})
|
146
|
-
|
147
|
-
|
89
|
+
Core::HasManyThroughQuery.new(
|
90
|
+
child_class,
|
91
|
+
parent_model,
|
92
|
+
association.target_name_symbol,
|
93
|
+
conditions
|
94
|
+
)
|
148
95
|
end
|
149
96
|
end
|
150
97
|
end
|
@@ -40,9 +40,11 @@ module PassiveRecord
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def child_class
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
@child_class ||= (
|
44
|
+
module_name = association.parent_class.name.deconstantize
|
45
|
+
module_name = "Object" if module_name.empty?
|
46
|
+
(module_name.constantize).const_get(association.child_class_name.singularize)
|
47
|
+
)
|
46
48
|
end
|
47
49
|
|
48
50
|
def id
|
@@ -25,26 +25,30 @@ module PassiveRecord
|
|
25
25
|
if @scope
|
26
26
|
matching = @scope.method(:matching_instances)
|
27
27
|
if negated?
|
28
|
-
|
28
|
+
raw_all.reject(&matching)
|
29
29
|
else
|
30
|
-
|
30
|
+
raw_all.select(&matching)
|
31
31
|
end
|
32
32
|
else
|
33
33
|
matching = method(:matching_instances)
|
34
|
-
|
34
|
+
raw_all.select(&matching)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
def_delegators :all, :sample
|
38
38
|
|
39
|
+
def raw_all
|
40
|
+
@klass.all
|
41
|
+
end
|
42
|
+
|
39
43
|
def each
|
40
44
|
if @scope
|
41
45
|
matching = @scope.method(:matching_instances)
|
42
46
|
if negated?
|
43
|
-
|
47
|
+
raw_all.each do |instance|
|
44
48
|
yield instance unless matching[instance]
|
45
49
|
end
|
46
50
|
else
|
47
|
-
|
51
|
+
raw_all.each do |instance|
|
48
52
|
yield instance if matching[instance]
|
49
53
|
end
|
50
54
|
end
|
@@ -182,5 +186,18 @@ module PassiveRecord
|
|
182
186
|
true
|
183
187
|
end
|
184
188
|
end
|
189
|
+
|
190
|
+
class HasManyThroughQuery < Query
|
191
|
+
def initialize(klass, instance, target_name_sym, conditions={})
|
192
|
+
@klass = klass
|
193
|
+
@instance = instance
|
194
|
+
@target_name_sym = target_name_sym
|
195
|
+
@conditions = conditions
|
196
|
+
end
|
197
|
+
|
198
|
+
def raw_all
|
199
|
+
@instance.send(@target_name_sym).all
|
200
|
+
end
|
201
|
+
end
|
185
202
|
end
|
186
203
|
end
|
data/spec/passive_record_spec.rb
CHANGED
@@ -288,64 +288,124 @@ describe "passive record models" do
|
|
288
288
|
end
|
289
289
|
|
290
290
|
context 'querying with scopes through relationships' do
|
291
|
-
let
|
292
|
-
let
|
293
|
-
let
|
294
|
-
let
|
295
|
-
let
|
291
|
+
let(:network) { Network.create }
|
292
|
+
let(:stream) { network.create_stream }
|
293
|
+
let(:channel) { stream.create_channel }
|
294
|
+
let(:feed) { channel.create_feed }
|
295
|
+
let(:a_blog) { feed.create_blog }
|
296
|
+
|
296
297
|
let!(:not_recent_post) { a_blog.create_post(published_at: 10.days.ago) }
|
297
298
|
let!(:recent_post) do
|
298
299
|
a_blog.create_post(published_at: 1.day.ago)
|
299
300
|
end
|
300
301
|
|
302
|
+
let!(:special_category) { recent_post.create_category(special: true) }
|
303
|
+
let!(:unspecial_category) { recent_post.create_category(special: false) }
|
304
|
+
|
305
|
+
let!(:approved_comment) { recent_post.create_comment(approved: true) }
|
306
|
+
let!(:unapproved_comment) { recent_post.create_comment(approved: false) }
|
307
|
+
|
308
|
+
###
|
309
|
+
#
|
310
|
+
let(:another_network) { Network.create }
|
311
|
+
let(:another_stream) { another_network.create_stream }
|
312
|
+
let(:another_channel) { another_stream.create_channel }
|
313
|
+
let(:another_feed) { another_channel.create_feed }
|
314
|
+
let(:another_blog) { another_feed.create_blog }
|
315
|
+
|
316
|
+
|
317
|
+
let!(:post_from_unrelated_blog) { another_blog.create_post(published_at: 1.day.ago) }
|
318
|
+
let!(:unrelated_comment) do
|
319
|
+
post_from_unrelated_blog.create_comment(approved: true)
|
320
|
+
end
|
321
|
+
|
322
|
+
let!(:another_category) do
|
323
|
+
post_from_unrelated_blog.create_category(special: true)
|
324
|
+
end
|
325
|
+
|
301
326
|
describe 'should find related models through a has many' do
|
302
|
-
it 'should
|
327
|
+
it 'should refine' do
|
303
328
|
expect(a_blog.posts.recent).to include(recent_post)
|
304
329
|
expect(a_blog.posts.recent).not_to include(not_recent_post)
|
305
330
|
end
|
331
|
+
|
332
|
+
it 'should restrict' do
|
333
|
+
a_blog.posts.all.each do |post|
|
334
|
+
expect(another_blog.posts.all.map(&:id)).not_to include(post.id)
|
335
|
+
end
|
336
|
+
end
|
306
337
|
end
|
307
338
|
|
308
339
|
describe 'should find related models on a has_many through' do
|
309
|
-
it 'should
|
340
|
+
it 'should refine' do
|
310
341
|
expect(feed.posts.recent).to include(recent_post)
|
311
342
|
expect(feed.posts.recent).not_to include(not_recent_post)
|
312
343
|
end
|
344
|
+
|
345
|
+
it 'should restrict' do
|
346
|
+
feed.posts.each do |post|
|
347
|
+
expect(another_feed.posts).not_to include(post)
|
348
|
+
end
|
349
|
+
end
|
313
350
|
end
|
314
351
|
|
315
352
|
describe 'should find related models on a nested has_many thru' do
|
316
|
-
it 'should
|
353
|
+
it 'should refine' do
|
317
354
|
expect(channel.posts.recent).to include(recent_post)
|
318
355
|
expect(channel.posts.recent).not_to include(not_recent_post)
|
319
356
|
end
|
357
|
+
|
358
|
+
it 'should restrict' do
|
359
|
+
channel.posts.each do |post|
|
360
|
+
expect(another_channel.posts).not_to include(post)
|
361
|
+
end
|
362
|
+
end
|
320
363
|
end
|
321
364
|
|
322
365
|
describe 'should find related models on a double-nested has_many thru' do
|
323
|
-
it 'should
|
366
|
+
it 'should refine' do
|
324
367
|
expect(stream.posts.recent).to include(recent_post)
|
325
368
|
expect(stream.posts.recent).not_to include(not_recent_post)
|
326
369
|
end
|
370
|
+
|
371
|
+
it 'should restrict' do
|
372
|
+
expect(stream.posts.all).not_to be_empty
|
373
|
+
stream.posts.all.each do |post|
|
374
|
+
expect(another_stream.posts.all).not_to include(post)
|
375
|
+
end
|
376
|
+
end
|
327
377
|
end
|
328
378
|
|
329
379
|
describe 'should find related models on a deeply nested has_many thru' do
|
330
|
-
it 'should
|
380
|
+
it 'should refine' do
|
331
381
|
expect(network.posts.recent).to include(recent_post)
|
332
382
|
expect(network.posts.recent).not_to include(not_recent_post)
|
333
383
|
end
|
384
|
+
|
385
|
+
it 'should restrict' do
|
386
|
+
network.posts.all.each do |post|
|
387
|
+
expect(another_network.posts.all.map(&:id)).not_to include(post.id)
|
388
|
+
end
|
389
|
+
end
|
334
390
|
end
|
335
391
|
|
336
392
|
describe 'should find related models on a recursive has_many thru' do
|
337
|
-
|
338
|
-
let!(:unapproved_comment) { Post.last.create_comment(approved: false) }
|
339
|
-
|
340
|
-
it 'should refine/restrict' do
|
393
|
+
it 'should refine' do
|
341
394
|
expect(network.comments.approved).to include(approved_comment)
|
342
395
|
expect(network.comments.approved).not_to include(unapproved_comment)
|
343
396
|
end
|
397
|
+
|
398
|
+
it 'should restrict' do
|
399
|
+
expect(network.comments.all).not_to be_empty
|
400
|
+
network.comments.all.each do |comment|
|
401
|
+
expect(another_network.comments).not_to include(comment)
|
402
|
+
end
|
403
|
+
end
|
344
404
|
end
|
345
405
|
|
346
406
|
describe 'should find related models a recursive has_many :thru a habtm' do
|
347
|
-
let!(:promoted_tag) {
|
348
|
-
let!(:unpromoted_tag) {
|
407
|
+
let!(:promoted_tag) { recent_post.create_tag(promoted: true) }
|
408
|
+
let!(:unpromoted_tag) { recent_post.create_tag(promoted: false) }
|
349
409
|
|
350
410
|
it 'should refine and restrict' do
|
351
411
|
expect(network.tags.promoted).to include(promoted_tag)
|
@@ -354,13 +414,19 @@ describe "passive record models" do
|
|
354
414
|
end
|
355
415
|
|
356
416
|
describe 'should find related nested models through a manual habtm' do
|
357
|
-
let!(:special_category) { Post.first.create_category(special: true) }
|
358
|
-
let!(:unspecial_category) { Post.last.create_category(special: false) }
|
359
417
|
|
360
|
-
it 'should refine
|
418
|
+
it 'should refine' do
|
361
419
|
expect(network.categories.special).to include(special_category)
|
362
420
|
expect(network.categories.special).not_to include(unspecial_category)
|
363
421
|
end
|
422
|
+
|
423
|
+
it 'should restrict' do
|
424
|
+
expect(another_network.categories.all).not_to be_empty
|
425
|
+
expect(network.categories.all).not_to be_empty
|
426
|
+
another_network.categories.where.all.each do |category|
|
427
|
+
expect(network.categories.where.all).not_to include(category)
|
428
|
+
end
|
429
|
+
end
|
364
430
|
end
|
365
431
|
end
|
366
432
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passive_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Weissman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|