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
@@ -0,0 +1,417 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'ruby-debug'
3
+
4
+ describe Elastictastic::OptimisticLocking do
5
+ include Elastictastic::TestHelpers
6
+
7
+ shared_examples_for 'updatable scope' do
8
+ let :post do
9
+ scope.new.tap do |post|
10
+ post.id = '123abc'
11
+ post.version = 1
12
+ post.persisted!
13
+ end
14
+ end
15
+
16
+ context 'when save is cancelled' do
17
+ before do
18
+ stub_request_json(
19
+ :get,
20
+ match_es_resource(index, 'post', '123abc'),
21
+ generate_es_hit('post', :id => '123abc', :index => index, :version => 1).merge('exists' => true),
22
+ generate_es_hit('post', :id => '123abc', :index => index, :version => 2, :source => { :title => 'Hey' }).merge('exists' => true)
23
+ )
24
+ end
25
+
26
+ describe '::update' do
27
+ before do
28
+ scope.update('123abc') do
29
+ raise Elastictastic::CancelSave
30
+ end
31
+ end
32
+
33
+ it 'should have 1 request for fetch' do
34
+ FakeWeb.should have(1).requests # 1 for fetch
35
+ end
36
+ end
37
+
38
+ describe '::create_or_update' do
39
+ before do
40
+ scope.create_or_update('123abc') do
41
+ raise Elastictastic::CancelSave
42
+ end
43
+ end
44
+
45
+ it 'should make no requests' do
46
+ FakeWeb.should have(0).requests
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'when version conflict raised from discrete persistence' do
52
+ describe '#save' do
53
+ before do
54
+ stub_request_json(
55
+ :put,
56
+ match_es_resource(index, 'post', '123abc'),
57
+ version_conflict
58
+ )
59
+ end
60
+
61
+ it 'should raise VersionConflict' do
62
+ expect { post.save }.to raise_error(Elastictastic::ServerError::VersionConflictEngineException)
63
+ end
64
+
65
+ it 'should yield VersionConflict when called with block' do
66
+ ex = nil
67
+ post.save { |e| ex = e }
68
+ ex.should be_a(Elastictastic::ServerError::VersionConflictEngineException)
69
+ end
70
+ end # describe '#save'
71
+
72
+ describe '::update' do
73
+ before do
74
+ stub_request_json(
75
+ :get,
76
+ match_es_resource(index, 'post', '123abc'),
77
+ generate_es_hit('post', :id => '123abc', :index => index, :version => 1).merge('exists' => true),
78
+ generate_es_hit('post', :id => '123abc', :index => index, :version => 2, :source => { :title => 'Hey' }).merge('exists' => true)
79
+ )
80
+ stub_request_json(
81
+ :put,
82
+ match_es_resource(index, 'post', '123abc'),
83
+ version_conflict,
84
+ generate_es_hit('post', :id => '123abc', :version => 3, :index => index)
85
+ )
86
+ scope.update('123abc') do |post|
87
+ post.comments_count = 3
88
+ end
89
+ end
90
+
91
+ it 'should make four requests' do
92
+ FakeWeb.should have(4).requests
93
+ end
94
+
95
+ it 'should add preference=_primary to get request' do
96
+ URI.parse(FakeWeb.requests.first.path).query.split('&').
97
+ should include('preference=_primary_first')
98
+ end
99
+
100
+ it 'should make final update with modification in update block' do
101
+ last_request_json['comments_count'].should == 3
102
+ end
103
+
104
+ it 'should make final update with data from latest version of doc' do
105
+ last_request_json['title'].should == 'Hey'
106
+ end
107
+
108
+ it 'should make final update with correct version' do
109
+ last_request_uri.query.split('&').should include('version=2')
110
+ end
111
+
112
+ it 'should send final update to correct index' do
113
+ last_request_uri.path.split('/')[1].should == index
114
+ end
115
+ end # describe '::update'
116
+
117
+ describe '::update_each' do
118
+ let(:last_update_request) do
119
+ FakeWeb.requests.reverse.find { |req| req.method == 'PUT' }
120
+ end
121
+
122
+ before do
123
+ stub_es_scan(
124
+ index, 'post', 100,
125
+ generate_es_hit('post', :index => index, :id => '1'),
126
+ generate_es_hit('post', :index => index, :id => '2')
127
+ )
128
+ stub_es_get(index, 'post', '2', { :title => 'Hey' }, 2)
129
+ stub_es_update(index, 'post', '1')
130
+ stub_request_json(
131
+ :put,
132
+ match_es_resource(index, 'post', '2'),
133
+ version_conflict,
134
+ generate_es_hit('post', :id => '2', :index => index, :version => 3)
135
+ )
136
+ scope.update_each do |post|
137
+ post.comments_count = 2
138
+ end
139
+ end
140
+
141
+ it 'should retry unsuccessful updates' do
142
+ FakeWeb.should have(7).requests # initiate scan, 2 cursors, update '1', update '2' (fail), get '2', update '2'
143
+ end
144
+
145
+ it 'should re-perform update on failed document' do
146
+ URI.parse(last_update_request.path).path.should == "/#{index}/post/2"
147
+ end
148
+
149
+ it 'should send data from latest version in persistence' do
150
+ Elastictastic.json_decode(last_update_request.body)['title'].should == 'Hey'
151
+ end
152
+
153
+ it 'should send data from update block' do
154
+ Elastictastic.json_decode(last_update_request.body)['comments_count'].should == 2
155
+ end
156
+
157
+ it 'should update with latest version' do
158
+ URI.parse(last_update_request.path).query.split('&').should include('version=2')
159
+ end
160
+ end # describe '::update_each'
161
+ end # context 'when version conflict raised from discrete persistence'
162
+
163
+ context 'when version conflict raised from bulk persistence' do
164
+ describe '#save' do
165
+ before do
166
+ stub_es_bulk(
167
+ 'index' => {
168
+ '_index' => index, '_type' => 'post', '_id' => '123abc',
169
+ 'error' => version_conflict['error']
170
+ }
171
+ )
172
+ end
173
+
174
+ it 'should raise error' do
175
+ expect { Elastictastic.bulk { post.save }}.to raise_error(Elastictastic::ServerError::VersionConflictEngineException)
176
+ end
177
+
178
+ it 'should yield an error when called with block' do
179
+ ex = nil
180
+ Elastictastic.bulk { post.save { |e| ex = e }}
181
+ ex.should be_a(Elastictastic::ServerError::VersionConflictEngineException)
182
+ end
183
+ end
184
+
185
+ describe '::update' do
186
+ before do
187
+ stub_request_json(
188
+ :get,
189
+ match_es_resource(index, 'post', 'abc123'),
190
+ generate_es_hit('post', :id => 'abc123', :index => index, :version => 1),
191
+ generate_es_hit('post', :id => 'abc123', :index => index, :version => 2, :source => { :title => 'Hey' })
192
+ )
193
+ stub_es_bulk(
194
+ 'index' => {
195
+ '_index' => index, '_type' => 'post', '_id' => '123abc',
196
+ 'error' => version_conflict['error']
197
+ }
198
+ )
199
+ stub_es_update(index, 'post', 'abc123')
200
+
201
+ Elastictastic.bulk do
202
+ scope.update('abc123') do |post|
203
+ post.comments_count = 2
204
+ end
205
+ end
206
+ end
207
+
208
+ it 'should make 4 requests' do
209
+ FakeWeb.should have(4).requests
210
+ end
211
+
212
+ it 'should send data from block in last request' do
213
+ last_request_json['comments_count'].should == 2
214
+ end
215
+
216
+ it 'should send data from most recent version in last request' do
217
+ last_request_json['title'].should == 'Hey'
218
+ end
219
+
220
+ it 'should send correct version in last request' do
221
+ last_request_uri.query.split('&').should include('version=2')
222
+ end
223
+
224
+ it 'should send last request to correct index' do
225
+ last_request_uri.path.split('/')[1].should == index
226
+ end
227
+ end # describe '::update'
228
+
229
+ describe '::update_each' do
230
+ before do
231
+ stub_es_scan(
232
+ index, 'post', 100,
233
+ generate_es_hit('post', :index => index, :id => '1'),
234
+ generate_es_hit('post', :index => index, :id => '2')
235
+ )
236
+ stub_request_json(
237
+ :post,
238
+ match_es_path('/_bulk'),
239
+ 'items' => [
240
+ { 'index' => generate_es_hit('post', :index => index, :id => '1').except('_source').merge('ok' => true) },
241
+ { 'index' => generate_es_hit('post', :index => index, :id => '2').except('_source').merge(version_conflict.slice('error')) }
242
+ ]
243
+ )
244
+ stub_es_get(index, 'post', '2', { 'title' => 'Hey' }, 2)
245
+ stub_es_update(index, 'post', '2', 3)
246
+ Elastictastic.bulk do
247
+ scope.update_each { |post| post.comments_count = 2 }
248
+ end
249
+ end
250
+
251
+ it 'should retry failed update' do
252
+ FakeWeb.should have(6).requests # start scan, 2 cursor reads, bulk update, reload '2', update '2'
253
+ end
254
+
255
+ it 'should update failed document' do
256
+ last_request_uri.path.should == "/#{index}/post/2"
257
+ end
258
+
259
+ it 'should update conflicted document with reloaded data' do
260
+ last_request_json['title'].should == 'Hey'
261
+ end
262
+
263
+ it 'should update conflicted document with data from block' do
264
+ last_request_json['comments_count'].should == 2
265
+ end
266
+
267
+ it 'should update conflicted document with proper version' do
268
+ last_request_uri.query.split('&').should include('version=2')
269
+ end
270
+ end
271
+ end # context 'when version conflict raised from bulk persistence'
272
+ end # shared_examples_for 'updatable scope'
273
+
274
+ describe 'default scope' do
275
+ let(:scope) { Post }
276
+ let(:index) { 'default' }
277
+ let :version_conflict do
278
+ {
279
+ 'error' => "VersionConflictEngineException: [[#{index}][3] [post][abc123]: version conflict, current[2], required[1]]",
280
+ 'status' => 409
281
+ }
282
+ end
283
+ it_should_behave_like 'updatable scope'
284
+ end
285
+
286
+ describe 'scoped in index' do
287
+ let(:scope) { Post.in_index('my_index') }
288
+ let(:index) { 'my_index' }
289
+ let :version_conflict do
290
+ {
291
+ 'error' => "VersionConflictEngineException: [[#{index}][3] [post][abc123]: version conflict, current[2], required[1]]",
292
+ 'status' => 409
293
+ }
294
+ end
295
+ it_should_behave_like 'updatable scope'
296
+ end
297
+
298
+ describe 'default scope with nested exception' do
299
+ let(:scope) { Post }
300
+ let(:index) { 'default' }
301
+ let(:version_conflict) do
302
+ {
303
+ 'error' => "RemoteTransportException: [[server][inet[/ip]][/index]]; nested: VersionConflictEngineException[[#{index}][0] [[post][abc123]: version conflict, current [2], required [1]]",
304
+ 'status' => 409
305
+ }
306
+ end
307
+ it_should_behave_like 'updatable scope'
308
+ end
309
+
310
+ describe 'scoped in index with nested exception' do
311
+ let(:scope) { Post.in_index('my_index') }
312
+ let(:index) { 'my_index' }
313
+ let(:version_conflict) do
314
+ {
315
+ 'error' => "RemoteTransportException: [[server][inet[/ip]][/index]]; nested: VersionConflictEngineException[[#{index}][0] [[post][abc123]: version conflict, current [2], required [1]]",
316
+ 'status' => 409
317
+ }
318
+ end
319
+ it_should_behave_like 'updatable scope'
320
+ end
321
+
322
+ context '::update called on nonexistent document' do
323
+ before do
324
+ stub_es_get('default', 'post', '1', nil)
325
+ end
326
+
327
+ it 'should not do anything' do
328
+ expect { Post.update('1') { |post| post.title = 'bogus' }}.to_not raise_error
329
+ end
330
+ end
331
+
332
+ describe '::create_or_update' do
333
+ let :already_exists do
334
+ {
335
+ 'error' => 'DocumentAlreadyExistsEngineException: [[my_index][2] [post][1]: document already exists]',
336
+ 'status' => 409
337
+ }
338
+ end
339
+
340
+ let :version_conflict do
341
+ {
342
+ 'error' => "VersionConflictEngineException: [[my_index][3] [post][1]: version conflict, current[2], required[1]]",
343
+ 'status' => 409
344
+ }
345
+ end
346
+
347
+ context 'with discrete persistence' do
348
+ context "when document doesn't already exist" do
349
+ before do
350
+ stub_es_create('my_index', 'post', '1')
351
+ Post.in_index('my_index').create_or_update('1') { |post| post.title = 'hey' }
352
+ end
353
+
354
+ it 'should post to create endpoint' do
355
+ last_request.path.should == '/my_index/post/1/_create'
356
+ end
357
+
358
+ it 'should yield before saving' do
359
+ last_request_json['title'].should == 'hey'
360
+ end
361
+ end
362
+
363
+ context "when document already exists" do
364
+ before do
365
+ stub_request_json(
366
+ :put,
367
+ match_es_path('/my_index/post/1/_create'),
368
+ already_exists
369
+ )
370
+ stub_es_get('my_index', 'post', '1', :comments_count => 2)
371
+ stub_es_update('my_index', 'post', '1')
372
+ Post.in_index('my_index').create_or_update('1') { |post| post.title = 'hey' }
373
+ end
374
+
375
+ it 'should re-update data with correct version' do
376
+ last_request.path.should == '/my_index/post/1?version=1'
377
+ end
378
+
379
+ it 'should include data from storage' do
380
+ last_request_json['comments_count'].should == 2
381
+ end
382
+
383
+ it 'should include updated data from block' do
384
+ last_request_json['title'].should == 'hey'
385
+ end
386
+ end
387
+ end
388
+
389
+ context 'with bulk persistence' do
390
+ context 'when document already exists' do
391
+ before do
392
+ stub_es_bulk(
393
+ 'create' => {
394
+ '_index' => 'my_index', '_type' => 'post', '_id' => '1',
395
+ 'error' => already_exists['error']
396
+ }
397
+ )
398
+ stub_es_get('my_index', 'post', '1', 'comments_count' => 2)
399
+ stub_es_update('my_index', 'post', '1')
400
+ Elastictastic.bulk { Post.in_index('my_index').create_or_update('1') { |post| post.title = 'hey' } }
401
+ end
402
+
403
+ it 'should send update' do
404
+ last_request.path.should == '/my_index/post/1?version=1'
405
+ end
406
+
407
+ it 'should send data from existing document' do
408
+ last_request_json['comments_count'].should == 2
409
+ end
410
+
411
+ it 'should send data from block' do
412
+ last_request_json['title'].should == 'hey'
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end
@@ -12,7 +12,7 @@ describe Elastictastic::ParentChild do
12
12
  describe 'child instance' do
