Skip to content

Commit

Permalink
init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
prepor committed Mar 23, 2011
1 parent 4fe4e2e commit c54e9d3
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 29 deletions.
4 changes: 4 additions & 0 deletions Gemfile
Expand Up @@ -11,4 +11,8 @@ group :development do
gem "bundler", "~> 1.0.0" gem "bundler", "~> 1.0.0"
gem "jeweler", "~> 1.5.2" gem "jeweler", "~> 1.5.2"
gem "rcov", ">= 0" gem "rcov", ">= 0"
gem "bluecloth"
gem "undev"
end end

gemspec
55 changes: 55 additions & 0 deletions Gemfile.lock
@@ -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)
93 changes: 93 additions & 0 deletions README.md
@@ -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"

В каждом модуле может быть определено произвольное количество интерфейсов
19 changes: 0 additions & 19 deletions README.rdoc

This file was deleted.

12 changes: 7 additions & 5 deletions Rakefile
@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-

require 'rubygems' require 'rubygems'
require 'bundler' require 'bundler'
begin begin
Expand All @@ -13,16 +15,16 @@ require 'jeweler'
Jeweler::Tasks.new do |gem| Jeweler::Tasks.new do |gem|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
gem.name = "ruby-interface" gem.name = "ruby-interface"
gem.homepage = "http://github.com/prepor/ruby-interface" gem.homepage = "http://git.undev.cc/small-things/ruby-interface"
gem.license = "MIT" gem.license = "MIT"
gem.summary = %Q{TODO: one-line summary of your gem} gem.summary = %Q{Более комплексные руби-интерфейсы, чем просто миксины}
gem.description = %Q{TODO: longer description of your gem} gem.description = %Q{Более комплексные руби-интерфейсы, чем просто миксины}
gem.email = "ceo@prepor.ru" gem.email = "ceo@prepor.ru"
gem.authors = ["Andrew Rudenko"] gem.authors = ["Andrew Rudenko"]
# Include your dependencies below. Runtime dependencies are required when using your gem, # Include your dependencies below. Runtime dependencies are required when using your gem,
# and development dependencies are only needed for development (ie running rake tasks, tests, etc) # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
# gem.add_runtime_dependency 'jabber4r', '> 0.1' gem.add_runtime_dependency 'i18n', '> 0.1'
# gem.add_development_dependency 'rspec', '> 1.2.3' gem.add_runtime_dependency 'activesupport', '> 0.1'
end end
Jeweler::RubygemsDotOrgTasks.new Jeweler::RubygemsDotOrgTasks.new


Expand Down
Empty file removed lib/ruby-interface.rb
Empty file.
8 changes: 8 additions & 0 deletions lib/ruby-interface/yard.rb
@@ -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
71 changes: 71 additions & 0 deletions lib/ruby_interface.rb
@@ -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
90 changes: 90 additions & 0 deletions ruby-interface.gemspec
@@ -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

0 comments on commit c54e9d3

Please sign in to comment.