ハッカソンでgithub連携のnode.jsアプリ作った話
疲れたので手短に。
土日2日間ぶっ通しのハッカソンでnode.jsを使ったgithub連携アプリを作った。仕事はRailsで、まともなアプリをnode.jsで書いたことなかったし、せっかくだからnode.js使ってみた(っていうか、勝ちに行っても勝つ見込みないから、楽しむことに専念した)。
ソースコードはこちら。
https://github.com/naoty/arounds
Express 3.x, MongoDBでHerokuにデプロイしてます。
github認証
認証ライブラリはいろいろあるようだけど、Passportを使ってみた。github認証したい場合はpassport-githubというものがあるので、それを併用する。使い方は載ってるので割愛。
まだnode.jsでのセッションの取り扱いとかちゃんと理解してないから、passport.serializeUser
らへんがよくわかってない。
mongoose, MongoLAB
MongoDBのORMとしてmongooseを使った。Heroku上のMongoDBにはMongoLABを使った。ブラウザからコレクションの中身とか見れるのでよかった。
$ heroku addons:add mongolab:starter
環境ごとの設定
github APIのclient IDやDBのホストのために環境ごとに設定ファイルを用意した。
// app.js require config = process.env.NODE_ENV == 'production' ? require('./config/production') : require('./config/development');
// configs/production.js module.exports = { github: { clientID: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, callbackURL: process.env.GITHUB_CALLBACK_URL }, mongodb: { path: process.env.MONGODB_URI || process.env.MONGOLAB_URI } };
本番環境ではAPIキーをHerokuの環境変数を経由して参照する。
// configs/development.js module.exports = { github: { clientID: 'GITHUB CLIENT ID', clientSecret: 'GITHUB CLIENT SECRET', callbackURL: 'http://127.0.0.1:3000/auth/github/callback' }, mongodb: { path: 'mongodb://localhost/arounds' } };
ソースコードを公開する場合は、APIキーを隠すためにconfig/development.jsを.gitignoreに追加しておく。
まだよくわかってないこと
とりあえずnode.jsでアプリを作ってみてわかんなかったところをメモ。
MVCな書き方
Expressは放っておくと、ルーティングやルーティングに対するアクション、モデルの定義などいろんなものをapp.jsに書くことができてしまう。viewは分かれてるけど。Rubyで言うと、RailsよりはSinatraが近い。簡単なアプリケーションなら1ファイルにまとめてしまった方がラクかもしれないけど、すぐにMVCが崩壊してしまう。
また、socket.ioを使ったコードを書くとき、view側のjavascriptにも複雑なロジックを書くことになる。
コールバック地獄
上のにも関連するけど、あっという間にコールバック内にコールバックを書いて、その中にコールバックを書くケースが出てくる。deferというものを教えてもらったので、それ使ってみたい。
ミドルウェア
app.use()
みたいなのがたくさんあるけど、あれらが何をやってるのかまだよくわかってない。express new
すると勝手にできてしまうから、あんまり意味を考えなくても動く。express new
に頼らずに書いて覚える。
Railsに組み込むgemを作るためのTips
params_inquirerというgemを作りました。何ができるかと言うと、文で説明するのがなかなか難しいので、下のコードを見てください。
# users_controller.rb def index if params[:status].accepted? # params[:status] == 'accepted' と同じ @users = User.accepted elsif params[:status].rejected? # params[:status] == 'rejected' と同じ @users = User.rejected else @users = User.all end end
params_inquirerを使うと上のaccepted?
のようなメソッドがparams
に対して呼べるようになります。すでにrubygemsで公開してるので、ちょっと試してみたい場合は、irbで試してもらうこともできます。
$ gem install params_inquirer $ irb irb > require 'params_inquirer' irb > params = ParamsInquirer::Parameters.new({ name: 'naoty' }) irb > params[:name].naoty? => true
params
の中身を文字列で比較するのがなんとなくダサいと感じていたので、作ってみました。あとは、Railsの中身について勉強してみたかったというのもあります。
Railsに組み込みgemを作るにあたって知っておいた方がいいポイントについてまとめてみます。
Bundlerでgemのひな形を作る
gemを作るとき、まず最初にBundlerを使ってgemのひな形を作ります。
$ gem install bundler $ bundle gem params_inquirer
これでgemのひな形ができます。作ったgemをローカル環境にインストールしたりrubygems.orgにリリースするためのRaketaskもここに含まれるので、かなり便利です。
Bundlerを使ったgemの開発についてはこの記事を参考にしました。
Railtie
Railtieは、Railsを起動するときにgemのコードをActionController::Base
にinclude
させるために使いました。これによって、自分のgemをRailsアプリケーションに組み込むことができます。
下のコードでは、Railsプロセスが起動するときにinitializer
ブロック内の処理が実行されて、自分で作ったParamsInquirer::ActionController::Base
がActionController::Base
にinclude
されるようになります。
# lib/params_inquirer/railtie.rb require 'params_inquirer/action_controller/base' module ParamsInquier class Railtie < ::Rails::Railtie initializer 'Initialize params_inquirer' do ::ActionController::Base.send :include, ParamsInquirer::ActionController::Base end end end
ただ、このファイルがRails起動時にrequire
されている必要があります。
インストールされたgemをrequire
するときlib/<gem_name>.rb
がrequire
されます。このgemであればlib/params_inquirer.rb
です。なので、ここでrailtieをrequire
しておく必要があります。
# lib/params_inquirer.rb if defined?(Rails) require 'lib/params_inquirer/railtie' else require 'lib/params_inquirer/parameters' end
require 'params_inquirer'
が実行されるとこのファイルが実行されます。もしRailsアプリケーション内であればrailtieをrequire
し、最初に見せたirbのような場合は必要なファイルだけrequire
するようにしています。
以上のようすることで、Rails起動時にrailtieをrequire
しrailtieから自分で作ったコードをRailsアプリケーション内にinclude
させることができました。
ActiveSupport::Concern
ここからは実際に使ったというよりは、actionpackやactivesupportなどのgemを読んでいくときに必要になったtipsです。
include
したモジュールを使ってクラスメソッドをmixinしたい場合、下のようにModule#.included
をオーバーライドしその中で内部モジュールをextend
するテクニックが定石みたいです。
module M def self.included(base) # extendによってクラスメソッドとしてmixinされる base.extend ClassMethods scope :disabled, where(disabled: true) end # クラスメソッドを定義する内部モジュール module ClassMethods ... end end
上のようなコードはActiveSupport::Concern
を使うと下のように書けます。
module M extend ActiveSupport::Concern included do scope :disabled, where(disabled: true) end module ClassMethods ... end end
一見すると、ClassMethods
モジュールがextend
されていないように見えますが、内部的にClassMethods
という名前のモジュールがextend
されます。「設定より規約」に従ってるんだと思います。
これを知らないと、クラスメソッドがextend
されていることに気づきにくいかもしれないです。また、ActiveSupport::Concern
はいろんなところに頻出するので、知っておいた方がいいと思いました。
ActiveSupport::Autoload
ActiveSupport::Autoload#autoload
はModule#autoload
の拡張で、Module#autoload
は必要なファイルを必要なタイミングでrequire
するメソッドです。
autoload(:Hoge, 'hoge') # 'hoge.rb'はこの時点ではrequireされていない p Hoge # ここで'hoge.rb'がrequireされる
ActiveSupport::Autoload#autoload
は、「Hoge
はhoge.rbにあるはず」という「設定より規約」に従って、Module#autoload
の第2引数を省略できるメソッドなので、上のコードは下のように書けます。
extend ActiveSupport::Autoload autoload :Hoge p Hoge # ここで'hoge.rb'がrequireされる
これもファイル名が省略されているということを知らないと、どのファイルをrequire
しているか見えづらいと思います。
最後に
あまりまとまらなくてすごい量になってしまいました。簡単なgemを作るのに知っておくべきことがいろいろあって大変でした。間違っていることがあれば修正しますので、コメントいただけると助かります。また、params_inquirerもまだ未完成なので、pull requestも待ってます。
最近のテスト事情
むかしに比べると、かなりテストが書けるようになってきたし、TDDもだんだん慣れてきた。最近テスト書いてて便利だと思ったことについてメモっておく。
スタブを使ってbefore_filterをスキップする
describe 'GET index' do context 'ログインしている場合' do before(:each) do controller.any_instances.stub(:authenticate_user).and_return(true) end it 'hogehogeなfugafugaを取得する' do get index, params assigns[:fugafuga].should be_hogehoge end end end
ログイン判定のような、リクエストをはじく処理をbefore_filter
で実装することがよくあるけど、そういうコントローラーをテストする場合、スタブが便利だということにようやく気づいた。スタブによって、メソッドの中身をごまかして好きな値を返すようにできる。だから、before_filter
をスキップしたい場合は、とにかくスタブしてtrue
を返すようにしとけばいい。skip_before_filter
でもスキップすることはできるけど、僕はスタブを使う方が好み。
FactoryGirlを使いこなす
FactoryGirl.define do factory :user do # 連番を使えばuniquenessのバリデーションにかからなくなる sequence(:name) {|n| "user #{n}" } sequence(:email) {|n| "user#{n}@example.com" } age { rand(18..30) } after(:build) do |user| # 余計なデータを作るコールバックがあればスキップできる User.skip_callback(:after, :create, :create_data) end # ネストしたfactoryで上書きできる factory :naoty do name 'naoty' email 'naoty@example.com' age 18 end # traitで属性のグループに名前をつけられる trait :resigned do resigned_at { Time.now } end end end user = FactoryGirl.create(:user) p user.name #=> "user 1" naoty = FactoryGirl.create(:naoty) p naoty.name #=> "naoty" resigned_user = FactoryGirl.create(:user, :resigned) p resigned_user.name #=> "user 2" p resigned_user.resigned_at #=> "2013-01-19 00:43:59 +0900"
factory_girl
はテスト用のデータを簡単につくるためのgem。似たようなgemは他にもあるけど、こういうgemを使うと、テストデータを作るロジックとテストコードを分離できる。なので、いろんなテストで使われるテストデータを重複なく簡単に作ることができる。
FactoryGirlでテストデータを作成するときに、よくひっかかるのがバリデーションやafter_save
などのコールバック内の余計な処理だと思う。こういう鬱陶しい処理は、FactoryGirlのコールバックを使ってスキップしてる。
trait
は特殊なデータを作る場合にすごく役に立つ。上記の例のような「退会ユーザー」をテストに使いたいときなど、特殊なデータの属性をひとまとめにしてFactoryGirl.create(:user, :resigned)
のように簡単に作成できる。
changeマッチャが便利
describe '#resign' do let(:user) { FactoryGirl.create(:user) } it 'resigned_atを更新する' do lambda { user.resign }.should change(user, :resigned?).from(false).to(true) end end
モデルの更新系のメソッドをテストするとき、change
マッチャが非常に便利。上の例で言うと、user.resigned?
の結果がlambda内の処理を実行した前後でfalse
からtrue
に変わることをテストしている。
東京Ruby会議10にいってきた
1/13, 1/14に千葉で行われた"東京"Ruby会議10にいってきた。
受付でまず渡されたのが、砂浜でMacを開く謎のバッヂ×4(後にささたつバッヂであると知らされる)。幸運にも、受付から3時間程度でバッヂをコンプし、レアバッヂをゲットできた。最初はよくわからなかったけど、楽しかった。スタッフの皆さんが楽しんでるのが伝わってきてよかった。
内容はというと、個人的にはmrubyの話がよかった。ルーターのこととかはよくわからなかったけど、組み込み機器の世界はまだまだレガシーらしく、mrubyにかかる期待は大きいみたい。"Internet of Things"的なことに興味があるので、CとかC++とかやる必要あるのかなと思ってたけど、mrubyでできるならmruby身につけたい。mrubyでつくられたスマートなルーターは実用性がありそうなデバイスでよかった。僕もmruby覚えて、一発ネタじゃない実用的なデバイスつくってみたい。mruby興味あるけど、どこから始めていいかよくわかってないので、なんとかしたい。
あとは、これまでTwitterでのみ知ってた方と初顔合わせできたのがよかった。#p4dをはじめ、コミュニティの楽しさも再確認できた。ひきつづき何かしらのコミュニティに参加したい。
2日目は大雪で途中で中断してしまったけど、スタッフの皆さんの英断のおかげで何事もなく帰宅できました。有難うございました。
Limechatのテーマつくった
昨年末から社内にIRCが導入されてから、クライアントツールにLimechatを使ってる。とりあえずデフォルトのLimelight
というテーマを使ってたけど、ビミョーに違和感があったので、自分でテーマを作ってみた。
https://github.com/naoty/Nakameguro
1時間くらいであっさりできてしまったので、そのコツをメモしておく。
cssとyamlを使う
Limechatのテーマはcssとyamlの2つで定義していく。cssはチャットのログが表示される部分(上下)のスタイルを定義するのに使い、yamlはユーザー一覧・サーバー一覧・入力部分のスタイルを定義するのに使う。これらを~/Library/Application Support/Limechat/Themes/
以下におけば、LimechatのPreferencesからテーマを設定できるようになる。
サンプルを参考にする
~/Library/Application Support/Limechat/Themes/
にSample.css
とSample.yaml
がある。これを参考に、どのクラスがどの部分にあたるのかだいたい把握できる。例えば、時間のテキストは.time
というクラスがついてるし、.sender[type=myself]
というクラスは自分のニックネームにあたる。
sassが便利
今回はcssは直接書かずにsassで書いてみた。色を多用するから、それぞれの色に変数名をつけたかった。
$ gem install sass $ sass --watch Nakameguro.sass:Nakameguro.css
とすると、Nakameguro.sass
を保存するたびにcssに変換されるため、すぐに変更を確認できる。便利。
小言
毎日使うツールは自由にカスタマイズできた方がいい。自分に合うようにカスタマイズすることで快適に開発できる。今はGUIなLimechatを使ってるけど、より柔軟にカスタマイズしたくなったらWeechatに移行するかも。
IntelliJ IDEAをvimっぽくする
Androidアプリ開発しててEclipseが重くてつらかったので、IntelliJ IDEAなるものを試してる。見た目がかっこいいからこっち使ってみようと思い、vimっぽく使えるか試してみた。
バージョン
- IntelliJ IDEA 12.0
テーマを黒にする
- こっちの方がイケてる。
Preferences > Appearance > Theme
をDarculaに変更する。
IdeaVimを追加
- キーマップをvim化する。
Preferences > Plugins > Browse repositories > IdeaVim
でダブルクリックするとインストールできる。
カーソルがどこまでも右にいける設定をオフにする
Preferences > Editor > Allow placement of caret after end of line
をオフにする。
行番号を表示する
Preferences > Editor > Appearance > Show line numbers
をオンにする。
キーマッピングをカスタマイズする
Preferences > Keymap
からいろいろ変更できる。Second Stroke
を指定することでPrefixみたいなキーマッピングも設定できる。- 変更したのは以下のとおり。これでだいたいvimと同じ動きになる。
Run
:実行Select Next Tab
,Select Previous Tab
:タブを前後に移動Recent Files
:最近開いたファイルのファイラーを起動(unite.vimっぽく使える)Split Horizontally
,Split Vertically
:エディタを水平分割、垂直分割Goto Next Splitter
,Goto Previous Splitter
:分割したエディタを前後に移動Close
:エディタを閉じる
Eclipseだとタブの移動とか画面分割をショートカットからできなかった気がするので、これだけでもインテリJ氏に替える価値があると思う。作業効率がだいぶ上がる。
2012年の振り返り・2013年の目標
KDDIの障害が復旧するのを見守るハメになったので、それまで2012年の振り返りをしようと思う。
2012年の技術面での目標は「iPhoneアプリをリリースすること」だったような気がする。これは無事に達成できた。おまけにAndroidアプリもリリースできた。あと、いくつかRailsでサービスをつくった。特にcui-about.meはなかなか好評で、海外の方にも使っていただいた。使ってもらえるサービスを作ったということも大きい成果だったと思う。覚え始めてもう2年くらいになるRailsでは、特にRSpecを書けるようになったことが一番の進歩だったと思う。まあ大半がAndroidかiOSを書いていたので、そこまで大きい進歩はなかったんだけど。最近はnode.jsをちょっとやり始めて、だんだんとわかってきたところ。Arduinoも買ってみて、ハードウェアに強い興味がでてきた。
総括すると、未知の領域にどんどんチャレンジできた年だった。
2013年の目標は「ハードウェアを開発すること」にしようと思ってる。ハードウェアといってもいろいろあるけど、とりあえず製品として売れるものを開発したい。いま一番興味があるのはハードウェアで、特にモノとモノとがインターネットにつながった未来に興味がある。Webサービスやアプリの開発自体もすごく楽しいんだけど、これまでの路線から想像がついてしまう未来ではなく、一歩先の未来を実現できるエンジニアになりたいと思う。そのために、これまでとはまったく異なる領域にチャレンジしたい気持ちが強い。2013年は2012年よりもっと大きくステップアップしたい。
HTMLも分からないまったくのド素人だった2年半前からここまで来れたという事実は今ではすごく自信になっていて、新しくチャレンジすることにポジティブになれる。今年だって、iPhoneアプリだけじゃなくAndroidアプリも作ることができて、さらに自信につながった。だから、来年もまた大きなチャレンジをしたいと思う。毎年そうやってチャレンジを繰り返して成功体験を重ねていけたら幸せだなぁと、年の瀬にしみじみ思いました。
今年お世話になった皆様、ありがとうございました。来年も宜しくお願い致します。よいお年をお迎えください。