toystore-mongo 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -1
- data/README.md +21 -1
- data/examples/atomic_updates.rb +36 -0
- data/lib/toy/mongo/atomic_updates.rb +55 -0
- data/lib/toy/mongo/querying.rb +0 -47
- data/lib/toy/mongo/version.rb +1 -1
- data/lib/toy/mongo.rb +4 -1
- data/spec/support/constants.rb +22 -20
- data/spec/toy/mongo/atomic_updates_spec.rb +163 -0
- data/spec/toy/mongo/querying_spec.rb +3 -151
- data/toystore-mongo.gemspec +3 -3
- metadata +77 -48
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -25,7 +25,27 @@ Including Toy::Mongo includes Toy::Store and then does a few things:
|
|
25
25
|
* Sets the key factory to object id
|
26
26
|
* Overrides get so that it also works with string representation of object id
|
27
27
|
* Overrides get_multi so that it performs one query instead of one query per id
|
28
|
-
|
28
|
+
|
29
|
+
|
30
|
+
### Atomic Updates
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class User
|
34
|
+
include Toy::Mongo
|
35
|
+
include Toy::Mongo::AtomicUpdates
|
36
|
+
|
37
|
+
adapter :mongo_atomic, Mongo::Connection.new.db('adapter')['testing']
|
38
|
+
|
39
|
+
attribute :name, String
|
40
|
+
attribute :bio, String
|
41
|
+
end
|
42
|
+
|
43
|
+
user = User.create(:name => 'John', :bio => 'Awesome!')
|
44
|
+
user.name = 'Nunes'
|
45
|
+
user.save # Equivalent to update({:_id => user.id}, {'$set' => {'name' => 'Nunes'}})
|
46
|
+
```
|
47
|
+
|
48
|
+
Caveat: At this time it only works with simple data types. Complex types like Hash, Array, and Set are not supported. Oddness will ensue if you expect them to work as they can be manipuled through means other than assignment.
|
29
49
|
|
30
50
|
## Contributing
|
31
51
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
|
5
|
+
Bundler.require(:default)
|
6
|
+
|
7
|
+
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
8
|
+
lib_path = root_path.join('lib')
|
9
|
+
$:.unshift(lib_path)
|
10
|
+
|
11
|
+
require 'toy/mongo'
|
12
|
+
|
13
|
+
class User
|
14
|
+
include Toy::Mongo
|
15
|
+
include Toy::Mongo::AtomicUpdates
|
16
|
+
|
17
|
+
adapter :mongo_atomic, Mongo::Connection.new.db('adapter')['testing']
|
18
|
+
|
19
|
+
attribute :name, String
|
20
|
+
attribute :bio, String
|
21
|
+
end
|
22
|
+
|
23
|
+
user = User.create(:name => 'John', :bio => 'Awesome!')
|
24
|
+
puts user.name # John
|
25
|
+
puts user.bio # Awesome!
|
26
|
+
|
27
|
+
# simulate another process updating only bio
|
28
|
+
user.adapter.client.update({:_id => user.id}, '$set' => {:bio => "Changed!"})
|
29
|
+
|
30
|
+
user.name = 'Nunes'
|
31
|
+
user.save # save performs update with $set's rather than full doc save
|
32
|
+
|
33
|
+
user.reload
|
34
|
+
|
35
|
+
puts user.name # Nunes
|
36
|
+
puts user.bio # Changed!
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Toy
|
2
|
+
module Mongo
|
3
|
+
class IncompatibleAdapter < Error
|
4
|
+
def initialize(name)
|
5
|
+
super "In order to use partial updates, you need to be using the :mongo_atomic adapter, but you are using :#{name}"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module AtomicUpdates
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
# Very basic method for determining what has changed locally
|
13
|
+
# so we can just update changes instead of entire document
|
14
|
+
#
|
15
|
+
# Does not work with complex objects (array, hash, set, etc.)
|
16
|
+
# as it does not attempt to determine what has changed in them,
|
17
|
+
# just whether or not they have changed at all.
|
18
|
+
def persistable_changes
|
19
|
+
attrs = {}
|
20
|
+
pattrs = persisted_attributes
|
21
|
+
changed.each do |key|
|
22
|
+
attribute = self.class.attributes[key.to_s]
|
23
|
+
next if attribute.virtual?
|
24
|
+
attrs[attribute.persisted_name] = pattrs[attribute.persisted_name]
|
25
|
+
end
|
26
|
+
attrs
|
27
|
+
end
|
28
|
+
|
29
|
+
def persist
|
30
|
+
if new_record?
|
31
|
+
adapter.write id, persisted_attributes
|
32
|
+
else
|
33
|
+
updates = persistable_changes
|
34
|
+
if updates.present?
|
35
|
+
adapter.write id, updates
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
private :persist
|
40
|
+
|
41
|
+
def atomic_update(update, opts={})
|
42
|
+
options = {}
|
43
|
+
criteria = {:_id => id}
|
44
|
+
criteria.update(opts[:criteria]) if opts[:criteria]
|
45
|
+
options[:safe] = opts.key?(:safe) ? opts[:safe] : adapter.options[:safe]
|
46
|
+
|
47
|
+
run_callbacks(:save) do
|
48
|
+
run_callbacks(:update) do
|
49
|
+
adapter.client.update(criteria, update, options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/toy/mongo/querying.rb
CHANGED
@@ -42,7 +42,6 @@ module Toy
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def query
|
45
|
-
# TODO: add object id keys to convert
|
46
45
|
Plucky::Query.new(adapter.client, :transformer => transformer).object_ids(object_id_attributes)
|
47
46
|
end
|
48
47
|
|
@@ -52,52 +51,6 @@ module Toy
|
|
52
51
|
end
|
53
52
|
end
|
54
53
|
end
|
55
|
-
|
56
|
-
# Very basic method for determining what has changed locally
|
57
|
-
# so we can just update changes instead of entire document
|
58
|
-
#
|
59
|
-
# Does not work with complex objects (array, hash, set, etc.)
|
60
|
-
# as it does not attempt to determine what has changed in them,
|
61
|
-
# just whether or not they have changed at all.
|
62
|
-
def persistable_changes
|
63
|
-
attrs = {}
|
64
|
-
pattrs = persisted_attributes
|
65
|
-
changed.each do |key|
|
66
|
-
attribute = self.class.attributes[key.to_s]
|
67
|
-
next if attribute.virtual?
|
68
|
-
attrs[attribute.persisted_name] = pattrs[attribute.persisted_name]
|
69
|
-
end
|
70
|
-
attrs
|
71
|
-
end
|
72
|
-
|
73
|
-
def atomic_update_attributes(attrs={})
|
74
|
-
self.attributes = attrs
|
75
|
-
if valid?
|
76
|
-
run_callbacks(:save) do
|
77
|
-
run_callbacks(:update) do
|
78
|
-
criteria = {'_id' => id}
|
79
|
-
update = {'$set' => persistable_changes}
|
80
|
-
adapter.client.update(criteria, update, {:safe => adapter.options[:safe]})
|
81
|
-
true
|
82
|
-
end
|
83
|
-
end
|
84
|
-
else
|
85
|
-
false
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def atomic_update(update, opts={})
|
90
|
-
options = {}
|
91
|
-
criteria = {'_id' => id}
|
92
|
-
criteria.update(opts[:criteria]) if opts[:criteria]
|
93
|
-
options[:safe] = opts.key?(:safe) ? opts[:safe] : adapter.options[:safe]
|
94
|
-
|
95
|
-
run_callbacks(:save) do
|
96
|
-
run_callbacks(:update) do
|
97
|
-
adapter.client.update(criteria, update, options)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
54
|
end
|
102
55
|
end
|
103
56
|
end
|
data/lib/toy/mongo/version.rb
CHANGED
data/lib/toy/mongo.rb
CHANGED
@@ -3,17 +3,20 @@ require 'toy'
|
|
3
3
|
require 'toy/extensions/bson_object_id'
|
4
4
|
require 'toy/identity/object_id_key_factory'
|
5
5
|
require 'toy/mongo/querying'
|
6
|
+
require 'toy/mongo/atomic_updates'
|
6
7
|
require 'adapter/mongo'
|
7
8
|
|
8
9
|
module Toy
|
9
10
|
module Mongo
|
10
11
|
extend ActiveSupport::Concern
|
11
12
|
|
13
|
+
class Error < StandardError; end
|
14
|
+
|
12
15
|
included do
|
13
16
|
include Toy::Store
|
14
17
|
include Querying
|
15
18
|
|
16
|
-
key
|
19
|
+
key Toy::Identity::ObjectIdKeyFactory.new
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
data/spec/support/constants.rb
CHANGED
@@ -1,42 +1,44 @@
|
|
1
1
|
module Support
|
2
2
|
module Constants
|
3
|
-
|
4
|
-
base.extend(ClassMethods)
|
5
|
-
end
|
3
|
+
extend ActiveSupport::Concern
|
6
4
|
|
7
5
|
module ClassMethods
|
8
6
|
def uses_constants(*constants)
|
9
|
-
before { create_constants
|
7
|
+
before { create_constants *constants }
|
8
|
+
after { remove_constants *constants }
|
10
9
|
end
|
11
10
|
end
|
12
11
|
|
13
12
|
def create_constants(*constants)
|
14
|
-
constants.each { |constant| create_constant
|
13
|
+
constants.each { |constant| create_constant constant }
|
15
14
|
end
|
16
15
|
|
17
16
|
def remove_constants(*constants)
|
18
|
-
constants.each { |constant| remove_constant
|
17
|
+
constants.each { |constant| remove_constant constant }
|
19
18
|
end
|
20
19
|
|
21
|
-
def create_constant(constant)
|
22
|
-
|
23
|
-
Kernel.const_set(constant, Model(constant))
|
20
|
+
def create_constant(constant, superclass=nil)
|
21
|
+
Object.const_set constant, Model(superclass)
|
24
22
|
end
|
25
23
|
|
26
24
|
def remove_constant(constant)
|
27
|
-
|
25
|
+
if Object.const_defined?(constant)
|
26
|
+
Object.send :remove_const, constant
|
27
|
+
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def Model(
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
30
|
+
def Model(superclass=nil)
|
31
|
+
if superclass.nil?
|
32
|
+
Class.new {
|
33
|
+
include Toy::Mongo
|
34
|
+
adapter :mongo, STORE
|
35
|
+
}
|
36
|
+
else
|
37
|
+
Class.new(superclass) {
|
38
|
+
include Toy::Mongo
|
39
|
+
adapter :mongo, STORE
|
40
|
+
}
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
42
|
-
end
|
44
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Toy::Mongo::AtomicUpdates do
|
4
|
+
uses_constants 'User'
|
5
|
+
|
6
|
+
before do
|
7
|
+
User.send :include, Toy::Mongo::AtomicUpdates
|
8
|
+
User.adapter :mongo_atomic, STORE
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#persistable_changes" do
|
12
|
+
before(:each) do
|
13
|
+
User.attribute :name, String
|
14
|
+
User.attribute :bio, String
|
15
|
+
User.attribute(:password, String, :virtual => true)
|
16
|
+
User.attribute(:email, String, :abbr => :e)
|
17
|
+
@user = User.create(:name => 'John', :password => 'secret', :email => 'nunemaker@gmail.com')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns only changed attributes" do
|
21
|
+
@user.name = 'Frank'
|
22
|
+
@user.persistable_changes.should == {'name' => 'Frank'}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns typecast values" do
|
26
|
+
@user.name = 1234
|
27
|
+
@user.persistable_changes.should == {'name' => '1234'}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "ignores virtual attributes" do
|
31
|
+
@user.password = 'ignore me'
|
32
|
+
@user.persistable_changes.should be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it "uses abbreviated key" do
|
36
|
+
@user.email = 'john@orderedlist.com'
|
37
|
+
@user.persistable_changes.should == {'e' => 'john@orderedlist.com'}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#atomic_update" do
|
42
|
+
before(:each) do
|
43
|
+
User.send :include, CallbacksHelper
|
44
|
+
User.attribute :name, String
|
45
|
+
@user = User.create(:name => 'John')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "performs update" do
|
49
|
+
@user.atomic_update('$set' => {'name' => 'Frank'})
|
50
|
+
@user.reload
|
51
|
+
@user.name.should == 'Frank'
|
52
|
+
end
|
53
|
+
|
54
|
+
it "defaults to adapter's :safe option" do
|
55
|
+
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => nil)
|
56
|
+
@user.atomic_update('$set' => {'name' => 'Frank'})
|
57
|
+
|
58
|
+
User.adapter(:mongo, STORE, :safe => false)
|
59
|
+
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => false)
|
60
|
+
@user.atomic_update('$set' => {'name' => 'Frank'})
|
61
|
+
|
62
|
+
User.adapter(:mongo, STORE, :safe => true)
|
63
|
+
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => true)
|
64
|
+
@user.atomic_update('$set' => {'name' => 'Frank'})
|
65
|
+
end
|
66
|
+
|
67
|
+
it "runs callbacks in correct order" do
|
68
|
+
doc = User.create.tap(&:clear_history)
|
69
|
+
doc.atomic_update({})
|
70
|
+
doc.history.should == [:before_save, :before_update, :after_update, :after_save]
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with :safe option" do
|
74
|
+
it "overrides adapter's :safe option" do
|
75
|
+
User.adapter(:mongo, STORE, :safe => false)
|
76
|
+
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => true)
|
77
|
+
@user.atomic_update({'$set' => {'name' => 'Frank'}}, :safe => true)
|
78
|
+
|
79
|
+
User.adapter(:mongo, STORE, :safe => true)
|
80
|
+
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => false)
|
81
|
+
@user.atomic_update({'$set' => {'name' => 'Frank'}}, :safe => false)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "with :criteria option" do
|
86
|
+
uses_constants('Site')
|
87
|
+
|
88
|
+
it "allows updating embedded documents using $ positional operator" do
|
89
|
+
User.attribute(:sites, Array)
|
90
|
+
site1 = Site.create
|
91
|
+
site2 = Site.create
|
92
|
+
@user.update_attributes(:sites => [{'id' => site1.id, 'ui' => 1}, {'id' => site2.id, 'ui' => 2}])
|
93
|
+
|
94
|
+
@user.atomic_update(
|
95
|
+
{'$set' => {'sites.$.ui' => 2}},
|
96
|
+
{:criteria => {'sites.id' => site1.id}}
|
97
|
+
)
|
98
|
+
@user.reload
|
99
|
+
@user.sites.map { |s| s['ui'] }.should == [2, 2]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#persist" do
|
105
|
+
it "only persists changes" do
|
106
|
+
User.attribute :name, String
|
107
|
+
User.attribute :bio, String
|
108
|
+
|
109
|
+
user = User.create(:name => 'John', :bio => 'Awesome.')
|
110
|
+
user.name = 'Johnny'
|
111
|
+
|
112
|
+
# simulate outside change
|
113
|
+
user.adapter.client.update({:_id => user.id}, {'$set' => {:bio => 'Surprise!'}})
|
114
|
+
|
115
|
+
user.save
|
116
|
+
user.reload
|
117
|
+
|
118
|
+
user.name.should eq('Johnny')
|
119
|
+
user.bio.should eq('Surprise!')
|
120
|
+
end
|
121
|
+
|
122
|
+
it "persists default values that did not change" do
|
123
|
+
User.attribute :version, Integer, :default => 1
|
124
|
+
user = User.new
|
125
|
+
user.adapter.should_receive(:write).with(user.id, {
|
126
|
+
'version' => 1,
|
127
|
+
})
|
128
|
+
user.save
|
129
|
+
end
|
130
|
+
|
131
|
+
it "does not persist virtual attributes" do
|
132
|
+
User.attribute :name, String
|
133
|
+
User.attribute :password, String, :virtual => true
|
134
|
+
|
135
|
+
user = User.new(:name => 'John')
|
136
|
+
user.password = 'hacks'
|
137
|
+
user.adapter.should_receive(:write).with(user.id, {
|
138
|
+
'name' => 'John',
|
139
|
+
})
|
140
|
+
user.save
|
141
|
+
end
|
142
|
+
|
143
|
+
it "does persist new records even without changes" do
|
144
|
+
user = User.create
|
145
|
+
user.persisted?.should be_true
|
146
|
+
end
|
147
|
+
|
148
|
+
it "does not persist if there were no changes" do
|
149
|
+
user = User.create
|
150
|
+
user.adapter.should_not_receive(:write)
|
151
|
+
user.save
|
152
|
+
end
|
153
|
+
|
154
|
+
it "works with abbreviated attributes" do
|
155
|
+
User.attribute :email, String, :abbr => :e
|
156
|
+
user = User.new(:email => 'john@doe.com')
|
157
|
+
user.adapter.should_receive(:write).with(user.id, {
|
158
|
+
'e' => 'john@doe.com',
|
159
|
+
})
|
160
|
+
user.save
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
describe Toy::Mongo::Querying do
|
4
|
-
uses_constants
|
4
|
+
uses_constants 'User'
|
5
5
|
|
6
6
|
before(:each) do
|
7
|
-
User.
|
8
|
-
User.attribute
|
9
|
-
User.attribute(:bio, String)
|
7
|
+
User.attribute :name, String
|
8
|
+
User.attribute :bio, String
|
10
9
|
end
|
11
10
|
|
12
11
|
describe "#query" do
|
@@ -24,153 +23,6 @@ describe Toy::Mongo::Querying do
|
|
24
23
|
end
|
25
24
|
end
|
26
25
|
|
27
|
-
describe "#atomic_update" do
|
28
|
-
before(:each) do
|
29
|
-
@user = User.create(:name => 'John')
|
30
|
-
end
|
31
|
-
|
32
|
-
it "performs update" do
|
33
|
-
@user.atomic_update('$set' => {'name' => 'Frank'})
|
34
|
-
@user.reload
|
35
|
-
@user.name.should == 'Frank'
|
36
|
-
end
|
37
|
-
|
38
|
-
it "defaults to adapter's :safe option" do
|
39
|
-
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => nil)
|
40
|
-
@user.atomic_update('$set' => {'name' => 'Frank'})
|
41
|
-
|
42
|
-
User.adapter(:mongo, STORE, :safe => false)
|
43
|
-
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => false)
|
44
|
-
@user.atomic_update('$set' => {'name' => 'Frank'})
|
45
|
-
|
46
|
-
User.adapter(:mongo, STORE, :safe => true)
|
47
|
-
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => true)
|
48
|
-
@user.atomic_update('$set' => {'name' => 'Frank'})
|
49
|
-
end
|
50
|
-
|
51
|
-
it "runs callbacks in correct order" do
|
52
|
-
doc = User.create.tap(&:clear_history)
|
53
|
-
doc.atomic_update({})
|
54
|
-
doc.history.should == [:before_save, :before_update, :after_update, :after_save]
|
55
|
-
end
|
56
|
-
|
57
|
-
context "with :safe option" do
|
58
|
-
it "overrides adapter's :safe option" do
|
59
|
-
User.adapter(:mongo, STORE, :safe => false)
|
60
|
-
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => true)
|
61
|
-
@user.atomic_update({'$set' => {'name' => 'Frank'}}, :safe => true)
|
62
|
-
|
63
|
-
User.adapter(:mongo, STORE, :safe => true)
|
64
|
-
@user.adapter.client.should_receive(:update).with(kind_of(Hash), kind_of(Hash), :safe => false)
|
65
|
-
@user.atomic_update({'$set' => {'name' => 'Frank'}}, :safe => false)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context "with :criteria option" do
|
70
|
-
uses_constants('Site')
|
71
|
-
|
72
|
-
it "allows updating embedded documents using $ positional operator" do
|
73
|
-
User.attribute(:sites, Array)
|
74
|
-
site1 = Site.create
|
75
|
-
site2 = Site.create
|
76
|
-
@user.update_attributes(:sites => [{'id' => site1.id, 'ui' => 1}, {'id' => site2.id, 'ui' => 2}])
|
77
|
-
|
78
|
-
@user.atomic_update(
|
79
|
-
{'$set' => {'sites.$.ui' => 2}},
|
80
|
-
{:criteria => {'sites.id' => site1.id}}
|
81
|
-
)
|
82
|
-
@user.reload
|
83
|
-
@user.sites.map { |s| s['ui'] }.should == [2, 2]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
describe "#persistable_changes" do
|
89
|
-
before(:each) do
|
90
|
-
User.attribute(:password, String, :virtual => true)
|
91
|
-
User.attribute(:email, String, :abbr => :e)
|
92
|
-
@user = User.create(:name => 'John', :password => 'secret', :email => 'nunemaker@gmail.com')
|
93
|
-
end
|
94
|
-
|
95
|
-
it "returns only changed attributes" do
|
96
|
-
@user.name = 'Frank'
|
97
|
-
@user.persistable_changes.should == {'name' => 'Frank'}
|
98
|
-
end
|
99
|
-
|
100
|
-
it "returns typecast values" do
|
101
|
-
@user.name = 1234
|
102
|
-
@user.persistable_changes.should == {'name' => '1234'}
|
103
|
-
end
|
104
|
-
|
105
|
-
it "ignores virtual attributes" do
|
106
|
-
@user.password = 'ignore me'
|
107
|
-
@user.persistable_changes.should be_empty
|
108
|
-
end
|
109
|
-
|
110
|
-
it "uses abbreviated key" do
|
111
|
-
@user.email = 'john@orderedlist.com'
|
112
|
-
@user.persistable_changes.should == {'e' => 'john@orderedlist.com'}
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
describe "#atomic_update_attributes" do
|
117
|
-
before(:each) do
|
118
|
-
@user = User.create(:name => 'John', :bio => 'I make things simple.')
|
119
|
-
end
|
120
|
-
|
121
|
-
it "updates document" do
|
122
|
-
@user.atomic_update_attributes(:name => 'Frank')
|
123
|
-
@user.reload
|
124
|
-
@user.name.should == 'Frank'
|
125
|
-
end
|
126
|
-
|
127
|
-
it "returns true if valid" do
|
128
|
-
@user.atomic_update_attributes(:name => 'Frank').should be_true
|
129
|
-
end
|
130
|
-
|
131
|
-
it "returns false if invalid" do
|
132
|
-
User.validates_presence_of(:name)
|
133
|
-
@user.atomic_update_attributes(:name => '').should be_false
|
134
|
-
end
|
135
|
-
|
136
|
-
it "only persists changes" do
|
137
|
-
query = {'_id' => @user.id}
|
138
|
-
update = {'$set' => {'name' => 'Frank'}}
|
139
|
-
@user.adapter.client.should_receive(:update).with(query, update, {:safe => nil})
|
140
|
-
@user.atomic_update_attributes(:name => 'Frank')
|
141
|
-
end
|
142
|
-
|
143
|
-
it "persists changes with safe option from adapter" do
|
144
|
-
User.adapter(:mongo, STORE, :safe => true)
|
145
|
-
query = {'_id' => @user.id}
|
146
|
-
update = {'$set' => {'name' => 'Frank'}}
|
147
|
-
@user.adapter.client.should_receive(:update).with(query, update, {:safe => true})
|
148
|
-
@user.atomic_update_attributes(:name => 'Frank')
|
149
|
-
end
|
150
|
-
|
151
|
-
it "runs callbacks in correct order" do
|
152
|
-
doc = User.create.tap(&:clear_history)
|
153
|
-
doc.name = 'John Nunemaker'
|
154
|
-
doc.atomic_update_attributes
|
155
|
-
doc.history.should == [:before_save, :before_update, :after_update, :after_save]
|
156
|
-
end
|
157
|
-
|
158
|
-
it "persists changes that happen in callbacks" do
|
159
|
-
User.class_eval do
|
160
|
-
attribute :name_lower, String
|
161
|
-
|
162
|
-
before_save do |record|
|
163
|
-
record.name_lower = name
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
user = User.create(:name => 'Frank')
|
168
|
-
user.atomic_update_attributes('name' => 'John')
|
169
|
-
user.name_lower.should == 'John'
|
170
|
-
user.reload.name_lower.should == 'John'
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
26
|
describe "#get" do
|
175
27
|
before(:each) do
|
176
28
|
@user = User.create
|
data/toystore-mongo.gemspec
CHANGED
@@ -12,9 +12,9 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = %q{Mongo integration for Toystore}
|
13
13
|
s.description = %q{Mongo integration for Toystore}
|
14
14
|
|
15
|
-
s.add_dependency
|
16
|
-
s.add_dependency
|
17
|
-
s.add_dependency
|
15
|
+
s.add_dependency 'plucky', '~> 0.5'
|
16
|
+
s.add_dependency 'toystore', '~> 0.10'
|
17
|
+
s.add_dependency 'adapter-mongo', '~> 0.5.5'
|
18
18
|
|
19
19
|
s.files = `git ls-files`.split("\n") - ['specs.watchr']
|
20
20
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
metadata
CHANGED
@@ -1,56 +1,78 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: toystore-mongo
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 51
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 11
|
9
|
+
- 0
|
10
|
+
version: 0.11.0
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- John Nunemaker
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
requirement: &70141056408660 !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0.5'
|
17
|
+
|
18
|
+
date: 2012-05-20 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
22
21
|
type: :runtime
|
23
22
|
prerelease: false
|
24
|
-
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: toystore
|
27
|
-
requirement: &70141056407780 !ruby/object:Gem::Requirement
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
28
24
|
none: false
|
29
|
-
requirements:
|
25
|
+
requirements:
|
30
26
|
- - ~>
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 1
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 5
|
32
|
+
version: "0.5"
|
33
|
+
version_requirements: *id001
|
34
|
+
name: plucky
|
35
|
+
- !ruby/object:Gem::Dependency
|
33
36
|
type: :runtime
|
34
37
|
prerelease: false
|
35
|
-
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: adapter-mongo
|
38
|
-
requirement: &70141056406040 !ruby/object:Gem::Requirement
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
|
-
requirements:
|
40
|
+
requirements:
|
41
41
|
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 31
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
- 10
|
47
|
+
version: "0.10"
|
48
|
+
version_requirements: *id002
|
49
|
+
name: toystore
|
50
|
+
- !ruby/object:Gem::Dependency
|
44
51
|
type: :runtime
|
45
52
|
prerelease: false
|
46
|
-
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 1
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
- 5
|
62
|
+
- 5
|
63
|
+
version: 0.5.5
|
64
|
+
version_requirements: *id003
|
65
|
+
name: adapter-mongo
|
47
66
|
description: Mongo integration for Toystore
|
48
|
-
email:
|
67
|
+
email:
|
49
68
|
- nunemaker@gmail.com
|
50
69
|
executables: []
|
70
|
+
|
51
71
|
extensions: []
|
72
|
+
|
52
73
|
extra_rdoc_files: []
|
53
|
-
|
74
|
+
|
75
|
+
files:
|
54
76
|
- .gitignore
|
55
77
|
- .travis.yml
|
56
78
|
- Gemfile
|
@@ -58,9 +80,11 @@ files:
|
|
58
80
|
- LICENSE
|
59
81
|
- README.md
|
60
82
|
- Rakefile
|
83
|
+
- examples/atomic_updates.rb
|
61
84
|
- lib/toy/extensions/bson_object_id.rb
|
62
85
|
- lib/toy/identity/object_id_key_factory.rb
|
63
86
|
- lib/toy/mongo.rb
|
87
|
+
- lib/toy/mongo/atomic_updates.rb
|
64
88
|
- lib/toy/mongo/querying.rb
|
65
89
|
- lib/toy/mongo/version.rb
|
66
90
|
- lib/toystore-mongo.rb
|
@@ -70,45 +94,50 @@ files:
|
|
70
94
|
- spec/support/constants.rb
|
71
95
|
- spec/toy/extensions/bson_object_id_spec.rb
|
72
96
|
- spec/toy/identity/object_id_key_factory_spec.rb
|
97
|
+
- spec/toy/mongo/atomic_updates_spec.rb
|
73
98
|
- spec/toy/mongo/querying_spec.rb
|
74
99
|
- spec/toy/mongo_spec.rb
|
75
100
|
- toystore-mongo.gemspec
|
76
|
-
homepage:
|
101
|
+
homepage: ""
|
77
102
|
licenses: []
|
103
|
+
|
78
104
|
post_install_message:
|
79
105
|
rdoc_options: []
|
80
|
-
|
106
|
+
|
107
|
+
require_paths:
|
81
108
|
- lib
|
82
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
110
|
none: false
|
84
|
-
requirements:
|
85
|
-
- -
|
86
|
-
- !ruby/object:Gem::Version
|
87
|
-
|
88
|
-
segments:
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
hash: 3
|
115
|
+
segments:
|
89
116
|
- 0
|
90
|
-
|
91
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
version: "0"
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
119
|
none: false
|
93
|
-
requirements:
|
94
|
-
- -
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
|
97
|
-
segments:
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
124
|
+
segments:
|
98
125
|
- 0
|
99
|
-
|
126
|
+
version: "0"
|
100
127
|
requirements: []
|
128
|
+
|
101
129
|
rubyforge_project:
|
102
130
|
rubygems_version: 1.8.10
|
103
131
|
signing_key:
|
104
132
|
specification_version: 3
|
105
133
|
summary: Mongo integration for Toystore
|
106
|
-
test_files:
|
134
|
+
test_files:
|
107
135
|
- spec/helper.rb
|
108
136
|
- spec/spec.opts
|
109
137
|
- spec/support/callbacks_helper.rb
|
110
138
|
- spec/support/constants.rb
|
111
139
|
- spec/toy/extensions/bson_object_id_spec.rb
|
112
140
|
- spec/toy/identity/object_id_key_factory_spec.rb
|
141
|
+
- spec/toy/mongo/atomic_updates_spec.rb
|
113
142
|
- spec/toy/mongo/querying_spec.rb
|
114
143
|
- spec/toy/mongo_spec.rb
|