How should one set a ruby instance variable to an RxRuby Observable? - ruby

I planning treating incoming data from a tcp port as if it were data from a "view". I would like to set up a number of RxRuby Observables, then depending on the data I get from the tcp port, select the appropriate Observable and publish something to it by calling the on_next method.
The following code works, but seems clumsy. The block passed to the Rx::Observable.create just sets an instance variable to the observable passed into it. It's not a huge amount of boiler plate, but something just does not seem right. Am I missing something?
require 'rx'
class GUIMessagePublisher
attr_accessor :handshake, :handshake_stream, :remote_button, :remote_button_stream
def initialize
self.handshake_stream = Rx::Observable.create { |o| self.handshake = o }
self.remote_button_stream = Rx::Observable.create { |o| self.remote_button = o }
end
def publish_handshake
handshake.on_next("hello")
end
def publish_remote_button
remote_button.on_next(nil)
end
end
publisher = GUIMessagePublisher.new
publisher.handshake_stream.subscribe { |m| puts "message = #{m}"}
publisher.remote_button_stream.subscribe { puts "remote button clicked" }
publisher.publish_handshake
publisher.publish_remote_button

After reading more about Rx::Subject, I think this would be the preferred way to handle this
require 'rx'
require 'forwardable'
class GUIMessagePublisher
extend Forwardable
attr_accessor :handshake_subject, :remote_button_subject
def_delegator :handshake_subject, :as_observable, :handshake_stream
def_delegator :remote_button_subject, :as_observable, :remote_button_stream
def initialize
self.handshake_subject = Rx::Subject.new
self.remote_button_subject = Rx::Subject.new
end
def publish_handshake
handshake_subject.on_next("hello")
end
def publish_remote_button
remote_button_subject.on_next("remote button")
end
end
publisher = GUIMessagePublisher.new
publisher.handshake_stream.subscribe { |m| puts "message = #{m}"}
publisher.remote_button_stream.subscribe { |m| puts "remote button clicked, message = #{m}" }
publisher.publish_handshake
publisher.publish_remote_button
The use of Forwardable is optional. I could have delegated via methods or even just called .as_observable on the exposed subject, but this seems right.

Related

What is the order of using blocks in Ruby

