Spring BootとVue.jsを使ったシステムを作ってみた

前にこの記事を読んで、vue.jsを入門してた。

GUIアプリケーションアーキテクチャ総合!みたいなやつ書いてる - 猫型の蓄音機は 1 分間に 45 回にゃあと鳴く

実際にギョームで採用したいなーと思ったけど サーバサイドとの結合がよく分からなかったので、簡単なシステムを作ってみた。

github.com

vue.jsで作ったページのボタンをクリックすると、 Spring Bootで作ったHello World WebAPIを呼び出して、 値を画面に表示するっていう簡単なシステム。

出来る限り簡素な作りにしようと思って作った。 READMEに実装の手順を書いてみたので、参考になれば嬉しい。


フロントエンドはvscodeで実装して、 バックエンド(Java)はeclipseで実装するのが一番ラクだった。

ビルド周り凝り始めるとわかりづらくなるので書いてない。

vue.jsはわかり始めるとかなり書きやすいし、 DDD-like Layered Architectureに沿った作りにすると見通しも良くなりそう。

ギョームで使うにはまだ足りない機能が多い

  • バリデーション
  • ユーザ認証
  • Flux的なやつ(vue.js storeでもよさそう)

今日やったリファクタリング - 2 : Enumの値と比較

今日じゃないけど、最近やったリファクタリング

言語はC#.

enumって列挙型で、定数とかを定義するのに使います。
簡単な例で言うと、曜日とかを列挙したり。

public enum Days {
    Sat=1,
    Sun,
    Mon,
    Tue,
    Wed,
    Thu,
    Fri
}

ただ、このままだと結構使いづらくて、こんなコードを書いてしまう場合もあります。

Before

// todayが火曜であることを確認する処理
string today = "4";//どこかから取得した値
bool b = ((int)Days.Tue).ToString().Equals(today);

// 日曜かどうかboolを取りたい場合
bool b = ((int)Days.Sun).ToString().Equals(today);

ひたすらintにcastしてToString().Equals()…!

ダサいので、やめましょう。

After

今回リファクタリングした結果はこんな感じ。

public class Days {
    
    private enum _Days {
        Sat=1,
        Sun,
        Mon,
        Tue,
        Wed,
        Thu,
        Fri
    }

    private _Days val;
    private Days(_Days d) {
        this.val = d;
    }

    public static readonly Days Saturday = new Days(_Days.Sat);
    public static readonly Days Sunday = new Days(_Days.Sun);
    public static readonly Days Monday = new Days(_Days.Mon);
    public static readonly Days Tuesday = new Days(_Days.Tue);
    public static readonly Days Wednesday = new Days(_Days.Wed);
    public static readonly Days Thursday = new Days(_Days.Thu);

    public bool Equals(string value) {
        return Val().Equals(value);
    }

    public string Val() {
        return ((int)this.val).ToString();
    }
}

こんな風にenumをwrapするクラスを作りました。
そんで、使うときには、

// todayが火曜であることを確認する処理
string today = "2";//どこかから取得した値
bool b = Days.Tuesday.Equals(today);

// 日曜かどうかboolを取りたい場合
bool b = Days.Sunday.Equals(today);

毎回castするよりかは可読性は上がりました。


今回は、C#の場合でしたが、Javaでも同じような書き方が出来ます。
Javaではenumに独自メソッド書けるけど、まぁどっちがいいやろかね。

この記事みた後だと、今回の修正後もダサく感じてきます。

C#enum に関連する小技。

qiita.com

C#ではこのやり方が一番良いかも!

以上。

今日やったリファクタリング - 1 : 連番付きフィールド

あまりの糞コードに遭遇し、怒りのリファクタリングを行った。

ついでに「こんなリファクタリングしたよ」と残しておくとなんか良さそうだなと思い付き、
久しぶりにブログを書き始めた。

今日したのは、言語はC#で、
フィールドに連番の付いたフィールドがあり、そのどれかに値が入っていることを調べる処理のリファクタリング

モデルクラスは以下、

public class Model {
    // モデルのフィールドの構造は諸事情により、変更不可とする。
    // メソッドなどは追加してOK。
    public string Value1 { get; set; }
    public string Value2 { get; set; }
    public string Value3 { get; set; }
    public string Value4 { get; set; }
    public string Value5 { get; set; }
    public string Value6 { get; set; }
    public string Value7 { get; set; }
    public string Value8 { get; set; }
    public string Value9 { get; set; }
    public string Value10 { get; set; }
}

このモデルに対し、別の処理でこのどれかに値が入ってくる。 どのValueNに値が入っているか、Nの数値を取得したい。

Before

public class BusinessLogic {
    
    public int GetValueIndex(Model model) {
        if(! string.IsNullOrEmpty(model.Value10)) {
            return 10;
        }
        else if(! string.IsNullOrEmpty(model.Value9)) {
            return 9;
        }
        else if(! string.IsNullOrEmpty(model.Value8)) {
            return 8;
        }
        else if(! string.IsNullOrEmpty(model.Value7)) {
            return 7;
        }
        else if(! string.IsNullOrEmpty(model.Value6)) {
            return 6;
        }
        else if(! string.IsNullOrEmpty(model.Value5)) {
            return 5;
        }
        else if(! string.IsNullOrEmpty(model.Value4)) {
            return 4;
        }
        else if(! string.IsNullOrEmpty(model.Value3)) {
            return 3;
        }
        else if(! string.IsNullOrEmpty(model.Value2)) {
            return 2;
        }
        else if(! string.IsNullOrEmpty(model.Value1)) {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}

あぁ!もう!!!

このIF文乱舞で嫌気が差さない場合は自分を疑ったほうが良い。

私が修正した結果がこちら

After

まず、ModelにValue達をDictionaryで返すメソッドを追加した。

public class Model {
    
    // モデルの構造は諸事情により、変更不可とする。
    public string Value1 { get; set; }
    public string Value2 { get; set; }
    public string Value3 { get; set; }
    public string Value4 { get; set; }
    public string Value5 { get; set; }
    public string Value6 { get; set; }
    public string Value7 { get; set; }
    public string Value8 { get; set; }
    public string Value9 { get; set; }
    public string Value10 { get; set; }

    public Dictionary<int, string> GetValues() {
        return new Dictionary<int, string>() {
            {1, Value1},
            {2, Value2},
            {3, Value3},
            {4, Value4},
            {5, Value5},
            {6, Value6},
            {7, Value7},
            {8, Value8},
            {9, Value9},
            {10, Value10},
        }
    }
}

このメソッドを利用して、BudinessLogicもこう修正した。

public class BusinessLogic {
    
    public int GetValueIndex(Model model) {
        return model.GetValues().Where(v => !string.IsNullOrEmpty(v)).Select(v => v.Key).Max();
    }
}

だいぶマシになった。

ついでにこの後、メソッド名もGetValueIndexでは意味が違っているので、GetMaxValueIndexに変更した。

リファクタリングはテストコードを書いて、現状のメソッド仕様を把握・テスト出来る状態にしてから取り掛かったほうが良いね。

以上。

Selenium WebDriverの画面キャプチャ 拡張子を変える

Selenium WebDriverでキャプチャを撮って、画像ファイルに保存するときに拡張子をjpeg、giff、pngなど変えるやり方。

普通にキャプチャをファイルに保存するには

public void takeScreenShot(Path outputPath) {
    System.out.println("= Screen Shot =");
    File img = ((TakesScreenshot)this.webDriverInstance).getScreenshotAs(OutputType.FILE);
    try {
        FileUtils.copyFile(img, outputPath.toFile());
        System.out.println("Name : " + outputPath.toFile().getAbsolutePath());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

selenium-javaの依存関係にcommons-ioが入っているので、特別追加しなくてもFileUtilsを使える。

画像の拡張子を指定して画像保存する

    /**
     * 起動済みのブラウザでスクリーンショットを撮る。
     */
    public void takeScreenShot(Path outputPath) {
        System.out.println("= Screen Shot =");
        File img = ((TakesScreenshot)this.webDriverInstance).getScreenshotAs(OutputType.FILE);
        try {
            FileUtils.forceMkdirParent(outputPath.toFile());
            ImageIO.write(ImageIO.read(img), "jpeg", outputPath.toFile());
            System.out.println("Name : " + outputPath.toFile().getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

javax.imageio.ImageIOクラスで以下の拡張子に対応してるらしい。

WriterFormatNames:
BMP
bmp
jpeg
wbmp
png
JPG
PNG
jpg
WBMP
JPEG

参考

stackoverflow.com

www.javadrive.jp

typesafe/Config ConfigBeanFactoryの良さそうな使い方

設定ファイルを扱いたくて、イマドキどんな形式が良いんだろうなと調べてみたら、 typesafe/ConfigというライブラリでHOCONというファイル形式があるらしい。

紹介記事や簡単な使い方については、以下を参照。

Java アプリケーションのための設定ファイル形式の比較

Play Frameworkでも使われているコンフィグライブラリが非常に便利


で、

結論から言うと、 ConfigBeanFactory使うなら、typesafe/Config + lombok が良さそう!


typesafe/Configで、version 1.3から追加されたらしいConfigBeanFactoryクラスを使ってみた。

https://github.com/typesafehub/config#configbeanfactory

自作のjava beanクラス AppConfigを生成させている。

    /**
     * application.confを読み込み、Beanで返す。
     * @return AppConfig
     */
    public static AppConfig getConfig() {
        return ConfigBeanFactory.create(load(), AppConfig.class);
    }

読み込ませたい設定ファイルは、こんな感じで、

webdriver {
    chrome=/usr/local/bin/chromedriver
    ie=/usr/local/bin/iedriver
    firefox=/usr/local/bin/firefoxdriver
}
output{
    dirpath=.
    caseid=case
    image{
        extension=jpg
    }
}
browser{
    width=300
    height=300
}

AppConfigクラスはこんな感じで、フィールド名と設定ファイルのkeyが一致している必要がある。
またフィールドに対応するgetter/setterを記述する必要があるが、
そこをlombokで生成(ここでは @Data)してあげることで、
簡易にConfigBeanを実装することが出来ている。

import lombok.Data;

@Data
public class AppConfig {
    private WebDriverConf webdriver;
    private OutputConf output;
    private BrowserConf browser;
}

フィールドの型はこんな風にネストしてBeanを使うことも可能。

import lombok.Data;

@Data
public class WebDriverConf {
    private String chrome;
    private String ie;
    private String firefox;
}

その他のクラスはgithubにおいてあります。

github.com


個人的にはConfigBeanを作ることで、設定のkeyを管理しなくて良くなったので嬉しかった。

でも欠点は、ConfigBeanの値を変更して設定ファイルに保存(シリアライズ)したい時に、typesafeのConfigやConfigObjectに再変換するのが大変なこと。 たぶん地道にMapにpushするみたく、withValueメソッドを呼びまくらないといけない。

少し調べてstackoverflowのどこかのページで、「typesafe/Configはimmutableな設定を扱うツールだよ :)」とあり、設定の変更はあんまりやらない思想なのかな?ということで諦めモード。

一応config.root().render()シリアライズされた文字列が返るので、それをファイルに保存してやれば良い。
ConfigObjectクラスのrenderメソッド

docpressをつかってみた

https://clashm45.github.io/electron-twitter-client/docpress-configuration.html f:id:clash_m45:20160910205957p:plain

docpress Configuration

docpressのサンプルはキレイな色使いがされていますが、デフォルトでは白黒のみです。
docpressの作者よりcssが提供されているので、それを参照するようにしてあげると同じ色使いになります。

docpressの設定はdocpress.jsonに書きます。

docpress.json
{
  "css": [
    "http://ricostacruz.com/docpress-rsc/style.css",
    "docs/assets/custom.css"
  ],
  "markdown": {
    "typographer": true,
    "plugins": {
      "decorate": {},
      "emoji": {}
    }
  },
  "plugins": {
    "docpress-core": {},
    "docpress-base": {}
  }
}

すこしの手間でキレイにHTML化して見栄えの良いサイトになるので、よさ気ですゼ

plugins

docpressはmarkdownの装飾やコンポーネントの追加に、markdown-itを冠したパッケージを導入できるみたいです。

markdown-it-emojiを導入してみました。

:smile:

> Classic markup: :wink:  :cry:  :laughing:  :yum:
>
> Shortcuts (emoticons): :-) :-( 8-) ;)

f:id:clash_m45:20160910210315p:plain

twitter client for electronを写経してみた

WEB+DB PRESS vol.94の "作って学ぶElectron Webの技術でデスクトップアプリ!” を実践したコードをgithubで公開しました。

github.com

ソースコードはgihyo.jpの記事サポートページに公開されています。
コメントが書かれてないので、記事を読まないと初心者には理解できないと思います。

このプロジェクトもほぼ同じコードとなっていますが、自分なりのコメントを追加しています。

作って学ぶElectron Webの技術でデスクトップアプリ!

Electronを利用すると,JavaScript,HTML,CSSなどWebの技術を利用してデスクトップアプリを作成できます。 GitHubが開発していて,Atomエディタに使われているほか,Visual Studio Codeなどにも利用されています。 本特集では,実際にElectronを使ってアプリを開発しているエンジニアにより,Electronの基礎知識からはじめ,実際にサンプルアプリを作成して主要なAPIの使い方,デバッグ/テスト手法,そしてアプリを配布するための手順まで解説します。

Build

Node.jsと、デスクトップ・アプリケーションを作成できるNode.jsフレームワークElectronを使って作成します。

Electronは、JavaScriptとHTMLで実装します。 HTMLコンポーネントを作成するReactフレームワークも使います。

Dependencies

  • "babel-plugin-transform-react-jsx": "^6.8.0",
  • "babel-register": "^6.14.0",
  • "photon": "git+https://github.com/connors/photon.git",
  • "react": "^15.3.1",
  • "react-dom": "^15.3.1",
  • "twit": "^2.2.4"

パッケージのversionに"^"が入っている場合は、最新にアップデートされるらしいです。
実際に開発して公開などする場合は、固定のバージョンを指定するようにしたほうが良さそうです。

Build command

npmで依存パッケージをDLできます。 docpressも依存に追加しているのでDLされます。

npm i

実行コマンド

package.jsonに記述していますが、npmのscripts設定からelectronを起動しています。

npm run start

Document

ついでにdocpressというMarkdownで書くwebsite generatorが紹介されていたので、ドキュメントに使ってみました。

https://clashm45.github.io/electron-twitter-clientclashm45.github.io f:id:clash_m45:20160910203717p:plain

Impressions

ちょうど個人的にElectronを勉強してたところでした。
この記事は、Node.jsとそのパッケージ管理ソフトnpmも、React.jsも実践できるのでいい教材です。

Electronはそう難しくなく、むしろReactの挙動を理解するまでが大変。

以上