elastictastic 0.5.0 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +161 -10
  3. data/lib/elastictastic/adapter.rb +84 -0
  4. data/lib/elastictastic/association.rb +6 -0
  5. data/lib/elastictastic/basic_document.rb +213 -0
  6. data/lib/elastictastic/bulk_persistence_strategy.rb +64 -19
  7. data/lib/elastictastic/callbacks.rb +18 -12
  8. data/lib/elastictastic/child_collection_proxy.rb +15 -11
  9. data/lib/elastictastic/client.rb +47 -24
  10. data/lib/elastictastic/configuration.rb +59 -4
  11. data/lib/elastictastic/dirty.rb +43 -28
  12. data/lib/elastictastic/discrete_persistence_strategy.rb +48 -23
  13. data/lib/elastictastic/document.rb +1 -85
  14. data/lib/elastictastic/embedded_document.rb +34 -0
  15. data/lib/elastictastic/errors.rb +17 -5
  16. data/lib/elastictastic/field.rb +3 -0
  17. data/lib/elastictastic/mass_assignment_security.rb +2 -4
  18. data/lib/elastictastic/middleware.rb +66 -84
  19. data/lib/elastictastic/multi_get.rb +30 -0
  20. data/lib/elastictastic/multi_search.rb +70 -0
  21. data/lib/elastictastic/nested_document.rb +3 -27
  22. data/lib/elastictastic/new_relic_instrumentation.rb +8 -8
  23. data/lib/elastictastic/observing.rb +8 -6
  24. data/lib/elastictastic/optimistic_locking.rb +57 -0
  25. data/lib/elastictastic/parent_child.rb +56 -54
  26. data/lib/elastictastic/persistence.rb +16 -16
  27. data/lib/elastictastic/properties.rb +136 -96
  28. data/lib/elastictastic/railtie.rb +1 -1
  29. data/lib/elastictastic/rotor.rb +105 -0
  30. data/lib/elastictastic/scope.rb +186 -56
  31. data/lib/elastictastic/server_error.rb +20 -1
  32. data/lib/elastictastic/test_helpers.rb +152 -97
  33. data/lib/elastictastic/thrift/constants.rb +12 -0
  34. data/lib/elastictastic/thrift/rest.rb +83 -0
  35. data/lib/elastictastic/thrift/types.rb +124 -0
  36. data/lib/elastictastic/thrift_adapter.rb +61 -0
  37. data/lib/elastictastic/transport_methods.rb +27 -0
  38. data/lib/elastictastic/validations.rb +11 -13
  39. data/lib/elastictastic/version.rb +1 -1
  40. data/lib/elastictastic.rb +148 -27
  41. data/spec/environment.rb +1 -1
  42. data/spec/examples/bulk_persistence_strategy_spec.rb +151 -23
  43. data/spec/examples/callbacks_spec.rb +65 -34
  44. data/spec/examples/dirty_spec.rb +160 -1
  45. data/spec/examples/document_spec.rb +168 -106
  46. data/spec/examples/middleware_spec.rb +1 -61
  47. data/spec/examples/multi_get_spec.rb +127 -0
  48. data/spec/examples/multi_search_spec.rb +113 -0
  49. data/spec/examples/observing_spec.rb +24 -3
  50. data/spec/examples/optimistic_locking_spec.rb +417 -0
  51. data/spec/examples/parent_child_spec.rb +73 -33
  52. data/spec/examples/properties_spec.rb +53 -0
  53. data/spec/examples/rotor_spec.rb +132 -0
  54. data/spec/examples/scope_spec.rb +78 -18
  55. data/spec/examples/search_spec.rb +26 -0
  56. data/spec/examples/validation_spec.rb +7 -1
  57. data/spec/models/author.rb +1 -1
  58. data/spec/models/blog.rb +2 -0
  59. data/spec/models/comment.rb +1 -1
  60. data/spec/models/photo.rb +9 -0
  61. data/spec/models/post.rb +3 -0
  62. metadata +97 -78
  63. data/lib/elastictastic/resource.rb +0 -4
  64. data/spec/examples/active_model_lint_spec.rb +0 -20
