日光のペンションで2泊3日の開発合宿してきました

日光のペンション「はじめのいっぽ」で開発合宿をしてきたのでメモというか感想というか、です。

成果物

やってよかったこと

  • 前日までに開発環境を準備した
    • Javascript素人なのでイマドキの環境わからない
    • 開発環境の準備で合宿中の時間の大半を潰した経験がある
      • やりたいことに集中する用意はしておくべき
      • 合宿で作るものは1から作り始めるのではなく、3くらいから始めるのが良さそう
  • 共通のチャットルームにSlackを使った
    • スマートフォンアプリがあるので移動中の連絡にも使えた
    • 雑談とか、ファイルの受け渡しとか、URLの共有とか
      • 大半はHubotで遊んでたけど


そのほか感想など

  • ルータの調子が悪いとかなんとかで、何度かWiFiがつながりにくいことがあった
    • IPPO0〜IPPO3まで用意してあるので繋がりやすいやつに繋ぐ運用
  • おふろがめっちゃ傾いてる
  • 籠りっきりになるから息抜きを用意しておいたほうがいい
    • PS VitaからリモートプレイでFF14にログインしてあそんだ^q^

飯テロの時間

ご飯美味しかったです。ご飯のおかわりは1人1杯までできます(重要)。





それにしても2年ぶりの更新である…

Railsアプリケーションを公開するならAssets on Cloudパターンを使おう

Assets on Cloudパターンとは

「Assets on Cloudパターン」*1Railsデザインパターン*2の一つ。Railsアプリケーションの静的なコンテンツ(Assets)をクラウドに配置するパターンである。ファイルサイズの大きい画像などをクラウドに配置することでウェブサーバへのリクエストを減らし、ネットワークリソースを節約する。
Assetsの配置先はAmazon S3を推奨する。

asset_syncの使い方

「Assets on Cloudパターン」はasset_syncというgemを利用する。

Amazon S3の設定方法

asset_syncを設定する前にAmazon S3バケットを作っておく。バケット名を自分が持ってるドメインサブドメインと同じにしておくと少しだけ幸せになれる。ドメインを持っていない場合、適当なバケット名でもいいが、全世界で一意にする必要がある。(他の人と同じバケット名でバケットを作成することができないので注意)
また、国内ならRegionをTokyoにしておかないと、ネットワークの速度が遅くなってしまうので注意。
以降はTokyoリージョンに作成したassets.example.comというバケットで説明を進める。

asset_syncの設定方法

Gemfileに以下の記述を追加する。

gem 'asset_sync'

以下の内容でconfig/asset_sync.ymlを用意する。

defaults: &defaults
  fog_provider: 'AWS'
  fog_directory: 'assets.example.com'
  fog_region: 'ap-northeast-1'
  aws_access_key_id: '<AWSのACCESS KEY>'
  aws_secret_access_key: '<AWSのSECRET ACCESS KEY>'
  existing_remote_files: delete
  manifest: true
  gzip_compression: true

development:
  <<: *defaults

test:
  <<: *defaults

production:
  <<: *defaults

config/environments/production.rbに以下の記述を追加する。

  config.action_controller.asset_host = '//s3-ap-northeast-1.amazonaws.com/assets.example.com'

以上で設定は完了。Assetsのprecompile時に自動的に配置されるようになる。

$ bundle exec rake assets:precompile

capistranoとの連携


capistranoの設定方法

Railsの汎用デプロイツールであるcapistranoと組み合わせることで、アプリケーションのデプロイと同時にAssetsをクラウドに配置することができる。
config/deploy.rbに以下の記述を追加する。

namespace :assets do
  task :precompile, :roles => :web do
    run "cd #{current_path} && RAILS_ENV=#{rails_env} bundle exec rake assets:precompile"
  end

  task :cleanup, :roles => :web do
    run "cd #{current_path} && RAILS_ENV=#{rails_env} bundle exec rake assets:clean"
  end
end
after :deploy, 'assets:precompile'

Amazon S3ならCDNの導入もスムーズ

