IEx.Pryでデバッグ

ここ

デバッガみたいなのはある?

と書いた。調べてみたところIEx.pryが使えることがわかった。

使い方

大雑把にいうとこんな感じ

  1. IEx モジュールを require する。
  2. ブレイクポイントコードに仕込む。
  3. iex でサーバを起動する
  4. リクエストを出す。
  5. ブレイクポイントに到達すると、pry を実行するかを尋ねられるので Y を入力する。
  6. respwan で処理を続行する。

コードはこんな感じ(ベースはここ)。

defmodule HelloPhoenix.PageController do
  use HelloPhoenix.Web, :controller

  require IEx

  def index(conn, _params) do
    IEx.pry                    # ブレイクポイント
    render conn, "index.html"
  end
end

できること

  • 変数の内容を表示させる

などなど(詳しく調べていない)

実行サンプル

localhost:4000/?foo=bar にリクエストを出したところ。

% iex -S mix phoenix.server
Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.0.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 17 Aug 21:02:01 - info: compiled 5 files into 2 files, copied 3 in 2280ms
Request to pry #PID<0.267.0> at web/controllers/page_controller.ex:7. Allow? [Yn] y

Interactive Elixir (1.0.5) - press Ctrl+C to exit (type h() ENTER for help)
pry(1)> _params
%{"foo" => "bar", "format" => "html"}
pry(2)> conn
%Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...}, assigns: %{},
 before_send: [#Function<1.90859823/1 in Plug.CSRFProtection.call/2>,
  #Function<4.77861751/1 in Phoenix.Controller.fetch_flash/2>,
  #Function<0.121794808/1 in Plug.Session.before_send/2>,
  #Function<1.60260050/1 in Plug.Logger.call/2>,
  #Function<0.94118563/1 in Phoenix.LiveReloader.before_send_inject_reloader/1>],
 body_params: %{}, cookies: %{}, halted: false, host: "localhost",
 method: "GET", owner: #PID<0.267.0>,
 params: %{"foo" => "bar", "format" => "html"}, path_info: [],
 peer: {{127, 0, 0, 1}, 36626}, port: 4000,
 private: %{HelloPhoenix.Router => {[], %{}}, :phoenix_action => :index,
   :phoenix_controller => HelloPhoenix.PageController,
   :phoenix_endpoint => HelloPhoenix.Endpoint, :phoenix_flash => %{},
   :phoenix_layout => {HelloPhoenix.LayoutView, :app},
   :phoenix_pipelines => [:browser],
   :phoenix_route => #Function<1.114667012/1 in HelloPhoenix.Router.match/4>,
   :phoenix_router => HelloPhoenix.Router,
   :phoenix_view => HelloPhoenix.PageView, :plug_session => %{},
   :plug_session_fetch => :done}, query_params: %{"foo" => "bar"},
 query_string: "foo=bar", remote_ip: {127, 0, 0, 1}, req_cookies: %{},
 req_headers: [{"user-agent", "curl/7.35.0"}, {"host", "localhost:4000"},
  {"accept", "*/*"}], request_path: "/", resp_body: nil, resp_cookies: %{},
 resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"},
  {"x-request-id", "jcei0fi1smlj9c9npf323nfukor7p6eo"},
  {"x-frame-options", "SAMEORIGIN"}, {"x-xss-protection", "1; mode=block"},
  {"x-content-type-options", "nosniff"}], scheme: :http, script_name: [],
 secret_key_base: nil, state: :unset, status: nil}
pry(3)> respawn

Interactive Elixir (1.0.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

PhoenixのLogger

とりあえず、アプリが起動する状態になった。

Phoenix・Elixir をちょっとさわって思ったのは、コード上に型が現れてれていないとなにかと調べにくい、ということ。馴染みのある言語なら、型が現れてなくてもなんとかなっちゃうんだけど、馴染みのない言語だとそうはいかない。

結局コード見ててもわからないので、ログを出しまくって、どういう動きをするか、どういうデータが入力・出力されるのかを追っているところ(デバッガみたいなのはある?)。まあ、始めはログの出し方もわからなかったのだけれど。

Logger

ドキュメントはこちら

debug, info, warn, error の4レベルがある。

使い方としてはこんな感じ(サンプルはここで作ったhello_phoenixプロジェクト)。

defmodule HelloPhoenix.PageController do
  use HelloPhoenix.Web, :controller

  require Logger

  def index(conn, _params) do
    Logger.error "Error!!!"
    Logger.warn  "Warn!!"
    Logger.info  "Info!"
    Logger.debug "Debug"

    render conn, "index.html"
  end
end

ログの設定は config の下の設定ファイルで変えられる。

出力先はデフォルトで :console となっている。 ファイルに落とすにはリダイレクトするのかな...?と思って調べたら Backend というものがあって、そこにファイルに直接ログを出力する拡張機能を追加すれば、リダイレクトは不要。標準ではファイルに落とす拡張機能は用意されていないが、logger_file_backendが公開されていたので使ってみる。

まずは mix.exs 依存関係を追加して、% mix deps.get でダウンロードする。

--- mix.exs.orig 2015-08-15 15:12:58.149425240 +0900
+++ mix.exs   2015-08-15 15:13:18.074069599 +0900
@@ -31,6 +31,7 @@
     [{:phoenix, "~> 0.16"},
      {:phoenix_html, "~> 2.0"},
      {:phoenix_live_reload, "~> 0.6", only: :dev},
+     {:logger_file_backend, "~> 0.0.3"},
      {:cowboy, "~> 1.0"}]
   end
 end

設定ファイルはこんな感じ。

--- config/dev.exs.orig  2015-08-15 15:08:00.514099611 +0900
+++ config/dev.exs    2015-08-15 15:10:06.139743337 +0900
@@ -24,4 +24,7 @@
   ]

 # Do not include metadata nor timestamps in development logs
