Module: Anise::Annotations

Included in:
Anise::Annotative::Attributes, Anise::Annotative::Methods, Anise::Annotative::Variables, Module
Defined in:
lib/anise/annotations.rb,
lib/anise/annotations/store.rb

Overview

The Annotation provides a framework for annotating class and module related objects, typically symbols representing methods, with arbitrary metadata. These annotations do not do anything in themselves. They are simply data. But they can be put to good use. For instance an attribute validator might check for an annotation called :valid and test against it.

The standard annotator is ‘:ann` and is the defualt value of annotating methods.

class X
  extend Anise::Annotations

  ann :a, :desc => "A Number"

  attr :a
end

X.ann(:a, :desc)  #=> "A Number"

As stated, annotations need not only annotate methods, they are arbitrary, so they can be used for any purpose. For example, we may want to annotate instance variables.

class X
  ann :@a, :valid => lambda{ |x| x.is_a?(Integer) }

  def validate
    instance_variables.each do |iv|
      if validator = self.class.ann(iv)[:valid]
        value = instance_variable_get(iv)
        unless validator.call(value)
          raise "Invalid value #{value} for #{iv}"
        end
      end
    end
  end
end

Or, we could even annotate the class itself.

class X
  ann self, :valid => lambda{ |x| x.is_a?(Enumerable) }
end

Although annotations are arbitrary they are tied to the class or module they are defined against.

Creating custom annotators used to entail using a special ‘#annotator` method, but this limited the way custom annotators could operate. The new way is to define a custom class method that calls the usual `#ann` method, but add in a namespace.

class X
  def self.cool(ref, *keys)
    ann "#{ref}/cool", *keys
  end
end

X.cool(:a, :desc) #=> "Awesome!"

The result is exactly the same as before, but now the custom annotator has complete control over the process.

Defined Under Namespace

Classes: Store

Instance Method Summary collapse

Instance Method Details

#ann(ref, *keys) ⇒ Object

Get/set annotations.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/anise/annotations.rb', line 94

def ann(ref, *keys)
  if ref.to_s.index('/')
    ref, ns = ref.to_s.split('/')
  else
    ns = :ann
  end
  ref, ns = ref.to_sym, ns.to_sym

  if keys.empty?
    annotations.lookup(ref, ns)
  else
    annotations.annotate(ns, ref, *keys)
  end
end

#ann!(ref, *keys) ⇒ Object

Get/set annotations in-place. Use this method instead of ‘#ann` when performing mass updates.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/anise/annotations.rb', line 113

def ann!(ref, *keys)
  if ref.to_s.index('/')
    ref, ns = ref.to_s.split('/')
  else
    ns = :ann
  end
  ref, ns = ref.to_sym, ns.to_sym

  if keys.empty?
    annotations[ref, ns]
  else
    annotations.annotate!(ns, ref, *keys)
  end
end

#annotation_added(ref, ns = :ann) ⇒ Object

Callback method. This method is called for each new annotation.



81
82
83
# File 'lib/anise/annotations.rb', line 81

def annotation_added(ref, ns=:ann)
  super(ref, ns) if defined?(super)
end

#annotationsObject

Access to a class or module’s annotations.



74
75
76
# File 'lib/anise/annotations.rb', line 74

def annotations
  @annotations ||= Store.new(self)
end