有頂天Ruby

ビールを飲みながらRubyについて書きます。

Understanding Computation (1) Part1 プログラムと機械

「Computation」とは何か?

ということを定義してる。

  • プログラムを読み
  • プログラムを実行し
  • 入力を取り
  • なにかしら出力する

Computationとは「コンピュータが何をするか」ということ。

Computationの3つの基本的な材料が

  • コンピュータが何を計算出来るか
  • 機械が理解できることばを書くための言語
  • ↑の言葉で書かれたプログラム、機械に何をさせるか書き下したもの

Part1の章について

  • 2章でプログラミング言語を作る
    • 同じ言語をいくつかの手法で作るっぽい
  • 3章で基礎となるマシンを作って
  • 4章でなんか洗練されたやつが出来る
  • 5章では汎用的な計算機作る

雑感

Part2ではなんかもっとヤバいことする方法を学び最終的に(作り方を知ってる範囲で)最強マシンを作るよう。楽しみだ。

Understanding Computation (0)

Understanding Computationを購入しました。

O'Reilly Coupon Codesに掲載されている「DSUG2」コードを入れて、O'Reilly版を半額で購入いたしました。Kindle版よりも安いです。

正式な価格だとKindle版の方がO'Reilly版よりも安いのですが、Kindle(DRMのかかった不自由な電子書籍)版は、AmazonのKindle以外では読むことができません。

O'Reilly版はPDF/ePub/Mobiなど、だいたいどこでも読めるフォーマットが用意されていて、DRMも掛かっていないので、お勧めです。

適当に読んでブログ書いていこうと思います。

オブジェクト指向Rails - よりよいコントローラーを書く

Ruby 5で紹介されていたObject Oriented Rails - Writing better controllers - Pivotal Labsの訳です。(翻訳の質はうんこかもしれません、元記事読みましょう)

私は最近、Objective-Cを使ってiOS、Javaを使ってAndroid、たくさんのモバイル開発をしている。

私はRuby開発者でもあるので、それらの言語やフレームワークから学んだことを可能な限りRubyでも試し、適用してみようとするのは当然のことだろう。

今日はRailsアプリでよりよいコントローラを書くため、僕のベストを尽くそうと思う。

私はAndroidやiOS向けのアプリを書くとき、スタブやモックを使わないよう、自分自身に強制している。それは、オブジェクトの設計を向上させ、DI(依存性の注入)を使ってテストを書くことを意図している。

では、コードの例を見てみよう。

class User
  validates_uniqueness_of :username
end

class RegistrationController < ApplicationController
  def create
    user = User.where(username: params[:username]).first
    user ||= User.new.tap do |new_user|
      new_user.username = params[:username]
      new_user.save!
    end
    render json: user
  end
end

ここで我々が必要としているのは、ユーザーを登録するためのユーザー名だ。 あと、もしもユーザーが既に登録していた際は、単にユーザーの情報を返すだけだ。

すっごくシンプルだね。

だけど、そのロジックはControllerに属していない。 それだと、うんこみたいなスタブやモックを使わずに、データベースに接続せずにコントローラーをテストすることは不可能だ。 any_instanceをテストで使うのは、「あなたは間違ったことをしている」ことを示す良い目印だ。

まず最初のカイゼンは次のようになるだろう

class User
  validates_uniqueness_of :username

  def self.register(username)
    user = User.where(username: username).first
    user ||= User.new.tap do |new_user|
      new_user.username = username
      new_user.save!
    end
  end
end

class RegistrationController < ApplicationController
  def create
    user = User.register(params[:username])
    render json: user
  end
end

これは既に元よりもはるかに優れたコードだ、君はコントローラーをテストするためにUser.registerをスタブすればいい。それでうまく行く。

もしも君がそこで足をとめ、君のアプリが大きく成長しすぎなければ、それでうまいこと出来るだろう。

だけど、もし君のアプリが成長しはじめたら、Userモデルはもっと大きく大きく育っていって、君はより多くの責務を与えていくだろう、

君はUserモデルにもっと責務を与えていくだろう。そして、君が知っていることを超えたあたりで「僕はただしいのかな?」と自分自身に問いかけるんだ。

コードをよりよく切り分ける道、実際にスタブを取り除く最終的なコードがこれだ:

class User
  validate_uniqueness_of :username
end

class RegistrationService
  def register(username)
    user = User.where(username: username).first
    user ||= User.new.tap do |new_user|
      new_user.username = username
      new_user.save!
    end
  end
end

class RegistrationController < ApplicationController
  before_filter :load_registration_service

  def create
    user = @registration_service.register(params[:username])
    render json: user
  end

  def load_registration_service(service = RegistrationService.new)
    @registration_service ||= service
  end
end

RegistrationServiceは、データベースに関連する登録に関してのみ、責務をおう。 このサービス自体はデータベースにアクセスを行わずに、HTTPサービスを使うような別のものに取り替えることが可能なことに注意して欲しい。

その上、あなたは今テストから完全にスタブを取り除くことが出来るようになった。 次のような感じだ

describe RegistrationController do
  before do
    @fake_registration_service = controller.
        load_registration_service(FakeRegistrationService.new)
  end

  describe "POST #create" do
    it "delegates to the registration service and renders the returned user" do
      expected_user = User.new.tap {|u| u.username = "damien"}
      @fake_registration_service.registered_user = expected_user

      post :create, username: "damien"

      expect(response.status).to eq(200)
      expect(response.body).to eq(expected_user.to_json)
      expect(@fake_registration_service.username_registered).to eq("damien")
    end
  end
