class Protocol::ProtocolModule

A ProtocolModule object

Public Class Methods

new(&block) click to toggle source

Creates an new ProtocolModule instance.

# File lib/protocol/protocol_module.rb, line 5
def initialize(&block)
  @descriptor     = Descriptor.new(self)
  @implementation = false
  block and module_eval(&block)
end

Public Instance Methods

=~(object)
Alias for: check!
[](name) click to toggle source

Return the Message object named name or nil, if it doesn’t exist.

# File lib/protocol/protocol_module.rb, line 77
def [](name)
  name = name.to_s
  find { |m| m.name == name }
end
check(object) click to toggle source

Check the conformity of object recursively and return true iff it does.

# File lib/protocol/protocol_module.rb, line 147
def check(object)
  check!(object)
rescue CheckFailed
  false
else
  true
end
check!(object) click to toggle source

Check the conformity of object recursively and raise an exception if it doesn’t.

# File lib/protocol/protocol_module.rb, line 130
def check!(object)
  checked = {}
  errors = CheckFailed.new
  each do |message|
    begin
      message.check(object, checked)
    rescue CheckError => e
      errors << e
    end
  end
  errors.empty? or raise errors
  true
end
Also aliased as: =~
check_failures(object) click to toggle source

Return all messages for whick a check failed.

# File lib/protocol/protocol_module.rb, line 156
def check_failures(object)
  check!(object)
rescue CheckFailed => e
  e.errors.map { |e| e.protocol_message }
end
descriptors() click to toggle source

Returns all the protocol descriptions to check against as an Array.

# File lib/protocol/protocol_module.rb, line 12
def descriptors
  descriptors = []
  protocols.each do |a|
    descriptors << a.instance_variable_get(:@descriptor)
  end
  descriptors
end
each()
Alias for: each_message
each_message() { |message| ... } click to toggle source

Iterate over all messages and yield to all of them.

# File lib/protocol/protocol_module.rb, line 88
def each_message(&block) # :yields: message
  messages.each(&block)
  self
end
Also aliased as: each
extend_object(object) click to toggle source
Calls superclass method
# File lib/protocol/protocol_module.rb, line 178
def extend_object(object)
  result = super
  check! object
  result
end
grep(pattern) click to toggle source

Return all message whose names matches pattern.

# File lib/protocol/protocol_module.rb, line 83
def grep(pattern)
  select { |m| pattern === m.name }
end
implementation() click to toggle source

Switch to implementation mode. Defined methods are added to the ProtocolModule as instance methods.

# File lib/protocol/protocol_module.rb, line 232
def implementation
  @implementation = true
end
implementation?() click to toggle source

Return true, if the ProtocolModule is currently in implementation mode. Otherwise return false.

# File lib/protocol/protocol_module.rb, line 238
def implementation?
  @implementation
end
included(modul) click to toggle source

