LEARNING OBJECTIVES
- Understand and explain what helper methods are.
- How to create custom helper methods.
- Learn how to test helpers and create them using TDD.
Definition: is a module which contains methods that are vital for the simplification and the dryness of your views.
Benefits
- Extract some complexity out of the view
- Make view logic easier to test
####Example
VIEW:
<% if @user && @user.email.present? %>
<%= @user.email %>
<% end %>
TRANSFER TO HELPER:
module SiteHelper
def user_email(user)
user.email if user && user.email.present?
end
end
NEW VIEW:
<%= user_email(@user) %>
We have all used built in rails helpers. Everyone take 5 minutes and pick a rails helper that you have used and explain it to the class.
What helpers were not covered by students?
Before we begin making helpers lets talk about where to put our code...
##Sorting our code
Models: For code about your database, the model is your first stop in Rails. Models are powerful, easy to test , reusable across applications and more like non-Rails code than most of Rails. If there's a good way to put the code in your model, that's usually a safe bet and a good idea.
Controllers: It's easy to put lots of code in your controllers, but it's almost always a mistake. Business logic for your app should get out of the controller and into the model as quickly as possible. Logic about how things are show to the user should go into the view. In general, the controller should be a tiny, thing glue layer putting together your other components.
Views: Having lots of logic in your views is a huge anti-pattern. Don't do it! It's hard to test, it's hard to find, it's hard to write sandwiched in between the HTML...Just don't. Instead your views should contain HTML, variables that turn into HTML, and calls to helper methods to generate HTML or whatever the final output is. There should be no logic in there to test.
Helpers: Rails "helpers" are very specifically view helpers. They're automatically included in views, but not in controllers or models. That's on purpose! Code in the application helper is included in all the views in your application. If you find yourself writing big loops, method calls or other logic in the view, move it into a method in the helper.
/lib directory: remember that helpers are specifically view helpers. What if you wanted a controller helper or model helper? Put them in this directory. The app controller could get large quick.
Lets create the tests for the helper example given above:
require 'rails_helper'
RSpec.describe SiteHelper, :type => :helper do
describe "#user_email" do
context "when the user exists and has an email" do
it "returns the user's email" do
user = double("user", :email => "foo")
expect(helper.user_email(user)).to eq("foo")
end
end
context "when the user exists and has no email" do
it "returns nil" do
user = double("user", :email => nil)
expect(helper.user_email(user)).to eq(nil)
end
end
context "when the user doesn't exist" do
it "returns nil" do
expect(helper.user_email(nil)).to eq(nil)
end
end
end
end
The second benefit listed above for using helpers is the fact that you can isolate the view logic and test it as a unit, I personally find this one very attractive. By using a helper as opposed to dumping everything in the view, you can isolate pieces that make up the view and test each one separately (edge cases and all). hat's really nice. You'll find the use of helpers really useful if you have a ton of view setup code like instance variables and such that you'd have to setup if you were to test the view instead.
###Code Along
*** Generate App***
rails new helperApp
cd helperApp
Add rspec-rails to gemfile
echo 'gem "rspec-rails", :group => [:development, :test]' >> Gemfile
RSpec
rails generate rspec:install
Generate a scaffold
rails generate scaffold Widgets name:string
Run migrations
rake db:migrate && rake db:test:prepare
Run RSpec
rspec spec --format documentation
Lets begin by using the below scenarios
SCENARIOS: create spec and helper methods for each.
- Helper method that returns a value
- Helper method that accesses an instance variable
- Application helper is included in helper object
- Url helpers are defined
###Helper Spec
Helper specs are marked by :type => :helper or if you have set config.infer_spec_type_from_file_location! by placing them in spec/helpers.
Helper specs expose a helper object, which includes the helper module being specified, the ApplicationHelper module (if there is one) and all of the helpers built into Rails. It does not include the other helper modules in your app.
To access the helper methods you're specifying, simply call them directly on the helper object.
NOTE: helper methods defined in controllers are not included.
I will create the first scenario then for the remainder of the time everyone must complete as many specs as possible. I will give the answer 20 min till break.