d.sunnyone.org
sunnyone.org

ページ

2020-05-29

Drawio Desktop を使ってコマンドラインで図を画像に変換する

Webブラウザで使えるオープンソースのドローツールdraw.ioは便利なのだけど、図を描いたあと他フォーマットに変換のがちょっと面倒なので変換する話。

drawio-desktopをインストールする

draw.ioにはブラウザ版の他に、Electronで作られたdrawio-desktopというのがある。まずこれをインストールする。snapだと簡単。

# snap install drawio

drawioコマンドで変換する

drawio-desktopの実体であるdrawioコマンドには、コマンドラインオプションを指定すると指定フォーマットでエクスポートする機能がついているので、これを使って変換する。

$ drawio -x -f png *.drawio
シェルスクリプトにしておくと便利。ちなみにデフォルトはpdfらしい。

ヘルプを見るとこんな感じ。
$ drawio --help
Usage: drawio [options] [input file/folder]

Options:
  -V, --version                      output the version number
  -c, --create                       creates a new empty file if no file is passed
  -k, --check                        does not overwrite existing files
  -x, --export                       export the input file/folder based on the given options
  -r, --recursive                    for a folder input, recursively convert all files in sub-folders also
  -o, --output   specify the output file/folder. If omitted, the input file name is used for output with the
                                     specified format as extension
  -f, --format               if output file name extension is specified, this option is ignored (file type is determined from
                                     output extension, possible export formats are pdf, png, jpg, svg, vsdx) (default: "pdf")
  -q, --quality             output image quality for JPEG (default: 90)
  -t, --transparent                  set transparent background for PNG
  -e, --embed-diagram                includes a copy of the diagram (for PNG format only)
  -b, --border               sets the border width around the diagram (default: 0)
  -s, --scale                 scales the diagram size
  --width                     fits the generated image/pdf into the specified width, preserves aspect ratio.
  --height                   fits the generated image/pdf into the specified height, preserves aspect ratio.
  --crop                             crops PDF to diagram size
  -a, --all-pages                    export all pages (for PDF format only)
  -p, --page-index        selects a specific page, if not specified and the format is an image, the first page is selected
  -g, --page-range ..      selects a page range (for PDF format only)
  -h, --help                         display help for command

2020-02-27

yalcを使って開発中のnpmパッケージを手元で確認する

ライブラリとして分離したnpmパッケージを作るときは、シンプルなものであればyarn linkでローカルファイルを参照しながら動作確認ができるのだけど、依存によってはややこしい問題が起きたりする。

例えばReactを使うライブラリだとこんなエラーが起きたりする。
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

そこで登場するのがyalcというツール。yarn linkはざっくり言うとライブラリ側のディレクトリへのシンボリックリンクをアプリ側のnode_modules以下に配置するのに対して、yalcを使うとローカル(~/.yalc)にpublishしてそこをpackage.jsonで見る形にできるので、npm registryにpublishされたものを使うのに近い状態にすることができる。

使い方

1. yalcのインストール

yalc自体をインストールする。
$ yarn global add yalc

2. publish

ライブラリ側でyalc publishする。
$ cd ~/proj/mylib
$ yarn build とか
$ yalc publish

3. パッケージ追加

アプリ側でyalc addする。
$ cd ~/proj/myapp
$ yalc add @myorg/mylib
$ yarn
これで使えるようになる。package.jsonにはこんな感じのエントリが追加されている。
    "@myorg/mylib": "file:.yalc/@myorg/mylib",

4. 内容更新

中身を変えたら publish と update をする。
$ cd ~/proj/mylib
$ yarn build とか
$ yalc publish
$ cd ~/proj/myapp
$ yalc update

5. yalc remove

