class Protocol::MethodParser

Parse protocol method definition to derive a Message specification.

Attributes

__source_cache__[RW]
arg_kinds[R]

Returns the kinds of the arguments of the parsed method.

args[R]

Returns the names of the arguments of the parsed method.

arity[R]

Returns the arity of the parsed method.

Public Class Methods

flush_source_cache() click to toggle source

Flushes the source cache.

# File lib/protocol/method_parser/ruby_parser.rb, line 10
def flush_source_cache
  __source_cache__.clear
  self
end
new(modul, methodname, eigenclass = false) click to toggle source

Create a new MethodParser instance for method methodname of module modul. For eigenmethods set eigenclass to true, otherwise bad things will happen.

# File lib/protocol/method_parser/ruby_parser.rb, line 20
def initialize(modul, methodname, eigenclass = false)
  @method = Module === modul ?
    modul.instance_method(methodname) :
    modul.method(methodname)
  compute_args
  parse_method
end

Public Instance Methods

arg(i) click to toggle source

Returns the i-th argument (beginning with 0).

# File lib/protocol/method_parser/ruby_parser.rb, line 32
def arg(i)
  @args[i]
end
arg_kind(i) click to toggle source

Returns the i-th kind of an argument (beginning with 0).

# File lib/protocol/method_parser/ruby_parser.rb, line 40
def arg_kind(i)
  @arg_kinds[i]
end
block_arg?() click to toggle source

Return true if a block argument was detected.

# File lib/protocol/method_parser/ruby_parser.rb, line 54
def block_arg?
  @arg_kinds.last == :block
end
complex?() click to toggle source

Return true if this protocol method is a complex method, which ought to be called for checking conformance to the protocol.

# File lib/protocol/method_parser/ruby_parser.rb, line 49
def complex?
  @complex
end

Private Instance Methods

cached_source(filename) click to toggle source
# File lib/protocol/method_parser/ruby_parser.rb, line 84
def cached_source(filename)
  cache = self.class.__source_cache__
  unless source = cache[filename]
    begin
      source = IO.readlines(filename)
      cache[filename] = source
      $DEBUG and warn "#{self.class} just cached #{filename.inspect}."
    rescue SystemCallError => e
      $DEBUG and warn "Caught #{e.class}: #{e}"
      nil
    end
  end
  source
end
compute_args() click to toggle source
# File lib/protocol/method_parser/ruby_parser.rb, line 60
def compute_args
  @arity    = @method.arity
  if @method.respond_to?(:parameters)
    parameters = @method.parameters
    @args, @arg_kinds = parameters.map do |kind, name|
      case  kind
      when :req
        [ name, kind ]
      when :opt
        [ name, kind ]
      when :rest
        [ :"*#{name}", kind ]
      when :block
        [ :"&#{name}", kind ]
      end
    end.compact.transpose
  else
    raise NotImplementedError,
      "#{@method.class}#parameters as in ruby version >=1.9.2 is required"
  end
  @args         ||= []
  @arg_kinds    ||= []
end
parse_method() click to toggle source
# File lib/protocol/method_parser/ruby_parser.rb, line 99
def parse_method
  @complex  = false
  filename, lineno = @method.source_location
  if filename
    source = cached_source(filename) or return
    source = source[(lineno - 1)..-1].join
    current = 0
    tree = nil
    parser = RubyParser.new
    while current = source.index('end', current)
      current += 3
      begin
        tree = parser.parse(source[0, current], filename)
        break
      rescue SyntaxError, Racc::ParseError
      end
      parser.reset
    end
    ary = tree.to_a.flatten
    @complex = ary.any? { |node| [ :call, :fcall, :vcall ].include?(node) }
    if ary.index(:yield) and @arg_kinds.last != :block
      @args.push :'&block'
      @arg_kinds.push :block
    end
  end
end