kondoumh のブログ

- とあるソフトウェアエンジニアのめったに更新されないブログ -

Pixel 3 XL Just Black 64GB を購入

Nexus 6P 購入してから丸3年。

blog.kondoumh.com

Android の更新も止まってしまい iPhone に回帰しようかとも思いましたが、じっと待ってました。そして10月になり Pixel が3世代目で初めて日本に投入されました。

発表直後にプリオーダーして11月4日到着。付属品の数々。イヤホンジャックがなくなったので USB-C 用のアダプタも入っています。

f:id:kondoumh:20181104152717j:plain

Nexus 6P (左) と比べるとやや小ぶりになっています。Nexus 6P で使ってた IIJmio の Nano SIM を装着。Pixel 3 は eSIM と Nano SIM のデュアルではないかという噂もありましたが、日本版は Nano SIM オンリーでした。SIM スロットは本体下にあります。

f:id:kondoumh:20181104152953j:plain

セットアップ時に旧デバイスからの移行を選択すると、アプリデータ移行のため Nexus 6P と Pixel 3 を USB-C ケーブルと付属の Quick Switch Adapter で接続するようにガイドされます。画面のガイドに従っていくとあっという間に終わりました。クロスケーブルなのでネット経由しないのもよい感じです。まあ、ローカルにしかないデータはほとんど無いので必須の作業ではないですが、普段使うアプリのインストールがオートになるのでかなりシームレスになります。

f:id:kondoumh:20181104155203j:plain

狭額縁と切り欠き(ノッチ)により画面サイズが約1インチ大きくなっているのがわかります。

f:id:kondoumh:20181104183513j:plain

Nexus 6P も有機 EL ディスプレイで視認性は高いのですが、屋外ではやや暗めでした。Pixel 3 の屋外での視認性は Nexus 6P をかなり上回っているように思います。

サイズ感は Nexus 6P とほとんど変わらず、指紋認証のセンサー位置や、各スイッチの位置もほぼ変わらないので持ち運んでいるときの操作感もそのまま。ただ、背面がガラスでつるつる感が凄いので滑りそうです。

Android 9 Pie 初体験。スクリーンの最下列が Dock のように固定化され、検索窓も下に来て、ベゼルレスでも使いやすさが損なわれないように工夫されています。iPhone XS は店頭で触ったことがあるだけですが操作感も似ている感じです。

f:id:kondoumh:20181104171104j:plain

ということで Pixel ユーザになりました。Nexus 時代と比べてメジャー感が増し、スマートフォンのトレンドにキャッチアップした機種に仕上がっていると思います。カメラや FeliCa も試していきたいと思います。

ブログのカテゴリーに説明文を入れてみました

はてなブログ公式ブログから記事のカテゴリーに説明文を入れられるようになったとアナウンスがありました。

staff.hatenablog.com

カテゴリー編集用のフォームに項目が増えてました。

こんな感じで、カテゴリー別記事一覧に説明文が表示されます。

ブログへのリンク挿入では、概要文 (meta description) の方が表示されます。

blog.kondoumh.com

Twitter などに OGP 情報を埋め込むときにも同様です。

概要文の方にも説明文のコピーを入れておいた方がいいでしょうね。

この機能、ブログの概要ページを作るのによいかも。カテゴリーごとにアイキャッチ画像を入れられるとなおよいのですが。

Wear OS by Google on Moto 360 2nd Gen

2年半ぐらい前に買った Moto 360 2nd Gen

blog.kondoumh.com

去年の6月に Android Wear のメジャーバージョンアップがありました。

blog.kondoumh.com

今年の10月はじめに Wear OS by Google としてメジャーバージョンアップが公開されました。

blog.google

そして11月に入って Moto 360 2nd gen にも降ってきました。まだアップデート対象でよかった。

今回の目玉は4方向のスワイプによる操作です。 これに伴い、ウォッチフェースの変更は従来の左右スワイプから画面長押しになりました。