yalc remove --allしておしまい。
$ cd ~/proj/myapp
$ yalc remove --all
$ yarn
詳しくはyalcのGitHub (https://github.com/whitecolor/yalc)を参照。

2020-02-12

tslint:disable を eslint-disable にするツールを雑に作った

[2021/7/8追記] tslint-to-eslint-configの作者さんから直々にコメントに対応した旨連絡頂いたので、このツールは役目を終えました。 https://github.com/sunnyone/tslint-comment-to-eslint/issues/1

TSLintからESLintに移行するには、
// tslint:disable-next-line:no-floating-promises
なんかのコメントを、
// eslint-disable-next-line @typescript-eslint/no-floating-promises
なんかに変える必要があるのだけど、ある程度機械的にできるんじゃないかと思って作った。
https://github.com/sunnyone/tslint-comment-to-eslint

使い方

使い方はこんな感じ。直接置き換えるのでgitなりできれいな状態にして実行を。
$ yarn global add tslint-comment-to-eslint
$ tslint-comment-to-eslint 'src/**/*.ts'

仕掛け

仕掛けとしては、tslint-to-eslint-config にMapが入っていたのでrequireして引いてきて、そのMapを使って正規表現置換で変えただけ。だからルール名は変わるけど、ルールの使い方が違っているとアウト(例えばmax-classes-per-fileをdisable-next-lineするには、TSLintだとそれを2回目のclassの行に使うけど、ESLintだと1行目に使う、とか)なので確認は必須。調整前にベースに使えればどうぞ。

2019-11-11

DeskMini A300 + Ryzen 3400G でPCを組んだ

そろそろメインPCを新しくしなきゃなと思いながらもずっとできなかったメインPCの調達をついにやった。DeskMini A300という箱とRyzen 3400GというAPU。Ryzen APUがZen2なりになるまで待つべきかどうしようかというところで、目をつけていたDeskMini A300がツクモのセールで\12k程だったのでここだということで購入。キーボードと比較してこの大きさ。小さい。

構成は以下の通り。
ケースDeskMini A300 (+ USB2.0ポート追加ケーブル)
CPURyzen 3400G
ファンNoctua NH-L9a-AM4
メモリCrucial 32GB Kit (2 x 16GB) DDR4-2666 SODIMM (CT2K16G4SFD8266)
SSDWD BLACK SN750 NVMe 500GB (WDS500G3X0C)


このDeskMini A300、この小ささでM.2 SSD 2枚、2.5" SSD 2枚が入る。驚き。USBの口が少ないので、増設ケーブルは\1kちょいなので買っておくのがおすすめ。

CPUはRyzen 3200Gでいいかと思ったけど、結局3400Gにしてしまって、いらなかったかなと思いつつ、あとで困るよりはいいかなと。ファンはケース付属ではパワー不足ではということで、ちょっと高めだけど安牌らしいNH-L9a-AM4をチョイス。

メモリは16Gでいいかなと思いつつ、VRAMに2G持っていくそうなので、まぁ後で増やすのも面倒だしと思って32GBにしてしまった。

SSDはこの小ささなのでM.2のタイプを。Intelのやつにしようかなと思ったのだけど、WD BLACKは発熱が抑えられているそうで、これにしてみた。

ベンチマークを取ってから載せようと思ったけど、先送りされそうなのでとりあえず構成だけ。

前のPCと比較しての効果はなんといっても4K/60fps出せるようになったこと。画面がきれい。前のPCは4K出せなかったので、4Kディスプレイの意味がなかった。

2019-09-21

SPA用にwebpack-dev-serverにproxyを設定する

SPA (Single Page Application) でWebアプリケーションを作る場合、HTMLの生成やREST APIはJavaScript/CSSの配信とは別のサーバーで行うものの、クラウド環境ではCloudFrontなりリバースプロキシなりが存在していて、結果全て同一ドメインで運用されていることがある。

この状況で、JavaScriptをローカル環境のwebpack-dev-serverで試験しようとすると、REST APIが別ポートになるのでローカル環境でだけCORSの設定が必要だったり、深いパスでリロードするとNot Foundになったりといったような面倒ごとが発生する。

これを解決するには、nginxなりでproxyを立てても良いが、webpack-dev-serverにもproxyの機能がついているので、以下のように設定すると簡単。

    devServer: {
        contentBase: 'dist',
        host: '0.0.0.0',
        port: 3000,
        proxy: {
            '/myapp/api': {
                target: 'http://127.0.0.1:9292'
            },
            '/myapp': {
                target: 'http://localhost:3000/examples/index.html',
                pathRewrite: {'^.*' : ''}
            }
        }
    }

examplesにローカル向けのindex.htmlを置いておいて、CopyWebpackPluginでコピーすると便利。

        new CopyWebpackPlugin([
            { from: 'examples', to: 'examples' }
        ]),

2018-10-04

Spring Data JDBC で Enum を序数でない数値に変換する

Spring Data JDBC では、デフォルトではenumはnameに変換される。しかし、DBに格納する際は特定のコード(数値)に変換したいことも多いと思うので、その方法について書く。
Kotlinで書いてしまうが、Javaへの読み替えは難しくないと思う。

インタフェースの作成と利用

enumはordinal(序数)を持っているが、それぞれの項目で固定されたコード(数値)を設定したいので、コードを設定できるinterfaceを作成する。

interface CodedEnum {
    val code: Int
}
enum側で使っておく。
enum class Status(override val code: Int): CodedEnum {
    SUCCESS(1),
    FAILURE(2),
}

NamedParameterJdbcTemplateをカスタマイズしてパラメータを差し替える

Spring Data JDBC は内部でSpring JDBC のNamedParameterJdbcTemplateを使っているが、これが@Query で指定したパラメータの変換を担っている。 パラメータをenumで受け取るには、これがCodedEnumを扱えるようになる必要があるので、カスタマイズされたNamedParameterJdbcTemplateを準備する。
対応してる箇所が低いところすぎて気持ち悪いけど、getPreparedStatementCreator() は protected なので、NamedParameterJdbcTemplate的にはここを置き換えることは想定されているんだろうなと思いつつ対応。 enumに限らずここを書き換えたい作業はあると思うので、将来的にはSpring Data JDBC がもっと便利になって、こういう処理はいらなくなるかも。

Converterの追加とCustomizedNamedParameterJdbcTemplateの適用

@EnableJdbcRepositories の @Configuration で、まずnamedParameterJdbcTemplateを差し替える。

そして、値の変換を行うJdbcCustomConversionsにもEnumの変換処理を入れる。基本的には、write方面はinterfaceをそのまま.codeで変換して、readは型がわからないのでfactoryを利用して変換する。 ここでポイントになるのは、Spring Data JDBC の BasicRelationalPersistentPropertyがDB向け変換を行う際にEnumをStringに変換するということ。 これに対応するため、to Stringの変換を用意しておく。

 これで、codeで設定した値が利用されるようになる。

2018-03-26

AWS CloudFront Lambda@Edge の罠たち

CloudFront Lambda@Edge は、Lambda関数を実行しCloudFrontのリクエスト/レスポンスに手を加えることができるサービス。CloudFront⇔オリジン間、ビューワ⇔CloudFront間のリクエスト側/レスポンス側の4つの場所から適用する場所を選ぶことができる。

適切に使えば便利だと思うが、使ってみないとわからない罠が多いので、経験した範囲をまとめておく。

CloudFrontにはバージョンつきのLambda関数のARNを設定する必要がある

CloudFront BehaviorにLambda関数のARNを設定するのだが、名前だけのARNは設定することができず、バージョン番号入りのものを設定する必要がある。

つまり、Lambda関数の中身を書き換えたら、新しいバージョン番号を発行して、その番号をCloudFrontに設定する必要がある。

毎回手動でなんて現実的ではないので、AWS Serverless Application Model (AWS SAM)を利用してCloudFormationでLambda関数をデプロイし、AutoPublishAlias 機能によってバージョン番号を自動的に設定するのが現実的であろう。同一スタックであれば、CacheBehaviorsに以下のように設定する。
LambdaFunctionAssociations:
  - EventType: origin-response
    LambdaFunctionARN: !Ref "Function.Version"
これが何を意味するかというとLambda関数を更新する度にCloudFront Distributionを更新する、すなわちコードを書き換えるたびに数十分かかるということを意味する。

Lambda@Edgeに設定する関数はus-east-1リージョンに配置する必要がある

Lambdaはap-northeast-1でも利用可能だが、Lambda@Edgeに設定する関数はus-east-1に配置する必要がある。先のCloudFormationスタックを作るのであれば、us-east-1リージョンで作る必要があるということになる。

日本のサービスであればap-northeast-1に配置することも多いと思うが、スタック間でのパラメータの引継ぎに利用できるクロススタック参照は同一リージョンでしか利用できない。したがって、us-east-1→ap-northeast-1のスタック間でリソースの参照がある場合、自前でパラメータ引き渡しをするための仕組みが必要となる。

Lambda@Edgeで設定した関数は各リージョンにレプリカが作られて実行される

us-east-1にLambda関数を設定したからといって、us-east-1で常に実行されるかというとそうではなく、CloudFrontのエッジのリージョンに必要なタイミングでコピーされて実行される。

「オレがCloudFront エッジをサービスしたいのは日本だけ!」ということはできないので、日本を含もうとすると、多くのリージョンにレプリカが作られることになる。

レプリカはLambdaから見ると、us-east-1.というprefixがついた変更不可能な関数にしか見えない。これは、Lambda@Edgeに設定した関数のログは各リージョンのCloudWatch Logsに散らばって配置されるということを意味する。 もしログを集約したりフィルタしたりといったことをしようとすると、ほぼ全リージョンのCloudWatch Logsのリソースに対して手続きを行う必要がある。

Lambda@Edgeでは環境変数は利用できない

Lambdaで環境ごとの値を設定するのに便利な環境変数は、Lambda@Edgeでは利用できない。よって、コードに埋め込むか何らかのAPI問い合わせで値を取得する必要がある。今回 必要な値は別のリソースのARNだったので、レプリカ元(マスター)のLambda関数リソースのタグに設定し、以下の手順で取得した。
  1. context.invokedFunctionArnからレプリカ関数のARNを取得
  2. レプリカ関数のARNからリージョンを取得し、そのリージョンに対してレプリカ関数を問い合わせるlambda:GetFunctionの呼び出しを行いマスター関数のARNを取得
  3. マスター関数のARNからリージョン(us-east-1)を取得し、lambda:GetFunctionを呼び出してタグを取得
別の関数の環境変数だとか、DynamoDBだとか、状況により選択肢はあると思うが、いずれにせよ工夫が必要である。

Lambdaよりもタイムアウトなどの制限が厳しい

通常のLambda関数よりも、制限が厳しくなっている(Lambda@Edge の制限)。

例えば、Lambda単体であればタイムアウトは300秒まで設定できるが、オリジンリクエスト/レスポンスに対する関数だと30秒、ビューワーだと5秒など項目によっては厳しくなっているので注意が必要。アップロードサイズも注意。

以上のように使えそうに見えて使ってみるとどうしよう、みたいなところが多いので皆様お気をつけください。