Ruby環境からPythonスクリプトを叩いてデータを得るまで

こんにちは。

Rubyを使っているとき、RubyからPythonスクリプトを叩いて、その結果をRubyで表示することが出来たらいいなと考えました。
想定するケース例としては、**RailsのバックエンドでPythonの機械学習コードを実行したい**といったものになります。

今回のブログでは、`Ruby環境からPythonスクリプトを叩いてデータを得るまで`について述べていきたいと思います。

このブログで得ることができる情報は以下になります。
>・Ruby環境でPythonスクリプトを実行したいときの手順
・Flaskの簡単な使い方
・RubyのNet::HTTPクラスの簡単な使い方

#実装した手順について簡単に概要を。

私が実装した方法は以下になります。
>**1. Flaskを用いて、PythonスクリプトをAPI化する
2. API化したものをローカル上で実行(Run)する
3. RubyのNet::HTTPクラスを用いてローカル上のAPIを叩く**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1728508/4331d749-d74d-6916-f14f-8ddbd8e48704.png)


私が実装した方法は、**とりあえずRubyからPythonスクリプトを叩く流れを理解しておきたい！**という方向けです。

ちなみに、今回のフローはこちらのページを参考にしました。

https://qiita.com/kakiuchis/items/e1a3b5d58987a7a769bc

こちらのページでは、APIに認証機能を設ける方法や、HerokuにAPIを挙げる方法も紹介しています。詳細の実装も大変わかりやすく紹介されているので、ぜひご覧になってください。

それでは、早速試してみましょう。

(追記) 
`PyCall`というRubyライブラリを用いることで、Ruby言語内でPythonスクリプトを書けるようです。こちらのライブラリを用いるのが一番近道かもしれないです。
https://github.com/mrkn/pycall.rb/


#Flaskを用いて、PythonスクリプトをAPI化する
まずは、PythonスクリプトをAPI化したものを作成してみましょう。

Flaskを用いることで、PythonスクリプトをAPI化することができます。
以下のFlaskの解説動画が非常におすすめです。
<a href="https://www.youtube.com/watch?v=CjYKrbq8BCw">Creating a RESTFul API With Flask</a>

Flaskは、Python言語を用いてWebアプリケーションを作成するフレームワークとなっており、DjangoやRailsのようなWebサイトを作成することができます。
Webアプリケーションでは、URLパスとHTTPメソッドが与えられたとき、その組み合わせに応じた画面出力が行われます。例として、Railsのコントローラーが挙がります。
ここで、画面出力の代わりに、JSON形式やXML形式の結果を出力をすることで、APIとして機能することができます。

```py:rest_api_cul.py
from flask import Flask, jsonify, request

app = Flask(__name__)

#cul_inputを書き換えることで、関数をカスタマイズしてください。
def cul_input(input_data: str) -> str:
    return input_data + " yeah!!!"

#APIが無事動いているかどうかのテスト
@app.route("/api/test", methods = ["GET"])
def test():
    return jsonify({"result": "TEST OK"})

#input_dataをcul_inputで計算して、JSONファイルで出力する
@app.route("/api/result/<string:input_data>", methods = ["GET"])
def result(input_data):
    result = cul_input(input_data)
    return jsonify({"result": result})

if __name__ == "__main__":
    app.run(debug=True, port=8080)
```


詳細部分を解説します。

```py
@app.route("/api/test", methods = ["GET"])
def test():
    return jsonify({"result": "TEST OK"})
```

`@app.route(<path>, methods = [method])`
このように記述することで、パスとメソッドの組み合わせを認識させます。この直後に関数を記述することで、リクエストが来た時に関数のリターン部分を出力します。


```py
@app.route("/api/result/<string:input_data>", methods = ["GET"])
def result(input_data):
    result = cul_input(input_data)
    return jsonify({"result": result})
```

`@app.route(<path><type:param>, methods = [method])`
このように記述することで、`<type:param>`部分の変数を関数の引数にすることができます。

```py
if __name__ == "__main__":
    app.run(debug=True, port=8080)
```
`app.run(port=<port>)`と記述することで、`localhost:<port>/>`という形で、ポートが指定されたサーバーが動きます。

以上のスクリプトを記述することで、PythonコードをAPI化することが出来ました。これによって、外部からHTTPリクエストを受け取った時にJSONファイルを返すようになります。

#API化したものをローカル上で実行(Run)する
それでは、作成したAPIサーバーをローカルPC上で動かしてみましょう。

ターミナル上で`rest_api_cul.py`を実行します。

```terminal
python3 rest_api_cul.py
```
以下のようなスクリプトが表示されたら、無事にAPIサーバーが実行(Run)出来ています。
表示されない場合は、スクリプトの誤字脱字や、ポートがすでに使われていることが考えられます。

```terminal
 * Serving Flask app 'rest_api_cul' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: ooo-ooo-ooo
 ```
それでは、`curl`コマンドでAPIを叩いてみましょう。別のターミナルウインドウを開いて、以下のスクリプトを入力してみます。

JSON出力が得られたら成功です。

```
#APIが動いているかのテスト
curl -X GET localhost:8080/api/test

#出力
{
  "result": "TEST OK"
}

#引数を"Hello"とするときの入力
curl -X GET localhost:8080/api/result/Hello

#出力
{
  "result": "Hello yeah!!!"
}
```

#RubyのNet::HTTPクラスを用いてローカル上のAPIを叩く
それでは、いよいよRuby環境から、API化したPythonスクリプトを叩いていきましょう。
方針としては、先ほど`Curl`コマンドでAPIを叩いた手順を、Rubyの`Net::HTTP`クラスによって代用します。
`Net::HTTP`クラスは、Pythonでいうと`request`モジュールのようなもので、スクリプト内でHTTP通信を用いて外部のサーバーからデータの取得を行うことができます。

作成したRubyスクリプトは以下になります。

```rb:result_from_rest_api_cul.rb
require "json"
require "net/http"
require "uri"

word = ARGV[0] #コマンドライン引数を入力

#result/<string:input_data>の部分にwordを代入しURLを作成
url = "http://localhost:8080/api/result/" + word
#URI形式に変換
uri = URI.parse(url)
#GETメソッドでHTTP通信して、APIを叩いてみる
result = Net::HTTP.get(uri)

#APIを叩いた結果を出力
puts result
```

それでは、ターミナル上で実行してみましょう。以下のような出力結果が得られたら成功です。

```terminal
#result_from_rest_api_cul.rbを実行する
ruby result_from_rest_api_cul.rb  Hello

#出力結果
{
  "result": "Hello yeah!!!"
}
```

#おわりに
今回実装した、Ruby環境からPythonスクリプトを叩いてデータを得るフローを再掲します。
>**1. Flaskを用いて、PythonスクリプトをAPI化する
2. API化したものをローカル上で実行(Run)する
3. RubyのNet::HTTPクラスを用いてローカル上のAPIを叩く**

このフローを行うことで、RubyとPythonのスクリプト間でデータを送受信できました。
Railsのコントローラーに手順3を盛り込むことで、Rails環境でもPythonスクリプトの使用ができるようになります。

そのため、今度は**RailsでのPythonスクリプトの使用方法**について、記事で書いていければと考えています。

それでは、最後までお読みいただきありがとうございました！