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
@@ -1,172 +1,227 @@
1
- require 'fakeweb'
1
+ begin
2
+ require 'fakeweb'
3
+ rescue LoadError => e
4
+ raise LoadError, "Elastictastic::TestHelpers requires the 'fakeweb' gem."
5
+ end
2
6
 
3
7
  module Elastictastic
4
8
  module TestHelpers
5
9
  ALPHANUM = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a
6
10
 
7
- def stub_elasticsearch_create(index, type, *args)
8
- options = args.extract_options!
9
- id = args.pop
11
+ def stub_es_create(index, type, id = nil)
10
12
  if id.nil?
11
- id = ''
12
- 22.times { id << ALPHANUM[rand(ALPHANUM.length)] }
13
- path = "/#{index}/#{type}"
13
+ id = generate_es_id
14
+ components = [index, type]
14
15
  method = :post
15
16
  else
16
- path = "/#{index}/#{type}/#{id}/_create"
17
+ components = [index, type, id, '_create']
17
18
  method = :put
18
19
  end
19
20
 
20
- FakeWeb.register_uri(
21
+ stub_request_json(
21
22
  method,
22
- /^#{Regexp.escape(TestHelpers.uri_for_path(path))}(\?.*)?$/,
23
- options.reverse_merge(:body => {
24
- 'ok' => 'true',
25
- '_index' => index,
26
- '_type' => type,
27
- '_id' => id
28
- }.to_json)
23
+ match_es_resource(components),
24
+ generate_es_hit(type, :id => id, :index => index).merge('ok' => 'true')
29
25
  )
30
26
  id
31
27
  end
32
28
 
33
- def stub_elasticsearch_update(index, type, id)
34
- FakeWeb.register_uri(
29
+ def stub_es_update(index, type, id, version = 2)
30
+ stub_request_json(
35
31
  :put,
36
- /^#{TestHelpers.uri_for_path("/#{index}/#{type}/#{id}")}(\?.*)?$/,
37
- :body => {
38
- 'ok' => 'true',
39
- '_index' => index,
40
- '_type' => type,
41
- '_id' => id
42
- }.to_json
32
+ match_es_resource(index, type, id),
33
+ generate_es_hit(type, :index => index, :id => id, :version => version)
34
+ )
35
+ end
36
+
37
+ def stub_es_head(index, type, id, exists)
38
+ stub_request(
39
+ :head,
40
+ match_es_resource(index, type, id),
41
+ :status => (exists ? 200 : 404),
42
+ :body => nil
43
43
  )
44
44
  end
45
45
 
46
- def stub_elasticsearch_get(index, type, id, doc = {})
47
- FakeWeb.register_uri(
46
+ def stub_es_get(index, type, id, doc = {}, version = 1)
47
+ stub_request_json(
48
48
  :get,
49
- /^#{Regexp.escape(TestHelpers.uri_for_path("/#{index}/#{type}/#{id}").to_s)}(\?.*)?$/,
50
- :body => {
51
- 'ok' => true,
52
- '_index' => index,
53
- '_type' => type,
54
- '_id' => id,
55
- '_source' => doc,
56
- 'exists' => !doc.nil?
57
- }.to_json
49
+ match_es_resource(index, type, id),
50
+ generate_es_hit(
51
+ type,
52
+ :index => index,
53
+ :id => id,
54
+ :version => version,
55
+ :source => doc
56
+ ).merge('exists' => !doc.nil?)
58
57
  )
59
58
  end
60
59
 
61
- def stub_elasticsearch_mget(index, type, *ids)
60
+ def stub_es_mget(index, type, *ids)
62
61
  given_ids_with_docs = ids.extract_options!
63
62
  ids_with_docs = {}
64
63
  ids.each { |id| ids_with_docs[id] = {} }
65
64
  ids_with_docs.merge!(given_ids_with_docs)
66
65
  path = index ? "/#{index}/#{type}/_mget" : "/_mget"
67
66
  docs = ids_with_docs.each_pair.map do |id, doc|
68
- id, index = *id if Array === id
69
- {
70
- '_index' => index,
71
- '_type' => type,
72
- '_id' => id,
73
- 'exists' => !!doc,
74
- '_source' => doc
75
- }
67
+ id, type, index = *id if Array === id
68
+ generate_es_hit(
69
+ type, :index => index, :id => id, :source => doc
70
+ ).merge('exists' => !!doc)
76
71
  end
77
72
 
78
- FakeWeb.register_uri(
73
+ stub_request_json(
79
74
  :post,
80
- TestHelpers.uri_for_path(path).to_s,
81
- :body => {
82
- 'docs' => docs
83
- }.to_json
75
+ match_es_path(path),
76
+ 'docs' => docs
84
77
  )
85
78
  end
86
79
 
87
- def stub_elasticsearch_destroy(index, type, id, options = {})
88
- FakeWeb.register_uri(
80
+ def stub_es_destroy(index, type, id, options = {})
81
+ stub_request_json(
89
82
  :delete,
90
- /^#{TestHelpers.uri_for_path("/#{index}/#{type}/#{id}")}(\?.*)?$/,
91
- options.reverse_merge(:body => {
92
- 'ok' => true,
93
- 'found' => true,
94
- '_index' => 'test',
95
- '_type' => 'test',
96
- '_id' => id,
97
- '_version' => 1
98
- }.to_json)
83
+ match_es_resource(index, type, id),
84
+ generate_es_hit(
85
+ type, :index => index, :id => id
86
+ ).merge('ok' => true, 'found' => true).merge(options)
99
87
  )
100
88
  end
101
89
 
102
- def stub_elasticsearch_destroy_all(index, type)
103
- FakeWeb.register_uri(
90
+ def stub_es_destroy_all(index, type)
91
+ stub_request_json(
104
92
  :delete,
105
- TestHelpers.uri_for_path("/#{index}/#{type}"),
106
- :body => { 'ok' => true }.to_json
93
+ match_es_resource(index, type),
94
+ 'ok' => true
107
95
  )
108
96
  end
109
97
 
110
- def stub_elasticsearch_bulk(*responses)
111
- FakeWeb.register_uri(
98
+ def stub_es_bulk(*responses)
99
+ stub_request_json(
112
100
  :post,
113
- TestHelpers.uri_for_path("/_bulk"),
114
- :body => { 'took' => 1, 'items' => responses }.to_json
101
+ match_es_path('/_bulk'),
102
+ 'took' => 1, 'items' => responses
115
103
  )
116
104
  end
117
105
 
118
- def stub_elasticsearch_put_mapping(index, type)
119
- FakeWeb.register_uri(
106
+ def stub_es_put_mapping(index, type)
107
+ stub_request_json(
120
108
  :put,
121
- TestHelpers.uri_for_path("/#{index}/#{type}/_mapping"),
122
- :body => { 'ok' => true, 'acknowledged' => true }.to_json
109
+ match_es_resource(index, type, '_mapping'),
110
+ 'ok' => true, 'acknowledged' => true
123
111
  )
124
112
  end
125
113
 
126
- def stub_elasticsearch_search(index, type, data)
114
+ def stub_es_search(index, type, data)
127
115
  if Array === data
128
116
  response = data.map do |datum|
129
- { :body => datum.to_json }
117
+ { :body => Elastictastic.json_encode(datum) }
130
118
  end
131
119
  else
132
- response = { :body => data.to_json }
120
+ response = { :body => Elastictastic.json_encode(data) }
133
121
  end
134
122
 
135
- uri = TestHelpers.uri_for_path("/#{index}/#{type}/_search").to_s
136
- FakeWeb.register_uri(
123
+ stub_request(
137
124
  :post,
138
- /^#{Regexp.escape(uri)}/,
125
+ match_es_resource(index, type, '_search'),
139
126
  response
140
127
  )
141
128
  end
142
129
 
143
- def stub_elasticsearch_scan(index, type, batch_size, *hits)
144
- scan_uri = Regexp.escape(TestHelpers.uri_for_path("/#{index}/#{type}/_search").to_s)
145
- scroll_ids = Array.new(batch_size + 1) { rand(10**100).to_s(36) }
146
- FakeWeb.register_uri(
130
+ def stub_es_msearch(*hits_collections)
131
+ responses = hits_collections.map do |collection|
132
+ { 'hits' => { 'hits' => collection, 'total' => collection.length }}
133
+ end
134
+ stub_request_json(
147
135
  :post,
148
- /^#{scan_uri}\?.*search_type=scan/,
149
- :body => {
150
- '_scroll_id' => scroll_ids.first,
151
- 'hits' => { 'total' => hits.length, 'hits' => [] }
152
- }.to_json
136
+ match_es_path('/_msearch'),
137
+ 'responses' => responses
153
138
  )
139
+ end
154
140
 
155
- batches = hits.each_slice(batch_size).each_with_index.map do |hit_batch, i|
156
- { :body => { '_scroll_id' => scroll_ids[i+1], 'hits' => { 'hits' => hit_batch }}.to_json }
141
+ def stub_es_msearch_count(*counts)
142
+ responses = counts.map do |count|
143
+ { 'hits' => { 'hits' => [], 'total' => count }}
157
144
  end
158
- batches << { :body => { 'hits' => { 'hits' => [] }}.to_json }
159
- scroll_uri = Regexp.escape(TestHelpers.uri_for_path("/_search/scroll").to_s)
160
- FakeWeb.register_uri(
145
+ stub_request_json(
161
146
  :post,
162
- /^#{scroll_uri}/,
163
- batches
147
+ match_es_path('/_msearch'),
148
+ 'responses' => responses
164
149
  )
150
+ end
151
+
152
+ def stub_es_scan(index, type, batch_size, *hits)
153
+ scroll_ids = Array.new(batch_size + 1) { rand(10**100).to_s(36) }
154
+ stub_request_json(
155
+ :post,
156
+ match_es_resource(index, type, '_search'),
157
+ '_scroll_id' => scroll_ids.first,
158
+ 'hits' => { 'total' => hits.length, 'hits' => [] }
159
+ )
160
+
161
+ batches = hits.each_slice(batch_size).each_with_index.map do |hit_batch, i|
162
+ {
163
+ :body => Elastictastic.json_encode(
164
+ '_scroll_id' => scroll_ids[i+1], 'hits' => { 'hits' => hit_batch }
165
+ )
166
+ }
167
+ end
168
+ batches << { :body => Elastictastic.json_encode('hits' => { 'hits' => [] }) }
169
+ stub_request(:post, match_es_path('/_search/scroll'), batches)
165
170
  scroll_ids
166
171
  end
167
172
 
168
- def self.uri_for_path(path)
169
- "#{Elastictastic.config.hosts.first}#{path}"
173
+ def self.match_es_path(path)
174
+ /^#{Regexp.escape(Elastictastic.config.hosts.first)}#{Regexp.escape(path)}(\?.*)?$/
175
+ end
176
+
177
+ def self.match_es_resource(*components)
178
+ match_es_path("/#{components.flatten.join('/')}")
179
+ end
180
+
181
+ def match_es_path(path)
182
+ TestHelpers.match_es_path(path)
183
+ end
184
+
185
+ def match_es_resource(*components)
186
+ TestHelpers.match_es_resource(*components)
187
+ end
188
+
189
+ def stub_request_json(method, uri, *responses)
190
+ json_responses = responses.map { |response| { :body => Elastictastic.json_encode(response) }}
191
+ json_responses = json_responses.first if json_responses.length == 1
192
+ stub_request(method, uri, json_responses)
193
+ end
194
+
195
+ def stub_request(method, url, options = {})
196
+ FakeWeb.register_uri(method, url, options)
197
+ end
198
+
199
+ def last_request
200
+ FakeWeb.last_request
201
+ end
202
+
203
+ def last_request_json
204
+ Elastictastic.json_decode(last_request.body)
205
+ end
206
+
207
+ def last_request_uri
208
+ URI.parse(last_request.path)
209
+ end
210
+
211
+ def generate_es_id
212
+ ''.tap do |id|
213
+ 22.times { id << ALPHANUM[rand(ALPHANUM.length)] }
214
+ end
215
+ end
216
+
217
+ def generate_es_hit(type, options = {})
218
+ {
219
+ '_id' => options[:id] || generate_es_id,
220
+ '_type' => type,
221
+ '_index' => options[:index] || Elastictastic.config.default_index,
222
+ '_version' => options[:version] || 1,
223
+ '_source' => options.key?(:source) ? options[:source] : {}
224
+ }
170
225
  end
171
226
  end
172
227
  end
@@ -0,0 +1,12 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.8.0-dev)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'elastictastic/thrift/types'
8
+
9
+ module Elastictastic
10
+ module Thrift
11
+ end
12
+ end
@@ -0,0 +1,83 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.8.0-dev)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'elastictastic/thrift/types'
9
+
10
+ module Elastictastic
11
+ module Thrift
12
+ module Rest
13
+ class Client
14
+ include ::Thrift::Client
15
+
16
+ def execute(request)
17
+ send_execute(request)
18
+ return recv_execute()
19
+ end
20
+
21
+ def send_execute(request)
22
+ send_message('execute', Execute_args, :request => request)
23
+ end
24
+
25
+ def recv_execute()
26
+ result = receive_message(Execute_result)
27
+ return result.success unless result.success.nil?
28
+ raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'execute failed: unknown result')
29
+ end
30
+
31
+ end
32
+
33
+ class Processor
34
+ include ::Thrift::Processor
35
+
36
+ def process_execute(seqid, iprot, oprot)
37
+ args = read_args(iprot, Execute_args)
38
+ result = Execute_result.new()
39
+ result.success = @handler.execute(args.request)
40
+ write_result(result, oprot, 'execute', seqid)
41
+ end
42
+
43
+ end
44
+
45
+ # HELPER FUNCTIONS AND STRUCTURES
46
+
47
+ class Execute_args
48
+ include ::Thrift::Struct, ::Thrift::Struct_Union
49
+ REQUEST = 1
50
+
51
+ FIELDS = {
52
+ REQUEST => {:type => ::Thrift::Types::STRUCT, :name => 'request', :class => Elastictastic::Thrift::RestRequest}
53
+ }
54
+
55
+ def struct_fields; FIELDS; end
56
+
57
+ def validate
58
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field request is unset!') unless @request
59
+ end
60
+
61
+ ::Thrift::Struct.generate_accessors self
62
+ end
63
+
64
+ class Execute_result
65
+ include ::Thrift::Struct, ::Thrift::Struct_Union
66
+ SUCCESS = 0
67
+
68
+ FIELDS = {
69
+ SUCCESS => {:type => ::Thrift::Types::STRUCT, :name => 'success', :class => Elastictastic::Thrift::RestResponse}
70
+ }
71
+
72
+ def struct_fields; FIELDS; end
73
+
74
+ def validate
75
+ end
76
+
77
+ ::Thrift::Struct.generate_accessors self
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,124 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.8.0-dev)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+
8
+ module Elastictastic
9
+ module Thrift
10
+ module Method
11
+ GET = 0
12
+ PUT = 1
13
+ POST = 2
14
+ DELETE = 3
15
+ HEAD = 4
16
+ OPTIONS = 5
17
+ VALUE_MAP = {0 => "GET", 1 => "PUT", 2 => "POST", 3 => "DELETE", 4 => "HEAD", 5 => "OPTIONS"}
18
+ VALID_VALUES = Set.new([GET, PUT, POST, DELETE, HEAD, OPTIONS]).freeze
19
+ end
20
+
21
+ module Status
22
+ CONT = 100
23
+ SWITCHING_PROTOCOLS = 101
24
+ OK = 200
25
+ CREATED = 201
26
+ ACCEPTED = 202
27
+ NON_AUTHORITATIVE_INFORMATION = 203
28
+ NO_CONTENT = 204
29
+ RESET_CONTENT = 205
30
+ PARTIAL_CONTENT = 206
31
+ MULTI_STATUS = 207
32
+ MULTIPLE_CHOICES = 300
33
+ MOVED_PERMANENTLY = 301
34
+ FOUND = 302
35
+ SEE_OTHER = 303
36
+ NOT_MODIFIED = 304
37
+ USE_PROXY = 305
38
+ TEMPORARY_REDIRECT = 307
39
+ BAD_REQUEST = 400
40
+ UNAUTHORIZED = 401
41
+ PAYMENT_REQUIRED = 402
42
+ FORBIDDEN = 403
43
+ NOT_FOUND = 404
44
+ METHOD_NOT_ALLOWED = 405
45
+ NOT_ACCEPTABLE = 406
46
+ PROXY_AUTHENTICATION = 407
47
+ REQUEST_TIMEOUT = 408
48
+ CONFLICT = 409
49
+ GONE = 410
50
+ LENGTH_REQUIRED = 411
51
+ PRECONDITION_FAILED = 412
52
+ REQUEST_ENTITY_TOO_LARGE = 413
53
+ REQUEST_URI_TOO_LONG = 414
54
+ UNSUPPORTED_MEDIA_TYPE = 415
55
+ REQUESTED_RANGE_NOT_SATISFIED = 416
56
+ EXPECTATION_FAILED = 417
57
+ UNPROCESSABLE_ENTITY = 422
58
+ LOCKED = 423
59
+ FAILED_DEPENDENCY = 424
60
+ INTERNAL_SERVER_ERROR = 500
61
+ NOT_IMPLEMENTED = 501
62
+ BAD_GATEWAY = 502
63
+ SERVICE_UNAVAILABLE = 503
64
+ GATEWAY_TIMEOUT = 504
65
+ INSUFFICIENT_STORAGE = 506
66
+ VALUE_MAP = {100 => "CONT", 101 => "SWITCHING_PROTOCOLS", 200 => "OK", 201 => "CREATED", 202 => "ACCEPTED", 203 => "NON_AUTHORITATIVE_INFORMATION", 204 => "NO_CONTENT", 205 => "RESET_CONTENT", 206 => "PARTIAL_CONTENT", 207 => "MULTI_STATUS", 300 => "MULTIPLE_CHOICES", 301 => "MOVED_PERMANENTLY", 302 => "FOUND", 303 => "SEE_OTHER", 304 => "NOT_MODIFIED", 305 => "USE_PROXY", 307 => "TEMPORARY_REDIRECT", 400 => "BAD_REQUEST", 401 => "UNAUTHORIZED", 402 => "PAYMENT_REQUIRED", 403 => "FORBIDDEN", 404 => "NOT_FOUND", 405 => "METHOD_NOT_ALLOWED", 406 => "NOT_ACCEPTABLE", 407 => "PROXY_AUTHENTICATION", 408 => "REQUEST_TIMEOUT", 409 => "CONFLICT", 410 => "GONE", 411 => "LENGTH_REQUIRED", 412 => "PRECONDITION_FAILED", 413 => "REQUEST_ENTITY_TOO_LARGE", 414 => "REQUEST_URI_TOO_LONG", 415 => "UNSUPPORTED_MEDIA_TYPE", 416 => "REQUESTED_RANGE_NOT_SATISFIED", 417 => "EXPECTATION_FAILED", 422 => "UNPROCESSABLE_ENTITY", 423 => "LOCKED", 424 => "FAILED_DEPENDENCY", 500 => "INTERNAL_SERVER_ERROR", 501 => "NOT_IMPLEMENTED", 502 => "BAD_GATEWAY", 503 => "SERVICE_UNAVAILABLE", 504 => "GATEWAY_TIMEOUT", 506 => "INSUFFICIENT_STORAGE"}
67
+ VALID_VALUES = Set.new([CONT, SWITCHING_PROTOCOLS, OK, CREATED, ACCEPTED, NON_AUTHORITATIVE_INFORMATION, NO_CONTENT, RESET_CONTENT, PARTIAL_CONTENT, MULTI_STATUS, MULTIPLE_CHOICES, MOVED_PERMANENTLY, FOUND, SEE_OTHER, NOT_MODIFIED, USE_PROXY, TEMPORARY_REDIRECT, BAD_REQUEST, UNAUTHORIZED, PAYMENT_REQUIRED, FORBIDDEN, NOT_FOUND, METHOD_NOT_ALLOWED, NOT_ACCEPTABLE, PROXY_AUTHENTICATION, REQUEST_TIMEOUT, CONFLICT, GONE, LENGTH_REQUIRED, PRECONDITION_FAILED, REQUEST_ENTITY_TOO_LARGE, REQUEST_URI_TOO_LONG, UNSUPPORTED_MEDIA_TYPE, REQUESTED_RANGE_NOT_SATISFIED, EXPECTATION_FAILED, UNPROCESSABLE_ENTITY, LOCKED, FAILED_DEPENDENCY, INTERNAL_SERVER_ERROR, NOT_IMPLEMENTED, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, INSUFFICIENT_STORAGE]).freeze
68
+ end
69
+
70
+ class RestRequest
71
+ include ::Thrift::Struct, ::Thrift::Struct_Union
72
+ METHOD = 1
73
+ URI = 2
74
+ PARAMETERS = 3
75
+ HEADERS = 4
76
+ BODY = 5
77
+
78
+ FIELDS = {
79
+ METHOD => {:type => ::Thrift::Types::I32, :name => 'method', :enum_class => Elastictastic::Thrift::Method},
80
+ URI => {:type => ::Thrift::Types::STRING, :name => 'uri'},
81
+ PARAMETERS => {:type => ::Thrift::Types::MAP, :name => 'parameters', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}, :optional => true},
82
+ HEADERS => {:type => ::Thrift::Types::MAP, :name => 'headers', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}, :optional => true},
83
+ BODY => {:type => ::Thrift::Types::STRING, :name => 'body', :binary => true, :optional => true}
84
+ }
85
+
86
+ def struct_fields; FIELDS; end
87
+
88
+ def validate
89
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field method is unset!') unless @method
90
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field uri is unset!') unless @uri
91
+ unless @method.nil? || Elastictastic::Thrift::Method::VALID_VALUES.include?(@method)
92
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field method!')
93
+ end
94
+ end
95
+
96
+ ::Thrift::Struct.generate_accessors self
97
+ end
98
+
99
+ class RestResponse
100
+ include ::Thrift::Struct, ::Thrift::Struct_Union
101
+ STATUS = 1
102
+ HEADERS = 2
103
+ BODY = 3
104
+
105
+ FIELDS = {
106
+ STATUS => {:type => ::Thrift::Types::I32, :name => 'status', :enum_class => Elastictastic::Thrift::Status},
107
+ HEADERS => {:type => ::Thrift::Types::MAP, :name => 'headers', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}, :optional => true},
108
+ BODY => {:type => ::Thrift::Types::STRING, :name => 'body', :binary => true, :optional => true}
109
+ }
110
+
111
+ def struct_fields; FIELDS; end
112
+
113
+ def validate
114
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field status is unset!') unless @status
115
+ unless @status.nil? || Elastictastic::Thrift::Status::VALID_VALUES.include?(@status)
116
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field status!')
117
+ end
118
+ end
119
+
120
+ ::Thrift::Struct.generate_accessors self
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,61 @@
1
+ begin
2
+ require 'thrift'
3
+ rescue LoadError
4
+ raise LoadError, 'Using the ThriftAdapter requires the "thrift" gem. Please install it.'
5
+ end
6
+ require 'cgi'
7
+ require 'faraday'
8
+ require 'elastictastic/thrift/rest'
9
+
10
+ module Elastictastic
11
+ class ThriftAdapter < Faraday::Adapter
12
+ def initialize(app, options = {})
13
+ super(app)
14
+ @options = options
15
+ end
16
+
17
+ def call(env)
18
+ super
19
+ url = env[:url]
20
+ req = env[:request]
21
+
22
+ request = Elastictastic::Thrift::RestRequest.new
23
+ request.method =
24
+ Elastictastic::Thrift::Method.const_get(env[:method].to_s.upcase)
25
+ request.body = env[:body] if env[:body]
26
+ request.uri = url.path
27
+ parameters = {}
28
+ request.parameters = url.query_values
29
+
30
+ response = thrift_request(url.host, url.inferred_port, request)
31
+
32
+ save_response(env, response.status, response.body) do |response_headers|
33
+ if response.headers
34
+ headers.each_pair do |key, value|
35
+ response_headers[key] = value
36
+ end
37
+ end
38
+ end
39
+
40
+ @app.call(env)
41
+ end
42
+
43
+ private
44
+
45
+ def thrift_request(host, port, request)
46
+ @thrift_clients ||= {}
47
+ client = @thrift_clients[[host, port]] ||=
48
+ begin
49
+ transport = ::Thrift::BufferedTransport.new(::Thrift::Socket.new(host, port, @options[:timeout]))
50
+ protocol = ::Thrift::BinaryProtocol.new(transport)
51
+ Elastictastic::Thrift::Rest::Client.new(protocol).tap do
52
+ transport.open
53
+ end
54
+ end
55
+ client.execute(request)
56
+ rescue ::Thrift::TransportException, IOError => e
57
+ @thrift_clients.delete([host, port])
58
+ raise Faraday::Error::ConnectionFailed, e.message
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,27 @@
1
+ module Elastictastic
2
+
3
+ module TransportMethods
4
+
5
+ def head(path)
6
+ request(:head, path)
7
+ end
8
+
9
+ def get(path)
10
+ request(:get, path)
11
+ end
12
+
13
+ def post(path, body)
14
+ request(:post, path, body)
15
+ end
16
+
17
+ def put(path, body)
18
+ request(:put, path, body)
19
+ end
20
+
21
+ def delete(path)
22
+ request(:delete, path)
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -15,22 +15,20 @@ module Elastictastic
15
15
  end
16
16
  end
17
17
 
18
- module InstanceMethods
19
- def save
20
- if valid?
21
- super
22
- true
23
- else
24
- false
25
- end
18
+ def save(options = {})
19
+ if options[:validate] == false || valid?
20
+ super
21
+ true
22
+ else
23
+ false
26
24
  end
25
+ end
27
26
 
28
- def save!
29
- if !save
30
- raise Elastictastic::RecordInvalid, errors.full_messages.to_sentence
31
- end
32
- self
27
+ def save!
28
+ if !save
29
+ raise Elastictastic::RecordInvalid, errors.full_messages.to_sentence
33
30
  end
31
+ self
34
32
  end
35
33
 
36
34
  class NestedValidator < ActiveModel::EachValidator
@@ -1,3 +1,3 @@
1
1
  module Elastictastic
2
- VERSION = '0.5.0'
2
+ VERSION = '0.10.2'
3
3
  end