現時点の最新版であるRails4.2.1以下MySQLデフォルトだと絵文字が保存できません。コンシューマー向けサービスのコメント欄など今どきは普通に絵文字を入力されるのですぐに問題になります。(Incorrect string valueエラーになる)

実直な対応方法はmysqlでutf8ではなくutf8mb4を使うというものです。4byteのunicodeも保存できるようになるので絵文字も問題無しです。絵文字の種類が増えても問題無いでしょう。

ActiveRecordをutf8mb4で動かす - Qiita

穏便な解決方法

rails + mysqlデフォで動かないのと、一部のカラムでだけ対応したいこと、全テーブルのインデックスが長くなるとパフォーマンスに影響でそう、mysqlが古いと対応してない、など後ろ向きの理由があって、怖話ではDBに格納するときだけhuman friendlyな文字に変換し、出すときに戻すという実装にしました。

前向きの理由としては、画像への変換と組み合わせて怖話独自の絵文字を追加し易いという点があります。(LINEスタンプ的なのやりたかった)

実装

class Comment
  def body=(text)
    write_attribute(:body, Rumoji.encode(text))
  end
    
  def body
    text = read_attribute(:body)    
    Rumoji.decode(text) if text.present?
  end
end
mysql> select body from comments order by id desc limit 1;
+-----------------------------------------+
| body                                    |
+-----------------------------------------+
| テスト:poop::thumbsup::musical_note:    |
+-----------------------------------------+
1 row in set (0.00 sec)

rumojiはまさにそのために作られたgemでとっても簡単です。

絵文字共通化問題

非対応プラットフォームでも表示できるよう、画像に変換するというのはまた別のお話・・・。

カラー絵文字ライセンス問題 - komagata

こちらの記事の解決編です。

ActiveRecord::RecordNotFoundのエラーは放置すべきか? - komagata

@hiroshi3110さんからズバリな答えをいただきました。

怖話で実装

# lib/record_not_found_by_trustless_param.rb:
class RecordNotFoundByTrustlessParam < StandardError; end
# app/controllers/comics_controller.rb:
class ComicsController < ApplicationController
  before_action :set_comic, only: :show
  
  def show
  end

  private
  def set_comic
    unless @comic = Comic.find_by(id: params[:id])
      raise RecordNotFoundByTrustlessParam                                                                                                                                                                  
    end
  end
end
# app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
  rescue_from RecordNotFoundByTrustlessParam, with: :not_found
  
  private
  def not_found
    render file: "#{Rails.root}/public/404.html", layout: false, status: 404
  end
end

@hiroshi3110さんの言うとおり、信頼出来ない外部からのidを元にしたfindで見つからない場合はRecordNotFoundByTrustlessParamにし、rescue_fromで拾って404を出すようにしました。(404を動的テンプレートで出すのは良くないと思うのでpublic以下の静的ファイルを読む)

自分の中のベストプラクティスにしようと思いました。

弊社ではGithub IssuesのWrapperであるHuboardをタスク管理に使っているんですが、ちょっと便利な機能が増えました。

それは複数のreposのissueを一つのカンバンに出せる機能です。

何が嬉しいのかというと、弊社サービス怖話で言えばgithubのreposは

  • fjordllc/kowabana
  • fjordllc/kowabana-ios
  • fjordllc/kowabana-android

というようにweb, ios, androidに分かれてるのですが、それぞれ専属の人がいるわけじゃないのでまとめて見れたほうが都合がいいのです。(そもそもwebviewメインなのでios, androidの方はそんなにissueがない)

(Workingにある「怖話1.5をリリース」ってのはkowabana-iosのissue)

ちょいと高価いですがいい感じの代替サービスが見つかるまでは使い続けそうです。

ほぼ全部インターンの@hir_aikさんのお陰で怖話をrails3.2から4.0.5にアップデートできました。

やったー!勢いにのって4.1行くぞー!

2011年5月22日のrails3.0.7が最初のコミットでちょっと古くなってきましたが、railsアップデートを諦めた時がサイトが死ぬときと心得て頑張ります。

Engine Yard Cloudを使っててrailsのproduction.logをちょっとしたフィルターかけてサッと見たい。(productionでのバグの原因究明などで)

production.logを探るのってEYCに限らず、機会が多いわりに面倒臭い。

$ scp deploy@app.kowabana.jp:/data/kowabana/shared/log/production* .
$ gzip -d production.log-*.gz
$ grep -r 'foo' .

とか。productionサーバーへのアクセス権がある人しか見れないのも手軽じゃない。

次からはこんなことしたくないのでEYCのlogentries addonを使ってみた。logentriesは無料でも使える範囲が大きいし、以前使ったことがあるので慣れているのでちょうどいい。

Getting started with Logentriesの通りに進めていき、下記のようにproduction.logを追加した。

diff --git a/cookbooks/le/recipes/configure.rb b/cookbooks/le/recipes/configure.rb
index 8a4432d..8225077 100644
--- a/cookbooks/le/recipes/configure.rb
+++ b/cookbooks/le/recipes/configure.rb
@@ -16,6 +16,7 @@ follow_paths = [
 ]
 (node[:applications] || []).each do |app_name, app_info|
   follow_paths << "/var/log/nginx/#{app_name}.access.log"
+  follow_paths << "/data/#{app_name}/shared/log/production.log"
 end

upload, applyしてからLogentriesのDashboardにアクセス。

自前でログサーバー作ってアレコレすると大変なので手軽で重宝しそうです。

怖話の英語版(kowabana.net)を作ろうとしていてその実装方法に迷っています。

前提情報

怖い話がメインコンテンツなので例えばアメリカ人に日本語の怖い話を見せても仕方が無い。それどころか、"Recent Scare Story"とかに日本語の怖い話のタイトルが並んでいたら「チャイニーズのサイトに来てしまったぜ」みたいな感じになって二度と来てくれないだろう。

システムやUIは流用できるが、データ(怖い話や多分ユーザーも)は流用できない。

実装の種類

  1. 2ソース・2DB方式

    別サイトとして作る。

  2. 1ソース・2DB方式

    別サイトとしてデプロイするが、ソースは共通。アプリ自体は言語に特化して起動する。ENV['KOWABANA_LANG']とかいう環境変数を作って、それが'en'だったら英語版、'ja'だったら日本語版を表示する。

  3. 1ソース・1DB方式

    一般的な国際化のようにAcept-Languageとかで切り替える。データはデータ自体に言語情報を持ち(usersやstoriesテーブルにlangカラムを持つ)アクセスしてきている人によって見せるデータを分ける。

どうしよう

正直言ってどの方法を選んでも大変な気がしてならない。以前、2の1ソース・2DB方式をやったことがあるが、やっぱり結構大変だった。CookPadは1の2ソース・2DB方式だよね確か。北米向けのレシピを日本のコンテンツから翻訳してきて載せている。怖話も最初の検証段階はそれをやるのがいいのかなあ?

みなさんだったらどうしますか?

怖話(こわばな)でラジオにCMを出しました。

怖話が山口敏太郎のラジオ番組に進出!ラジオCMになりました! - 怖話からのお知らせ

世界最大の怖い話サイト「怖話」がラジオに進出!

オカルト評論家 山口敏太郎のラジオ番組内でラジオCMが放送されます!

以前発表いたしました「怖い話グランプリ」の期待の高まるなか、タッグを組んでいる山口敏太郎のラジオ番組「日本大好き」に怖話のラジオCMが流れることになりました!

音源は@machidaさんが知り合いに声も録音して作ってくれました。

@machidaさんはWebデザインよりDTM歴の方が圧倒的に長いのでメインで使われてるBGMも自作です。PS版弟切草のCMのイメージで作ったと言ってました。

Githubも使ってたというAWSベースのPaaSの老舗Engine Yard Cloudを試してみました。

Engine Yard CloudはAWSの東京リージョンもサポートし、最初の500時間無料とのことで今後複数台構成を考えている怖話の移行先として動くかどうかデプロイしてみます。

Application作成。githubのreposを直接入れることができるのは嬉しい。

deployのためのキーが発行されます。これをgithubや自分のgit repos serverに登録してやれば勝手に取ってきてdeployしてくれると。

怖話のgithub reposのDeploy Keysにさっきのキーを登録。

Environmentの作成。Application Serverが選べるのか。怖話はunicornなので選択。rubyもgemもデフォルトでOK。

無料お試し中は東京リージョンは選べないそうです。残念。MySQLのバージョンを選んで、へーDBバックアップやインスタンスのスナップショットもデフォルトで取ってくれるのか。

