ログ日記

作業ログと日記とメモ

Googleアナリティクス4プロパティで使われているフロントエンドのツール

メインのプログラムは<script id=base-js ・・・となっている場所にある。
あとはHTMLやJSのcopyrightなどを見ていく。
依存関係は知らないので(angularを使えばlessとmaterialは勝手に付いてくるよ!など)見つけた順に書いていく。

今までと大きな違いは無い感じ。
Angular用の細かいライブラリは省略。

StackでHaskellの環境を作れなかったログ

今のPCにGHCが無かったので入れた。
どうやって環境を作ったのか忘れていて、どこかに書いた気がするけど見つからないのでメモ。
うーん…stackの一通りの使い方とか、前にも書いた気がするんだが…。GHCJSのことを書いて肝心のGHCのことは書いてなかった?

sudo apt-get install g++ gcc libc6-dev libffi-dev libgmp-dev make xz-utils zlib1g-dev git gnupg netbase
https://docs.haskellstack.org/en/stable/install_and_upgrade/#linux

今の環境だと全てインストールされていたので実行する必要はなかった。無い場合は入れる。
https://get.haskellstack.org/stable/linux-x86_64.tar.gz
バイナリを取ってきて ~/bin/ 等にコピー。


最初のhello worldを実行。

stack new helloworld new-template
cd helloworld
stack build

まだ環境が何もないのでダウンロードが始まる。 ~/.stack/ に環境が作られる。


ghc-modをインストール。

stack install ghc-mod

エラーでインストールできない。


Failing to build ghc-mod with stack · Issue #940 · DanielG/ghc-mod · GitHub

~/.stack/global-project/stack.yaml でLTSのバージョンを変える。

resolver: lts-9.21

ちょっとバージョンが古くなってしまったけれど、これでいけた。

stack install intero

次はintero-modeが動かない。
intero-modeのページにも、別のツールを使えと書いていた。

ghc-modのページにも、古いから haskell-ide-engine を見ろと書いていて、haskell-ide-engine のページには haskell-language-server を見ろと書いていた。カオス。あんまり複雑なのは嫌なんだけど…。


https://n314.hatenablog.com/entry/2019/06/01/191056
ここで色々やったみたい。うーん諦めて新しい方のツールを入れるべきか。

TCPDFで円とベジェ曲線で図形を描く

TCPDFでちょっとした図形を入れたい場合、画像を使わずに直接PDFに出力してしまう方法がある。
TCPDFのソースとマニュアルを見ていたらベジエ曲線が簡単に描けるようなのでやってみた。
ベジェ曲線?どっち?昔聞いたときはベジエ曲線と発音されていたような気がする。)


まずは円弧を描く。

<?php
// (snip)
    $x = $pdf->GetX();
    $y = $pdf->GetY();
    $w = 20;
    $h = 20;

    $cx = $x + $w / 2;
    $cy = $y + $h / 2;
    $cw = $w / 2;

    $start = 0;
    $end = $start + 255;

    $pdf->Circle($cx, $cy, $cw, $start, $end, 'D');

角度は右が0度で半時計回りに指定するようだ。
これで右から255度分の円弧を描ける。

ベジエ曲線は、開始座標+3座標を指定する。

<?php
// (snip)
    $ax = $cx + $cw * cos(deg2rad($end));
    $ay = $cy - $cw * sin(deg2rad($end));
    $aw = $x + $w - $ax;

    $ax1 = $ax + $aw / 2 * $sign;
    $ax2 = $ax + $aw * $sign;
    $ax3 = $ax + $aw * $sign;
    $ay1 = $ay;
    $ay2 = $ay + $w / 3;
    $ay3 = $ay + $w / 2.5;
    $p1 = [$ax1, $ay1, $ax2, $ay2, $ax3, $ay3];

    $pdf->Polycurve($ax, $ay, [$p1], 'D');

円弧の端の座標をsinとcosで求める。
角度は$endをラジアンに変換する。deg2radっていう組み込み関数があったんだね。

そこからなめらかになるように、座標を配列で指定して Polycurve 関数で出力する。[$p1]となっているところは、複数繋げると複雑な曲線が描ける。

tcpdf-bezier

こんな感じに線を引けた。

中を塗る場合は、反転したベジエ曲線切らずに繋げて空白を埋めるように工夫する必要がある。

<?php

require_once 'vendor/autoload.php';


function drawCircle($pdf, $reverse)
{
    $x = $pdf->GetX();
    $y = $pdf->GetY();
    $w = 20;
    $h = 20;

    $cx = $x + $w / 2;
    $cy = $y + $h / 2;
    $cw = $w / 2;

    $start = 0;
    $end = $start + 255;

    $pdf->Circle($cx, $cy, $cw, $start, $end, 'D');
}

