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
@@ -9,21 +9,27 @@ module Elastictastic
9
9
  define_model_callbacks(*HOOKS)
10
10
  end
11
11
 
12
- module InstanceMethods
13
- def save
14
- run_callbacks(:save) { super }
15
- end
12
+ def save(options = {})
13
+ with_callbacks(:save, options) { super }
14
+ end
16
15
 
17
- def create
18
- run_callbacks(:create) { super }
19
- end
16
+ def create(options = {})
17
+ with_callbacks(:create, options) { super }
18
+ end
20
19
 
21
- def update
22
- run_callbacks(:update) { super }
23
- end
20
+ def update(options = {})
21
+ with_callbacks(:update, options) { super }
22
+ end
23
+
24
+ def destroy(options = {})
25
+ with_callbacks(:destroy, options) { super }
26
+ end
27
+
28
+ private
24
29
 
25
- def destroy
26
- run_callbacks(:destroy) { super }
30
+ def with_callbacks(name, options)
31
+ if options[:callbacks] == false then yield
32
+ else run_callbacks(name) { yield }
27
33
  end
28
34
  end
29
35
  end
@@ -1,6 +1,6 @@
1
1
  module Elastictastic
2
2
  class ChildCollectionProxy < Scope
3
- attr_reader :parent, :transient_children
3
+ attr_reader :parent
4
4
 
5
5
  def initialize(association, parent)
6
6
  super(
@@ -12,10 +12,10 @@ module Elastictastic
12
12
  'filter' => { 'term' => { '_parent' => parent.id }}
13
13
  }
14
14
  }
15
- )
15
+ ),
16
+ self
16
17
  )
17
18
  @parent = parent
18
- @parent_collection = self
19
19
  @transient_children = []
20
20
  end
21
21
 
@@ -25,28 +25,32 @@ module Elastictastic
25
25
  end
26
26
 
27
27
  def first
28
- super || @transient_children.first
28
+ super || transient_children.first
29
29
  end
30
30
 
31
31
  def each(&block)
32
32
  if block
33
- super
34
- @transient_children.each(&block)
33
+ super if @parent.persisted?
34
+ transient_children.each(&block)
35
35
  else
36
36
  ::Enumerator.new(self, :each)
37
37
  end
38
38
  end
39
39
 
40
- def persisted!(child)
41
- @transient_children.delete(child)
42
- end
43
-
44
40
  def <<(child)
45
- child.parent_collection = self
41
+ child.parent = @parent
46
42
  @transient_children << child
47
43
  self
48
44
  end
49
45
 
46
+ def transient_children
47
+ @transient_children.tap do |children|
48
+ children.reject! do |child|
49
+ !child.transient?
50
+ end
51
+ end
52
+ end
53
+
50
54
  private
51
55
 
52
56
  def params_for_find
@@ -1,48 +1,70 @@
1
- require 'faraday'
2
-
3
1
  module Elastictastic
4
2
  class Client
5
3
  attr_reader :connection
6
4
 
7
5
  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
6
+ adapter_options = {
7
+ :request_timeout => config.request_timeout,
8
+ :connect_timeout => config.connect_timeout
9
+ }
16
10
  if config.hosts.length == 1
17
- builder.adapter config.adapter
18
- @connection =
19
- Faraday.new(:url => config.hosts.first, :builder => builder)
11
+ connection = Adapter[config.adapter].
12
+ new(config.hosts.first, adapter_options)
20
13
  else
21
- builder.use Middleware::Rotor, *config.hosts
22
- builder.adapter config.adapter
23
- @connection = Faraday.new(:builder => builder)
14
+ connection = Rotor.new(
15
+ config.hosts,
16
+ adapter_options.merge(
17
+ :adapter => config.adapter,
18
+ :backoff_threshold => config.backoff_threshold,
19
+ :backoff_start => config.backoff_start,
20
+ :backoff_max => config.backoff_max
21
+ )
22
+ )
24
23
  end
24
+ if config.logger
25
+ connection = Middleware::LogRequests.new(connection, config.logger)
26
+ end
27
+ connection = Middleware::JsonDecodeResponse.new(connection)
28
+ connection = Middleware::JsonEncodeBody.new(connection)
29
+ connection = Middleware::RaiseServerErrors.new(connection)
30
+ @connection = connection
25
31
  end