f:id:kondoumh:20181103115119j:plain

上から下にスワイプすると設定画面

f:id:kondoumh:20181103115149j:plain

左から右にスワイプすると Google Asistant

f:id:kondoumh:20181103115130j:plain

下から上にスワイプすると通知リスト

f:id:kondoumh:20181103115203j:plain

左から右にスワイプすると Google Fit

f:id:kondoumh:20181103115136j:plain

アプリ一覧はこれまで通り竜頭プッシュで

f:id:kondoumh:20181103122020j:plain

Android のブランドを外してリニューアルした OS で Moto 360 2nd Gen もまたリフレッシュされた感じです。

VS Code の Python 開発環境を整える for macOS and Windows

仕事でちょっと Jupyter Notebook とか触るようになったので、素の Python 開発環境も構築して Python 力高めたいなあということでやってみました。

ちょっと前は何も考えずに Anaconda 入れてしまうレベルだったので、このあたりから読んで心を入れ替えました。

qiita.com

これ読んで Windows でも mac でも苦労なく Jupyter 環境が構築できました。

qiita.com

しかし Trac を触ってた頃から比べると Python のツールチェインも随分様変わりしましたねえ・・。

Python をインストールすると

$ python -m venv <environment directory>

で、venv という OS にインストールされた Python 環境とは隔離された仮想的な Python 環境が手に入ります。仮想といっても VM のようなヘビーなものではなく Python インタープリタやライブラリ環境をフォルダ単位で隔離している程度 (Node.js でいうとプロジェクトの node_modules に node / npm も入っているイメージ) で pyenv とやってることはそんなに変わらなそうです 。ただ、サードパーティではなく Python 標準で提供されているところが正義です。

venv ではターミナルのプロンプトが

(env) $

のように分かりやすい表示になります。

VS Code では、Microsoft 公式の Python 拡張が提供されていますのでインストールします。

marketplace.visualstudio.com

VS Code ではプロジェクト単位(workspace 単位) に設定をカスタマイズできます。Python のプロジェクトディレクトリで、venv を作成します。

$ python -m venv env

プロジェクトの Python ファイルを開くと、Python 拡張が導入されていれば Python 環境を選択するプロンプトが表示されますので venv の Python を選びます。

f:id:kondoumh:20181024221954p:plain

workspace の .vscode/settings.json を直接編集する場合、macOS では次のように python.pythonPath を設定します。

{
    "python.pythonPath": "${workspaceFolder}/env/bin/python"
}

Windows でも同様です。

{
    "python.pythonPath": "${workspaceFolder}\\env\\Scripts\\python.exe",
    "terminal.integrated.shell.windows": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
}

Windows で統合ターミナルに Git for Windows (MSYS2) の bash を指定していると Python の REPL がうまく動かないし cmd.exe は辛いので PowerShell を指定してます。

f:id:kondoumh:20181024210824p:plain

ステータスバーに Python 環境が ('env': venv) のように表示されていれば OK です。

統合ターミナルで Python 使うには、macOS なら

$ . env /bin/activate 

Windows なら

PS> .\env\Scripts\activate

で起動する必要があります。これがちょっと面倒かな。venv に入って pip install で numpy など必要なパッケージをインストールしていきます。requirements.txt を追加して必要なパッケージとバージョンを管理することもできます。npm init で package.json が作成される Node.js と比べるとツールがこなれてない感ありますが・・。

Python 拡張で色々コマンドが使えます。

f:id:kondoumh:20181024224926p:plain

F5 で普通にデバッグできます。

f:id:kondoumh:20181024225131p:plain

ということで VS Code 上に割と快適っぽい Python 開発環境が構築できました。

Spring Initializr でコンテナフレンドリーな Spring Boot アプリ開発をスタートする

