JupyterとRubyカーネルの通信をのぞいてみた


## はじめに

こんにちは。

JupyterとIRubyカーネルがどんな通信をしているのか観察してみたいなあと思ったことはありませんか？

ここでは、RubyカーネルとJupyterがどのような通信をしているのか、簡単な方法で観察してみたいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/f14886eb-d2e8-1b49-f4ad-8b24a518065f.png)

方針は以下のとおりです。

## 方法

1. [Flammarion](https://github.com/zach-capalbo/flammarion) のWindowを立ち上げる。
2. dRubyサーバーを立てて、Flammarionに自由に書き込めるようにしておく。
3. IRubyのコードをちょっと書き換えて、通信のたびにFlammarionに書き込む。

ターミナルの標準出力でも大丈夫ですが、あとで応用がききそうなのでFlammarionを使います。

## Flammarionの紹介
　Flammarionを知っている人はほぼいないと思うので説明が必要でしょう。FlammarionはelectronとChromeを使って手軽にGUIを作れるイケてるツールです。Rubyのお気軽GUIツールというとShoesを思い浮かべる人がいるかもしれませんが、Flammarionは情報をどんどん表示していくパネルのようなつかい方が向いてます。

使い方はこんな感じです。

```ruby
require 'flammarion'

# Flammarionを立ち上げる
f = Flammarion::Engraving.new
f.puts "hello world!"
```
上記をpryやirbで実行すると、下記のようなウィンドウが表示されると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/111d30fc-dd68-aaa9-a8bb-1ad40b987883.png)

あとは、`f.puts "hoge"` つづければ、情報を逐次的に表示できる簡易表示版として使えます。
もちろん、Flammarionにはいろいろな機能がついていて、普通のGUIツールキットのように、ボタンを表示したり、

```ruby
f.button("Click Here!!!") {f.puts "You clicked the button!"}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/c4426de3-f80e-4b5f-7817-4f07f0608195.png)

簡単に本格的なグラフを表示したり

```ruby
f.plot(5.times.map{|t| {y:100.times.map{rand * t}}})
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/3e586371-722e-6356-fe37-6b441d3dd724.png)

あっという間に松江の地図を表示したりと、魔法じみた機能が用意されています。

```ruby
f.map("松江")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/90a9827b-0690-73c1-aece-d117e49914c5.png)

Flammarionに搭載されているメソッドを表形式で表示してみます。

```ruby
f.table (f.methods - Object.methods).each_slice(4).to_a
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/cf25ea70-20cf-96bb-fd69-061bd4c685df.png)

これは良い意味でかなりクレイジーなツールではないでしょうか。

## dRubyサーバーを立ち上げる

dRubyもまた古くからある魔術感のあふれるツールですが、私はそんなにスキルが高くないので今回使うのはその機能の表面だけです。ここではFlammarionのウィンドウを立ち上げて、dRubyのフロントオブジェクトとしてセットします。これでIRubyカーネルからFlammarionに書き込めるようになります。

```ruby
require 'drb/drb'
require 'flammarion'

# Flammarionを立ち上げる
f = Flammarion::Engraving.new

# サーバを起動する
DRb.start_service("druby://localhost:8787", f, :safe_level => 1)
# DRb のスレッドが終了するのを待つ
DRb.thread.join
```

## IRubyに細工をする
ここではRubyのオープンクラスをつかって、Jupyter上から、IRubyに細工をします。`ai` は awesome_print における `inspect`です。

```ruby
require 'drb/drb'
require 'colorize'
require 'awesome_print'

module Monkey
  @@f = DRbObject.new_with_uri("druby://localhost:8787")
  def send(sock, data)
    @@f.puts sock.ai.colorize(:blue)
    @@f.puts data.ai.colorize(:green)
    super(sock, data)
  end
 
  def recv(sock)
    msg = super
    @@f.puts msg.ai.colorize(:yellow)
    msg
  end
end

IRuby::SessionAdapter::FfirzmqAdapter.prepend Monkey
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/6afceead-7c5f-d0c7-25cc-48de7ebcc605.png)

prependをつかって、FfirzmqAdapterクラスの手前にモンキーモジュールを配置しています。これによって、ffi-rzmqアダプターを経由する通信は、モンキーのメソッドを経由するので、Flammarionに表示されるようになります。他のアダプターを使っている場合も、だいたい同じような感じでできると思います。

こんなかんじでJupyterで行われている通信がFlammarionのウィンドウに表示されれば成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/f88eb4b6-747f-1229-521b-b9fb0824be87.png)

なお、ここではIRubyとJupyterの通信に直接変更を加えているので、typoしたりするとRubyカーネルが行方不明になり、2度と通信ができなくなります。その場合は仕方ないのでカーネルを再起動します。

いろいろ改善余地はありますが、初回なので、こんな感じだと思います。

この記事は以上です。