スポンサーリンク

2015年8月26日

[Rails Tutorial for Phoenix]Templates refactoring

Goal

テンプレートをリファクタリングする。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Phoenix Framework: v0.13.1
PostgreSQL: postgres (PostgreSQL) 9.4.4

Wait a minute

遅れました~すいません。
リファクタリング第三弾!
ここでやること・・・
  • カスタムCSSの導入
  • 記述/名称の修正
  • テンプレート分割
  • 冗長な部分の共通化

Index

Templates refactoring
|> The introduction of custom CSS
|> Improvement of template
|> Modify the view

The introduction of custom CSS

カスタムCSSを導入します。
遅すぎる導入ですが、ここまで来ると修正も面倒な状態に・・・
Rails Tutorial(Github)のcustom.css.scssを使いたかったのですが、
Phoenix-Frameworkでscssを利用できなかったため、一度CSSに変換して利用しています。
多分、Phoenix-Frameworkだとscss(sass?)に対応してないのでしょう。

ファイル: priv/static/css/custom.css (新規)

@import url("app.css");

/* universal */
html {
  overflow-y: scroll;
}

body {
  padding-top: 60px;
}

section {
  overflow: auto;
}

textarea {
  resize: vertical;
}

.center {
  text-align: center;
}

.center h1 {
  margin-bottom: 10px;
}

/* typography */
h1, h2, h3, h4, h5, h6 {
  line-height: 1;
}

h1 {
  font-size: 3em;
  letter-spacing: -2px;
  margin-bottom: 30px;
  text-align: center;
}

h2 {
  font-size: 1.2em;
  letter-spacing: -1px;
  margin-bottom: 30px;
  text-align: center;
  font-weight: normal;
  color: #777777;
}

p {
  font-size: 1.1em;
  line-height: 1.7em;
}

/* header */
#logo {
  float: left;
  margin-right: 10px;
  font-size: 1.7em;
  color: white;
  text-transform: uppercase;
  letter-spacing: -1px;
  padding-top: 9px;
  font-weight: bold;
  line-height: 1;
}

#logo:hover {
  color: white;
  text-decoration: none;
}

/* footer */
footer {
  margin-top: 45px;
  padding-top: 5px;
  border-top: 1px solid #eaeaea;
  color: #777777;
}

footer a {
  color: #555555;
}

footer a:hover {
  color: #222222;
}

footer small {
  float: left;
}

footer ul {
  float: right;
  list-style: none;
}

footer ul li {
  float: left;
  margin-left: 10px;
}

/* miscellaneous */
.debug_dump {
  clear: both;
  float: left;
  width: 100%;
  margin-top: 45px;
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

/* sidebar */
aside section {
  padding: 10px 0;
  border-top: 1px solid #eeeeee;
}

aside section:first-child {
  border: 0;
  padding-top: 0;
}

aside section span {
  display: block;
  margin-bottom: 3px;
  line-height: 1;
}

aside section h1 {
  font-size: 1.4em;
  text-align: left;
  letter-spacing: -1px;
  margin-bottom: 3px;
  margin-top: 0px;
}

.gravatar {
  float: left;
  margin-right: 10px;
}

.stats {
  overflow: auto;
}

.stats a {
  float: left;
  padding: 0 10px;
  border-left: 1px solid #eeeeee;
  color: gray;
}

.stats a:first-child {
  padding-left: 0;
  border: 0;
}

.stats a:hover {
  text-decoration: none;
  color: #3677a3;
}

.stats strong {
  display: block;
}

.user_avatars {
  overflow: auto;
  margin-top: 10px;
}

.user_avatars .gravatar {
  margin: 1px 1px;
}

/* forms */
input, textarea, select, .uneditable-input {
  border: 1px solid #bbb;
  width: 100%;
  margin-bottom: 15px;
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

input {
  height: auto !important;
}

#error_explanation {
  color: #f00;
}

#error_explanation ul {
  list-style: none;
  margin: 0 0 18px 0;
}

/* Users index */
.users {
  list-style: none;
  margin: 0;
}

.users li {
  overflow: auto;
  padding: 10px 0;
  border-top: 1px solid #eeeeee;
}

.users li:last-child {
  border-bottom: 1px solid #eeeeee;
}

/* microposts */
.microposts {
  list-style: none;
  margin: 10px 0 0 0;
}

.microposts li {
  padding: 10px 0;
  border-top: 1px solid #e8e8e8;
}

.content {
  display: block;
}

.timestamp {
  color: #777777;
}

.gravatar {
  float: left;
  margin-right: 10px;
}

aside textarea {
  height: 100px;
  margin-bottom: 5px;
}

ファイル: web/templates/layout/applicaiton.html.eex (追加)

CSSの読み込みを追加。
<link rel="stylesheet" href="<%= static_path(@conn, "/css/custom.css") %>">

Improvement of template

テンプレートの改善をしていきます。
Caution:
修正した分量多いので、修正前の状態は記述しません。

ファイル: web/templates/user/input_form.html.eex (変更)

名称を変更しました。
変更後: web/templates/user/user_form.html.eex

ファイル: web/templates/user/new.html.eex (変更)

input_form.html.eexの名称変更に伴う変更。
<h1>Sign up</h1>

<%= render "user_form.html", changeset: @changeset,
                        action: user_path(@conn, :create) %>

ファイル: web/templates/user/edit.html.eex (変更)

input_form.html.eexの名称変更に伴う変更。
<h2>Edit UserProfile</h2>

<%= render "user_form.html", changeset: @changeset,
                        action: user_path(@conn, :update, @user) %>

ファイル: web/templates/user/index.html.eex (修正)

テンプレートを改善。(分割と共通化)
<h1>All users</h1>

<%= if !is_empty_list?(@users) do %>
  <%= render SampleApp.PaginationView, "pagination.html",
             action: user_path(@conn, :index),
             current_page: @current_page,
             page_list: @page_list,
             total_pages: @total_pages %>

  <ul class="users">
    <%= for user <- @users do %>
      <%= render "user.html", conn: @conn, user: user %>
    <% end %>
  </ul>

  <%= render SampleApp.PaginationView, "pagination.html",
             action: user_path(@conn, :index),
             current_page: @current_page,
             page_list: @page_list,
             total_pages: @total_pages %>

<% end %>

ファイル: web/templates/user/show.html.eex (修正)

テンプレートを改善。(分割と共通化)
<h2>User profile</h2>

<div class="row">
  <aside class="span4">
    <section>
      <%= render SampleApp.SharedView, "user_info.html", conn: @conn, user: @user %>
    </section>
    <section>
      <%= render SampleApp.SharedView, "stats.html", conn: @conn, user: @user %>
    </section>
    <%= if current_user?(@conn, @user) do %>
      <section>
        <%= link "Edit", to: user_path(@conn, :edit, @user), class: "btn btn-default btn-xs" %>
        <%= link "Delete", to: user_path(@conn, :delete, @user), method: :delete, class: "btn btn-danger btn-xs" %>
      </section>
    <% end %>
    <section>
      <%= render "micropost_form.html", conn: @conn, changeset: @changeset, user: @user %>
    </section>
  </aside>

  <div class="span8">
    <%= render "follow_form.html", conn: @conn, user: @user %>

    <h3>Microposts</h3>
    <%= unless is_empty_list?(@posts) do %>
      <%= render SampleApp.SharedView, "microposts.html", conn: @conn, posts: @posts, user: @user %>

      <%= render SampleApp.PaginationView, "pagination.html",
               action: user_path(@conn, :show, @user),
               current_page: @current_page,
               page_list: @page_list,
               total_pages: @total_pages %>
    <% end %>
  </div>
</div>

ファイル: web/templates/user/show_follow.html.eex (修正)

テンプレートを改善。(分割と共通化)
<div class="row">
  <aside class="span4">
    <section>
      <%= render SampleApp.SharedView, "user_info.html", conn: @conn, user: @user %>
    </section>
    <section>
      <%= render SampleApp.SharedView, "stats.html", conn: @conn, user: @user %>
      <%= if @users do %>
        <div class="user_avatars">
          <%= for follow_user <- @users do %>
            <a href="<%= user_path(@conn, :show, follow_user) %>">
              <img src="<%= get_gravatar_url(follow_user) %>" class="gravatar">
            </a>
          <% end %>
        </div>
      <% end %>
    </section>
  </aside>

  <div class="span8">
    <h3>Users</h3>
    <%= if @users do %>
      <ul class="users">
        <%= for follow_user <- @users do %>
          <%= render "user.html", conn: @conn, user: follow_user %>
        <% end %>
      </ul>

      <%= render SampleApp.PaginationView, "pagination.html",
               action: @action,
               current_page: @current_page,
               page_list: @page_list,
               total_pages: @total_pages %>
    <% end %>
  </div>
</div>

ファイル: web/templates/user/user.html.eex (新規)

<li>
  <img src="<%= get_gravatar_url(@user) %>" class="gravatar">
  <%= link @user.name, to: user_path(@conn, :show, @user) %>
</li>

ファイル: web/templates/user/micropost_form.html (新規)