Amazon S3Amazon CloudFront(CDNサービス)との相性がいいため、CDNの導入がスムーズにできる。公開したアプリケーションへのアクセスが増えてきたら導入を検討してもいいだろう。
また、Amazon S3は1GBまでなら月額10円程度で使うことができる。しかもAWSに新規で登録したユーザなら12ヶ月間無料で使うことができる。まだAWSのアカウントを持っていないユーザはこの「Assets on Cloudパターン」を今すぐ無料で試そう。

*1:僕が勝手に名付けました

*2:僕が勝手に作ったデザインパターンです

はぐれメタルBOTをRuby on Railsに移植しました

はぐれメタルのBOTそのサイトRuby on Railsに移植しました。2〜3年前に最初のはぐれメタルができてからほとんど手を加えずに動かしてきたのですが、TwitterAPIがそろそろ使えなくなりそうなのと、当時の自分のコードなんてメンテナンスする気になれないので、1から作り直しています。
今回の移植で勉強になったことをつらつらと書き連ねます。

Haml

http://haml.info/
もともとのはぐれメタルのサイトはベタ書きの静的なhtmlで構成されていました。それをマルッとHamlに書き直しています。これと言って特別なことはしていません。HamlかわいいよHamlペロペロ。

rails runner

はぐれメタルBOTは定期的に動かすスクリプトなのでRailsには乗せられないかと思ってたんですが、そんなことはなかった。これを使えばRailsに乗ったRubyのコードを簡単に実行できます。

# lib/batches/battle/main.rb
module Batches
  module Battle
    class Main
      def self.start
        # はぐれメタルの処理
      end
    end
  end
end
$ rails runner Batches::Battle::Main.start
本番環境では
$ rails runner -e production Batches::Battle::Main.start

ファイルをどこに置くか迷ったのですが、lib/batches/battle/main.rbに置きました。

便利なGemたち

便利な世の中になったもので、何かしたいと思ったらたいてい誰かが便利なgemを作ってくれています。今回使ったgemの中で特に便利だと思ったものをいくつか挙げます。

tweetstream

https://github.com/intridea/tweetstream
twitterのストリーミングAPIに対応したgem。このgemのおかげではぐれメタルがリアルタイムに応答するようになりました。以下のように使用しています。

EM.run do
  stream = TweetStream::Client.new

  EM::Timer.new(180) do
    stream.stop # 180秒後にストップ
  end

  stream.userstream do |status|
    # はぐれメタルへの攻撃に対する処理
  end
end
settingslogic

https://github.com/binarylogic/settingslogic
YAMLで定数を管理することができるgem。develop環境とproduction環境で値を変えるなんてこともできます。
こんな感じでtwitterの接続情報をYAMLに書いてます。

# config/twitter.yml
development:
    consumer_key: hogehoge
    consumer_secret: fugafuga
    oauth_token: foofoo
    oauth_token_secret: barbar

production:
    consumer_key: hogehoge
    consumer_secret: fugafuga
    oauth_token: foofoo
    oauth_token_secret: barbar
# lib/settings/twitter.rb
module Settings
  class Twitter < Settingslogic
    source "#{Rails.root}/config/twitter.yml"
    namespace Rails.env
  end
end
capistrano

https://github.com/capistrano/capistrano
定番のデプロイツールです。今回使ってみてその簡単さに惚れ惚れしました。
最終的に以下のコマンドでデプロイできるようになりました。プログラムの更新、DBのmigration、assetsのprecompile、unicornの再起動、cronの設定などもろもろが1コマンドで完了です。

$ cap production deploy
whenever

https://github.com/javan/whenever
cronの設定をRubyで書ける優れものです。cronの設定って結構間違えやすいのですが、これなら間違えない設定を書き出してくれます。

# config/schedule.rb
set :output, {:error => 'log/error.log', :standard => 'log/cron.log'}
every 6.hours do
  runner 'Batches::Battle::Main.start'
end
# crontab -l
0 0,6,12,18 * * * /bin/bash -l -c 'cd /path/to/application && script/rails runner -e development '\''Batches::Battle::Main.start'\'' >> log/cron.log 2>> log/error.log'
asset_sync

