2009-03-03 4 views
16

저는 C# 배경에서 왔고 Ruby에서 프로그래밍을 시작했습니다. 문제는 클래스에서 이벤트를 발생시켜 상황이 발생할 때 다양한 관찰자가 트리거 될 수 있도록하는 방법을 알아야한다는 것입니다.루비에서 이벤트 어떻게?

문제는 루비에 관한 책들도 이벤트를 언급하지 않고 혼자만의 예제를 제공한다는 것입니다. 누구든지 나를 도울 수 있습니까?

답변

4

나는 약간의 C와 주로 루비로 GUI 라이브러리를 작성하려고 시도했다. 그것은 너무 천천히 끝내고 나는 결코 포기하지 않고 풀어 줬다. 하지만 C#보다 쉽게 ​​만들려고 이벤트 시스템을 작성했습니다. 필자는 사용하기 쉽도록 두 번 다시 작성했습니다. 다소 도움이되기를 바랍니다.

class EventHandlerArray < Array 
    def add_handler(code=nil, &block) 
    if(code) 
     push(code) 
    else 
     push(block) 
    end 
    end 
    def add 
    raise "error" 
    end 
    def remove_handler(code) 
    delete(code) 
    end 
    def fire(e) 
    reverse_each { |handler| handler.call(e) } 
    end 
end 

# with this, you can do: 
# event.add_handler 
# event.remove_handler 
# event.fire (usually never used) 
# fire_event 
# when_event 
# You just need to call the events method and call super to initialize the events: 
# class MyControl 
# events :mouse_down, :mouse_up, 
#   :mouse_enter, :mouse_leave 
# def initialize 
#  super 
# end 
# def when_mouse_up(e) 
#  # do something 
# end 
# end 
# control = MyControl.new 
# control.mouse_down.add_handler { 
# puts "Mouse down" 
# } 
# As you can see, you can redefine when_event in a class to handle the event. 
# The handlers are called first, and then the when_event method if a handler didn't 
# set e.handled to true. If you need when_event to be called before the handlers, 
# override fire_event and call when_event before event.fire. This is what painting 
# does, for handlers should paint after the control. 
# class SubControl < MyControl 
# def when_mouse_down(e) 
#  super 
#  # do something 
# end 
# end 
def events(*symbols) 
    # NOTE: Module#method_added 

    # create a module and 'include' it 
    modName = name+"Events" 
    initStr = Array.new 
    readerStr = Array.new 
    methodsStr = Array.new 
    symbols.each { |sym| 
    name = sym.to_s 
    initStr << %Q{ 
     @#{name} = EventHandlerArray.new 
    } 
    readerStr << ":#{name}" 
    methodsStr << %Q{ 
     def fire_#{name}(e) 
     @#{name}.fire(e) 
     when_#{name}(e) if(!e.handled?) 
     end 
     def when_#{name}(e) 
     end 
    } 
    } 
    eval %Q{ 
    module #{modName} 
     def initialize(*args) 
     begin 
      super(*args) 
     rescue NoMethodError; end 
     #{initStr.join} 
     end 
     #{"attr_reader "+readerStr.join(', ')} 
     #{methodsStr.join} 
    end 
    include #{modName} 
    } 
end 

class Event 
    attr_writer :handled 
    def initialize(sender) 
    @sender = @sender 
    @handled = false 
    end 
    def handled?; @handled; end 
end 
-1

는 말인지 정확히 모르겠어요하지만 당신은 아마 당신의 클래스에서 예외를 사용하여 특정 "이벤트"에 그들을 올릴 수 있습니다. GUI 개발을위한 이벤트가 필요한 경우 대부분의 GUI 프레임 워크는 자체 이벤트 처리 스타일을 정의합니다.

희망이 다소 답을하면 질문입니다.

+0

에 도움이 될 수 있습니다. – Ash

21

질문에 대해서는 이미 대답했지만, 한 번 훑어보고 싶으면 observer이 표준 라이브러리에 내장되어 있습니다. 나는 작은 게임 프로젝트를 위해 그것을 과거에 사용했고, 그것은 아주 잘 작동한다.

3

매우 간단한 루비 리스너입니다. 이것은 .NET 이벤트를 대체하는 것은 아니지만 매우 간단한 리스너의 아주 간단한 예입니다.

module Listenable 

    def listeners() @listeners ||= [] end 

    def add_listener(listener) 
     listeners << listener 
    end 

    def remove_listener(listener) 
     listeners.delete listener 
    end 

    def notify_listeners(event_name, *args) 
     listeners.each do |listener| 
      if listener.respond_to? event_name 
       listener.__send__ event_name, *args 
      end 
     end 
    end 

end 

사용하려면

class CowListenable 
    include Listenable 

    def speak 
     notify_listeners :spoken, 'moooo!' 
    end 

end 

class CowListener 

    def initialize(cow_listenable) 
     cow_listenable.add_listener self 
    end 

    def spoken(message) 
     puts "The cow said '#{message}'" 
    end 

end 

cow_listenable = CowListenable.new 
CowListener.new(cow_listenable) 
cow_listenable.speak 

출력 :

The cow said 'moooo!' 
+1

이 답변은 가장 루비 같은 느낌입니다. 강력한 형식의 언어를 모방하는 것이 아니라 언어의 동적 인 특성에 의존합니다. 사용하기 쉽고 이해하기 쉽고 멀티 캐스트를 지원합니다. –

1

공개 : 나는 당신이 접근하는 방법에 따라 event_aggregator 보석

의 메인테이너 오전 잠재적으로 이벤트 수집을 사용할 수있는 문제 ator. 이렇게하면 특정 유형의 메시지를 게시 한 다음 개체에서 수신 할 형식을들을 수 있습니다. 개체 간의 연결이 매우 느슨해지기 때문에 정상적인 이벤트보다 더 나은 경우가 있습니다. 이벤트 생성자와 수신기는 다른쪽에 대한 참조를 공유 할 필요가 없습니다.

여기에 event_aggregator이라고하는 데 도움이되는 보석이 있습니다. 그것으로 당신은 다음과 같은 작업을 수행 할 수

#!/usr/bin/ruby 

require "rubygems" 
require "event_aggregator" 

class Foo 
    include EventAggregator::Listener 
    def initialize() 
     message_type_register("MessageType1", lambda{|data| puts data }) 

     message_type_register("MessageType2", method(:handle_message)) 
    end 

    def handle_message(data) 
     puts data 
    end 

    def foo_unregister(*args) 
     message_type_unregister(*args) 
    end 
end 

class Bar 
    def cause_event 
     EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3]).publish 
    end 
    def cause_another_event 
     EventAggregator::Message.new("MessageType2", ["Some More Stuff",2,3]).publish 
    end 
end 

f = Foo.new 

b = Bar.new 
b.cause_event 
b.cause_another_event 
# => Some Stuff 
    2 
    3 
# => Some More Stuff 
    2 
    3 

는 기본적으로 비동기임을 알고 있어야합니다, 그래서 당신은 그냥이 스크립트를 실행하면 이벤트가 전달되기 전에 스크립트가 종료 할 수도 있습니다. 비동기 동작을 사용하지 않으려면 :

EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3], false).publish 
#The third parameter indicates async 

는 희망이 사건 나는 관찰자 디자인 패턴을 사용하려면