Rails 3 Combine Two Variables - ruby-on-rails

I have the following code which I need to update...
<% #users.each do |user| %>
<tr>
<td><%= link_to user.fname, user %></td>
</tr>
<% end %>
I want to learn how to update that so instead of just showing the fname, it shows fname + lname
so for a record like James Bond, it shows james bond and links it to the user in Rails.
thanks

You can join the string right in the ERB:
<%= link_to user.fname + " " + user.lname, user %>
<!-- or, slightly better: avoids problems with nil values -->
<%= link_to "#{user.fname} #{user.lname}", user %>
<!-- or, even better -->
<%= link_to [user.fname, user.lname].join(" "), user %>
Or, you can move that ugly logic into a helper method, e.g. in app/helpers/users_helper.rb:
module UsersHelper
def full_name(user)
[user.fname, user.lname].join(" ")
end
end
<%= link_to full_name(user), user %>
Or, (this is what I would do) you can put a full_name method in the model:
class User < ActiveRecord::Base
def full_name
[fname, lname].join(" ")
end
end
<%= link_to user.full_name, user %>

Related

Why Rails doesn't print output?

I need your help: I have a simple application in Rails and I don't know why it doesn't print attributes on the screen with whatever model I am working on.
For example I have a "Account" model with attributes "first_name", "last_name", "username", etc .. and 4 entries inserted for users in the database table Account.
These are the controller and views files:
class PublicController < ApplicationController
def main
#users = Account.all
end
end
And this is the view file (public/main.html.erb):
<% #users.each do |user| %>
<% user.first_name %>
<% user.last_name %>
<% user.username %>
<% end %>
This doesn't print anything but if I change the second line of the view file with:
<% if user.first_name? %> first_name exists <% end %>
The output is 4 times: first_name exists.
So why it doesn't print the attributes first_name, last_name, username?
In ERB the <% %> tag evaluates Ruby code but does not print the output, where as <%= %> does print the output
This will correct the issue
<%= user.first_name %>
<%= user.last_name %>
<%= user.username %>

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 search function solutions

I'm new to Ruby on Rails, so I have serveral questions to my search function.
Search View:
<p>
<%= form_tag students_path, :method => 'get' do %>
<p> Advanced_search: <%= check_box_tag "advanced_search", value = "1" %> </P>
<%= select_tag(:attribute, options_for_select([['Prename',0],['Lastname',1]])) %>
<%= text_field_tag :search%>
<%= submit_tag "Search"%></p>
Controller:
def index
#stud = Student.search(params[:search], params[:advanced_search], params[:attribute])
end
Model:
def self.search(search, advanced_search, attribute)
ary = []
if advanced_search
case attribute
when '0'
ary << Array(where(Student.arel_table[:prename].matches("%#{search}%")))
when '1'
ary << Array(where(Student.arel_table[:lastname].matches("%#{search}%")))
else
raise ArgumentError, 'Something strange happened! problem with select_tag in the search function'
end
elsif search
case attribute
when '0'
ary << Array(where(prename: search))
when '1'
ary << Array(where(lastname: search))
else
raise ArgumentError, 'Something strange happened! problem with select_tag in the search function'
end
else
Student.all
end
ary
end
Index View:
<% #stud.each do |student_arr|%>
<% student_arr.each do |student| %>
<li> <%= student.prename + " " + student.lastname + " " + student._format_birthday + " DaZ: " + student.daz.to_s%>
(<%= link_to "Details", action: "detail", id: student.id %>)
(<%= link_to "Edit", action: "edit", id: student.id %>) </li>
<% end %>
<% end %>
My solution works, I don't get duplicates, but the code look really badly and I have to say "look for pre- OR lastname". May anyone can help me with a better solution.
For me it would be perfect if u are just searching for a Student and u don't have to say for what u are looking for (pre- or lastname) and u don't get duplicates. Maybe a Student name is "Peter Peter" (bad example, but it could happen^^), so I just want to get Peter once in my Student_array. Besides I would like to have the option to search for an explicit prename, so if there is Student called "Hans-Peter" and Student called "Hans" but I just want to find "Hans"...
Is there a way to realize this problems with less and more beautiful code?
Thanks for help and sorry for my bad english.. I'm doing my best :)
Bye Bye
IMO, your code need a complete refactor. Here is the base to help you refactor your search logic:
# student.rb
def self.search(searched_string)
searchable_columns = %w( prename lastname )
sql_conditions = searchable_columns.map do |column_name|
"#{column_name} ILIKE :searched_string"
# use ILIKE if you are using PostgreSQL
# use LIKE if you are using MySQL or SQLite
end.join(' OR ')
where(sql_conditions, searched_string: "%#{searched_string}%")
end
Any Student record having prename or lastname containing the string searched will be returned.
You would have to change your controller's call to the search method accordingly.

Can I move this code from erb to the model?

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

For the first x in array?

I imagine this has a rather simple answer
<% for user in #users %>
<li>
<%= link_to user.username, user %>
</li>
<% end %>
Taking my simple example above, how would I apply a class to my <li> to the first three instances returned?
Secondly, how could I just have the the second two items have a different class from the first one? as in 1..2
Either you could count manually (which is kinda ugly):
<% i = 0 %>
<% for user in #users %>
<li class=<%= (i < 3 ? "foo" : "bar") %>>
<%= link_to user.username, user %>
</li>
<% i = i.next %>
<% end %>
or use each_with_index
<% #users.each_with_index do |user, i| %>
<li class=<%= (i < 3 ? "foo" : "bar") %>>
<%= link_to user.username, user %>
</li>
<% end %>
Once you get to more complex things than i < 3 (like your 1..2 issue) you should think about a helper_method (in helpers) like class_by_position(pos) so that you can write
<li class=<%= class_by_position(i) %>>
The :first-child pseudoselector might be a better way to go, but you'll need to have a counter variable that keeps track of the iterations to do it your way.
Your question is a little vague. I can't tell if you want to stop processing the array after the first x, or not.
If you're just looking to stop after the first x items, or just looking for the 2nd and 3rd items the solution is to use a slice.
Example: just the first 3:
#user[0,3].each do |user|
... # only executed for user = #user[0],user = #user[1] and user = #user[3]
end
Example: just the second and third:
#user[1,2].each do |user|
... #only only executed for user = #user[1] and user = #user[3]
end
And here's a more specific answer to your question using these new concepts and the content_tag to programatically decide the class of the list item. If you're going to be doing this often, makes a great candidate for a function.
<% first = true %>
<% #user[0,2].each do |user| %>
<% content_tag :li,
:class => first ? "class-for-first-item" : "class-for-2nd-&-3rd" do %>
<%= link_to user.username, user %>
<% end %>
<% first = false %>
<% end %>
<!-- Now to do the rest of them:-->
<% #user[3,-1].each do |user| %>
<% content_tag :li, :class => "class-for-rest" do %>
<%= link_to user.username, user %>
<% end %>
<% end %>

Resources