Ruby on Railsのマイグレーションのまとめ

Ruby on Railsにはマイグレーションという、データベースの変更管理を簡単に行うための便利な枠組みが用意されています。

この投稿では、このマイグレーション機能の使い方についてまとめたいと思います。(徐々に更新していきます。)

マイグレーションファイルの作成

マイグレーションファイルとは、データベースの変更内容を記述したruby形式のファイルで、app/db/migrate以下に保存します。このファイルをコミットに含めることで、ソースコードの変更に同期する形で、データベースの変更履歴をGit等のソースコードリポジトリ上で管理することができます。

マイグレーションファイルは、手動で作成しても良いですが、作成するためのrailsコマンドが用意されています。

$ rails g migration <変更内容>

変更内容には、どのような変更を行うのかを英語で記述します。例えば、新しくusersテーブルを作成する場合は

$ rails g migration create_users

とします。上記はスネークケースで書きましたが、パスカルケースで

$ rails g migration CreateUsers

としても同じ結果が得られます。このコマンドを実行すると、app/db/migrateディレクトリ以下に次のような名前のファイルが作成されます。

20201207233110_create_users.rb

最初の数字の列はマイグレーションファイルの作成日時を「yyyyMMddhhmmss」形式で入力されます。

ファイルの中身は次のようになります。

class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
    end
  end
end

RailsでエラーをSlackに通知する

Rails標準のLoggerを活用して、エラーログをSlackのチャンネル上に表示する方法について説明いたします。

Slack Notifierのインストール

まず、RailsからSlackへのメッセージの送信に関しては、slack-notifierという便利なgemが公開されておりますので、こちらを利用させていただきます。

https://github.com/stevenosloan/slack-notifier

Slack Notifierをインストールするには

gem "slack-notifier"

をGemfileに追加し、

$ bundle install

を実行します。

Slack通知用のLoggerの作成

次に、Slack Notifierを利用してSlackにメッセージを通知するためのLoggerを、Rails標準のLoggerを継承して作成します。

class SlackLogger < ActiveSupport::Logger
  def initialize
    super(SlackDevice.new)
  end

  class SlackDevice
    def initialize
      webhook_url = '<Webhook用のURL>'
      channel = '<チャンネル名(#含む)>'
      username = '<投稿ユーザー名>'
      @notifier = Slack::Notifier.new webhook_url, channel: channel, username: username
    end

    def write(message)
      @notifier.ping message
    end

    def close
      # Do nothing
    end
  end
end

SlackLoggerにはオーバーライドしたコンストラクタ(initialize)とSlackDeviceというサブクラスを持たせています。

コンストラクタでは、SlackDeviceを初期化し、そのインスタンスを継承元のコンストラクタに渡しています。

通常、継承元のActiveSupport::Loggerの初期化時(内部ではRuby標準のLoggerを呼んでいます。)には、その引数にログを書き込むファイルパスかIOオブジェクトを指定します。ただ、ソースコードを追ってみると、writeメソッドとcloseメソッドを持つオブジェクトであれば、とりあえず動作することが分かります。(将来的にこの挙動が続く保証はありませんので、Rubyのバージョンアップにはご注意ください。)

https://github.com/ruby/logger/blob/master/lib/logger/log_device.rb

今回はその特性を利用し、ファイルパスやIOオブジェクトの代わりに、writeメソッドとcloseメソッドを持つSlackDeviceというクラスを定義し、そのオブジェクトを渡しています。

SlackDeviceはコンストラクタにおいて、先ほどインストールしたSlack Notifierを初期化しています。その際、引数としてSlackへの接続情報を渡します。(実際には、接続情報はハードコードせず、設定ファイルから渡すと良いと思います。)

Webhook用のURLの取得の仕方はSlackの公式ドキュメントをご参照ください。

https://slack.com/intl/ja-jp/help/articles/115005265063-Slack-%E3%81%A7%E3%81%AE-Incoming-Webhook-%E3%81%AE%E5%88%A9%E7%94%A8

