kinoppyd.dev

blog

products

accounts & contact

kinoppyd dev - page 3

kinoppyd blog

株式会社ドワンゴを退職します(5年2ヶ月ぶり1回目)

2019-08-30 23:44:49 +0900

2019年8月末で、株式会社ドワンゴを退職します。これは主に社内の友人に向けた文章ですが、一応自己紹介は書いておきます。

私はkinoppydという名前で、2014年にドワンゴへ入社し、Scala/Rubyエンジニアをやっていました。最初に配属されたプロジェクトはニコニコ生放送のScala化プロジェクトで、ニコ生のScala化、ニコ生のHTML5化のお手伝い、公式生放送の老朽化した機能のマイクロサービス化などを行いました。その後はチームを異動し、ニコナレのバックエンド開発に従事しました。

他にも、社内でSlack芸人やBot芸人をやっており、なんか記事にされたりしたこともありました。

【bot、暴走中!】「Slackは福利厚生」と言い切る、ドワンゴ流・Slackの超活用術とは

絵文字コミュニケーション術、サーバーワークスとドワンゴが明かす

趣味はRubyのコードを書くことで、OSS活動としてSinatraライクな書き味のBotフレームワーク Mobb の開発などをやっていたり、技術書典でSinatraのコードリーディング本やメタプログラミングRubyの副読本などを書いていたりしました。次の技術書典では「ActiveRecord完全に理解した」という本を出します。

退職の理由

大まかに言うと、ライフスタイルが変わったためです。具体的に言うと、結婚をしました。

人生にはお金が必要です。ドワンゴからもらっていた給与は、私一人で生きていく分には十分なものでした。しかし、私自身の人生と将来生まれてくる子供の人生の2つを支えるには不十分でした。また、昇給のペースなどを考えても今後私が求める水準に達するにはかなりの努力と時間がかかると思い、転職して給与を上げる決断をしました。

私はとても夜型の人間です。そしてドワンゴには、真の裁量労働が存在しています。成果を出しチームの中で合意があれば、何時に出社しても何時に帰っても何も問題はなく、なんなら一日8時間働く必要すらありませんでした。私は在籍中、月に160時間働いてない月がそこそこあるくらいです。そのため、私は概ね昼の12時から14時の間に出社し、夜の20時から0時の間に帰る、という生活を続けていました。しかし、妻は9時に家を出て20時に帰ってくるという生活を送っており、夫婦間で生活時間の不一致が問題となりました。私は真の裁量労働の権利を持っているので、その時間に合わせて生活すればいいとは思いましたが、努力しても夜型の生活を改善できませんでした。そのため、定時が存在する会社に勤め強制的に妻と同じ時間を生きることを決めました。

また、ドワンゴは2019年2月の経営陣交代以降、事業の集中と選択を進めており、私の所属するチームが持っていたニコナレもクローズの運命からは逃れられませんでした。ニコナレの使っている仕組みの一部をScala化するなどをしていたため、この仕事を離れるとなるとそれなりの引き継ぎが必要でした。しかしサービスのクローズにより、私の持ち物は何もなくなってしまいました。結果としてとても身軽になったことも決断の大きな後押しになりました。

他にもいろいろな理由はありますが、まあこの3つに比べると些細なものです。

思い出

会社では、とても友人に恵まれました。まさか社会人になってからこんなにたくさん友達ができると思っていなかったので、驚きと感動があります。ドワンゴに入ってからの新しい友人に影響された結果様々な趣味に目覚め、今までの人生の中で考えられないほど自分の変化があった5年間でした。ポーカーやロードバイク、創作活動を始めたのもそうですし、そもそも今のメイン言語であるRubyを書き始めたのもドワンゴに入ってからです。ドワンゴは、非常に多種多様な人たちが在籍しており、積極的に関わっていくとプラスだったりマイナスだったりの影響をガンガン受けていきます。

仕事で関わる人達にも恵まれ、多くは良き友人で尊敬できる仲間でした。特に、生放送チームで一緒に働いた先輩と後輩、そしてニコナレチームの上長とチームメイトは、一緒に働けたことが光栄に思えるほどでした。私は本当に幸運で、働いている間に人間関係で困ったことにほぼ出くわすことがありませんでした。後述する理由でドワンゴには大変気難しい人も多い中で、このことは本当に運が良かったとしか言いようがありません。

ドワンゴは、自分の中の感覚で一言で表せば、良くも悪くも十数年前のインターネット・アトモスフィアをだいたい東銀座に再現した会社です。人々はネットでコミュニケーションをとり、その雰囲気は独特で、世間にさらけ出すと微妙な顔をされそうな世界観と価値観がありました。社内コミュニケーションツールのSlackにはその影響が特に色濃く出ており、私にとっては心地よかったけどこの空気に馴染めない人は本当に馴染めないだろうなぁという感じです。他人の認識はアイコンとハンドルで、実際に顔を合わせてもその人の名前すらわからないことも多々あります。Slackで雑談するのが大好きで、古のインターネットのコンテキストをずっと積み重ね続けたような会話が繰り返され、さながら歩くインターネット老人会のようでした。00年代や10年代にネットで流行った出来事をみんな知っているのが半ば当然のような、生まれたときからネットしてたんじゃないかみたいな人たちがたくさん住み着いていました。繰り返しますが、自分はそんな雰囲気が大好きでした。しかしその弊害として、やはりネットの中で繋がることが好きな人が多く、実際の対人関係では頻繁に衝突を起こす人も多かったです(ネットの上で衝突している人もいましたが)。これが私がドワンゴに気難しい人が多いと思う理由で、人と話すときにはその人のコンテキストをきちんと把握する努力が必要だと感じるところです。

挨拶

ドワンゴのインターネッツを去ることは、とても寂しいことです。すっかり自分の中に染み付いてしまった感覚を忘れることは、恐らく一生無理でしょう。同僚としても友人としても仲良く接してくれた人達にはいくら感謝を伝えても足りません。また、何度も退職の決意を鈍らせるくらい、一緒に働き続けたいと思えた上長にも、会えて本当に良かったです。いま、ドワンゴが大変な時期に離れてしまうことをとても心苦しく思いますが、ニコニコという文化をこれからも守り続けてくれると、一人のユーザーとしてこれほど嬉しいことはありません。

これから

9月からは、SmartHRという会社でRubyエンジニアをやっていく予定です。今後もよろしくお願いします。


技術書典6で、メタプログラミングRuby本を出します

2019-04-12 02:40:21 +0900

技術書典6に参加します

今回も技術書典にサークル参加します。ブースは「か54」です。

今回の本は、メタプログラミングRuby第2版の内容を追いかけつつ、現実のプログラミングという世界ではどのようにメタプログラミングが利用されているのかを解説する本です。具体的には、Sinatraのコードのなかからメタプログラミングが用いられている箇所をピックアップして、それを教本に従って解説します。

メタプログラミングRuby第2版は、本の中でいくつか現実のコードを出して解説をしている箇所がありますが、いずれも断片的で複数の巨大なコードベースにまたがったものなので、追跡が難しいです。そこで、Sinatraというミニマルなコードベースの中から、強力なメタプログラミングを使っている箇所を取り出し、それがプロダクトに対してどのような意味を持っているのかを考えていきます。

本を書いた環境

こっちが本題です。

前回の技術書典5では、StackEdit+GitBookという環境で執筆を行いました。これはいくつかの要件があったけれど、最終的になんかいろいろうまく行かなくてこうなってしまった、という環境の成れの果てです。詳細は前回のブログを見てください。

今回の環境

いくつかの選択肢がありましたが、今回は最初からRe:VIEWを使って本を書くことに決めました。理由としては、次にあげるいくつかの前回の反省があります。

  • GitBookの成果物は、印刷するのにやや難がある

  • Markdownから望むRe:VIEWの形式に変換するのには、現実的に無理がある

  • というよりも、そもそも文章フォーマットの相互変換に無理がある

Markdownで執筆したものを、望むような形で印刷可能なPDFに変換することは、もはや無理だと悟ったというのが最も大きな理由です。PandocやSphinxも、変換は行ってくれますが結局の所は出力の形式が望んだものになるかどうかは確かではありません。そうなった場合、PandocやSphinxの気持ちを感じながらMarkdownを修正していくということはもはや不可能です。ならば最初から余計な相互変換を持たないRe:VIEWを使うべきだという結論になりました。複数の入力を受けつけ、複数の出力をしてくれるソフトは確かに便利ですが、アンコントローラブルになる箇所が多く、執筆時間が限られているときには選択が難しかったのです。

幸い、Re:VIEWのフォーマットは比較的わかりやすく、リストなどにやや不満はありつつも十分に1日で書けるようになるものでした。そのため、今回は最初からRe:VIEW形式で執筆を行おうと考えました。しかしその一方で、いくつか諦める必要のあるものもありました。

  • ライブプレビュー

  • クラウドエディタ

この二点です。

ライブプレビューに関しては、まあそれはそうという感じしか無く、都度ビルドして確認することしかできません。

クラウドエディタに関しては、まあクラウドエディタでRe:VIEWファイルを書けば良いので特に諦める必要はなかったのですが、EditStackに不満を持っていた私はあれからもっと良いクラウドエディタに出会っていませんでした。良いエディタという意味で言えば、esa.ioはかなり気に入っています。しかし、これはMarkdownに特化しているため、使えませんでした。

しかし、原稿をクラウドエディタで書くことを諦められなかった私は、考えました。結果、Re:VEIWフォーマットとMarkdownはなんとなく似てないこともなかったので、今回はまず概要と草稿をesa.io上で執筆し、そのMarkdownデータを手でRe:VEIWに変換した上で、本を作るための細かな命令などを手元のエディタで行いました。おそらく、技術書を書く上で一番つらいのは、最初の草稿をあげるまでで、そこから先は多少の原稿の修正と、印刷用のマークアップ作業です。なので、その辛い箇所をクラウドエディタでなんとかして、機械的にちかい作業はローカルのエディタで行うように分担したのです。

最終的に、Re:VIEWのファイルはGitで管理してGitHubにプッシュしていたので、GitHub上からクラウドエディタのようなこともできなくはなかったですが、それはやらなかったです。

用意した環境


Mobb 0.5 and Repp 0.4 out now

2018-12-26 00:01:12 +0900

このエントリは Mobb/Repp Advent Calendar の二十五日目です

Mobb 0.5.0 out now

🎉

クリスマスなので、超急ぎでリリースしました。Ruby 2.6.0 も出たし。

Mobb 0.5.0では、Advent Calendar で予告していたいくつかの機能がリリースされます。

Mobbのメソッド呼び出しをチェーンする、 chain/trigger シンタックス

BotはBotと会話するべきかどうか?

Mobbの正規表現解釈と、MatchDataの行方

Mobb製のBotになにか処理をさせたが、何も反応を返したくないときはどうするのか

Mobbにおけるマッチのパッシング

これらの機能の新規実装により、 chain/trigger, react_to_bot/include_myself, matched, say_nothing/silent, pass キーワードが新たにMobbに追加されました。

require 'mobb'

# chain/trigger
on 'hello' do
  chain 'chain1', 'chain2'
  'yo'
end

trigger 'chain1' do
  chain 'chain3'
  'yoyo'
end

trigger 'chain2' do
  'yoyoyo'
end

trigger 'chain3' do
  'yoyoyoyo'
end

# react_to_bot/include_myself
on /i'm (\w+)/, react_to_bot: true do |name|
  "hello #{name}"
end

on /yo (\w+)/, react_to_bot: true, include_myself: true do |name|
  "yo #{name}"
end

# matched
on /taks (?<task_name>\w+)/ do
  "act #{matched[:task_name]}"
end

# say_nothing/silent
on /do (\w+)/ do |task|
  say_nothing if task == 'slow_task'
  "act #{task}"
end

on 'bad!', silent: true do
  $stderr.puts("#{@env.user.name} is bad")
end

# pass
on 'yo' do
  pass
  'yo'
end

on 'yo' do
  'yoyo'
end

また、次の機能は予告していましたが0.5.0には入りませんでした。

MobbのLogger

Mobbのcronを秒単位で動かす

Mobbのマッチングにどれもヒットしなかった場合のフック

理由としては、実装そのものは概ね出来ているのですが、大きな機能追加が入りきちんとリリース前の検証が出来なかったからです。この機能は、検証が終わり次第リリースします。

Happy Mobb

25日間なんとかACを完走できました、これからもMobbをよろしくおねがいします。


Mobb/Repp Advent Calendar のネタが尽きたので、開発してて面倒な話をします

2018-12-24 23:53:57 +0900

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

Mobb開発とRepp開発で困る依存性

Mobbの開発には、大きく分けて2つのケースがあります。Mobb単体の機能追加や修正で済む場合と、Repp側にも機能追加や修正が必要な場合です。

Mobb単体で終わる場合にはなにも困ることは無いのですが、Repp側との連携をしなくてはいけない場合は面倒なことが発生します。Repp側の機能追加や修正を、開発中のMobbにどうやって適用するかです。場合によっては、ReppとMobbのコードを行ったり来たりしながら修正を行う場合もあります。


Mobb/Repp Advent Calendar のネタが尽きたので、対応したいハンドラとか書いときます

2018-12-23 23:39:17 +0900

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

ネタが切れたので対応したいハンドラとか書いておきます

本当に作るかどうかはわかりませんが

  • Discord

  • LINE

  • IRC

  • ChatWork

  • 他にも何かあれば教えてください


MobbアプリケーションをRack上で起動できるか?

2018-12-22 23:40:51 +0900

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

Mobbアプリケーション is Rackアプリケーション?

結論から言うと、動きません。

試しにこんなアプリを書いて起動してみました。

app.rb

require 'mobb/base'

class MyApp < Mobb::Base
  before do
    p @env
  end
end

config.ru

require './app'

run MyApp.new

起動

bundle exec rackup

MobbはRackのアプリケーションとほぼ互換なので、理屈の上では動きそうなものですが、動きませんでした。理由としては、Mobbがサービスからの情報を受け取ったときに処理するfilterやhandle_eventメソッドの中で使われている、process_eventメソッドにありました。

process_event の中身は次のようなメソッドです。

def process_event(pattern, conditions, block = nil, values = [])
  res = pattern.match?(@env.body)
  catch(:pass) do
    conditions.each { |c| throw :pass unless c.bind(self).call }

    case res
    when ::Mobb::Matcher::Matched
      block ? block[self, *(res.matched)] : yield(self, *(res.matched))
    when TrueClass
      block ? block[self] : yield(self)
    else
      nil
    end
  end
end

この中で、 @env.body を参照している箇所に問題がありました。Reppと違い、Rackの送ってくるenvオブジェクトには、bodyというメソッドが存在しないからです。

比較のために、Mobbが参考にしているSinatraのprocess_routeメソッドを見てみましょう。

def process_route(pattern, conditions, block = nil, values = [])
  route = @request.path_info
  route = '/' if route.empty? and not settings.empty_path_info?
  route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
  return unless params = pattern.params(route)

  params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
  force_encoding(params)
  original, @params = @params, @params.merge(params) if params.any?

  regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} )
  if regexp_exists
    captures           = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
    values            += captures
    @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty?
  else
    values += params.values.flatten
  end

  catch(:pass) do
    conditions.each { |c| throw :pass if c.bind(self).call == false }
    block ? block[self, values] : yield(self, values)
  end
rescue
  @env['sinatra.error.params'] = @params
  raise
ensure
  @params = original if original
end

最初に参照しているのが、 @request.path_info というメソッドで、これはどう考えてもHTTPに存在し、チャットボットに存在しない概念です。

残念ながら、MobbをRackで動かすという試みは、このRackとReppの微妙な世界観の違いで頓挫しました。

MobbをRackで動かせるべきか?

答えはNoです。MobbはSinatraを最大限にリスペクトしていますが、Sinatraの世界観とは違うものです。もちろん動かせれば面白いとは思いますが、MobbをRackに対応させる理由は全くありません。


Mobbを使った複数サービス間のゲートウェイを実現する方法

2018-12-21 23:32:58 +0900

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

サービス間ゲートウェイ

Slackの発言をIRCに転送したり、HTTPアクセスを受け取ってSlackに投稿するIncomming Webhook のようなものをMobbで書きたい場合にはどうすれば良いでしょうか?

Mobbのロジックに書く

一つ目の答えは、Mobbアプリケーションのロジックに、転送先のサービスのクライアントを記述して、入力をすべてそちらに飛ばし、ブロックの戻り値はnilにして入力元のサービスには何も返さない方法です。多分これは一番直感的で楽だと思います。

Reppハンドラを書く

もう一つの手段は、専用のReppハンドラを用意してしまうことです。デフォルトのハンドラでは、入力と出力のソースが同じため、容易にゲートウェイの動作をさせることはできません。しかし、入力と出力を別々にもつReppハンドラを記述することはできます。


IoTしてますか? 難しいですよね? でもお手軽にWxBeacon2を使って室内環境監視ダッシュボードとか作れますよ?

2018-12-21 01:00:22 +0900

このエントリは、 dwango Advent Calendar の二十一日目です

TL; DR

  • WxBeacon2を使って、簡易室内環境モニタを作ります

  • どこでも確認したいので、DBとフロントはWebに置きます

  • WxBeacon2 + Python + Fluentd + InfluxDB + Grafana + DockerCompose

お手軽に室内環境を監視したい

世の中IoTとかMakerとかいう言葉が流行り始めて数年が立ちましたが、Raspberry Piは買ったものの特に何を作るわけでもなく完全に腐らせている方は、私の他にも多いのではないでしょうか。電子工作の本とか買ってみて、いろいろなんかやろうとか思ってみはしたものの、本業や趣味のコーディングのほうが楽しくて、あまり真剣に向き合って来ませんでした。

ブレッドボードになんか刺したり、秋月や千石に行ってパーツを探したり、はんだ付けしたり、なんかかっこいいものを作って人にオォーって言われたかったりしたかった人生なんですが、まあそれはそれとして漠然と何かを作れる人に憧れがありました。そんなとき、技術書展5に出た際にブースを手伝ってくれた友人が、何やらカバンに奇妙な物体をつけているのを見ました。話を聞くと、どうやらそいつは気象系のセンサをいろいろ詰め込んだ便利なやつで、スマホアプリと接続して情報を見たり、BLEでPCとつなげたりもできるとのことでした。実際にスマホアプリを見てみると、気温や湿度、気圧に周囲の光量や騒音まで定期的に取得していました。それはなんだと聞くと、WxBeacon2だと言われました。

WxBeacon2

WxBeacon2とは、Weather News が販売している簡易気象観測器です。Weather News のアプリからポイントを貯めると貰えるらしいですが、まどろっこしいのでお金を払って買うことも出来ます。本体と消費税と送料込みで、5000円しないので、パパっと買ったほうが良いです。

WxBeacon2は、内容としてはオムロン製の2JCIE-BL01というIoTセンサのOEMです。2JCIEシリーズには、WxBeacon2と同型のバッグ型センサの他に、USBドングルの形をしたセンサや、PCB型のセンサも販売されています。ただ不思議なことに、日本や海外のどのセンサ通販サイトを見ても、USB型やBAG型は軒並み単価10000円を超え(しかもボリュームディスカウントも薄い)、PCB型に至っては売っているサイトすら見つけられません。そんな中、なぜかWeather News は半額以下の5000円未満でBAG型のOEM品を販売していて、ダントツで安く手に入れられるのです。なので、どうしてもUSB型が必要とかいう場合を除いて、Weather News で買うのが最も安く手に入れられる方法です。

そしてWxBeacon2もとい2JCIE-BL01は、なぜかGitHubに通信用のサンプルコードが置かれています。多分公式ではないと思うのですが(他にOmronのリポジトリも無いし、そもそもOrganizationもないので)、ここに載せられているサンプルプログラムだけで十分なので、こちらを参照します。

https://github.com/OmronMicroDevices/envsensor-observer-py

今回作りたいもの


Mobbのマッチングにどれもヒットしなかった場合のフック

2018-12-20 17:02:18 +0900

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

どれにもマッチしなかった

これは次のバージョンで実装される機能の話です。

Mobbはonキーワードでマッチングを登録し、サービスからの入力に対して一致チェックを行い、一致すればブロックを実行します。しかし、どこにもマッチしなかった入力は虚空に消えていきます。

Webフレームワークの場合、どこにもマッチしなかった場合には404が帰ります。そのため、404のページにはなにを表示するかといった設定ができます。しかし、チャットBotはWebのようにリクエストとレスポンスが対象的な世界ではないので、特にそういうものはありません。

しかしその一方で、どこにもマッチしなかった入力に対してなにか処理をしたいという需要はあると思います。それは、マッチングの対象ではない要素に対してなにかをしたい場合であったり(フィルタを使うという手もありますが)、すべての入力に対して一律なにか処理をしたりという場合です。

require 'mobb'

on 'Hi' do
  'Yo'
end

on_unregistered do
  # デバッグメッセージ
  puts "#{@env.body} は登録されているパターンにマッチしませんでした"
end

on_unregistered キーワードは、すべてのマッチング処理の最後にチェックされ、どこにも一致しているものがない場合は必ずこのブロックを実行するというものです。うえのbotでは、マッチングのデバッグを行うために、一致しなかったすべての処理を出力しています。


Mobbにおけるマッチのパッシング

2018-12-19 15:28:43 +0900

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

マッチのパッシング

この機能は次のバージョンにおいて実装される予定です。

次のようなBotを作成し、「hello Mobb」というメッセージを送った場合、得られる結果は「Yo」です。

require 'mobb'

on /hello (\w+)/ do |name|
  'Yo'
end

on 'hello Mobb' do
  'Survival of the fittest'
end

これは、Mobbのパターンマッチは定義した順番にチェックされるので、最初の /hello (\w+)/ がすべての hello で始まるメッセージを吸収してしまい、次に定義されている ‘hello Mobb’ にマッチすることは決してありません。

この例は非常に極端な例ですが、特定のケースにおいてマッチングをパスしたいことは発生すると思われます。そのため、次のバージョンではpassキーワードが導入されます。

require 'mobb'

on /hello (\w+)/ do |name|
  pass if name.start_with?('M')
  'Yo'
end

on 'hello Mobb' do
  'Survival of the fittest'
end

passキーワードは、呼び出されるとその場でブロックの評価を停止し、on/receive キーワードのマッチングを再開します。上のBotでは、nameがMで始まる場合は、 on /hello (\w+)/ のブロックを抜け、次の on ‘hello Mobb’ にマッチします。その結果、得られる返答は「Yo」ではなく「Survival of the fittest」になります。(もちろんこの例では、Mobb以外のMで始まる名前を送るとすべてのケースでなにも返答しなくなってしまいますが)

Next Mobb

年内リリースがんばります