data/lib/elastictastic.rb CHANGED
@@ -1,80 +1,201 @@
1
1
  require 'active_support/core_ext'
2
2
  require 'active_model'
3
+
4
+ require 'elastictastic/adapter'
5
+ require 'elastictastic/basic_document'
3
6
  require 'elastictastic/errors'
7
+ require 'elastictastic/client'
8
+ require 'elastictastic/configuration'
9
+ require 'elastictastic/discrete_persistence_strategy'
10
+ require 'elastictastic/embedded_document'
11
+ require 'elastictastic/field'
12
+ require 'elastictastic/index'
13
+ require 'elastictastic/middleware'
14
+ require 'elastictastic/multi_get'
15
+ require 'elastictastic/multi_search'
16
+ require 'elastictastic/optimistic_locking'
17
+ require 'elastictastic/persistence'
18
+ require 'elastictastic/properties'
19
+ require 'elastictastic/scope'
20
+ require 'elastictastic/scope_builder'
21
+ require 'elastictastic/scoped'
22
+ require 'elastictastic/search'
23
+ require 'elastictastic/server_error'
24
+ require 'elastictastic/util'
4
25
 
5
26
  module Elastictastic
6
27
  autoload :Association, 'elastictastic/association'
7
28
  autoload :BulkPersistenceStrategy, 'elastictastic/bulk_persistence_strategy'
8
29
  autoload :Callbacks, 'elastictastic/callbacks'
9
30
  autoload :ChildCollectionProxy, 'elastictastic/child_collection_proxy'
10
- autoload :Client, 'elastictastic/client'
11
- autoload :Configuration, 'elastictastic/configuration'
12
31
  autoload :Dirty, 'elastictastic/dirty'
13
- autoload :DiscretePersistenceStrategy, 'elastictastic/discrete_persistence_strategy'
14
32
  autoload :Document, 'elastictastic/document'
15
- autoload :Field, 'elastictastic/field'
16
- autoload :Index, 'elastictastic/index'
17
33
  autoload :MassAssignmentSecurity, 'elastictastic/mass_assignment_security'
18
- autoload :Middleware, 'elastictastic/middleware'
19
34
  autoload :NestedCollectionProxy, 'elastictastic/nested_collection_proxy'
20
- autoload :NestedDocument, 'elastictastic/nested_document'
21
35
  autoload :Observer, 'elastictastic/observer'
22
36
  autoload :Observing, 'elastictastic/observing'
23
37
  autoload :ParentChild, 'elastictastic/parent_child'
24
- autoload :Persistence, 'elastictastic/persistence'
25
- autoload :Properties, 'elastictastic/properties'
26
- autoload :Resource, 'elastictastic/resource'
27
- autoload :Scope, 'elastictastic/scope'
28
- autoload :ScopeBuilder, 'elastictastic/scope_builder'
29
- autoload :Scoped, 'elastictastic/scoped'
30
- autoload :Search, 'elastictastic/search'
31
- autoload :ServerError, 'elastictastic/server_error'
38
+ autoload :Rotor, 'elastictastic/rotor'
32
39
  autoload :TestHelpers, 'elastictastic/test_helpers'
33
- autoload :Util, 'elastictastic/util'
40
+ autoload :ThriftAdapter, 'elastictastic/thrift_adapter'
34
41
  autoload :Validations, 'elastictastic/validations'
35
42
 
43
+ autoload :NestedDocument, 'elastictastic/nested_document' # Deprecated
44
+
36
45
  class <<self
37
46
  attr_writer :config
38
47
 
48
+ #
49
+ # Elastictastic global configuration. In a Rails environment, you can
50
+ # configure Elastictastic by creating a `config/elastictastic.yml` file,
51
+ # whose keys will be passed into the Configuration object when your
52
+ # application boots. In non-Rails environment, you can configure
53
+ # Elastictastic directly using the object returned by this method.
54
+ #
55
+ # @return [Configuration] global configuration object
56
+ #
39
57
  def config
