Skip to content

Instantly share code, notes, and snippets.

@JonathonMA
Created April 14, 2014 00:29
Show Gist options
  • Save JonathonMA/10608022 to your computer and use it in GitHub Desktop.
Save JonathonMA/10608022 to your computer and use it in GitHub Desktop.
module IPoint
attr_accessor :x, :y
end
module IReadonlyPoint
attr_reader :x, :y
end
module IReadonlyLine
attr_reader :source, :destination
end
class Point
attr_accessor :x, :y
end
class HalfPoint
attr_accessor :x
end
class ReadonlyPoint
attr_reader :x, :y
end
class StraightLine
attr_accessor :source, :destination
end
class EmailAnalyzer
def source user, address
end
def destination user, address
end
end
module Interface
InterfaceArityError = Class.new StandardError
def self.implements interface, *objects
objects.each do |object|
interface.instance_methods(false).each do |method|
fail NotImplementedError, "#{object}##{method}" unless object.respond_to? method
interface_arity = interface.instance_method(method).arity
implementation_arity = object.method(method).arity
fail InterfaceArityError, method if interface_arity != implementation_arity
end
end
end
end
describe Interface do
it "it should not raise an error for full implementations" do
Interface.implements IPoint, Point.new
Interface.implements IReadonlyPoint, Point.new, ReadonlyPoint.new
Interface.implements IReadonlyLine, StraightLine.new
end
it "should fail on non-implementations" do
expect {
Interface.implements IPoint, HalfPoint.new
}.to raise_error(NotImplementedError)
expect {
Interface.implements IPoint, ReadonlyPoint.new
}.to raise_error(NotImplementedError)
end
it "should fail on implementations with incorrect arity" do
expect {
Interface.implements IReadonlyLine, EmailAnalyzer.new
}.to raise_error(Interface::InterfaceArityError)
end
it "can be used in classes" do
class Drawer
def draw point1, point2
Interface.implements IPoint, point1, point2
# draws a line ...
end
end
drawer = Drawer.new
point1 = Point.new
point2 = Point.new
drawer.draw(point1, point2)
expect {
drawer.draw("5,3", point2)
}.to raise_error
end
end
@JonathonMA
Copy link
Author

module Interface
  InterfaceArityError = Class.new StandardError

  def self.implements interface, *objects
    objects.each do |object|
      interface.instance_methods(false).each do |method|
        fail NotImplementedError, "#{object}##{method}" unless object.respond_to? method

        interface_arity = interface.instance_method(method).arity
        implementation_arity = object.method(method).arity

        fail InterfaceArityError, method if interface_arity != implementation_arity
      end
    end
  end

  def self.enforced(interface)
    Module.new do
      define_method(:initialize) do |*args|
        Interface.implements(interface, self)
      end
    end
  end
end

module IPoint
  attr_accessor :x, :y
end

module IHalfPoint
  attr_accessor :x
end

class HalfPoint
  attr_accessor :x

  include Interface.enforced(IHalfPoint)
end

class BadPoint
  attr_accessor :x

  include Interface.enforced(IPoint)
end

a = HalfPoint.new
b = BadPoint.new
# => interface.rb:7:in `block (2 levels) in implements': #<BadPoint:0x007f9eeb8965c0>#y (NotImplementedError)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment