Why am i getting a NoMethodError on this? - ruby-on-rails

I have this in my config/routes.rb:
get '/:category/:region', to: 'categories#filtered_by_region'
The filtered_by_region action is as shown below:
#filtered_by_region method
def filtered_by_region
#region = Region.where(title: params[:region]).first
#category = Category.where(title: params[:category]).first
#teams = Team.where(region_id: #region.id, category_id: #category.id)
end
I have a view filtered_by_region.html.erb that looks as follows:
Region: <%= #region.title %>
Category: <%= #category.title %>
<% #teams.each do |team|%>
<%=team.title %>
<% end %>
region.rb model is as follows:
class Region < ActiveRecord::Base
has_many :teams
attr_accessible :title
end
category.rb model is as follows:
class Category < ActiveRecord::Base
has_many :teams
attr_accessible :title
end
team.rb model is as shown below
class Team < ActiveRecord::Base
belongs_to :category
belongs_to :region
end
I also have the corresponding regions, teams and categories tables already populated with data.
when i enter a url that looks like this:
http://localhost:3000/football/south_west
i get an error with the following message:
undefined method ``title' for nil:NilClass I have realized both the #region and #category are returning nil but i do not understand why. i do have a category with football title and a region with south_west title in categories and regions tables respectively.

Why don't you use find_by (if you're using Rails 4) or find_by_title (if you're using Rails 3):
def filtered_by_region
#category = Category.find_by_title(params[:category])
#region = Region.find_by_title(params[:title])
if defined?(#category) && defined?(#region)
#teams = Team.where(region_id: region.id, category_id: category.id)
else
redirect_to root_path
end
end
I think the likely issue will either be your query is not finding any records, or you'll be trying to access a collection as a record (regardless of the use of .first)

Related

How to dispaly a login student written message in ruby on rails

I want to display login student name and message.
after login student can write messages and send related to courses.the messages he send is displayed above in same page with his/her name and message
I got name, but message field fetches all messages that are in database. How to display a particular student name and message?
Here is my code
controller.erb
class CourseQueriesController <ApplicationController
def index
#course_queries = CourseQuery.all
#course_query = CourseQuery.new
end
def create
#course_query = CourseQuery.new(student_id: current_student.id, coach_id: "2", message: params[:course_query][:message])
if #course_query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
end
course_queries/index.html.erb
<% #course_queries.each do |queries| %>
<p><b><%= current_student.name %></b></p>
<%= queries.message %>
<% end %>
<%= simple_form_for (#course_query) do |f| %>
<%= f.input :message %>
<%= f.button :submit , "Send or press enter"%>
<% end %>
how to display a particular student name and message
You need to have the relevant associations established in your models, like what Pavan wrote.
I'll give you some more information on why this is important...
ActiveRecord
One of the main reasons Rails works so well is the way it helps you create & manage objects. In OOP, objects form everything from your init commands to your user input responses, Ruby being a prime exponent of this structure.
Rails is built on Ruby, and therefore is object orientated too. It uses ActiveRecord, the MVC structure & classes to give you a platform from which you can populate and manipulate objects:
Thus, you shouldn't be treating your application's interactions as a way to edit a database, or "display a login message" - it should be a way to invoke & manipulate objects.
Objects - in the case of Rails - are built in the models. The model data can then be used in the controllers and views.
This seems to be lacking in your code. If you can remedy it, your code will become a lot simpler and more powerful...
Associations
I'd do something like this:
#app/models/student.rb
class Student < ActiveRecord::Base
has_many :queries
has_many :coarse_queries, through: :queries
end
#app/models/course.rb
class Course < ActiveRecord::Base
has_and_belongs_to_many :coaches
has_many :queries
has_many :student_queries, through: :queries
end
#app/models/coach.rb
class Coach < ActiveRecord::Base
has_and_belongs_to_many :courses
has_many :queries
end
#app/models/query.rb
class Message < ActiveRecord::Base
belongs_to :course
belongs_to :student
belongs_to :coach (maybe)
end
This structure will allow a student to send queries to specific courses, selecting the coach as necessary. Importantly, this sets up your associations so that you don't have to invoke multiple classes each time you want to populate the various objects.
#app/controllers/course_queries_controller.rb
class CourseQueriesController <ApplicationController
def index
#queries = Query.all
#query = current_student.queries.new
end
def create
#query = current_student.queries.new query_params
if #query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
private
def query_params
params.require(:query).permit(:message).merge(coach_id: "2")
end
end
#app/views/queries/index.html.erb
<% #queries.each do |query| %>
<p><b><%= query.student.name %></b></p>
<%= query.message %>
<% end %>
<%= simple_form_for #query do |f| %>
<%= f.input :message %>
<%= f.button :submit , "Send or press enter"%>
<% end %>
You should add has_many :course_queries to the Student model
#student.rb
class Student < ActiveRecord::Base
has_many :course_queries
...
end
And in the controller in index method change #course_queries = CourseQuery.all to #course_queries = current_student.course_queries
Now <%= queries.message %> will only display the course_query's message of the current_student

Nested models' views with Geocoder

I'm trying to make a simple app that records your trips. In should get two locations (via nested model) and then show distance between them in /show~
Unfortunately, I can make a new trip, but I can't get the view for the distance. However, if I want to put 'location.address' it doesn't work too.
trip controller:
def index
#trips = Trip.all
#trip = Trip.new
2.times do
location = #trip.locations.build
end
end
def show
#trip = Trip.includes(:locations).find(params[:id])
end
location model:
class Location < ActiveRecord::Base
belongs_to :trip
extend Geocoder::Model::ActiveRecord
geocoded_by :address
after_validation :geocode, :if => :address_changed?
end
trip model:
class Trip < ActiveRecord::Base
has_many :locations
accepts_nested_attributes_for :locations
end
show view for trips:
<% for location in #trip.locations %>
<li><%= location.distance %></li>
<% end %>
I'd appreciate any help.
Edit:
Ok, so I found how to calculate distance between specific places, but I don't know how to add two locations from one nested model to this code?:
#distance = Geocoder::Calculations.distance_between([place1], [place2])

Include associated model for all objects (in index action)

I am trying to develop ratings for my application, where a User is able to set a specific rating for a comment. I have followed the following tutorial in order to do so.
Here are my associations:
class Rating < ActiveRecord::Base
belongs_to :comment
belongs_to :user
end
class Comment < ActiveRecord::Base
has_many :ratings
belongs_to :user
end
class User < ActiveRecord::Base
has_many :ratings
has_many :comments
end
My problem here is that, in the index action of my comments controller, I need to include the rating that the user has done for that comment. In the tutorial is just shown how to select a particular rating by doing this:
#rating = Rating.where(comment_id: #comment.id, user_id: #current_user.id).first
unless #rating
#rating = Rating.create(comment_id: #comment.id, user_id: #current_user.id, score: 0)
end
However, I will have several ratings, because in my controller I have:
def index
#comments = #page.comments #Here each comment should have the associated rating for the current_user, or a newly created rating if it does not exist.
end
You want to find the comment's rating where the rating's user_id matches the current user.
<%= comment.ratings.where(user_id: current_user.id).first %>
However this sort of logic is pretty cumbersome in the views, a better strategy would be to define a scope in Rating that returns all ratings made by a specific user.
class Rating
scope :by_user, lambda { |user| where(user_id: user.id) }
end
class Comment
# this will return either the rating created by the given user, or nil
def rating_by_user(user)
ratings.by_user(user).first
end
end
Now in your view, you have access to the rating for the comment created by the current user:
<% #comments.each do |comment| %>
<%= comment.rating_by_user(current_user) %>
<% end %>
If you want to eager load all ratings in your index page, you can do the following:
def index
#comments = page.comments.includes(:ratings)
end
You can then find the correct rating with the following:
<% #comments.each do |comment| %>
<%= comment.ratings.find { |r| r.user_id == current_user.id } %>
<% end %>
This would return the correct rating without generating any extra SQL queries, at the expense of loading every associated rating for each comment.
I'm not aware of a way in ActiveRecord to eager load a subset of a has_many relationship. See this related StackOverflow question, as well as this blog post that contains more information about eager loading.

Rails has_many relationship find name based on foreign key

I have two models:
class country < ActiveRecord::Base
has_many :companies
end
class company < ActiveRecord::Base
belongs_to :country
end
In my view index for company I can show which country the company belongs to by showing the following:
<%= company.country_id %>
This will show me the id number it's associated with, but I can't seem to work out how to resolve this back to the country name which is country.name, everything I seem to try crashes rails, I don't think I'm approaching the problem the correct way?
<%= company.country.try(:name) %>
really ought to do what you want.
Edited as comment suggested.
I wouldn't recommend using #try though. It is much better when you tell Rails to give you the filtered collection, that's what controllers are for.
In this case you'd have
class CompaniesController < ApplicationController
def index
#companies = Company.where('country_id IS NOT NULL') #if using the relational db
# or #companies = Company.all.select { |company| company.country.present? }
end
end
and in your view
<% #companies.each do |company| %>
<%= company.country.name %>
<% end %>
Update:
Even better aproach is to create a named scope in your model for such things.
class Company < ActiveRecord::Base
#...
scope :with_country, ->() { where('country_id IS NOT NULL') }
end
Now you can change your controller action
def index
#companies = Company.with_country
end
This will make your code much more consistent and readable.

Understanding associations in rails 3

Seems I need to brush up on my associations in rails. At present I am trying to display all posts that have the department name as staff.
two models exist at present, posts and departments
class Post < ActiveRecord::Base
belongs_to :department
attr_accessible :title, :comments, :department_id
end
class Department < ActiveRecord::Base
has_many :posts
attr_accessible :name, :post_id
#Scopes
scope :staff_posts, where(:name => "Staff")
end
So i want to display all posts that have the department name staff
to do this i have put this in my controller
class PublicPagesController < ApplicationController
def staffnews
#staffpost = Department.staff_posts
end
end
In my view i am trying to display all these posts like so
<% #staffpost.each do |t| %>
<h2><%= t.title %>
<h2><%= t.comments %></h2>
<% end %>
Clearly going wrong somewhere as i get undefined method nil, even though i have 3 posts with the name 'Staff'
Can someone please explain where i am misunderstanding the association as would love to get this right
EDIT
Routes
scope :controller => :public_pages do
get "our_news"
match "our_news/staffnews" => "public_pages#staffnews"
In controller it returns department with name staff. And you are using title and comments on on department objects thats why its giving nil method error.
Use like this:
def staffnews
#dept_staff = Department.staff_posts
end
<% #dept_staff.each do |ds| %>
<% ds.posts.each do |p| %>
<h2><%= p.title %></h2>
<h2><%= p.comments %></h2>
<% end %>
<% end %>
or
In post model create named_scope
class Post < ActiveRecord::Base
belongs_to :department
attr_accessible :title, :comments, :department_id
scope :staff_posts, :include => :department, :conditions => {"departments.name" => "Staff"}
end
class Department < ActiveRecord::Base
has_many :posts
attr_accessible :name, :post_id
end
Controller:
def staffnews
#staffpost = Post.staff_posts
end
View: #No change
<% #staffpost.each do |t| %>
<h2><%= t.title %></h2>
<h2><%= t.comments %></h2>
<% end %>
Your staff_posts scope is only selecting the Departments with the name "Staff". Assuming you will have one and only one department named staff, you have a few ways to handle this.
This will find all departments with the name staff, and eager load the posts that go along with it:
#department = Department.where(name: "Staff").include(:posts).first
Since you are trying to scope Post, however, this belongs in Post. Here's an example using a method as scope:
class Post < ActiveRecord::Base
belongs_to :department
attr_accessible :title, :comments, :department_id
def self.staff
where(department_id: staff_department_id)
end
def staff_department_id
Department.find_by_name!("Staff").id
end
end
This way, you can use #staff_posts = Post.staff and iterate over that collection (Note: I don't recommend getting staff_department_id this way permanently. This could be set to a constant when the app boots up, or some other more robust solution).
You can find the all the posts that have the department name staff by following changes:
class PublicPagesController < ApplicationController
def staffnews
#get all the department which have name is staff
departments = Department.where("name=?","staff")
#get all the ids
department_ids = departments.map(&:id)
#retrieve post that department name is staff
#staffpost = Post.find_by_department_id(department_ids)
end
end

Resources