Ruby on Rails Nesting Blocks - ruby-on-rails

I have a block that I need to nest another block in. Or maybe I don't and I am doing this totally wrong.
There is a UNIT and the UNIT has_many COMPANIES
Then COMPANIES has_many USERS
on the UNIT show page I am trying to do something like this:
<% #company.each do |c|%>
<%= c.name %>
<% ??? each do |f| %>
<p>
Name: <%= f.name %>
</p>
<% end %>
<% end %>
So basically for each Company I show the name of the Company, no problem there, but then I am trying to show that within each company there are particular Users I am listing that belong to that company. I can't define that in the controller because there are multiple companies.
That user is:
cc = #user.find(:all, :conditions => ["position = ?", "Company Commander"])
I am not sure how to loop through for each Company and then for that Company loop through and list a particular user for that company.
Sorry if I did a poor job of explaining this. I am not sure if this is even the right way to be doing this.
Thanks in advance.

Try this:
<% #company.each do |c| %>
<%= h(c.name) %>
<% c.users.each do |u| %>
<p>Name: <%= h(u.name) %></p>
<% end %>
<% end %>

Firstly, you need to have relations defined in your model:
# Unit model
has_many :companies
has_many :users, :through => :companies
# Company model
has_many :users
belongs_to :unit
...
Than in your Unit controller:
#unit = Unit.find(params[:id]) # or something similar
And in your view:
<% #unit.companies.each do |c|%>
<%= c.name %>
<% c.users each do |f| %>
<p>
Name: <%= f.name %>
</p>
<% end %>
<% end %>

It seems you want to list the users with a position of 'Company Commander' associated with each company.
Maybe a named scope on user would help:
class User < ActiveRecord::Base
...
named_scope :company_commanders, :conditions => "position = 'Company Commander'"
end
Now you can loop through them as you see fit.
<% #company.each do |c|%>
<%= c.name %>
<% c.users.company_commanders each do |f| %>
<p>
Name: <%= f.name %>
</p>
<% end %>
<% end %>

if a company has many users, then you should be able to just do 'company.users' to get the list of users associated with that company. then you can loop through or list those however you like.
Assuming you set up the relationship in your models, this is just how active record sets up these associations for you.

This is what ending up working:
named_scope :company_commanders, :conditions =>{:position => 'Company Commander'}
<% #company.each do |c| %>
<%= h(c.name) %><br />
<% if c.users.company_commanders.blank? %>
<%= link_to "Add this User", new_user_path %><br />
<% else %>
<% c.users.company_commanders.each do |u| %>
<p>Name: <%= h(u.name) %></p>
<% end %>
<% end %>
<% end %>
</p>

Related

Rails 4 - validate model is not working

Appreciate if you help me. I have a rails project, where people can book tickets to an airplanes (not a real project, just trying to learn rails :) ).
I have two cfaffolded objects - 'seats' and 'flights', and customer can buy a seat from every flight page (seats loads as partials).
My /app/views/flights/show.html.erb looks like that:
<p>
<strong>Departure:</strong>
<%= #flight.departure %>
</p>
<p>
<strong>Destination:</strong>
<%= #flight.destination %>
</p>
<p>
<strong>Baggage allowance:</strong>
<%= #flight.baggage_allowance %>
</p>
<%= render partial: "new_seat", locals: {seat: Seat.new(flight_id: #flight.id)} %>
<%= link_to 'Edit', edit_flight_path(#flight) %> |
<%= link_to 'Back', flights_path %>
My new_seat partial /app/views/flights/_new_seat.html.erb :
<h1>New Seat</h1>
<%= form_for(seat) do |f| %>
<% if seat.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(seat.errors.count, "error") %> prohibited this seat from being saved:</h2>
<ul>
<% seat.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :flight_id %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :baggage %><br>
<%= f.text_field :baggage %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
If it is important - I made new_seat partial by myself through copying it from /app/views/seats/new.html.erb
And now I need to validate baggage amount to prevent my clients grab big bags. I wrote my /app/models/seat.rb this way:
class Seat < ActiveRecord::Base
belongs_to :flight
def validate
if baggage > flight.baggage_allowance
seat.errors.add(:base, message: "You have too much baggage")
end
end
end
But it is not working - there is no errors on site when clients enters big amounts in baggage field. What am I doing wrong?
validate method is already available(its a class method that ActiveRecord provides us to implement our custom validations). You can use that method which AR provides. something like the code below
validate :check_baggage_limit
def check_baggage_limit
if baggage > self.flight.baggage_allowance
self.errors.add(:base, "You have too much baggage")
end
end
You can add your custom validations in two ways.
First is Custom method like below
class Seat < ActiveRecord::Base
belongs_to :flight
validate :baggage_limit
def baggage_limit
if baggage > flight.baggage_allowance
errors.add(:base, message: "You have too much baggage")
end
end
end
Second is Custom Validators
class BaggageValidator < ActiveModel::Validator
def validate(seat)
if seat.baggage > seat.flight.baggage_allowance
seat.errors.add(:base, message: "You have too much baggage")
end
end
end
class Seat < ActiveRecord::Base
include ActiveModel::Validations
belongs_to :flight
validates_with BaggageValidator
end
You must have misread the http://guides.rubyonrails.org/active_record_validations.html#custom-validators

Rails: Displaying a nested list

I have a property listing site with the following relationship structure
Region has_many :cities
City has_many :investor_cities
City has_many :investors through: :investor_cities
Investor has_many :investor_cities
Investor has_many :cities through: :investor_cities
An investor_city records cities that an investor prefers to buy in. It will be used to match properties with investors.
In the show view for an investor, I want to show cities by region that they are interested in.
In the controller
#regions = Region.all
In the view
<% #regions.each do |r| %>
<p><%= r.name %></p>
<ul>
<% r.cities.each do |c| %>
<% #inv_cty = InvestorCity.find_by_city_id_and_investor_id(c.id, #investor.id) %>
<% if #inv_city %>
<%= #inv_city.city.name %>
<% end %>
<% end %>
</ul>
<% end %>
However no investor_cities are rendered (despite there being two in the database).
I also tried this:
r.cities.investor_cities.each do...
But got this error.
undefined method `investor_cities' for #<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_City:0x007fb47e09b4d0>
Am at a bit of a loss so any ideas would be great. Thanks.
Edit - the below has worked. I would love to know how to do it a bit cleaner however. Thanks
<% #regions.each do |r| %>
<p><%= r.name %></p>
<ul>
<% r.cities.each do |c| %>
<% c.investor_cities.each do |ic| %>
<% if ic.investor_id == #investor.id %>
<li><%= ic.city.name %> <%= ic.investor.id %></li>
<% end %>
<% end %>
<% end %>
</ul>
<% end %>
Instead of use r.cities.investor_cities.each do...,
try using r.cities.select('investor_cities').where(investor_id: #investor.id).each do ...

How to have a polymorphic model's partials associate to different things on the same page

Essentially, I have a binary voting system Like/Dislike. Thee class is called Like It has polymorphic associations to likeable:
class Like < ActiveRecord::Base
belongs_to :likeable, polymorphic: true
end
and we have the class Comment, which also has polymorphic associations to commentable and can be liked
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
has_many :likes, :as :likeable
end
We have the class Section, which can also be liked and commented on
class Section < ActiveRecord::Base
has_many :likes, as: :likeable
has_many :comments, as: commentable
end
However, on the page section#show I display the Section information, the section likes, and then the comments (from a comments/comments partial). Here is the Section#show view:
<h1><%= exercise.name %></h1>
<p><%= exercise.description %></p>
<%= render 'likes/like_button' %>
<%= render 'comments/comments' %>
<%= render 'comments/comment_form' %>
However, I want the ability to vote on each comment.
The following code is from the _comments.html.erb - What currently doesn't work is the rendering of the _like_button.html.erb because it doesn't apply to the comment at hand.
<% #comments.each do |comment| %>
<%= comment.content %>
<%= render 'likes/like_button' %>
<hr />
<% end %>
And here is the _like_button.html.erb partial
<% if #like.nil? %>
<%# No record of Like in table %>
<%= form_for [#likeable, Like.new] do |f| %>
<%= f.submit "Like" %>
<%= f.submit "Dislike" %>
<% end %>
<% else %>
<%# Marks current chosen option, if the opposite option is chosen, the record is updated to reflect the descion by the user %>
<%= form_for [#likeable, #like] do |f| %>
<% if #like.is_liked %>
Currently Liked!
<%= f.submit "Dislike" %>
<% else %>
<%= f.submit "Like" %>
Currently Disliked!
<% end %>
<% end %>
<% end %>
So ultimately, I just want to know how to make it possible to vote on a comment from within the Section#show view
Thanks!
Try this:
<% #comments.each do |comment| %>
<%= comment.content %>
<%= render 'likes/like_button', :like => comment.like, :likeable => comment %>
<hr />
<% end %>
<% if like.nil? %>
<%# No record of Like in table %>
<%= form_for [likeable, Like.new] do |f| %>
<%= f.submit "Like" %>
<%= f.submit "Dislike" %>
<% end %>
<% else %>
<%# Marks current chosen option, if the opposite option is chosen, the record is updated to reflect the descion by the user %>
<%= form_for [likeable, like] do |f| %>
<% if like.is_liked %>
Currently Liked!
<%= f.submit "Dislike" %>
<% else %>
<%= f.submit "Like" %>
Currently Disliked!
<% end %>
<% end %>
<% end %>

Rails Geocoder - Still learning

I'm sure it's a simply issue due to me not fully understanding how bits fit together in Rails...
I've followed the rails cast but I'm having trouble implementing it into my app (I've had it working stand-alone).
The error I get is
undefined method `nearbys'
Here's what I've got:
user.rb
geocoded_by :full_address
after_validation :geocode
def full_address
[address1, address2, address3, city, country, postcode].compact.join(', ')
end
users_controller.rb
def index
#title = "All users"
if params[:search].present?
#users = User.near(params[:search], 50, :order => :distance)
else
#users = User.all
end
end
index.html.erb
<h3>Nearby locations</h3>
<ul>
<% for user in #users.nearbys(10) %>
<li><%= link_to user.address1, user %> (<%= user.distance.round(2) %> miles)</li>
<% end %>
</ul>
_sidebar.html.erb
<%= form_tag users_path, :method => :get do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search Near", :name => nil %>
</p>
<% end %>
Thanks
If I comment out the .nearbys
<% for user in #users#.nearbys(10) %>
<li><%= link_to user.latitude, user %> (<%= user.distance.round(2) %> miles)</li>
<% end %>
The search works. Could this be a problem with the install of geocoder?
The function nearbys is a function on your model, not on a collection of models. The variable #users contains a collection of User models. You need to call the function on a single model instance, for example for each user in #users.
As an example, but not sure if you really want this:
<% #users.each do |user| %>
<% user.nearbys(10).each do |near_user| %>
<li><%= link_to near_user.latitude, near_user %> (<%= near_user.distance.round(2) %> miles)</li>
<% end %>
<% end %>
(Note that I also changed the for user in #users to use #users.each do |user|, which is more "Rubyish".)

How can I refactor out needing so many for-loops in rails?

I need help refactoring this multi-loop thing. Here is what I have:
Campaign has_many Contacts
Campaign also has many Models which are templates: (Email, Call, and Letter).
Because I am looking for overdue on each, I created an array called Event which I'd like to loop through that contains ['email', 'call', 'letter'].
I need a list of all the Emails, Calls and Letters that are "overdue" for every Contact that belongs to a Campaign. Overdue is determined by a from_today method which looks at the date the Contact was entered in the system and the number of days that needs to pass for any given Event. from_today() outputs the number of days from today that the Event should be done for a given Contact.
Here is what I've done, it works for all Emails in a Campaign across all contacts. I was going to try to create another each do loop to change the class names.
Wasn't sure where to begin: named_scope, push some things into a method, etcetera, or -- minimum to be able to dynamically change the class names so at least it loops three times across the different events rather than repeating the code three times:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<% #events.each do |event| %>
<%= event %>
<% for email in campaign.emails %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<% for contact in campaign.contacts.find(:all, :order => "date_entered ASC" ) %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Just to add to Patrick's answer, I would also use the :collection option of render to simplify this a bit further, e.g. have a partial _contact.html.erb to render each contact:
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
and then render the contacts collection with
= render :partial => "contact", :collection => #contacts
I also wouldn't do a find in the view, instead I would setup all the variables in the controller, and probably move all the conditional code into a helper. It's preferable to keep as much logic as possible out of the views.
I'd put the output for each resource into a partial, like so:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<%= render 'events', :events => campaign.events %>
<% end %>
then in app/views/campaigns/_events.html.erb
<% events.each do |event| %>
<%= event %>
<%= render 'emails', :emails => event.emails %>
<% end %>
then in app/views/campaigns/_emails.html.erb
<% emails.each do |email| %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<%= render 'contacts', :contacts => email.contacts.all(:order => "date_entered ASC", :email => email) %>
<% end %>
then in app/views/campaigns/_contacts.html.erb
<% contacts.each do |contact| %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>

Resources