Reform 0.2 Released – With has_one And has_many Support!

{{{
“Reform”:https://github.com/apotonick/reform gives you a new abstraction layer for handling forms without hard-wiring them to your database. It just went 0.2 bringing you nesting to easily create forms for @has_one@ and @has_many@ relationships.

h3. Composition Forms.

In earlier versions, reform could automatically build a composition object to handle forms for multiple, unrelated objects.

class SongForm < Reform::Form
  include Reform::Form::Composition

  property :title,       on: :song
  property :written_by, on: :artist
end

This still works, however, the @DSL@ module got replaced by @Composition@, which you must include to make reform understand what this @on:@ option is about.

SongForm.new(
  song:   Song.find(1), 
  artist: Artist.find(2))

When creating a composite form you still need to pass in the separate objects using a hash.

h3. One-To-One Relationships.

Technically, every kind of model relations could be handled with this @Composition@ trick. Nevertheless, the new reform makes it super easy to compose forms of multiple associated models.

Say we had the following database configuration.

class Song < ActiveRecord::Base
  has_one :artist
end

A classic 1-to-1 association! Yay!

Although I’m using @ActiveRecord@ to demonstrate reform’s new goodies, it is important to understand that this gem *doesn’t speak a single word of @ActiveRecord@* – it uses public readers and writers, only.

To create a form to handle fields for both @Song@ and @Artist@ you can now define _nested_ forms.

class SongWithArtistForm < Reform::Form
  property :title

  property :artist do
    property :name
    property :gender

    validates :name, presence: true
  end
end

See how you can now pass a block to @property@ and simply create another form class inline? Awesome, isn’t it?

(Tech note: the new inline representer feature in “representable 1.6”:https://github.com/apotonick/representable/#inline-representers made it extremely easy to implement nesting in reform).

h3. Render The Association Form.

Now, check out how this form is instantiated.

@form = SongWithArtistForm.new(Song.find(1))

As you’re _not_ using the composition feature, all you do is pass in a single model.

song = Song.find(1)
song.artist
#=> 

Since you have a nested setup, this model is required to respond to @#artist@, which in turn must expose readers for @name@ and @gender@.

That should save you some work when creating the form.

Even cooler: rendering the form using Rails’ (nested) form helpers now works out-of-the-box – without inheriting all the flaws from @accepts_nested_attributes@ code.

= form_for @form do |f|
  = f.text_field :title

  = fields_for :artist do |a|
    = a.text_field :name
    = a.text_field :gender

This just works, so you don’t have to worry about rendering the proper markup – the most annoying part when writing forms in my opinion.

h3. Validating And Processing.

All you need to do now is passing the submitted data to reform.

@form.validate(params[:song])

This will run all validations from the form, even the nested one from the artist form.

Error messages – in case of bull data – can be rendered using the common steps.

- @form.errors.full_messages.each do |msg|
  %li
    = msg

Using the block-less @#save@ will push submitted and validated data to all objects automatically.

@form.save

#=> @form.song.title       = "Beachparty"
#   @form.song.artist.name = "No Fun At All"

As before, you can do the saving manually: @#save@ will yield the nested input.

@form.save do |data, hash|
  data.artist.name
  #=> "No Fun At All"

  hash[:artist][:name]
  #=> "No Fun At All"

  Artist.create(hash[:artist])
end

Here, it’s up to you how to process the nested data. Reform just makes sure things are correctly nested.

h3. One-To-Many Relationships.

You thought that’s it? No way, we also got support for nested collections.

class Album < ActiveRecord::Base
  has_many :songs
end

Mapping this association in your form is pretty straight-forward.

class AlbumForm < Reform::Form
  property :name

  collection :songs do
    property :title
  end
end

Creating the form works just like the @has_one@ example.

@form = AlbumForm.new(Album.new(
  songs: [Song.new, Song.new]
)

Here, it is important the @Album#songs@ returns a _collection_ of objects.

Rendering, validating and displaying errors works likewise.

You can use @fields_for@ and Rails will render the form collection. You could also go manually through the collection.

= @form.songs.each_with_index do |f, i|
  = text_field_tag "title_#{i}" ..

Currently, it’s your job to keep the number of forms visible on the page in sync with the forms created internally. That is why I pass in two new @Song@ instances to @Album@, as this will make two nested song forms appear after rendering.

If this feels inconvenient, we’re open for suggestions.

When saving sane data, you get a collection of data for the song forms.

@form.save do |data, hash|
  data.songs[0].title
  #=> "Sanity"

  hash[:artist][:songs][0][:title]
  #=> "Sanity"
end

h3. From Here.

The new nesting feature was “requested by”:https://github.com/apotonick/reform/issues/6 “many users”:https://github.com/apotonick/reform/issues/26 and we’re really happy to release “this version of reform”:https://github.com/apotonick/reform. There will surely be issues with certain use cases and we can’t wait for your feedback!
}}}

Advertisement

One thought on “Reform 0.2 Released – With has_one And has_many Support!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s