<%= if current_user?(@conn, @user) do %>
  <%= form_for @changeset, micropost_path(@conn, :create), fn f -> %>
    <%= hidden_input f, :user_id %>
    <%= if f.errors != [] do %>
      <div class="alert alert-danger">
        <p>Oops, something went wrong! Please check the errors below:</p>
        <ul>
          <%= for {attr, message} <- f.errors do %>
            <li><%= humanize(attr) %> <%= message %></li>
          <% end %>
        </ul>
      </div>
    <% end %>

    <div class="form-group">
      <label>Content</label>
      <%= textarea f, :content, class: "form-control" %>
      <%= submit "Post", class: "btn btn-primary" %>
    </div>
  <% end %>
<% end %>

ファイル: web/templates/user/follow_form.html (新規)

<%= unless current_user?(@conn, @user) do %>
  <div id="follow_form">
  <%= if following?(@conn, @user.id) do %>
    <%= form_tag(relationship_path(@conn, :delete, current_user(@conn)), method: :delete) %>
      <input type="hidden" name="unfollow_id" value="<%= @user.id %>">
      <%= submit "Unfollow", class: "btn btn-large" %>
    </form>
  <% else %>
    <%= form_tag(relationship_path(@conn, :create), method: :post) %>
      <input type="hidden" name="id" value="<%= current_user(@conn).id %>">
      <input type="hidden" name="follow_id" value="<%= @user.id %>">
      <%= submit "Follow", class: "btn btn-large btn-primary" %>
    </form>
  <% end %>
  </div>
<% end %>

ディレクトリ: web/templates/shared/ (新規)

ディレクトリを作成。

ファイル: web/templates/shared/microposts.html.eex (新規)

<ol class="microposts">
  <li>
  <%= for post <- @posts do %>
    <span class="content"><%= post.content %></span>
    <span class="timestamp">
      Posted <%= post.inserted_at %> ago.
    </span>
    <%= link "Delete", to: micropost_path(@conn, :delete, post), method: :delete, class: "btn btn-danger btn-xs" %>
  <% end %>
  </li>
</ol>

ファイル: web/templates/shared/stats.html.eex (新規)

<div class="stats">
  <a href="<%= user_path(@conn, :following, @user) %>">
    <strong id="following" class="stat">
      (<%= Enum.count(@user.followed_users) %>)
    </strong>
    following
  </a>
  <a href="<%= user_path(@conn, :followers, @user) %>">
    <strong id="followers" class="stat">
      (<%= Enum.count(@user.followers) %>)
    </strong>
    followers
  </a>
</div>

ファイル: web/templates/shared/user_info_html.eex (新規)

<a href="<%= user_path(@conn, :show, @user) %>">
  <img src="<%= get_gravatar_url(@user) %>" class="gravatar">
</a>
<h1><%= @user.name %></h1>
<span>
  <%= link "view profile", to: user_path(@conn, :show, @user), class: "btn btn-default btn-xs" %>
</span>

Modify the view

ビューの変更を行います。

ファイル: web/views/shared_view.ex (新規)

defmodule SampleApp.SharedView do
  use SampleApp.Web, :view
end

ファイル: web/views/user_view.ex (関数削除)

defmodule SampleApp.UserView do
  use SampleApp.Web, :view

  def is_empty_list?(list) when is_list(list) do
    list == []
  end

  def following?(conn, follow_user_id) do
    SampleApp.Relationship.following?(conn.assigns[:current_user].id, follow_user_id)
  end
end

ファイル: lib/helpers/view_helper.ex (関数追加)

defmodule SampleApp.Helpers.ViewHelper do
  def current_user(conn) do
    conn.assigns[:current_user]
  end

  def current_user?(conn, %SampleApp.User{id: id}) do
    user = SampleApp.Repo.get(SampleApp.User, id)
    conn.assigns[:current_user] == user
  end

  def get_gravatar_url(%SampleApp.User{email: email}) do
    SampleApp.Gravator.get_gravatar_url(email, 50)
  end
end

Speaking to oneself

テンプレートのリファクタリングは完了。
Rails Tutorialでやっているテンプレート構成に修正しました。
最初からやれよ!?って話ですが、
CSSの適応ができなかったので、仕方なく自分で作っていました。
(その結果が散々なあれですが・・・)
CSSの適応方法が分かったので、今回Rails Tutorialの方へ合わせました。
(まだ、足りないところはありますが・・・)
後は、CSSの修正ですね・・・デザインがRails Tutorialのように表示されません。
こいつも追々やっていきます。
今回の差分はこちら・・・
Github: darui00kara/phoenix_tutorial - Templates refactoring
ってかこれ、リファクタリングってより・・・改修っぽい?

Bibliography

人気の投稿