Rails Geocoder - Still learning - ruby-on-rails

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".)

Related

Updating attributes from check_box

I'm using nested models in my first Ruby on Rails app and now I've run into a problem. I have a Survey model that has_many :questions which in turn belongs_to :survey and the model Question has_many :answers. My Answer model then belongs_to :question.
Now I want to update a boolean attribute :guess within my :answers controller. To this end I've created a new action :quiz_guess which looks like this:
def quiz_guess
Answer.update(params[:id], guess: false)
puts("Guess saved")
redirect_to(:action => 'show', :id => #survey.next, :survey_id => #survey.id)
end
my view looks like this:
<%= form_tag quiz_guess_questions_path, :method => :put do %>
<% for question in #survey.questions do %>
<li><%= h question.content %></li>
<% for answer in question.answers do %>
<li><%= h answer.content %>
<%= form_for :guess do |f| %>
<%= f.check_box(:guess, :method => :put) %>
<% end %>
</li>
<% end %>
<% end %>
<% end %>
Obviously I've read the documentation, as well as this blog post, but I don't understand how to implement it in my controller and view.
With this code I'm not able to update the correct :guess attribute but rather I update a different one (with a different :id). Can anybody shed some light as to what I do wrong?
I ended up looking at this Railscast where Ryan Bates shows how to update multiple attributes with fields_for. This is my new view:
<%= form_tag quiz_guess_questions_path, :method => :put do %>
<% for question in #survey.questions do %>
<li><%= question.content %></li>
<% for answer in question.answers do %>
<li>
<%= fields_for "answers[]", answer do |f| %>
<%= answer.content %>
<%= f.check_box :guess %>
<% end %>
</li>
<% end %>
<% end %>
<%= submit_tag "Guess" %>
<% end %>
and my new controller:
def quiz_guess
Answer.update(params[:answers].keys, params[:answers].values)
flash[:notice] = "Guess saved successfully."
redirect_to questions_url
end

Best way to define global objects in Ruby on Rails

I have an app that has two models Category and Product
Products belong to categories.
I have created a navbar dropdown that requires an #category and #product object to be available across the entire app (the navbar is shown on every page of the application.)
What I can't work out is the best place to put these basic definitions without defining them multiple times in every page definition.
I need to define the following:
#category = Category.all
#products = #category.products.all
The navbar loop will then look something like this.
<% #category.each do |c| %>
<%= c.name %>
<% #products.each do |p| %>
<% link_to product_path(p) do %>
<%= p.name %>
<% end %>
<% end %>
<% end %>
I am a bit of a rails newbie so I am sure there are some errors in here but any help would be much appreciated!
If you need them in every single page of app, you can set them in ApplicationController's before_filter:
class ApplicationController
before_filter :get_categories
# ...
private
def get_categories
#categories = Category.includes(:products)
end
end
then, you can write in your view:
<% #categories.each do |category| %>
<%= category.name %>
<% category.products.each do |product| %>
<%= link_to p.name, p %>
<% end %>
<% end %>
I also fixed some other errors and convention incompatibilities.
The following code is incorrect.
#category = Category.all
#products = #category.products.all
This code assigns to #categories all the categories, then it attempts to fetch the products. It will not work, unless you have defined a products class method in the Category model. But I don't think so, otherwise you will just have to call Product.all.
Moreover, in the code below, you are trying to display the list of products per category, which definitely don't work with the two assignments before. According to what you are trying to achieve, you can't pre-assign the #products, because you want the products for a specific category.
Let's inline everything into the code.
<% Category.all.each do |category| %>
<%= category.name %>
<% category.products.each do |product| %>
<%= link_to product_path(product) do %>
<%= product.name %>
<% end %>
<% end %>
<% end %>
Next step is to make the code a little bit more performant, giving you need it everywhere.
<% Category.select(:id, :name).each do |category| %>
<%= category.name %>
<% category.products.select(:id, :name).each do |product| %>
<%= link_to product_path(product) do %>
<%= product.name %>
<% end %>
<% end %>
<% end %>
You could use pluck, but it will return an array and it will require a little bit more manipulation. However, it's way more performant.
<% Category.pluck(:id, :name).each do |category_id, category_name| %>
<%= category_name %>
<% Product.where(category_id: category_id).pluck(:id, :name).each do |product_id, product_name| %>
<%= link_to product_name, product_path(id: product_id) %>
<% end %>
<% end %>
It's not a good idea to chain all those methods inside a view, let's extract some code into the model.
class Category
def self.simple_listing
order(:name).pluck(:id, :name)
end
end
class Product
def self.simple_category_listing(category_id)
where(category_id: category_id).order(:name).pluck(:id, :name)
end
end
<% Category.simple_listing.each do |category_id, category_name| %>
<%= category_name %>
<% Product.simple_category_listing(category_id).each do |product_id, product_name| %>
<%= link_to product_name, product_path(id: product_id) %>
<% end %>
<% end %>
You can leave all this code into the view, or extract it into a partial. You don't even need to add a controller before filter, or make it "global". The code is self-contained, does not pollute the name space with instance variables, and it can easily be placed whenever you need it.

rails: display unique records in each loop

I have a product display page which is displaying all products on website. Here I want to filter products as per their Owner. As a start, i am displaying owner names on page using each loop:
<% #products.each do |p| %>
<%= link_to p.user.profile.first_name, store_index_path %>
<% end %>
But as owner has multiple products, his name gets displayed multiple times. How to show the name only once?
In simple way you can do like this:
<% #products.map(&:user).uniq.each do |u| %>
<%= link_to u.profile.first_name, store_index_path %>
<% end %>
You can use group_by to create a hash of User => [Products]. Then, you iterate through the unique set of Users, display the information about that owner, and then display each product for that User.
<% products_by_owner = #product.group_by(&:user) %>
<% products_by_owner.keys.each do |user| %>
<%= link_to p.user.profile.first_name, store_index_path %>
<% products_by_owner[user].each do |product| %>
<%= link_to product.name, store_index_path %>
<% end %>
<% end %>
You can use group_by to create a hash with the users as keys and arrays of products as values:
# Eager loading of users will prevent multiple database hits.
# Using find:
#products = Product.find(:all, :include => :user).group_by(&:user)
# using relations:
#products = Product.where(:some_condition => 'etc').includes(:user).group_by(&:user)
In the view:
<% #products.each do |user, user_products| %>
<%= link_to p.user.profile.first_name, store_index_path %>
<% user_products.each do |product| %>
...
<% end %>
<% end %>

Ruby/Rails Conditional search method (if ... else "no results found")

Here is the code of a "simple search form" (thanks to jordinl) which I try to improve. I would like to add the case if there is no match found.
So, there is the view (views/users/index.html.erb)
<% form_tag users_path, :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<% end %>
<% #users.each do |user| %>
<p><%= link_to "#{user.name}", user %></p>
.
.
.
<% end %>
The controller ( users_controller.rb)
def index
#users = User.search(params[:search])
end
and the method in user model:
def self.search(search)
search.blank? ? [] : all(:conditions => ['name LIKE ?', "%#{search.strip}%"])
end
I tried the following:
def self.search(search)
if search.to_s.size < 1
[]
else
if #users.size > 0
all(:conditions => ['name LIKE ?', "%#{search.strip}%"])
else
render :text => "No result found"
end
end
end
reporting the following error: "You have a nil object when you didn't expect it!..." (no instance in the array). Then, I tried to add
<% if #users? %>
<% #users.each do |user| %>
.
.
.
In the view. But it doesn't work either.
I would be pleased to understand why I'm wrong on this. Thank you!
You're on the right track. Try this:
<% if (#users) %>
<% if (#users.empty?) %>
<p>No users found.</p>
<% else %>
<% #users.each do |user| %>
<p><%= link_to "#{user.name}", user %></p>
<% end %>
<% end %>
<% else %>
<p>Use the search form to search for stuff.</p>
<% end %>
Change your search method to only return something if searching was used:
def self.search(search)
search.present? and all(:conditions => [ 'name LIKE ?', "%#{search.strip}%" ])
end
you can't render in your model.
In your view:
<% form_tag users_path, :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<% end %>
<% if #users.empty? %>
No records found!
<% else %>
<% #users.each do |user| %>
<p><%= link_to "#{user.name}", user %></p>
<% end %>
<% end %>
In your model:
def self.search(search)
search.blank? ? [] : all(:conditions => ['name LIKE ?', "%#{search.strip}%"])
end
You are close with your check on #users? but it should be:
<% if !#users.empty? %>
<% #users.each do |user| %>
...
<% end %>
<% else %>
No users found.
<% end %>
This will work with your original self.search implementation which, correctly, is used just to find users and does not need to worry about reporting if none are found.
Your self.search method should return an array, either full or empty. Try:
in you model
def self.search
self.all(:conditions => ['name LIKE ?', "%#{search.strip}%"])
end
and in your view
<% if #users? %>
<% #users.each do |user| %>
…
<% end %>
<% else %>
No result
<% end %>

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