Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I've had great success building form objects with portrayal (https://github.com/scottscheapflights/portrayal) and ActiveModel. You can put validations into them, and auto-filter the params by making a `from_params(params)`-type constructor. In it you can call require/permit with `portrayal.keywords`. You're right that it'd be nice to have this out of the box, but this is just a few lines of glue code to setup. I'm in the process of making a new app template that does that.


This gem is just for construction, no validation ?

Schema layer purpose is to enforce invariant at the boundary of HTTP.


The gem is just for declaring struct keywords (slightly more featureful than vanilla structs). Validation is left up to ActiveModel::Model. I would do a setup like this:

    class ApplicationForm
      extend Portrayal
      include ActiveModel::Model

      class << self
        def from_params(params)
          new(**filter_params(params))
        end

        def filter_params(params)
          params
            .require(model_name.param_key)
            .permit(*portrayal.keywords)
            .to_hash
            .transform_keys(&:to_sym)
        end
      end
    end

    class MyForm < ApplicationForm
      keyword :first_name
      keyword :last_name

      validates :first_name, :last_name, presence: true
    end
Can use it in a controller action like

    form = MyForm.from_params(params)
    if form.valid?…
      # the usual
One of the good things about this approach is that you can also use this object in form_for/form_with in your views.


Have you looked at ActiveModel::Attributes ? That coupled with ActiveModel::Model makes for simple form objects quite nicely. Also, you can write your _own_ ActiveModel::Type so you can require that a particular attribute is a particular (business) model. I've been finding it a very nice way to write form objects.

It's a bit dated now, but, I did a short presentation for my local ruby meetup and the slides are here [1]

1. https://slides.com/patrickdavey/rails-5-2-attributes-api#/25


Thanks for the suggestion. I actually explored switching to Attributes for a new Rails app, but 3 reasons made me come back to portrayal gem: 1) attributes don't support defaults. 2) I prefer not to set types on form object attributes, since they are supposed to deal with messy user input. Instead I lean on validations. And 3) I like read-onlyness, freeze, equality, duplication, and introspection features of portrayal gem.


Attributes do support defaults. You can absolutely do:

   attribute :my_time_at, :datetime, default: -> { Time.now }
   attribute :persisted, :boolean, default: false

What I like about the types is that it will automatically coerce the messy user data automatically (this is what AR is doing under the hood anyway right?), you can just be more intentional about it.

Anyway, glad you're happy with portrayal :) I definitely agree with you on the "freeze, read-onlyness" etc. being good things to have!


Ah, I accidentally lied by trying to reconstruct a vague memory, apologies. Now I'm remembering it in more detail.

It wasn't lack of defaults, it was handling of defaults. Portrayal does a couple of smart things like evaluating defaults in a specific order in the correct context (while still only evaluating once), such that this becomes possible:

    keyword :name
    keyword :greeting, default: proc { "Hello, #{name}" }
I like being able to do this. (This also works gracefully with subclassing.)

The coercion argument is good, but I'm not a fan of doing it implicitly. (This was the real counter-argument I should've made). I prefer to have a single place where input is entirely processed, such as a `from_params(params)`-style constructor. In that constructor you could either coerce, or do anything else with the input prior to passing it through to `.new`.


That said, there are similar objects one can make without using the "form" moniker, for API submissions for example. Granted, I also wish a solution was built-in.




Consider applying for YC's Winter 2027 batch! Applications are open till November 2.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: