Development

Railsで request.body を触るのは避けたほうがいいかも

API等を実装していて create アクションなどで以下のように json payload をパースするケースがありました。

def create
  payload = JSON.parse(request.body.string)
  ...
end

開発中だとこれでも問題ないのですが、本番環境などで unicorn を使っていると request.body の返すオブジェクトが変わってくるのでエラーしてしまいました。

ググったところこちらの方法が良さそうです。
http://stackoverflow.com/questions/24566423/checking-request-body-in-grape-api

def create
  payload = JSON.parse(env["rack.input"])
  ...
end

ただこれでもテスト環境だと env[“rack.input”] は nil を返すので、テストが落ちてしまいました。

で最終的に落ち着いたのがこれ

def create
  payload = JSON.parse(env["rack.input"].try(:read) || request.body.string)
  ...
end

これでなんとかどの環境でも動くようになりました。

ちなみに JSON.parse が空文字や nil を受け取ると例外を投げるので実際には以下のようにするといいと思います。

def create
  payload = parse_json_payload
  ...
end

private
  def parse_json_payload
    JSON.parse(env["rack.input"].try(:read) || request.body.string)
  rescue => _
    nil
  end
標準
Development

rubyで 3 / 2 は 1.5 じゃありません

rubyで小数点のつくデータ型はFloatですが、整数同士の計算をしていても解に小数点は勝手につかないので注意しましょう。
(久々に初歩的なところでハマった…)

3 / 2 # => 1

こうしなければいけない

3.to_f / 2 # => 1.5

どちらかがFloatならok

ちなみにjavascriptだと 3 / 2 は 1.5 を返します。
number型しかないからですかね。

標準
Development

ES2015でenumを作ってみる

javascriptにはenumという型はありませんので、今まで(ES5)は以下のように擬似的にenum的なものを作っていました

var Sample = {
  One: "one",
  Two: "two"
};

ついでに const 宣言も無かったので後から書き換え可能になっているのも残念なところです。

ES2015になってつい嬉しくて const 宣言を使って以下のようにしていました。

const Sample = {
  One: "one",
  Two: "two"
};

しかしこれも実は残念なところがあって、値が文字列である以上他の由来の文字列でも比較がtrueになってしまうのです。

const Sample = {
  One: "one",
  Two: "two"
};
const Other = {
  One: "one",
  Two: "two"
};

console.log(Sample.One === Other.One); // => true

これはいただけませんね。enumの役割をちゃんと果たしていません。

ES2015ではSymbolが使えるようになったので以下のようにするととってもいい感じだとおもいます。

const Sample = {
  One: Symbol("one"),
  Two: Symbol("two")
};
const Other = {
  One: Symbol("one"),
  Two: Symbol("two")
};

console.log(Sample.One === Other.One); // => false

Symbol() はグローバルにユニークな値を返すので、同じ引数で実行しても結果が異なるためにこのような使い方ができるわけです。

ちなみにグローバルに同一なSymbolを参照したい場合は Symbol.for() を使うといいみたいです。

let one = Symbol.for("one");
console.log(one === Symbol.for("one")); // => true

Symbolのブラウザでの実装状況はこちら http://kangax.github.io/compat-table/es6/#test-Symbol

標準
Development

Github Flow的な作業の始めかた

ハレノヒではプロジェクト管理はtrelloかgithubを使っています。これは決まりではありませんがどちらもチームが使い慣れていますので今では当たり前のようにこれらを使っています。
他にもいいプロジェクト管理ツールがあれば使ってみたいですね。

さて、Githubを使っているということは当然gitを使ってバージョンを管理をしているのですが、ブランチを作り作業を始める際の定番手順を紹介したいとおもいます。

Github Flowについてはこちらに日本語翻訳記事がありますので御覧ください
https://gist.github.com/Gab-km/3705015

はじめに

例としてSuper Awesomeなポートフォリオサイトを構築していたとします。
ポートフォリオサイトなので作品紹介ページは作ったのですが、お問い合わせページがまだ無いのでそれを追加する作業を進めていきます。

またGithub社が提供する hub コマンドを使うと便利なのでこれも合わせて紹介します。

1. masterブランチからfeatureブランチを作成する

まずは作業するためのブランチを作成します。この場合新機能の開発なので feature(機能)という名前空間の下に作業内容がわかりやすい名前で作成します。

git checkout -b feature/contact_form

git checkout -bを使うとブランチの作成とチェックアウトを同時に行えるので便利です。

2. 空のコミットをしておく

新しいブランチではcommitが一つもないのでこのままではPull Requestが作成できません。
なので以下のように空のコミットでブランチを作成したという内容だけを記録しておきます。

git commit --allow-empty -m "Create a branch"

3. originにpushする

git push origin feature/contact_form

4. Pull Requestを作成する

ここでhubコマンドの登場です。
https://github.com/github/hub

Mac OS XならHomebrewでインストールできます。

brew install hub

hub コマンドでPull Requestを作成する方法はこちらです

hub pull-request -m "[WIP] お問い合わせフォームを実装"

-m でPull Requestのタイトルを付けられますが、先頭に[WIP]をつけました。
これは Work In Progress の略称です。
つまりまだ作業中ですよという意味ですね。

5. チェックリストを作成する

hubコマンドでPull Requestを作成すると標準出力にWebページのURLが表示されますのでそこにアクセスしましょう。

 hub pull-request -m "[WIP] お問い合わせフォームを実装"
https://github.com/yourname/yourrepo/pull/1

チェックリストは作業を意味のある単位で分割して書くようにしています。
たとえば `GET /conatct_us` と `POST /contact_us` という2つのRouteを追加するのであれば
「contact_usのroutesを追加」という項目にまとめてしまいます。

より詳細に記述したい場合はチェックリストに階層をつけるといいとおもいます。

