OmniAICreator/Qiita-1.07M huggingface dataset:
サイトレイアウトを可視化するJavascript

WEBサイトの幅や高さを可視化するスクリプトです。

```Javascript
const maskOverlay = document.createElement('div');
maskOverlay.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 9998; pointer-events: none;';
document.body.appendChild(maskOverlay);

const layoutOverlay = document.createElement('div');
layoutOverlay.style.cssText = 'position: fixed; background-color: #fff; border: 1px solid #ccc; padding: 5px; z-index: 9999; pointer-events: none; display: none;';
document.body.appendChild(layoutOverlay);

document.addEventListener('mouseover', function(event) {
  const target = event.target;
  const targetRect = target.getBoundingClientRect();

  maskOverlay.style.display = 'block';
  layoutOverlay.style.display = 'block';
  
  layoutOverlay.innerHTML = `幅: ${targetRect.width}px, 高さ: ${targetRect.height}px`;
  layoutOverlay.style.top = `${targetRect.top}px`;
  layoutOverlay.style.left = `${targetRect.left}px`;
  layoutOverlay.style.width = `${targetRect.width}px`;
  layoutOverlay.style.height = `${targetRect.height}px`;
});

document.addEventListener('mouseout', function() {
  maskOverlay.style.display = 'none';
  layoutOverlay.style.display = 'none';
});

const backButton = document.createElement('button');
backButton.textContent = '戻る';
backButton.style.cssText = 'position: fixed; top: 10px; left: 10px; z-index: 9999;';
backButton.onclick = function() {
  maskOverlay.remove();
  layoutOverlay.remove();
  backButton.remove();
  document.removeEventListener('mouseover');
  document.removeEventListener('mouseout');
};
document.body.appendChild(backButton);
```

ブックマークレットは以下
```Javascript
javascript:(()=>{const e=document.createElement('div'),t=document.createElement('div');e.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0.5);z-index:9998;pointer-events:none;',t.style.cssText='position:fixed;background-color:#fff;border:1px solid #ccc;padding:5px;z-index:9999;pointer-events:none;display:none;',document.body.appendChild(e),document.body.appendChild(t),document.addEventListener('mouseover',n=>{const o=n.target,r=o.getBoundingClientRect();e.style.display='block',t.style.display='block',t.innerHTML=`幅: ${r.width}px, 高さ: ${r.height}px`,t.style.top=`${r.top}px`,t.style.left=`${r.left}px`,t.style.width=`${r.width}px`,t.style.height=`${r.height}px`}),document.addEventListener('mouseout',()=>{e.style.display='none',t.style.display='none'}),b=document.createElement('button'),b.textContent='戻る',b.style.cssText='position:fixed;top:10px;left:10px;z-index:9999;',b.onclick=()=>{e.remove(),t.remove(),b.remove(),document.removeEventListener('mouseover'),document.removeEventListener('mouseout')},document.body.appendChild(b)})();
```
以上
AWSのLIGHTSAIL(WORDPRESS)をGITHUB ACTIONS連携する話（1）

個人のWORDPRESSブログサイトをAWS（LIGHTSAIL）にお引越しした際、GITHUB Actionと連携した。
忘れないように、備忘録として記事を残します。

* 記事の展開
1. なるべく安価でAWSサービスのブログサイトを展開したい
2. GITHUBと連携して、main にpushする, pullされるだけでローンチさせたい
3. 無料でHTTPSにしたい

LightSailのインスタンスでWordPressのインスタンスを起動させるところまで、記載します。

## LightSailのインスタンスを起動する

### 1.AWSのLightSailのインスタンスを起動させる
    * AWSにログインして、Amazon Lightsail のダッシュボードに移動します。
### 2.「インスタンスの作成」をクリックする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/690cdfb1-5f6f-5e3c-eab0-e0ddb3ced5d1.png)

### 3.設計図の選択でWordpressプリインストールを選択する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/77ecb24b-330f-7a51-db88-42573977e1ac.png)

### 4.SSHキーペアの変更

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/45d8a9db-58fa-ce8e-36d8-be24a221354b.png)

このキーペアがGITHUBアクションと連携させる時、大事になってきます。
GITHUB actions のDEPLOYキーに使用されるのが、このキーペアです。
プライベートキーをダウンロードしてPCに保存してください。
またパブリックキーが インスタンスキーの　`.ssh/authorized_keys` にデフォルトで設定されます。

他のリポジトリのDEPLOYKEYまたはGITHUBのSSHキーで使用しているキーペアは使わないでください。GITHUBのDEPLOY_KEYに `Key is already in use` となり、登録できません。不安ならこのタイミングでキーペアを作ってしまいましょう。

> ※ 余談ですが、プライベートキーを秘密鍵って呼ぶのやめてほしい。プライベートと秘密じゃ意味が全然ちがう。

### 5.インスタンスプランの選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/f98e971b-af4c-f21c-8629-15c0f6909ecf.png)

安価目的なので一番安いプランを選択します。

### 6.インスタンスの作成ボタンをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/9be5fe04-92db-59f4-1166-1900edf4864b.png)

### 7.インスタンスが起動されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/698c4d9d-dc19-1f59-10c7-c7845006890d.png)

### 8.WORPRESSの管理画面にログインします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/22b8dbb6-a3ca-a873-7e98-85be20a97478.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/fea2fbd9-7140-6d4f-cdf6-b1620157257a.png)

Wordpressの初期ユーザーは `user` です。
パスワードは「デフォルトのパスワードを取得する」をクリックして、指示通りにしたら取得できます。
クラウドシェルを起動してコマンドをコピーするだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/51970db7-9803-b27e-2e1e-bce694912bd0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/47da716c-c22c-1e28-3b37-c78173e51096.png)

赤く囲んだ箇所に初期パスワードが生成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/0d756403-3156-b7aa-221d-10293f160a9d.png)

### 9.ログイン
ログインできました

本記事はここまでです。
つづいて、GITHUB Actionsとの連携方法を
[AWSのLIGHTSAIL(WORDPRESS)をGITHUB ACTIONS連携する話（2）](https://qiita.com/weeeveeee/items/af46fb3f05a1d823436a
)にて記述します。


