Created
November 3, 2013 09:40
-
-
Save clicube/7288457 to your computer and use it in GitHub Desktop.
C#のeventみたいなことやりたかった
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
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. |
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
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 |
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
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