kinoppyd.dev

blog

products

accounts & contact

kinoppyd dev - page 5

kinoppyd blog

Mobbの実装とSinatraの実装の比較

2018-12-06 02:52:08 +0900

このエントリは、Mobb/Repp Advent Calendar の六日目です

Mobbの実装の元ネタ

これまで何度も出てきましたが、Mobbの実装はSinatraから非常に強烈な影響を受けています。しかし、はっきりと宣言しておきますが、強烈な影響を受けているなんていう生易しいものではありません。Mobbのコードは、ほとんどSinatraをコピペして作られていると言っても過言ではありません。

Mobbの文法やDLSのIFは、ほぼSinatraの形を踏襲しています。そしてその形を受け継ぐのであれば、どうやってもSinatraと同じコードが頻出することが最適解になっていきます。なにせSinatraは10年以上の歴史があるフレームワークで、その歴史の中でコードが無駄なく洗練され続け、その後追いをするならば必然的に同じコードが頻出することになります。なぜなら、Sinatraはわずか2000行ほどのコードで構成され、どうしてもそれ以上コードを削ることが難しいためです。

Sinatraと全く同じコード

昨日のエントリでも書きましたが、helpers/registerは全く同じコードが出現します。

https://github.com/kinoppyd/mobb/blob/6c089f5aee4763c6cf374905e5ce8ca8813e54d6/lib/mobb/base.rb#L250-L253

https://github.com/sinatra/sinatra/blob/ba63ae84bd52174af03d3933863007ca8a37ac1c/lib/sinatra/base.rb#L1403-L1406

def helpers(*extensions, &block)
  class_eval(&block)   if block_given?
  include(*extensions) if extensions.any?
end

https://github.com/kinoppyd/mobb/blob/6c089f5aee4763c6cf374905e5ce8ca8813e54d6/lib/mobb/base.rb#L255-L262

https://github.com/sinatra/sinatra/blob/ba63ae84bd52174af03d3933863007ca8a37ac1c/lib/sinatra/base.rb#L1410-L1417

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

2018-12-05 01:29:42 +0900

この記事は 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

2018-12-04 03:27:06 +0900

このエントリは、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の基本的な書き方

2018-12-03 00:23:45 +0900

このエントリは、Mobb/Repp Advent Calendar の三日目です

Mobbの基本的な機能

Mobb/Reppがなんなのかは昨日までのエントリで書いたので、今回はMobbで書ける基本的な機能を紹介します。

以下に上げるのは、Mobbのほとんどすべての機能なので、これを知っていれば秒でBotが作れます!

特定の文字列との完全マッチ

receive 'テクテクテクテク' do
  # テクテクテクテク という発言があったらこのブロックに入る
end

on 'ポッポポッポハトポッポ' do
  # on は receive のエイリアス
end

秒でBotを作れるMobb、その裏にあるReppって何者?

2018-12-02 01:06:24 +0900

このエントリは、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にしましょう。秒で。

2018-12-01 00:11:37 +0900

この記事は、Slack Advent CalendarMobb/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プログラマであれば。


技術書典5にて、Sinatraのコード解説本を頒布します。あと執筆環境の話。

2018-10-06 22:24:34 +0900

技術書典5に参加します

2018/10/08 に池袋サンシャインシティで開催される技術書典5にサークル参加します。すぺーすは「い22」です。よろしくお願いします。

頒布する本の内容は、ここ半年ほど作っていたMobbというボットフレームワークのコードを書くために読み込んだSinatraの、コードリーディングと解説の本です。Sinatraは非常にミニマルながらも強力な黒魔術と設計に支えられた、Ruby力を上げる教材としてとても良い題材だと思ったので、その知見を広く共有するために本にしました。

本の詳細と言い訳

頒布する本は、本文44ページで頒布価格は1000円にする予定です。これは、印刷した本の45%が売れた場合に、印刷代と参加費、その他諸経費がペイするように設定しました。45%の根拠は、同じサークルの檻総君が夏と冬に刷っている同人誌の売れ行きを観察するに、これくらいの数値設定が妥当だとおもわれるためです。

本文は、おおよそ二週間かからずに書き終わりました。しかし、その特急スケジュールのおかげで文字以外の図解などを作る暇がなく、やや読んでて疲れる本になってしまったという自覚があります。今回の技術書典でこの本を求めてくれる人が多ければ、もっと読みやすくした版を後日作成し、本を買っていただいた方には何かしらの形で無料で配れればと思ったりもしますが、その思いを実行できるくらい当日売れることを祈るばかりです。


Mobb 0.3 をリリースしました、これで実践的に毎秒クソボットをリリースできます

2018-09-17 22:07:05 +0900

Mobb 0.3.0 をリリースしました

今回のリリースでは、 cron/every キーワードが Mobb DSL に追加されています。

https://github.com/kinoppyd/mobb/pull/5

これらのキーワードは、 Mobb(およびRepp) になにかを定期実行させるためのものであり、このリリースによってようやく Mobb は実用的で最も簡易な Bot フレームワークを名乗ることができるようになったと思います。

最初に Mobb を作ろうと考えたときに、秒でクソボットを作れるようになるために必要なものはなんだろうと考えました。そして、必要だと考えたのは、シンプルなシンタックスで書けること、条件分岐がフレームワークの機能に備わっていること、なにかを定期実行できること、の三点でした。 Mobb 0.3 のリリースにより、これら3つの要素が全て揃い、ようやく Mobb が実用的な Botフレームワークとして皆さんに使ってもらえる機能を備えました。

Syntax

cron/every キーワードは、名前の通りなにかを定期実行するためのものです。具体的なシンタックスは、次のように書きます。

cron '0 12-21 * * *', dest_to: 'times_kinoppyd' do
  "Hi bro, wazup?
end

この例では、毎日12時から21時までの毎事0分に、botが times_kinoppyd という配信先(Slackであれば #times_kinoppyd) に対して、 “Hi bro, wazup?” というメッセージを配信します。

cron キーワードは、そのまま Cron の文法が使えます。具体的には、 parse-cron というGemを使ってパースできるものであれば動作します。

また、 cron の簡易的な使い方を目的として、 every というキーワードも追加しています。

every :day, at: '14:00', dest_to: 'times_kinoppyd' do
  "ぞい"
end

cron よりも、より直感的に定期実行を記述できます。これも、 whenever というGemを内部で利用しており、 whenever が解釈できる引数をそのまま渡すことができます。例えば次の例では、2というIntegerオブジェクトへのWheneverの拡張により、hoursというメソッドが追加されていて、それをそのまま使用することができます。

every 2.hours, dest_to: 'times_kinoppyd' do
  'ぞい'
end

every キーワードが受け取る事のできるシンタックスに関しては、 whenever のREADMEを参考にしてください。

注意点として、 cron/every キーワードでは、必ず dest_to というコンディションが必要な点に注意してください。これを設定しないと、Botはどこのチャネルにポストすれば良いのかがわからず、メッセージが闇に消えることになります。(Shellアダプタなど、チャネルの区別が無い環境であれば必要ありません)


Mobb 0.2.0 out now

2018-08-27 01:45:21 +0900

Mobb 0.2.0 をリリースしました

このバージョンでの大きな変更点は、helpersメソッドの追加とconditionメソッドの追加です。それぞれ、Mobb DSLのベースとなっているSinatraでは非常に大きな役目を果たしていたけれど、まだ移植が終わっていなかった機能です。

https://github.com/kinoppyd/mobb/releases/tag/v0.2.0

新機能の解説

0.2.0では、 helpers と condition が大きな変更点です。

helpers

helpersメソッドは、onイベントの中でアクセス可能なメソッドをMobbアプリケーションのトップレベルに定義するためのメソッドです。

require 'mobb'

helpers do
  def greet(name)
    "Hello #{name}"
  end
end

on "Hello" do
  greet(@env.user.name)
end

このように、 on メソッドのブロックの中でアクセスできる greet メソッドのような、ヘルパーと呼ばれるメソッドを定義します。

本来、 on メソッドのブロックは、この記述の場合は main オブジェクトに対するクロージャになっています。しかし、MobbやSinatraは内部でこの binding を書き換えるトリックを使っており、通常の手順では main オブジェクトに定義したメソッドや値を参照できません。例えば、次のコードは実際にブロックの中が呼び出される時にエラーになります。

require 'mobb'

def yo
  "Yo"
end

on 'Yo' do
  yo # error on runtime!
end

そのため、 helpers というメソッドを使い、一時的に self を on メソッドのブロックが実行される時と同じスコープに書き換えます。そして、そのスコープ内で def を使ってメソッドを定義することで、 on のブロックからアクセスできるヘルパーメソッドを定義することができるのです。


技術書展5で、Sinatraのコードリーディング本を頒布します

2018-08-23 23:43:31 +0900

技術書展5に当選しました

当選してしまったので、技術系の同人誌を書こうと思います。

最近の個人的な活動の成果として、SinatraのDSLっぽくBotが書けるMobbというプロダクトを作っています。そしてその過程で得たSinatraのコードリーディングの知見を広く共有するのが良いだろうと思っているので、同人誌にしようと思います。

たまたま、今日の夕方に会社のイベントでこういう発表をしたのですが、これを更に詳細に掘り下げた本になると思います。

kinoppydさんの「Sinatra trick」 / kinoppyd さん - ニコナレ

スペースはどこ

◎貴サークル「トレイリア学園」は、 い22 に配置されました。

どういう本にするの

Sinatraの全コードの解説を目指します。一冊読めば、自分でSinatraライクのDSLを書けるようになるくらいの本にしたいと思っています。対象読者層は、メタプログラミングRubyを読んだことがあるRubyエンジニアを想定していますが、それではあまりにもリーチが狭い(気がする)ので、メタプログラミングRubyの本を読んでいなくても簡単なエッセンスくらいは理解できる感じに解説を入れようと思っています。

また、現在コミットをしているMobbというBotフレームワークのプロジェクトに関しても、簡単な解説本を書こうと思っています。