proxy_method
Prevent running an inherited method directly.
The purpose of this gem is to prevent directly running the inherited methods you choose to block, and instead raise a custom Error message. The original method can still be called under a different name.
This was created to help enforce the use of interactors over directly calling ActiveRecord methods like create, save, and update. One downside of interactors is that they require top-of-mind awareness in order to use them, and by default new/forgetful/overworked developers will create ActiveRecord instances all willy nilly until society crumbles.
While this was created for ActiveRecord models specifically, it works with any inherited methods. The method being proxied has to already exist, or it can't be overridden.
Usage
Given these basic classes:
class Animal
def self.create
'created'
end
def save
'saved'
end
def update
'updated'
end
end
class Turtle < Animal
include ProxyMethod
proxy_class_method :create
proxy_instance_method :save
# for instance methods, you can also just call "proxy_method"
proxy_method :update
end
You can do this:
Turtle.create
# => RuntimeError: Disabled by proxy_method
Turtle.new.save
# => RuntimeError: Disabled by proxy_method
Turtle.unproxied_create
# => 'created'
Turtle.new.unproxied_save
# => 'saved'
Raise a custom error message:
class CustomCow < Animal
include ProxyMethod
proxy_method :save, raise: "Don't save here, use an interactor!"
end
CustomCow.new.save
# => RunTimeError: Don't save here, use an interactor!
Specify multiple methods at once:
class MultiMonkey < Animal
include ProxyMethod
proxy_method :save, :update, raise: "Use an interactor!"
end
MultiMonkey.new.save
# => RunTimeError: Use an interactor!
MultiMonkey.new.update
# => RunTimeError: Use an interactor!
Supply your own prefix for the original, unproxied method:
class PrefixPelican < Animal
include ProxyMethod
proxy_method :save, prefix: 'pelican_'
end
PrefixPelican.new.pelican_save
# => 'saved'
Use an "unproxied" version of an instance:
class DefaultDuck < Animal
include ProxyMethod
proxy_method :save
end
duck = DefaultDuck.new
duck_unproxied = duck.unproxied
duck.save
# => RunTimeError: Disabled by proxy_method
duck_unproxied.save
# => 'saved'
This is important, because if you proxy a method like #save
in ActiveRecord,
you're also preventing the natural use of #create
and #update
since these
rely on #save
under the hood. "Unproxying" the entire instance then frees
an interactor to use #create
, #update
and #save
in an intentional way.
You can also use an unproxied version of the entire class:
duck = DefaultDuck.new
duck_unproxied = DefaultDuck.unproxied.new
duck.save
# => RunTimeError: Disabled by proxy_method
duck_unproxied.save
# => 'saved'
Run a custom block, instead of raising an exception at all. In other words, actually proxy the method! The object which is passed to the block is already unproxied, you don't have to do this yourself:
class MethodicalMeerkat < Animal
include ProxyMethod
proxy_method(:save) do |unproxied_object|
"indirectly #{unproxied_object.save}!"
end
end
MethodicalMeerkat.new.save
# => 'indirectly saved!'
The object which is passed to the block is already unproxied, so it's free to use as you'd expect.
From there, you can allow your proxied block it to be used for multiple methods:
class MethodicalMeerkat < Animal
include ProxyMethod
proxy_method(:save, :update) do |object, method_name|
"indirectly #{object.send(method_name)}!"
end
end
MethodicalMeerkat.new.save
# => 'indirectly saved!'
MethodicalMeerkat.new.update
# => 'indirectly updated!'
Provide your proxied block with args and a block of its own. The block will be given the
unproxied object itself, followed by the standard arguments to method_missing
,
so it works in much the same way.
class MethodicalMeerkat < Animal
include ProxyMethod
proxy_method(:save) do |object, method_name, *args, &block|
"indirectly #{object.send(method_name, *args, &block)}!"
end
end
MethodicalMeerkat.new.save
# => 'indirectly saved!'
MethodicalMeerkat.new.update
# => 'indirectly updated!'
Installation
Add this line to your application's Gemfile:
gem 'proxy_method'
And then execute:
$ bundle
Or install it yourself as:
$ gem install proxy_method
Contributing
Feel free to fork and create a pull request.
License
The gem is available as open source under the terms of the MIT License.