ActiveRecord enums で列挙型の値を取り出す方法

Rails のモデルで次のように列挙型を定義した場合

class Customer < ActiveRecord::Base
  enum payment_system: { credit_card: 1, bank_transfer: 2 }
end

列挙型の値を取り出すときは次のように書く。

Customer.payment_systems[:credit_card] #=> 1

ビューやヘルパーで使うからメモしておく。

『Chef 実践入門』読書メモ

読了。 Chef Server の章は流し読み。

第1章

  • Infrastructure as Code
    • インフラをコード(= Chef のレシピ)で記述し Github で管理

第2章

  • chef-solo は Chef のスタンドアロン
  • コードで書いたサーバー設定の手順が「レシピ」
  • レシピに必要なデータやファイルをまとめる入れ物が「クックブック」
  • クックブック群を含む、Chef の実行に必要なファイルをまとめる入れ物が「リポジトリ」または「キッチン」
  • knife はリポジトリを操作するツール
  • knife-solo は Chef を chef-solo 環境で実行するためのユーティリティツール
  • Chef で管理するサーバーのことを「ノード」
  • ノードの状態を JSON で記述したものが「Node オブジェクト」

第3章

  • 「リソース」は service, package, template といった部品
  • 「Attribute」はテンプレートやレシピで参照できる key-value の値を管理する仕組み
  • 「Ohai」は Chef がシステム情報を取得するのに使っている Ruby ライブラリ
  • Attribute はノードの属性
  • 各ノードで共有したいデータには「Data Bug」を使う

第4章

  • Berkshelf を使ってコミュニティクックブックをインポートできる
  • 複数ノードへ chef-solo を実行する場合
    • xargs を使う
    • capistrano や fabric といった外部ツールを使う
    • Chef Server を使う

第5章

第6章

  • 特に無し

第7章

  • Test Kitchen
    • Chef のクックブックをテストするためのツール
    • Vagrant と組み合わせて、複数の OS や OS のバージョンでクックブックをテストできる
  • Test Kitchen では Minitest、Serverspec でテストを記述できる
  • さらに Jenkins を使ってクックブックでも CI

第8章

  • 従来のサーバー構築手順やスクリプトをクックブック化するところから始める
  • コミュニティクックブックを参考にする
    • Opscode
    • Basecamp
  • Foodcritic を使えばクックブックを検査できる
  • Chef でアプリケーションをデプロイしたい場合
  • Chef の拡張

第9章

  • Chef Server のメリット
    • Search 機能でロールなどを絞り込める
    • クックブックの同期作業がいらない
    • Chef Client をデーモンとしても扱える

第10章

  • 各ノードで chef-client コマンドを一括実行
    • tomahawk を使う
    • または knife ssh を使う
  • 大量物理サーバーをセットアップ

Chef実践入門 ~コードによるインフラ構成の自動化 (WEB+DB PRESS plus)

Chef実践入門 ~コードによるインフラ構成の自動化 (WEB+DB PRESS plus)

技術書を電子書籍で購入できるサイト

技術書は重いし場所をとるので、 既に電子書籍が出ていればそれを買い、 無ければ紙の本を買うようにしたい。 電子書籍の方が紙の本よりも安いことが多いし。

そこで、技術書の電子書籍を購入できるサイトを探してみた。

環境や開発者に依存しない設定は Rails の config に追加すればよかった

ヘッダーに表示するサービス名や、フッターに表示するコピーライトを変更しやすいように、 設定ファイルとして YAML で抜き出そうと思っていたけど、 Rails の config に設定を追加できることを今さら知った。

例えば、config/application.rb に

module RailsSample
  class Application < Rails::Application
    # ...

    # タイトルとコピーライトを設定に追加
    config.app_name = "サンプルサービス"
    config.copyright = "tnakamura"
  end
end

と書いておけば、

Rails.application.config.app_name #=> サンプルサービス
Rails.application.config.copyright #=> tnakamura

という風に利用できた。 設定を追加したら、Rails アプリの再起動が必要だけど。

環境や開発者に依存する設定は dotenv 使うとして、 そういった環境に依存しない設定は config/application.rb に書けばいいや。

Rails のドキュメントはちゃんと読んでおくべきだったな。

Sidekiq をバックエンドに ActiveJob を導入

Heroku に 30 秒でレスポンスを返さないといけないルールがあったのを忘れていたので、 急遽 Rails アプリで時間がかかる処理を非同期にすることにした。

Rails で非同期というと Resque や Sidekiq が今のところ人気だけど、 今回は Rails 4.2 で追加予定の ActiveJob を使うことにした。 バックエンドには Sidekiq。

Redis をインストール

バックエンドに使う Sidekiq は Redis が必要なので、 Vagrant + Chef + Berkshelf で構築している開発環境にインストールする。

Berksfile に

cookbook "redisio"

を追加し

rm -rf cookbooks
berks vendor cookbooks

で Cookbook をインストール。

