On Rails 5, Presenters And Form Objects.

My original plan to not blog about conceptual problems in Rails for the next months has failed.

Too many discussions about presenters, decorators, object-oriented helpers and “oh-so-awesome-and-new” form objects I had to overhear. With Aaron’s great keynote and many comments on the aforementioned concepts, I feel the urge to clarify what’s a presenter, a view model, and a form object.

Presenters

I completely agree with tenderlove when he says there doesn’t have to be a presenter library. Presenters (or decorators?) are usually composition objects that add presentation logic to attributes.

However, what many people ask for is the ability to map widgets, or fragments, or parts of their UI, to something in Rails. And this something has turned out to be a mix of controller code, before_filter, partials and helpers. And many people are not happy with this, as their widget is not encapsulated and not reusable across their app.

To summarize: what people want in order to implement widgets is

  • A place in the file system for code and templates.
  • An asset where to put that Ruby code.
  • The ability to render partials in order to present their widget.

Especially the latter one is important and is what makes the difference between presenters and widgets: I want to render templates in order to present an arbitrary object in my UI. And I don’t want to hack ActionView in any way to achieve that.

View Models

This was my motivation to write Cells a good while ago. Instead of cluttering widget logic across the entire framework, there’s a new abstraction layer to solve this. It gives you a view model class where you put presentation logic, but it also lets you render templates.

However, these are not global templates but views that sit in an encapsulated directory, just like the view model’s code is isolated in the cell and doesn’t have global access. Likewise, JavaScript and CSS code can be bundled with the cell. This makes a cell reusable across many controllers, or even apps.

app
├── cells
│   ├── comment
│   │   ├── cell.rb
│   │   ├── show.haml
│   │   ├── grid.haml
│   │   ├── comment.coffee

I am not gonna argue any more whether or not you need Cells. Some people like it, some prefer using POROs and hack Rails’ rendering into that object to achieve what Cells does.

My point is that Cells view models give developers a defined structure and standard how to implement view fragments (not to speak about how Cells handles view inheritance, polymorphic views, caching, and more, hahahaha).

So next time you talk about presenters, ask yourself: Am I talking about a strictly attributes-decorating thing? Then that’s a decorator. As soon as this involves rendering of views, you might want to checkout view models.

Form Objects

The other thing I need to clarify is form objects.

We all know that the way validations are handled in Rails models is a mess. It breaks down as soon as you need to use a model in two different contexts, for two different forms. Everyone reading this blog post has felt the pain with accepts_nested_attributes, which is supposed to handle deserialization of nested forms.

And this brings me to the point of this section. One job of a form object is validating an object graph (e.g. an album composed of songs with artists) and collect validation errors in the top object.

The other job is the deserialization of the incoming hash. And this is completely underestimated by Rails core. Deserialization is the actual problem of forms. Validating and bubbling up errors is easy.

How do I parse a hash into an object? Where do I attach this object? Do I create a new object for that hash fragment, or do I need to find an associated object in the database? How do I handle additional semantics like deleting objects and save? And, how do I prevent the persistence layer to get involved until validation is done?

Reform

This is the real issue a form object (the way we expect it) has to solve. Again, I’d like to point you to another gem called Reform. In Reform, a separate class takes care of that. You define validations and properties in a new class.

class AlbumForm < Reform::Form
  property :name
  validates :name, presence: true

  collection :songs do
    property :title
    validates :title, presence: true
  end
end

Deserialization and validation are done with separated entities. While a representer internally takes care of deserializing the incoming hash, validation is handled by the form itself. Usually, you don’t have to worry about this as it happens automatically.

The upcoming Reform 2.0 is doing this in a very neat way, where you can use your Roar representer for parsing, and the Reform object for validation, making it reusable for both document APIs and UI forms. It’s possible to completely replace deserialization with your own code without losing the decoupling from the persistence that Reform gives you.

This is the result of years of work, running into problems, taking a step back, reconsidering, collecting feedback from hundreds of use cases, and so on.

Please don’t brand a form object as a validation, only. There’s more to it to solve the actual problem we have.

And, yes, ActiveForm started as a pure copy of Reform, and then got “re-implemented”. Let’s not fight.