## 関連記事
> [AWSのLIGHTSAIL(WORDPRESS)をGITHUB ACTIONS連携する話（1）](https://qiita.com/weeeveeee/items/7596b777d463c9ac76ba)
[AWSのLIGHTSAIL(WORDPRESS)をGITHUB ACTIONS連携する話（2）](https://qiita.com/weeeveeee/items/af46fb3f05a1d823436a)
[AWSのLIGHTSAIL(WORDPRESS)をGITHUB ACTIONS連携する話（3）](https://qiita.com/weeeveeee/items/111e2353a5b08e992d71)
xpathの基本と上手に指定するコツのまとめ


## はじめに
xpathの基本と上手に指定するコツのまとめに関しての**備忘録**です。
初心者です😅
間違えてる部分が多々あると思います。
もし見つけた場合、ツッコミいただけると助かります🙇
<br>
## 🦁<INS>**結論**</INS>🦁
### find_elements_by_xpath(xpath)
### 基本構文を使って条件を適切に行うことで修正を減らすことができる。

:::note info
押さえておくべき点
* xpathはHTML文書、XMLなどに要素、属性などにアクセスするための言語
* pathを指定する際に「/」が1つ→ルートをしての絶対パスになり、「/」が2つだと文書内の任意のpathになる。
* 「.」が一つだと現在の要素、「.」が2つだと親要素を示す。
* @を利用して属性を選択（@idなど）
* 「*」によりすべてのものを選択できる（@*これは全ての属性）
* 条件選択する場合は[]を使う。
:::  
## 基本構文
### 基本は条件式で行う。
```python:sample.py
elements = driver.find_elements_by_xpath("//div[@class='myClass']")
```
***  

### 条件選択を2つにする
```python:sample.py
elements = driver.find_elements_by_xpath("//div[@class='myClass' and @id='myId']")
```
***  

### 条件を子孫要素で絞り込み
```python:sample.py
elements = driver.find_elements_by_xpath("//div[@class='myClass']//p[@id='myId']")
```
***  

### どちらかの条件に当てはまる場合
```python:sample.py
elements = driver.find_elements_by_xpath("//div[@class='myClass' or @id='myId']")
```
***

### どちらかの条件かつ複数条件ある場合
```python:sample.py
elements = driver.find_elements_by_xpath("//div[@class='myClass' or @id='myId']")
```
※子要素の条件を追加してさらに絞り込めるようにすることも可能。
※「or」もいくつも追加することも可能。
***


:::note alert
注意点
* 「element」と「elements」の違い。
:::  
##  「element」と「elements」の違い
### 「element」  find_element_by_xpath(xpath)
* 単一の要素を返す（複数あったとしても一番最初の要素のみ）
* 要素が見つからない場合にNoSuchElementExceptionエラーを投げる（selenium）
***

### 「elements」  find_elements_by_xpath(xpath)
* 要素のリストを返す
* 要素が見つからない場合には空のリストを返す
***

:::note warn
xpathを上手に使うためのコツ
* 属性よりも「ID」「class」を優先する
* 複数の条件により絞り込む（子要素）
:::  
***


## まとめ😺
xpathの指定の仕方で、修正を減らすことができるなと感じた。xpathの指定の上手さ＝コードを理解してるということなんだなと感じた。
***
凡人リーダーの失敗録

この記事を読んでくださっている方の中に、リーダーシップに自信がある人ってどのくらいいますか？
実際、リーダーシップをとって行動できる人って多くはないなと思います。

自分は一応何度か学生、社会人としてリーダーを経験したのですが正直めぼしい実績もなくてあまり自信をもってリーダーをやったぞ！とはいえないです。ですがリーダーをやってよかったとは思っています。

この記事では自分のリーダー経験を振り返りつつ、自分なりにリーダーのうまみとつらみについて書き連ねていければと思っています。凡人なりのリーダーシップ録です。リーダーに自分向いているのかな？とかリーダーやってみたいけど迷うな、という人の背中を押せたらと思い書いてみました。
最後まで読んでいただけたら嬉しいです。

## 学生時代
学生時代は体育会の陸上部に所属していたのですが、100人以上の部員の部活の副部長をしていました。
所詮は学生の部活といえばそうなんですが、今につながる経験をたくさんさせていただいたな〜と思っています。
具体的に言えば、この辺ですね。

- 組織の中の対立の仲裁をする経験
- 組織がちょうど良くモチベートされる目標を設定し、その達成に向けてチームを鼓舞する難しさ
- 組織の目的と個人の目的のバランスの取り方

特に一つ目と3つめは学生の部活ならではの経験だったんじゃないかと思っています。
体育会とはいえ、学生なので学業のこともあったり遊びたい！って人もいるし将来のことでも悩む時期です。そういう時期にあたる人々を組織としてどう纏めていくかを考えるのはとても難しかったように思います。

このおかげもあって、「人は組織の都合だけでは動けない」とか「自分が組織の中で成し遂げたいことをみんなも同じように成し遂げたいと思っているわけではない」とか、会社員として組織を作る上で自分が大事にしているものの基礎となる価値観を得たように思います。

## 社会人3年目ではじめてのリーダーになった
社会人3年目、2社目で、エンジニアとしては1年目の終わり頃にリーダーのチャンスが巡ってきました。
具体的には3~4人ほどのプロジェクト二つのリーダーと、そこに関わる部下の育成(=組織図上の上司)です。

人生で初めて自分が「ジョウシ」と呼ばれる存在になって混乱した記憶があります。
この人にとっては上司は他の誰でもない自分なんだ、この人の給料が自分の取り組み次第で変わる可能性があるんだと思うと、どこか自分ごとじゃない感じがしてソワソワしていたように思います。

上司として自分が大事にしていたのは、「部下の特性にあった働き方を一緒に考える」というスタンスでした。
当時の部下はみんな未経験からエンジニアになって1年もたたない人たちで、まだまだ将来のキャリアを絞るには早いと思ったからです。
日々の業務にがむしゃらに取り組んでいただきつつ、定期的に業務を振り返りながら自分の適性や将来目指したいものについて一緒に考えるような接し方をしていました。
これは本当に部下のおかげだと思っているのですが、当時の部下から一人社内表彰を受ける人がいました。他の二人も現在は組織のリーダーとして頑張っています。自分が携わっている人の成長は嬉しいですね。

一方のプロジェクトリーダーはというと、正直あまりうまくいきませんでした。
具体的にはお客さんの期待値の把握を誤ってしまい、プロジェクトメンバーのアサイン対してうまくオーダーを出すことができませんでした。

これってなんで失敗したんだっけな〜と思うと、メンバーに目を向けすぎていて組織全体のことやプロジェクト全体のことに目を向けられていなかったからなのかなと考えています。もっとプロジェクト全体におけるお客さんとの関係値や会社全体の中の自分の位置付けを意識してチームを運営せねばならないと感じた瞬間でした。

トータル3ヶ月で、はじめてのリーダー生活は幕を閉じます。

## 社会人5年目で2度目のリーダーに
社会人5年目、エンジニア3年目で2回目のプロジェクトリーダーになりました。
今回のリーダーでは、1度目の失敗を繰り返したくなかったので会社の上長やお客さんとのコミュニケーションを大事にし会社としてどういうアウトプットを期待されているのかを把握しながらチームを運営するようにしました。

結果としてプロジェクトは1年以上、現在に至るまで続いており前回よりもプロジェクト運営は上達したかな？と思っています。

ただ、お客さんや会社とのコミュニケーションを重視した一方でプロジェクトメンバーとのコミュニケーションはうまくいかないことがありました。プロジェクトメンバーとはリモートで仕事をしていたのですが、自分のコミュニケーション不足もありなかなか自分の気持ちをうまく伝えることができず、プレッシャーを感じさせすぎてしまいました。最大4人いたメンバーのうち2人がプロジェクトを離脱、うち1人は退職と正直メンタルにきました。

自分が傷つけられることに対してはある程度割り切れるのですが、人を傷つけてしまった経験はなかなか心にきました。緊張と緩和のバランスを考えながらプロジェクトメンバーが何を感じているのかをつかみ取れるようになる必要があるのだということを学びました。そして今までメンバーとして携わってくださった方が自分にいろいろ気を遣ってくださっていたことがとても痛感できました。

## そして今の話
そして、ぼちぼち社会人6年目になる私ですが今はプロジェクトのリーダーとして1人のメンバーのサポートをしつつ社内的にはチームの後輩4人の育成をやっています。組織図的な上長ではない、ちょっとどっちつかずなポジションです。プロジェクトメンバーもずっと一緒にやっている方なので、「リーダー」という感じのポジションからは少し離れてリラックスして仕事をしています。(決して手は抜いていないですが)

リーダーをやったり、リーダーを降りたり、プロジェクトメンバーが離れていったりいろんな経験をしていく中で自分はリーダーに向いているんだろうかということを何度か考えたことがあります。正直、めちゃくちゃ向いているのかというと自信はないです。

リーダーとしてうまくいかなくなるたびに上長の方にサポートいただいたのですが、リーダーとして成果を出している人と比べると自分はあまりメンタルが強くないなと思いました。どれだけプレイヤーとして成果を出していたとしても、自分に乗っかっている責任を引き受ける器の大きさはまた別問題で、ネガティブな状況でも他責にせず問題解決のために前向きに取り組んでいくリーダーの方をたくさん見てきました。正直すごいの一言でした。

ただ、そんな自分でもリーダーをやってよかったとは自信を持って言えます。
なんでかというと、リーダーをやっている方へのリスペクトを持つことができたからです。
リーダーをやるということの緊張感や、それを理解してくれる方が少ないことの孤独感、自分の僅かな経験でも実感することがありました。そういった感情に自分の心が折れたことがあるので、ちゃんと向き合っている人はすごいなと思います。
リーダーをやっている方へのリスペクトを持つことができると、会社への不満みたいなものはあまり感じず周囲への感謝を感じながら働くことができるようになるように思います(もちろん100か0かではなく程度の問題だと思うけど)。それが仕事のパフォーマンスにもポジティブに働くような気がします。

さて、とても個人の主観でしかないお話でしたが最後まで読んでくださった方はいるのでしょうか。
もしいらっしゃれば嬉しいです。ありがとうございました。
REST API

## はじめに
これから本格的にRailsの学習の方に入っていくのですが、
その前にどうやらREST APIという概念を知っておかなくてはいけないようです。
なので今回はそのREST APIについての学習を記事にします。

## REST APIとは
REST APIは一言で言うとRESTなAPIのことです。
RESTful APIとも呼ぶみたいですね。
これを理解するためにはRESTとAPIについて理解する必要があるので
以下それらについてまとめます。

### REST
RESTはRepresentational State Transfer（分散型システムにおける設計原則群）の略で
webシステムを構築する際の設計思想です。
RESTには設計における原則が以下の6つがあります。

1.クライアント/サーバー
→ネットワークに繋いで使うアプリはほぼ全てこの構成だと思います。
　コンピュータやスマホのようなクライアントにサーバーから
 　送信されたデータを表示し、サーバーはクライアントからこういうデータが欲しい
  　というリクエストを受けてデータを生成しレスポンスとしてそのデータを返します。
 
2.ステートレス
→状態を保存しないという意味です。会話に例えると前に話した内容を相手が
　覚えていないということを前提でお話が進みます。
 　相手に欲しいものを頼むときに最初「Aが欲しい。」と言った後に
  　やっぱりBも欲しくなったときにステートレスであると以前の会話を
   　覚えていないので「AとBが欲しい。」と言う必要があります。
    
3.キャッシュ制御
→クライアント側はサーバーからのレスポンスを明示的または暗黙的に
　キャッシュできます。適切にキャッシュを行うことで余計な通信が省けて
 　ユーザーの体験の向上、リソース効率の向上、拡張性の向上につながる。
  　ただ、デメリットとして古いデータを戻してしまうとデータの不整合が起きて
   　信頼性の低下につながるので注意が必要です。
　
4.統一インターフェース
→「リソースの識別」、「表現を用いたリソース操作」、「自己記述メッセージ」、
　「HATEOAS」の4つの制約から成り立っています。
　「リソースの識別」はURIでサーバーに保存されたリソースを識別することを
 　　意味しています。　ここで言うリソースとはサーバーに保存されている
　　名前がつけられているあらゆるデータのことです。
　 「表現を用いたリソース操作」はクライアントに返されるレスポンスやサーバーへ
 　　POSTするデータなどの断面情報を用いてサーバー上のデータを
   　　操作することです。
     　「自己記述メッセージ」はメッセージ内容がヘッダーに記述されていることです。
       　　自己記述とはデータ自身がデータの中身を説明していることで、
         　　メッセージとはクライアントとサーバーの間でやり取りされるデータを指します。
　「HATEOAS」は「Hypermedia as the Engine of Application State」の略で
 　　レスポンスの情報に現在の状態を踏まえたハイパーリンクを含めることです。
   　　webページにおける「次へ」ボタンなどがそれに当たります。
  

5.階層化システム
→各サーバーでAPサーバー、DBサーバーのように役割を分担させて
　進化と再利用をできるようにする構成です。しかし、一回の処理で様々な
 　サーバーとの通信が発生（オーバーヘッド）してレスポンスが遅れてしまいます。
   　これはキャッシュを利用すると改善が見込めます。
    
6.コードオンデマンド
→サーバーからソースコードをダウンロードしてきて
　クライアントでそれを実行することです。
 　これによりサーバーのソースコードのみを変更するだけで機能の拡張ができ、
  　後からの調整が容易になります。ただ、クライアントで実行するブラウザが
   　それぞれで異なるためテストが面倒になります。

### API
「Application Programming Interface」の略で外部のアプリケーションから
データや機能やデータを呼び出せるようにする規約です。
APIを利用することによってすでに世の中に公開されているAPIで
実装を効率よく進めることができます。

### 最後に
結論としてREST APIはRESTの原則に則って作られたAPIと言うことになります。
学習していて初耳のワードもたくさん出てきて最初は理解するのに苦労しました。
調べてみるとRESTというのはあくまで設計思想なので厳密にこうすべきみたい
なものってGAFAみたいな企業などがREST APIについてのガイドラインを作るまで
なかったようですね。まだ歴史が浅いITという世界ならではという感じがします。
Incus: Almalinux 9 を使う

## image を探す

```bash
incus image list images:alma | grep amd64
```

## コンテナーのインストール

```bash
incus launch images:almalinux/9/amd64 alma9
```

インストールされたことを確認

```text
$ incus list
+------------+---------+----------------------+------+-----------+-----------+
|    NAME    |  STATE  |         IPV4         | IPV6 |   TYPE    | SNAPSHOTS |
+------------+---------+----------------------+------+-----------+-----------+
| alma9      | RUNNING | 10.56.160.58 (eth0)  |      | CONTAINER | 0         |
+------------+---------+----------------------+------+-----------+-----------+
| alpine319  | RUNNING | 10.56.160.133 (eth0) |      | CONTAINER | 0         |
+------------+---------+----------------------+------+-----------+-----------+
| arch       | RUNNING | 10.56.160.101 (eth0) |      | CONTAINER | 0         |
+------------+---------+----------------------+------+-----------+-----------+
| fedora39   | RUNNING | 10.56.160.21 (eth0)  |      | CONTAINER | 0         |
+------------+---------+----------------------+------+-----------+-----------+
| ubuntu2204 | RUNNING | 10.56.160.209 (eth0) |      | CONTAINER | 0         |
+------------+---------+----------------------+------+-----------+-----------+
```

## root のパスワードを設定

```bash
incus exec alma9 bash
```

```text
$ incus exec alma9 bash
[root@alma9 ~]#
```

## login

```bash
incus console alma9
```

```text
$ incus console alma9
To detach from the console, press: <ctrl>+a q
login: timed out after 60 seconds
AlmaLinux 9.3 (Shamrock Pampas Cat)
Kernel 6.7.3-arch1-1 on an x86_64

alma9 login: 
```

## Login 後の設定

ユーザーの作成

```bash
useradd -u 1200 -m -g wheel uchida
groupadd -g 1200 uchida
passwd uchida
```

sudo のインストール

```bash
yum install sudo
```

sshd のインストール

```bash
yum -y install openssh-server
systemctl start sshd
```

## 作成したユーザーでログイン

```bash
ssh 10.56.160.58
```

パスワードなしで sudo が実行できるようにする

```bash
sudo visudo
```

```text
（省略）
%wheel ALL=(ALL)    NOPASSWD: ALL
（省略）
```

パッケージのアップデート

```bash
sudo dnf update
```

タイムゾーンの設定

```bash
sudo timedatectl set-timezone Asia/Tokyo
```

## ping を使えるようにする

```bash
sudo setcap 'cap_net_raw+p' /bin/ping
```

確認

```text
$ getcap /bin/ping
/bin/ping cap_net_raw=p
```

```text
$ ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=114 time=16.6 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=114 time=36.0 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=114 time=13.4 ms
```

## avahi を使えるようにする

```bash
sudo yum install avahi
```

```bash
sudo systemctl start avahi-daemon
```
Terraformの環境構築(Windows)

# 1.前提条件
・AWSアカウントが作成されていること
・AWS CLIがインストールされていること
・GitBashがインストールされていること
# 2.環境構築手順
## 2-1 tfenvのインストール
・<a href="https://github.com/tfutils/tfenv" target="blank">GitHub</a>でtfenvのリポジトリURLをコピーする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/4fa9a831-b2e4-07c0-4421-1fc9c670a43a.png)
</br>
・GitBashを起動。「<b>git clone URL</b>」を入力して実行する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/c5ac611a-57b8-9149-51e2-4fd07f5a5189.png)
</br>
・EnterでCloneを実行する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/490e099a-738e-6cfe-69ce-b81d9396f408.png)
</br>
・「<b>.bashrc</b>」ファイルを「C:\Users\#UserName#」フォルダに新規作成する
・テキストエディタで「<b>export PATH=$PATH:#tfenvのbinフォルダ#</b>」を入力して保存する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/f7b5d809-ef78-73fa-c112-2b55ed3062a8.png)
</br>
・GitBashを再起動。「<b>tfenv</b>」コマンドを実行し、Pathが通っていることを確認する
（<b>初回起動時のみ上部にWarningが表示される</b>）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/771f6f57-85ae-f2f8-adb7-c96e16ee1e57.png)
## 2-2 Terraformのインストール
・GitBash上で「<b>tfenv list-remote</b>」コマンドを実行する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/85708c68-4f46-4927-af0a-8a95635cc1cc.png)
</br>
・インストール可能なTerraformのversionが一覧表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/4cabc948-9f53-6bc8-789f-ae735116176c.png)
</br>
・「<b>tfenv install #version#(今回は1.7.2)</b>」コマンドを実行し、インストールする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/669d3b31-796e-586d-02db-087aa3597f88.png)
</br>
・「<b>tfenv use #version#</b>」コマンドを実行し、使用するversionに指定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/cdba17b7-890b-2eaf-fe49-b9a7a9051c17.png)
</br>
・「<b>terraform #version#</b>」コマンドを実行し、使用されるversionを確認する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/bd471d86-3040-0f80-389b-110cf462e3c2.png)
</br>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/19f62649-f90a-87bf-a746-d5fe3628331c.png)
## 2-3 HashiCorp Terraform(VS Code)のインストール(任意)