26
32
 
27
33
  def create(index, type, id, doc, params = {})
28
34
  if id
29
35
  @connection.put(
30
- path_with_query("/#{index}/#{type}/#{id}/_create", params), doc)
36
+ path_with_query("/#{index}/#{type}/#{id}/_create", params),
37
+ doc
38
+ )
31
39
  else
32
- @connection.post(path_with_query("/#{index}/#{type}", params), doc)
40
+ @connection.post(
41
+ path_with_query("/#{index}/#{type}", params),
42
+ doc
43
+ )
33
44
  end.body
34
45
  end
35
46
 
36
47
  def update(index, type, id, doc, params = {})
37
- @connection.put(path_with_query("/#{index}/#{type}/#{id}", params), doc)
48
+ @connection.put(
49
+ path_with_query("/#{index}/#{type}/#{id}", params),
50
+ doc
51
+ ).body
38
52
  end
39
53
 
40
54
  def bulk(commands, params = {})
41
55
  @connection.post(path_with_query('/_bulk', params), commands).body
42
56
  end
43
57
 
58
+ def exists?(index, type, id, params = {})
59
+ @connection.head(
60
+ path_with_query("/#{index}/#{type}/#{id}", params)
61
+ ).status == 200
62
+ end
63
+
44
64
  def get(index, type, id, params = {})
45
- @connection.get(path_with_query("/#{index}/#{type}/#{id}", params)).body
65
+ @connection.get(
66
+ path_with_query("/#{index}/#{type}/#{id}", params)
67
+ ).body
46
68
  end
47
69
 
48
70
  def mget(docspec, index = nil, type = nil)
@@ -67,11 +89,12 @@ module Elastictastic
67
89
  ).body
68
90
  end
69
91
 
92
+ def msearch(search_bodies)
93
+ @connection.post('/_msearch', search_bodies).body
94
+ end
95
+
70
96
  def scroll(id, options = {})
71
- @connection.post(
72
- "/_search/scroll?#{options.to_query}",
73
- id
74
- ).body
97
+ @connection.post("/_search/scroll?#{options.to_query}", id).body
75
98
  end
76
99
 
77
100
  def put_mapping(index, type, mapping)
@@ -1,15 +1,20 @@
1
1
  module Elastictastic
2
2
  class Configuration
3
3
 
4
- attr_writer :hosts, :adapter, :default_index, :auto_refresh, :default_batch_size
5
- attr_accessor :logger
4
+ attr_writer :hosts, :default_index, :auto_refresh, :default_batch_size, :adapter
5
+ attr_accessor :logger, :connect_timeout, :request_timeout, :backoff_threshold, :backoff_start, :backoff_max
6
+ attr_reader :extra_middlewares
7
+
8
+ def initialize
9
+ @extra_middlewares = []
10
+ end
6
11
 
7
12
  def host=(host)
8
13
  @hosts = [host]
9
14
  end
10
15
 
11
16
  def hosts
12
- @hosts ||= ['http://localhost:9200']
17
+ @hosts ||= [default_host]
13
18
  end
14
19
 
15
20
  def adapter
@@ -17,7 +22,12 @@ module Elastictastic
17
22
  end
18
23
 
19
24
  def default_index
20
- @default_index ||= 'default'
25
+ return @default_index if defined? @default_index
26
+ if url_from_env && url_from_env.path =~ /^\/([^\/]+)/
27
+ @default_index = $1
28
+ else
29
+ @default_index = 'default'
30
+ end
21
31
  end
22
32
 
23
33
  def auto_refresh
@@ -28,6 +38,51 @@ module Elastictastic
28
38
  @default_batch_size ||= 100
29
39
  end
30
40
 