Rails 5: Stillstand

One last thing: Rails 5 comes with a new “render anywhere” feature where ApplicationController.render lets you render partials from virtually everywhere. While this might look startling first, this is the exact wrong way to go.

A globally accessable renderer is the lowest-level tool you can give a developer. Instead of providing new, object-oriented, abstraction layers to solve problems, Rails core resists the idea of introducing new concepts for the sake of Basecamp-compati.. sorry, backward-compatibility.

The result will be render calls from models, hundreds of different “presenter” implementations across Rails projects, and confused people who don’t know where to put their code.

Conclusion

I hope I managed to point out what’s a presenter, a view model and a form object.

My message is: There is gems to help you solving a lot of problems that have been around since Rails’ inception. These solutions are mature and used in thousands of production apps. Many people have put a lot of work into them.

The fact that Rails core now, after almost 10 years, slowly starts to pick up ideas like form objects, is a good sign. However, I am skeptical if view models and real form objects will ever make it into Rails core. Luckily, we got gems to fill the gap.

8 thoughts on “On Rails 5, Presenters And Form Objects.

  1. As someone said: You can use Rails, but don’t marry to it.
    It’s just a gem like many other gems. Use whatever features you like, and NOT use those you don’t like. Then find a better gem for whatever you are missing.

    Finally you might start writing on your own gem, even it’s a small one (actually I like small gems much more).

    Like

  2. Great article to succinctly clarify the differences and needs. And I agree with PikachuEXE that Rails is a great gem and can be bundled with other great gems like yours to create a nicely architectured system.

    On a side note, it’s troubling that Rails core hasn’t embraced encapsulation. And by “troubling” I also mean “embarrassing.” Like Apple, they dig the hole a little deeper by saying, “Rails doesn’t need this. Just follow the Rails Way and for anyone who struggles, well — you must be Doing It Wrong.” Over a decade of development and the view layer hasn’t seen much architectural refactor love.

    Like

  3. In all of the projects that I’ve been involved with, the Rails’ view layer was a total mess.
    A lot of templates, partials, duplicated HTML, tons of CSS and Javascript all over the place.
    When a more sophisticated AJAX update was required then ad-hoc hacks were put in place and in the end
    changing the visual part of the application became a slow and painful process.

    I’ve looked into different Javascript frameworks and desktop UI toolkits and what I see in all of them
    is that they manage the UI by composition of widgets and basically every sane UI you can think of is represented
    as tree of objects. Some call that Views, some UI components or Widgets, but in the end we are speaking about
    encapsulated, reusable objects.

    The biggest issue I had in the Rails projects where you manage your UI layer in Rails is that there is no sane encapsulation,
    there is nothing that can represent an isolated part of the UI. And no presenter library solves that, because we are talking about
    isolating the Views(parts that will encapsulate the HTML partials), not the Models.

    For me the only sane alternative is to explore and use Cells or go totally for client side framework, but if you have written
    a lot in Ruby and your interface is complex in terms of elements, but doesn’t require real time responsiveness, then it’s
    perfect to re-factor using ViewModels as you call them. This is what we are moving towards in my current project.

    Like

  4. Rails 5.0.1 is out and use of presenters/decorators still haven’t been codified, either as a convention, guideline or part of the framework. Re: your definition of presenters, I think that , still, this concept represents a decorator that adds new responsibilities related to presenting the model (methods that format certain model attributes, like created_at, or combine several attributes). It then overlaps with the concept of Serializer, where a developer can define custom methods for model attributes formatting.

    So, looks like there’s a space for improvement and I think that the >>easiest<< way to improve it (slightly) is to encourage Rails developers to move attribute-formatting methods (anything like created_at.strftime… , "$#{price}…" etc.) to app/presenters, find out how to use those presenters in app/serializers and app/views in most convenient way.

    Like

      1. Congrats! I think that what’s feasible in a reasonable timeframe is baby steps, wide and standardized adoption of presenters and forms in the community (or best, framework). Cells might seem like a revolution to most of the Rails devs out there. Just a thought.

        Like

Leave a reply to Kevin Triplett Cancel reply