module Anise module Annotations # The {Annotations::Store} class tracks annotations on a per-class bases. # class Store # Setup new Annotations instance. # # @param space [Class,Module] # Class or Module to have annotations. # def initialize(space) @space = space @table = Hash.new { |h,k| h[k]={} } end # Ancestors of spaceal class/module. def ancestors @space.ancestors end # Annotations local to spaceal class/module. def local @table end # Access to local table. def [](ref, ns=:ann) @table[ns][ref] end # Lookup an annotation. Unlike `self[ref]` # this provides a complete annotation _heritage_, # pulling annotations of the same reference name # from ancestor classes and modules. # # Unlike the other annotation methods, this method takes # the `ref` argument before the `ns` argument. This is # it allow `ns` to default to the common annotator `ann`. # # @param ref [Object] Annotation reference key. # # @param ns [Symbol] Annotation namespace. # def lookup(ref=nil, ns=:ann) return @table if ref.nil? ref, ns = ref.to_sym, (ns || :ann).to_sym ann = {} ancestors.reverse_each do |anc| next unless anc.is_a?(Annotations) if h = anc.annotations.local[ns][ref] ann.merge!(h) end end return ann end # Set or read annotations. # # IMPORTANT! Do not use this for in-place modifications. # Use #annotate! instead. # # @pararm ns [Symbol] Annotation namespace. # # @param ref [Object] Annotation reference key. # # @since 0.7.0 def annotate(ns, ref, keys_or_class, keys=nil) if Class === keys_or_class keys ||= {} keys[:class] = keys_or_class else keys = keys_or_class end if Hash === keys update(ns, ref, keys) else key = keys.to_sym lookup(ref, ns)[key] end end # To change an annotation's value in place for a given class or module # it first must be duplicated, otherwise the change may effect annotations # in the class or module's ancestors. # # @pararm ns [Symbol] Annotation namespace. # # @param ref [Object] Annotation reference key. # # @since 0.7.0 def annotate!(ns, ref, keys_or_class, keys=nil) if Class === keys_or_class keys ||= {} keys[:class] = keys_or_class else keys = keys_or_class end if Hash === keys update(ns, ref, keys) else key = keys.to_sym @table[ns][ref] ||= {} begin @table[ns][ref][key] = lookup(ref, ns)[key].dup rescue TypeError @table[ns][ref][key] = lookup(ref, ns)[key] end end end # Update annotations for a given namespace and reference. def update(ns, ref, hash) ref = ref.to_sym @table[ns][ref] ||= {} hash.each do |k,v| @table[ns][ref][k.to_sym] = v end # callback @space.annotation_added(ref, ns) #if method_defined?(:annotation_added) end end end end