How to show online users

2019-07-03 14:17发布

问题:

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

回答1:

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



回答2:

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


回答3:

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


回答4:

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