Rubyを使って天気予報を取得してみた

# 前書き
先月、3年間常駐していた客先を撤退しました。自社待機中で暇なので触ったことのないRubyをちょっと触ってみました。
※ 悲しいことに会社PCのアカウントでは権限の問題であらゆるインストールできませんでした。。。Rubyは誰かが入れてたみたいなので、Railsを使わずにいきます。（ハードル高い・・・）

Rubyは本当に初心者なので、もし間違いなどがあったらコメントしていただけるとうれしいです。

# 何を作ろうか
Ruby on RailsがないのでWEBページ関係はちょっと厳しい。なにせよ初Rubyだし。
そこそこ簡単で勉強になるということで、天気予報APIから天気情報を取得して~~twitterでつぶやくアプリケーションを作ることにしました。~~twitterは今度やります。

# 環境とか
OS：Windows7
IDE:Eclipse
verson:Ruby2.3
ライブラリ:httpclient

使うことにしたAPIはこちらです。
Livedoor Weather Hacks
http://weather.livedoor.com/weather_hacks/

選定理由ですが、APIキーの発行が要らないこと。お手軽！

# コーディング
さっそくRubyをコーディングしていきます。その前にプロジェクトの構成はこんな感じ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/50991/2e63bcb1-3a2b-0ed9-13cd-d773675658a8.png)

### 設定ファイル
API接続に必要な情報は設定ファイルに外出しにしています。

``` config.yml
# 天気予報API URL
weather-url : http://weather.livedoor.com/forecast/webservice/json/v1
# 天気予報取得拠点コード
location-code : 130010
```

### クラスファイル
今回はクラスファイルとして、2つのファイルを作りました。
1. Weather.rb
2. WeatherInfo.rb

#### Weather.rb
API接続からデータの加工までを行うクラスです。クラス分割を検討しましたが、最初ということでそのままでいきます。
まずはソース。その次に解説を行っていきます。

``` ruby:Weather.rb
require 'httpclient'
require 'resolv'
require 'json'
require 'yaml'
require './src/WeatherInfo'

class Weather

  @@DESCRIPTION = "description"
  @@TEXT = "text"
  @@FORECASTS = "forecasts"
  @@TELOP = "telop"
  @@DATE = "date"
  @@TEMPERATURE = "temperature"
  @@CELSIUS = "celsius"
  @@MIN = "min"
  @@MAX = "max"
  @@TODAY = 0
  @@TMRW = 1

  # コンストラクタ
  def initialize()
  end

  # メイン処理メソッド
  def doProcess()
    # 拠点コード
    keyWord = YAML.load_file('./conf/config.yml')["location-code"]
    # 天気API URL取得
    url = YAML.load_file('./conf/config.yml')["weather-url"]
    return analysisWeather(connectionAPI(keyWord, url))
  end

  # API接続部
  # 戻り値:ハッシュ化されたレスポンス
  def connectionAPI(keyWord, url)
    # http接続クライアントの生成
    client = HTTPClient.new
    # 指定した拠点のコードをリクエストに設定する。
    query = { 'city' => keyWord }
    # APIリクエスト
    res = client.get(url, query)
    # ハッシュ化して返却
    return JSON.parse(res.body)
  end

  # 天気情報をHashより解析する
  def analysisWeather(hash)

    info = WeatherInfo.new
    # 概要の取得
    info.description=(convertNil(hash.dig(@@DESCRIPTION, @@TEXT)))

    # 本日の天気情報
    info.todayTelop=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TELOP)))
    info.today=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@DATE)))
    info.todayTempMin=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TEMPERATURE, @@MIN, @@CELSIUS)))
    info.todayTempMax=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TEMPERATURE, @@MAX, @@CELSIUS)))

    # 明日の天気情報
    info.tmrwTelop=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TELOP)))
    info.tmrw=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@DATE)))
    info.tmrwTempMin=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TEMPERATURE, @@MIN, @@CELSIUS)))
    info.tmrwTempMax=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TEMPERATURE, @@MAX, @@CELSIUS)))

    return info
  end

  # nil判定、nilの場合は「-」を返却
  def convertNil(value)
    return value == nil ? "－" : value
  end
end
```

