RubyでAPIを叩く(#4 その他ライブラリ編)

[前回](http://qiita.com/kazuyat/items/56c8a87aca9fc0180516)はRuby標準のnet/httpでDigitalOceanのAPIを叩いてみました。

RubyにはHTTPアクセスのためのライブラリがいくつか出ていますが、それらを使うことでよりAPIが叩きやすくなるのかどうか、検証してみたいと思います。

## open-uri
net/httpのwrapperとして歴史があるライブラリです。
wget的に、とにかくWebから何か取ってくるには記述量が少なくて済みます。いつものDroplet一覧をやってみます。

```ruby:do_listdroplet_openuri.rb
require 'open-uri'

token = '(トークン)'
droplet_ep = 'https://api.digitalocean.com/v2/droplets'

res = open(droplet_ep,
  "Authorization" => "bearer #{token}") do |f|
    f.each_line do |line|
      puts line
    end
end
```
はい、いきなり短いです！
後ろの方なんてレスポンスを行ごとにputsしているだけですので、そこがなければめちゃくちゃ短いですね。

ただし、open-uriには重大な問題がありまして、**GETしかできない**んですね。一応、POSTができるようにしたりするパッチ等もあるようですが、PUTやPOST、DELETEを多用するAPI操作にはちょっと向かない感じです。

## rest-client
次はまさにREST操作のために生まれてきたような名前のrest-clientです。

```ruby:do_listdroplet_rest-client
require 'rest-client'

token = '(トークン)'
droplet_ep = 'https://api.digitalocean.com/v2/droplets'

res = RestClient.get droplet_ep, { :Authorization => "bearer #{token}" }

puts res
```
これも短い！GETについてはopen-uriと肩を並べます。

次にDropletを作成する場合、

```ruby:do_createdroplet_rest-client.rb
require 'rest-client'
require 'json'

token = '(トークン)'
droplet_ep = 'https://api.digitalocean.com/v2/droplets'

data = {
	"name" => "testserver2",
	"region" => "sgp1",
	"size" => "512MB",
	"image" => "ubuntu-14-04-x64",
	"ipv6" => "false"
}.to_json

res = RestClient.post droplet_ep, data, { :Authorization => "bearer #{token}", content_type: :json }

puts res
```
これもnet/httpに比べればだいぶ短い気がします。
Content-Typeで指定する"application/json"は:jsonとシンボル化されています。

## Faraday
FaradayはDigitalOceanの公式SDKも使っているライブラリです。
売りとしては記述の長さ云々よりも"middleware"と呼ばれる拡張の仕組みにより挙動をカスタマイズできることにある、ようです。

```ruby:do_listdroplet_faraday.rb
require 'uri'
require 'faraday'

droplet_ep = 'https://api.digitalocean.com/v2/droplets'
token = '(トークン)'

uri = URI.parse(droplet_ep)

http = Faraday.new(:url => "#{uri.scheme}://#{uri.host}")
res = http.get do |req|
  req.url uri.path
  req.headers['Authorization'] = "bearer #{token}"
end

p res.body
```
Faradayは先にアクセス先ホストでオブジェクトを作っておいてから、そのURLのパスに向けてGETなりPOSTなりのメソッドを打ち出すようになっている(同じホストオブジェクトに対して複数のリクエストを出せますね)ため、いったんparseするという面倒なことをしています(最初から分けとけばいいんですが)。

次にPOSTです。

```ruby:do_createdroplet_faraday.rb
require 'uri'
require 'faraday'
require 'json'

droplet_ep = 'https://api.digitalocean.com/v2/droplets'
token = '(トークン)'

uri = URI.parse(droplet_ep)

http = Faraday.new(:url => "#{uri.scheme}://#{uri.host}")
res = http.post do |req|
  req.url "#{uri.path}"
  req.headers['Authorization'] = "bearer #{token}"
  req.headers['Content-Type'] = "application/json"
  req.body = {
	"name" => "testserver",
	"region" => "sgp1",
	"size" => "512MB",
	"image" => "ubuntu-14-04-x64",
	"ipv6" => "false"
  }.to_json
end

p res.body
```

rest-clientよりは記述量が多いですが、誤差の範疇な気もします。
で、同じPOSTですが"faraday-middleware"を使ってみると、こうなります。

```ruby:do_createdroplet_faraday2.rb
require 'uri'
require 'faraday'
require 'faraday_middleware'
require 'json'

droplet_ep = 'https://api.digitalocean.com/v2/droplets'
token = '(トークン)'

uri = URI.parse(droplet_ep)

http = Faraday.new(:url => "#{uri.scheme}://#{uri.host}") do |h|
  h.request :json
  h.adapter Faraday.default_adapter
end

res = http.post do |req|
  req.url "#{uri.path}"
  req.headers['Authorization'] = "bearer #{token}"
  req.body = {
	"name" => "testserver",
	"region" => "sgp1",
	"size" => "512MB",
	"image" => "ubuntu-14-04-x64",
	"ipv6" => "false"
  }
end

p res.body
```

**間違い探し**みたいになってますが、ホストオブジェクト作成時に`request :json`と指定することで、POSTリクエスト中でのContent-Type記述、およびリクエストボディのjson変換が要らなくなっています。

「それだけかよ」という気もしますが…faraday_middlewareは自分で作り込むことで柔軟に活用するところがミソで、そのままでは大した効果は得られない、ということなのでしょう。

## 総評
open-uriはしょうがないとして、一般的なREST操作の範疇に要件が収まるならばrest-clientが扱いやすいように思います。API操作に絡めて高度な処理を行うのであれば、Faradayもアリです。

正直なところ、記述量としてはnet/httpから極端に減るものではないので、諸般の事情によりgemがrequireがしにくい開発であれば、無理に使わなくてもなんとかなってしまうでしょう。ただし今回取り上げていないのですが、こういうライブラリは例外処理にも目が行き届いているので、総合的に判断するのが吉なようにも。

私は素人なので、扱いやすいそうなrest-clientでしばらくお世話になってみようかなと思ってます(途中で変えるかもしれませんが)。