How to detect break during yield - ruby

For a gem I intend to publish, I want to create an enumerable interface wrapping an external library. (Called via FFI)
I have this code (stripped for clarity)
def each_shape(&block)
callback = lambda do |*args|
yield
end
cpSpaceEachShape(callback) # FFI function, calls callback
end
which is called with a block like this
space.each_shape do
# ...
end
cpSpaceStep() # Other FFI function
The cpSpaceEachShape is an external C library function, which in returns calls callback a number of times (synchronously, no way to cancel it)
This works great, until I use break in the top-level block, like this:
space.each_shape do
break
end
cpSpaceStep() # Error from C library because iteration is still running
I'm not sure how Ruby, FFI and the C library are interacting here. From the Ruby perspective it looks like the break "jumps" out of the top-level block, the callback block, from cbSpaceEachShape and out of each_shape.
Side question: What happens here from a low-level perspective, e.g. what happens to the stack?
Main question: Can I capture the break from the top-level block?
I was hoping for something like this: (not working, pseudo code)
def each_shape(&block)
#each_shape_cancelled = false
callback = lambda do |*args|
yield unless #each_shape_cancelled
rescue StopIteration
#each_shape_cancelled = true
end
cpSpaceEachShape(callback)
end
EDIT: This is for a gem I intend to publish. I want my users to be able to use regular Ruby. If they were required to use throw... How would they know...? If there is no good solution I will - begrudgingly - construct an array beforehand and collect/cache all callbacks before yielding its contents.

Related

A better way to call methods on an instance

My question has a couple layers to it so please bear with me? I built a module that adds workflows from the Workflow gem to an instance, when you call a method on that instance. It has to be able to receive the description as a Hash or some basic data structure and then turn that into something that puts the described workflow onto the class, at run-time. So everything has to happen at run-time. It's a bit complex to explain what all the crazy requirements are for but it's still a good question, I hope. Anyways, The best I can do to be brief for a context, here, is this:
Build a class and include this module I built.
Create an instance of Your class.
Call the inject_workflow(some_workflow_description) method on the instance. It all must be dynamic.
The tricky part for me is that when I use public_send() or eval() or exec(), I still have to send some nested method calls and it seems like they use 2 different scopes, the class' and Workflow's (the gem). When someone uses the Workflow gem, they hand write these method calls in their class so it scopes everything correctly. The gem gets to have access to the class it creates methods on. The way I'm trying to do it, the user doesn't hand write the methods on the class, they get added to the class via the method shown here. So I wasn't able to get it to work using blocks because I have to do nested block calls e.g.
workflow() do # first method call
# first nested method call. can't access my scope from here
state(:state_name) do
# second nested method call. can't access my scope
event(:event_name, transitions_to: :transition_to_state)
end
end
One of the things I'm trying to do is call the Workflow#state() method n number of times, while nesting the Workflow#event(with, custom_params) 0..n times. The problem for me seems to be that I can't get the right scope when I nest the methods like that.
It works just like I'd like it to (I think...) but I'm not too sure I hit the best implementation. In fact, I think I'll probably get some strong words for what I've done. I tried using public_send() and every other thing I could find to avoid using class_eval() to no avail.
Whenever I attempted to use one of the "better" methods, I couldn't quite get the scope right and sometimes, I was invoking methods on the wrong object, altogether. So I think this is where I need the help, yeah?
This is what a few of the attempts were going for but this is more pseudo-code because I could never get this version or any like it to fly.
# Call this as soon as you can, after .new()
def inject_workflow(description)
public_send :workflow do
description[:workflow][:states].each do |state|
state.map do |name, event|
public_send name.to_sym do # nested call occurs in Workflow gem
# nested call occurs in Workflow gem
public_send :event, event[:name], transitions_to: event[:transitions_to]
end
end
end
end
end
From what I was trying, all these kinds of attempts ended up in the same result, which was my scope isn't what I need because I'm evaluating code in the Workflow gem, not in the module or user's class.
Anyways, here's my implementation. I would really appreciate it if someone could point me in the right direction!
module WorkflowFactory
# ...
def inject_workflow(description)
# Build up an array of strings that will be used to create exactly what
# you would hand-write in your class, if you wanted to use the gem.
description_string_builder = ['include Workflow', 'workflow do']
description[:workflow][:states].each do |state|
state.map do |name, state_description|
if state_description.nil? # if this is a final state...
description_string_builder << "state :#{name}"
else # because it is not a final state, add event information too.
description_string_builder.concat([
"state :#{name} do",
"event :#{state_description[:event]}, transitions_to: :#{state_description[:transitions_to]}",
"end"
])
end
end
end
description_string_builder << "end\n"
begin
# Use class_eval to run that workflow specification by
# passing it off to the workflow gem, just like you would when you use
# the gem normally. I'm pretty sure this is where everyone's head pops...
self.class.class_eval(description_string_builder.join("\n"))
define_singleton_method(:has_workflow?) { true }
rescue Exception => e
define_singleton_method(:has_workflow?) { !!(puts e.backtrace) }
end
end
end
end
# This is the class in question.
class Job
include WorkflowFactory
# ... some interesting code for your class goes here
def next!
current_state.events.#somehow choose the correct event
end
end
# and in some other place where you want your "job" to be able to use a workflow, you have something like this...
job = Job.new
job.done?
# => false
until job.done? do job.next! end
# progresses through the workflow and manages its own state awareness
I started this question off under 300000 lines of text, I swear. Thanks for hanging in there! Here's even more documentation, if you're not asleep yet.
module in my gem

In Ruby, why wrap "yield" in a call you are making anyway?