40
58
  @config ||= Configuration.new
41
59
  end
42
60
 
61
+ def multi_get(&block)
62
+ MultiGet.new.tap(&block).to_a
63
+ end
64
+
65
+ #
66
+ # Perform multiple searches in a single request to ElasticSearch. Each
67
+ # scope will be eagerly populated with results.
68
+ #
69
+ # @param [Scope, Array] collection of scopes to execute multisearch on
70
+ #
71
+ def multi_search(*scopes)
72
+ end
73
+
74
+ #
75
+ # Return a lower-level ElasticSearch client. This is likely to be extracted
76
+ # into a separate gem in the future.
77
+ #
78
+ # @return [Client] client
79
+ # @api private
80
+ #
43
81
  def client
44
82
  Thread.current['Elastictastic::client'] ||= Client.new(config)
45
83
  end
46
84
 
85
+ #
86
+ # Set the current persistence strategy
87
+ #
88
+ # @param [DiscretePersistenceStrategy,BulkPersistenceStrategy] persistence strategy
89
+ # @api private
90
+ # @see ::persister
91
+ #
47
92
  def persister=(persister)
48
93
  Thread.current['Elastictastic::persister'] = persister
49
94
  end
50
95
 
96
+ #
97
+ # The current persistence strategy for ElasticSearch. Usually this will
98
+ # be the DiscretePersistenceStrategy singleton; inside a ::bulk block, it
99
+ # will be an instance of BulkPersistenceStrategy
100
+ #
101
+ # @return [DiscretePersistenceStrategy,BulkPersistenceStrategy] current persistence strategy
102
+ # @api private
103
+ # @see ::bulk
104
+ #
51
105
  def persister
52
106
  Thread.current['Elastictastic::persister'] ||=
53
107
  Elastictastic::DiscretePersistenceStrategy.instance
54
108
  end
55
109
 
56
- def bulk
110
+ #
111
+ # Perform write operations in a single request to ElasticSearch. Highly
112
+ # recommended for any operation which writes a large quantity of data to
113
+ # ElasticSearch. Write operations (e.g. save/destroy documents) are buffered
114
+ # in the client and sent to ElasticSearch when the bulk operation exits
115
+ # (or when an auto-flush threshold is reached; see below).
116
+ #
117
+ # @example Create posts in bulk
118
+ # Elastictastic.bulk do
119
+ # params[:posts].each do |post_params|
120
+ # Post.new(post_params).save!
121
+ # end
122
+ # end # posts are actually persisted here
123
+ #
124
+ # Since write operations inside a bulk block are not performed
125
+ # synchronously, server-side errors will only be raised once the bulk block
126
+ # completes; you may pass a block into Document#save and Document#destroy
127
+ # that will be called once the operation completes. The block is passed an
128
+ # error param if the operation was not successful.
129
+ #
130
+ # @example Custom handling for conflicting IDs in a bulk block
131
+ # errors = []
132
+ # Elastictastic.bulk do
133
+ # params[:posts].each do |post_params|
134
+ # Post.new(post_params).save! do |e|
135
+ # case e
136
+ # when nil # success!
137
+ # when Elastictastic::ServerError::DocumentAlreadyExistsEngineException
138
+ # conflicting_ids << post_params[:id]
139
+ # else
140
+ # raise e
141
+ # end
142
+ # end
143
+ # end
144
+ # end
145
+ #
146
+ # @option options [Fixnum] :auto_flush Flush to ElasticSearch after this many operations performed.
147
+ # @yield Block during which all write operations are buffered for bulk
148
+ #
149
+ def bulk(options = {})
57
150
  original_persister = self.persister
151
+ bulk_persister = self.persister =
152
+ Elastictastic::BulkPersistenceStrategy.new(options)
58
153
  begin
59
- self.persister = Elastictastic::BulkPersistenceStrategy.new
60
154
  yield
61
- self.persister.flush
62
- rescue Elastictastic::CancelBulkOperation
63
- # Nothing to see here...
64
155
  ensure
65
156
  self.persister = original_persister
66
157
  end