**メソッド一覧**

| メソッド       |       説明 |
|:-----------------|:------------------|
|initialize|コンストラクタ|
|doProcess|プログラムの開始位置|
|connectionAPI|API接続を実施|
|analysisWeather|APIレスポンスの解析|
|convertNil|nilの場合にダミー文字列を設定する|

処理の順番も上記のとおりになっています。

##### doProcessメソッド

``` Ruby:doProcess
 # メイン処理メソッド
  def doProcess()
    # 拠点コード
    keyWord = YAML.load_file('./conf/config.yml')["location-code"]
    # 天気API URL取得
    url = YAML.load_file('./conf/config.yml')["weather-url"]
    return analysisWeather(connectionAPI(keyWord, url))
  end
```
上から4行目の**YAMAL.load_file('./conf/config.yml')["location-code"]**で、設定フィルから、天気予報を表示したい拠点のコードを取得しています。
[]内に書かれた文字列がキーになります。

6行目も4行目とやっていることは同じです。設定ファイルからAPI接続先URLを取得しています。
7行目で実際にAPI接続を行うメソッドとAPIレスポンス解析を行うメソッドを実行しています。

##### connectionAPIメソッド

```Ruby:connectionAPI
 # API接続部
  def connectionAPI(keyWord, url)
    # http接続クライアントの生成
    client = HTTPClient.new
    # 指定した拠点のコードをリクエストに設定する。
    query = { 'city' => keyWord }
    # APIリクエスト
    res = client.get(url, query)
    # ハッシュ化して返却
    return JSON.parse(res.body)
  end
```

接続周りはライブラリの「HttpClient」を使用しています。詳しくはドキュメントに書かれているので簡単に説明すると、API接続を行う前にURLにパラメータを付与して「GET」にて接続を行っています。

今回のAPIレスポンスは**JSON**なので、扱いやすいように**JSON.parse**でハッシュ化しています。このメソッドはどうやら文字コードを文字にデコードする機能が備わっているみたいです。神ですね。

ハッシュ化って便利ですね。キーを指定すれば欲しい値が取得できる。これってキーの重複が発生した場合はどうなるんだろう。


##### analysisWeatherメソッド

``` ruby:analysisWeatherメソッド
# 天気情報をHashより解析する
  def analysisWeather(hash)

    info = WeatherInfo.new
    # 概要の取得
    info.description=(convertNil(hash.dig(@@DESCRIPTION, @@TEXT)))

    # 本日の天気情報
    info.todayTelop=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TELOP)))
    info.today=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@DATE)))
    info.todayTempMin=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TEMPERATURE, @@MIN, @@CELSIUS)))
    info.todayTempMax=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TEMPERATURE, @@MAX, @@CELSIUS)))

    # 明日の天気情報
    info.tmrwTelop=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TELOP)))
    info.tmrw=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@DATE)))
    info.tmrwTempMin=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TEMPERATURE, @@MIN, @@CELSIUS)))
    info.tmrwTempMax=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TEMPERATURE, @@MAX, @@CELSIUS)))

    return info
  end

```
ここでは、ハッシュ化されたAPIレスポンスを解析し、欲しい情報のデータオブジェクトへの詰め替えを行います。このAPIでは、取得項目がnilとなることがあるため、コンバートをかけています。
各行の**hash.dig()**ですが、これはキーを指定して値を取得するメソッドです。引数に指定している変数はコンストラクタの直上に定義したものを使用しています。

#### WeatherInfo.rb
データオブジェクトクラスです。

