mix phoenix.new のオプション

アプリケーション作成時のオプションに何があるか調べてみた(といっても、大して調べてないけれど)。

答えはここに

  • --app
    • アプリケーション名?よくわかってない。
  • --module
    • モジュール名を指定する。phoenix.new したときのディレクトリ名とモジュール名を異なる名前にできる。
  • --database
  • --no-brunch
    • brunchを使わないときに指定する。APIサーバとして使うならこれを指定した方がいいかも。
  • --no-ecto
    • Ectoを使用しない場合に使用。

Phoenix のアップデート

v0.17.0 がリリースされていたので更新してみる。フレームワーク(というか、Mixでインストールしたライブラリ)をアップデートするのは今回が初めて。

% mix archive.install https://github.com/phoenixframework/phoenix/releases/download/v0.17.0/phoenix_new-0.17.0.ez
Found existing archive(s): phoenix_new-0.16.1.ez.
Are you sure you want to replace them? [Yn] y
* creating .mix/archives/phoenix_new-0.17.0.ez

特別悩むことはなくアップデート完了。インストールと同じコマンドで大丈夫だった。

変更点は http://www.phoenixframework.org/blog/upgrading-from-v016-to-v017 に書かれている。

大きな変更はないけれど、個人的に良かったと思う点は、パラメータに %{"format" => "html"} が追加されなくなったところ。jsonしか返さないAPIサーバを作る場合とか邪魔だったので。

求めていたのはinspect/2だった

to_string/1 ではなくinspect/2 だった。

iex(1)> to_string %{a: 1, b: "hoyhoy"}
** (Protocol.UndefinedError) protocol String.Chars not implemented for %{a: 1, b: "hoyhoy"}
    (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir) lib/string/chars.ex:17: String.Chars.to_string/1
iex(1)> inspect %{a: 1, b: "hoyhoy"}
"%{a: 1, b: \"hoyhoy\"}"

Map には to_string が定義されていないので、どうすればログに出力できるのかを悩んでいたのだけれど、これで解決。

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 の下にはきちんとアイコンが用意されていた。

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