10yroの開発日記

株式会社10yro(トイロ)のエンジニアが書いています

【Angular】システム変更通知

SPA アプリケーションにおいてシステムアップデート(デプロイ)した際に

キャッシュのファイルを参照しており更新されないケースを想定し、

ユーザにキャッシュしているファイルを更新してほしい旨通知する方法を記載します。


今回やることはrevision管理用ファイル(何らかの設定ファイル)にrevisionを記載しておき

システム起動時にその時のrevisionを保持しておきます。

そして、任意のタイミングでそのファイルのrevisionを確認し、変更されていたらユーザにその旨通知するという内容となります。


手順としては以下となります。



1. revision 保持用のrevisionファイルを用意 (json 等)

以下のような設定ファイルを追加します。

assets/application.config.json

{
    "revision": "b2a91a752e068ccf4a3b0f4759d66ec096c14821"
}

2. revisionファイルからrevisionを取得

revision 管理用の Serviceを作成し、取得用の関数を追加する。

revision.service.ts

revision : string | null = null;  // ※今のrevisionを保持する変数やLocal strage 等を用意

get(): Observable<string | null> {
    let hash = new Date().getTime();
    return this.http.get('assets/application.config.json?' + hash).pipe(
        map((conf: any) => {
            return conf['revision'] ?? null;
        })
    );
}

set(): void {
    this.get().subscribe((rev) => {
        this.revision = rev;
    });
}

また、このServiceはベースのコンポーネントでinjectし、setしておきます。

これにより、システム起動時、リロード時などベースコンポーネントが更新されるタイミングで

最新のrevisionがセットされるため画面のファイルキャッシュ状態と合わせておきます。

app.component.ts

export class AppComponent {
    constructor(
        private revision: RevisionService,
        ...
    ) {
        revision.set();
    }
}

3. 任意のタイミング(画面遷移時 等)で再度revisionファイルを確認する。

今回は画面遷移時にチェックしたいので

Routing moduleにチェック処理を追加します。

layout-routing.module.ts

constructor(
    private revision: RevisionService,
    private router: Router
) {
    this.router.events
        .pipe(filter((e: Event): e is RouterEvent => e instanceof RouterEvent))
        .subscribe((e: RouterEvent) => {
            if (e instanceof NavigationEnd) {
                // チェック処理を追加 ※↓のisRevised() は (4)にて追加する。
                this.revision.isRevised();
            }
        });
}

4. 変更があればAlert等ユーザに通知

先ほどのrevision serviceにチェック処理を追加する。

revision.service.ts

this.get().subscribe((rev) => {
    if (this.revision != rev) {
        this.notifyRevised();
    }
});

notifyRevised(): void {
    // Alert を出すなどユーザに通知
    // 例)「システムがアップデートされました。画面をリロードしてください。」メッセージ出力
}

便利なMicrosoft公式ツール

Windowsの作業効率をアップさせるMicrosoft公式ツール「Powertoys」を解説します。
今回は個人的によく使用するものを一部紹介しますが、他にもいろいろな機能があるので、試してみるととても便利です。
また、今回は解説しませんが、「DevToys」というエンジニア向けのツールもかなり重宝します。

1. Color Picker

画面レイアウトとかを作成する際に、簡単に色をクリップボードにコピーできます。
ショートカットキーの Windowsキー + Shift + C をクリック。

ColorPicker-1

対象の個所をクリックすると、いかが画面が表示されるので右側のアイコンをクリックしてコピー。

ColorPicker-2

2. マウスユーティリティ

マウスカーソルの変更できます。 リモート会議とか、画面を共有して説明するときなどに使用できます。 ショートカットキーのCtrlキーを2回クリック。

マウスの検索

ショートカットキーの Windowsキー + Shift + H をクリック。

マウスの蛍光ペン

3. PowerToys Run

いろいろな機能を起動するクリックランチャー。 ショートカットキーの Altキー + Space をクリック。 ツールの起動のほかにも、電卓としても使用できます。

PowerToysRun-1

PowerToysRun-2

docs.microsoft.com

devtoys.app

【Windows】フォルダ構成、ファイル名を一覧化して出力する(treeコマンド)