```ruby:WeatherInfo.rb
class WeatherInfo
  attr_accessor :description,
  :todayTelop,
  :today,
  :todayTempMin,
  :todayTempMax,
  :tmrwTelop,
  :tmrw,
  :tmrwTempMin,
  :tmrwTempMax
  :comment

  # コンストラクタ
  def initialize()
  end

  # 概要
  def description()
    @description
  end

  # 概要
  def description=(value)
    @description = value
  end

  # 本日テロップ
  def todayTelop()
    @todayTelop
  end

  # 本日テロップ
  def todayTelop=(value)
    @todayTelop = value
  end

  # 本日最低気温
  def todayTempMin()
    @todayTempMin
  end

  # 本日最低気温
  def todayTempMin=(value)
    @todayTempMin = value
  end

  # 本日最高気温
  def todayTempMax()
    @todayTempMax
  end

  # 本日最高気温
  def todayTempMax=(value)
    @todayTempMax = value
  end

  # 明日テロップ
  def tmrwTelop()
    @tmrwTelop
  end

  # 明日テロップ
  def tmrwTelop=(value)
    @tmrwTelop = value
  end

  # 明日最低気温
  def tmrwTempMin()
    @tmrwTempMin
  end

  # 明日最低気温
  def tmrwTempMin=(value)
    @tmrwTempMin = value
  end

  # 明日最高気温
  def tmrwTempMax()
    @tmrwTempMax
  end

  # 明日最高気温
  def tmrwTempMax=(value)
    @tmrwTempMax = value
  end

  # コメント
  def comment()
    @comment
  end

  # 明日最高気温
  def comment=(value)
    @comment = value
  end

end

```

setter,getterがJavaとは異なるので初めは戸惑いました。


## 実際に動かしてみる。
超簡単なテストクラスからWeather.rbを呼び出してみます。

```ruby:test.rb
require "./src/Weather"
require "./src/WeatherInfo"

weatherobj = Weather.new
info = weatherobj.doProcess()

puts "今日、#{info.today()} 東京の天気です。"
puts "天気    ：#{info.todayTelop()}"
puts "最高気温：#{info.todayTempMax()}"
puts "最低気温：#{info.todayTempMin()}"
puts ""

puts "明日、#{info.tmrw()} 東京の天気です。"
puts "天気    ：#{info.tmrwTelop()}"
puts "最高気温：#{info.tmrwTempMax()}"
puts "最低気温：#{info.tmrwTempMin()}"
puts ""


puts "【概要】"
puts info.description()
```

```lang:実行結果
今日、2018-06-01 東京の天気です。
天気    ：晴時々曇
最高気温：26
最低気温：－

明日、2018-06-02 東京の天気です。
天気    ：晴時々曇
最高気温：26
最低気温：18

【概要】
 高気圧が西日本から東日本に張り出しています。一方、東北地方を気圧の
谷が南下中です。

【関東甲信地方】
 関東甲信地方は晴れや曇りで、長野県や関東地方北部の山沿いでは雨の降
っている所があります。

 1日は、次第に日本海に中心を持つ高気圧に覆われるため晴れますが、寒
気や湿った空気の影響により曇る所もあるでしょう。

 2日は、日本の東に中心を持つ高気圧に覆われておおむね晴れますが、湿
った空気の影響により朝晩を中心に曇る見込みです。

 関東近海では、1日から2日にかけて波がやや高く、1日はうねりを伴う
所があるでしょう。

【東京地方】
 1日から2日にかけて、晴れ時々曇りとなる見込みです。

```

ちゃんと天気予報が取得できた！

# 感想
今まではJavaがメインだったので、Rubyの触り始めは頭が「？？？」だらけでした。
書き方や概念の理解にまだちょっと戸惑ってますが、継続して続けていけたらな思います。
twitterへの投稿機能はまたこんど

書き方がおかしかったり、こうしたほうがいいんじゃないかなどアドバイスがあれば、コメントしていただけると喜びます。