一番気になるインスタンス構成の選択画面。

Single Instance
1台でアプリもDBも賄う。
Staging Configuration
appサーバー2台とDBサーバー1台。
Production Configuration
appサーバー3台とDBサーバー2台(master and slave)
Custom Configuration
カスタム。(お試し中は派手にできないらしい)

appサーバーは良い感じにbalanceしてくれるらしい。しかしDBサーバーはどうなってるんだろう。やっぱりアプリ内で明示的にmaster, slave分けてアクセスするんだよね?

とりあえずStaging configurationで。

後はただ起動処理を眺めます。ログを見るとchefで色々ガーっとやってくれてるみたいです。

rake db:migrateのところをrake db:seedにして怖話のseedを実行。そして割り当てられた仮のドメインにアクセスしてみると。

おお、特に何もいじってないのに怖話のproductionが動いた!凄いぞEYC!

鬼サポート

実は初めてdeployした時は500番が出て動きませんでした。

右下にあるサポートチャットに、

"I deployed an app. But dosen't work."

とか物凄い抽象的な助けを求めたら、

「railsアプリにエラーがあるのかもしれない。ここからsshでログインできるから"/path-to/production.log"にあるログを見てみたらどうだい」

みたいなこと言われたので見てみたら、Engin Yard Cloudとか関係無く、普通に怖話のproduction(のseedのコード)がバグってただけでした。修正してアップし直したら動きました。

多分あっちは昼間なんだろうけど、夜の2時頃、お酒を飲みながら適当にインフラを弄ってた俺としては助けを求めた瞬間に的確に帰ってきたサポート体制に感動しました。(飲酒も手伝って)

logentriesのサポートチャットを使った時も同じような体験をしましたが、技術的にちゃんとわかってるサポートが瞬時に答えてくれるってのは今時のサービスでは当たり前なんでしょうか?ありがたすぎるぞ。

感想

VPSで動かしてた怖話のproductionが何の変更も無しに動いたのはびっくり。あと金額も俺の超概算だとAWS直に10〜20%ぐらい載っけるぐらいの思ってたより安い価格設定。

自分の用途としてはある程度規模のサービスで、しっかりした会社からの受託開発を受けたら本気で検討する価値ありだなと思いました。

なんだかんだ行っても複数台構成には色々と面倒がついてきますし(デプロイ、監視、ロギング、バックアップ等)、何よりもAWSで作りこんだら引き継ぎが超面倒そう。リアルに想像すると「EC2で作りこんであの会社に引き継いだら運用できるのかな・・・」って思う時がありますが、EYCなら引き継げそう。特に鬼サポートはちゃんとした会社にとってはありがたいと思います。ググレカスとか言われないですし。

昨日Team Dashboardを設定して今日見てみたら…

もう、オマエら勝手に弄るなよなぁw

そもそもイントラ向けっぽいのでデータ変更系メソッドだけにBASIC認証かけときました。

# app/controllers/api/base_controller.rb: 
module Api
  class BaseController < ApplicationController
    http_basic_authenticate_with name: 'admin', password: 'foo', only: [:create, :update, :destroy]
    # some codes
  end
end

これでよし。

追記:


Created with Gifboom

弊社オフィスのダッシュボードは現Engine Yard@yandoさんから貰ったEeePCで動かしています。@yandoさんとても助かっています。ありがとうございます。みんなEngine Yard使おう!(東京リージョンもあるよ!)

オフィスでGoogle AnalyticsとかMixpanelとかlogentriesとかディスプレイに表示させてたんですが、適当じゃなくてそろそろちゃんとした指標(ユーザー増加率とか)を出して共有したいなと思いました。自分で作るのは面倒なので探していたらTeam Dashboardがrails + backborne.jsで出来てて手頃そう。(qnypさんのブログで知りました)

$ git clone git://github.com/fdietz/team_dashboard.git
$ bundle
$ cp config/database.example.yml config/database.yml
$ rake db:create && rake db:migrate
$ rake populate
$ rails s

デフォルトはmysqlなのでherokuで動かしたい場合はpostgresql-adapterを追加。

# Gemfile:
group :production do
  gem "activerecord-postgresql-adapter"
end

怖話では毎月PV・収益の報告をしてますが、これでリアルタイムで見れるようになりました。

怖話 - Team Dashboard