OmniAICreator/Qiita-1.07M huggingface dataset:
「SPA」とは何か

## この記事について

この記事の内容は、かつて社内の技術共有の場で共有した内容。
以下共有対象の開発チームについての前提

- 自社のWebアプリケーションは伝統的なBackendでHTML生成 + jQueryという構成
- 画面の一部jQueryで非同期に更新しているが、基本はサーバー側でHTML生成して返す
- SPAという言葉は知っている（はず）が、技術要件などは共有されていない

## Wikipedia の定義

Single Page Application の略。
[Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%B3%E3%82%B0%E3%83%AB%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)

```text
シングルページアプリケーション（英: single-page application、SPA）とは、単一の Web ページのみから構成することで、デスクトップアプリケーションのようなユーザ体験を提供する Web アプリケーションまたは Web サイトである。必要なコード（HTML、JavaScript、CSS）は最初にまとめて読み込むか[1]、ユーザの操作などに応じて動的にサーバと通信し、必要なものだけ読み込みを行う。
```

→ ここでいう「単一の Web ページ」とは単一の HTML のこと
狭義の SPA は大体下の 3 点を満たす

1. Client 側（ブラウザ）での HTML 生成 - VirtualDOM、宣言的 UI / 命令的 UI みたいな言葉で議論されるのはここ。React や Vue はここ
2. Client 側（ブラウザ）での状態管理（State/Store） - MV〇や Flux などで議論されるのは主にここ。Redux や RxJS、VueX、Flux（実装）はここ。サーバーサイド MVC からクライアントサイド MVC へ(=アプリケーションステートの管理をフロント側が担うようになる)
3. Client 側（ブラウザ）でのルーティング - react-router、Vue-Router はここ

## Not SPA の場合

SPA ではない Web サイト／アプリの場合

1. ユーザーがブラウザに URL を入力する（request 発行）
2. サーバー側の処理で HTML を生成する。その過程で DB への通信なども行われる。
    1. DBから取得したユーザー固有の情報やユーザーの入力／操作／画面などのアプリケーションの**状態**をサーバー側で保存・管理する（所謂**セッション**）
    1. サーバー側で管理している「アプリケーションの状態」に応じて、サーバー側でHTMLを生成（jspとか）
3. クライアント側（ブラウザ）へ生成した HTML を返す（response）
4. ブラウザはサーバーから返ってきた HTML を頭から読み込み必要なリソース（JavaScript や CSS 等）を要求する request を発行する
5. 必要なリソース（JavaScript,CSS,png など）がすべて Client 側で読み込まれたとき、ページの表示が完了する
6. リンク等（`<a href="hogehoge">`）をクリックした際には上記 1 から 6 が繰り返される（← ここが SPA との大きな違い）

## SPA の場合

1. ユーザーがブラウザに URL を入力する（request 発行） - 同じ
2. 大体の場合サーバーが返す HTML は Static でアプリケーションを表示するための`ガワ`だけ持っている（`<body><div id="app-root"></div></body>`みたいなの） - ここが違う
3. クライアント側（ブラウザ）へ（Static な）HTML を返す（response） - 同じ
4. ブラウザは response の HTML を読み込み必要なリソース（JavaScript や CSS 等）を要求する request を発行する - 同じ
5. ここで要求される JavaScript によって HTML 内の`ガワ`に画面に必要な要素が生成される - ここが違う。画面の生成は Client 側の JavaScript
    1. ユーザーの入力／操作など状態として保存しておきたいものはClient側で「アプリケーションの状態」として保存しておく（**state/store**）
6. リンク等をクリックした際には 5 で読み込んだ JavaScript で`ガワ`の中身を書き換える。検索結果など動的に変わるものは、その結果を要求する request を発行する（非同期でデータだけ持ってくる） - ここが違う。大体の場合 JSON の配列で結果が返ってくる
    1. 「リンクをクリックしたからリクエストを発行する」のではなく、「表示するコンポーネント内に動的なもの（検索結果とか最新のニュース記事とか）を表示したいから、その情報を取得するためのリクエストを発行する」ということ。
    1. この「リンクをクリックしたときに表示したいコンポーネントを指定する」時に使われるのがreact-routerとかvue-router
    1. DBからのデータ取得はバックエンド側でWebAPIとして実装される
    


## SPA のメリット

- 宣言的UIやFluxなど、開発体験が良い（個人差あり）
- Web アプリケーション（≠Web サイト）ではほぼ標準に近い

## SPA のデメリット

- (工夫をしないと)最初の読み込み時に大きなJavaScriptファイルを読み込むことになるので、そこに時間はかかる

## まとめ

- SPAではかつてサーバー側で実装されていた、1.画面の動的な生成、2.アプリケーションの状態管理、をどちらもクライアント側で実装する
- かつてバックエンド側で、ORMとかDB用のライブラリとかを使ってDBからデータを取得していた部分を、WebAPIとして実装。
- （多分語弊がある）かつてのWebサーバー＝今のフロントエンドのプロジェクト、かつてのアプリサーバー＝上記のようなWebAPIサーバー
- フロントエンドのリポジトリの目的は、Webサーバーに配置するStaticなリソース群（実際はCDNからユーザーへ配信される）をビルドすること
    - staticなindex.html、各種画像やfavicon、webpackでビルドされるbundle.js(app.js)
- 最終的には、SPA ＋ Serverless WebAPIになる（多分）
【Firebase】Firebase Authenticationでユーザーを識別する

# ユーザーを識別する

## やりたいこと
vue + firebaseで投げ銭アプリ作成中。
ログイン時、ユーザ（ドキュメント）を識別して、そのデータを取得したい。

新規登録時にuidをドキュメントIDに指定

```js
        // 新規登録
        signUp (context, payload) {
            // firebase Authにユーザ情報登録
            firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
                .then(() => {
                    // firebase Authのユーザ名更新
                    const user = firebase.auth().currentUser
                    user.updateProfile({
                    displayName: payload.username,
                    },)
                .then(() => {
                    // データベースへ登録
                    const db = firebase.firestore();
                    db.collection("userData").doc(user.uid).set({
                        uid: user.uid,
                        email: payload.email,
                        password: payload.password,
                        username: payload.username,
                        myWallet: payload.myWallet,
                    })
                })
                .then(() => {
                    context.commit('registerUserData', payload)
                })
                .then(() => {
                    router.push('/')
                })
                })
                .catch(error => {
                    alert(error.message)
                })
        },

```

指定したuidでドキュメントを識別し、ユーザを取得する

```js

        // ログイン
        signIn (context, payload) {
            // 登録済みのメールアドレスとパスワードでログイン
            firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
            .then(() => {
                const user = firebase.auth().currentUser
                const docRef = firebase.firestore().collection("userData").doc(user.uid);
                docRef.get()
                .then((doc) => {
                    if (doc.exists) {
                        context.commit('setUserData', doc)
                    } else {
                        console.log("No such document!");
                    }
                }).catch((error) => {
                    alert(error.message)
                });
            })
            .then(() => {
                router.push('/')
            })
            .catch(error => {
                alert(error.message)
            })
        }

```

# 参考
https://firebase.google.com/docs/rules/rules-and-auth?hl=ja#identify_users

https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja#web_1

https://www.wakuwakubank.com/posts/723-firebase-firestore-query/#%E5%8D%98%E4%B8%80%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%82%92%E5%8F%96%E5%BE%97br-collectionreferencedocdocidbr-documentreferencegetbr-%E6%88%BB%E3%82%8A%E5%80%A4-documentsnapshot

https://teratail.com/questions/325119
RealSense T265の位置情報を使って、VR空間を移動する


OculusQuest2, ROSなどを使ってARラジコンを作っているのですが、今回ラジコンの移動に合わせてOculusQuest2上のVR空間を移動できるようにしたいと考えました。
そこで、ラジコンの位置を***RealSense T265***で取得し、それをROSを使ってOculusQuest2に送ることでVR空間を移動できるようにしました。

なお今回実装をするにあたり、OculusQuest2でROS#が、RaspberryPi4でRealsenseT265がすでに動く状態となっていることを前提としています。
それぞれの動かし方については以下の記事を参照してください。

