dynamoid 0.6.1 → 0.7.0
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.
- data/.travis.yml +4 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +40 -45
- data/README.markdown +55 -25
- data/Rakefile +31 -0
- data/VERSION +1 -1
- data/doc/Dynamoid.html +58 -42
- data/doc/Dynamoid/Adapter.html +666 -179
- data/doc/Dynamoid/Adapter/AwsSdk.html +752 -236
- data/doc/Dynamoid/Associations.html +28 -21
- data/doc/Dynamoid/Associations/Association.html +102 -49
- data/doc/Dynamoid/Associations/BelongsTo.html +28 -25
- data/doc/Dynamoid/Associations/ClassMethods.html +95 -52
- data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +28 -25
- data/doc/Dynamoid/Associations/HasMany.html +28 -25
- data/doc/Dynamoid/Associations/HasOne.html +28 -25
- data/doc/Dynamoid/Associations/ManyAssociation.html +138 -94
- data/doc/Dynamoid/Associations/SingleAssociation.html +67 -38
- data/doc/Dynamoid/Components.html +60 -22
- data/doc/Dynamoid/Config.html +61 -44
- data/doc/Dynamoid/Config/Options.html +90 -61
- data/doc/Dynamoid/Criteria.html +28 -21
- data/doc/Dynamoid/Criteria/Chain.html +508 -100
- data/doc/Dynamoid/Criteria/ClassMethods.html +26 -19
- data/doc/Dynamoid/Dirty.html +424 -0
- data/doc/Dynamoid/Dirty/ClassMethods.html +174 -0
- data/doc/Dynamoid/Document.html +451 -84
- data/doc/Dynamoid/Document/ClassMethods.html +281 -102
- data/doc/Dynamoid/Errors.html +29 -22
- data/doc/Dynamoid/Errors/ConditionalCheckFailedException.html +141 -0
- data/doc/Dynamoid/Errors/DocumentNotValid.html +36 -25
- data/doc/Dynamoid/Errors/Error.html +27 -20
- data/doc/Dynamoid/Errors/InvalidField.html +27 -19
- data/doc/Dynamoid/Errors/InvalidQuery.html +131 -0
- data/doc/Dynamoid/Errors/MissingRangeKey.html +27 -19
- data/doc/Dynamoid/Fields.html +94 -77
- data/doc/Dynamoid/Fields/ClassMethods.html +166 -37
- data/doc/Dynamoid/Finders.html +28 -21
- data/doc/Dynamoid/Finders/ClassMethods.html +505 -78
- data/doc/Dynamoid/IdentityMap.html +492 -0
- data/doc/Dynamoid/IdentityMap/ClassMethods.html +534 -0
- data/doc/Dynamoid/Indexes.html +41 -28
- data/doc/Dynamoid/Indexes/ClassMethods.html +45 -29
- data/doc/Dynamoid/Indexes/Index.html +100 -62
- data/doc/Dynamoid/Middleware.html +115 -0
- data/doc/Dynamoid/Middleware/IdentityMap.html +264 -0
- data/doc/Dynamoid/Persistence.html +326 -85
- data/doc/Dynamoid/Persistence/ClassMethods.html +275 -109
- data/doc/Dynamoid/Validations.html +47 -31
- data/doc/_index.html +116 -71
- data/doc/class_list.html +13 -7
- data/doc/css/full_list.css +4 -2
- data/doc/css/style.css +60 -44
- data/doc/file.LICENSE.html +26 -19
- data/doc/file.README.html +152 -48
- data/doc/file_list.html +14 -8
- data/doc/frames.html +20 -5
- data/doc/index.html +152 -48
- data/doc/js/app.js +52 -43
- data/doc/js/full_list.js +14 -9
- data/doc/js/jquery.js +4 -16
- data/doc/method_list.html +446 -540
- data/doc/top-level-namespace.html +27 -20
- data/{Dynamoid.gemspec → dynamoid.gemspec} +21 -8
- data/lib/dynamoid/adapter.rb +11 -10
- data/lib/dynamoid/adapter/aws_sdk.rb +40 -19
- data/lib/dynamoid/components.rb +2 -1
- data/lib/dynamoid/criteria/chain.rb +29 -11
- data/lib/dynamoid/dirty.rb +6 -0
- data/lib/dynamoid/document.rb +34 -19
- data/lib/dynamoid/fields.rb +36 -30
- data/lib/dynamoid/finders.rb +7 -5
- data/lib/dynamoid/persistence.rb +37 -10
- data/spec/app/models/address.rb +2 -0
- data/spec/app/models/camel_case.rb +10 -0
- data/spec/app/models/car.rb +6 -0
- data/spec/app/models/nuclear_submarine.rb +5 -0
- data/spec/app/models/subscription.rb +2 -2
- data/spec/app/models/vehicle.rb +7 -0
- data/spec/dynamoid/adapter/aws_sdk_spec.rb +20 -11
- data/spec/dynamoid/adapter_spec.rb +67 -82
- data/spec/dynamoid/associations/association_spec.rb +30 -30
- data/spec/dynamoid/criteria/chain_spec.rb +56 -9
- data/spec/dynamoid/criteria_spec.rb +3 -0
- data/spec/dynamoid/dirty_spec.rb +8 -0
- data/spec/dynamoid/document_spec.rb +109 -47
- data/spec/dynamoid/fields_spec.rb +32 -3
- data/spec/dynamoid/finders_spec.rb +12 -0
- data/spec/dynamoid/persistence_spec.rb +73 -8
- data/spec/spec_helper.rb +1 -0
- data/spec/support/with_partitioning.rb +15 -0
- metadata +22 -9
data/spec/dynamoid/dirty_spec.rb
CHANGED
|
@@ -22,6 +22,14 @@ describe 'Dynamoid::Dirty' do
|
|
|
22
22
|
tweet.reload
|
|
23
23
|
tweet.changed?.should be_false
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
it 'should be empty after an update' do
|
|
27
|
+
tweet = Tweet.create!(:tweet_id => "1", :group => 'abc')
|
|
28
|
+
tweet.update! do |t|
|
|
29
|
+
t.set(msg: "foo")
|
|
30
|
+
end
|
|
31
|
+
tweet.changed?.should be_false
|
|
32
|
+
end
|
|
25
33
|
|
|
26
34
|
it 'track changes after saves' do
|
|
27
35
|
tweet = Tweet.new(:tweet_id => "1", :group => 'abc')
|
|
@@ -4,9 +4,9 @@ describe "Dynamoid::Document" do
|
|
|
4
4
|
|
|
5
5
|
it 'initializes a new document' do
|
|
6
6
|
@address = Address.new
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
@address.new_record.should be_true
|
|
9
|
-
@address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>nil, :options=>nil, :deliverable => nil}
|
|
9
|
+
@address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>nil, :options=>nil, :deliverable => nil, :lock_version => nil}
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
it 'responds to will_change! methods for all fields' do
|
|
@@ -16,21 +16,21 @@ describe "Dynamoid::Document" do
|
|
|
16
16
|
@address.should respond_to(:created_at_will_change!)
|
|
17
17
|
@address.should respond_to(:updated_at_will_change!)
|
|
18
18
|
end
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
it 'initializes a new document with attributes' do
|
|
21
21
|
@address = Address.new(:city => 'Chicago')
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
@address.new_record.should be_true
|
|
24
|
-
|
|
25
|
-
@address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>"Chicago", :options=>nil, :deliverable => nil}
|
|
24
|
+
|
|
25
|
+
@address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>"Chicago", :options=>nil, :deliverable => nil, :lock_version => nil}
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
it 'initializes a new document with a virtual attribute' do
|
|
29
29
|
@address = Address.new(:zip_code => '12345')
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
@address.new_record.should be_true
|
|
32
|
-
|
|
33
|
-
@address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>"Chicago", :options=>nil, :deliverable => nil}
|
|
32
|
+
|
|
33
|
+
@address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>"Chicago", :options=>nil, :deliverable => nil, :lock_version => nil}
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
it 'allows interception of write_attribute on load' do
|
|
@@ -41,10 +41,10 @@ describe "Dynamoid::Document" do
|
|
|
41
41
|
end
|
|
42
42
|
Model.new(:city => "Chicago").city.should == "chicago"
|
|
43
43
|
end
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
it 'creates a new document' do
|
|
46
46
|
@address = Address.create(:city => 'Chicago')
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
@address.new_record.should be_false
|
|
49
49
|
@address.id.should_not be_nil
|
|
50
50
|
end
|
|
@@ -53,61 +53,54 @@ describe "Dynamoid::Document" do
|
|
|
53
53
|
@address = Address.create(:city => 'Chicago')
|
|
54
54
|
Address.exists?(@address.id).should be_true
|
|
55
55
|
Address.exists?("does-not-exist").should be_false
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
it 'tests equivalency with itself' do
|
|
59
|
-
@address = Address.create(:city => 'Chicago')
|
|
60
|
-
|
|
61
|
-
@address.should == @address
|
|
56
|
+
Address.exists?(:city => @address.city).should be_true
|
|
57
|
+
Address.exists?(:city => "does-not-exist").should be_false
|
|
62
58
|
end
|
|
63
59
|
|
|
64
|
-
it 'is not equivalent to another document' do
|
|
65
|
-
@address.should_not == Address.create
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it 'is not equivalent to another object' do
|
|
69
|
-
@address = Address.create(:city => 'Chicago')
|
|
70
|
-
@address.should_not == "test"
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
it "isn't equal to nil" do
|
|
74
|
-
@address = Address.create(:city => 'Chicago')
|
|
75
|
-
@address.should_not == nil
|
|
76
|
-
end
|
|
77
|
-
|
|
78
60
|
it 'gets errors courtesy of ActiveModel' do
|
|
79
61
|
@address = Address.create(:city => 'Chicago')
|
|
80
|
-
|
|
62
|
+
|
|
81
63
|
@address.errors.should be_empty
|
|
82
64
|
@address.errors.full_messages.should be_empty
|
|
83
65
|
end
|
|
84
|
-
|
|
85
|
-
it 'reloads itself and sees persisted changes' do
|
|
86
|
-
@address = Address.create
|
|
87
|
-
|
|
88
|
-
Address.first.update_attributes(:city => 'Chicago')
|
|
89
|
-
|
|
90
|
-
@address.reload.city.should == 'Chicago'
|
|
91
|
-
end
|
|
92
66
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
67
|
+
context '.reload' do
|
|
68
|
+
let(:address){ Address.create }
|
|
69
|
+
let(:message){ Message.create({:text => 'Nice, supporting datetime range!', :time => Time.now.to_datetime}) }
|
|
70
|
+
let(:tweet){ tweet = Tweet.create(:tweet_id => 'x', :group => 'abc') }
|
|
71
|
+
|
|
72
|
+
it 'reflects persisted changes' do
|
|
73
|
+
address.update_attributes(:city => 'Chicago')
|
|
74
|
+
address.reload.city.should == 'Chicago'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'uses a :consistent_read' do
|
|
78
|
+
Tweet.expects(:find).with(tweet.hash_key, {:range_key => tweet.range_value, :consistent_read => true}).returns(tweet)
|
|
79
|
+
tweet.reload
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'works with range key' do
|
|
83
|
+
tweet.reload.group.should == 'abc'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'works with a :datetime range key' do
|
|
87
|
+
expect { message.reload }.to_not raise_error(ArgumentError)
|
|
88
|
+
end
|
|
96
89
|
end
|
|
97
|
-
|
|
90
|
+
|
|
98
91
|
it 'has default table options' do
|
|
99
92
|
@address = Address.create
|
|
100
|
-
|
|
93
|
+
|
|
101
94
|
@address.id.should_not be_nil
|
|
102
95
|
Address.table_name.should == 'dynamoid_tests_addresses'
|
|
103
96
|
Address.hash_key.should == :id
|
|
104
97
|
Address.read_capacity.should == 100
|
|
105
98
|
Address.write_capacity.should == 20
|
|
106
99
|
end
|
|
107
|
-
|
|
100
|
+
|
|
108
101
|
it 'follows any table options provided to it' do
|
|
109
102
|
@tweet = Tweet.create(:group => 12345)
|
|
110
|
-
|
|
103
|
+
|
|
111
104
|
lambda {@tweet.id}.should raise_error(NoMethodError)
|
|
112
105
|
@tweet.tweet_id.should_not be_nil
|
|
113
106
|
Tweet.table_name.should == 'dynamoid_tests_twitters'
|
|
@@ -115,4 +108,73 @@ describe "Dynamoid::Document" do
|
|
|
115
108
|
Tweet.read_capacity.should == 200
|
|
116
109
|
Tweet.write_capacity.should == 200
|
|
117
110
|
end
|
|
111
|
+
|
|
112
|
+
shared_examples 'it has equality testing and hashing' do
|
|
113
|
+
it 'is equal to itself' do
|
|
114
|
+
document.should == document
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'is equal to another document with the same key(s)' do
|
|
118
|
+
document.should == same
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'is not equal to another document with different key(s)' do
|
|
122
|
+
document.should_not == different
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'is not equal to an object that is not a document' do
|
|
126
|
+
document.should_not == 'test'
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'is not equal to nil' do
|
|
130
|
+
document.should_not == nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'hashes documents with the keys to the same value' do
|
|
134
|
+
{document => 1}.should have_key(same)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context 'without a range key' do
|
|
139
|
+
it_behaves_like 'it has equality testing and hashing' do
|
|
140
|
+
let(:document) { Address.create(id: 123, city: 'Seattle') }
|
|
141
|
+
let(:different) { Address.create(id: 456, city: 'Seattle') }
|
|
142
|
+
let(:same) { Address.new(id: 123, city: 'Boston') }
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
context 'with a range key' do
|
|
147
|
+
it_behaves_like 'it has equality testing and hashing' do
|
|
148
|
+
let(:document){ Tweet.create(:tweet_id => 'x', :group => 'abc', :msg => 'foo') }
|
|
149
|
+
let(:different) { Tweet.create(:tweet_id => 'y', :group => 'abc', :msg => 'foo') }
|
|
150
|
+
let(:same) { Tweet.new(:tweet_id => 'x', :group => 'abc', :msg => 'bar') }
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'is not equal to another document with the same hash key but a different range value' do
|
|
154
|
+
document = Tweet.create(:tweet_id => 'x', :group => 'abc')
|
|
155
|
+
different = Tweet.create(:tweet_id => 'x', :group => 'xyz')
|
|
156
|
+
|
|
157
|
+
document.should_not == different
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
context 'single table inheritance' do
|
|
162
|
+
it "should have a type" do
|
|
163
|
+
Vehicle.new.type.should == "Vehicle"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "reports the same table name for both base and derived classes" do
|
|
167
|
+
Vehicle.table_name.should == Car.table_name
|
|
168
|
+
Vehicle.table_name.should == NuclearSubmarine.table_name
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
context '#count' do
|
|
173
|
+
it 'returns the number of documents in the table' do
|
|
174
|
+
document = Tweet.create(:tweet_id => 'x', :group => 'abc')
|
|
175
|
+
different = Tweet.create(:tweet_id => 'x', :group => 'xyz')
|
|
176
|
+
|
|
177
|
+
Tweet.count.should == 2
|
|
178
|
+
end
|
|
179
|
+
end
|
|
118
180
|
end
|
|
@@ -36,13 +36,13 @@ describe "Dynamoid::Fields" do
|
|
|
36
36
|
@address.updated_at.should_not be_nil
|
|
37
37
|
@address.updated_at.class.should == DateTime
|
|
38
38
|
end
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
context 'with a saved address' do
|
|
41
41
|
before do
|
|
42
42
|
@address = Address.create(:deliverable => true)
|
|
43
43
|
@original_id = @address.id
|
|
44
44
|
end
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
it 'should write an attribute correctly' do
|
|
47
47
|
@address.write_attribute(:city, 'Chicago')
|
|
48
48
|
end
|
|
@@ -75,6 +75,12 @@ describe "Dynamoid::Fields" do
|
|
|
75
75
|
@address.id.should == @original_id
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
it 'should update only created_at when no params are passed' do
|
|
79
|
+
@initial_updated_at = @address.updated_at
|
|
80
|
+
@address.update_attributes([])
|
|
81
|
+
@address.updated_at.should_not == @initial_updated_at
|
|
82
|
+
end
|
|
83
|
+
|
|
78
84
|
it 'adds in dirty methods for attributes' do
|
|
79
85
|
@address.city = 'Chicago'
|
|
80
86
|
@address.save
|
|
@@ -85,7 +91,7 @@ describe "Dynamoid::Fields" do
|
|
|
85
91
|
end
|
|
86
92
|
|
|
87
93
|
it 'returns all attributes' do
|
|
88
|
-
Address.attributes.should == {:id=>{:type=>:string}, :created_at=>{:type=>:datetime}, :updated_at=>{:type=>:datetime}, :city=>{:type=>:string}, :options=>{:type=>:serialized}, :deliverable => {:type => :boolean}}
|
|
94
|
+
Address.attributes.should == {:id=>{:type=>:string}, :created_at=>{:type=>:datetime}, :updated_at=>{:type=>:datetime}, :city=>{:type=>:string}, :options=>{:type=>:serialized}, :deliverable => {:type => :boolean}, :lock_version => {:type => :integer}}
|
|
89
95
|
end
|
|
90
96
|
end
|
|
91
97
|
|
|
@@ -94,6 +100,18 @@ describe "Dynamoid::Fields" do
|
|
|
94
100
|
Address.new city: ("Ten chars " * 6_600)
|
|
95
101
|
end
|
|
96
102
|
|
|
103
|
+
context '.remove_attribute' do
|
|
104
|
+
subject { @address }
|
|
105
|
+
before(:each) do
|
|
106
|
+
Address.field :foobar
|
|
107
|
+
Address.remove_field :foobar
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it('should not be in the attributes hash') { Address.attributes.should_not have_key(:foobar) }
|
|
111
|
+
it('removes the accessor') { should_not respond_to(:foobar) }
|
|
112
|
+
it('removes the writer') { should_not respond_to(:foobar=) }
|
|
113
|
+
it('removes the interrogative') { should_not respond_to(:foobar?) }
|
|
114
|
+
end
|
|
97
115
|
|
|
98
116
|
context 'default values for fields' do
|
|
99
117
|
before do
|
|
@@ -124,4 +142,15 @@ describe "Dynamoid::Fields" do
|
|
|
124
142
|
end
|
|
125
143
|
end
|
|
126
144
|
|
|
145
|
+
context 'single table inheritance' do
|
|
146
|
+
it "has only base class fields on the base class" do
|
|
147
|
+
Vehicle.attributes.keys.to_set.should == Set.new([:type, :description, :created_at, :updated_at, :id])
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "has only the base and derived fields on a sub-class" do
|
|
151
|
+
#Only NuclearSubmarines have torpedoes
|
|
152
|
+
Car.attributes.should_not have_key(:torpedoes)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
127
156
|
end
|
|
@@ -118,6 +118,7 @@ describe "Dynamoid::Finders" do
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
context 'find_all' do
|
|
121
|
+
|
|
121
122
|
it 'should return a array of users' do
|
|
122
123
|
users = (1..10).map { User.create }
|
|
123
124
|
User.find_all(users.map(&:id)).should eq(users)
|
|
@@ -131,5 +132,16 @@ describe "Dynamoid::Finders" do
|
|
|
131
132
|
it 'should return an empty array' do
|
|
132
133
|
User.find_all([]).should eq([])
|
|
133
134
|
end
|
|
135
|
+
|
|
136
|
+
it 'returns empty array when there are no results' do
|
|
137
|
+
Address.find_all('bad' + @address.id.to_s).should eq []
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'passes options to the adapter' do
|
|
141
|
+
user_ids = [%w(1 red), %w(1 green)]
|
|
142
|
+
Dynamoid::Adapter.expects(:read).with(anything, user_ids, :consistent_read => true)
|
|
143
|
+
User.find_all(user_ids, :consistent_read => true)
|
|
144
|
+
end
|
|
145
|
+
|
|
134
146
|
end
|
|
135
147
|
end
|
|
@@ -33,7 +33,25 @@ describe "Dynamoid::Persistence" do
|
|
|
33
33
|
|
|
34
34
|
Dynamoid::Adapter.read("dynamoid_tests_addresses", @address.id)[:id].should == @address.id
|
|
35
35
|
end
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
it 'prevents concurrent writes to tables with a lock_version' do
|
|
38
|
+
@address.save!
|
|
39
|
+
a1 = @address
|
|
40
|
+
a2 = Address.find(@address.id)
|
|
41
|
+
|
|
42
|
+
a1.city = 'Seattle'
|
|
43
|
+
a2.city = 'San Francisco'
|
|
44
|
+
|
|
45
|
+
a1.save!
|
|
46
|
+
expect { a2.save! }.to raise_exception(Dynamoid::Errors::ConditionalCheckFailedException)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
configured_with 'partitioning' do
|
|
50
|
+
it 'raises an error when attempting to use optimistic locking' do
|
|
51
|
+
expect { address.save! }.to raise_exception
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
37
55
|
it 'assigns itself an id on save only if it does not have one' do
|
|
38
56
|
@address.id = 'test123'
|
|
39
57
|
@address.save
|
|
@@ -89,14 +107,14 @@ describe "Dynamoid::Persistence" do
|
|
|
89
107
|
@address.options = (hash = {:x => [1, 2], "foobar" => 3.14})
|
|
90
108
|
Address.undump(@address.send(:dump))[:options].should == hash
|
|
91
109
|
end
|
|
92
|
-
|
|
110
|
+
|
|
93
111
|
[true, false].each do |bool|
|
|
94
112
|
it "dumps a #{bool} boolean field" do
|
|
95
113
|
@address.deliverable = bool
|
|
96
114
|
Address.undump(@address.send(:dump))[:deliverable].should == bool
|
|
97
115
|
end
|
|
98
116
|
end
|
|
99
|
-
|
|
117
|
+
|
|
100
118
|
it 'raises on an invalid boolean value' do
|
|
101
119
|
expect do
|
|
102
120
|
@address.deliverable = true
|
|
@@ -142,7 +160,7 @@ describe "Dynamoid::Persistence" do
|
|
|
142
160
|
|
|
143
161
|
lambda {Address.create(hash)}.should_not raise_error
|
|
144
162
|
end
|
|
145
|
-
|
|
163
|
+
|
|
146
164
|
context 'create' do
|
|
147
165
|
{
|
|
148
166
|
Tweet => ['with range', { :tweet_id => 1, :group => 'abc' }],
|
|
@@ -151,11 +169,11 @@ describe "Dynamoid::Persistence" do
|
|
|
151
169
|
it "checks for existence of an existing object #{fields[0]}" do
|
|
152
170
|
t1 = clazz.new(fields[1])
|
|
153
171
|
t2 = clazz.new(fields[1])
|
|
154
|
-
|
|
172
|
+
|
|
155
173
|
t1.save
|
|
156
174
|
expect do
|
|
157
175
|
t2.save!
|
|
158
|
-
end.to raise_exception
|
|
176
|
+
end.to raise_exception Dynamoid::Errors::ConditionalCheckFailedException
|
|
159
177
|
end
|
|
160
178
|
end
|
|
161
179
|
end
|
|
@@ -177,7 +195,7 @@ describe "Dynamoid::Persistence" do
|
|
|
177
195
|
clazz.new(:deliverable => true) #undump is called here
|
|
178
196
|
end.to raise_error(ArgumentError)
|
|
179
197
|
end
|
|
180
|
-
|
|
198
|
+
|
|
181
199
|
it 'raises when dumping a column with an unknown field type' do
|
|
182
200
|
doc = clazz.new
|
|
183
201
|
doc.deliverable = true
|
|
@@ -186,13 +204,29 @@ describe "Dynamoid::Persistence" do
|
|
|
186
204
|
end.to raise_error(ArgumentError)
|
|
187
205
|
end
|
|
188
206
|
end
|
|
189
|
-
|
|
207
|
+
|
|
190
208
|
context 'update' do
|
|
191
209
|
|
|
192
210
|
before :each do
|
|
193
211
|
@tweet = Tweet.create(:tweet_id => 1, :group => 'abc', :count => 5, :tags => ['db', 'sql'], :user_name => 'john')
|
|
194
212
|
end
|
|
195
213
|
|
|
214
|
+
it 'runs before_update callbacks when doing #update' do
|
|
215
|
+
CamelCase.any_instance.expects(:doing_before_update).once.returns(true)
|
|
216
|
+
|
|
217
|
+
CamelCase.create(:color => 'blue').update do |t|
|
|
218
|
+
t.set(:color => 'red')
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'runs after_update callbacks when doing #update' do
|
|
223
|
+
CamelCase.any_instance.expects(:doing_after_update).once.returns(true)
|
|
224
|
+
|
|
225
|
+
CamelCase.create(:color => 'blue').update do |t|
|
|
226
|
+
t.set(:color => 'red')
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
196
230
|
it 'support add/delete operation on a field' do
|
|
197
231
|
@tweet.update do |t|
|
|
198
232
|
t.add(:count => 3)
|
|
@@ -222,6 +256,17 @@ describe "Dynamoid::Persistence" do
|
|
|
222
256
|
end
|
|
223
257
|
}.to raise_error(Dynamoid::Errors::ConditionalCheckFailedException)
|
|
224
258
|
end
|
|
259
|
+
|
|
260
|
+
it 'prevents concurrent saves to tables with a lock_version' do
|
|
261
|
+
@address.save!
|
|
262
|
+
a2 = Address.find(@address.id)
|
|
263
|
+
a2.update! { |a| a.set(:city => "Chicago") }
|
|
264
|
+
|
|
265
|
+
expect do
|
|
266
|
+
@address.city = "Seattle"
|
|
267
|
+
@address.save!
|
|
268
|
+
end.to raise_error(Dynamoid::Errors::ConditionalCheckFailedException)
|
|
269
|
+
end
|
|
225
270
|
|
|
226
271
|
end
|
|
227
272
|
|
|
@@ -233,4 +278,24 @@ describe "Dynamoid::Persistence" do
|
|
|
233
278
|
}.should_not raise_error
|
|
234
279
|
end
|
|
235
280
|
end
|
|
281
|
+
|
|
282
|
+
context 'single table inheritance' do
|
|
283
|
+
let(:car) { Car.create(power_locks: false) }
|
|
284
|
+
let(:sub) { NuclearSubmarine.create(torpedoes: 5) }
|
|
285
|
+
|
|
286
|
+
it 'saves subclass objects in the parent table' do
|
|
287
|
+
c = car
|
|
288
|
+
Vehicle.find(c.id).should == c
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it 'loads subclass item when querying the parent table' do
|
|
292
|
+
c = car
|
|
293
|
+
s = sub
|
|
294
|
+
|
|
295
|
+
Vehicle.all.to_a.tap { |v|
|
|
296
|
+
v.should include(c)
|
|
297
|
+
v.should include(s)
|
|
298
|
+
}
|
|
299
|
+
end
|
|
300
|
+
end
|
|
236
301
|
end
|