In ruby, you can do this:
class Thing
public
def f1
puts "f1"
end
private
def f2
puts "f2"
end
public
def f3
puts "f3"
end
private
def f4
puts "f4"
end
end
where now f1 and f3 and public, f2 and f4 is private. What is going on internally that allows you to invoke a class method that then changes the method definition? How can I implement the same functionality (ostensibly to create my own java like annotations)
for example...
class Thing
fun
def f1
puts "hey"
end
notfun
def f2
puts "hey"
end
end
and fun and notfun would change the following function definitions.
Thanks
You can sometimes shove Ruby into an espressso cup. Let's see how.
Here's a module FunNotFun...
module FunNotFun
def fun
@method_type = 'fun'
end
def notfun
@method_type = 'not fun'
end
def method_added(id)
return unless @method_type
return if @bypass_method_added_hook
orig_method = instance_method(id)
@bypass_method_added_hook = true
method_type = @method_type
define_method(id) do |*args|
orig_method.bind(self).call(*args).tap do
puts "That was #{method_type}"
end
end
@bypass_method_added_hook = false
end
end
... that you can use to extend a class ...
class Thing
extend FunNotFun
fun
def f1
puts "hey"
end
notfun
def f2
puts "hey"
end
end
... with this result:
Thing.new.f1
# => hey
# => That was fun
Thing.new.f2
# => hey
# => That was not fun
But see below the line for a better way.
Annotations (see normalocity's answer) are less trouble and, being a common Ruby idiom, will more easily communicate your code's intent. Here's how to do it with annotations:
module FunNotFun
def fun(method_id)
wrap_method(method_id, "fun")
end
def notfun(method_id)
wrap_method(method_id, "not fun")
end
def wrap_method(method_id, type_of_method)
orig_method = instance_method(method_id)
define_method(method_id) do |*args|
orig_method.bind(self).call(*args).tap do
puts "That was #{type_of_method}"
end
end
end
end
In use, the annotation comes after the method is defined, rather than before:
class Thing
extend FunNotFun
def f1
puts "hey"
end
fun :f1
def f2
puts "hey"
end
notfun :f2
end
The result is the same:
Thing.new.f1
# => hey
# => That was fun
Thing.new.f2
# => hey
# => That was not fun