Reform 1.1 Brings More Reusability!

The new Reform 1.1 release brings several nice improvements and lots of fixes to the form object gem. This release is just a transitional one, the 2.0 version is gonna move huge chunks of code into a separate gem. Let me talk about this in a minute.

Non-Intrusive Validation.

Reform comes with a mechanism to populate your object graph by deserialising the incoming form data to objects.

class AlbumForm < Reform::Form
  collection :songs, populate_if_empty: Song do
    property :title
  end
end

This would create Song instances where they were missing in the album.

AlbumForm.new(album).validate(
  "songs" => [
    {"title": "Eat Your Words"}
  ]
)

In former versions of Reform, just by calling #validate Reform would already attach those new songs to the album model – by pushing them via songs[]=.

This created confusion. And is different now. The built-in populators do not touch the model at all. This only happens when saving or syncing the form.

Coercion Simplified.

Reform allows you to use virtus coercion.

class AlbumForm < Reform::Form
  property :released_at, type: DateTime
end

From 1.1 onwards, coercion only happens in #validate – any other API method will not trigger coercion. This makes the workflow predictable as we had problems with coercion happening where it shouldn’t.

Manual Coercion.

You can also implement your own filtering by overriding the setter for a property.

class AlbumForm < Reform::Form
  property :released_at
 
  def released_at=(v)
    super DateTime.parse(v)
  end
end

Note that you can use super to call the original setter but still provide your own code for filtering or sanitising or whatever fun stuff you’re after.

Overriding In Nested Forms.

This brings me to the next improvement. The above example only worked for top-level properties. That sucked and is fixed now, allowing you stuff as follows.

class AlbumForm < Reform::Form
  collection :songs do
    property :written_at

    def written_at=(v)
      super DateTime.parse(v)
    end
  end
end

Methods defined in nested forms actually extend that nested form class – as it should be!

Forms In Modules.

This is my favourite improvement as it maximises the reusability of forms: you can now define forms along with properties, validations and accessors/helpers in modules!

module SongsForm
  include Reform::Form::Module

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

This module can now be included into real forms.

class AlbumForm < Reform::Form
  property :title

  include SongsForm
end

That should help to keep forms DRY, as it is a common pattern to have several different forms for the same model with lots of shared functionality.

Inheritance Improved.

Reform 1.1 uses representable 2.0 internally for all kinds of mappings and declarations. This actually allows you to “fine-tune” forms and overriding or extending properties that were inherited.

class AlbumForm < Reform::Form
  property :title

  include SongsForm

  collection :songs, inherit: true do
    property :artist
  end
end

This will extend the existing, inherited songs form and add the artist property. Read the docs for in-depth information about this or sign up for my upcoming book which discusses this pattern in detail.

Validations From Models.

While Reform/Trailblazer encourages you to have empty models that only configure your persistence layer, Reform now allows copying validations from models. This way you can quickly set up forms without having to write redundant validations.

class SongForm < Reform::Form
  property :title

  extend ActiveModel::ModelValidations
  copy_validations_from Song
end

Thanks to Cameron Martin for his excellent work.

Deserialise JSON.

A simple change in Reform now allows forms to deserialise JSON, XML and YAML besides the original support for hashes. While this might sound weird, this little improvement actually allows forms to become part of your HTTP API.

This is an integral part of Trailblazer: Here, every domain action is encapsulated in a so called “Operation” which internally uses a form object to deserialise incoming data, setup the object graph and validate the application state.

By making forms do JSON and friends, too, they can be used for normal web forms, console/model API and HTTP APIs, which is pretty awesome.

Again, this is all in my book but you can have a sneak peek in the Traiblazer example app gemgem – thanks to @GoGoGarrett for that name, BTW 🙂

class AlbumForm < Reform::Form
  representer_class.send :include, Representable::JSON

  def deserialize_method
    :from_json
  end

  property :title

  include SongsForm

  collection :songs, inherit: true do
    property :artist
  end
end

This rather clumsy extension (to be improved!) allows to call #validate with JSON.

AlbumForm.new(album).
  validate('"songs": [{"title": "Eat ..')

The API is not yet finalised, I just wanted to give you an out-sight on where this is going.

Reform 2.0 coming.

Speaking of out-sights: Reform 2.0 is already on its way. A major improvement here will be to move all the heavy model lifting (populating, syncing, saving, etc) into disposable which will be a model abstraction layer in Trailblazer (or without Trb).

Use it, and let me know what you think!

4 thoughts on “Reform 1.1 Brings More Reusability!

  1. Will the Modules work in forms with compositions? Currently all my forms (even when using a single model) uses composition to keep a default workflow behavior.

    I have 3 forms that handles the same class, I already included the validations in a concern but I need to keep the same properties on all forms.

    “`
    properties [
    :name,
    :address,

    ], on: :pessoa
    “`

    Can I move that into a module, with the validations?

    Like

Leave a comment