もとになるジェネレータの格納場所
カスタマイズを加えるもとになるジェネレータのソースファイルは、Rubyのインストールディレクトリの配下である「lib\ruby\gems\1.8\gems\rails-1.2.5\lib\rails_generator\generators\components\web_service」といったパスに存在しています。このパスに存在しているファイルに対してカスタマイズを加えます。
これから作成するジェネレータの格納場所
カスタマイズを加えたジェネレータの格納場所
さて、これから作成するジェネレータの格納場所ですが、Railsは次に示すディレクトリからユーザー定義のジェネレータを探しますので、本稿では「lib\generators」配下にジェネレータを配置します。「generators」フォルダが無い場合は、作成します。
- RAILS_ROOT\lib\generators
- RAILS_ROOT\vendor\generators
- RAILS_ROOT\vendor\plugins\任意のサブディレクトリ\generators
格納場所の作成
前述の「もとになるジェネレータの格納場所」にあるディレクトリやファイルをコピーして、次のようにファイル名を変更することで、「これから作成するジェネレータの格納場所」を作ります。
- 「RAILS_ROOT\lib\generators」に「mst_web_service」というディレクトリを作成します。その配下に「mst_web_service_generator.rb」というファイルを作成します。
- また、「mst_web_service」ディレクトリの配下に「templates」ディレクトリを作成します。「templates」ディレクトリの配下にはジェネレートするファイルのテンプレートを保存します。
ジェネレータのカスタマイズコード その1
web_service_generator.rbのカスタマイズ
まず、「web_service_generator.rb」をカスタマイズします。各要素の内容は次のとおりです。
NamedBaseは、Railsにおけるジェネレータの拡張元となるクラスです。class_colissionsメソッドにより、これから生成するクラスが、RubyやRailsのクラスとして既に使用されていないことを確認します。directoryメソッドにより、これから生成するクラスを格納するディレクトリを作成します。templateメソッドにより、テンプレートをもとにクラスを生成します。templateメソッドでは、第1引数にテンプレートとなるファイルを指定し、第2引数に出力するファイルパスを指定します。
class MstWebServiceGenerator < Rails::Generator::NamedBase def manifest record do |m| # Check for class naming collisions. m.class_collisions class_path, "#{class_name}ServiceApi","#{class_name}ServiceController","#{class_name}ApiTest" # API and test directories. m.directory File.join('app/apis', class_path) m.directory File.join('app/controllers', class_path) m.directory File.join('test/functional', class_path) # API definition, controller, and functional test. m.template 'api_definition.rb', File.join('app/apis', class_path, "#{file_name}_service_api.rb") m.template 'controller.rb', File.join('app/controllers', class_path, "#{file_name}_service_controller.rb") m.template 'functional_test.rb', File.join('test/functional', class_path, "#{file_name}_service_api_test.rb") end end end
次に、テンプレートをカスタマイズしていきます。
「api_definition.rb」テンプレートのカスタマイズ
「api_definition.rb」テンプレートは、WebサービスのAPIクラスを作成するテンプレートです。データベース上のテーブルを更新するサービスメソッドを外部に公開するためには、データベース上のテーブルに対応する構造体をAPIクラスで定義する必要があります。先頭のmodule XmlrpcStructで、その構造体を定義しています。
Webサービスでメソッドを公開するためには、api_methodでそのメソッドを指定する必要があります。api_methodではexpectsで引数を、returnsで返り値に関する定義を行います。
Webサービスを通じてデータベース上のテーブルを更新するためには、そのテーブルのデータ型を公開する必要があります。RailsではActiveRecordを継承したモデルクラスが、テーブルのデータ型に当たるので、これを公開すれば良いように思えます。しかし、ActiveRecordは返り値に指定できても、引数には指定することができません。
そこでfind_allでは、returnsにActiveRecordを継承したクラスを指定します。find_all以外のメソッドでは、expectsにActiveRecordを継承したクラスを指定することはできないので、module XmlrpcStructで定義したクラスを指定し、その代わりとしています。
module XmlrpcStruct class <%= class_name %> < ActionWebService::Struct <% @model_klass = Module.const_get(class_name) %> <% for column in @model_klass.columns %> <% if column.name == 'created_at' or column.name == 'updated_at' next end %> <% if column.name == 'id' %> member :id, :integer <% else %> member :<%= column.name %>, :string <% end %> <% end %> end end class <%= class_name %>ServiceApi < ActionWebService::API::Base <% for method_name in args -%> api_method :<%= method_name %> <% end -%> api_method :find_all, :returns => [[<%= class_name %>]] api_method :create, :expects=>[{:arg_<%= file_name %>=>XmlrpcStruct::<%= class_name %>}], :returns=>[:string] api_method :update, :expects=>[{:arg_<%= file_name %>=>XmlrpcStruct::
<%= class_name %>}], :returns=>[:string] api_method :destroy, :expects=>[{:arg_<%= file_name %>=>XmlrpcStruct::
<%= class_name %>}], :returns=>[:string] end
