CodeIQ MAGAZINECodeIQ MAGAZINE

世界初!?Opal製Hubotプラグインで解答する「RubyでHubot!?」問題解説 #Ruby #Opal #JavaScript

2015.08.18 Category:CodeIQ問題解説・リーダーボード Tag: , , , ,

  • 19
  • このエントリーをはてなブックマークに追加
cielavenir-300x300

お待たせしました!
Cielさんによる「RubyでHubot!?」問題の解説記事です!
今回は相当難易度の高い問題だったようで、挑戦された方は猛者ばかり!はたしてどんな解答だったのでしょうか。
というわけで、解説をどうぞ!
by CodeIQ運営事務局

はじめに

2015年7月3日~2015年7月20日の期間、「RubyでHubot!?」というRuby/Opal/JavaScript複合問題を出題し、5名の方が挑戦してくださいました。

環境構築が必要なことに加え、ネット上に直接的な情報が皆無(おそらくこの問題の解答が世界初のOpal製Hubotプラグイン)のため、知識を総動員して解かなければならなかったことが人数の低さの原因でしょうか。
OpalでUserScriptを書く方法をヒントとして提示しました
( http://qiita.com/cielavenir/items/ffa86fc70bce8ae63db6 )が、少し遅かった感があります。

問題のおさらい

まずは、問題のおさらいから。今回の問題は以下の内容でした。

前説

Hubotは最もよく使われているチャットボットフレームワークですが、プラグインはJavaScript(もしくはAltJS)を使って作成する必要があります。
CoffeeScriptはRubyやPythonに似た言語ですが、細かい部分でかなり相違があります。
そのため、RubyでHubotを扱いたい場合、Rubotyを使うことになるのが通常です。
しかし!RubyでHubotを取り扱う方法はあります!
そうです、今話題のOpalを使うのです。この問題ではそれを実践していただきます。

設問

Hubotプラグインを作成して下さい。ただし、以下の要件を満たして下さい。

  • 「CodeIQ」と応答する「/codeiq/i」インターフェースを含めること
  • Ruby(Opal)で作成すること

以上ができていれば最高評価となりますが、他にもインターフェースを付けて頂いて構いません。
面白い機能であれば解説記事で紹介させていただきます(紹介不要な場合はコメントに明記して下さい)。

提出方法

まず、(「opal -c script.rb >script.js」で)コンパイルしたJavaScriptで動作を確認して下さい。
その後、コンパイル前のRubyスクリプトをアップロードしてください。

解答

https://github.com/cielavenir/codeiq_problems/tree/master/q1605
私の答案はIntercal生成のHubot版Ruboty版を複合させたものです。

解答例

JavaScriptによる充足解は以下のとおりです。

module.exports=function(robot){
    robot.respond(/codeiq/i,function(msg){msg.send('CodeIQ');});
}

これをOpalに直せば正解となります。
やり方は複数ありますが、一番簡単なのは、nativeモジュールを使うことでしょうか。
moduleはJavaScript側のグローバル変数なのでバッククオートで読み込み、exportsに代入するのはlambdaを使えばよいでしょう。

require 'native'
Native(`module`).exports = ->(robot){
    Native(robot).respond(/codeiq/i){|msg|Native(msg).send('CodeIQ')}
}

解答紹介

みけCAT様

# tested with Opal v0.7.2 and Hubot 2.8.2

# Hubot wrapper class
class Hubot
  # Hubot Response object wrapper class
  class Res
    def initialize(native_res)
      @res = native_res
    end

    def send(str)
      `self.res.send(str)`
    end

    def reply(str)
      `self.res.reply(str)`
    end

    def emote(str)
      `self.res.emote(str)`
    end

    def match
      `self.res.match`
    end
  end

  def initialize(native_robot)
    @robot = native_robot
  end

  def respond(expr, &block)
    %x{
      self.robot.respond(expr, function(res) {
        block.$call(Opal.Hubot.Res.$new(res));
      })
    }
  end

  def hear(expr, &block)
    %x{
      self.robot.hear(expr, function(res) {
        block.$call(Opal.Hubot.Res.$new(res));
      })
    }
  end
end

def exports(robot)
  robot.respond(/codeiq/i) do |res|
    res.reply 'CodeIQ'
  end

  robot.respond(/paiza/i) do |res|
    res.reply 'paiza'
  end

  robot.respond(/checkio/i) do |res|
    res.reply 'CheckiO'
  end

  robot.respond(/recommend/i) do |res|
    res.reply %w(CodeIQ paiza CheckiO).sample
  end

  # test for robot.hear and res.emote
  robot.hear(/sublime/i) do |res|
    res.emote 'Sublime Text!? Do you use it!?'
  end

  # test for res.send
  robot.hear(/asumi/i) do |res|
    res.send 'Yes! Asumisu!!'
  end

  # test for res.match
  robot.respond(/add +([0-9]+) +([0-9]+)/i) do |res|
    a = res.match[1]
    b = res.match[2]
    res.reply((a.to_i + b.to_i).to_s)
  end

  # something like the famous Flash
  robot.respond(/you +are +an +idiot/i) do |res|
    10.times { res.send 'You are an idiot! Ahahahahahaha! Hahahahaha!' }
  end

  # fashion show
  robot.respond(/fashion +show/i) do |res|
    res.send(('Brainfuck'.chars.map { |c| [c, c, '*'].sample }).join '')
  end
end

# register "exports" to the Hubot system
%x{
  module.exports = function(robot) {
    Opal.Object.$$proto.$exports(Opal.Hubot.$new(robot));
  }
}
  • 今回、全挑戦者中唯一nativeモジュールを使っていない解答です。
  • 私の答案でNative(`module`).exportsNative(robot).respondに相当する部分は、%x(バッククオート)内で直接Opalのオブジェクトを拾う形で実装されています。Opalがどのようにコンパイルされるかについて理解が見られる(私自身はよくわかっていないのですが)解答でした。
  • Hubotへの応答はsendだけでなくreplyやemoteも使える点、勉強になりました。

せきゅあ様

# coding: utf-8

require 'opal'
require 'native'

def get_menu(day)
  yday = day.yday
  title = ["献立", "主菜", "副菜1", "副菜2", "コメント"]

  # 献立一覧
  # 引用サイト:毎日おうちでご飯
  # URL:  http://www.teku.jp/kondate/200807w1.php
  menus =
    [["ミックスの日", "マカロニグラタン", "野菜サラダ", "コーンスープ", "エビは殻をむいて背わた,腹わたを取る。玉葱とマッシュルームは薄切りにして、全てフライパンで炒めて塩コショウで味付けします。マカロニは茹でます。耐熱皿にバターを塗り具材全てとホワイトソース,バター,チーズを乗せてオーブンでこんがり焼きます。\n\nありもの野菜でサラダにします。\n\nクリームコーン缶と同じ分量の水で火にかけ、シチューのルー少し、最後に牛乳を入れて温めて出来上がり。"],
     ["野菜の日", "ぎょうざ", "もやしナムル", "かに玉", "キャベツ,ニラ,豚ひき肉,卵,中華スープのもと,醤油,ごま油,塩,おろしニンニクを手でよくこねます。餃子の皮にたっぷりめに包んで焼く。\n\nしょうゆ・ごま油・いりごま・にんにく・豆板醤で茹でたもやしをあえます。\n\n卵に中華スープの素、塩、薄口醤油、砂糖を混ぜておきます。カニ(カニカマでも)筍細切りを炒め卵でとじます。スープ、しょうが汁、塩、コショウ、しょう油、砂糖を合わせて煮立て、水溶きかたくり粉でとろみをつけたものをかけて出来上がり。"],
     ["牛肉の日", "ハヤシライス", "水菜とえのきの炒め物", "ポテトサラダ", "牛薄切り、玉葱薄切り、マッシュルームなどをバターで炒め、ハヤシライスの素で煮込む。(素を使わないときは、赤ワイン・オイスターソース・お好みソース・ケチャップ(全て同量)で煮込み、最後に同量の生クリームでさっと煮る)\n\n水菜とえのきをいためて、塩コショウで味付けする。\n\nジャガイモを茹でてつぶし、玉葱の薄切り、ハム等とマヨネーズ、塩少々であえて出来上がり。"],
     ["鶏肉の日", "鳥からあげ", "なすの揚げびたし", "ハヤシライスの残りでマカロニサラダ", "1口大の鳥もも肉にからあげの素(or しょうが,醤油,酒,みりん,片栗粉)をまぶして揚げます。\n\nナスは切って、素揚げします。だし汁、醤油、砂糖を煮立てたところに漬け込みます。\n\nマカロニを茹でて、ハヤシライスのソースの残りであえて出来上がり。"],
     ["魚の日", "煮魚", "豚肉と大根の炒め煮", "きゅうりとシラスの酢の物", "魚が浸るくらいの水に醤油、酒、しょうがを煮立て、魚を入れて落し蓋をして煮る。\n\n大根は1口大に切って、豚うす切りと炒める。ひたひたの水にしょうが、酒、みりん、醤油、砂糖を入れ煮込みます。\n\nキュウリは薄く切って、塩で水切りする。酢、砂糖、しょうゆとシラスを混ぜて出来上がり。"],
     ["豚肉の日", "ポークソテー&人参とジャガイモのバター炒め", "ニラとベーコンの卵とじ", "なすゴマポンかけ", "玉葱のすりおろし、醤油、みりんを混ぜてレンジでチン。豚ロースは軽く筋切りして塩コショウ、小麦粉まぶして焼きます。玉葱ソースをかける。人参とジャガイモは茹でておく。バター、塩コショウで炒める。\n\nニラとベーコンを2cm程度の長さで切る。フライパンで炒めて鶏がらスープ、塩コショウで味をつけ、とき卵でとじる。\n\n茄子は薄く切ってお皿に並べてラップをしてレンジでチン。大根おろし、ごま油、ポン酢をかけて出来上がり。お好みで七味をかけても。"]]

  wday = yday % menus.size
  day.strftime("%m月%d日") + "\n" + title.zip(menus[wday]).map{|t, m| t + ":" + m}.join("\n")
end

module_exports = ->(robot) {
  Native(`robot`).respond(/codeiq/i, ->(msg) {Native(`msg`).send("CodeIQ")})

  Native(`robot`).respond(/menu/i, ->(msg) {Native(`msg`).send(get_menu(Time.now))})
}

%x{
  module.exports = #{module_exports};
}
  • 日替わりで献立を表示します。
  • 表示の仕方も、zipやmapといった、Rubyならではのコンパクトさを活かして記述しています。

angel様

require 'native'
Native(`module`).exports=->r{
  robj=Native(r)
  # required
  #   respond "CodeIQ" for "codeiq" ( case insensitive )
  robj.hear(
    Native(`/codeiq/i`),
    ->m{
      Native(m).send("CodeIQ")
    }
  )
  # optional
  #   responde the version of hubot ( default ) or node or opal or ruby
  #   for "version" or "version of XX"
  version_responder=->m,what{
    response=case what
      when "hubot"
        robj.version
      when "node"
        Native(`process`).version
      when "opal"
        Native(`Opal`)["RUBY_ENGINE_VERSION"]
      when "ruby"
        Native(`Opal`)["RUBY_VERSION"]
      else
        "unknown"
    end
    Native(m).send(response)
  }
  robj.hear(
    Native(`/version$/`),
    ->m{ version_responder[m,"hubot"] }
  )
  robj.hear(
    Native(`/version of (.*)/`),
    ->m{ version_responder[m,Native(m).match[1]] }
  )
}
  • Hubot/Node/Opal/Ruby(準拠)のバージョンを表示します。互換性チェックとかに重宝しそうです。
  • Opal 0.7.2はRuby 2.1.1準拠のようです(拙環境のRubyは2.0なので、ちゃんと準拠バージョンが表示されていることがわかります)

CodeIQ運営事務局より

Cielさん、解説ありがとうございました!
Cielさんの今後の問題にご期待ください!!

  • 19
  • このエントリーをはてなブックマークに追加

■関連記事

【謎解きプログラム】乱数で発生する数値は?【組み合わせ】解答と解説... 【謎解きプログラム】乱数で発生する数値は?【組み合わせ】 本問題は、表題のテーマで、プログラムにちなんだ謎を解くというものでした。 それでは以下、各問題とその解答を見ていきましょう。 問題のオープニング ある日、出社すると、あなたのPCのログイン画面に、謎の挑戦状が表示されていた。 「24...
「放物線とマス目の関係」問題の解答と解説... table.nabe{ margin-left:30px; } .nabefloat{ float:right; } table.nabe td, table.nabe th{ padding:3px; } table.nabe th{ ...
【謎解きプログラム】データをバイナリで見てみよう【バイナリ】解答と解説... 【謎解きプログラム】データをバイナリで見てみよう【バイナリ】 本問題は、表題のテーマで、プログラムにちなんだ謎を解くというものでした。 それでは以下、各問題とその解答を見ていきましょう。 問題のオープニング ある日、出社すると、あなたのPCのログイン画面に、謎の挑戦状が表示されていた。 「...
【謎解きプログラム】データベースを扱ってみよう【SQLite】解答と解説... 【謎解きプログラム】データベースを扱ってみよう【SQLite】 本問題は、表題のテーマで、プログラムにちなんだ謎を解くというものでした。 それでは以下、各問題とその解答を見ていきましょう。 問題のオープニング ある日、出社すると、あなたのPCのログイン画面に、謎の挑戦状が表示されていた。 ...
【謎解きプログラム】弾幕の軌跡を作ってみよう【描画】解答と解説... 【謎解きプログラム】弾幕の軌跡を作ってみよう【描画】 本問題は、表題のテーマで、プログラムにちなんだ謎を解くというものでした。 それでは以下、各問題とその解答を見ていきましょう。 問題のオープニング ある日、出社すると、あなたのPCのログイン画面に、謎の挑戦状が表示されていた。 「24時間...
【謎解きプログラム】フィルターを使ってみよう【SVG】解答と解説... 【謎解きプログラム】フィルターを使ってみよう【SVG】 本問題は、表題のテーマで、プログラムにちなんだ謎を解くというものでした。 それでは以下、各問題とその解答を見ていきましょう。 問題のオープニング ある日、出社すると、あなたのPCのログイン画面に、謎の挑戦状が表示されていた。 「24時...

今週のPickUPレポート

新着記事

週間ランキング

CodeIQとは

CodeIQ(コードアイキュー)とは、自分の実力を知りたいITエンジニア向けの、実務スキル評価サービスです。

CodeIQご利用にあたって
関連サイト
codeiq

リクルートグループサイトへ