13
13
  let(:post) { blog.posts.new }
14
14
  let(:blog) do
15
- stub_elasticsearch_create('default', 'blog')
15
+ stub_es_create('default', 'blog')
16
16
  Blog.new.tap { |blog| blog.save }
17
17
  end
18
18
 
@@ -24,40 +24,74 @@ describe Elastictastic::ParentChild do
24
24
  blog.posts.from_hash('title' => 'hey').blog.should == blog
25
25
  end
26
26
 
27
+ describe 'when retrieved directly' do
28
+ let(:post) { Post.in_index('my_index').fields('_source', '_parent').first }
29
+
30
+ before do
31
+ stub_es_search(
32
+ 'my_index', 'post',
33
+ 'hits' => {
34
+ 'total' => 1,
35
+ 'hits' => [{
36
+ '_id' => '2',
37
+ '_type' => 'post',
38
+ '_index' => 'my_index',
39
+ '_source' => {},
40
+ 'fields' => { '_parent' => '1' }
41
+ }]
42
+ }
43
+ )
44
+
45
+ stub_es_get(
46
+ 'my_index', 'blog', '1',
47
+ { 'name' => 'Awesome Blog' }
48
+ )
49
+ end
50
+
51
+ it 'should retrieve parent via GET' do
52
+ post.blog.name.should == 'Awesome Blog'
53
+ end
54
+
55
+ it 'should lookup in correct index' do
56
+ post.blog
57
+ FakeWeb.last_request.path.split('?').first.should == '/my_index/blog/1'
58
+ end
59
+ end
60
+
27
61
  describe 'discrete persistence' do