新人の頃、納品物の大量のソースファイルをお客さんへ送る際に、
全ファイル名を一覧化したドキュメントを作成する作業を振られたことがありました。
全部書き出すの??と途方に暮れそうになっていた時にtreeコマンドの存在を知り、
当時の私はちょっと感動した記憶があります。

今でも時々使う機会があるので、改めて書いておきます。

今回は、コマンドプロンプトを使用します。

 

①まずは中身を一覧化したいフォルダをエクスプローラーで開きます。

f:id:tomi510dev:20220218183830p:plain

②アドレスバーに「cmd」を入力してエンターキーを押します。

f:id:tomi510dev:20220218185520p:plain

 

すると、コマンドプロンプトが起動します。

f:id:tomi510dev:20220218184511p:plain

③以下のコマンドを入力してエンターキーを押下します。

tree /f

すると、こんな感じでツリー上に書き出されます。

f:id:tomi510dev:20220218185915p:plain

 

ツリーの結果をテキストファイルに書き出したい場合は、以下のコマンドを入力します。

tree /f >ファイル名.txt

f:id:tomi510dev:20220218190358p:plain

すると、指定したファイル名のテキストファイルが生成されます。

f:id:tomi510dev:20220218190633p:plain

f:id:tomi510dev:20220218191131p:plain

簡単ですね。

困ってる新人さんがいたら教えましょう~

【JavaScript/TypeScript】日付操作のいろいろ

JavaScriptで日付型(Date)を扱う際、ある日付の月初、月末を取る、決算期として期の開始を取得 等々

日付を加工して利用したいケースが多々あるかと思います。

今回、日付操作に関する方法を残しておきたいと思います。


目次:



1. 年を取得 (number)

getYear(date: Date): number {
    return date.getFullYear();
}

例)2022/02/07 13:00:00 (Date) ⇒ 2022 (number)

2. 月を取得 (number)

getMonth(date: Date): number {
    return date.getMonth() + 1;
}

※ getMonth() は 0 start (1月=0, 2月=1 ...)のため +1 するのをお忘れなく
例)2022/02/07 13:00:00 (Date) ⇒ 2 (number)

3. 日を取得 (number)

getDay(date: Date): number {
    return date.getDate();
}

例)2022/02/07 13:00:00 (Date) ⇒ 7 (number)

4. 月初を取得 (Date)

getFirstDateTimeOfMonth(date: Date): Date {
    return new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0);
}

例)2022/02/07 13:00:00 (Date) ⇒ 2022/02/01 00:00:00.000 (Date)
時刻も最初 00:00:00.000 を指定しています。
時間はdateのものとしたい場合は末尾の引数 「0, 0, 0, 0」 を↓に置き換えればOK
date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()

5. 月末を取得 (Date)

getLastDateTimeOfMonth(date: Date): Date {
    return new Date(date.getFullYear(), date.getMonth() + 1, 0, 23, 59, 59, 999);
}

例)2022/02/07 13:00:00 (Date) ⇒ 2022/02/28 23:59:59:999 (Date)
時刻も最後 23:59:59.999 を指定しています。
ポイントは月を +1 して 日を 0 (-1) としているところです。

6. 決算年度を取得 (number)

getFiscicalYear(date: Date): number {
    const year = date.getFullYear();
    const month = date.getMonth() + 1;     
    let tmpQuarter = Math.floor((month - 1) / 3) + 1;
    const quarter = tmpQuarter - 1 === 0 ? 4 : tmpQuarter - 1;
    return quarter == 4 ? year - 1 : year;
}

例)2022/02/07 13:00:00 (Date) ⇒ 2021 (number)
4半期(3月決算)を前提とした値(計算)となっています。
4半期のため1期3か月なので Math.floor((month - 1) / 3) しています。
※それに+1しているのは1-3月が0になるため1始まりにしています。(特に+1せず期を算出する際の -1を消してもOKです。)
そして、3月決算で4月始まりの為1-3月は4とし、その他を繰り下げます。
後は4期は前年度にあたるため 年は -1 になります。 これは「7. 決算期を取得」の内容を絡め記載してますが、決算年度を得るだけであれば以下のように -3 ヶ月したものの年を求めることでもできます。

