LoginSignup
1
0

More than 1 year has passed since last update.

【Rails】Twitter APIとLINE APIでジャニーズの私物を特定して、すぐに購入できるサービスを作ってみた!!! PF [gem部分]

Posted at

はじめに

PFでジャニーズの私物を特定して、すぐに購入できるサービスを作成したのでまとめます。
あまりにも長くなりそうだったので、4回に分けてまとめています。
今回が最後です使用したgemについてまとめます。

サービスの概要

ジャニーズファンは推しと同じものを日常生活やライブで身につけたいという心理があります。
twitte上にファンの人が私物を特定してツイートをするので、そのツイートをキャッチして、LINEで通知・購入できるサービスになっています。

なぜこのサービスを作成したか

私物の特定のツイートがされるとすぐファンが購入し、売り切れてしまうことがよくあります。
私自身、私物特定のツイートを発見し、サイトを検索してももう売り切れてしまっていたということが何回もあります。
他のファンよりも早く購入できるサービスがあればいいなと思い、このサービスを作成しました。

完成したサービス

Webサイトは以下になります↓

LINEのQRコードになります。
ぜひ動かしてみてください!!
a163e5fa2b2098ef30926fb607faa8ff.png

サービスのイメージがしづらいと思いますがこんな流れになります
1.Twitterで私服特定ツイートがされます。
S__37232668.jpg

2.LINEにこのような通知が届きます。
S__37232670.jpg

3.商品検索ボタンから検索可能です。
S__37232676.jpg

4.To tweetボタンからソース元に遷移可能です。
S__37232678aaa.jpg

LINE友達登録後できること
1.最新の情報を確認できます。
リッチメニュー右上の最新の情報ボタンを押す→情報を確認したいメンバーを選択する→最新の情報が表示される
S__37232679ss.jpg

S__37232680ss.jpg

2.情報を受け取るメンバーを選択できます。
リッチメニュー左上のメンバー設定を変更するを選択する→設定したいメンバーを選択する→通知を受け取るか、受け取らないかを選択する

S__37232682b.jpg

S__37232683b.jpg

もちろん、現在の通知設定についてもリッチメニュー左下のメンバー設定を確認する から確認可能です。

S__37232681b.jpg

使用した機能

Twitter API
LINE Messaging API
dotenv-rails'
config gem
ransack gem
sorcery gem
kaminari gem
enum_help gem
rails-i18n gem
AWS
heroku

ER図

3917056f5181a1ce73694684e344b358.png

画面遷移図

d657ee3591bde2b85796fbee48567573.png

他に使用しているgemについて

他にも今回導入しているgemがあるのでまとめていきます。

whenever gem

Gemfile
gem 'whenever', require: false

cronの設定を簡単な文法で書けます。
定時実行の処理をwheneverを使うと設定できます。
今回は、Twitterの検索をrakeタスクに作成して、定期的に私物に関するツイートがないか検索をかけました。

デプロイにはherokuを使用しました。herokuwhenever gemに対応していないため、schedulerを使用して定期実行を行っています。

❶lib/tasks以下にcronを使用して、定期的に実行する処理のかたまり(rakeタスク)を作成します。
herokuでは10分に1回しか定期実行できない為、3.timessleep(150)を使用して、時間の帳尻を合わせています。

lib/tasks/make_goods_find_article.rake
namespace :make_goods_find_article do
  desc '「GoodsFind」のツイートが私物に関するものかを判別し私物に関するものであれば記事を作成する'
  task account_goods_find: :environment do
    3.times{
    #3回行っています。
      article = Article.new
      article.make_GoodsFind_article
      #article.rbで作成したmake_GoodsFind_articleメソッドを実行
      sleep(150)
      #150秒経ったら次の処理へ
    }
  end
end

config/suchedule.rbを作成して、定期実行したいタスク設定(実行の間隔、時間)を書き込んで反映させます。

以下のコマンドをアプリディレクトリ内で実行します。
実行すると、config/suchedule.rbが作成されます。

$ wheneverize . 

ここに定期実行したいタスク設定(実行の間隔、時間)を書き込んで反映させます。

config/suchedule.rb
# Rails.rootを使用するために必要
require File.expand_path(File.dirname(__FILE__) + '/environment')
# cronを実行する環境変数
rails_env = ENV['RAILS_ENV'] || :development
# cronを実行する環境変数をセット
set :environment, rails_env
# cronのログの吐き出し場所
set :output, "#{Rails.root}/log/cron.log"
# 10分ごとに実行
every 10.minute do
  rake "make_goods_find_article:account_goods_find"
end

❸設定を反映させます。

設定内容にエラーがないか確認

$ bundle exec whenever

cronにデータを反映させるコマンド

 $ bundle exec whenever --update-crontab

参照(開発中はこの記事を参考にして、定期実行を行いました。)

herokuschedulerを使用し定期実行した方法はこちらの記事にまとめてあります。

ransack gem

Gemfile
# 検索
gem 'ransack'

検索部分をこのgemで作成しました。

このような感じで、ブランド名メンバー名から検索できるように作成しました。
7b856537485694627ffe20df5db41cb3.png

今回は、article一覧ページのヘッダーに検索フォームを作成したいです。

❶まずメソッドを確認します。

params[:q] → この後に作成するビューファイルから送られてくるパラメーターです。

ransackメソッド → 送られてきたパラメーターを元にテーブルからデータを検索するメソッドです。
(whereメソッドのransack版というイメージです。)

resultメソッド → ransackメソッドで取得したデータをActiveRecord_Relationのオブジェクトに変換するメソッドです。

❸controllerを確認します。今回はヘッダー部分に検索フォームを作成したいので、application_controller.rbcontrollers/articles_controller.rbcontrollers/admins/articles_controller.rbに作成します。

共通で使用するメソッドを作成します。

application_controller.rb
class ApplicationController < ActionController::Base

  def set_item_search
    @q = Article.ransack(params[:q])
  #ransackメソッドを使用しparams[:q]でviewから送られてくるパラメーターを元にテーブルからデータを検索する。
    @set_items = @q.result
#ransackメソッドで取得したデータをActiveRecord_Relationのオブジェクトに変換する
  end
end
controllers/articles_controller.rb
def index
  @articles = @set_items.includes(:members).published.order(created_at: :desc).page(params[:page]).per(6)
end
controllers/admins/articles_controller.rb
def index
  @articles = @set_items.includes(:members).order(created_at: :desc).page(params[:page])
end

❸検索フォームのviewを確認します。

共通で使用する検索フォームはパーシャルにしました。

views/layouts/_search.html.erb
<div class = "search mx-auto py-4">
  <%= search_form_for @q do |f| %> 
    <%= f.search_field :brand_cont, class: " search_btn btn-outline-dark", placeholder: t("activerecord.attributes.article.brand") %> 
    <%= f.search_field :members_name_cont, class: " search_btn btn-outline-dark", placeholder: Member.model_name.human %> 
    <%= f.submit 'search', class: "search_btn  btn-outline-dark"%>
  <% end %>
</div>

ヘッダーでそのときのpathに合わせて表示する形にしました。

views/layouts/_header.html.erb
<% if current_page?(articles_path) %>
  <%= render partial: 'layouts/search', url: articles_path %>
<% elsif current_page?(admins_articles_path) %>
  <%= render partial: 'layouts/search', url: admins_articles_path %>
<% end %> 

sorcery gem

Gemfile
# ログイン
gem 'sorcery'

認証のための最小限のロジックを積んだgemで、ユーザーの必要に応じてメソッドを増やしたり拡張をしていくことを目的としています。

以下の点を考えて今回はsorceryを使用しました。
使いたいのはログイン機能のみです。

deviseに比べて重くない
カスタマイズしやすい。

❶コマンドを実行し必要なファイルを生成します。

$ rails generate sorcery:install

↓のファイルが生成されます。

create  app/models/user.rb
create  db/migrate/XXXXXXXXX_sorcery_core.rb

(1)db/migrate/XXXXXXXXX_sorcery_core.rbについて