- [Raspberry Pi 4+ROSでRealsense T265を使う](https://qiita.com/hiro-han/items/90faf29406e689243b4a)
- [Oculus Quest 2でROS Sharpを使う](https://qiita.com/hiro-han/items/a36c54e869d2a199402d)
- [Oculus GoにRaspiのカメラ画像を表示する&姿勢を取得する](https://qiita.com/hiro-han/items/5303ac758d798f1db587)

# デバイス、環境
- Raspberry Pi4 Model B
    - Ubuntu Server 18.04
    - ROS melodic
- Oculus Quest 2
    - Unity
    - ROS# v1.6
- RealSense T265

# 実装
ROSでRealSenseT265を使うと*nav_msgs/Odometry*の型のデータが*/camera/odom/sample*というtopic名で出力されます。
ROS# v1.6では、Odometryを受信する*OdometorySubscriber*がすでに用意されているので、これを使用します。

## Odometryの受信
1. RosConnectorのInspectorウィンドウを開く
2. `Assets/RosSharp/Scripts/RosBridgeClient/RosCommuncation`にある`OdometorySubscriber`をドラッグ＆ドロップする
3. Topicに`/camera/odom/sample`と入力する


## RealSense T265に合わせて、移動できるようにする
RealSense T265から受信したOdometryを使って移動するオブジェクトを作成し、その上にOculusQuest2のカメラを置くことで、VR空間を移動できるようにします。

1. Hierarchyウィンドウで右クリックし、3D Object->Cubeを作成し、名前をRC-Carとする
2. 作成したCubeの大きさを実際のラジコンと同じ大きさにする。また、位置を後輪軸の中心が原点になるように位置を調整する。
    - 自分は以下のようにしましたが、VR空間を移動したいだけならデフォルト値で大丈夫です
        - Position  x : 0,    y : 0,    z : 0.08
        - Rotation  x : 0,    y : 0,    z : 0.08
        - Scale     x : 0.26, y : 0.21, z : 0.33
3. Hierarchyウィンドウで、`OVRCameraRig`を`RC-Car`の下にドラッグ&ドラッグする
4. `OVRCameraRig`の位置をラジコン上のカメラ位置と一致するようにTransformを設定する
    - Position  x : 0,    y : 0.17, z : 0.08
    - Rotation  x : 0,    y : 0,    z : 0.08
    - Scale     x : 1,    y : 1,    z : 1
5. Hierarchyウィンドウで、`PlaneRotator`を`RC-Car`の下にドラッグ&ドラッグする
    - 「[Oculus GoにRaspiのカメラ画像を表示する&姿勢を取得する](https://qiita.com/hiro-han/items/5303ac758d798f1db587)」の記事で、ヘッドセットの向きに合わせてカメラ画像の位置を動かすために、PlaneRotatorを実装しました。それも一緒に移動するようRC-Carの下に移動します。
       - Position  x : 0,    y : 0,    z : 0
       - Rotation  x : 0,    y : 0,    z : 0
       - Scale     x : 1,    y : 1,    z : 1

## ビルド
通常の手順でUnityアプリをビルドしOculusQuest2にデプロイします

## launchファイルの作成
ROSのRealSense用のノードやRosBridgeをまとめて動かすために、以下のlaunchファイルを作成しました。
`rosbridge_websocket/address`のIPアドレスは、実行環境に合わせて変更してください。

```t265_test.launch
<launch>
  <include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch" /> 
  <param name="rosbridge_websocket/address" value="192.168.4.8"/>
  <include file="$(find realsense2_camera)/launch/rs_t265.launch" /> 
</launch>
```


# 実行
## Raspi側
launchファイルを実行します

```
roslaunch t265_test.launch
```
## OculusQuest側
アプリの提供不明から、開発したアプリを実行します。

# 結果
RealSense T265を動かして、それに合わせてカメラが移動すれば成功です。
以下の動画はカメラの移動が分かりやすいように、cubeを置いてみた動画です。
![realsense-unity.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293692/582326cd-7627-6667-7dee-b3240209d873.gif)


# 参考
- [Raspberry Pi 4+ROSでRealsense T265を使う]()
- [Oculus Quest 2でROS Sharpを使う](https://qiita.com/hiro-han/items/a36c54e869d2a199402d)
- [Oculus GoにRaspiのカメラ画像を表示する&姿勢を取得する](https://qiita.com/hiro-han/items/5303ac758d798f1db587)
ファイル結合→展開時に全てのカラムを展開する（展開するカラムを個別に指定しないで展開）

#データをマージして展開するときや、フォルダからファイルを指定して展開する

今回はフォルダを指定して、その中の複数ファイルを取り込んで結合するシナリオを考えてみます。（マージしたテーブルを展開するときも同様に処理できる）

Table と表示されているカラムを展開するときに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/48d3728b-19b2-add1-2bb7-6927d02b380e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/b1ce0d5e-29e0-715f-dd87-681c18db3a0a.png)

↑この画面で「（すべての列の選択）」を選んでも、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/977cdd88-73c9-9b95-741b-4e3718fa745a.png)


生成される式はこんな感じで、カラム名が自動的に指定されて展開されます。

　↓自動で作成された式

    = Table.ExpandTableColumn(削除された他の列, "展開用", {"カラム１", "カラム２", "カラム３", "カラム４"}, {"展開用.カラム１", "展開用.カラム２", "展開用.カラム３", "展開用.カラム４"})


#元データにカラムが増えたらどうなる？

元データにカラムが増えちゃうこともありますよね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/ef8a6b16-e28e-741d-7bb2-fa6d68e0b76c.png)
↑「カラム５」が増えた

データを更新しても…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/a4ff40c8-d499-5dcd-4e80-e8aec41bccc9.png)

うん、カラム５は無視されますね。

#新しく増えたカラムもそのまま取り込みたい！

元の式を見ると、こんなふうになっているので(第四引数は任意なのでグレーで着色）
　Table.ExpandTableColumnの説明はこちら↓
　　https://docs.microsoft.com/ja-jp/powerquery-m/table-expandtablecolumn
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/8620ed28-e60a-74e3-4073-536a10e8d4dc.png)

元のカラム名のまま展開してよいなら、第三引数に使用するための、テーブルに含まれるカラム名のリストが取得できれば何とかなりそうです。

##テーブル内のカラム名のリストってどうやって取得できるの？

それぞれのテーブルのカラムの中身はこれで取得できそう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/bc4aea6d-b9ea-f98a-6179-91aa339c9c01.png)

    = List.Transform(削除された他の列[展開用],each Table.ColumnNames(_))

ただ、今回は複数ファイルを結合しているため、ファイルごとにカラム名のリストがそれぞれできちゃうので、このまま突っ込むと怒られます。。。
なので、リストの中身同士の和集合（全部足して重複をなくした全カラムのリスト）をList.Unionで作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/e58e7edc-fc88-d448-2d3c-d562a716a7ea.png)

    = List.Union(List.Transform(削除された他の列[展開用],each Table.ColumnNames(_)))

うん、これで目的のカラム名の一覧になりそう。

##あとは、元の式を置き換えるだけ！

一回、いつも通り展開した式をPowerQueryさんに自動で作ってもらって、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/27dbcce0-5a70-c229-8850-2972c126517c.png)

水色のところをさっきの式で置き換え＆青いところを削除
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120006/bca6abb2-e45d-12fd-74a8-62b8383eb3c2.png)

ぶらっぼう！Book1にしかない、新規追加された カラム５まで取得できるようになりました。
#5 午後Ⅱに歯が立たない件

