dynamoid 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Dynamoid.gemspec +18 -8
- data/Gemfile +3 -1
- data/Gemfile.lock +48 -24
- data/README.markdown +6 -3
- data/VERSION +1 -1
- data/lib/dynamoid.rb +4 -0
- data/lib/dynamoid/adapter.rb +3 -3
- data/lib/dynamoid/adapter/aws_sdk.rb +19 -13
- data/lib/dynamoid/components.rb +5 -4
- data/lib/dynamoid/config.rb +7 -4
- data/lib/dynamoid/criteria/chain.rb +22 -13
- data/lib/dynamoid/dirty.rb +41 -0
- data/lib/dynamoid/document.rb +55 -26
- data/lib/dynamoid/errors.rb +5 -0
- data/lib/dynamoid/fields.rb +0 -5
- data/lib/dynamoid/finders.rb +60 -12
- data/lib/dynamoid/identity_map.rb +96 -0
- data/lib/dynamoid/indexes/index.rb +1 -1
- data/lib/dynamoid/middleware/identity_map.rb +16 -0
- data/lib/dynamoid/persistence.rb +45 -6
- data/spec/app/models/message.rb +9 -0
- data/spec/app/models/tweet.rb +3 -0
- data/spec/dynamoid/adapter/aws_sdk_spec.rb +34 -34
- data/spec/dynamoid/criteria/chain_spec.rb +17 -9
- data/spec/dynamoid/criteria_spec.rb +8 -2
- data/spec/dynamoid/dirty_spec.rb +49 -0
- data/spec/dynamoid/document_spec.rb +5 -1
- data/spec/dynamoid/fields_spec.rb +47 -17
- data/spec/dynamoid/finders_spec.rb +27 -11
- data/spec/dynamoid/identity_map_spec.rb +45 -0
- data/spec/dynamoid/indexes/index_spec.rb +0 -2
- data/spec/dynamoid/persistence_spec.rb +65 -29
- data/spec/dynamoid_spec.rb +4 -0
- data/spec/spec_helper.rb +21 -25
- metadata +54 -18
- data/lib/dynamoid/adapter/local.rb +0 -196
- data/spec/dynamoid/adapter/local_spec.rb +0 -242
@@ -48,7 +48,7 @@ module Dynamoid #:nodoc:
|
|
48
48
|
#
|
49
49
|
# @since 0.2.0
|
50
50
|
def table_name
|
51
|
-
"#{Dynamoid::Config.namespace}_index_#{
|
51
|
+
"#{Dynamoid::Config.namespace}_index_" + source.table_name.sub("#{Dynamoid::Config.namespace}_", '').singularize + "_#{name.collect(&:to_s).collect(&:pluralize).join('_and_')}"
|
52
52
|
end
|
53
53
|
|
54
54
|
# Given either an object or a list of attributes, generate a hash key and a range key for the index. Optionally pass in
|
data/lib/dynamoid/persistence.rb
CHANGED
@@ -32,8 +32,7 @@ module Dynamoid
|
|
32
32
|
# @since 0.4.0
|
33
33
|
def create_table(options = {})
|
34
34
|
if self.range_key
|
35
|
-
|
36
|
-
range_key_hash = { range_key => range_key_type}
|
35
|
+
range_key_hash = { range_key => dynamo_type(attributes[range_key][:type]) }
|
37
36
|
else
|
38
37
|
range_key_hash = nil
|
39
38
|
end
|
@@ -57,6 +56,10 @@ module Dynamoid
|
|
57
56
|
Dynamoid::Adapter.tables.include?(table_name)
|
58
57
|
end
|
59
58
|
|
59
|
+
def from_database(attrs = {})
|
60
|
+
new(attrs).tap { |r| r.new_record = false }
|
61
|
+
end
|
62
|
+
|
60
63
|
# Undump an object into a hash, converting each type from a string representation of itself into the type specified by the field.
|
61
64
|
#
|
62
65
|
# @since 0.2.0
|
@@ -75,7 +78,11 @@ module Dynamoid
|
|
75
78
|
#
|
76
79
|
# @since 0.2.0
|
77
80
|
def undump_field(value, options)
|
78
|
-
|
81
|
+
if value.nil? && (default_value = options[:default])
|
82
|
+
value = default_value.respond_to?(:call) ? default_value.call : default_value
|
83
|
+
else
|
84
|
+
return if value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
85
|
+
end
|
79
86
|
|
80
87
|
case options[:type]
|
81
88
|
when :string
|
@@ -105,6 +112,26 @@ module Dynamoid
|
|
105
112
|
end
|
106
113
|
end
|
107
114
|
|
115
|
+
def dynamo_type(type)
|
116
|
+
case type
|
117
|
+
when :integer, :float, :datetime
|
118
|
+
:number
|
119
|
+
when :string, :serialized
|
120
|
+
:string
|
121
|
+
else
|
122
|
+
raise 'unknown type'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
# Set updated_at and any passed in field to current DateTime. Useful for things like last_login_at, etc.
|
129
|
+
#
|
130
|
+
def touch(name = nil)
|
131
|
+
now = DateTime.now
|
132
|
+
self.updated_at = now
|
133
|
+
attributes[name] = now if name
|
134
|
+
save
|
108
135
|
end
|
109
136
|
|
110
137
|
# Is this object persisted in the datastore? Required for some ActiveModel integration stuff.
|
@@ -120,8 +147,6 @@ module Dynamoid
|
|
120
147
|
def save(options = {})
|
121
148
|
self.class.create_table
|
122
149
|
|
123
|
-
@previously_changed = changes
|
124
|
-
|
125
150
|
if new_record?
|
126
151
|
run_callbacks(:create) { persist }
|
127
152
|
else
|
@@ -131,6 +156,19 @@ module Dynamoid
|
|
131
156
|
self
|
132
157
|
end
|
133
158
|
|
159
|
+
def update!(conditions = {}, &block)
|
160
|
+
options = range_key ? {:range_key => dump_field(self.read_attribute(range_key), self.class.attributes[range_key])} : {}
|
161
|
+
new_attrs = Dynamoid::Adapter.update_item(self.class.table_name, self.hash_key, options.merge(:conditions => conditions), &block)
|
162
|
+
load(new_attrs)
|
163
|
+
end
|
164
|
+
|
165
|
+
def update(conditions = {}, &block)
|
166
|
+
update!(conditions, &block)
|
167
|
+
true
|
168
|
+
rescue Dynamoid::Errors::ConditionalCheckFailedException
|
169
|
+
false
|
170
|
+
end
|
171
|
+
|
134
172
|
# Delete this object, but only after running callbacks for it.
|
135
173
|
#
|
136
174
|
# @since 0.2.0
|
@@ -146,7 +184,8 @@ module Dynamoid
|
|
146
184
|
# @since 0.2.0
|
147
185
|
def delete
|
148
186
|
delete_indexes
|
149
|
-
|
187
|
+
options = range_key ? {:range_key => dump_field(self.read_attribute(range_key), self.class.attributes[range_key])} : {}
|
188
|
+
Dynamoid::Adapter.delete(self.class.table_name, self.hash_key, options)
|
150
189
|
end
|
151
190
|
|
152
191
|
# Dump this object's attributes into hash form, fit to be persisted into the datastore.
|
data/spec/app/models/tweet.rb
CHANGED
@@ -2,20 +2,20 @@ require 'dynamoid/adapter/aws_sdk'
|
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
|
3
3
|
|
4
4
|
describe Dynamoid::Adapter::AwsSdk do
|
5
|
-
|
5
|
+
|
6
6
|
if ENV['ACCESS_KEY'] && ENV['SECRET_KEY']
|
7
|
-
|
7
|
+
|
8
8
|
context 'without a preexisting table' do
|
9
9
|
# CreateTable and DeleteTable
|
10
10
|
it 'performs CreateTable and DeleteTable' do
|
11
11
|
table = Dynamoid::Adapter.create_table('CreateTable', :id, :range_key => { :created_at => :number })
|
12
|
-
|
12
|
+
|
13
13
|
Dynamoid::Adapter.connection.tables.collect{|t| t.name}.should include 'CreateTable'
|
14
|
-
|
14
|
+
|
15
15
|
Dynamoid::Adapter.delete_table('CreateTable')
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
context 'with a preexisting table' do
|
20
20
|
before(:all) do
|
21
21
|
Dynamoid::Adapter.create_table('dynamoid_tests_TestTable1', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable1')
|
@@ -34,20 +34,20 @@ describe Dynamoid::Adapter::AwsSdk do
|
|
34
34
|
Dynamoid::Adapter.get_item('dynamoid_tests_TestTable1', '1').should == {:name => 'Josh', :id => '1'}
|
35
35
|
|
36
36
|
Dynamoid::Adapter.delete_item('dynamoid_tests_TestTable1', '1')
|
37
|
-
|
37
|
+
|
38
38
|
Dynamoid::Adapter.get_item('dynamoid_tests_TestTable1', '1').should be_nil
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
it 'performs GetItem for an item that does exist with a range key' do
|
42
42
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :name => 'Josh', :range => 2.0})
|
43
43
|
|
44
44
|
Dynamoid::Adapter.get_item('dynamoid_tests_TestTable3', '1', :range_key => 2.0).should == {:name => 'Josh', :id => '1', :range => 2.0}
|
45
45
|
|
46
46
|
Dynamoid::Adapter.delete_item('dynamoid_tests_TestTable3', '1', :range_key => 2.0)
|
47
|
-
|
48
|
-
Dynamoid::Adapter.get_item('dynamoid_tests_TestTable3', '1', :range_key => 2.0).should be_nil
|
47
|
+
|
48
|
+
Dynamoid::Adapter.get_item('dynamoid_tests_TestTable3', '1', :range_key => 2.0).should be_nil
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
it 'performs DeleteItem for an item that does not exist' do
|
52
52
|
Dynamoid::Adapter.delete_item('dynamoid_tests_TestTable1', '1')
|
53
53
|
|
@@ -59,53 +59,53 @@ describe Dynamoid::Adapter::AwsSdk do
|
|
59
59
|
|
60
60
|
Dynamoid::Adapter.get_item('dynamoid_tests_TestTable1', '1').should == {:id => '1', :name => 'Josh'}
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
# BatchGetItem
|
64
64
|
it "performs BatchGetItem with singular keys" do
|
65
65
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
|
66
66
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable2', {:id => '1', :name => 'Justin'})
|
67
|
-
|
67
|
+
|
68
68
|
results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable1' => '1', 'dynamoid_tests_TestTable2' => '1')
|
69
69
|
results.size.should == 2
|
70
70
|
results['dynamoid_tests_TestTable1'].should include({:name => 'Josh', :id => '1'})
|
71
71
|
results['dynamoid_tests_TestTable2'].should include({:name => 'Justin', :id => '1'})
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
it "performs BatchGetItem with multiple keys" do
|
75
75
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
|
76
76
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2', :name => 'Justin'})
|
77
|
-
|
77
|
+
|
78
78
|
results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable1' => ['1', '2'])
|
79
79
|
results.size.should == 1
|
80
80
|
results['dynamoid_tests_TestTable1'].should include({:name => 'Josh', :id => '1'})
|
81
81
|
results['dynamoid_tests_TestTable1'].should include({:name => 'Justin', :id => '2'})
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
it 'performs BatchGetItem with one ranged key' do
|
85
85
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :name => 'Josh', :range => 1.0})
|
86
86
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '2', :name => 'Justin', :range => 2.0})
|
87
|
-
|
87
|
+
|
88
88
|
results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable3' => [['1', 1.0]])
|
89
89
|
results.size.should == 1
|
90
90
|
results['dynamoid_tests_TestTable3'].should include({:name => 'Josh', :id => '1', :range => 1.0})
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
it 'performs BatchGetItem with multiple ranged keys' do
|
94
94
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :name => 'Josh', :range => 1.0})
|
95
95
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '2', :name => 'Justin', :range => 2.0})
|
96
|
-
|
96
|
+
|
97
97
|
results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable3' => [['1', 1.0],['2', 2.0]])
|
98
98
|
results.size.should == 1
|
99
99
|
results['dynamoid_tests_TestTable3'].should include({:name => 'Josh', :id => '1', :range => 1.0})
|
100
100
|
results['dynamoid_tests_TestTable3'].should include({:name => 'Justin', :id => '2', :range => 2.0})
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
# ListTables
|
104
104
|
it 'performs ListTables' do
|
105
105
|
Dynamoid::Adapter.list_tables.should include 'dynamoid_tests_TestTable1'
|
106
106
|
Dynamoid::Adapter.list_tables.should include 'dynamoid_tests_TestTable2'
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
# Query
|
110
110
|
it 'performs query on a table and returns items' do
|
111
111
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
|
@@ -119,30 +119,30 @@ describe Dynamoid::Adapter::AwsSdk do
|
|
119
119
|
|
120
120
|
Dynamoid::Adapter.query('dynamoid_tests_TestTable1', :hash_value => '1').should == { :id=> '1', :name=>"Josh" }
|
121
121
|
end
|
122
|
-
|
122
|
+
|
123
123
|
context 'range queries' do
|
124
124
|
before do
|
125
125
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :range => 1.0})
|
126
126
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :range => 3.0})
|
127
127
|
end
|
128
128
|
|
129
|
-
it 'performs query on a table with a range and selects items in a range' do
|
129
|
+
it 'performs query on a table with a range and selects items in a range' do
|
130
130
|
Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_value => 0.0..3.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
|
131
131
|
end
|
132
132
|
|
133
|
-
it 'performs query on a table with a range and selects items greater than' do
|
133
|
+
it 'performs query on a table with a range and selects items greater than' do
|
134
134
|
Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_greater_than => 1.0).should =~ [{:id => '1', :range => BigDecimal.new(3)}]
|
135
135
|
end
|
136
136
|
|
137
|
-
it 'performs query on a table with a range and selects items less than' do
|
137
|
+
it 'performs query on a table with a range and selects items less than' do
|
138
138
|
Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_less_than => 2.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}]
|
139
139
|
end
|
140
140
|
|
141
|
-
it 'performs query on a table with a range and selects items gte' do
|
141
|
+
it 'performs query on a table with a range and selects items gte' do
|
142
142
|
Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_gte => 1.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
|
143
143
|
end
|
144
144
|
|
145
|
-
it 'performs query on a table with a range and selects items lte' do
|
145
|
+
it 'performs query on a table with a range and selects items lte' do
|
146
146
|
Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_lte => 3.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
|
147
147
|
end
|
148
148
|
end
|
@@ -165,22 +165,22 @@ describe Dynamoid::Adapter::AwsSdk do
|
|
165
165
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
|
166
166
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2', :name => 'Josh'})
|
167
167
|
|
168
|
-
Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', :name => 'Josh').should
|
168
|
+
Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', :name => 'Josh').should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
|
169
169
|
end
|
170
|
-
|
170
|
+
|
171
171
|
it 'performs scan on a table and returns all items if no criteria are specified' do
|
172
172
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
|
173
173
|
Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2', :name => 'Josh'})
|
174
174
|
|
175
|
-
Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', {}).should
|
176
|
-
end
|
175
|
+
Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
|
176
|
+
end
|
177
177
|
end
|
178
|
-
|
178
|
+
|
179
179
|
# DescribeTable
|
180
|
-
|
180
|
+
|
181
181
|
# UpdateItem
|
182
|
-
|
182
|
+
|
183
183
|
# UpdateTable
|
184
|
-
|
184
|
+
|
185
185
|
end
|
186
186
|
end
|
@@ -73,6 +73,14 @@ describe "Dynamoid::Associations::Chain" do
|
|
73
73
|
@chain.send(:records_without_index).should == [@user]
|
74
74
|
end
|
75
75
|
|
76
|
+
it "doesn't crash if it finds a nil id in the index" do
|
77
|
+
@chain.query = {:name => 'Josh', "created_at.gt" => @time - 1.day}
|
78
|
+
Dynamoid::Adapter.expects(:query).
|
79
|
+
with("dynamoid_tests_index_user_created_ats_and_names", kind_of(Hash)).
|
80
|
+
returns([{ids: nil}, {ids: Set.new([42])}])
|
81
|
+
@chain.send(:ids_from_index).should == Set.new([42])
|
82
|
+
end
|
83
|
+
|
76
84
|
it 'defines each' do
|
77
85
|
@chain.query = {:name => 'Josh'}
|
78
86
|
@chain.each {|u| u.update_attribute(:name, 'Justin')}
|
@@ -88,10 +96,10 @@ describe "Dynamoid::Associations::Chain" do
|
|
88
96
|
|
89
97
|
it 'finds range querys' do
|
90
98
|
@chain = Dynamoid::Criteria::Chain.new(Tweet)
|
91
|
-
@chain.query = { :
|
99
|
+
@chain.query = { :tweet_id => 'test' }
|
92
100
|
@chain.send(:range?).should be_true
|
93
101
|
|
94
|
-
@chain.query = {:
|
102
|
+
@chain.query = {:tweet_id => 'test', :group => 'xx'}
|
95
103
|
@chain.send(:range?).should be_true
|
96
104
|
|
97
105
|
@chain.query = { :group => 'xx' }
|
@@ -100,32 +108,32 @@ describe "Dynamoid::Associations::Chain" do
|
|
100
108
|
@chain.query = { :group => 'xx', :msg => 'hai' }
|
101
109
|
@chain.send(:range?).should be_false
|
102
110
|
end
|
103
|
-
|
111
|
+
|
104
112
|
context 'range queries' do
|
105
113
|
before do
|
106
114
|
@tweet1 = Tweet.create(:tweet_id => "x", :group => "one")
|
107
115
|
@tweet2 = Tweet.create(:tweet_id => "x", :group => "two")
|
108
|
-
@tweet3 = Tweet.create(:tweet_id => "xx", :group => "two")
|
116
|
+
@tweet3 = Tweet.create(:tweet_id => "xx", :group => "two")
|
109
117
|
@chain = Dynamoid::Criteria::Chain.new(Tweet)
|
110
118
|
end
|
111
|
-
|
119
|
+
|
112
120
|
it 'finds tweets with a simple range query' do
|
113
121
|
@chain.query = { :tweet_id => "x" }
|
114
122
|
@chain.send(:records_with_range).size.should == 2
|
115
123
|
@chain.all.size.should == 2
|
116
|
-
@chain.limit(1).size.should == 1
|
124
|
+
@chain.limit(1).size.should == 1
|
117
125
|
end
|
118
|
-
|
126
|
+
|
119
127
|
it 'finds tweets with a start' do
|
120
128
|
@chain.query = { :tweet_id => "x" }
|
121
129
|
@chain.start(@tweet1)
|
122
130
|
@chain.all.should =~ [@tweet2]
|
123
131
|
end
|
124
|
-
|
132
|
+
|
125
133
|
it 'finds one specific tweet' do
|
126
134
|
@chain = Dynamoid::Criteria::Chain.new(Tweet)
|
127
135
|
@chain.query = { :tweet_id => "xx", :group => "two" }
|
128
|
-
@chain.send(:records_with_range).should == [@tweet3]
|
136
|
+
@chain.send(:records_with_range).should == [@tweet3]
|
129
137
|
end
|
130
138
|
end
|
131
139
|
|
@@ -57,10 +57,16 @@ describe "Dynamoid::Criteria" do
|
|
57
57
|
User.where(:name => 'x').consistent.first
|
58
58
|
|
59
59
|
Dynamoid::Adapter.expects(:query).with { |table_name, options| options[:consistent_read] == true }.returns([])
|
60
|
-
Tweet.where(:
|
60
|
+
Tweet.where(:tweet_id => 'xx', :group => 'two').consistent.all
|
61
61
|
|
62
62
|
Dynamoid::Adapter.expects(:query).with { |table_name, options| options[:consistent_read] == false }.returns([])
|
63
|
-
Tweet.where(:
|
63
|
+
Tweet.where(:tweet_id => 'xx', :group => 'two').all
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'raises exception when consistent_read is used with scan' do
|
67
|
+
expect do
|
68
|
+
User.where(:password => 'password').consistent.first
|
69
|
+
end.to raise_error(Dynamoid::Errors::InvalidQuery)
|
64
70
|
end
|
65
71
|
|
66
72
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe 'Dynamoid::Dirty' do
|
4
|
+
|
5
|
+
context 'changes' do
|
6
|
+
it 'should be empty' do
|
7
|
+
tweet = Tweet.new
|
8
|
+
tweet.msg_changed?.should be_false
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should not be empty' do
|
12
|
+
tweet = Tweet.new(:tweet_id => "1", :group => 'abc')
|
13
|
+
tweet.changed?.should be_true
|
14
|
+
tweet.group_was.should be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should be empty when loaded from database' do
|
18
|
+
Tweet.create!(:tweet_id => "1", :group => 'abc')
|
19
|
+
tweet = Tweet.where(:tweet_id => "1", :group => 'abc').first
|
20
|
+
tweet.changed?.should be_false
|
21
|
+
tweet.group = 'abc'
|
22
|
+
tweet.reload
|
23
|
+
tweet.changed?.should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'track changes after saves' do
|
27
|
+
tweet = Tweet.new(:tweet_id => "1", :group => 'abc')
|
28
|
+
tweet.save!
|
29
|
+
tweet.changed?.should be_false
|
30
|
+
|
31
|
+
tweet.user_name = 'xyz'
|
32
|
+
tweet.user_name_changed?.should be_true
|
33
|
+
tweet.user_name_was.should be_nil
|
34
|
+
tweet.save!
|
35
|
+
|
36
|
+
tweet.user_name_changed?.should be_false
|
37
|
+
tweet.user_name = 'abc'
|
38
|
+
tweet.user_name_was.should == 'xyz'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'clear changes on save' do
|
42
|
+
tweet = Tweet.new(:tweet_id => "1", :group => 'abc')
|
43
|
+
tweet.group = 'xyz'
|
44
|
+
tweet.group_changed?.should be_true
|
45
|
+
tweet.save!
|
46
|
+
tweet.group_changed?.should be_false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|