On the use of callbacks in hexagonal rails

609 views
Skip to first unread message

Nikolay Sturm

unread,
Nov 25, 2012, 2:17:19 AM11/25/12
to objects-...@googlegroups.com
Hi,

I tried to apply the ideas of callbacks as described by Matt Wynne in
his "Hexagonal Rails" talks and Kevin Rutherford in his blog
(http://silkandspinach.net/2012/07/06/hexagonal-rails-hiding-the-finders/)
to some code at work for error handling.

The code has several layers (controller -> service -> use case object ->
ActiveRecord model). It is written with exceptions, which the controller
catches to render an error page. At points it felt as going in the
direction of using exceptions for flow control, which is the reason I
tried callbacks.

Callbacks, however didn't work any better IMHO. The code was littered
with failure callbacks and had I added success callbacks as well,
program flow would have been totally obfuscated.

I am wondering if I misunderstood something or if I just happened to use
callbacks in the wrong context.

cheers,

Nikolay

--
"It's all part of my Can't-Do approach to life." Wally

Matt Wynne

unread,
Nov 26, 2012, 7:40:49 AM11/26/12
to objects-...@googlegroups.com
Hi Nikolay,

I think that one of the benefits of defining clear protocols (which is part of that callback-based approach) is that you don't need to worry so much about program flow.

Instead, you just look at an individual object and say "right, so if it gets this message, this is what it will do". That's the advantage of this style, for me. I don't have to hold too much of the code in my head at any one time.

Without seeing the code or some specific examples, it's hard to offer any more advice. One guess is that if you're still worrying about program flow, perhaps the responsibilities of the objects you've got in your architecture aren't yet clearly enough defined?

Perry Smith

unread,
Nov 26, 2012, 9:51:56 AM11/26/12
to objects-...@googlegroups.com

On Nov 26, 2012, at 6:40 AM, Matt Wynne wrote:

> Hi Nikolay,
>
> I think that one of the benefits of defining clear protocols (which is part of that callback-based approach) is that you don't need to worry so much about program flow.
>
> Instead, you just look at an individual object and say "right, so if it gets this message, this is what it will do". That's the advantage of this style, for me. I don't have to hold too much of the code in my head at any one time.
>
> Without seeing the code or some specific examples, it's hard to offer any more advice. One guess is that if you're still worrying about program flow, perhaps the responsibilities of the objects you've got in your architecture aren't yet clearly enough defined?

I may sound contradictory here.

The arguments that DCI makes is that what you described is not good in the bigger picture. The reason I may sound contradictory is I'm still on the fence about DCI -- at least what I've seen so far.

Sometimes I can stay content with the mental "right, so if it gets this message, this is what it will do". But other times one of two things happens. The most prevalent is I'm looking at the code that generates the message and asking "ok... where does that take me?" and its not clear or simple how to track it down. In the case of an ActiveRecord foo.save, I can be looking straight at the call and not realize that a callback is going to be invoked.

The other thing that happens is the opposite. The question comes up "why would that message be generated in the first place?"

callbacks are used a lot in ajax. You send a message to the server and wait for the reply and then the reply does something -- perhaps sends another ajax call. I've been frustrated at the splintering effect this has on the code. It would be wonderful if the whole algorithm could be viewed in one (small) place together.

pedz

Matt Wynne

unread,
Nov 26, 2012, 10:50:15 AM11/26/12
to objects-...@googlegroups.com
The thing is, you can't have it both ways. If you want to see how the code all flows together, then the best way to do that is have it all in One Big Method. But there are other maintenance costs with that. So you break things up.

My experience of breaking things up is not one of splintering, but of simplifying. But it does depend how you break things up. I've definitely got it wrong before, and that's when you find yourself hunting around trying to trace where messages go. I think one symptom of a good design is where you stop feeling the need to do that.

Perry Smith

unread,
Nov 26, 2012, 11:44:41 AM11/26/12
to objects-...@googlegroups.com
I don't think we disagree anywhere. I agree that you can't have it both ways. That's where I'm being contradictory or ambivalent. And I agree that often, callbacks work. But I've found myself, particularly with ajax / javascript code, wishing I had an alternative.

Christopher McGrath

unread,
Nov 26, 2012, 11:54:47 AM11/26/12
to objects-...@googlegroups.com
On 26 Nov 2012, at 14:51, Perry Smith wrote:

Sometimes I can stay content with the mental "right, so if it gets this message, this is what it will do".  But other times one of two things happens.  The most prevalent is I'm looking at the code that generates the message and asking "ok... where does that take me?" and its not clear or simple how to track it down.  In the case of an ActiveRecord foo.save, I can be looking straight at the call and not realize that a callback is going to be invoked.

I'm wondering if the place where that information should live is in the functional tests, not the code. I have felt the same pain though.

At  a certain level of complexity I've found myself taking some scattered callbacks and moving them to a factory class though to be fair the code was fairly mature and the scattered behaviour not that likely to change. I did it both for performance and not breaking my brain reasons.

Chris

Matt Wynne

unread,
Nov 26, 2012, 12:14:55 PM11/26/12
to objects-...@googlegroups.com
This is like the GOOS concept of a 'matchmaker' class whose sole responsibility is to wire up other objects. That way your lower-level objects become a DSL, and the matchmaker uses the DSL to compose a working program.

@Perry maybe that's what you've been missing in the javascript apps?

Steve Klabnik

unread,
Nov 26, 2012, 12:51:08 PM11/26/12
to objects-...@googlegroups.com
"callback hell" is what promises/monads are for.

Perry Smith

unread,
Nov 26, 2012, 1:07:11 PM11/26/12
to objects-...@googlegroups.com

On Nov 26, 2012, at 11:14 AM, Matt Wynne wrote:

This is like the GOOS concept of a 'matchmaker' class whose sole responsibility is to wire up other objects. That way your lower-level objects become a DSL, and the matchmaker uses the DSL to compose a working program.

@Perry maybe that's what you've been missing in the javascript apps?

It might very well be.

Google of "goos matchmaker" doesn't provide any (obvious) hits.  Do you have some pointers?

I found a book about "GOOS" but no general description.

arun vydianathan

unread,
Nov 26, 2012, 1:15:40 PM11/26/12
to objects-...@googlegroups.com
Here is a sneak peek into the chapter on matchmaker in GOOS Book http://goo.gl/kXJqW
--
Thanks,
--
Arun
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you're not on the edge, you're taking up too much room.

Matt Wynne

unread,
Nov 26, 2012, 2:15:54 PM11/26/12
to objects-...@googlegroups.com
On 26 Nov 2012, at 18:15, arun vydianathan wrote:

Here is a sneak peek into the chapter on matchmaker in GOOS Book http://goo.gl/kXJqW

That's the book. The concept of a matchmaker, and a composite object ('simpler than the sum of its parts') comes up again and again in that book. I highly recommend it if you're interested in this topic.

Jim Gay

unread,
Nov 26, 2012, 4:27:13 PM11/26/12
to objects-...@googlegroups.com
On Mon, Nov 26, 2012 at 11:54 AM, Christopher McGrath <ch...@octopod.info> wrote:

I'm wondering if the place where that information should live is in the functional tests, not the code. I have felt the same pain though.


I'm really curious about desires like this.
Why would you *want* to look at tests to understand code? 


--
Write intention revealing code #=> http://www.clean-ruby.com

Jim Gay
Saturn Flyer LLC

Mike Kelly

unread,
Nov 26, 2012, 4:34:06 PM11/26/12
to objects-...@googlegroups.com
it might be the same sort of reasons people like learning how to use
gems from code samples?
--
Mike

http://twitter.com/mikekelly85
http://github.com/mikekelly
http://linkedin.com/in/mikekelly123

David Doolin

unread,
Nov 26, 2012, 4:34:12 PM11/26/12
to objects-...@googlegroups.com
I've found that reading through well-implemented
test code is the fastest way for me to understand the
intent of the code, and possibly why such code was written.

I'm only speaking for myself here.

For anyone implementing thoughtful, accurate
and informative tests, thank you.
--
primus inter parse

Steve Klabnik

unread,
Nov 26, 2012, 4:40:01 PM11/26/12
to objects-...@googlegroups.com
> Why would you *want* to look at tests to understand code?

Tests are like run-able documentation.

Nick Novitski

unread,
Nov 26, 2012, 4:42:28 PM11/26/12
to objects-...@googlegroups.com
Tests communicate usage, whereas descriptive code communicates implementation.

Jim Gay

unread,
Nov 26, 2012, 4:51:54 PM11/26/12
to objects-...@googlegroups.com
On Mon, Nov 26, 2012 at 10:50 AM, Matt Wynne <ma...@mattwynne.net> wrote:

The thing is, you can't have it both ways. If you want to see how the code all flows together, then the best way to do that is have it all in One Big Method. But there are other maintenance costs with that. So you break things up.

My experience of breaking things up is not one of splintering, but of simplifying. But it does depend how you break things up. I've definitely got it wrong before, and that's when you find yourself hunting around trying to trace where messages go. I think one symptom of a good design is where you stop feeling the need to do that.

cheers,
Matt


I've been exploring this a lot and I recommend Alistair Cockburn's "Writing Effective Use Cases"
Writing a use case gives you an overview of what's happening in the system and should cover all the possible outcomes. He also writes about how you'll have different levels of use cases for different stakeholders.

Much of what we do is contextual so I try to create things in context first. When I first was working with concepts in DCI I expected that the roles my objects were playing needed to be pulled out of the context into separate files. This is a no-brainer because then you can reuse them.

Over time, however, I found this to be a serious headache. The behaviors of the use cases were only relevant within a given context and I ended up moving all my role related behaviors back into a context.

I don't see how One Big Method is the only way to see what's happening. I'm constantly exploring this, but bits and pieces need a place to come together.

Rinaldi Fonseca

unread,
Nov 26, 2012, 5:16:10 PM11/26/12
to objects-...@googlegroups.com
If I just can understand the code when I read the tests, then I have a problem.

Steve Klabnik

unread,
Nov 26, 2012, 5:48:22 PM11/26/12
to objects-...@googlegroups.com
> If I just can understand the code when I read the tests, then I have a
> problem.

A witty saying proves nothing.

Perry Smith

unread,
Nov 26, 2012, 7:05:49 PM11/26/12
to objects-...@googlegroups.com

On Nov 26, 2012, at 11:51 AM, Steve Klabnik wrote:

> "callback hell" is what promises/monads are for.

Very nice. Thank you. I've seen promises before but had not seen them used with monads to create pipelines. This is really nice.

Steve Klabnik

unread,
Nov 26, 2012, 7:22:50 PM11/26/12
to objects-...@googlegroups.com
It's more that promises _are_ a monad, but yes. :) Here's a blog post
on the subject that goes further into depth:
http://blog.jcoglan.com/2011/03/11/promises-are-the-monad-of-asynchronous-programming/

Perry Smith

unread,
Nov 26, 2012, 7:50:55 PM11/26/12
to objects-...@googlegroups.com
That post and another by the same author was what I bumped into via google. Wish I had that a year or so ago :-)

