スポンサーリンク

2015年10月20日

[Elixir]Timexを使って日時のフォーマット変換を行う

とある錬金術師の万能薬(Elixir)

Goal

TimexのDateFormatを使う。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Timex: v0.19.5

Context

入力フォームからyear、month、day…っと言ったように各個に入力されたデータを、
“2015-10-21T14:52:00”のようなフォーマットに変換したい。
今のところ、日時関連を扱うライブラリだとTimex一択なので、それを使う。
しかし、変換の仕方がよく分からなかったので、忘れないようにメモ。
Timexで使われるオブジェクトを指定した値で生成する。

Example:

iex> use Timex
nil
iex> date = Date.from({{2015, 10, 21}, {14, 00, 00}})
%Timex.DateTime{calendar: :gregorian, day: 21, hour: 14, minute: 0, month: 10,
 ms: 0, second: 0,
 timezone: %Timex.TimezoneInfo{abbreviation: "UTC", from: :min,
  full_name: "UTC", offset_std: 0, offset_utc: 0, until: :max}, year: 2015}
日付を変換する。

Example:

iex> ymd = DateFormat.format!(date, "%Y-%m-%d", :strftime)
"2015-10-21"
(ymd = やればもっとできる)

Example:

iex> hms = DateFormat.format!(date, "{h24}:{0m}:{0s}")
"14:00:00"
二つの文字列を連結する。

Example:

iex> ymd <> "T" <> hms
"2015-10-21T14:00:00"
これでできあがり。
(時間の標準規格(ISO)って、末尾にZ付いたっけ?まぁいいや…)
何でこんなめんどくさいことやってんの?って話ですけれど…
FullCalendarでイベントに指定する時間と言うのがこのフォーマットなんです。
ちなみに…FullCalendarで、
“2015-10-1T1:00:00”とかでイベントのstartかendを指定すると、イベントに表示されないので注意です。

追記

Z必要でした。
正しくは、”2015-10-1T1:00:00Z”でした。
ついでに、逆にParseする一例を書いておきます。

Example:

iex> DateFormat.parse!("2015-10-21T14:00:00Z", "{ISO}")

Bibliography

2015年10月19日

Phoenixにajax-requestを投げる

Goal

Phoenixにajaxリクエストを投げる。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V7.1, OTP-Version 18.1
Elixir: v1.1.1
Phoenix Framework: v1.0.3
PostgreSQL: postgres (PostgreSQL) 9.4.4

Text

色々と検証をしていたら、
ajaxにリクエストを投げることもやったので忘れないように簡単にまとめる。
(jQueryが必要なので、アプリケーションに読み込みをしておいて下さい)
getとpostリクエストを投げてみます。
まずは、getからやっていきます。
newとcreateのルーティングを追加。

Example:

scope "/", Sample do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  resources "/events", PageController, only: [:new, :create]
end
デフォルトのPageコントローラにアクションを追加。

Example:

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

  ...

  def new(conn, _params) do
    render conn, "new.html"
  end

  def create(conn, _params) do
    render conn, "new.html"
  end
end
newテンプレートは適当に作る。中身のない空でも良い。

Example:

<h1>New!</h1>
index.html.eexに以下の内容を追加する。

Example:

<script language="JavaScript">
function sample() {
  $.ajax({
    url: "<%= page_path(@conn, :new) %>",
    type: "get",
    data: {event: {value1: "hoge", value2: "huge"}},
    success: function (data) {
      console.log(data);
      alert("success:" + data);
    },
    error: function (error) {
      console.log(error);
      alert("error:" + error);
    }
  });
};
</script>

<a href="#" onClick="sample(); return false;">ajax request</a>
  • url: リクエストを投げるパス
  • type: HTTPメソッド
  • data: 送信するパラメータ
  • success: 成功時の処理
  • error: 失敗時の処理
dataは投げなくてもいいんですが、一応パラメータとして送れるのか確認のため設定しています。

Result:

aタグのリンクをクリックすると、newアクションが動作をしてHTMLを返します。
alertで出力していますので、HTMLが返ってくることを確認できると思います。
これは、Phoenix側でHTMLを返すようにしているためです。
JSONのAPIサーバとして実装していれば、JSONが返ってくると思います。
サーバ側のログを見てみると…
[info] GET /events/new
[debug] Processing by Sample.PageController.new/2
  Parameters: %{"event" => %{"value1" => "hoge", "value2" => "huge"}}
  Pipelines: [:browser]
newアクションが呼び出され、パラメータの値も確認できますね。
データ形式的にはJSONっぽく投げていましたが、Elixirのマップに変換されているようです。
おかげ様で、アクションの引数にてパターンマッチで取得できますね。
次は、postを投げてみます。
index.html.eexに以下の内容に変更する。

Example:

<script language="JavaScript">
function sample() {
  $.ajax({
    url: "<%= page_path(@conn, :create) %>",
    type: "post",
    data: {event: {value1: "hoge", value2: "huge"}},
    headers: {
      "X-CSRF-TOKEN": document.querySelector("meta[name=csrf]").content
    },
    success: function (data) {
      console.log(data);
      alert("success:" + data);
    },
    error: function (error) {
      console.log(error);
      alert("error:" + error);
    }
  });
};
</script>

<meta name="csrf" content="<%= Plug.CSRFProtection.get_csrf_token() %>">
<a href="#" onClick="sample(); return false;">ajax request</a>
csrfトークンの追加とpathとtypeを変更しただけです。
postを投げる場合は、csrfトークンも一緒に投げてあげると良いかと思います。
(対策的に考えて。)

Result

[info] POST /events
[debug] Processing by Sample.PageController.create/2
  Parameters: %{"event" => %{"value1" => "hoge", "value2" => "huge"}}
  Pipelines: [:browser]
ちゃんと、createアクションが呼び出されていますね。
簡単なまとめなので以上。
分からないことがあれば、コメントにでも書いて頂ければ対応します。

Bibliography

なし

2015年10月18日

[Elixir+Phoenix]prod実行する

Goal

Phoenix-Frameworkでprod実行をする。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V7.1, OTP-Version 18.1
Elixir: v1.1.1
Phoenix Framework: v1.0.3
PostgreSQL: postgres (PostgreSQL) 9.4.4

Content

何のかんのdevかtest環境でしか実行したことがなかったので、
prod実行をしてみた。
手順の簡単なまとめ。
(Linuxだと少し異なる)

Example:

> set MIX_ENV=prod
> echo %MIX_ENV%
prod

> set PORT=4001
> echo %PORT%
4001

> mix phoenix.digest

> mix ecto.create
> mix ecto.migrate
> mix run priv/repo/seeds.exs (初期データが必要なら)

> mix phenix.server
Windowsだと環境変数を”mix phoenix.server”実行時に指定できないため、先に設定しています。
以上。

Bibliography

Phoenix Framework (DEPLOYMENT - Introduction): http://www.phoenixframework.org/docs/deployment

2015年10月17日

[Elixir+Phoenix]Ectoでpreloadする対象にorder_byしたい

Goal

Ectoでpreloadする対象にorder_byを行う。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V7.1, OTP-Version 18.1
Elixir: v1.1.1
Phoenix Framework: v1.0.3
PostgreSQL: postgres (PostgreSQL) 9.4.4

Content

ふと思いました。
Ectoのpreloadで取得しているデータを昇順または降順に並び替えたい時どうすればいいんだろう…っと。
Ectoのドキュメントに書いてありますが、忘れないようにTipsとしてアップします。
(但し、超簡略版…)
二つのモデルがあるとする。
(投稿とコメント)

Example:

defmodule LazyAlchemist.Post do
  use LazyAlchemist.Web, :model

  alias LazyAlchemist.Comment

  schema "posts" do
    field :title, :string
    field :content, :string
    field :permalink, :string

    has_many :comments, Comment

    timestamps
  end

  @required_fields ~w(title content permalink)
  @optional_fields ~w()

  ...
end

Example:

defmodule LazyAlchemist.Comment do
  use LazyAlchemist.Web, :model

  alias LazyAlchemist.Post

  schema "comments" do
    field :name, :string
    field :comment, :string

    belongs_to :post, Post, foreign_key: :post_id

    timestamps
  end

  @required_fields ~w(post_id name comment)
  @optional_fields ~w()

  ...
end
二つのモデル間では、has_manyとbelongs_toの設定をしている。
そして、投稿を表示する時にpreloadして、コメントのデータは取得します。
但し、この時にコメントのデータは、コメントが入力された日付でdesc(降順)にソートされたデータとして取得をしたい。

Example:

