2 flat logo on transparent 256
2018-07-10

Ruby On Railsで外部APIを利用して、簡単なアプリケーションを作成する

Event 0213123

今回はRuby On Railsで外部APIを利用して、イベント検索機能を実装する方法を公開します。

基本的なJsonAPIの取得方法から検索フォームから値を加工する部分までをサンプルコードを用いて解説していきます。

利用する外部API

株式会社ビープラウドが開発・運営しているITエンジニアのイベント検索サービスのAPIを利用します。

connpassの詳細

connpass APIの詳細

アプリケーション概要

Ruby On Railsのアプリケーションから、API経由でイベント情報をgetし、イベント結果を表示します。

検索フォームでキーワードと日付を選択すれば検索を実行できるようにします。

動作は こちらから確認できます。

全体処理の概要

処理のサンプルコードは下記3つになります。

1:ライブラリのサンプルコード

2:Controllerのサンプルコード

3:フォームのサンプルコード

※若干クソコードの匂いがする部分があるので、その点はご了承いただければと思います。

ライブラリファイルのサンプルコード解説

今回はAPIでイベント情報をgetするだけなので、Modelなどは特に利用しません。その為本API用のライブラリを作成します。

ファイルパス:lib/connpass_client.rb

class ConnpassClient

  def search_area(form_words, start_date, end_date, retry_count = 10)
    raise ArgumentError, 'too many HTTP redirects' if retry_count == 0

    uri = Addressable::URI.parse("https://connpass.com/api/v1/event/?keyword=#{form_words}&ym=#{start_date},#{end_date}&count=30&order=3")

    begin
      response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
        http.open_timeout = 5
        http.read_timeout = 10
        http.get(uri.request_uri)
      end

      case response
        when Net::HTTPSuccess
          json = JSON.parse(response.body)
          if json['results_returned'] == 0
            nil
          else
            json
          end

        when Net::HTTPRedirection
          location = response['location']
          Rails.logger.error(warn "redirected to #{location}")
          search_area(form_words, start_date, end_date, retry_count - 1)
        else
          Rails.logger.error([uri.to_s, response.value].join(" : "))
      end

    rescue => e
      Rails.logger.error(e.message)
      raise e
    end
  end
end

基本コードはこの記事を参考にしました

またRuby On Rails5の場合Libファイルの読み込みを以下に変更する必要があります。

#config/application.rb

config.paths.add 'lib', eager_load: true

詳細はRails5でlib配下のクラス読み込みがNameErrorになる

search_areaメソッドの引数でフォームで入力された、検索キーワードとイベントの開始 終了日付を渡します。

retry_countはリダイレクト処理が発生した場合に何回その処理を行うかの上限となります。

class ConnpassClient
  def search_area(form_words, start_date, end_date, retry_count = 10)
 #省略
end

Net::HTTPSuccessはレスポンスコードが200した場合の処理です。

connpassのイベント結果はjsonで返却されます。 検索結果が0の場合はnilを返し、検索結果が存在すれば そのままjsonをパースした結果を返します。

 case response
        when Net::HTTPSuccess
          json = JSON.parse(response.body)
          if json['results_returned'] == 0
            nil
          else
            json
          end

本当は以下のようにしたかったのですが、以下の場合だと なぜかnilを返さなかったので、if elseで設定しています。

json = JSON.parse(response.body)
  if json['results_returned'] == 0
    return json
end

以下はレスポンスが300台の場合の処理です location = response['location']でリダイレクトのURLを取得し、 ロガーへリダイレクトのURLを記録し、再度メソッドを回帰的に呼び出します。呼び出す際にカウントを減らして、カウントがなくなったらエラーとなります

        when Net::HTTPRedirection
          location = response['location']
          Rails.logger.error(warn "redirected to #{location}")
          search_area(form_words, start_date, end_date, retry_count - 1)
        else
          Rails.logger.error([uri.to_s, response.value].join(" : "))

なおエラーの場合はロガーへ出力していますが、 ローカルでの検証の場合はputsで問題ないかと思います。

Controllerのサンプルコード解説

以下はControllerのサンプルコードです