Perry Smith

unread,
Nov 26, 2012, 10:42:28 PM11/26/12
to objects-...@googlegroups.com

On Nov 26, 2012, at 3:42 PM, Nick Novitski wrote:

> Tests communicate usage, whereas descriptive code communicates implementation.

Also, the tests represent "the truth". It is what the code should do even if it isn't doing it... correct?

Rinaldi Fonseca

unread,
Nov 26, 2012, 6:08:56 PM11/26/12
to objects-...@googlegroups.com
A witty saying proves nothing.

I dont need to  prove nothing in this case. Every body knows that when you write code you are comunicating, and when you(or other people) read the code and dont undersdand, the comunication failed. Make sense?

Steve Klabnik

unread,
Nov 27, 2012, 12:45:53 PM11/27/12
to objects-...@googlegroups.com
I was trying to be triply ironic, sorry. Replying on an iPod doesn't help. :)

I actually didn't understand what you meant, but I do understand it
better now, thank you. However, I disagree.

Since this is an OO list, I can assume that we're talking about OO.
One of the big principles of OO is encapsulation and data hiding. I'm
actually not supposed to know the internals of how my objects work.
Reading the code is knowing the internals.

Jim Gay

unread,
Nov 27, 2012, 1:06:17 PM11/27/12
to objects-...@googlegroups.com
On Tue, Nov 27, 2012 at 12:45 PM, Steve Klabnik <st...@steveklabnik.com> wrote:
Reading the code is knowing the internals.

What?

Aside from being confused by that, your point about not knowing the internals of how something works is good. The argument that Jim Coplien has made for DCI is that we see our objects from the inside out. We understand all of the internals of an object, but don't understand it in its environment. The goal of DCI is to capture an understanding of the environment of objects and how they interact.

Steve Klabnik

unread,
Nov 27, 2012, 1:09:34 PM11/27/12
to objects-...@googlegroups.com
>> Reading the code is knowing the internals.
> What?

If the answer to "How do I use this thing?" is "look at the source,"
then you're examining internals to know how something works, right?

Jim Gay

unread,
Nov 27, 2012, 1:17:22 PM11/27/12
to objects-...@googlegroups.com
I understand.

In other words, would you say that the tests give you relevant frame of reference?

Steve Klabnik

unread,
Nov 27, 2012, 1:22:18 PM11/27/12
to objects-...@googlegroups.com
Right. The tests are like examples of how to use the code. They're a
client to your code.

Q: "I'd like to use Foo.bar, how should I use it?"

A1: "Read the code!"

class Foo
def bar(a, b, c={})
baz(a, c) + qux(@quxx, b)
end
end

Me: FFFFFUUUUUUU

A2: "Check the tests!"

it "can bar with an id, and name" do
foo = Foo.new
foo.bar(1, "steve").should be(something)
end

it "can bar with options" do
foo = Foo.new
foo.bar(1, "steve", :with_extra_spice => "yeah!").should be(something_else)
end

Me: Ahhh, so clear!

Jim Gay

unread,
Nov 27, 2012, 1:28:19 PM11/27/12
to objects-...@googlegroups.com
I agree with your premise, but it's a straw man argument.
Taking unclear code and showing how tests solve the problem of clarity doesn't help when you are working on or working with that code.

Steve Klabnik

unread,
Nov 27, 2012, 1:32:31 PM11/27/12
to objects-...@googlegroups.com
I'm not trying to make it particular obfuscatory. How is calling two
other methods and adding them together obfuscatory?

Maybe using metasyntactic variable names was a poor choice. My point
was that to understand the one call, now you have to look at the two
other calls, and understand them... which shouldn't be necessary.

Jim Gay

unread,
Nov 27, 2012, 1:47:05 PM11/27/12
to objects-...@googlegroups.com
I understand that.
However, isn't our code a communication tool? Software needs to work, but the ways we use it changes over time and the clarity of the code is a benefit that we all seem to be eager to attain to adjust to changes.

This thread started with Nikolay mentioning that his code was littered with failure callbacks. The implication is (as far as I can tell) that it became more and more difficult to discern what the application was written to do.

If our answer to understanding code is to read the tests, then why worry about the structure or naming conventions in the code at all?

Your point about needing relevant information is still apt though. Seeing input and output is always valuable.

Ian White

unread,
Nov 27, 2012, 1:55:35 PM11/27/12
to objects-...@googlegroups.com
Maybe using metasyntactic variable names was a poor choice. My point
was that to understand the one call, now you have to look at the two
other calls, and understand them... which shouldn't be necessary.

Perhaps another way to put this point might be that trying to understand what code does based on the names of methods is inherently fragile.  It relies on the coder's subjective point of view, and their ability to reason correctly about the code they are writing.

The unreliable narrator is possibly an often seen, if unintended, feature of code, especially with respect to naming.

Tests can suffer from this problem too, but this can be ameliorated by striving to have dead simple tests, composed of easy to understand chunks (not always possible in the codebase).  Even better if the chunks that name, are themselves executable, e.g. rspec's 'executable natural language' approach, e.g. its(:foo) { should be_empty }

That said, I'm sure there's more to 'intention revealing code' (I've not yet read Jim's book) than naming and organising your code for what strikes the coder as human consumption, it seems to me that your code *should* be easy to understand.  However tests bring an extra level of knowledge about the code that can't be gained otehrwise (for Steve's client of your code point), and hopefully drive the code in that direction.

Cheers,
Ian White

Peak District, UK

Jim Gay

unread,
Nov 27, 2012, 2:01:42 PM11/27/12
to objects-...@googlegroups.com
On Tue, Nov 27, 2012 at 1:55 PM, Ian White <ian.w...@gmail.com> wrote:
Maybe using metasyntactic variable names was a poor choice. My point
was that to understand the one call, now you have to look at the two
other calls, and understand them... which shouldn't be necessary.

Perhaps another way to put this point might be that trying to understand what code does based on the names of methods is inherently fragile.  It relies on the coder's subjective point of view, and their ability to reason correctly about the code they are writing.

Wouldn't having code that makes it easy to "reason correctly" be a goal?
 

The unreliable narrator is possibly an often seen, if unintended, feature of code, especially with respect to naming.

Tests can suffer from this problem too, but this can be ameliorated by striving to have dead simple tests, composed of easy to understand chunks (not always possible in the codebase).  Even better if the chunks that name, are themselves executable, e.g. rspec's 'executable natural language' approach, e.g. its(:foo) { should be_empty }

Nothing about this limits it to ONLY tests. Why would you work to achieve all of this in code?
And, after all, tests are code.
 

That said, I'm sure there's more to 'intention revealing code' (I've not yet read Jim's book) than naming and organising your code for what strikes the coder as human consumption, it seems to me that your code *should* be easy to understand.  However tests bring an extra level of knowledge about the code that can't be gained otehrwise (for Steve's client of your code point), and hopefully drive the code in that direction.

Cheers,
Ian White

Peak District, UK

Jim Gay

unread,
Nov 27, 2012, 2:02:37 PM11/27/12
to objects-...@googlegroups.com

Nothing about this limits it to ONLY tests. Why would you work to achieve all of this in code?


Oops. That should say "Why wouldn't you..."

Dave Newton

unread,
Nov 27, 2012, 2:07:12 PM11/27/12
to objects-...@googlegroups.com
On Tue, Nov 27, 2012 at 2:01 PM, Jim Gay <j...@saturnflyer.com> wrote:
> Nothing about this limits it to ONLY tests. Why wouldn't you work to achieve
> all of this in code?
> And, after all, tests are code.

I don't think anybody said that non-test code shouldn't be as
expository as possible.

I can't see how deciphering API usage is easier through API
internals--that isn't what an entire implementation is best at
describing.

Dave

--
s: davelnewton_skype
t: @dave_newton
b: Bucky Bits
g: davelnewton
so: Dave Newton

Ian White

unread,
Nov 27, 2012, 2:12:17 PM11/27/12
to objects-...@googlegroups.com
Hi Jim,

Nothing about this limits it to ONLY tests. Why would you work to achieve all of this in code?

Oops. That should say "Why wouldn't you..."

I agree entirely, and said as much in the last paragraph of my post.  Easy to understand code is the best, but tests give you knowledge you can't get by that route alone.

Cheers,

Jim Gay

unread,
Nov 27, 2012, 2:15:40 PM11/27/12
to objects-...@googlegroups.com
On Tue, Nov 27, 2012 at 2:12 PM, Ian White <ian.w...@gmail.com> wrote:
Nothing about this limits it to ONLY tests. Why would you work to achieve all of this in code?

Oops. That should say "Why wouldn't you..."

I agree entirely, and said as much in the last paragraph of my post.  Easy to understand code is the best, but tests give you knowledge you can't get by that route alone.


You're right. I replied too quickly. Sorry for skipping your point there. 
Tests provide environmental factors like parameters that make the execution concrete.

Sam Livingston-Gray

unread,
Nov 27, 2012, 2:23:59 PM11/27/12
to objects-...@googlegroups.com
On Tue, Nov 27, 2012 at 10:47 AM, Jim Gay <j...@saturnflyer.com> wrote:
> However, isn't our code a communication tool? Software needs to work, but
> the ways we use it changes over time and the clarity of the code is a
> benefit that we all seem to be eager to attain to adjust to changes.

Tests and code are *both* communication tools. I've seen good and bad
versions of both. However, in all but the most pathological of cases
(i.e., a Rails "unit" test with 50 lines of setup), I've found that
tests *tend* to be more approachable. Tangled code often requires
that I hold the entire structure in my head at once, which is
something I'm not very good at. Because each test should only focus
on one aspect of behavior, I can step through them and build your
understanding over time. (Refactoring and renaming those tests as I
go also makes the job that much easier for the next developer[1], even
if I never touch the code under test. Good tests support refactoring
in a way that no amount of "staring at the code and reasoning through
it" can ever do.)

> If our answer to understanding code is to read the tests, then why worry
> about the structure or naming conventions in the code at all?

Speaking of straw men... ;>

Good developers pay attention to readability in both code and tests.
Ideally, one could discern all one needed to know by reading the code
directly. However, in cases where a downstream API makes that code
uglier than we'd like, it's still within our power[2] to write clear,
communicative tests. (Or, in extreme cases, an adapter layer, though
I'm not sure that's possible in a callback-oriented environment.)

-Sam


[1] Who might be me, according to Livingston-Gray's Equation: "you +
time = someone else"
[2] Insert Spider-Man cliché here.

Ian White

unread,
Nov 27, 2012, 2:35:03 PM11/27/12
to objects-...@googlegroups.com
You're right. I replied too quickly. Sorry for skipping your point there. 
=|:-)  (my attempt at a 'you are a gentleman' smiley)

This thread piqued my interest because I too have experienced stuff similar to 'callback hell' in recent months, and am constantly gripped by the urge to refine.

It's interesting to think about how the structural features of a coding style (callback 'hell' vs promise 'heaven'?, tell don't ask, even goto vs gosub etc) impacts, in a general way, on our ability to reason about code written in that style.  Does anyone know of any studies on this stuff, it strikes me that someone must have tried to do some empirical physiological research on it.

Cheers,
Ian White

Peak District, UK

ps. This really is a great list, and I'd like to thank everyone for their contributions.

Jim Gay

unread,
Nov 27, 2012, 2:40:11 PM11/27/12
to objects-...@googlegroups.com
On Tue, Nov 27, 2012 at 2:35 PM, Ian White <ian.w...@gmail.com> wrote:
You're right. I replied too quickly. Sorry for skipping your point there. 
=|:-)  (my attempt at a 'you are a gentleman' smiley)

Thanks!
 

This thread piqued my interest because I too have experienced stuff similar to 'callback hell' in recent months, and am constantly gripped by the urge to refine.

It's interesting to think about how the structural features of a coding style (callback 'hell' vs promise 'heaven'?, tell don't ask, even goto vs gosub etc) impacts, in a general way, on our ability to reason about code written in that style.  Does anyone know of any studies on this stuff, it strikes me that someone must have tried to do some empirical physiological research on it.

I talked a bit with a friend of mine about attempting to put together a study on different approaches to programming. 
It's a very difficult task and we'd be unlikely to walk away with much empirical evidence of anything. Still I think it would be worth the effort to see what can be done.
 

Cheers,
Ian White

Peak District, UK

ps. This really is a great list, and I'd like to thank everyone for their contributions.

Agreed! 

Mike Kelly

unread,
Nov 27, 2012, 2:41:43 PM11/27/12
to objects-...@googlegroups.com
Fwiw, I think its important that every component (object) should be understandable on its own and so (unit) tests are the most natural way to prove this and at the same time communicate the object's intended purpose.

I think this discussion would benefit with some more concrete examples of where the "callback" approach breaks down despite decent test coverage.

Sent from my iPhone

Matt Wynne

unread,
Nov 27, 2012, 2:53:12 PM11/27/12
to objects-...@googlegroups.com
On tests vs source, here's my take:

Tests (or client code, or examples) tell you *what* the thing does does; reading the source tells you *how* it does it. It's a step down the ladder of abstraction. Ideally you only want to burden yourself with that extra detail when you have to. I've used Nokogiri hundreds of times, for example, but I don't think I've ever found the need to look under the covers.


doug livesey

unread,
Nov 28, 2012, 4:30:54 AM11/28/12
to objects-...@googlegroups.com
I'm going to digest all the monad stuff later on, but in the meantime, one suggestion that I heard at a recent talk in Sheffield suggested using responder objects.
I've not done this yet, so may have some details wrong, but the basic idea is that you can do something like:

class GetItemService < Struct.new( :id, :responder )
  def get
    item = Item.find( id )
    item ? responder.success( item ) : responder.failure( item )
  end
end

class GetItemResponder < SimpleDelegator
  def success( item )
    @item = item
    render( "show" )
  end
  def failure
    # handle failure -- render a 404, or whatever
  end
end

class ItemsController < ApplicationController
  def show
    GetItemService.new( params[:id], GetItemResponder.new( self ) ).get
  end
end

There was more to it than this, and this is a *way* simplified example, but you can see how you might be able to re-use and compose responder objects with an approach like this.
Cheers,
   Doug.

doug livesey

unread,
Nov 28, 2012, 4:34:31 AM11/28/12
to objects-...@googlegroups.com
(Okay, so the instance variable setting wouldn't work, there, so more thought needed, but you get the idea! ;) )

David Burrows

unread,
Nov 28, 2012, 5:33:01 AM11/28/12
to objects-...@googlegroups.com

It's a step down the ladder of abstraction.

The code I find easiest to read is itself a "ladder of abstraction". Good code starts with a top level abstraction and as you move through the tree of includes you step down the ladder, at any point you see the shape of the whole system, but at a greater or lesser level of detail (whoah, fractals.)  

To take an example, the Twitter gem starts with a base module Twitter, which requires Twitter::Client, if we look at Client it requires Twitter::API::Users, Twitter::API::Timeline, etc. I don't need to look at any docs or tests to work out what's going on here. 

If the code is a collection of what Avdi's book calls "lone wolf" objects it's incredibly hard to comprehend as there's no way to get into the underlying abstraction and you need to resort to docs/tests. 

Christopher McGrath

unread,
Nov 28, 2012, 5:41:36 AM11/28/12
to objects-...@googlegroups.com

On 26 Nov 2012, at 21:27, Jim Gay wrote:

> On Mon, Nov 26, 2012 at 11:54 AM, Christopher McGrath <ch...@octopod.info>wrote:
>
>>
>> I'm wondering if the place where that information should live is in the
>> functional tests, not the code. I have felt the same pain though.
>>
>>
> I'm really curious about desires like this.
> Why would you *want* to look at tests to understand code?
>

I have read the rest of this thread but this question was directed at me so I'll add my 2p here.

All code should be clear of course, and one of the main tools to make it clear is for it to have a single responsibility. Code that has a single responsibility must collaborate with other code to get stuff done. When this collaboration is listed out in a method it's easy to read that particular class and reason about the total behaviour. When this collaboration is via something like callbacks to break dependencies you cannot now read that single class to understand the total behaviour. You don't know what other classes may register to receive callbacks and you don't have a list of descriptive method names to work with. You've gone from being able to read a text file on disk to needing to see the messages of the running system.

So where is the total behaviour described? Perhaps functional tests are wrong and it should be in acceptance tests. Perhaps the unit tests for the matchmaker class Matt described are the right place instead of using callbacks. I haven't read the monad / promise article but look forward to reading it and seeing other solutions there.

To answer the question, it's not that I want to look at the tests to understand the code, it's that that may be the easiest place to understand how the code collaborates to produce the behaviour I'm trying to reason about.

Chris

doug livesey

unread,
Nov 28, 2012, 7:59:10 AM11/28/12
to objects-...@googlegroups.com
Really nice clarification of what I meant, here, from the guy whose talk I saw: http://blog.jonrowe.co.uk/2012/10/08/adventures-in-the-hexagonal.html

Nikolay Sturm

unread,
Nov 28, 2012, 12:35:10 PM11/28/12
to objects-...@googlegroups.com
* Matt Wynne [2012-11-26]:
> Instead, you just look at an individual object and say "right, so if
> it gets this message, this is what it will do". That's the advantage
> of this style, for me. I don't have to hold too much of the code in my
> head at any one time.

Understood, I just can't figure out how to get to this point. #-)

> Without seeing the code or some specific examples, it's hard to offer
> any more advice. One guess is that if you're still worrying about
> program flow, perhaps the responsibilities of the objects you've got
> in your architecture aren't yet clearly enough defined?

Please have a look at https://gist.github.com/4162623
If the code looks strange, it's because it's based on even stranger
legacy code I hadn't have the time to fully refactor.

The basic idea: a CMS updates articles published on a webserver through
an API. Each update could change either author or article data und
needs to wipe the varnish cache afterwards.

This way of using callbacks feels just like checking return values all
over the place. The flow is obfuscated and I don't see any benefit
compared to the first version, using exceptions.

Hoping for insights.

cheers,

Nikolay

Richard Livsey

unread,
Nov 28, 2012, 1:23:29 PM11/28/12
to objects-...@googlegroups.com
This is what my controllers currently look like:

class ChatMessagesController < ActionController::Base

class ChatMessageCreatedResponse < SimpleDelegator
include StandardResponses

def success(message)
render json: ChatMessageSerializer.new(message)
end
end

def create
uc = PostChatMessage.new(params[:message], current_user)
uc.add_subscriber ChatMessageCreatedResponse.new(self)
uc.add_subscriber SearchIndexer.new, :success => :chat_message_created
uc.add_subscriber ChatMessageEmails.new(current_user)
uc.add_subscriber PusherNotifications.new(request.headers['X-Pusher-Socket']), :success => :chat_message_created
uc.call
end
end


This is working quite well for us so far, at a glance of the controller I can see the broad strokes of what's going to happen and can look at the individual subscribers to dig deeper into what messages they respond to (usually at least :success).

All my use-cases and the majority of the subscribers are out in a separate gem, as are all the serializers, so the Rails app is purely glue code whose controllers coordinate plugging together the appropriate use-cases and subscribers.

Some subscribers are generic, so we can re-name the message (:success => :chat_message_created) if we want to route it to a different method in the subscriber.

The StandardReponses module handles generic messages like :permission_denied, :not_found or :failure and generate the appropriate JSON and HTTP status codes and also has a default :success which sends a blank 204 response.

I currently mixin a PubSub module into all use cases, I'm tempted to extract that out to a separate Listener/PubSub object, but I don't feel too bad that use cases know about publishing for now.

Cheers.

--
Richard Livsey
Co-Founder, MinuteBase
Meeting collaboration made easy
http://minutebase.com
+44 (0) 7841 260 797

Andrew Stewart

unread,
Nov 29, 2012, 10:04:05 AM11/29/12
to objects-...@googlegroups.com
On 28 Nov 2012, at 19:23, Richard Livsey wrote:
> This is what my controllers currently look like:
>
> class ChatMessagesController < ActionController::Base
>
> class ChatMessageCreatedResponse < SimpleDelegator
> include StandardResponses
>
> def success(message)
> render json: ChatMessageSerializer.new(message)
> end
> end
>
> def create
> uc = PostChatMessage.new(params[:message], current_user)
> uc.add_subscriber ChatMessageCreatedResponse.new(self)
> uc.add_subscriber SearchIndexer.new, :success => :chat_message_created
> uc.add_subscriber ChatMessageEmails.new(current_user)
> uc.add_subscriber PusherNotifications.new(request.headers['X-Pusher-Socket']), :success => :chat_message_created
> uc.call
> end
> end
>
>
> This is working quite well for us so far, at a glance of the controller I can see the broad strokes of what's going to happen and can look at the individual subscribers to dig deeper into what messages they respond to (usually at least :success).

I like your approach and it's great to have a concrete example to inspect.

I was wondering how you pass data into your HTML views?

> The StandardReponses module handles generic messages like :permission_denied, :not_found or :failure and generate the appropriate JSON and HTTP status codes and also has a default :success which sends a blank 204 response.

Is that available somewhere to see?

Yours,
Andy Stewart

Richard Livsey

unread,
Nov 29, 2012, 1:07:40 PM11/29/12
to objects-...@googlegroups.com

> I like your approach and it's great to have a concrete example to inspect.
I need to write up a blog post at some point, but essentially it would be the same as Graham Ashton's post on the topic:
https://www.theagileplanner.com/blog/building-agile-planner/refactoring-with-hexagonal-rails

> I was wondering how you pass data into your HTML views?
I just use the locals hash instead of relying on instance variables to implicitly connect the controllers to the views.

> > The StandardReponses module handles generic messages like :permission_denied, :not_found or :failure and generate the appropriate JSON and HTTP status codes and also has a default :success which sends a blank 204 response.
> Is that available somewhere to see?


The standard responses are pretty simple and just map to serializers, this would be a bit more involved if this was more than just a JSON API:

module StandardResponses
def success(*args)
render nothing: true, status: 204
end

def failure(errors, *args)
render json: ValidationFailedSerializer.new("Validation Failed", errors), status: 422
end

def not_found(message, *args)
render json: DocumentNotFoundSerializer.new(message), status: 404
end

def denied(permission, *args)
render json: PermissionDeniedSerializer.new(permission), status: 403
end
end

Matt Wynne

unread,
Nov 30, 2012, 4:28:05 PM11/30/12
to objects-...@googlegroups.com
On 28 Nov 2012, at 18:23, Richard Livsey wrote:

This is what my controllers currently look like:

class ChatMessagesController < ActionController::Base

 class ChatMessageCreatedResponse < SimpleDelegator
   include StandardResponses

   def success(message)
     render json: ChatMessageSerializer.new(message)
   end
 end

 def create
   uc = PostChatMessage.new(params[:message], current_user)
   uc.add_subscriber ChatMessageCreatedResponse.new(self)
   uc.add_subscriber SearchIndexer.new, :success => :chat_message_created
   uc.add_subscriber ChatMessageEmails.new(current_user)
   uc.add_subscriber PusherNotifications.new(request.headers['X-Pusher-Socket']), :success => :chat_message_created
   uc.call
 end
end


This is working quite well for us so far, at a glance of the controller I can see the broad strokes of what's going to happen and can look at the individual subscribers to dig deeper into what messages they respond to (usually at least :success).

All my use-cases and the majority of the subscribers are out in a separate gem, as are all the serializers, so the Rails app is purely glue code whose controllers coordinate plugging together the appropriate use-cases and subscribers.

Some subscribers are generic, so we can re-name the message (:success => :chat_message_created) if we want to route it to a different method in the subscriber.

The StandardReponses module handles generic messages like :permission_denied, :not_found or :failure and generate the appropriate JSON and HTTP status codes and also has a default :success which sends a blank 204 response.

I currently mixin a PubSub module into all use cases, I'm tempted to extract that out to a separate Listener/PubSub object, but I don't feel too bad that use cases know about publishing for now.

Cheers.

You're living the hexagonal dream!

y...@reverb.com

unread,
Apr 3, 2013, 11:47:59 AM4/3/13
to objects-...@googlegroups.com
Hi guys,
I liked the idea of wrapping up the callbacks in a delegator class because it doesn't pollute the controller (or other layer) with random methods. Here's a sample hexagonal refactoring I've done which reuses some business logic across a rails controller and Grape API. Would love some feedback. One thing that is not good about this still is that my business logic still hits an AR method, which will be remedied soon.


thanks,

Yan Pritzker

y...@reverb.com

unread,
Apr 3, 2013, 11:49:54 AM4/3/13
to objects-...@googlegroups.com
Another thing I'm not clear on is where things like analytics would go. While I like the idea that the business case publishes events that others subscribe to, where does the wiring happen? The answer is not the controller because I want this to happen in all of: [controller, console, api, etc]. So do I just hardcode the analytics into the business action? Feels dirty. This feels like some aspect oriented programming territory. 

Yan Pritzker

Kris Leech

unread,
Apr 6, 2013, 12:58:33 PM4/6/13
to objects-...@googlegroups.com
I recently created a gem which wraps a pattern I've been using for a
little while: https://github.com/krisleech/wisper

It subscribes 'listeners' to a publisher, but also allow subscribing of
a block to a specific published event. This is less verbose than
wrapping the controller in a decorator and passing it in as a regular
subscriber.

If you scroll down to the ActionController section of the README you
will see that instead of using decorating the controller I can use
lambda's. I've kind of gone full circle with this as I started passing
in lambda's to my service object, but got frustrated that I had to
reassign instance var's for the view.

Then I tried SimpleDelegator around the controller, but:

1. Verbose, extra code, cluttering up the controller file
2. Private methods not accessible (e.g current_user)
3. You can't assign instance var's for use in the view, you must pass
the var's render's local hash.

Finally with (much * 100) inspiration from others I can up with the code
that allows both subscribing of either an object or a block, so I could
have the best of both worlds.

- Kris.

P.S Would be interested in some feedback.
> --
> You received this message because you are subscribed to the Google
> Groups "Objects on Rails" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to objects-on-rai...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Kris Leech

unread,
Apr 6, 2013, 1:09:48 PM4/6/13
to objects-...@googlegroups.com
y...@reverb.com wrote:
Another thing I'm not clear on is where things like analytics would go. While I like the idea that the business case publishes events that others subscribe to, where does the wiring happen? The answer is not the controller because I want this to happen in all of: [controller, console, api, etc]. So do I just hardcode the analytics into the business action? Feels dirty. This feels like some aspect oriented programming territory.
More self promotion, but with Wisper you could do this: https://gist.github.com/krisleech/5326823

Of course this doesn't answer you question as such, since I guess you don't want to `subscribe` each listener each time a client (controller, CLI etc.) wants to execute a business case?

But this does remove analytics from the business case itself. The same technique applies to cache invalidation, web sockets responses, activity feed generation etc.


Yan Pritzker

On Wednesday, April 3, 2013 10:47:59 AM UTC-5, y...@reverb.com wrote:
Hi guys,
I liked the idea of wrapping up the callbacks in a delegator class because it doesn't pollute the controller (or other layer) with random methods. Here's a sample hexagonal refactoring I've done which reuses some business logic across a rails controller and Grape API. Would love some feedback. One thing that is not good about this still is that my business logic still hits an AR method, which will be remedied soon.


thanks,

Yan Pritzker
--

Rinaldi Fonseca

unread,
Apr 6, 2013, 1:46:30 PM4/6/13
to objects-...@googlegroups.com
Hi Kris,

I like your approach.

Guys, what's the problem with "to `subscribe` each listener each time a client " ?
I think that its ok.

Matt Wynne

unread,
Apr 6, 2013, 4:23:57 PM4/6/13
to objects-...@googlegroups.com
On 6 Apr 2013, at 17:58, Kris Leech <kris....@gmail.com> wrote:

I recently created a gem which wraps a pattern I've been using for a little while: https://github.com/krisleech/wisper

It subscribes 'listeners' to a publisher, but also allow subscribing of a block to a specific published event. This is less verbose than wrapping the controller in a decorator and passing it in as a regular subscriber.

If you scroll down to the ActionController section of the README you will see that instead of using decorating the controller I can use lambda's. I've kind of gone full circle with this as I started passing in lambda's to my service object, but got frustrated that I had to reassign instance var's for the view.

Then I tried SimpleDelegator around the controller, but:

1. Verbose, extra code, cluttering up the controller file
2. Private methods not accessible (e.g current_user)
3. You can't assign instance var's for use in the view, you must pass the var's render's local hash.

Finally with (much * 100) inspiration from others I can up with the code that allows both subscribing of either an object or a block, so I could have the best of both worlds.

- Kris.

P.S Would be interested in some feedback.

I think this looks great and I can't wait to try it out.


y...@reverb.com wrote:
Hi guys,
I liked the idea of wrapping up the callbacks in a delegator class because it doesn't pollute the controller (or other layer) with random methods. Here's a sample hexagonal refactoring I've done which reuses some business logic across a rails controller and Grape API. Would love some feedback. One thing that is not good about this still is that my business logic still hits an AR method, which will be remedied soon.

http://gist.github.com/skwp/5302250

thanks,

Yan Pritzker
reverb.com | yanpritzker.com
--
You received this message because you are subscribed to the Google Groups "Objects on Rails" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objects-on-rai...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

--
You received this message because you are subscribed to the Google Groups "Objects on Rails" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objects-on-rai...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Kevin Rutherford

unread,
Apr 7, 2013, 6:35:20 AM4/7/13
to objects-...@googlegroups.com
Hi Kris,

On Sat, Apr 6, 2013 at 5:58 PM, Kris Leech <kris....@gmail.com> wrote:
> I recently created a gem which wraps a pattern I've been using for a little
> while: https://github.com/krisleech/wisper

I use the Publisher gem https://rubygems.org/gems/publisher for the same thing.

But I often find that refactoring can push the publication part down
deeper into the middle, away from direct contact with the controller.
So I find I more often use my own EventBus gem
https://rubygems.org/gems/event_bus for notifications when I don't
care which object raises them.

Come to my talk at ScotRubyConf to hear more about this... </plug>
Cheers,
Kevin
--
http://kevinrutherford.co.uk -- software and team development
http://refactoringinruby.info -- Refactoring in Ruby, the book

David Bock

unread,
Apr 7, 2013, 9:42:24 AM4/7/13
to objects-...@googlegroups.com, objects-...@googlegroups.com
While I like the look of both whisper and publisher, is there a compelling reason to use them over ActiveSupport::Notifications?

Sent from my iPad. Please excuse any brvity, punctuation; or spalling mistakes.

Kevin Rutherford

unread,
Apr 7, 2013, 9:46:00 AM4/7/13
to objects-...@googlegroups.com
Hi David,

On Sun, Apr 7, 2013 at 2:42 PM, David Bock <bok...@gmail.com> wrote:
> While I like the look of both whisper and publisher, is there a compelling reason to use them over ActiveSupport::Notifications?

I found the ActiveSupport::Notifications API (especially the
terminology used) to be unintuitive.
And I vaguely heard somewhere that it has caching problems...

Kris Leech

unread,
Apr 7, 2013, 10:10:46 AM4/7/13
to objects-...@googlegroups.com
On Sunday, 7 April 2013 11:35:20 UTC+1, Kevin Rutherford wrote:
Hi Kris,

On Sat, Apr 6, 2013 at 5:58 PM, Kris Leech <kris....@gmail.com> wrote:
> I recently created a gem which wraps a pattern I've been using for a little
> while: https://github.com/krisleech/wisper

I use the Publisher gem https://rubygems.org/gems/publisher for the same thing.  

But I often find that refactoring can push the publication part down
deeper into the middle, away from direct contact with the controller.
So I find I more often use my own EventBus gem
https://rubygems.org/gems/event_bus for notifications when I don't
care which object raises them.

event_bus was an influence on the implimentation within wisper :)

