orangain flavor

じっくりコトコト煮込んだみかん2。知らないことを知りたい。

Rubyによるデザインパターンを読んだ

恥ずかしながら10年以上プログラミングをやっていて、今までちゃんとデザインパターンを勉強したことがありませんでした。

それぞれのパターンはなんとなく知っているけど、ちゃんと理解できてはいないという状態でした。

デザインパターンを勉強しようとしたことは何度かありました。しかしJavaC++で解説した書籍・Webサイトでは、いちいち本質的ではない基底クラスやインターフェイスを作っているように見えて、理解が進みませんでした。

そこで読んだのが「Rubyによるデザインパターン」です。

Rubyによるデザインパターン

Rubyによるデザインパターン

この本の良いところ

この本が良いと思ったのは以下の点です。

  1. Rubyの動的な性質を活かしている
  2. パターンが厳選されており、説明の順序もわかりやすい
  3. 解決したい問題とそれを解決するコードがある

1. Rubyの動的な性質を活かしている

本書では、Rubyの動的な性質を活かすことで、無駄な基底クラスやインターフェイスなしにパターンを実現しています。

最初は Java に近いクラス設計をした上で、Rubyのパワーでリファクタリングしていき、簡潔なコードでデザインパターンを表現していきます。

ちなみに第2章にRuby自体の解説もあるので、Rubyに詳しくない人でも読めると思います。

2. パターンが厳選されており、説明の順序もわかりやすい

本書では、GoFの23パターンのうちの14パターン+Rubyオリジナルの3パターンを解説しています。

一度に多くのパターンを学んでも理解が追いつかなくなるので、よく使うものに絞られていて良かったです。

説明する順番も、パターン同士の結びつきが考慮されており、理解しやすいです。最初に Template Method というわかりやすいものを取り上げ、次に継承ではなく委譲で実現するために Strategy を使うといった具合です。

ABC順にAbstract Factoryから解説されると辛いです。

3. 解決したい問題とそれを解決するコードがある

それぞれのパターンについてまず問題があり、それを解決するよう設計を改善していくという流れで説明されているのでわかりやすいです。

なぜ他のやり方ではダメで、そのやり方を選んだのかがわかるので、理解が深まります。

Webサイトによっては、UMLやコードがほとんどで、背景が簡単にしか書かれていないことがありますが、そういうサイトは一度ちゃんと理解した人が見返すためのものなんだと気づきました。

さらに知るために

本書で、解説しているのはGoFの23パターンのうちの14パターン+Rubyオリジナルの3パターン(DSLメタプログラミング、Convention over Configuration)です。

GoFのパターンのうち、解説されていないのは以下の9パターンです。

  • Prototype
  • Bridge
  • Facade
  • Flyweight
  • Chain of Responsibility
  • Mediator
  • Memento
  • State
  • Visitor

これらを含めて、今後改めてGoF本を読んで勉強したいと思います。

オブジェクト指向における再利用のためのデザインパターン

オブジェクト指向における再利用のためのデザインパターン

まとめ

パターンによって、あるあるだなーとか、こんなやり方があるんだとか、感想はいろいろでした。ただ、全体を通して名前がついていることの重要性、そして「パターン」という言葉の意味を理解できました。

名前をつけて他人と共有できる「パターン」は、デザインパターンだけではないので今後も勉強していきたいと思います。

ここまで書いておいて非常にアレですが、本書は現時点で入手が困難です。実は、何年も前に良さげだなーと思って買った後、そのまま積んであったのです。 

新品の紙の本は売ってないし、電子書籍にもなっていないのが残念です。良書なので、ぜひ電子書籍で発売してほしいです。

ちなみに原著は電子書籍でも販売されていますが、読んだことないのでなんとも言えません。Amazon.co.jpのレビューには平易で読みやすいと書かれていますが、Amazon.comにはKindle版のフォーマットが非常に読みにくいというレビューもあります。

Design Patterns in Ruby (Addison-Wesley Professional Ruby Series)

Design Patterns in Ruby (Addison-Wesley Professional Ruby Series)

Ruby製アプリじゃなくてもTurnipで自動受け入れテストがしたい

Python製のWebアプリケーションの自動受け入れテストをしたくて、調べてみました。

Ruby界隈だと、最近ではCucumberに代わってTurnipというツールが流行っているみたいなので試してみました。


(c) .foto project

自動受け入れテスト用のライブラリ

Rubyで自動受け入れテストをしようとすると、以下の3つの選択肢があるようです。

Cucumberは以前にも使ったことがあるので、今回はTurnipを選択してみました。詳しい説明は以下のページに譲ります。

Webページを自動操作するライブラリ

Webページの自動操作にはCapybaraを使います。デフォルトではRackTestというドライバが使われますが、これはHTTP通信を行わないドライバでありRackアプリケーションでしか使えません。

このためHTTP通信を行う他のドライバを使います。ヘッドレスでJavaScriptを利用できるドライバとして、以下の2種類があるようです。

最近ではPoltergeistのほうがインストールが簡単かつ高速で人気らしいので、今回はPoltergeistを使いました。

前提

前提として、Ruby 2.1*1 およびBundlerがインストールされているものとします。

PoltergeistのインストールにはPhantomJSが必要なので、 PoltergeistのREADMEに従ってインストールします。

Macならば brew install phantomjs で簡単にインストール出来ました。

準備

適当なフォルダを作ります。

$ mkdir bus-spec
$ cd bus-spec

以下のコマンドでGemfileを作ります。

$ bundle init

Gemfileを以下のように編集します。

source "https://rubygems.org"

gem 'turnip'
gem 'capybara'
gem 'poltergeist'

以下のコマンドでGemをインストールします。

$ bundle install --path=vendor/bundle

TurnipのREADMEを参考に、テストに必要なファイル・フォルダを作成します。

$ mkdir -p spec/{features,steps}

.rspecを以下の内容で作成します。

-r turnip/rspec

spec/spec_helper.rbを以下の内容で作成します。

Dir.glob('spec/steps/*steps.rb') { |f| load f, true }

require 'capybara/rspec'
require 'capybara/poltergeist'

Capybara.default_driver = :poltergeist

# 以下の関数はGeolocation APIをシミュレートするヘルパー関数です。
def simulate_location(latitude, longitude, accuracy=0)
  Capybara.current_session.execute_script <<-EOS
    window.navigator.geolocation = {
      getCurrentPosition: function(success) {
        var position = {coords: {latitude: #{latitude}, longitude: #{longitude}, accuracy: #{accuracy}}};
        success(position);
      }
    };
  EOS
end

rspecを実行して以下のように表示されれば準備完了です。

$ bundle exec rspec
No examples found.


Finished in 0.00005 seconds
0 examples, 0 failures

Turnipによる受け入れテスト

それではTurnipによる自動受け入れテストを書いていきます。

spec/features/search.featureには、プログラムに詳しくない人が読んでもわかる書式(Gherkin形式)で機能を記述します。

# encoding: utf-8
# language: ja

機能:PCまたはスマートフォンでバス停を検索
     シナリオ:トップページにアクセスして検索する
          前提ポケドスにアクセスする
          前提"京都市役所"に居る
          もしトップページを表示する
          ならば画面に"近くのバス停を探す"ボタンが表示されていること
          もし"近くのバス停を探す"ボタンをクリックする
          ならば画面に"この近くのバス停"と表示されていること
          かつ画面に"京都市役所前: 91m"と表示されていること

プレースホルダーに日本語を使う場合は、""で囲うなど少し工夫が必要です。参考:Ruby - Turnip で placeholder のマッチングを柔軟にする - Qiita

spec/steps/bus_steps.rbには、上で書いたGherkin形式のステップ(「前提」「もし」「ならば」など)に対応するプログラムを書きます。

# encoding: utf-8

step 'ポケドスにアクセスする' do
  Capybara.app_host = 'http://bus.capybala.com/'
end

step ':placeに居る' do |place|
  case place
  when '京都市役所'
    @place = {lat: 35.01129870069897, lng: 135.76807713296512}
  else
    raise "Unknown place"
  end
end

step 'トップページを表示する' do
  visit '/'
  simulate_location(@place[:lat], @place[:lng]) if @place
end

step '画面に:labelボタンが表示されていること' do |label|
  expect(page).to have_link(label)
end

step ':labelボタンをクリックする' do |label|
  click_link label
end 

step '画面に:textと表示されていること' do |text|
  expect(page).to have_content(text)
end

再度rspecを実行して以下のように表示されれば成功です。

$ bundle exec rspec
.

Finished in 2.92 seconds
1 example, 0 failures

まとめ

Turnipはこのように、テストする機能を自然文で書けるテストライブラリであり、Capybaraと組み合わせることでWebサービスの自動受け入れテストを行えます。

2012年に書いた記事と比べると、Cucumber → Turnip、capybara-mechanize/capybara-webkit → Poltergeistと変化しました。

正直Rubyをゴリゴリ書いていない身としては、TurnipのCucumberに対する利点として挙げられる、RSpecが使える点とプレースホルダーが簡単に書ける点が、それほど嬉しいことなのかよくわかりませんでした。

また、CucumberやTurnipのGherkin書式は、普通のテストを書くのに比べてコストがかかる割に、どの程度受け入れられるの?という考えもあります。 参考:RSpecで、Cucumberのような結合試験のspecを記述する - 高尾宏治日記 on はてな

うまく使えば強力なツールですが、使いドコロをよく考えて使う必要があるでしょう。

ここで説明したものをもう少し複雑にしたコードをGitHubで公開しています。

参考サイト・書籍

*1:今回は2.1を使いましたが、1.9.3以上ぐらいで動くと思います。

かっこいいダッシュボードが簡単に作れるDashingがすごい

はじめに

まずはデモを見てくれ!デスクトップPCなどの大きい画面で見るのがおすすめです。

Dashingのデモ(中)

Windows 8風のフラットなデザインで、いろいろ動いていてかっこいいですね。こんな感じのダッシュボードを簡単に作れるライブラリがDashingです。

Ruby, HTML, SCSS, CoffeeScriptで少しコードを書くだけで簡単にカスタマイズできるので、使い方を簡単に紹介します。

プロジェクトの開始

Getting Startedに書いてあるとおり、

$ gem install dashing
$ dashing new sweet_dashboard_project
$ cd sweet_dashboard_project
$ bundle
$ dashing start

これで、http://localhost:3030/をブラウザで開けば、デモと同じダッシュボードが表示されます。

ちなみにGem環境が汚れるのが気になる場合は、以下のようにするのがおすすめです。dashing newは、templates/project をカレントディレクトリにコピーしてくるだけです。

$ wget https://github.com/Shopify/dashing/archive/master.tar.gz
$ tar zxvf master.tar.gz
$ mv dashing-master/templates/project sweet_dashboard_project
$ cd sweet_dashboard_project
$ bundle install --path=vendor/bundle
$ bundle exec dashing start

プロジェクトのディレクトリ構成

プロジェクトにはいくつかのファイルとディレクトリがありますが、重要なのは以下の3つのディレクトリです。

  • dashboards:ウィジェットの配置を定義するHTML (eRuby)
  • jobs:Rubyで書かれたサーバーサイドのジョブ
  • widgets:HTML / SCSS / CoffeeScriptで書かれた一つ一つのウィジェット

レイアウト

ウィジェットの配置はHTMLで記述します。<li>で配置やサイズを記述し、<div>ウィジェットの種類やそのパラメータを記述します。

以下は、オレンジ色のテキストを表示するウィジェットと、紫色のSynergyのメーターのウィジェットの例です。

dashboards/sample.erbより抜粋

<li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
  <div data-id="welcome" data-view="Text" data-title="Hello" data-text="This is your shiny new dashboard." data-moreinfo="Protip: You can drag the widgets around!"></div>
</li>

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
  <div data-id="synergy" data-view="Meter" data-title="Synergy" data-min="0" data-max="100"></div>
</li>

ウィジェット

ウィジェットは、HTML / SCSS / CoffeeScriptで記述します。以下は、テキストを表示するウィジェットのHTMLの例です。

widgets/text/text.html

<h1 class="title" data-bind="title"></h1>

<h3 data-bind="text | raw"></h3>

<p class="more-info" data-bind="moreinfo | raw"></p>

<p class="updated-at" data-bind="updatedAtMessage"></p>

データを表示するだけであれば、データ・バインディングを使って簡単にデータを表示できます。

ジョブとHTTP API

サーバーサイドのジョブを以下のようにRubyで書くことができます。SCHEDULERで定期的にジョブを実行し、send_eventでクライアント側のウィジェットに値をプッシュできます。

jobs/twitter.rb

require 'net/http'
require 'json'

search_term = URI::encode('#todayilearned')

SCHEDULER.every '10m', :first_in => 0 do |job|
  http = Net::HTTP.new('search.twitter.com')
  response = http.request(Net::HTTP::Get.new("/search.json?q=#{search_term}"))
  tweets = JSON.parse(response.body)["results"]
  if tweets
    tweets.map! do |tweet|
      { name: tweet['from_user'], body: tweet['text'], avatar: tweet['profile_image_url_https'] }
    end

    send_event('twitter_mentions', comments: tweets)
  end
end

また、ダッシュボードの一番下に表示されているとおり、次のようにしてJSONをPOSTすることで、外部からダッシュボードの値を更新することもできます。

$ curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "text": "Hey, Look what I can do!" }' \http://localhost:3030/widgets/welcome

iPadをダッシュボードにする

適当なディスプレイが手元になかったので、iPad用にダッシュボードを作ってみました。時計と天気とスケジュールと特に意味のないメーターを表示させています。

f:id:mi_kattun:20130412224548j:plain

実際のところ、iPadはずっとつけておくと電池が持たないし、どこかに持って行ってしまうこともあるのであまり使えてないですが。。

orangain/mydashboard · GitHub 

まとめ

Dashingによって、簡単に綺麗なダッシュボードが作れます。オリジナルのウィジェットを作る際も、面倒なところをほとんど気にせずコードを書けるので、幸せな気持ちになれます。

綺麗に表示させるためには、ディスプレイのサイズに合わせてウィジェットのサイズや文字のサイズを調整しないといけない点は、改善の余地があると思いますが。

 

iPad用に作ったダッシュボードの他に、勤務先でもDashingでダッシュボードを使って情報を表示させていますが、やはり綺麗に表示されているといろんな人が興味を持ってくれるのでよいです。

この記事 にあるような、大きなディスプレイが使えるといいのですが、簡単には買ってもらえません。仕方なく20インチ程度のディスプレイにダッシュボードを表示させていますが、近づかないとよく見えないので、チーム全員で共有する用途には難しいなーと思ってます。

大きなディスプレイが欲しいです。

参考: