elastictastic 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. data/LICENSE +19 -0
  2. data/README.md +326 -0
  3. data/lib/elastictastic/association.rb +21 -0
  4. data/lib/elastictastic/bulk_persistence_strategy.rb +70 -0
  5. data/lib/elastictastic/callbacks.rb +30 -0
  6. data/lib/elastictastic/child_collection_proxy.rb +56 -0
  7. data/lib/elastictastic/client.rb +101 -0
  8. data/lib/elastictastic/configuration.rb +35 -0
  9. data/lib/elastictastic/dirty.rb +130 -0
  10. data/lib/elastictastic/discrete_persistence_strategy.rb +52 -0
  11. data/lib/elastictastic/document.rb +98 -0
  12. data/lib/elastictastic/errors.rb +7 -0
  13. data/lib/elastictastic/field.rb +38 -0
  14. data/lib/elastictastic/index.rb +19 -0
  15. data/lib/elastictastic/mass_assignment_security.rb +15 -0
  16. data/lib/elastictastic/middleware.rb +119 -0
  17. data/lib/elastictastic/nested_document.rb +29 -0
  18. data/lib/elastictastic/new_relic_instrumentation.rb +26 -0
  19. data/lib/elastictastic/observer.rb +3 -0
  20. data/lib/elastictastic/observing.rb +21 -0
  21. data/lib/elastictastic/parent_child.rb +115 -0
  22. data/lib/elastictastic/persistence.rb +67 -0
  23. data/lib/elastictastic/properties.rb +236 -0
  24. data/lib/elastictastic/railtie.rb +35 -0
  25. data/lib/elastictastic/resource.rb +4 -0
  26. data/lib/elastictastic/scope.rb +283 -0
  27. data/lib/elastictastic/scope_builder.rb +32 -0
  28. data/lib/elastictastic/scoped.rb +20 -0
  29. data/lib/elastictastic/search.rb +180 -0
  30. data/lib/elastictastic/server_error.rb +15 -0
  31. data/lib/elastictastic/test_helpers.rb +172 -0
  32. data/lib/elastictastic/util.rb +63 -0
  33. data/lib/elastictastic/validations.rb +45 -0
  34. data/lib/elastictastic/version.rb +3 -0
  35. data/lib/elastictastic.rb +82 -0
  36. data/spec/environment.rb +6 -0
  37. data/spec/examples/active_model_lint_spec.rb +20 -0
  38. data/spec/examples/bulk_persistence_strategy_spec.rb +233 -0
  39. data/spec/examples/callbacks_spec.rb +96 -0
  40. data/spec/examples/dirty_spec.rb +238 -0
  41. data/spec/examples/document_spec.rb +600 -0
  42. data/spec/examples/mass_assignment_security_spec.rb +13 -0
  43. data/spec/examples/middleware_spec.rb +92 -0
  44. data/spec/examples/observing_spec.rb +141 -0
  45. data/spec/examples/parent_child_spec.rb +308 -0
  46. data/spec/examples/properties_spec.rb +92 -0
  47. data/spec/examples/scope_spec.rb +491 -0
  48. data/spec/examples/search_spec.rb +382 -0
  49. data/spec/examples/spec_helper.rb +15 -0
  50. data/spec/examples/validation_spec.rb +65 -0
  51. data/spec/models/author.rb +9 -0
  52. data/spec/models/blog.rb +5 -0
  53. data/spec/models/comment.rb +5 -0
  54. data/spec/models/post.rb +41 -0
  55. data/spec/models/post_observer.rb +11 -0
  56. data/spec/support/fakeweb_request_history.rb +13 -0
  57. metadata +227 -0
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2011 Mat Brown
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ of the Software, and to permit persons to whom the Software is furnished to do
9
+ so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # Elastictastic #
2
+
3
+ Elastictastic is an object-document mapper and lightweight API adapter for
4
+ [ElasticSearch](http://www.elasticsearch.org/). Elastictastic's primary use case
5
+ is to define model classes which use ElasticSearch as a primary
6
+ document-oriented data store, and to expose ElasticSearch's search functionality
7
+ to query for those models.
8
+
9
+ ## Dependencies ##
10
+
11
+ Elastictastic requires Ruby 1.9 and ActiveSupport 3. Elastictastic does not
12
+ require Rails, but if you do run Rails, Elastictastic will only work with Rails
13
+ 3.
14
+
15
+ You will also need a running ElasticSearch instance (or cluster). For local
16
+ development, you can easily [download](http://www.elasticsearch.org/download/)
17
+ and
18
+ [install](http://www.elasticsearch.org/guide/reference/setup/installation.html)
19
+ a copy, or your preferred package manager might have it available.
20
+
21
+ ## Installation ##
22
+
23
+ Just add it to your Gemfile:
24
+
25
+ ```ruby
26
+ gem 'elastictastic'
27
+ ```
28
+
29
+ ## Defining models ##
30
+
31
+ Elastictastic's setup DSL will be familiar to those who have used other
32
+ Ruby object-document mappers such as [Mongoid](http://mongoid.org/). Persisted
33
+ models mix in the `Elastictastic::Document` module, and fields are defined with
34
+ the `field` class macro:
35
+
36
+ ```ruby
37
+ class Post
38
+ field :title
39
+ end
40
+ ```
41
+
42
+ The `field` method can take options; the options available here are simply those
43
+ that are available in a
44
+ [field mapping](http://www.elasticsearch.org/guide/reference/mapping/core-types.html)
45
+ in ElasticSearch. Elastictastic is (mostly) agnostic to the options you pass in;
46
+ they're just used to generate the mapping for ElasticSearch.
47
+
48
+ By default, ElasticSearch assigns fields a `string` type. An example of how one
49
+ might define a field with some options:
50
+
51
+ ```ruby
52
+ class Post
53
+ include Elastictastic::Document
54
+
55
+ field :comments_count, :type => :integer, :store => 'yes'
56
+ end
57
+ ```
58
+
59
+ ### Multi-fields ###
60
+
61
+ ElasticSearch allows you to define
62
+ [multi-fields](http://www.elasticsearch.org/guide/reference/mapping/multi-field-type.html),
63
+ which index the same data in multiple ways. To define a multi-field in
64
+ Elastictastic, you may pass a block to the `field` macro, in which the alternate
65
+ fields are defined using the same DSL:
66
+
67
+ ```ruby
68
+ field :title, :type => 'string', :index => 'analyzed' do
69
+ field :unanalyzed, :type => 'string', :index => 'not_analyzed'
70
+ end
71
+ ```
72
+
73
+ The arguments passed to the outer `field` method are used for the default field
74
+ mapping; thus, the above is the same as the following:
75
+
76
+ ```ruby
77
+ field :title,
78
+ :type => 'multi_field',
79
+ :fields => {
80
+ :title => { :type => 'string', :index => 'analyzed' },
81
+ :unanalyzed => { :type => 'string', :index => 'not_analyzed' }
82
+ }
83
+ ```
84
+
85
+ ### Embedded Objects ###
86
+
87
+ ElasticSearch supports deep nesting of properties by way of
88
+ [object fields](http://www.elasticsearch.org/guide/reference/mapping/object-type.html).
89
+ To define embedded objects in your Elastictastic models, use the `embed` class
90
+ macro:
91
+
92
+ ```ruby
93
+ class Post
94
+ include Elastictastic::Document
95
+
96
+ embed :author
97
+ embed :recent_comments, :class_name => 'Comment'
98
+ end
99
+ ```
100
+
101
+ The class that's embedded should include the `Elastictastic::Resource` mixin,
102
+ which exposes the same configuration DSL as `Elastictastic::Document` but does
103
+ not give the class the functionality of a top-level persistent object:
104
+
105
+ ```ruby
106
+ class Author
107
+ include Elastictastic::Resource
108
+
109
+ field :name
110
+ field :email, :index => 'not_analyzed'
111
+ end
112
+ ```
113
+
114
+ ### Parent-child relationships ###
115
+
116
+ You may define
117
+ [parent-child relationships](http://www.elasticsearch.org/blog/2010/12/27/0.14.0-released.html)
118
+ for your documents using the `has_many` and `belongs_to` macros:
119
+
120
+ ```ruby
121
+ class Blog
122
+ include Elastictastic::Document
123
+
124
+ has_many :posts
125
+ end
126
+ ```
127
+
128
+ ```ruby
129
+ class Post
130
+ include Elastictastic::Document
131
+
132
+ belongs_to :blog
133
+ end
134
+ ```
135
+
136
+ Unlike in, say, ActiveRecord, an Elastictastic document can only specify one
137
+ parent (`belongs_to`) relationship. A document can have as many children
138
+ (`has_many`) as you would like.
139
+
140
+ The parent/child relationship has far-reaching consequences in ElasticSearch,
141
+ and as such you will generally interact with child documents via the parent's
142
+ association collection. For instance, this is the standard way to create a new
143
+ child instance:
144
+
145
+ ```ruby
146
+ post = blog.posts.new
147
+ ```
148
+
149
+ The above will return a new Post object whose parent is the `blog`; the
150
+ `blog.posts` collection will retain a reference to the transient `post`
151
+ instance, and will auto-save it when the `blog` is saved.
152
+
153
+ You may also create a child instance independently and then add it to a parent's
154
+ child collection; however, you must do so before saving the child instance, as
155
+ ElasticSeach requires types that define parents to have a parent. The following
156
+ code block has the same outcome as the previous one:
157
+
158
+ ```ruby
159
+ post = Post.new
160
+ blog.posts << post
161
+ ```
162
+
163
+ In most other respects, the `blog.posts` collection behaves the same as a
164
+ search scope (more on that below), except that enumeration methods (`#each`,
165
+ `#map`, etc.) will return unsaved child instances along with instances
166
+ persisted in ElasticSearch.
167
+
168
+ ### Syncing your mapping ###
169
+
170
+ Before you start creating documents with Elastictastic, you need to make
171
+ ElasticSearch aware of your document structure. To do this, use the
172
+ `sync_mapping` method:
173
+
174
+ ```ruby
175
+ Post.sync_mapping
176
+ ```
177
+
178
+ If you have a complex multi-index topology, you may want to consider using
179
+ [ElasticSearch templates](http://www.elasticsearch.org/guide/reference/api/admin-indices-templates.html)
180
+ to manage mappings and other index settings; Elastictastic doesn't provide any
181
+ explicit support for this at the moment, although you can use e.g.
182
+ `Post.mapping` to retrieve the mapping structure which you can then merge into
183
+ your template.
184
+
185
+ ### Reserved Attributes ###
186
+
187
+ All `Elastictastic::Document` models have an `id` and an `index` field, which
188
+ combine to define the full resource locator for the document in ElasticSearch.
189
+ You should not define fields or methods with these names. You may, however, set
190
+ the id explicitly on new (not yet saved) model instances.
191
+
192
+ ## Persistence ##
193
+
194
+ Elastictastic models are persisted the usual way, namely by calling `save`:
195
+
196
+ ```ruby
197
+ post = Post.new
198
+ post.title = 'You know, for search.'
199
+ post.save
200
+ ```
201
+
202
+ To retrieve a document from the data store, use `get`:
203
+
204
+ ```ruby
205
+ Post.find('123')
206
+ ```
207
+
208
+ You can look up multiple documents by ID:
209
+
210
+ ```ruby
211
+ Post.find('123', '456')
212
+ ```
213
+
214
+ You can also pass an array of IDs; the following will return a one-element
215
+ array:
216
+
217
+ ```ruby
218
+ Post.find(['123'])
219
+ ```
220
+
221
+ For child documents, you **must** perform GET requests using the parent's
222
+ association collection:
223
+
224
+ ```ruby
225
+ post = blog.posts.new
226
+ post.save
227
+
228
+ blog.posts.find(post.id) # this will return the post
229
+ Post.find(post.id) # but this won't!
230
+ ```
231
+
232
+ ### Specifying the index ###
233
+
234
+ Elastictastic defines a default index for your documents. If you're using Rails,
235
+ the default index is your application's name suffixed with the current
236
+ environment; outside of Rails, the default index is simply "default". You can
237
+ change this using the `default_index` configuration key.
238
+
239
+ When you want to work with documents in an index other than the default, use
240
+ the `in_index` class method:
241
+
242
+ ```ruby
243
+ new_post = Post.in_index('my_special_index').new # create in an index
244
+ post = Post.in_index('my_special_index').get('123') # retrieve from an index
245
+ ```
246
+
247
+ To retrieve documents from multiple indices at the same time, pass a hash into
248
+ `get` where the keys are index names and the values are the IDs you wish to
249
+ retrieve from that index:
250
+
251
+ ```ruby
252
+ Post.get('default' => ['123', '456'], 'my_special_index' => '789')
253
+ ```
254
+
255
+ ## Search ##
256
+
257
+ ElasticSearch is, above all, a search tool. Accordingly, aside from direct
258
+ lookup by ID, all retrieval of documents is done via the
259
+ [search API](http://www.elasticsearch.org/guide/reference/api/search/).
260
+ Elastictastic models have class methods corresponding to the top-level keys
261
+ in the ElasticSearch search API; you may chain these much as in ActiveRecord
262
+ or Mongoid:
263
+
264
+ ```ruby
265
+ Post.query(:query_string => { :query => 'pizza' }).facets(:cuisine => { :term => { :field => :tags }}).from(10).size(10)
266
+ # Generates { :query => { :query_string => { :query => 'pizza' }}, :facets => { :cuisine => { :term => { :field => :tags }}}, :from => 10, :size => 10 }
267
+ ```
268
+
269
+ Elastictastic also has an alternate block-based query builder, if you prefer:
270
+
271
+ ```ruby
272
+ Post.query do
273
+ query_string { query('pizza') }
274
+ end.facets { cuisine { term { field :tags }}}.from(10).size(10)
275
+ # Same effect as the previous example
276
+ ```
277
+
278
+ The scopes that are generated by the preceding calls act as collections of
279
+ matching documents; thus all the usual Enumerable methods are available:
280
+
281
+ ```ruby
282
+ Post.query(:query_string => { :query => 'pizza' }).each do |post|
283
+ puts post.title
284
+ end
285
+ ```
286
+
287
+ You may access other components of the response using hash-style access; this
288
+ will return a `Hashie::Mash` which allows hash-style or object-style access:
289
+
290
+ ```ruby
291
+ Post.facets(:cuisine => { :term => { :field => :tags }})['facets'].each_pair do |name, facet|
292
+ facet.terms.each { |term| puts "#{term.term}: #{term.count}" }
293
+ end
294
+ ```
295
+
296
+ You can also call `count` on a scope; this will give the total number of
297
+ documents matching the query.
298
+
299
+ In some situations, you may wish to access metadata about search results beyond
300
+ simply the result document. To do this, use the `#find_each` method, which
301
+ yields a `Hashie::Mash` containing the raw ElasticSearch hit object in the
302
+ second argument:
303
+
304
+ ```ruby
305
+ Post.highlight { fields(:title => {}) }.find_each do |post, hit|
306
+ puts "Post #{post.id} matched the query string in the title field: #{hit.highlight['title']}"
307
+ end
308
+ ```
309
+
310
+ Search scope also expose a #find_in_batches method, which also yields the raw
311
+ hit. The following code gives the same result as the previous example:
312
+
313
+ ```ruby
314
+ Post.highlight { fields(:title => {}) }.find_in_batches do |batch|
315
+ batch.each do |post, hit|
316
+ puts "Post #{post.id} matched the query string in the title field: #{hit.highlight['title']}"
317
+ end
318
+ end
319
+ ```
320
+
321
+ Both `find_each` and `find_in_batches` accept a :batch_size option.
322
+
323
+ ## License ##
324
+
325
+ Elastictastic is distributed under the MIT license. See the attached LICENSE
326
+ file for all the sordid details.
@@ -0,0 +1,21 @@
1
+ module Elastictastic
2
+ class Association
3
+ attr_reader :name, :options
4
+
5
+ def initialize(name, options = {})
6
+ @name, @options = name.to_s, options.symbolize_keys
7
+ end
8
+
9
+ def class_name
10
+ @options[:class_name] || @name.to_s.classify
11
+ end
12
+
13
+ def clazz
14
+ @clazz ||= class_name.constantize
15
+ end
16
+
17
+ def extract(instance)
18
+ instance.__send__(name)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,70 @@
1
+ require 'stringio'
2
+
3
+ module Elastictastic
4
+ class BulkPersistenceStrategy
5
+ def initialize
6
+ @buffer = StringIO.new
7
+ @handlers = []
8
+ end
9
+
10
+ def create(instance, params = {})
11
+ if instance.pending_save?
12
+ raise Elastictastic::OperationNotAllowed,
13
+ "Can't re-save transient document with pending save in bulk operation"
14
+ end
15
+ instance.pending_save!
16
+ add(
17
+ { 'create' => bulk_identifier(instance) },
18
+ instance.elasticsearch_doc
19
+ ) do |response|
20
+ instance.id = response['create']['_id']
21
+ instance.persisted!
22
+ end
23
+ end
24
+
25
+ def update(instance)
26
+ instance.pending_save!
27
+ add(
28
+ { 'index' => bulk_identifier(instance) },
29
+ instance.elasticsearch_doc
30
+ )
31
+ end
32
+
33
+ def destroy(instance)
34
+ instance.pending_destroy!
35
+ add(:delete => bulk_identifier(instance)) do |response|
36
+ instance.transient!
37
+ end
38
+ end
39
+
40
+ def flush
41
+ return if @buffer.length.zero?
42
+
43
+ params = {}
44
+ params[:refresh] = true if Elastictastic.config.auto_refresh
45
+ response = Elastictastic.client.bulk(@buffer.string, params)
46
+
47
+ response['items'].each_with_index do |op_response, i|
48
+ handler = @handlers[i]
49
+ handler.call(op_response) if handler
50
+ end
51
+ response
52
+ end
53
+
54
+ private
55
+
56
+ def bulk_identifier(instance)
57
+ identifier = { :_index => instance.index.name, :_type => instance.class.type }
58
+ identifier['_id'] = instance.id if instance.id
59
+ identifier['parent'] = instance._parent_id if instance._parent_id
60
+ identifier
61
+ end
62
+
63
+ def add(*requests, &block)
64
+ requests.each do |request|
65
+ @buffer.puts(request.to_json)
66
+ end
67
+ @handlers << block
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,30 @@
1
+ module Elastictastic
2
+ module Callbacks
3
+ extend ActiveSupport::Concern
4
+
5
+ HOOKS = [:save, :create, :update, :destroy]
6
+
7
+ included do
8
+ extend ActiveModel::Callbacks
9
+ define_model_callbacks(*HOOKS)
10
+ end
11
+
12
+ module InstanceMethods
13
+ def save
14
+ run_callbacks(:save) { super }
15
+ end
16
+
17
+ def create
18
+ run_callbacks(:create) { super }
19
+ end
20
+
21
+ def update
22
+ run_callbacks(:update) { super }
23
+ end
24
+
25
+ def destroy
26
+ run_callbacks(:destroy) { super }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,56 @@
1
+ module Elastictastic
2
+ class ChildCollectionProxy < Scope
3
+ attr_reader :parent, :transient_children
4
+
5
+ def initialize(association, parent)
6
+ super(
7
+ parent.index,
8
+ association.clazz,
9
+ Search.new(
10
+ 'query' => {
11
+ 'constant_score' => {
12
+ 'filter' => { 'term' => { '_parent' => parent.id }}
13
+ }
14
+ }
15
+ )
16
+ )
17
+ @parent = parent
18
+ @parent_collection = self
19
+ @transient_children = []
20
+ end
21
+
22
+ def initialize_instance(instance)
23
+ super
24
+ self << instance
25
+ end
26
+
27
+ def first
28
+ super || @transient_children.first
29
+ end
30
+
31
+ def each(&block)
32
+ if block
33
+ super
34
+ @transient_children.each(&block)
35
+ else
36
+ ::Enumerator.new(self, :each)
37
+ end
38
+ end
39
+
40
+ def persisted!(child)
41
+ @transient_children.delete(child)
42
+ end
43
+
44
+ def <<(child)
45
+ child.parent_collection = self
46
+ @transient_children << child
47
+ self
48
+ end
49
+
50
+ private
51
+
52
+ def params_for_find
53
+ super.merge('routing' => @parent.id)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,101 @@
1
+ require 'faraday'
2
+
3
+ module Elastictastic
4
+ class Client
5
+ attr_reader :connection
6
+
7
+ def initialize(config)
8
+ builder = Faraday::Builder.new do |builder|
9
+ builder.use Middleware::RaiseServerErrors
10
+ builder.use Middleware::JsonEncodeBody
11
+ builder.use Middleware::JsonDecodeResponse
12
+ if config.logger
13
+ builder.use Middleware::LogRequests, config.logger
14
+ end
15
+ end
16
+ if config.hosts.length == 1
17
+ builder.adapter config.adapter
18
+ @connection =
19
+ Faraday.new(:url => config.hosts.first, :builder => builder)
20
+ else
21
+ builder.use Middleware::Rotor, *config.hosts
22
+ builder.adapter config.adapter
23
+ @connection = Faraday.new(:builder => builder)
24
+ end
25
+ end
26
+
27
+ def create(index, type, id, doc, params = {})
28
+ if id
29
+ @connection.put(
30
+ path_with_query("/#{index}/#{type}/#{id}/_create", params), doc)
31
+ else
32
+ @connection.post(path_with_query("/#{index}/#{type}", params), doc)
33
+ end.body
34
+ end
35
+
36
+ def update(index, type, id, doc, params = {})
37
+ @connection.put(path_with_query("/#{index}/#{type}/#{id}", params), doc)
38
+ end
39
+
40
+ def bulk(commands, params = {})
41
+ @connection.post(path_with_query('/_bulk', params), commands).body
42
+ end
43
+
44
+ def get(index, type, id, params = {})
45
+ @connection.get(path_with_query("/#{index}/#{type}/#{id}", params)).body
46
+ end
47
+
48
+ def mget(docspec, index = nil, type = nil)
49
+ path =
50
+ if index.present?
51
+ if type.present?
52
+ "/#{index}/#{type}/_mget"
53
+ else index.present?
54
+ "#{index}/_mget"
55
+ end
56
+ else
57
+ "/_mget"
58
+ end
59
+ @connection.post(path, 'docs' => docspec).body
60
+ end
61
+
62
+ def search(index, type, search, options = {})
63
+ path = "/#{index}/#{type}/_search"
64
+ @connection.post(
65
+ "#{path}?#{options.to_query}",
66
+ search
67
+ ).body
68
+ end
69
+
70
+ def scroll(id, options = {})
71
+ @connection.post(
72
+ "/_search/scroll?#{options.to_query}",
73
+ id
74
+ ).body
75
+ end
76
+
77
+ def put_mapping(index, type, mapping)
78
+ @connection.put("/#{index}/#{type}/_mapping", mapping).body
79
+ end
80
+
81
+ def delete(index = nil, type = nil, id = nil, params = {})
82
+ path =
83
+ if id then "/#{index}/#{type}/#{id}"
84
+ elsif type then "/#{index}/#{type}"
85
+ elsif index then "/#{index}"
86
+ else "/"
87
+ end
88
+ @connection.delete(path_with_query(path, params)).body
89
+ end
90
+
91
+ private
92
+
93
+ def path_with_query(path, query)
94
+ if query.present?
95
+ "#{path}?#{query.to_query}"
96
+ else
97
+ path
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,35 @@
1
+ module Elastictastic
2
+ class Configuration
3
+
4
+ attr_writer :hosts, :adapter, :default_index, :auto_refresh, :default_batch_size
5
+ attr_accessor :logger
6
+
7
+ def host=(host)
8
+ @hosts = [host]
9
+ end
10
+
11
+ def hosts
12
+ @hosts ||= ['http://localhost:9200']
13
+ end
14
+
15
+ def adapter
16
+ @adapter ||= :net_http
17
+ end
18
+
19
+ def default_index
20
+ @default_index ||= 'default'
21
+ end
22
+
23
+ def auto_refresh
24
+ !!@auto_refresh
25
+ end
26
+
27
+ def default_batch_size
28
+ @default_batch_size ||= 100
29
+ end
30
+
31
+ ActiveModel::Observing::ClassMethods.public_instance_methods(false).each do |method|
32
+ delegate method, :to => :"::Elastictastic::Observing"
33
+ end
34
+ end
35
+ end