I am creating a gem to support some Mailing from the command line. I use some Gem.
I am using the Mail Gem. As you can see in the description of mail gem is something like this.
mail = Mail.new do
from 'mikel#test.lindsaar.net'
to 'you#test.lindsaar.net'
subject 'This is a test email'
body File.read('body.txt')
end
In the block I call the methods from the Mail class (from, to, subject, body). This makes sense so I build it in my own mailer class
def initialize(mail_settings, working_hours)
#mail_settings = mail_settings
#working_hours = working_hours
#mailer = Mail.new do
to mail_settings[:to]
from mail_settings[:from]
subject mail_settings[:subject]
body "Start #{working_hours[:start]} \n\
Ende #{working_hours[:end]}\n\
Pause #{working_hours[:pause]}"
end
end
This looks straight forward. Just call the block und fill in my values I get through the constructor. Now comes my question.
I tried to put out the body construction for the mail into a separated method. But I cannot use it in the Mail constructor of the gem.
module BossMailer
class Mailer
def initialize(mail_settings, working_hours)
#mail_settings = mail_settings
#working_hours = working_hours
#mailer = Mail.new do
to mail_settings[:to]
from mail_settings[:from]
subject mail_settings[:subject]
body mail_body
end
end
def mail
#mailer.delivery_method :smtp, address: "localhost", port: 1025
#mailer.deliver
end
def mail_body
"Start #{working_hours[:start]} \n\
Ende #{working_hours[:end]}\n\
Pause #{working_hours[:pause]}"
end
end
end
This error came out this code.
That means I cannot use my class method or class variable (beginning with #a) in this block.
Questions
What is the order of the execution in a Block? If I set my variable #mail_settings, I can't use it in the block. Is Ruby searching for #mail_settings in Mail class where I give the block to? Why can I use the given parameter from the BossMailer::Mailer constructor through the block and no error appears?
And why does this works if I am using and variable to parse the content into the block? (body_content = mail_body) works!
def initialize(mail_settings, working_hours)
#mail_settings = mail_settings
#working_hours = working_hours
body_content = mail_body
#mailer = Mail.new do
to mail_settings[:to]
from mail_settings[:from]
subject mail_settings[:subject]
body body_content
end
end
It's all about the context.
mail = Mail.new do
from 'mikel#test.lindsaar.net'
to 'you#test.lindsaar.net'
subject 'This is a test email'
body File.read('body.txt')
end
from, to methods (and the rest) are methods on Mail::Message instance. For you to be able to call them in this nice DSL-manner, the block you pass to constructor is instance_eval'ed.
What this means is that inside of this block, self is no longer your mailer, but a mail message instead. As a result, your mailer method is not accessible.
Instead of instance_eval, they could have just yield or block.call, but this wouldn't make the DSL possible.
As to why the local variable works: it's because ruby blocks are lexically-scoped closures (meaning, they retain local context of their declaration. If there was a local variable visible from where the block is defined, it'll remember the variable and its value when the block is called)
Alternative approach
Don't use the block form. Use this: https://github.com/mikel/mail/blob/0f9393bb3ef1344aa76d6dac28db3a4934c65087/lib/mail/message.rb#L92-L96
mail = Mail.new
mail['from'] = 'mikel#test.lindsaar.net'
mail[:to] = 'you#test.lindsaar.net'
mail.subject 'This is a test email'
mail.body = 'This is a body'
Code
Try commenting/uncommenting some lines.
class Mail
def initialize(&block)
# block.call(self) # breaks DSL
instance_eval(&block) # disconnects methods of mailer
end
def to(email)
puts "sending to #{email}"
end
end
class Mailer
def admin_mail
# get_recipient = 'vasya#example.com'
Mail.new do
to get_recipient
end
end
def get_recipient
'sergio#example.com'
end
end
Mailer.new.admin_mail
The problem is that mail_body is evaluated in the context of Mail::Message and not in the context of your BossMailer::Mailer class. Consider the following examples:
class A
def initialize
yield
end
end
class B
def initialize(&block)
instance_eval { block.call }
end
end
class C
def initialize(&block)
instance_eval(&block)
end
end
class Caller
def test
A.new { hi 'a' }
B.new { hi 'b' }
C.new { hi 'c' }
end
def hi(x)
puts "hi there, #{x}"
end
end
Caller.new.test
This will get you
hi there, a
hi there, b
`block in test': undefined method `hi' for #<C:0x286e1c8> (NoMethodError)
Looking at the gem's code, this is exactly what happens:
Mail.new just passes the block given to Mail::Message's constructor.
The said constructor works exactly as the C case above.
instance_eval basically changes what self is in the current context.
About why B and C cases work differently - you can think that & will 'change' the block object from proc to block (yes, my choice of variable name wasn't great there). More on the difference here.

Ruby custom iterators

I have a class game which contains some arrays of custom objects (dinosaurs, cacemen etc.), that are returned by different accessors, such as game.dinosaurs, game.cavemen etc.
At present, all these accessors just return the internally stored arrays. But now I'd like to add some custom iteration methods to these arrays returned by those accessors, to be able to write code such as game.dinosaurs.each_carnivore { ... } etc. similarly to each_element and each_attr iterators in LibXML::XML::Node. But the objects returned from my accessors game.dinosaurs and game.cavemen have to behave like arrays still.
How are things like that usually done in Ruby?
Should I make the objects returned from my accessors to be some custom classes derived from Ruby's Array class? Or maybe should I just create a custom class with Enumerable mixed in?
I know I can use map or select externally on my collections, but I wanted to encapsulate these iterations internally that my class's users won't need to bother how to set up an iteration to select only carnivore dinosaurs from the internal array.
Edit: I'm not asking about how to use iterators or how to implement them, but how to add just some custom iterators to object which previously were just plain arrays (and still need to be).
It depends (as always). You could use an array subclass and you you could build a custom class and use composition and delegation. Here's a simple example with an array subclass:
class DinosaurArray < Array
def carnivores
select { |dinosaur| dinosaur.type == :carnivore }
end
def herbivores
select { |dinosaur| dinosaur.type == :herbivore }
end
def each_carnivore(&block)
carnivores.each(&block)
end
def each_herbivore(&block)
herbivores.each(&block)
end
end
And here's a simple one with composition and delegation:
class DinosaurArray
def initialize
#array = []
end
def <<(dinosaur)
#array << dinosaur
end
def carnivores
#array.select { |dinosaur| dinosaur.type == :carnivore }
end
def herbivores
#array.select { |dinosaur| dinosaur.type == :herbivore }
end
def each(&block)
#array.each(&block)
end
def each_carnivore(&block)
carnivores.each(&block)
end
def each_herbivore(&block)
herbivores.each(&block)
end
end
Both implementation can be used like this:
require 'ostruct'
dinosaurs = DinosaurArray.new
dinosaurs << OpenStruct.new(type: :carnivore, name: "Tyrannosaurus")
dinosaurs << OpenStruct.new(type: :carnivore, name: "Allosaurus")
dinosaurs << OpenStruct.new(type: :herbivore, name: "Apatosaurus")
puts "Dinosaurs:"
dinosaurs.each.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
puts
But also has custom iterators:
puts "Carnivores:"
dinosaurs.each_carnivore.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
puts
puts "Herbivores:"
dinosaurs.each_herbivore.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
Output:
Dinosaurs:
1. Tyrannosaurus
2. Allosaurus
3. Apatosaurus
Carnivores:
1. Tyrannosaurus
2. Allosaurus
Herbivores:
1. Apatosaurus
You can do this via using ruby blocks. Read more
Simple example here:
class Game
def initialize
#carnivoures = [1,2,3]
end
def each_carnivoures
#carnivoures.each do |carni|
yield carni
end
end
end
Game.new.each_carnivoures{ |c| p c}
It also would be nice to have a possibility for chaining such filters. You can achieve this simply by wrapping select method into custom one, returning your new class instead of array. You may wrap some other methods as well, e.g. map:
class Units < Array
def select
self.class.new(super)
end
def dinosaurs
select{ |unit| unit.kind == 'dinosaur' }
end
def cavemen
select{ |unit| unit.kind == 'caveman' }
end
def carnivore
select{ |unit| unit.type == 'carnivore' }
end
def herbivore
select{ |unit| unit.type == 'herbivore' }
end
end
Units.dinosaurs.carnivore
Units.cavemen.herbivore

Ruby: having callbacks on 'attr' objects

Essentially I'm wondering how to place callbacks on objects in ruby, so that when an object is changed in anyway I can automatically trigger other changes:
(EDIT: I confused myself in my own example! Not a good sign… As #proxy is a URI object it has it's own methods, changing the URI object by using it's own methods doesn't call my own proxy= method and update the #http object)
class MyClass
attr_reader :proxy
def proxy=(string_proxy = "")
begin
#proxy = URI.parse("http://"+((string_proxy.empty?) ? ENV['HTTP_PROXY'] : string_proxy))
#http = Net::HTTP::Proxy.new(#proxy.host,#proxy.port)
rescue
#http = Net::HTTP
end
end
end
m = MyClass.new
m.proxy = "myproxy.com:8080"
p m.proxy
# => <URI: #host="myproxy.com" #port=8080>
m.proxy.host = 'otherproxy.com'
p m.proxy
# => <URI: #host="otherproxy.com" #port=8080>
# But accessing a website with #http.get('http://google.com') will still travel through myproxy.com as the #http object hasn't been changed when m.proxy.host was.
Your line m.proxy = nil will raise a NoMethodError exception, since nil does no respond to empty?. Thus #http is set to Net::HTTP, as in the rescue clause.
This has nothing to do with callbacks/setters. You should modify your code to do what you want (e.g. calling string_proxy.blank? if using activesupport).
I managed to figure this one out for myself!
# Unobtrusive modifications to the Class class.
class Class
# Pass a block to attr_reader and the block will be evaluated in the context of the class instance before
# the instance variable is returned.
def attr_reader(*params,&block)
if block_given?
params.each do |sym|
# Create the reader method
define_method(sym) do
# Force the block to execute before we…
self.instance_eval(&block)
# … return the instance variable
self.instance_variable_get("##{sym}")
end
end
else # Keep the original function of attr_reader
params.each do |sym|
attr sym
end
end
end
end
If you add that code somewhere it'll extend the attr_reader method so that if you now do the following:
attr_reader :special_attr { p "This happens before I give you #special_attr" }
It'll trigger the block before it gives you the #special_attr. It's executed in the instance scope so you can use it, for example, in classes where attributes are downloaded from the internet. If you define a method like get_details which does all retrieval and sets #details_retrieved to true then you can define the attr like this:
attr_reader :name, :address, :blah { get_details if #details_retrieved.nil? }

How to do Events in Ruby?

I come from a C# background, and have just started programming in Ruby. The thing is, that I need to know how I can raise events in my classes so that various observers can be triggered when things need to happen.
The problem is the books I have on Ruby don't even mention events, let alone provide examples. Is anyone able to help me?
The question has already been answered, but there's an observer built right into the standard library if you want to give that a look. I've used it in the past for a small game project, and it works very well.
I tried writing a GUI library in Ruby with a little C and primarily Ruby. It ended up being so slow I gave up and never released it. But I wrote an event system for it that I tried to make easier than C#'s. I rewrote it a couple times to make it easier to use. I hope it is somewhat helpful.
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
Extremely simple Ruby listener. This is not exactly a replacement for .NET events, but this one is an extremely simple example of a very simple listener.
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
To use:
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
Output:
The cow said 'moooo!'
Disclosure: I am the maintainer of the event_aggregator gem
Depending on how you want to approach the problem you could potentially use an event aggregator. This way you can publish messages of a certain type and then have your objects listen to the types you want them to receive. This can in certain cases be better than normal events because you get a very loose coupling between your objects. The event producer and listener does not need to share a reference to the other.
There is a gem that helps you with this called event_aggregator. With it you can do the following:
#!/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
Be aware that it is async by default, so if you execute just this script the script might exit before the events are passed. To disable async behaviour use:
EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3], false).publish
#The third parameter indicates async
Hopefully this can be helpful in your case
I'm not sure of exactly what you mean but you could probably use exceptions in your classes and raise them on certain "events". If you need event for GUI development then most GUI frameworks define their own event handling style.
Hope this somewhat answers you're question.

What is the equivalent of .NET events in Ruby?

The problem is very simple. An object needs to notify some events that might be of interest to observers.
When I sat to validate a design that I cooked up now in Ruby just to validate it.. I find myself stumped as to how to implement the object events. In .Net this would be a one-liner.. .Net also does handler method signature verification,etc. e.g.
// Object with events
public delegate void HandlerSignature(int a);
public event HandlerSignature MyEvent;
public event HandlerSignature AnotherCriticalEvent;
// Client
MyObject.MyEvent += new HandlerSignature(MyHandlerMethod); // MyHandlerMethod has same signature as delegate
Is there an EventDispatcher module or something that I am missing that I can strap on to a Ruby class ? Hoping for an answer that plays along with Ruby's principle of least surprise.
An event would be the name of the event plus a queue of [observer, methodName] objects that need to be invoked when the event takes place.
Firstly, in Ruby there are no method signatures. The closest would be checking the function's arity. Duck typing requires thinking differently (slightly).
The Observable module is a start, but if you have a requirement to have multiple events from a single class it might not be enough.
This is a quick sketch. It supports methods and blocks. Modify as necessary to adapt for your code, threading approach, etc. For example, you could use method_missing to have the event name in the method name rather than having it as a parameter.
class EventBase
def initialize
#listeners = Hash.new
end
def listen_event(name, *func, &p)
if p
(#listeners[name] ||= Array.new) << p
else
(#listeners[name] ||= Array.new) << func[0]
end
end
def ignore_event(name, func)
return if !#listeners.has_key?(name)
#listeners[name].delete_if { |o| o == func }
end
def trigger_event(name, *args)
return if !#listeners.has_key?(name)
#listeners[name].each { |f| f.call(*args) }
end
end
class MyClass < EventBase
def raise_event1(*args)
trigger_event(:event1, *args)
end
def raise_event2(*args)
trigger_event(:event2, *args)
end
end
class TestListener
def initialize(source)
source.listen_event(:event1, method(:event1_arrival))
source.listen_event(:event2) do |*a|
puts "event 2 arrival, args #{a}"
end
end
def event1_arrival(*a)
puts "Event 1 arrived, args #{a}"
end
end
s = MyClass.new
l = TestListener.new(s)
s.raise_event1("here is event 1")
s.raise_event2("here is event 2")
Why not write your own event class? Something like
class Event
def initialize
#handlers = Array.new
end
def fire
#handlers.each do |v|
v.call
end
end
def << handler
#handlers << handler
end
end
e = Event.new
e << lambda { puts "hello" }
e << lambda { puts "test" }
e.fire
This is just a minimal sample, but can be extended in any ways. Add parameters like sender and eventArgs in .Net, or whatever you like ;-)
The Observable module?
I'd echo that there isn't a language-level analog in Ruby to .NET events. The way that rails deals with it is with ActiveSupport::Callbacks (there is an example on that page).
Take a look into the various ruby state machine libraries. They intend to solve a large problem than just events, but may provide you with a solution.
I've used the state_machine gem with success, which does include events.
I wrote a gem just for this because I had exactly the same issue. Try this:
gem install ruby_events
Follow the instructions as on http://github.com/nathankleyn/ruby_events, but in a nutshell:
require 'rubygems'
require 'ruby_events'
class Example
def initialize
events.listen(:test_event) do |event_data|
puts 'Hai there!'
puts event_data
end
end
def call_me
events.fire(:test_event, 'My name is Mr Test Man!')
end
end
e = Example.new
e.call_me # Fires the event, and our handler gets called!
A quick note on this. I suggest you look at EventMachine
https://rubygems.org/gems/eventmachine
It is a different look a the same idea. It implements an event driven paradigm so you are one-level above the equivalent for .Net Events and consider the EventMachine module as the CLR event handler.
Also taking a step back, Ruby follows a Smalltalk processing model where any call to a method is a message (as is an Event) sent to the object (see the Send() method). EventMachine gives you is a single-threaded slice on the events. You can use something like Rack to handle threads or workers.
I'm a noob but Ruby seems really powerful. You can implement C# style events yourself like this:
module Observable
class Event
def initialize
#to_call = []
end
def fire(*arguments)
#to_call.each { |proc| proc.call(*arguments) }
end
def call(proc)
#to_call << proc
end
def dont_call(proc)
#to_call.delete proc
end
end
def self.append_features(cls)
def cls.event(sym)
define_method(sym.to_s) do
variable_name = "##{sym}"
if not instance_variable_defined? variable_name then
instance_variable_set variable_name, Event.new
end
instance_variable_get variable_name
end
end
end
end
# Example
class Actor
include Observable
event :whenActed
def act
whenActed.fire("Johnny") # fire event whenActed with parameter Johnny
end
end
actor = Actor.new
def apploud(whom)
print "Bravo #{whom}!\n"
end
applouder = method(:apploud)
actor.whenActed.call applouder
actor.act
I have created a gem doing exactly what you want and surprisingly called event_dispatcher as you mentioned. I hope it gonna help someone : event_dispatcher

Resources