Ruby Freaks Lounge

第35回 実用的なダミーサーバ ww(double-web)(1)

この記事を読むのに必要な時間:およそ 3.5 分

実際にwwを使ってみる

それでは,実際にwwを使ってみましょう。今回はサンプルとして,とても簡単なミニブログサービスのクライアントを作ってみます。ミニブログサービスには次の機能があります。

  • ユーザごとのエントリをJSON形式で取得できる。
  • JSON形式で自分のエントリを投稿できる。

このサービスの「ダブル⁠⁠,つまり開発用のサーバをwwで実装します。

その上で,クライアントに求められる機能は次のようなものになりそうです。

  • 指定した複数ユーザのエントリを集約し,時系列に表示する。
  • エントリ本文だけを指定すると,JSON形式でミニブログに投稿する。

とても簡単ですが,これをwwを活用して作っていきましょう。

ユーザごとのエントリをJSON形式で取得する

まずは,wwを使ってミニブログのサーバを実装します。wwはgemでインストールできます。また今回はJSONを扱うので,そのライブラリもインストールしておきましょう。

$ gem install ww json

ユーザごとにエントリの一覧をJSONで返す 機能をwwで実装すると次のようになります。

# miniblog.ru
require 'rubygems'
require 'json'

require 'ww'

app = WW::SpyEye.to_app do
  min_offset = 0

  get("/messages/:user.json") do |user|
    content_type "application/json"
    t = Time.local(2010, 3, 13, 12, 34 + (min_offset += 1), 56)
    body = [
      {:message => "#{user}です。ミニブログ始めました", :posted_on => t.iso8601},
      {:message => "2つめの#{user}つぶやきです", :posted_on => (t + 10*60).iso8601},
    ].to_json
  end
end

run app

WW::SpyEyeは,目視確認用のサーバを作るためのクラスです。WW::SpyEye.to_app()メソッドにブロックを渡し,Sinatraの記法でアクションを定義します。この例ではGETで/messages/#{ユーザ名}.jsonというパスにアクセスされた場合のアクションを定義しています。今回は2件のエントリを返します。文章はほぼ固定ですが,ソート順をわかりやすくするため,投稿時刻("posted_on")にバラツキを加えています。

正しく定義できているかを確認するため,次のコマンドでRack経由で起動してみます。

$ rackup -p 3080 miniblog.ru

起動後にcurlコマンドなどでアクセスすると,目的のJSONを取得できます。

$ curl http://localhost:3080/messages/moro.json
[{"message":"moro\u3067\u3059\u3002\u30df\u30cb\u30d6\u30ed\u30b0\u59cb\u3081\u307e\u3057\u305f",
"posted_on":"2010-03-13T12:35:56+09:00"},
{"message":"2\u3064\u3081\u306emoro\u3064\u3076\u3084\u304d\u3067\u3059",
"posted_on":"2010-03-13T12:45:56+09:00"}]

※見やすくするため改行を入れています。

クライアント側のコードは次のようになります。

# miniblog_client.rb
require 'rubygems'
require 'json'
require 'open-uri'

class MiniblogClient
  def initialize(conn, *friends)
    @connection = conn
    @friends = friends
  end

  def entry_list(newer_first = true)
    entries = @friends.
    map {|f| JSON.parse(@connection.get("/messages/#{f}.json")) }.
flatten.
    sort_by {|e| e["posted_on"] }

    newer_first ? entries.reverse : entries
  end
end

class Connection
  def initialize(host, port)
    @host, @port = host, port
  end
  def get(abs_path)
    open("http://#{@host}:#{@port}#{abs_path}").read
  end
end

if __FILE__ == $0
  conn = Connection.new("localhost", 3080)
  client = MiniblogClient.new(conn, "alice", "bob", "charls")
  puts "新着順"
  client.entry_list.each{|e| puts "#{e["posted_on"]}: #{e["message"]}" }
  puts "古い順"
client.entry_list(false).each{|e| puts "#{e["posted_on"]}: #{e["message"]}" }
end

実行すると,次のように表示され,Connection#getのOpenURIを使ったアクセスで意図通りの 結果が返っていることが分かります。

$ ruby miniblog_client.rb
新着順
2010-03-13T13:05:56+09:00: 2つめのcharlsつぶやきです
2010-03-13T13:04:56+09:00: 2つめのbobつぶやきです
2010-03-13T13:03:56+09:00: 2つめのaliceつぶやきです
2010-03-13T12:55:56+09:00: charlsです。ミニブログ始めました
2010-03-13T12:54:56+09:00: bobです。ミニブログ始めました
2010-03-13T12:53:56+09:00: aliceです。ミニブログ始めました
古い順
2010-03-13T12:56:56+09:00: aliceです。ミニブログ始めました
2010-03-13T12:57:56+09:00: bobです。ミニブログ始めました
2010-03-13T12:58:56+09:00: charlsです。ミニブログ始めました
2010-03-13T13:06:56+09:00: 2つめのaliceつぶやきです
2010-03-13T13:07:56+09:00: 2つめのbobつぶやきです
2010-03-13T13:08:56+09:00: 2つめのcharlsつぶやきです

著者プロフィール

諸橋恭介(もろはしきょうすけ)

(株)永和システムマネジメントサービスプロバイディング事業部所属,Rails勉強会@東京を主催。RubyやRailsを使った受託開発に従事。CucumberやRSpecを使って,楽しくテスト駆動開発を行いたいと思っている。
著書に「Railsレシピブック 183の技」(共著・ソフトバンククリエイティブ)がある。

ブログ:http://d.hatena.ne.jp/moro/
Twitter:http://twitter.com/moro/