Rails display table alphabetically - ruby-on-rails

I want to display the entirety of the user table alphabetically, I know this needs to be done in the controller:
The code which I have been using in my controller :
class UsersController < ApplicationController
#users = User.find(:all)
#users.sort! { |a,b| a.name.downcase <=> b.name.downcase }
...
end
form code :
<% #users.each do |user| %>
<tr>
<td><%= user.name %></td>
</tr>
<% end %>
Receiving the error undefined method `downcase' for nil:NilClass
What have I missed? Thank you.

You should add an order clause to your query.
#users = User.reorder(:name)
I am not sure what you are going for but if you have a significant number of users you should probably add a limit or pagination to this.

You have a user without name, that's why you got that error.
You can avoid that error with
#users.sort! { |a,b| a.name.try(:downcase) <=> b.name.try(:downcase) }
But it is a ugly solution, the correct way is doing that directly on the query
User.order("name")

Actually I think this should belongs to model, I believe that will make your controller code more readable.
and having scope will make your conditions chain able, read more about scope
assuming u have rails 3.x
#users model
class User < ActiveRecord::Base
scope :asc_by_name, lambda {
{:order => 'name ASC'}
}
end
#users controller
#users = User.asc_by_name
and in your loop use,
<% #users.each do |user| %>
<tr>
<td><%= user.try(:name) %></td>
</tr>
<% end %>

Your database engine is usually better suited for sorting so I would sort in the query:
#user = User.order(:name)
will give you all User objects sorted by the name column. This would also work the same:
#user = User.find(:all, order: :name)

Related

Defining a method

I am new to coding & I am taking ruby on rails online class. I have followed the lecture and documented everything but I am getting "NonMethod" error. Here what I have in my file
Controller
class CoursesController < ApplicationController
def index
#search_term = 'jhu'
#courses = Coursera.for(#search_term)
end
end
Model
class Coursera
include HTTParty
base_uri 'https://api.coursera.org/api/catalog.v1/courses'
default_params fields: "smallIcon,shortDescription", q: "search"
format :[enter image description here][1]json
def self.for term
get("", query: { query: term})["elements"]
end
end
Views
<h1>Searching for - <%= #search_term %></h1>
<table border="1">
<tr>
<th>Image</th>
<th>Name</th>
<th>Description</th>
</tr>
<% #courses.each do |course| %>
<tr class=<%= cycle('even', 'odd') %>>
<td><%= image_tag(course["smallIcon"])%></td>
<td><%= course["name"] %></td>
<td><%= course["shortDescription"] %></td>
</tr>
<% end %>
</table>
These are the messages I am getting
NoMethodError in Courses#index
Showing /Users/Dohe/my_app/app/views/courses/index.html.erb where line #11 raised:
undefined method `each' for nil:NilClass
Can any help me with what I am doing wrong
Ruby 2.2.9 and Rails 4.2.3
Check what #courses contains in the controller, and change <% end %> to <% end if #courses.present? %> in your view, Its just making sure to only try to iterate through the #courses and populate it in the view if it actually contains any data, if it is nil then nil does not have a each method defined for it so you're getting the
undefined method `each' for nil:NilClass
As Subash stated in the comment, #courses is nil, which means that:
get("", query: { query: term})["elements"]
is returning nil. So, when you try #courses.each, you're getting the NoMethod error.
If you expect:
get("", query: { query: term})["elements"]
not to be nil, then you'll have to debug that. You could show us your console logs and we might be able to help with that.
Also, to protect from the NoMethod error, you could do:
def self.for term
get("", query: { query: term})["elements"] || []
end
This says, essentially, "return an empty array if get("", query: { query: term})["elements"] is nil". This will resolve your error, but could mask other problems you might be having. So, proceed with caution.

undefined method `order' for "1":String

On my landing_pages "show" page, I'm trying to show the leads that came in via that page and sort them via their "score".
My landing_page model has:
has_many :page_leads
My page_lead model has:
belongs_to :landing_page
In my index method within my page_leads controller, I have this, which works:
def index
#page_leads = PageLead.order(score: :desc)
end
I try to duplicate this in the landing_pages controller for the show method. Here is what I have:
def show
#landingpage = LandingPage.find(params[:id]).order(score: :desc)
end
When I try to go to the "Show" page, I get an error "undefined method `order' for "1":String". How can I get this to work?
My show page has the following code:
<tbody>
<% #landingpage.page_leads.each do |page_lead| %>
<tr>
<td><%= page_lead.fname %></td>
<td><%= page_lead.lname %></td>
<td><%= page_lead.score %></td>
</tr>
<% end %>
</tbody>
It is just:
def show
#landingpage = LandingPage.find(params[:id])
end
Cause find returns the LandingPage with id == params[:id]. No need to sort a single item.
find returns an array or records if the params[:id] is an array.. the order can't be applied to the array, because order isn't part of the array class.
If params[:id] is a single element then the find returns the record. Again order doesn't apply, because it's just one record not a container of records..
If you want an sorted list then use
LandingPage.where(id: params[:id]).order
But since this is in the show I suspect you want the single item... As spickermann said drop the order
LandingPage.find(params[:id])
If you want to sort Page Leads, you would do something like
def show
#landingpage = LandingPage.find(params[:id])
#page_leads = #landingpage.page_leads.order(score: :desc)
end
As #spickermann mentioned, .find() only returns one model instance. You can't order this using ActiveRecord query methods.
If you want to order the page leads, you have to call .order on the set of page_leads that belong to the landing page

How can I get access to foreign_key?

There are
a user-model/table,
a schedule-model/table and
a users_schedules-table.
The models has_and_belongs_to_many each other.
I can add a relationship with #user.schedules << #schedule in the controller.
How can I get access to the join-table 'users_schedules' ?
I want to show the users which has_and_belongs_to_many schedules:
I thought about something like this: schedules.users_belongs_to. As you can see in the view-code below.
view: (There is an example I want to add)
<table class="table table-hover">
<tbody>
<% #user_schedules_date.sort.each do |date_time, schedules| %>
<tr class="thead success">
<th colspan="4" scope="col"><p><%= date_time.strftime("%A, %d.%m.%Y") %></p></th>
</tr>
<% for schedule in schedules %>
<tr>
<th scope="row"><p><%= schedule.titel %></p></th>
<td><p><%= schedules.users_belongs_to #ALL USERS WHO ARE BINDED TO THIS SCHEDULE# %></p></td>
<td><p><%= schedule.date_time.strftime("%H:%M:%S") %></p></td>
<td><p><%= schedule.location %></p></td>
<td>
<p>
<%= link_to 'Bearbeiten', {:controller => 'schedule', :action => 'edit', :id => schedule.id} %>
oder
<%= link_to 'löschen', {:controller => 'schedule', :action => 'delete', :id => schedule.id} %>
</p>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
In the controller I tried the following, but I don't know how to replace/fit the placeholder (:email).
controller:
class WelcomeController < ApplicationController
def index
if(current_user)
#user_schedules = current_user.schedules
#user_schedules_date = #user_schedules.order(:date_time).group_by { |sched| sched.date_time.beginning_of_day }
#users_schedules_shared = User.find_by(:email) #HERE I NEED THE USER WHICH BELONGS_TO THIS SCHEDULE
end
end
end
I hope you can understand my problem.
Thanks for your help!
EDIT:
I gather all the data in the controller:
class WelcomeController < ApplicationController
def index
if(current_user)
#user_schedules = current_user.schedules
#user_schedules_date = #user_schedules.order(:date_time).group_by { |sched| sched.date_time.beginning_of_day }
#users_all = User.includes(user_schedules: :schedules)
end
end
end
and edit the view as the following:
<% #users_all.each do |user| %>
<% user.name %>
<% end %>
But I get the following error:
Association named 'user_schedules' was not found on User; perhaps you misspelled it?
I red this, as deyan said, but I dont understand it.
#users_all = User.includes(user_schedules: :schedules) <- returns an array ?!?? (If I understood it correctly)
So I need each array-item.name to show the Users name??
Can anyone tell me what I am doing wrong?
Database:
http://i.stack.imgur.com/Apg7y.png
users
schedules_users (join-table with fk)
schedules
I want to show the users which has_and_belongs_to_many schedules
This will use something called a many-to-many relationship - meaning that if you access the associated data through a model - you'll have an appended attribute / method to capture them.
So what you'd do is the following:
#app/models/schedule.rb
Class Schedule < ActiveRecord::Base
has_and_belongs_to_many :users
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_and_belongs_to_many :schedules
end
This will append a collection to each of these model objects, allowing you to call the collection as required:
#schedules = Schedule.all
#schedules.each do |schedule|
schedule.users #-> outputs a collection
schedule.users.each do |user|
user.name
end
end
Using includes with Rails is actually quite a bad thing, considering you can call ActiveRecord associations to do the heavy-lifting for you
--
has_and_belongs_to_many
Simply, you can't access the has_and_belongs_to_many table directly, as it has no primary_keys in place.
Rails basically uses the relational database infrastructure (through ActiveRecord) to access the associative data. This means that if you're using this particular type of table, you'll just be able to access the collection it provisions:
#app/models/user.rb
Class User < ActiveRecord::Base
has_and_belongs_to_many :schedules
end
#app/models/schedule.rb
Class Schedule < ActiveRecord::Base
has_and_belongs_to_many :users
end
This will allow you to access:
#user = User.find 1
#user.schedules #-> shows schedules collection. You will have to loop through this
If I understand you correctly, you want to show the schedules for your current user, and for each schedule to show what other users belong to it.
Based on your view, to get all users for a schedule, all you would need is to replace schedules.users_belongs_to withschedule.users.
If you want to get together all unique users for all the schedules the current user might have, then in the controller you could do current_user.schedules.collect{|s| s.users|}.uniq. This is only useful if you want to show all users, no matter which schedule each user belongs to.
I would advice you to gather all the data in the controller and then to print it in the view. You could join all tables in a single call to the DB, which might look something like this: User.includes(users_schedules: :schedules) but you would need to adapt it depending on what your models are called.
Methods 1 or 2 are solving your problem using your current code, but might be slow. I would suggest you read more here: http://guides.rubyonrails.org/active_record_querying.html and get all data at once (method 3).