redisio のレシピを使うように Vagrantfile を

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # ...

  config.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = ["./chef/cookbooks", "./chef/site-cookbooks"]

    # ...

    # Redis のレシピを追加
    chef.add_recipe "redisio::install"
    chef.add_recipe "redisio::enable"
  end
end

という風に修正したら、vagrant provision で chef-solo を実行。

ActiveJob と Sidekiq をインストール

ここからは vagrant ssh で開発環境に入っての作業。

Gemfile に

gem "activejob", "4.2.0.beta1", require: "active_job/railtie"
gem "sidekiq"
gem "sinatra"

を追加して bundle install

ActiveJob は gem を直接使う。 Rails 4.1 のアプリで使いたかったので、activesuport 4.2.0.beta2 が不要な、4.2.0.beta1 を使用。 active_job/railtie を require しないと、ActiveJob が Rails に読み込まれなかった。

あとは config/environment/development.rb で ActiveJob の設定を記述。 バックエンドで Sidekiq を使うように指定する。

Rails.application.configure do
  # ...

  # ActiveJob のバックエンドに Sidekiq を指定
  config.active_job.queue_adapter = :sidekiq
end

ローカルにインストールした Redis を使うので、Sidekiq の Redis 設定は省略可能。

Sidekiq の Web コンソールをマウント

ちゃんとジョブが実行されたか確認したいので、Sidekiq の Web コンソールをマウントする。 sinatra もインストールしたのは、これが理由。 Sidekiq の Web コンソールは Sinatra を別にインストールしないといけない。

config/routes.rb を次のように修正。

Rails.application.routes.draw do
  # ...

  # Sidekiq の Web コンソールをマウント
  mount Sidekiq::Web => "/sidekiq"
end

ジョブを作成

次のコマンドでジョブを生成。

bundle exec railg generate job calc_score

すると

class CalcScoreJob < ActiveJob::Base
  def perform(*args)
  end
end

みたいな雛形が app/jobs/calc_score_job.rb に生成されるので、 perform メソッドに非同期で実行したい処理を書く。 こんな感じ。

class CalcScoreJob < ActiveJob::Base
  def perform(*args)
    project_id = args[0]
    project = Project.find(project_id)
    project.calc_score # 30 秒以上かかる集計処理
  end
end

コントローラーでジョブを使う

コントローラーで作成したジョブを使ってみる。

ドキュメントには perform_later を使うと書いてあったけど、 RubyGems.org からインストールした 4.2.0.beta1 にはまだ実装されていないので、NoMethodError。 代わりに古い APIenqueue を呼び出す。

class ProjectsController < ApplicationController

  # ...

  def calc_score
    # 本当は
    # CalcScoreJob.perform_later(data)

    # ジョブ実行
    CalcScoreJob.enqueue(@project.id)
    respond_to do |format|
      format.html { redirect_to @project }
    end
  end
end

ジョブを実行

Rails プロジェクト直下に、次の内容を書いた Procfile を作成する。

web: bundle exec unicorn -p $PORT -c config/unicorn.rb
worker: bundle exec sidekiq

Foreman を使って Rails サーバーと Sidekiq ワーカーを起動。

web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
worker: bundle exec sidekiq

コントローラーのアクションを呼び出してジョブを実行したら、 Sidekiq の Web コンソールで確認してみる。

ブラウザで http://localhost:8080/sidekiq にアクセスして

f:id:griefworker:20140928122506p:plain

Processed の数が増えていれば、ActiveJob のバックエンドとして Sidekiq がちゃんと動いたことになる。

プロフェッショナルのための実践Heroku入門

趣味レベルで Heroku にはさわってきたけど、 公式のドキュメントを隅から隅まで読んだわけではないので、 知らない情報を本書から得ることができた。

例えば Herokuのアーキテクチャ、特に Slug。 .slugignore に Slug に含めないファイルを指定して、 Slug コンパイル高速化&サイズ縮小する手法なんかは即試してみた。

本番環境へ移行するときに役立つであろう情報がまとまっているのもありがたい。 SSL の導入手順や、 Heroku Postgres でのデータベースのバックアップ方法と移行方法は、 「なんとなく知ってるけどきちんと調べなきゃいけないな」と思っていただけにタイムリーだった。

欲を言えば、アドオンの解説がもっと欲しいところ。 Logging 系のアドオンは必要って何度も書いてあるから、 Papertrail とか紹介してもよかったと思う。 あと NewRelic はもはや必須アドオンといっても過言じゃないので、 他の説明のついでではなく、ちゃんと専用にページを割いて欲しかった。

最初、買おうかどうか迷っていたけど、 新しい情報を得ることができたし、本番環境へ移行するための方法もゲットできたので、 今では買ってよかったと思っている。

Heroku Postgres のデータベースをリセットする方法メモ

忘れて毎回ネットで検索しているので、ブログにメモしておく。

heroku pg:reset DATABASE

上記コマンドをそのままターミナルにコピペして実行すればいい。DATABASE のところを置き換える必要なし。

ステージング用とプロダクション用に複数のアプリがある場合は

heroku pg:reset DATABASE --app <your_app_name>

を実行する。<your_app_name> は Heroku アプリの名前で置き換える。