読者です 読者をやめる 読者になる 読者になる

spring of life

技術、ときどき日常のブログ(予定)

PlantUML のインストール

sphinx

最近あんまり書くことがないな。。。

これもあんまり大した話じゃないし。。。

まぁ、Sphinx で PlantUMLを使えるようにしたって話

  1. Javaのインストール
    • PlantUML を使うにはJavaが必要で、使うにはインストールが必要
    • まぁ自分のPCには既に入っているのでスキップした
  2. PlantUML のダウンロード
    • jar ファイルをダウンロードする
    • $ wget 'http://downloads.sourceforge.net/project/plantuml/plantuml.jar'
  3. sphinxcontrib-plantuml のインストール
    • $ sudo easy_install sphinxcontrib-plantuml
  4. Graphviz のインストール
    • どうやら少なくともクラス図を書くにはこのツールがないとダメらしいのでインストール
    • Mac だとbrew install graphvizでいけた
  5. conf.py に設定追加
    • ↓の設定を追加
extensions = ['sphinxcontrib.plantuml']
plantuml = ['java', '-jar', '/path/to/plantuml.jar']

まだあんまり使ってないんで、使い方とかはここを見ればいいのかな〜
Open-source tool that uses simple textual descriptions to draw UML diagrams.

Web サーバーのモック

Java

雨が降るか降らないか微妙なところでどんよりしててあんま外に出る気が起きない。。。

