🛤️ Ruby on Rails - Helpers
Helpers are meant to simplify view code by moving all commonly used or complex layout functionality away from the views.
In Ruby on Rails 4, all methods in helper classes at app/helpers
are automatically available in all views. Not in models, controllers or anywhere else.
# app/helpers/funny_helper.rb
module FunnyHelper
def tell_a_joke
"no."
end
end
# in any view
<%= tell_a_joke %>
Namespace your helpers to helpers
if you need them outside of views. You can access helpers through ActionController::Base.helpers
.
class MyPresenterOrViewModelOrSomethingElse
def do_stuff
helpers.number_to_currency(amount, :precision => 0)
# the following would also work:
# ActionController::Base.helpers.number_to_currency(amount, :precision => 0)
end
private
def helpers
ActionController::Base.helpers
end
end
Store custom field definitions in the database. If you have multiple custom fields for you data, avoid just using helpers to render them but store the custom field name, type and Rails partial view name into the database. It makes having a lot of custom field more manageable.
Development Helpers
debug
helper is good for development.
<% my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]} %>
<%= debug(my_hash) %>
Benchmarking execution time of a block in a template.
<% benchmark "Process data files" do %>
<%= expensive_files_operation %>
<% end %>
Use cache
for views fragments. Like menus, lists and other semi-static elements.
<% cache do %>
<%= render "shared/footer" %>
<% end %>
capture
allows extracting a template into a variable. Helps to simplify deeply nested or looping view code.
<% @greeting = capture do %>
Welcome! The date and time is <%= Time.now %>
<% end %>
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<%= @greeting %>
</body>
</html>
Date Time Helpers
Use human readable interval helpers.
now = Time.current
later = Time.current + 15.seconds
distance_of_time_in_words(now, later)
# => less than a minute
distance_of_time_in_words(now, later, include_seconds: true)
# => less than 20 seconds
time_ago_in_words(3.minutes.from_now)
# => 3 minutes
Asset Helpers
Asset tag helpers allow linking to compiled sources.
image_tag "rails.png" # public/images
image_url "rails.png" # public/images
video_tag "movie.ogg" # public/videos
video_url "movie.ogg" # public/videos
audio_tag "music.mp3" # public/audios
audio_url "music.mp3" # public/audios
javascript_include_tag "common" # {app,lib,vendor}/assets/javascript
javascript_path "common" # {app,lib,vendor}/assets/javascript
javascript_url "common" # {app,lib,vendor}/assets/javascript
stylesheet_link_tag "application" # {app,lib,vendor}/assets/stylesheets
stylesheet_path "application" # {app,lib,vendor}/assets/stylesheets
stylesheet_url "application" # {app,lib,vendor}/assets/stylesheets
auto_discovery_link_tag :rss, "http://www.example.com/feed.rss"
By default, Rails links to these assets in the public
folder.
# customizing asset host
# config/environments/production.rb
config.action_controller.asset_host = "assets.example.com"
image_tag("rails.png")
# => <img src="http://assets.example.com/images/rails.png" alt="Rails" />
Fingerprint is added if config.assets.digest
is true.
ActiveRecord Helpers
Forms can be bind to a model.
# @person variable will have been created in the controller
# e.g. @person = Person.new
<%= form_for @person, url: {action: "create"} do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.submit %>
<% end %>
# submit sends something like this...
{
"action" => "create",
"controller" => "people",
"person" => {"first_name" => "William", "last_name" => "Smith"}
}
You can define what model bound inputs are shown.
label(:article, :title)
check_box(:article, :validated)
file_field(:user, :avatar)
hidden_field(:user, :token)
radio_button(:article, :category, :rails)
radio_button(:article, :category, :java)
text_field(:article, :title)
email_field(:user, :email)
url_field(:user, :url)
date_field(:user, :date_of_birth)
password_field(:login, :pass)
text_area(:comment, :text, size: "20x30")
Use date time input helpers.
date_select(:article, :published_on) # bound to model attribute
time_select(:order, :submitted) # bound to model attribute
datetime_select(:article, :published_on) # bound to model attribute
# all the following take Date, Time, DateTime as the first argument
select_date(Time.today + 6.days) # generic date select
select_time(Time.now) # generic time select
select_datetime(Time.now + 4.days) # generic date and time select
select_year(Date.today, start_year: 1900, end_year: 2009) # specify between
select_month(Date.today) # between 1 and 12
select_day(Time.today + 2.days) # between 1 and 31
select_hour(Time.now + 6.hours) # between 0 and 23
select_minute(Time.now + 6.hours) # between 0 and 59
select_second(Time.now + 16.minutes) # between 0 and 59
# how to convert back to date
Date.civil(
params[:start_date][:year].to_i,
params[:start_date][:month].to_i,
params[:start_date][:day].to_i)
Use file fields for uploads.
<%= form_for @person do |f| %>
<%= f.file_field :picture %>
<% end %>
def save_upload
uploaded_io = params[:person][:picture]
path = Rails.root.join('public', 'uploads', uploaded_io.original_filename)
File.open(path, 'wb') do |file|
file.write(uploaded_io.read)
end
end
Use collection selection to support model relations.
class Article < ActiveRecord::Base
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :articles
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end
collection_select(
:article,
:author_id,
Author.all,
:id,
:name_with_initial,
{prompt: true}
)
Optionally use collection radio buttons to support model relations.
class Article < ActiveRecord::Base
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :articles
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end
collection_radio_buttons(
:article, :author_id, Author.all, :id, :name_with_initial)
Use collection check boxes for one-to-many relations.
class Article < ActiveRecord::Base
has_and_belongs_to_many :authors
end
class Author < ActiveRecord::Base
has_and_belongs_to_many :articles
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end
collection_check_boxes(
:article, :author_ids, Author.all, :id, :name_with_initial)
Use option groups to group selections.
class Continent < ActiveRecord::Base
has_many :countries
# attribs: id, name
end
class Country < ActiveRecord::Base
belongs_to :continent
# attribs: id, name, continent_id
end
option_groups_from_collection_for_select(
@continents, :countries, :name, :id, :name, 3)
For custom options, use options_for_select
.
# you have to wrap this call in a regular HTML select tag
options_for_select([ "VISA", "MasterCard" ])
country_select offers a country listing.
country_select(:user, :country)
Time zone should define how users see all dates and times.
time_zone_select(:user, :time_zone)
Generic Form Helpers
Form tag helpers don't bind to ActiveRecord objects. They all end in _tag
.
<%= form_tag "/articles" do %>
<%= submit_tag "Save" %>
<%= image_submit_tag("login.png") %> <%# clicking image submits the form %>
<% end %>
<%= field_set_tag do %>
<%= text_field_tag "name" %>
<% end %>
<%= label_tag "name" %>
<%= text_field_tag "name" %>
<%= text_area_tag "article" %>
<%= email_field_tag "email" %>
<%= url_field_tag "url" %>
<%= date_field_tag "date_of_birth" %>
<%= hidden_field_tag "token", "VUBJKB23UIVI1UU1VOBVI@" %>
<%= password_field_tag "pass" %>
<%= check_box_tag "accept" %>
<%= radio_button_tag "gender", "male" %>
<%= select_tag "people", "<option>David</option>" %>
<%= form_tag({action:"post"}, multipart: true) do %>
<%= file_field_tag "file" %>
<%= submit_tag %>
<% end %>
<%= javascript_tag "alert('All is good')" %>
Number Helpers
number_with_delimiter(12345678) # => 12,345,678
number_with_precision(111.2345, 2) # => 111.23
number_to_currency(1234567890.50) # => $1,234,567,890.50
number_to_human_size(1234) # => 1.2 KB
number_to_percentage(100, precision: 0) # => 100%
number_to_phone(1235551234) # => 123-555-1234
Sanitization Helpers
Sanitize helper will HTML encode all tags and strip all attributes that aren't specifically allowed.
sanitize @article.body
# allow specific tags and attributes
sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style)
# globally allow tags
class Application < Rails::Application
config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
end
sanitize_css("body { background: red; }")
# => body { background: red; }
strip_links(%q(<a href="http://rubyonrails.org">Ruby on Rails</a>))
# => Ruby on Rails
strip_tags("Strip <i>these</i> tags!")
# => Strip these tags!