passive_record 0.1.5 → 0.1.6
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 +14 -4
- data/lib/passive_record.rb +9 -5
- data/lib/passive_record/associations.rb +1 -1
- data/lib/passive_record/associations/has_many_through.rb +31 -15
- data/lib/passive_record/associations/has_one.rb +1 -0
- data/lib/passive_record/core/identifier.rb +1 -1
- data/lib/passive_record/version.rb +1 -1
- data/spec/passive_record_spec.rb +50 -10
- data/spec/spec_helper.rb +9 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f29ddf4304ae896f01ee7455929a409966799df
|
4
|
+
data.tar.gz: cfa9db856459164c5864e4df1f2679cf2764a20f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cb01f630b4bf056a4747a0f181afa3bd86859b663c831ef4f845d099d83078cc9fc20eef247ac3a08619fed9bc381996554409c113591cda491c9328817abbe
|
7
|
+
data.tar.gz: ae8db06335510ad6bb9213e4ce7c6024e85a02a979f0d4d718b1bea05de713a398488b74bd9d3dfdbe678857ad873c34d0c4b38397793c8347a9b98965300ff7
|
data/README.md
CHANGED
@@ -36,6 +36,7 @@ PassiveRecord may be right for you!
|
|
36
36
|
|
37
37
|
## Examples
|
38
38
|
|
39
|
+
````ruby
|
39
40
|
require 'passive_record'
|
40
41
|
|
41
42
|
class Model
|
@@ -58,20 +59,29 @@ PassiveRecord may be right for you!
|
|
58
59
|
|
59
60
|
# Let's create some models!
|
60
61
|
parent = Parent.create
|
62
|
+
=> Parent (id: 1)
|
61
63
|
|
62
64
|
child = parent.create_child
|
65
|
+
=> Child (id: 1)
|
66
|
+
|
63
67
|
dog = child.create_dog
|
68
|
+
=> Dog (id: 1)
|
64
69
|
|
65
70
|
# inverse relationships
|
66
|
-
dog.child
|
71
|
+
dog.child
|
72
|
+
=> Child (id: 1)
|
67
73
|
|
68
|
-
Dog.find_by
|
74
|
+
Dog.find_by child: child
|
75
|
+
=> Dog (id: 1)
|
69
76
|
|
70
77
|
# has many thru
|
71
|
-
parent.dogs
|
78
|
+
parent.dogs
|
79
|
+
=> [Dog (id: 1)]
|
72
80
|
|
73
81
|
# nested queries
|
74
|
-
Dog.find_all_by(child: { parent: parent })
|
82
|
+
Dog.find_all_by(child: { parent: parent })
|
83
|
+
=> [Dog (id: 1)]
|
84
|
+
````
|
75
85
|
|
76
86
|
## Requirements
|
77
87
|
|
data/lib/passive_record.rb
CHANGED
@@ -42,11 +42,15 @@ module PassiveRecord
|
|
42
42
|
|
43
43
|
# from http://stackoverflow.com/a/8417341/90042
|
44
44
|
def instance_variables_hash
|
45
|
-
Hash[
|
45
|
+
Hash[
|
46
|
+
instance_variables.
|
47
|
+
reject { |sym| sym.to_s.start_with?("@_") }.
|
48
|
+
map { |name| [name, instance_variable_get(name)] }
|
49
|
+
]
|
46
50
|
end
|
47
51
|
|
48
52
|
def relata
|
49
|
-
@
|
53
|
+
@_relata ||= self.class.associations.map do |assn|
|
50
54
|
assn.to_relation(self)
|
51
55
|
end
|
52
56
|
end
|
@@ -169,15 +173,15 @@ module PassiveRecord
|
|
169
173
|
end
|
170
174
|
|
171
175
|
protected
|
172
|
-
def find_by_id(
|
173
|
-
instances_by_id
|
176
|
+
def find_by_id(_id)
|
177
|
+
key = instances_by_id.keys.detect { |id,_| id == _id }
|
178
|
+
instances_by_id[key] if key
|
174
179
|
end
|
175
180
|
|
176
181
|
def find_by_ids(ids)
|
177
182
|
instances_by_id.select { |id,_| ids.include?(id) }.values
|
178
183
|
end
|
179
184
|
|
180
|
-
|
181
185
|
private
|
182
186
|
def instances_by_id
|
183
187
|
@instances ||= {}
|
@@ -23,7 +23,7 @@ module PassiveRecord
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def has_many(collection_name_sym, opts={})
|
26
|
-
target_class_name = (collection_name_sym.to_s).split('_').map(&:capitalize).join
|
26
|
+
target_class_name = opts.delete(:class_name) { (collection_name_sym.to_s).split('_').map(&:capitalize).join }
|
27
27
|
|
28
28
|
if opts.key?(:through)
|
29
29
|
through_class_collection_name = opts.delete(:through)
|
@@ -7,29 +7,45 @@ module PassiveRecord
|
|
7
7
|
end
|
8
8
|
|
9
9
|
class HasManyThroughRelation < HasManyRelation
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
lookup
|
10
|
+
def intermediary_relation
|
11
|
+
association.base_association.to_relation(parent_model)
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
def results
|
15
|
+
intermediary_relation.lookup
|
16
|
+
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
elsif intermediate_results.first.respond_to?(plural_target_sym)
|
22
|
-
intermediate_results.flat_map(&plural_target_sym)
|
23
|
-
end
|
18
|
+
def lookup
|
19
|
+
if target_sym && results
|
20
|
+
results.flat_map(&target_sym)
|
24
21
|
else
|
25
22
|
[]
|
26
23
|
end
|
27
24
|
end
|
28
25
|
|
26
|
+
def target_sym
|
27
|
+
name_str = association.target_name_symbol.to_s
|
28
|
+
singular_target_sym = name_str.singularize.to_sym
|
29
|
+
plural_target_sym = name_str.pluralize.to_sym
|
30
|
+
|
31
|
+
singular_class_name_sym = association.child_class_name.underscore.singularize.to_sym
|
32
|
+
plural_class_name_sym = association.child_class_name.underscore.pluralize.to_sym
|
33
|
+
|
34
|
+
if !results.empty?
|
35
|
+
if results.first.respond_to?(singular_target_sym)
|
36
|
+
singular_target_sym
|
37
|
+
elsif results.first.respond_to?(plural_target_sym)
|
38
|
+
plural_target_sym
|
39
|
+
elsif results.first.respond_to?(singular_class_name_sym)
|
40
|
+
singular_class_name_sym
|
41
|
+
elsif results.first.respond_to?(plural_class_name_sym)
|
42
|
+
plural_class_name_sym
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
29
47
|
def create(attrs={})
|
30
|
-
|
31
|
-
raise "missing intermediate relational key #{association.through_class}" unless attrs.key?(association.through_class)
|
32
|
-
super(attrs)
|
48
|
+
child_class.create(attrs)
|
33
49
|
end
|
34
50
|
end
|
35
51
|
end
|
data/spec/passive_record_spec.rb
CHANGED
@@ -35,22 +35,42 @@ describe Model do
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
describe "#create" do
|
38
|
+
describe "#create" do
|
39
39
|
it 'should assign attributes' do
|
40
40
|
expect(model.foo).to eq('foo_value')
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
describe "#destroy_all" do
|
45
|
-
before {
|
46
|
-
SimpleModel.create(foo: '
|
47
|
-
SimpleModel.create(foo: '
|
45
|
+
before {
|
46
|
+
SimpleModel.create(foo: 'val1')
|
47
|
+
SimpleModel.create(foo: 'val2')
|
48
48
|
}
|
49
|
+
|
49
50
|
it 'should remove all models' do
|
50
51
|
expect { SimpleModel.destroy_all }.to change { SimpleModel.count }.by(-SimpleModel.count)
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
55
|
+
context 'querying by id' do
|
56
|
+
describe "#find" do
|
57
|
+
subject(:model) { SimpleModel.create }
|
58
|
+
it 'should lookup a record based on an identifier' do
|
59
|
+
expect(SimpleModel.find(-1)).to eq(nil)
|
60
|
+
expect(SimpleModel.find(model.id)).to eq(model)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should lookup records based on primary key value' do
|
64
|
+
expect(SimpleModel.find(model.id.value)).to eq(model)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should lookup records based on ids' do
|
68
|
+
model_b = SimpleModel.create
|
69
|
+
expect(SimpleModel.find([model.id, model_b.id])).to eq([model, model_b])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
54
74
|
context 'querying by attributes' do
|
55
75
|
describe "#find_by" do
|
56
76
|
it 'should be retrievable by query' do
|
@@ -108,15 +128,15 @@ describe Model do
|
|
108
128
|
let(:another_child) { Child.create }
|
109
129
|
|
110
130
|
it 'should create children' do
|
111
|
-
expect { child.
|
112
|
-
expect(child.
|
131
|
+
expect { child.create_toy }.to change { Toy.count }.by(1)
|
132
|
+
expect(child.toy).to eq(Toy.last)
|
113
133
|
end
|
114
134
|
|
115
135
|
it 'should have inverse relationships' do
|
116
|
-
|
117
|
-
expect(
|
118
|
-
|
119
|
-
expect(
|
136
|
+
toy = child.create_toy
|
137
|
+
expect(toy.child).to eq(child)
|
138
|
+
another_toy = another_child.create_toy
|
139
|
+
expect(another_toy.child).to eq(another_child)
|
120
140
|
end
|
121
141
|
end
|
122
142
|
|
@@ -156,6 +176,26 @@ describe Model do
|
|
156
176
|
expect(Dog.find_all_by(child: {parent: parent})).
|
157
177
|
to eq(parent.dogs)
|
158
178
|
end
|
179
|
+
|
180
|
+
it 'should work for has-one intermediary relationships' do
|
181
|
+
child.create_toy
|
182
|
+
expect(parent.toys).to all(be_a(Toy))
|
183
|
+
expect(parent.toys.count).to eq(1)
|
184
|
+
expect(parent.toys.first).to eq(child.toy)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should attempt to construct intermediary relations' do
|
188
|
+
expect { parent.create_toy(child: child) }.to change {Toy.count}.by(1)
|
189
|
+
expect(Toy.last.child).to eq(child)
|
190
|
+
expect(Toy.last.child.parent).to eq(parent)
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should accept class name' do
|
194
|
+
post = Post.create
|
195
|
+
user = User.create
|
196
|
+
Comment.create(post: post, user: user)
|
197
|
+
expect(post.commenters).to eq([user])
|
198
|
+
end
|
159
199
|
end
|
160
200
|
|
161
201
|
context 'many-to-many' do
|
data/spec/spec_helper.rb
CHANGED
@@ -21,7 +21,14 @@ class Dog < Model
|
|
21
21
|
after_create {@sound = 'bark'}
|
22
22
|
end
|
23
23
|
|
24
|
+
class Toy < Model
|
25
|
+
belongs_to :child
|
26
|
+
attr_reader :kind
|
27
|
+
after_create {@kind = %w[ stuffed_animal blocks cards ].sample}
|
28
|
+
end
|
29
|
+
|
24
30
|
class Child < Model
|
31
|
+
has_one :toy
|
25
32
|
has_many :dogs
|
26
33
|
belongs_to :parent
|
27
34
|
|
@@ -34,6 +41,7 @@ end
|
|
34
41
|
class Parent < Model
|
35
42
|
has_many :children
|
36
43
|
has_many :dogs, :through => :children
|
44
|
+
has_many :toys, :through => :children
|
37
45
|
end
|
38
46
|
|
39
47
|
###
|
@@ -71,6 +79,7 @@ end
|
|
71
79
|
|
72
80
|
class Post < Model
|
73
81
|
has_many :comments
|
82
|
+
has_many :commenters, :through => :comments, :class_name => "User"
|
74
83
|
end
|
75
84
|
|
76
85
|
class User < Model
|