この記事はシリーズものです。トップはこちらから。

 - [ネットワークスペシャリスト試験を受けることにしました【令和3年春季】](https://qiita.com/foxtail1979/items/5e333d28f2e93acb552b)

# やはり難易度が高い
午後Ⅰに比べるとやはり難易度が高いです。

 - 問題文が長いので、問題文を正しく読み解くのが困難
 - 記述問題が多く、回答を推敲する時間が足りない


過去問をやってみた限り絶望的な印象ですが、兎にも角にもやらないことには始まらないので、最後まで頑張ってみます。

# H29午後Ⅱ　問1
 - 無線は苦手なので、消去法で問１を選択。

# H28午後Ⅱ　問2
 - VoIPの用語が見えたので、消去法で問２を選択。
 - IPSec区間とトンネルモードの両端が一致する場合、GREを使えばトランスポートモードを選択する必要がない
 - SAの生存期間終了まえに共通鍵を事前生成しておくことをRekeyという
 - トランスポートモードで暗号化されるデータを抑えておく
 - VRRPは通常デフォルトゲートウェイの冗長化が目的なため、ホストが接続されているサブネットの数だけ仮想IPアドレスをもつ

# H27午後Ⅱ　問2
 - FTPの知識は基本知識は抑えておく。アクティブモードとパッシブモード(サーバから見て、データ転送ポートを決定するのがどっちか)
 - マルチキャストMACアドレスはマルチキャストIPアドレスの下位23ビットをそのまま使う。上位24+0ビット分は固定なので01-00-5e
 - IPSecのNATトラバーサルの方法の一つはUDPヘッダの挿入。ISAKMPのポート番号変換のために、500と4500以外も受信可能なように通信経路で設定が必要
 - マルチキャストMACアドレスは送信元になることがないので、スイッチにおいて送出ポートとMACアドレスの対応が存在しないので、MACアドレステーブルに学習されない
 - VXLANフレームの送信元IPアドレスは仮想スイッチになる
 - マルチキャストグループに所属していることの存在通知（IGMP join）は、マルチキャストを受け取るクライアント側のアプリケーションが行う。

# H26午後Ⅱ　問1
 - MTA。。。メール転送プログラム。postfixとか。メールクライアントはMUA。
 - SPFレコードを用いた認証では、メールアドレスのドメイン名とメール送信元のMTAのIPアドレス所属ドメインを比較する。
 - プリマスタシークレットはサーバ側の公開鍵で暗号化される(TLS1.2までの場合)
 - ネットワークアドレスの重複時に通信がうまくいかないのは、ルーティングが決定できないから。

# H25午後Ⅱ　問1
 - NICチーミングもリンクアグリゲーションの1種といって差し支えない。
 - モバイルIPは無線AP間を移動しても、同じIPアドレスを利用できる（ローミング）
 - IEEE802.1X認証だとPMKを再利用できるので、無線AP間移動時に再認証のシーケンスが省略できる
 - GARPの目的は、接続機器のARPテーブルを更新させるため。
 - 無線AP設置時は電波の到達範囲計測、電波の遮蔽物による減衰がどの程度かを現地で行う（サイトサーベイ）

# H24午後Ⅱ　問1
 - ICMPリダイレクトはルータの基本機能。より効率的なネクストホップが同一セグメントに存在する場合、そのデフォルトゲートウェイの存在を送信元に通知する。

# H23午後Ⅱ　問2
 - DNSのゾーン転送感覚はrefresh
 - そもそもvlanが異なるポート間ではループが発生しない（ブロードキャストしないから）
 - ワンタイムパスワードの有効時間が短いのは、再利用を防ぐため
 - 移行を期間を分けて行う場合、トラフィックに注目すると正解の場合がある（一気に実施しないのはなぜか？を考える）
 - VRRPトリガ。リンク障害時に全てのVRRPがマスタルータを統一する機能。監視対象インタフェースとpriority値の設定が必要。
じゃあどうしたらRedmineの各項目をREST APIで更新できるのか


てきとーにググっただけだと[公式](https://www.redmine.org/projects/redmine/wiki/Rest_Issues#Updating-an-issue)にあるような「題名」「コメント」の例しか見当たらなかった。
で、見よう見まねで優先度の更新をしようとしてもなかなかうまくできずハマったのでメモ。

とりあえずサクッと動きを見るため、`curl`+JSONでお試し。
Redmine側は、[bitnami Redmine](https://www.google.co.jp/search?q=bitnami+redmine)をインストールしたそのままで確認。

## 基本コマンド
以下の記事を参考に、`{JSON指定}`を後述する各項目のものに書き換えて確認する。

* [cURLでRedmineのIssueを更新する](https://qiita.com/kon_yu/items/7b6734807967e4f9372c)

```
$ curl [REDMINE_HOME]/issues/[ID].json?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxx  -H "Accept: application/json" -H "Content-type: application/json" -X PUT -d '{"issue": {JSON指定}}'
```

例えば、優先度を変更したい時は以下のようにする。

```
$ curl [REDMINE_HOME]/issues/[ID].json?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxx  -H "Accept: application/json" -H "Content-type: application/json" -X PUT -d '{"issue": {"priority_id": 3 }}'
```

## 項目ごとのJSON指定
Web経由でチケット更新するとbitnami Redmineのログに残るので、そこで指定しているパラメータを使用することで更新できそう。
今回はJSONだけど、同じ構成でXMLもいけるんじゃないかな。未確認。


### デフォルト項目
説明、優先度、開始日、期日、カスタムフィールド以外は推測 (試してない)。

|項目|JSON例|設定値|
|:--|:--|:--|
|プライベート設定|`{"is_private": 0 }`|0なら公開、1ならプライベート?|
|プロジェクト|`{"project_id": 0 }`|プロジェクトごとに割り振られた数値|
|トラッカー|`{"tracker_id": 0 }`|トラッカーごとに割り振られた数値|
|題名|`{"subject": "更新後の題名" }`|テキスト|
|説明|`{"description": "更新後の説明" }`|テキスト|
|ステータス|`{"status_id": 0 }`|ステータスごとに割り振られた数値|
|優先度|`{"priority_id": 0 }`|優先度ごとに割り振られた数値|
|担当者|`{"assigned_to_id": 0 }`|担当者ごとに割り振られた数値|
|親チケット|`{"parent_issue_id": 0 }`|親チケットの番号|
|開始日|`{"start_date": "2020-02-20" }`|日付文字列|
|期日|`{"due_date": "2020-02-20" }`|日付文字列|
|予定工数|`{"estimated_hours": 0 }`|ステータスごとに割り振られた数値|
|ステータス|`{"status_id": 0 }`|ステータスごとに割り振られた数値|
|コメント|`{"notes": "コメントの内容" }`|テキスト|

### カスタムフィールド
カスタムフィールドの場合、JSON指定に`{"custom_field_values": { "カスタムフィールドID": カスタムフィールド設定に対応する入力値 }}`が入る。
例えば、カスタムフィールドIDが`12`、入力形式がON/OFF(bool)の場合、`{"custom_field_values": { "12": 1 }}`とすることで設定値をONに設定できる。

### xxxごとに割り振られた数値やカスタムフィールドのIDを確認する方法
1. Web上のチケット編集画面を開く
2. ブラウザのデベロッパーツールを開く
3. 調べたい対象の入力項目をインスペクターで選択
4. 選択式の入力欄なら`<select>`タグの子要素に`<option value="~~">`の記載があり、この`value`の値がIDに対応する。
5. カスタムフィールドIDも同様に調べられる。`<label for="issue_custom_field_values_12">`となっていたりするので、この末尾の数値を使用すればよい。これがIDになっている。
【SpringBoot】画像表示する2つの方法（1/2）

#SpringBoot2で画像表示
今回は画像表示をやっていきます。
やり方としては2つあります。

<strong>①HTMLのimgタグを利用してサーバにリクエストを出し、画像データをレスポンスとして返す方法。</strong>

<strong>②画像データをBase64に変換しimgタグに直接埋め込む方法。</strong>

今回は①のほうでやっていきます。
②の方法は以下のリンクからどうぞ
[SpringBoot2で画像表示する2つの方法（2/2）](https://qiita.com/nmaolks/items/e55718880e260a553c62)

##全体の流れ
①GETリクエストで画面にアクセス
②画面からResponseBodyにGETリクエストを送る
③画面に画像データをレスポンスする。

<h2>各ファイルの作成</h2>

####Controllerを作成する
Fileクラスに画像フォルダのディレクトリを指定しています。
画像をバイト列に変換しResponseHeaderに画像情報をセットしています。
その後バイト列にした画像、ヘッダー情報を送信しています。


```java:ImageController
@Controller
public class ImageController {

	@GetMapping("/")
	public String getIndex() {
		return "image";
	}
	
	@RequestMapping("/getImg")
	@ResponseBody
	public HttpEntity<byte[]> getImg(@RequestParam("name") String fileName){
		File fileImg = new File("img/"+ fileName +".png");
		
		byte[] byteImg = null;
		HttpHeaders headers = null;
		try {
			//バイト列に変換
			byteImg = Files.readAllBytes(fileImg.toPath());
			headers = new HttpHeaders();
			
			//Responseのヘッダーを作成
			headers.setContentType(MediaType.IMAGE_PNG);
			headers.setContentLength(byteImg.length);
		}catch(IOException e) {
			return null;
		}
		return new HttpEntity<byte[]>(byteImg,headers);
	}
	
}
```
####htmlを作成する
src属性から上記ResponseBodyにリクエストを送ります。

```html:image.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<title>画像表示</title>
</head>
<body>
	<p>画像表示</p>
	<img th:src="@{./getImg?name=testimg}">
</body>
</html>
```
ActiveModel::Errorsインスタンスから、エラー対象のモデルのインスタンスを取得する

# 環境
rails: 6.0.3.6
ruby: 2.6.5

# 結論

```
errors.instance_values["base"]
```
を用いることで、対象のインスタンスを取得することができる。

# 背景
localeが使われていないプロダクトで、localeを少しづつ導入を進めていた。
localeが使われていないので、model内で、バリデーションメッセージを全て手入力で入れていた。

```hoge.rb
validate: name, present: {message: 'hogeを入力してください'}
```

そのため、slimのrailsフォームのエラーメッセージを表示する共通テンプレートの中で、エラーメッセージを出力するロジックが以下のように書かれていた。

```form_error.html.slim
- errors.messages.values.each |message| do
   p = message
```

# 直面したこと
一気にlocaleを反映させるのはビッグコミットになるため、一部機能から順に差し替えていった。

```hoge.rb
validate: name, present: true
```
しかし、そうすると、

```form_error.html.slim
- errors.messages.values.each |message| do
   p = message /'を入力してください'と出てしまう
```
そのため、「locale対応している場合はfull_messagesを、そうではない場合はmessages」を出すことで対応することに。
このファイルの中で、このモデルがlocal対応しているかどうかを判別して処理を分けようと試みたが、errorsから「どのモデルのエラーなのか」を取得する方法が出てこなかった。
(簡単に取れそうなのに、、！！)

# 試したこと
console上では

```console
[1] pry(main)> hoge = Hoge.new
[2] pry(main)> hoge.save
[3] pry(main)> hoge.errors
=> #<ActiveModel::Errors:0x00007ff0e2a6cb30
 @base=
  #<Hoge:0x00007ff0eaaa6738
   id: nil,
   name: nil>,
 @details=
  {:name=>[{:error=>:blank}]}]},
 @messages={:name=>["を入力してください"]}>

```
と出ていたが、

```console
hoge.errors.base
-> undefined method `base' for #<ActiveModel::Errors:0x00007ff0e2a6cb30>
```
あれ、取得できない。

こういう時はrailsに聞くのが一番。

```console
hoge.errors.methods
->  :to_hash,
    :full_messages,
    :delete,
    :to_a,
    .
    .
    :instance_values,
    .
    .
```
それっぽいの見つけた！

```console
hoge.errors.instance_values
-> {"base"=>
  #<Hoge:0x00007ff0eaaa6738
   id: nil,
   name: nil>,
 "messages"=>{:name=>["を入力してください"]},
 "details"=>
  {:name=>[{:error=>:blank}]}
```
hashで中身を返してくれるらしい。

暫定コードとしてはこんなかんじ。

```form_error.html.slim
ruby:
  error_messages = if I18n.exists?("activerecord.attributes.#{errors.instance_values["base"].class.name.underscore}")
                     errors.full_messages
                   else
                     errors.messages.values
                   end
- error_messages.values.each |message| do
   p = message
```

早く全てlocale移管したい。
Poetry + pyenv-win 覚書

# やること
Windows の VSCode で Poetry + pyenv-win の環境を構築します

# 環境
Windows 10 Home バージョン 20H2
Python 3.9.4
Visual Studio Code (以下、 VSCode) 1.55.0

※「ターミナル」と出ている場合は特筆する場合を除き VSCode の「ターミナル」より Git Bash で実行しています
※「ターミナルを再起動」は、 VSCode 自体を開きなおしてください

# 手順
## pyenv-win の準備
ターミナルで以下のコマンドを実行する

```bash
pip install pyenv-win --target $HOME\\.pyenv
```

ターミナルを Power Shell に切り替え、以下のコマンドを実行する

```powershell
[System.Environment]::SetEnvironmentVariable('PYENV',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")
[System.Environment]::SetEnvironmentVariable('PYENV_HOME',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")
[System.Environment]::SetEnvironmentVariable('path', $env:USERPROFILE + "\.pyenv\pyenv-win\bin;" + $env:USERPROFILE + "\.pyenv\pyenv-win\shims;" + [System.Environment]::GetEnvironmentVariable('path', "User"),"User")
```

ターミナルを開きなおし、 `pyenv --version` でバージョンが表示されることを確認する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199232/37063c75-8d4d-0f1c-a49f-d4b7470b8876.png)

`pyenv update` コマンドでアップデートしておく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199232/0c56d8c1-ad46-dd8c-bed2-adcd405c4946.png)


## poetry の準備
ターミナルで以下のコマンドを実行する

```bash
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
```

ターミナルを開きなおし、 `poetry --version` でバージョンが表示されることを確認する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199232/ac906208-a5fc-4d06-f378-cf4435a6ec82.png)

# 参考
https://github.com/pyenv-win/pyenv-win

https://cocoatomo.github.io/poetry-ja/
Githubでリポジトリ作成したときに出てくるやつ（メモ）

# 注意
記事として独創性は皆無。
リポジトリを作成したときにいつもコミットの仕方を忘れるので残しておきます。

## ローカルで設定すること

コマンドラインからリポジトリを作成する場合：

```bash
echo "# [repo. name]" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/[username]/[repo.].git
git push -u origin main
```

既存のリポジトリをプッシュする場合：

```bash
git remote add origin https://github.com/[username]/[repo.].git
git branch -M main
git push -u origin main
```

久々に利用したらいつの間にかデフォルトのbranch名が`master`から`main`に変わっていました。

# 最後に

gitちゃんと勉強しないと、後々めんどくさいことになります。
（少なくとも、rebaseやresetといったリモート先のコミット履歴を消すまでは反射的に体にしみこませないと。）
Carthageを旧バージョンに戻した話



# 気の迷いでHomebrewをアップデートしたらCarthageが0.37になってしまいました。
Xcode12.4でビルドしていたmacos用のプロジェクトがいきなり下記のようなエラーを吐いた。lipoのエラーでビルドできなくなってた。M1対応はまだここでは不要だったので、仕方なく元に戻すことにする。

```
A shell task (/usr/bin/xcrun lipo ... failed with exit code 1:
error: /Applications/Xcode12.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: one of -create, -thin <arch_type>, -extract <arch_type>, -remove <arch_type>, -replace <arch_type> <file_name>, -verify_arch <arch_type> ... , -archs, -info, or -detailed_info must be specified ...
```
チェックアウト対象を探す

```
$ cd /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula
$ git log carthage.rb
```
0.37が入ったままだとダメそうなので、まず、今のやつをunlinkする

```
$ brew unlink carthage
```
例えばログから辿った0.35.0をチェックアウトする

```
$ git checkout 6392e419ab3827152e9ea4f1475f4556b158ddae carthage.rb
```

再インストール

```
$ brew install carthage
```

バージョンチェックする。

```
$ carthage version
Please update to the latest Carthage version: 0.37.0. You currently are on 0.35.0
0.35.0
```
バージョンアップするようメッセージが出るが、今必要なのはこっち。
checkoutしたcarthage.rbを元に戻さないといけなそうだけど、ひとまずこのまま運用。


参考文献

https://www.meganii.com/blog/2019/12/03/how-to-install-an-older-version-of-a-homebrew-package/
[Java]フィールド・メソッドのアクセス修飾子について

### フィールド・メソッドのアクセス修飾子の種類

- public
- protected
- 記述なし
- private

以上の４つが存在する。 

### それぞれのアクセス修飾子を比較

```JAVA
public class Abc{
    
    // 1."public"の場合。
    // 他のパッケージのクラスからもアクセス可能なフィールド
    public String a1;
　　
    // 他のパッケージのクラスからもアクセス可能なメソッド
    public String b1(String b2){
         処理
    }

    // 2."protected"の場合。→他のパッケージのクラスからアクセスできない。
    // class Abcのサブクラスと同一パッケージのクラスからアクセス可能なメソッド
    protected char c1;

    // class Abcのサブクラスと同一パッケージのクラスからアクセス可能なメソッド
    protected String d1(String d2){
        処理
    }

    // 3.記述なしの場合。
    // 同一パッケージ内のみ使用可能なフィールド
    String e1;
    
    // 同一パッケージ内のみ使用可能なフィールド
    String f1(String f2){
        処理
    }

    // 4."private"の場合。
    // class Abcでしかアクセスできないフィールド
    String g1;

    // class Abcでしかアクセスできないメソッド
    String h1(String h1){
        処理
    }

}
```

<b>"protected"と記述なしの違いとしてはサブクラスがアクセス可能かどうかという点。<b>
サブクラスが違うパッケージに属していてもアクセス可能。

クラスのアクセス修飾子については[こちら](https://qiita.com/taccc_/items/8efb1db9d46e48eda152)
既存のファイルの中身を空にする（シェルスクリプト ）

こんにちは。
既存のファイルに対して、その中身を空(empty)にするシェルスクリプトを作りました[^1]。

しかし実体は ```rm``` コマンドですので、不用意に使うことは避けたほうが良さそうです。

```console
$ ./truncate0.sh a.txt b.txt
$ ls -ld a.txt b.txt
-rw-r--r--  1 kkdd  staff  0  4  5 23:35 a.txt
-rw-r--r--  1 kkdd  staff  0  4  5 23:35 b.txt
```

```truncate0.sh
#!/bin/sh
for file in "$@"; do
  [ -e "$file" ] && rm -f "$file" && touch "$file"
done
```

[^1]: 参考：「[既存のファイルを空にするLinuxコマンド](https://qiita.com/nabewata07/items/e67d008a1f10429bb3bf)」
#PowerAutomate で #Outlook の休日の予定から祝日時のみ動作する処理を作る方法 

定期実行するけど、祝日だったら動作する（もしくはしない）っていう処理って結構ありませんか？
でも、曜日は比較的簡単に取れるけど、祝日ってそう簡単に取れるのでしょうか？

ということで、そんな貴方に朗報。
Power Automate と Outlook で、簡単に祝日判定ができるやり方をまとめました。

## 祝日の取得方法

Outlook には、日本の休日という予定表が自動的に入っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/6709d12b-3506-ffde-f92b-de137d3469ce.png)


これを使えば、いちいち毎年祝日を管理するようなテーブルを作る必要もありませんし、祝日が増えたら（もしくは減ったら）勝手に更新されるので、使わない手はないですよね。

なので。休日は以下のように、「イベントのカレンダービューの取得」を使うことにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/fac5030f-6871-b7eb-247f-cc7e5b468b6f.png)

### 開始時刻と終了時刻

開始時刻や終了時刻は、実行当日の日付を入れる必要があります。
なお、日本の休日のタイムゾーンUTCで宣言されていますが、時刻は以下のようにすれば良いみたいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/be21aaef-a474-5991-23bd-8fecd616f893.png)

```yyyy-MM-ddT00:00:00.000000``` で時刻を指定する必要があるようですし、開始時間が現在日付、終了時刻に翌日の日付を指定する必要があります。

なので、関数を使ってこちらを処理することになります。

```bash:開始時刻

formatDateTime(
    convertTimeZone(
        utcNow(),
        'UTC',
        'Tokyo Standard Time',
        'yyyy-MM-dd'
    )
)
```

```bash:終了時刻

formatDateTime(
    convertTimeZone(
        addDays(
            utcNow(),
            1
        ),
        'UTC',
        'Tokyo Standard Time',
        'yyyy-MM-dd'
    )
)
```

関数の解説ですが

1. ```utcNow``` で現在の日時をUTCで取得します。
2. 終了時刻のみ、```utcNow``` に対して ```addDays``` で1日加算します。
3. ```convertTimeZone``` でUTCをJST（日本標準時）に変換、かつ時間を切り捨てるためにフォーマットを ```yyyy-MM-dd``` に変換します。
4. ```formatDateTime``` で ```yyyy-MM-dd```を ```yyyy-MM-ddT00:00:00.000000``` に変換します。

すると、以下のように、現在日時を元に、開始時刻と終了時刻に適切な値が入力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/e6a5fd03-9ef2-e647-1eb6-bb720c75289e.png)


### 結果はアレイ変数へ

Outlookのこのアクションは、複数のデータが出ることを前提（アレイ型）で出力されます。
今回、この結果が存在するか否かという判定をすることで、祝日判定を行います。
なので、後続で無用な Apply to Each を追加されないよう、アレイ型で変数を宣言します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/2579db9a-7f56-a0b2-453c-5b8f2c9e713f.png)

変数宣言時の値に割り当てる動的コンテンツには、イベントのカレンダービューで取得した値の一覧を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/a083dce1-0e73-2239-0e12-912bb1e745df.png)

## いよいよ判定です

変数がカラだった場合は平日、そうじゃない時は祝日と判定できますので、そのとおりに組んでいきます。

条件式を以下のようにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/61b04da1-14dd-54b2-d1ee-81ae7af68afc.png)


```bash:判定対象

empty(variables('結果'))
※変数名を「結果」としていた場合

```

このように組むことで、はいの部分には平日のみが動作する処理、いいえの部分には祝日のみが動作する処理が完成します。

なお、この処理は、LogicApps でも応用して導入することができると思いますので、社内システムのバッチ処理に使っている方は、ぜひこちらの祝日判定をお試しされるとよいのではないでしょうか？


## まとめ

いかがでしたでしょうか？
実はこの処理、より関数を使いこなせば、より少ないアクションで判定させることができると思います。
ですが、そうなるとプロ開発者に依頼しないといけなくなってしまいます。
ですので、できるだけ処理を簡単に、ある程度GUIでもらくらく作ることができる方法としてまとめてみました。
皆さんの業務の一助や、アイデアにつながれば幸いです。
KeycloakでMattermost(Team Edition)にSSOログインする際に必要な数値IDをスクリプトマッパーでUUIDから生成する

# 概要

Keycloakの応答をGitLabに偽装してMattermost(Team Edition)に対応させる場合、GitLabのユーザーIDに対応するクレームをMattermostに渡す必要がある。詳細はネタ元の下記記事を参照のこと。

https://qiita.com/wadahiro/items/8b118c34aae904353865

同記事で言及されている通り、GitLabのユーザーID（GitLabのDB上では `users.id` ）はユニークな数値であり、Keycloakに標準で用意されているユーザープロパティ・属性には適当なものがない。

とはいえKeycloakのユーザーIDはUUID(v4)形式、つまり128bitの数値なので、16進数の `FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF` のフォーマットから10進数に変換するスクリプトマッパーを用意すれば専用のユーザー属性を追加することなく対応できる。

# スクリプトマッパーの作成

## JavaScript

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63934/a35ed9ca-357b-1a14-02cc-338e65df15b5.png)

KeycloakのユーザーIDはDB上では `user_entity.id` に格納されており、スクリプトマッパーからは `user.getId()` で取得できる。

今回は先頭の12桁を使用し、48bitの整数を得る。

```javascript:uuid-to-int48-mapper.js
parseInt(user.getId().split("-").join("").substr(0,12),16).toString();
```

`toString()` を行うのはKeycloakの仕様によるもの。出力時のフォーマットはクライアント設定で選べる（後述）。

## jar

スクリプトマッパーをKeycloakの管理コンソール上で直接記述する機能もあるが大分前からdepricatedのため、jarファイルに固めて読み込ませる。

[公式ドキュメント](https://www.keycloak.org/docs/latest/server_development/#_script_providers)に従って `keycloak-scripts.json` にメタデータを記述する。例えば以下のようになる。

```json:keycloak-scripts.json
{
  "mappers": [
    {
      "name": "UUID to INT48",
      "fileName": "uuid-to-int48-mapper.js",
      "description": "Convert Keycloak User ID (UUID v4) to 48-bit integer (16 digits) for Mattermost"
    }
  ]
}
```

ファイルを規定の構造でzip圧縮し、拡張子を `jar` に変更する。

```plaintext
uuid-to-int48-mapper.jar
|-- META-INF
|   `-- keycloak-scripts.json
`-- uuid-to-int48-mapper.js
```

書庫のルートにMETA-INFディレクトリとJSファイルが配置されるようにする。

# Keycloakの設定

## jarの配置

作成したjarファイルを `standalone/deployments` に配置する。
Keycloak公式Dockerイメージの場合は `/opt/jboss/keycloak/standalone/deployments` にマウントする。

## JavaScriptプロバイダーの有効化

設定ファイル `profile.properties` でJavaScriptプロバイダー（スクリプトマッパーはこの機能の一部）を有効化する。

```plaintext:profile.properties
feature.scripts=enabled
```

設定ファイルは `standalone/configuration/profile.properties` に配置する。
Keycloak公式Dockerイメージの場合は `/opt/jboss/keycloak/standalone/configuration/profile.properties` としてマウントする。

上記手順を実施してKeycloakを起動すると、正常に読み込まれていれば「管理コンソール→サーバー情報→プロバイダー」に表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63934/7576b438-8933-5980-2cad-da6a467482e0.png)

## クライアント設定

Mattermostのクライアント設定で作成したスクリプトマッパーをトークンクレーム名 `id` に割り当てる。型は `long` とする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63934/2f2cdba6-180b-23aa-f7fe-00567c2739fe.png)

# 動作確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63934/7c720797-dc2c-c198-2228-81af90d46c86.png)

最初の12桁 `82286c9c2d4c` を10進数に変換すると `143110132477260` 。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63934/71a71f60-6166-5e45-f890-8185f6e7117f.png)

Mattermostの `users.authdata` に同じ値が入っている。
Visual Studio for Mac 2019にStyleCopAnalyzersを導入する

## この記事を読んでできるようになること
Visual Studio for Mac 2019にStyleCopAnalyzersを導入し、C#コードの静的解析ができる。

## きっかけ
Unity開発をしているので、綺麗なコードを書くためにVisual Studio for Macに静的開発ツールを導入したくなった。そこで色々と調べたところStyleCopAnalyzersがヒット。
すぐに導入を試みたがNugetパッケージを導入する方法、GitHubから直接slnをもらってきて導入する方法、どれもうまくStyleCopAnalyzersが機能しなかった。
調べてもWindows版のVisual Studioしか良さそうな記事がなく困ったので
これを機にVS for Macへの導入法をまとめる。
（基本的にVisual Studio for Macは記事が少ない…）

## 使用環境
macOS Big Sur ： Version 11.2.3
Visual Studio for Mac 2019： Version 8.9.4 (build 25)

## 導入する
非常に簡単。
[StyleCopAnalyzersのGitHub](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/StyleCop.Analyzers/Directory.Build.props)からDirectory.Build.propsをダウンロードし、これを静的解析を適用させたいhoge.csproj、fuga.slnと同じ階層に配置するだけ。

Directory.Build.propsは、複数のプロジェクトに共通の設定を一括反映するために使うらしい。自分は次のQiitaを参考にした。
[C#の全プロジェクトに便利な設定を一括反映する
](https://qiita.com/kojimadev/items/45b4c8e002c226cc1c36)

## 詳細を設定する
StyleCopAnalyzersは英語圏での使用を想定されているからなのか、デフォルトでは「コメントの文末にピリオドをつけろ〜」など日本で使う上では不必要な指摘まで入る。快適に使うにはルールごとに警告かエラーか、適用外かなどを自由にカスタマイズする必要がある。
具体的にどんなルールがあるのかは、[StyleCopAnalyzersのGitHubDoc](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/tree/master/documentation)から確認してほしい。

カスタマイズしたいルールを決めたらVS for Macを開き、左上のVSメニューから
ユーザー設定 > ソースの解析 > C# > コード規則
ここに次のようにカスタマイズしたいもののみルールを書いていく（記載のないものはデフォルトでは警告が適用される）
コピペ用に下に自分が試しに用いたものを記載するが、使用するときは適宜カスタマイズしてほしい。

```
<RuleSet Name="Global Rules" ToolsVersion="12.0">
    <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
      <Rule Id="SA1600" Action="Error" />
      <Rule Id="UNT0001" Action="Error" /><!-- 不要なUpdate文の削除 -->  
      <Rule Id="UNT0002" Action="Error" /><!-- tagの比較制約 --> 
    </Rules>
</RuleSet>
```

![スクリーンショット 2021-04-05 23.31.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/34f86498-73f8-5cb4-2ce7-4b458f3a62a9.png)

上の例だとSA1600をエラーとして表示する設定にしたので、コード上では次のような指摘が入る（サマリーを書いてねという指摘）
![スクリーンショット 2021-04-05 23.42.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/712635/05d020b0-e797-30c0-9de9-ab06407b93fc.png)

ちなみにしれっと書いたが、このコード規則のところには
IDEやCS、UNTから始まるコード規則も追加できる。これらも規則コードで色々検索して好みのものを追加してほしい。

## 最後に
GitHub Actionsや、Unityエディタから静的解析の結果を確認するプラグインなど色々調べたが結局どれが一番使い心地がいいのかわからん…
Unityによるチーム開発でコードを綺麗に書くために導入しているおすすめの方法があればぜひ教えてください

ここまで読んでいただきありがとうございました。
この記事が何かお役に立てれば幸いです。
0 からアプリをリリースしてみよう！〜第一話〜

#0からアプリをリリースしてみよう！
###はじめに
みなさんこんにちは。paulownia friendsです。みなさんは、iosアプリを作りて〜けどどうやってやればいいのか分からね〜とか、始めたはいいもののつまづくポイントが多すぎて挫折しそう〜といった経験をしたことはないでしょうか？私たちも、ついこの間まで、同じような状態でした。そこで、同じような悩みを抱える方のために、2本のiosアプリをリリースした経験を元にして、「決断メーカー」の制作過程をベースにiosアプリの計画からリリース、広告の貼り付けまでの一連の流れを大公開したいと思います！この記事が何かの役に立てば幸いです。

###1.企画
アプリを作ろう！となっても、何も計画を持たないで始めてしまうと、当然初手でつまづいてしまいます。第1話でこけたシンジくんのエヴァみたいですね。なので、どのようなアイデアをどのようなアプリに落とし込み、どのようにして収益化を図ろうとするのか、どのような機能を持たせるのか、どのようなデザインにするのかなどの検討をじっくり重ねた上で作り始めます。
今回、私たちは検討の末、自分に変わって決断を下してくれるアプリが欲しい、と思ったので、そのようなアプリを作ることにしました。

以下企画案↓
・コンセプト：決断を下してくれるアプリ
・収益化：admobの広告(インタースティシャル広告)
・機能：決断を下してくれる、選択肢の数を増減させることができる
・デザイン：とてもシンプルなものにする
・予定作成期間：一週間

###2.作成の準備
さて、いざ作るぞーとなったとしても、環境が整っていないと先には進めません。ここでは、開発の環境を整えていきます。
まず、iosアプリを作る際は、基本的にXcodeというmac用のアプリを使います。（app storeからダウンロードできます）環境としては、Xcodeがインストールされたmacと、やる気があれば大丈夫です。わざわざタイトルをつけたのに、これだけかよ、と思う方もいるかもしれません。でも実際これだけなのです。この手軽さも魅力の一つではないでしょうか。

###3.作成開始
それでは、いよいよ、作成を始めていきましょう。まず、Xcodeを開くと、このような画面が出てきます。ここで、「Create a new Xcode project」を押してください。
![スクリーンショット 2021-04-05 23.08.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1128140/9ea62fac-19a1-643a-aad1-3f72bf35d124.png)

すると、このような画面になります。様々なアプリの形式がありますが、ここでは「App」を選択します。

![スクリーンショット 2021-04-05 23.09.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1128140/d47739b8-fa82-6c03-7d9e-77943912c066.png)

すると、このような画面が出てきます。ここで、product Nameを入力してください。写真では日本語で入力していますが、エラー回避のために、アルファベットで打つようにしましょう。Identifireは、ダブりが生じないようなものにしてください。ダブらなければ何でも良いですが、エンジニアの方々の間では、メアドをひっくり返して入力するのが主流だそうです。つまり、paulowniaFriends@mail.comというメアドを持っていたとしたら、Identifireはcom.mail@paulowniaFriendsにする、といった感じです。
![スクリーンショット 2021-04-05 23.09.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1128140/0cdce734-1eda-eb72-1c7b-e7ae2a43402d.png)

すると、次にこのような画面になります。ここでは、作成したプロジェクトの保管場所を定めることができます。場所を決めたら、「Create」ボタンを押します。

![スクリーンショット 2021-04-05 23.09.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1128140/d583dda5-8558-e65a-9b08-4a91337e2508.png)

そうしたら、このような画面が出てきます。このような画面が出てきたら、いよいよコーディング開始です！がんばっていきましょう！！
![スクリーンショット 2021-04-05 23.09.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1128140/e84e057c-1b44-a1f9-7aad-7340d60de0b5.png)

ここから先は、少し長い作業になるので、第一話はこの辺りで締めたいと思います。このくらいはまだ簡単すぎる、もっと最後の部分を知りたい、という方も多いとは思いますが、続編が出るまで待っていただけると幸いです。近日中にはアップロードします。

##次回予告
コーディングをスタートしたものの、あちらこちらからエラーが出てしまい、どこをどうしたら良いのかも分からない、バグへの対処方法が分からない、などといった悩みを抱える方も多いと思います。そこで、次回は、バグに対処していく方法をお伝えする回にしたいと思います。ただし、ここで紹介するのは、個別具体的なエラーへの対処ではなく、バグ対処の一般的なコツのようなものになります。それでは、次回記事にて！

##自分たちのアプリの宣伝
私たちは、自作のリマインダーアプリ「タスクリマインダー -TaskReminder 課題管理-」、決断代行アプリ「決断メーカー」をAppStoreにて公開しています。

~ダウンロードはこちらから~
 [タスクリマインダー -TaskReminder 課題管理-](https://apple.co/3drJswx "タスクリマインダー -TaskReminder 課題管理- App Store")
 [決断メーカー](https://apple.co/2R38PgL "決断メーカー App Store")
[自分用メモ]  Portfolio 作成方法


###1.何を作りたいかを決める（テーマ設定）　
まず何を作りたいかを明確にしましょう。
オリジナルアプリを作ると言っても
・自分は何を作りたいのか？
・課題を発見して、どう解決するかを明確化する　などなど

アイデアを紙やメモに書いてみましょう。

参考(筆者)
僕は、glass house というポートフォリオを作成しましたが

◎課題
・コロナ禍でオフラインでの交流ができなくなった。ビジネスマンなど交流する機会はないか？
◎解決方法
・zoomを使ってオンライン交流。自分を紹介して「話してみたい人」とつながる機会をを作る。
・異業種の人とつながることができるのでビジネス・アイデアが広がる可能性がある。

を考えて作成しました。

なぜ、このサービスをつくろうと考えたのか
コロナ禍で家にいることが多くなったので、人と会う機会が少なくなりました。
テキストでの交流もいいですが、実際に映像で話し合った方が情報量が増えるのではないかと考えました。
オンラインで繋がりを増やせないかなと考えていたときにこのサービスを作ろうと考えました。
異業種の方やビジネスマン、起業家などの方と簡単にトークできるプラットフォームを作りたいと考えました。

GithubのRaedMeに自分の考えをまとめてみよう
GithubにはReadmeに
・アプリの概要
・課題
・解決法
を書いてみましょう。
書くことによって自分が作っているサービスを可視化できるのがいいですね。

###2 レイアウト作成
次はレイアウト作成です。どんな画面が必要か書いてみましょう！　例えば
・ログイン画面
・ユーザー一覧画面
・ユーザー詳細画面
など、必要な画面を書き出します。

ここで大事なのが、　
UI/UXを考えて設計する
がポイントです。

「ユーザーが使いやすい機能」
「このボタンを押したらこの画面に遷移する」
「ボタンを押したらフラッシュメーセージが出る」

など

ポートフォリオに必要な機能を洗い出して画面を設計してみましょう。
イメージはざっくりとした感じで大丈夫です。

作り方ポイント
まずはメイン機能や必須機能のレイアウトを作成する
レイアウトで共通化するところを洗い出す
ボタンの配置(どこに画面遷移するか決めておく)
どこを共通化して使うかレイアウトを組み立てるか。
後でここの機能を追加するときに崩れると困るから大枠を先に作っておこうなど。
を意識して作ると良いです！

レイアウト作成方法
レイアウトは紙に書くのもいいですが
僕は Adobe XDを使って画面を描きました。

チュートリアルも整っているのでざっと学んだらすぐに使えます！

###3 テーブル設計
次にテーブル設計です。
「Readme」「画面設計」で作ったのを元にして、テーブル設計をしていきます。

・どんなテーブルが必要か？　カラムが必要か？
・リレーションの設計
を考えて設計します。

不要なカラムなどは追加しないようにしましょう

またリレーションは混乱します。

こうすることで、テーブル設計をきちんとすることによってあとで混乱しなくて済みます。


###4 issueとTrello
ここまで終えたら、Githubでissue管理をしましょう。必要な機能をタグ付けで管理しましょう。
また開発途中で機能の不具合などがでてきます。そういうときにもどこでエラー・不具合が起きているかをissueで管理すると問題点が浮き彫りになるのでおすすめします。

特に最初はあまり気づかないと思いますが,
機能をたくさん追加していくと思った通りに動かないことや、不正アクセスができてしまうなど
いろんな不具合があります。
issueは現場でも使われているので、是非issueを使って開発をしましょう。

あと、個人的になんですが、私はタスク管理アプリ「Trello」を使って開発しました！
「やるべきこと」「作業中」「作業完了」などにタスクを分けて管理しました。
実務になると「期日」というものがあり、日付期限などもう少し細かくやると思いますが、私がTrelloを使っている理由は
#####「今、やるべきタスクは何か？」
を意識して開発しています。またモチベーションアップにも繋がります。

開発をしていて、途中で、「検索機能」「メール通知設定」「セキリュティ対策」など追加したい機能がどんどん挙がっていきます。タスク管理をしていないと
「何の開発をしているのか」
「優先順位が分からない」
「どの機能追加を完了したのか」
を確認できなくなるので、
駆け出しエンジニア方はこの機会にタスク管理を取り入れてみましょう。
もちろん、「Trello」以外にもタスク管理アプリはあるので自分の使いやすいタスク管理アプリを使いましょう！

###5 開発へ
1~4を作成して、いざ開発に入ります。分からなくなったらまた1~4に戻って確認できるので
スムーズに開発ができると思います。

プルリクエストを使って開発
開発はgitを使っての開発が大半となりますが、git開発でも
「Git flow」
「Github flow」
があります。私はGithub flowを使ってmaster(main)からブランチを切って開発をします。
機能追加するときはfeature/○○でブランチを作成して開発をします。

実務ではプルリクエストやレビューがあるので、疑似的ですが実務に近い開発をするとgoodです！！

###6 さいごに
僕はテーマ設定、レイアウト、テーブル設計などにあまり時間をかけずに開発をしてしまいました。
その結果
・テーマ設定は後になると簡単には変えられない
・レイアウトのずれ(後々、レイアウトの修正工数に時間がかかった)
・テーブル設計の大幅な修正、追加

で、修正に時間がかかってしまいました。
これからポートフォリオを作成する方は、

デザイン
画面レイアウト
テーマ設定
テーブル設計
をしっかり固めておいたほうが後々、大幅な修正をやらずに済むからです。

また、ざっくりと思いついたことは、メモしておいた方がいいです。
開発に専念していると忘れてしまうからです。
evernoteなど便利なメモツールがあるので、アイデアや機能一覧など書き留めていた方がいいです！
ポートフォリオ制作は自分が作りたいサービス・機能を作ればいいと思っています。
もちろん、プログラミングだけでなく
・マーケティング
・マネタイズ
などやるべくことはたくさんあると思いますが
「なぜこういうサービスを作ったのか」を
説明できるようにしとくといいと思います。
GAS WebAppのローカル開発時に google.script.runの補間が効くようにする

# 概要

Google Apps Scriptを以下のような構成で開発している際に、エディタの候補に「google.script.run」が出てこず困るケースがありました。よってこれを補完出来るパッケージを開発致しました

# 環境
- Google Apps Script
- Node.js
- clasp
- typescript

# 作ったもの
https://github.com/ymuichiro/gas-declare-script-run

# 利用時の前提

- Google Apps Scriptをローカルのエディタで開発する際を想定しています。
- かつTypescriptを導入しており、型補完が効く状態を想定しております。

# 使い方

1 - パッケージをインストール

```console
yarn add https://github.com/ymuichiro/gas-declare-script-run.git
```

2 - importする

```index.ts
import google from 'gas-declare-script-run';
```

3 - 利用する

```index.ts
google.script.run
  .withSuccessHandler(() => {})
  .withFailureHandler(() => {})
  .serverSideFunction();
```
Citrix Universal Print Server

少し前のWindows Serverでは、Print Spool ServiceのCluster化がサポートされており、
Clusterによりプリントサーバーの冗長化を採っているケースがいくつかあるようです。

Windows 2012R2以降よりCluster化をサポートしなくなってしまい、そのころ構築されていた
XenAppやXenDesktopを最新のOSやバージョンへ刷新する際、プリントサーバーをどうするのかが
問題になります。

Citrix Virtual Apps and Desktop(CVAD)の7.9以降のバージョンでは、プリンタサーバーの負荷分散、
冗長化をサポートしており、その機能が「Universal Print Server(UPS)」です。
構築方法もすごく簡単で、プリントサーバーへUPSのコンポーネントを追加し、ポリシーを設定すれば
即利用できます。

ユーザーへプリンタを割り当てる場合、CVADにある「セッションプリンター」や「プリンタの割り当て」
のポリシーを利用します。UPSを利用する場合にもこのポリシーでプリンターを割り当てます。

UPSの利用方法に関して、検索してみても「ポリシーを割り当ててプリンターをUNCパスで指定してね」と書かれているだけであまり詳しい情報を探すことが出来ませんでした。

そこで、設定方法を少しまとめてみます。

【設定手順１】
１．プリンタサーバー用のOSを2台用意し、「役割と機能」より「プリントサーバー」の
　　役割を追加します。追加したら２台のサーバーそれぞれに同じプリンターを登録します。
　　登録するプリンターには、メーカーの純正ドライバーで登録します。

２．2台のプリントサーバーへCVADのメディアより「ユニバーサルプリントサーバー」を
　　インストールします。

設定は以上です。

【設定手順２」
１．CVADのポリシーより次のポリシーを設定します。
　　「ユニバーサルプリントサーバーの有効化」
　　「負荷分散のためのユニバーサルプリントサーバー」
　　「ユニバーサルプリントサーバーのサービス停止のしきい値」
　　しきい値はデフォルトで180秒が設定されていますので環境によって調整して
　　ください。

２．「プリンターの割り当て」や「セッションプリンター」のポリシーでプリンターを
　　割り当てます。

ここで気になるのが、これらポリシーでプリンターを設定するにはUNCパスで設定するのですが、
「何を設定するの？」「どうやって2台のサーバのプリンターを登録するの？」です。
まず最初に思いつくのが2台のプリンタサーバーに登録されているプリンターをそれぞれ登録
する方法です。2台のプリンタサーバーに登録されているプリンターをそれぞれ登録してしまうと
想定の2倍のプリンターがセッション内で割り当てられてしまいます。
正しい設定内容は「\\どちらかのプリンタサーバー\プリンター名」です。

「1台のサーバー分しか登録していないけど大丈夫なの？」と思いますが、この設定でOKです。
複数人のユーザーでログオンすると、各セッションごとに割り当てられているプリンタサーバーが
異なっています。自動で負荷分散されているようです。
次に、片方のプリンタサーバーを停止するとどうなるでしょうか。
この場合も正常に動作しているプリンタサーバーへ接続先が変更されます。
切り替わるタイミングをレジストリでも調整できますのでCTX281978として公開されている技術情報を
確認してください。

負荷分散についてですが、パフォーマンスモニターを利用し負荷を確認しているようです。
ちなみに、この機能を利用するとユニバーサルプリンタドライバーを利用するため、
プリントサーバー以外へプリンタドライバーをインストールする必要がなくなります。

VDI環境もオンプレではCitrix、VMware、Microsoft、クラウド環境でもこの3社が導入候補に
上がることが多く、プリントサーバーの冗長化に関してHorizonではThinPrintの上位ライセンスの
追加購入が必要になり、Microsoftでは・・・。
その点Citrixでは、標準でプリントサーバーの冗長化が考慮された製品構成になっています。
【Laravel】ちゃんとフォームリクエストでバリデーションしてね

*この記事は初心者エンジニア(実務経験あり)が書いている記事です。

こんにちは @bas_chan(qiita) です。

今年度は、たくさんアウトプット出そうと思ってqiitaを始めてみました！！
つよつよな人が周りにそんないなくて試行錯誤でやってます:sunny:

## 結論 :ocean:
バリデーションしてないWebサービスは危ない！

今はCMSを主に開発していますがもちろんテストはしてないしバリデーションすらしてないっていう悲しい現状だったので、自分が担当するところから積極的に書いていこうということでチョロチョロっと学んでみました！

## バリデーションのすすめ :earth_asia:
まずバリデーションはアプリケーションのフォームからデータベースに入れる処理の際に入力され送られてきたデータをチェックして、そのデータが本当にデータベースに入れていいのか判断することです！
これがないと本来メールアドレスが入って欲しいカラムに"間抜けなアプリ"のような文字列が簡単に入ってしまうためやるべき処理なのです！

ではlaravelではどうやってやるの！という話ですね。

### validateメソッドを使う！
validateメソッドは連想配列にしたルールを引数に入れるだけで簡単にバリデーションしてくれます。

```php:ArticleController.php
/**
 * 新記事データの保存
 *
 * @param  Request  $request
 * @return Response
 */
public function store(Request $request)
{
    $validatedData = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);
}
```

今回の場合は$requestで受け取ったデータのtitle, bodyが空じゃないかつtitleが255文字以下でデータベースにそのtitleが存在しないことが確認できたらそのまま通ってくれるのです！


> unique:posts

これだけで既存のデータまでも見れるという超便利で優しいlaravelって最高ですね。

### フォームリクエストバリデーションを使う

これが今回のタイトルにもあるイチオシのばばばバリデーションです！

このやり方だとファイルで分かれているので使い回しもできるしコントローラが簡潔に済むのでおすすめです:raised_hand:

まず以下のコマンドを叩きます。

```
php artisan make:request StoreArticlePost
```

このコマンドを叩くとHttp/Requestディレクトリ配下にStoreArticlePost.phpというファイルが完成します！
それがこちら↓


```php:Http/Request/StoreArticlePost.php
/**
 * ユーザーがこのリクエストの権限を持っているかを判断する
 *
 * @return bool
 */
public function authorize()　＊１
{
    // 認証系が必要な場合はfalseにしましょう
    return true;
}

/**
 * リクエストに適用するバリデーションルールを取得
 *
 * @return array
 */
public function rules() *２
{
    return [
        // titleが存在する、データベースにそのtitleが存在しない、255文字以下
        'title' => 'required|unique:posts|max:255',
        // bodyが存在する
        'body' => 'required',
    ];
}
```


```php:ArticleController.php
/**
 * 新記事データの保存
 *
 * @param  StoreArticlePost $request
 * @return Response
 */
public function store(StoreArticlePost $request)
{
   // validateメソッドを書かなくても↑だけでok
   return $response;
}
```

ここでは*１に認証についてのロジックがかけます。ただ他のところに書いていたり、そもそも認証する部分ではない場合が多かったりするので基本trueにしておきます。例えば特定のuserしか入力できないデータなのであればパスワードチェックするロジックなどをかけたりします。もしその認証に通っていない場合(falseを返す)は自動的に403を返してくれます！

*２では上のvalidationメソッドと同じく既存のルールを用いて各リクエストの中身をチェックして問題なかったら通り、ダメだったら何がダメだったのかを教えてくれるのです。

といった感じでバリデーションがなくても動くアプリケーションはできるんです。
けどやっぱり安全かつどんなデータを入れる場所なのかをバリデーションでわかっちゃうって結構初心者にも優しいと思うんですよね。

ぜひ使ってみてください！
カードを集めるプログラム

あなたは友達に勧められて、とあるカードゲームを始めることにしました。
そのカードゲームには、カード番号 1 から M までの M 種類のカードがあります。
あなたは完璧主義者なので、どうしても M 種類全てのカードが欲しくなり、カードを N 枚買いました。

さあ、買ってきた N 枚のカードを順番に開けていきましょう!

開けたカードのカード番号が順番に与えられるので、M 種類そろったのは何枚開けたときかを出力するプログラムを書いて下さい。

以下は入力例 1 を図示したものです。

![スクリーンショット 2021-04-05 23.16.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918117/a9a4e327-9440-dec7-7b32-2a46766e7ef1.png)

##解き方のポイント

ポイントは「カードの種類が揃った」という条件をどのようにして表現するか。
今回は配列にあらかじめ、総種類の枚数を入れておき、cardsとcardの番号を照らし合わせて同じ番号があったら配列からデリートしていく。
if文で配列の中身が無くなった時にブロック変数の「i + 1」で数を出力し、exitで繰り返し処理をストップさせる。
また、「unlucky」の出力は、真偽値を用いて行う。

##コード例

```
N, M = gets.chomp.split(' ').map(&:to_i)
cards = Array.new
flag = false

M.times do |i|
    cards << (i + 1)
end

N.times do |i|
    card = gets.to_i
    cards.delete(card)
        if cards == []
            puts i + 1
            flag = true
            exit
        end
end 

puts 'unlucky' if flag == false
```
複数の QuickSpec での beforeSuite と it の実行順序

担当しているプロジェクトのUIテストコードに Quick/Nimble（テスト用フレームワーク） を導入しようとして若干ハマったのでメモします。

# 何に詰まったのか

最初、Spec1とSpec2のように複数のQuickSpecを用意して、それぞれ初期化処理をしてから実際のテストを走らせようとしていました。

- Spec1
  - beforeSuite1 - **(A)**
  - it1 - **(B)**
- Spec2
  - beforeSuite2 - **(C)**
  - it2 - **(D)**
  
構造としては上記にようにして、私は以下の実行順序を期待します。

1. Spec1について、beforeSuite -> it
2. Spec2について、beforeSuite -> it

しかし実際にテストを実行してみると、Spec2のbeforeSuiteの処理が効いているように見えません。。

なぜ...？

# 簡単なプロジェクトを作ってチェック

そこで、簡単なサンプルプロジェクトを作ってログを仕込み、実行順序を見てみることにしました。

私が用意したプロジェクトはこちらです。

https://github.com/branch10480/QuickSample

こんな感じに Spec1 と Spec2 を用意して実行します。

```swift:Spec1.swift

class Spec1: QuickSpec{
    
    override func spec() {
        beforeSuite {
            print("===============================")
            print("> This is a beforeSuite on Spec1.")
            print("===============================")
            
            let app = XCUIApplication()
            app.launch()
            
        }
        describe("description") {
            context("context") {
                it("it1") {
                    print("-------------------------------")
                    print("> This is a testing on Spec1.")
                    print("-------------------------------")
                    sleep(5)
                    expect(true).to(beTrue())
                }
            }
        }
    }
   
}
```

```swift:Spec2.swift
class Spec2: QuickSpec{
    
    override func spec() {
        beforeSuite {
            print("===============================")
            print("> This is a beforeSuite on Spec2.")
            print("===============================")
            
            let app = XCUIApplication()
            app.launch()
            
        }
        describe("description") {
            context("context") {
                it("it2") {
                    print("-------------------------------")
                    print("> This is a testing on Spec2.")
                    print("-------------------------------")
                    sleep(5)
                    expect(true).to(beTrue())
                }
            }
        }
    }
    
}
```

実行してみた結果、このようにログ出力されました。
（間に入ったログは省略）

```
===============================
> This is a beforeSuite on Spec1.
===============================
...
===============================
> This is a beforeSuite on Spec2.
===============================
...
-------------------------------
> This is a testing on Spec1.
-------------------------------
...
-------------------------------
> This is a testing on Spec2.
-------------------------------
```

# 結論

- Spec1
    - beforeSuite1 - **(A)**
    - it1 - **(B)**
- Spec2
    - beforeSuite2 - **(C)**
    - it2 - **(D)**

上記の場合、実行順序はこのようになるということでした。

```
(A) -> (C) -> (B) -> (D)
```

UIテストで beforeSuite 上にアプリの起動処理を書いて初期化しようとしていましたが、起動を2回してからテストを実行していたということですね。

結局、UIテストでは beforeSuite に起動処理を書かずに it1, it2 内に書くことで対処しました。
正規表現備忘録

<b>受け取った値が電話番号の形式かどうかを判定するだけの正規表現

以下の電話番号の形式であれば、trueを返す。
（USの電話番号に倣ったもの。またcountry codeを使う場合、行頭に1を付与する。）

>555-555-5555
(555)555-5555
(555) 555-5555
555 555 5555
5555555555
1 555 555 5555



```test.js:

function telephoneCheck(str) {
  var reg = /^(1\s?)?(\(\d{3}\)|\d{3})[\s\-]?\d{3}[\s\-]?\d{4}$/;
  return reg.test(str);
}

```

^ ...文字列の始まりを表す。
\s ...空文字を表す。
? ...直前の1文字またはグループは省略可、を表す。
<b>(1\s?)?</b> ..."1 " または "1"が1つ以上あるかどうかの判定

\d ...数字1文字を表す。
{n}... 直前の文字のn回の繰り返し
\ ...つぎに来る文字または記号を正規表現ではなく、単なる文字として扱いたい場合に使う。
| ...orを表す。
<b>(\(\d{3}\\)|\d{3})</b>... (\d{3}) または d{3}を判定。 

[ ] ...[ ]の中のどれか1文字を対象にする
<b>[\s\\-]?</b> ...空文字 または - の探索

$... 行末

---
以上。