How do I run multiple lines of Ruby in html.erb file - ruby-on-rails

I'm using Ruby on Rails and need to run a block of Ruby code in one of my html.erb files. Do I do it like this:
<% def name %>
<% name = username %>
<%= name %>
or like this:
<% def name
name = username %>
<%= name %>
Thanks for reading.

If you need extra functions in your view, you normally declare those inside a helper.
For each controller, if there is a helper it is automatically loaded. For instance, if you have a PeopleController, in the app/helpers folder, there should be a people_helper.rb, and it should look like this
module PeopleHelper
def name
#do something
username
end
end
Another, very clean alternative, is to use the Presenter pattern, but i think it is less common (unfortunately).
Otherwise, if you do need multiple lines of ruby code inside a erb view, which i try to avoid, i prefer the following style:
<%
counter_1 = 0
counter_2 = 1
do_some_more_prep_here
%>
<% #records.each do |rec|%>
<%# do something with the prepped date in each row %>
<% end %>
Also for me code indentation is more important than html indentation, so i will prefer something like
<table>
<% #rows.each do |row| %>
<tr>
<td><%= row.item1 %></td>
<% if row.some_test %>
<td><%= row.item2 %></td>
<% end %>
</tr>
<% end %>
</table>
But i am always very interested to hear different opinions in this matter.

It is unusual to define a method in an ERB file, so I recommend against it.
If you want to call a block like #each, you can do something like the following:
<% names.each do |name| %>
<%= name %>
<% end %>
Don't forget the <% end %>.

I can imagine someone needing it in one particular template (no point in creating a helper) to not duplicate html markup. That is, when resulting html page has a couple of similar blocks of html markup. Though, it can easily be abused (unreadable code).
<% def f1(a, b, c) %>
test: <%= a %>, <%= b %>, <%= c %>
<% end %>
<% f1(1, 2, 3) %>
<% f1(4, 5, 6) %>

Related

Rails: most elegant way to render attributes of a record in a citation