・VS Codeで開発する場合、コード補完してくれるプラグインが利用できる
・VS Codeのマーケットプレースから「<b>HashiCorp Terraform</b>」をインストールする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/b7342a53-9bee-76be-77ea-e1383f29d6b3.png)
</br>
・以下のように入力候補が表示されるようになる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690862/2ddd5c90-415d-1137-82fb-b004ccdb6d28.png)

以上
rex0220 計算式プラグイン 郵便番号〒マークの表示

計算式プラグインで郵便番号〒マークの表示を行ってみます。

# 概要

郵便番号〒マークの表示を kintone 標準機能のラベルと、計算式プラグインでスペース項目に表示する方法を試してみます。
編集時のズレを気にしなければ、ラベルでも十分対応できると思います。

- ラベル
    - 改行と文字サイズを調整するとそれらしく表示される
    - 〒マークと郵便番号の間隔が少し離れている
    - 詳細画面と編集画面では、編集エリアの高さが変わるため、ラベルがずれたように見える
- スペース項目に計算式プラグインで表示
    - 書式と文字サイズを調整するとそれらしく表示される
    - 詳細画面と編集画面では、編集エリアの高さにあわせて調整される

![2024-02-03_10h43_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/99f78e3b-d4b5-5898-ad2a-07b29e344f22.png)


# ラベル

ラベルに右寄せで、スペース改行、〒を設定します。
ラベルの文字サイズは、普通・やや大きいなどがあるが、細かな調整ができないので、開発ツールでfont-size を直接いじって保存すると、反映されます。
ラベルの位置は、前行スペースの font-size で調整します。

![2024-02-03_11h05_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/d58c0f3f-7778-1030-dbae-1c3b377ee360.png)

# スペース項目に計算式プラグインで表示
スペース項目に計算式プラグインで〒マークを表示する手順です。

## スペース項目の追加と要素IDの設定

![2024-02-03_11h14_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/91566c1c-d78d-d762-db02-8d0cc6490d61.png)

## 計算式プラグイン設定

スペース項目に計算式を設定します。

![2024-02-03_11h20_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/bd856b29-bc07-5303-e05b-366f88217ca5.png)