おかげで作業が捗るわー(´・_・`)(せっかくの3連休だし出かけたい。。。


今回もテストの話で、モックを使ってみた。

HTTPクライアントとか作っててそのクラスのテストをしたい時に、中でリモートのWebサーバーとかにアクセスしてるけど、テストではアクセスしたくないな〜

って思ってWebサーバーのモックを作ってみた。

モック用のライブラリっていろいろあるみたいだけど、個人的に使いやすかった mockwebser っていうのを使った

github.com

jar はここにあった(公開してるのかよく分からないけど。。)

まだあんまり使い方よく分かってないんだけどね〜こんな感じにした

モックサーバーの立て方
@Before
protected void setUp() throws Exception {
  MockWebServer server = new MockWebServer();

  final Dispatcher dispatcher = new Dispatcher() {
    @Override
    public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
      // 以下にリクエストのパスで場合分けしてステータスコードとかレスポンスボディを設定する
      if (request.getPath().equals("/hoge")) {
        return new MockResponse().setResponseCode(200).setBody("{\"hoge\":\"fuga\"}"); // Web サーバーのレスポンスが JSON なら JSON文字列をセット
      } else {
        return new MockResponse().setResponseCode(404);
      }
    }
  };
  server.setDispatcher(dispatcher);

  server.start();
  ...
}
  1. サーバーを作成 (MockWebServer server = new MockWebServer();)
  2. 挙動設定 (final Dispatcher dispatcher = new Dispatcher() { ... })
  3. サーバーを起動 (server.start();)

の流れかな。挙動設定は他にもいろいろあるっぽいから github の方を見た方がいいかも

モックサーバーの使い方

自分で作ったHTTPクライアントの中でモックサーバーにアクセスしてもらうようにするためには、クライアントの中で設定しているホスト名とかポート番号とかを変更しないといけない

クライアント使用時に引数としてURLを渡してる場合なら、

HTTPClient httpClient = new HTTPClient();
httpClient.sendRequest(server.url("/hoge").toString());

フィールドとしてホスト名やポート番号があって、それをWebサーバーにアクセスする時に使ってる場合なら、

HTTPClient httpClient = new HTTPClient();
httpClient = new HTTPClient();
Class<? extends HTTPClient> c = httpClient.getClass();
Field f;
f = c.getDeclaredField("port");
f.setAccessible(true);
f.set(httpClient, String.valueOf(server.getPort()));

f = c.getDeclaredField("host");
f.setAccessible(true);
f.set(httpClient, server.getHostName());

httpClient.sendRequest();

とかで変えれるかな

JUnit でパラメーターを変えながらテストする

Java

あるメソッドのテストで、引数のパターンをいろいろ変えながらテストする時に、パラメーターごとに

@Test
public void testHoge1() {
  assertEquals(hoge(pattern1), fuga);
}

@Test
public void testHoge2() {
  assertEquals(hoge(pattern2), fuga);
}

@Test
...

みたいに引数のパターンごとに書いてて、面倒だし、コードが長くなるな〜と思ってて困ってたんだが、

@Parameterizedっていうのがあるっぽいね

javaworld.helpfulness.jp

自分が書いてるのは、日付のフォーマットチェック(yyy-mm-ddのみOK)だけど、↓のような感じになる

@RunWith(Parameterized.class)
public static class CheckDateTest {
  private String date;
  private boolean expected;
  private InputChecker inputChecker;

  public CheckDateTest(String date, boolean expected) {
    this.date = date;
    this.expected = expected;
    inputChecker = new InputChecker();
  }

  @Parameters(name = "{0} is {1}")
  public static Iterable<Object[]> getParameters() {
    return Arrays.asList(new Object[][] {
      {"1000-01-01", true},
      {"1000/01/01", false},
      {"01-01-1000", false},
      {"01/01/1000", false},
      {"10000101", false},
      {"1000-13-01", false},
      {"1000-01-00", false},
      {"1000-13-00", false}
    });
  }

  @Test
  public void testCheckDate() {
    assertEquals(inputChecker.checkDate(date), expected);
  }
}


まだまだ@RunWithのところにいろんなクラス指定できるらしいから調べてみた方がいいかも?

例えば、Enclosed.classを使うと、@RunWithを入れ子にできたりするっぽい

@RunWith(Enclosed.class)
public class InputCheckerTest extends TestCase {
  @RunWith(Parameterized.class)
  public static class CheckDateTest {
    ...
  }
  
  @RunWith(Parameterized.class)
  public static class CheckPriceTest {
    ...
  }
}

こんな感じでね!

jbuilder の使い方

Rails

使い方、というか自分用にこういう時はどう書けばいいか、というのをメモしておく

まぁ、WebAPI で json を返す時もビューと同じように処理しようかな〜と考えてて、そういう風に実装したんだけど、

まだまだ慣れなくていろいろ調べながらやってしまって時間がかかったので残しとこうかな〜と。

普通にモデルのオブジェクトを json で返す時
json.(@object, :hoge, :fuga, :piyo) 
# これで {"hoge": xxx, "fuga": yyy, "piyo": zzz} の形式の json が返る
# hoge, fuga, piyo はモデルのインスタンス変数 @object の属性
モデルのオブジェクトの配列を json で返す時
json.array! @objects do |object|
  json.(object, :hoge, :fuga, :piyo)
end
# これで [{"hoge": xxx, "fuga": yyy, "piyo": zzz}, {"hoge": ... }] の形式の json が返る
# hoge, fuga, piyo はモデルのインスタンス変数 @object の属性
モデルとは関係のないオブジェクトを json で返す時
# @object = {"hoge" => xxx, "fuga": yyy, "piyo": zzz} の場合

@object.each do |key, value|
  json.set! key, value
end
# これで {"hoge": xxx, "fuga": yyy, "piyo": zzz} の形式の json が返る

ActiveRecord の Date型の検証

Rails

Date型の検証がうまくいってない。。

class Account < ActiveRecord::Base
  ...
  validates :date, :presence => true, :format => {:with => /\A\d{4}-\d{2}-\d{2}\z/}
  ...

な感じで日付のフォーマットを指定してるんだが、

2.2.0 :004 >   a = Account.new(:account_type => 'income', :date => '01-01-1000', :content => 'test', :category => 'test', :price => 100)
 => #<Account id: nil, account_type: "income", date: "1000-01-01", content: "test", category: "test", price: 100, created_at: nil, updated_at: nil> 
2.2.0 :005 > a.validate
 => true 
2.2.0 :006 > a.errors
 => #<ActiveModel::Errors:0x00000005afda70 @base=#<Account id: nil, account_type: "income", date: "1000-01-01", content: "test", category: "test", price: 100, created_at: nil, updated_at: nil>, @messages={}> 
2.2.0 :007 >

多分だけど、Account.newした時点でDate.parseされてる気がするんだよね〜

db/schema

create_table "accounts", force: :cascade do |t|
  t.string   "account_type", limit: 255
  t.date     "date"
  t.string   "content",      limit: 255
  t.string   "category",     limit: 255
  t.integer  "price",        limit: 4
  t.datetime "created_at",               null: false
  t.datetime "updated_at",               null: false
end

と書いてあるから自動的に変換されるのかな。。。と推測

まぁフォーマットなんて気にしない!っていう人はいいけど、new する前に検証するにはどうしたらいいんだろ

別の検証用クラスとか作るのかな〜(・ω・)


ちなみに、t.integerの属性も new した時にto.iされるっぽいね

rails 用の emacs の設定

Emacs

rails でビューのコードとか編集したりするけど、.erb とかのモードを入れてなくて見にくかったので emacs を更新

~/.emacs.elに以下を追加した!

(require 'package)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/#/"))
(add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))
(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/"))
(package-initialize)

(add-to-list 'auto-mode-alist '("\\.erb$"       . web-mode))
(defun web-mode-hook ()
  (setq web-mode-markup-indent-offset   2))
(add-hook 'web-mode-hook 'web-mode-hook)

それで emacs 上でM-x package-installの後、インストールするパッケージを聞かれるのでweb-modeを入力

これで erb はいい具合に色がついてインデントがスペース2つになりましたと

あと、同じように coffeescript のパッケージもインストールした

これもM-x package-installの後、coffee-modeでオッケー

こっちの方は、設定を追加すると javascript の変換結果もリアルタイムで確認できるみたいだね〜

EmacsでCoffeeScriptの変換結果をリアルタイムに確認する - Web学び

まだやってないけど、忘れないようにリンク貼っとこう。。

ページネーションの ajax 対応

Rails

kaminari の ajax 対応にかなり時間かかった。。。

いわゆるページネーションのリンクをクリックしたら(例えば)次のリソース一覧が表示される、とかじゃなくて

ページ上部にリソース登録画面があって、submit ボタン押したらリソース一覧とページネーションのリンクが非同期で更新される

という感じに実装したかった


ちなみに、何作ってるかというと、家計簿を管理するアプリです。完全に自分用ですが。。。

というか作ったのは勉強のためっていうのが大きいかな

今度改めてちゃんと書いとこうっと。まだ他にもアプリ作ってるし

リソース一覧の更新

これは楽勝だった

家計簿を表示する部分テンプレートを用意しといて、それを描画するだけ

accounts_controller.rb

  • WebAPI として使われる場合もあるので、その場合はformat.jsonで処理
  • ajax の場合は js なので家計簿を取得して後で使う変数に代入
def create
  ...
  @account = Account.create!(accounts)
  ...
  respond_to do |format|
    format.json { render :status => :created }
    format.js { @accounts = Account.order(:date => :desc).page(params[:page]) }
  end
  ...
end

accounts.html.erb

  • テーブルのデータ(ヘッダ以外)だけ更新したいので、<tbody>にidをつけて更新
...
<tbody id="accounts">
  <%= render 'account' %>
</tbody>
...

create.js.erb

  • 部分テンプレートを再描画
$('#accounts').html("<%= j(render 'account') %>");

_account.html.erb

<% @accounts.each do |account| %>
<tr>
  <td><%= account.date %></td>
  ...
</tr>
<% end %>

ページネーション部分の更新

これの挙動がさっぱり分からなくて、とりあえず解決したけどまだあまり納得してない。。。

とりあえず↓のように書いたんだけどダメだった

accounts.html.erb

...
<div id="pagination">
  <%= paginate @accounts %>
</div>
...

create.js.erb

  • 再描画時に家計簿一覧と同時に更新
$('#accounts').html("<%= j(render 'account') %>");
$('#pagination').html("<%= j(paginate @accounts) %>");

まぁ、ブラウザにはちゃんとリンクが表示されるんだけども、そのリンク先が意味わからない(´Д` )

create のパスになってて、登録した家計簿のフォームデータがクエリになってついてたんだよね。。。

で、いろいろ調べたんだけど、コントローラーやアクションを変えれるようで

create.js.erb

$('#accounts').html("<%= j(render 'account') %>");
$('#pagination').html("<%= j(paginate @accounts, {:params => {:action => 'hogehoge’}}) %>");

とかいう感じでparamsにcontrollerとかactionとか指定すればいいっぽい

で、これでリンクをクリックしたら次のページとかに飛ぶようになったんだけど、クエリは変わらず付いたまま(´・_・`)

どうしようかな〜と悩んだ末、paramsに消したいクエリのキーも追加するようにした

create.js.erb

$('#accounts').html("<%= j(render 'account') %>");
$('#pagination').html("<%= j(paginate @accounts, {:params => {:action => 'hogehoge’, :accounts => {}}}) %>");

とかいう感じで空ハッシュを指定すると消えるので。

この解決方法がいいのかよく分からんけど、これで見た感じの問題はなくなったかな〜

create のパスになってるのはなんとなく分かるけど、POSTで登録したのがなんでクエリになって付くんだろ。。

kaminari のソースコード読まないとダメかな(^_^;)