Live data from Hacker News

Rails – The Missing Parts

eng.joingrouper.com

51–60 of 173 posts

Re: Rails – The Missing Parts

#51
post #47
post #39

The proof is always in the pudding. While there are good and reasonable times to introduce "interactors", this particular example is poor. The tests presented are anemic, and the code is absolutely not any clearer by being extracted and wrapped. I would indeed have kept all this in the controller. The key point for an "interactor" extraction is imo when you have multiple models being created in symphony, like a Signu…

I rewrote the code in this example to use the "Beginner's Version" of Rails ( sigh ). You judge which you like better: https://gist.github.com/dhh/9333694

I've spent the last few months re-writing a medium-sized code-base (several hundred thousand LoC) that looks like your version into code that looks like the blog version. Test suite run time has dramatically decreased, code is more loosely coupled and we see far fewer bugs.

> You obviously need to choose the patterns that fit the problem you’re trying to solve – it’s rarely one-size-fits-all, and some of these principles may be overkill in a very simple 15-minute blog application. Our objection is that Rails feels like it’s a Beginners’ Version, often suggesting that one size does fit all

This quote from the post (minus the "Beginners" jibe - sorry!) is my core objection. Your example works fine if you're working with a simple application. But the "Rails Way" feels like it starts to fall over when you get to medium or large codebases.

I feel like Rails could benefit hugely from a few core "Advanced" features that help you grow a small application into a larger, functioning business. Sure, you can add them yourself, but then you lose the advantage of shared standards and strong architectural conventions. Every new developer we hire has to learn what exactly we mean by "Interactors" or "Decorators" or "Policies".

Re: Rails – The Missing Parts

#52
post #47
post #39

The proof is always in the pudding. While there are good and reasonable times to introduce "interactors", this particular example is poor. The tests presented are anemic, and the code is absolutely not any clearer by being extracted and wrapped. I would indeed have kept all this in the controller. The key point for an "interactor" extraction is imo when you have multiple models being created in symphony, like a Signu…

I rewrote the code in this example to use the "Beginner's Version" of Rails ( sigh ). You judge which you like better: https://gist.github.com/dhh/9333694

Here's another version that doesn't even use private methods in the controller and uses a PORO for the email grouping: https://gist.github.com/dhh/9333991

Re: Rails – The Missing Parts

#53
post #48

Earlier quoted context omitted.

I agree with you - Interactors are particularly useful when you're creating multiple models, and the example could have been better. But they're useful for dealing with side-effects of a particular operation too (sending multiple emails, notifying admins). Much better than ActiveRecord callbacks. If you put this logic in the controller, what happens when you want a separate API controller that does the same thing? Or…

Coulda, woulda, damn shoulda. You're future coding with your "what ifs". Most controller actions are not reused. The time to extract for reuse is when you need to reuse. Not crystal balling about that you probably will be in the future. Because when the reuse case actually arrives, you might find that you need to reuse some, but not all of the action. If you're literally doing the exact same thing for both web and ap…

Sure, I think we're saying the same thing here.

This example is from a real-life codebase, and the interactor was extracted from 3 or 4 (bloated, repetitive) controllers. Perhaps this could have been made clearer in the post.

I'm not arguing at all that every controller needs an Interactor, or all Rails codebases should start out with Interactors present.

I am saying that Interactors are very useful concepts for re-using code, and that many medium and large Rails codebases could benefit from them.

Re: Rails – The Missing Parts

#54
post #47

Earlier quoted context omitted.

I rewrote the code in this example to use the "Beginner's Version" of Rails ( sigh ). You judge which you like better: https://gist.github.com/dhh/9333694

I've spent the last few months re-writing a medium-sized code-base (several hundred thousand LoC) that looks like your version into code that looks like the blog version. Test suite run time has dramatically decreased, code is more loosely coupled and we see far fewer bugs. > You obviously need to choose the patterns that fit the problem you’re trying to solve – it’s rarely one-size-fits-all, and some of these princi…

Yes, rewriting a system after it's already settled and designed can indeed bring a cleaner code base about. But don't confuse that with "better architecture".

Don't even get me started on the hand-wavy "loosely coupled".

If this is a poor example, pick a good example. I'll be happy to code ping pong you whatever example you choose.

One way to spend hundreds of thousands of LOC on an application is to stuff it with needless abstractions. That doesn't make it "advanced", and it's not Rails that's falling over, it's probably just some shitty code.

Maybe that's a clue that your chosen paradigms weren't that helpful. Or rather, that they convoluted the code base instead of clarified it. It's certainly a red herring that you need to teach your abstractions to new developers and that it's an endeavor to do so.

POROs are great, though. Our app/models is full of them. We even added app/services too. The trouble I have is with people who, like you, fall in love with the flawed notion that their application is such a special snowflake that it deserves "advanced techniques". Bullshit. There are good techniques and bad techniques. Size of the application is rarely a factor, except in ballooning the ego of the programmer.

Anyway, the invitation stands. Present any piece of real code and we can ping pong on it. I enjoy talking about specifics and improving real code. I detest "oh that was just a poor example, but the general principle is..." kind of debates. If you can't produce a good example, you don't have a general principle.

Re: Rails – The Missing Parts

#55
post #47

Earlier quoted context omitted.

I rewrote the code in this example to use the "Beginner's Version" of Rails ( sigh ). You judge which you like better: https://gist.github.com/dhh/9333694

I've spent the last few months re-writing a medium-sized code-base (several hundred thousand LoC) that looks like your version into code that looks like the blog version. Test suite run time has dramatically decreased, code is more loosely coupled and we see far fewer bugs. > You obviously need to choose the patterns that fit the problem you’re trying to solve – it’s rarely one-size-fits-all, and some of these princi…

I agree 100 percent with the statements in the first two paragraphs on "Part 1 Inflectors". I have first hand experience with every statement you made from testing to managing the callback dependency mayhem. This post seems interesting to me mainly for the test suite benefits alone, being able to maintain independency in the test-suite base would definately save lots of bloated stubbing and/or db hits. Im wondering what kind of time improvements the suite got?

Re: Rails – The Missing Parts

#56
post #52
post #47

Earlier quoted context omitted.

I rewrote the code in this example to use the "Beginner's Version" of Rails ( sigh ). You judge which you like better: https://gist.github.com/dhh/9333694

Here's another version that doesn't even use private methods in the controller and uses a PORO for the email grouping: https://gist.github.com/dhh/9333991

Thanks for writing this. While I often look for ways to take external-interaction-responsibility away from Rails objects, the OP didn't really make sense to me, at least in terms of saving time and making things more logical.

However, there's a distinction that has to be made in your example and the OP's. In the OP, the failure of the Interactor, including the delivery of emails, would cause the controller to enter the "failure" branch:

    if interactor.success?
      redirect_to home_path
    else
      flash[:error] = interactor.message
      render :new
    end

Whereas your example, the controller would take the successful branch if the data model was saved, regardless of whether email delivery failed:

    if @grouper.save
      ConfirmedGrouperEmails.new(@grouper).deliver
      AssignBarForGrouper.enqueue(@grouper.id)
 
      redirect_to home_path
    else
      render :new
    end


So we're not comparing apples to apples here. However, as a layperson, I'd have to agree with you: Why should the error be raised to the grouper-creating user, when it should be going to the part of the system that handles mailing? But maybe the actual details are more complicated than that...

Re: Rails – The Missing Parts

#58
post #54

Earlier quoted context omitted.

I've spent the last few months re-writing a medium-sized code-base (several hundred thousand LoC) that looks like your version into code that looks like the blog version. Test suite run time has dramatically decreased, code is more loosely coupled and we see far fewer bugs. > You obviously need to choose the patterns that fit the problem you’re trying to solve – it’s rarely one-size-fits-all, and some of these princi…

Yes, rewriting a system after it's already settled and designed can indeed bring a cleaner code base about. But don't confuse that with "better architecture". Don't even get me started on the hand-wavy "loosely coupled". If this is a poor example, pick a good example. I'll be happy to code ping pong you whatever example you choose. One way to spend hundreds of thousands of LOC on an application is to stuff it with ne…

You can't possibly think that there's never a use case for the Interactor pattern. Please watch Uncle Bob's talk: http://www.confreaks.com/videos/759-rubymidwest2011-keynote-...

Re: Rails – The Missing Parts

#59
post #10

I've been looking into incorporating this pattern into my larger Rails apps as well. Another benefits of interactions is DRYing up your code for use in APIs. Some of the more popular interactor gems: https://github.com/orgsync/active_interaction https://github.com/cypriss/mutations

Mutations is a great gem. I've used in my latest project and it has been an exceptionally useful tool for managing complexity.

Re: Rails – The Missing Parts

#60
post #14

One thing I can't figure out is whether to place my service objects under `/app/models` or under `/lib`. There doesn't seem to be a clear consensus on this? I tend more towards the former, because autoload works better during development. Also, I consider them part of my domain model. What do you do?

I use /app/interactors or /use_cases (per some Uncle Bob talk about architecture). I don't consider them models in the 'Data Model' sense so I don't put them in /app/models
Post reply on HN