OmniAICreator/Qiita-1.07M huggingface dataset:
nginx→rails(puma)の構成で長め(〜１分)の処理が失敗する


## 事象
とある方向けの社内Webアプリ。
OpenMediaVaultのNginxでRailsを動かす、というごくごく一般的な構成。

あるときから長めの処理を行うページが動かなくなった。
具体的な事象は
* ブラウザで操作すると「サーバーとの接続が予期せず解除されました」みたいな感じになる
* curlで叩いても「Empty reply from server」
* 処理時間によっては問題なく成功する

（結構大きいファイルを変換する処理だったので、最初はアップロードまわりかと思ったがそうでもなく単純に長い処理をnginxが待ってくれない、という状態だった。）

nginx/error.logを参照した
* `*14 client closed connection while waiting for request, client: -.-.-.-, server: 0.0.0.0:3003` (ipはマスク済み)のエラーがある
* ↑のあとに`*4 open socket #25 left in connection 13`, `aborting`と出ている
* ネットで探してもよくみるupstream関連のエラーじゃなさそう
* debugレベル有効にしてもこれといった追加情報は得られず

## timeout伸ばす
ごくごく一般的なtimeout設定を伸ばした

``` conf
proxy_connect_timeout　　10;
proxy_send_timeout　　10;
proxy_read_timeout　　60;
```

例としてこのへん（実際はもっと長い時間にした）
が、解決せず（関係なさそうなtimeout項目は全部入れたが変わらず）

## upstreamとのkeep-alive
debugのログをみているとproxy headerに`Connection: close`とあった。
upstreamとでkeep-aliveになってないのか？と思い、以下を設定。

``` conf
ocation / {
proxy_http_version 1.1;
proxy_set_header Connection "";
.....
}
```

確かにヘッダは変わったが、事象は解決せず…


ここでunix　socketのタイムアウトがあったりするのか？など、TCPコネクションまわりを調べてみる。
確かに設定自体はあるのだが、社内アプリくらいなのでアクセスが大量にあるわけではない。
linux自体の設定も十分にあり、これも解決には至らず。

* 参考にさせていただきました）https://qiita.com/SenriQ/items/eaa44d961f6022284d36
* https://unix.stackexchange.com/questions/717952/tcp-timeouts-in-nginx

なお、fileを開く数の制限なども疑ったがこれも空振り

pumaの設定も漁ったがこれも同じく空振り…

## 犯人候補:systemd
ここでnginxのprocessを見ていて、pidが頻繁に変わっていることに気づいた。
worker processの再起動タイミングが早いのか？と思ったがnginxにその設定は特に無い（していない

サービスが定期的に再起動されてる？ということはsystemdまわり？
と思い、nginx serviceの定義を見てみる。

``` conf
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

あるはずの「Restart」の項目がない…
ということはsystemdでは再起動していない？


## 犯人はmonit
`service nginx status`で見ていると確かに１分くらいで再起動されていた。
ここで`journalctl -xe`でログを見てみろ、と天啓
見た。

``` log
openmediavault monit[1343]: 'nginx' failed protocol test [HTTP] at [127.0.0.1]:443 [TCP/IP TLS] -- HTTP>
openmediavault monit[1343]: 'nginx' trying to restart
openmediavault monit[1343]: 'nginx' stop: '/bin/systemctl stop nginx'
openmediavault systemd[1]: Stopping A high performance web server and a reverse proxy server...
Subject: A stop job for unit nginx.service has begun execution
Defined-By: systemd
Support: https://www.debian.org/support

A stop job for unit nginx.service has begun execution.

The job identifier is 2826996.
```

あった…
どうやらmonitがHTTPの疎通に失敗して再起動をかけていた様子。
確かにサーバー上でlocalhostにcurl打ったら403になる。

ここで思い出す。nginxの海外IPアクセス制限を入れたことを。
自分自身のipもブロックしていたので、monitの監視に失敗してnginxを再起動させていた。

## ということで真犯人は自分だった
教訓
* もともと入ってるservice使わずにおとなしくDocker使う
* トラブルはjounalctl見るのが一番早い
VirtualBoxマネージャーが起動しなくなった場合の対処法

# 目次

[1. はじめに](#1-はじめに)
[2. エラーの内容](#2-エラーの内容)
[3. エラーの原因](#3-エラーの原因)
[4. 直し方](#4-直し方)
[5. おわりに](#5-おわりに)

# 1. はじめに
VirtualBoxを実行してもVirtualBoxマネージャーのGUIが表示されない不具合が発生したので忘備録として記載します。

# 2. エラーの内容
VirtualBoxを実行してもバックグラウンド処理が行われるだけで一切起動しません。この状態でシャットダウンや再起動をするとプロセスが起動中にはなっているため強制終了の確認画面になります。
Virtual Boxの再インストールやコア分離の無効化、Hyper-Vの無効化を行っても改善しませんでした。

# 3. エラーの原因

**ゲームのコントローラーを有線接続していた**

リンク先の記事にあるようにVirtual Boxは起動時にUSBデバイスをチェックしますが、ゲームのコントローラーには対応できず無限ループしているようです。

# 4. 直し方

**ゲームコントローラーの有線接続を解除する**

単純ですがこれに気付かず、だいぶ時間を浪費しました。

# 5. おわりに

本記事作成に当たり、下記のリンク先の記事を参考にしました。リンク先の記事では詳細が記載されていてわかりやすいです。
参考にしてください。

[Virtualbox Manager (GUI)が起動しないバグと解決方法](https://zenn.dev/tbistr/articles/virtualbox-gui-bug-repost)

[virtualbox.org End user forums for VirtualBox](https://forums.virtualbox.org/viewtopic.php?t=97261)
XAIの大局説明/局所説明と活用方法をまとめる

# はじめに
説明可能なAI(Explainable AI, XAI)とは、AIが出した答えや意思決定プロセスに対して、人間が理解しやすい形で説明する技術やプロセスのことをいう。

例えば、画像認識の分野ではAIモデルが画像のどの部分に着目して予測を出しているのか可視化する技術が存在する。以下の例では、AIが画像を見て犬と判断する場合は犬の顔に着目していることが読み取れる。

![スクリーンショット 2024-03-06 120956.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324032/003457c6-ea7c-e997-9a2a-410a1d35bae3.png)
※ [1]のfig.1より一部抜粋


今回はXAIの説明範囲や活用方法などXAIの概要についてまとめる。

# XAIの説明範囲
XAIによるAIの説明は`大局説明`と`局所説明`に大別することができる。それぞれについて説明する。

## 大局説明とは
大局説明とは、**AIモデル全体の振る舞いや決定ロジックを理解するための説明**である。AIモデルの判断ロジックを可視化したり、特徴量ごとの予測に対する重要性を算出する技術は大局説明に当たる。

例えば、決定木は意志決定のプロセスを可視化することができるため大局説明をしているといえる。

![スクリーンショット 2024-03-06 123620.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324032/9f995049-6463-8778-daff-c906235467e6.png)

また、LASSO回帰では回帰式において特徴量に乗じる係数の値によって予測に対する特徴量の重要度を評価することもできる。そのため、LASSO回帰でも大局的説明ができると考えることができる。

このように大局的説明ではモデルの全体的な振る舞いを把握することができる。

### 大局説明の活用方法
大局説明の活用方法は以下が挙げられる。
* モデルの改善と最適化
* 透明性の確保

#### モデルの改善と最適化
AIモデルに重要な特徴量を把握することで、不要または誤解を招く特徴量を排除することができる。これにより、モデルのパフォーマンスを向上させるとともに、過学習を防ぐことが可能になる。

#### 透明性の確保
AIモデルがどのような特徴量やパターンに基づいて決定を下すのかを理解することで、意思決定プロセスの透明性を確保する。

例えば、科学研究にAIを活用する場合、AIの意思決定プロセスを理解することで、人間が実験計画を立てる時に役立てることができる。意思決定をサポートするAIシステムでは大局説明は特に重要である。

## 局所説明とは
局所説明とは、**与えられる1つの事例(入力データ)に対する予測過程を説明**することである。局所説明は特定の予測に焦点を当てて、その個別の事例についてAIモデルがどのようにして結論に至ったのかを説明する。

例えば、上述のAIモデルが画像のどこに着目しているかを可視化する技術(Grad-CAM)は局所説明に当たる。
![スクリーンショット 2024-03-06 120956.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324032/40cdac7e-8fda-7925-8e6e-33ac737f7022.png)
※ [1]のfig.1より一部抜粋

このように各々の予測に対して、AIがどう判断をしたのかの説明が局所説明である。

### 局所説明の活用方法
局所説明の活用方法は以下が挙げられる。
* 予測の妥当性検証
* 意図とは異なる学習の見直し

#### 予測の妥当性検証
先に挙げたGrad-CAMの事例では犬の顔に焦点を当てて"犬"と予測をしているため信頼できる予測と考えることができる。


一方で、犬とは関係のない領域を着目して"犬"と出力していた場合、たとえ予測が当たっていたとしても予測の信頼性は低いと判断できる。

このように局所説明を行うことによって、予測が妥当かどうかを検証することが可能となる。

#### 意図とは異なる学習の見直し
局所説明を応用することで、AIモデルの学習が適切に行われたかどうかを検証することができる。

例えば、上述のGrad-CAMの例で、窓を見て犬と判断していたのであれば、訓練データに犬と窓がセットになった画像が多すぎるのではと推測できる。

これにより、学習データを見直すことができる。このように局所説明は予測の妥当性だけなく、学習の妥当性についても示唆を得ることができる。

# 敵対性攻撃に対するXAIの活用
AIについて懸念されている問題の1つに敵対性攻撃がある。例えば、パンダの画像にノイズとなるデータを加えることで、テナガザルと誤認識する事例が報告されている[3]。

![スクリーンショット 2024-03-06 132358.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324032/909ea2f1-9887-4622-986f-33558249719c.png)
※ [3]のfig.1から抜粋

パンタの画像であればそこまで問題ないかもしれないが、自動運転の分野で画像を誤認識をするように攻撃されたら安全性に大きな懸念がでる。

XAIを活用することで、敵対的に操作された個々の事例の理解や、AIモデルの意思決定プロセスの妥当性を理解することで敵対性攻撃を検知できる可能性がある。

このようにXAIは敵対性攻撃の防御戦略の一部として有効に機能する可能性がある。

# おわりに
今回はXAIの概要について記載した。具体的なXAIの手法については次回以降の記事で少しずつ執筆していこうと思う。
何か間違いがあれば、コメントで気軽にご指摘ください。


# 参考文献
[1] Ramprasaath R. Selvaraju, _et al._; Proceedings of the IEEE International Conference on Computer Vision (ICCV), 2017, pp. 618-626

[2] [XAI(説明可能なAI)--そのとき人工知能はどう考えたのか?](https://amzn.to/4cHP2bA)

[3] https://arxiv.org/pdf/1412.6572.pdf

# 関連記事
https://qiita.com/sasshi_i/items/71189dc3a5c637fda3a4

https://qiita.com/sasshi_i/items/2af0a76b95176437f923
[備忘録] AWSのそんな話聞いてない

# 概要

AWSを触って大事そうなことを殴り書く予定

# 同一VPC内でLambdaから別のLambdaを呼び出す

VPC内だから特別記述することはないと思っていたら、どうやらLambdaのエンドポイントをVPCに設定しないといけないらしい。

CDKで定義している場合は以下のように定義する。

```typescript
    vpc.addInterfaceEndpoint('LambdaEndpoint', {
      service: ec2.InterfaceVpcEndpointAwsService.LAMBDA,
      subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
    });
