はじめに
外部APIを叩くという経験が初めてだったので、どんな大変な作業が待っているのだろうと心構えをしていたのですが、驚いたことに
実は難易度が低くて、高機能が実装できるハイコスパな方法
でした。
今回実装した機能の概要は以下の通りです。メモ程度ですが参考程度に載せておきます。
前提
- deviseでユーザーに関する機能作成済み
- food_record(料理記録)モデルを作成済み
- new, createなどの基本的な機能は既に実装済み
環境構築
- Ruby2.7
- Ruby on Rails6
API_KEYの取得
OpenWeatherMapで会員登録して、API_KEYを取得します。こちらは、多くの記事が出回っているので、割愛させて頂きます。
https://qiita.com/matsubishi5/items/fcd77eacb0ed111299e2
↑私はこちらを参照しました。
http://api.openweathermap.org/data/2.5/weather?q=Tokyo&appid=
末尾に取得したAPI_KEYを貼り付けて、ブラウザで実行してみてください。東京の今の天気が返ってくるはずです。これだけで、APIってこんなものかと実感できますし、疎通確認の意味でもやっておいた方が無難です。
ちなみに、会員登録からAPI_KEYが使えるようになるまで数時間程度かかります。(いくぞうは、3時間くらいでした)
必要なライブラリをインストール & API_KEYを格納
#API_KEYを環境変数として管理する(Keyを外部流出させないための措置)
gem 'dotenv-rails'
#アプリケーション内でHTTPリクエストを投げたい場合に使うクラス
gem 'httpclient'
Gemfile記載後、bundle installします。
``` ..env. OPEN_WEATHER_MAP_API = 'API_KEY貼り付け' URI = 'https://api.openweathermap.org/data/2.5/weather' ```
.envには、前もって取得したAPI_KEYをコピペします。
/.env
ここで絶対に忘れずに.gitignoreを作ってください。.envをgithubにpushした場合、全世界にAPI_KEYを晒すことになってしまいます。(私は1回やらかしました、、、)
APIを叩く処理
controllerでAPIを叩きます。
-
Api::OpenWeatherMap::Request
という処理がありますが、こちらで、下記モジュール(request.rb)を呼び出しています。 - コードがブサイクでごめんなさい!!!!でもちゃんと動きます。
def create
#料理記録
@food_record = current_user.food_records.build(food_record_params)
@food_record.food_date = Time.zone.today
#天気API
#リクエストを出す
open_weather = Api::OpenWeatherMap::Request.new(current_user.location_id)
#戻り値を受け取る
response = open_weather.request
if @food_record.valid?
#APIが正常に動作した場合
if response['cod'] == 200
params_weather = Api::OpenWeatherMap::Request.attributes_for(response)
@food_record.update(params_weather)
flash[:notice] = "登録に成功しました"
else
#APIが正常に動作しない場合も料理情報は記録する
flash[:notice] = "天気情報の取得に失敗しましたが、登録に成功しました"
end
redirect_to root_url
else
render 'new'
end
end
- 処理本体は、モジュールに書きました。 - 今回、`取得する天気の地域`としてlocation_idを使用していますが、'id: location_id'を`q: 'Tokyo'`というように変えても上手くいきます。
module Api
module OpenWeatherMap
class Request
attr_accessor :query
def initialize(location_id)
@query = {
id: location_id,
units: 'metric',
appid:
'OPEN_WEATHER_MAP_API']
}
end
def request
client = HTTPClient.new
request = client.get(ENV['URI'], query)
JSON.parse(request.body)
end
# 取得したjsonをparamsに変換
def self.attributes_for(attrs)
{
weather_main: attrs['weather'][0]['main'],
weather_description: attrs['weather'][0]['description'],
weather_icon: attrs['weather'][0]['icon'],
weather_id: attrs['weather'][0]['id'],
temp: attrs['main']['temp'],
temp_max: attrs['main']['temp_max'],
temp_min: attrs['main']['temp_min'],
humidity: attrs['main']['humidity'],
pressure: attrs['main']['pressure']
}
end
end
end
end
lib以下はデフォルトではtask以外読み込まれないので、config/application.rbに、以下の設定が必要です
config.paths.add 'lib', eager_load: true
完成
<%= image_tag "http://openweathermap.org/img/wn/#{@food_record.weather_icon.chop}d@2x.png" %>
アイコンはこのように表示させています。chop処理をしているのは、夜に天気を取得すると、太陽が黒色になってしまうからです。
{@food_record.weather_icon.chop}d
このようにchopで末尾のnを削って、d「昼間(おそらくdaytime)」を付け足しています。
補足
- 今回はlocation_idを使用しました。私は、下記のCSVを読み込ませて、cityテーブルに初期データとして投入して使用しています。
- 都市名, location_id, 緯度, 経度 の順になっています。
- 説明は割愛しますが、下記のようにすれば簡単に実装できます。
札幌,2128295,141.346939,43.064171
青森,2130658,140.740005,40.82444
盛岡,2111834,141.152496,39.703609
仙台,2111149,140.871933,38.26889
秋田,2113126,140.116669,39.716671
山形,2110556,140.363327,38.240559
福島,2112923,140.467773,37.75
水戸,2111901,140.446671,36.341389
宇都宮,1849053,139.883606,36.56583
前橋,1857843,139.060837,36.391109
さいたま,1853226,35.857208
千葉,2113015,140.123337,35.604721
東京,1850147,139.691711,35.689499
横浜,1848354,139.642502,35.447781
新潟,1855431,139.023605,37.902222
富山,1849876,137.211395,36.695278
金沢,1860243,136.625565,36.59444
福井,1863983,136.225174,35.850101
山梨,1848649,138.608002,35.61602
長野,1856210,138.040771,36.13464
岐阜,1863640,137.053986,35.78091
静岡,1851715,138.325424,35.025219
名古屋,1856057,136.906403,35.181469
津,1849796,136.508606,34.730282
大津,1853574,135.868332,35.00444
京都,1857910,135.753845,35.021069
大阪,1853909,135.502182,34.693741
神戸,1859171,135.182999,34.691299
奈良,1855612,135.804855,34.685051
和歌山,1926004,135.167496,34.226109
鳥取,1849890,133.850815,35.367859
松江,1857550,133.050568,35.472221
岡山,1854383,133.934998,34.661671
広島,1862415,132.459366,34.396271
山口,1848689,131.47139,34.185829
徳島,1850158,134.559433,34.06583
高松,1851100,134.043335,34.340279
松山,1926099,132.765747,33.839161
高知,1859146,133.531113,33.559719
福岡,1863967,130.41806,33.606392
佐賀,1853303,130.298798,33.249321
長崎,1856177,129.873611,32.74472
熊本,1858421,130.741669,32.789719
大分,1854487,131.612503,33.23806
宮崎,1856717,131.423889,31.91111
鹿児島,1860827,130.558136,31.560181
那覇,1856035,127.681107,26.2125
require "csv"
CSV.foreach('db/csv/cities.csv') do |row|
City.create(
name: row[0],
location_id: row[1],
lon: row[2],
lat: row[3]
)
end
上記ファイルを作って、cityテーブルも作成済みならば、下記コマンドを実行します。
rails db:seed
最後に
少しでも参考になればLGMTよろしくお願いします。
また、説明不十分な点や、説明が間違っている箇所が御座いましたら、コメントいただけると幸いです。
Twitterもやっているので、よかったらフォローお願いします!!
https://twitter.com/engineer_ikuzou