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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9580976e3da148321a6f89b5c8aef63f43d4eba2
4
- data.tar.gz: 92849267be3d5db48a02ceaa6018dfbba882d79b
3
+ metadata.gz: 2244d23d7746c76519f66795e7799800d7ed84f5
4
+ data.tar.gz: f3aa364123d96e1477e64eeda2676e616a712515
5
5
  SHA512:
6
- metadata.gz: 83e137d58c0e537264726628f761d5819c503a801de7e803602121fa3448ee1cba2cc1db1c5fbd20534e4133fb64ca62250818cbd0ea048cc8d2797dfd43d18c
7
- data.tar.gz: a3f48dcfe2e00f862e4d2ef978c2cc11c5ded5e3b9bd955f623abe18f0b0b9ede6733794c05b6c9e40f121bb727e92cfe67f57eb5fb3f34b7cf8ea584950c0ae
6
+ metadata.gz: 4baa0facacc3099d0f8c8e36e0fb283ee1dd319067a6249ef2d6ffbdad6afddca95d70779bd8b140b713b6717778182afcfccb26661113e22401aa2cf18aed62
7
+ data.tar.gz: fea3b1c06ef2b0f90738d53d9f1783024527582f5c49bd4869233a71882e248623c7b5980b7679701d6e1c1f61f0bb78886807fd861570017e2710f21a4916df
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## Version 0.7.3
2
+
3
+ - Only save attributes which have changed
4
+
1
5
  ## Version 0.7.2
2
6
 
3
7
  - Add license to gemspec for the benefit of [Rubygems.org](http://rubygems.org/gems/perpetuity)
@@ -17,5 +17,9 @@ module Perpetuity
17
17
  def to_s
18
18
  name
19
19
  end
20
+
21
+ def =~ regexp
22
+ name.to_s =~ regexp
23
+ end
20
24
  end
21
25
  end
@@ -13,7 +13,7 @@ module Perpetuity
13
13
  def << object
14
14
  klass = object.class
15
15
  id = object.instance_variable_get(:@id)
16
- map[klass][id.to_s] = object
16
+ map[klass][id.to_s] = object.dup
17
17
  end
18
18
  end
19
19
  end
@@ -47,18 +47,25 @@ module Perpetuity
47
47
 
48
48
  def reindex!
49
49
  indexes.each { |index| data_source.activate_index! index }
50
- (data_source.active_indexes(mapped_class) - indexes).reject do |index|
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 do |obj|
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
- update object, serialize(object), false
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.class.attribute_set.map do |attrib|
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Perpetuity
2
- VERSION = "0.7.2"
2
+ VERSION = "0.7.3"
3
3
  end
@@ -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
- subject { Attribute.new :article, Object }
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
@@ -18,7 +18,8 @@ module Perpetuity
18
18
  mapper.should_receive(:find).with(1) { first }
19
19
 
20
20
  derefer.load first_ref
21
- derefer[first_ref].should == first
21
+ id = derefer[first_ref].instance_variable_get(:@id)
22
+ id.should == 1
22
23
  end
23
24
  end
24
25
 
@@ -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) { double('Object', class: Object) }
8
+ let(:object) { Object.new }
11
9
 
12
- before { object.instance_variable_set :@id, 1 }
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 << object
16
- id_map[Object, 1].should == object
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 << object
21
- id_map[Object, '1'].should == object
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.2
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-08-04 00:00:00.000000000 Z
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.5
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