```

# ランタイムバージョン更新が自動だとライブラリが動かなくなることがある。

ランタイムのメジャーバージョン自体は以下のように指定していても、マイナーなランタイムパッチとセキュリティパッチが自動更新される。

```typescript
new lambda.Function(this, 'lambda', {
    runtime: lambda.Runtime.PYTHON_3_9,
});
```

なので以下のような欄タイムバージョン更新を固定または指定する。
※`lambdaRuntimeVersionArn` はcdk.jsonで定義。

```diff
+ const lambdaRuntimeVersionArn  = this.node.tryGetContext('lambdaRuntimeVersionArn');
  
  new lambda.Function(this, 'lambda', {
+     runtimeManagementMode: lambda.RuntimeManagementMode.manual(lambdaRuntimeVersionArn),
      runtime: lambda.Runtime.PYTHON_3_9,
  });
```

詳しくはこちら

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.RuntimeManagementMode.html#class-runtimemanagementmode
項目として運用しているメモフィールドを拡張メモとしてセットする


+ [Migrate notes fields to ContentNote records](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007ZBlVg)

拡張メモもContentDocumentと同じでContentDocumentLinkで対象のオブジェクトと関連付けないといけないので、データローダなどではできなくはないけど、面倒だろう。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/232b5ab2-d7a5-f492-685f-e066152eb665.png)


とりあえず、１レコードずつ処理する簡単なバージョンをさくっと作ってみた。

```Java
public class BAT_Create_ContentNote implements Database.Batchable<sObject> {

    public Database.QueryLocator start(Database.BatchableContext BC) {
        // collect the batches of records or objects to be passed to execute
        String query = 'Select Id,Name,Note1__c,Note2__c,Note3__c From Opportunity where Id =\'0066D000003595SQAQ\'';
        return Database.getQueryLocator(query);
    }

    public void execute(Database.BatchableContext BC, List<Opportunity> oppList) {
        System.debug('******** oppList ' + oppList);
        List<ContentDocumentLink> cdlinkList = new List<ContentDocumentLink>();
        for (Opportunity o : oppList){
            if(o.Note1__c != null ){
                ContentNote cn = new ContentNote();
                cn.Title = 'test1';
                String body = o.Note1__c;
                cn.Content = Blob.valueOf(body.escapeHTML4());
                insert(cn);
                ContentDocumentLink cdl = new ContentDocumentLink(); 
                cdl.LinkedEntityId = o.id;
                cdl.ShareType = 'V';
                //ContentVersion cv2 = cvMap.get(cv.Id);
                //Id cdId = cv2.ContentDocumentId;
                cdl.ContentDocumentId = cn.Id;
                cdlinkList.add(cdl);
            }
            if(o.Note2__c != null ){
                ContentNote cn = new ContentNote();
                cn.Title = 'test1';
                String body = o.Note2__c;
                cn.Content = Blob.valueOf(body.escapeHTML4());
                insert(cn);
                ContentDocumentLink cdl = new ContentDocumentLink(); 
                cdl.LinkedEntityId = o.id;
                cdl.ShareType = 'V';
                //ContentVersion cv2 = cvMap.get(cv.Id);
                //Id cdId = cv2.ContentDocumentId;
                cdl.ContentDocumentId = cn.Id;
                cdlinkList.add(cdl);
            }

            if(o.Note3__c != null ){
                ContentNote cn = new ContentNote();
                cn.Title = 'test1';
                String body = o.Note3__c;
                cn.Content = Blob.valueOf(body.escapeHTML4());
                insert(cn);
                ContentDocumentLink cdl = new ContentDocumentLink(); 
                cdl.LinkedEntityId = o.id;
                cdl.ShareType = 'V';
                //ContentVersion cv2 = cvMap.get(cv.Id);
                //Id cdId = cv2.ContentDocumentId;
                cdl.ContentDocumentId = cn.Id;
                cdlinkList.add(cdl);
            }
        }

        try {       
            if(cdlinkList.size() > 0) insert cdlinkList;
        } catch(Exception e) {
            System.debug(e);
        }

    }  
    public void finish(Database.BatchableContext BC) {
    }
}
```
【Git・VSCode】VSCodeのソース管理が機能しなくなった時の対処

VSCodeで開発中に「そろそろコミットしとこうかー」とソース管理からコミットしようと確認したところ、変更あるはずなのにいつの間にか更新の認識がされていないことに気がつきました。こちらの復旧方法をいろいろ調べて対処したのでまとめておきます。

　　　　　　　　　　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3605750/001b48cc-8d37-6bed-d20a-71dfc5f4f790.png)
　　　　　　　　　　*保存してるのにコードの選択肢が出てこない*

## 結論: 別でリモートリポジトリをクローンして.gitファイルをcpする

調べた結果これが一番手っ取り早そうでした。

## 作業

任意の場所に新たにリモートリポジトリからクローンをする

```bash
git clone <remote-repo-url>
```

新たにクローンしたリポジトリから.gitファイルを元のリポジトリへコピーする
私の場合はssh先に元リポジトリがあるので scpコマンドで以下のように実行

```bash
scp -r [ローカルファイルのパス] [ユーザー名]@[リモートサーバーのアドレス]:[リモート側のディレクトリパス]
```

この時点でコード管理が復活します

ssh先のターミナルでnameとemailを再設定

```bash
git config user.name [お名前]
git config user.email [メールアドレス]
```

これでコミットもプッシュもできるようになりました。
STM32C011で周波数を測って遊ぶ。0,1,2,...10KHzぐらいまで?。(STM32C011J4M7)(IN DC5V?,DC3.3V))


x DC5V,DC3V以外の測定対象をつなぐとパソコンが壊れます。
x コイルのあるものでサーボ等をつなぐとパソコンが壊れます。
x カプラ等で安全対策を取ってください。
x 特に家庭用AC100V電源、200V,配電用6600V,DC1500V,AC30000Vは、絶対にだめ

目的
周波数を測って遊ぶ

結果

![o_cop982.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/b8739ee6-0fdc-f167-9132-ad85c7d21ac2.jpeg)


プログラム


```c++



//GPIO_pulseIn_Frequency_C011_1


//初期化
void setup() {

  delay(3000); //do not delete

  //シリアルの初期化
  Serial.setTx(PA8);
  Serial.setHalfDuplex();
  Serial.begin(9600);

  pinMode(PA12, INPUT);//pulseInのピン

} //setup


//1 PB7 (14)
//2
//3
//4 
//
//8 PA14 (12)
//7 PA13 (11)
//6 PA12 (10)
//5 PA8  (8)

//メインループ
void loop() {

  //パルスの読み取り
  unsigned long duration; //読み取り値
  duration =  pulseIn( PA12 , HIGH , 1000000);  //C011 PA12
  delay(1);
  duration += pulseIn( PA12 , LOW ,  1000000);  //C011 PA12
  //duration=0;//debug
  //duration=1000000+1000000;//debug
  if(duration > 1000000) {duration = 1000001;}
  if(duration <= 0) {duration = -1;}

  //周波数をHzで出力
  Serial.printf("%8d Hz\r\n",1000000/((int)duration));

  delay(1000);//1秒待つ
  
} //loop



```
「Struts」 ⇒「Spring Framework」に移行されると聞いて

## この記事を書こうと思った経緯
私が今いるプロジェクトでは使用されているフレームワークとして
Struts(正確にはStrutsベースのオリジナルフレームワーク）が使われているのですが、
後々にシステムをマイグレーションすることになりました。
そこで選ばれたフレームワークがSpring Frameworkでしたので、
いい機会だと思い、それぞれの特徴を調べることにしてみました。
## 各フレームワークの特徴
<dt>■Struts</dt>
・MVCモデルが採用されている<br>
・2001年に初版リリースされており、歴史が長い<br>
・短期間での開発や初心者にも取り組みやすい<br>
・Javaのコードの代わりに利用できる専用のタグライブラリを設けており、<br>
　JSPの記述を統一し可読性を上げることができる<br>
・過去に脆弱性を指摘されている。（Struts2ではパッチで対策済み）<br>
・Struts2とは互換性はない。
<dt>■Spring Framework</dt>
・2003年に初版がリリースされている<br>
・AOP（Aspect Orientation Programming）によりメンテナンスがしやすい<br>
・DI（Dependency Injection）により変更がしやすい<br>
・ORM（Object-Relational Mapping）機能がある<br>
・テストを容易に実施可能<br>
・学習コストが高い<br>
・アプリケーションのメモリ使用量が増加する可能性がある

ここに記載したものは一部にはなりますが、
StrutsはMVCモデルを採用しアクションベースのアプローチを取るのに対して、
Spring FrameworkはDIでアノテーションベースのアプローチを取ることが大きな特徴にも思えます。

## シェアについて
公開されている情報の中での状況になりますが、
<dt>Spring Framework　＞　Struts</dt>
といったシェアとなっております。<br>
※2021年7月時点の案件数ではSpringが4577件、Strutsが1073件となっておりました。

しかしながら、
この2つは他のJavaフレームワークよりもかなり案件数が多いことから、
Javaを学習する際は両方を並行して学習するのがよいと思われます。
（私が前いたプロジェクトではSpring Frameworkでした）

## Struts→Spring Frameworkに移行するとどうなるのか？
個人的な観点では以下が気になっております。
- 学習コストがかかる
　⇒全員がSpring経験者でない場合、各個人がフレームワークについて学習コストがかかります。
- AOPやDIのおかげで生産性が上がる。
　⇒開発チーム・運用保守チームとも助かります。
- 運用保守としては調査や障害対応に関しては最初に時間がかかる可能性がある
　⇒しかし、ある程度知識が習得できればSpringの特徴上、運用保守作業がしやすくなります。
- 移行後、安定するまでは思わぬバグや障害が出る可能性がある。
　⇒移行直後は稼働が高くなりそうですが、良い経験を積むこともできそうです。

## 所感
私は運用保守チームに所属しているため、
移行に関係する作業は直接行わない可能性がありますが
事前の調査や移行後の作業ではよい経験を積めるのではと感じております。
また、今いるプロジェクトから変わって別のプロジェクトに参画することになった時、
Spring Frameworkの経験と知識があることで選べる案件が増えそうだなと思いました。
Spring Frameworkの学習をこれからも進めていこうと思います。
【p5.js】p5.soundをtypescriptで使う

# 方法
- p5.soundを直接importすると型が取れます
- global.jsでp5を登録することで、p5.soundを使えるようになります
```typescript:src/index.ts
import p5 from "p5";
import "p5/lib/addons/p5.sound";
import './global.js';

const sketch = (p: p5) => {
  let sound: p5.SoundFile;

  p.preload = () => {
    sound = p.loadSound('../assets/demo.mp3');
  };

  p.setup = () => {
    p.createCanvas(800, 600);
    sound.loop();
  };

  p.draw = () => {
    // なにか書く
  };
};

new p5(sketch);
```

```javascript:src/global.js
import p5 from "p5";
window.p5 = p5;
```

# 参考
https://stackblitz.com/edit/p5-sound-typescript-0001-7erayj
Chromebookのバッテリーが急に減る原因

# 概要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587871/c993e4af-afb8-2542-8b78-cd83fa05c83f.png)
Chromebookを使っていると時々出合う、バッテリーが突然なくなる現象の原因を考察してみた。

:::note warn
**「負荷のかかる作業のせいで減りが速い」** ということではなく、 **「直前まで充分にあったバッテリー残量が、数分でほとんどなくなる」** という現象のことである。
:::


# 背景
学校で生徒がChromebookを使っていると、しばしば次のような声を聞く。

- さっきまでは50%あったのに、いきなり電源が切れた
- 朝は70%あったのに、一気に2%に減った
- バッテリーが突然なくなった


ウェブで検索すると、負荷のかかる作業によってバッテリーの減りが速い現象はたくさんヒットするが、この件のように急に減ることについて書かれた記事は発見できなかった。

バッテリーが劣化しているならまだしも、ほぼ新品のChromebookでこんなことがあるのだろうか。この現象の原因を探るため、我々調査隊はアマゾンの奥地へと向かった――。


# 原因の予想

## 予想
画面を閉じたまま長時間放置されたタブレットのバッテリー残量が遅れて更新され、一気に減ったように見えたのではないか。

## イメージ
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587871/100bc740-5348-b93e-5796-07a7830b0822.png">

## ありそうなモデルケース
1. 💬充電する（残り100%🔋）
2. 💬いくつかの授業で使う（残り70%🔋）
3. 🤔（明日もまだ使えるだろう……）
4. 💬画面を閉じる（残り70%🔋）
5. 💬一晩放置する
6. 💬朝、バッテリー残量を確認する（残量5%🪫）
   → 表示はまだ70%
7. 😊（まだ70%ある）
8. 💬授業で少し使う（残量5%🪫）
   → 表示が更新される
9. 😱（急に減った！）

## 詳しい説明
バッテリー残量の表示は、OSの処理の中では優先度が低そうである。他の処理と比べて、更新頻度も低くてよさそうだ。例えば、ウィンドウの描画や内部時計の更新が5分ごとだったら、コンピュータは使い物にならない。しかし、バッテリーの残量表示はそれくらいでも大きな問題にはならない。

また、もしかしたらバッテリーの残量を測定するのには時間がかかるのかもしれない。特に、使用できる残り時間の予測は、直近のバッテリーの減り具合を測定しないと算出が難しそうだ。

そのため、特にスリープ（もしくは単に画面の電源オフ）からの復帰直後は、バッテリー残量表示の更新はすぐには行われないのではないだろうか。

そこで、バッテリーの残量を確認したときにまだ充分あると思っても、実際にはもうほとんどなく、使用再開の直後に切れてしまうのではないだろうか。

Chromebookのバッテリーが10時間程度持つとして、70%の状態から省エネ状態で一晩経てば、ちょうどほとんどない状態にまで減りそうなもので、感覚的にも納得できる。

# 対策

:::note info
**対策**
- 毎晩充電する
- 長時間使わないときは電源を切る（シャットダウンする）
:::

**電源を切る方法**

https://support.google.com/chromebook/answer/3420029?hl=ja

公立学校での一人一台端末の取り組みは、毎日持ち帰って家でも同じ環境で学習することが想定されている。また、Chromebookのバッテリーは持ちがよいとはいえ、動画の再生やビデオ通話などの負荷の高い作業では減りが速くなることもある。だから、基本的に毎日持ち帰って家でも使い、充電して持ってくるべきである（方針は学校によって違うので要確認）。
pythonで並列処理する際のシード値設定

# はじめに
pythonで疑似的にデータを大量に生成する際、かなり時間がかかったので、高速化するために並列処理をしたくなりました。  
Pythonでは、並列処理をするためのライブラリがいくつかありますが、実験再現性担保のため、ランダムのシード値を固定し並列処理で生成されるデータの固定化を行う必要がありました。ですが、シード値を用いた並列処理関連の記事が見つからなかったので、忘れないようにここにまとめておきます。

# 対象者
- pythonでCPUの並列処理したい人
- シード値使った並列処理したい人


# random モジュールのシード値の固定化
pythonでは、以下のようにランダムのシード値を固定できます。
```py
import random
random.seed(123)
```
逐次処理をする際は、これで十分です。しかし並列処理をする際は、ランダムモジュールの呼び出しタイミングがバラバラになるので、これだけでは不十分でした。  
どうやって解決できるかなーと色々調べてみたら、[このサイト](https://zenn.dev/ganariya/articles/python-random-independent)でランダムモジュールのインスタンスを分けることができる、ということがわかりました！

## ランダムモジュールのインスタンス化
実際に、こんな感じでランダムモジュールのインスタンスを立てることができるらしいです。インスタンス化さえしてしまえば、通常のランダムモジュールの使い方と同じようなので楽ですね。

```py
from random import Random
random_instance_123 = Random(123) 
random_instance_456 = Random(456) 

print(random_instance_123.random()) # >> 0.052363598850944326
print(random_instance_456.random()) # >> 0.7482025358782363
```



# シード値を考慮した並列処理プログラム例
上記のやつを用いてプログラムを書いてみます。並列処理をする際、各プロセスごとでインスタンス化してしまえば、他のプロセスからの干渉を防げそうです。
ここでは例として、ランダムで作成した配列をnumpy配列で保存するプログラム例を掲載しておきます。
## ライブラリ
- tqdm
- numpy

## コード

```py
from random import Random
import multiprocessing
import numpy as np
from tqdm import tqdm


class MultiDatasetCreator:
    def __init__(self, number):
        self.number = number
        self.random = Random(number)

    def create(self, size):
        # randomモジュールを使用してsize個の乱数リストを生成
        self.data = []
        for _ in range(size):
            self.data.append(self.random.random())
        self.data = np.array(self.data)
        return

    def save(self):
        file_name = f"dataset/{self.number}.npy"
        np.save(file_name, self.data)


def generate_dataset(number):
    creator = MultiDatasetCreator(number)
    creator.create(10)
    creator.save()


if __name__ == "__main__":
    num_dataset = 100  # 作成したいデータセットの数
    num_process = multiprocessing.cpu_count() # 使用するCPUの数をカウント
    random_seed_lst = [
        i for i in range(num_dataset)
    ]  # シード値のリストをあらかじめ作成しておく
    
    # 並列処理
    pool = multiprocessing.Pool(num_process)
    with tqdm(total=num_dataset) as pbar:
        for _ in pool.imap_unordered(generate_dataset, random_seed_lst):
            pbar.update(1)
    pool.close()
    pool.join()