y...@reverb.com

unread,
Apr 8, 2013, 6:36:13 PM4/8/13
to objects-...@googlegroups.com
Kris,

Thanks for the link to wisper. I refactored one of my controllers and business actions to use that gem and am enjoying it. I especially like the block syntax because it removes the need for creating self shunts in the controller (the SimpleDelegator wrappers). Great job on that.

I am still trying to figure out a good way to inject default listeners into every action (in my case, specifically an analytics listener).

A few options:
1. all actions share a base class. each listener must call super in its initializer. cons: it's easy to forget to call super
2. all actions include a base module. the module somehow taps into initialize to inject listeners immediately after the initialize call. cons: very monkeypatchy. don't even know if it's possible
3. use template method pattern - base class provides a method called "execute" which injects the default listener chain, then calls some standardized private method in the child

don't really love any of those. Any more ideas?

Yan

Kris Leech

unread,
Apr 9, 2013, 5:05:29 AM4/9/13
to objects-...@googlegroups.com
y...@reverb.com wrote:
Kris,

Thanks for the link to wisper. I refactored one of my controllers and business actions to use that gem and am enjoying it. I especially like the block syntax because it removes the need for creating self shunts in the controller (the SimpleDelegator wrappers). Great job on that.
Thanks :)


I am still trying to figure out a good way to inject default listeners into every action (in my case, specifically an analytics listener).