Spring はアプリケーション基盤として成熟しエンプラではデファクトスタンダードの地位を築いています。Spring Boot は Spring によるアプリケーション開発立ち上げを Boost してくれるフレームワークです。アプリケーションサーバ不要で Fat Jar をそのままデプロイできてクラウドやマイクロサービスとの親和性が高いため、数多くのオプションがモジュールとして公開されています。

Spring Initializr でプロジェクト名などの項目をポチポチ入力して送信するとソースコードを含むプロジェクトが生成され zip で降ってきます。

f:id:kondoumh:20181015094426p:plain

ビルドシステムは Gradle と Maven から選べます。Maven を選択すると Maven Wrapper (mvnw) が組み込まれ mvn コマンドのインストールも自動化されますので、事前準備は JDK インストールと環境変数 JAVA_HOME の設定だけ。あとは、IntelliJ IDEA や Eclipse にプロジェクトをインポートすればすぐコードを書き始められます。

Spring Boot はアプリケーションアーキテクチャにはノータッチです。トランザクションスクリプトでも DDD でも Clean Architecture でもお好みで。

大きめの開発プロジェクトでひな形を共有したい場合は、Spring Initializr で生成するのではなく、共通で利用するモジュールへの依存・パッケージ構造まで含めた雛形 (Maven Archetype や Gradle のプロジェクトテンプレート) を用意した方がよいでしょう。

Docker 対応

Dockerfile (DB を別コンテナにする場合などは docker-compose.yml も) を定義して Docker コンテナにアプリをデプロイしてテストし、プロダクション環境まで持っていけます。最近は Docker for Windows / Mac のおかげで Linux 仮想マシンを別途用意しなくてもよくなり、開発環境構築は非常に楽になりました*1

Spring Boot の設定ファイルは昔ながらの XML や properties に加え YAML に対応しています。最近は Docker や Swagger など YAML で定義ファイルを書くことが多いので、YAML で統一できるのはよいことです。環境依存の定義は @Configuration アノテーションを付与したクラスから値を読み込むのがお作法のようです。ポータブルなコンテナを構成するためには設定ファイルに値を書くよりは環境変数に寄せた方がよさそうです。

コンテナフレンドリーにするにはアプリの死活やリソース使用状況などをモニタリングできるようにすることも重要になってきます。

Spring Boot では spring-boot-starter-actuator というモジュールが提供されており、依存関係に組み込むだけで利用できます。

pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
         :

公開する health 機能を指定できます。この例では、health , env, metrics を指定しています。

application.yml

# Actuator Settings
management:
  health:
    defaults:
      enabled: false
  endpoints:
    web:
      base-path: /
      exposure:
        include: health, env, metrics

この他にも、リソース監視のためのツール Prometheus なども簡単に組み込めます。

Swagger の組み込み

クラウドに限らず REST な API は Open API (Swagger) でテスト可能な仕様を書いて開発することが普通になっています。Spring Boot アプリに Swagger を組み込むには、依存関係を追加して Configuration クラスを定義します。

pom.xml

    <dependencies>
        <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
        </dependency>
        <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
        </dependency>
         :

Configuration クラス*2

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(selector())
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Sample API")
                .description("SpringBoot Sample API")
                .version("1.0")
                .build();
    }

    // api のパスフィルタ
    private Predicate<String> selector() {
        return or(
                regex("/api/.*")
        );
    }
}

Controller クラスで、RequestMapping と RequestMethod を指定します*3

@RestController
@RequestMapping("/api")
public class CustomerController {

    @ResponseBody
    @RequestMapping(value = "/usr/{id}", method = RequestMethod.GET)
    public Customer getCustomer(@PathVariable("id") Long id) {
        :
        return customer; 
    }

    @ResponseBody
    @RequestMapping(value = "/user/", method = RequestMethod.POST)
    public Long registerCustomer(@RequestBody @Validated Customer customer) {
        :
        return id;
    }

}

実際の開発では、実装に先立って Swagger Editor で API 仕様を作成してプロジェクトに YAML または JSON 形式で含めて構成管理するとよいでしょう。API の開発が完了してなくても利用者側に動く仕様を提供できます。

おわりに

コンテナ技術の普及でサーバーサイド Java 開発も様変わりしていますね。Java のリリースサイクルの激変と Oracle の Java サポート有償化で Java 離れが進むとの予想もありますが、実際には「まだ終わらんよ」という感じだと思います。データクラスのサポートが弱い問題とか*4 Kotlin など AltJava 勢の出現に目を奪われがちではあるものの、これまでのソフトウェア資産、デベロッパーの調達などを考えるとすぐに廃れるとは思えません。

MongoDB でデータを CRUD する REST API サンプルプロジェクトを作ってみました。

github.com

*1:メモリも 8GB あればなんとか動くレベルです

*2:この例では設定値の読み込みはしていません。

*3:RequestMethod を指定しないと、Controller メソッドごとに 全ての RequestMethod のAPI ドキュメントが生成されてしまいます。

*4:Lombok などのライブラリ機能で補完可能です。

C++ REST SDK で null safe な JSON デシリアライズ処理を書く

iEdit では XML 形式のデータエクスポート・インポートをサポートしていますが、今時は XML より JSON だよねってことでこの前対応しました。

blog.kondoumh.com

この対応のために C++ の JSON 処理系ライブラリを探して、Microsoft 製の OSS C++ REST SDK (cpprestsdk) を知りました。

blog.kondoumh.com

iEdit からエクスポートした JSON ファイルはフルセットのデータがあるので何も考えずにインポートして問題ありませんが、ユーザはエディタで作ったり別のプログラムからエクスポートしたりしたデータをインポートしたいもしれません。その場合、全ての値が適切に入ってくると想定するとまずいことになります。例えば、ノードの ID は数値ですが文字列が指定されてしまうかもしれません。ノードの位置を表す座標は何も設定されていないかもしれません。入力データをチェックして不正なデータを弾くようにすれば、ランタイムのエラーは防げますが、インポートデータを作成するのが大変になってしまいます。

ということで、入力されない属性については極力デフォルト値を割り当て、なるべくインポートを成功させる方向で実装しました。JSON の Parse が終わった後、所定のキーで値を取得してノードやリンクの情報を構築していきますが、値が取得できるかを事前に判定し、取得できない場合はデフォルト値を割り当てます。

iEdit で使用するデータ型は、文字列、数値、配列、真偽値 の4つです。cpprestsdk では、JSON value に対して、is_null() / is_string() / is_number() などのチェック用のヘルパーメソッドが用意されています。これを使って HasValue メソッドを書きました。ちょっとダサいですが キーは2つまでとし 値が目的の型で取れるかをチェックします。

bool JsonProcessor::HasValue(json::value v, json::value::value_type type, const wchar_t* key1, const wchar_t* key2)
{
    json::value target = v[key1];
    if (target.is_null()) return false;

    if (key2 != L"") {
        target = v[key1][key2];
        if (target.is_null()) {
            return false;
        }
    }
    switch (type) {
    case json::value::String: return target.is_string();
    case json::value::Number: return target.is_number();
    case json::value::Array: return target.is_array();
    case json::value::Boolean: return target.is_boolean();
    }
    return false;
}

これにより、値の構築コードは以下のように三項演算子でデフォルト値を指定しながら書けます。

auto fillColor = HasValue(v, json::value::String, L"fill_color") ?
                            v[L"fill_color"].as_string() : L"#FFFFFF";
node.SetFillColor(ConvertToRGB(fillColor));

座標など、複数項目にわたる属性もあるので、それらについては個別に値設定ロジックを書きました。

iEdit 2.40 リリース - JSON サポート

f:id:kondoumh:20160410114137p:plain

従来の XML に加え、JSON ファイルのインポート、エクスポート機能を追加しました。

全般的にコードリファクタリングしてちょっと機能を整理・削除したりもしました。

修正内容はリリースノートをご覧ください。