2016年7月31日日曜日

「超高速なJSON APIをElixirフレームワークのPhoenixでビルドしてテストしよう」のアップデート

「超高速なJSON APIをElixirフレームワークのPhoenixでビルドしてテストしよう」

上の記事は、Elixir/PhoenixでJSON APIを作るガイドです。

実際には、mixのタスクを使って作るのが効率的ですが、TDD的にテストコードを書いて、テストを通すようにコードを作っていくため、PhoenixでのJSON APIの扱いをステップバイステップで理解することができます。

↓こちらはmix タスクを使った例

http://qiita.com/FL4TLiN3/items/41ca80cdbfca1956ed78

↓こちらはバージョニングした例

http://qiita.com/5t111111/items/79b861349f0e1ffae1e9

ただ、現在Phoenixは活発に開発が進んでいるため、一部コードが動かなくなっています。

今回、以下のバージョンで動くようにしました。

% iex -v
Erlang/OTP 18 [erts-7.3] [source-d2a6d81] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

IEx 1.2.3
% mix -v
Mix 1.2.3
  defp deps do
    [{:phoenix, "~> 1.1.4"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_ecto, "~> 2.0"},
     {:phoenix_html, "~> 2.4"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.9"},
     {:cowboy, "~> 1.0"}]
  end

変更箇所

mix ecto migration

add/1 は廃止。型を指定する必要がある

priv/repo/migrations/XXX_create_contacts.exs

-      add :name
-      add :phone
+      add :name, :string
+      add :phone, :string

確認

$ psql -U postgres -W
# database確認
postgres# /l
# テーブル定義確認
postgres# /d table_name

transaction のエラー

以下のerrorが出る。

理由は、現行だとtest_helper.exsが自動でtransactionをスタートするコードになっている。

** (RuntimeError) cannot begin test transaction because we are already inside one

以下を削除する

test/controllers/contact_controller_test.exs

  setup do
    SQL.begin_test_transaction(Repo)

    on_exit fn ->
      SQL.rollback_test_transaction(Repo)
    end
  end

ConnCaseモジュールを使用する(test/support/conn_case.ex(自動生成))

test/controllers/contact_controller_test.exs

-  use ExUnit.Case, async: false
-  use Plug.Test
+  use HelloPhoenixConnCase, async: false
  alias HelloPhoenix.Contact
  alias HelloPhoenix.Repo
  alias Ecto.Adapters.SQL

Poison.encode!できない

Repo.insert の戻り値が{:ok, ...}で戻ってくるため、Poinson.encodeに失敗する

test/controllers/contact_controller_test.exs

       |> List.wrap
+      |> Enum.map(&(elem(&1, 1))) 
       |> Poison.encode!

controller内にplug :actionがあると、responseが重複する

以下のエラーが出る。

** (Plug.Conn.AlreadySentError) the response was already sent

原因はここ参考。 https://github.com/phoenixframework/phoenix/issues/888 http://stackoverflow.com/questions/32448308/phoenix-elixir-ejabberd-response-already-sent-error

plug :action を削除

HelloPhoenix.ContactController

-     plug :action

clean upについて

  • そのまま動きます。
  • 場所はtest_helper.ex, test/support/conn_case.exどちらでもOK
  • modelはテストケースで直接読み込んだ方がいいかもしれません

Elixir/Phoenixの活用方法としては、APIサーバーとしての利用がまず考えられると思います。

その入り口として、動かしてみたい方は参考にどうぞ

2016年7月27日水曜日

Bloggerのポストをtwitterに自動投稿する

設定した手順をまとめました

すでに非常に詳しい記事があるので、やってみました。

http://www.blogger-customize.com/2013/12/twitter.html

記事が詳しいだけに複数ページに渡っているので、手順をまとめました。

  • BloggerのFeedURL(RSS)確認
    • BloggerのRSSは以下で取得できる。
    • http://ブログのアドレス/feeds/posts/default
    • ブラウザのアドレスバーに入れて叩くと、Feed内容が出るので動作確認できる。
  • FeedBurnerにBloggerのFeedURLを登録
  • BloggerにFeedBurnerのFeedAddressを登録(手順は上のページの後半)
    • 設定するFeedAddressはFeedBurnerのEdit Feed Detailで確認できる。
    • urlの形式はこれ。http://feeds.feedburner.com/
  • FeedBurnerでtwitter連携設定

debian で scala をセットアップする 2016年7月版

手順は一般的ですが、まとまったのがなかった。

2016年7月現在、Scala は、Java8を使ってます。DebianでJava8のインストール方法を検索すると、若干古いので、まとめました。

どちらもごく一般的な手順です。

Java8 のインストール

参考ページ(Ubuntu)

$ sudo apt-get update
$ sudo apt-cache search jdk
$ apt-get install openjdk-8-jdk
$ javac -version

Scalaのセットアップ

ScalaはActivatorを使ってセットアップします。

Activator のzip をダウンロードしてパスを通します。

画面からダウンロードします。 以下はコマンド例です。

# zipファイルのurlを確認
$ curl -sL http://www.lightbend.com/activator/download | egrep -o "\"https.*activator.*.zip\""
# zipをダウンロード
$ curl -L ${上記のurl}
# unzip する
$ unzip ${上記のzip}
$ cd ${上記のdir}
$ chmod +x bin/activator
$ sudo ln -s /full_path/to/activator /usr/local/bin/activator
$ rehash
$ cd ~ && activator new
$ cd ${project_name}
$ activator test

1行でダウンロードして、unzip

# curl -L $(curl -sL http://www.lightbend.com/activator/download | egrep -o "\"https.*activator.*.zip\"" | grep -v "minimal" | sed 's/"//g') -o $(basename $(curl -sL http://www.lightbend.com/activator/download | egrep -o "\"https.*activator.*.zip\"" | grep -v "minimal" | sed 's/"//g')) && unzip !$

良きDebian & Scala Life を!

2016年7月24日日曜日

Elixir 1.3.1 を CentOS で yum install

最新の Elixir を CentOS 6/7 にインストールしたい

ググるとソースからビルドするという記事が出てきます。

調べてみると、以下の状況。

  • CentOS6(EPEL)は、Elixir入ってない。
  • CentOS7(EPEL)は、Elixir入ってるが、0.12.5(ドキュメントほとんどないし、開発環境が違いすぎる)。
  • Fedoraは、Elixir入っているて、yumでインストールできる。
  • CentOS6/7でも、ソースからなら、問題なく入る。

Fedoraで動いていれば、rpmを作り直せば、そのまま使えそうなので、作りました。 コンテナもいいですが、手に馴染んだ環境にサクッとyum installで入るのは魅力的ですよね。

すぐに使ってみたい方は、こちら。Fedora公式RPMホスティングサービスのCOPRを使いました。

# CentOS7
$ curl -OL https://copr.fedorainfracloud.org/coprs/ynishi/elixir/repo/epel-7/ynishi-elixir-epel-7.repo
$ mv ynishi-elixir-epel-7.repo /etc/yum.repos.d/
# CentOS6
$ curl -L https://copr.fedorainfracloud.org/coprs/ynishi/elixir/repo/epel-6/ynishi-elixir-epel-6.repo
$ mv ynishi-elixir-epel-7.repo /etc/yum.repos.d/
$ sudo yum install elixir

COPRのプロジェクトページ

https://copr.fedorainfracloud.org/coprs/ynishi/elixir/

CentOS7ので、公式のElixirをとにかく触ってみたい(Phoenixもmixもいらない)という方はこちら。

$ sudo yum install epel-release
$ sudo yum install elixir

一応mixも入るのですが、最新のmixに用意されてるタスクとは随分違っていて、最低限の状態です。

CentOS7の場合

基本的なやり方はここと同じです。 http://qiita.com/bellflower2015/items/d33ca026b7d9c626adc9

rpmを作る一般的なやり方はこちら。

https://blog.tnmt.info/2011/04/29/rpmbuild-for-beginner/ https://osdn.jp/magazine/14/01/10/090000/

COPRというサービスは、RPMソースをアップして、サーバ上でRPMビルドして、できたRPMをホスティングしてくれますので、作業用インスタンスで、ビルド・インストールの確認をして、できたRPMソースをアップしました。

COPRについてはこちら。

http://qiita.com/hnakamur/items/336758b1d9564b759d49

コマンドはまとめて、Githubにあげました。build.shの通りでできるはずです。

https://github.com/ynishi/elixir-rpm

Erlangのインストール

まずは、Erlangを入れます。これは、Fedoraの最新版ソースからそのままリビルドできました。

ビルド中にメモリ使用量が最大で1GBくらいまで行きました。 メモリ500MBくらいの一番安いインスタンスだと厳しかったです。 ビルド時間は1時間弱くらいでした。(GCPの共有CPUインスタンスで)

 CXX  x86_64-redhat-linux-gnu/wxe_funcs.o
 g++: internal compiler error: Killed (program cc1plus)
 Please submit a full bug report,
 with preprocessed source if appropriate.
 See <http://bugzilla.redhat.com/bugzilla> for instructions.
 make[3]: *** [x86_64-redhat-linux-gnu/wxe_funcs.o] Error 4
 make[3]: Leaving directory `/home/YutakaNishimura/rpmbuild/BUILD/otp-OTP-18.3.4.1/lib/wx/c_src'
 make[2]: *** [opt] Error 2
 make[2]: Leaving directory `/home/YutakaNishimura/rpmbuild/BUILD/otp-OTP-18.3.4.1/lib/wx'
 make[1]: *** [opt] Error 2
 make[1]: Leaving directory `/home/YutakaNishimura/rpmbuild/BUILD/otp-OTP-18.3.4.1/lib'
 make: *** [libs] Error 2
 エラー: /var/tmp/rpm-tmp.X8FB4G の不正な終了ステータス (%build)


RPM ビルドのエラー:
    /var/tmp/rpm-tmp.X8FB4G の不正な終了ステータス (%build)

インスタンス上でビルドできることを確認したら、COPRにFedoraのRPMソースを指定して、ビルドします。

Elixir

これもFedoraの最新版をほぼそのまま使えました。

ただ、現行のElixirのgitリポジトリは、Erlangのビルドツール rebar のバイナリが含まれていて、単体でビルドできるようになっています。

ですので、Elixirのgitリポジトリのビルド方法を使うことにしました。

そうするとLOCALEがUTF8でないという警告が出るので、LOCALE指定も追加しました。

こちらは、オリジナルで作ったRPMソースをWeb上にアップロードして、それをCOPR に指定しました。

こちらは5分くらいでビルドできました。

COPRでは、ビルドするときのyum レポジトリを外部リポジトリとして設定することで、EPELにない、rpmを読み込みます。

最初、Erlang用のレポジトリを作って、Elixirのリポジトリから呼び出してましたが、Elixirのリポジトリにまとめて入れておけば、ビルドもインストールも簡単になるので、一つにまとめました。

Cent OS7は以上です。

Cent OS6 の場合

Erlang

CentOS6と、Fedora/CentOS7との最大の違いはsystemdです。(Erlangのrpm ビルドを考えると)

結局、specファイルでsystemdがないバージョンは回避するようになっていたのですが、いくつか修正が必要でした。

  • makeのオプションにベタ書きされていた
  • empdのサービスファイルが全てのバージョンで使われてた

あと、erlang, elixir のtar.gzソースファイルを自分で設置する必要がありました。 その辺りも、build.shに残しました。

また、依存関係のrpmは、CentOS7では、Fedoraの最新rpmソースからまとめてインストールしていたのですが、CentOS6では、systemdがないため、インストールできませんでした。

そのため、一旦EPELのErlangをインストールしてからビルドしましたが、いくつか手動でパッケージを追加しました。

上記対応をして、ビルドしたRPMソースをCentOS7の時と同様にCOPRでビルドしました。

Elixir

CentOS7とほぼ同じものでうまくできました。 ただ、testの際に、改めてLOCALEを設定しないと、テストが通らず、インストール完了しませんでした。

上記修正して、同じくCOPRにアップしました。

今後

今回、必要なファイルだけのリポジトリを切って作りましたが、RPMの本元のリポジトリからフォークして作成した方が、今後の保守性を考えると良いと思います。

また、COPRは、今回初めて知りましたが、カスタムRPMを作って配布するプラットフォームとしては、かなり良いと思いました。

簡単に使えて、プレーンな環境でビルドしなおしているので、一定の動作は保証できるためです。

パッケージをメンテナンスしている方のおかげで、さっくりと使えるRPMができました。

2016年7月10日日曜日

Evernoteの次を探す

Evernoteのプラン変更

6/29 にEvernoteのプラン変更が発表されました。

一番の変更点は、「無料プランの利用端末数を2台に限る」です。

Evernoteの特徴である、「どこからでも使える」という利点を制限し、実質的な有料化と言える内容です。

Evernoteのメリットを感じていたのはこの2点です。

  • Web clip、テキスト、画像などをメモ+ノートという形で貯めていける
  • ほぼ全ての端末・オンライン・オフラインにかかわらず使える

特に、類似のメモ用サービスを使っていて、最終的にEvernoteに落ち着いていたのは、2番目の「どこでも・いつでも使える」という部分でした。

今回のプラン変更に合わせて、有料プランにするという選択肢もあります。

ただ、Evernoteはmarkdownに対応していないなど不満な点があったので、代替することにしました。

Evernoteの代替

主な用途と代替先は以下です。

  • Web clip => はてぶ
  • テキスト,画像など => markdown + git + editor

テキスト、画像については、この記事が詳しいです。 http://qtamaki.hatenablog.com/entry/2016/07/08/133247

この記事では、dropboxを使っていますが、その代わりにgitを使いました。

gitにすれば、コミットログが残り管理が楽になる、自分の好きなeditorを選べる、データの共有なども自由にできる、といいことづくめですね。

また、移行したい時は、リポジトリを移動すればいいだけなので、ホスティングサービスを変えることも簡単です。

githubはプライベートは有料ですが、bitbucketならば、プライベートでも無料なので、個人用で使うには、オススメです。

すでにEvernoteに入れているデータは、xmlでエクスポートできます。MicrosoftのOneNoteへ移行したり、とりあえずgitに乗せて検索かけるようにしてもいいと思います。

hugo(静的サイトフレームワークを使う)

シンプルにメモを取るだけならば、gitにメモ用リポジトリを作ればOKですが、github pageのように使いたい場合、hugoがオススメです。

https://gohugo.io/overview/introduction/

こちらは、golangで実装された、静的Webサイト用のフレームワークでmarkdownの記事から静的サイトを作るものです。

gitで運用されることが前提になっているので、今回のような用途に向いています。

2016年7月6日水曜日

簡単なWEBスクレイピングをElixirで試す

ページを取得して、タグの内容を表示するシンプルなWebスクレイパーをElixirで実装してみます。

まずはシンプルにージを取得して表示するだけのものを作ります。
defmodule WebScripe do
  def main([]) do
    url = "http://www.pythonscraping.com/pages/page1.html"
    response = HTTPoison.get(url)
    IO.puts "#{inspects response}" 
  end
end

次に、例外処理を追加します。 Elixirは例外処理としてtry-rescueなどを持ちますが、パターンマッチを使うことにします。 そのため、HTTPoison.getを使います。(get!は例外を出すため今回は使用しません)

ok/errorを返してくれるので、そのままマッチさせます。ただ、HTTPステータスエラーについてはokが返ってくるためガードを使って2xx系のみbodyを取って表示するようにします。

それ以外の場合は、ステータスコードを表示するだけにしました。

case response do
  {:ok, %HTTPoison.Response{status_code: cd, body: body}} when (div cd, 100) == 2 ->
     IO.puts body
  {:ok, %HTTPoison.Response{status_code: cd}} ->
     IO.puts "HTTP Status Code: #{cd}"
  {:error, %HTTPoison.Error{reason: reason}} ->
     IO.puts "Error: #{inspect reason}"
end

HTMLパーサを追加して、h1タグを取得してみます。

Flokiのfindは、[{tag, attr, content}]を返すので、Listのheadを取って、elemでtupleから内容を取得して表示させました。

obj = Floki.find(body, "h1")
IO.puts "#{elem hd(obj), 2}"

まとめたコード

defmodule WebScripe do
  def main([]) do
    url = "http://www.pythonscraping.com/pages/page1.html"
    response = HTTPoison.get(url)
    case response do
      {:ok, %HTTPoison.Response{status_code: cd, body: body}} when (div cd, 100) == 2 ->
         obj = Floki.find(body, "h1")
         IO.puts "#{elem hd(obj), 2}"
      {:ok, %HTTPoison.Response{status_code: cd}} ->
         IO.puts "HTTP Status Code: #{cd}"
      {:error, %HTTPoison.Error{reason: reason}} ->
         IO.puts "Error: #{inspect reason}"
    end
  end
end
2016年7月3日日曜日

Phoenix Routeの便利コマンド

PhoenixのRoutesについて、便利そうな機能を公式GUIDEから抜粋しました。

本家はこちら

Routesを確認する

Phoenixには、routeの設定を確認するツールがあります。

設定ファイル(web/router.ex)を確認するより有効なものが整理されてでてくるので、実際にどのように設定されたかを確認できます。

$ mix phoenix.routes
page_path GET / Prj.PageController :index
# /への全てのGETリクエストはPrj.PageControllerのindexを使うということ 

まだ、初回実行してない場合は、以下をまず実行します。

$ mix do deps.get, compile

Resourcesを使う

PhoenixのRouteは、HTTPアクセスをまとめて作るmacroを提供しています。

resourcesを使って以下のようなシンプルな設定をrouteに追記するだけで、各種HTTPアクセスに対応したルーティング設定ができます。

resource "/users", UserController

phoenix.routesの結果はこちらです

user_path  GET     /users           Prj.UserController :index
user_path  GET     /users/:id/edit  Prj.UserController :edit
user_path  GET     /users/new       Prj.UserController :new
user_path  GET     /users/:id       Prj.UserController :show
user_path  POST    /users           Prj.UserController :create
user_path  PATCH   /users/:id       Prj.UserController :update
           PUT     /users/:id       Prj.UserController :update
user_path  DELETE  /users/:id       Prj.UserController :delete

必要なメソッドのみ設定することも、:only, :exceptを使って実現できます。