Ragot
A hack to create hooks around methods, either unobstrusive, with no change
whatsoever in the source of the targeted code, or with the use of Ragot
inherited class methods before
and after
.
Installation
Ruby 2.* (i guess).
Install it with rubygems: gem install ragot
With bundler, add it to your Gemfile
: gem ragot
Use
Ragot.about
(unobstrusive)
require 'ragot'
Ragot.about String, :upcase # only to show it's easy, not to tell you it's a good idea...
"example".upcase
# puts `upcase` called, with params : []. Got 'EXAMPLE' as result, at 2016-09-25 20:47:14 +0200 .0821934
=> "EXAMPLE"
Ragot.about
can be declared anywhere given the targeted class (or module) is
already defined.
Targeted methods don't even need to be defined. If not already, they'll be hooked when added to the targeted class or module.
Options
Let's say you have Person
class in an App
, itself with a log
method.
- block
It is executed in instance context. The return value of the targeted method, as welle as the arguments passed to it, are yielded to the block.
Ragot.about Person, :save do |save_return_value, *params|
# code executed in instance context, `full_name`, `department`, `errors` are Person instance methods
if save_return_value
App.log "#{full_name} created, and added to #{department} at #{Time.now}"
else
App.log "Problem : #{errors} when trying to create #{full_name}"
end
end
- hook
Option default is after
, but can be set to before
.
Ragot.about Person, :change_status, hook: :before do |new_status_param|
App.log "#{full_name} status was #{status} before it was changed to #{new_status_param}"
end
- class
You want to hook a class, instead of instance, method.
Ragot.about Person, :name, class: true do |name, *empty_params|
App.log "#{self} class is asked its name again !" # self is singleton_class
end
- failsafe
You don't want to allow a hook to crash your application.
Ragot.about Person, :cleanup_info, failsafe: true do |result, *info_to_clean|
self.risky_external_push! info_to_clean # won't crash your app even though API call went wrong
end
- env
You may want to setup a hook only in a given set of environments. This option
will be compared to Ragot.env
value, which can be set as such :
Ragot.env = App.env
Ragot.about Person, :save, env: ['staging', 'production'] do |save_return_value|
if save_return_value
self.costly_stats_gathering! # that you don't want to do in 'development'
end
end
- stamp
This will timestamp a call to a method.
irb(main):001:0> require 'ragot'
irb(main):001:0> Ragot.about Person, :suspicious_behaviour_method, stamp: true
irb(main):002:0> Person.new.suspicious_behaviour_method
Entered to_s, with params '[]', at <this time>
Entered to_s, with params '[]', at <this other time>
Entered to_s, with params '[]', at <this yet other time>
=> "suspicious_behaviour_method_result" # called three times then...
before
and after
before
and after
are available as class methods when Ragot is included.
They have the same behaviour and options as about
above (except the hook
option).
If before
or after
methods already exist in class or module, Ragot's won't
overload them. You would still have access to ragot_before
and ragot_after
methods to which before
or after
are aliases anyway.
class PersonAssignment
include Ragot
after :person= do |person, person|
App.log "#{person.full_name} was given a new job as #{self.name}"
end
end
Use cases
- callbacks
Any occurrence when you need to do something that is not the job per se of your application. You then don't want to your class declaration to be clumbered by bits you consider extraneous.
It can be logging, stats, external API push, ...
- debug
Also, when you don't understand the behaviour of a library, or experience a
crash. You can get help from Ragot
to take a closer look without ripping it
open and garble its code with you traces.
Ragot.about JSON, :dump, class: true, stamp: true
=> :dump
JSON.dump({a: 1})
Entered dump, with params '[]',{:a=>1} at 2016-09-26 02:32:12 +0200 .553876
`dump` called, with params : '[{:a=>1}]'. Got '{"a":1}' as result, at 2016-09-26 02:32:12 +0200 .5540571
=> "{\"a\":1}"
Thanks
Eager and sincere thanks to all the Ruby guys and chicks for making all this so easy to devise.
Copyright
I was tempted by the WTFPL, but i have to take time to read it. So far see LICENSE.