perpetuity 0.7.2 → 0.7.3
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/CHANGELOG.md +4 -0
- data/lib/perpetuity/attribute.rb +4 -0
- data/lib/perpetuity/identity_map.rb +1 -1
- data/lib/perpetuity/mapper.rb +35 -14
- data/lib/perpetuity/mongodb/serializer.rb +6 -1
- data/lib/perpetuity/mongodb.rb +5 -1
- data/lib/perpetuity/version.rb +1 -1
- data/spec/integration/mongodb_spec.rb +32 -0
- data/spec/integration/update_spec.rb +18 -0
- data/spec/perpetuity/attribute_spec.rb +7 -1
- data/spec/perpetuity/dereferencer_spec.rb +2 -1
- data/spec/perpetuity/identity_map_spec.rb +14 -8
- data/spec/perpetuity/mapper_spec.rb +16 -0
- data/spec/perpetuity/mongodb/serializer_spec.rb +12 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2244d23d7746c76519f66795e7799800d7ed84f5
|
4
|
+
data.tar.gz: f3aa364123d96e1477e64eeda2676e616a712515
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4baa0facacc3099d0f8c8e36e0fb283ee1dd319067a6249ef2d6ffbdad6afddca95d70779bd8b140b713b6717778182afcfccb26661113e22401aa2cf18aed62
|
7
|
+
data.tar.gz: fea3b1c06ef2b0f90738d53d9f1783024527582f5c49bd4869233a71882e248623c7b5980b7679701d6e1c1f61f0bb78886807fd861570017e2710f21a4916df
|
data/CHANGELOG.md
CHANGED
data/lib/perpetuity/attribute.rb
CHANGED
data/lib/perpetuity/mapper.rb
CHANGED
@@ -47,18 +47,25 @@ module Perpetuity
|
|
47
47
|
|
48
48
|
def reindex!
|
49
49
|
indexes.each { |index| data_source.activate_index! index }
|
50
|
-
|
51
|
-
# TODO: Make this not MongoDB-specific
|
52
|
-
index.attribute.name.to_s == '_id'
|
53
|
-
end.each do |index|
|
50
|
+
unspecified_indexes.each do |index|
|
54
51
|
data_source.remove_index index
|
55
52
|
end
|
56
53
|
end
|
57
54
|
|
55
|
+
def unspecified_indexes
|
56
|
+
active_indexes = data_source.active_indexes(mapped_class)
|
57
|
+
active_but_unspecified_indexes = (active_indexes - indexes)
|
58
|
+
active_but_unspecified_indexes.reject { |index| index.attribute =~ /id/ }
|
59
|
+
end
|
60
|
+
|
58
61
|
def attributes
|
59
62
|
self.class.attributes
|
60
63
|
end
|
61
64
|
|
65
|
+
def attribute_set
|
66
|
+
self.class.attribute_set
|
67
|
+
end
|
68
|
+
|
62
69
|
def delete_all
|
63
70
|
data_source.delete_all mapped_class
|
64
71
|
end
|
@@ -66,14 +73,7 @@ module Perpetuity
|
|
66
73
|
def insert object
|
67
74
|
raise "#{object} is invalid and cannot be persisted." unless self.class.validations.valid?(object)
|
68
75
|
objects = Array(object)
|
69
|
-
serialized_objects = objects.map
|
70
|
-
attributes = serialize(obj)
|
71
|
-
if o_id = obj.instance_exec(&self.class.id)
|
72
|
-
attributes[:id] = o_id
|
73
|
-
end
|
74
|
-
|
75
|
-
attributes
|
76
|
-
end
|
76
|
+
serialized_objects = objects.map { |obj| serialize(obj) }
|
77
77
|
|
78
78
|
new_ids = data_source.insert(mapped_class, serialized_objects)
|
79
79
|
objects.each_with_index do |obj, index|
|
@@ -87,6 +87,10 @@ module Perpetuity
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
def generate_id_for object
|
91
|
+
object.instance_exec(&self.class.id)
|
92
|
+
end
|
93
|
+
|
90
94
|
def self.data_source(configuration=Perpetuity.configuration)
|
91
95
|
configuration.data_source
|
92
96
|
end
|
@@ -186,7 +190,12 @@ module Perpetuity
|
|
186
190
|
end
|
187
191
|
|
188
192
|
def save object
|
189
|
-
|
193
|
+
changed_attributes = serialize_changed_attributes(object)
|
194
|
+
if changed_attributes && changed_attributes.any?
|
195
|
+
update object, changed_attributes
|
196
|
+
else
|
197
|
+
update object, serialize(object), false
|
198
|
+
end
|
190
199
|
end
|
191
200
|
|
192
201
|
def increment object, attribute, count=1
|
@@ -226,7 +235,19 @@ module Perpetuity
|
|
226
235
|
end
|
227
236
|
|
228
237
|
def serialize object
|
229
|
-
data_source.serialize(object, self)
|
238
|
+
attributes = data_source.serialize(object, self)
|
239
|
+
if o_id = generate_id_for(object)
|
240
|
+
attributes[:id] = o_id
|
241
|
+
end
|
242
|
+
|
243
|
+
attributes
|
244
|
+
end
|
245
|
+
|
246
|
+
def serialize_changed_attributes object
|
247
|
+
cached = identity_map[object.class, id_for(object)]
|
248
|
+
if cached
|
249
|
+
data_source.serialize_changed_attributes(object, cached, self)
|
250
|
+
end
|
230
251
|
end
|
231
252
|
|
232
253
|
def self.mapped_class
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'perpetuity/data_injectable'
|
2
|
+
require 'perpetuity/reference'
|
2
3
|
|
3
4
|
module Perpetuity
|
4
5
|
class MongoDB
|
@@ -22,7 +23,7 @@ module Perpetuity
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def serialize object
|
25
|
-
attrs = mapper.
|
26
|
+
attrs = mapper.attribute_set.map do |attrib|
|
26
27
|
next unless has_attribute? object, attrib.name
|
27
28
|
|
28
29
|
value = attribute_for object, attrib.name
|
@@ -45,6 +46,10 @@ module Perpetuity
|
|
45
46
|
Hash[attrs.compact]
|
46
47
|
end
|
47
48
|
|
49
|
+
def serialize_changes changed, original
|
50
|
+
Hash[Array(serialize(changed)) - Array(serialize(original))]
|
51
|
+
end
|
52
|
+
|
48
53
|
def unserialize data
|
49
54
|
if data.is_a? Array
|
50
55
|
unserialize_object_array data
|
data/lib/perpetuity/mongodb.rb
CHANGED
@@ -146,7 +146,7 @@ module Perpetuity
|
|
146
146
|
end
|
147
147
|
|
148
148
|
def update klass, id, new_data
|
149
|
-
find(klass, id).update(new_data)
|
149
|
+
find(klass, id).update('$set' => new_data)
|
150
150
|
end
|
151
151
|
|
152
152
|
def can_serialize? value
|
@@ -214,6 +214,10 @@ module Perpetuity
|
|
214
214
|
Serializer.new(mapper).serialize object
|
215
215
|
end
|
216
216
|
|
217
|
+
def serialize_changed_attributes object, original, mapper
|
218
|
+
Serializer.new(mapper).serialize_changes object, original
|
219
|
+
end
|
220
|
+
|
217
221
|
def unserialize data, mapper
|
218
222
|
Serializer.new(mapper).unserialize data
|
219
223
|
end
|
data/lib/perpetuity/version.rb
CHANGED
@@ -100,6 +100,38 @@ module Perpetuity
|
|
100
100
|
retrieved_time.to_f.should be_within(0.001).of time.to_f
|
101
101
|
end
|
102
102
|
|
103
|
+
describe 'serialization' do
|
104
|
+
let(:object) { Object.new }
|
105
|
+
let(:foo_attribute) { double('Attribute', name: :foo) }
|
106
|
+
let(:baz_attribute) { double('Attribute', name: :baz) }
|
107
|
+
let(:mapper) { double('Mapper',
|
108
|
+
mapped_class: Object,
|
109
|
+
mapper_registry: {},
|
110
|
+
attribute_set: Set[foo_attribute, baz_attribute],
|
111
|
+
data_source: mongo,
|
112
|
+
) }
|
113
|
+
|
114
|
+
before do
|
115
|
+
object.instance_variable_set :@foo, 'bar'
|
116
|
+
object.instance_variable_set :@baz, 'quux'
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'serializes objects' do
|
120
|
+
mongo.serialize(object, mapper).should == {
|
121
|
+
'foo' => 'bar',
|
122
|
+
'baz' => 'quux'
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'can serialize only modified attributes of objects' do
|
127
|
+
updated = object.dup
|
128
|
+
updated.instance_variable_set :@foo, 'foo'
|
129
|
+
|
130
|
+
serialized = mongo.serialize_changed_attributes(updated, object, mapper)
|
131
|
+
serialized.should == { 'foo' => 'foo' }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
103
135
|
describe 'serializable objects' do
|
104
136
|
let(:serializable_values) { [nil, true, false, 1, 1.2, '', [], {}, Time.now] }
|
105
137
|
|
@@ -26,6 +26,24 @@ describe 'updating' do
|
|
26
26
|
mapper.find(mapper.id_for article).title.should eq new_title
|
27
27
|
end
|
28
28
|
|
29
|
+
it 'only updates attributes which have changed since last retrieval' do
|
30
|
+
first_mapper = Perpetuity[Article]
|
31
|
+
second_mapper = Perpetuity[Article]
|
32
|
+
article_id = first_mapper.id_for(article)
|
33
|
+
first_article = first_mapper.find(article_id)
|
34
|
+
second_article = second_mapper.find(article_id)
|
35
|
+
|
36
|
+
# Change different attributes on each
|
37
|
+
first_article.title = 'New title'
|
38
|
+
second_article.views = 7
|
39
|
+
first_mapper.save first_article
|
40
|
+
second_mapper.save second_article
|
41
|
+
|
42
|
+
canonical_article = mapper.find(article_id)
|
43
|
+
canonical_article.title.should == 'New title'
|
44
|
+
canonical_article.views.should == 7
|
45
|
+
end
|
46
|
+
|
29
47
|
it 'updates an object with referenced attributes' do
|
30
48
|
user = User.new
|
31
49
|
article.author = user
|
@@ -2,7 +2,9 @@ require 'perpetuity/attribute'
|
|
2
2
|
|
3
3
|
module Perpetuity
|
4
4
|
describe Attribute do
|
5
|
-
|
5
|
+
let(:attribute) { Attribute.new :article, Object }
|
6
|
+
subject { attribute }
|
7
|
+
|
6
8
|
it 'has a name' do
|
7
9
|
subject.name.should == :article
|
8
10
|
end
|
@@ -15,5 +17,9 @@ module Perpetuity
|
|
15
17
|
attribute = Attribute.new :article, Object, embedded: true
|
16
18
|
attribute.should be_embedded
|
17
19
|
end
|
20
|
+
|
21
|
+
it 'can match a regex' do
|
22
|
+
expect(attribute =~ /article/).to be_true
|
23
|
+
end
|
18
24
|
end
|
19
25
|
end
|
@@ -2,23 +2,29 @@ require 'perpetuity/identity_map'
|
|
2
2
|
|
3
3
|
module Perpetuity
|
4
4
|
describe IdentityMap do
|
5
|
-
let(:object_mapper) { double('Mapper', id_for: 1) }
|
6
|
-
let(:object) { double('Object', class: Object) }
|
7
5
|
let(:id_map) { IdentityMap.new }
|
8
6
|
|
9
7
|
context 'when the object exists in the IdentityMap' do
|
10
|
-
let(:object) {
|
8
|
+
let(:object) { Object.new }
|
11
9
|
|
12
|
-
before
|
10
|
+
before do
|
11
|
+
object.instance_variable_set :@id, 1
|
12
|
+
id_map << object
|
13
|
+
end
|
13
14
|
|
14
15
|
it 'returns the object with the given class and id' do
|
15
|
-
id_map
|
16
|
-
|
16
|
+
retrieved = id_map[Object, 1]
|
17
|
+
|
18
|
+
retrieved.instance_variable_get(:@id).should == 1
|
19
|
+
end
|
20
|
+
|
21
|
+
specify 'the object returned is a duplicate, not the same object' do
|
22
|
+
id_map[Object, 1].should_not be object
|
17
23
|
end
|
18
24
|
|
19
25
|
it 'stringifies keys when checking' do
|
20
|
-
id_map
|
21
|
-
|
26
|
+
retrieved = id_map[Object, '1']
|
27
|
+
retrieved.instance_variable_get(:@id).should == 1
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
@@ -123,6 +123,22 @@ module Perpetuity
|
|
123
123
|
mapper.save object
|
124
124
|
end
|
125
125
|
|
126
|
+
it 'can serialize only changed attributes for updates' do
|
127
|
+
mapper_class.attribute :modified
|
128
|
+
mapper_class.attribute :unmodified
|
129
|
+
object = Object.new
|
130
|
+
object.instance_variable_set :@id, 1
|
131
|
+
object.instance_variable_set :@modified, false
|
132
|
+
object.instance_variable_set :@unmodified, false
|
133
|
+
mapper.identity_map << object
|
134
|
+
|
135
|
+
object.instance_variable_set :@modified, true
|
136
|
+
|
137
|
+
mapper.serialize_changed_attributes(object).should == {
|
138
|
+
'modified' => true
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
126
142
|
it 'deletes an object from the data source' do
|
127
143
|
object = Object.new
|
128
144
|
|
@@ -61,6 +61,18 @@ module Perpetuity
|
|
61
61
|
}
|
62
62
|
end
|
63
63
|
|
64
|
+
it 'can serialize only changed attributes' do
|
65
|
+
book = Book.new('Original Title')
|
66
|
+
updated_book = book.dup
|
67
|
+
updated_book.title = 'New Title'
|
68
|
+
book_mapper.stub(data_source: data_source)
|
69
|
+
data_source.stub(:can_serialize?).with('New Title') { true }
|
70
|
+
data_source.stub(:can_serialize?).with('Original Title') { true }
|
71
|
+
serializer.serialize_changes(updated_book, book).should == {
|
72
|
+
'title' => 'New Title'
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
64
76
|
context 'with objects that have hashes as attributes' do
|
65
77
|
let(:name_data) { {first_name: 'Jamie', last_name: 'Gaskins'} }
|
66
78
|
let(:serialized_data) { { 'name' => name_data } }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perpetuity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Gaskins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -161,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
161
|
version: '0'
|
162
162
|
requirements: []
|
163
163
|
rubyforge_project:
|
164
|
-
rubygems_version: 2.0.
|
164
|
+
rubygems_version: 2.0.6
|
165
165
|
signing_key:
|
166
166
|
specification_version: 4
|
167
167
|
summary: Persistence library allowing serialization of Ruby objects
|