# 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.