28
62
  it 'should pass parent param on create' do
29
- stub_elasticsearch_create('default', 'post')
63
+ stub_es_create('default', 'post')
30
64
  post.save
31
65
  URI.parse(FakeWeb.last_request.path).query.should == "parent=#{blog.id}"
32
66
  end
33
67
 
34
68
  it 'should pass parent param on update' do
35
- stub_elasticsearch_create('default', 'post')
69
+ stub_es_create('default', 'post')
36
70
  post.save
37
- stub_elasticsearch_update('default', 'post', post.id)
71
+ stub_es_update('default', 'post', post.id)
38
72
  post.save
39
- URI.parse(FakeWeb.last_request.path).query.should == "parent=#{blog.id}"
73
+ URI.parse(FakeWeb.last_request.path).query.split('&').should include("parent=#{blog.id}")
40
74
  end
41
75
 
42
76
  it 'should pass parent on delete' do
43
- stub_elasticsearch_create('default', 'post')
77
+ stub_es_create('default', 'post')
44
78
  post = blog.posts.new
45
79
  post.save
46
- stub_elasticsearch_destroy('default', 'post', post.id)
80
+ stub_es_destroy('default', 'post', post.id)
47
81
  post.destroy
48
- URI.parse(FakeWeb.last_request.path).query.should == "parent=#{blog.id}"
82
+ URI.parse(FakeWeb.last_request.path).query.split('&').should include("parent=#{blog.id}")
49
83
  end
