概要
Sinatra + Redis を使って簡単な like ボタンを実装してみました
Redis にデータを格納しているため like 数を永続化しています
今回はコーディングの方法を 1 から紹介したいと思います
環境
- macOS 10.14.4
- Ruby 2.6.2p47
- Redis 4.0.9
- Sinatra 2.0.5
環境構築
まず開発するための環境を準備します
必要な gem のインストールや redis の起動を行います
bundle init
vim Gemfile
gem "sinatra"
gem "redis"
bundle install --path vendor
でライブラリのインストールは OK です
あとはアプリに必要なファイルを作成します
touch app.rb
touch config.ru
この 2 つはサーバ側のコードを書くファイルになります
この 2 つは Ruby で実装します
mkdir views
mkdir public
touch views/index.erb
touch public/custom.css
touch public/custom.js
WebUI を構成するファイルになります
HTML や CSS, JavaScript を書きます
これで今回必要なファイルの準備が整いました
あとは redis を localhost で起動しておきましょう
redis-server
とりあえず like ボタンを設置する
まずはとりあえず like ボタンを設置してみましょう
今回はボタンのデザインに FontAwesome を使います
vim views/index.erb
<html>
<head>
<link rel="stylesheet" href="/custom.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
</head>
<body>
<button id="like">
<i class="fas fa-heart fa-2x"></i>
</button>
<span id="like_count"><%= @like %></span>
</body>
</html>
FontAwesome
を使うのに必要な CSS ファイルの読み込みとボタンおよびカウントの設置をしています
@like
は Ruby 側から受け取った変数を展開するための erb
の機能です
vim public/custom.css
#like_count {
margin-left: 10px;
}
#like {
border: none;
}
適当にスタイルシートを当てています
この辺りは好きなようにカスタムしてもらって大丈夫です
vim app.rb
require 'sinatra'
require 'redis'
class LikeApp < Sinatra::Base
redis = Redis.new
get '/' do
@like = redis.get('like') || 0
erb :index
end
end
サーバ側のコードです
先程作成した index.erb
を返すだけのコードになります
Redis にアクセスするコードも含めています
like
というキーの値を取得しています
もしまだ値が設定されていない場合は 0 を設定します
vim config.ru
require './app.rb'
run LikeApp
あとはアプリケーションを Rack で起動するための起動スクリプトを書けば OK です
作成した app.rb
を読み込んで Rack のコマンドの run
を記載しているだけです
bundle exec rackup config.ru
これで localhost:9292
にアクセスすると以下のように like ボタンが表示されると思います
当然ですがクリックしてもまだ何も起きません
ボタンをクリックしたときのクライアント側の処理
次にボタンを押したときにサーバ側にリクエストするクライアント側のコードを作成しましょう
具体的には JavaScript を書きます
vim public/custom.js
$('#like').click(function() {
$.ajax({
url: '/like',
type: 'POST'
})
.done((data) => {
$('#like_count').text(data)
})
});
DOM の操作には jQuery を使っています
button
タグには id="like"
という ID を振りました
それが .click()
された場合にこの関数が呼ばれます
クリックされた場合にはサーバ側に Ajax を使ってリクエストします
具体的には POST /like
をリクエストします
この POST /like
のサーバ側の処理はこの後実装します
あとはレスポンスを受け取ったあとの処理を .done()
内に記載します
サーバからは like された後の like 数が返ってくる想定なので、それを DOM に表示してあげる処理を実装しています
Ajax では他にもエラー時の処理を .fail()
, 常に行う処理を .always()
として実装することができます
今回はそれらは特に使っていませんが例えば .fail()
ではエラーの原因などの文章を DOM に表示してあげたりすると良いでしょう
vim views/index.erb
<html>
<head>
<link rel="stylesheet" href="/custom.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body>
<button id="like">
<i class="fas fa-heart fa-2x"></i>
</button>
<span id="like_count"><%= @like %></span>
<script type="text/javascript" src="/custom.js"></script>
</body>
</html>
作成した JavaScript を HTML 内で読み込む処理を追記しましょう
具体的には <script>
タグを 2 箇所追記しています
ポイントは自分で作成した custom.js
は body タグの直前に記載している点です
HTML が描画される前に jQuery を使った JavaScript がロードされると対象の DOM がまだない状態でロードされてしまうためうまく動作しません
なので DOM がすべて描画されたあとで JavaScript をロードしてあげましょう
もしくは JavaScript の実装を $(function() { ... })
で囲ってあげれば ready
後にロードされるのでそれでも OK です
これで再度アプリを起動しましょう
特に見た目は変わっていません
またサーバ側を実装していないので動きはしませんがコンソールを見ると 404 が発生しサーバ側にリクエストしようとしていることがわかると思います
ボタンをクリックしたときのサーバ側の処理
さてではメインのサーバ側の処理を実装します
やることは以下の通り
- リクエストを受け取ったら現在の like 数に +1 して Redis に格納する
- レスポンスには +1 後の like 数を返却してあげる
これを踏まえたサーバ側のコードは以下のようになります
vim app.rb
require 'sinatra'
require 'redis'
class LikeApp < Sinatra::Base
redis = Redis.new
get '/' do
@like = redis.get('like') || 0
erb :index
end
post '/like' do
like = redis.get('like')
if like.nil?
like = 1
else
like = like.to_i + 1
end
redis.set('like', like)
like.to_s
end
end
post /like do ... end
の部分を追記しています
redis.get
した値は必ず String
になっているので計算する際には .to_i
メソッドを呼び出して Integer
にしてあげましょう
また Sinatra はメソッドの最後に評価した値をレスポンスボディ (またはレスポンスコード) として返却します
文字列などの場合はレスポンスボディとして返却し数字の場合にはレスポンスボディが空で指定されたレスポンスコードで返却します
今回はレスポンスコードが 200 でレスポンスボディが +1 後の like 数なので like 数を文字列に変換することで、それをレスポンスボディとして返却しています
これで JavaScript 側で実装した .done()
の data
に like.to_s
の値が返ってくるようになります
bundle exec rackup config.ru
これで正常に動作します
like ボタンを押すとカウントアップするのが確認できると思います
アニメーションを入れる
これだけだと少しさびしいのクリックした際にアニメーションを入れてみましょう
クリックしたことがわかれば何でもいいので好きなアニメーションを入れれば OK です
今回は Animate.css を使ってみます
vim views/index.erb
<html>
<head>
<link rel="stylesheet" href="/custom.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body>
<div id="section">
<button id="like">
<i class="fas fa-heart fa-2x"></i>
</button>
</div>
<span id="like_count"><%= @like %></span>
<script type="text/javascript" src="/custom.js"></script>
</body>
</html>
animate.min.css
を head
タグ内で読み込んでいるのとアニメーションさせるためにボタンを <div id="section">
で囲んでいます
animate.css
の仕様ですがブロック要素でないとアニメーションしないため div
で囲っています
なお div
などのブロック要素で囲っても display: inline
などにするとインライン要素になりアニメーションしないので注意してください
vim public/custom.css
#like_count {
float: left;
margin-left: 10px;
}
#like {
border: none;
}
#section {
float: left;
}
ボタンとカウンタが横並びになるようにしています
vim public/custom.js
$('#like').click(function() {
$('#section').addClass('animated bounce')
$('#section').on('animationend', function() {
$('#section').removeClass('animated bounce')
})
$.ajax({
url: '/like',
type: 'POST'
})
.done((data) => {
$('#like_count').text(data)
})
});
あとはボタンが押されたさいにアニメーションするための animate.css
のクラスを追加してあげるだけです
jQuery を使ってクラスの追加や削除を行うのは addClass/removeClass
を使います
また animationend
がアニメーション終了時に発火するのでそれを拾ってちゃんとクラスを削除するようにしましょう
動作確認
あとはアプリを起動して動作確認してみましょう
今回の記事のアプリを完成されると以下のように動作すると思います
最後に
Sinatra + Redis で永続可能な like ボタン的なのを作ってみました
今回の仕様だと 1 人で何回も like できてしまいます
一番良いのはアカウント登録させて 1 アカウント 1 回だけ like させるような処理をサーバ側で実装するのが良いと思います
ただ、アカウント登録させるとなるとそれだけでグッとハードルが上がってしまいます
なので session
などを使って制限するのが簡単かなと思います
例えば前回の like から 24 時間経過していないと次の like ができないようにするなどです
ただ session
の場合はクライアント側で cookies
を削除してしまえばどうにでも対応できるので完璧な対応ではないのでご注意ください
0 件のコメント:
コメントを投稿