I have a class with some methods. It's supersekret but I've reproduced what I can here.
Class RayGun
# flashes red light
# requires confirmation
# makes "zowowowowowow" sound
def stun!
# ...
end
# flashes blue light
# does not require confirmation
# makes "trrrtrtrtrrtrtrtrtrtrtr" sound
def freeze!
# ...
end
# doesn't flash any lights
# does not require confirmation
# makes Windows startup sound
def killoblast!
# ...
end
end
I want to be able to, at runtime, interrogate the class about one of the methods and receive a hash or struct like so:
{:lights => 'red', :confirmation => false, :sound => 'windows'}
What's the best way of doing this? Obviously you could have separate YAML file alongside and set up a convention to relate the two, but ideally I want code and metadata in one place.
The most promising idea I can come up with is something like this:
class RayGun
cattr_accessor :metadata
def self.register_method(hsh)
define_method(hsh.name, hsh.block)
metadata[hsh[:name]] = hsh
end
register_method({
:name => 'stun!',
:lights => 'red',
:confirmation => 'true',
:sound => 'zowowo',
:block => Proc.new do
# code goes here
})
# etc.
end
Anyone got some better ideas? Am I barking up a very wrong tree?
-
Just a little beautyfication:
class RayGun cattr_accessor :metadata def self.register_method(name, hsh, &block) define_method(name, block) metadata[name] = hsh end register_method( 'stun!', :lights => 'red', :confirmation => 'true', :sound => 'zowowo', ) do # code goes here end # etc. end
You do lose easy access to the original closure, but probably don't need it.
To answer the question, it doesn't look bad, you could do something a little more convetional but probably good enough:
class RayGun cattr_accessor :metadata @metadata[:stun!] = {:lights => 'red', :confirmation => 'true', :sound => 'zowowo'} def stun! # ... end # etc. end
In the original example register_method is public, if you planned to use it that way then the second option becomes less usefull because it doesn't ensure consistency.
-
There's the YARD tool which lets you add metadata to methods. I'm pretty sure it just sticks the metadata into the
rdoc
files you generate, but you could easily build off of that to get runtime information. -
I found another strategy poking around http://github.com/wycats/thor/tree. Thor lets you write stuff like this:
Class RayGun < Thor desc "Flashes red light and makes zowowowowow sound" method_options :confirmation => :required def stun! # ... end end
It manages this by using the (undocumented) hook
Module#method_added
. It works like this:Call to
Thor#desc
andThor#method_options
set instance variables@desc
,@method_options
.Defining method
stun!
callsThor#method_added(meth)
Thor#method_added
registersTask.new(meth.to_s, @desc, @method_options)
(roughly speaking) and unsets@desc
,@method_options
.It's now ready for the next method
Neat! So neat that I am going to accept my own answer :)
Daniel Ribeiro : method_added may be undocumented on rdoc, but it is on a documentation linked by the official site: http://www.ruby-doc.org/docs/ProgrammingRuby/html/ospace.html
0 comments:
Post a Comment