Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
453 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,55 @@ | |||
PATH | |||
remote: . | |||
specs: | |||
ruby-interface (0.0.1) | |||
activesupport (> 0.1) | |||
i18n (> 0.1) | |||
ruby-interface | |||
|
|||
GEM | |||
remote: http://rubygems.org/ | |||
specs: | |||
activesupport (3.0.5) | |||
bluecloth (2.1.0) | |||
builder (3.0.0) | |||
diff-lcs (1.1.2) | |||
geminabox (0.2.11) | |||
builder | |||
sinatra | |||
git (1.2.5) | |||
i18n (0.5.0) | |||
jeweler (1.5.2) | |||
bundler (~> 1.0.0) | |||
git (>= 1.2.5) | |||
rake | |||
rack (1.2.2) | |||
rake (0.8.7) | |||
rcov (0.9.9) | |||
rspec (2.3.0) | |||
rspec-core (~> 2.3.0) | |||
rspec-expectations (~> 2.3.0) | |||
rspec-mocks (~> 2.3.0) | |||
rspec-core (2.3.1) | |||
rspec-expectations (2.3.0) | |||
diff-lcs (~> 1.1.2) | |||
rspec-mocks (2.3.0) | |||
sinatra (1.2.1) | |||
rack (~> 1.1) | |||
tilt (>= 1.2.2, < 2.0) | |||
tilt (1.2.2) | |||
undev (0.0.7) | |||
geminabox | |||
yard (0.6.5) | |||
|
|||
PLATFORMS | |||
ruby | |||
|
|||
DEPENDENCIES | |||
bluecloth | |||
bundler (~> 1.0.0) | |||
jeweler (~> 1.5.2) | |||
rcov | |||
rspec (~> 2.3.0) | |||
ruby-interface! | |||
undev | |||
yard (~> 0.6.0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,93 @@ | |||
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, возвращающий родительский объект. У класса интерфейса есть <tt>interface_base</tt>, возвращающий класс, куда интерфейс был заинклужен. | |||
|
|||
Помимо инстанс метода, создается так же класс-метод. В него можно передать блок, который выполнится в скоупе класса интерфейса. Сам метод возвращает класс интерфейса. | |||
|
|||
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 | |||
|
|||
При наследовании класса с интерфейсом, создается новый класс интерфейса и наследуется от предыдущего, т.е. повторяет иерархию класса, в который он включен. | |||
|
|||
Если в блоке <tt>interface</tt> вызывается метод <tt>interfaced</tt>, то исполнение блока, передаваемого <tt>interfaced</tt> | |||
происходит после добавления интерфейса в класс, в контексте этого класса. | |||
|
|||
Пример: | |||
|
|||
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" | |||
|
|||
В каждом модуле может быть определено произвольное количество интерфейсов |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,8 @@ | |||
class RubyInterfaceHandler < YARD::Handlers::Ruby::Base | |||
handles method_call(:interface) | |||
|
|||
def process | |||
parse_block(statement.last.last) | |||
rescue YARD::Handlers::NamespaceMissingError | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,71 @@ | |||
# -*- coding: utf-8 -*- | |||
require 'active_support/core_ext/class/attribute' | |||
require 'active_support/core_ext/string/inflections' | |||
|
|||
module RubyInterface | |||
def interface(method_name, &interface_body) | |||
mod_inst = self.const_set("#{method_name.to_s.camelize}InstanceMethods", Module.new) | |||
mod_inst.module_eval <<-EOT, __FILE__, __LINE__ + 1 | |||
def #{method_name} | |||
@#{method_name}_interface ||= self.class.#{method_name}_interface.new(self) | |||
end | |||
EOT | |||
|
|||
|
|||
mod_class = self.const_set("#{method_name.to_s.camelize}ClassMethods", Module.new) | |||
mod_class.module_eval <<-EOT, __FILE__, __LINE__ + 1 | |||
def #{method_name}(&blk) | |||
self.#{method_name}_interface.class_eval(&blk) if blk | |||
self.#{method_name}_interface | |||
end | |||
def inherited(subclass) | |||
new_class = subclass.const_set("#{method_name.to_s.camelize}InterfaceClass", Class.new(self.#{method_name}_interface)) | |||
new_class.interface_base = subclass | |||
subclass.#{method_name}_interface = new_class | |||
super | |||
end | |||
EOT | |||
|
|||
interface_module = self | |||
|
|||
add_interface do |base| | |||
base.send(:class_attribute, "#{method_name}_interface") | |||
interface_class = base.const_set("#{method_name.to_s.camelize}InterfaceClass", Class.new(RubyInterface::InterfaceClass)) | |||
interface_class.interface_base = base | |||
interface_class.class_eval(&interface_body) if interface_body | |||
base.send("#{method_name}_interface=", interface_class) | |||
base.extend mod_class | |||
base.send :include, mod_inst | |||
base.class_eval(&interface_class.interfaced) if interface_class.interfaced | |||
end | |||
|
|||
interface_module.define_singleton_method(:included) do |base| | |||
@_deps.each {|d| d.call(base)} | |||
end | |||
end | |||
|
|||
private | |||
def add_interface &block | |||
@_deps ||= [] | |||
@_deps << block | |||
end | |||
|
|||
class InterfaceClass | |||
class_attribute :interface_base | |||
attr_accessor :owner | |||
def initialize(owner) | |||
@owner = owner | |||
end | |||
|
|||
class << self | |||
def interfaced(&block) | |||
if block_given? | |||
@_interfaced_block = block | |||
else | |||
@_interfaced_block | |||
end | |||
end | |||
end | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,90 @@ | |||
# Generated by jeweler | |||
# DO NOT EDIT THIS FILE DIRECTLY | |||
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' | |||
# -*- encoding: utf-8 -*- | |||
|
|||
Gem::Specification.new do |s| | |||
s.name = %q{ruby-interface} | |||
s.version = "0.0.1" | |||
|
|||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= | |||
s.authors = ["Andrew Rudenko"] | |||
s.date = %q{2011-03-23} | |||
s.description = %q{Более комплексные руби-интерфейсы, чем просто миксины} | |||
s.email = %q{ceo@prepor.ru} | |||
s.extra_rdoc_files = [ | |||
"LICENSE.txt", | |||
"README.rdoc" | |||
] | |||
s.files = [ | |||
".document", | |||
".rspec", | |||
"Gemfile", | |||
"LICENSE.txt", | |||
"README.rdoc", | |||
"Rakefile", | |||
"VERSION", | |||
"lib/ruby-interface.rb", | |||
"spec/ruby-interface_spec.rb", | |||
"spec/spec_helper.rb" | |||
] | |||
s.homepage = %q{http://git.undev.cc/small-things/ruby-interface} | |||
s.licenses = ["MIT"] | |||
s.require_paths = ["lib"] | |||
s.rubygems_version = %q{1.3.7} | |||
s.summary = %q{Более комплексные руби-интерфейсы, чем просто миксины} | |||
s.test_files = [ | |||
"spec/ruby-interface_spec.rb", | |||
"spec/spec_helper.rb" | |||
] | |||
|
|||
if s.respond_to? :specification_version then | |||
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION | |||
s.specification_version = 3 | |||
|
|||
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then | |||
s.add_runtime_dependency(%q<ruby-interface>, [">= 0"]) | |||
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"]) | |||
s.add_development_dependency(%q<yard>, ["~> 0.6.0"]) | |||
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"]) | |||
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"]) | |||
s.add_development_dependency(%q<rcov>, [">= 0"]) | |||
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"]) | |||
s.add_development_dependency(%q<yard>, ["~> 0.6.0"]) | |||
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"]) | |||
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"]) | |||
s.add_development_dependency(%q<rcov>, [">= 0"]) | |||
s.add_runtime_dependency(%q<i18n>, ["> 0.1"]) | |||
s.add_runtime_dependency(%q<activesupport>, ["> 0.1"]) | |||
else | |||
s.add_dependency(%q<ruby-interface>, [">= 0"]) | |||
s.add_dependency(%q<rspec>, ["~> 2.3.0"]) | |||
s.add_dependency(%q<yard>, ["~> 0.6.0"]) | |||
s.add_dependency(%q<bundler>, ["~> 1.0.0"]) | |||
s.add_dependency(%q<jeweler>, ["~> 1.5.2"]) | |||
s.add_dependency(%q<rcov>, [">= 0"]) | |||
s.add_dependency(%q<rspec>, ["~> 2.3.0"]) | |||
s.add_dependency(%q<yard>, ["~> 0.6.0"]) | |||
s.add_dependency(%q<bundler>, ["~> 1.0.0"]) | |||
s.add_dependency(%q<jeweler>, ["~> 1.5.2"]) | |||
s.add_dependency(%q<rcov>, [">= 0"]) | |||
s.add_dependency(%q<i18n>, ["> 0.1"]) | |||
s.add_dependency(%q<activesupport>, ["> 0.1"]) | |||
end | |||
else | |||
s.add_dependency(%q<ruby-interface>, [">= 0"]) | |||
s.add_dependency(%q<rspec>, ["~> 2.3.0"]) | |||
s.add_dependency(%q<yard>, ["~> 0.6.0"]) | |||
s.add_dependency(%q<bundler>, ["~> 1.0.0"]) | |||
s.add_dependency(%q<jeweler>, ["~> 1.5.2"]) | |||
s.add_dependency(%q<rcov>, [">= 0"]) | |||
s.add_dependency(%q<rspec>, ["~> 2.3.0"]) | |||
s.add_dependency(%q<yard>, ["~> 0.6.0"]) | |||
s.add_dependency(%q<bundler>, ["~> 1.0.0"]) | |||
s.add_dependency(%q<jeweler>, ["~> 1.5.2"]) | |||
s.add_dependency(%q<rcov>, [">= 0"]) | |||
s.add_dependency(%q<i18n>, ["> 0.1"]) | |||
s.add_dependency(%q<activesupport>, ["> 0.1"]) | |||
end | |||
end | |||
|
Oops, something went wrong.