Redisでランキングを実装

#初めに
Redisでランキングを実装してみました。

![rank.png](https://qiita-image-store.s3.amazonaws.com/0/207921/a2745f1b-6f58-326d-c50a-42778d119c92.png)



こちらのサイトを参考にしました。
RailsでRedisを使うメモ ランキングつくったり
http://o.inchiki.jp/obbr/307

#設定

デザインはbootstrap
ダミーデータ作成にfakerをつかいました。

あとredisを使うので

gemfileに

```
gem 'redis'
gem 'bootstrap'
gem 'jquery-rails'
gem 'popper_js'
gem 'tether-rails'
gem 'faker'
```

こんな感じで書いてbundle installしました。

userモデルを作成しました。

bootstrapの設定fakerでのダミーデータ作成は省略


#実装


users_controllerを

```ruby:users_controller.rb
class UsersController < ApplicationController
  def index
    @ranking_users_data = (REDIS.zrevrangebyscore "users", 10, 0, withscores: true)
  end

  def add_score
    user = User.find params[:id]
    REDIS.zincrby "users", 1, user.id
  end 
end
```

こんな感じにしました。

REDISに関しては


config/initializers以下にredis.rbを作成して中身に

```ruby:redis.rb
REDIS = Redis.new(host: "localhost", port: 6379)
```

と書くことで使えます。


```ruby
REDIS.zincrby "users", 1, user.id
```

みたいに書いているところでスコアのカウントをしています。

usersがスコアを計算する大きなまとまりの名前でusersでなくても良いのですが今回はusersと名前を付けています。

そのあとの1が追加する数です

user.idごとにスコアがカウントされていきます。

集められたスコアをソートされた状態で出力するには

```ruby 
REDIS.zrevrangebyscore "users", "+inf", 0, withscores: true
```

このコードです。
スコアが+inf（最大？）から0まで表示することになっています。

例えばスコアが0から10まで表示したい場合は

```ruby
REDIS.zrevrangebyscore "users", 10, 0, withscores: true
```

あくまでスコアなので0から10番目を表示というわけではありません。


自分の場合これだけだと登録されたばかりのuserはredisのusersのランキングの中に入らないので

```ruby 
REDIS.zrevrangebyscore "users", 10, 0, withscores: true
```

これで表示されませんでした。

なのでuserが作成されたときに1スコアをたして表示されるようにしました。


models/user.rb

```ruby:user.rb

class User < ApplicationRecord
    after_create{ REDIS.zincrby "users", 1, self.id}
end
```


routesは

```ruby
Rails.application.routes.draw do
  get 'users/index'
  post 'users/add_score/:id', to: 'users#add_score'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
```

このようにして

viewsは

```erb:index.html.erb
<br>
<br>
<br>
<br>
<br>
<div class="container">
    <table class="table table-dark">
        <thead>
            <tr>
            <th scope="col">Ranking</th>
            <th scope="col">Name</th>
            <th scope="col">Count</th>
            <th scope="col">Like Button</th>
            </tr>
        </thead>
        <tbody>
            <% @ranking_users_data.each_with_index do |rank_data,index| %>
                <tr>
                    <th scope="row"><%= index + 1 %></th>
                    <td><%= user_name(rank_data[0]) %></td>
                    <td class="count"><%= rank_data[1].to_i %></td>
                    <td>
                        <%= button_to "/users/add_score/#{rank_data[0]}", class: "like-btn", id: "addBtn",  remote: true do %>
                            <i class="fas fa-heart" style="font-size: 15px;" data-count="<%= index  %>"></i>
                        <% end %>

                    </td>
                </tr>
            <% end %>
        </tbody>
    </table>
</div>


<script>
var allBtn = document.getElementsByClassName('like-btn')
var allCount = document.getElementsByClassName('count')
var btnLength = allBtn.length 
var addLikeCount = function(i) {
    allCount[i].innerHTML = parseInt(allCount[i].innerHTML) + 1
}
for(var i = 0; i < btnLength; i++ ) {
    allBtn[i].addEventListener('click', function(a) {
        let dataCount = this.getElementsByClassName('fas')[0].dataset.count
        addLikeCount(dataCount);
    });
}

</script>
```

例のごとく自分はjavascriptをerbの中に書いています。

とりあえず見た目はこんな感じになると思います。

![rank.png](https://qiita-image-store.s3.amazonaws.com/0/207921/7a3ba290-0830-5cfb-f890-ba9d4401f48e.png)


fontawesomeを使っているので

layoutsのheadの中に

```
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css">
```

を書いて

assets/stylesheets/application.scssに

```scss
@import '/*';
@import 'bootstrap';
@import 'tether';

body 
{
    background: #95a5a6;
}

.like-btn
{
    transition: all 0.2s;
    border: none !important;
}

.like-btn:hover
{
    background: rgb(224, 0, 37) !important;
    color: white !important;
    border: none !important;
}
```

こんな感じにしてあると見た目は同じになると思います。


これでクリックされるたびにスコアが追加されていきます。


#ちなみに

redisに追加されているデータをみたい場合は

rails cで

```
REDIS.keys
```

とすれば登録されているkeyが出て

```
REDIS.zrevrangebyscore "users", "+inf", 0, withscores: true
```

と入力すればuser.idとスコアが二次配列の形で表示されると思います。

おわり:sunny: