I'm writing a simple chat, and I need to list out users online. I don't use devise
for authentication, there's a custom user
model which authenticates via omniauth
.
user.rb
class User < ActiveRecord::Base
has_many :messages, dependent: :delete_all
class << self
def from_omniauth(auth)
provider = auth.provider
uid = auth.uid
info = auth.info.symbolize_keys!
user = User.find_or_initialize_by(uid: uid, provider: provider)
user.name = info.name
user.avatar_url = info.image
user.profile_url = info.urls.send(provider.capitalize.to_sym)
user.save!
user
end
end
end
application_controller.rb
def current_user
@current_user ||= User.find_by(id: cookies[:user_id]) if cookies[:user_id]
end
helper_method :current_user
I tried to do that in such way: add to application_controller.rb
a show_online
method:
def show_online
@users = User.where(status: online)
end
helper_method :online_users
and then add to a view:
<%= online_users.each do |user| %>
<ul>
<li><%= user.name %></li>
</ul>
<%end%>
but it throws an exception ActionView::Template::Error (undefined method 'online_users' for #<MessagesController:0x007f52d7f82740>)
source code here
EDIT
the best solution as for me I found here, but I completely don't get how to implement it correctly :( But that's definitely what I need
It should be <% %>
not <%= %>
<% @users.each do |user| %>
<ul>
<li><%= user.name %></li>
</ul>
<% end%>
Secondly
but also you need to check whether @users
is nil so nil.each each will throw that error ActionView::Template::Error (undefined method 'each' for nil:NilClass)
so it will be look like
<% if @users %>
<% @users.each do |user| %>
<ul>
<li><%= user.name %></li>
</ul>
<% end%>
<% end %>
or in controller
def show_online
@users = User.where(status: 'Online')
end
and
<% @users.each do |user| %>
<ul>
<li><%= user.try(:name) %></li>
</ul>
<%end%>
why I choose where not find all
Your application controller code is wrong:
class ApplicationController < ActionController::Base
def show_online
@users = User.where(status: 'online')
end
helper_method :online_users
end
Should be:
class ApplicationController < ActionController::Base
def online_users
@users ||= User.where(status: 'online')
end
helper_method :online_users
end
EDIT:
The error you get can be solved in one of two ways.
You can use helper methods and call them from your view, as it seems you want to do.
Or, you can avoid using them completely and just call the show_online method from whichever method is currently being when the view is loaded. If you're going to the show
, it'll be the show method, and so on and so on.
Your own answer does correctly fix the errors using the first method, but I recommend this way.
What needs to be done to implement these fixes:
Call show_online when the new
is being loaded so that view can access the @users variable. We can do that with before_action
In the view, you have a loop which iterates over online_users
, but it should iterate over @users
In the that same loop in the view, you have a simple syntax error. The first line starts with <%=
but should start with <%
without the =. This should be changed no matter what way you write the code.
So all together the code is:
application_controller.rb
#put this line at the top of the controller, just below the line ApplicationController:: .....
before_action :show_online, only: [:new]
def show_online
@users = User.where(online: true)
the view file
<% @users.each do |user| %>
<ul>
<li><%= user.name %></li>
</ul>
<% end %>
Why this method?
- Making one method to get the online users means the logic is only one one place
- Having your logic/code in one place means you don't ever repeat yourself, and you know where to look if a problem occurs
- Using before_action means the call won't be made unless it is needed
- If you later add pages which need to get the list of online users, you just add those to the list of methods in the brackets here:
only: [:new]
- When choosing between placing logic in a view or in a controller, the correct answer is almost always the controller
From the error message it seems like your @users is not an array or ActiveRecord::Relation.
I would print out the @users on the view to debug. Also, find(:all, :conditions => ["status = ?", "online"])
is not the preferable way to query.
Use User.where(:status => "online")
. Reference - http://guides.rubyonrails.org/active_record_querying.html#conditions