Repo.get_by(Post, permalink: permalink)
|> Repo.preload(:comments) <- ソートしたい

Q: preloadで取得するデータをorder_byを使ってソートするにはどうしたら良いでしょうか?

A: preloadの中にさらにクエリを記述することができるので、fromをpreloadの中に記述する。

分かり辛いのでサンプルを提示します。

Example:

Repo.get_by(Post, permalink: permalink)
|> Repo.preload(comments: (from c in Comment, order_by: [desc: c.inserted_at]))
つまり、こういう記述ができるとのこと。
大体ドキュメントに書いてある。
書き方はこれだけじゃないので、詳しいところはpreloadのドキュメントを参照すること。
以上。

Bibliography

2015年10月15日

[Elixir+Phoniex]JavaScriptへ値を渡す方法まとめ

Goal

Phoenix-FrameworkでのJavaScriptへの値の渡し方を習得する。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V7.1, OTP-Version 18.1
Elixir: v1.1.1
Phoenix Framework: v1.0.3
PostgreSQL: postgres (PostgreSQL) 9.4.4

Wait a minute

この記事ではJavaScriptへ、
Phoenix-Frameworkで使っている変数の値を渡す方法を記述しています。
後半の方は知的好奇心を満たすためだけのものですので、ご注意下さい。
Ruby on RailsでJavaScriptへ渡している方法と同じなので、
そちらの方法をご存知の方は見る必要がないと思います。

Index

From Phoenix to JavaScript you pass a value
|> Before you start
|> Pass a value
|> Using content_tag/3
|> Little experiment
|> Extra

Before you start

検証を行うためのプロジェクトを準備します。
既に作成している適当なプロジェクトがあれば、そちらで試しても構いません。

Example:

>cd path/to/create/directory

>mix phoenix.new pass_by_value

>cd pass_by_value

>mix ecto.create

>mix phoenix.server
Ctrl+C
準備終わり。

Pass a value

早速、JavaScriptへ値を渡してみましょう。
デフォルトで生成されているPageコントローラのindexアクションで適当な変数を渡します。

File: web/controllers/page_controller.ex

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

  def index(conn, _params) do
    render conn, "index.html", hoge: "hoge"
  end
end
indexテンプレートを以下のように編集して下さい。

File: web/templates/page/index.html.eex

<script language="JavaScript">
function sample() {
  var e = document.getElementById("hoge");
  var value = e.getAttribute("data-hoge");
  window.alert(value)
}
</script>

<div id="hoge" data-hoge="<%= @hoge %>">
  <a onclick="sample()">hoge</a>
</div>
動作的には、aタグのリンクをクリックすると渡している値をアラートで表示するだけです。
HTML5にある独自データ属性を利用しています。
(data-*で記述される属性です。)

Using content_tag/3

Phoenix.HTMLのcontent_tag/3関数を利用して、書き換えてみます。

File: web/templates/page/index.html.eex

<script language="JavaScript">
function sample() {
  var e = document.getElementById("hoge");
  var value = e.getAttribute("data-hoge");
  window.alert(value)
}
</script>

<%= content_tag(:div, raw("<a onclick=\"sample()\">hoge</a>"),
                  id: "hoge", data: [hoge: @hoge]) %>
少し、ごちゃごちゃしたような気が…まぁいいや。

Little experiment

幾つか思いついたことを実験してみます。

Q: Elixirの埋め込みコード使えるの?

A: 使えます。

記述しているのはEExテンプレートに対してですので、
こういう風にElixirコードの埋め込みでも書けます。

Example:

<script language="JavaScript">
function sample() {
  window.alert("<%= @hoge %>")
}
</script>

<a onclick="sample()">hoge</a>
ただ、個人的にはあんまりよくない書き方な気がする。
JavaScriptのコードに対して、埋め込みとはいえ別言語の記述が混じるわけですから。
最終出力は、結局HTMLやJavaScriptになるとしても、
プログラムをする時に、複数の言語が混在するわけですから…

Q: マップやリストの値を渡したらどうなるか?

A: 実行時エラーになる。

