Faradayを触ってみた
実はこれが、2014年最初のエントリー。サボりすぎ。
WebAPIを呼ぶスクリプト書くのにFaradayを使ってみたので、使い方などをメモっておこうと思います。
Faraday
自体は前から知ってたんですが、リダイレクトとかめんどくさい事考えなくて良い場合に、
特にHTTPクライアントのライブラリ入れて使うより、Net::HTTP
で十分じゃない?と思って使った事ありませんでした。
なんとなーく、Ruby書きたくなって、使ったことないgemを使ってみたかったので、使ってみた次第です。
ちなみに、読み方は ふぁらでい
なんでしょうかね?
環境
使用するライブラリのバージョン
gem 'faraday', '~> 0.9.0' gem 'sinatra', '~> 1.4.5' gem 'sinatra-contrib', '~> 1.4.2'
今回のコードはこちら
今回の流れ
- まずはWebAPI作っとく
- Faradayの基本的な使い方
- Middlewareを使ってみる
- Middlewareを作ってみる
まずはWebAPI作っとく
とりあえず、簡単にJSONでも返してくれるAPIが欲しいので、何でも良いのですが、
サーバサイドのデバッグもしてみたいので、自前で用意してみます。
といってもすっごい簡単な物で良いので、Sinatra
さんにご協力頂き、さくっと作ります。
require "sinatra" require "sinatra/reloader" if development? require "sinatra/json" get "/test" do json name: :hoge, age: 100 end post "/test" do json params end post "/test.json" do json JSON.parse(request.body.read) end
ポイント
reloder
入れとくと、コード変えた時に再起動の必要なくなります。(Sinatra::Contrib)- 単純に
require "json"
して{ key: value }.to_json
返しても良いんですが、それだけだとresponseのcontent-typeがtext/html
になるので、Sinatra::Contrib
のsinatra/json
を使いますprovides: :json
すれば良いのですが、めんどくさいので
Faradayの基本的な使い方
先程作ったSinatraアプリは起動しておきます。
GETしてみる
require "faraday" require "json" require "pp" res = Faraday.new(:url => "http://localhost:4567").get("/test") body = JSON.parse(res.body) pp body
※status:200以外の場合の事はまったく考えてません
とまぁ、こんな感じでget
する訳なんですが、これだと、Net::HTTP
でもたいして変わらないんですよね。
require "net/http" require "uri" require "json" require "pp" res = Net::HTTP.get URI("http://localhost:4567/test") body = JSON.parse res pp body
URLを分割してなくて良い分、こちらの方が楽かも?。 ただ、Faradayの良い所はこれだけじゃわからないので、先に進みます。
POSTしてみる
Faraday
client = Faraday.new(:url => "http://localhost:4567") res = client.post "/test", { hoge: 100, foo: :bar } body = JSON.parse res.body pp body
Net::HTTP
res = Net::HTTP.post_form URI("http://localhost:4567/test"), { hoge: 100, foo: :bar } body = JSON.parse res.body pp body
と、まぁ、ここまでもあまりメリット感じませんね。
Content-Type変えてみる
jsonをpostしてみます。
Faraday
client = Faraday.new(:url => "http://localhost:4567") res = client.post do |req| req.url '/test.json' req.headers['Content-Type'] = 'application/json' req.body = { hoge: 100, foo: :bar }.to_json end body = JSON.parse res.body pp body
Net::HTTP
uri = URI("http://localhost:4567/test") req = Net::HTTP::Post.new uri, initheader = {'Content-Type' =>'application/json'} req.body = { hoge: 100, foo: :bar }.to_json res = Net::HTTP.start(uri.host, uri.port, use_ssl: false) do |http| http.request req end body = JSON.parse res.body pp body
少し楽になりましたね。 実はもっと楽になります。
Middlewareを使ってみる
FaradayにはMiddleware
という仕組みがあります。(Rackと同じような仕組みになってる)
こいつを使う事で、拡張できるようになってます。
ここには、一番最初に書いてあるんですけどね。
request/responseをログ出力してみる
client = Faraday.new(:url => "http://localhost:4567") do |faraday| faraday.response :logger faraday.adapter Faraday.default_adapter end res = client.get "/test" body = JSON.parse res.body pp body
ちなみにmiddleware使う場合、adapter
は必ず指定しないとエラーになるっぽい??です。
ソースまで追ってないですが。
出力結果
I, [2014-05-10T23:45:34.114079 #12029] INFO -- : get http://localhost:4567/test D, [2014-05-10T23:45:34.114147 #12029] DEBUG -- request: User-Agent: "Faraday v0.9.0" I, [2014-05-10T23:45:34.117054 #12029] INFO -- Status: 200 D, [2014-05-10T23:45:34.117099 #12029] DEBUG -- response: content-type: "application/json" content-length: "25" x-content-type-options: "nosniff" server: "WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)" date: "Sat, 10 May 2014 14:45:34 GMT" connection: "close" {"name"=>"hoge", "age"=>100}
こんな感じで出力されます。
FaradayMiddlewre
faraday_middleware
っていうgemが用意されていて、使えるmiddlewareが既に結構用意されています。
これを使うと、さっきのjsonパターンは数倍楽になります。
bundler使っている場合は、Gemfileに以下を追加してインストールしておきます。
gem 'faraday_middleware', '~> 0.9.1'
使ってみます。
require "faraday" require "faraday_middleware" require "json" require "pp" client = Faraday.new(:url => "http://localhost:4567") do |faraday| faraday.request :json # ここ faraday.adapter Faraday.default_adapter end res = client.post "/test.json", { hoge: 100, foo: :bar } body = JSON.parse res.body pp body
middlewareでrequestのmiddlewareとして:json
を入れとくだけで
っていう事をやってくれます。
この辺でハマったこと
こんな事をするとContent-Type
がapplication/json
になりません。
client = Faraday.new(:url => "http://localhost:4567") do |faraday| faraday.request :url_encoded faraday.request :json faraday.adapter Faraday.default_adapter end
この辺で、ちゃんとソース読んだ方が良さそうなので、見てみます。 まずは、
https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb#L87
この辺。
request
にmiddlewareの設定を渡すと use_symbol
-> use
という流れでここに来ます。
@handlers
というインスタンス変数にpushしていってますね。
次は、
https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb#L162
この辺。
app
-> to_app
という流れでここに来ると、先程の@handlers
をreverse
してinject
してます。
その中でbuild
しているので、Hanlder
のbuild
を見ると、
https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb#L47
klass
をnewして(引数にapp渡してる)返してますね。
つまり、最初useした時は
- url_encode
- json
という順番で追加しているので、これをreverseしてbuildすると
https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb#L150
結果、↑ここの@app
はurl_encodeのオブジェクトになる為、
- url_encode
- json
- 大本のapp
という順番で実行されるわけですね。
Middlewareを作ってみる
というわけで、とりあえず簡単なMiddlewareを作ってみたいと思います。
デフォルトのlogger
だとbodyを出力してくれないので、bodyを出力するmiddlewareを書いてみます。
require "faraday" require "json" require 'logger' class BodyDumper def initialize app @app = app @logger = Logger.new(STDOUT) end def call env dump_body("request", env) @app.call(env).on_complete do dump_body("response", env) end end private def dump_body phase, env @logger.info("#{phase} : #{env.body}") if env.body end end client = Faraday.new(:url => "http://localhost:4567") do |faraday| faraday.request :url_encoded faraday.use BodyDumper faraday.adapter Faraday.default_adapter end res = client.post "/test", { hoge: 100, foo: :bar }
まとめ
FaradayはMiddlewareなどでの拡張性(柔軟性)がうりな気がします。
(どっかのWebAPIを呼ぶクライアントを作ったりとか向いてるかも)
adapterも結構用意されていて、em_http
もあるみたいですね。
あとはfaraday_middleware
にoatuh2
のmiddlewareがあって、
Authorization
ヘッダー埋め込んでくれたりとか、地味に便利でした。
もう少し踏み込んで、拡張して使ってみたいと思います。
次回は気が向いたら、parameterのescape周りを修正してみたい、、、かも。
デフォルトだと半角スペースが+
にescapeされるので。
VMWareからVirtualBox+Vagrantに移行する時の話
普段、VMWareFusionを使って開発してるのですが、 特にVMWareに依存していないので 「Vagrant+VirtualBoxにしてみようかな」と思い立ち、 年末にちょこちょこ作業したのでやったことをメモしておきます。
PackerやVeeweeを使って1からVagrantのBoxを作る方法は ググれば大量に出てきますが、 既存のvmdkから作る方法はあまり情報がなかったです。 なのでそんなに需要はないのかもしれませんね、、、
環境
- OS : Mac OS X 10.9.1
- VirtualBox : 4.3.6
- Vagrant : 1.4.1
- GuestOS : CentOS 6.4
VirtualBoxのインストール
https://www.virtualbox.org/wiki/Downloads
今回は4.3.6
を選択しました。
vmdkをvdiに変換する
VirtualBoxを入れるとVBoxManageコマンドが使えるようになるので、
以下の感じのコマンドでvmdk
をvdi
に変換します。
VBoxManage clonehd --format VDI XXXX.vmwarevm/仮想ディスク.vmdk output.vdi
※vmdkのパスは各自の環境のパスを設定
※output.vdi
の名前はなんでも良いです
とりあえず起動してみる
- VirtualBoxを起動(GUI)
- 先程作成した
output.vdi
を起動ディスクにして新規作成 - 起動できる事が確認できたら、とりあえずシャットダウン
ネットワーク設定
VirtualBox上のゲストOSの設定
Settings > Network
Adapter1 => NAT
Adapter 1
のAttached to
をNAT
にしておきます。
Terminal等からsshでログインして作業したい場合は、advancedでPortフォワーディングの設定をしておきます。
host:2222 -> guest:22
ゲストOS内の設定
eth0 (NAT)
/etc/sysconfig/network-scripts/ifcfg-eth0
こいつを変更しておきます
DEVICE=eth0 ONBOOT=yes HWADDR=08:00:27:8F:56:EE TYPE=Ethernet BOOTPROTO=dhcp NM_CONTROLLED=yes UUID=6d8d1ced-b056-42cd-b7a3-195eff064f1d
ポイントは
ONBOOT
をyes
HWADDR
(MACアドレス)はVirtualBox上のゲストOSの設定のアダプタの所(Advanced)に表示されている値を指定
networkをrestart
$ sudo service network restart $ ifconfig
でeth0
が上がっている事を確認する
あとはping
等で外部にアクセスできる事を確認しておきます。
(yumでインストールしたりするので)
ローカルにvagrantをインストールする
http://www.vagrantup.com/downloads.html
現時点での最新版1.4.1
を入れる事をおすすめします。
あとは以下の通りにやる
http://futurismo.biz/archives/1678
※元のイメージのサイズが大きいと、boxを作ったり、box addしたり、結構時間がかかります。
上記手順の注意点
Rubyのバージョン
現在最新のchefはRuby1.9系以上じゃないと入らないので、Rubyのバージョンを上げておかないといけないです。 rpmを作るか、自分でビルドしてPATH通すか、rbenvもしくはRVM入れるか、方法はなんでも良いかと思います。
Vagrantfileの編集
名前解決がめっちゃ遅い
http://shibayu36.hatenablog.com/entry/2013/08/12/090545
ということらしいので、以下を入れておきます
Vagrant.configure("2") do |config| config.vm.provider :virtualbox do |vb| ... vb.customize ["modifyvm", :id, "--natdnsproxy1", "off"] vb.customize ["modifyvm", :id, "--natdnshostresolver1", "off"] end end
CLI for Redmine - climine (クリミネ) を書いてみた
Rubyを書きたくなったので、gemを作りました。
普段、タスク管理はRedmineを使ってるのですが、 わざわざRedmine見る為にブラウザにCommand+TABするのがめんどくさくて さくっと、コマンド打ってチケット確認したいなぁ、という思いがあったので、 ひとまず、チケット見れる所まで書いてみました。
http://www.redmine.org/projects/redmine/wiki/Rest_api
https://github.com/yagince/climine
https://rubygems.org/gems/climine
使い方
インストール
bundlerを使う場合
Gemfile
gem 'climine'
で、インストール
$ bundle install
まずはinitします
$ climine init -u [RedmineURL] -k [APIKey]
これでローカルにconfig.yml
が作成されます。
チケットを参照
$ climine issue get [TICKET_NO] <<機能>> [新規] #225 Test ---------------------------------------------------------------------------------------- [PROJECT ] Test [created ] 2013-11-10 01:06:34 [Author ] natsuki yagi [Assigned] natsuki yagi Hogehoge ----------------------------------------------------------------------------------------
その他の使い方は、 yagince/climine · GitHub こちらを参照してください。
現状の機能
現状、以下の機能があります。
- チケット
- 一覧表示
- 詳細表示
- 登録
- プロジェクト
- 一覧表示
- プロジェクトメンバー
- 一覧表示
- トラッカー
- 一覧表示
- チケットステータス
- 一覧表示
- ユーザー
- 一覧表示(検索可能)
特徴
今回やりたかった事で、入れた特徴的(?)な機能が以下の2つです。
- テンプレートの切替(ERB)
- 週指定の検索
テンプレート切替
自由なフォーマットでチケットの一覧なり内容なりを表示したかったので、 各自が自分で作ったERBテンプレートを使って、表示できるようしました。
$ climine issue get 100 -t template/issue.erb
のような感じで、テンプレートのpathを指定すれば使えます。
例えば、textile形式で一覧を出力したい場合、こんな感じになります。
templates/issues.erb
<% res.issues.each{|issue| -%> * <%= issue.tracker.name %> [<%= issue.status.name %>] #<%= issue.id %> <%= issue.subject %> <% } -%>
これを使って以下を実行
$ climine issue get -t templates/issues.erb * 機能 [新規] #225 Test * バグ [新規] #224 hoge * バグ [新規] #223 hoge * バグ [新規] #222 Test
週指定の検索
先週なにやったんだっけ?
みたいな事をまとめるために、
自分が担当で一週間以内に更新したチケットを一覧で知りたい
みたいな事がやりたい時があったので、週指定で検索できるようにしました。
自分が担当で一週間以内に更新したチケットを一覧で知りたい
を行うためには
$ climine user -n yagi
でユーザーIDを調べ
$ climine issue -w 1 -u [USER_ID]
を実行します。 ユーザーIDは一回調べてしまえば変わらないので、上記のコマンドをaliasにでもしてしまえば、 毎週すぐに調べる事ができます。
課題
helpがちゃんと表示されない
今回はThorを使ったのですが、
$ climine issue new
みたいに取得・登録・更新などのコマンドが必要な為、全てサブコマンドにしました。
ただ、サブコマンド指定しなかったらデフォルトで一覧表示して欲しいと思っていて、
default_command
を使ってサブコマンドなし時のコマンドを指定しているのですが、
そうすると、今度はhelpがちゃんと表示されないのです。
$ climine help member Usage: climine member [COMMAND] subcommands for member $ climine member help Usage: climine help [COMMAND] Describe subcommands or one specific subcommand $ climine member help get Usage: climine help [COMMAND] Describe subcommands or one specific subcommand
default_command
使わないとちゃんと見れるので、使い方がわるいのか、、、、
まだ、ちゃんと調べてないので、次のバージョンで直したいと思います。
今日はこのへんで。。。
Chef-solo+knife-solo+Vagrantでサーバ構築を自動化してみる - その2 sudoers
Chef-solo+knife-solo+Vagrantでサーバ構築を自動化してみる - その1 ユーザー追加
本日(8/24)、LL祭りで、
Inftastructure as LLというセッションを見てきました。
最近、Chef-solo+knife-soloをいろいろいじっていたので、今日は一番興味深いセッションでした。
いい機会なので、最近自分がChef+Vagrantでやった事を、メモしていきたいと思います。
続きを読む