elastictastic 0.5.0 → 0.10.2

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.
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