Skip to content

Instantly share code, notes, and snippets.

@logrusorgru
Last active December 16, 2022 02:54
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save logrusorgru/9867976 to your computer and use it in GitHub Desktop.
Save logrusorgru/9867976 to your computer and use it in GitHub Desktop.
Rails - How to check for online users? Redis.

Rails - How to check for online users? Redis.

Init Redis

   # config/initializers/redis.rb
    
   $redis_onlines = Redis.new
   # it's the simplest way, but i'd recommend:
   # $redis_onlines = Redis.new path: "/tmp/redis.sock", db: 15, driver: :hiredis
   # => hiredis driver more fast
   # => use unix domain socket if it's possible
   # => default db is No.0, but it is better to use a specific database
   #    and No.0 leave for testing-development-and-more-and-more

Gemfile

    # Gemfile
    
    gem 'redis'
    gem 'hiredis' # optional
    # don't forget to run `bundle`

Set to online

    # app/controllers/application_controller.rb
    after_filter :set_online
    # the Rails 4 way is:
    # after_action :set_online
    
    private
    
      # set to online
      def set_online
        if !!current_user
          # using separate Redis database
          # such as $redis_onlines = Redis.new db: 15
          # value not need, only key
          $redis_onlines.set( current_user.id, nil, ex: 10*60 )
          # 'ex: 10*60' - set time to live - 10 minutes
        end
      end

Is online?

    # app/models/user.rb
    
    def online?
      $redis_onlines.exists( self.id )
    end

And small bonus (if you are using separate redis database)

    # app/cpntrollers/application_controller.rb
    
    def all_who_are_in_touch
      $redis_onlines.keys
      # => [ "123", "234", "1", "23" ]
      # array of ids
    end

Rework for anonymous

Set to online

    # app/controllers/application_controller.rb
    
      def set_online
        if !!current_user
          $redis_onlines.set( "user:#{current_user.id}", nil, ex: 10*60 )
        else
          $redis_onlines.set( "ip:#{request.remote_ip}", nil, ex: 10*60 )
        end
      end

Is online?

    # app/models/user.rb
    
    def online?
      $redis_onlines.exists( "user:#{self.id}" )
    end

All who are in touch:

    # app/cpntrollers/application_controller.rb
    
    def all_signed_in_in_touch
      ids = []
      $redis_onlines.scan_each( match: 'user*' ){|u| ids << u.gsub("user:", "") }
      ids
    end
    
    def all_anonymous_in_touch
      $redis_onlines.scan_each( match: 'ip*' ).to_a.size
    end
    
    def all_who_are_in_touch
      $redis_onlines.dbsize
    end

Recommendations

  1. Use a before_filter instead of a after_filter
    # app/controllers/application_controller.rb
      before_filter :set_online # before_action for Rails 4
  1. If you watch the anonymous (for example session[:user_id] is id of current user)
    # app/controllers/session_controller.rb
      before_filter :clear_from_signed_in_touch, only: :destroy
      before_filter :clear_from_anonymous_in_touch, only: :create
    
      private
    
        def clear_from_anonymous_in_touch
          $redis_onlines.del( "ip:#{request.remote_ip}" )
        end
    
        def clear_from_signed_in_touch
          $redis_onlines.del( "user:#{session[:user_id]}" )
        end

Postscript: Redis | set | exists | expire | scan | scan_each | redis-rb | hiredis | nice introduction for beginners

#ruby #rails #redis

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment