passive_record 0.1.6 → 0.1.7
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 +1 -2
- data/lib/passive_record/class_methods.rb +98 -0
- data/lib/passive_record/instance_methods.rb +70 -0
- data/lib/passive_record/pretty_printing.rb +33 -0
- data/lib/passive_record/version.rb +1 -1
- data/lib/passive_record.rb +5 -161
- data/spec/passive_record_spec.rb +30 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd4210b6d42edbbc3b345887b2a15918d452fdd2
|
4
|
+
data.tar.gz: c5c73ddb836c84ceaaf9290e829f7b7d31ae6e20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c56f0f03af33458ed3a8d0e0d8b8d4795187df9197a71c95625a688f47636b1898c4ce03450142bc4baaedf615c70b34ca075e018e038cdb2731a05e94254f07
|
7
|
+
data.tar.gz: 18e488e25f0ad1a0ca791920160bd0fc7f4c3fb49987f1801fa5f1485aa93dd8463ca5cf8be723ae966a5b43544c69c15aa7ea640335aba4342f4dc6780c1f07
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
[](https://codeclimate.com/github/deepcerulean/passive_record)
|
8
8
|
[](https://codeship.com/projects/128700)
|
9
9
|
[](https://codeclimate.com/github/deepcerulean/passive_record/coverage)
|
10
|
-
|
10
|
+
[](https://badge.fury.io/rb/passive_record)
|
11
11
|
|
12
12
|
## Description
|
13
13
|
|
@@ -27,7 +27,6 @@ PassiveRecord may be right for you!
|
|
27
27
|
|
28
28
|
## Features
|
29
29
|
|
30
|
-
- New objects are tracked and assigned IDs
|
31
30
|
- Build relationships with belongs_to, has_one and has_many
|
32
31
|
- Query on attributes and associations
|
33
32
|
- Supports many-to-many and self-referential relationships
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module PassiveRecord
|
2
|
+
module ClassMethods
|
3
|
+
include PassiveRecord::Core
|
4
|
+
include PassiveRecord::Associations
|
5
|
+
include PassiveRecord::Hooks
|
6
|
+
|
7
|
+
include Enumerable
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
# from http://stackoverflow.com/a/2393750/90042
|
11
|
+
def descendants
|
12
|
+
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
instances_by_id.values
|
17
|
+
end
|
18
|
+
def_delegators :all, :each
|
19
|
+
|
20
|
+
def last
|
21
|
+
all.last
|
22
|
+
end
|
23
|
+
|
24
|
+
def find(id_or_ids)
|
25
|
+
if id_or_ids.is_a?(Array)
|
26
|
+
find_by_ids(id_or_ids)
|
27
|
+
else
|
28
|
+
find_by_id(id_or_ids)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_by(conditions)
|
33
|
+
if conditions.is_a?(Identifier)
|
34
|
+
find_by_id(conditions)
|
35
|
+
elsif conditions.is_a?(Array) && conditions.all? { |c| c.is_a?(Identifier) }
|
36
|
+
find_by_ids(conditions)
|
37
|
+
else
|
38
|
+
where(conditions).first
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_all_by(conditions)
|
43
|
+
if conditions.is_a?(Array) && conditions.all? { |c| c.is_a?(Identifier) }
|
44
|
+
find_by_ids(conditions)
|
45
|
+
else
|
46
|
+
where(conditions).all
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def where(conditions)
|
51
|
+
Query.new(self, conditions)
|
52
|
+
end
|
53
|
+
|
54
|
+
def create(attrs={})
|
55
|
+
instance = new
|
56
|
+
|
57
|
+
instance.singleton_class.class_eval { attr_accessor :id }
|
58
|
+
instance.send(:"id=", Identifier.generate(self))
|
59
|
+
|
60
|
+
register(instance)
|
61
|
+
|
62
|
+
attrs.each do |(k,v)|
|
63
|
+
instance.send("#{k}=", v)
|
64
|
+
end
|
65
|
+
|
66
|
+
after_create_hooks.each do |hook|
|
67
|
+
hook.run(instance)
|
68
|
+
end
|
69
|
+
|
70
|
+
instance
|
71
|
+
end
|
72
|
+
|
73
|
+
def destroy_all
|
74
|
+
@instances = {}
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
def find_by_id(_id)
|
79
|
+
key = instances_by_id.keys.detect { |id,_| id == _id }
|
80
|
+
instances_by_id[key] if key
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_by_ids(ids)
|
84
|
+
instances_by_id.select { |id,_| ids.include?(id) }.values
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def instances_by_id
|
89
|
+
@instances ||= {}
|
90
|
+
end
|
91
|
+
|
92
|
+
def register(model)
|
93
|
+
instances_by_id[model.id] = model
|
94
|
+
self
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module PassiveRecord
|
2
|
+
module InstanceMethods
|
3
|
+
include PrettyPrinting
|
4
|
+
|
5
|
+
def respond_to?(meth,*args,&blk)
|
6
|
+
if find_relation_by_target_name_symbol(meth)
|
7
|
+
true
|
8
|
+
else
|
9
|
+
super(meth,*args,&blk)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(meth, *args, &blk)
|
14
|
+
if (matching_relation = find_relation_by_target_name_symbol(meth))
|
15
|
+
send_relation(matching_relation, meth, *args, &blk)
|
16
|
+
else
|
17
|
+
super(meth,*args,&blk)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def send_relation(matching_relation, meth, *args, &blk)
|
24
|
+
target_name = matching_relation.association.target_name_symbol.to_s
|
25
|
+
|
26
|
+
case meth.to_s
|
27
|
+
when target_name
|
28
|
+
matching_relation.lookup
|
29
|
+
when "#{target_name}="
|
30
|
+
matching_relation.parent_model_id = args.first.id
|
31
|
+
when "create_#{target_name}", "create_#{target_name.singularize}"
|
32
|
+
matching_relation.create(*args)
|
33
|
+
when "#{target_name}_id"
|
34
|
+
matching_relation.parent_model_id
|
35
|
+
when "#{target_name}_id="
|
36
|
+
matching_relation.parent_model_id = args.first
|
37
|
+
when "#{target_name}_ids", "#{target_name.singularize}_ids"
|
38
|
+
matching_relation.parent_model.send(target_name).map(&:id)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def relata
|
43
|
+
@_relata ||= self.class.associations.map do |assn|
|
44
|
+
assn.to_relation(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def find_relation_by_target_name_symbol(meth)
|
51
|
+
relata.detect do |relation| # matching relation...
|
52
|
+
possible_target_names(relation).include?(meth.to_s)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def possible_target_names(relation)
|
57
|
+
target_name = relation.association.target_name_symbol.to_s
|
58
|
+
[
|
59
|
+
target_name,
|
60
|
+
"#{target_name}=",
|
61
|
+
"#{target_name}_id",
|
62
|
+
"#{target_name}_ids",
|
63
|
+
"#{target_name.singularize}_ids",
|
64
|
+
"#{target_name}_id=",
|
65
|
+
"create_#{target_name}",
|
66
|
+
"create_#{target_name.singularize}"
|
67
|
+
]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module PassiveRecord
|
2
|
+
module PrettyPrinting
|
3
|
+
def inspect
|
4
|
+
pretty_vars = instance_variables_hash.map do |k,v|
|
5
|
+
"#{k.to_s.gsub(/^\@/,'')}: #{v.inspect}"
|
6
|
+
end.join(', ')
|
7
|
+
"#{self.class.name} (#{pretty_vars})"
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def uninspectable_instance_variables
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def inspectable_instance_variables
|
19
|
+
vars = instance_variables
|
20
|
+
vars += members rescue []
|
21
|
+
vars - uninspectable_instance_variables
|
22
|
+
end
|
23
|
+
|
24
|
+
# from http://stackoverflow.com/a/8417341/90042
|
25
|
+
def instance_variables_hash
|
26
|
+
Hash[
|
27
|
+
inspectable_instance_variables.
|
28
|
+
reject { |sym| sym.to_s.start_with?("@_") }.
|
29
|
+
map { |name| [name, (instance_variable_get(name) rescue send(name))] }
|
30
|
+
]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/passive_record.rb
CHANGED
@@ -10,6 +10,11 @@ require 'passive_record/class_inheritable_attrs'
|
|
10
10
|
require 'passive_record/associations'
|
11
11
|
require 'passive_record/hooks'
|
12
12
|
|
13
|
+
require 'passive_record/pretty_printing'
|
14
|
+
|
15
|
+
require 'passive_record/instance_methods'
|
16
|
+
require 'passive_record/class_methods'
|
17
|
+
|
13
18
|
module PassiveRecord
|
14
19
|
def self.included(base)
|
15
20
|
base.send :include, InstanceMethods
|
@@ -31,165 +36,4 @@ module PassiveRecord
|
|
31
36
|
def self.drop_all
|
32
37
|
(model_classes + model_classes.flat_map(&:descendants)).each(&:destroy_all)
|
33
38
|
end
|
34
|
-
|
35
|
-
module InstanceMethods
|
36
|
-
def inspect
|
37
|
-
pretty_vars = instance_variables_hash.map do |k,v|
|
38
|
-
"#{k.to_s.gsub(/^\@/,'')}: #{v.inspect}"
|
39
|
-
end.join(', ')
|
40
|
-
"#{self.class.name} (#{pretty_vars})"
|
41
|
-
end
|
42
|
-
|
43
|
-
# from http://stackoverflow.com/a/8417341/90042
|
44
|
-
def instance_variables_hash
|
45
|
-
Hash[
|
46
|
-
instance_variables.
|
47
|
-
reject { |sym| sym.to_s.start_with?("@_") }.
|
48
|
-
map { |name| [name, instance_variable_get(name)] }
|
49
|
-
]
|
50
|
-
end
|
51
|
-
|
52
|
-
def relata
|
53
|
-
@_relata ||= self.class.associations.map do |assn|
|
54
|
-
assn.to_relation(self)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
|
-
def find_relation_by_target_name_symbol(meth)
|
60
|
-
relata.detect do |relation| # matching relation...
|
61
|
-
meth == relation.association.target_name_symbol ||
|
62
|
-
meth.to_s == relation.association.target_name_symbol.to_s + "=" ||
|
63
|
-
meth.to_s == relation.association.target_name_symbol.to_s + "_id" ||
|
64
|
-
meth.to_s == relation.association.target_name_symbol.to_s + "_id=" ||
|
65
|
-
meth.to_s == "create_" + relation.association.target_name_symbol.to_s ||
|
66
|
-
meth.to_s == "create_" + (relation.association.target_name_symbol.to_s).singularize
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def respond_to?(meth,*args,&blk)
|
71
|
-
if find_relation_by_target_name_symbol(meth)
|
72
|
-
true
|
73
|
-
else
|
74
|
-
super(meth,*args,&blk)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def method_missing(meth, *args, &blk)
|
79
|
-
matching_relation = find_relation_by_target_name_symbol(meth)
|
80
|
-
|
81
|
-
if matching_relation
|
82
|
-
if meth.to_s == matching_relation.association.target_name_symbol.to_s + "_id"
|
83
|
-
matching_relation.parent_model_id
|
84
|
-
elsif meth.to_s.end_with?("_id=")
|
85
|
-
matching_relation.parent_model_id = args.first
|
86
|
-
elsif meth.to_s.end_with?("=")
|
87
|
-
matching_relation.parent_model_id = args.first.id
|
88
|
-
elsif meth.to_s.start_with?("create_")
|
89
|
-
matching_relation.create(*args)
|
90
|
-
else
|
91
|
-
# lookup the matching associated entities
|
92
|
-
matching_relation.lookup
|
93
|
-
end
|
94
|
-
else
|
95
|
-
super(meth,*args,&blk)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
module ClassMethods
|
101
|
-
include PassiveRecord::Core
|
102
|
-
include PassiveRecord::Associations
|
103
|
-
include PassiveRecord::Hooks
|
104
|
-
|
105
|
-
include Enumerable
|
106
|
-
extend Forwardable
|
107
|
-
|
108
|
-
# from http://stackoverflow.com/a/2393750/90042
|
109
|
-
def descendants
|
110
|
-
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
111
|
-
end
|
112
|
-
|
113
|
-
def all
|
114
|
-
instances_by_id.values
|
115
|
-
end
|
116
|
-
def_delegators :all, :each
|
117
|
-
|
118
|
-
def last
|
119
|
-
all.last
|
120
|
-
end
|
121
|
-
|
122
|
-
def find(id_or_ids)
|
123
|
-
if id_or_ids.is_a?(Array)
|
124
|
-
find_by_ids(id_or_ids)
|
125
|
-
else
|
126
|
-
find_by_id(id_or_ids)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def find_by(conditions)
|
131
|
-
if conditions.is_a?(Identifier)
|
132
|
-
find_by_id(conditions)
|
133
|
-
elsif conditions.is_a?(Array) && conditions.all? { |c| c.is_a?(Identifier) }
|
134
|
-
find_by_ids(conditions)
|
135
|
-
else
|
136
|
-
where(conditions).first
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
def find_all_by(conditions)
|
141
|
-
if conditions.is_a?(Array) && conditions.all? { |c| c.is_a?(Identifier) }
|
142
|
-
find_by_ids(conditions)
|
143
|
-
else
|
144
|
-
where(conditions).all
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def where(conditions)
|
149
|
-
Query.new(self, conditions)
|
150
|
-
end
|
151
|
-
|
152
|
-
def create(attrs={})
|
153
|
-
instance = new
|
154
|
-
|
155
|
-
instance.singleton_class.class_eval { attr_accessor :id }
|
156
|
-
instance.send(:"id=", Identifier.generate(self))
|
157
|
-
|
158
|
-
register(instance)
|
159
|
-
|
160
|
-
attrs.each do |(k,v)|
|
161
|
-
instance.send("#{k}=", v)
|
162
|
-
end
|
163
|
-
|
164
|
-
after_create_hooks.each do |hook|
|
165
|
-
hook.run(instance)
|
166
|
-
end
|
167
|
-
|
168
|
-
instance
|
169
|
-
end
|
170
|
-
|
171
|
-
def destroy_all
|
172
|
-
@instances = {}
|
173
|
-
end
|
174
|
-
|
175
|
-
protected
|
176
|
-
def find_by_id(_id)
|
177
|
-
key = instances_by_id.keys.detect { |id,_| id == _id }
|
178
|
-
instances_by_id[key] if key
|
179
|
-
end
|
180
|
-
|
181
|
-
def find_by_ids(ids)
|
182
|
-
instances_by_id.select { |id,_| ids.include?(id) }.values
|
183
|
-
end
|
184
|
-
|
185
|
-
private
|
186
|
-
def instances_by_id
|
187
|
-
@instances ||= {}
|
188
|
-
end
|
189
|
-
|
190
|
-
def register(model)
|
191
|
-
instances_by_id[model.id] = model
|
192
|
-
self
|
193
|
-
end
|
194
|
-
end
|
195
39
|
end
|
data/spec/passive_record_spec.rb
CHANGED
@@ -21,6 +21,12 @@ describe Model do
|
|
21
21
|
let!(:model) { SimpleModel.create(foo: value) }
|
22
22
|
let(:value) { 'foo_value' }
|
23
23
|
|
24
|
+
describe "#inspect" do
|
25
|
+
it 'should report attribute details' do
|
26
|
+
expect(model.inspect).to eq("SimpleModel (id: 1, foo: \"foo_value\")")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
24
30
|
describe "#id" do
|
25
31
|
it 'should be retrievable by id' do
|
26
32
|
expect(SimpleModel.find_by(model.id)).to eq(model)
|
@@ -69,6 +75,27 @@ describe Model do
|
|
69
75
|
expect(SimpleModel.find([model.id, model_b.id])).to eq([model, model_b])
|
70
76
|
end
|
71
77
|
end
|
78
|
+
|
79
|
+
describe "#where" do
|
80
|
+
it 'should return a query obj' do
|
81
|
+
expect(SimpleModel.where(id: 'fake_id')).to be_a(PassiveRecord::Core::Query)
|
82
|
+
end
|
83
|
+
|
84
|
+
context "queries" do
|
85
|
+
describe "#create" do
|
86
|
+
it 'should create objects' do
|
87
|
+
expect{SimpleModel.where(id: 'new_id').create }.to change{SimpleModel.count}.by(1)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#first_or_create" do
|
92
|
+
it 'should create the object or return matching' do
|
93
|
+
expect{SimpleModel.where(id: 'another_id').first_or_create }.to change{SimpleModel.count}.by(1)
|
94
|
+
expect{SimpleModel.where(id: 'another_id').first_or_create }.not_to change{SimpleModel.count}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
72
99
|
end
|
73
100
|
|
74
101
|
context 'querying by attributes' do
|
@@ -157,6 +184,7 @@ describe Model do
|
|
157
184
|
|
158
185
|
expect(child.id).not_to eq(another_child.id)
|
159
186
|
expect(parent.children).to eq([child, another_child])
|
187
|
+
expect(parent.children_ids).to eq([child.id, another_child.id])
|
160
188
|
end
|
161
189
|
end
|
162
190
|
|
@@ -169,6 +197,7 @@ describe Model do
|
|
169
197
|
expect(parent.dogs).to all(be_a(Dog))
|
170
198
|
expect(parent.dogs.count).to eq(1)
|
171
199
|
expect(parent.dogs.first).to eq(child.dogs.first)
|
200
|
+
expect(parent.dog_ids).to eq([child.dogs.first.id])
|
172
201
|
end
|
173
202
|
|
174
203
|
it 'should do the nested query example from the readme' do
|
@@ -228,4 +257,5 @@ describe Model do
|
|
228
257
|
end
|
229
258
|
end
|
230
259
|
end
|
260
|
+
|
231
261
|
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.1.
|
4
|
+
version: 0.1.7
|
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-02-
|
11
|
+
date: 2016-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -140,9 +140,12 @@ files:
|
|
140
140
|
- lib/passive_record/associations/has_many_through.rb
|
141
141
|
- lib/passive_record/associations/has_one.rb
|
142
142
|
- lib/passive_record/class_inheritable_attrs.rb
|
143
|
+
- lib/passive_record/class_methods.rb
|
143
144
|
- lib/passive_record/core/identifier.rb
|
144
145
|
- lib/passive_record/core/query.rb
|
145
146
|
- lib/passive_record/hooks.rb
|
147
|
+
- lib/passive_record/instance_methods.rb
|
148
|
+
- lib/passive_record/pretty_printing.rb
|
146
149
|
- lib/passive_record/version.rb
|
147
150
|
- passive_record.gemspec
|
148
151
|
- spec/passive_record_spec.rb
|