Ranno is a way to annotate your project, to provide useful “compile”-time
and runtime behavior. Examples of this behavior will eventually be made and
put into the specs, as will documentation. For now, you have to either look
at the specs or read the small amount of code there is. Or ask me.
-Max
Currently, there are two poorly named types of annotations: class annotations and
instance annotations.
Class annotations execute once and only once — when the method is defined. The
class annotation is passed the name of the method it’s annotating when that
method becomes defined.
module MyAnnotations
extend Ranno::Annotations
class_annotation :announce do |method_name|
puts "Huzzah, #{method_name.inspect} has been defined!"
# To store state, use a @instance-style variable here
# and in MyClass, use the same @instance-style variable in your class
# methods
end
end
class MyClass
include Ranno::Base
use_annotations ClassAnnotations
announce
def parse_data(data)
"Consider it parsed!"
end
end
Upon loading this file, the output "Huzzah, :parse_data has been defined!
will
be printed to the console.
Instance annotations, on the other hand, run every time a method is called,
either before, after, or before and after it’s called. Unlike class
annotations, instance annotations have access to the class’s instance variables
(since they’re invoked after the class is fully initialized).
module InstanceAnnotations
extend Ranno::Annotations
# Count the total number of times method_name has been executed (in this class)
# This is executed each time before the annotated method is called
instance_annotation :counter, :hook => :before do |method_name|
@counter ||= {}
@counter[method_name] = 0 unless @counter.key? method_name
@counter[method_name] += 1
end
end
class MyClass
include Ranno::Base
use_annotations InstanceAnnotations
counter
def count_me
1 + 1
end
def check_counter(method_name)
@counter[method_name]
end
end
m = MyClass.new
4.times { m.count_me }
puts m.check_counter(:count_me) # => will output 4
The possibilities for the :hook argument are:
:before
:after
:both
[:before, :after]
- omitted (defaults to
:before
)
Another thing you may notice from the above example is that instance annotations
are actually defined in the scope of the class, meaning you have access to all
the instance and class variables you would in one of your own methods.
You can also do slightly more interesting things, like pass in arguments to the
annotation, like so:
module InstanceAnnotations
extend Ranno::Annotations
instance_annotation :is_like do |method_name, opts|
opts ||= {}
if opts.key? :smells_like
puts method_name + ' smells like ' + opts[:smells_like]
end
if opts.key? :sounds_like
puts method_name + ' sounds like ' + opts[:sounds_like]
end
end
end
class MyClass
include Ranno::Base
use_annotations InstanceAnnotations
is_like :smells_like => 'a monkey', :sounds_like => 'a dog'
def your_mom
1 + 1
end
end
Any arguments you pass in to the annotation (like :smells_like, above) will
be passed in after method_name to the annotation definition block (it doesn’t
have to be a hash). If you want, you can pass in arguments at the annotation
definition level:
module InstanceAnnotations
extend Ranno::Annotations
instance_annotation :generic_ann, :foo => bar do |method_name|
opts ||= {}
ranno_params[:foo] == :bar
# HINT: you can access the hook state this way too, but I tweak the :hook
# value a bit before passing it in here. If you hook before AND after a
# method is called, I change the ranno_params[:hook] value to :before
# before the annotated method is called and to :after after it's called.
end
end
Questions? Let me know by sending me a message!
For more examples and/or if you think this document is out of date, check out
the specs.