class ConnpassClientsController < ApplicationController

  def index
    if params[:start_date]
      start_year = params[:start_date][:year].to_i
      start_month = params[:start_date][:month].to_i
    else
      start_year = Time.now.year.to_i
      start_month = Time.now.month.to_i
    end

    if params[:end_date]
      end_year = params[:end_date][:year].to_i
      end_month = params[:end_date][:month].to_i
    else
      end_year = Time.now.year.to_i
      end_month = Time.now.month.to_i
    end

    @start_date = Time.mktime(start_year,start_month)
    @end_date = Time.mktime(end_year,end_month)

    start_date = @start_date.strftime("%Y%m")
    end_date = @end_date.strftime("%Y%m")

    @form_words = params[:form_words]

    if params[:form_words].blank?
      params[:form_words] = ","
    else
      params[:form_words] = params[:form_words].gsub(/( | )+/, ",")
    end

    #ライブラリをnewしてメソッドを呼び出す
    @result = ConnpassClient.new.search_area(params[:form_words], start_date, end_date)
  end
end

以下Controllerのparams部分は選択フォームでイベント開始月と終了月を選択された場合は、その値を選択されていない場合は 現在日時の年月を設定します。

  def index
    if params[:start_date]
      start_year = params[:start_date][:year].to_i
      start_month = params[:start_date][:month].to_i
    else
      start_year = Time.now.year.to_i
      start_month = Time.now.month.to_i
    end

    if params[:end_date]
      end_year = params[:end_date][:year].to_i
      end_month = params[:end_date][:month].to_i
    else
      end_year = Time.now.year.to_i
      end_month = Time.now.month.to_i
    end

    #paramsの値をTimeクラスへ変換する
    @start_date = Time.mktime(start_year,start_month)
    @end_date = Time.mktime(end_year,end_month)

    #Timeクラスから年月へ変換
    start_date = @start_date.strftime("%Y%m")
    end_date = @end_date.strftime("%Y%m")

@form_wordsはgetした後に、テキスト入力欄で入力された内容を表示する際に利用します。

 #中略
    @form_words = params[:form_words]

    if params[:form_words].blank?
      #テキスト入力欄へ未入力の場合カンマを設定
      params[:form_words] = ","
    else
      #入力された値の半角 全角スペースを,へ変換
      params[:form_words] = params[:form_words].gsub(/( | )+/, ",")
    end

connpassの仕様でキーワードでor検索をする場合、空の値でgetするとbadリクエストになるため、テキスト欄が空の場合はカンマを設定するようにしています。

この部分は変更の余地があるかもしれません。

フォームのサンプルコード解説

Modelを利用しないフォームなので、form_tagを利用しています。

※Ruby On Rails5の場合はformtagも、formfor もform_withに統一されています。

- start_time = @start_date || Time.now
- end_time = @end_date || Time.now

= form_tag connpass_clients_path, :method => 'get' do
  div = text_field_tag :form_words, @form_words, placeholder: '東京都 プログラミング'

  div = select_date(start_time, {:use_month_numbers => true, discard_day: true, prefix: :start_date, :start_year => Time.now.year - 1, :end_year => Time.now.year + 1})
  div = select_date(end_time, {:use_month_numbers => true, discard_day: true, prefix: :end_date, :start_year => Time.now.year - 1, :end_year => Time.now.year + 1})
  = submit_tag "Search"

  - if @result.present?
    - @result['events'].each_with_index do |event, i|
      - start_time_to_time = Time.parse(event['started_at'])
      - end_time_to_time = Time.parse(event['ended_at'])

      .m-t-b-30
        .articleEntry-Title
          a.event-link href= event['event_url'] target="_blank"
            = event['title']
        .articleContent-event-dates = event['catch']
        .flex.articleContent-event-dates
          div = t('text.date_and_time')
          div = ":"
          div = l(start_time_to_time, format: :long)
          div = ""
          div = l(end_time_to_time, format: :short)
        .flex.articleContent-event-dates
          div = event['address']
  - else
    div = "検索結果がありません"

イベント情報の戻り値はjsonの['events']内に設定されるので、 配列で回して、イベント情報を表示します。

日付情報はTimeクラスへ変換するためにパースします。

- @result['events'].each_with_index do |event, i|
      - start_time_to_time = Time.parse(event['started_at'])
      - end_time_to_time = Time.parse(event['ended_at'])

      #以下は日付の情報をi18へ変換しています。
      div = l(start_time_to_time, format: :long)

日付情報をi18へ変換する際は以下の記事を御覧ください

Ruby On Railsでi18を使ってDate型の項目を年月日形式に変換する

以上になります。

その他関連記事

Ruby On RailsでRedisを利用してランキング機能を実装する

Ruby On Railsでredcarpetを利用し、シンタックスハイライトに対応したブログ機能を実装する

チャットボット Hubot(ヒューボット)とSlackを連携してみる

Ruby On Railsとtwitterの投稿機能を連携する

Ruby On Rails4で簡易ブログを作成する1 | VIew Controller ルートを作成する

Ruby On Rails でページネーションを設定する

前の記事
次の記事