158
+ bulk_persister.flush
67
159
  end
68
160
 
69
- def Index(name_or_index)
70
- Index === name_or_index ? name_or_index : Index.new(name_or_index)
161
+ #
162
+ # Use Elastictastic's configured JSON encoder to encode a JSON message.
163
+ #
164
+ # @param [Object] Object to encode to JSON
165
+ # @return JSON representation of object
166
+ # @api private
167
+ #
168
+ def json_encode(object)
169
+ if config.json_engine.respond_to?(:encode)
170
+ config.json_engine.encode(object)
171
+ else
172
+ config.json_engine.dump(object)
173
+ end
71
174
  end
72
175
 
73
- private
176
+ #
177
+ # Use Elastictastic's configured JSON decoder to decode a JSON message
178
+ #
179
+ # @param [String] JSON message to decode
180
+ # @return Ruby object represented by json param
181
+ # @api private
182
+ #
183
+ def json_decode(json)
184
+ if config.json_engine.respond_to?(:decode)
185
+ config.json_engine.decode(json)
186
+ else
187
+ config.json_engine.load(json)
188
+ end
189
+ end
74
190
 
75
- def new_transport
76
- transport_class = const_get("#{config.transport.camelize}Transport")
77
- transport_class.new(config)
191
+ #
192
+ # Coerce the argument to an Elastictastic index.
193
+ #
194
+ # @param [String,Elastictastic::Index] name_or_index Index name or object
195
+ # @api private
196
+ #
197
+ def Index(name_or_index)
198
+ Index === name_or_index ? name_or_index : Index.new(name_or_index)
78
199
  end
79
200
  end
80
201
  end
data/spec/environment.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'bundler'
2
2
  Bundler.require(:default, :test, :development)
3
3
 
4
- %w(author blog comment post post_observer).each do |model|
4
+ %w(author blog comment photo post post_observer).each do |model|
5
5
  require File.expand_path("../models/#{model}", __FILE__)
6
6
  end
@@ -6,7 +6,7 @@ describe Elastictastic::BulkPersistenceStrategy do
6
6
  let(:last_request) { FakeWeb.last_request }
7
7
  let(:bulk_requests) do
8
8
  last_request.body.split("\n").map do |request|
9
- JSON.parse(request)
9
+ Elastictastic.json_decode(request)
10
10
  end
11
11
  end
12
12
 
@@ -14,7 +14,7 @@ describe Elastictastic::BulkPersistenceStrategy do
14
14
  let(:post) { Post.new }
15
15
 
16
16
  before do