50
84
  end
51
85
 
52
86
  describe 'bulk persistence' do
53
87
  let(:bulk_requests) do
54
88
  FakeWeb.last_request.body.split("\n").map do |line|
55
- JSON.parse(line)
89
+ Elastictastic.json_decode(line)
56
90
  end
57
91
  end
58
92
 
59
93
  before do
60
- stub_elasticsearch_bulk
94
+ stub_es_bulk
61
95
  end
62
96
 
63
97
  it 'should pass parent param on create' do
@@ -102,7 +136,7 @@ describe Elastictastic::ParentChild do
102
136
  end
103
137
 
104
138
  it 'should set index' do
105
- stub_elasticsearch_create('my_index', 'blog')
139
+ stub_es_create('my_index', 'blog')
106
140
  blog = Blog.in_index('my_index').new
107
141
  blog.save
108
142
  post = blog.posts.new
@@ -112,7 +146,7 @@ describe Elastictastic::ParentChild do
112
146
 
113
147
  describe 'collection proxies' do
114
148
  let(:blog) do
115
- stub_elasticsearch_create('my_index', 'blog')
149
+ stub_es_create('my_index', 'blog')
116
150
  Blog.in_index('my_index').new.tap { |blog| blog.save }
117
151
  end
118
152
  let(:posts) { blog.posts }