- スペース項目にラベルを表示する計算式
ラベルとラベルの書式、および値と値の書式をARRAY関数の配列で指定します。
    - ARRAY(ラベル,ラベルの書式,値,値の書式)

```.js
// sp1:(sp1) SPACER
"〒"

// sp2:(sp2) SPACER
ARRAY(
  "","",
  "〒",
  "width:8px;
   position: absolute;
   right: 0;
   background-color: initial;
   border-width: 0;"
)

// sp3:(sp3) SPACER
ARRAY(
  "","",
  "〒",
  "font-size:18px;width:10px;
   position: absolute;
   right: 0;
   top:26px;
   background-color: initial;
   border-width: 0;"
)
```

# モバイル表示の問題

モバイル表示時は、ラベル・スペース項目ともに〒マークが1行表示になるので邪魔になります。

![2024-02-03_11h25_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/c3514299-e039-3491-5f7c-fe6fa418cbac.png)

# モバイル表示時、スペース項目を非表示

計算式で、モバイル表示時、スペース項目を非表示にします。
少し行間が空きますが、少しはマシになります。

![2024-02-03_11h36_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/735f41f8-76d1-aec9-66a4-df83aa1390fa.png)

## 計算式の設定

EV_INFO("mobile") で、モバイル表示を判定し、スペース項目を非表示に設定します。

```.js
// sp1:(sp1) SPACER
IF(EV_INFO("mobile"),
  ARRAY("","display:none;","","display:none;"),
  "〒"
)

// sp2:(sp2) SPACER
IF(EV_INFO("mobile"),
ARRAY("","display:none;","","display:none;"),
ARRAY(
  "","",
  "〒",
  "width:8px;
   position: absolute;
   right: 0;
   background-color: initial;
   border-width: 0;"
))

// sp3:(sp3) SPACER
IF(EV_INFO("mobile"),
ARRAY("","display:none;","","display:none;"),
ARRAY(
  "","",
  "〒",
  "font-size:18px;width:10px;
   position: absolute;
   right: 0;
   top:26px;
   background-color: initial;
   border-width: 0;"
))
```
アダプティブカードのAction.SubmitボタンのDataをPowerAutomateで抽出

# 背景
TeamsチャットへPowerAutomateでアダプティブカードを送信してワンクリックで勤務状況報告できるようにしようとしてプチハマり。

![アダプティブカードイメージ図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/bcff4d96-196b-44ee-d679-4c0e79075ae1.png)
イメージ図

クリックされた各ボタンのDataをPowerAutomateで抽出する方法について。