function drawBezier($pdf, $reverse)
{
    $x = $pdf->GetX();
    $y = $pdf->GetY();
    $w = 20;
    $h = 20;

    $cx = $x + $w / 2;
    $cy = $y + $h / 2;
    $cw = $w / 2;

    if (!$reverse){
        $start = 0;
        $end = $start + 255;
    }else{
        $start = 285;
        $end = 180 + 360;
    }

    if (!$reverse){
        $sign = 1;
        $point = $end;
    }else{
        $sign = -1;
        $point = $start;
    }

    $ax = $cx + $cw * cos(deg2rad($point));
    $ay = $cy - $cw * sin(deg2rad($point));


    if (!$reverse)
        $aw = $x + $w - $ax;
    else
        $aw = $ax - $x;
    $ax1 = $ax + $aw / 2 * $sign;
    $ax2 = $ax + $aw * $sign;
    $ax3 = $ax + $aw * $sign;
    $ay1 = $ay;
    $ay2 = $ay + $w / 3;
    $ay3 = $ay + $w / 2.5;
    $p1 = [$ax1, $ay1, $ax2, $ay2, $ax3, $ay3];

    $pdf->Polycurve($ax, $ay, [$p1], 'D');
}

$pdf = new TCPDF();
$pdf->AddPage();
$pdf->SetFont('genshingothic', '', 8);

$color = [255,100,100];

$pdf->SetFillColor(...$color);
$pdf->SetDrawColor(...$color);

$pdf->Cell(0, 0, '円弧を描く', 0, 1);
drawCircle($pdf, false);

$pdf->SetY($pdf->GetY() + 30);

$pdf->Cell(0, 0, '円弧を反転してもう一つ描く', 0, 1);
drawCircle($pdf, false);
$pdf->SetX($pdf->GetX() + 20);
drawCircle($pdf, true);

$pdf->SetY($pdf->GetY() + 30);
$pdf->Cell(0, 0, 'ベジエ曲線を描く', 0, 1);
drawCircle($pdf, false);
drawBezier($pdf, false);

$pdf->SetY($pdf->GetY() + 30);
$pdf->Cell(0, 0, '円弧とベジエ曲線を反転して描く', 0, 1);
drawCircle($pdf, false);
drawBezier($pdf, false);
$pdf->SetX($pdf->GetX() + 20);
drawCircle($pdf, true);
drawBezier($pdf, true);



$pdf->Output();

Debian busterにmxeをインストールしてQtCreatorで使う

Mastering Qt 5 の Chapter 2 で、いきなり windows.h が要求されて詰まった。ハードル高くない?スルーしてLinuxのコードの部分だけやればいいのかもしれないけど、もやもやする。
そもそもQtを使う人は様々なOSで使いたいわけだから、最初の方のchapterに複数OSの対応を書いておくのが良いということかな。
あまり詳しい説明はないままWindowsLinuxMac用のコードが書いてある。それぞれのPCで各自環境構築はやってねということか。OSはあることはあるけど、いちいちソースを移動するのは面倒だよね。それぞれに開発環境を入れないといけないし。
仕方がないのでwindowsバイナリをコンパイルできるように設定する。


まず、mxeを取ってきておもむろにmxeをmakeする。

git clone https://github.com/mxe/mxe.git
cd mxe
make qt5

missing ... と表示された場合は、足りないものを https://mxe.cc/#requirements を見ながらインストールする。
(aptコマンドをコピペしても良いが、一応一つ一つ確認しながらインストールしたいので)

今の自分の環境だと以下のパッケージが必要だった。

apt install bison flex gperf intltool-bin libtool lzip

あとは

make -j10 MXE_TARGETS='x86_64-w64-mingw32.static' qt5

してしばらく待つ。
結構時間がかかる。(-j オプションと64bitターゲット指定を忘れないように)


# 4/27 追記
身内だけで使うならこれでもいいけど、公開するなら

make -j10 MXE_TARGETS='x86_64-w64-mingw32.shared' qt5

shared の方が良いかも。
まあ配布するなら、配布用バイナリはWindowsコンパイルする方が良いのかもしれないが。

あと、そもそもqtcreatorをwineで入れる方法があるようだ。
Cross compiling Qt application for Windows on Linux with dynamic linking - Stack Overflow
こっちはちょっと面倒かな。それなら仮想マシンWindowsにqtcreatorを入れる方が楽な気がする。


# 4/28 追記
mxe で sharedのバイナリは起動できなかった。

This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Unhandled exception: unimplemented function KERNEL32.dll.RaiseFailFastException called in 64-bit code (0x000000007bc5d60c).
Register dump:

というエラーが出て、ログには

wine: Call from 0x7bc5d60c to unimplemented function KERNEL32.dll.RaiseFailFastException, aborting
wine: Unimplemented function KERNEL32.dll.RaiseFailFastException called at address 0x7bc5d60c (thread 0009), starting debugger...

というエラーが出ている。他にも

0025:err:winediag:xrandr12_init_modes Broken NVIDIA RandR detected, falling back to RandR 1.0. Please consider using the Nouveau driver instead.
002b:fixme:dbghelp:elf_search_auxv can't find symbol in module

のエラーが出ていた。NVIDIAのエラーは、正常に動く場合でも出てる。

仕方がないので普通にWindowsVisual Studio 2019 と Qt を入れた。

# 追記ここまで


コンパイルが終われば、QtCreatorの「オプション」「Kits」の設定で
コンパイラ
 Cコンパイラ:mxe/usr/bin/x86_64-w64-mingw32.static-gcc
 C++コンパイラ:mxe/usr/bin/x86_64-w64-mingw32.static-g++
CMake
 mxe/usr/bin/x86_64-w64-mingw32.static-cmake
Qtバージョン
 mxe/usr/x86_64-w64-mingw32.static/qt5/bin/qmake
キット
 上記設定したものを指定

適当にMinGWとでも名前を付けてPATHを手動追加する。
コンパイラのABIは x86 windows msys pe 64bit にする。


chapter 02 をある程度進めて、Windows版はビルドしてから wine64で立ち上げる。
これでいいのかな?とりあえずCPU WidgetLinux版もWindows版もDebian上で確認できた。



Macはクロスコンパイルは無理っぽいのであとで実機でやる。

Qtをやってみてる

Qt Creatorをインストールする。

apt install qtcreator

他にも何か入れたかもしれない。Qt Creatorを使わずに qmake だけ入れたこともあって、そのときのライブラリが残っているのかもしれない。忘れた。

デバッグでエラーが出るのでpythongdbが使えるようにしておく?

The selected build of GDB does not support python scripting.
It cannot be used in Qt Creator.

デバッグ実行するとこのエラーが出る。
gdbは入っているのになと思っていたら、gdb-minimalではダメで

apt install gdb

する必要があったようだ。

Debugger does not start. GDB not support Python scripting. | Qt Forum
python-dbg は要らなかった。


そうすると起動はしたけどwarningが出る。

&"warning: GDB: Failed to set controlling terminal: \343\203\207\343\203\220\343\202\244\343\202\271\343\201\253\345\257\276\343\201\231\343\202\213\344\270\215\351\201\251\345\210\207\343\201\252ioctl\343\201\247\343\201\231\n"
printf "warning: GDB: Failed to set controlling terminal: \343\203\207\343\203\220\343\202\244\343\202\271\343\201\253\345\257\276\343\201\231\343\202\213\344\270\215\351\201\251\345\210\207\343\201\252ioctl\343\201\247\343\201\231\n" | nkf    
warning: GDB: Failed to set controlling terminal: デバイスに対する不適切なioctlです


c++ - Setup GDB with QtCreator - Stack Overflow
ターミナルで実行すれば解決するみたいだけど、いちいちターミナルを開くのは見づらいかもしれない。
とりあえずスルーで。


これでMastering Qt 5を写経してみている。

Amazon | Mastering Qt 5: Create stunning cross-platform applications using C++ with Qt Widgets and QML with Qt Quick, 2nd Edition | Lazar, Guillaume, Penea, Robin | Tutorials

英語だけれど意外と読める。
英語の児童小説は1ページも読めなかったけれど、プログラムの本なら読めるというのが新感覚。
時々英語のAPIドキュメントとかチュートリアルとか見てるおかげ?文章の意味が分からなくてもプログラムの意味は分かるのが大きいのかも。Dockerのセットアップとかが英語で書かれていても、読むというよりはざっと眺める感じだし、この本もそれに近い。重要な概念の説明っぽい箇所は意味を調べつつ読んでるけれども。
Google翻訳アプリのカメラでリアルタイム翻訳が、ちょっと惜しい感じ。文章を上書きしないで単語の説明を出してほしいところだけど、カメラが揺れるし使いやすくするのは難しいのかも。
今はChapter 01 をやったところ。

Googleの検索順位を手軽に数えるブックマークレット

普段ブラウザのブックマークレットとして登録しているけれど、他のPCで使いたくなったとき用にメモ。

まず、Googleの検索オプションで100件表示にする。
シークレットウィンドウでも使うために、規定の検索エンジンのURLに「&num=100」を付け足すのでも良い。


その後、検索結果ページ(SERP)のHTMLをJavaScriptで調べる。

javascript:s = prompt('url'); document.querySelectorAll('#search a h3').forEach((v, i) => {if (v.parentNode.href && v.parentNode.href.match(s)) alert(v.parentNode.href +': ' + (i+1) + '位')})

alertしているけれど、コピペするならconsole.logで表示または画面に独自ダイアログを表示しても良いかも。


HTMLに影響されるのでSERPのHTMLに変更があったらその都度対応していく必要があるかもしれない。

PHPでDDDのようなClean Architectureのような単純なLayeredとIoC

最近PHPコードをリファクタリングしている。

DDDやクリーンアーキテクチャの記事や
5年間 Laravel を使って辿り着いた,全然頑張らない「なんちゃってクリーンアーキテクチャ」という落としどころ
この辺の記事を読んだりして、自分なりの最適解を探している。


まだやり始めたところだけど、現状をメモしておく。


まず、PHPで今書いているコードは基本的にUIに関連する処理が最も多く、ドメインロジック的なものよりプレゼンテーションロジックが多いということをふまえて。

src/
└── Ddd
    ├── Application
    │   ├── UseCase
    │   │   └── Cart
    │   └── ViewUseCase
    │       ├── Cart
    │       └── Product
    ├── Domain
    │   ├── Cart
    │   └── Product
    └── Io
        └── Infrastructure
            ├── Cart
            ├── Order
            └── Product

アプリケーション層のユースケースと同じ階層に表示のためのユースケースを入れる。
これは、表示のための文字列を整形する処理が結構な分量あり、それをテストしたいため。CQRS(というかCQS)のクエリ結果を整形して、それが正しく整形されたかチェックしたい。なので複雑なクエリもアプリケーション層に入れる。
ViewUseCaseではデータの保存は無し。
「保存 => 結果を取得して表示」の場合はまずUseCaseで保存して ViewUseCase のクエリで表示用のデータを取得する。

また、リポジトリはアプリケーション層に所属する。ドメインリポジトリを扱わない。
他のDDDなどの解説で、ドメインロジックがfindやstoreを実行しているけど、それってユースケースでは?という単純な疑問がある。あとユースケースに沿ったSQLを書きたくて、ドメイン用の全部入りSQLは発行したくないので。
それにstrict layeredとの相性も悪そうだった。
ドメインロジックは、ロジックというか型変換に徹する。DomainではなくてType というネームスペースにしようか迷ったぐらい。基本的にはユースケースのコードが膨らむことになる。
そこは諦めてテストを書く。
そうすると結局、昔ながらのレイヤー構造+IoCリポジトリだけ依存関係を逆転させた普通の構造になったかもしれない。



テストはユースケースに対して行う。ドメイン(というかここでは細かい型用のクラス)はリファクタリングしたいのでテストを細かく書かない。
ユースケースにプレゼンテーションを含めたことにより、ブラウザが表示するUIと直結しているので、UIのテストも多少は兼ねることになっている。


PHPのファイルも込みで並べると

└── src
    ├── Controller
    │   ├── Cart
    │   │   └── IndexPage.php
    │   └── Product
    │       ├── DetailPage.php
    │       └── IndexPage.php
    └── Ddd
        ├── Application
        │   ├── UseCase
        │   │   └── Cart
        │   │       ├── AddCartUseCase.php
        │   │       ├── CartItemRow.php
        │   │       ├── CartRepository.php
        │   │       └── CartRow.php
        │   └── ViewUseCase
        │       ├── Cart
        │       │   ├── CartDto.php
        │       │   ├── CartItemDto.php
        │       │   ├── CartQuery.php
        │       │   ├── GetCartPageUseCase.php
        │       │   └── RowDtoConverter.php
        │       └── Product
        ├── Domain
        │   ├── Cart
        │   │   ├── Cart.php
        │   │   └── CartItem.php
        │   └── Product
        └── Io
            └── Infrastructure
                ├── Cart
                │   ├── CartQueryImpl.php
                │   └── CartRepositoryImpl.php
                ├── Order
                └── Product

こんな感じになっている。
UseCaseからViewUseCaseへの参照は無し。逆はあり。

Row はリポジトリからのマッピングクラス、Dto はUIへのマッピングクラスになっている。
本当はここもインターフェースと実装クラスを分ける方が綺麗だけど、大量にあるカラムのgetterを用意するのが面倒なので、オブジェクトのpublicプロパティにした。そうするとユースケースがDBのカラムを少し知っていることになるけれど、これは妥協で。インフラ層で詰め替えてもいいけど、インフラは基本的にコードを少なくしたくて、何の加工もせずにそのまま返したい。加工はアプリケーション層でやって、それはテスト対象になる。本気で切り離してもあまりメリットはなく、変更を追いやすく修正しやすければ良いので。

基本的にstrict layered、つまり階層を飛び越えて参照しない。
コントローラーからはドメインを参照しない。ここを甘くするとテンプレートでメソッドを呼び出したりしてしまうので。
テンプレートエンジンに渡すのは、ユースケースから返ってきたDtoをそのまま渡す。
コントローラーでフォームの処理とエラーチェックをしているけど、本当は Io\Form 等に入れたい。まだそこまで行ってない。

とりあえず今はこんな感じ。