また、SlackDeviceのwriteメソッドではSlack Notifierオブジェクトのpingメソッドを実行しています。closeメソッドは特になにもしません。

Loggerの設定

Railsでは標準でプロジェクトルートのlogフォルダ以下に<環境名>.logという名前のログファイルを作成します。

その挙動を維持したまま、Slackへの通知を追加するには、次のコードをapp/config/environments/<環境名>.rbに追加します。(環境別に分ける必要がない場合は、app/config/application.rbでも大丈夫です。)

logger = ActiveSupport::Logger.new("#{Rails.root}/log/#{Rails.env}.log")

# Slack連携用Logger
slack_logger = SlackLogger.new
extended_logger = logger.extend(ActiveSupport::Logger.broadcast(slack_logger))

config.logger = extended_logger

非同期処理への対応

Slackへの通知は同期的に行われるため、ログレベルをdebug(config.log_level = :debug)やinfoに設定していると、画面表示に非常に時間が掛かるようになり、現実的ではありません。ログレベルをerror以上に設定するか、または、Sidekiq等を利用して、Slackへの通知を非同期的に処理する仕組みを入れるのが良いかと思います。

Rails(Turbolinks)でGoogle Tag Managerを利用

TurbolinksはPjax(pushState + Ajax)を実現するためのライブラリで、Rails 5では標準で有効になっています。Turbolinksを使うと、ページ遷移時に画面全体の読み込みが行われないため、SPAにしなくても、非常にスムーズな操作性を実現することができます。

ただ、Turbolinksを利用している場合、ページ遷移時にGoogle Tag Manager(以下GTM)のページビューイベントが発火しません。

発火させるためには、次のJavascriptのコードを利用します。

document.addEventListener('turbolinks:render', function() {
    dataLayer.push({
      'event': 'pageview',
      'virtualUrl': window.location.pathname
    });
  });

ページ遷移時に無名関数が実行され、GTMのスクリプトによって定義されたdataLayer配列にpageviewイベントを挿入することで、GTMにイベントが送られます。

GTM上でこのイベントをキャッチするには、新たなトリガーを作成する必要があります。トリガーのタイプを「カスタム イベント」とし、イベント名には先ほど指定した「pageview」というイベント名を指定します。(コードと同じであれば別のイベント名でもOK)

作成したトリガーを「配信トリガー」として、必要なタグに割り当てれば完了です。

Railsでリダイレクト時にアンカーを設定

Railsでは、redirect_toメソッドに対してモデルのオブジェクトを渡すと、そのオブジェクトを表示するページに簡単に遷移させることができます。

# /users/<id> に遷移
redirect_to @user

ただ、アンカー(ページ内での位置)を指定するには少し工夫が必要で、次のように指定します。

# /users/<id>#description に遷移
redirect_to user_path(@user, anchor: 'description')

※user_pathはroutes.rbで定義しておく必要があります。

Rails(Turbolinks)でGoogle AdSenseを利用

TurbolinksはPjax(pushState + Ajax)を実現するためのライブラリで、Rails 5では標準で有効になっています。Turbolinksを使うと、ページ遷移時に画面全体の読み込みが行われないため、非常にスムーズな操作性を実現することができます。

しかし、Turbolinksが適用されたサイトにGoogle AdSense(Googleが提供する広告配信サービス)を導入すると、ページ遷移後に広告が正しく表示されない問題が発生する場合があります。

これを防ぐには、Turbolinksによる画面の読み込みをトリガとして、新たに表示された広告ユニットを認識させる処理を加える必要があります。

例えば、自動サイズの広告ユニットを表示させる場合、Turbolinksを適用させていない通常のサイトでは、<head>タグ内に

<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>

を追加した上で、HTMLの広告を表示させたい場所に次のようなコードを挿入します。一つのページに複数の広告ユニットを表示することも可能です。

<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-xxxxxxxxxxxxxxxx"
     data-ad-slot="xxxxxxxxxx"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

