passive_record 0.4.14 → 0.4.15
Sign up to get free protection for your applications and to get access to all the features.
- 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
|