人間の脳みその限界とツールについて

複数の設定があり、それらの組み合わせによって挙動が変わるアプリを書いていると、だんだんそれぞれの組み合わせについて頭がこんがらがってくる。コードを修正するたびにそれぞれの組み合わせについて、どのように影響が出るか考えなければならなくて、つらくなってくる。つらいだけでなく、確実にミスが生まれる。

こういう状況が続くと、人間の脳みその処理能力について限界を感じてくる。人間はこういうことに向いていないと思うようになる。人力によるチェックにも自信が持てなくなる(そして、それでいいのだと思うようになる)。人間の脳みそに限界を感じるようになると、ツールに頼りたくなってくる。限界を感じることで自分自身に対して謙虚になり、ツールの使い所を実感するんだと思う。

例えば、脳内で多数の組み合わせをシミュレーションするのをやめてプログラムにテストさせるとか、手でひとつひとつ丁寧にメソッド名を置換するのをやめて、Eclipseに任せるとか。何度も同じコマンドを手打ちするのをやめてシェルの補完を使うとか。あと、publicなメソッド・プロパティを必要最小限にすれば、そのクラスを使う人は考えることが減るんだから、public/privateという概念も脳みその負荷を減らす工夫なのかもしれない。

とにかく、脳に強いストレスを感じたら、たぶんその作業は人間には向いていないと思うので、ツールに任せられないか一回検討した方がいいと思う。もっと自分の脳みその能力を疑った方がいいと思う。

Arduinoと感圧センサーで圧力をサーバーに送信する

秋葉原の秋月電子に行って、慣れない雰囲気の中、なんとかこの感圧センサーをゲットしました。

オライリーPrototyping Labという本を見ながら回路を接続して、上のようなプログラムを実行するとこんな感じになりました。

f:id:naoty_k:20130123235951g:plain

センサーに加わった圧力を0番のアナログピンから受け取り、シリアル通信でそのままモニタに出力しています。

この圧力をサーバーに送信するために、以下のものを買いました。

ArduinoにぶっさすEthernetシールド。ここにLANケーブルをさしてインターネットに接続できます。

PLANEX 300Mbps 超小型ハイパワー無線LANマルチファンクションルータ/アクセスポイント/コンバータ MZK-MF300N

PLANEX 300Mbps 超小型ハイパワー無線LANマルチファンクションルータ/アクセスポイント/コンバータ MZK-MF300N

うちがE-mobilepocket wifiを使っててLANケーブルをさすところがなかったので、有線を無線で使えるようにするコンバータも買いました。

PLANEX 無線LANルータ/アクセスポイント/コンバータ「MZK-MF300N」「FFP-PKR01」専用 USB給電ケーブル SSOP-USB02

PLANEX 無線LANルータ/アクセスポイント/コンバータ「MZK-MF300N」「FFP-PKR01」専用 USB給電ケーブル SSOP-USB02

USBから給電するためのケーブル。

これらをつないでHerokuのサーバーにPOSTリクエストを送ると、node.jsがwebsocketを使ってグラフを更新するようにしました。

コードはこんなかんじ。

上2つのコードを組み合わせると、圧力をPOSTでサーバーに送信できます。で、node.jsで書いたサーバーに送ると以下のような感じになりました。

arduinoのコードとサーバーのコードはともにgithubで公開しています。

https://github.com/naoty/makura-arduino

https://github.com/naoty/makura-web

ハッカソンで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::Baseincludeさせるために使いました。これによって、自分のgemをRailsアプリケーションに組み込むことができます。

下のコードでは、Railsプロセスが起動するときにinitializerブロック内の処理が実行されて、自分で作ったParamsInquirer::ActionController::BaseActionController::Baseincludeされるようになります。

# 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>.rbrequireされます。この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

ここからは実際に使ったというよりは、actionpackactivesupportなどの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#autoloadModule#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というテーマを使ってたけど、ビミョーに違和感があったので、自分でテーマを作ってみた。

f:id:naoty_k:20130113000307p:plain

https://github.com/naoty/Nakameguro

1時間くらいであっさりできてしまったので、そのコツをメモしておく。

cssyamlを使う

Limechatのテーマはcssyamlの2つで定義していく。cssはチャットのログが表示される部分(上下)のスタイルを定義するのに使い、yamlはユーザー一覧・サーバー一覧・入力部分のスタイルを定義するのに使う。これらを~/Library/Application Support/Limechat/Themes/以下におけば、LimechatのPreferencesからテーマを設定できるようになる。

サンプルを参考にする

~/Library/Application Support/Limechat/Themes/Sample.cssSample.yamlがある。これを参考に、どのクラスがどの部分にあたるのかだいたい把握できる。例えば、時間のテキストは.timeというクラスがついてるし、.sender[type=myself]というクラスは自分のニックネームにあたる。

sassが便利

今回はcssは直接書かずにsassで書いてみた。色を多用するから、それぞれの色に変数名をつけたかった。

$ gem install sass
$ sass --watch Nakameguro.sass:Nakameguro.css

とすると、Nakameguro.sassを保存するたびにcssに変換されるため、すぐに変更を確認できる。便利。

小言

毎日使うツールは自由にカスタマイズできた方がいい。自分に合うようにカスタマイズすることで快適に開発できる。今はGUILimechatを使ってるけど、より柔軟にカスタマイズしたくなったらWeechatに移行するかも。