Skip to content

Commit ded2519

Browse files
karmijordan-brough
authored andcommitted
[MODEL] Fixed the handling of changed attributes in Indexing to work with older Rails versions
This patch builds on work in elastic#738 by @jkeam and elastic#758 by @Geesu to prevent deprecation warnings on Rails 5.1 due to changes in the handling of "changed attributes", originally reported by @davedash in elastic#714. It's primary focus is to restore the compatibility with older Rails versions (so we don't break compatibility without proper version bump and in a single isolated commit), and to make the naming a bit more descriptive. First, the condition has been changed to work with the `changes_to_save` and `changes` methods, as opposed to the original `changed_attributes` / `attributes_in_database` naming. This communicates much more clearly the intent, and the original usage of `changed_attributes` has been misleading. Also, the "internal instance variable" which keeps track of the changes has been renamed to `@__changed_model_attributes`, in order to cleary differentiate it from the `changed_attributes` method name in older ActiveRecord versions. Closes elastic#758
1 parent ef5d15d commit ded2519

File tree

4 files changed

+62
-26
lines changed

4 files changed

+62
-26
lines changed

elasticsearch-model/lib/elasticsearch/model/indexing.rb

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -303,17 +303,23 @@ module InstanceMethods
303303

304304
def self.included(base)
305305
# Register callback for storing changed attributes for models
306-
# which implement `before_save` and `attributes_in_database` methods
306+
# which implement `before_save` and return changed attributes
307+
# (ie. when `Elasticsearch::Model` is included)
307308
#
308309
# @note This is typically triggered only when the module would be
309310
# included in the model directly, not within the proxy.
310311
#
311312
# @see #update_document
312313
#
313-
base.before_save do |instance|
314-
instance.instance_variable_set(:@__attributes_in_database,
315-
Hash[ instance.changes_to_save.map { |key, value| [key, value.last] } ])
316-
end if base.respond_to?(:before_save) && base.instance_methods.include?(:attributes_in_database)
314+
base.before_save do |i|
315+
if i.class.instance_methods.include?(:changes_to_save) # Rails 5.1
316+
i.instance_variable_set(:@__changed_model_attributes,
317+
Hash[ i.changes_to_save.map { |key, value| [key, value.last] } ])
318+
elsif i.class.instance_methods.include?(:changes)
319+
i.instance_variable_set(:@__changed_model_attributes,
320+
Hash[ i.changes.map { |key, value| [key, value.last] } ])
321+
end
322+
end if base.respond_to?(:before_save)
317323
end
318324

319325
# Serializes the model instance into JSON (by calling `as_indexed_json`),
@@ -387,7 +393,7 @@ def delete_document(options={})
387393
# @see http://rubydoc.info/gems/elasticsearch-api/Elasticsearch/API/Actions:update
388394
#
389395
def update_document(options={})
390-
if attributes_in_database = self.instance_variable_get(:@__attributes_in_database)
396+
if attributes_in_database = self.instance_variable_get(:@__changed_model_attributes)
391397
attributes = if respond_to?(:as_indexed_json)
392398
self.as_indexed_json.select { |k,v| attributes_in_database.keys.map(&:to_s).include? k.to_s }
393399
else

elasticsearch-model/lib/elasticsearch/model/proxy.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,21 @@ def __elasticsearch__ &block
5454
end
5555

5656
# Register a callback for storing changed attributes for models which implement
57-
# `before_save` and `attributes_in_database` methods (when `Elasticsearch::Model` is included)
57+
# `before_save` method and return changed attributes (ie. when `Elasticsearch::Model` is included)
5858
#
5959
# @see http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
6060
#
6161
before_save do |i|
62-
changed_attr = i.__elasticsearch__.instance_variable_get(:@__attributes_in_database) || {}
63-
i.__elasticsearch__.instance_variable_set(:@__attributes_in_database,
64-
changed_attr.merge(Hash[ i.changes_to_save.map { |key, value| [key, value.last] } ]))
65-
end if respond_to?(:before_save) && instance_methods.include?(:attributes_in_database)
62+
if i.class.instance_methods.include?(:changes_to_save) # Rails 5.1
63+
a = i.__elasticsearch__.instance_variable_get(:@__changed_model_attributes) || {}
64+
i.__elasticsearch__.instance_variable_set(:@__changed_model_attributes,
65+
a.merge(Hash[ i.changes_to_save.map { |key, value| [key, value.last] } ]))
66+
elsif i.class.instance_methods.include?(:changes)
67+
a = i.__elasticsearch__.instance_variable_get(:@__changed_model_attributes) || {}
68+
i.__elasticsearch__.instance_variable_set(:@__changed_model_attributes,
69+
a.merge(Hash[ i.changes.map { |key, value| [key, value.last] } ]))
70+
end
71+
end if respond_to?(:before_save)
6672
end
6773
end
6874

elasticsearch-model/test/unit/indexing_test.rb

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,6 @@ def self.before_save(&block)
181181
(@callbacks ||= {})[block.hash] = block
182182
end
183183

184-
def attributes_in_database; [:foo]; end
185-
186184
def changes_to_save
187185
{:foo => ['One', 'Two']}
188186
end
@@ -196,8 +194,6 @@ def self.before_save(&block)
196194
(@callbacks ||= {})[block.hash] = block
197195
end
198196

199-
def attributes_in_database; [:foo, :bar]; end
200-
201197
def changes_to_save
202198
{:foo => ['A', 'B'], :bar => ['C', 'D']}
203199
end
@@ -207,15 +203,28 @@ def as_indexed_json(options={})
207203
end
208204
end
209205

