Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Ruby
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib
spec
.document
.gitignore
.rspec
Gemfile
Gemfile.lock
LICENSE.txt
README.md init commit
Rakefile
ruby-interface.gemspec

README.md

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"

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

Something went wrong with that request. Please try again.