onfire
onfire copied to clipboard
Event Return values
From my current reading, the framework doesn't handle any return value from an event. Often, the signal-slot pattern provides for a method for dealing with possible return values. Either through collecting them, or aggregating them somehow. I believe it would make an invaluable addition to the gem.
Please make an example of how and especially where you would like to use the event return value.
This is a snippet from a personal application I'm working on. The following component is responsible for loading the model (and only loading the model).
class UserLoadWidget < Apotomo::Widget
responds_to_event :load
responds_to_event :load_multiple
expose(:users, params: :options)
expose(:user, params: :options)
def load evt
trigger :load_multiple, user: { id: options[:id] }
evt[:context][user_context] = user
end
def load_multiple evt
options[:user] = evt[:user] unless evt[:user].empty?
end
private
def user_context
options[:user_context] || :user
end
end
Here I use decent_exposure to convert the options hash into the object (it has a well defined interface, so there isn't a lot of magic there). This works as expected, but notice the second line of the load method? -- this is the return value.
Here's another component that uses the one above:
class UserPageWidget < Apotomo::Widget
expose(:user) { widget(:user, user: options[:context][:user]) }
has_widgets do |root|
options[:context] ||= {}
root << widget(:user_load, id: options[:id])
root.respond_to_event :load, on: :user_load
trigger :load, context: options[:context]
root << user
end
def display
render
end
end
I am aware that this is not optimally organized at the moment, but the main idea is to trigger the load event here (and possibly in other, specialized components), and funnel them through a single point, the loader. Naturally, the loader has to return something. As you can see, in this model the page passes in a "context" to the loader to populate with data, which is then being passed along to yet another component specializing in user data display.
Here's that component to complete the picture:
class UserWidget < Apotomo::Widget
expose(:user) { options[:user] }
def display mode = :full, language = nil
render locals: { mode: mode }
end
end
Bear in mind that I lifted this model from Drupal (my day job). So this whole chain of user_page -> user && user_(load|load_multiple) of components mimicks the flow found there, so that should answer why I've laid things out in this particular way. Basically I plan to make this very dynamic in nature, so isolation/encapsulation is priority.
The basic idea should be fairly simple to implement:
module ProcessingMethods
def bubble!
node = source
results = []
# in a real visitor pattern, the visited would call #process_node.
begin process_node(node, results) end while node = node.parent
result_processor.call(results)
end
def process_node(node, results)
node.handlers_for_event(self).each do |proc|
return if stopped?
results << call_handler(proc, node)
end
end
def call_handler(proc, node)
proc.call(self)
end
def result_processor
# Logic here to allow for a custom processor or return a default
return custom_processor if custom_processor
proc do |results| # The default simply returns the accumulated results.
results
end
end
end
I grabbed this concept from the signals library of boost. What do you think?
Edit: I would even go as far as abstracting the collection of the results so that you can use different strategies, like allowing to collect information into any arbitrary structure. So instead of:
results << call_handler(proc, node)
I would do:
collector.add(call_handler(proc, node))
With a pluggable object to process the collection:
def collector
@collector ||= custom_collector || default_collector
end
And in bubble!:
result_processor.call(collector.results)
This is all very rough. But I hope it's clear what I'm getting at.