```
ランダムモジュールのインスタンスを別々で立ち上げることで、他のプロセスによる干渉をなくし、実験再現性の担保をすることができました。
このおかげでかなり高速化をすることができました👏

## 注意
今回のランダムのインスタンス化、Python標準ライブラリのrandomモジュールではできたのですが、numpyの方ではそれっぽいのが(私が見る限り)見つかりませんでした。なのでnumpyの乱数生成を実装したい際は、randomモジュールに置き換えて実装するしかなさそうです。(numpyの方は乱数生成する際の関数が多く実装されているので便利なんですけどね..)

numpyでも早く実装されるといいですね
【JavaScript】async / await（fetch api）でQiita apiから記事情報を取得する方法【非同期処理】

javascriptで外部データを取得する方法が知りたいと思い、練習でQiita apiから記事情報を取得してみようと思った。
会社のテックリードの方に **axios** が便利と勧められたが、その前に **async / await** を使ったやり方も知っておきたいなと思ったので備忘録です。

## サンプル
とりあえず以下がコードの例。
Qiita記事でjavascriptのタグが付いた記事と取得したデータからいいねが1つ以上ついた記事のURLを抽出しています。
```diff_javascript
async function getQiitaArticle(){
    //①自身のアクセストークン
    const MyToken = 'XXXXX';
    //②記事を100件取得する
    const QiitaURL = 'https://qiita.com/api/v2/tags/javascript/items?per_page=100';
    const FetchOptions = {
        method: 'GET',
        headers: {
            Authorization: `Bearer ${MyToken}`
        },
    };
    const ApiResponse = await fetch(QiitaURL, FetchOptions);
    if(ApiResponse.ok){
        const QiitaArticle = await ApiResponse.json();
        //取得した素のデータ。
        console.log(QiitaArticle)
        // いいね数が1個もしくは1個以上の記事URLを抽出する
         for(let i=0;i < QiitaArticle.length; i++){
            if(QiitaArticle[i].likes_count >= 1){
                console.log(QiitaArticle[i].url);
            }
        }
    } else {
        console.log('記事取得できなかった時のエラーを書く');
    }
}
getQiitaArticle();
```
今回はデータ取得にフォーカスするのでQiita apiのアクセストークン認証など仕様は省きますが「 **?per_page=XX** 」を設定することでデフォルトで取得する数を決められます。
※パラメーター自体を省くと20件取得されます。

chromeの開発ツールのconsole欄を見てみると素で取得したデータと抽出したURLが確認できると思います。

## 解説
まず非同期関数として宣言するために **async** を先頭に書きます。
```Javascript
async function getQiitaArticle(){};
```
apiのURLを変数で宣言したと後にapi取得の記述を簡易化するためにオプションで以下を宣言しておきます。
```javascript
const FetchOptions = {}
```
ここでGETやPOSTなどリクエストの指定とapi認証をするための方法をまとめておきます。
```javascript
const ApiResponse = await fetch(QiitaURL, FetchOptions);
```
apiのURLとオプションをfetchでリクエストします。
リクエストが正常に行えたら指定した分のデータ数と条件に沿ったデータを返してくれます。
```javascript
if(ApiResponse.ok){}
```
「**.ok**」は読んで字のごとくリクエストが通った時のプロパティです。
直感的で分かりやすいですね。
## 所感
思ってたよりシンプルな実装でデータ取得が出来ました。
apiはサービスによって仕様が異なるので、そこは要確認ですがデータを取得する方法自体はそこまで複雑ではないなという印象です。
## 参考
[Qiitaの記事､JavaScriptのasync/awaitを使って取得してみる](https://okalog.info/async-await/)
[async/await 入門（JavaScript）](https://qiita.com/soarflat/items/1a9613e023200bbebcb3)

## 2024年3月7日追記
サンプルコード内のfor文でiがグローバル変数:fearful:になっていたのを修正しました。
```diff_javascript
- for(i=0;i < QiitaArticle.length; i++){
+ for(let i=0;i < QiitaArticle.length; i++){
```
ご指摘ありがとうございました:pray:
Openshift環境で各ノード用ignitionファイルの有効期限についての備忘録

## はじめに
この記事は、インフラエンジニアの自身がOpenshiftクラスタを構成する際に
引っかかってしまったIgnition Configファイルの有効期限についてまとめた備忘録です。
## 対象者
Openshift環境の構築をされている方・学んでいる方向けです。
- サーバエンジニア
- インフラエンジニア
## 結論
Openshiftクラスタを構成するIgnition Configの有効期限が24時間のため、
再生成+再アップロードが必要でした。


## 詳細
openshiftでは、Bootstrapノード、Masterノード、Infraノード、Workerノードを作成します。
ざっくりとした、各ノードの作り方としては下記に記す。
1、yamlファイルからマニュフェストファイルを生成
2、マニュフェストファイルから各ノード用のIgnition Configファイルを生成
3、HTTPサーバへアップロード
4、それぞれの各ノードを2で生成した各ノード用のIgnition Configを読み込ませて起動

上記の作業を行い、各ノードを作成するが
Iginion Configファイルの有効期限が24時間だと知らず
上記の作業を複数日程で作業したため証明書が期限切れとなり、認証が通らなかった。
## おわりに
今回のopenshiftクラスタ作成のためのIgnition Configの有効期限について
詳しく調べたところ認証が通らない理由としては、X509形式の証明書が含まれているため
24時間の有効期限になっているとのことでした。
また、有効期限が24時間過ぎてもノードが起動するが認証が通らないようになっていることが分かった。
勝手な話だが、生成する際にわかりやすく有効期限を出力してほしいと思った。
Ruby on Rails: 未来の予約に対するユニーク制約の実装方法

## 背景

Webアプリケーションにおいて、ユーザーが特定の条件下でのみデータを作成できるように制限する必要がある場合がよくあります。特に、予約システムにおいては、一人のユーザーが一つの企業に対して、未来に一つの予約しか持てないようにすることが望ましいケースがあります。

この記事では、Ruby on Railsを使用して、このようなビジネスルールを実装する方法を解説します。

## モデルのバリデーション設定

カスタムバリデーションを`Reservation`モデルに追加することで、上記のルールを適用します。

```ruby
class Reservation < ApplicationRecord
  belongs_to :customer
  belongs_to :company

  validate :unique_future_reservation_per_customer_and_company

  private

  def unique_future_reservation_per_customer_and_company
    return if date.blank? || customer_id.blank? || company_id.blank?

    future_reservations = Reservation.where(customer_id: customer_id, company_id: company_id).where('date >= ?', Date.today)

    future_reservations = future_reservations.where(deleted_at: nil)

    future_reservations = future_reservations.where.not(id: id) unless new_record?

    if future_reservations.exists?
      errors.add(:base, "You can only have one future reservation per company.")
    end
  end
end
```

## 解説

このカスタムバリデーションメソッドは、以下のステップで機能します：

1. **早期リターン**: `date`、`customer_id`、または`company_id`が未設定の場合は、バリデーションをスキップします。
2. **未来の予約の検索**: 同一顧客、同一企業に対して、今日以降の日付で設定された未来の予約を検索します。
3. **論理削除された予約の除外**: `deleted_at`が`nil`である、つまり論理削除されていない予約のみを対象にします。
4. **自身の除外**: 既存の予約を編集している場合は、自身を検索結果から除外します。
5. **エラーの追加**: 上記条件に一致する予約が存在する場合は、エラーメッセージを追加します。

### 参考記事

https://qiita.com/KM9973/items/e1839d21252d36551ee1

https://qiita.com/KM9973/items/67f31d9a186e18b4e1e2
## ビューとコントローラー

このバリデーションは、モデルレベルで完結しているため、ビューやコントローラーに特別な変更は必要ありません。ただし、ユーザーに対して明確なフィードバックを提供するために、フォームにエラーメッセージを表示するロジックを確実に含めることが重要です。

## まとめ

Railsにおけるカスタムバリデーションを利用することで、複雑なビジネスルールも柔軟に実装することができます。今回の事例では、顧客が未来に一つの企業に対して一つの予約しか持てないようにする制約を、簡潔に実現することができました。この方法は、アプリケーションの要件に応じて様々な制約条件に応用可能です。
Dockerのvi(vim)が入っていなときにファイルの中身を確認する方法

## dockerコンテナ内でcat,lessコマンドを使用する
```
docker exec -it [コンテナ名またはID] cat /path/to/file
```
または
```
docker exec -it [コンテナ名またはID] less /path/to/file
```

## dockerのcpコマンドを使用して確認したいファイルをホストマシンにコピーする
```
docker cp [コンテナ名またはID]:/path/to/file /path/on/host
```
Cloud9 学習記録

AWS Cloud9は統合開発環境・IDEのこと
----------------------------

1.統合開発環境・IDEとは
----------------------------
プログラム開発ツールをブラウザ上で提供する開発環境のことで、具体的には以下機能などがあります
・ソースコードエディタ
・デバッガ
・ビルド
・プログラミング言語(JavaScript,Python,PHPなど)
https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/welcome.html

2.AWS Cloud9の仕組み
----------------------------
・ローカルコンピュータのウェブブラウザでAWS Cloud9 IDEを使用して、AWS Cloud9環境をコンピューティングリソースを作成し、そのサーバで操作する

3.AWS Cloud9環境
----------------------------
・作成したコードは実行環境に保存され、AWS CodeCommitまたはGitなどのリポジトリに保存することも可能

４.Cloud9環境を作成する
----------------------------
a ) AWS管理コンソールからナビゲーションバーにCloud9を入力し、Cloud9サービス画面へ移動する
※前提としてパブリックサブネットがあること

環境を作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/07fec3de-2d9a-332c-5d0c-fe0b4d091105.png)

b ) 新しいEC2インスタンス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/7bbe42e6-afd7-c0df-e3e8-c9cdd73764d1.png)
・インスタンスタイプは使用目的に応じて変更します
今回は、Terraformでいろいろ検証したいので、t2.microを選択します

・プラットフォーム
今回はAmazon Linux 2023が推奨されているため、使用します
https://aws.amazon.com/jp/linux/amazon-linux-2023/faqs/

・タイムアウト
自動休止時間は30分で指定します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/2a055346-17fa-66dc-8028-1362a9f17774.png)

c ) ネットワーク設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/017bbd83-94e7-58fd-9aad-eb6dd5c1843d.png)

接続
・SSM
SSM Session Managerを利用すると、Cloud9とEC2の間にSSMを経由する構成となり、EC2インスタンスのインバウンド通信を閉じたまま使用できる

Cloud9上から以下のコマンドを入力することで、ssmセッションIDを確認することができる
```
ec2-user:~/environment $ ps -ef|grep ssm-session-worker|grep -v grep
```
system session manager
また、コンソール上からも確認することが可能
AWS Systems Manager > Session Manager
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/f08d5020-034f-f2db-786d-5a958837001c.png)
セッションIDが同じであることも確認できる

・SSH
Cloud9 と EC2インスタンスの間にsshのセッションを張ることで接続でき、セキュリティグループが自動的に作成される
インスタンス間のインバウンド通信は許可されるため、SSMに比べ汎用性はあるように見える

今回は、SSM等でセッション管理する必要もなければ、制約を設ける理由もないため、接続方法はSSHを選択する


・インターネットにアクセスできるVPCを選択します

d ) タグ
こちらに関しては、タグによる戦略等は特に考慮していないため、設定はなし

https://docs.aws.amazon.com/ja_jp/tag-editor/latest/userguide/tagging.html#tag-strategies

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/369e9222-3f04-d57d-c7fb-899b8479c92f.png)

https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/welcome.html

https://pikawaka.com/curriculums/programming-introduction/aws-cloud9
インクルード順序を変えるとBoostがコンパイルエラーになる

Boostを使ってコードを書いているときに遭遇したエラーです。

# 実行環境
- Visual Studio 2019 (cl.exe 19.29.30136)
- boost 1.84.0
- OpenSSL 3.0.0


# 再現コード
再現可能なコードは以下の通りです。
```c++:main.cpp
#include <boost/asio.hpp>

#include <openssl/ssl.h>

#include <windows.h>
#include <wincrypt.h>

#include <boost/asio/ssl.hpp>
```
```
D:\win\boost\boost_1_84_0\boost/asio/ssl/impl/rfc2818_verification.ipp(107): error C2065: 'name': 定義されていない識別子です。
D:\win\boost\boost_1_84_0\boost/asio/ssl/impl/rfc2818_verification.ipp(110): error C2065: 'name': 定義されていない識別子です。
D:\win\boost\boost_1_84_0\boost/asio/ssl/impl/rfc2818_verification.ipp(112): error C2065: 'name': 定義されていない識別子です。
```

不可解かもしれませんが、一つでもインクルードの順序が変わったり、コメントアウトしたりするとエラーが発生しなくなります。

# なぜ発生する？
なんとなくしか原因を把握できていないのですが、最初の`#include <boost/asio.hpp>`を削除すると、エラーが変化します。
```c++
#include <openssl/ssl.h>

#include <windows.h>
#include <wincrypt.h>

#include <boost/asio/ssl.hpp>
```
```
D:\win\boost\boost_1_84_0\boost/asio/detail/socket_types.hpp(24): fatal error C1189: #error:  WinSock.h has already been included
```
これはBoostが吐いたエラーで、推測するに`WinSock.h`があらかじめインクルードされている状況を想定していないと思われます。通常はこのチェックにより、問題の所在を明らかにするはずです。

しかし、最初の例ではこのインクルードチェックが行われていません。どういうことかというと、
```c++
#include <boost/asio.hpp>
// boost/asio/detail/socket_type.hppをインクルード、WinSock.hをチェック

#include <openssl/ssl.h>
#include <windows.h>
#include <wincrypt.h>
// WinSock.hなどなどをインクルード

#include <boost/asio/ssl.hpp>
// boost/asio/detail/socket_type.hppをインクルードするも、インクルードガードにより無視される
// -> WinSock.hのチェックをすり抜けてエラー
```
という流れになっていると思われます。

# 対策
今回はboost関連のインクルードを分散して書いてしまった結果、間に他のヘッダが入り込むことで不可解なエラーが発生してしまいました。Boost関連のヘッダをまとめてインクルードすれば、こういった問題は起こりません。

```c++:example1.cpp
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <openssl/ssl.h>
#include <windows.h>
#include <wincrypt.h>
```

```c++:example2.cpp
#include <openssl/ssl.h>
#include <windows.h>
#include <wincrypt.h>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
// errorになるが、#error: WinSock.h has already been included となるので解決できる
```

今回のコードはいささか恣意的にも見えるので、わざわざこんなコードを書こうとは思わないかもしれません。しかし、いろんなライブラリがそれぞれインクルードをした結果、このような構造になってしまうことはしばしばあると思います。

普遍的な対策としては、Boostのような複数のファイルからなるライブラリをインクルードするときに、それ専用のヘッダファイルを作ってしまうということです。
```c++:boost_wrapper.hpp
#ifndef MY_BOOST_WRAPPER_HEADER_INCLUDED
#define MY_BOOST_WRAPPER_HEADER_INCLUDED

#include <boost/asio.hpp>
// その他いろいろインクルード

#endif
```
```c++:lib.hpp
#include <boost_wrapper.hpp>
void my_some_func() {}
```
```c++:main.cpp
#include <boost_wrapper.hpp>
#include <lib.hpp>
int main(){}
```
複数のファイルからBoostなどをインクルードしても、きちんと順序を守ってインクルードされます。が、このような方法がいつもとれるとは限らないし、なにより非効率です。
(`#define USE_XXX`などインクルード前に記述が必要な場合などはwrapperを作った方がいいかもしれません)

他の対策としては、別々にコンパイルしてリンクする方法です。今回のケースでは、
- `boost/asio.hpp`に依存したコード
- `wincrypt.h`に依存したコード

の両方をinline展開したくてヘッダに書いてインクルードした結果、衝突が起こってしまいました。この二つを別々のオブジェクトとしてコンパイルした後、必要なインターフェースだけヘッダファイルに摘出してリンクすれば衝突を避けることができます。

# 所感
とはいえ、c++のプリプロセッサは基本的にグローバルスコープである以上、他のライブラリと何らかの要因で衝突する可能性は排除できません。何らかの実装ミスがない限り、こういったエラーが発生するのはごく稀なのであまり気にしなくてよいと思います。
Ruby on Rails: 未来の予約に一意性のバリデーションを適用する

## 概要

Ruby on Railsで予約システムを構築する際、特定のビジネスロジックを実装する必要がある場合があります。例えば、一人の顧客が一つの企業に対して、ある任意の1日につき作成できる予約は1つだけにする、といったケースです。さらに、この制限を未来の日付にのみ適用し、過去の日付には適用しないようにする必要があります。この記事では、そのような機能を実装する方法について説明します。

## バリデーションの設定

`Reservation`モデルに、顧客ID、企業ID、および日付の組み合わせが一意であることを保証するバリデーションを追加します。このバリデーションは、日付が今日以降である場合にのみ適用されます。

```ruby
class Reservation < ApplicationRecord
  validates :customer_id, uniqueness: {
    scope: [:company_id, :date],
    message: "can only make one reservation per company per day",
    if: :date_in_the_future?
  }

  private

  # dateがnilでなく、かつ今日以降の日付であることの確認
  def date_in_the_future?
    date && date >= Date.today
  end
end
```

## バリデーションの解説

- `validates`メソッドは、指定されたフィールドの値がユニークであることを保証します。この場合、`customer_id`フィールドがユニークである必要がありますが、`scope`オプションを使用して、このユニーク性が`company_id`と`date`フィールドの組み合わせに対してのみ適用されるように指定しています。
- `message`オプションでは、バリデーション違反が発生した場合に表示するカスタムエラーメッセージを設定します。
- `if: :date_in_the_future?`オプションにより、`date_in_the_future?`メソッドが`true`を返す場合にのみ、このバリデーションが適用されます。これにより、過去の日付に対してはこの一意性のバリデーションが適用されません。


## まとめ

上記の実装により、システムは未来の日付に対する予約を適切に管理できるようになります。1人の顧客が同じ企業に対して同日に複数の予約を行うことができないため、予約の重複を防ぐことができます。
Ruby on Rails: `new_record?`メソッドの活用（レシーバなし含む）

## はじめに
Active Recordのオブジェクトが新規作成されたものか、既存のデータベースのレコードから読み込まれたものかを判別する必要があったため調べてみました。
このような場合、`new_record?`メソッドが非常に役立ちます。本記事では、`new_record?`メソッドの基本的な使い方と、特定のコンテキストでのレシーバーなしでの使用について解説します。

## `new_record?`メソッドとは

`new_record?`はActive Recordのインスタンスメソッドであり、オブジェクトがデータベースにまだ保存されていない新規レコードである場合に`true`を返します。つまり、`new`メソッドや関連するビルダーメソッドによってオブジェクトが作成され、まだ`save`が呼び出されていない状態を示します。

## 主な使い方

```ruby
user = User.new(name: "Taro")
user.new_record?  # => true

user.save
user.new_record?  # => false
```

この例では、最初に`User`モデルの新規オブジェクトを作成した時点で`new_record?`は`true`を返します。これは、オブジェクトがまだデータベースに永続化されていないことを意味します。`save`メソッドを呼び出してオブジェクトがデータベースに保存されると、`new_record?`は`false`を返すようになります。

## レシーバーなしでの使用について

Active Recordのコールバックやバリデーションの定義内で`unless new_record?`のようにレシーバーなしで`new_record?`を使用することができます。これは、これらのコンテキスト内では現在のオブジェクト（`self`）が自動的にレシーバーとして扱われるためです。

```ruby
class Post < ApplicationRecord
  before_save :do_something_unless_new_record

  private

  def do_something_unless_new_record
    unless new_record?
      # 何らかの処理...
    end
  end
end
```

この例では、`before_save`コールバック内で`unless new_record?`が使用されています。これは`self.new_record?`の短縮形であり、現在の`Post`インスタンスが新規レコードでない場合に限り、特定の処理を実行することを意味します。

## まとめ

`new_record?`メソッドは、Active Recordのオブジェクトの状態を理解し、条件に応じた処理を実装する際に非常に便利です。コールバックやバリデーション内でレシーバーなしで使用できるのは、Rubyに設定されている、現在のコンテキスト内の`self`が暗黙のレシーバーとして機能するためです。

## 参考記事

https://qiita.com/KM9973/items/24d1abe21f41137810d7

https://qiita.com/KM9973/items/e1839d21252d36551ee1
【Ruby】問題集解答 Part5（paizaラーニング）

# カンマ区切りのN個のデータの入力 (paizaランク D 相当)
https://paiza.jp/works/mondai/stdin/stdin_comma_n
### 自分の解答
```ruby:practice.rb
num = gets.to_i

num -= 1

inputs = gets.chomp.split(",")

for i in 0..num
  puts inputs[i]
end
```
今回は最初に受け取った数値を活かすコードが書けた。
カンマ区切りでどれだけたくさん入力しても、最初に受け取った`num`分だけ出力する。
### ChatGPTの解答
理解しました。以下は、入力されたN個の文字列を入力として受け取り、順にN行でそのまま出力するRubyプログラムの例です。

```ruby
# Nの値を受け取る
n = gets.to_i

# N個の文字列をカンマ区切りで受け取り、配列に格納する
input_strings = gets.chomp.split(',')

# 配列の要素を順に出力する
input_strings.each do |string|
  puts string
end
```

このプログラムでは、最初に`gets.to_i`で標準入力から整数Nを受け取り、次に`gets.chomp.split(',')`でカンマ区切りの文字列を受け取り、それを配列に格納します。最後に、`each`メソッドを使用して配列の要素を順に出力しています。

### 感想
最初に受け取った数値を活かしている分、自分のコードの方が上手いように思えますが、ChatGPTの解答も仕様は満たしているため、優劣はないかな。
Power Appsを最新バージョンで使ってもらう方法

# 背景
Power Appsでアプリ更新時の↓通知を押さずに使い続ける人が多数出没。。。
![RefreshNotice.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/f648fa4f-5b76-ea04-7640-3e82f47c448e.jpeg)
強引に常に最新バージョンで使ってもらう方法考えてみました。
もっとスマートな方法ご存じの方おられたらご教授お願いします:bow_tone1:

# 方法
① まずアプリ内にバージョンを記載したラベルを配置。
![1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/ac5dd68b-b276-4f7c-3370-4fe203a0b702.jpeg)

② 次にSharepointリスト等のデータソースに↑と同じ文字列を持ったレコードを配置。
![2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/8e83463a-edf7-70ac-7e30-ef6f0b873a39.jpeg)

③ 初期表示スクリーンのOnVisibleに①=②だったら`false`、そうじゃないときは`true`となる変数を定義。
![3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/136282ac-7d8c-2705-733e-46ef1a390adf.jpeg)

④ 初期表示スクリーンの最前面に全面を覆うコンテナ等を配置して警告表示。そのVisibleプロパティに③の変数を設定する
![4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/e6664f72-bcc5-b535-25d0-fad82f7ba6ec.jpeg)

⑤ 最後に、アプリ更新時に①と②を同じ文字列にしてから発行すれば、旧バージョン使用者にだけ④の警告表示。

# 最後に
強制更新オプションみたいなの無いのかな・・・
[no.008]EC2 SWAP領域の設定

# 参考サイト
EC2インスタンスのSwap領域を作成する
https://qiita.com/nakamto/items/5e78e9caceeff6b9e2b4　(2020)

AWS EC2 インスタンスに swap 領域を作成する
https://zenn.dev/wsuzume/articles/3ef4b2d3caf203
→ほぼそのままでいけました。ありがとうございます！！


# 手順

# 1.事前の確認
free -h
sudo su
swapon -s 
→swap領域が確保されていない

#Swap 領域を作成するストレージのファイルシステムによって、作成方法が微妙に異なるため、確認
df -Th
df -Th /dev/xvda1
→今回はxfs

sudo su



# 2.SWAP領域の確保

#[ext4の場合の] Swap領域の確保
fallocate -l 2G /swapfile 


#[btrfsの場合の] Swap領域の確保
truncate -s 0 /swapfile
chattr +C /swapfile
fallocate -l 2G /swapfile


#[xfsの場合の] Swap領域の確保　（少し時間がかかる）
dd if=/dev/zero of=/swapfile bs=1M count=2048
 ｜
^[[O2048+0 レコード入力
2048+0 レコード出力
2147483648 バイト (2.1 GB) コピーされました、 30.5896 秒、 70.2 MB/秒
 ｜
 ｜補足
ーーーーーーーーーーーーーー
dd: ディスクやファイルなどのデータをコピーするためのコマンドです。
if=/dev/zero: 入力ファイルとして、/dev/zero デバイスを指定しています。
/dev/zero は無限のゼロバイトのストリームを提供する特殊なデバイスです。
つまり、これは無限のゼロバイトのデータを生成します。
of=/swapfile: 出力ファイルとして、/swapfile というファイルを指定しています。
/swapfile は通常、スワップ領域（仮想メモリ）を格納するために使用されます。
bs=1M: ブロックサイズを1メガバイトに設定しています。
これは、dd コマンドがデータを読み取る/書き込む際に使用するブロックのサイズです。
count=2048: ブロックの数を指定しており、ここでは2048となっています。
したがって、1メガバイトのブロックを2048個書き込むことになります。
つまり、このコマンドは、2GBのサイズのゼロで埋められた /swapfile という名前のファイルを作成します。
通常、これはスワップ領域（仮想メモリの一部）を作成するために使用されます
ーーーーーーーーーーーーーー


free -h
sudo su
swapon -s 
→まだSWAPは見えない




# 3.パーミッション設定

#Swap ファイルの権限は 644 または 600 が推奨。特に理由がなければ root 以外のユーザーにアクセスを許す必要はないので権限は 600
chmod 600 /swapfile
ll /swapfile


# 4.Swap領域の有効化 

#Swap領域の有効化 (Swap ファイルは swap ファイル用にフォーマットされている必要がある)
mkswap /swapfile
 |
スワップ空間バージョン 1 を設定します。サイズ = 2 GiB (2147479552 バイト)
ラベルはありません, UUID=606dd35e-e70a-4cf6-a86c-b3787883cc19

#swap領域の有効化
swapon /swapfile
 |
# SWAP領域が確認できる
free -h
swapon -s
　｜
ファイル名  タイプ  サイズ  使用済み  優先順位
/swapfile  file  2097148 0  -2


ーーーーーーーーーーーーーーーー
# 5'.（任意）ループバックデバイスとしてマウント
- ファイルをループバックデバイスとしてマウントすると、そのファイルをあたかもブロックデバイスであるかのように扱うことができます。
- 要するにファイルを論理ボリュームのように扱うことができます

#ループバックデバイスとして登録されているファイルを確認
losetup -l

#/swapfile を新規のループバックデバイスとしてマウント(名前は自動で割り当てられます)
losetup -f /swapfile

#確認
losetup -l

#swap 領域として有効化(※仮に、任意の名前がloop0だった場合。)
swapon /dev/loop0
ーーーーーーーーーーーーーーーー


# 5.SWAP領域の永続化

#起動時swapファイルをswap領域としてマウントする設定を記述(Swap 領域のマウントは再起動する度にリセットされてしまうので)
cd /etc
cp fstab fstab.yyyymmdd

#１行だけ追記（UUID=XXXXXXXXXX     /           xfs    defaults,noatime  1   1の下）
vi /etc/fstab
 |
/swapfile swap swap defaults 0 0
#/swapfile none swap defaults 0 0




# 6.再起動
reboot
free -h
→Swapのtotalが表示される


# 7.Swap 領域を無効化＆削除する 【必要な場面になったら】
- 必要な場面
	- インスタンスをアップグレードした場合などはswap 領域がいらなくなる場合や作り直したい場合がありえる。

#/etc/fstabの記述を削除 
vi /etc/fstab 

#swap 領域を無効化（ループデバイスで設定している場合は /swapfile を /dev/loop0 などで置き換えること）
swapoff -v /swapfile

#swap領域の削除
rm -f /swapfile
アクセルキャンプ復習動画　3/6 復習動画　コードでブロックを置いてみよう

```lua
--①名前をつける
local blockA = 
	Instance.new("Part", game.Workspace)


--②座標を決める
blockA.Position = Vector3.new(10, 10, 0)


--③大きさを決める
blockA.Size = Vector3.new(5, 5, 5)


--④アンカーを付ける
blockA.Anchored = true
```
file_pickerでファイルを一つ選択して読み込む

# はじめに
ファイルをインポートする処理を作りたく、ファイルを選択してやりたいなと思い調べたら、[file_picker](https://pub.dev/packages/file_picker)にたどり着きました。
Googleドライブからも読み込めるのでまさにやりたい子ができます。
なお、導入時に注意点があるようなので、[GithubのSetup](https://github.com/miguelpruivo/flutter_file_picker/wiki/Setup)を見ておいた方がよさそう。

今回は、ファイルを一つ選んで読み込む処理を作井精しました。

現時点ではAndroidのみ。
ゆくゆくはiOSでも試すのでその時に記事をアップデートします。

# 導入

導入は以下のコマンドでOK
```
flutter pub add file_picker
```
[GithubのSetup](https://github.com/miguelpruivo/flutter_file_picker/wiki/Setup)にAndroidの注意点が書いてありますが、特に何もしなくても動きました。
リリースビルドの時の対応のようなので、必要に応じて参照して対応するのが良いと思います。


# Androidで使ってみる

## ファイルの選択

私の場合、kmlの取り込み処理を作りたく、以下のように使いました。

```dart
    final result = await FilePicker.platform.pickFiles(
      type: FileType.custom,
      allowedExtensions: ['kml','xml'],
    );
```

FilePickerクラスのstatic変数のplatformを使い、pickFiles()メソッドを呼び出しています。
pickFiles()メソッドを呼び出すとファイル選択ダイアログが表示されます。

[コンストラクタのドキュメント](https://pub.dev/documentation/file_picker/latest/file_picker/FilePicker/pickFiles.html)に引数の説明が載っているのでそちらを参照してもらうのが一番良いかと思います。

今回私が使った引数は以下の通りです。
|引数|概要|
|--|--|
|type|FileType列挙型で定義されているファイルタイプを指定します。指定することで選択可能なファイルの種類の指定ができます。FileType.customの場合、扱いたいファイルの拡張子を指定できます。|
|allowedExtensions|扱いたい拡張子をString配列で指定します。拡張子の.(ピリオド)は不要です。|

## 選択後の読み込み

戻り値は FilePickerResult? になります。
nullの場合、ファイルが選択されなかったことを表します。
今回は単一のファイルの選択になります。
FilePickerResult.files.singleを参照すると、様々な情報を取得できます。
ファイルの情報はPlatformFileオブジェクトで提供されます。
PlatformFileクラスのbytes変数にデータが入っているかと思ったのですが、取得できなかったので、ローカルに保存されたファイルを読み込みました。
```dart
      var file = File(result.files.first.path!);
      var kmlData = file.readAsStringSync();
```

# まとめ
まだ、簡単にしか触っていませんが、お手軽にファイル選択を行えるのが良いです。
近いうちにiOSでも確認する予定ですので、確認した際には内容をアップデートします。
SQLクエリの安全性とプレースホルダーの重要性

## はじめに

開発において、データベースクエリの安全性は非常に重要です。この記事では、プレースホルダーの使用意義と、固定値を扱う際にそれをどのように適用すべきかについて説明します。具体的な例として、Railsアプリケーションにおけるカスタムバリデーションのコードを見ていきましょう。

## カスタムバリデーションの例

以下は、一人の顧客が未来の日付に一つの企業に対して一つの予約のみを持てるよう制限するカスタムバリデーションです。

```ruby
class Reservation < ApplicationRecord
  validate :unique_future_reservation_per_customer_and_company

  private

  def unique_future_reservation_per_customer_and_company
    return if date.blank? || customer_id.blank? || company_id.blank?

    future_reservations = Reservation.where(customer_id: customer_id, company_id: company_id).where('date >= ?', Date.today)

    future_reservations = future_reservations.where(deleted_at: nil)
    future_reservations = future_reservations.where.not(id: id) unless new_record?

    if future_reservations.exists?
      errors.add(:base, "You can only have one future reservation per company.")
    end
  end
end
```

このバリデーションでは、`Date.today`を使用して、今日もしくはそれ以降の日付に対する予約の存在をチェックしています。

## プレースホルダーの意味

SQLクエリ内でプレースホルダー（`?`）を使用する主な理由は、SQLインジェクション攻撃からアプリケーションを守ることです。プレースホルダーを使うことで、SQLクエリに挿入される値が適切にエスケープされ、安全なクエリが構築されます。

## 固定値を扱う際のプレースホルダー使用

`Date.today`のような固定値を使用する場合、SQLインジェクションの危険性は基本的にはありません。なぜなら、この値はアプリケーション内で直接生成され、外部からの不正な入力によって操作されることがないからです。

## あえてプレースホルダーを用いる理由
1. **一貫性**: SQLクエリを構築する際に一貫してプレースホルダーを使用することで、コードの読みやすさと保守のしやすさが向上します。
2. **将来的な変更への備え**: 当初は固定値を使用していたコードが将来的に外部からの入力を受け取るように変更されることがあります。初めからプレースホルダーを使用しておくことで、そのような変更が容易になります。
3. **ベストプラクティスの遵守**: SQLインジェクション攻撃を防ぐためのベストプラクティスを遵守することは、セキュリティ意識の高い開発文化を促進します。

## [補足]具体的なSQLインジェクションのリスク
上記の通り、Date.todayのようにアプリケーション内で生成された値を使用する場合、SQLインジェクションのリスクはほぼありません。問題となるのは、外部からの入力を直接クエリに組み込むような場合です。例えば、以下のようなコードは危険です。

```ruby
# 危険なコード例
date = params[:date] # ユーザー入力を直接使用
Reservation.where("date >= '#{date}'")
```

このコードでは、ユーザーがフォームを通じて送信したdateパラメータをそのままSQLクエリに組み込んでいます。攻撃者がdateパラメータに`'2023-01-01'; DROP TABLE reservations;`のような値を送信した場合、意図しないSQL命令が実行される可能性があります。

## まとめ

プレースホルダーの使用は、SQLインジェクション攻撃からアプリケーションを守るために使用します。現時点では使用する必要がない場合でも、一貫性や拡張性を考慮して原則としては記述する方が好ましいと考えられます。

## 参考記事

https://qiita.com/KM9973/items/24d1abe21f41137810d7

https://qiita.com/KM9973/items/67f31d9a186e18b4e1e2
仕事をするうえで大切な「Why」という考え方

こんにちは。
Crane＆I　藤島です。

Qiitaの記事を書くのは初めてです。
弊社のメンバーが作成してくれた、マークダウン記法の使い方を読みながら一生懸命書いてみます（笑）

https://qiita.com/choco_joulu/items/30c130e64f8964e43b7c

一番初めに書く内容はどんなことがいいかなぁ。
そんなことを考えながら書き始めています。
折角ですから、私の根本になっている考え方をご紹介しようかなと思います。


# 人は「なぜ？（Why）」に心を動かされる
ゴールデンサークル理論というものをご存じでしょうか。
マーケティングコンサルタントであるサイモン・シネック（Simon Sinek）氏がTEDで話した「優れたリーダーはどうやって行動を促すか」の中で提唱した理論です。
これ、検索するといろんな場所で紹介されているので、ご存じの方も多いかと思います。

https://youtu.be/qp0HIF3SfI4?si=Il514XyFwCbyObU6

私は、仕事上、同僚や部下、研修生など沢山の人とお話をします。
業務の報告を受けたり、進捗を教えてもらったり、新しいチャレンジする内容を議論したり。
これはどうしたらいいのか。うまくいかない、どうやったらいいのか。など、アドバイスを求められることも多くあります。

そんな時、自分自身や、お話している相手に常日頃、問いかけます。
**「これ、何をやりたくてやってるんだっけ？」**


# 人はWhat（何を）を考えるのが得意
若かりし頃、評価を得ることに必死だった私は、いろんなことに100点を取ろうとしました。
残念ながら私はあまり頭が良くなかったので、テストではなく仕事でならいけるかも！とか思いながら・・・（笑）
例えば、「設計書を取っても綺麗に作ること」
例えば、「ショートカットを死ぬほど早く使うこと」
例えば、「ブラインドタッチをめちゃくちゃ早く行うこと」
例えば、「Excelの関数を使いこなして数字にミスが無いようにすること」
こういった、自分に出来そうなことを一つ一つ見つけ出し、研鑽し、沢山の仕事をこなしました。
しかし、やってもやっても、なんというか。
「やりたいことが出来ている」という感覚持てないのです。
更には、私がやっていることや、大事にしていることは、
同僚の方が重要視していることと、何かしらずれているように思えたものです。



# Whyとは何か
Why（なぜ？）とは、「目的」です。
目的とは何か。それは、自らの心に寄り添うテーマのようなものです。
人は、何かの目的をもって行動を起こしていることがほとんど。
ですが、不思議とそれは言語化されていないことが多くあると感じます。
言語化されていないということは、自分自身でも気づいていないということです。


# この記事を書いた「Why？」
私は「ゴールデンサークル理論」を知って人生が変わりました。
サイモン・シネック氏は「優れたリーダーになるため」に、この理論を提唱しました。
ですが、Whyを考えることは、自らを成長させる何よりも起爆剤であると私は実感しています。
むしろ、ビジネスマンにとっては最低限の必須能力なのではないかなと感じます。
Whyの中身は、沢山の種類があります。大きいものから小さいものまで。
例えば以下のようなことです。
* この検討をしてどこにいい影響を与えたいのか
* 自分がどうなるためにこの行動をしているのか
* 相手に何を届けたいのか
* 私はどうなりたいのか
* 相手に何を解決してほしいのか

これらを明確にし、相手に言語化して伝えられるようになると
無茶苦茶いいことが起きます。


# 「Why？」を伝えられるようになると、どんな嬉しいことが起こるか
**共通言語を用いて、逆算で考えることができる。**
本当にこれです。
先に話した、一生懸命「Waht」で行動していた私がどうすればよかったのか。
**「私は、お客様に納品する成果物を信頼していただきたいのです。
　今作成している成果物をもっと丁寧に作成し、信頼できる納品物にしたいです。」**
これを、同僚や上司に伝えられていたならば、どれだけ良い仕事が出来たかと思います。
例えば、「そうだね。皆でそこを意識しよう！」でも、
「それは大事だけれども、今重要なのはここだからそこは後ほどまとめて修正しよう」でも
私はどちらでも納得したでしょうし、理解をしたと思います。
動くべき順序が分かったと思います。


# 仕事をするうえで大切な「Why」という考え方
仕事っていうか、人生において、とても大切な考え方だなと、心から思います。
Whyを中心に考えられるようになれば、自分自身のキャリアだとか、ライフだとか、大切な方への接し方などなど。
様々なところに良い影響を与えられるし、自分だけではない相手とのコミュニケーションに大きく役立ちます。
これを読んでくださっている方に、ほんのかけらでも「いいものなのかも？」と伝わったら嬉しいなと思います。
すでに知ってる方に、「そんなの今更だよ！でもやっぱ大切だよね」なんて思ってもらえたら、それも幸せだなぁ。

ゴールデンサークル理論は、YouTubeで観れますので、興味を持っていただけましたら是非視聴してみてください。
また、この考えは、様々なビジネス書で手を変え品を変え、根本としては同じことととして表現されています。
「あ、きっとこれはゴールデンサークルと同様の事を言ってるんだな！」と気づけるようになると、より、どの角度からも大事なことなんだなと感じられると思います。
是非そんな閃き体験を探してみてくださいね。

最後までお読みくださり、ありがとうございました。
DBにおけるインデックスとは　〜DB検索の処理を速くするために〜

### はじめに
作成したWEBサイトの負荷テストで、データを一覧表示しているページにアクセスしたところ、表示に時間がかかりすぎタイムアウトエラーが発生したため、DBにインデックスを設定する対応を行なった。

### インデックスとは
インデックス（index）とは、データの検索や並び替え、取得を高速化するために利用されるデータベース管理の仕組みの１つ。

例えば本についてのデータ（タイトル、著者、出版社、発売日）が格納されている10億行のテーブルから、「著者」列が「村上春樹」であるデータを取得したい場合、下記のようなsqlを書くが、そこで「著者」列にインデックスを設定しておくことで、素早くDBが条件に合致するデータを取ってきてくれる。

```sql:
'SELECT *　FROM テーブル名　where 著者 = 村上春樹'
```

### 設定方法
インデックスの設定方法は簡単で、基本的によくwhere句で使用されている列に対して、下記のように設定すればよい。


```sql:
'CREATE INDEX インデックス名 ON テーブル名 (著者)'
```

### まとめ
インデックスは本棚における本の目次のようなもので、データベース内の情報を整理し、素早く目的のデータを見つけるのに役立つもの。

[参考サイト]
・https://products.sint.co.jp/siob/blog/index
・https://analytics-okinawa.jp/database/2005/
・https://it-biz.online/it-skills/index/
【Ruby】問題集解答 Part4（paizaラーニング）

# カンマ区切りの3つのデータの入力 (paizaランク D 相当)
https://paiza.jp/works/mondai/stdin/stdin_comma_3
### 自分の解答
```ruby:practice_01.rb
inputs = gets.chomp.split(",")

inputs.each do |input|
  puts input
end
```
入力を受け取ると同時にカンマ区切りで変数に格納し、eachメソッドで出力する。

### ChatGPTの解答
以下は、問題を解決するためのRubyプログラムの例です。

```ruby:ruby
# 入力を受け取る
input_str = gets.chomp

# カンマで分割して各文字列を取得
strings = input_str.split(',')

# 順に3行で出力
strings.each do |str|
  puts str
end
```

このプログラムは、標準入力から1行の文字列を受け取り、カンマで分割して配列に格納します。そして、その配列を順に取り出して各文字列を3行で出力します。
【Git】最新のコミットからの変更があるか確認する方法

## ステージング前の変更あるか確認する

ステージング前の変更があるか確認するには以下のコマンドを実行します。

```terminal
git diff --quiet HEAD
```

変更がある場合に異常終了します。

未追跡のファイルは無視されます。

https://git-scm.com/docs/git-diff#Documentation/git-diff.txt-emgitdiffemltoptionsgt--ltpathgt82308203

https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---quiet

## ステージング後の変更があるか確認する

ステージング後の変更があるか確認するには以下のコマンドを実行します。

```terminal
git diff --cached --quiet
```

変更がある場合には異常終了します。

https://git-scm.com/docs/git-diff#Documentation/git-diff.txt-emgitdiffemltoptionsgt--cached--merge-baseltcommitgt--ltpathgt82308203

## 何らかの変更があるか確認する

以下のコマンドを実行すると未追跡のファイルも含めて何らかの変更があるか確認できます。

```terminal
test -z "$(git status --porcelain)"
```

`git status --porcelain`の出力がないか`test -z`で検証しています。

変更がある場合には異常終了します。

https://git-scm.com/docs/git-status
可読性の高いバッチでファイル検索機能を実装してみた


# 経緯
そもそもバッチの開発はあんまり携わったことがなかった。バッチ自体が可読性を高めるのは難しいという印象だった。

今回は、そんなバッチでも可読性の高いものを書いてみようと思った。

>※注意※
可読性はあくまで個人的にそう感じるというだけで、絶対そうしなきゃいけないというわけではない。もちろん、pythonは改行しなきゃいけない（しかもスペース５つ分）など指定がある言語もある。

## 第一に、パラメータは冒頭にまとめる

```1.bat
REM パラメータ一覧

    set FileName=file_name.txt

    set LogFile=sample_batch_001.log

    set flg=0


```
ファイル名はスネークケースを使って、変数はキャメルケースに統一をするというのも一つの可読性を高めるテクニックかもしれない。


# 第二に、コメントアウト
ロジックのフローをわかりやすくするのが肝心。
```2.bat
REM ファイルの存在確認

    if exist %FileName% (

        set flg=100
        
    ) else (

        set flg=200

        REM 処理中断
        goto fin
        
    )

    
```
上のように、Windowsの改行とタブスペースを使う。最低でも改行は一つ設けた方が読みやすいと思う。

個人的な趣向かもしれないが、フラグやカウントをとる処理はいちいちコメントに書く必要はないのかと思う。


# 第三に、インデント
ロジックの包含関係を意識してインデントする。
```3.bat
REM ファイルパス一覧の存在確認

    for /f "delims== tokens=1" %%f in ('type %FileName% ') do (


        REM 関数の呼び出し
        call :func "%%f"

    )

    exit /b %flg%


REM 関数の開始
:func 

        set tgtPath=%~1

        if exist %tgtPath% (

            set flg=100

            REM ファイルが存在した
            echo EXISTS %tgtPath% >>%LogFile%

        ) else (

            set flg=200

            REM ファイルが存在しない
            echo DOESNT EXIST %tgtPath% >>%LogFile%

        )


REM 処理終了
:fin

echo %flg%>>%LogFile%

```
大見出しで機能の概要だけをコメントすると読みやすい。
さらに、改行２個に増やしてロジックの分け目が見やすくした。


# おまけ
別視点になりますが、例３）で関数を使うことで遅延展開の回避をしている。実は、下の記事を参考にした。
https://qiita.com/yz2cm/items/4983be006116c369d08b


個人的に`setlocal enabledelayedexpansion`がいくつも入ってると読みにくいし、デバッグしづらいと感じる。なので、なるべく関数を使い読みやすくしている。


そういえば、よく「関数」にうるさい人がいたのを思い出した。たぶん、一日に平均４回は「関数を使ってください」と言わせていた。

もしかしたら、なるべく関数を使うのが第四の鉄則になるかもしれない。

厳しく指導してくれる人があんまりいないので、少し寂しい。
furima アプリにて　【商品が売れるとsold out の表示】

結論から言うと

```ruby'
     <% if item.order.present? %>
```
こちらが回答。

なんとなく予想の形はできていたのですが
アソシエーションの表記がまちがっていました。

もし選択した商品に紐づく購入記録が存在していたら（空ではなかったら）、"sold out"と表示する
ということがヒントのようでしたが、意味が分からずメンターさんに聞きました。


orderモデルに記載していた
```ruby'
class Order < ApplicationRecord
  belongs_to :user
  belongs_to :item
  has_one :address
end

```
そのままの意訳だと
もし（if）
商品（item）に
購入記録（order）があれば（present）

フォームオブジェクト（今回であればpurchase_record）
かと思っていましたがどうやらフォームオブジェクトはこない。なぜならモデルではないから！！とおっしゃっていたような。。（もしかしたら間違いかも）

ちなみに確実にいえることは
フォームオブジェクトにはアソシエーションが組めない！！！！これは確実です！！！
Laravel session保存と削除の方法

## sessionについて
- ざっくりで言うとデータを一時的に保存して、他のページでもデータを共有可能な仕組み

## 実際にsessionを使ってみる
- 今回は何かの申請処理後に表示される画面が何度も同じURLでアクセス出来ないように、申請処理があった場合のみ表示されるような用途で使ってみます
    - 申請処理時にsessionを保存
    - sessionがある時のみ申請完了画面を表示してそのsessionを削除する
    - sessionがない場合は別のページにリダイレクトさせるようにする

## sessionを保存

```php

 // コントローラ

    public function application(Request $request)
    {
        $request->session()->put('application', true);

        return redirect()->route('foods.completion');
    }
```

## sessionを取得と削除

```php

 // コントローラ

    public function completion(Request $request)
    {
        // session有無チェック、ある場合viewを表示、無い場合foods.main画面にリダイレクト
        if ($request->session()->get('application') === true) {
            $request->session()->forget('application');
        } else {
            return redirect()->route('foods.main');;
        }
        return view('foods.completion');
    }

```

上記ではforget()を使ってsession「application」削除しているため、foods.completionの画面でページリロードをすると「foods.main」画面に遷移します。
今回だとcompletion()の処理を挟んだ状態つまり、session「application」が無いとviewが表示されない設計にしてあります