This callback is called, when a module, that was extended with Protocol, is included (via Modul#include/via Class#conform_to) into some other module/class. If modul is a Class, all protocol descriptions of the inheritance tree are collected and the given class is checked for conformance to the protocol. modul isn’t a Class and only a Module, it is extended with the Protocol module.

Calls superclass method
# File lib/protocol/protocol_module.rb, line 170
def included(modul)
  result = super
  if modul.is_a? Class
    check! modul
  end
  result
end
infer(modul, methodname = modul.public_instance_methods(false), block_expected = nil) click to toggle source

Infer a method signature from an instance method named methodname of modul. This means that this protocol should understand these instance methods with their arity and block expectation. Note that automatic detection of blocks does not work for Ruby methods defined in C. You can set the block_expected argument if you want to do this manually.

# File lib/protocol/protocol_module.rb, line 216
def infer(modul, methodname = modul.public_instance_methods(false), block_expected = nil)
  Module === modul or
    raise TypeError, "expected Module not #{modul.class} as modul argument"
  methodnames = methodname.respond_to?(:to_ary) ?
    methodname.to_ary :
    [ methodname ]
  methodnames.each do |methodname|
    m = parse_instance_method_signature(modul, methodname)
    block_expected and m.block_expected = block_expected
    @descriptor.add_message m
  end
  self
end
inspect() click to toggle source

Returns a short string representation of this protocol, that consists of the understood messages. This protocol

FooProtocol = Protocol do
  def bar(x, y, &b) end
  def baz(x, y, z) end
  def foo(*rest) end
end

returns this string:

#<FooProtocol: bar(2&), baz(3), foo(-1)>
# File lib/protocol/protocol_module.rb, line 124
def inspect
  "#<#{name}: #{messages.map { |m| m.shortcut } * ', '}>"
end
messages() click to toggle source

Returns all messages this protocol (plus the included protocols) consists of in alphabetic order. This method caches the computed result array. You have to call reset_messages, if you want to recompute the array in the next call to messages.

# File lib/protocol/protocol_module.rb, line 45
def messages
  result = []
  seen = {}
  descriptors.each do |d|
    dm = d.messages
    dm.delete_if do |m|
      delete = seen[m.name]
      seen[m.name] = true
      delete
    end
    result.concat dm
  end
  result.sort!
end
Also aliased as: to_a
method_added(methodname) click to toggle source

Capture all added methods and either leave the implementation in place or add them to the protocol description.

Calls superclass method
# File lib/protocol/protocol_module.rb, line 256
def method_added(methodname)
  methodname = methodname.to_s
  if specification? and methodname !~ /^__protocol_check_/
    protocol_check = instance_method(methodname)
    parser = MethodParser.new(self, methodname)
    if parser.complex?
      define_method("__protocol_check_#{methodname}", protocol_check)
      understand methodname, protocol_check.arity, parser.block_arg?
    else
      understand methodname, protocol_check.arity, parser.block_arg?
    end
    remove_method methodname
  else
    super
  end
end
protocols() click to toggle source

Return self and all protocols included into self.

# File lib/protocol/protocol_module.rb, line 21
def protocols
  ancestors.select { |modul| modul.is_a? ProtocolModule }
end
reset_messages() click to toggle source

Reset the cached message array. Call this if you want to change the protocol dynamically after it was already used (= the messages method was called).

# File lib/protocol/protocol_module.rb, line 65
def reset_messages
  @messages = nil
  self
end
specification() click to toggle source

Switch to specification mode. Defined methods are added to the protocol description in order to be checked against in later conformance tests.

# File lib/protocol/protocol_module.rb, line 244
def specification
  @implementation = false
end
specification?() click to toggle source

Return true, if the ProtocolModule is currently in specification mode. Otherwise return false.

# File lib/protocol/protocol_module.rb, line 250
def specification?
  !@implementation
end
to_a()
Alias for: messages
to_ruby(result = '') click to toggle source

Concatenates the protocol as Ruby code to the result string and return it. At the moment this method only supports method signatures with generic argument names.

# File lib/protocol/protocol_module.rb, line 28
def to_ruby(result = '')
  result << "#{name} = Protocol do"
  if messages.empty?
    result << "\n"
  else
    messages.each do |m|
      result << "\n"
      m.to_ruby(result)
    end
  end
  result << "end\n"
end
to_s() click to toggle source

Returns a string representation of this protocol, that consists of the understood messages. This protocol

FooProtocol = Protocol do
  def bar(x, y, &b) end
  def baz(x, y, z) end
  def foo(*rest) end
end

returns this string:

FooProtocol#bar(2&), FooProtocol#baz(3), FooProtocol#foo(-1)
# File lib/protocol/protocol_module.rb, line 108
def to_s
  messages * ', '
end
understand(methodname, arity = nil, block_expected = false) click to toggle source

This method defines one of the messages, the protocol in question consists of: The messages which the class, that conforms to this protocol, should understand and respond to. An example shows best which +message+descriptions_ are allowed:

MyProtocol = Protocol do
  understand :bar            # conforming class must respond to :bar
  understand :baz, 3         # c. c. must respond to :baz with 3 args.
  understand :foo, -1        # c. c. must respond to :foo, any number of args.
  understand :quux, 0, true  # c. c. must respond to :quux, no args + block.
  understand :quux1, 1, true # c. c. must respond to :quux, 1 arg + block.
end
# File lib/protocol/protocol_module.rb, line 196
def understand(methodname, arity = nil, block_expected = false)
  m = Message.new(self, methodname.to_s, arity, block_expected)
  @descriptor.add_message(m)
  self
end
understand?(name, arity = nil) click to toggle source

Returns true if it is required to understand the

# File lib/protocol/protocol_module.rb, line 71
def understand?(name, arity = nil)
  name = name.to_s
  !!find { |m| m.name == name && (!arity || m.arity == arity) }
end

Private Instance Methods

parse_instance_method_signature(modul, methodname) click to toggle source
# File lib/protocol/protocol_module.rb, line 202
def parse_instance_method_signature(modul, methodname)
  methodname = methodname.to_s
  method = modul.instance_method(methodname)
  real_module = Utilities.find_method_module(methodname, modul.ancestors)
  parser = MethodParser.new(real_module, methodname)
  Message.new(self, methodname, method.arity, parser.block_arg?)
end