Ruboty の Plugin のつくりかた #ruboty
概要
Ruboty の Plugin のつくりかたについて説明します
※今回説明するのは Handler の Plugin です。Adapter の Plugin を作成する方は少数だと思いますので。
前提
下記記事で、 Ruboty の 基本構成と Handler + Action について理解してから読み進めてください。
作成する前に・・・
Ruboty の Plugin 作成を支援するツール類を使わずに、あえて手動で Plugin を作成してみます。
特に、 Ruboty 未経験者は最初に 1 回手動で作成しておくことで、
自動生成ツールがどういった部分をカバーしてくれているのか理解しやすくなります。
また、Ruboty の Plugin は Handler のみでも作成できますが、
構成の統一、テスタビリティなどを考え、 Handler + Action 構成を推奨します。
そのため、 Handler + Action 構成で説明します。
手順
仕様
- FizzBuzz Plugin を作成します
gem のひな形を生成
- bundler で gem のひな形を生成します。gem 名のプリフィックスとして 「ruboty-」 をつけます
$ bundle gem ruboty-fizzbuzz
Handler を作成します
- lib/ruboty/handlers/ ディレクトリを作成します
$ cd ruboty-fizzbuzz
$ cd ruboty
$ mkdir handlers
- lib/ruboty/handlers/fizzbuzz.rb を作成します
$ cd handlers
$ touch fizzbuzz.rb
- lib/ruboty/handlers/fizzbuzz.rb を実装します
Handler の作成時は、 Ruboty::Handlers::Base を継承します。
require "ruboty/fizzbuzz/actions/fizzbuzz"
module Ruboty
module Handlers
class Fizzbuzz < Base
end
end
end
-
on
メソッドを実装します
on
メソッドに 呼び出しパターン(正規表現), アクション名, アクションの説明を記述します
on
の内容は、 ruboty help
で呼び出されるヘルプにも利用されます。
require "ruboty/fizzbuzz/actions/fizzbuzz"
module Ruboty
module Handlers
class Fizzbuzz < Base
on /fizzbuzz (?<number>.*?)\z/, name: 'fizzbuzz', description: 'output fizzbuzz result'
end
end
end
ここ指定した正規表現中の名前付きキャプチャは、後続の処理で message[:your_key]
として参照できます。
Action の作成時に登場します。
-
on
に設定したアクション名と同じ public メソッドをアクション数分追加します。
メソッド内容は、 Action の呼び出しのみです。
Handler は名前の通り、コマンドに応じた Action に専念します。
単一責務。
Action の呼び出しは call メソッドで統一するようになっています。
gem 名と同一のアクション名なのでちょっとわかりにくいですが、
Ruboty::Fizzbuzz::Actions::Fizzbuzz の最初の Fizzbuzz は gem の名前、
最後の Fizzbuzz はアクションの名前です。
例えば、文字列を大文字・小文字にする Plugin StringConverter があったとしたら
Ruboty::StringConverter::Actions::Upcase
Ruboty::StringConverter::Actions::Downcase
のようになります。
以下は、 Ruboty::Handlers::Fizzbuzz のコードです。
require "ruboty/fizzbuzz/actions/fizzbuzz"
module Ruboty
module Handlers
class Fizzbuzz < Base
on /fizzbuzz (?<number>.*?)\z/, name: 'fizzbuzz', description: 'output fizzbuzz result'
def fizzbuzz(message)
Ruboty::Fizzbuzz::Actions::Fizzbuzz.new(message).call
end
end
end
end
Action を作成します
- lib/ruboty/fizzbuzz/actions/ ディレクトリを作成します
# プロジェクトルートからスタートしたとします
$ cd lib/ruboty/fizzbuzz
$ mkdir actions
- lib/ruboty/fizzbuzz/actions/fizzbuzz.rb を作成します
$ cd actions
$ touch fizzbuzz.rb
- lib/ruboty/fizzbuzz/actions/fizzbuzz.rb を実装します
Action の作成時は、 Ruboty::Actions::Base を継承します。
module Ruboty
module Fizzbuzz
module Actions
class Fizzbuzz < Ruboty::Actions::Base
end
end
end
end
今回は 1 Action しかありませんが、 複数ある場合は件数分 actions ディレクトリ配下に作成します。
-
call
メソッドを実装します。 これで、 Handler からcall
を呼び出し可能になりました
message.reply
は文字列を引数にとり、チャットメッセージを送るメソッドです。
module Ruboty
module Fizzbuzz
module Actions
class Fizzbuzz < Ruboty::Actions::Base
def call
message.reply('dummy')
end
end
end
end
end
- FizzBuzz の処理本体を作成します。 private メソッドとして作成してみます
Handler の部分で説明した 名前付きキャプチャの値を message[:number]
から取得しています
module Ruboty
module Fizzbuzz
module Actions
class Fizzbuzz < Ruboty::Actions::Base
def call
message.reply(fizzbuzz)
end
private
def fizzbuzz
case message[:number].to_i
when fizzbuzz? then "FizzBuzz"
when buzz? then "Buzz"
when fizz? then "Fizz"
else message[:number]
end
end
def fizzbuzz?; ->(v){v % 15 == 0}; end
def buzz?; ->(v){v % 5 == 0}; end
def fizz?; ->(v){v % 3 == 0}; end
end
end
end
end
gem の公開に必要な情報を設定します
- ./README.md
Plugin の内容を説明する内容を記述してください。
最低限 TODO になっている
description (概要説明)
と
Usage
の部分を記述してください。
- ./ruboty-fizzbuzz.gemspec
bundle gem 直後は以下のようになっています。
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'ruboty/fizzbuzz/version'
Gem::Specification.new do |spec|
spec.name = "ruboty-fizzbuzz"
spec.version = Ruboty::Fizzbuzz::VERSION
spec.authors = ["your name"]
spec.email = ["your mail address"]
spec.summary = %q{TODO: Write a short summary. Required.}
spec.description = %q{TODO: Write a longer description. Optional.}
spec.homepage = ""
spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0")
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 1.7"
spec.add_development_dependency "rake", "~> 10.0"
end
gem の仕様を記述します。
spec.authors
spec.email
spec.summary
spec.description
に任意の内容を設定し、
spec.add_development_dependency の直前に
spec.add_runtime_dependency "ruboty"
を追加してください。
GitHub に公開する場合は、
spec.homepage = ""
に、 URL を設定します。
GitHub に公開する
- git に登録します
$ git init
$ git add -A
$ git commit -m "first commit"
-
GitHub で
ruboty-fizzbuzz
プロジェクトを作成します。 -
remote に 作成した GitHub のリポジトリを登録します
$ git remote add origin git@github.com:tbpgr/ruboty-fizzbuzz.git
- GitHub に push します
$ git push origin master
Shell Adapter で Ruboty の動作確認をする
- ruboty の作業ディレクトリを作成
# 好きなディレクトリに移動
$ ruboty --generate
$ cd ruboty
- Gemfile に今作成した ruboty-fizzbuzz を追加
source "https://rubygems.org"
gem "ruboty"
gem 'ruboty-fizzbuzz', :git => "git://github.com/your_account_name/ruboty-fizzbuzz.git"
※ Windows / OSX 環境の方は rb-readline gem も追加
RubyGems に公開せずに gem を利用する方法について、詳しくは下記を参照。
Ruboty Plugin を RubyGems に公開せずに利用する
- ruboty-fizzbuzz をインストールします
$ bundle install
- 動作確認をします
$ bundle exec ruboty
> ruboty help
ruboty /fizzbuzz (?<number>.*?)\z/ - output fizzbuzz result
ruboty /help( me)?\z/i - Show this help message
ruboty /ping\z/i - Return PONG to PING
ruboty /who am i\?/i - Answer who you are
> ruboty fizzbuzz 1
1
> ruboty fizzbuzz 2
2
> ruboty fizzbuzz 3
Fizz
> ruboty fizzbuzz 5
Buzz
> ruboty fizzbuzz 15
FizzBuzz
お気づきですか?
Ruboty の Plugin を2回以上作ろうとした場合、 WET (Write Every Twice) な部分が多いことに気が付きます。
※ WET は DRY (Don't repeat yourself) の対義語として用いられます
ディレクトリ構成、クラス・モジュールの宣言、決まりきった継承などなど。
ちょっとしたジョーク Plugin を作成する場合などは、実装部はごくわずかで、ひな型部分の作成がメインになりかねません。
コピペでもいいのですが、コピペはタイポによるミスを誘発しやすく、人間はミスをするものです。
そこで、ツールの出番です。
@blockgiven さんや私が boilertemplate となる重複した部分を生成するジェネレータを作成しました。
各ジェネレータに関する詳しい情報は下記の記事を参照ください。
プロジェクトテンプレート、コード生成
README の生成
- Ruboty Handler Plugin 作成時の README テンプレートジェネレータ ruboty-megen を作成した
- Ruboty Handler Plugin 作成時の README テンプレートジェネレータ ruboty-megen の REAMDE 内の Commands から Usage へのページ内リンクを追加 #ruboty
- Ruboty ruboty-megen の command ブロックに example を追加
Qiita の記事の生成
上記の全ツールをまとめて紹介
外部資料
Ruboty の Plugin だけではなく gem の作りかたについては下記サイトが入門内容としてわかりやすいです
Ruboty Advent Calendar
明日は @r82 さんです