Why is this so slow to load? Is there any technique to make this faster?

I'm using the gem called "Mailboxer" ( https://github.com/ging/mailboxer )
This enables messaging system within Rails app.
With this gem, I'm showing 10 received messages for each page.
I'm using Kaminari for pagination here.
But, it is kinda too slow with my codes.
It's issuing more than 25 sql at once :(
How can I make this faster? It takes more than 1500ms to show just 1 page.
Here are my codes
What's wrong with this? Is there any technique to make this faster?
controller
#number_of_messages_to_display = 10
#messages = current_user.mailbox.inbox.page(params[:page]).per(#number_of_messages_to_display)
#messages_count = current_user.mailbox.inbox.count
view(messages/index.html.erb)
<%= #messages_count.to_s %> messages in your received message box.
<table>
<% #messages.each do |m| %>
<tr>
<td><%= check_box_tag "id[]",m.id %></td>
<td><%= if m.is_read?(current_user) then "Read" else "Un-read" %></td>
<td><%= profile_link(m.recipients.first) if m.recipients.first != current_user %></td>
<td><%= link_to m.subject, show_messages_path(:id => m) %></td>
<td><%= today_datetime(m.last_message.created_at) %></td>
</tr>
<% end %>
</table>
view(helpers/application_helper.rb)
def profile_link(user)
if user
nickname = user.user_profile.try(:nickname)
username = user.try(:username)
link_to nickname, show_user_path(username)
else
"Un-known"
end
end
def today_datetime(date_time)
date_time.to_date == Date.current.to_date ? "<span class='text-info'>#{date_time.to_s(:us)}</span>".html_safe : date_time.to_s(:us)
end
routes.rb
get 'messages/:id' => 'messages#show', :as => :show_messages
get "users/:id" => 'users#show', :as => :show_user
models/user.rb
def to_param
"#{username}"
end
Classic example of the N + 1 problem.
You retrieve #messages = current_user.mailbox.inbox.page, which will retrieve records from the messages table.
In the view, you loop through them and check each message's recipients list (a has_many relationship, probably based on the receipts table, as can be seen here). So, for each message, you end up sending another query to the database.
You can correct this by retrieving the recipients together with the messages (and the last_message association as well, since you're using it):
#messages = current_user.mailbox.inbox.
includes(:receipt, :last_message).page
Also, you may have a different problem compounding this, as 25 queries should execute pretty quickly on a modern computer. I'd recommend using something like the RailsPanel tool to track down where the time is being spent.

How to display only those Rails records where a specific field is not empty?

I think I'm overlooking something very simple here, but would really appreciate some help to work out what it is.
In the project show view, I'm displaying associated (has_many) tasks in a partial. I only want to display those records where a particular field is not empty. My view code looks like this.
<% for task in #tasks %>
<% unless task.user.notes.empty? %>
<tr>
<td><%= task.user.name %></td>
<td><%= task.user.notes %></td>
</tr>
<% end %>
<% end %>
This is returning undefined method 'notes' for nil:NilClass. This is strange as :notes is definitely in the User model.
The Project controller handling this is contains:
def show
#tasks = #project.tasks.paginate(:page => params[:page])
end
My models look as follows
Project
has_many :tasks
end
Task
belongs_to :project
belongs_to :user
end
User
has_many :tasks
end
What have I missed here? Am I using empty? correctly? Or should I be handling this in the controller? I currently have three partials on the Project show, all using the same Task query. Performance and/or best practice -wise, does it make more sense to have all three partials sourcing data from the same controller query, or to have a sperate query just for this case?
Thanks for any pointers.
The problem was that your User model was undefined when you called task.user.notes.
You can solve this problem as well as improve your overall design by making use of the #delegate macro provided by ActiveSupport in Rails. For example, inside of the Task model, try adding
delegate :notes, :to => :user, :prefix => true, :allow_nil => true
This adds a task.user_notes method to the Task model which will allow you to fetch the User's notes straight from the Task itself. Additionally, because we specified the allow_nil option, if there is no User associated with the Task, the result of the method will be nil instead of an exception.
You can also add this for the name attribute of the User allowing you to call task.user_name from within the view.
Resources:
delegate - http://apidock.com/rails/v3.0.9/Module/delegate
"Law of Demeter" - http://devblog.avdi.org/2011/07/05/demeter-its-not-just-a-good-idea-its-the-law/
In the controller
def show
#tasks = #project.tasks.paginate(
:page => params[:page],
:conditions=>["notes is not ? and notes !=?",nil,'']
)
end
OR, not in the controller
Write a helper method to abstract this.
Add a new helper method
def filter_tasks(tasks)
tasks.find(
:all,
:conditions=>["notes is not ? and notes !=?",nil,'']
)
end
And use helper in view
<% for task in filter_tasks(#tasks) %>
<% unless task.user.notes.empty? %>
<tr>
<td><%= task.user.name %></td>
<td><%= task.user.notes %></td>
</tr>
<% end %>
<% end %>

Resources