elastictastic 0.5.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/LICENSE +19 -0
- data/README.md +326 -0
- data/lib/elastictastic/association.rb +21 -0
- data/lib/elastictastic/bulk_persistence_strategy.rb +70 -0
- data/lib/elastictastic/callbacks.rb +30 -0
- data/lib/elastictastic/child_collection_proxy.rb +56 -0
- data/lib/elastictastic/client.rb +101 -0
- data/lib/elastictastic/configuration.rb +35 -0
- data/lib/elastictastic/dirty.rb +130 -0
- data/lib/elastictastic/discrete_persistence_strategy.rb +52 -0
- data/lib/elastictastic/document.rb +98 -0
- data/lib/elastictastic/errors.rb +7 -0
- data/lib/elastictastic/field.rb +38 -0
- data/lib/elastictastic/index.rb +19 -0
- data/lib/elastictastic/mass_assignment_security.rb +15 -0
- data/lib/elastictastic/middleware.rb +119 -0
- data/lib/elastictastic/nested_document.rb +29 -0
- data/lib/elastictastic/new_relic_instrumentation.rb +26 -0
- data/lib/elastictastic/observer.rb +3 -0
- data/lib/elastictastic/observing.rb +21 -0
- data/lib/elastictastic/parent_child.rb +115 -0
- data/lib/elastictastic/persistence.rb +67 -0
- data/lib/elastictastic/properties.rb +236 -0
- data/lib/elastictastic/railtie.rb +35 -0
- data/lib/elastictastic/resource.rb +4 -0
- data/lib/elastictastic/scope.rb +283 -0
- data/lib/elastictastic/scope_builder.rb +32 -0
- data/lib/elastictastic/scoped.rb +20 -0
- data/lib/elastictastic/search.rb +180 -0
- data/lib/elastictastic/server_error.rb +15 -0
- data/lib/elastictastic/test_helpers.rb +172 -0
- data/lib/elastictastic/util.rb +63 -0
- data/lib/elastictastic/validations.rb +45 -0
- data/lib/elastictastic/version.rb +3 -0
- data/lib/elastictastic.rb +82 -0
- data/spec/environment.rb +6 -0
- data/spec/examples/active_model_lint_spec.rb +20 -0
- data/spec/examples/bulk_persistence_strategy_spec.rb +233 -0
- data/spec/examples/callbacks_spec.rb +96 -0
- data/spec/examples/dirty_spec.rb +238 -0
- data/spec/examples/document_spec.rb +600 -0
- data/spec/examples/mass_assignment_security_spec.rb +13 -0
- data/spec/examples/middleware_spec.rb +92 -0
- data/spec/examples/observing_spec.rb +141 -0
- data/spec/examples/parent_child_spec.rb +308 -0
- data/spec/examples/properties_spec.rb +92 -0
- data/spec/examples/scope_spec.rb +491 -0
- data/spec/examples/search_spec.rb +382 -0
- data/spec/examples/spec_helper.rb +15 -0
- data/spec/examples/validation_spec.rb +65 -0
- data/spec/models/author.rb +9 -0
- data/spec/models/blog.rb +5 -0
- data/spec/models/comment.rb +5 -0
- data/spec/models/post.rb +41 -0
- data/spec/models/post_observer.rb +11 -0
- data/spec/support/fakeweb_request_history.rb +13 -0
- metadata +227 -0
@@ -0,0 +1,233 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastictastic::BulkPersistenceStrategy do
|
4
|
+
include Elastictastic::TestHelpers
|
5
|
+
|
6
|
+
let(:last_request) { FakeWeb.last_request }
|
7
|
+
let(:bulk_requests) do
|
8
|
+
last_request.body.split("\n").map do |request|
|
9
|
+
JSON.parse(request)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'create without ID' do
|
14
|
+
let(:post) { Post.new }
|
15
|
+
|
16
|
+
before do
|
17
|
+
stub_elasticsearch_bulk(
|
18
|
+
'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
|
19
|
+
)
|
20
|
+
Elastictastic.bulk do
|
21
|
+
post.title = 'Bulky'
|
22
|
+
post.save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should send bulk request' do
|
27
|
+
last_request.path.should == '/_bulk'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should send index operation' do
|
31
|
+
bulk_requests.should == [
|
32
|
+
{ 'create' => { '_index' => 'default', '_type' => 'post' }},
|
33
|
+
post.elasticsearch_doc
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should set ID' do
|
38
|
+
post.id.should == '123'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should set persisted' do
|
42
|
+
post.should be_persisted
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should have final newline' do
|
46
|
+
last_request.body[-1].should == "\n"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'before bulk operation completes' do
|
51
|
+
let(:post) { Post.new }
|
52
|
+
|
53
|
+
around do |example|
|
54
|
+
Elastictastic.bulk do
|
55
|
+
example.run
|
56
|
+
# have to do this here because the before/after hooks run inside the
|
57
|
+
# around hook
|
58
|
+
stub_elasticsearch_bulk(
|
59
|
+
'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should not set ID' do
|
65
|
+
post.save
|
66
|
+
post.id.should be_nil
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should not set persisted' do
|
70
|
+
post.should be_transient
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should not allow you to call save again on transient document' do
|
74
|
+
post.save
|
75
|
+
expect { post.save }.to raise_error(Elastictastic::OperationNotAllowed)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'creating multiple' do
|
80
|
+
let(:posts) { Array.new(2) { Post.new }}
|
81
|
+
|
82
|
+
before do
|
83
|
+
stub_elasticsearch_bulk(
|
84
|
+
{ 'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }},
|
85
|
+
{ 'create' => { '_index' => 'default', '_type' => 'post', '_id' => '124', '_version' => 1, 'ok' => true }}
|
86
|
+
)
|
87
|
+
Elastictastic.bulk { posts.each { |post| post.save }}
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should send both create operations' do
|
91
|
+
bulk_requests.should == posts.map do |post|
|
92
|
+
[
|
93
|
+
{ 'create' => { '_index' => 'default', '_type' => 'post' }},
|
94
|
+
post.elasticsearch_doc
|
95
|
+
]
|
96
|
+
end.flatten
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should set IDs' do
|
100
|
+
posts.map { |post| post.id }.should == %w(123 124)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'create with ID set' do
|
105
|
+
let(:post) do
|
106
|
+
Post.new.tap do |post|
|
107
|
+
post.id = '123'
|
108
|
+
post.title = 'bulky'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
before do
|
113
|
+
stub_elasticsearch_bulk(
|
114
|
+
'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
|
115
|
+
)
|
116
|
+
Elastictastic.bulk { post.save }
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should send ID in request to create' do
|
120
|
+
bulk_requests.should == [
|
121
|
+
{ 'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123' }},
|
122
|
+
post.elasticsearch_doc
|
123
|
+
]
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should set object persistent' do
|
127
|
+
post.should be_persisted
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should retain ID' do
|
131
|
+
post.id.should == '123'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '#update' do
|
136
|
+
let(:post) do
|
137
|
+
Post.new.tap do |post|
|
138
|
+
post.id = '123'
|
139
|
+
post.persisted!
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
before do
|
144
|
+
stub_elasticsearch_bulk(
|
145
|
+
'index' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should send update' do
|
150
|
+
Elastictastic.bulk { post.save }
|
151
|
+
|
152
|
+
bulk_requests.should == [
|
153
|
+
{ 'index' => { '_index' => 'default', '_type' => 'post', '_id' => '123' }},
|
154
|
+
post.elasticsearch_doc
|
155
|
+
]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe 'destroy' do
|
160
|
+
let(:post) do
|
161
|
+
Post.new.tap do |post|
|
162
|
+
post.id = '123'
|
163
|
+
post.title = 'bulky'
|
164
|
+
post.persisted!
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
before do
|
169
|
+
stub_elasticsearch_bulk(
|
170
|
+
'destroy' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
|
171
|
+
)
|
172
|
+
Elastictastic.bulk { post.destroy }
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should send destroy' do
|
176
|
+
bulk_requests.should == [
|
177
|
+
{ 'delete' => { '_index' => 'default', '_type' => 'post', '_id' => '123' }}
|
178
|
+
]
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should mark record as not persistent' do
|
182
|
+
post.should_not be_persisted
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
shared_examples_for 'block with error' do
|
187
|
+
it 'should not run bulk operation' do
|
188
|
+
error_proc.call rescue nil
|
189
|
+
last_request.should_not be
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should return to individual persistence strategy' do
|
193
|
+
error_proc.call rescue nil
|
194
|
+
stub_elasticsearch_create('default', 'post')
|
195
|
+
Post.new.save
|
196
|
+
last_request.path.should == '/default/post'
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe 'with uncaught exception raised' do
|
201
|
+
let :error_proc do
|
202
|
+
lambda do
|
203
|
+
Elastictastic.bulk do
|
204
|
+
Post.new.save
|
205
|
+
raise
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should propagate error up' do
|
211
|
+
expect(&error_proc).to raise_error(RuntimeError)
|
212
|
+
end
|
213
|
+
|
214
|
+
it_should_behave_like 'block with error'
|
215
|
+
end
|
216
|
+
|
217
|
+
describe 'raising CancelBulkOperation' do
|
218
|
+
let :error_proc do
|
219
|
+
lambda do
|
220
|
+
Elastictastic.bulk do
|
221
|
+
Post.new.save
|
222
|
+
raise Elastictastic::CancelBulkOperation
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'should not propagate error' do
|
228
|
+
expect(&error_proc).not_to raise_error
|
229
|
+
end
|
230
|
+
|
231
|
+
it_should_behave_like 'block with error'
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastictastic::Callbacks do
|
4
|
+
include Elastictastic::TestHelpers
|
5
|
+
|
6
|
+
let(:MyModel) do
|
7
|
+
Class.new do
|
8
|
+
def self.name
|
9
|
+
'MyModel'
|
10
|
+
end
|
11
|
+
|
12
|
+
include Elastictastic::Document
|
13
|
+
|
14
|
+
before_save :before_save_ran!
|
15
|
+
before_create :before_create_ran!
|
16
|
+
before_update :before_update_ran!
|
17
|
+
before_destroy :before_destroy_ran!
|
18
|
+
|
19
|
+
def hooks_that_ran
|
20
|
+
@hooks_that_ran ||= Set[]
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_run_hook?(hook)
|
24
|
+
hooks_that_ran.include?(hook.to_sym)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def method_missing(method, *args, &block)
|
30
|
+
if method.to_s =~ /^(.*)_ran!$/
|
31
|
+
hooks_that_ran << $1.to_sym
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:id) { '123' }
|
40
|
+
let(:instance) { MyModel().new }
|
41
|
+
let(:persisted_instance) do
|
42
|
+
MyModel().new.tap do |instance|
|
43
|
+
instance.elasticsearch_hit = { '_id' => id, '_index' => 'default' }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
before do
|
48
|
+
stub_elasticsearch_create('default', 'my_model')
|
49
|
+
stub_elasticsearch_update('default', 'my_model', id)
|
50
|
+
stub_elasticsearch_destroy('default', 'my_model', id)
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#before_save' do
|
54
|
+
|
55
|
+
it 'should run before create' do
|
56
|
+
instance.save
|
57
|
+
instance.should have_run_hook(:before_save)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should run before update' do
|
61
|
+
persisted_instance.save
|
62
|
+
persisted_instance.should have_run_hook(:before_save)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#before_create' do
|
67
|
+
it 'should run before create' do
|
68
|
+
instance.save
|
69
|
+
instance.should have_run_hook(:before_create)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should run before update' do
|
73
|
+
persisted_instance.save
|
74
|
+
persisted_instance.should_not have_run_hook(:before_create)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#before_update' do
|
79
|
+
it 'should not run before create' do
|
80
|
+
instance.save
|
81
|
+
instance.should_not have_run_hook :before_update
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should run before update' do
|
85
|
+
persisted_instance.save
|
86
|
+
persisted_instance.should have_run_hook(:before_update)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#before_destroy' do
|
91
|
+
it 'should run before destroy' do
|
92
|
+
persisted_instance.destroy
|
93
|
+
persisted_instance.should have_run_hook(:before_destroy)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastictastic::Dirty do
|
4
|
+
include Elastictastic::TestHelpers
|
5
|
+
|
6
|
+
let(:post) do
|
7
|
+
Post.new.tap do |post|
|
8
|
+
post.elasticsearch_hit = {
|
9
|
+
'_id' => '1',
|
10
|
+
'_type' => 'post',
|
11
|
+
'_index' => 'default',
|
12
|
+
'_source' => source
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
before do
|
18
|
+
stub_elasticsearch_update('default', 'post', '1')
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'top-level attribute' do
|
22
|
+
let(:source) { { 'title' => 'first title' } }
|
23
|
+
|
24
|
+
context 'with no attribute changed' do
|
25
|
+
it 'should not be changed' do
|
26
|
+
post.should_not be_changed
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should not have any changes' do
|
30
|
+
post.changes.should be_empty
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should not have the title attribute changed' do
|
34
|
+
post.should_not be_title_changed
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'with title attribute changed' do
|
39
|
+
before do
|
40
|
+
post.title = 'hey guy'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should be changed' do
|
44
|
+
post.should be_changed
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should expose change' do
|
48
|
+
post.changes[:title].should == ['first title', 'hey guy']
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should have the title attribute changed' do
|
52
|
+
post.should be_title_changed
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'after change and save' do
|
57
|
+
before do
|
58
|
+
post.title = 'hey guy'
|
59
|
+
post.save
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should not be changed' do
|
63
|
+
post.should_not be_changed
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should not have any changes' do
|
67
|
+
post.changes.should be_empty
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should not have the title attribute changed' do
|
71
|
+
post.should_not be_title_changed
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'single nested document' do
|
77
|
+
let(:source) { { 'author' => { 'name' => 'Mat Brown' }} }
|
78
|
+
|
79
|
+
context 'with nothing changed' do
|
80
|
+
it 'should not be changed' do
|
81
|
+
post.should_not be_changed
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with nested document attribute changed' do
|
86
|
+
before do
|
87
|
+
post.author.name = 'Barack Obama'
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should be changed' do
|
91
|
+
post.should be_changed
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should mark association as changed' do
|
95
|
+
post.changed.should == %w(author)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should return *_changed? for the association' do
|
99
|
+
post.should be_author_changed
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should have change' do
|
103
|
+
change = post.changes['author']
|
104
|
+
change.map { |author| author.name }.should == ['Mat Brown', 'Barack Obama']
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should not be changed after save' do
|
108
|
+
post.save
|
109
|
+
post.should_not be_changed
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should not have changed nested document after save' do
|
113
|
+
post.save
|
114
|
+
post.author.should_not be_changed
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'with nested document replaced' do
|
119
|
+
before do
|
120
|
+
post.author = Author.new(:name => 'Barack Obama')
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should be changed' do
|
124
|
+
post.should be_changed
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should expose changes' do
|
128
|
+
post.changes['author'].map { |author| author.name }.should ==
|
129
|
+
['Mat Brown', 'Barack Obama']
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'nested document collection' do
|
135
|
+
let(:source) { { 'comments' => [{ 'body' => 'I like pizza' }] } }
|
136
|
+
|
137
|
+
let(:mapped_changes) do
|
138
|
+
post.changes['comments'].map do |change|
|
139
|
+
change.map { |comment| comment.body }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'with nothing changed' do
|
144
|
+
it 'should not be changed' do
|
145
|
+
post.should_not be_changed
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when document changed' do
|
150
|
+
before do
|
151
|
+
post.comments.first.body = 'I like lasagne'
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should be changed' do
|
155
|
+
post.should be_changed
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should expose changed document' do
|
159
|
+
mapped_changes.should == [['I like pizza'], ['I like lasagne']]
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should not be changed after save' do
|
163
|
+
post.save
|
164
|
+
post.should_not be_changed
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should not have changed nested document after save' do
|
168
|
+
post.save
|
169
|
+
post.comments.first.should_not be_changed
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when document added' do
|
174
|
+
before do
|
175
|
+
post.comments << Comment.new(:body => 'Lasagne is better')
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should be changed' do
|
179
|
+
post.should be_changed
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should expose new document' do
|
183
|
+
mapped_changes.should == [
|
184
|
+
['I like pizza'],
|
185
|
+
['I like pizza', 'Lasagne is better']
|
186
|
+
]
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'should not be changed after save' do
|
190
|
+
post.save
|
191
|
+
post.should_not be_changed
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'should not have changed nested documents after save' do
|
195
|
+
post.save
|
196
|
+
post.comments.each { |comment| comment.should_not be_changed }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context 'when document removed' do
|
201
|
+
before do
|
202
|
+
post.comments.delete(post.comments.first)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'should be changed' do
|
206
|
+
post.should be_changed
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should show changes' do
|
210
|
+
mapped_changes.should == [['I like pizza'], []]
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'should not be changed after save' do
|
214
|
+
post.save
|
215
|
+
post.should_not be_changed
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'with collection replaced' do
|
220
|
+
before do
|
221
|
+
post.comments = [Comment.new(:body => 'I like lasagne')]
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should be changed' do
|
225
|
+
post.should be_changed
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'should expose changes' do
|
229
|
+
mapped_changes.should == [['I like pizza'], ['I like lasagne']]
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'should not be changed after save' do
|
233
|
+
post.save
|
234
|
+
post.should_not be_changed
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|