https://github.com/rumblelabs/asset_sync
precompileしたassetsをクラウドに配置できるgemです。僕はこれを使ってCSSJavascriptと画像をAmazonS3に配置しています。静的なファイルを置くならAmazonS3が一番優秀だと思います。
以下の設定ファイルを用意すればassetsのprecompile時に自動的に配置してくれるようになります。

# config/asset_sync.yml
production:
  fog_provider: 'AWS'
  fog_directory: 'Bucket名'
  fog_region: 'ap-northeast-1'
  aws_access_key_id: 'AWSアクセスキー'
  aws_secret_access_key: 'AWSシークレットアクセスキー'
  existing_remote_files: delete
  manifest: true
  gzip_compression: true
# config/environment/production.rb
Hoge::Application.configure do
  ...
  config.action_controller.asset_host = '//s3-ap-northeast-1.amazonaws.com/バケット名'
end

三者レビュアー

今回は id:nise_nabe という第三者のレビュアーがいたので、趣味とはいえ緊張感をもってコードを書くことができました。ソースコードBitbucketで管理して、彼のアカウントにRead権限だけ与えていました。これはもっと流行らせるべき。
オープンにしたくないソースコードを、一部の知人に公開することでよりよいコードが書けるようになると思います。

これからのはぐれメタルBOT

今回のリニューアルでさりげなくゴールドがもらえるようになったはぐれメタルBOT。ゴールドを集めておくといいことあるかも?

なんで誰もrails destroyを教えてくれなかったんだ #rails

あけましておめでとうございます。年末年始の休みを利用してRailsで遊んでおります。
そんななか地味に便利なRailsコマンドを見つけたのでメモ。

$ rails destroy

rails generateって便利ですよね。モデルやらコントローラやら何やらかんやらひな形を作ってくれる便利コマンドです。例えばユーザのひな形を作りたいなんていったら以下のコマンドで一発です。

$ rails generate scaffold user name:text age:integer
      invoke  active_record
      create    db/migrate/20130104071427_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    test_unit
      create      test/functional/users_controller_test.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      test_unit
      create        test/unit/helpers/users_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.js.coffee
      invoke    scss
      create      app/assets/stylesheets/users.css.scss
      invoke  scss
   identical    app/assets/stylesheets/scaffolds.css.scss

git statusをみると沢山のファイルが増えたことが確認できます。config/routes.rbまで編集してくれてますね。賢い!

$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   config/routes.rb
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       app/assets/javascripts/users.js.coffee
#       app/assets/stylesheets/users.css.scss
#       app/controllers/users_controller.rb
#       app/helpers/users_helper.rb
#       app/models/user.rb
#       app/views/users/
#       db/migrate/20130104072447_create_users.rb
#       test/fixtures/users.yml
#       test/functional/users_controller_test.rb
#       test/unit/helpers/users_helper_test.rb
#       test/unit/user_test.rb
no changes added to commit (use "git add" and/or "git commit -a")

こんなに沢山のファイルをコマンド1発で作ってくれるとかrails△とか言ってたら間違いに気づく訳ですよ。「あかんnameはtextやなくてstringや」と。
この間違いに気づいたとき、今までの僕はファイルを一つ一つ消していってました。作るのは簡単なのに取り消すのがめんどくさいってどういうことやねん。
ところが、これらのファイルをコマンド1発で消してくれるコマンドがあったんです。マジ、rails△。以下のコマンドで1発です。

$ rails destroy scaffold user
      invoke  active_record
      remove    db/migrate/20130104071427_create_users.rb
      remove    app/models/user.rb
      invoke    test_unit
      remove      test/unit/user_test.rb
      remove      test/fixtures/users.yml
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      remove    app/controllers/users_controller.rb
      invoke    erb
      remove      app/views/users
      remove      app/views/users/index.html.erb
      remove      app/views/users/edit.html.erb
      remove      app/views/users/show.html.erb
      remove      app/views/users/new.html.erb
      remove      app/views/users/_form.html.erb
      invoke    test_unit
      remove      test/functional/users_controller_test.rb
      invoke    helper
      remove      app/helpers/users_helper.rb
      invoke      test_unit
      remove        test/unit/helpers/users_helper_test.rb
      invoke  assets
      invoke    coffee
      remove      app/assets/javascripts/users.js.coffee
      invoke    scss
      remove      app/assets/stylesheets/users.css.scss
      invoke  scss