db/migrate/XXXXXXXXX_sorcery_core.rb
class SorceryCore < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :email, null: false
      t.string :crypted_password
      t.string :salt
      t.integer :role, default: 0

      t.timestamps
    end
  end
end

saltについて

標準の暗号化では、「ソルト」を使用して、パスワードハッシュの安全性を高めます。Sorceryでは、パスワードの末尾にランダムな文字列を結合し、その文字列をソルトフィールドに記憶することでこれを行います。

$ rails db:migrateで反映させます。

今回は、ユーザー作成ページは作成しませんでした。
管理者のみ必要なので、一般ユーザーはログインせずに使用できる流れになります。

なので、ログイン用のviewやcontrollerのみ作成しています。

ログイン用のviewやcontroller

ログイン・ログアウト機能の実装についてです。sorceryで提供しているメソッドについては、公式のこちらのに記述があります。

loginメソッドは、email、password、remenber_me(デフォルト値はfalse)の3つの引数を取ることができると分かります。
今回はemail、passwordを使用してログインさせます。

controllers/admins/sessions_controller.rb
class Admins::SessionsController < ApplicationController
  skip_before_action :require_login, only: %i[create new]

  def new;end

  def create
    @user = login(params[:email], params[:password])
    if @user
      redirect_back_or_to admins_articles_path, success: t('flash.login')
    else
      flash.now[:danger] = t('flash.not_login')
      render :new
    end
  end

  def destroy
    logout
    redirect_back_or_to admins_login_path, success: t('flash.logout')
  end
end

ここのrequire_loginについて

controllers/admins/sessions_controller.rb
skip_before_action :require_login, only: %i[create new]

require_loginメソッドはSorceryで提供されているメソッドです。
ログインしていないユーザーをアクション単位で弾き、not_authenticatedメソッドを発火します。

not_authenticatedはSorceryで提供されているメソッドです。
デフォルトではredirect_to root_pathが定義されています。
自分で変更したい際には、application_controller.rbで記述します。

controllers/application_controller.rb
class ApplicationController < ActionController::Base
  add_flash_types :success, :info, :warning, :danger
  #add_flash_typesメソッドで指定したキーがredirect時のflashで使えるようになる。
  #通常はnoticeとalertだけだが、任意のキーを指定できるようになる。
  before_action :require_login

  private

  def not_authenticated
    redirect_to admins_login_path, danger: t('flash.before_login')
  end
end

それぞれまとめているページはこちらになります。

rails-i18n gem

Gemfile
# ja.yml
gem 'rails-i18n'

デフォルトの言語を日本語に設定します。
config/locales/ja.ymlを作成し、日本語に対応させます。

config/application.rbの設定を変更します。

config/application.rb
config.i18n.default_locale = :ja 
#デフォルトの言語を日本語(ja)にします。
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
#i18nの複数ロケールファイルが読み込まれるようpathを通します。

config/locales以下にロケールファイルを配置し、日本語を設定します。

config/locales/activerecord/ja.yml
ja:
  activerecord:
    models:
      member: 'Member'
      report: 'REPORT'
    attributes:
        article:
          brand: 'Brand'
          member: 'メンバー'
          twitter_url: 'TwitterへのURL'
        member:
          email: 'メールアドレス'
          password: 'パスワード'

enum_help gem

Gemfile
# enum
gem 'enum_help'

enumで定義した値をi18n化させることができます。
i18n対応セレクトボックスを作成することも可能です。

❶enumで定義した値をi18n化はこのように設定し、使用します。
config/locales/activerecord/ja.yml
ja:
  enums:
    user:
      role:
        other: '一般'
        admin: '管理者'
❷今回はi18n対応セレクトボックスも作成、使用しています。

以下の形で、編集ページで公開非公開かを選択できるようにしています。

af56edcf1afe32f5624b971ce14e0c83.png

views/admins/articles/edit.html.erb
<%= form_with model: @article, url: admins_article_path(@article.id), local: true do |f| %>
#省略
<%= f.select :status, Article.statuses_i18n.invert %>
#省略
<% end %>

