Can I move this code from erb to the model? - ruby-on-rails

I have 2 models Auteur and Biblio that both have_and_belongs_to_many of each other.
In the template displaying the Authors (auteur) and its books (biblio), I have this code :
<% #auteurs.each do |e| %>
<p><%= e.nom_complet %>, <i> <%= e.biblios.map{|l| l.titre }.join(', ') %></i>, <%= e.biblios.map { |l| l.lieu }.join(', ') %>, <%= e.biblios.map { |l| l.annee }.join(', ') %>
</p>
<% end %>
As each biblio has 8 fields and can belong to different types according to its form of publication and type of content, this would lead to doing a lot of map and if statements in erb.
Although this works fine, there must be a better way of doing this.
Thanks

You can move this code as instance methods in Author model
def biblios_titre
biblios.map(&:titre).join(', ')
end
def biblios_lieu
biblios.map(&:lieu).join(', ')
end
def biblios_annee
biblios.map(&:annee).join(', ')
end
And use it in views
<% #auteurs.each do |e| %>
<p><%= e.nom_complet %>,
<i><%= e.biblios_titre %></i>,
<%= e.biblios_lieu %>,
<%= e.biblios_annee %>
</p>
<% end %>

You can create a helper method in the model.
def view_biblios
self.nom_complet << "<i>" <<
self.biblios.map(&:titre).join ", " <<
"</i>, " << self.biblios.map(&:lieu).join ", " <<
", " << self.biblios.map(&:annee).join ", "
end
And in the view
<%= e.view_biblios.html_safe %>
UPDATE
Since you probably want to keep titles together, based on your comment, you may need this method instead
def view_biblios
self.nom_complet << self.biblios.map{|b|
"<i>#{b.titre}</i>, #{b.lieu}, #{b.annee}"}.join ", "
end

Thanks for all the answers. In the end I did it this way. Using each was sufficient. The code below also has a bit of css, to titles appear indented below each author (in bold).
I'll do all the cleaning up of the code and moving pieces to helpers after I get my 8 models to communicate correctly :)
<% #auteurs.each do |e| %>
<p><strong><%= e.nom_complet %></strong>
<% e.biblios.each do |l| %>
<br><span id="indent"><i><%= l.titre %></i>, <%= l.lieu %>, <%= l.annee.to_s %></span>
<% end %>
</p>
<% end %>

Related

Iterate over an Active Record Relation in Rails 3.2

I have the following code:
#items = QuestionGroup.search(params[:search]).limit(50)
This returns an ActiveRecord relation. In the view I want to iterate through it so I use:
<% if #items.present? %>
<%= #items.each do |r| %>
<%= div_for r do %>
<div><%= r.subject %></div>
<% end %>
<% end %>
<% end %>
This does print r.subject to the view but it then follows it with the entire relation. e.g.
the pipe
[#<QuestionGroup id: **, subject: "the pipe", created_at: "*******", updated_at: "******"]
Why is this and how can I fix it?
Problem is here:
<%= #items.each do |r| %>
This line of code iterates over each of the relations and due to the '=' you output its content. Change it to:
<% #items.each do |r| %>
and you are good to go!

Dont have a comma on the last iteration of an each loop in Rails

I want to print out a list of links separated by commas in Rails.
Heres what I've got:
<%= topics.each do |topic| %>
<a href="<%= topic.link %>" ><%= topic.name %></a>
,
<% end %>
Heres what I want:
Thing A,
Thing B,
Thing C
But right now I get an extra comma on the last iteration of the loop! What should I do?
One way of doing this is with map then Array#join:
<%= topics.map { |topic| link_to(topic.name, topic.link) }.join(',').html_safe %>
if you want to do minimum possible change to your code, you can use the following
<%= topics.each do |topic| %>
<a href="<%= topic.link %>" ><%= topic.name %></a>
<% if(topic != topics.last) %>
,
<% end %>
<% end %>
How about using each_with_index, and only put comma before the content unless it's not the first item.
<% topics.each_with_index do |topic, i| %>
<% if i > 0 %>
,
<% end %>
<%= topic.name %>
<% end %>
I made it in one line call (for active records collections) using the concat helper:
<% concat (',') if e.bills.last != b %>
concat is an ERB helper (TextHelper) to add some HTML without the <%= %> syntax, helpful to add few characters.
Here is the full code to make it clear:
<% event.bills.each do |b| %>
<%= link_to(b.number.to_s, bill_display_path(b)) %>
<% concat (',') if e.bills.last != b %>
<% end %>
Simply try this. It works for me
<%= topics.map{|p| p.topic.name}.join(",") %>
You can do the following to print out the comma for all items except for the last:
<% topics.each do |topic| %>
<%= topic %>
<%= "," if topic != topics.last %>
<% end %>
This will check if the current item in the loop is the last item, and will use the <%= %> syntax to output the comma.

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) %>

Splitting string into linked words

So I have standart scaffold cycle that prints a string of tags of a meet.
<% #meets.each do |meet| %>
<p class="MeetTags"><%= meet.tags %></p>
<% end %>
How can I separate string of for example 3 words, so that each would be in separate box.
Thank you!
You can do some think like
<% #meets.each do |meet| %>
<% meet_tags = meet.tags.split(' ') %>
<% meet_tags.each do |meet_tag|%>
<p class="MeetTags"><%= meet_tag %></p>
<%end%>
<% end %>
your problem is that meet.tags is an array, so you're telling rails to display the array: that's why you're seeing the square braces.
If you want to split tags into groups of 3, and then show them separated by spaces, you could do this:
<% #meets.each do |meet| %>
<% tag_arr = meet.tags.split(' ') %>
<% tag_arr.in_groups_of_3.each do |tag_subarr|%>
<p class="MeetTags"><%= tag_subarr.reject(&:blank?).join(" ") %></p>
<%end%>
<% end %>
I'm using .reject(&:blank?) because the in_groups_of method will pad the last subarray out with nil, which will mess up your formatting, and maybe cause other problems if you try and do anything with the member elements of tag_subarr.
Assuming that your tags are joined with space:
<% #meets.each do |meet| %>
<% meet.tags.split(' ').each do |word| %>
<p class="MeetTags"><%= word %></p>
<% end %>
<% end %>

Looping through space separated list in Rails 3

I am using Rails 3.
I got a tags in a column in the database that is saved with space separating each of them like so:
apple orange banana
I want to loop over them and put each on a separate line (between P tags). I got it somewhat running using the code below but the last line outputs the entire string too.
<p>apple</p>
<p>orange</p>
<p>banana</p>
apple orange banana (I do not want this line)
The code I use is this, how can I make it better / replace it so that I do not get the last line when I output the post?
<% item.options.each(' ') do |item| %>
<p><%= item %></p>
<% end %>
<% item.options.split(" ").each do |item| %>
<p><%= item %>
<% end %>
or better yet you might create a virtual attribute in your model:
def tags do
self.options.split(" ")
end
and then
<% item.tags.each do |tag| %>
<p><%= tag %>
<% end %>
<% (item.options.split(' ')).each do |item| %>
<p><%= item %></p>
<% end %>
that should work well

Resources