41
+ def json_engine=(json_engine)
42
+ original_engine = MultiJson.engine
43
+ MultiJson.engine = json_engine
44
+ @json_engine = MultiJson.engine
45
+ ensure
46
+ MultiJson.engine = original_engine
47
+ end
48
+
49
+ def json_engine
50
+ @json_engine || MultiJson.engine
51
+ end
52
+
53
+ def use_middleware(*args)
54
+ @extra_middlewares << args
55
+ end
56
+
57
+ def presets
58
+ @presets ||= ActiveSupport::HashWithIndifferentAccess.new
59
+ end
60
+
61
+ def presets=(new_presets)
62
+ presets.merge!(new_presets)
63
+ end
64
+
65
+ private
66
+
67
+ def default_host
68
+ if url_from_env
69
+ url_from_env.class.build(
70
+ :host => url_from_env.host,
71
+ :port => url_from_env.port
72
+ )
73
+ else
74
+ 'http://localhost:9200'
75
+ end
76
+ end
77
+
78
+ def url_from_env
79
+ return @url_from_env if defined? @url_from_env
80
+ @url_from_env =
81
+ if ENV['ELASTICSEARCH_URL']
82
+ URI.parse(ENV['ELASTICSEARCH_URL'])
83
+ end
84
+ end
85
+
31
86
  ActiveModel::Observing::ClassMethods.public_instance_methods(false).each do |method|
32
87
  delegate method, :to => :"::Elastictastic::Observing"
33
88
  end
@@ -50,54 +50,68 @@ module Elastictastic
50
50
  end
51
51
  end
52
52
 
53
- module InstanceMethods
54
- def write_attribute(field, value)
55
- attribute_will_change!(field)
56
- super
57
- end
53
+ def write_attribute(field, value)
54
+ attribute_may_change!(field) { super }
55
+ end
58
56
 
59
- def write_embed(field, value)
60
- attribute_will_change!(field)
57
+ def write_embed(field, value)
58
+ attribute_may_change!(field) do
61
59
  if Array === value
62
60
  value.each do |el|
63
61
  el.nesting_document = self
64
62
  el.nesting_association = field
65
63
  end
66
64
  super(field, NestedCollectionProxy.new(self, field, value))
67
- else
65
+ elsif value
68
66
  value.nesting_document = self
69
67
  value.nesting_association = field
70
68
  super
69
+ else
70
+ super
71
71
  end
72
72
  end
73
+ end
73
74
 
74
- def save
75
- super
76
- clean_attributes!
77
- end
75
+ def save(options = {})
76
+ super
77
+ clean_attributes!
78
+ end
78
79
 
79
- def elasticsearch_doc=(doc)
80
- super
81
- clean_attributes!
82
- end
80
+ def elasticsearch_doc=(doc)
81
+ super
82
+ clean_attributes!
83
+ end
83
84
 
84
- protected
85
+ protected
85
86
 
86
- def clean_attributes!
87
- changed_attributes.clear
88
- @embeds.each_pair do |name, embedded|
89
- Util.call_or_map(embedded) { |doc| doc.clean_attributes! }
90
- end
87
+ def clean_attributes!
88
+ changed_attributes.clear
89
+ @_embeds.each_pair do |name, embedded|
90
+ Util.call_or_map(embedded) { |doc| doc && doc.clean_attributes! }
91
91
  end
92
92
  end
93
93
 
94
- module NestedDocumentMethods
94
+ def attribute_may_change!(field)
95
+ attribute_will_change!(field) unless changed_attributes.key?(field)
96
+ old_value = changed_attributes[field]
97
+ yield
98
+ attribute_not_changed!(field) if old_value == __send__(field)
99
+ end
100
+
101
+ def attribute_not_changed!(field)
102
+ changed_attributes.delete(field)
103
+ end
104
+
105
+ module EmbeddedDocumentMethods
95
106
  attr_writer :nesting_document, :nesting_association
96
107
 
97
- def attribute_will_change!(field)
98
- super
108
+ def attribute_may_change!(field)
99
109
  if @nesting_document
100
- @nesting_document.__send__("attribute_will_change!", @nesting_association)
110
+ @nesting_document.attribute_may_change!(@nesting_association) do
111
+ super
112
+ end
113
+ else
114
+ super
101
115
  end
102
116
  end
103
117
  end
@@ -116,8 +130,9 @@ module Elastictastic
116
130
  ].each do |destructive_method|
117
131
  module_eval <<-RUBY, __FILE__, __LINE__+1
118
132
  def #{destructive_method}(*args)
119
- @owner.__send__("\#{@embed_name}_will_change!")
120
- super
133
+ @owner.__send__(:attribute_may_change!, @embed_name) do
134
+ super
135
+ end
121
136
  end
122
137
  RUBY
123
138
  end
