Wednesday, February 22, 2012

Gumadβで使ったgemまとめ

昨日、Gumadβをリリースしました。話題のGumroadをまとめたサービスです。

Rails/Heroku/AmazonS3 というベタな構成のサービスを構築するにあたって使ったgemをまとめます。

今回、開発にあたってのポイントとしては、
  • リリースまでの時間を抑えるためにできるだけ新しい試みは避ける
  • 開発、本番環境の運用の時間的、金銭的コストを抑える構成
  • 話題のサービスの関連ということで、一時的なアクセス増に耐えられる構成
ということで、ベタ構成で安全にスタートすることにしました。

まずは、Gemfile をどどんと。

source 'http://rubygems.org'

gem 'rails', '3.1.3'
gem 'i18n'
gem 'haml'
gem 'oauth'
gem 'twitter'
gem 'nokogiri'
gem 'paperclip', "~> 2.0"
gem 'aws-sdk', "~> 1.3.4"

group :assets do
  gem 'sass-rails',   '~> 3.1.5'
  gem 'therubyracer'
  gem 'coffee-rails', '~> 3.1.1'
  gem 'uglifier', '>= 1.0.3'
end

gem 'jquery-rails'

group :test do
  # Pretty printed test output
  gem 'turn', '~> 0.8.3', :require => false
end

group :test, :development do
  gem 'sqlite3'
  gem 'rspec-rails'
  gem 'factory_girl'
  gem 'factory_girl_rails'
end

group :production do
  gem 'pg'
  gem 'dalli'
end

# vim: filetype=ruby
次にそれぞれのgemについての説明をしていきます。

ベースとして使っているgem

プロダクトとして必要としているgemはRails本体を含めて9つでした。 複雑なサービスではないのでこんなものでしょうか。
rails
言わずと知れた、Ruby on Rails です。これがないと始まらないです。
i18n
最近Railsを使うときは必ず、i18nするようにしてます。「世界に向けたサービスを」みたいなたいそれたことを考えている訳ではないですが、誤字脱字や文言の修正が楽になったりします。

あと、どうでもいいことなんですが、controller に日本語書いたりすると、

#-*- coding: utf-8 -*-
がなくて怒られたりするので、精神衛生上も日本語はコードから追い出しておきます。
haml
始めてみたときは、「せっかくMVCして、viewをデザイナーさんにお任せできるというのに、プログラマしかわからないHTMLもどきを使うなんて。。」と思いましたが、使ってみると、素敵です。

コードの可読性が増し、きれいに書こうって言う気持ちになります。 間違いも拾ってくれることが多いので、今や手放せない存在です。

oauth
Gumadはtwitterのつぶやきから、アイテムを自動収集しています。 twitter APIを使うために、OAuthが必要です。
twitter
twitter APIを使うためのgemですね。使い方が簡単すぎます。 キーを設定したら、
Twitter.search('gumroad.com')
だけですね。 jnunemakerさんのmongomapperにも別のプロダクトではお世話になっていて、ありがたい限りです。
nokogiri
Gumroadからデータをどうやって持ってきているのか、そうです。Nokogiriです。 HTMLをパースしてます。

Gumroadさん、デザインは変えてもいいですが、HTML構造は変えないようにしていただきたい。と願う毎日です。

image_url = Nokogiri::HTML(@body).css('#image-preview-container img:first').first
みたいな。なんて地味なんでしょう。いまにもGumroadのHTMLソースがかわるんじゃないかと夜も眠れません。
paperclip
Gumroadは太っ腹なので、サムネイルの画像サイズもえらい大きかったりします。 開発当初はGumroadのサムネイルをそのまま表示していましたが、画面表示に影響が出るほど、サムネイルのサイズが大きかったので、取得することにしました。 paperclipはActiveRecordに画像を添付することができるGemです。
class Item < ActiveRecord::Base
  has_attached_file :image,
      :styles => { :original => "250>x210", :large => "1000>x400" }
end
なんてことをすると、
item.image? # アイテムに画像が添付されていたら true
item.image.url # アイテムの添付画像のURL
item.image.url(:large) # largeサイズアイテムの添付画像のURL
と言った感じでサムネイルを扱うことができます。
aws-sdk
herokuにサムネイルをおいておくことはできません。それで、サムネイルをどこにおくか。というと、Amazon S3。Amazonです。

paperclipはaws-sdkを使うことで、S3をストレージとしてサムネイルの管理をすることができます。以前はaws-s3というgemだったようですが、最新ではaws-sdkが推奨されています。

requireして、S3のキーなんかを has_attached_file したときに指定すると使うことができます。paperclipとともに初めて使ったんですが、S3が東京リージョンだとアップロードはできるけど、参照URLが間違っているという状況が発生しました。

これは、item.image.url で返ってくるURLのドメイン部分がAWSからのレスポンスとかではなく、ライブラリ内で決めうちのため、みたいです。has_attached_file で s3_host_name => “s3-ap-northeast-1.amazonaws.com” と指定することで、東京リージョンのS3ドメインを返すようになりました。

class Item < ActiveRecord::Base
  has_attached_file :image,
    :styles => { :original => "250>x210" },
    :storage => :s3,
    :s3_protocel => "https",
    :bucket => SERVICE_CONFIG['s3_bucket_name'],
    :s3_host_name => SERVICE_CONFIG['s3_host_name'],
    :s3_credentials => {
      :access_key_id => SERVICE_CONFIG['s3_access_key_id'],
      :secret_access_key => SERVICE_CONFIG['s3_secret_access_key'],
    },
    :path => ":attachment/:id/:style:file_extension"
end
READMEをみていると、paperclipは本来、ユーザーがアップロードしたファイルを処理するためのgemみたいで、今回のように、URL参照で画像ファイルを取得する
image = image_url.blank? ? nil : open(image_url)
の様なことをすると保存されたファイル名が open-uri.xxxxxxxxxxxx みたいな何とも間抜け、かつ、拡張子のない不安な感じになってしまいます。 そのため、拡張子は元のURLから取得する形で、ファイル名を指定しています。
class Item < ActiveRecord::Base
  Paperclip.interpolates :file_extension do |attachment, style|
    attachment.instance.image_url.blank? ? nil : File.extname(attachment.instance.image_url.split('?')[0])
  end
end
jquery-rails
jQueryです。必要です。あんまりたいしたことはしてないですけどね。

asset管理のgem

Rails3でasset pipelineという機能が追加になってから、staticファイルをproduction環境用に準備する手間が減り、ずいぶん楽になりました。 あとは、CSSスプライトを勝手にしてくれるとありがたいんですけどね。
sass-rails
SASSはいいですね。 プログラマはみんな、スタイルシートの非効率さにイライラするはずです。 SASSを使ってからそんなイライラから解放されています。
$page-width: 980px; 
$base-color: #C12D03; 
みたいなことを書いていると気持ちがすーっと楽になります。

ただ、controllerをgenerateするたびにファイルが作成される意味がいまいちわかりません。 どういう使い分けを期待されてるのかわからないので、1つのファイルにまとめています。

therubyracer
javascriptの実行環境です。Rails3.1 に必要と言われるので、使っています。特にこだわりもありません。
coffee-rails
さて、coffeescriptです。無理矢理使ってます。いまだに良い理由がわかりません。 確かに生のjavascriptよりは可読性が増しますが、それは学習コストに見合ったメリットではない気がします。 もう少し使って、coffeescriptらしいコードがかけるようになればわかるのか、もう少し試しているところです。
uglifier
javascriptのcompressorです。production環境のパフォーマンスを考えると、絶対必要ですが、正直、何でも良いです。

開発、テスト環境のgem

短期間で制作したこともあり、今のところ、テスト環境はありません。 開発環境は開発中だったりする他のサービスとともに、さくらのクラウド上に構築しました。

specやfactory_girlが開発環境でも使われているのは rails generate したときにテンプレートを作ってもらうためです。

sqlite3
これも基本です。herokuは開発環境もpostgresを使ってproduction環境との差を最小限にすることを推奨していますが、手抜きです。ごめんなさい。

実際に、こんなコードでproduction環境でのみ発生するエラーがありました。

user = User.where(:twitter_post_id => twitter_user_id).first
SQLiteではSTRINGなカラムに対してのintegerの比較を許してくれますが、PostgreSQLでは許してくれないようです。
rspec-rails
テストはRspecです。普通です。GumroadのHTMLパーサー部分のテストを重点的に書きました。 未だに満足の行くテストを書けたことがないです。いつか、リリース時にストレスを感じない、満足のいくレグレッションテストをもつアプリケーションを書くのが夢ですね。
factory_girl
ほとんど使っていないんですが、factory_girlです。 あまり使いこなしている感もないので、こちらも取り立ててどうこうって言う感じではないです。 それにしても、paperclipといい、thoughtbotの人は便利なものを作りますね。
factory_girl_rails
Railsで使うので、セットです。 fixtureのauto loaderとrails generate model をしたときのテンプレート作成を提供するみたいです。

テスト環境のgem

turn
良く知らないです。。テスト結果がわかりやすく見えるってこと?

本番環境のgem

本番環境はheroku上に構築しました。(といってもgit pushするだけ) herokuだと、日本からのアクセスでlatencyの問題があるので、fragment cacheなどを使い、その他のところでできるだけレスポンス時間を下げるようにしました。
pg
herokuなので、PostgreSQLを使っています。 最近、MongoHQを使うことがあったので、このサービスでも使おうかと考えましたが、今回の場合、扱いやすい以外の大きなメリットもないので、普通にRDBを使うことにしました。

herokuでは1分おきのcronとかが有料なので、そういった処理をする場合、MongoHQを使うとcron起動用の環境を別に作って、そこからcron処理をすることができるので便利です。

dalli
初めて使ったgemなんですが、fragment cacheのために使っています。 herokuではviewのcacheをファイルにしておくことができないので、memcacheを使ってview cacheを実現します。

コード上で特別なことをする必要は全くなく、使うにあたってやったことは、addonをenableしただけでした。

- cache(item) do
  %div.item
    = item.title
という感じで fragment cacheしています。
以上が、今回開発したサービスで使ったgemです。

Notes

  1. naopidayo reblogged this from detham
  2. matuken reblogged this from detham
  3. yasubkk reblogged this from detham
  4. detham posted this