Rubyを使って秒でBotを作るなら、秒でRedisだって使えなきゃ、やっぱり話にならないですよね?
このエントリは、 Mobb/Repp Advent Calendar の十一日目です
mobb-redis
昨日のエントリでは、MobbでActiveRecordを利用する方法を紹介して、Sinatraの資産をMobbが継承できるという話をしました。そして今日はRedisです。こっちは本当に秒でいけます。
https://github.com/kinoppyd/mobb-redis
まず、検証用のRedisを立てましょう。今ならDockerで秒です。
docker pull redis
docker run --rm -p 6379:6379 redis
次に、mobb-redisをインストールします。例ではBundlerを使います。
# frozen_string_literal: true
source "https://rubygems.org"
gem "mobb"
gem "mobb-redis"
最後にアプリケーションを書きます。
require 'mobb'
require 'mobb-redis'
register Mobb::Cache
on 'hello' do
settings.cache.fetch('great') { "hello world #{Time.now}" }
end
はい、秒ですね。起動して動作を見てみましょう。
bundle exec ruby app.rb
== Mobb (v0.4.0) is in da house with Shell. Make some noise!
hello
hello world 2018-12-11 01:36:16 +0900
hello
hello world 2018-12-11 01:36:16 +0900
このように、返答の中の時間が変化していないので、Redisを経由していることがわかります。
redis-sinatra
Rubyを使って秒でBotを作るなら、秒でActiveRecord使えなきゃ話にならないですよね?
このエントリは、 Mobb/Repp Advent Calendar 2018 の十日目です
ActiveRecord
ここ数日書いているエントリで、Mobbがいかに秒でBotを作れるすごいやつかというのは伝わったかと思います。しかし、世の中には秒で複雑で素敵なBotのアイディアが思い浮かぶ人もいます。そして、そういう人の多くは「いやいや、いくら秒でロジック書けても、DB使えないと話にならんでしょ」という人もいます。
そうです、Botのロジックは会話的なものが多いため、状態を記録したりデータを保持したりという行動を行いたいケースが非常に多いです。それでは、DBを使いましょう。RubyでDBといえば、ActiveRecordですね。ActiveRecord、使いたくないですか?
Mobbは、秒でBotを作るエンジニアを本気で応援するフレームワークです。当然、ActiveRecordも使えなくては話になりません。
安心してください、使えますActiveRecord。たった一つのgemを追加するだけで。
https://github.com/kinoppyd/mobb-activerecord
Usage
Mobb, test, spec
このエントリは、 Mobb/Repp Advent Calender の八日目です
最初にはっきり言っておくこと
このエントリは読まなくていい
Mobbのテスト
無い。現状、存在が無い。皆無。虚無。無。無って漢字に飽きてきた? じゃあ観測できないって言いかえる。
Mobbをテストするということ
Mobbをテストするということは簡単です。簡単ではないですが、書けます。なぜなら、MobbはSinatraのコピーとも言えるプロダクトなので、テストもコピーしてしまえば言いわけです。
ただ、現状そうもいっていない。私の時間ややる気の大小は感情的問題なので、定量的な判断からは除いておくと、Mobbの開発はあまり活発とは言えません。なので、単に書いていないだけです。これに関しては激しく切腹という言葉が脳裏に浮かびつつもどうにもならない問題で、時間とキーボードのストロークが解決してくれると思っています。どうしてMobbそのもののテストが無いのかという問題に立ち向かうとき、あなたはテストを書きましょう、私は怠けてしまったのです。としか言えません。
Mobbアプリケーションをテストするということ
Mobbアプリケーションをテストするということは、ほぼそのままの意味でReppアプリケーションをテストするということです。なぜなら、MobbはReppアプリケーションを作成するためのDLSであるからです。これをわかりやすく言うと、SinatraのテストをするということはRackアプリケーションをテストするということ、と同義です。
さて、Sinatraのテストはどう書くのか? 正確に言うと、Rackアプリケーションのテストはどう書くのか? それは、Rack::Testというものを使います。
https://github.com/rack-test/rack-test
これは、シンプルな意味でRackアプリケーションをテストするためのヘルパー群の集まりです。Rspecやtest-unitなどの各種テストスイーツを利用できます。
同じように、RackにもRack::Testというモジュールを追加する予定があります。現在鋭意製作中ですが、概ねRack::Testと似たような構成になると思っています。
Rack::Testは、内側でセッション情報などいろいろなものを頑張って扱っていますが、幸いMobb/Repp構成においてセッション情報とかいうものはあまり関係無かったりするので、Rack::Testよりは多少シンプルになるのではないでしょうか。
最後に
ネタが切れてきた
Reppのインターフェイス
この記事は Mobb/Repp Advent Calendar の7日目です
Reppのインターフェイス
少し前のエントリで、Reppはインターフェイスだと解説しましたが、実はそのインターフェイスの定義をきちんと書いたドキュメントは存在しません。それは何故かと言うと、まだインターフェイスをどのように固めれば柔軟に今後対応していけるかという確信が、書いている本人である私にないからです。現状の仮実装として、Slackを事実上の対象サービスとして想定した記述ができるように、次のように実装されています。
-
environmentのハッシュを引数に一つ受け取るcallメソッドが実装されている
-
2つの要素からなる配列を返す。サービスに投稿する文字列が1つ目の要素、各サービスごとにオプショナルに解釈してほしい内容をハッシュで2つめの要素に入れる
ReppはRackの模倣をして作られた、各種サービスとBotフレームワークとの間をつなぐ取り決めですが、Rackの取り扱うHTTP(正確にはWebサーバー)の世界とは違い、Botが接続するSlackやShellやChatWorkやLINEや、あるいはHTTPかもしれないサービスとの接続はその形式が多種多様に渡り、抽象化するとどのようにまとまるのかをうまく調整することが難しいのです。
たとえば、SlackにはAttachmentsというチャットの発言とは別にチャットの内容を装飾するための取り決めがありますが、それは他のサービスにはありません。しかし、Reppとしては存在したりしなかったりする内容に関してもきちんと橋渡しをする必要があります。
Mobbの実装とSinatraの実装の比較
このエントリは、Mobb/Repp Advent Calendar の六日目です
Mobbの実装の元ネタ
これまで何度も出てきましたが、Mobbの実装はSinatraから非常に強烈な影響を受けています。しかし、はっきりと宣言しておきますが、強烈な影響を受けているなんていう生易しいものではありません。Mobbのコードは、ほとんどSinatraをコピペして作られていると言っても過言ではありません。
Mobbの文法やDLSのIFは、ほぼSinatraの形を踏襲しています。そしてその形を受け継ぐのであれば、どうやってもSinatraと同じコードが頻出することが最適解になっていきます。なにせSinatraは10年以上の歴史があるフレームワークで、その歴史の中でコードが無駄なく洗練され続け、その後追いをするならば必然的に同じコードが頻出することになります。なぜなら、Sinatraはわずか2000行ほどのコードで構成され、どうしてもそれ以上コードを削ることが難しいためです。
Sinatraと全く同じコード
昨日のエントリでも書きましたが、helpers/registerは全く同じコードが出現します。
def helpers(*extensions, &block)
class_eval(&block) if block_given?
include(*extensions) if extensions.any?
end
def register(*extensions, &block)
extensions << Module.new(&block) if block_given?
@extensions += extensions
extensions.each do |extension|
extend extension
extension.registered(self) if extension.respond_to?(:registered)
end
end
この2つは本当に全く同じコードを使っています。そのおかげで、Sinatraの資産をそのまま流用できる箇所でもあります。例えば、mobb-activerecordなどです。
他にも、before/afterフィルタや、invokeメソッド、デバッグ用にスタックトレースを追跡するコードや、デリゲータのコードなど、全く同じものが出てくる箇所がいくつもあります。
Mobbの機能拡張を実現するhelpersとregister
この記事は Mobb/Repp Advent Calendar の五日目です
Mobbの機能拡張
これまで見てきたように、MobbにはSinatraとほぼ同じ仕組みが多数存在します。そして今日紹介するhelpers/registerメソッドも、Sinatra由来の機能です。これら2つのメソッドは、Sinatraと全く同じコードが書かれているので、挙動も全く同じです。
helpers
helpersメソッドは、トップレベルモード(require ‘mobb’をしてそのままロジックを書き始めるケース)では暗黙的に作成されるMobbのアプリケーションクラスを、モジュラーモード(require ‘mobb/base’ して自分でクラスを定義するケース)ではhelpersが呼び出されたクラスをそれぞれコンテキストとして、渡されたブロックをclass_evalで実行します。ブロックではなくモジュールを渡した場合は、そのモジュールがincludeされます。
require 'mobb'
def hoge
'hoge'
end
on 'hello' do
hoge
end
require 'mobb/base'
class Bot < Mobb::Base
helpers do
def hoge
'hoge'
end
end
on 'hello' do
hoge
end
end
Bot.run!
モジュラーモードでは、そもそもhelpersの中で定義しようがクラスの中で定義しようが、クラスのインスタンスメソッドとして定義されるのであまり違いはありません。ですが、トップレベルモードの場合、Rubyのmainクラスで定義したメソッドはObjectのプライベートメソッドとして定義されるので、いろいろなものを汚染しかねません。そのため、暗黙的に作られるMobbアプリケーションのクラスに、helpersを使って直接定義を行います。
https://docs.ruby-lang.org/ja/latest/class/main.html
やっていることは結局の所include呼び出しとあまり変わりませんが、ブロックを渡せるところが軽量でいいと思います。
helpersメソッドの中身は、Mobb::Baseを継承したアプリケーションを拡張しています。
MobbのCron
このエントリは、Mobb/Repp Advent Calendar の四日目です
Cron Job
Mobbには、Botのための定期実行の仕組みが備わっています。MobbはSinatraをベースとしたBotフレームワークですが、完全にSinatraと同じことをするわけにはいきません。その一つが、Cronです。
Botの重要な役割の一つとして、定期実行が存在します。これはWebアプリケーションという世界には存在しない概念ですが、Botには必ず備わっていて欲しい機能です。例えば、BotをSlackに接続して、毎日決まった時刻に備忘のためのメンションをくれる、とかは重要な機能です。
重要な機能である一方、Webアプリケーションの世界に存在しないこの機能をどう扱おうかという考えはなかなか難しかったので、詳細はこのエントリを読んでください。
http://tolarian-academy.net/mobb-0-3-out-now/
Mobbでは、Cron実行のためにそのままCron Syntaxを採用しました。それに加えて、より人間にわかりやすいCron記述のために、WheneverというGemのパーサーも利用しています。CronとWheneverのどちらが簡単かは人によりますが、多くの人はWheneverのシンタックスのほうが親しみがあると思います。
https://github.com/javan/whenever
詳細はWheneverのドキュメントを参考にしていただければと思いますが、簡単な例をいくつかあげておきます。
Mobbの基本的な書き方
このエントリは、Mobb/Repp Advent Calendar の三日目です
Mobbの基本的な機能
Mobb/Reppがなんなのかは昨日までのエントリで書いたので、今回はMobbで書ける基本的な機能を紹介します。
以下に上げるのは、Mobbのほとんどすべての機能なので、これを知っていれば秒でBotが作れます!
特定の文字列との完全マッチ
receive 'テクテクテクテク' do
# テクテクテクテク という発言があったらこのブロックに入る
end
on 'ポッポポッポハトポッポ' do
# on は receive のエイリアス
end
秒でBotを作れるMobb、その裏にあるReppって何者?
このエントリは、Mobb/Repp Advent Calendar の2日目です
Reppというもの
昨日のエントリで、Mobbという秒でBotを作るための強力なフレームワークを紹介しました。このエントリでは、そのMobbを支えるReppという仕組みに関して書いていきます。
Webサービスを作ることに詳しいRubyプログラマ向けに説明すると、MobbとはSinatraであり、ReppとはRackです。それで理解できる方は、此処から先はそれ以上の情報が書かれていないので読む必要はありません。
ReppとはRackと言われてピンとこなかった方のために、Botを作るための複雑性を排除し、いかに秒でBotを作るかという話を交えつつ、Mobbに対してReppが何者を説明したいと思います。
Botを秒で作るために必要なもの
Botを秒で作るために必要なものは、複雑な物事をうまく隠す方法です。そしてSlackやTwitterを始めとした各種サービスとの結合部分は、その複雑な物事の最たるものです。
例えば、フレームワークに頼らず自前でBotを作るとしましょう。各サービスのSDKを探してきて、そのドキュメントを軽く読み、データの受取と投稿部分のコードを書きます。そしてその後、自分のbotが本当にやりたいロジックを書き、サービスと連携するコードと結合します。ドキュメント読むの、面倒じゃないですか? サービスと接続する部分の実装、面倒じゃないですか? こんなのでは秒でBotを作れません。
秒でBotを作るためには、サービスとの結合部分を徹底的に隠さなくてはいけません。サービスとの接続に手間取っているようでは、それは複雑も複雑であり、怪奇です。また、サービスとの接続部分は使いまわせなくてはいけません。あなたが作りたいBotは、一つではないからです。秒でBotを作るということは、毎秒新しいBotのアイディアを思いついて、すぐに実装する。そういうことです。
アイディアが閃いた? それすぐにSlackBotにしましょう。秒で。
この記事は、Slack Advent CalendarとMobb/Repp Advent Calendar 共通の1日目の記事です
Slack Bot を秒で作る方法
日常生活を送っていると、突然身の回りで何かが流行ることがありませんか? 私はよくあります。例えば、ある日突然絵文字をシャッフルすることが流行ったり、ちょっと昔には「Yo」と言ったら「Yo」と返すのが突然流行ったりしました。
何かが流行ったとき、それはbotを作るチャンスです。幸いあなたはプログラマで、流行った何かを更に面白くするアイディアとロジックは一瞬で頭に浮かびます!
けれど、そのロジックを実装するのは簡単ですが、実際にその機能をbotとしてSlackの野に放つのは簡単でしょうか? 私はあまり簡単ではないと思います。
例えば、多くの言語にはBot用のフレームワークが存在します。RubyであればRuboty、JSであればHubot、Pythonであればslackbot、JavaであればJBotなどが存在しますし、そもそも各言語のSlack用ライブラリが必ず存在するので、それを使えばフレームワークすら必要とせずにBotを作れます。
しかし、それらは本当に簡単でしょうか? 楽しいアイディアを思いついて、それをBot化するまでの間に情熱が冷めてしまうほど時間がかかりませんか? そして他の人が似たアイディアのBotをSlackに先に放ち、「俺の考えてたアイディアのほうが面白いのに、先を越された」なんていう愚痴をSlackで吐いたりするようなことになってしまいやしませんか?
Botのアイディアは、秒で形に出来なくてはいけません。そして毎秒クソボットを作れるくらいのスピード感が必要なのです。けど、そんなこと本当に可能なのでしょうか?
可能です。あなたがRubyプログラマであれば。