# Annotations ## Creating and Reading Annotations Load the Anise library. require 'anise' Given an example class X we can apply annotations to it using the #ann method. class X extend Anise::Annotations ann :x1, :a=>1 ann :x1, :b=>2 end We can then use #ann to lookup the set annotations. X.ann(:x1,:a).should == 1 The #ann method is a public interface, so we can define annotation externally as well. X.ann :x1, :a => 2 X.ann(:x1, :a).should == 2 ## Annotation Added Callback Given a sample class Y, we can use a standard callback method #annotation_added(). class Y extend Anise::Annotations class << self attr :last_callback def annotation_added(ref, ns) @last_callback = [ns, ref, ann(ref/ns)] end end end Now if we add an annotation, we will see the callback catches it. Y.ann :x1, :a=>1 Y.last_callback.should == [:ann, :x1, {:a => 1}] We will do it again to be sure. Y.ann :x1, :b=>2 Y.last_callback.should == [:ann, :x1, {:a => 1, :b => 2}] ## Using Callbacks for Attribute Defaults class ::Module def annotation_added(key, ns) return unless ns == :ann base = self if value = ann(key, :default) define_method(key) do instance_variable_set("@#{key}", value) unless instance_variable_defined?("@#{key}") base.module_eval{ attr key } instance_variable_get("@#{key}") end end end end Try it out. class Z extend Anise::Annotations attr :a ann :a, :default => 10 end z = Z.new z.a.should == 10 z.a.should == 10 # Annotative Attributes Create a class that uses the `Annotative::Attributes` mixin. class X extend Anise::Annotative::Attributes attr :a, :count => 1 end Then we can see tht the attribute method `:a` has an annotation entry. X.ann(:a, :count) #=> 1 # Method Annotations Create a class that uses the `Annotative::Methods` mixin. class X extend Anise::Annotative::Methods def self.doc(string) method_annotation(:doc=>string.to_s) end doc "See what I mean?" def see puts "Yes, I see!" end end See that it is set. X.ann(:see, :doc) #=> "See what I mean?" Method Annotators can override the standard annotation procedure with a custom procedure. In such case no annotations will actually be created unless the `#ann` is called in the procedure. class Y extend Anise::Annotative::Methods def self.list @list ||= [] end def self.doc(string) method_annotation do |method| list << [method, string] end end doc "See here!" def see puts "Yes, I see!" end end See that it is set. Y.list #=> [[:see, "See here!"]] # Variable Annotations Create a class that uses the `Annotative::Variables` mixin. class X extend Anise::Annotative::Variables variable_annotator :@doc @doc = "See what I mean?" def see puts "Yes, I see!" end end See that it is set. X.ann(:see, :@doc).should == "See what I mean?" Variable annotations can override the standard annotation procedure with a custom procedure. class Y extend Anise::Annotative::Variables def self.list @list ||= [] end variable_annotator :@doc do |method, value| list << [method, value] end @doc = "See here!" def see puts "Yes, I see!" end end See that it is set. Y.list #=> [[:see, "See here!"]] = TOPLEVEL Annotations Extending Object with `Annotations` should make them available to all classes. class ::Object extend Anise::Annotations end Given a example class X we can apply annotations to it using the #ann method. class X ann :x1, :a=>1 ann :x1, :b=>2 end We can then use #ann to lookup the set annotations. X.ann(:x1,:a).should == 1 The #ann method is a public interface, so we can define annotation externally as well. X.ann :x1, :a => 2 X.ann(:x1, :a).should == 2 Alternatively the `Annotations` module could be included into the `Module` class. = TOPLEVEL Annotated Attributes Including `AnnotatedAttributes` at the toplevel, i.e. Object, will make annotated attributes univerally available. class ::Object extend Anise::Annotative::Attributes end Create a class that uses it. class X attr :a, :count=>1 end X.ann(:a, :count) #=> 1 Alternatively the `Annotative::Attributes` module could be included into the `Module` class.