Annotated Attributes

This framework modifies the attr_* methods to allow easy addition of annotations. It the built in attribute methods (attr, attr_reader, attr_writer and attr_accessor), to allow annotations to added to them directly rather than requireing a separate ann statement.

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

See annotation.rb for more information.

NOTE This library was designed to be backward compatible with the standard versions of the same methods.

Methods
Included Modules
Public Instance methods
ann(*args)
# File lib/facets/more/annotation.rb, line 232
  def ann(*args)
    harg = args.last.is_a?(Hash) ? args.pop : {}

    #return annotator if args.empty? and harg.empty?
    return @_ann ||= Annotator.new( self ) if args.empty? and harg.empty?

    # for array of "mini-hashes" format
    if harg.empty? and Hash === args[0]
      args.each{|e| e.each{|k,v| harg[k]=v}}
      args = []
    end

    # querying a single annotation
    if harg.empty? and args.size == 1
      return annotation if self == args[0] or :self == args[0]
      #return args[0].annotation unless args[0].is_a?(Symbol) or args[0].is_a?(String)
      return method_annotation( args[0] ) #.inherit
    end

    # if last element is a class (and there are more then just the one element)
    harg[:class] = args.pop if args.last.is_a?(Class) and args.size > 1

    if args.empty?  # hash format
      harg.each do |key,hsh|
        if key == :self
          annotation.__update__( hsh )
          next
        end
        note = annotated?( key )
        if note and null != note
          note.__update__( hsh )
        else
          define_annotation( key, hsh )
        end
      end
      return harg.keys
    else  # name, hash format
      args.each do |key|
        if key == :self
          annotation.__update__( harg )
          next
        end
        note = annotated?( key )
        if note and null != note
          note.__update__( harg )
        else
          define_annotation( key, harg )
        end
      end
      return args
    end

  end
annotated?( key )

Does this module/class or its ancestors have any annotation for a given method?

# File lib/facets/more/annotation.rb, line 204
  def annotated?( key )
    return annotation if self == key
    r = method_annotation( key )
    null == r ? nil : r
  end
annotated_methods(include_ancestors=true)

Returns a list of all annotated methods.

# File lib/facets/more/annotation.rb, line 211
  def annotated_methods(include_ancestors=true)
    annlist = []
    if include_ancestors
      ancestors.inject([]){ |memo, anc|
        memo | anc.method_annotation.keys
      }
    else
      method_annotation.keys
    end
  end
annotation( harg=nil )

def annotation

  @_annotation ||= Annotation.new.inherits( self ){ |anc| anc.annotation }

end

# File lib/facets/more/annotation.rb, line 183
  def annotation( harg=nil )
    @_annotation ||= Annotation.new.inherits( self ){ |anc| anc.annotation }
    return @_annotation unless harg
    @_annotation.__update__( harg ) if harg
  end
append_features( base )
# File lib/facets/more/classmethods.rb, line 106
  def append_features( base )
    result = append_features_without_classmethods( base )
    if const_defined?( :ClassMethods )
      base.extend( self::ClassMethods )
      unless base.is_a?( Class )
        unless base.const_defined?( :ClassMethods )
          base.const_set( :ClassMethods, Module.new )
        end
        my = self
        base::ClassMethods.class_eval do
          include my::ClassMethods
        end
      end
    end
    result
  end
attr( *args )
# File lib/facets/more/annattr.rb, line 45
  def attr( *args )
    args.flatten!
    case args.last
    when TrueClass
      args.pop
      attr_accessor( *args )
    when FalseClass
      args.pop
      attr_reader( *args )
    else
      attr_reader( *args )
    end
  end
class_inherit( &yld )

Alias for class_methods

class_methods( &yld )
This method is also aliased as class_inherit
# File lib/facets/more/classmethods.rb, line 123
  def class_methods( &yld )
    if const_defined?( :ClassMethods )
      self::ClassMethods.class_eval( &yld )
    else
      self.const_set( :ClassMethods, Module.new( &yld ) )
    end
    extend( self::ClassMethods )
    self::ClassMethods
  end
extend(*args)
# File lib/facets/more/paramix.rb, line 117
  def extend(*args)
    params = args.last.is_a?(Hash) ? args.pop : {}
    args.each do |mod|
      (class << self; self; end).class_eval do
        mixin_parameters[mod] = params
        if mod.basename
          define_method( mod.basename ) do |key|
            if params.key?(key)
              params[key]
            else
              super if defined?( super )
            end
          end
        end
      end
    end
    r = extend_without_parameters(*args)
    for mod in args
      if mod.method_defined?(:extended_with_parameters)
        mod.extended_with_parameters( self, params )
      end
    end
    r
  end
include(*args)
# File lib/facets/more/paramix.rb, line 92
  def include(*args)
    params = args.last.is_a?(Hash) ? args.pop : {}
    args.each do |mod|
      mixin_parameters[mod] = params
      if mod.basename
        define_method( mod.basename ) do |key|
          if params.key?(key)
            params[key]
          else
            super if defined?( super )
          end
        end
      end
    end
    r = include_without_parameters(*args)
    for mod in args
      if mod.respond_to?(:included_with_parameters)
        mod.included_with_parameters( self, params )
      end
    end
    r
  end
method_annotation( key=nil )
# File lib/facets/more/annotation.rb, line 189
  def method_annotation( key=nil )
    @_method_annotation ||= {}
    return @_method_annotation unless key
    key = key.to_sym
    if a = @_method_annotation[key]
      return a
    elsif ancestors.any? { |anc| anc.method_annotation[key] }
      @_method_annotation[key] = Annotation.new.inherits( self ){ |anc| anc.method_annotation[key] }
    else
      null
    end
  end
method_overloads()
# File lib/facets/more/overload.rb, line 43
  def method_overloads
    @method_overloads ||= {}
  end
mixin_parameters()

Store for module parameters. This is local per module and indexed on module/class included-into.

# File lib/facets/more/paramix.rb, line 88
  def mixin_parameters ; @mixin_parameters ||= {} ; end
overload( name, *signiture, &block )
# File lib/facets/more/overload.rb, line 47
  def overload( name, *signiture, &block )
    name = name.to_sym

    if method_overloads.key?( name )
      method_overloads[name][signiture] = block

    else
      method_overloads[name] = {}
      method_overloads[name][signiture] = block

      if method_defined?( name )
        #method_overloads[name][nil] = instance_method( name ) #true
        alias_method( "#{name}Generic", name )
        has_generic = true
      else
        has_generic = false
      end

      define_method( name ) do |*args|
        ovr = self.class.method_overloads["#{name}".to_sym]
        sig = args.collect{ |a| a.class }
        hit = nil
        faces = ovr.keys.sort { |a,b| b.size <=> a.size }
        faces.each do |cmp|
          next unless cmp.size == sig.size
          cmp.size.times { |i|
            next unless cmp[i] < sig[i]
          }
          hit = cmp
        end

        if hit
          ovr[hit].call(*args)
        else
          if has_generic #ovr[nil]
            send( "#{name}Generic", *args )
            #ovr[nil].bind(self).call(*args)
          else
            raise NoMethodError
          end
        end

      end

    end

  end
Private Instance methods
define_annotation( key, note )

Define annotation for a key. The note parameter must be a Hash or Hash-like object.

# File lib/facets/more/annotation.rb, line 225
  def define_annotation( key, note )
    key = key.to_sym
    method_annotation[key] = Annotation.new(note).inherits(self){ |anc| anc.method_annotation[key] }
    method_annotation[key]
  end