渡す値のデータ形式がElixirのマップやリストだったらどうなるでしょうか?
まずは、content_tag/3を使って渡してみた。
実行時エラーとなりました。
正確に書くなら、content_tag/3の引数(属性)を解釈する段階で失敗する。
属性の解釈ができないと書いた方が良いかもしれない。
関数の問題なのでどうにもならん。
(詳しく知りたければ、Phoenix_HTMLのソース見て下さい。)
おそらく、マップを渡しても同様のエラーになるのが簡単に予想できるので、
content_tag/3を使わないで渡してみる。

Example:

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

  def index(conn, _params) do
    render conn, "index.html", hoge: ["hoge", "huge", "foo", "bar"]
  end
end

<div id="hoge" data-hoge="<%= @hoge %>">
  <a onclick="sample()">hoge</a>
</div>
実行して動作を確認したが、リストの内容が一つの文字列(?)になって表示される。
[“hoge”, “huge”, “foo”, “bar”] → hogehugefoobar、といったように。
とりあえず、実行はできるようなのでマップの方も渡してみる。

Example:

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

  def index(conn, _params) do
    render conn, "index.html", hoge: %{"foo" => "bar"}
  end
end
実行エラーで落ちますね。
エラーの内容を読むと、EEx(HTML)テンプレートで解釈できるデータ形式じゃないからっぽいですね。
そりゃそうか…ってことはEEx(HTML)テンプレートをこう変えてあげれば問題ないですね。

Example:

<div id="hoge" data-hoge="<%= @hoge["foo"] %>">
  <a onclick="sample()">hoge</a>
</div>
こうすれば、key:”foo”の値がでますね。
しかしそうすると、Ectoでよく使うデータ形式である、
リストマップ([%{…}, %{…}])で複数のデータを渡したい時はどうしましょうね。
もう少し、実験してみましょう。

Q: EctoでDBを検索して返ってくる値を渡せないの?

A: JSON形式に変換して渡す

この方法が正しいのか分かりません。
ただ、複数の値を渡すための共通のデータ形式が、JSONしか思いつかなかったんです。
なので、一例としてって程度で…
これは動かない…

Example:

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

  def index(conn, _params) do
    render conn, "index.html", users: [%{name: "hoge"},
                                      %{name: "huge"},
                                      %{name: "foo"},
                                      %{name: "bar"}]
  end
end

<%= for user <- @users do %>
  <div id="user" data-user-name="<%= user.name %>">
    <a onclick="sample()"><%= user.name %></a>
  </div>
<% end %>
いっそのこと、JavaScriptの関数の引数で渡してみましょうか。
そうすれば、JavaScriptの関数内に直接埋め込みコードを記述するわけではないので、見苦しくはならないはず…
しかし、マップは参照してあげないとEExで解釈できない。
よし!JSONにエンコードしたデータを渡そう!!(妙案)
これで、一応実行できる。

Example:

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

  def index(conn, _params) do
    users = Poison.encode!([%{name: "hoge", age: 20},
                            %{name: "huge", age: 21},
                            %{name: "foo", age: 23},
                            %{name: "bar", age: 25}])
    render conn, "index.html", users: users
  end
end

<script language="JavaScript">
function sample(users) {
  console.info(users)
  window.alert(users)
}
</script>

<a onclick="sample(<%= @users %>)">users</a>
しかし、視覚的にはConsoleからしか確認できないため、
画面に出力できるように少し修正する。

Example:

<script language="JavaScript">
function sample(users) {
  console.info(users)
  for(var i in users) {
    $("#output").append("<li>" + users[i].name + "(" + users[i].age + ")" + "</li>")
  }
}
</script>

<a onclick="sample(<%= @users %>)">users</a>
<ul id="output"></ul>
usersリンクをクリックすると各ユーザのデータがliタグで表示されます。
段々と脱線してきたので、これで実験終わりにします。
動かなかったら、多分jQueryが必要かも…?

Speaking to oneself

お好きな方法をお使い下さい。
どれを使うかの判断は、お任せします。
Phoenix-FrameworkでFullCalendarを使っているサンプルにて、参考になるソースコードを作成しています。
Github: https://github.com/darui00kara/full_calendar_sample

Bibliography

HTML5.jp: http://www.html5.jp/tag/attributes/data.html
JavaScript プログラミング講座: http://hakuhin.jp/js/json.html
JavaScriptのデバッグが捗る!コンソールにログを出力する方法: http://www.hp-stylelink.com/news/2014/01/20140112.php
設計力に学ぶデザインドリル jQuery入門: http://www.jquerystudy.info/index.html

人気の投稿