I need a class to call a class containing it as a property for a number of scenarios - how should this be implemented? - ruby

I'm fairly new to Ruby, coming from Objective-C and Swift.
I've hit upon a problem where I have an object containing another object as a property. The second object has a beginStream function, by which it streams data from a server, and when it gets new data then it yields, thus the first object can respond. It looks like this:
class StreamManager
def initialize
#streams = Array.new
end
def setup_user_stream(user_id)
stream = Stream.new(user_id)
#streams << stream
stream.begin_stream do |message|
puts "A message was received: #{message}"
end
end
end
class Stream
def initialize(user_id)
#user_id = user_id
end
def begin_stream
Thread.new do
# Begins stream
#client = Stream::Client.new(user_id)
#client.on_error do
# need to let StreamManager know about this
end
#client.on_message do |message|
yield message if block_given?
end
end
end
end
Now I have this on_error call that I'm getting from my stream client, and I need to let my StreamManager know about it. How would I go about doing this?
In Objective-C/Swift, I'd have a protocol called StreamDelegate, which the stream would have as a weak property, and then the StreamManager would set the Stream's delegate to be itself, and respond to the functions provided in the protocol. So then the Stream would call the delegate function #delegate?.streamDidReceiveError, and the stream manager would be set as the delegate, and have that function implemented, and it'd be called.
I've simplified this example - the Stream is an abstraction over the Stream::Client, which is from another library and also gives out a bunch of other messages. But now I'm writing this, I'm thinking perhaps their way of having those different blocks I'm yielding is the way to go. In which case, I'd need to understand how to implement that myself? Or perhaps that would be a poor way to design my class - I don't know?

There's a few different approaches here:
1) You could still use the delegate pattern you're used to in Cocoa. The only difference is you wouldn't have a formal protocol/interface for it. Your Stream would take a delegate/callback handler object which is any object that implements certain methods. You can make those methods optional by checking the object responds to them before calling them. Your StreamManager could implement this interface and pass itself in to the stream as a dependency.
2) You could define callbacks on your Stream class for errors and messages rather than passing a single block to the begin_stream method.
3) Keep your existing API by instead of yielding the message, encapsulate the message or error in a Result object and yield that instead. I think this might be my preferred option.
Sorry for the lack of code examples but I'm writing this on my iPhone.

The parent StreamManager can pass itself as a variable to the child Stream.
def initialize(stream_manager, user_id)
#stream_manager = stream_manager
#user_id = user_id
end
And the initializing it, in setup_user_stream:
stream = Stream.new(self, user_id)
If you want slightly more verbose code, you can use named keywords:
def initialize(:stream_manager, :user_id)
then:
Stream.new(stream_manager: self, user_id: user_id)

Related

Decorating an object's interface with additional behavior in Ruby?

