Peepholeという怪しげな名前のgemを作った

https://github.com/tnantoka/peephole
Railsのログをそれなりに閲覧できる画面を追加するエンジン。

ちょっと複雑なテキスト処理をしているサービスがあって、「何か起きたらメールで通知しつつ、ユーザーには最低限の結果を返す」ということをやっている。

で、なぜか最近ヘビーなユーザーがいらっしゃるらしく、エラーがわりと来る。
そのたびに、「エラーの再現のためログからデータをサルベージして…」というのが面倒になってきたので、さくっとログを閲覧できるこいつを作った。

受託開発とかで、「お客さんに一応見れせるようにはなったけど、まだ細部は荒いですよ」的なフェーズにも役立つんじゃないかと思っている。
(ステージングサーバーへのアクセス許可を与えなくても、ログだけ開発メンバーに共有できる。どういう操作をされたかはやっぱりログを見るのが一番。)

類似gemはあるんだろうけど、久々にEngine作ってみたかったので、あえて調べてない。
昔作った時はいろいろハマった気がするけど、今回はスムーズに作れた。わりと満足。

oauth2 gemでアクセストークンをリフレッシュする

oauth2 を使った処理でOAuth2::Errorというふんわりしたエラーが発生したから何かと思ったら、アクセストークンの期限が切れてた。

READMEにはrefresh_token関連の記述はなさそうだったけど、メソッドは用意されている模様。
https://github.com/intridea/oauth2/blob/0ab3c213f7baa43f232eb18fa46301e1a9460f7d/lib/oauth2/access_token.rb#L80

こんな感じで更新できた。
(credentialsはomniauthのrequest.env['omniauth.auth'].credentialsを想定。)

  def initialize(credentials)
    self.client = OAuth2::Client.new(
      Settings.example.client_id,
      Settings.example.client_secret,
      site: Settings.example.site,
      ssl: { verify: false }
    )   
    self.access_token = OAuth2::AccessToken.new(
      client,
      credentials.token,
      refresh_token: credentials.refresh_token, # 追加
      expires_at: credentials.expires_at # 有効期限の判定のためこれも必要
    )   
    # トークンの有効期限が切れていたら更新する
    self.access_token = access_token.refresh! if self.access_token.expired?
    access_token
  end 

注意:access_tokenをDBとかに保存している場合は、更新した後のトークンを保存しておくこと。

Railsの中間テーブルの名前をどうするか

UserとTeamの関連をMember(ship)にするとか、適切な名前があればそれがいい。
問題なのは適切な名前がない場合。

昔、それぞれのテーブルの複数形をアルファベット順に繋げるという規約を聞いたことがあった。(teams_usersみたいにする)
ただ、ソースを覚えてなかったので改めて調べた。

日本語は、Railsの基礎知識 - - Railsドキュメント などすぐに見つかった。
これはどこから来てるのか?

公式ガイドのhas_and_belongs_to_manyのところに記述があった。
http://guides.rubyonrails.org/association_basics.html#creating-join-tables-for-has-and-belongs-to-many-associations
http://railsguides.jp/association_basics.html#has-and-belongs-to-many%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91%E3%81%AB%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E7%B5%90%E5%90%88%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B

has_and_belongs_to_manyは純粋な結合用のテーブル(モデルを持たない)を使った関連。
:join_tableオプションで指定しなければ、先述のルールのテーブル名が利用される、と。

適切な名前がないようなケースでは、中間テーブルが情報を持つのはおかしいだろうから、そもそもhas_and_belongs_to_manyを使うべきだったのか…。

find_or_create_by!のblockにハマる

createした時だけじゃなく、見つかった時にもblock内を実行して更新してくれると勘違いしてた。

Railsのfind_or_create_byのblockはcreateした際にしか実行されない - Qiita
これと同じ。

tokenが更新されることを期待して、

def find_or_create_with_auth_hash!(auth_hash)
  find_or_create_by!(uid: auth_hash.uid, provider: auth_hash.provider) do |identity|
    identity.token = auth_hash.credentials.token
  end
end

みたいに書いてたんだけど、

Octokit::Unauthorized: GET https://api.github.com/user: 401 - Bad credentials

が出続けてハマった…。

CentOS 7でNginx、Unicornにハマる

今まで何度も使ったことある設定なのに、502 Bad Gatewayになった。

エラーログには以下が出ているが、socketファイルは存在して、パーミッションも問題なし。

[crit] 27195#0: *2 connect() to unix:/tmp/unicorn-example.socket failed (2: No such file or directory) while connecting to upstream, client: IPアドレス, server: example.com, request: "GET / HTTP/1.1", upstream: "http://unix:/tmp/unicorn-example.socket:/", host: "example.com"

困り果てていたら、RedmineをCentOS 7上で動かすーUnicornとNginx編 - ソフトウェアエンジニアリング - Torutk に辿り着いた。
なんと、NginxとUnicorn間で/tmpを共有できないとな。

試しにshared/tmpに置いてみたらあっさり動いた。


後日またエラーになってて、socketファイルをみたら775になってた。(のでnginxユーザーからアクセスできなかった)
777にしたら動いたけど、原因不明。

Ansibleで

- name: shared
  file: path=/var/www/{{ item }}/shared state=directory owner={{ user }} group={{ user }} mode=0775 recurse=yes

をやってたせいだった。