I have a #story record with the following attributes:
author
author_title
year
source
source_link
I'm rendering it in the view so it comes out like this:
James Joyce (author), 1882, Wikipedia
I am hoping there is a less convoluted way to generate the DOM for the citation than this (which is imperfect, as I explain below):
<%= #story.author %><% if !#story.author_title.blank? %> (<%= #story.author_title %>)<% end %><% if !#story.year.blank? %>, <%= #story.year %><% end %><% if !#story.source_link.blank? %>, <%= link_to #story.source, #story.source_link, target: "_blank" %><% end %>
As none of the fields are mandatory, the if-field-not-nil-then-you-may-need-a-comma issue is what I suspect could be handled more elegantly. For example, if author is blank, then I don't want to display the author_title or the trailing comma.
You can try the below code
create a two helper method
def author_story(author)
[#story.author_title, #story.year].compact.join(',')
end
def author_link
link_to(#story.source_link, text: 'testing')
end
and in view
<% if #story.author.present? %>
<div>
<span>
<%= author_story(#story) %>
</span>
<span>
<%= author_link(#story) %>
</span>
</div>
<% end %>
See my comments, Decorators is the way to go. You'll learn to love it and never go back.
If you think it is too much overhead for only a one-time simple task... then create a model method for this:
class Story < ApplicationRecord
.... #other stuff
def author_with_title_and_year
"#{author} #{author_title}, #{year}".squish
end
end
And add the link manually behind it:
<p><%= #story.author_with_title_and_year %> <%= link_to source, source_link %></p>

Ruby on rails - each do class?

<% consents_checkboxes.each do |checkbox| %>
<%= checkbox.html_safe %>
<% end %>
Hello there,
can i give them a class while looping through them? I can't get it to work and tried several different ways.
This is something I would like to achieve
<% consents_checkboxes.each do |checkbox| %>
<%= checkbox.html_safe, class: 'checkbox' %>
<% end %>
thank you
You can only do it with an element. What you want to do is:
<% consents_checkboxes.each do |checkbox| %>
<p class="checkbox"><%= checkbox.html_safe %></p>
<% end %>
Of course, you can use another element (span, div etc.).
What's on consents_checkboxes? You should provide more context when you ask for something...
It looks like you have strings with the html code, right? you will have to parse the string with something like nokogiri and add a class
<%= Nokogiri.parse(checkbox).add_class('checkbox') -%>
Or you could modify the process that generates that consents_checkboxes to include the class you need. Maybe there's better options, but with only that information it's really hard to tell.

Find all entities in database and show them if they exist, gives error: no block given yield

How to make this code work?
<%= articles= Article.find_each
if articles
a.each do |a| %>
****some html****
<% end %>
<% end %>
right now it gives me an error:
no block given (yield)
It's hard to tell because your code is such a mess but i think you are trying to do this:
<% Article.all.each do |article| %>
<!-- some html - reference the local variable `article` in here, inside erb tags, eg -->
<div>
<%= article.name %>
</div>
<% end %>
EDIT: the above code will work fine (by which i mean happily generate no html at all) if there are no Article records in the db. Sometimes in this situation you might want to display some sort of extra info, like "You haven't created any Articles yet" or something. if this is the case you could do something like this:
<!-- typically this variable would be defined in the controller -->
<% #articles = Article.all %>
<% if #articles.blank? %>
<p>You haven't created any Articles yet</p>
<% else %>
<% Article.all.each do |article| %>
<!-- some html - reference the local variable `article` in here, inside erb tags, eg -->
<div>
<%= article.name %>
</div>
<% end %>
<% end %>

Scope of each on rails template

I'm new to rails and I'm trying to build a view that will list the parents and related children
Ex:
Passport has many Visas
I want to list information about the passport and the visas that the passport has.
So I have
<% #passport_list.each do |passport| %>
# passportFields
<% passport.visas.each do |visa| %>
<%= t.text_field :visa_type %>
<% end %>
<% end %>
I'm getting the error
undefined method `visa_type' for #Passport:0x000000091b8b28
It looks like rails is trying to find the property visa_type for passport, instead of in visa. How does the scope work within each? Can I force it to access visa_type from visa?
I think you're looking for the fields_for form helper. This will allow you to create fields for the relevant visa attributes. Replace your code sample with the following, and you should be all set.
<% #passport_list.each do |passport| %>
# passportFields
<% t.fields_for :visas do |visa_fields| %>
<%= visa_fields.text_field :visa_type %>
<% end %>
<% end %>
You can also iterate over the list as follows:
<% #passport_list.each do |passport| %>
# passportFields
<% passport.visas.each do |visa| %>
<% t.fields_for :visas do |visa_fields| %>
<%= visa_fields.text_field :visa_type %>
<% end %>
<% end %>
<% end %>
For more information on fields_for, check out the link I added above, and to customize further for your use case, check out the "One-to-many" section.
IMO you should always handle the null case of an object.
Something like this if you use rails (present? is a Rails function)...
<% if #passport_list.present? %>
<% #passport_list.each do |passport| %>
passportFields
<% passport.visas.each do |visa| %>
<%= t.text_field :visa_type %>
<%end%>
<%end%>
<% else %>
<p>Nothing to see here</p>
<% end %>
However if your #passport_list is backed by an ActiveRecord Query, you can handle this in the model/helper/controller by returning the .none query on the model. Note that this differs from an empty array because it is an ActiveRecord Scope, so you can chain AR queries onto it
# scope on AR model
def self.awesomeville
where(country_of_origin: "awesomeville")
end
# method queried in controller
#passport_list = Passport.all
if #passport_list.present?
#passport_list
else
Passport.none
end
# additional filtering in view is now possible without fear of NoMethodError
#passport_list.awesomeville
Whereas a ruby Array would raise an error as it would respond to the Array methods.

Conditional line spacing in ERb partials

This is the code for an address partial I just wrote. People might put single line addresses in either street line, company name is optional, etc... It works exactly how I want it to, but I know that checking each variable twice is ugly and terrible.
<%= "#{a.name}" unless a.name.blank? %>
<% unless a.name.blank? %> <br> <% end %>
<%= "#{a.company_name}" unless a.company_name.blank? %>
<% unless a.company_name.blank? %> <br> <% end %>
<%= "#{a.street_1}" unless a.street_1.blank? %>
<% unless a.street_1.blank? %> <br> <% end %>
<%= "#{a.street_2}" unless a.street_2.blank? %>
<% unless a.street_2.blank? %> <br> <% end %>
<%= "#{a.city}, #{a.state} #{a.zip}" %>
So, my gratuitous use of unless aside, how should I be putting in a conditional line break?
Update:
As discussed below, it is dangerous to use .html_safe on user input. If you do use a helper method as suggested below, you must also sanitize all user input on the way into the database. I've rewritten the code above as:
<% unless a.name.blank? %>
<%= a.name %>
<br>
<% end %>
<% unless a.company_name.blank? %>
<%= a.company_name %>
<br>
<% end %>
<% unless a.street_1.blank? %>
<%= a.street_1 %>
<br>
<% end %>
<% unless a.street_2.blank? %>
<%= a.street_2 %>
<br>
<% end %>
<%= "#{a.city}, #{a.state}" %> <%= a.zip %>
The redundant checking was just me overcomplicating things. I'd strongly recommend against using .html_safe in a situation like this, since you create new problems for yourself: sanitizing the input, and remembering which fields are safe. Better to not override the sensible protection Rails provides.
There are many, many ways to go about cleaning it up, but a helper would be appropriate here:
module ApplicationHelper
def format_address(a)
top = [a.name, a.company_name, a.street_1, a.street_2]
top.reject! {|s| s.blank?} # remove null and empty values
"#{top.join('<br/>')}#{a.city}, #{a.state} #{a.zip}".html_safe
end
end
Then in your view:
<%= format_address(a) %>

Resources