I am new to Ruby. I am confused by something I am reading here:
http://alma-connect.github.io/techblog/2014/03/rails-pub-sub.html
They offer this code:
# app/pub_sub/publisher.rb
module Publisher
extend self
# delegate to ActiveSupport::Notifications.instrument
def broadcast_event(event_name, payload={})
if block_given?
ActiveSupport::Notifications.instrument(event_name, payload) do
yield
end
else
ActiveSupport::Notifications.instrument(event_name, payload)
end
end
end
What is the difference between doing this:
ActiveSupport::Notifications.instrument(event_name, payload) do
yield
end
versus doing this:
ActiveSupport::Notifications.instrument(event_name, payload)
yield
If this were another language, I might assume that we first call the method instrument(), and then we call yield so as to call the block. But that is not what they wrote. They show yield being nested inside of ActiveSupport::Notifications.instrument().
Should I assume that ActiveSupport::Notifications.instrument() is returning some kind of iterable, that we will iterate over? Are we calling yield once for every item returned from ActiveSupport::Notifications.instrument()?
While blocks are frequently used for iteration they have many other uses. One is to ensure proper resource cleanup, for example
ActiveRecord::Base.with_connection do
...
end
Checks out a database connection for the thread, yields to the block and then checks the connection back in.
In the specific case of the instrument method you found what it does is add to the event data it is about to broadcast information about the time it's block took to execute. The actual implementation is more complicated but in broad terms it's not so different to
event = Event.new(event_name, payload)
event.start = Time.now
yield
event.end = Time.now
event
The use of yield allows it to wrap the execution of your code with some timing code. In your second example no block is passed to instrument, which detects this and will record it as an event having no duration
The broadcast_event method has been designed to accept an optional block (which allows you to pass a code block to the method).
ActiveSupport::Notifications.instrument also takes an optional block.
Your first example simply takes the block passed in to broadcast_event and forwards it along to ActiveSupport::Notifications.instrument. If there's no block, you can't yield anything, hence the different calls.

How can I find a Ruby method dependencies?

Is there a way to get the list of methods that implement a Ruby method when this method is invoked?
For example:
def foo
puts "foo"
end
def foo2
foo
end
I want to know that when calling "foo2" it calls 1st "foo" and 2nd "puts" and the corresponding files these methods are defined into. (If "puts" calls other methods, I would like to know them too)
Is that possible? and if 'yes' how? I could say that my question is about finding the method dependencies.
You can sort of get this using set_trace_func, but since Ruby is dynamic you would also need test code to call the methods so that the call order is printed.
set_trace_func proc { |event, filename, line, id, binding, klass| puts "#{klass}##{id}" }
In Ruby 2.0, TracePoint is a superior alternative.
Static code analysis, especially one you'd like to perform (listing all methods called within a method), is very hard in ruby (close to impossible) because the language is dynamic and allows for very strong metaprogramming techniques. Even the parser itself doesn't know the methods required until it tries to execute the code.
Example: calling eval with code read from a file.

Running a passed proc with params inside a class instance in ruby

This example is a bit contrived but it's from a test so it sort of just works this way and will help me figure out how to use this in the actual library. I have a lambda, defined like this:
l = lambda { |v| process(v) }
I want to pass this to an object and have the proc run in the context of that object. The object might look like this:
class LambdaRunner
def process(v)
puts v
end
def run(proc)
# code to run proc in the context of the object
end
end
I am familiar with the solution of running the proc using instance_eval.
instance_eval(&proc)
But this won't work because I need to pass a value to the proc. I've managed something like a working solution, but it feels hacky and error prone.
self.class.class_eval do
define_method(:runner, &proc)
end
runner('hi from lambda runner')
All other method have been met with undefined method process
See the documentation for instance_exec (this is backported to 1.8.7). Assuming your proc takes a single argument |v|, you will probably end up with something like this in LambdaRunner.
def run(proc)
instance_exec(v_value, &proc)
end
By the way, in version of Ruby that don't have #instance_exec, your hack is basically the only way to implement it. This was how I implemented it for older Rubies which didn't have the backported method.

Make dynamic method calls using NoMethodError handler instead of method_missing

I'm trying to make an API for dynamic reloading processes; right now I'm at the point where I want to provide in all contexts a method called reload!, however, I'm implementing this method on an object that has some state (so it can't be on Kernel).
Suppose we have something like
WorkerForker.run_in_worker do
# some code over here...
reload! if some_condition
end
Inside the run_in_worker method there is a code like the following:
begin
worker = Worker.new(pid, stream)
block.call
rescue NoMethodError => e
if (e.message =~ /reload!/)
puts "reload! was called"
worker.reload!
else
raise e
end
end
So I'm doing it this way because I want to make the reload! method available in any nested context, and I don't wanna mess the block I'm receiving with an instance_eval on the worker instance.
So my question is, is there any complications regarding this approach? I don't know if anybody has done this already (haven't read that much code yet), and if it has been done already? Is there a better way to achieve the objective of this code?
Assuming i understand you now, how about this:
my_object = Blah.new
Object.send(:define_method, :reload!) {
my_object.reload!
...
}
Using this method every object that invokes the reload! method is modifying the same shared state since my_object is captured by the block passed to define_method
what's wrong with doing this?
def run_in_worker(&block)
...
worker = Worker.new(pid, stream)
block.call(worker)
end
WorkerForker.run_in_worker do |worker|
worker.reload! if some_condition
end
It sounds like you just want every method to know about an object without the method or the method's owner having been told about it. The way to accomplish this is a global variable. It's not generally considered a good idea (because it leads to concurrency issues, ownership issues, makes unit testing harder, etc.), but if that's what you want, there it is.

Resources