一方、Turbolinksが適用されたサイトでは、<head>内でのadsbygoogle.jsの読み込みは同じですが、広告を表示させたい場所には<ins>タグのみを挿入し、

<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-xxxxxxxxxxxxxxxx"
     data-ad-slot="xxxxxxxxxx"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>

その上で次のコードを<head>タグ内に挿入します。(外部Javascriptファイルに切り出して、読み込ませてもOKです。)turbolinks:loadに対するイベントリスナを設定することで、初回表示時やTurbolinksによる画面遷移時に処理が走ります。

<script>
  document.addEventListener('turbolinks:load', function () {
    var ads = document.querySelectorAll('.adsbygoogle');
    if (ads.length > 0) {
      ads.forEach(function (ad) {
        if (ad.firstChild) {
          ad.removeChild(ad.firstChild);
        }
        window.adsbygoogle = window.adsbygoogle || [];
        window.adsbygoogle.push({});
      });
    }
  });
</script>

ここでは、 adsに格納された<ins>要素を一つ一つ走査し、ブラウザバック等で既に広告の実体が挿入されている<ins>に関しては初期化を行った上で、 window.adsbygoogleに対して空のハッシュを挿入することで、adsbygoogle.jsに対して広告ユニットの存在を知らせています。

以上で、Turbolinksが利用されたサイトでもGoogle AdSenseによる広告が正しく表示されるようになります。

WindowsにRuby on Rails環境をセットアップ

Windows10においてRubyおよびRailsフレームワークをインストールする手順についてのメモです。

Rubyのインストール

WindowsでRubyをインストールする手段はいくつかあるようですが、RubyInstallerを使うのが簡単なようです。

RubyInstallerのダウンロード

RubyInstallerをダウンロードするには、http://rubyinstaller.org/にアクセスをして、Downloadボタンをクリックします。

左側のRubyInstallersの中から、インストールしたいバージョンのリンクをクリックします。ここでは、最新安定版であるv2.2.4の64ビットマシン用のインストーラを選択しました。リンクをクリックするとダウンロードが開始します。

 

インストーラの実行

ダウンロードしたインストーラを起動すると、まず言語の選択ウィンドウが表示されます。ここでは日本語を選択しました。

次に、使用許諾契約書に同意をし、「次へ」ボタンをクリックします。

次に、インストール先とオプションを指定するための画面に進みます。インストール先は初期値とし、「Rubyの実行ファイルへ環境変数PATHを設定する」にチェックを入れ、「インストール」ボタンをクリックします。

インストールのインジケーターが進み、以下の画面が出たら「完了」をクリックしてインストールは終了です。

DevKitのインストール

Windows上でネイティブC/C++拡張を利用するためのツールで、Railsを利用するために必要となります。ダウンロードは、RubyInstallerと同じページです。左側のメニューのDEVELOPMENT KITから、インストールしたRubyに合ったパッケージをクリックします。

ダウンロードしたファイルは、自己解凍アーカイブになっているため、先ほどRubyをインストールしたフォルダにdevtoolsというサブフォルダを作成し、そこを選択します。

次のようなインジケーターが進み、100%になると自動的にウィンドウが閉じます。

次に、コマンドプロンプトを開き、DevToolsを展開した場所に進みます。(エクスプローラで対象のフォルダを開き、Shift + 右クリックで表示されるコンテキストメニューから「コマンドウィンドウをここで開く」を選択すると楽です。)

コマンドプロンプト上で以下のコマンドを実行します。

>ruby dk.rb init
>ruby dk.rb install

以上でDevKitのインストールは完了です。インストール後もdevtoolsフォルダは消さないよう注意してください。

Railsのインストール

Railsをインストールする準備が整いましたので、gemを利用してインストールします。gemはRubyのパッケージマネージャで、RubyInstallerを使ってRubyをインストールすると、一緒にインストールされています。Railsをインストールするには、以下のコマンドを利用します。

>gem install rails

完了までに数分かかります。正しくインストールできていることを確認するために、railsコマンドを実行します。

うまくインストールできているようですね。

Top