206+
class ::DummyIndexingModelWithOldDirty
207+
extend Elasticsearch::Model::Indexing::ClassMethods
208+
include Elasticsearch::Model::Indexing::InstanceMethods
209+
210+
def self.before_save(&block)
211+
(@callbacks ||= {})[block.hash] = block
212+
end
213+
214+
def changes
215+
{:foo => ['One', 'Two']}
216+
end
217+
end
218+
210219
should "register before_save callback when included" do
211220
::DummyIndexingModelWithCallbacks.expects(:before_save).returns(true)
212221
::DummyIndexingModelWithCallbacks.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods
213222
end
214223

215-
should "set the @__attributes_in_database variable before save" do
224+
should "set the @__changed_model_attributes variable before save" do
216225
instance = ::DummyIndexingModelWithCallbacks.new
217226
instance.expects(:instance_variable_set).with do |name, value|
218-
assert_equal :@__attributes_in_database, name
227+
assert_equal :@__changed_model_attributes, name
219228
assert_equal({foo: 'Two'}, value)
220229
true
221230
end
@@ -227,6 +236,23 @@ def as_indexed_json(options={})
227236
end
228237
end
229238

239+
# https://github.com/elastic/elasticsearch-rails/issues/714
240+
# https://github.com/rails/rails/pull/25337#issuecomment-225166796
241+
should "set the @__changed_model_attributes variable before save for old ActiveModel::Dirty" do
242+
instance = ::DummyIndexingModelWithOldDirty.new
243+
instance.expects(:instance_variable_set).with do |name, value|
244+
assert_equal :@__changed_model_attributes, name
245+
assert_equal({foo: 'Two'}, value)
246+
true
247+
end
248+
249+
::DummyIndexingModelWithOldDirty.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods
250+
251+
::DummyIndexingModelWithOldDirty.instance_variable_get(:@callbacks).each do |n,b|
252+
instance.instance_eval(&b)
253+
end
254+
end
255+
230256
should "have the index_document method" do
231257
client = mock('client')
232258
instance = ::DummyIndexingModelWithCallbacks.new
@@ -307,7 +333,7 @@ def as_indexed_json(options={})
307333
instance = ::DummyIndexingModelWithCallbacks.new
308334

309335
# Reset the fake `changes`
310-
instance.instance_variable_set(:@__attributes_in_database, nil)
336+
instance.instance_variable_set(:@__changed_model_attributes, nil)
311337

312338
instance.expects(:index_document)
313339
instance.update_document
@@ -318,7 +344,7 @@ def as_indexed_json(options={})
318344
instance = ::DummyIndexingModelWithCallbacks.new
319345

320346
# Set the fake `changes` hash
321-
instance.instance_variable_set(:@__attributes_in_database, {foo: 'bar'})
347+
instance.instance_variable_set(:@__changed_model_attributes, {foo: 'bar'})
322348

323349
client.expects(:update).with do |payload|
324350
assert_equal 'foo', payload[:index]
@@ -341,7 +367,7 @@ def as_indexed_json(options={})
341367
instance = ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson.new
342368

343369
# Set the fake `changes` hash
344-
instance.instance_variable_set(:@__attributes_in_database, {'foo' => 'B', 'bar' => 'D' })
370+
instance.instance_variable_set(:@__changed_model_attributes, {'foo' => 'B', 'bar' => 'D' })
345371

346372
client.expects(:update).with do |payload|
347373
assert_equal({:foo => 'B'}, payload[:body][:doc])
@@ -360,7 +386,7 @@ def as_indexed_json(options={})
360386
client = mock('client')
361387
instance = ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson.new
362388

363-
instance.instance_variable_set(:@__attributes_in_database, { 'foo' => { 'bar' => 'BAR'} })
389+
instance.instance_variable_set(:@__changed_model_attributes, { 'foo' => { 'bar' => 'BAR'} })
364390
# Overload as_indexed_json
365391
instance.expects(:as_indexed_json).returns({ 'foo' => 'BAR' })
366392

@@ -382,7 +408,7 @@ def as_indexed_json(options={})
382408
instance = ::DummyIndexingModelWithCallbacks.new
383409

384410
# Set the fake `changes` hash
385-
instance.instance_variable_set(:@__attributes_in_database, {author: 'john'})
411+
instance.instance_variable_set(:@__changed_model_attributes, {author: 'john'})
386412

387413
client.expects(:update).with do |payload|
388414
assert_equal 'foo', payload[:index]

elasticsearch-model/test/unit/proxy_test.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ def self.before_save(&block)
2323
(@callbacks ||= {})[block.hash] = block
2424
end
2525

26-
def attributes_in_database; [:foo]; end
27-
2826
def changes_to_save
2927
{:foo => ['One', 'Two']}
3028
end
@@ -43,10 +41,10 @@ def changes_to_save
4341
DummyProxyModelWithCallbacks.__send__ :include, Elasticsearch::Model::Proxy
4442
end
4543

46-
should "set the @__attributes_in_database variable before save" do
44+
should "set the @__changed_model_attributes variable before save" do
4745
instance = ::DummyProxyModelWithCallbacks.new
4846
instance.__elasticsearch__.expects(:instance_variable_set).with do |name, value|
49-
assert_equal :@__attributes_in_database, name
47+
assert_equal :@__changed_model_attributes, name
5048
assert_equal({foo: 'Two'}, value)
5149
true
5250
end

0 commit comments

Comments
 (0)