# アダプティブカード側の設定
[Designer](https://adaptivecards.io/designer/)にて、まずは各ボタンの`Data`プロパティ値をセットするが、`{"Key":"Value"}`の形式で記載必要。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/4c4e00a3-59dc-ecbc-b455-b0db25a07073.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/f3c48c30-986a-4ffd-7b3d-977ce7cd98b5.png)
Keyは同じにしておいてValueをそれぞれのボタンで変更し、押されたボタンのValueでその後の処理を条件分岐させる作戦。

# PowerAutomate側の設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/700c2c70-ab5e-8758-55b4-dafdfe4d325f.png)
下記の数式でトリガーからデータの抽出。
```triggerBody()?['entity']?['cardOutputs']?['Key']```
Keyはアダプティブカード作成時に設定したもの。
PerseJSONでの抽出も試みたが何故かできなかった。。。
QRコードを印刷　HTML＋JavaScript on CakePHP5

CakePHP5 を只今使用中。

### QRコードはこちらのライブラリで。
https://davidshimjs.github.io/qrcodejs/

58㎜幅のレシートプリンタが接続されている想定です。

### 印刷ボタン（呼び出す側）
```
<a class="btn btn-secondary btn-sm"
 target="_blank"
 href="<?= $this->Url->build('/kegs/qr/' . $ledger->keg) ?>"
><i class="fa-solid fa-print"></i>
</a>
```
単純に _blank でリンクを張ります。

### 印刷ページ（呼び出される側）
```
<?php
$this->disableAutoLayout();
?>
<html>
<body>
<script src="<?= $this->Url->build('/js/qrcode.min.js') ?>"></script>
<div id="qrcode"></div>
<div style="font-size:22px;"><?= $keg->keg ?></div>

<script type="text/javascript">
    new QRCode(document.getElementById("qrcode"),
        {text:"keg:<?= $keg->keg ?>", width:128, height:128,}
    );
    print();
    window.onafterprint = (event) => {
        window.close();
    };
</script>
</body>
</html>
```

### ポイント
印刷ダイアログ終了イベント window.onafterprint がほとんどのブラウザで使えます。
印刷ダイアログでプリンタの選択まではJavascriptで操作できませんので、運用に任せます。
「eTaxソフト 落ちる」で検索している人のために、記録を残します。

## 「利用者ファイルを開く」⇒ OK すると…　強制終了(@_@。　ギリギリ君のeTax送信は切羽詰まってるんです。送信できなきゃヤバいんです。

eTaxソフト 落ちまくりで起動しませんTT
しかもエラーメッセージなし強制終了...

同じ症状なら参考にしてください。

![](https://storage.googleapis.com/zenn-user-upload/637329e2da1a-20240131.png)

この後、強制終了。。。
ちなみに、個人の利用者ファイルを選択するとソフトは正常に動作します。
法人の利用者ファイルを選択したときのみ、ソフトはクラッシュ。

印刷していない送信済みファイルを、お正月休みに印刷して整理しようと思ってたのに。。

## 【原因】
.nccファイル（利用者ファイル）のファイルサイズが大きすぎることが原因っぽいです。確認すると134MB。

このソフトの仕様として、過去送信したファイルの履歴を nccファイル（利用者ファイル）にXMLの平文テキストで保存していくようです。ゴミ箱に入れた内容も。
特に、イメージ送信でPDFファイルをバンバン送っている人は要注意。バイナリをそのままテキストで追記されていっています。

eTaxサポートセンターに電話しても、マニュアル通りの回答を繰り返されて解決になりません。ヽ(`Д´)ﾉﾌﾟﾝﾌﾟﾝ

## 【解決方法】

利用者ファイルを作り直し。

過去送信したファイルを印刷したい、参照したい
## 【解決方法１】
eTaxソフトWEB版にログインして、メッセージボックス一覧から１つずつメッセージを開き「帳票表示」⇒「印刷」
※大量にある場合はちょっと面倒だけど、、正しいやりかたはこれしかない。。

## 【解決方法２】
※ XMLの知識ある人のみ。自己責任で。

nccファイルをテキストエディタで開き、ファイルを２分割します。
サイズが小さくなると正常起動できますよ！

```
<item filename="" key="" ... >

</item>
```
３TAB インデントの item タグでくくられているのが１ファイルのようです。ファイルのまんなか辺りで２分割しましょう。

２分割したそれぞれの利用者ファイルを選択すると、それぞれのXMLファイルに含まれている分の過去データが参照＆印刷できます。

正規表現"￥n￥t￥t￥t<item "で検索するといいかも。
		   
![](https://storage.googleapis.com/zenn-user-upload/38ecda7f12dd-20240131.png)
スクレイピングがエラー Selenium×Chrome

Selenium スクレイピング、大変便利ですが、Chromeがアップデートするたびに、Chrome↔Selenium 間のドライバを更新する必要があります。

VBAなら
```

On Error GoTo ErrorHandler
    Dim Driver As New Selenium.WebDriver
    Dim elm As Selenium.WebElement
    Driver.Start "chrome"
    On Error GoTo 0
    ...
    通常の処理
    ...
    
ErrorHandler:

    InputBox Err.Description, "Chromdriverのバージョンエラー 更新しよう！", "https://chromedriver.chromium.org/downloads"
    
    InputBox "chromedriver.exe を上書きします。場所はこちら", "chromedriver.exeの場所", "C:\Program Files\SeleniumBasic"    
    
    On Error GoTo 0
```
こんな感じかな。
https://chromedriver.chromium.org/downloads
バージョン115以降でJSONエンドポイントを配信してくれているので、もうちょっとゴニョゴニョしたら使いやすくなるかもね。
MySQL トリガーでエラーを発生させる CakePHP5

## ルールに合わないデータ更新に対してDBレベルでエラーを発生させる
データ整合性の為のルールを保ちたい場合、MySQLから見たクライアントAPPの種類が増えてくると、MySQL側で一括管理するほうが良い場合が多いと思います。その際に活躍するのがトリガーでのエラーチェック。

```
CREATE TRIGGER trigger_ledgers BEFORE DELETE ON ledgers
BEGIN
SELECT COUNT(*) INTO @cnt FROM ledgers
    WHERE id > OLD.id
      AND user_id = OLD.user_id;
IF @cnt > 0 THEN
    SIGNAL SQLSTATE '45000' 
      SET MESSAGE_TEXT = '過去は消せません。ただし１つ前の過去だけ消すことができます。';
END IF;
END
```

ledgersテーブルに対して、DELETE を許可するかどうかのトリガー。
BEFORE なので削除しようとしているレコードもまだ存在します。
積み上げ集計用のカラムがあるから、最新のレコードのみ削除可能としたい。
最新のレコードかどうかの判定を　@cnt > 0 で行っています。

> 一般的な SQLSTATE 値を通知するには、'45000' を使用します。これは、「未処理のユーザー定義の例外」を示します。
> https://dev.mysql.com/doc/refman/8.0/ja/signal.html

### デメリットもある
データベースエラーなのでAPP側では致命的エラー扱い。
実装時のデバッグ環境が乏しい。
複数テーブルにかかわる場合は特にデッドロックしないようアクセスする順番を決めておこう。

## CakePHP で補足できないじゃないかTT
if($this->Ledgers->delete($ledger)){...} ここで反応してほしい。
BUT データベースエラーなので致命的エラーです。「An Internal Error Has Occurred.」
ということで、トリガーは保険として、Behaviorを作ろう！

ビヘイビアでチェックしイベントチェーンを停止させる。
コードはほぼ同じ要領。

```php:LedgersBehavior.php
public function beforeDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)
    {    
            $ledgersTable = TableRegistry::getTableLocator()->get('Ledgers');
            $ledger = $ledgersTable->find()
                ->where(['user_id'=>$entity->user_id])
                ->where(['id >'=>$entity->id])
                ->first();
            if($ledger){
                $event->stopPropagation();
                $event->setResult(false);
                return;
            }
    }
```
CakePHP5 WEHE句でのbind

## WHERE id NOT IN(SELECT id FROM xxx WHERE code=?) こんなクエリを作りたい

> https://book.cakephp.org/5/ja/orm/database-basics.html#id15
> ステートメントを準備する
あなたは execute() か prepare() でステートメントオブジェクトを生成できます。 execute() メソッドは引き継いだ値をバインドしたステートメントを返します。 それに対して prepare() 
は不完全なステートメントを返します。

ConnectionManager::get('default') して、prepare して bind して...　とあれこれ試してみたもののシンプルに書けなかった。
## シンプルな方法
```
$rows = $this->Xxx->find('all')
->where('Xxx.id NOT IN (SELECT id FROM xxx WHERE code = :code)')
->bind(':code',$code,'integer');
```

プレースホルダ「？」として、bind にプレースホルダを指定しなくてもいい方法を探ったけど断念。。

誰か教えて。。

直感的にこう書きたい。。
```
->where('Xxx.id NOT IN (SELECT id FROM xxx WHERE code = ? AND code2 = ?)')
->bind([$code,$code2],['integer','integer']);
```
CakePHP Form シンプルHTMLタグを出力して～～

$this->Form->create();
$this->Form->control();
$this->Form->submit();
大変ありがたい機能です。
...が、便利な反面おせっかいなHTMLタグに悩まされることも。
## シンプルな <input /> <submit /> を出力したい！
```
<?php
$this->Form->setTemplates(['inputContainer'=>'{{content}}', 'submitContainer'=>'{{content}}']);
?>
```

以上！
MySQL トリガで同一テーブルを更新する

預金通帳の「現在高」にあたる情報を保存すべくカラムを作った。
パフォーマンス的な観点から。

INSERTがメインのログ的なテーブルなので、INSERTする都度アプリ側のモデルで集計すればいいのだが、MySQL側のトリガでの処理がベストかと思い実装する。

```
略　このトリガはINSERT時にエラーがでます

CREATE TRIGGER trigger_ledgers_insert
AFTER INSERT
ON ledgers FOR EACH ROW
BEGIN

SELECT SUM(quantity) INTO @sum
 FROM ledgers;
UPDATE ledgers SET
 quantity_current = @sum
 WHERE id = NEW.id;

END

略
```

ところが

#1442 - Can't update table 'ledgers' in stored function/trigger because it is already used by statement which invoked this stored function/trigger


> ストアドファンクションまたはトリガーは、そのストアドファンクションまたはトリガーを呼び出したステートメントによって (読み取りまたは書き込みに) すでに使用されているテーブルを変更できません。
> 
> https://dev.mysql.com/doc/refman/8.0/ja/stored-program-restrictions.html#stored-routines-trigger-restrictions

SQLServerの時は気にせず書いていたような覚えがあるが…

で、ナイスな方法ありました。

```
DELIMITER //

CREATE TRIGGER trigger_ledgers_insert
BEFORE INSERT
ON ledgers FOR EACH ROW
BEGIN

SELECT SUM(quantity)+NEW.quantity INTO @sum
 FROM ledgers;
SET NEW.quantity_current = @sum;

END//

DELIMITER ;
```

ポイントは BEFORE の NEW を更新すること。
テーブルには書き込めないから、書き込む前のNEWの内容を更新しよう という方針。

BEFOREのNEW ということはこの集計時点ではまだテーブルには書き込まれていません。
したがってSUMに含まれないため、+NEW.quantity をしています。
CakePHP5 モデル の メソッド・定数 どこに書く？

CakePHPはモデルがTableとEntityに分かれています。別のフレームワークではモデルが１つのことが多かった。さて、どうしようか。

> テーブルオブジェクト がオブジェクトのコレクションへのアクセスを表し、提供するのに対し、 エンティティーは個々の行やドメインオブジェクトを表します。エンティティーは保持するデータにアクセスして 操作するための永続的なプロパティーとメソッドを保有しています。

https://book.cakephp.org/5/ja/orm/entities.html#namespace-Cake\ORM

、、となると、エンティティかな。定数はテーブルかな。

ただ、DB仕様変更が多いから、bin/cake bake model … で、チャチャっと更新したい。なので、エンティティに書くのは最小限にしたいなぁ。できれば１か所にまとめたいなぁ。

ということで、

### CRUD の R のみ ならエンティティ
「レコードの状態から判定する」とか「アラート表示をつくる」とかの「データ更新を伴わないもの」はエンティティ
1. コントローラーやビューで短いコードで済む

### ほかは ビヘイビア
「定数」「レコードの取得、更新」「DBのトリガ的なコード」とかはビヘイビア
1. できるだけモデル単位で１か所にまとめたい
2. bake model でジャンジャン上書きしても差分編集が楽ちん

ということにしました。

## ビヘイビアをつくる

まずは、bake。

```
bin/cake bake behavior [ModelName]
```
ビヘイビア編集
```
<?php

class ModelNameBehavior extends Behavior
{
    //固定値
    public const STATE_DONE = 'done';

    protected array $_defaultConfig = [];
    public function initialize(array $config): void
    {
    }
    //update前の補完等はここに
    public function beforeSave(EventInterface $event, EntityInterface $entity, ArrayObject $options)
    {

    }
    //update後のデータ編集はここに
    public function afterSave(EventInterface $event, EntityInterface $entity, ArrayObject $options)
    {
    }

}
```

テーブルにビヘイビア追加

```
        $this->addBehavior('Timestamp');
        $this->addBehavior('ModelNameBehavior');
```

## イベントはこれだけある


>Model.initialize
>Model.beforeMarshal
>Model.afterMarshal
>Model.beforeFind
>Model.buildValidator
>Model.buildRules
>Model.beforeRules
>Model.afterRules
>Model.beforeSave
>Model.afterSave
>Model.afterSaveCommit
>Model.beforeDelete
>Model.afterDelete
>Model.afterDeleteCommit

https://book.cakephp.org/5/ja/orm/table-objects.html#table-callbacks



## あと…

考えたら当たり前なんだけど、テーブルのバリデーションが完了してから、ビヘイビアのbeforeSave、afterSave と着火する。デバッグ時には、どうもビヘイビアを一番に疑ってしまうので、忘備録。

## ビヘイビアに、別テーブルへの書き込みを書く場合…

設計としてはあんまりよろしくないんだろうけど「開発スピード＆メンテ楽する」を最優先！
更新の方向（順番）を決めておこう。
何も考えずにやっていると、デッドロックしちゃうよ～。
やり過ぎてスパゲッティにならないようにね。
CakePHP5 論理削除 Softdelete

https://github.com/fusic/Reincarnation

ありがとうございます。感謝。

## 別のアプローチ
データベースのトリガーで対処するシナリオも手間少なく実装できそうです。
AFTER DELETE トリガでログ用テーブルに INSERT。
CakePHP側では物理削除のまま実装できますので、作業コストを減らしたい派にはこの方法かな。
CakePHP5+Bootstrap5 ページ表示時にモーダルを開きたい

ウィザード形式に入力してほしいときとか、必須項目を入力してほしいときとか、ページ表示時にモーダルを表示させたい。
```
<?php
if(!$user->hasValue('email_activated') {
    $this->Html->scriptStart(['block'=>true]);
?>
const modal = new bootstrap.Modal(document.getElementById('modalEmailActivateForm'));
modal.show();
<?php
    $this->Html->scriptEnd();
?>
```
CakePHP5 アソシエーションの方法　複数カラムから 同じテーブルへ

カラム名を　[モデルの単数形]_id　とすると 、bake でサクッとアソシエーションのコードを作ってくれますが、ルールから外れるとコード作ってくれません。（そりゃそうだ；）

例えば　スタッフ(staffs) が バーガー(burgers)を焼いて包む場合

テーブル名：burgersカラム：id, grilled_staff_id, packed_staff_id, created, modified

テーブル名：staffsカラム：id, name, create, modified

（はい。お腹すいてます。。ごはん前です。。）
```
<?= $burger->grilled_staff->name ?> ←エラー！
<?= $burger->packed_staff->name ?> ←エラー！
```
としたいけどどうする？！

アソシエーションを設定する方法はこちら。https://book.cakephp.org/5/ja/orm/associations.html#id1

...理解するまで時間がかかったのでこの記事書いてます。

## 解決方法はこちら！

```php:src/Model/BurgersTable.php
    public function initialize(array $config): void
    {
        ...

        $this->belongsTo('GrilledStaffs',[
            'className' => 'Staffs',
            'foreignKey' => 'grilled_staff_id',
            'joinType' => 'INNER',
            'property' => 'grilled_staff',   /* このカラム名だと省略可 */ 
        ]);
        $this->belongsTo('PackedStaffs',[
            'className' => 'Staffs',
            'foreignKey' => 'packed_staff_id',
            'joinType' => 'INNER',
            'property' => 'packed_staff',   /* このカラム名だと省略可 */ 
        ]);
        
        ...
```
コントローラー
```
$burger = $this->Burgers->get($id, contain: ['GrilledStaffs', 'PackedStaffs']);
```
ビュー
```
<dl>
    <dt>バーガーを焼いたスタッフ</dt>
    <dd>
        <?= $burgers->hasValue('grilled_staff') ? h($burgers->grilled_staff->name) : '' ?>
    </dd>
    <dt>バーガーを包んだスタッフ</dt>
    <dd>
        <?= $burgers->hasValue('packed_staff') ? h($burgers->packed_staff->name) : '' ?>
    </dd>
</dl>
```
CakePHP5 翻訳

## ロケールの設定

```php:config/app.php

    'App' => [
        'namespace' => 'App',
        'encoding' => env('APP_ENCODING', 'UTF-8'),
        'defaultLocale' => env('APP_DEFAULT_LOCALE', 'ja_JP'),
        'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'Asia/Tokyo'),
```
## POTファイル作成
```
bin/cake i18n extract
```
resources/locales/ に cake.pot　default.pot ファイルができました。（参考）https://book.cakephp.org/5/ja/console-commands/i18n.html

## Poeditで編集、poファイル作成

1. Poedit でPOTファイルを開く
Poedit
https://poedit.net/

2. 翻訳プロジェクトを新規作成する
3. 同じ名前.po で保存（次からはこのファイルをPoedit で開き編集）

## ディレクトリ構造

resources/locales
  /en_US
  /ja_JP
    cake.po
    default.po

（参考）
https://book.cakephp.org/5/ja/core-libraries/internationalization-and-localization.html#

各locale の poファイルの作成は、全テーブルの bin/bake all が完了してからがいいかな。編集して差分とるのがひと手間かかってしまうので。
CakePHP5, Bootstrap5 でサクッと開発

すぐ忘れて毎回調べなおすのが面倒なので忘備録。サクッとしたい。このスピード感、大好き(#^^#)

## 環境

- Win10, VSCode, A5M2, XAMPP
- composer
- Git

## いざ作業

A5M2 DDLを作ってデータベース作成※ここからよく忘れるCakePHPインストール
```
composer self-update
composer create-project --prefer-dist cakephp/app [PROJECTNAME]
```
Git ここにリポジトリ作成Git コミット しておこうここからはVSCodeで。
CakePHPのデータベース設定config/app_local.phpブラウザで確認

## 次、Bootstrap ！
```
composer require friendsofcake/bootstrap-ui
bin/cake plugin load BootstrapUI

bin/cake bootstrap install
bin/cake bootstrap modify_view
bin/cake bootstrap copy_layouts
```
で、開発スタート。まずはバンバンbakeしまくる
```
bin/cake bake all [TABLENAME] -t BootstrapUI
bin/cake bake all [TABLENAME] -t BootstrapUI
bin/cake bake all [TABLENAME] -t BootstrapUI
bin/cake bake all [TABLENAME] -t BootstrapUI
bin/cake bake all [TABLENAME] -t BootstrapUI
...
```
↓の順でもいいよ。
```
bin/cake bake all -t BootstrapUI [TABLENAME]
```
A5M2からエンティティ定義書をExcel出力して、テーブル名一覧で上記コマンドを一括作成すると、bakeコマンドコピペで一撃！

## VSCodeのボックス選択は SHIFT + ALT でドラッグです

ここで、多分エラー

## Cake\View\Helper\HtmlHelper::link(): Argument #1 ($title) must be of type array|string, null given, called in C:\xxxxxx\[projectname]\templates\layout\TwitterBootstrap\dashboard.php on line 18📋

なぬ！

```php:templates/layout/TwitterBootstrap/dashboard.php

<?= $this->Html->link(
            Configure::read('App.title'), // line 18
```
Config に App.titleが欲しいそうです。ここを消しちゃうか、残すなら config/app.php に追記。
```php:config/app.php
'App' => [
    ...
    'title' => '[PROJECTNAME]', //追記
```
動作確認

ここらへんで Git コミット

仕事した感が出たので今日はこの辺で。まだ数分ですが…(笑)

あれ？Bootstrapの動きが？？...と思ったらはい、そうです。webroot/bootstrap_u_i/ の bootstrapを最新にしましょうー

あと、

bootstrapテンプレートはdashboardだけでなく cover, signin も作ってくださっております（感謝）signinは↓

```
bin/cake bake template [CONTROLENAME] login -t BootstrapUI
```

詳しいusageはこちら

FriendsOfCake/bootstrap-ui
https://github.com/FriendsOfCake/bootstrap-ui

CakePHP
https://book.cakephp.org/5/ja/
CakePHP5　主キーに「id」を使用しない　「id」以外の主キー

別システムからの移行でCakeのルールに沿わないテーブル構造です。マスタ的なテーブルであるため、ルールに沿ってCakeの恩恵をうけるよりはそのままのテーブル構造で進める方がコストが少ないと判断しました。

cake bake のあと

Entityの_accessibleに追加
```php:src/Model/Entity/Xxx.php
class XXX extends Entity
{
    protected array $_accessible = [
        'code' => true,  // ← 主キーカラム追加
        ...
```
Table の validator を追加
```php:src/Model/Table/Xxx.php
public function validationDefault(Validator $validator): Validator
    {
        $validator
            ->scalar('code')
            ->requirePresence('code', 'create')
            ->notEmptyString('code');
```
ビューに追加
```php:templates/xxx/add.php
<?php
    echo $this->Form->control('code', ['type' => 'text']);
```
edit.php は編集しない。主キーは変更できない仕様にするのがよさそう。頑張るところじゃないね。

あと、アソシエーションの設定が必要になるかもsrc/Model/Table/XxxTable.php の initialize に hasManyの設定と、相手のTable.phpのinitialezeに belongsTo の設定と。
MySQL のフロントにAccessを使う？

結論、今回のプロジェクトでは 不採用。

中小企業のちょっとした業務システムの開発には、Accessがぴったりなケースが非常に多いと考えています。

## Accessのメリット

数十年の歴史、情報が多い

学習コストが低く、使える人が多い

爆速で開発できる

プリンタとの相性が抜群に良い

Access２万円、配布用には無料のランタイムがあって低コスト

DBが１ファイルなのでバックアップ管理が簡単

## Accessのデメリット

謎のクラッシュがまれに起こる　⇒ファイル分割で回避、バックアップデータ件数が数億件とかなってくると動作がモッサリ

ひととおりAccessで開発完了して、次のステップをいろいろやってたら将来見据えて…　で、思いつくわけです。バックエンドMySQL、フロント Access。いいとこどりのいい案だと思うんです。ODBCドライバでの接続になり、多少の難点はありますが、エラー処理やら、各テーブルのタイムスタンプカラムやらひととおり対策すれば開発は容易に進みます。そしてAccessでのフロント開発はとにかく爆速。速い！

要求事項がここまでならおススメです。

が、今回のプロジェクトはレンタルサーバーのMySQLとレプリケーションがしたかった。業務で普段使いするLAN内のMySQLの一部の情報を、レンタルサーバー経由で公開したかったのです。でですね、
## XServerもロリポップも、MySQLには外部から接続できません！
早く気付こう(^^；AmazonやSakuraに自分でDB立てるしかなさそう。パパっと完了したいので今回はパス。

Access爆速すぎてバージョン１をつくったあと、バージョン２もいちからつくっちゃったので、データモデルが設計当初よりかなり洗練されました。結果どうなったかというと、メインXServerにMySQL×Cakephp＋ローカルAccess（MySQL使わず）となりました。
石油化学工業の紹介

石油化学業界は「[内閣サイバーセキュリティセンターが重要インフラとみなす14分野](https://www.nisc.go.jp/policy/group/infra/index.html)」に含まれる、生活に不可欠な業界です。一方で石油化学の技術は高度に専門化しており、ITエンジニアにとってわかりにい用語が含まれます。この記事では用語紹介として、「[石油化学工業ハンドブック](https://ndlsearch.ndl.go.jp/books/R100000039-I2497882)」の目次を紹介します。個別の内容は機会がある時に紹介します。


https://ndlsearch.ndl.go.jp/books/R100000039-I2497882

# I 総論
- 1 石油化学工業の概要
- 2 石油化学工業の発展経緯
# II 炭化水素資源
- 3 天然ガスならびに石油資源
- 4 製油所ガス
# III 炭化水素の分解，変成
- 5 アセチレンの製法
- 6 エチレンならびにプロピレンの製法
- 7 ブチレンならびにブタジエンの製法
- 8 合成ガスの製法
- 9 石油芳香族
# IV 石油化学工業二次製品
- 10 メタンおよび合成ガス系製品
- 11 アセチレン系製品
- 12 エタンおよびエチレン系製品
- 13 プロパンおよびプロピレン系製品
- 14 ブタン，ブチレンおよびブタジエン系製品
- 15 芳香族系石油化学
- 16 その他の二次製品
# V 石油化学工業の最終製品
- 17 合成樹脂
- 18 合成繊維
- 19 合成ゴム
- 20 合成洗剤および合成界面活性剤
- 21 肥料
- 22 溶剤
- 23 農薬
- 24 医薬
- 25 石油添加剤
- 26 爆薬
- 27 可塑剤
- 28 糊剤
Stripe PHP SDK Custom子アカウントに紐づく外部口座の口座番号末尾4桁を取得

## 概要

Stripe PHP SDKにてCustom子アカウントに紐づく外部口座の口座番号末尾4桁を取得する方法をメモ的にまとめる。

## 方法

下記のように記載することで取得できる。

```php
$stripe = new \Stripe\StripeClient('sk_test_51OAW2lEwNfmSaNN2fe6Iq27lX3sopoE0LGTk0Uhu6m59eU7nXyQmsOHioJktPtxJRPZRkBCu5tURURmyZpKeI2ej00cFrSfeed');
$account = $stripe->accounts->retrieve('acct_1OezMbImLSY2PRF5', []);
$externalAccounts = $account->external_accounts->data;

foreach ($externalAccounts as $externalAccount) {
    $last4 = null;
    if ($externalAccount->default_for_currency && ($externalAccount instanceof Stripe\BankAccount)) {
        $last4 = $externalAccount->last4;
        // デフォルトの外部口座は1件だけのはずなので、ヒットしたらbrake;してもいいかも
    }
}

echo $last4;
```

外部アカウントの情報を一つ一つ取り出し、デフォルトに設定されている、かつ、`Stripe\BankAccount`のオブジェクトである場合にだけlast4プロパティにアクセスし、データを取得する。

## 参考文献

https://stripe.com/docs/api/accounts/object

https://stripe.com/docs/api/accounts/retrieve
総計とそれ以外で処理を変える方法

## 総計とは？
ワークシート内の列・行単位の総計を表示する機能です。
合計値を別ワークシートで出さなくて済むので活用する場面も多いかと思います。
![Untitled (15).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/bf6bd5de-0c5b-951a-2399-bb3f0bf26f31.png)
メニューバー→分析→合計→(列・行)の総計を表示
で表示可能です。
計算方法を「自動」で設定すると、指定した列・行の単位を無視して指定された計算してくれます。
以下は個別のカウント(COUND)を使用した時の総計の結果
![Untitled (16).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/44eb58c3-b795-55ba-0cbf-a9f0c6c97d95.png)

## 総計で困ったこと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/fb0ac07b-e6d9-43a3-5270-33694eb9755f.png)
以前、各製品に対し、特定の日にその製品を販売した「店舗数」を、特定の日に販売の実績があった「全店舗数」で割った値を「納品率」として表示してほしいという依頼がありました。
全店舗数の計算に工夫が必要となります。
各日付毎については
```
{FIXED[日付]:COUNTD([店舗名])}
```
とすればよいですが、この計算に対して総計を行うと各日付毎の値に対して集計処理を行うことになり、計算が合わなくなります。
![Untitled (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/9abdd95b-cc0a-5a6a-259b-5c8fd7722efd.png)
![Untitled (17).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/748c9755-1cd5-2ed5-9659-a702720352d5.png)

そのため、総計と各日付ごとで処理を分ける必要があります。

## 解決案
日付のカウント数が1の時には日付毎の個別のカウント、2以上の時は日付も無視した個別のカウント、という分岐をすることで総計の処理を変更することを考えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/46bbe727-0791-7615-e78c-473acc6e728c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/d60afc0c-870b-5b65-0e22-42457f19e287.png)

これにより製品「AA」「BB」は全店舗数の使い分けが出来るようになりました。
ただし製品「CC」は2021/10/01しか実績がないため総計でも日付の個別のカウントが1という扱いになってしまいますので、分岐しません。そのため、手順としては必ず日付が入るように設定します。

## 実施手順
Tableau Prepで処理を行います。
集計でグループ化したフィールドを「店舗名」「製品」に設定し、そのデータをユニオンしたものを出力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/814ad6b0-a428-4d0f-1604-1edee71ec703.png)
このデータをTableauに接続します。
計算フィールド「日付の個数」を作成(NULLの日付はダミーで1900年などのありえない日付としてカウントする)。
```
COUNTD(IFNULL([日付],DATE("1900/1/1")))
```
計算フィールド「店舗数」を作成(ダミー行の店舗数をカウントしないためにNULLの判定がついている)
```
COUNTD(IF NOT(ISNULL([日付])) THEN [店舗名]END)
```
計算フィールド「全店舗数」を作成(ダミー行の店舗数をカウントしないためにNULLの判定がついている)
```
IF [日付の個数]=1 THEN SOM({FIXED[日付]:COUNTD([店舗名])})
ELSE SUM({FIXED:COUNTD(
IF NOT(ISNULL([日付])) THEN
[店舗名]
END
)})
END
```
計算フィールド「納品率」を作成
```
[店舗数]/[全店舗数]
```
クロス集計表を作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/a8ddd40f-3628-3456-99c2-ddf62aa8b14a.png)
NULLを選択し、非表示にする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/4458c0b8-bcaa-3e5a-2728-67a4c8280632.png)
正しい表が得られる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/c4050062-1462-0fed-0768-3a14edc5abea.png)
日付のフィルターを使用する場合は必ず「NULL値を含める」にチェックを入れ、コンテキストに追加の設定を行うこと。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/caf55461-5c56-77cb-1891-e334ca73fb50.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/c781f105-3040-e8a3-1867-d879a136057d.png)

## 終わりに
総計は便利ですが計算が複雑になると意図しない変わった数を返すことが多くあるため注意が必要です。
総計に入るディメンションの数を使用した分岐により上手く制御する方法を紹介しました。是非ご活用ください。
GO言語における nil と null の違い

GO言語において、nil と null は似ているようで異なる概念です。これらの用語は、異なるプログラミング言語で使われることがあり、それぞれの言語において異なる挙動を示すことがあります。GO言語では、nil という用語がより一般的に使用されていますが、一部の文脈では null という言葉も見られることがあります。

### 1. nil の使用
GO言語では、nil は主に[ポインタ、スライス、チャネル、マップ、インターフェース](https://qiita.com/zhao-xy/items/4fcbf5b64eb4cbf23b6c)、関数などの特定の型の変数が値を持たないことを示すために使用されます。例えば、ポインタの初期値は通常 nil です。

```go
package main

import "fmt"

func main() {
	var myPointer *int
	if myPointer == nil {
		fmt.Println("ポインタはnilです。")
	}

}
```

```
ポインタはnilです。
```

https://go.dev/play/p/rCxDFLRZ6kZ

### 2. null の使用
一方で、null はGO言語では一般的ではありませんが、一部の他のプログラミング言語（例: JavaScript、Java、C#）で使われています。null は通常、オブジェクトが存在しないことを示すために使われ、変数が何も参照していないことを示します。


```javascript
var myObject = null;

```

```
./prog.go:8:17: undefined: null
```

https://go.dev/play/p/HAEz9W4CMvP

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/e097f4c2-b75b-888b-daae-b2315a4a4543.png)


### 3. 結論
GO言語では nil が主に使用され、特定の型が値を持たないことを示します。一方で、null はGO言語ではあまり一般的ではなく、他のプログラミング言語でよく見られる用語です。どちらも変数が値を持たない状態を示すための概念であり、コンテキストによって使い分けられるべきです。

#### デバッグツール
ファイルの検索と複製-Linux

## コマンド
```
find . -type f -name "*ファイル名1*" | grep . | xargs -I {} cp {} xxx
```
### find　引数について
.：カレントディレクトリ配下を検索する
-type f:ファイル形式だけを調査する
-name filename:filenameに一致するファイルだけを選択する。-nameを記載することで、filenameがなくても、findの実行結果は0になる。

### grep 引数について
.:任意の1文字以上を検索する

### xargs　引数について
-I {}：渡されたデータを{}に設定する。今回のケースでは、cpコマンドに渡す引数リストをxargs -I{}を使用して渡している。

## grepを使用する理由について
-nameを付けることにより指定したファイルがなくても、処理が進んでしまう。cpコマンドでもこけることがないので、正常終了してしまう。指定したファイルがない場合に異常終了を起こすために、grep .を使用する。
findの実行結果がgrepに渡され、ファイルがなければ任意の1文字以上を検索することもできないためgrepでこけるようになる。
Tableau2022.3の新機能「値を使用して表示状態を制御する」で実現できる嬉しいこと

## 「値を使用して表示状態を制御する」とは？
「値を使用して表示状態を制御する」はTableau2022.3で追加された、ブール値が正となる際にそのオブジェクトを表示できるという機能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/0b2f364c-d5ec-b242-f9d4-056af96e4b25.png)

ここで、少し慣れているTableauユーザーであれば、コンテナにワークシートを入れ、フィルターで制御すればワークシートの切り替え機能って作成できるでしょ？って思われたかもしれないです。

この記事では、「値を使用して表示状態を制御する」でないと実現できないことをいくつかまとめてきました。

## 円グラフも切り替えられる
従来のフィルターによるワークシート切替の場合、非表示となるワークシートは幅が1となりほぼ完全に消えているように見えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/f0c2da93-d9bf-6491-0970-57dfad9130b2.png)

これを円グラフにするとなぜか完全に消えません。
これによりもう一方のグラフの開始地点がズレたりし、分析の阻害になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/8b604571-a385-533b-b2ad-e2b3e551f2a5.png)

これを「値を使用して表示状態を制御する」で制御すれば円グラフも完全に消すことが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/afcb9f9b-266b-45f3-4362-b7254d8249f8.png)

## ワークシート以外の表示も消せる
これが一番大きいです。ワークシート以外を消すことが出来て嬉しい場面をいくつか紹介します。

### 凡例
切り替える2つのワークシートそれぞれで別の色を使用すると、凡例のタイトルが残ってしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/99b3892f-1018-7643-8cb1-0dc4281d63f9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/166f9640-391c-a1ef-fb1f-9d22a782855f.png)

「値を使用して表示状態を制御する」はあらゆるオブジェクトに対して設定可能なので、凡例に対して「値を使用して表示状態を制御する」を設定することにより正しく表示可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/a76fe13c-3d7c-6727-57d5-425a0484f544.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/ff53994d-82cb-a3e6-9810-7bffa7312878.png)

パラメーター
2つ目のグラフが、指定した件数分売上の上位の製品名を表示するグラフだったとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/d2db03d0-7f5e-6c31-21d7-85113d7d89f9.png)

このとき1つ目のグラフへ切り替えたときにランク入力のパラメーターが残ってしまい、ダッシュボード上に余計な情報が残ってしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/b40ed83b-848e-fd36-61c0-0a5c06b7f25c.png)

パラメーターもオブジェクトなので「値を使用して表示状態を制御する」の設定が可能です。
2つ目のグラフを表示するときのみパラメーターも表示する設定しすることで、余計な情報を減らすことが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687866/9b0aeb7a-c5ff-e7c4-486a-85a3a9026200.png)

## おわりに
Tableauはアップデートがかなり頻繁かつ、新機能にユーザーの意見を取り入れることがかなり多いため、今までのダッシュボード作成の常識が大きく変わるということがたびたび発生するため、是非最新情報は定期的に追いかけるのをおススメします。
「値を使用して表示状態を制御する」はかなりいろんなことが出来るようになる機能ですので是非いろんな活用法を紹介してほしいです！
Railsガイド +αメモず【DSL】

# はじめに
[Railsガイド](https://railsguides.jp/active_record_migrations.html)でわからなかった語句シリーズです。
「マイグレーションではRubyのDSLが使われているので・・・」
DSL？何？
# DSLとは
  
> DSL（Domain-Specific Language）は、特定のアプリケーションドメインに特化したプログラミング言語です。これらの言語は、一般的なプログラミング言語とは異なり、特定の種類の問題やタスクを扱うために設計されています。DSLの目的は、特定のドメインにおいて、より効率的かつ直感的にプログラミングすることを可能にすることです。
> 
> DSLには大きく分けて2種類あります：
> 
> 1. **内部DSL（Internal DSLs）**：これは既存のプログラミング言語に基づいて構築され、その言語の構文と機能を活用します。例えば、RubyやPythonで書かれたDSLがこれに該当します。
>     
> 2. **外部DSL（External DSLs）**：これらは独自の構文を持ち、特定のドメインの問題を解決するためにゼロから設計されます。SQL（データベースの問い合わせ言語）やHTML（ウェブページのマークアップ言語）は外部DSLの例です。
>     
> 
> DSLの利点は、特定のドメインにおいて効率的かつ効果的なプログラミングを可能にすることです。これにより、開発者はそのドメインに固有の問題に集中し、より簡潔で理解しやすいコードを書くことができます。しかし、DSLはその特定の用途に限定されているため、一般的なプログラミング言語と比べると汎用性には欠けます。
# GPLとは
  
> DSL（ドメイン固有言語）の対となる言葉としては、**GPL（General-Purpose Language）**、つまり**汎用プログラミング言語**が挙げられます。
> 
> 汎用プログラミング言語は、特定のドメインやタスクに特化しているわけではなく、あらゆる種類のプログラミング作業に適用可能な言語です。これらの言語は、多様な問題を解決するために設計されており、幅広い用途に使用できます。代表的な汎用プログラミング言語には、Python、Java、C、C++、JavaScriptなどがあります。
> 
> 要約すると、DSLは特定のドメインに特化しているのに対し、GPLは幅広い用途に使用できる汎用性の高いプログラミング言語です。
# おわり
分類を知ると、体系的に理解が進むので楽しいですね。
VSCode で LaTeX ソースを書くときにインデントが思うように動かない対策

# TL;DR これを設定に加えると解決する
```settings.json
  "[latex]": {
    "editor.autoIndent": "keep"
  }
```

# 問題
VSCode で LaTeX ソースを書くときにインデントが思うように動かない
具体的には、以下のような振る舞いをする

# 事例
```.tex
      この様にインデントしたものの下に改行すると
      同じ位置にインデントして欲しい (これは満たされている)

% --------------

また、
      この様にインデントしたものの下にインデントのない空行を入れて改行すると

この様に空行のインデントの個数(ここでは0)に合わせて欲しい


% --------------

あるいは、
      この様にインデントしたものの下により少ないインデントの空行を入れて改行すると
   
   この様に空行のインデントの個数(ここでは3)に合わせて欲しい


% --------------


しかし、
      この様にインデントしたものの下にインデントのない(少ない)空行を入れて改行すると

      上の文と同じ位置にインデントされてしまう

```

# 解決法
これを設定に加えると解決する
```settings.json
  "[latex]": {
    "editor.autoIndent": "keep"
  }
```
ただし、既に `"[latex]"` が設定ファイルにある場合は設定ファイルの `"[latex]:{...}"` の中に `"editor.autoIndent": "keep"` を追加する。

なお、この設定はデフォルトでは `"keep"` ではなく `"full"` になっており、各言語ごとに設定された改行のルールに優先的に従うようになっているらしい。そして、latex に対して定義された改行のルールが非直感的なルールになっているため上記の問題が発生していた。

`"keep"` は「現在の行のインデントを保持する」というシンプルかつ他の言語ではよく見られる直感的なルールのためこの問題を解決する。
データ漏洩しないためにやること（.env, .gitignore）



## はじめに
データ漏洩しないためにやることに関しての備忘録です。
初心者です😅
間違えてる部分が多々あると思います。
もし見つけた場合、ツッコミいただけると助かります🙇

私はGitHubを使ってますが（皆さんも使ってるとは思いますが）、データを更新するたびに、アップロードしてはいけないファイルがないか？APIKEYなどが記載されてないか？
また、誤ってアップロードしてしまって、各社から通知がくるみたいなことが度々ありました。

これに対しての対策を学ばせていただき、知識定着化のためアウトプット。

<br>
## 🦁<INS>**結論**</INS>🦁
.envファイルと.gitignoreファイルを活用して行う。

:::note info
押さえておくべき点
* gitignoreにアップロードしないデータを指定する
* envファイルに鍵になるもの、URLなどを保管する
:::  
***


:::note warn
利用シーン
* 全てのシーンで利用。
:::  
***


:::note alert
注意点
* envファイルはは機密情報を多く含んでいるため、別の形で漏洩しないように注意が必要。
* 隠しファイルになっている（cmd + .  これで解除できる）
* envファイルはgitignoreファイルで指定されてなかったり、gitignoreファイル自体がないことで漏洩などで漏洩してしまう可能性がある。
:::  

***

## 詳細内容
### envファイル
・envファイルに書かれたものは「環境変数」になる。
鍵になるもの（jsonファイルなども含む）
サイトURL、ログイン情報など
こういうものを保管しておくファイル

隠しファイルになっている。
異なる開発環境（ローカル、開発、本番など）で異なる設定を使用する場合、それぞれに対応する複数の env ファイル（例：.env.local、.env.dev、.env.prod）を用意することが有効。

```.env.py
APIKEY='MY_API_KEY'

LOGIN_ID='MY_LOGIN_ID'
LOGIN_PASS='MY_LOGIN_PASS'
```
***  

### gitignoreファイル
・指定したファイル、形式などをGitHubへのアップロードをしないようにしてくれる。

・隠しファイルになってる。

・スラッシュ（/）を使って、ルートディレクトリに対する相対パスを指定することで、特定の場所にあるファイルやディレクトリを無視する。

```.gitignore.py
# ログファイルを無視
*.log

# node_modules ディレクトリを無視
node_modules/

# 特定の設定ファイルを無視
.env
```

***  


## まとめ😺
ほとんどのプロジェクトで
.envファイル
.gitignoreファイル
これらを使って、情報漏洩リスクを減らす。

これにより生産性がかなり上がりました！
確認する時間を一気に減らすことができました。
情報漏洩の可能性も、ほぼ無くせると思います。
***