I'm writing a module in Ruby 1.9.2 that defines several methods. When any of these methods is called, I want each of them to execute a certain statement first.
module MyModule
def go_forth
a re-used statement
# code particular to this method follows ...
end
def and_multiply
a re-used statement
# then something completely different ...
end
end
But I want to avoid putting that a re-used statement code explicitly in every single method. Is there a way to do so?
(If it matters, a re-used statement will have each method, when called, print its own name. It will do so via some variant of puts __method__.)
Like this:
module M
def self.before(*names)
names.each do |name|
m = instance_method(name)
define_method(name) do |*args, &block|
yield
m.bind(self).(*args, &block)
end
end
end
end
module M
def hello
puts "yo"
end
def bye
puts "bum"
end
before(*instance_methods) { puts "start" }
end
class C
include M
end
C.new.bye #=> "start" "bum"
C.new.hello #=> "start" "yo"
This is exactly what aspector is created for.
With aspector you don't need to write the boilerplate metaprogramming code. You can even go one step further to extract the common logic into a separate aspect class and test it independently.
require 'aspector'
module MyModule
aspector do
before :go_forth, :add_multiply do
...
end
end
def go_forth
# code particular to this method follows ...
end
def and_multiply
# then something completely different ...
end
end
You can implement it with method_missing through proxy Module, like this:
module MyModule
module MyRealModule
def self.go_forth
puts "it works!"
# code particular to this method follows ...
end
def self.and_multiply
puts "it works!"
# then something completely different ...
end
end
def self.method_missing(m, *args, &block)
reused_statement
if MyModule::MyRealModule.methods.include?( m.to_s )
MyModule::MyRealModule.send(m)
else
super
end
end
def self.reused_statement
puts "reused statement"
end
end
MyModule.go_forth
#=> it works!
MyModule.stop_forth
#=> NoMethodError...
You can do this by metaprogramming technique, here's an example:
module YourModule
def included(mod)
def mod.method_added(name)
return if #added
#added = true
original_method = "original #{name}"
alias_method original_method, name
define_method(name) do |*args|
reused_statement
result = send original_method, *args
puts "The method #{name} called!"
result
end
#added = false
end
end
def reused_statement
end
end
module MyModule
include YourModule
def go_forth
end
def and_multiply
end
end
works only in ruby 1.9 and higher
UPDATE: and also can't use block, i.e. no yield in instance methods
I dunno, why I was downvoted - but a proper AOP framework is better than meta-programming hackery. And thats what OP was trying to achieve.
http://debasishg.blogspot.com/2006/06/does-ruby-need-aop.html
Another Solution could be:
module Aop
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def before_filter(method_name, options = {})
aop_methods = Array(options[:only]).compact
return if aop_methods.empty?
aop_methods.each do |m|
alias_method "#{m}_old", m
class_eval <<-RUBY,__FILE__,__LINE__ + 1
def #{m}
#{method_name}
#{m}_old
end
RUBY
end
end
end
end
module Bar
def hello
puts "Running hello world"
end
end
class Foo
include Bar
def find_hello
puts "Running find hello"
end
include Aop
before_filter :find_hello, :only => :hello
end
a = Foo.new()
a.hello()
It is possible with meta-programming.
Another alternative is Aquarium. Aquarium is a framework that implements Aspect-Oriented Programming (AOP) for Ruby. AOP allow you to implement functionality across normal object and method boundaries. Your use case, applying a pre-action on every method, is a basic task of AOP.
Related
How to define an original name scope in module/class with Ruby
I want to implement class like the following:
module SomeModule
extend OriginalNameScope
scope(:some) do
def method1
puts 1
end
def method2
puts 2
end
end
end
class SomeClass
include SomeModule
end
c = SomeClass.new
# I want to call methods like the following:
c.some_method1
c.some_method2
How to implement the OriginalNameScope module? I found out to get the method definitions in this method, but I don't know how to redefine methods with a prefix scope.
module OriginalNameScope
def scope(name, &method_definition)
puts method_definition.class
# => Proc
end
end
This is actually just a combination of some simple standard Ruby metaprogramming patterns and idioms:
module OriginalNameScope
def scope(name)
singleton_class.prepend(Module.new do
define_method(:method_added) do |meth|
if name && !#__recursion_guard__
#__recursion_guard__ = meth
method = instance_method(meth)
undef_method(meth)
define_method(:"#{name}_#{meth}") do |*args, &block|
method.bind(self).(*args, &block)
end
end
#__recursion_guard__ = nil
super(meth)
end
end)
yield
end
end
I just slapped this together, there's probably a lot that can be improved (e.g. use Refinements) and simplified.
I try to write a metaprogramming for execute a method before 'master' method. Why ? Because, I have several class and it's ugly to repeat the call in the head of the method
Case :
class MyClass
include MySuperModule
before :method, call: before_method
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
module MySuperModule
# the awesome code
end
Output :
SomeClass.new.method => "Before.. Method.."
So, I try write a module with ClassMethodsor method_missingwithout success.
You don't need a gem for simple metaprogramming like this. What you can do is redefine the "after" method to call the "before" method and then the original "after" method.
This works even when using before multiple times on the same method or when creating a chain of before calls.
module MySuperModule
def before meth, opts
old_method = instance_method(meth)
define_method(meth) do
send opts[:call]
old_method.bind(self).call
end
end
end
class MyClass
extend MySuperModule
def foo
puts "foo"
end
def bar
puts "bar"
end
def baz
puts "baz"
end
before :foo, call: :bar
before :bar, call: :baz
end
MyClass.new.foo
# baz
# bar
# foo
If it is just for subclassing purposes you can take advantage of Module#prepend:
class Superclass
def self.inherited(subclass)
# subclass.send :prepend, Module.new { on Ruby < 2.1
subclass.prepend Module.new {
def method
before_method
super
end
}
end
def before_method
puts 'Before'
end
end
class Subclass < Superclass
def method
puts 'Method'
end
end
Subclass.new.method
#=> Before
#=> Method
What you are looking for is Aspect oriented programming support for ruby. There are several gems implementing this, like aquarium.
Another way to do this is to use the rcapture gem.
It is pretty awesome.
Eg:
require 'rcapture'
class A
# Makes the class intercept able
include RCapture::Interceptable
def first
puts 'first'
end
def second
puts 'second'
end
end
# injects methods to be called before each specified instance method.
A.capture_pre :methods => [:first, :second] do
puts "hello"
end
n = A.new
n.first
n.second
produces:
hello
first
hello
second
Maybe you can use a decorator. In ruby there is a nice gem called 'drapeer'. See Drapper Link
Every call in ruby runs through set_trace_func so you can hook into that and call exactly what you want. Not the prettiest solution and there are better ways but it does work. Another option is the Hooks gem, though I haven't tried it myself, it looks like it should give you the ability to do what you want.
module MySuperModule
# the awesome code
end
class MyClass
include MySuperModule
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
set_trace_func proc { |event, file, line, id, binding, class_name|
if event == "call" && class_name == SomeClass && id == :method
caller = binding.eval("self")
caller.send(:before_method)
end
}
SomeClass.new.method
#=> Before..
#=> Method..
In the code below, I would like to call the class method done of the class that includes the module from inside self.hello
Explanation:
A::bonjour will call Mod::hello so will B::ciao
I would like to be able to detect the "calling class" (A or B) in Mod::hello in order to be able to call the A::done or B::done
module Mod
def self.hello
puts "saying hello..."
end
end
class A
include Mod
def self.bonjour
Mod::hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
Mod::hello
end
def self.done
puts "finitto"
end
end
While (perhaps) not as clean as Niklas' answer, it's still easily doable, and IMO cleaner than the usage pattern shown in the OP which relies on knowing which module is mixed in.
(I prefer not having to pass an argument to mixin methods like this when other means exist.)
The output:
pry(main)> A::bonjour
saying hello...
fini
pry(main)> B::ciao
saying hello...
finitto
The guts:
module Mod
module ClassMethods
def hello
puts "saying hello..."
done
end
end
def self.included(clazz)
clazz.extend ClassMethods
end
end
The modified class declarations, removing the explicit module reference:
class A
include Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
hello
end
def self.done
puts "finitto"
end
end
You may also supply a default implementation of done:
module Mod
module ModMethods
def hello
puts "saying hello..."
done
end
def done
throw "Missing implementation of 'done'"
end
end
def self.included(clazz)
clazz.extend ModMethods
end
end
As a comment to this post points out, if the snippet in the OP is a faithful representation of the actual usecase, you might as well use extend (instead of include), leaving everything cleaner:
module Mod
def hello
puts "saying hello..."
done
end
def done
raise NotImplementError("Missing implementation of 'done'")
end
end
And the classes using extend:
class A
extend Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end
Heres what I have/want:
module Observable
def observers; #observers; end
def trigger(event, *args)
good = true
return good unless (#observers ||= {})[event]
#obersvers[event].each { |e| good = false and break unless e.call(self, args) }
good
end
def on(event, &block)
#obersvers ||= {}
#obersvers[event] ||= []
#observers[event] << block
end
end
class Item < Thing
include Observable
def pickup(pickuper)
return unless trigger(:before_pick_up, pickuper)
pickuper.add_to_pocket self
trigger(:after_pick_up, pickuper)
end
def drop(droper)
return unless trigger(:before_drop, droper)
droper.remove_from_pocket self
trigger(:after_drop, droper)
end
# Lots of other methods
end
# How it all should work
Item.new.on(:before_pickup) do |item, pickuper|
puts "Hey #{pickuper} thats my #{item}"
return false # The pickuper never picks up the object
end
While starting on trying to create a game in Ruby, I thought it would be great if it could be based all around Observers and Events. The problem is have to write all of these triggers seems to be a waste, as it seems like a lot of duplicated code. I feel there must be some meta programming method out there to wrap methods with functionality.
Ideal Sceanrio:
class CustomBaseObject
class << self
### Replace with correct meta magic
def public_method_called(name, *args, &block)
return unless trigger(:before_+name.to_sym, args)
yield block
trigger(:after_+name.to_sym, args)
end
###
end
end
And then I have all of my object inherit from this Class.
I'm still new to Ruby's more advanced meta programming subjects, so any knowledge about this type of thing would be awesome.
There are a several ways to do it with the help of metaprogramming magic. For example, you can define a method like this:
def override_public_methods(c)
c.instance_methods(false).each do |m|
m = m.to_sym
c.class_eval %Q{
alias #{m}_original #{m}
def #{m}(*args, &block)
puts "Foo"
result = #{m}_original(*args, &block)
puts "Bar"
result
end
}
end
end
class CustomBaseObject
def test(a, &block)
puts "Test: #{a}"
yield
end
end
override_public_methods(CustomBaseObject)
foo = CustomBaseObject.new
foo.test(2) { puts 'Block!' }
# => Foo
Test: 2
Block!
Bar
In this case, you figure out all the required methods defined in the class by using instance_methods and then override them.
Another way is to use so-called 'hook' methods:
module Overrideable
def self.included(c)
c.instance_methods(false).each do |m|
m = m.to_sym
c.class_eval %Q{
alias #{m}_original #{m}
def #{m}(*args, &block)
puts "Foo"
result = #{m}_original(*args, &block)
puts "Bar"
result
end
}
end
end
end
class CustomBaseObject
def test(a, &block)
puts "Test: #{a}"
yield
end
include Overrideable
end
The included hook, defined in this module, is called when you include that module. This requires that you include the module at the end of the class definition, because included should know about all the already defined methods. I think it's rather ugly :)
Is it possible to make this work, without having to include the module at the end of the class and just include it at the top?
module VerboseJob
def self.included(job_class)
class << job_class
alias_method :original_perform, :perform
def perform(*args)
JobLogger.verbose { original_perform(*args) }
end
end
end
end
class HelloJob
include VerboseJob
def self.perform(arg1, arg2)
puts "Job invoked with #{arg1} and #{arg2}"
end
end
What I want to happen, is for HelloJob.perform to actually invoke VerboseJob.perform (which then calls the original method inside a block). Because the module here is included at the top of the class, this doesn't work, since perform isn't yet defined. Moving the include to the end does work, but is there a way that's a bit more forgiving? I like to keep all the included modules at the top of my class definitions.
I'm sort of looking for some method that is called on Module or Class when it has been fully loaded, instead of as it is interpreted by the runtime.
Here is a rather roundabout/hackish way of doing it that I came up with by deferring the definition of the wrapper method until the original method had been defined:
module A
def self.included(base)
base.class_eval do
def self.singleton_method_added(name)
##ran||=false
if name==:perform && !##ran
##ran=true
class<<self
alias_method :original_perform, :perform
def perform(*args)
puts "Hello"
original_perform(*args)
end
end
end
end
end
end
end
class B
include A
def self.perform
puts "Foobar"
end
end
B.perform
Edit:
d11wtq simplified this to the much cleaner:
module VerboseJob
module ClassMethods
def wrap_perform!
class << self
def perform_with_verbose(*args)
JobLogger.verbose { perform_without_verbose(*args) }
end
alias_method_chain :perform, :verbose \
unless instance_method(:perform) == instance_method(:perform_with_verbose)
end
end
def singleton_method_added(name)
wrap_perform! if name == :perform
end
end
def self.included(job_class)
job_class.extend ClassMethods
job_class.wrap_perform! if job_class.respond_to?(:perform)
end
end
Assuming that you want all of your classes defined before you want perform run, you may want to use Kernel#at_exit:
Converts block to a Proc object (and therefore binds it at the point
of call) and registers it for execution when the program exits. If
multiple handlers are registered, they are executed in reverse order
of registration.
def do_at_exit(str1)
at_exit { print str1 }
end
at_exit { puts "cruel world" }
do_at_exit("goodbye ")
exit
produces:
goodbye cruel world
You may also want to look at how unit testing frameworks, such as Test::Unit or MiniTest, handle delaying the running of tasks.