@@ -4,39 +4,61 @@ module Elastictastic
4
4
  class DiscretePersistenceStrategy
5
5
  include Singleton
6
6
 
7
+ DEFAULT_HANDLER = proc { |e| raise(e) if e }
8
+
7
9
  attr_accessor :auto_refresh
8
10
 
9
- def create(doc)
10
- response = Elastictastic.client.create(
11
- doc.index,
12
- doc.class.type,
13
- doc.id,
14
- doc.elasticsearch_doc,
15
- params_for(doc)
16
- )
11
+ def create(doc, &block)
12
+ block ||= DEFAULT_HANDLER
13
+ begin
14
+ response = Elastictastic.client.create(
15
+ doc.index,
16
+ doc.class.type,
17
+ doc.id,
18
+ doc.elasticsearch_doc,
19
+ params_for(doc)
20
+ )
21
+ rescue => e
22
+ return block.call(e)
23
+ end
17
24
  doc.id = response['_id']
25
+ doc.version = response['_version']
18
26
  doc.persisted!
27
+ block.call
19
28
  end
20
29
 
21
- def update(doc)
22
- Elastictastic.client.update(
23
- doc.index,
24
- doc.class.type,
25
- doc.id,
26
- doc.elasticsearch_doc,
27
- params_for(doc)
28
- )
30
+ def update(doc, &block)
31
+ block ||= DEFAULT_HANDLER
32
+ begin
33
+ response = Elastictastic.client.update(
34
+ doc.index,
35
+ doc.class.type,
36
+ doc.id,
37
+ doc.elasticsearch_doc,
38
+ params_for(doc)
39
+ )
40
+ rescue => e
41
+ return block.call(e)
42
+ end
43
+ doc.version = response['_version']
29
44
  doc.persisted!
45
+ block.call
30
46
  end
31
47
 
32
- def destroy(doc)
33
- response = Elastictastic.client.delete(
34
- doc.index.name,
35
- doc.class.type,
36
- doc.id,
37
- params_for(doc)
38
- )
48
+ def destroy(doc, &block)
49
+ block ||= DEFAULT_HANDLER
50
+ begin
51
+ response = Elastictastic.client.delete(
52
+ doc.index.name,
53
+ doc.class.type,
54
+ doc.id,
55
+ params_for(doc)
56
+ )
57
+ rescue => e
58
+ return block.call(e)
59
+ end
39
60
  doc.transient!
61
+ block.call
40
62
  response['found']
41
63
  end
42
64
 
@@ -46,6 +68,9 @@ module Elastictastic
46
68
  {}.tap do |params|
47
69
  params[:refresh] = true if Elastictastic.config.auto_refresh
48
70
  params[:parent] = doc._parent_id if doc._parent_id
71
+ params[:version] = doc.version if doc.version
72
+ routing = doc.class.route(doc)
73
+ params[:routing] = routing if routing
49
74
  end
50
75
  end
51
76
  end
@@ -3,96 +3,12 @@ module Elastictastic
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- extend Scoped
7
- include Properties
8
- include Persistence
9
- include ParentChild
6
+ include BasicDocument
10
7
  include Callbacks
11
8
  include Observing
12
9
  include Dirty
13
10
  include MassAssignmentSecurity
14
11
  include Validations