I have a Ruby object that dispatches events to a third-party service:
class Dispatcher
def track_event(e)
ThirdPartyService.track e.id, e.name, my_api_key
end
end
The use of ThirdPartyService may raise errors (e.g. if no network connection is available). It's usually appropriate for consumers of Dispatcher to decide how to deal with these, rather than Dispatcher itself.
How could we decorate the use of Dispatcher in such a way that objects appear to be using a Dispatcher, but all exceptions are caught and logged instead? That is, I want to be able to write:
obj.track_event(...)
but have exceptions be caught.
Thoughtbot has a great blog post on different ways to implement decorators in Ruby. This one ("Module + Extend + Super decorator") is especially succinct:
module ErrorLoggingMixin
def track_event(event)
super
rescue ex
Logger.warn(ex)
end
end
obj = Dispatcher.new # initialize a Dispatcher as usual
obj.extend(ErrorLoggingMixin) # extend it with the new behavior
obj.track_event(some_event) # call its methods as usual
The blog post lists these pros and cons:
The benefits of this implementation are:
it delegates through all decorators
it has all of the original interface because it is the original object
The drawbacks of this implementation are:
can not use the same decorator more than once on the same object
*difficult to tell which decorator added the functionality
I recommend reading the rest of the post to see the other implementations and make the choice that best fits your needs.
The closest idea I've got is to surround the usage of track_event in a method which provides a block that catches exceptions, like this:
module DispatcherHelper
def self.dispatch(&block)
dispatcher = Dispatcher.new
begin
yield dispatcher
rescue NetworkError
# ...
rescue ThirdPartyError
# ...
end
end
end
so that we can then do:
DispatcherHelper.dispatch { |d| d.track_event(...) }
The other alternative I can see is to mimic the signature of track_event, so that you get:
module DispatcherHelper
def self.track_event(e)
begin
Dispatcher.new.track_event(e)
rescue NetworkError
# ...
rescue ThirdPartyError
# ...
end
end
end
but I'm less fond of that since it ties the signatures together.
Use a Bang Method
There's more than one way to do this. One way to accomplish your goal would be a small refactoring to redefine the exception-raising method as a "cautionary" method, make it private, and use the "safer" bang-free method in the public interface. For example:
class Dispatcher
def track_event(e)
result = track_event! e rescue nil
result ? result : 'Exception handled here.'
end
private
def track_event! e
ThirdPartyService.track e.id, e.name, my_api_key
end
end
Using the refactored code would yield the following when an exception is raised by ThirdPartyService:
Dispatcher.new.track_event 1
#=> "Exception handled here."
There are certainly other ways to address this type of problem. A lot depends on what your code is trying to express. As a result, your mileage may vary.

How to return the receiver instance's self from should_receive block

I'd like to have instance methods of a class return self, and be init with another class instance self.
However I'm struggling to see how to spec this succintly:
::Api.should_receive(:new).once do |arg|
arg.should be_an_instance_of(::Cli)
end
When running this spec, this ensures that the next method is called on true instead of the Api instance, as expected, that is the return value of the block. Example:
class Cli
def eg
api = Api.new(self)
api.blowup # undefined method for true
end
end
I'd really like the block to return the Api instance self without invoking another call to Api.new(...) in the spec, the example below does this and to my mind a non-rspec reader would wonder why the spec passes when clearly Api.new(...) has been called more than once.
Can anyone suggest how best to do this?
Current solution:
This reads like ::Api.new(...) is called thrice: once to create api, once to create cli, once to create start. Yet the spec of one call passes. I understand why and that this is correct, so not a bug. However I'd like a spec that a reader not familiar with rspec could scan and not have the impression that Api.new has been called more than once. Also note that ...once.and_return(api){...} does not work, the block needs to return api in order to pass.
let(:cli){ ::Cli.start(['install']) }
let(:start){ ::Cli.start(['install']) }
it 'is the API' do
api = ::Api.new(cli)
::Api.should_receive(:new).once do |arg|
arg.should be_an_instance_of(::Cli)
api
end
start
end
You can save the original method (new) in a local variable and then use it to return the new api from within the block:
original_method = ::Api.method(:new)
::Api.should_receive(:new).once do |arg|
arg.should be_an_instance_of(::Cli)
original_method.call(arg)
end
This will run the expectation, checking that the argument is an instance of ::Cli, and then return the value from the original method (i.e. the api).

Marshaling and undefined attributes/classes