めっちゃ消えた!git statusをみるとちゃんとconfig/routes.rbも元通りです。

$ git status
# On branch master
nothing to commit, working directory clean

うむ。すっきり。

まとめ

generateしたものをdestroyする準備はできているからこれからも怖がらずにgenerateしまくろうね!ってことで。

Rails3のルーティングについて自分なりにまとめてみる #rails

最近Rails3で遊んでて、ようやくルーティングが分かってきたのでまとめます。僕の知ってる範囲しかまとめないので、すべての機能を網羅するわけではありませんが、これだけ知ってれば不自由しない気がするのでそれで良いです。ウフフ、オッケー。

基本的なこと

Railsの思想

Railsのルーティングを理解するのに、押さえておくべき思想は一つかなと思います。

「すべての操作はリソースに対するCRUD操作である」

これを念頭においておくと、ルーティングの理解がしやすくなると思います。

編集するファイル

ルーティングは以下のファイルに記述します。基本的にこれだけを見れば全部のルーティングがわかるようになっています。

config/routes.rb

ルーティングの確認方法

Railsアプリケーションのルートディレクトリで以下のコマンドを打てばいつでもルーティングを確認することができます。ルーティングをいじったらこまめに確認すると良いでしょう。

$ rake routes

resources

引数に与えた名前のリソースに対するCRUD操作を行う為のルートを定義します。

  resources :posts

これを rake routes で確認すると以下のようになります。

$ rake routes
    posts GET    /posts(.:format)          posts#index
          POST   /posts(.:format)          posts#create
 new_post GET    /posts/new(.:format)      posts#new
edit_post GET    /posts/:id/edit(.:format) posts#edit
     post GET    /posts/:id(.:format)      posts#show
          PUT    /posts/:id(.:format)      posts#update
          DELETE /posts/:id(.:format)      posts#destroy
各アクションの役割

各アクションの役割は以下のとおりです。

アクション 役割 viewを持つか CRUD おもな遷移先
posts#index postの一覧を表示する o R show,new,destroy
posts#create postを新規作成する x C show
posts#new postを新規作成するための情報を入力する o R(C) create
posts#edit postを更新するための情報を入力する o R(U) update
posts#show postの詳細な情報を表示する o R edit
posts#update postを更新する x U show
posts#destroy postを削除する x D index
アクションを限定することができる

新規作成はできるけど更新はできないとか、削除させないとかを制御することができます。

:onlyを使うことで、指定したアクションだけのルートを定義することができます。

resources :posts, :only => [:new, :create]
$ rake routes
   posts POST /posts(.:format)     posts#create
new_post GET  /posts/new(.:format) posts#new

:exceptを使うことで、指定したアクション以外のルートを定義することができます。

resources :posts, :except => [:edit, :update, :destroy]
$ rake routes
   posts GET  /posts(.:format)     posts#index
         POST /posts(.:format)     posts#create
new_post GET  /posts/new(.:format) posts#new
    post GET  /posts/:id(.:format) posts#show

意図に合わせて適宜使い分けると良いでしょう。

resourcesのネスト

resourcesをネストさせることもできます。

resources :posts do
  resources :comments
end
$ rake routes
    post_comments GET    /posts/:post_id/comments(.:format)          comments#index
                  POST   /posts/:post_id/comments(.:format)          comments#create
 new_post_comment GET    /posts/:post_id/comments/new(.:format)      comments#new
edit_post_comment GET    /posts/:post_id/comments/:id/edit(.:format) comments#edit
     post_comment GET    /posts/:post_id/comments/:id(.:format)      comments#show
                  PUT    /posts/:post_id/comments/:id(.:format)      comments#update
                  DELETE /posts/:post_id/comments/:id(.:format)      comments#destroy
            posts GET    /posts(.:format)                            posts#index
                  POST   /posts(.:format)                            posts#create
         new_post GET    /posts/new(.:format)                        posts#new
        edit_post GET    /posts/:id/edit(.:format)                   posts#edit
             post GET    /posts/:id(.:format)                        posts#show
                  PUT    /posts/:id(.:format)                        posts#update
                  DELETE /posts/:id(.:format)                        posts#destroy

commentsに対するルーティングが定義されました。パスの先頭に /posts/:post_id がつきます。

namespace

管理画面を作りたいときなんかに便利なのがnamespaceです。例を見ましょう。

namespace :admin do
  resources :users
end
$ rake routes
    admin_users GET    /admin/users(.:format)          admin/users#index
                POST   /admin/users(.:format)          admin/users#create
 new_admin_user GET    /admin/users/new(.:format)      admin/users#new
edit_admin_user GET    /admin/users/:id/edit(.:format) admin/users#edit
     admin_user GET    /admin/users/:id(.:format)      admin/users#show
                PUT    /admin/users/:id(.:format)      admin/users#update
                DELETE /admin/users/:id(.:format)      admin/users#destroy

パスの先頭に /admin がつくようになります。注意事項が2点あります。

  1. model, view, controller の各ファイルを(基本的に)adminディレクトリ以下に配置する必要がある
  2. model, controller のクラスを(基本的に)Adminモジュールの内部に宣言する必要がある
MVC ファイル クラス
model app/models/admin/user.rb Admin::User
view app/views/admin/〜 なし
controller app/controllers/admin/users_controller.rb Admin::UsersController

root

ルート(/)へのルーティングを定義します。

root :to => 'posts#index'
$ rake routes
root  / posts#index

namespaceの下に定義することもできます。

root :to => 'posts#index'
namespace :admin do
  root :to => 'users#index'
end
$ rake routes
      root  /                posts#index
admin_root  /admin(.:format) admin/user#index

match

matchを使うと、比較的自由にルーティングを定義することができます。例えばomniauth等のプラグインを使って外部サービス経由でのログインをする場合、以下のように設定することになると思います。

match '/auth/:provider/callback' => 'sessions#create'
$ rake routes
  /auth/:provider/callback(.:format) sessions#create

まとめ

Rails3のルーティングについて僕が知ってることは以上です。間違ってたりしてたら指摘してもらえると嬉しいです。もっと高度な設定を教えてくれる人も大歓迎です。

「すべての操作はリソースに対するCRUD操作である」

とにかく、このことだけ忘れなければ色々すっきりすると思っています。

ApacheのHttpClient 4でPOSTメソッドでも自動的にリダイレクトする方法メモ

HttpClient 4ってPOSTリクエストの時に相手のサーバが302を返してきても自動的にリダイレクトしてくれないんです。
自分でリダイレクトするしかないかなーって思ってたところにいい方法を見つけたのでメモします。

DefaultHttpClient client = new DefaultHttpClient();
client.setRedirectStrategy(new DefaultRedirectStrategy() {
    @Override
    public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
        boolean isRedirected = super.isRedirected(request, response, context);
        if (!isRedirected) {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 301 || statusCode == 302) {
                return true;
            }
        }
        return isRedirected;
    }
});

HttpClientのインスタンスにRedirectStrategyを追加するだけっていう簡単なお仕事です。

PHPのstrtotime関数で +1 month するときの注意

PHPのstrtotime関数で来月、再来月のmonthを取得してたらハマったのでメモ。実際ハマったのは+2 monthの時だけど、月によっては+1 monthでハマれると思う。

今日(2012/07/31)の時点で以下のコードを実行すると '2012-10' と表示されます。本当は '2012-09' であって欲しかった・・・。

<?php
// 今日から2ヶ月後のtimestampが欲しいよー
$timestamp = strtotime('+2 month', date());
$yyyymm = date('Y-m', $timestamp);
var_dump($yyyymm);

解決策は以下。 '2012-07-01+2 month' になるようにすればいいらしい。

<?php
$timestamp = strtotime(date('Y-m-01').'+2 month');
$yyyymm = date('Y-m', $timestamp);
var_dump($yyyymm);

あ、dayまで求めたい時は '2012-10-01' になるのが正解なのか。