Help us understand the problem. What is going on with this article?

Ruboty の Plugin のつくりかた #ruboty

More than 3 years have passed since last update.

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 の生成

Qiita の記事の生成

上記の全ツールをまとめて紹介

外部資料

Ruboty の Plugin だけではなく gem の作りかたについては下記サイトが入門内容としてわかりやすいです

Ruboty Advent Calendar

明日は @r82 さんです

tbpgr
Rubyを扱う人事(研修開発、エンジニア採用) Learning Designer。 tbpgr の読み方は(てぃーびー) 個人ブログでも色々と情報を垂れ流してます。 http://tbpgr.hatenablog.com/
http://tbpgr.hatenablog.com/
studist
「伝えることを、もっと簡単に」をミッションにビジュアルSOPマネジメントプラットフォームのBtoB SaaS「Teachme Biz」を開発・運営するスタートアップ
https://medium.com/studist-dev
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした