RubyInterface

Простенький патерн определения интерфейсов в руби. В противовес стандартным миксинам, для каждого интерфейса создается свой класс и соответсвенно экземпляр класса для каждого объекта с интерфейсом.

module Tree
  extend RubyInterface
  interface :tree do
    include Enumerable
    attr_accessor :parent

    def childs
      @childs ||= []
    end

    def each(&blk)
      blk.call(owner)
      childs.each { |v| v.tree.each(&blk) }
    end

    def set_parent(parent)
      parent.tree.childs << owner
      @parent = parent
    end
  end
end

class A
  include Tree
end

При разработке интерфейса не нужно задумываться о конфликтах имен переменных, методов, можно делать все что угодно. Аргументом к методу interface передается название метода, по которому этот интерфейс будет доступен.

a = A.new
b = A.new

a.tree.set_parent b
b.tree.childs # => [a]
b.tree.map { |o| o } # => [b, a]

А при использовании методов относящихся к интерфейсу мы явно видим к какому же интерфейсу он относится. Всем профит!

В интерфейсе доступен метод owner, возвращающий родительский объект. У класса интерфейса есть interface_base, возвращающий класс, куда интерфейс был заинклужен.

Помимо инстанс метода, создается так же класс-метод. В него можно передать блок, который выполнится в скоупе класса интерфейса. Сам метод возвращает класс интерфейса.

module StateMachine
  extend RubyInterface
  interface :state_machine do
    def self.state(name)
      puts "New state #{name}"
    end
  end
end

class A
  include StateMachine

  state_machine do
    state(:parked) # => New state parked
    state(:idling) # => New state idling
  end
end

При наследовании класса с интерфейсом, создается новый класс интерфейса и наследуется от предыдущего, т.е. повторяет иерархию класса, в который он включен.

Если в блоке interface вызывается метод interfaced, то исполнение блока, передаваемого interfaced происходит после добавления интерфейса в класс, в контексте этого класса.

Пример:

 module A
   extend RubyInterface
   interface :int do
     interfaced do
       def baz
         self.class.int_interface.foo
       end
     end

     def self.foo
       "bar"
     end
   end
 end

 class B
   include A
 end

 B.new.baz # => "bar"

В каждом модуле может быть определено произвольное количество интерфейсов