getFiscicalYear(date: Date): number {
        let date = new Date();
        date.setMonth(xxx.getMonth() - 3);
        return date.getFullYear();
}

7. 決算期を取得 (number)

getQuarter(date: Date): number {
    const month = date.getMonth() + 1;
    let quarter = Math.floor((month - 1) / 3) + 1;
    return quarter - 1 === 0 ? 4 : quarter - 1;
}

例)2022/02/07 13:00:00 (Date) ⇒ 4 (number)
4半期(3月決算)を前提とした値(計算)となっています。

8. 期の最初の日付を取得 (Date)

getFirstDateTimeOfQuarter(year: number, quarter: number): Date {
    const month = (quarter - 1) * 3 + 1;
    const date = new Date(year, month - 1, 1);
    // fiscal year end is March (+3 : Shift one fiscal year)
    date.setMonth(date.getMonth() + 3);
    return Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0);
}

例)year = 2021 / quarter = 4 ⇒ 2022/01/01 00:00:00 (Date)
4半期(3月決算)を前提とした値(計算)となっています。
※ここでは12月決算(1月始まり)で計算し、後に+3か月しています。
時刻も最初 00:00:00.000 を指定しています。

9. 期の最後の日付を取得 (Date)

getLastDateTimeOfQuarter(year: number, quarter: number): Date {
    const month = quarter * 3;
    const date = new Date(year, month - 1, 1);
    // fiscal year end is March (+3 : Shift one fiscal year)
    date.setMonth(date.getMonth() + 3);
    return new Date(date.getFullYear(), date.getMonth() + 1, 0, 23, 59, 59, 999);
}

例)year = 2021 / quarter = 4 ⇒ 2022/03/31 23:59:59.999 (Date)
4半期(3月決算)を前提とした値(計算)となっています。
時刻も最後 23:59:59.999 を指定しています。

10. 00:00:00の日時を取得 (Date)

getFirstTimeOfDay(date: Date): Date {
    let firstDatetime = new Date(date);
    firstDatetime.setHours(0, 0, 0, 0);
    return firstDatetime;
}

例)2022/02/07 13:00:00 (Date) ⇒ 2022/02/07 00:00:00.000 (Date)
setHours は (時, 分, 秒, ミリ秒) なので、他の時刻にしたい場合ここを変更すればOKです。

C# NLogを使ってログ出力する

今回はC#でログ出力する方法です。
以前は自前でクラスを作成することもあったかと思いますが、最近では自作することはほぼないかと思います。
今回はNLogというライブラリを使ってログ出力したいと思います。

nlog-project.org

事前準備1

Visual StudioのNuGetでNLog.Configをインストールします。
NLog.Configをインストールすれば関連するライブラリがインストールされます。

f:id:nack10:20220131083043p:plain

事前準備2

インストールすると、プロジェクト直下に「NLog.config」というログ出力の設定ファイルが作成されます。
しかし、これはリンクで読み取り専用となっており修正できません。
おそらくデフォルトで利用する場合はこのリンクの状態で利用して、もし設定を変更したい場合は上記ファイルをコピーして設定してということだと思います。

設定を変更したいので、コピーして使います。
右クリックメニューから「このアイテムのフォルダーを開く」をクリックします。

f:id:nack10:20220131085112p:plain

エクスプローラーが開くので、NLog.configをコピーします。

f:id:nack10:20220131085214p:plain

Visual Studio上で元々のリンクされたNLog.configを削除した後に、上記でコピーしたNLog.configをプロジェクト配下にコピーします。

f:id:nack10:20220131085307p:plain

出力ディレクトリにコピーは「新しい場合はコピーする」に変更します。

f:id:nack10:20220131190003p:plain

これで修正が可能となります。

設定

NLog.configに設定を追加します。
今回は以下の設定を追加しました。

  • targetsタグ内にファイル出力の設定を追加
  • targetには日本語で出力可能にするために encoding="utf-8" と writeBom="true" を設定
  • rulesタグ内に上記で追加したファイル出力の設定を追加

修正前

修正後

ログ出力

以下の様にログ出力の処理を追加して実行してみます。

f:id:nack10:20220131190121p:plain

bin\Debug配下にlogsディレクトリが生成され、本日日付でログファイルが出力されました。

f:id:nack10:20220131190233p:plain

