Skip to content

Commit f9a87ff

Browse files
authored
[MODEL] Refactor module/mixin organization for clarity (#893)
* [MODEL] Refactor module inclusion for clarity * [MODEL] Don't use Hash#transform_values as it's not available in ruby 2.2 * [MODEL] Make before_save callbacks consistent
1 parent 0f22449 commit f9a87ff

File tree

3 files changed

+61
-64
lines changed

3 files changed

+61
-64
lines changed

Diff for: elasticsearch-model/lib/elasticsearch/model.rb

+9-36
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,10 @@ module Model
8989

9090
# Adds the `Elasticsearch::Model` functionality to the including class.
9191
#
92-
# * Creates the `__elasticsearch__` class and instance methods, pointing to the proxy object
93-
# * Includes the necessary modules in the proxy classes
94-
# * Sets up delegation for crucial methods such as `search`, etc.
92+
# * Creates the `__elasticsearch__` class and instance method. These methods return a proxy object with
93+
# other common methods defined on them.
94+
# * The module includes other modules with further functionality.
95+
# * Sets up delegation for common methods such as `import` and `search`.
9596
#
9697
# @example Include the module in the `Article` model definition
9798
#
@@ -108,44 +109,16 @@ def self.included(base)
108109
base.class_eval do
109110
include Elasticsearch::Model::Proxy
110111

111-
Elasticsearch::Model::Proxy::ClassMethodsProxy.class_eval do
112-
include Elasticsearch::Model::Client::ClassMethods
113-
include Elasticsearch::Model::Naming::ClassMethods
114-
include Elasticsearch::Model::Indexing::ClassMethods
115-
include Elasticsearch::Model::Searching::ClassMethods
116-
end
117-
118-
Elasticsearch::Model::Proxy::InstanceMethodsProxy.class_eval do
119-
include Elasticsearch::Model::Client::InstanceMethods
120-
include Elasticsearch::Model::Naming::InstanceMethods
121-
include Elasticsearch::Model::Indexing::InstanceMethods
122-
include Elasticsearch::Model::Serializing::InstanceMethods
123-
end
124-
125-
Elasticsearch::Model::Proxy::InstanceMethodsProxy.class_eval <<-CODE, __FILE__, __LINE__ + 1
126-
def as_indexed_json(options={})
127-
target.respond_to?(:as_indexed_json) ? target.__send__(:as_indexed_json, options) : super
128-
end
129-
CODE
130-
131-
# Delegate important methods to the `__elasticsearch__` proxy, unless they are defined already
132-
#
112+
# Delegate common methods to the `__elasticsearch__` ClassMethodsProxy, unless they are defined already
133113
class << self
134114
METHODS.each do |method|
135-
delegate method, to: :__elasticsearch__ unless self.public_instance_methods.include?(method)
115+
delegate method, to: :__elasticsearch__ unless self.respond_to?(method)
136116
end
137117
end
138-
139-
# Mix the importing module into the proxy
140-
#
141-
self.__elasticsearch__.class_eval do
142-
include Elasticsearch::Model::Importing::ClassMethods
143-
include Adapter.from_class(base).importing_mixin
144-
end
145-
146-
# Add to the registry if it's a class (and not in intermediate module)
147-
Registry.add(base) if base.is_a?(Class)
148118
end
119+
120+
# Add to the model to the registry if it's a class (and not in intermediate module)
121+
Registry.add(base) if base.is_a?(Class)
149122
end
150123

151124
# Access the module settings

Diff for: elasticsearch-model/lib/elasticsearch/model/indexing.rb

+11-7
Original file line numberDiff line numberDiff line change
@@ -338,13 +338,17 @@ def self.included(base)
338338
#
339339
# @see #update_document
340340
#
341-
base.before_save do |i|
342-
if i.class.instance_methods.include?(:changes_to_save) # Rails 5.1
343-
i.instance_variable_set(:@__changed_model_attributes,
344-
Hash[ i.changes_to_save.map { |key, value| [key, value.last] } ])
345-
elsif i.class.instance_methods.include?(:changes)
346-
i.instance_variable_set(:@__changed_model_attributes,
347-
Hash[ i.changes.map { |key, value| [key, value.last] } ])
341+
base.before_save do |obj|
342+
if obj.respond_to?(:changes_to_save) # Rails 5.1
343+
changes_to_save = obj.changes_to_save
344+
elsif obj.respond_to?(:changes)
345+
changes_to_save = obj.changes
346+
end
347+
348+
if changes_to_save
349+
attrs = obj.instance_variable_get(:@__changed_model_attributes) || {}
350+
latest_changes = changes_to_save.inject({}) { |latest_changes, (k,v)| latest_changes.merge!(k => v.last) }
351+
obj.instance_variable_set(:@__changed_model_attributes, attrs.merge(latest_changes))
348352
end
349353
end if base.respond_to?(:before_save)
350354
end

Diff for: elasticsearch-model/lib/elasticsearch/model/proxy.rb

+41-21
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ module Elasticsearch
1919
module Model
2020

2121
# This module provides a proxy interfacing between the including class and
22-
# {Elasticsearch::Model}, preventing the pollution of the including class namespace.
22+
# `Elasticsearch::Model`, preventing the pollution of the including class namespace.
2323
#
2424
# The only "gateway" between the model and Elasticsearch::Model is the
25-
# `__elasticsearch__` class and instance method.
25+
# `#__elasticsearch__` class and instance method.
2626
#
2727
# The including class must be compatible with
2828
# [ActiveModel](https://github.com/rails/rails/tree/master/activemodel).
2929
#
30-
# @example Include the {Elasticsearch::Model} module into an `Article` model
30+
# @example Include the `Elasticsearch::Model` module into an `Article` model
3131
#
3232
# class Article < ActiveRecord::Base
3333
# include Elasticsearch::Model
@@ -53,40 +53,48 @@ module Proxy
5353
# module and the functionality is accessible via the proxy.
5454
#
5555
def self.included(base)
56+
5657
base.class_eval do
57-
# {ClassMethodsProxy} instance, accessed as `MyModel.__elasticsearch__`
58-
#
58+
59+
# `ClassMethodsProxy` instance, accessed as `MyModel.__elasticsearch__`
5960
def self.__elasticsearch__ &block
6061
@__elasticsearch__ ||= ClassMethodsProxy.new(self)
6162
@__elasticsearch__.instance_eval(&block) if block_given?
6263
@__elasticsearch__
6364
end
6465

65-
# {InstanceMethodsProxy}, accessed as `@mymodel.__elasticsearch__`
66-
#
67-
def __elasticsearch__ &block
68-
@__elasticsearch__ ||= InstanceMethodsProxy.new(self)
69-
@__elasticsearch__.instance_eval(&block) if block_given?
70-
@__elasticsearch__
66+
# Mix the importing module into the `ClassMethodsProxy`
67+
self.__elasticsearch__.class_eval do
68+
include Adapter.from_class(base).importing_mixin
7169
end
7270

7371
# Register a callback for storing changed attributes for models which implement
7472
# `before_save` method and return changed attributes (ie. when `Elasticsearch::Model` is included)
7573
#
7674
# @see http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
7775
#
78-
before_save do |i|
79-
if i.class.instance_methods.include?(:changes_to_save) # Rails 5.1
80-
a = i.__elasticsearch__.instance_variable_get(:@__changed_model_attributes) || {}
81-
i.__elasticsearch__.instance_variable_set(:@__changed_model_attributes,
82-
a.merge(Hash[ i.changes_to_save.map { |key, value| [key, value.last] } ]))
83-
elsif i.class.instance_methods.include?(:changes)
84-
a = i.__elasticsearch__.instance_variable_get(:@__changed_model_attributes) || {}
85-
i.__elasticsearch__.instance_variable_set(:@__changed_model_attributes,
86-
a.merge(Hash[ i.changes.map { |key, value| [key, value.last] } ]))
76+
before_save do |obj|
77+
if obj.respond_to?(:changes_to_save) # Rails 5.1
78+
changes_to_save = obj.changes_to_save
79+
elsif obj.respond_to?(:changes)
80+
changes_to_save = obj.changes
81+
end
82+
83+
if changes_to_save
84+
attrs = obj.__elasticsearch__.instance_variable_get(:@__changed_model_attributes) || {}
85+
latest_changes = changes_to_save.inject({}) { |latest_changes, (k,v)| latest_changes.merge!(k => v.last) }
86+
obj.__elasticsearch__.instance_variable_set(:@__changed_model_attributes, attrs.merge(latest_changes))
8787
end
8888
end if respond_to?(:before_save)
8989
end
90+
91+
# {InstanceMethodsProxy}, accessed as `@mymodel.__elasticsearch__`
92+
#
93+
def __elasticsearch__ &block
94+
@__elasticsearch__ ||= InstanceMethodsProxy.new(self)
95+
@__elasticsearch__.instance_eval(&block) if block_given?
96+
@__elasticsearch__
97+
end
9098
end
9199

92100
# @overload dup
@@ -130,6 +138,11 @@ def inspect
130138
#
131139
class ClassMethodsProxy
132140
include Base
141+
include Elasticsearch::Model::Client::ClassMethods
142+
include Elasticsearch::Model::Naming::ClassMethods
143+
include Elasticsearch::Model::Indexing::ClassMethods
144+
include Elasticsearch::Model::Searching::ClassMethods
145+
include Elasticsearch::Model::Importing::ClassMethods
133146
end
134147

135148
# A proxy interfacing between Elasticsearch::Model instance methods and model instance methods
@@ -138,6 +151,10 @@ class ClassMethodsProxy
138151
#
139152
class InstanceMethodsProxy
140153
include Base
154+
include Elasticsearch::Model::Client::InstanceMethods
155+
include Elasticsearch::Model::Naming::InstanceMethods
156+
include Elasticsearch::Model::Indexing::InstanceMethods
157+
include Elasticsearch::Model::Serializing::InstanceMethods
141158

142159
def klass
143160
target.class
@@ -153,8 +170,11 @@ def class
153170
def as_json(options={})
154171
target.as_json(options)
155172
end
156-
end
157173

174+
def as_indexed_json(options={})
175+
target.respond_to?(:as_indexed_json) ? target.__send__(:as_indexed_json, options) : super
176+
end
177+
end
158178
end
159179
end
160180
end

0 commit comments

Comments
 (0)