17
- stub_elasticsearch_bulk(
17
+ stub_es_bulk(
18
18
  'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
19
19
  )
20
20
  Elastictastic.bulk do
@@ -38,6 +38,10 @@ describe Elastictastic::BulkPersistenceStrategy do
38
38
  post.id.should == '123'
39
39
  end
40
40
 
41
+ it 'should set version' do
42
+ post.version.should == 1
43
+ end
44
+
41
45
  it 'should set persisted' do
42
46
  post.should be_persisted
43
47
  end
@@ -55,7 +59,7 @@ describe Elastictastic::BulkPersistenceStrategy do
55
59
  example.run
56
60
  # have to do this here because the before/after hooks run inside the
57
61
  # around hook
58
- stub_elasticsearch_bulk(
62
+ stub_es_bulk(
59
63
  'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
60
64
  )
61
65
  end
@@ -80,7 +84,7 @@ describe Elastictastic::BulkPersistenceStrategy do
80
84
  let(:posts) { Array.new(2) { Post.new }}
81
85
 
82
86
  before do
83
- stub_elasticsearch_bulk(
87
+ stub_es_bulk(
84
88
  { 'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }},
85
89
  { 'create' => { '_index' => 'default', '_type' => 'post', '_id' => '124', '_version' => 1, 'ok' => true }}
86
90
  )
@@ -99,6 +103,10 @@ describe Elastictastic::BulkPersistenceStrategy do
99
103
  it 'should set IDs' do
100
104
  posts.map { |post| post.id }.should == %w(123 124)
101
105
  end
106
+
107
+ it 'should set versions' do
108
+ posts.each { |post| post.version.should == 1 }
109
+ end
102
110
  end
103
111
 
104
112
  describe 'create with ID set' do
@@ -110,7 +118,7 @@ describe Elastictastic::BulkPersistenceStrategy do
110
118
  end
111
119
 
112
120
  before do
113
- stub_elasticsearch_bulk(
121
+ stub_es_bulk(
114
122
  'create' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
115
123
  )
116
124
  Elastictastic.bulk { post.save }
@@ -130,30 +138,38 @@ describe Elastictastic::BulkPersistenceStrategy do
130
138
  it 'should retain ID' do
131
139
  post.id.should == '123'
132
140
  end
141
+
142
+ it 'should set version' do
143
+ post.version.should == 1
144
+ end
133
145
  end
134
146
 
135
147
  describe '#update' do
136
148
  let(:post) do
137
149
  Post.new.tap do |post|
138
150
  post.id = '123'
151
+ post.version = 1
139
152
  post.persisted!
140
153
  end
141
154
  end
142
155
 
143
156
  before do
144
- stub_elasticsearch_bulk(
145
- 'index' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
157
+ stub_es_bulk(
158
+ 'index' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 2, 'ok' => true }
146
159
  )
160
+ Elastictastic.bulk { post.save }
147
161
  end
148
162
 
149
163
  it 'should send update' do
150
- Elastictastic.bulk { post.save }
151
-
152
164
  bulk_requests.should == [
153
- { 'index' => { '_index' => 'default', '_type' => 'post', '_id' => '123' }},
165
+ { 'index' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1 }},
154
166
  post.elasticsearch_doc
155
167
  ]
156
168
  end
169
+
170
+ it 'should set version' do
171
+ post.version.should == 2
172
+ end
157
173
  end
158
174
 
159
175
  describe 'destroy' do
@@ -161,20 +177,21 @@ describe Elastictastic::BulkPersistenceStrategy do
161
177
  Post.new.tap do |post|
162
178
  post.id = '123'
163
179
  post.title = 'bulky'
180
+ post.version = 1
164
181
  post.persisted!
165
182
  end
166
183
  end
167
184
 
168
185
  before do
169
- stub_elasticsearch_bulk(
170
- 'destroy' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }
186
+ stub_es_bulk(
187
+ 'delete' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 2, 'ok' => true }
171
188
  )
172
189
  Elastictastic.bulk { post.destroy }
173
190
  end
174
191
 
175
192
  it 'should send destroy' do
176
193
  bulk_requests.should == [
177
- { 'delete' => { '_index' => 'default', '_type' => 'post', '_id' => '123' }}
194
+ { 'delete' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 1 }}
178
195
  ]
179
196
  end
180
197
 
@@ -191,7 +208,7 @@ describe Elastictastic::BulkPersistenceStrategy do
191
208
 
192
209
  it 'should return to individual persistence strategy' do
193
210
  error_proc.call rescue nil
194
- stub_elasticsearch_create('default', 'post')
211
+ stub_es_create('default', 'post')
195
212
  Post.new.save
196
213
  last_request.path.should == '/default/post'
197
214
  end
@@ -214,20 +231,131 @@ describe Elastictastic::BulkPersistenceStrategy do
214
231
  it_should_behave_like 'block with error'
215
232
  end
216
233
 
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
234
+ describe 'with :auto_flush specified' do
235
+ before do
236
+ responses = Array.new(3) do
237
+ { 'create' => generate_es_hit('post').except('_source').merge('ok' => true) }
238
+ end.each_slice(2).map { |slice| { 'items' => slice } }
239
+ stub_request_json(
240
+ :post,
241
+ match_es_path('/_bulk'),
242
+ *responses
243
+ )
244
+ Elastictastic.bulk(:auto_flush => 2) { 3.times { Post.new.save }}
245
+ end
246
+
247
+ it 'should perform multiple requests when auto-flush triggered' do
248
+ FakeWeb.should have(2).requests
249
+ end
250
+
251
+ it 'should flush after specified number of operations' do
252
+ FakeWeb.requests.first.body.split("\n").should have(4).items
253
+ FakeWeb.requests.last.body.split("\n").should have(2).items
254
+ end
255
+ end
256
+
257
+ describe 'multiple operations on the same document' do
258
+ let(:post) do
259
+ Post.new.tap do |post|
260
+ post.id = '1'
261
+ post.version = 1
262
+ post.persisted!
263
+ end
264
+ end
265
+
266
+ before do
267
+ stub_es_bulk(
268
+ 'delete' => generate_es_hit('post', :version => 2, :id => '1').merge('ok' => true)
269
+ )
270
+ Elastictastic.bulk do
271
+ post.save
272
+ post.destroy
273
+ end
274
+ end
275
+
276
+ it 'should only send one operation per document' do
277
+ bulk_requests.length.should == 1
278
+ end
279
+
280
+ it 'should send last operation for each document' do
281
+ bulk_requests.last.should == { 'delete' => generate_es_hit('post', :id => '1').except('_source') }
282
+ end
283
+ end
284
+
285
+ describe 'multiple creates' do
286
+ before do
287
+ stub_es_bulk(
288
+ { 'create' => generate_es_hit('post', :id => '1').merge('ok' => true) },
289
+ { 'create' => generate_es_hit('post', :id => '2').merge('ok' => true) }
290
+ )
291
+ Elastictastic.bulk { 2.times { |i| Post.new(:title => "post #{i}").save }}
292
+ end
293
+
294
+ it 'should create all documents' do
295
+ bulk_requests.length.should == 4
296
+ end
297
+
298
+ it 'should send correct info for each document' do
299
+ bulk_requests.each_slice(2).map do |commands|
300
+ commands.last['title']
301
+ end.should == ['post 0', 'post 1']
302
+ end
303
+ end
304
+
305
+ describe 'updating documents with same ID but different index' do
306
+ let(:posts) do
307
+ %w(default my_index).map do |index|
308
+ Post.in_index(index).new.tap do |post|
309
+ post.id = '1'
310
+ post.persisted!
223
311
  end
224
312
  end
225
313
  end
226
314
 
227
- it 'should not propagate error' do
228
- expect(&error_proc).not_to raise_error
315
+ before do
316
+ stub_es_bulk(
317
+ { 'index' => generate_es_hit('post', :id => '1').merge('ok' => true) },
318
+ { 'index' => generate_es_hit('post', :index => 'my_index', :id => '1').merge('ok' => true) }
319
+ )
320
+ Elastictastic.bulk { posts.map { |post| post.save }}
229
321
  end
230
322
 
231
- it_should_behave_like 'block with error'
323
+ it 'should send updates for both documents' do
324
+ bulk_requests.length.should == 4
325
+ end
326
+ end
327
+
328
+ describe 'with routing' do
329
+ let(:photo) { Photo.new(:post_id => 1) }
330
+
331
+ it 'should include routing on create' do
332
+ stub_es_bulk(
333
+ 'create' => { '_index' => 'default', '_type' => 'photo', '_id' => '123', '_version' => 1, 'ok' => true }
334
+ )
335
+ Elastictastic.bulk { photo.save }
336
+ bulk_requests.first['create']['_routing'].should == '1'
337
+ end
338
+
339
+ it 'should include routing on update' do
340
+ photo.id = '123'
341
+ photo.version = 1
342
+ photo.persisted!
343
+ stub_es_bulk(
344
+ 'index' => { '_index' => 'default', '_type' => 'photo', '_id' => '123', '_version' => 2, 'ok' => true }
345
+ )
346
+ Elastictastic.bulk { photo.save }
347
+ bulk_requests.first['index']['_routing'].should == '1'
348
+ end
349
+
350
+ it 'should include routing on destroy' do
351
+ photo.id = '123'
352
+ photo.version = 1
353
+ photo.persisted!
354
+ stub_es_bulk(
355
+ 'delete' => { '_index' => 'default', '_type' => 'post', '_id' => '123', '_version' => 2, 'ok' => true }
356
+ )
357
+ Elastictastic.bulk { photo.destroy }
358
+ bulk_requests.first['delete']['_routing'].should == '1'
359
+ end
232
360
  end
233
361
  end
@@ -15,6 +15,10 @@ describe Elastictastic::Callbacks do
15
15
  before_create :before_create_ran!
16
16
  before_update :before_update_ran!
17
17
  before_destroy :before_destroy_ran!
18
+ after_save :after_save_ran!
19
+ after_create :after_create_ran!
20
+ after_update :after_update_ran!
21
+ after_destroy :after_destroy_ran!
18
22
 
19
23
  def hooks_that_ran
20
24
  @hooks_that_ran ||= Set[]
@@ -45,52 +49,79 @@ describe Elastictastic::Callbacks do
45
49
  end
46
50
 
47
51
  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)
52
+ stub_es_create('default', 'my_model')
53
+ stub_es_update('default', 'my_model', id)
54
+ stub_es_destroy('default', 'my_model', id)
51
55
  end
52
56
 
53
- describe '#before_save' do
57
+ %w(before after).each do |position|
58
+ describe "##{position}_save" do
54
59
 
55
- it 'should run before create' do
56
- instance.save
57
- instance.should have_run_hook(:before_save)
58
- end
60
+ it "should run #{position} create" do
61
+ instance.save
62
+ instance.should have_run_hook(:"#{position}_save")
63
+ end
59
64
 
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
+ it 'should run before update' do
66
+ persisted_instance.save
67
+ persisted_instance.should have_run_hook(:"#{position}_save")
68
+ end
65
69
 
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
70
+ it 'should not run before create when callbacks disabled' do
71
+ instance.save(:callbacks => false)
72
+ instance.should_not have_run_hook(:"#{position}_save")
73
+ end
71
74
 
72
- it 'should run before update' do
73
- persisted_instance.save
74
- persisted_instance.should_not have_run_hook(:before_create)
75
+ it 'should not run before update when hooks disabled' do
76
+ persisted_instance.save(:callbacks => false)
77
+ instance.should_not have_run_hook(:"#{position}_save")
78
+ end
75
79
  end
76
- end
77
80
 
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
81
+ describe "##{position}_create" do
82
+ it "should run #{position} create" do
83
+ instance.save
84
+ instance.should have_run_hook(:"#{position}_create")
85
+ end
86
+
87
+ it "should not run #{position} update" do
88
+ persisted_instance.save
89
+ persisted_instance.should_not have_run_hook(:"#{position}_create")
90
+ end
91
+
92
+ it "should not run #{position} create when callbacks disabled" do
93
+ instance.save(:callbacks => false)
94
+ instance.should_not have_run_hook(:"#{position}_create")
95
+ end
82
96
  end
83
97
 
84
- it 'should run before update' do
85
- persisted_instance.save
86
- persisted_instance.should have_run_hook(:before_update)
98
+ describe "##{position}_update" do
99
+ it "should not run #{position} create" do
100
+ instance.save
101
+ instance.should_not have_run_hook :"#{position}_update"
102
+ end
103
+
104
+ it "should run #{position} update" do
105
+ persisted_instance.save
106
+ persisted_instance.should have_run_hook(:"#{position}_update")
107
+ end
108
+
109
+ it "should not run #{position} update if callbacks disabled" do
110
+ persisted_instance.save(:callbacks => false)
111
+ persisted_instance.should_not have_run_hook(:"#{position}_update")
112
+ end
87
113
  end
88
- end
89
114
 
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)
115
+ describe "##{position}_destroy" do
116
+ it "should run #{position} destroy" do
117
+ persisted_instance.destroy
118
+ persisted_instance.should have_run_hook(:"#{position}_destroy")
119
+ end
120
+
121
+ it "should not run #{position} destroy if callbacks disabled" do
122
+ persisted_instance.destroy(:callbacks => false)
123
+ persisted_instance.should_not have_run_hook(:"#{position}_destroy")
124
+ end
94
125
  end
95
126
  end
96
127
  end