@@ -137,20 +171,20 @@ describe Elastictastic::ParentChild do
137
171
  end
138
172
 
139
173
  it 'should search correct index' do
140
- stub_elasticsearch_scan('my_index', 'post', 100, { '_id' => '1' })
174
+ stub_es_scan('my_index', 'post', 100, { '_id' => '1' })
141
175
  posts.to_a.first.id.should == '1'
142
176
  end
143
177
 
144
178
  it 'should set routing to parent ID on get' do
145
- stub_elasticsearch_get('my_index', 'post', 1)
179
+ stub_es_get('my_index', 'post', 1)
146
180
  blog.posts.find(1)
147
181
  URI.parse(FakeWeb.last_request.path).query.should == "routing=#{blog.id}"
148
182
  end
149
183
 
150
184
  it 'should set routing to parent ID on multiget' do
151
- stub_elasticsearch_mget('my_index', 'post')
185
+ stub_es_mget('my_index', 'post')
152
186
  blog.posts.find(1, 2)
153
- JSON.parse(FakeWeb.last_request.body).should == {
187
+ Elastictastic.json_decode(FakeWeb.last_request.body).should == {
154
188
  'docs' => [
155
189
  { '_id' => 1, 'routing' => blog.id },
156
190
  { '_id' => 2, 'routing' => blog.id }
@@ -160,14 +194,14 @@ describe Elastictastic::ParentChild do
160
194
 
161
195
  it 'should save transient instances when parent is saved' do
162
196
  post = posts.new
163
- stub_elasticsearch_update('my_index', 'blog', blog.id)
164
- stub_elasticsearch_create('my_index', 'post')
197
+ stub_es_update('my_index', 'blog', blog.id)
198
+ stub_es_create('my_index', 'post')
165
199
  blog.save
166
200
  post.should be_persisted
167
201
  end
168
202
 
169
203
  it 'should not attempt to save transient instances that are pending for save in a bulk block' do
170
- stub_elasticsearch_bulk(
204
+ stub_es_bulk(
171
205
  { 'create' => { '_index' => 'my_index', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }},
172
206
  { 'index' => { '_index' => 'my_index', '_type' => 'blog', '_id' => blog.id, '_version' => 2, 'ok' => true }}
173
207
  )
@@ -177,34 +211,34 @@ describe Elastictastic::ParentChild do
177
211
  blog.save
178
212
  end
179
213
  request_statements =
180
- FakeWeb.last_request.body.each_line.map { |line| JSON.parse(line) }
214
+ FakeWeb.last_request.body.each_line.map { |line| Elastictastic.json_decode(line) }
181
215
  request_statements.length.should == 4
182
216
  end
183
217
 
184
218
  it 'should not save transient instances again' do
185
219
  post = posts.new
186
- stub_elasticsearch_update('my_index', 'blog', blog.id)
187
- stub_elasticsearch_create('my_index', 'post')
220
+ stub_es_update('my_index', 'blog', blog.id)
221
+ stub_es_create('my_index', 'post')
188
222
  blog.save
189
223
  FakeWeb.clean_registry
190
- stub_elasticsearch_update('my_index', 'blog', blog.id)
224
+ stub_es_update('my_index', 'blog', blog.id)
191
225
  expect { blog.save }.to_not raise_error
192
226
  end
193
227
 
194
228
  it 'should populate parent when finding one' do
195
- stub_elasticsearch_get('my_index', 'post', '1')
229
+ stub_es_get('my_index', 'post', '1')
196
230
  blog.posts.find('1').blog.should == blog
197
231
  end
198
232
 
199
233
  it 'should populate parent when finding many' do
200
- stub_elasticsearch_mget('my_index', 'post', '1', '2')
234
+ stub_es_mget('my_index', 'post', '1', '2')
201
235
  blog.posts.find('1', '2').each do |post|
202
236
  post.blog.should == blog
203
237
  end
204
238
  end
205
239
 
206
240
  it 'should populate parent when retrieving first' do
207
- stub_elasticsearch_search(
241
+ stub_es_search(
208
242
  'my_index', 'post',
209
243
  'total' => 1,
210
244
  'hits' => { 'hits' => [{ '_id' => '2', 'index' => 'my_index', '_type' => 'post' }]}
@@ -213,7 +247,7 @@ describe Elastictastic::ParentChild do
213
247
  end
214
248
 
215
249
  it 'should populate parent when iterating over cursor' do
216
- stub_elasticsearch_scan(
250
+ stub_es_scan(
217
251
  'my_index', 'post', 100,
218
252
  '_id' => '1'
219
253
  )
@@ -221,7 +255,7 @@ describe Elastictastic::ParentChild do
221
255
  end
222
256
 
223
257
  it 'should populate parent when paginating' do
224
- stub_elasticsearch_search(
258
+ stub_es_search(
225
259
  'my_index', 'post',
226
260
  'hits' => {
227
261
  'total' => 1,
@@ -232,15 +266,21 @@ describe Elastictastic::ParentChild do
232
266
  end
233
267
 
234
268
  it 'should iterate over transient instances along with retrieved results' do
235
- stub_elasticsearch_scan('my_index', 'post', 100, '_id' => '1')
269
+ stub_es_scan('my_index', 'post', 100, '_id' => '1')
236
270
  post = blog.posts.new
237
271
  posts = blog.posts.to_a
238
272
  posts[0].id.should == '1'
239
273
  posts[1].should == post
240
274
  end
241
275
 
276
+ it 'should iterate over only transient instances if parent is transient' do
277
+ blog = Blog.new
278
+ post = blog.posts.new
279
+ blog.posts.to_a.should == [post]
280
+ end
281
+
242
282
  it 'should return transient instance as #first if no persisted results' do
243
- stub_elasticsearch_search(
283
+ stub_es_search(
244
284
  'my_index', 'post', 'hits' => { 'total' => 0, 'hits' => [] })
245
285
  post = blog.posts.new
246
286
  blog.posts.first.should == post
@@ -274,7 +314,7 @@ describe Elastictastic::ParentChild do
274
314
 
275
315
  describe 'searching children directly' do
276
316
  before do
277
- stub_elasticsearch_search(
317
+ stub_es_search(
278
318
  'default', 'post',
279
319
  'hits' => {
280
320
  'hits' => [
@@ -291,7 +331,7 @@ describe Elastictastic::ParentChild do
291
331
  let(:post) { Post.first }
292
332
 
293
333
  it 'should provide access to parent' do
294
- stub_elasticsearch_get('default', 'blog', '3')
334
+ stub_es_get('default', 'blog', '3')
295
335
  post.blog.id.should == '3'
296
336
  end
297
337
 
@@ -300,7 +340,7 @@ describe Elastictastic::ParentChild do
300
340
  end
301
341
 
302
342
  it 'should save post without dereferencing parent' do
303
- stub_elasticsearch_update('default', 'post', post.id)
343
+ stub_es_update('default', 'post', post.id)
304
344
  post.save
305
345
  URI.parse(FakeWeb.last_request.path).query.should == 'parent=3'
306
346
  end