I am using ruby marshaling to send data between two clients. Each client has a set of class definitions which they will use to help load the marshaled data. The definitions are stored in an external ruby file which they can load anytime they want (but usually when they start up)
A simple use case would be
Client A marshal dumps the data and sends it to client B
Client B marshal loads the data and then writes it out to a file
However, sometimes one client is sending data that contains objects that isn't defined in the other client's definitions, in which case the other client should update its definitions accordingly.
It might be a new instance variable that should be added to the definition of class xyz or it might be a new class completely.
Marshal#Load currently just throws an exception when it runs into an undefined variable (eg: undefined class/method abc).
Is there a way for me to take this exception and update the definitions accordingly so that the client can happily read the data and write it out?
All classes will contain data that Marshal already knows how to encode/decode, such as strings, arrays, hashes, numbers, etc. There won't be any data that requires custom dump/load methods.
My solution would be to automatically create the class (and constant hierarchy, i.e. Foo::Bar::Baz) and make the class autorespond to attribute access attempts.
class AutoObject
def method_missing(*args,&b)
if args.size == 1
name = args[0]
if instance_variable_defined? "##{name}"
self.class.send :attr_accessor, name
send(*args)
else
super
end
elsif args.size == 2 && args[0].to_s[/=$/]
name = args[0].to_s[0...-1]
if instance_variable_defined? "##{name}"
self.class.send :attr_accessor, name
send(*args)
else
super
end
end
end
end
def Marshal.auto_load(data)
Marshal.load(data)
rescue ArgumentError => e
classname = e.message[%r(^undefined class/module (.+)$), 1]
raise e unless classname
classname.split("::").inject(Object) do |outer, inner|
if !outer.const_defined? inner
outer.const_set inner, Class.new(AutoObject)
else
outer.const_get inner
end
end
retry
end
This could easily be extended to log all classes created, and even to determine what instance variables they might have. Which could then aid you in updating the files, perhaps programatically.

ActiveSupport::Notifications access payload from inside block

I recently found out about ActiveSupport::Notifications. I think it's a great tool to monitor your applications.
I am currently using it outside of Rails to monitor my Sinatra app and it's working great.
However I use it a lot, to instrument custom queries and would like to somehow notify the result of a query:
my_input = "some_query"
ActiveSupport::Notifications.instrument("myapp.create", :input => my_input) do
#stuff
result = search_for my_input
#stuff
result
end
Now I can subscribe to this and can also get the query that was executed (which is available in the payload hash). But I would also like to see the result in my subscriber.
So is there a way to add any custom value to the payload while I am executing the block?
Just stumbled upon your question when I was looking for the same thing.
You can simply pass in a block variable, it will represent your payload to you, and you can fill it up while you're in the block
my_input = "some_query"
ActiveSupport::Notifications.instrument("myapp.create", :input => my_input) do |instrument|
#stuff
result = search_for my_input
instrument[:my_result] = result
#stuff
result
end
In your subscriber create a new event from the passed argument:
ActiveSupport::Notifications.subscribe("myapp.create") do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
Rails.logger.info event.payload
end

Alternative initialize for a Class to avoid processing already known information

I have a class, Autodrop, that contains several methods , a.o. 'metadata', that call an external API (dropbox). They are slow.
However, I already often have that metadata around when initializing the AutodropImage, so I should make the methods smarter.
What I have in mind is this:
class Autodrop
include Dropbox
attr_reader :path
def initialize(path)
#path = path
end
def self.from_entry(drop_entry)
#drop_entry = drop_entry
self.initialize(#drop_entry.path)
end
def metadata
if #drop_entry = nil
return heavy_lifting_and_network_traffic
else
return #drop_entry.metadata
end
end
#...
end
Now, I would expect to call
entry = BarEntry.new()
foo = Autodrop.from_entry(entry)
foo.metadata
In order to avoid that heavy lifting and network traffic call.
But this does not work. And somehow, in all my newbieness, I am sure I am goind at this all wrong.
Is there a term I should look for and read about first? How would you go for this?
Note, that the examples are simplified: in my code, I inherit AutodropImage < Autodrop for example, which is called from withing AutodropGallery < Autodrop. The latter already knows all metadata for the AutodropImage, so I mostly want to avoid AutodropImage going over the heavy lifting again.
You are creating an instance variable #drop_entry in your class method from_entry and obviously it wont be available to your object that you are creating in this method. One workaround is to pass it as a parameter when you are initializing the class. It should work if you do the following modifications:
In your from_entry class method change
self.initialize(#drop_entry)
to
new(#drop_entry)
Modify initialize method to:
def initialize(drop_entry)
#drop_entry = drop_entry
#path = #drop_entry.path
end
Or if your class is tied up to pass only the path parameter, ie. you dont want to change the other existing code then you can use an optional parameter drop entry like so
def initialize(path, drop_entry=nil)
You would need to cache the metadata in a class variable.
Edit: Or in a class level instance variable.
Maybe this read will help: http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/

Resources