15
-
16
- extend ActiveModel::Naming
17
- include ActiveModel::Conversion
18
- end
19
-
20
- module ClassMethods
21
- delegate :find, :destroy_all, :sync_mapping, :inspect, :find_each,
22
- :find_in_batches, :first, :count, :empty?, :any?, :all,
23
- :query, :filter, :from, :size, :sort, :highlight, :fields,
24
- :script_fields, :preference, :facets, :to => :current_scope
25
-
26
- def mapping
27
- { type => { 'properties' => properties }}
28
- end
29
-
30
- def type
31
- name.underscore
32
- end
33
-
34
- def in_index(name_or_index)
35
- Scope.new(Elastictastic::Index(name_or_index), self)
36
- end
37
-
38
- def scoped(params)
39
- current_scope.scoped(params)
40
- end
41
-
42
- private
43
-
44
- def default_scope
45
- in_index(Index.default)
46
- end
47
- end
48
-
49
- module InstanceMethods
50
- attr_reader :id
51
-
52
- def initialize(attributes = {})
53
- self.class.current_scope.initialize_instance(self)
54
- end
55
-
56
- def elasticsearch_hit=(hit) #:nodoc:
57
- @id = hit['_id']
58
- @index = Index.new(hit['_index'])
59
- persisted!
60
-
61
- doc = {}
62
- doc.merge!(hit['_source']) if hit['_source']
63
- fields = hit['fields']
64
- if fields
65
- unflattened_fields =
66
- Util.unflatten_hash(fields.reject { |k, v| v.nil? })
67
- if unflattened_fields.has_key?('_source')
68
- doc.merge!(unflattened_fields.delete('_source'))
69
- end
70
- doc.merge!(unflattened_fields)
71
- end
72
- self.elasticsearch_doc=(doc)
73
- end
74
-
75
- def id=(id)
76
- assert_transient!
77
- @id = id
78
- end
79
-
80
- def index
81
- return @index if defined? @index
82
- @index = Index.default
83
- end
84
-
85
- def ==(other)
86
- index == other.index && id == other.id
87
- end
88
-
89
- def inspect
90
- inspected = "#<#{self.class.name} id: #{id}, index: #{index.name}"
91
- attributes.each_pair do |attr, value|
92
- inspected << ", #{attr}: #{value.inspect}"
93
- end
94
- inspected << ">"
95
- end
96
12
  end
97
13
  end
98
14
  end
@@ -0,0 +1,34 @@
1
+ module Elastictastic
2
+ module EmbeddedDocument
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Properties
7
+ include Dirty
8
+ include Dirty::EmbeddedDocumentMethods
9
+ include MassAssignmentSecurity
10
+ include Validations
11
+
12
+ include ActiveModel::Serializers::JSON
13
+ include ActiveModel::Serializers::Xml
14
+
15
+ self.include_root_in_json = false
16
+ end
17
+
18
+ def initialize_copy(original)
19
+ self.write_attributes(original.read_attributes.dup)
20
+ end
21
+
22
+ def attributes
23
+ {}
24
+ end
25
+
26
+ def ==(other)
27
+ other.nil? ? false : @_attributes == other.read_attributes && @_embeds == other.read_embeds
28
+ end
29
+
30
+ def eql?(other)
31
+ self.class == other.class && self == other
32
+ end
33
+ end
34
+ end
@@ -1,7 +1,19 @@
1
1
  module Elastictastic
2
- CancelBulkOperation = Class.new(StandardError)
3
- IllegalModificationError = Class.new(StandardError)
4
- OperationNotAllowed = Class.new(StandardError)
5
- NoServerAvailable = Class.new(StandardError)
6
- RecordInvalid = Class.new(StandardError)
2
+ Error = Class.new(StandardError)
3
+ CancelSave = Class.new(Error)
4
+ IllegalModificationError = Class.new(Error)
5
+ OperationNotAllowed = Class.new(Error)
6
+ MissingParameter = Class.new(Error)
7
+
8
+ class ConnectionFailed < Error
9
+ attr_reader :source
10
+
11
+ def initialize(source)
12
+ super(source.message)
13
+ @source = source
14
+ end
15
+ end
16
+
17
+ NoServerAvailable = Class.new(ConnectionFailed)
18
+ RecordInvalid = Class.new(Error)
7
19
  end
@@ -10,6 +10,9 @@ module Elastictastic
10
10
 
11
11
  def self.with_defaults(options)
12
12
  options = Util.deep_stringify(options)
13
+ if preset = options.delete('preset')
14
+ options = ::Elastictastic.config.presets[preset].merge(options)
15
+ end
13
16
  { 'type' => 'string' }.merge(options).tap do |field_properties|
14
17
  if field_properties['type'].to_s == 'date'
15
18
  field_properties['format'] = 'date_time_no_millis'
@@ -6,10 +6,8 @@ module Elastictastic
6
6
  include ActiveModel::MassAssignmentSecurity
7
7
  end
8
8
 
9
- module InstanceMethods
10
- def attributes=(attributes)
11
- super(sanitize_for_mass_assignment(attributes))
12
- end
9
+ def attributes=(attributes)
10
+ super(sanitize_for_mass_assignment(attributes))
13
11
  end
14
12
  end
15
13
  end