今回は以下のようにしました

Githubのコメント欄にはMarkdownが書けますが、- [ ]というリストを使うとチェックリストを作成できます

このように

6. 作業をすすめる

ここまでできたら後はガンガンコードを書いて開発をすすめます。
途中で一段落ついたらこまめにoriginへpushすることをオススメします。
そうすると何か間違っていたり、別のブランチで同じ作業をしてしまっていたりしたらお互いにコメントしあえるからです。

紆余曲折があってレビューの際に質問されそうだなと思う部分があるなら自分から先にコメントしてしまうのも手です。

例えば以下のように

7. レビューしてもらう

チェックリストが全部Doneしてマージする準備ができたらチームメンバーにレビューしてもらいます。
先にPull Requestのタイトルから [WIP] を削除しておきましょう。

ほんの小さな変更であればこの手順を省略してもいいとおもいますが、できるだけレビューしてもらうことで自分でも気づかなかった部分が見えてくることがあります。

この時、粗探しだけをするのではなく感心したり勉強になるようなことがあれば伝えてあげるといいとおもいます。

8. マージする

マージは基本Pull Requestを作った本人が行います。
GithubのWebページからマージボタンを使うと、忘れがちなDeleteもすぐに行えてマージ済みブランチがたくさんある状態を防げます。

リモートでマージをしたら忘れずにローカルにも反映しておきます。

git checkout master
git pull origin master
git branch -d feature/contact_form

ローカルの feature/contact_form ブランチも削除しておきましょう。

まとめ

以上がgithubを使った作業の進め方です。
そんなに難しいことはありませんが、気をつけるとことがあるとすれば自分の書いたコードは誰かに読んでもらうということを前提に作業をしていると、レビューしてもらう相手はもちろん未来の自分にも優しくなれるように気がします。

標準
Development

Elasticsearch 1.5.2をインストールする

Amazon Elasticsearch Service が 1.5.2 を使っているということで(2016-01-29)

ローカルMacに1.5.2をインストールしようかと思って、brew search elasticsearch を叩くと 2.1系しかヒットしません。
homebrew/versions も見てみましたが一番古くて1.7でした。

ということで本家のダウンロードページからzipをダウンロードすることに
https://www.elastic.co/downloads/past-releases/elasticsearch-1-5-2

ここでzipをダウンロードして、次はインストールか!と意気込んでいたのですが、READMEを読むと

h3. Installation

* "Download":https://www.elastic.co/downloads/elasticsearch and unzip the Elasticsearch official distribution.
* Run @bin/elasticsearch@ on unix, or @bin\elasticsearch.bat@ on windows.
* Run @curl -X GET http://localhost:9200/@.
* Start more servers ...

え、マジ?
指示通り適当な場所に移動させて bin/elasticsearch を叩くとサーバー起動しました!
こんなに簡単なのかー。開発用ならこれで全然問題無いですね。

標準
ruby-on-rails
Development

Railsで複数のasset_hostを動的に切り替える方法

弊社ではRailsアプリのproduction環境ではasset_syncを使ってs3にcssやjsを置くのが定番になっています(世間的に見てもそうかもしれないけど)

asset_syncを使った場合。静的ファイルのホストがRailsアプリと別になるので以下のようにasset_hostを設定します

# config/environments/production.rb
Rails.application.configure do
  config.action_controller.asset_host = '//s3-bucket-name.ap-northeast-1.amazonaws.com'
end

通常はこれで問題ないのですが、s3 bucketの別名を複数用意して静的ファイル読み込みの高速化を図るとか、物理的にデータが別のs3 bucketにあるから変えなきゃいけないなんてときに困ってしまいますよね。

最初はasset_pathをゴニョゴニョするのか…?なんて思ったんですがasset_hostにProcオブジェクトを設定するともとスマートに対応出来ました。

例として、/assets以下のリソースにアクセスする場合と、その他でs3 bucketを分ける場合は

# config/environments/production.rb
Rails.application.configure do
  config.action_controller.asset_host = proc { |source|
    bucket = source.include?('/assets') ? 'assets-bucket' : 'other-bucket'
    "//#{ bucket }.ap-northeast-1.amazonaws.com"
  }
end

引数のsourceはリクエストのパスが渡ってきます。この場合だと ‘/assets/application-87rfhsou4fo4.css’ みたいな感じですね。

標準
ruby-on-rails
Development

複数のRailsアプリで同じDBを使う場合にschema_migrationsを分ける方法

複数のRailsアプリで同じDBに接続する場合DBのマイグレーション履歴が同じschema_migrationsテーブルに格納されるのはちょっとこわい。

でもschema_migrationsって名前はRailsが決めているものだからどうやって変更するの?というのを調べたら接頭辞を使えばいいみたいで

# config/application.rb
module SampleApp
  class Application < Rails::Application
    ...
    config.active_record.table_name_prefix = 'sample_'
  end
end

上記のようにやっておくとアプリ全体のテーブル名にsample_が付くようになって、問題の箇所はsample_schema_migrationsというテーブル名になります。

ちなみにAdminユーザーテーブルを共有して同じアカウントでログイン出来るようにする場合は、sample_接頭辞をつけている側で

# app/models/admin.rb
class Admin < ActiveRecord::Base
  self.table_name_prefix = ''
end

というように個別モデル毎に接頭辞をなくしてあげることも出来るので複数アプリで同じテーブルを参照できますね。

ちなみにDeviseを使っているのでconfig/initializers/devise.rbでconfig.secret_keyを合わせてあげないとログインできませんでした。

多分複数アプリでschema_migrationsを共有しても問題なくて、rake db:migrate:statusすると存在しないmigrationの部分は ********** NO FILE ********** と表示されるだけなんだけどね。

標準