ポイントはセレクトボックスの値です。

select(オブジェクト名, メソッド名, 要素(配列 or ハッシュ) [, オプション or HTML属性 or イベント属性])

とすればいいので、以下のようにすれば動きます。

<%= f.select :status,[["公開中", "published"], ["非公開", "draft"]] %>
# f.select :プロパティ名,[["表示される文字", "渡される値"], ["表示される文字", "渡される値"]]

ですが、enum_helpを使用するともっと簡単に書けますし、enumに変更があったとしてもコードを変更する必要はありません。
↓このように書くことができます。

<%= f.select :status, Article.statuses_i18n.invert %>

上で見た以下のコードは以下のような構成になっていたので、

f.select :プロパティ名,[["表示される文字", "渡される値"], ["表示される文字", "渡される値"]]

enum_helpを使用して[[表示される選択肢, 渡される値], [表示される選択肢, 渡される値]]という形にして渡すには...

ターミナル
irb(main):012:0> Article.statuses
=> {"published"=>0, "draft"=>1}

irb(main):013:0> Article.statuses_i18n
=> {"published"=>"公開中", "draft"=>"非公開"}

irb(main):014:0> Article.statuses_i18n.map { |k, v| [v, k] }
=> [["公開中", "published"], ["非公開", "draft"]]

irb(main):015:0>Article.statuses_i18n.invert 
#値からキーへのハッシュを作成
=> {"公開中"=>"published", "非公開"=>"draft"}

invertメソッド

値からキーへのハッシュを作成して返します。

select(オブジェクト名, メソッド名, 要素(配列 or ハッシュ) [, オプション or HTML属性 or イベント属性])

の通り、配列 or ハッシュを渡せばいいので、以下の形で同じように動きます!もし並列にしたければto_aメソッドを最後に使用すれば配列になりますが、必要ないので使用しません。

Article.statuses_i18n.invert 
#値からキーへのハッシュを作成
=> {"公開中"=>"published", "非公開"=>"draft"}

kaminari gem

Gemfile
# ページネーション
gem 'kaminari'

1つのページに表示する記事の数を指定し、複数ページに分けて表示させるようにするgemです。
今回は1ページに8つの記事を表示させています。
8個以上の記事がある場合は以下を表示し、別ページに表示されています。

91b35ef89d846c4abdaf01a9e88b6a1d.png

❶設定ファイルを作成し、デフォルトの表示数を設定します。

$ rails g kaminari:config

config/initializers/kaminari_config.rbが作成されます。

ここで、表示数を指定します。デフォルトは25が設定されています。
今回は8で設定します。

config/initializers/kaminari_config.rb
Kaminari.configure do |config|
  config.default_per_page = 8
#省略
end

❷controllerで表示数などを指定します。

デフォルトで8を指定したので、8こ表示したい場合は記載は必要ないですが、この一覧部分は6個を指定したかったので、.per(6)を使用して数を指定しています。

controllers/articles_controller.rb
def index
  @articles = @set_items.includes(:members).published.order(created_at: :desc).page(params[:page]).per(6)
end

❸view部分を指定します。
❷のコントローラー、アクションでレンダリングされるページを例にすると...
以下を記載するだけで表示されます。

views/articles/index.html.erb
<%= paginate @articles %>

❹デザインを変更します。bootstrap4を導入し、自分で後から少し変更しています。

$ rails g kaminari:views bootstrap4

aws-sdk-s3 gem
Gemfile
gem "aws-sdk-s3", require: false

Heroku には “一時的” ハードドライブがあります。これは、ファイルをディスクに書き込むことはできますが、アプリケーションを再起動するとそれらのファイルは失われてしまいます。

activestorageを使用すると、しばらくは問題ありませんが、添付ファイルは表示動作に支障をきたすようになり、最終的には消えてしまいます。

それが起きないように、アップロードしたファイルをディスクに保存する代わりに、クラウドファイルストレージサービスを使用します。

それに使用するのがこのaws-sdk-s3 gemです

config gem

定数管理を行います。
今回は、Twitterのアカウントidなどをここで管理しました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0