Sonntag, 31. Mai 2015

Clean up nested forms with form objects in Rails!

Nested forms consist of form fields representing at least 2 objects. For example one form with user and address attributes. In Ruby On Rails nested forms very often are built with ActiveRecord::NestedAttributes::ClassMethods#accepts_nested_attributes_for and ActionView::Helpers::FormHelper#fields_for. Meanwhile it is proven going that path is a bad idea for several reasons. I fiddles with awful request parameters, burdens the model with false responsibilities and is hard to test. But there is an alternative, which decreases complexity: form objects.
Whenever:
  1. Nested forms
  2. Virtual model attributes
  3. Multiple varying forms for one resource
are required, form objects have to be considered, because they:
  1. Extract responsibility from the model objects
  2. Decouple models from forms
  3. Simplify forms
  4. Flatten parameter hashes and therefore simplify parameter check
  5. Simplify form handling when there are multiple different forms attached to one resource
The more a form is getting complex the more an appropriate form object is the solution.
For example a user form with address data has to be saved at once. First the Address model (address.rb):
class Address < ActiveRecord::Base
  validates :street, :number, presence: true
end
Starting from the original model object user.rb:
class User < ActiveRecord::Base
  belongs_to :address
  validates :name, presence: true
  accepts_nested_attributes_for :address
end
a new form object class (user_address.rb) has to be created to achieve the goal:
class UserAddress
  include ActiveModel::Model
  validates :name, :street, :number, presence: true
  delegate :name, :save, to: :user
  delegate :street, :number, to: :address

  def attributes= new_attributes
    user.attributes = new_attributes.slice :name
    address.attributes = new_attributes.slice :street, :number
  end 

  def user
    @user ||= User.new
  end 

  def address
    @address ||= user.build_address
  end 
end
The UserAddress is a lightweight plain old Ruby object including the ActiveModel::Model, which just means including validation and conversion stuff. To marry User and Address there is a reader acessor for each object and also delegators to their required attribute acessor methods. At least there is an writing accessor for all attributes which just fills both objects.
At first sight the form object seems to cost more effort, than just using accepts_nested_attributes_for, but it offers more flexibility especially when the objects get more complex. Furthermore form logic baggage is extracted from the User model object, which is NOT responsible for it. And the good thing is, the controller and the view stuff is way cleaner through the form object.
The original working but awful nested form (users/_form.html.haml)
= form_for @user do |user_form|
  .text
    = user_form.label :name
    = user_form.text_field :name
  = user_form.fields_for :address do |address_fields|
    .text
      = address_fields.label :street
      = address_fields.text_field :street
    .text
      = address_fields.label :number
      = address_fields.text_field :number

  = user_form.submit 'Save'
could be way cleaner, if it looked like:
= form_for @user_address do |f| 
  .text
    = f.label :name
    = f.text_field :name
  .text
    = f.label :street
    = f.text_field :street
  .text
    = f.label :number
    = f.text_field :number

  = f.submit 'Save'
Please note the @user_address representing the form object and the straightforward form without any nesting.
Also compare the parameter hash from the nested:
"user" => { 
  "name" => "Chris", 
  "address_attributes" => { 
    "street" => "Main street", 
    "number" => "1" 
  } 
}
to the flattened:
"user_address" => { 
  "name" => "Chris", 
  "street" => "Main street", 
  "number" => "1" 
}
Finally even the original controller (controllers/users_controller.rb):
class UsersController < ApplicationController  
  def new 
    @user = User.new
    @user.build_address
  end 

  def create
    @user = User.new 
    @user.attributes = user_params
    @user.save
  end 
  private
  def user_params
    params.require(:user)
      .permit(:name, address_attributes: [:street, :number])
  end 
end
can be moved to the new resource (controllers/user_addresses_controller.rb):
class UserAddressesController < ApplicationController  
  def new 
    @user_address = UserAddress.new
  end 

  def create
    @user_address = UserAddress.new 
    @user_address.attributes = user_address_params
    @user_address.save
  end 
  private
  def user_address_params
    params.require(:user_address)
     .permit(:name, :street, :number)
  end 
end
having an easier parameter check.
Even that small example illustrates how nested forms can be simplified with lots of nice side effects.
Further articles of interest:

Supported by Ruby 2.2.1 and Ruby on Rails 4.2.1

Sonntag, 24. Mai 2015

Use SASS for CSS!

Ruby On Rails ships with SASS as CSS extension and there is a reason why.
It should be preferred over plain CSS because SASS:
  1. adds very useful features like variables and mixins
  2. allows nesting for reducing selector overhead and increasing readability by mapping the selectors depth
  3. adds properties like control directives and inheritance
  4. provides functions for colors, strings and lists
For example a pure CSS file people.css styles a toggle button and its look switching between the two states CSS based:
.icon:before{
  font-family: "Glyphicons Halflings";
}
.chevron-up:before{
  content: "\e113";
}
.chevron-down:before{
  content: "\e114";
}
#person input.toggle{
  display: none;
}
#person input.toggle:checked + .icon.chevron-down:before{
  content: "\e114";
}
styling the HTML snippet:
suffers from several point of views:
  1. even in that tiny CSS it repeats the DOM selector structure over and over (e.g. "#person input.toggle")
  2. a general style (e.g. content: "\e114";) has to be repeated if it has to be used in different contexts.
  3. styles belonging to the same selector structure are separated (e.g. "#person input.toggle" and "#person input.toggle:checked")