A few options:
1. all actions share a base class. each listener must call super in its initializer. cons: it's easy to forget to call super
2. all actions include a base module. the module somehow taps into initialize to inject listeners immediately after the initialize call. cons: very monkeypatchy. don't even know if it's possible
3. use template method pattern - base class provides a method called "execute" which injects the default listener chain, then calls some standardized private method in the child

don't really love any of those. Any more ideas?
I've been thinking about exactly the same thing last night and actually sketched up an idea in a branch: https://github.com/krisleech/wisper/commit/b4840c9653b6c80b5f4471133b9c0f109e4f27d2

Basically I allow adding of "global subscribers" to Wisper which are automatically added to every publisher. I'm not sure it the right way to go yet.

What do you think?

Yan

On Sunday, April 7, 2013 9:10:46 AM UTC-5, Kris Leech wrote:
On Sunday, 7 April 2013 11:35:20 UTC+1, Kevin Rutherford wrote:
Hi Kris,

On Sat, Apr 6, 2013 at 5:58 PM, Kris Leech <kris....@gmail.com> wrote:
> I recently created a gem which wraps a pattern I've been using for a little
> while: https://github.com/krisleech/wisper

I use the Publisher gem https://rubygems.org/gems/publisher for the same thing.  
But I often find that refactoring can push the publication part down
deeper into the middle, away from direct contact with the controller.
So I find I more often use my own EventBus gem
https://rubygems.org/gems/event_bus for notifications when I don't
care which object raises them.

event_bus was an influence on the implimentation within wisper :)
--

Arne Brasseur

unread,
Apr 9, 2013, 7:22:55 AM4/9/13
to objects-...@googlegroups.com
On 6 April 2013 18:58, Kris Leech <kris....@gmail.com> wrote:
I recently created a gem which wraps a pattern I've been using for a little while: https://github.com/krisleech/wisper

Very nice! I actually started almost the exact same thing a few months back, inspired by this thread. https://github.com/arnebrasseur/eavesdrop

The main difference is that I went for the ability to have multiple types of listeners registered to the same object. This way an object can implement multiple 'protocols'. This is certainly in part from doing Java programming in a past life where this pattern is very common. The benefit I see is to have a single place to document the messages/protocols that an object emits.

class Foo
  include Eavesdrop
  signals :bar do
    send_out :fizz
    send_out :buzz
  end
end

Foo.new.add_bar_listener( bar_listener )


 
It subscribes 'listeners' to a publisher, but also allow subscribing of a block to a specific published event. This is less verbose than wrapping the controller in a decorator and passing it in as a regular subscriber.

If you scroll down to the ActionController section of the README you will see that instead of using decorating the controller I can use lambda's. I've kind of gone full circle with this as I started passing in lambda's to my service object, but got frustrated that I had to reassign instance var's for the view.

Then I tried SimpleDelegator around the controller, but:

1. Verbose, extra code, cluttering up the controller file
2. Private methods not accessible (e.g current_user)
3. You can't assign instance var's for use in the view, you must pass the var's render's local hash.

Finally with (much * 100) inspiration from others I can up with the code that allows both subscribing of either an object or a block, so I could have the best of both worlds.

- Kris.

P.S Would be interested in some feedback.
Hi guys,
I liked the idea of wrapping up the callbacks in a delegator class because it doesn't pollute the controller (or other layer) with random methods. Here's a sample hexagonal refactoring I've done which reuses some business logic across a rails controller and Grape API. Would love some feedback. One thing that is not good about this still is that my business logic still hits an AR method, which will be remedied soon.

http://gist.github.com/skwp/5302250

thanks,

Yan Pritzker
reverb.com | yanpritzker.com
--
You received this message because you are subscribed to the Google Groups "Objects on Rails" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objects-on-rails+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
--
You received this message because you are subscribed to the Google Groups "Objects on Rails" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objects-on-rails+unsubscribe@googlegroups.com.

Matt Wynne

unread,
Apr 9, 2013, 7:52:53 AM4/9/13
to objects-...@googlegroups.com
On 9 Apr 2013, at 10:05, Kris Leech <kris....@gmail.com> wrote:

y...@reverb.com wrote:
Kris,

Thanks for the link to wisper. I refactored one of my controllers and business actions to use that gem and am enjoying it. I especially like the block syntax because it removes the need for creating self shunts in the controller (the SimpleDelegator wrappers). Great job on that.
Thanks :)

I am still trying to figure out a good way to inject default listeners into every action (in my case, specifically an analytics listener).

A few options:
1. all actions share a base class. each listener must call super in its initializer. cons: it's easy to forget to call super
2. all actions include a base module. the module somehow taps into initialize to inject listeners immediately after the initialize call. cons: very monkeypatchy. don't even know if it's possible
3. use template method pattern - base class provides a method called "execute" which injects the default listener chain, then calls some standardized private method in the child

don't really love any of those. Any more ideas?
I've been thinking about exactly the same thing last night and actually sketched up an idea in a branch: https://github.com/krisleech/wisper/commit/b4840c9653b6c80b5f4471133b9c0f109e4f27d2

Basically I allow adding of "global subscribers" to Wisper which are automatically added to every publisher. I'm not sure it the right way to go yet.

What do you think?

That sounds suspiciously like the rumblings of a DI framework to me. Tread carefully!


Yan

On Sunday, April 7, 2013 9:10:46 AM UTC-5, Kris Leech wrote:
On Sunday, 7 April 2013 11:35:20 UTC+1, Kevin Rutherford wrote:
Hi Kris,

On Sat, Apr 6, 2013 at 5:58 PM, Kris Leech <kris....@gmail.com> wrote:
> I recently created a gem which wraps a pattern I've been using for a little
> while: https://github.com/krisleech/wisper

I use the Publisher gem https://rubygems.org/gems/publisher for the same thing.  
But I often find that refactoring can push the publication part down
deeper into the middle, away from direct contact with the controller.
So I find I more often use my own EventBus gem
https://rubygems.org/gems/event_bus for notifications when I don't
care which object raises them.

event_bus was an influence on the implimentation within wisper :)
--
You received this message because you are subscribed to the Google Groups "Objects on Rails" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objects-on-rai...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


--
You received this message because you are subscribed to the Google Groups "Objects on Rails" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objects-on-rai...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Andrew Stewart

unread,
Apr 9, 2013, 8:47:48 AM4/9/13
to objects-...@googlegroups.com

On 9 Apr 2013, at 00:36, y...@reverb.com wrote:
> I am still trying to figure out a good way to inject default listeners into every action (in my case, specifically an analytics listener).
>
> A few options:
> 1. all actions share a base class. each listener must call super in its initializer. cons: it's easy to forget to call super
> 2. all actions include a base module. the module somehow taps into initialize to inject listeners immediately after the initialize call. cons: very monkeypatchy. don't even know if it's possible
> 3. use template method pattern - base class provides a method called "execute" which injects the default listener chain, then calls some standardized private method in the child

You could use a template method like this:

class Base
def execute
listeners.each do |listener|
...
end
end

def listeners
default_listeners + registered_listeners
end

def default_listeners
[ AnalyticsListener.new ]
end

def registered_listeners
...
end
end

class A < Base
def default_listeners
# empty array, or some other defaults...whatever you need
end
end

This way the subclass knows about the superclass but not vice versa. And nobody has to call a private method.

Yours,
Andy Stewart

y...@reverb.com

unread,
Apr 9, 2013, 10:29:53 AM4/9/13
to objects-...@googlegroups.com
Yes I was looking at the template method approach also. But the superclass also has to call some child method to actually "do the work" besides doing the listener invocations. The point of the base is not to do something with the listeners, it's to provide the listeners to the child.

Very roughly..(and using wisper's syntax)...

class Base 
    def self.execute 
      subclass_instance = create_a_child_instance
      subclass_instance.subscribe(default_listener_list)
      subclass_instance.do_actual_work # this is either a private method or a public one
    end 
end

class SomeChild < Base
  def do_actual_work
    #..this is where the fun happens
    publish(:some_event)
  end
end

Meh...

I like what Kris is suggesting with the GlobalListener work described here: https://github.com/krisleech/wisper/commit/b4840c9653b6c80b5f4471133b9c0f109e4f27d2

Yes it's approaching DI framework land. However we have to be realistic about real world requirements. There often is a case for having a listener on every possible invocation of your code. Specifically Analytics, Emails, and etc come to mind. Things that are really core to your business. I want to make sure these listeners are invoked no matter whether I'm running my code from a controller, console, rake task, etc. Our business is built on analytics and omitting an analytics listener is just not an option. In this sense, having globally injected defaults feels pretty good to me.

Yan Pritzker

Yan Pritzker

unread,
Apr 9, 2013, 10:34:07 AM4/9/13
to objects-...@googlegroups.com
(that could probably be simplified by not using class methods..please ignore my early morning ramblings). Kris, I would love to see that global listener branch merged.

thanks,
Yan Pritzker
reverb.com


--
You received this message because you are subscribed to a topic in the Google Groups "Objects on Rails" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/objects-on-rails/5dEvKAUmuV4/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to objects-on-rai...@googlegroups.com.

Andrew Stewart

unread,
Apr 9, 2013, 10:38:08 AM4/9/13
to objects-...@googlegroups.com
On 9 Apr 2013, at 16:29, y...@reverb.com wrote:
> Yes I was looking at the template method approach also. But the superclass also has to call some child method to actually "do the work" besides doing the listener invocations. The point of the base is not to do something with the listeners, it's to provide the listeners to the child.

But a superclass shouldn't know about its subclasses. That's what the template method is for: the superclass has a template method, e.g. `do_actual_work`, which it calls and which can be empty. The subclasses override it as necessary.

Yours,
Andy Stewart

Yan Pritzker

unread,
Apr 9, 2013, 10:40:47 AM4/9/13
to objects-...@googlegroups.com
You're right, I confused the issue by using a class level method. But still I like Kris's approach because it makes the global listener registration explicit rather than hidden in the base class.

thanks,
Yan Pritzker
reverb.com


--
You received this message because you are subscribed to a topic in the Google Groups "Objects on Rails" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/objects-on-rails/5dEvKAUmuV4/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to objects-on-rai...@googlegroups.com.

y...@reverb.com

unread,
Apr 9, 2013, 3:36:48 PM4/9/13
to objects-...@googlegroups.com
Hey Kris - I posted an issue on Wisper on github. Unless I'm missing something, I am finding the wispered code very difficult to test, because it's hard to tap into the objects with the blocks


Any commentary from people on this thread appreciated, as this is relevant to more hexagonal style and taking logic out of controllers.

Yan Pritzker

Kris Leech

unread,
Apr 9, 2013, 4:40:01 PM4/9/13
to objects-...@googlegroups.com


On Tuesday, 9 April 2013 15:29:53 UTC+1, y...@reverb.com wrote:
Yes I was looking at the template method approach also. But the superclass also has to call some child method to actually "do the work" besides doing the listener invocations. The point of the base is not to do something with the listeners, it's to provide the listeners to the child.

Very roughly..(and using wisper's syntax)...

class Base 
    def self.execute 
      subclass_instance = create_a_child_instance
      subclass_instance.subscribe(default_listener_list)
      subclass_instance.do_actual_work # this is either a private method or a public one
    end 
end

class SomeChild < Base
  def do_actual_work
    #..this is where the fun happens
    publish(:some_event)
  end
end

Meh...

I like what Kris is suggesting with the GlobalListener work described here: https://github.com/krisleech/wisper/commit/b4840c9653b6c80b5f4471133b9c0f109e4f27d2

Yes it's approaching DI framework land. However we have to be realistic about real world requirements. There often is a case for having a listener on every possible invocation of your code. Specifically Analytics, Emails, and etc come to mind. Things that are really core to your business. I want to make sure these listeners are invoked no matter whether I'm running my code from a controller, console, rake task, etc. Our business is built on analytics and omitting an analytics listener is just not an option. In this sense, having globally injected defaults feels pretty good to me.

My use cases for global listeners are Analytics and Activty Stream. These two listeners are added to all publishers and its a bit boring to keep doing `command.subscribe(ActivityListener.new)`. The flip side is you might forget to add a method for every event to the global listeners. As a safety net I could see making these listeners `respond_to?` always return true when under test, thus a missing method for an event would raise NoMethodError. Bit hacky, but might work. 

I don't know the specifics of what a DI framework is, other than it sounds like some way to ask for the correct class to use. Global listeners wouldn't be anything like that. Maybe global listeners isn't the right name...

Kris Leech

unread,
Apr 9, 2013, 4:54:13 PM4/9/13
to objects-...@googlegroups.com


On Tuesday, 9 April 2013 15:34:07 UTC+1, Yan Pritzker wrote:
(that could probably be simplified by not using class methods..please ignore my early morning ramblings). Kris, I would love to see that global listener branch merged.

I need this myself, so I will take a look tomorrow, I think I just need decide on the API and tidy up the specs. I also need to see what happens in Rails development environment, with class reloading. It should be okay because Wisper is a gem and the global listeners can be added in an initializer.

The branch has an API for adding global listeners which looks like this:

Wisper::GlobalRegistrations.add_listener(ActivityStream.new) 

^ It needs renaming to GlobalListeners, registrations is an internal thing really. Unless there is a better suggestion.

I wish I could do Wisper.add_listener, but Wisper is the module which is mixed in to the publisher, so it would end up adding the method for adding global listeners to all publishers. Really I should have had Wisper::Publisher as the mixin. Maybe that could something earmarked for v2 (codename Hindsight).

Kris Leech

unread,
Apr 9, 2013, 4:57:34 PM4/9/13
to objects-...@googlegroups.com
On Tuesday, 9 April 2013 21:54:13 UTC+1, Kris Leech wrote:

On Tuesday, 9 April 2013 15:34:07 UTC+1, Yan Pritzker wrote:
(that could probably be simplified by not using class methods..please ignore my early morning ramblings). Kris, I would love to see that global listener branch merged.

I need this myself, so I will take a look tomorrow, I think I just need decide on the API and tidy up the specs. I also need to see what happens in Rails development environment, with class reloading. It should be okay because Wisper is a gem and the global listeners can be added in an initializer.

The branch has an API for adding global listeners which looks like this:

Wisper::GlobalRegistrations.add_listener(ActivityStream.new) 

^ It needs renaming to GlobalListeners, registrations is an internal thing really. Unless there is a better suggestion.

Ha - I already did the rename to GlobalListeners before commiting the other night. 

Kris Leech

unread,
Apr 9, 2013, 6:15:18 PM4/9/13
to objects-...@googlegroups.com


On Tuesday, 9 April 2013 20:36:48 UTC+1, y...@reverb.com wrote:
Hey Kris - I posted an issue on Wisper on github. Unless I'm missing something, I am finding the wispered code very difficult to test, because it's hard to tap into the objects with the blocks


It looks like testing redirect/render/flash in the blocks is okay, but its hard to inject a 'service' object double which publishes canned events to the listening controller:

request/params => controller => execute service => publish event => controller

If I'm right we don't want to execute the real code in the service because thats already tested, we want a double to publish an event back to the controller (listener) and test the controller does the right thing. So less integration, more isolation.

Yan Pritzker

unread,
Apr 9, 2013, 6:47:54 PM4/9/13
to objects-...@googlegroups.com
Yes exactly right - I commented as well on the github issue. Let's keep the conversation there, as it's probably spamming people who don't care about Wisper to discuss it here ;)

I believe we should not be testing business logic at the controller layer if we're truly hexagonal. The controller only knows how to render templates or redirect us based on events it receives.

thanks,
Yan Pritzker
reverb.com


You received this message because you are subscribed to a topic in the Google Groups "Objects on Rails" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/objects-on-rails/5dEvKAUmuV4/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to objects-on-rai...@googlegroups.com.

Matt Wynne

unread,
Apr 9, 2013, 7:01:55 PM4/9/13
to objects-...@googlegroups.com
On 9 Apr 2013, at 21:40, Kris Leech <kris....@gmail.com> wrote:



On Tuesday, 9 April 2013 15:29:53 UTC+1, y...@reverb.com wrote:
Yes I was looking at the template method approach also. But the superclass also has to call some child method to actually "do the work" besides doing the listener invocations. The point of the base is not to do something with the listeners, it's to provide the listeners to the child.

Very roughly..(and using wisper's syntax)...

class Base 
    def self.execute 
      subclass_instance = create_a_child_instance
      subclass_instance.subscribe(default_listener_list)
      subclass_instance.do_actual_work # this is either a private method or a public one
    end 
end

class SomeChild < Base
  def do_actual_work
    #..this is where the fun happens
    publish(:some_event)
  end
end

Meh...

I like what Kris is suggesting with the GlobalListener work described here: https://github.com/krisleech/wisper/commit/b4840c9653b6c80b5f4471133b9c0f109e4f27d2

Yes it's approaching DI framework land. However we have to be realistic about real world requirements. There often is a case for having a listener on every possible invocation of your code. Specifically Analytics, Emails, and etc come to mind. Things that are really core to your business. I want to make sure these listeners are invoked no matter whether I'm running my code from a controller, console, rake task, etc. Our business is built on analytics and omitting an analytics listener is just not an option. In this sense, having globally injected defaults feels pretty good to me.

My use cases for global listeners are Analytics and Activty Stream. These two listeners are added to all publishers and its a bit boring to keep doing `command.subscribe(ActivityListener.new)`. The flip side is you might forget to add a method for every event to the global listeners. As a safety net I could see making these listeners `respond_to?` always return true when under test, thus a missing method for an event would raise NoMethodError. Bit hacky, but might work. 

I don't know the specifics of what a DI framework is, other than it sounds like some way to ask for the correct class to use. Global listeners wouldn't be anything like that. Maybe global listeners isn't the right name...

The danger of a DI framework is that the setup of the dependencies - in this case your Analytics or Activity Stream listeners - is done somewhere off to the side. That means that if I read the code, I'm unlikely to realise that the ActivityListener is being invoked.

What I'd suggest is that if you feel the need to have a domain concept that always includes an activity listener, then either have that object add the activity listener itself as it's constructed, or wrap it in a higher level concept that does that for you, and then use that higher-level concept everywhere instead. That way, you'll have code that anyone can read and understand what's going on.

--
You received this message because you are subscribed to the Google Groups "Objects on Rails" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objects-on-rai...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Kris Leech

unread,
Apr 10, 2013, 4:36:18 AM4/10/13
to objects-...@googlegroups.com


On Wednesday, 10 April 2013 00:01:55 UTC+1, Matt Wynne wrote:

On 9 Apr 2013, at 21:40, Kris Leech <kris....@gmail.com> wrote:
On Tuesday, 9 April 2013 15:29:53 UTC+1, y...@reverb.com wrote: 
Yes it's approaching DI framework land. However we have to be realistic about real world requirements. There often is a case for having a listener on every possible invocation of your code. Specifically Analytics, Emails, and etc come to mind. Things that are really core to your business. I want to make sure these listeners are invoked no matter whether I'm running my code from a controller, console, rake task, etc. Our business is built on analytics and omitting an analytics listener is just not an option. In this sense, having globally injected defaults feels pretty good to me.

My use cases for global listeners are Analytics and Activty Stream. These two listeners are added to all publishers and its a bit boring to keep doing `command.subscribe(ActivityListener.new)`. The flip side is you might forget to add a method for every event to the global listeners. As a safety net I could see making these listeners `respond_to?` always return true when under test, thus a missing method for an event would raise NoMethodError. Bit hacky, but might work. 

I don't know the specifics of what a DI framework is, other than it sounds like some way to ask for the correct class to use. Global listeners wouldn't be anything like that. Maybe global listeners isn't the right name...

The danger of a DI framework is that the setup of the dependencies - in this case your Analytics or Activity Stream listeners - is done somewhere off to the side. That means that if I read the code, I'm unlikely to realise that the ActivityListener is being invoked.

Thanks for clarifying, good point.


What I'd suggest is that if you feel the need to have a domain concept that always includes an activity listener, then either have that object add the activity listener itself as it's constructed, or wrap it in a higher level concept that does that for you, and then use that higher-level concept everywhere instead. That way, you'll have code that anyone can read and understand what's going on.

So, if I understand, something like:

# add a global listeners like ActivityListener
command = SubscribeDefaultListeners.new(MyCommand.new)) 

# continue as normal...
 

Matt Wynne

unread,
Apr 10, 2013, 5:18:16 AM4/10/13
to objects-...@googlegroups.com
Ah OK, so you want to add this to every single one of your commands. *ponders*

This feels more like a factory than, so I'd probably find it easier to understand a factory than a wrapper.

You could either do it with an object (probably overkill):

command = CommandFactory.new.build(MyCommand)

or just put a factory method somewhere useful:

command = Command.build(MyCommand)

or

command = Command.build_with_listeners(MyCommand)

or

command = MyCommand.new_with_listeners

etc.

Yan Pritzker

unread,
Apr 10, 2013, 9:36:01 AM4/10/13
to objects-...@googlegroups.com
But that puts the onus on every caller to remember to use the factory with the default listeners rather than invoking the command directly. I'm not sure we've won anything. 

Yes - decoupling the global listeners thing has the danger of hiding code and making things magical, but I think the alternative (having every caller aware that default listeners exist) feels too easy to screw up for the callers.

My pattern so far has been to use a Base publisher that has all the default listeners declared. This feels like a happy medium - if you were to inspect the code of any publisher, you can see the base extension, and reading the base code you can see all the default listeners.

thanks,
Yan Pritzker
reverb.com


--
You received this message because you are subscribed to a topic in the Google Groups "Objects on Rails" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/objects-on-rails/5dEvKAUmuV4/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to objects-on-rai...@googlegroups.com.

Matt Wynne

unread,
Apr 10, 2013, 10:17:29 AM4/10/13
to objects-...@googlegroups.com
On 10 Apr 2013, at 14:36, Yan Pritzker <y...@reverb.com> wrote:

But that puts the onus on every caller to remember to use the factory with the default listeners rather than invoking the command directly. I'm not sure we've won anything. 

Yes - decoupling the global listeners thing has the danger of hiding code and making things magical, but I think the alternative (having every caller aware that default listeners exist) feels too easy to screw up for the callers.

In that case, you might be able to do something with this, and just change the behaviour of Command.new

You received this message because you are subscribed to the Google Groups "Objects on Rails" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objects-on-rai...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
 
 

Kris Leech

unread,
Jul 5, 2013, 4:37:52 AM7/5/13
to objects-...@googlegroups.com
For anyone interested I have an article [1] published on RubySource
about using Hexaganol Rails style callbacks. Any feedback here or in the
comments would be greatly welcome.

[1] http://rubysource.com/using-wisper-to-decompose-applications/

Neal Clark

unread,
Jul 5, 2013, 4:55:40 AM7/5/13
to objects-...@googlegroups.com, objects-...@googlegroups.com
If sounds like you're doing too much, too soon. Matt had a great blog post recently about caching.... Hexagonal rails blog posts/books notwithstanding, can you be more specific about the problems your app is facing ?
​ 


On Sun, Nov 25, 2012 at 12:17 AM, Nikolay Sturm <goo...@erisiandiscord.de> wrote:

Hi,

I tried to apply the ideas of callbacks as described by Matt Wynne in
his "Hexagonal Rails" talks and Kevin Rutherford in his blog
(http://silkandspinach.net/2012/07/06/hexagonal-rails-hiding-the-finders/)
to some code at work for error handling.

The code has several layers (controller -> service -> use case object ->
ActiveRecord model). It is written with exceptions, which the controller
catches to render an error page. At points it felt as going in the
direction of using exceptions for flow control, which is the reason I
tried callbacks.

Callbacks, however didn't work any better IMHO. The code was littered
with failure callbacks and had I added success callbacks as well,
program flow would have been totally obfuscated.

I am wondering if I misunderstood something or if I just happened to use
callbacks in the wrong context.

cheers,

Nikolay

--
"It's all part of my Can't-Do approach to life." Wally


Reply all
Reply to author
Forward
0 new messages