駅すぱあとWebサービスとRuby/Gtkを組み合わせて回数券情報を表示するアプリを作ってみる

[ヴァル研究所 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/val) 22日目の記事です。
今日は[駅すぱあとWebサービス](http://docs.ekispert.com/v1/)の[回数券一覧](http://docs.ekispert.com/v1/api/coupon/list.html)と[Ruby/Gtk](https://ruby-gnome2.osdn.jp/ja/)を組み合わせて、簡単なGUIアプリを作成する手順を紹介しようと思います。

## 回数券一覧API

[回数券一覧](http://docs.ekispert.com/v1/api/coupon/list.html)は「[駅すぱあと](https://ekiworld.net/service/package/about.html)」の保持する回数券の一覧を取得するWebAPIになります。

使い方としては、[回数券一覧](http://docs.ekispert.com/v1/api/coupon/list.html)APIで回数券の名称を取得し、金額や有効期限といった回数券の詳細は[回数券詳細](http://docs.ekispert.com/v1/api/coupon/detail.html)APIで取得するという形になります。

 * [回数券一覧](http://docs.ekispert.com/v1/api/coupon/list.html)(/coupon/list)
 * [回数券詳細](http://docs.ekispert.com/v1/api/coupon/detail.html)(/coupon/detail)

この2つのAPIは、[3日目の記事](https://qiita.com/valsitoh/items/966ead6c0b27fad7b27a)で紹介した[Amazon SaaSストア](http://www.amazon.co.jp/saas/)で購入できる[駅すぱあとWebサービス](http://docs.ekispert.com/v1/)で利用可能なAPIとして提供されています。

## Ruby/Gtk

[Ruby/Gtk](https://ruby-gnome2.osdn.jp/ja/)は[Ruby](https://www.ruby-lang.org/ja/)から[Gtk](https://ja.wikipedia.org/wiki/GTK%2B)を利用するための拡張ライブラリです。Linuxだけでなく、macOSやWindowsでも動作するため、今回紹介するサンプルプログラムもmacOSやWindowsで動作させられるかと思います。

## 回数券一覧のサンプルを作成する

今回のサンプルプログラムはGitHubで公開しています。

 * [ValLaboratory/advcal](https://github.com/ValLaboratory/advcal)
   * [ValLaboratory/advcal/2018/ruby/ews/sample/coupon_list](https://github.com/ValLaboratory/advcal/tree/master/2018/ruby/ews/sample/coupon_list)

動作イメージは以下のような感じです。

![sample7.gif](https://qiita-image-store.s3.amazonaws.com/0/152217/c3c79194-8d60-1c33-0dc4-d62a18a4a7e0.gif)

### WebAPIのレスポンスを把握する

[Ruby/Gtk](https://ruby-gnome2.osdn.jp/ja/)でアプリを作り込んでゆく前に、WebAPIのレスポンスを把握しておきます。

[回数券一覧](http://docs.ekispert.com/v1/api/coupon/list.html)(/coupon/list)のレスポンスは以下のようになっています。 `$.ResultSet.Coupon[].Name` が回数券の名称になります。

```
http://api.ekispert.jp/v1/json/coupon/list?key=アクセスキーを入力してください
```

```json
{
  "ResultSet": {
    "Coupon": [
      {
        "Name": "新幹線回数券(グリーン車)"
      },
      ...
      {
        "Name": "こまち４枚回数券(指定席)"
      }
    ],
```

ここで取得した回数券の名称を[回数券詳細](http://docs.ekispert.com/v1/api/coupon/detail.html)(/coupon/detail)のパラメータに指定することで、回数券の詳細情報を取得できます。例えば、「新幹線回数券(グリーン車)」という回数券の詳細情報を取得する場合は以下のようなリクエストになります。

```
http://api.ekispert.jp/v1/json/coupon/detail?key=アクセスキーを入力してください&name=新幹線回数券(グリーン車)
```

取得できるレスポンスは以下です。

```json
{
  "ResultSet": {
    "Coupon": [
      {
        "Detail": {
          "Count": "6",
          "Price": {
            "Amount": "47340"
          },
          "ValidPeriod": "3",
          "Direction": "Both"
        },
        "Name": "新幹線回数券(グリーン車):東京都区内:静岡"
      },
      ...
```

### Rubyで回数券情報を取得する

[回数券詳細](http://docs.ekispert.com/v1/api/coupon/detail.html)(/coupon/detail)のレスポンスを取得してRubyで利用できる形にするサンプルは以下になります。

```ruby
#!/usr/bin/env ruby

require 'open-uri'
require 'json'
require 'uri'

ACCESS_KEY = '駅すぱあとWebサービスのアクセスキー'

coupon_name = URI.escape('東北新幹線回数券(自由席)')
url = "http://api.ekispert.jp/v1/json/coupon/detail?key=#{ACCESS_KEY}&name=#{coupon_name}"
JSON.parse(open(url).read)['ResultSet']['Coupon'].each do |item|
  next if item.class != Hash
  str = <<_EOF
    定期券名称： #{item['Name']}
    回数券の枚数： #{item['Detail']['Count']}
    合計金額： #{item['Detail']['Price']['Amount']}
    有効期限(月)： #{item['Detail']['ValidPeriod']}
    - - - - - - - - - -
_EOF
  puts str
end
```

実行結果は以下になります。これで[Ruby](https://www.ruby-lang.org/ja/)と[駅すぱあとWebサービス](http://docs.ekispert.com/v1/)を使用して[回数券一覧](http://docs.ekispert.com/v1/api/coupon/list.html)のデータを取得するところまでできました。

```sh 
$ ./sample.rb
    定期券名称： 東北新幹線回数券(自由席):東京山手線内:小山
    回数券の枚数： 6
    合計金額： 17880
    有効期限(月)： 3
    - - - - - - - - - -
    定期券名称： 東北新幹線回数券(自由席):大宮:宇都宮
    回数券の枚数： 6
    合計金額： 16020
    有効期限(月)： 3
    - - - - - - - - - -
```

### 今回使用するRuby/GtkのGUI部品

あとは[Ruby/Gtk](https://ruby-gnome2.osdn.jp/ja/)の使い方を把握すればアプリの作成まで漕ぎ着けそうです。
今回は `Gtk::Window` と `Gtk::ComboBox` 、 `Gtk::ListStore` と `Gtk::TreeView` の使い方を見てみましょう。

#### Gtk::Window 

まずは基本的な `Gtk::Window` の使い方です。単にアプリケーションウインドウを表示するだけなので難しいことはありません。

```ruby
require 'gtk2'

module EWS
  class SampleApp
    def initialize
      window = Gtk::Window.new
      window.set_default_size(320, 240)
      window.show_all
      window.signal_connect("destroy") { Gtk.main_quit }

      Gtk.main
    end
  end
end

if $PROGRAM_NAME == __FILE__
  EWS::SampleApp.new
end
```

実行すると以下のような表示になります。
![Screenshot at 2018-12-23 03-49-23.png](https://qiita-image-store.s3.amazonaws.com/0/152217/aa2ab269-0b70-d055-6697-42a313414b53.png)

#### Gtk::ComboBox

続いて `Gtk::ComboBox` です。文字列データを順次コンボボックスに設定するだけでOKです。また、 `Gtk::ComboBox.signal_connect("changed")` で選択された文字列を取得できます。
今回は回数券の名称を文字列データに使い、コンボボックスのデータが選択された際に回数券の詳細情報を取得するという振る舞いにすれば良さそうです。

```ruby
require 'gtk2'

module EWS
  class SampleApp
    def initialize
      window = Gtk::Window.new

      combo = Gtk::ComboBox.new
      %w[apple grape orange banana].each do |label|
        combo.append_text(label)
      end
      combo.signal_connect("changed") do |widget|
        puts(widget.active_text)
      end

      window.add(combo)
      window.show_all
      window.signal_connect("destroy") { Gtk.main_quit }

      Gtk.main
    end
  end
end

if $PROGRAM_NAME == __FILE__
  EWS::SampleApp.new
end
```

実行すると以下のような表示になります。
![Screenshot at 2018-12-23 03-50-28.png](https://qiita-image-store.s3.amazonaws.com/0/152217/33d4ef26-7644-1d4c-1f13-b122646630af.png)

#### Gtk::ListStore,Gtk::TreeView

最後はツリービュー(今回は表形式)です。これはちょっと複雑なサンプルになります。 `Gtk::ListStore` でツリービューに入れたいデータの構造を用意し、 `Gtk::TreeView` に表の列に相当するデータを追加する形になります。

```ruby
require 'gtk2'

module EWS
  class SampleApp
    COLUMN_ENGLISH, COLUMN_JAPANESE = *(0..1).to_a

    def initialize
      @store = Gtk::ListStore.new(String, String)

      [
        %w[ apple  りんご   ],
        %w[ grape  ぶどう   ],
        %w[ banana ばなな   ],
        %w[ orange おれんじ ],
      ].freeze.each do |item|
        iter = @store.append
        item.each_with_index do |value, index|
          iter[index] = value
        end
      end

      treeview = Gtk::TreeView.new(@store)
      treeview.rules_hint = true
      treeview.search_column = COLUMN_ENGLISH

      add_columns(treeview, '英語',   COLUMN_ENGLISH)
      add_columns(treeview, '日本語', COLUMN_JAPANESE)

      sw = Gtk::ScrolledWindow.new(nil, nil)
      sw.shadow_type = Gtk::SHADOW_ETCHED_IN
      sw.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC)
      sw.add(treeview)

      window = Gtk::Window.new
      sw = Gtk::ScrolledWindow.new(nil, nil)
      sw.shadow_type = Gtk::SHADOW_ETCHED_IN
      sw.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC)
      sw.add(treeview)

      window = Gtk::Window.new
      window.add(sw)
      window.set_default_size(320, 240)
      window.show_all
      window.signal_connect("destroy") { Gtk.main_quit }

      Gtk.main
    end

    def add_columns(treeview, name, index)
      renderer = Gtk::CellRendererText.new
      column = Gtk::TreeViewColumn.new(name, renderer, 'text' => index)
      column.set_sort_column_id(index)
      treeview.append_column(column)
    end
  end
end

if $PROGRAM_NAME == __FILE__
  EWS::SampleApp.new
end
```

実行すると以下のような表示になります。
![Screenshot at 2018-12-23 04-16-34.png](https://qiita-image-store.s3.amazonaws.com/0/152217/41ed439c-8026-cf92-81ea-b69309d429c9.png)

### 組み合わせてアプリを作る

ここまでで必要な材料がそろったので、把握できた内容を組み合わせてアプリを作ります。
サンプルコートは以下になります。これまで説明した内容を組み合わせた形になっているので、ぜひソースコードを参照してみてください。

 * [ValLaboratory/advcal/2018/ruby/ews/sample/coupon_list/sample.rb](https://github.com/ValLaboratory/advcal/blob/master/2018/ruby/ews/sample/coupon_list/sample.rb)

実行画面は以下のようになります。

![Screenshot at 2018-12-23 03-59-00.png](https://qiita-image-store.s3.amazonaws.com/0/152217/5666f0ad-048f-b0d4-4860-c929de047271.png)

## まとめ

[駅すぱあとWebサービス](http://docs.ekispert.com/v1/)の[回数券一覧](http://docs.ekispert.com/v1/api/coupon/list.html)を使用したアプリ作成例を紹介しました。[Ruby/Gtk](https://ruby-gnome2.osdn.jp/ja/)はLinux/macOS/Windowsのそれぞれの環境で動作できるようになっている(と思う)ので、今回のアプリはちょっと便利かもしれません。
また、[回数券一覧](http://docs.ekispert.com/v1/api/coupon/list.html)(/coupon/list)と[回数券詳細](http://docs.ekispert.com/v1/api/coupon/detail.html)(/coupon/detail)は[Amazon SaaSストア](http://www.amazon.co.jp/saas/)で購入できる[駅すぱあとWebサービス](http://docs.ekispert.com/v1/)で利用可能であるため、ぜひ皆様の環境でもAPIを利用して面白い活用事例を見つけてもらえると幸いです。