Nesting selectors and inheriting from previous defined classes solve the issues. The exactly same CSS can be generated by a SASS file people.css.scss like:
.icon:before{
  font-family: "Glyphicons Halflings";
}
.chevron-up:before{
  content: "\e113";
}
.chevron-down:before{
  content: "\e114";
}
#person{
  input.toggle{ display: none; }
  &:checked + .icon.chevron-up:before{
    @extend .chevron-down;
  }
}
The #person specific CSS was improved by nesting the selectors below #person and input.toggle. The @extend directive allows chevron-up icons to inherit their content from the more general chevron-down class.
Mixins also are nice. Whenever you want the output of the inherited style to change depending on how you call it, mixins are to be preferred over @extend. For example:
.width25{
  width: 25%;
}
.width50{
  width: 50%;
}
.width100{
  width: 100%;
}
#person .icon{
  display: block;
}
and the HTML:
cries for a inheritance in SASS:
@mixin .width($percent){
  width: $percent;
}
#person .icon{
  @include width(50%);
  display: block;
}
and in result even the HTML can be cleaned up by removing the nasty width class:
Variables not only help to assign parameters, they also help to dry out value repetition. For example:
.icon{
  background-color: #5CB85C;
  color: #A347A3;
}
.icon:first-child{
  background-color: #5CB85C;
  color: #A347A3;
}
which just styles the color and the background-color for icons, but inverting the colors for each first icon. The same with SASS:
$brand-primary: #5CB85C;
$brand-invert: invert($brand-primary);

.icon{
  background-color: $brand-primary;
  color: $brand-invert;
  &:first-child{
    background-color: $brand-invert;
    color: $brand-primary;
  }
}
using the variables $brand-primary and $brand-invert for defining the colors globally. Please note variables should be defined in a separate variables.css.scss
The inverted color code is calculated by the SASS function invert(), which ensures the invert color is always inverted.
SASS offers a lot more for many more use cases. Reading the well written SASS documentation helps to solve them.
Further articles of interest:

Supported by Ruby 2.1.1 and Ruby on Rails 4.1.8

Sonntag, 17. Mai 2015

Use HAML for Rails templates!

Ruby On Rails ships with ERB as templating system. It is simple and high generalized. Its flexibility is also its severest drawback.
A more suitable HTML templating system would:
  1. be tightly focused on generating HTML pages
  2. demand correct intendation
  3. reduce HTML tag verbosity and therefore increase readability
HAML is such a templating system and very common in the Ruby framework world.
It is easy to integrate by adding:
gem 'haml'
to the Gemfile.
A simple user.html.erb file:
<h1>Welcome <%= @user.name %></h1>
<div id="friends">
  <% @user.friends.each do |friend| %>
    <div id="<%= dom_id friend %>" class="friend">
      <%= friend.name %>
    </div>
  <% end %>
</div>
can refactored by creating the user.html.haml containing:
%h1= @user.name
#friends
  - @user.friends.each do |friend|
    .friend{ id: dom_id(friend) }= friend.name
The result is way shorter. HAML is well documented.
Please note that HAML forces you to take care of correct indentation. Otherwise it might crash with errors like:
Inconsistent indentation: 6 spaces used for indentation, 
but the rest of the document was indented using 4 spaces.
when there is a indent missing.
Or a little less intuitive:
SyntaxError - syntax error, unexpected keyword_ensure, 
expecting keyword_end
when indenting a Ruby block (like an interator) was incorrect.
Or even harder to get is unintentional DOM structure by wrong indentation.
Anyway HAML forces to indent correctly because whitespaces matter. And that is a benefit, because it helps to write cleaner HTML.
Further articles of interest:

Supported by Ruby 2.1.1 and Ruby on Rails 4.1.8

Sonntag, 3. Mai 2015

Specify Ruby method consumer fallbacks!

A public API is not always as simple as getting or setting a value. Especially when it comes to different expectations on unexpected behaviour. Should an exception be raised, or should it be handled. Best bet is to let the API consumer decide.
The Hash#fetch is such a Ruby example:
 %w(Ruby Java).fetch(0)
=> "Ruby"
and it raises the KeyError exception, when something went unexpected:
 %w(Ruby Java).fetch(2)
=> IndexError: index 2 outside of array bounds: -2...2
but the API provides a user friendly way to deal with unexpected behaviour. It just delegates the decision to the consumer. There are 3 options. First one is capturing the exception like:
begin
  %w(Ruby Java).fetch(2)
rescue IndexError
  puts "Python is missing."
end
or a more convenient way:
%w(Ruby Java).fetch(2, "Add a language.")
by simply assigning the fallback value as the second parameter. The third way to deal with the unexpected behaviour is passing a block:
%w(Ruby Java).fetch(2) { |index| 
  "Add a language at position #{index}." 
}
That consumer friendly approach not only applies to Hash#fetch but also to Hash#delete and many others. And it should also be adopted to the own API if required.
For example:
class Person
  def from_csv file
    File.open file
  end
end
should be refactored to:
class Person
  def from_csv file, &fallback
    File.open file
    rescue Errno::ENOENT => error
    if block_given?
      yield(error, self)
    else
      raise
    end
    self
  end
end
Now it is up to the consumer how to deal with the exceptional behaviour:
Person.new.from_csv('person_1.csv')
=> Errno::ENOENT: No such file or directory @ rb_sysopen - person_1.csv

Person.new.from_csv('person_1.csv') { |e| 
  raise IOError.new e.message 
}
=> IOError: No such file or directory @ rb_sysopen - person_1.csv

person = Person.new.from_csv('person_1.csv') { |e, person| 
  person.name = 'unknown' 
}
=> #<Person:0x00000005303f90 @name="unknown">
but the method itself can be refactored even a little more to:
class Person
  def from_csv file, &fallback
    fallback ||= ->(error) { raise }
    File.open file
    rescue Errno::ENOENT => error
    fallback.call(error)
    self
  end
end
It works the same but removes the awful if/else condition by setting up a default lambda.
Further articles of interest:

Supported by Ruby 2.1.1