class Deterministic::InstanceBuilder

Public Class Methods

new(protocol, type, block) click to toggle source
# File lib/deterministic/protocol.rb, line 43
def initialize(protocol, type, block)
  @protocol, @type, @block = protocol, type, block
  @instance = Class.new(@protocol::Protocol)
end

Public Instance Methods

build() click to toggle source
# File lib/deterministic/protocol.rb, line 48
def build
  @instance.class_exec(&@block)
  protocol  = @protocol::Protocol
  methods   = protocol.methods(false)
  inst_type = @type

  @instance.instance_exec {
    methods.each { |name|
      if method_defined?(name)
        meth        = instance_method(name)
        signature   = protocol.send(name)
        params      = signature.params
        expect_type = inst_type[signature.return_type]

        define_method(name) { |*args|
          args.each_with_index { |arg, i|
            name     = params.keys[i]
            arg_type = params.fetch(name)
            expect_arg_type = inst_type.fetch(arg_type)

            raise TypeError, "Expected arg #{name} to be a #{expect_arg_type}, got #<#{arg.class}: #{arg.inspect}>" unless arg.is_a? expect_arg_type
          }

          result = meth.bind(self).call(*args)
          raise TypeError, "Expected #{name}(#{args.join(', ')}) to return a #{expect_type}, got #<#{result.class}: #{result.inspect}>" unless result.is_a? expect_type
          result
        }
      end
    }
  }

  missing = methods.detect { |m| !@instance.instance_methods(false).include?(m) }

  raise NotImplementedError, "`#{missing}` has no default implementation for #{@protocol} #{@type.to_s}" unless missing.nil?

  @instance
end