Exist places in our repo where are copies of code from a gem with few changes of the original method.
If never happened an error is difficult to know when the original source changes.
That means that is possible to have old code in Pulse360 that not correspond with the current version of the original gem.
That is particularly important if we are doing a big upgrade, for example, bumping to a higher version of Rails.
One possible solution is t use Durable Decorator
Slogan: "Reducing stress and guilt while engaging in monkey patching since... 2013?"
Furthermore, we can hash the contents of a method as it exists at inspect-time and seal it by providing extra options to the decorator. If the method definition gets tampered with, the decorator will detect this at decoration-time and raise an error for your review.
Find the SHA of the method as currently loaded into memory, works with classes as well as modules:
DurableDecorator::Base.determine_sha('ExampleClass#instance_method')
Or for class (singleton) methods:
DurableDecorator::Base.determine_sha('ExampleClass.class_level_method')
Armed with this knowledge, we can enforce a strict mode:
DurableDecorator::Base.determine_sha('ExampleClass#no_param_method')
# => 'ba3114b2d46caa684b3f7ba38d6f74b2'
ExampleClass.class_eval do
durably_decorate :string_method, mode: 'strict', sha: 'WRONG-SHA-123456' do
original_string_method + " and new"
end
end
DurableDecorator::TamperedDefinitionError: Method SHA mismatch, the definition has been tampered with
DurableDecorator may also decorate methods with params like so:
class ExampleClass
def string_method(text)
"original #{text}"
end
end
ExampleClass.class_eval do
durably_decorate :string_method, mode: 'strict', sha: 'ba3114b2d46caa684b3f7ba38d6f74b2' do |text|
original_string_method(text) + " and new"
end
end
instance = ExampleClass.new
instance.string_method('test')
# => "original test and new"
Once you decorate the method and seal it with its SHA, if some gem tries to come in and overwrite your work BEFORE decorate-time, DurableDecorator will warn you. Similarly, expect to see an exception bubble up if the definition of the original method has changed and requires a review and a re-hash.
The usefulness is for gem consumers, and their application-level specs.
Currently, dealing with default parameter values is problematic due to how Ruby answer inquiries about methods' arity. If the method you are overriding has default values, consider appending a splat argument and manually parsing the contents. Here is an example:
class ExampleClass
def string_method arg1, args2 = [], arg3 = {}
"original"
end
end
ExampleClass.class_eval do
durably_decorate :string_method do |arg1, *args|
arg2 = args[0] || []
arg3 = args[1] || {}
original_string_method + " and new"
end
end
durable_decorator gem verifies the monkey-patched code and declares the implied dependency where it didn't before.
Here an example of the general pattern.
describe 'our monkeypatch' do
let(:monkeypatched_method) { Foo.instance_method(:bar) }
it 'should patch intended implementation' do
original_method = monkeypatched_method.super_method
expected_implementation_sha = DurableDecorator::Util.method_sha(original_method)
expect(expected_implementation_sha).to eq('1234567890abcdef')
end
end
In this PR 7347 are two examples.
notice to add a test for durable_decorator/lib/extensions/activerecord/query_form_alias we create a similar file path in the test folder, ending in _test.rb:
test/durable_decorator/lib/extensions/activerecord/query_form_alias_test.rb
Example:
begin
Gem::Dependency.new('activerecord', '~> 4.1').to_spec
Gem::Dependency.new('arel', '<= 6.0.4').to_spec
rescue Gem::LoadError
fail Gem::LoadError, <<~MSG
ActiveRecord and Arel patch to forward-port functionality from Rails 4.0
Re-evaluate compatibility on change.
MSG
end
-
11 files in extensions folder