f:id:nack10:20220131190342p:plain

設定等は色々とあるので、詳細は公式Wikiを参照してください。

github.com

【objective-c】特定のカスタムコントロールを回転時に再描画する方法

こんばんわ。 最近一段と寒いですね。先日も雪が積もってましたので子供に雪遊び誘ったら断られました;;


つい先日、iOSアプリを動作テストしている中でデバイスの回転時(縦と横)にカスタムコントロールのレイアウトが崩れる問題が発生しました。

※カスタムコントロールはdrawRectの中で画面サイズに応じて、frameサイズを決定するような形でが、画面が回転しても再描画されず中途半端なサイズになりました。


って事で画面回転時に再描画を促すような仕組みを作りましたので、備忘録として。

方法としては、回転時のイベントでself.view配下の全てのSubViewをチェックし、特定のクラスであれば再描画を促すって形ですね。

各画面のViewで継承しているベースViewがあれば、ベースに実装したら良いので楽ですね。

// 画面回転時に発生
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{
    [coordinator animateAlongsideTransition:^(id context) {
        // 回転前
    } completion:^(id context){
        // 回転後
          [self updateCustomControll:self.view.subviews];
    }];
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}

// 再帰的にviewをチェックする。
-(void)updateCustomControl:(NSArray<__kindof UIView *> *) subviews{

    for (UIView* view in subviews) {
        if ([view isMemberOfClass:[CustomUITextField class]] ||
            [view isMemberOfClass:[CustomUIButton class]] ||
            [view isMemberOfClass:[CustomUIWebView class]]) {
          
            // 再描画を促す。
            [view setNeedsDisplay];
        }else{
            if(view.subviews.count >0){
                // 再帰的にsubviewsをチェックする
                [self updateCustomControll:view.subviews];
            }
        }
    }
}

C# Span<T>構造体と配列

C# 7.2以降で追加されたSpan<T>構造体や配列などに追加された演算子についての備忘録です。

Span<T>構造体

Span<T>構造体とは、配列などのデータが並んでいるものから一部を取り出して、値を読み書きするものです。

Span<T>構造体は、ref 構造体という参照型の構造体になっていて、文字列などの連続したメモリ領域を直接参照できるので、バイナリデータなどに対してもC言語ポインターのような感覚で安全に扱うことができます。おまけに高速です。

また、読み取り専用のReadOnlySpan<T>構造体も提供されています。

配列を参照する

// int型の配列
var array = new int[]{ 2022, 1, 23, 4, 0 };

// 2番目から2要素分の参照
var spanData = new Span<int>(array, 1, 2);

foreach (var val in spanData)
{
    Console.Write(val); // 123が出力される
}

spanData[0] = 5; // spanDataの1番目を書き換える

foreach (var val in array)
{
    Console.Write(val); // 202252340が出力される
}

文字列を参照する

以下は、ReadOnlySpan<T>構造体を返す拡張メソッドのAsSpanを使用したサンプルです。
なお、Sliceメソッドは位置と長さを指定してデータを切り出すメソッドです。

// 3文字目から3文字を切り出し
var spanData2 = "XXABCXXX".AsSpan().Slice(2, 3);

foreach (var val in spanData2)
{
    Console.Write(val);   // "ABC" が出力される
}

C#のstring型は変更できないオブジェクトなので、AsSpanメソッドは読み取り専用のReadOnlySpanを返却します。
よって、上記のspanData2は、ReadOnlySpanなので書き換えできません。
なお、配列に対してAsSpanメソッドを使う場合は、Spanを返します。

インデックスと範囲の演算子「^」「..」

C# 8.0で追加された演算子^..を使うと、範囲を指定する構文がシンプルに記述できるようになりました。

Span<T>ReadOnlySpan<T>に範囲を指定する際にも使用できます。

例)
 ^N: 末尾からN番目

 N..^M: N+1からM番目。※先頭の位置は0から数える。

var array = new int[]{ 2022, 1, 23, 4, 0 };

// 末尾から3番目
Console.WriteLine(array [^3]); // 23が出力される

// 2~4番目
var span = array.AsSpan()[1..4];

foreach (var val in span)
{
    Console.Write(val); // 1234が出力される
}