Reputation: 861
I'd like to define a singleton method for a class object that works just like def
but also does some other stuff. Inheriting classes can use that method like def without having any other special syntax other than using the method name rather than def
.
A concrete example will help. In the code I'm developing I want to mark certain methods as "alpha" or "beta". That way you can query a class object for which methods are normal, which alpha, and which beta. Here's how I do it currently:
# Person
class Person
def self.states
if not instance_variable_defined?(:@states)
@states = {}
end
return @states
end
def self.set_state(method_name, state)
states[method_name] = state
end
end
# Guest
class Guest < Person
set_state 'sender', 'alpha'
def sender(param1, param2)
# do stuff
end
end
puts Guest.states
That last line outputs
{:sender=>"alpha"}
OK, that works. But what I'd really like is to be able to call a method that works just like def
, but which can flag a method as alpha or beta. So the Guest class might look like this:
class Guest < Person
alpha sender(param1, param2)
# do stuff
end
end
I'd rather not use a block, like as follows. Setting aside scoping and other issues, it just doesn't feel like it's defining a method.
alpha('sender') do |param1, param2|
# do stuff
end
Is there a way to metaprogram such a thing?
Upvotes: 0
Views: 68
Reputation: 11050
Instead of attempting to do this with def
, you could instead do this by emulating public
/private
/protected
, something like:
module MethodState
def states
@states ||= {}
end
def current_state
@current_state ||= :normal
end
private :current_state
[:alpha, :beta, :normal].each do |method_state|
define_method method_state do |*method_names|
if method_names.empty?
@current_state = method_state
else
method_names.each { |name| states[name] = method_state }
end
end
end
def method_added(method_name)
states[method_name] = current_state
end
def self.extended(base)
# all already defined methods are 'normal' state methods
base.normal *base.instance_methods(false)
end
end
class Guest
def normal_method_one
end
def normal_method_two
end
extend MethodState
alpha
def sender(p1, p2)
end
beta
def a_beta_method
end
def another_alpha_method
end
alpha :another_alpha_method
normal
def one_last_normal
end
end
Guest.states # => {:normal_method_two=>:normal,
# :normal_method_one=>:normal, :sender=>:alpha,
# :a_beta_method=>:beta, :another_alpha_method=>:alpha,
# :one_last_normal=>:normal}
Upvotes: 2
Reputation: 62668
You can't quite do what you want, because alpha sender(...)
attempts to pass the value of sender through to the alpha
method. This obviously can't work.
However, method definition does return the symbol name of the defined method, which you can then pass to your alpha
method:
class Person
def self.alpha(method_name)
set_state method_name, "alpha"
end
# ...
end
class Guest < Person
alpha def sender(param1, param2)
# do stuff
end
end
This will define sender
and call alpha(:sender)
, which will then invoke set_state
appropriately.
Upvotes: 2