Skip to content

Instantly share code, notes, and snippets.

@clicube
Created November 3, 2013 09:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save clicube/7288457 to your computer and use it in GitHub Desktop.
Save clicube/7288457 to your computer and use it in GitHub Desktop.
C#のeventみたいなことやりたかった
require_relative 'eventlistenable'
class Neko
extend EventListenable
event :touched
def touch
touched.notify("nyan")
end
end
neko = Neko.new
neko.touched << proc do |msg|
puts "neko says \"#{msg}\""
end
neko.touch # => neko says "nyan".
neko.touch # => neko says "nyan".
neko.touched.notify "woops" rescue puts "call protected." # => call protected.
module EventListenable
module ProtectedMethods
protected
def notify(*args)
return nil unless @listeners
@listeners.each do |listener|
listener.call(*args)
end
return nil
end
end
class Event
include ProtectedMethods
def add(listener)
raise ArgumentError.new("listener must respond to \"call\".") unless listener.respond_to? :call
@listeners ||= []
@listeners << listener unless @listeners.include?(listener)
return listener
end
alias << add
def delete(listener)
return listener unless @listeners
@listeners.delete(listener)
return listener
end
end
def event(*names)
names.each do |name|
define_method name do
event = Event.new
# Event#notifyを呼び出せるように継承関係をつくる
if self.respond_to? :notify
# もとからnotifyメソッドを持っている場合
# もとのnotifyメソッドを保存して
# extend後にもう一度上書きする
m = method(:notify)
extend ProtectedMethods
(class << self; self; end).class_eval do
define_method :notify do |*args|
m.call(*args)
end
end
else
# もともとnotifyメソッドを持っていない場合
# extendでnotifyメソッドが追加されてしまうのでundefする
extend ProtectedMethods
if self.respond_to? :notify
(class << self; self; end).class_eval do
undef_method :notify
end
end
end
# #{name}メソッドを再定義して、次からは単にEventオブジェクトを返すようにする
(class << self; self; end).class_eval do
define_method name do event; end
end
event
end
end
end
end
if $0 == __FILE__
class Neko
extend EventListenable
event :touched
def touch
touched.notify("nyan")
end
end
neko = Neko.new
neko.touched << proc do |msg|
puts "neko says \"#{msg}\""
end
neko.touch # => neko says "nyan".
neko.touch # => neko says "nyan".
neko.touched.notify "woops" rescue puts "call protected." # => call protected.
neko2 = Neko.new
neko2.instance_eval{ neko.touched.notify "woops" } rescue puts "call protected." # => call protected.
end
require 'minitest/autorun'
require_relative 'eventlistenable'
class Foo
extend EventListenable
event :event1
event :event2, :event3
event :event4, :event4
def do_notify
event1.notify "event1"
event2.notify "event2"
event3.notify "event3"
event4.notify "event4"
end
end
class Bar < Foo
extend EventListenable
def notify
"original notify"
end
end
class TestEventListenable < MiniTest::Test
def setup
end
def teardown
end
def test_creating_event
foo1 = Foo.new
assert_instance_of EventListenable::Event, foo1.event1
assert_instance_of EventListenable::Event, foo1.event2
assert_instance_of EventListenable::Event, foo1.event3
assert_instance_of EventListenable::Event, foo1.event4
assert_raises(NoMethodError){ foo1.notify }
end
def test_registering_and_notifying_event
foo = Foo.new
result1 = nil
result1_2 = nil
result2 = nil
result3 = nil
result4 = nil
foo.event1 << proc{|msg| result1 = msg }
foo.event1 << proc{|msg| result1_2 = msg }
foo.event2 << proc{|msg| result2 = msg }
foo.event3 << proc{|msg| result3 = msg }
foo.event4 << proc{|msg| result4 = msg }
assert_raises(ArgumentError){ foo.event1 << Object.new }
assert_raises(ArgumentError){ foo.event2 << Object.new }
assert_raises(ArgumentError){ foo.event3 << Object.new }
assert_raises(ArgumentError){ foo.event4 << Object.new }
foo.do_notify
assert_equal "event1", result1
assert_equal "event1", result1_2
assert_equal "event2", result2
assert_equal "event3", result3
assert_equal "event4", result4
result1 = nil
result1_2 = nil
result2 = nil
result3 = nil
result4 = nil
foo.do_notify
assert_equal "event1", result1
assert_equal "event1", result1_2
assert_equal "event2", result2
assert_equal "event3", result3
assert_equal "event4", result4
end
def test_protected
foo1 = Foo.new
assert_raises(NoMethodError){ foo1.event1.notify("__event1__") }
assert_raises(NoMethodError){ foo1.event2.notify("__event2__") }
assert_raises(NoMethodError){ foo1.event3.notify("__event3__") }
assert_raises(NoMethodError){ foo1.event4.notify("__event4__") }
foo2 = Foo.new
assert_raises(NoMethodError){
foo2.instance_eval{ foo1.event1.notify("__event1__") }
}
assert_raises(NoMethodError){
foo2.instance_eval{ foo1.event2.notify("__event2__") }
}
assert_raises(NoMethodError){
foo2.instance_eval{ foo1.event3.notify("__event3__") }
}
assert_raises(NoMethodError){
foo2.instance_eval{ foo1.event4.notify("__event4__") }
}
end
def test_redef_notify
bar = Bar.new
assert_equal "original notify", bar.notify
bar.event1 << proc{}
assert_equal "original notify", bar.notify
bar.do_notify
assert_equal "original notify", bar.notify
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment