LINE Messaging APIでオウム返しbotを作りながらRuby on Railsをかじる

前回の記事でherokuを導入した目的は、LINE botを作ってみようと思ったからです。
さっそく下記の記事を参照して、もっとも基本的なオウム返しbotを作成してみました。
qiita.com
これを教材にRuby on Railsの言語仕様やLINE Messaging APIの仕組みについて学んだことを書き残しておきます。
コードは上記記事からの切り貼りなので、単純に作り進めたい人は元記事を参照してください。

はじめる前に

LINE Messaging APIには前身となるLINE botと呼ばれていたバージョンがあり、使い方がかなり違います*1
情報を探すときにはどちらのバージョンを使っている記事なのか注意しましょう。

routes.rbとController

RailsにおけるMVC(モデル/ビュー/コントローラ) - Ruby on Rails入門
サーバーにURLのリクエストが来たとき、何を返すかをroutes.rbに記述する。
今回の場合は(コメント部筆者追記)

Rails.application.routes.draw do
  post '/callback' => 'linebot#callback'   #post '/URI' => 'コントローラ名#メソッド'
end

ここでlinebotはapp/controllers/linebot_controller.rbを暗黙的に意味している*2
コントローラを生成するときは、Terminalから

$rails generate controller linebot

を叩く*3
これで、/callbackのリクエストはlinebot_controller.rb内の

class LinebotController < ApplicationController
…中略…
  def callback

と紐つけられた。

linebot#client

    def client
      @client ||= Line::Bot::Client.new { |config|
        config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
        config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
      }
    end

def callback ~ endがメソッドの定義範囲。
@で始まるのはインスタンス変数(Javaでいうフィールド)*4

a ||= xxx

この記法は自己代入。aがFalseか未定義ならxxxを代入。

newでインスタンスを生成しているっぽいが、引数は|config|ととらえて、引数を渡す前の処理をブロックの部分に追記できると理解するのが良さそう*5

オブジェクト.メソッド{|ブロックパラメータ|
  # ブロック
}

ブロックパラメータというらしい。

戻り値がないじゃん、と思うのだが、Rubyは最後に評価した値が戻るらしい。つまりここでの戻り値は@client*6

linebot#callback

      body = request.body.read

突如出てくるrequestオブジェクトは親クラスであるActionControllerで定義されていて、ブラウザからのリクエストに含まれる情報を持っているオブジェクトを取得できる*7

      signature = request.env['HTTP_X_LINE_SIGNATURE']
      unless client.validate_signature(body, signature)
        error 400 do 'Bad Request' end
      end

request.envは指定されたヘッダ情報を返す。request.headersと同義?*8ここではlinebot#clientで意図したLine Botからのリクエストかどうかを判定している。
unlessはif notと同義。

      events = client.parse_events_from(body)
  
      events.each { |event|
        case event
        when Line::Bot::Event::Message
          case event.type
          when Line::Bot::Event::MessageType::Text
            message = {
              type: 'text',
              text: event.message['text']
            }
            client.reply_message(event['replyToken'], message)
          end
        end
      }

parse_events_fromでbodyに含まれているイベントのコレクションが返ってくるので、ひとつずつ調べて(each)、テキストメッセージの場合のみメッセージを作成する。
このときeventsの要素がeach内で変数eventに展開されているのだが、前述のブロックパラメータの挙動とはちょっと違っている。こちらはイテレータと呼ばれているらしいが、記法が同じだけで実装が違うのか、やはり同じ実装によるのか?
なお、この分岐はifで良いような気がするが、別のイベントを拾いたくなったときのためにcaseになっているのだろう。

messageの右辺はハッシュの記法で、キー:値。

{ a:"aaa", b:"bbb" }

感想

なんだか暗黙のルールや記号が多くて読みにくい…文章的に書けそうな気はするけども。
次はデータベースを使ってみよう。