end

class FakeRegistrationService
  attr_accessor :registered_user
  attr_reader :username_registered

  def register(username)
    @username_registered = username
    registered_user
  end
end

哀しいことに、我々はRailsがどうやってControllerをインスタンス化するのかコントロールしていない。 だから我々はbefore_filterの中でこんな感じのトリックを使って、サービスのデフォルト値を使うようにしなくちゃならない。 PORO(Plain Old Ruby Object、素のRuby)でこれをやるならコンストラクターを作って似たようなことをやればい。

AndroidやiOSのアプリでテストを走らせるとき、依存性の注入を使うのは非常に一般的だ。 私は個人的にはこれらのプラットフォーム上でモックを使わないことを好む。

そして、私はRuby on Railsのアプリケーションでもこのテクニックをもっと試してみたい。 それはあなたによりよいオブジェクト設計について考えること、より特定の目的を持ったオブジェクトを持つこと、値オブジェクト上にロジックを持つことを制限(モデルとプレゼンターのように)することを強制するだろう。

あなたがこの記事について考えていることを、ぜひ教えて下さい! :)

Ruby5 - Episode #383 - July 2, 2013

Ruby関連の情報はもっぱらRuby Weekly(週一で届く無料のRubyニュースメルマガ)に頼ってます。

Ruby5も聞いてみようと思いましたが、日本語じゃない(`;ω;´)

日本語記事を探してみたら

Ruby5をひたすら翻訳する日記

すごく良いブログなんだけど更新止まってるみたい。 せっかくなので僕もやってみたいな〜。と思って聞いてみました。

  1. 一旦英語で聴きとって書きだす
  2. 意訳

の順番で行こうと思ったがそんなに甘くなかった。普通に知らない単語出てくるし、聞き取れない。 なので

  1. 聞き取れた範囲で意訳

のみで頑張ろうと思います。TopRubyJobsは翻訳しない方向で。

Ruby5 - Episode #383 - July 2, 2013

ワンライナーウェブサーバー、コントローラーへの提案、きちっとしたモック、モッキングライブラリを作る、Rails4への用意、オープンソースプロジェクトの地図

ワンライナーウェブサーバー

nobu (Nobuyoshi Nakada)さん(Rubyコントリビュータ)による、Rubyだけを使って、ワーキングディレクトリでウェブサーバーを立ち上げる美しくてシンプルな方法。

ruby -run -e httpd . -p 5000

を呼ぶだけ。Rubyの標準ライブラリのWEBRickを使ったサーバーがhttp://localhost:5000/で立ち上がる。(index.htmlがあればそれが表示される) 静的なファイル配信のに特化してるすごい技w

よいコントローラを書く

週末にPivotal Labsのダミアンレベゴッド(※訳注: 発音難しいのでカタカナでちゃんと書けてない可能性高いです)が書いた興味深いブログについて。

コントローラをテストするときモックとスタブを使わず、オブジェクト指向な設計を育てよう。

コントローラをテストするよりもインテグレーションテストをする方にみんな興味持ってそうなのに、Controllerのテストの話。 any_instanceを使ってオブジェクトのメソッドをスタブするのは、コードの臭いぷんぷん。

まずクラスメソッドに切り出して、それをサービスクラスに切り出す。 (※このあとの部分聞き取りづらかった。ブログ記事のコメント欄の方も盛り上がっていて面白い)

verified_double

rspec-fireと似てるモック用のライブラリらしい。 違うのは、そのモックがスタブしたメソッドが正しい値を返すかテストで保証出来ること。 スタブが返す値をテストしていない場合warningがでるらしい。

モッキングライブラリを作る

7/23にあったAncient City RubyでのAndy Lindemanさんのモックライブラリを作るついての話。YouTubeで配信中。(今まで見た中で一番のライブコーディングらしい) モックが出来ること、レッド・グリーン・リファクタのサイクルを高速に回して、どれほどパワフルなことが出来るか理解出来るよう。(※ぼくもあとでみる)

Rails4 Ready?

Francesco RodríguezさんとFlorent Guilleuxが作ったready4rails4.netの話。 gemがRails 4に対応してるかしてないかが調べられる。

オープンソース地図

世界オープンソースコントリビュータ地図。 David FischerがGitHub's Date Challengeに投稿した超ヤバいツール。 GitHubで最も活発な200のオープンソースリポジトリのコントリビューターが、世界のどこにいるのか地図にプロットした。

サイト

Rails 4.0.0を入れた後でRails 3.2.13を入れ直したいとき

当方rbenvを使っています。 gem install railsしてうっかり4.0.0を入れてしまったあと、3.2.13を入れなおしたのですが、rails -vrails --versionしても4.0.0のままで困っていました。

状況

gem install rails             # 4.0.0が入る
gem uninstall rails           # 3.2.13が使いたいので、4.0.0を消す
gem install rails -v '3.2.13' # 3.2.13を指定して入れる
rails -v  # 3.2.13だと思った?? ざーんねんっww4.0.0でしたっwww

解決策

railtiesを消そう

gem uninstall railties -v '4.0.0'

なんで?

railsコマンドはrailsgemじゃなくてrailtiesgemのコマンドだからです。 https://github.com/rails/rails/blob/master/railties/bin/rails