-config :logger, :console, format: "[$level] $message\n"
+config :logger, backends: [{LoggerFileBackend, :file}]
+config :logger, :file,
+  path: "/tmp/hello_phoenix.log",
+  level: :debug

結果

% cat /tmp/hello_phoenix.log
15:16:29.472 [info] Running HelloPhoenix.Endpoint with Cowboy on http://localhost:4000
15:16:49.496 [info] GET /
15:16:49.510 [debug] Processing by HelloPhoenix.PageController.index/2
  Parameters: %{"format" => "html"}
  Pipelines: [:browser]
15:16:49.526 [info] Sent 200 in 29ms

Phoenixを始めてみた

Phoenix が気になったので、さわり始めた。

Phoenix は Elixir で書かれていて、アプリを作る場合も Elixir で書く。Phoenix もElixir も今まで使ったことがなく、初めての言語、初めてのフレームワークで、いろいろとつまづくことが多そう。その反面、面白いこともありそうなわけで。

とりあえず環境を整えよう。

環境構築

Erlang, Elixir, Phoenix をインストールする。

Elang, Elixir は anyenv を使ってインストールする。

今回インストールしたバージョンは

  • Erlaang/OTP 18
  • Elixir 1.0.5
  • Phoenix 0.16.0

OS は Ubuntu 14.04.3。

*env

anyenv のインストールはREADME.md を参照。

% anyenv install erlenv
% anyenv install exenv
Erlangのインストール

Erlangerlang-build みたいなのがないので、自分でビルドする。

% curl -O http://www.erlang.org/download/otp_src_18.0.tar.gz
% tar xzf otp_src_18.0.tar.gz
% cd otp_src_18.0
% ./configure --prefix=/home/ymmtmsys/.anyenv/envs/erlenv/releases/18.0
% make -j4
% make install
% erlenv global 18.0

ソースは消してOK.

Elixirのインストール
% exenv  install -l     # 最新バージョンの確認のため
% exenv install 1.0.5
% exenv global 1.0.5
Phoenix のインストール

公式ドキュメントを参照

node.js はすでに入っているので省略。

Up And Running

ここ を参考に、サンプルのアプリが起動するか確認。

% mix phoenix.new hello_phoenix
% hello_phoenix
% mix phoenix.server
.
.
.
[info] Running HelloPhoenix.Endpoint with Cowboy on http://localhost:4000
[warning] the :size option when configuring HelloPhoenix.Repo is deprecated, please use :pool_size instead
14 Aug 11:03:56 - info: compiled 5 files into 2 files, copied 3 in 2675ms
[info] GET /
[debug] Processing by HelloPhoenix.PageController.index/2
  Parameters: %{"format" => "html"}
  Pipelines: [:browser]
[info] Sent 200 in 17ms

Ok.

warning は設定ファイルのフィールド名を変えれば出なくなる。

--- config/dev.exs.orig  2015-08-14 11:21:57.671526209 +0900
+++ config/dev.exs    2015-08-14 11:22:01.059526209 +0900
@@ -32,4 +32,4 @@
   username: "postgres",
   password: "postgres",
   database: "hello_phoenix_dev",
-  size: 10 # The amount of database connections in the pool
+  pool_size: 10 # The amount of database connections in the pool

mipmap

問題

R.drawable.ic_launcher と書くと Cannot resolve symbol ic_launcher というエラーが出る。

解決

drawable ではなく mipmap を使う。R.mipmap.ic_launcher

googledevjp.blogspot.jp


現在使用している Android Studio のバージョンは 1.2.2。プロジェクトのディレクトリをよくよく見ると res/drawable の下には何もなく、res/mipmap の下にはきちんとアイコンが用意されていた。

本に載っていたコードを写していたのだけれど、写しているだけで、そこまで見れていなかった。

Google Play services out of date

問題

Androidアプリ開発時以下のようなログが出力され、アプリが落ちることがあった。

Google Play services out of date. Requires xxxxxxx but found yyyyyyy

xxxxxxx や yyyyyyy バージョン番号。

解決

Google Play 開発者サービス」アプリをアップデートする。

もしかしたら、アップデート後 Android Studio を再起動する必要があるかも。 なぜかというと、アップデート後 マニフェストファイルにパーミッションを追加せよ、というエラーが出力された。でも、そのパーミッションはすでに追加されていた。Android Studio を再起動したところその問題は解消されたので。

リンク

Google Play開発者サービス - Google Play の Android アプリ

UNIXの哲学と関数型プログラミング

UNIXの哲学と関数型プログラミングは共通しているところがあるよね、というのを最近同僚と話した。

ここでいうUNIXの哲学とは

  • スモール・イズ・ビューティフル
  • 一つのプログラムには一つのことを うまくやらせる
  • ソフトウェアの挺子を有効に活用する

など(「UNIXという考え方-その設計思想と哲学」より)。

それらと、関数型プログラミングの、一つの機能に絞った小さな関数を組み合わせ、大きな関数を作っていくという部分が似ているなと。

とりわけ強くそう思うのが、UNIXでコマンドをパイプで繋げて自分の期待する結果を得るところ。関数型プログラミングだと、関数合成にあたる。

UNIXの操作に慣れている人なら、関数型プログラムもしっくりくるのでは。