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