ActionWebServiceでxmlrpcサービスを作る
靴下が変な人に「くれりんこ」と云われたような気もするので作り方を解説します。
RailsではActionWebServiceを使うとxmlrpcやsoapのサービスを簡単に作ることができます。今回はTraipyという以前実装したTropyクローンに簡単にxmlrpcで投稿(newTropy)、エントリーの取得(getTropy)をできるAPIを実装してみましょう。
まずAPIのライブラリを置く場所ですが、Rails的にはRAILS_ROOT/app/apis/*_api.rbとして置くのが一般的です。今回はxmlrpc_api.rbという名前を付けて配置します。
module XmlrpcStruct
class Tropy < ActionWebService::Struct
member :key, :string
member :source, :string
end
end
class XmlrpcApi < ActionWebService::API::Base
inflect_names false
api_method :getTropy,
:returns => [XmlrpcStruct::Tropy],
:expects => [{:key => :string}]
api_method :newTropy,
:returns => [:string],
:expects => [{:source => :string}]
end
class XmlrpcService < ActionWebService::Base
web_service_api XmlrpcApi
def getTropy(key = nil)
if key
tropy = Tropy.find_by_key key
else
tropy = Tropy.find_by_random
end
XmlrpcStruct::Tropy.new :key => tropy.key, :source => tropy.source
end
def newTropy(source)
tropy = Tropy.new(:source => source)
tropy.save
tropy.key
end
end
moduleのXmlrpcStructではTropyというActionWebservice::Structを継承したクラスを作ってます。べつにこれは必須というわけではなく、getTropyメソッドが叩かれた時の返す構造を簡単に定義するために利用します。
class XmlrpcApiではActionWebService::API::Baseクラスを継承し、APIインターフェイスを定義します。ここではメソッド名と返り値、入力値の型を定義します。たとえばgetTropyメソッドでは、返り値は先ほど定義したXmlrpcStruct::Tropyの構造体、一番目の引数の型はstring(定義しなくても動くが、それにあわせキャストされるため定義すべき)を定義してます。inflect_namesをfalseにしてるのは、railsお節介機能でメソッド名の自動キャメライズを防ぐためです。
そして最後のXmlrpcServiceでは、ActionWebService::Baseクラスを継承し、各メソッドの実装部を定義します。web_service_apiではどのクラスのAPIを利用するか定義してます。getTropyではkeyがあればそのkeyのデータをDBから検索し、なければランダムで1件取得してます。そして返り値でXmlrpcService::Tropyの構造体を作って返しています。netTropyではsource(本文)をDBに新規データ登録して、keyの値を返しています。
次にコントローラの実装です。今回はxmlrpc_controllerとずばりそのまんまなコントローラを作ります。
class XmlrpcController < ApplicationController
web_service_dispatching_mode :delegated
web_service :api, XmlrpcService.new
end
で終わりです。web_service_dispatching_mode :delegatedと指定することで、コントローラのアクション実行をActionWebServiceに委譲します。そして、web_serviceにapiという名前でXmlrpcServiceを登録します。これでhttp://host/xmlrpc/api を叩くことによってxmlrpcが利用できるようになります。
以上で、実装における説明は終わりです。長ったらしいですけど、慣れれば簡単に実装可能でしょう。今回のxmlrpcを実装したTraipyのソースが欲しい方は http://svn.rails2u.com/public/traipy/trunk/ からどうぞ。また実際にhttp://traipy.rails2u.com/xmlrpc/api を叩くことができます。
それでは実際にxmlrpcとして使えるかどうかテストしてみましょう。
#!/usr/bin/env ruby
require 'xmlrpc/client'
server = XMLRPC::Client.new('example.com', '/xmlrpc/api')
# ランダムに取得
p server.call('getTropy')
# 新規に作成してkeyを取得
key = server.call('newTropy', "title hoge\nsource foobar")
p key
# 先ほどのkeyを引数にしてgetTropyを実行
p server.call('getTropy', key)
# 結果
{"source"=>"rails!rails!", "key"=>"a9e9daba40e1c3f8"}
"85baf8804159ac01"
{"source"=>"title hoge\nsource foobar", "key"=>"85baf8804159ac01"}
うまく使えているようですね。このようにActionWebServiceを使うと、xmlrpcやsoap(試してないけど)をすんなり使えるようになります。また、BloggerAPIやMetaWeblogApiはTypoの実装を見るのが一番理解しやすいと思います。たとえば、metaWeblogApiのgetPostの実装は
def getPost(postid, username, password)
article = Article.find(postid)
article_dto_from(article)
end
のように行っており、username引数で渡してるけど認証どうやってるの?という疑問はbefore_invocation :authenticateで行ってたり、非常に参考になります。
ActionWebServiceはrailsライブラリの中でもバツグンにドキュメントが少ないので、他の実装例を見るのが手っ取り早いですね、、、。