OmniAICreator/Qiita-1.07M huggingface dataset:
C#/VB.NET：Excel で特定のセルをロックする方法

セルのロックは、Excelのシートで特定のセルを誤って変更しないように保護する機能です。セルをロックすることで、パスワードまたは権限があるユーザーだけがこれらのセルを編集でき、他のユーザーは閲覧または他のロックされていないセルを編集することしかできません。これは、重要なデータを共有し、保護する際に非常に便利です。Free Spire.XLS for .NETは.NETプラットフォームでこの機能を実現することをサポートします。詳細な方法とサンプルコードを次に示します。

## Free Spire.XLS for .NETをインストールする
**方法1**：[NuGet](https://www.nuget.org/packages/FreeSpire.XLS)でFree Spire.XLS for .NETをインストールする
Visual Studioを開いて新しいプロジェクトを作成します。次に、「Solution Explorer」で「References」を右クリックし、「Nuget Manage Packages」を選択します。Free Spire.XLS for .NETを検索してインストールします。
**方法2**：手動でSpire.xls.dllを追加する
[Free Spire.XLS for .NET](https://jp.e-iceblue.com/download/free-spire-xls-for-net.html)をローカルにダウンロードしインストールします。次に、Visual Studioて新しいプロジェクトを作成し、右側の「Solution Explorer」で「References」を右クリックし、「Add Reference」>「Browse」を選択して、インストールパス下のBINフォルダのdllファイルを見つけ、「OK」をクリックして、プログラムに参照を追加します。
## 方法とサンプルコード
* Workbook クラスのオブジェクトを作成します。
* Workbook.LoadFromFile() メソッドを使用して、Excelドキュメントを読み込みます。
* Workbook.Worksheet[sheetIndex] 属性を使用して、最初のシートを取得します。
* シートで使用されている範囲にアクセスし、CellRange.Style.Locked プロパティを false に設定して、すべてのセルのロックを解除します。
* 特定のセルにアクセスし、CellRange.Style.Locked プロパティを true に設定してセルをロックします。
* XlsWorksheetBase.Protect() メソッドを使用してシートを保護します。
* Workbook.SaveToFile() メソッドを使用して結果ドキュメントを保存します。
C#:
```C#
using Spire.Xls;

namespace LockCells
{
    class Program
    {
        static void Main(string[] args)
        {
            //Workbook クラスのオブジェクトを作成する
            Workbook workbook = new Workbook();

            //Excelドキュメントを読み込む
            workbook.LoadFromFile(@"sample.xlsx");

            //最初のシートを取得する
            Worksheet sheet = workbook.Worksheets[0];

            //すべてのセルのロックを解除する
            CellRange usedRange = sheet.Range;
            usedRange.Style.Locked = false;

            //特定のセルをロックする
            CellRange cells = sheet.Range["A1:A4"];
            cells.Style.Locked = true;

            //パスワードでシートを保護する
            sheet.Protect("123456", SheetProtectionType.All);

            //ドキュメントを保存する
            workbook.SaveToFile("result.xlsx", ExcelVersion.Version2016);
        }
    }
}
```
VB.NET:
```VB
Imports Spire.Xls

Namespace LockCells
    Class Program
        Shared Sub Main(ByVal args() As String)
            'Workbook クラスのオブジェクトを作成する
            Dim workbook As Workbook = New Workbook()

            'Excelドキュメントを読み込む
            workbook.LoadFromFile("sample.xlsx")

            '最初のシートを取得する
            Dim sheet As Worksheet = workbook.Worksheets(0)

            'すべてのセルのロックを解除する
            Dim usedRange As CellRange = sheet.Range
            usedRange.Style.Locked = False

            '特定のセルをロックする
            Dim cells As CellRange = sheet.Range("A1:A4")
            cells.Style.Locked = True

            'パスワードでシートを保護する
            sheet.Protect("123456", SheetProtectionType.All)

            'ドキュメントを保存する
            workbook.SaveToFile("result.xlsx", ExcelVersion.Version2016)
        End Sub
    End Class
End Namespace
```
![2023-04-07_165333.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811193/7ad41a24-44fd-48bf-2a9b-e8f0c0793660.png)
OpensslでPEM形式の証明書をp12形式に変換する（中間証明書を添えて）

証明書をとあるアプライアンス製品にインストールしようとしたのですが、PEM形式ではインストールできなかったので、変換する必要がありました。忘れないうちにメモっておきます。
# PEM形式って？
証明書の形式の一つで中身はASCIIテキストです。

```
-----BEGIN CERTIFICATE-----
MIIHSjCCBjKgAwIBAgrgerthgj5rtSj3iPrcE6Ga8DANBgkqhkiG9w0BAQsFADBa
MQswCQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4s
fjjgojeg,thsmEFkrgkefERGHjasTjhr4eJKYUKDeg・・・・・
fl2x8lqH8aHcGYyY9e8Mc+vm/uD5PqKaT3RpD//Hex/8akvCZ3fXm1k8/U6sKg==
-----END CERTIFICATE-----
```
こんな感じで、-----BEGIN CERTIFICATE-----ではじまり、-----END CERTIFICATE-----で終わります。

# p12への変換
変換するのに必要なものは
* 署名済みの証明書
* 証明書の秘密鍵
* 秘密鍵のパスフレーズ
* 中間証明書
になります。用意しましょう。
もちろん、opensslをインストールしておく必要はあります。
```
＄ openssl pkcs12 -export -in <署名済みの証明書ファイル名> -inkey <秘密鍵のファイル名> -certfile <中間証明書のファイル名> -out <出力するファイル名>
```
実行すると
```
Enter pass phrase for <秘密鍵のファイル名>: <秘密鍵のパスフレーズ>
Enter Export Password: <p12の出力パスフレーズ>
Verifying - Enter Export Password: <p12の出力パスフレーズ>
```
そうするとp12形式で出力されます。
fileコマンドでファイル形式を確認すると、pemはASCII、p12はdataになります。
Vue.jsビルド時のメモリ不足エラー対処法

ビルド中に以下のようなメモリ不足のエラーが発生した場合、Node.jsプロセスに割り当てるメモリの最大サイズを増やすことで解決できることがあります。以下の手順で、環境変数NODE_OPTIONSを設定してメモリを増やしましょう。
```
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
```

ターミナルで、以下のコマンドを実行して、Old Spaceの最大サイズを1024MB（1GB）に設定します。

```
export NODE_OPTIONS="--max-old-space-size=1024"
```
このコマンドは、環境変数NODE_OPTIONSに--max-old-space-size=1024を設定し、Node.jsプロセスのV8 JavaScriptエンジンに対して最大ヒープサイズ（Old Space）を1024MB（1GB）に制限することを指示します。

設定が完了したら、再度Vue.jsプロジェクトのビルドを実行します。

```
npm run build
```
または
```
yarn build
```
メモリ不足のエラーが解決しない場合は、--max-old-space-sizeの値をさらに増やすか、他の原因を調査してください。
また、exportコマンドは環境変数を現在のシェルのセッションに対して適用するため、シェルを更新するたびに環境変数はリセットされます。今後もメモリ不足が続くような場合は、シェルの設定ファイルにexportコマンドの部分を追記して永続化します。
`
vi ~/.bashrc
`
で
`export NODE_OPTIONS="--max-old-space-size=1024"`
を追記した上で、
`source ~/.bashrc`
で変数を読み込みましょう。
ixp425のpci

ixp425にはUSBが入っているのですが1.0なのでターゲットのモジュールにはPCIにUSB 2.0にチップがぶら下がっています。

PICバスはリトルエンディアンでixp425はビッグエンディアンなので変換する必要があります。

変換用のbusアクセスをarm/xscale/ixp425_pci_asm.Sで用意しています。この中では__ARMEB__が定義されている場合は変換して処理して、そうでない場合はそのまま処理するようになっています。

ところがixp425にも変換機能があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/f97f8d42-6fd5-f959-1df1-0e68d9e173f6.png)

これをevbarm/ixdp425/ixdp425_pci.cで設定します。

```
        PCI_CSR_WRITE_4(sc, PCI_CSR, CSR_IC | CSR_ABE | CSR_PDS);
```

なぜかCSR_ADSが抜けています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/23ac67ff-9f11-c62e-ebf4-2916fdced635.png)

おそらく変換を意図しているので、反対の反対なので、変換しない状態、すなわちリトルエンディアンになり、正しく処理されないようなきがします。

またEHCIなデバイスはプローブできてattachな処理が行われるのですが、レジスタが正しく見えません。

EHCIの先頭からバイトでダンプしたところです。

```
10 00 95 00 04 22 00 00 02 00 00 00 00
```

3バイト目(オフセット2)は2バイトのHCIVERSIONになりますが、なんだか変な値です。正しくは01,00です。エンディアンが逆であっても変です。

なんでかな。。。

関係ないですが、初期のチップ(ステップA0)にはバグがあって、arm/xscale/ixp425_pci.cでPCI_NP_HAVE_BUGが定義されていて、ワークアラウンドが入っています。

割り込み4本とリセットとクロックはGPIOを使っています。

NSLU2は同じようにPCIバスのUSB 2.0チップのNEC uPD720101が使われていて、動作していたようなのだが、なんで動かないかな。。。

自分の手元にあるターゲットは、SOCのGPIOのCLKを使わず、水晶発信器のクロックを使っているようだ。
５年間触られていなかったプロジェクトのruby・railsのバージョンアップで発生したerror等

# 記事の内容
タイトルにある通り、５年間改修が一度もはいらず放置されていたプロジェクトがなぜかこのタイミングで脆弱性診断に引っかかってしまい自分が担当することになりました。railsのバージョン上げることで解消しそうなため対応しました。
その際に、発生したerror等をまとめていこうと思います。
`rails app:update` は実行したものの上書きできなかったため発生したものもありますが、ご容赦いただけますと。。

# 環境
改修前：ruby: 2.2.8 → 改修後：2.7.6
改修前：rails: 4.2.8 → 改修後：6.1.7.6



## `method_missing': undefined method `raise_in_transactional_callbacks=' for ActiveRecord::Base:Class (NoMethodError)

このエラーは`raise_in_transactional_callbacks` というメソッドが ActiveRecord::Base クラスに存在しないことを示しています。具体的には、NoMethodError が発生しています。
Rails 4.1 で導入され、Rails 5.0 で非推奨となり、Rails 5.1 で削除されました。
そのため今回発生しました。

### 解決策
config/application.rb より`raise_in_transactional_callbacks`を使用しているコードを削除しました。
Rails 5.0以降では、raise_in_transactional_callbacks= はデフォルトで有効になっています。


## undefined method `alias_method_chain' for String:Class (NoMethodError)
Did you mean?  alias_method

このエラーは`alias_method_chain` というメソッドが String クラスに存在しないため発生しました。このメソッドは先程同様Rails 5.0 で非推奨となり、Rails 5.1 で削除されました。


### 解決策
そのため`prepend`に置き換えることで対応しました。
参考記事は以下です。
https://qiita.com/rorensu2236/items/3f94ff653ba0c45141a7


## NoMethodError (undefined method `before_filter'...

`before_filter` も同じくRails 5.0 で非推奨となり、Rails 5.1 以降では削除されました。

解決策
`before_filter`を`before_action` に置き換えます。
参考
https://pikawaka.com/rails/before_action


※`skip_before_filter`も同様に`skip_before_action`に置き換えました。

## NoMethodError: undefined method `new' for BigDecimal:Class
decimal型を使用しているカラムを使用した際に発生しました。
Ruby2.7からRubyで使われているgem bigdecimalのバージョンが2.0.0になってるそうです。

### 解決策
以下のようにbigdecimalのバージョンを2.0.0未満にすればBigDecimal.newが使えようになりました。
```
gem "bigdecimal", "1.4.4"
```

参考
https://zenn.dev/harasho/articles/undefined-method-new-for-big-decimal-class

## ActionView::MissingTemplate

### その１

`render nothing: true`がRails 5.1以降では削除されています。


### 解決策
`render body: nil`のように修正

参考
https://stackoverflow.com/questions/34688726/the-nothing-option-is-deprecated-and-will-be-removed-in-rails-5-1

その２

`render text`オプションは、Rails 5.1以降では削除されています。

### 解決策
`render plain`のように修正

参考
https://kenchan0130.github.io/post/2020-07-07-1#plaintext%E3%81%AE%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E6%96%B9%E6%B3%95%E3%81%8C%E5%A4%89%E6%9B%B4

# 終わりに
rubyのバージョンはマイナーアップデートだったので変更はほとんどなかったと思います。
ただ、railsはメジャーアップデートしたので結構errorがでて驚きました。
特にrails5.1で削除されたものが多かったみたいです。
それだけ重要なアップデートだったのだと。。
各プロジェクトにて、少しづつ上げていったほうが後々楽だなと感じました。
Gitコマンドよく使うやつ一覧

# Gitでよく使うコマンド一覧

| コマンド | 説明 |
|:-----------|:------------:|
|git init|	空のGitリポジトリを作成する|
|git clone <URL>|	リモートリポジトリのコピーを作成する|
|git add <file>	|ファイルをステージングエリアに追加する|
|git commit -m "<message>"|	ステージングエリアの変更をコミットする|
|git status|	ファイルのステータスを表示する|
|git diff	|変更点を表示する|
|git branch|	ローカルブランチの一覧を表示する|
|git checkout <branch>	|指定したブランチに移動する|
|git merge <branch>|	指定したブランチを現在のブランチにマージする|
|git pull	|リモートリポジトリから変更を取得し、ローカルブランチにマージする|
|git push	|ローカルリポジトリの変更をリモートリポジトリにプッシュする|
|git remote add <name> <URL>	|リモートリポジトリを追加する|
|git remote -v|	接続されているリモートリポジトリの一覧を表示する|
|git log	|コミットログを表示する|
|git reset <file>	|ステージングエリアから指定したファイルを削除する|
|git revert <commit>	|指定したコミットの変更を取り消す新しいコミットを作成する|
|git rm <file>	|ファイルを削除して、ステージングエリアに追加する|
|git tag <tagname>	|現在のコミットにタグを付ける|
|git show <commit>	|指定したコミットの詳細を表示する|
|git stash	|変更を一時的に保存する|
|git fetch	|リモートリポジトリから変更を取得するが、ローカルブランチにマージしない|
[PHP]条件分岐の書き方

# PHPにおける条件分岐の書き方
条件分岐の書き方は、phpで記載すると下記の通り。
**if文**と**switch文**がある。

## if文
```php:if文
$x = 20;
if(条件式1){
//条件式1に対してtrueの際の処理をここに書く
} elseif(条件式2){
//条件式2に対してtrueの際の処理を書く
} else{
//条件式1,2両方falseの際の処理を書く
}

//if文に文末の;はいらない
```


## switch文
```php:switch文
$x = 値
switch(式){
 case 値1:
    echo "式=値1の時の処理";
    break;
 case 値2:
    echo "式=値2の時の処理";
    break;
 default:
    echo "式!=値1,式!=値2の時の処理";
    break;
}

```

- **point1**
switchの後の()内の**式**と、caseの**値**が一致した時に処理を実行し、caseのどれとも一致しなかった時、defaultの処理を実行する。

- **point2**
また、caseブロックの最後には```break;```を必ず記載する。
```break;```が無い場合、**後に続くcaseブロックの処理が続けて実行されてしまう**。


なお、比較演算子(<,==　等)、論理演算子(&&かつ,||もしくは,!条件の否定)の使い方はいつも通り。
その働き方、やめてみない？

# はじめに

私の開発チームでは、毎週おこなう振り返りの中でチームのパフォーマンス向上や問題点の改善を目的とした「トライ」という取り組みを考え、次の1週間でそれを実践するようにしています。
この記事では、これまでに試してきたトライの中で特に印象的だったものをいくつか紹介していきたいと思います。
みなさんの職場でも取り入れられそうなものがあれば、ぜひ参考にしてみてください。

## 18時〜翌朝6時は仕事禁止
言葉の通り、夕方18時から翌朝6時までの間に仕事をしてはダメ、というものです。

### ねらい
- リミットがあることで仕事に集中できる
- 18時になったら合法的にタスクを手放せることによる心理的負荷の軽減

### やってみて

集中力が高まる・心理的負担が軽減されることに関しては効果を感じた人が多かった印象です。また強制的に朝型になるので、生活リズムが改善された人もちらほらいたようです。
一方で、「タスクに追われている状況だときつい」「調子が出てきても18時になったら終わらなければならないのは嫌」「新人の場合はよりしんどそう」などの声も。
「18時になったら基本帰ってよし。やりたければどうぞ」くらいのスタンスがちょうどいいのかもしれませんね。

## 定例会議の時間変更

私のチームでは以前、リファインメントとデイリーを夕方16時にまとめておこなっていました。
このうちリファインメントを頭の働く午前中に行い、デイリーを18時にずらしてみようという試みです。

### ねらい

- リファインメントの質を上げる

### やってみて

こちらは概ね好評でした。疲れの少ない午前中に頭を使うリファインメントをやることのメリットを全員感じることができたようです。また、リファインメントからデイリーまでの時間が長くなることで、まとまった作業時間が増えたことにメリットを感じたメンバーもいました。
このスタイルは現在でも引き続き採用されています。

## 普段の仕事場以外の場所で仕事する

私たちは普段、チームですぐにコミュニケーションがとれるように決まったスペースでまとまって働いています。それをやめてみようというのがこの取り組みです。場所はカフェや家などいつもの場所以外ならどこでも自由で、質問があるときはslackやzoomで行うというルールを決めて実践しました。

### ねらい

- 周囲に人がいない環境に身を置くことで、集中力・作業効率を上げる

### やってみて

全体的に「あえて強制する必要はない」と、離れて働くことを強制した点にマイナスな印象を持った人が多かったようです。「別に他の場所で働いてもいいよ」くらいがいいのかもしれませんね。
また、zoomを使うことに抵抗があるという意見に対して、「いざという時にオンラインで質問できるようハードルを下げておくことも大事」という意見も挙がりました。パンデミックをきっかけにオンラインでの仕事が当たり前になった今、ある程度適応する必要はありそうですね。

## コードレビューの時間を固定する

私のチームでは、レビューが溜まりやすいということが度々課題として上がります。これを解決するため、「レビューは毎日〇時から□時の間に行う。それ以外は禁止」というルールを試してみました。禁止にしたのは、より強制力を高めるためです。

### ねらい

- コードレビューを溜めない

### やってみて

結論、大不評でした。
「やるべきことがあってもその時間だけは融通が効かない」「自分のタスクのモチベが上がっている時にレビューの時間が来るとテンションが下がる」「自分のタイミングでやりたい」「決めた時間以外にやってはいけないというルールは不要」「時間内にしっかりとしたレビューができない」などなど、マイナスに感じる部分が多かったようです。
一方、「レビューが返ってくる時間が想定できることがありがたくはあった」というプラスな意見も一応ありました。

## レビュー依頼が来たら即座にレビュー

こちらはレビューを溜めないための取り組みの別バージョンです。時間を固定するのではなく、レビュー依頼が来たら何をしていようが即レビューするというもの。私のチームではslackのチャンネルでレビュー依頼を出しているので、slackが飛んできたら即レビューということになります。

### ねらい

- コードレビューを溜めない

### やってみて

「依頼が来たら即レビュー」というまあまあ強い拘束力があるにも関わらず、なんとこちらは意外と好評でした。絶対にレビューが溜まらないことに大きなメリットを感じる人が多かったようです。
一方で、「各々がslackに気づいたタイミングでレビューをするから、レビューが来るタイミングはバラバラになる」という意見もありました。とはいえ、以前のようにレビューが長時間放置されるような事態はこれで防げそうですね。

# 最後に

みなさんの職場ではどんな働き方改善が行われていますか？
試して良かったものがあればぜひコメント欄で教えてください。
BIMを表現するためのIFCファイルについて調べてみた

# IFCとは

## 概要

IFC（Industry Foundation Classes）は、建築・土木分野のCAD（Computer-Aided Design）ソフトウェアで使用されるファイル形式の一つです。IFCファイルは、建物情報モデル（BIM）を記述するためのオープンな標準フォーマットです。
拡張子がifcの場合、中身はステップファイル形式となっています。

## ステップファイルとは

> ステップファイル（STEPファイル）とは、コンピュータ支援設計（CAD）ソフトウェアやコンピュータ支援製造（CAM）ソフトウェアなどで使用される、3Dモデルの交換フォーマットの1つです。
> 
> 
> STEPは、「Standard for the Exchange of Product model data」の略で、国際標準化機構（ISO）によって規定されたフォーマットです。このフォーマットは、異なるCADソフトウェア間でデータをやりとりするための標準化された形式として使用されます。
> 
> STEPファイルは、CADソフトウェアやCAMソフトウェアで作成された3Dモデルのジオメトリや属性、および構造情報を格納することができます。また、複数のCADソフトウェアがSTEPファイルを読み書きできるため、異なるCADソフトウェア間でのデータ交換に役立ちます。
> 

[ISO 10303-21 - Wikipedia](https://en.wikipedia.org/wiki/ISO_10303-21#:~:text=ISO%2010303-21%20defines%20the%20encoding%20mechanism%20for%20representing,is%20also%20called%20p21-File%20and%20STEP%20Physical%20File.)

## サンプル

```json
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
FILE_NAME('sample_house.ifc', '2012-01-01T12:00:00',(''),'','BIM Project','');
FILE_SCHEMA(('IFC2X3'));

#10=IFCPERSON($,'John',$,$,$,$,$,$);
#11=IFCORGANIZATION($,'XYZ Architecture Firm',$,$,$);
#12=IFCPERSONANDORGANIZATION(#10,#11,$);
#13=IFCPROJECT('2FMSP00R6yRBf6y8WbYygE',#14,'Sample House','',$12,#22,$23,'',($));
#14=IFCOWNERHISTORY(#15,#13,$,.ADDED.,$,$,$,1434355200);
#15=IFCPERSONANDORGANIZATION(#16,#17,$);
#16=IFCPERSON($,'Jane',$,$,$,$,$,$);
#17=IFCORGANIZATION($,'ABC Engineering',$,$,$);
#18=IFCLOCALPLACEMENT(#8,#19);
#19=IFCAXIS2PLACEMENT3D(#20,#21,#22);
#20=IFCDIRECTION((1.,0.,0.));
#21=IFCDIRECTION((0.,1.,0.));
#22=IFCPOINT((0.,0.,0.));
#23=IFCPROJECTLIBRARY('0Himr8WYrA_DfNQAXd3xye',#24,'',$12,$,$,$);
#24=IFCLIBRARYINFORMATION(#25,$,'Building Library','Building library for sample project',$,$);
#25=IFCORGANIZATION($,'BIM-Expert',$,$,$);

...

#559=IFCOPENINGELEMENT('2E6vjhFcjBaQ2ExoSDPWe0',#14,'Opening 1','A small window opening',#506,$,$,$,.OPENING.);
#560=IFCRELVOIDSELEMENT('3akXvhIcLEURr1rUVxyeuf',#14,$,#559);
#561=IFCOPENINGELEMENT('3akXvhIcLEURr1rUVxyeuf',#14,'Opening 2','A larger window opening',#507,$,$,$,.OPENING.);
#562=IFCRELVOIDSELEMENT('1
```

## バージョン

 主にIFC 2x3が現時点ではBIMアプリケーションの標準として使われているそうです。
 その他にも、IFC4などがあります。

参考
[https://www.cadjapan.com/special/bim-navi/know/glossary/ifc.html](https://www.cadjapan.com/special/bim-navi/know/glossary/ifc.html)

## ドキュメント

各要素などについては、下記ドキュメントを参照

**IFC2x3**
[Start Page of IFC2x3 Final Documentation](https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/FINAL/HTML/)

**IFC4**
[IFC4 Documentation](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/FINAL/HTML/)

## 参考文献

- https://www.building-smart.or.jp/
- https://www.buildingsmart.org/

## 関連リポジトリ

- ifctoolkit
[https://github.com/JackTing/ifctoolkit/](https://github.com/JackTing/ifctoolkit/)
- XbimEssentials
[https://github.com/xBimTeam/XbimEssentials](https://github.com/xBimTeam/XbimEssentials)
【Ruby on Rails】 ローカルで本番環境を動かした時にCSSやらなんやらが反映されない。

# 結論
config/environments/production.rb の設定が原因。

```config/environments/production.rb
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
```
この値がFalseだと assets:precompile で作成した、public/assets 配下のファイルが参照出来なくなる。
ssh先のmlflow uiをローカルPCで確認する方法

通常、mlflowで結果を確認したい時、以下のコマンドで( http://127.0.0.1:5000/ )にアクセスすることで表示できる。
```terminal
mlflow ui
```

ssh接続をしている時に結果を確認したい時は、以下のコマンドで(http://\<ssh先のipアドレス>:5000/)にアクセスすることで表示できる。
```terminal
mlflow ui -h <ssh先のipアドレス>
```

### おまけ
tracking_uriを設定している場合は以下で表示できる。
```terminal
mlflow ui -h <ssh先のipアドレス> --backend-store-uri <tracking_uriのパス>
```
PythonのDockerfileに書いてある環境変数の、「PYTHONBUFFERED」ってなんだ

# 初めに
Dockerfileで指定される環境変数について調べたことをメモしておく。

# 経緯
Dockerの勉強をしてたら、PythonのDockerfileでこんな行があった。
```Dockerfile
ENV PYTHONBUFFERED 1
```
Pythonの公式ドキュメント曰く、
> 標準出力と標準エラー出力のストリームのバイナリーレイヤー (buffer 属性として利用可能) がバッファされないよう強制します。それでもテキスト I/O レイヤーは、コンソールに出力する場合はラインバッファされ、日対話的なファイルにリダイレクトされた場合はブロックバッファされます。

読んでもよくわからない。そもそもバッファってなんだ？

# バッファリングとは
標準出力されるデータは、直接ターミナルに渡されるわけではない。いったんバッファと呼ばれる場所に貯めておき、ある程度まとめてからターミナルで出力される。入力も同様である。これは、データの送信時に本体以外に宛先などの付加情報が必要なので、まとめて送るほうが効率が良いためである。

`PYTHONBUFFERED`が空でないときは、Pythonは標準出力と標準エラー出力でこのバッファリングを行わなくなる。こうすることでデータは即座に出力され、途中でプログラムが止まっても直前までのデータを失わずに済む。

# 参考文献
https://stackoverflow.com/questions/59812009/what-is-the-use-of-pythonunbuffered-in-docker-file

https://docs.python.org/ja/3.5/using/cmdline.html#cmdoption-u

https://programming-place.net/ppp/contents/c/043.html
spring securityについて


# spring securityでは以下の脆弱性がデフォルトで有効になっている
- [セッションハイジャック（Session Hijacking）対応](https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_4.html)
    - Form ログインを使っている場合、 Spring Security はデフォルトでログイン時にセッションIDを変更してくれまる
- [クロスサイト・スクリプティング（XSS）対応](https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_5.html)
- Thymeleafのth:xxxタグでもサニタイズしてくれます
- [クロスサイト・リクエスト・フォージェリ（CSRF）対応](https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_6.html)
-  [HTTP ヘッダ・インジェクション対応](https://www.ubsecure.jp/blog/http-header-injection#:~:text=HTTP%E3%83%98%E3%83%83%E3%83%80%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%81%AF,%E3%81%8A%E3%82%88%E3%81%B3%E6%94%BB%E6%92%83%E2%BC%BF%E6%B3%95%E3%81%A7%E3%81%99%E3%80%82) 
    - HttpServletResponse がラップされ、ヘッダーに改行コードを含んだ文字を渡したときの動作が変わる
    - 改行コード（\n or \r）が含まれると、 IllegalArgumentException がスローされる
- [クリックジャッキング対応](https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_9.html)
    - デフォルトで X-Frame-Options がレスポンスヘッダに設定される
    - ※X-Frame-Options に deny が指定されているため <iframe> に元ページを表示できなくなっている
- [SQL インジェクション対応](https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_1.html)については、spring securityの管轄外のため、独自で対応する必要がある

## デフォルトのセキュリティヘッダー
-  [HTTP レスポンスヘッダー](https://spring.pleiades.io/spring-security/site/docs/5.0.18.RELEASE/reference/html/headers.html) 

##レスポンスヘッダーにSameSite属性を追加
- SameSite属性とは、WebサーバとWebブラウザの間でやり取りされるCookie（クッキー）に指定できる属性の一つで、サイトをまたがるアクセス時にクライアントからCookieを送信するか否かを指定するもの。
指定値は「None」「Lax」「Strict」の3種類。
[SameSite属性とは](https://e-words.jp/w/SameSite%E5%B1%9E%E6%80%A7.html#:~:text=SameSite%E5%B1%9E%E6%80%A7%E3%81%A8%E3%81%AF%E3%80%81Web,%E3%81%8B%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B%E3%82%82%E3%81%AE%E3%80%82) 

# 参考サイト
 [IPA／安全なウェブサイトの作り方](https://www.ipa.go.jp/security/vuln/websecurity/about.html)
 [OWASPとは](https://owasp.org/www-chapter-japan/) 
 [Webシステム／Webアプリケーションセキュリティ要件書](https://github.com/OWASP/www-chapter-japan/tree/master/secreq)
 [CRYPTREC暗号リスト](https://www.cryptrec.go.jp/list.html) 
 [SSL Server Test](https://www.ssllabs.com/ssltest/)
Qiita　マークダウンの書き方

## 見出しを付けたいとき

見出しを付けたいときには#を使う

書き方は

```
# 見出し1番大きい
## 見出し2番目
```
こんな感じで１番目立つようにしたい場合は　シャープ１つ
2番目はシャープ２つのように足していく
＃を足していくと少しずつ小さくなる

# 見出し1番大きい
## 見出し2番目
### 見出し3番目

書くときはシャープは半角で見出しとの間に半角スペースを空ける
空けていないと↓のようにただ頭にシャープが付くだけになる

#見出し1番大きい


## 黒背景のブロックをつくる

ブロックの中に入れたい範囲の上下に　`　を3つつける

## 箇条書きにする

箇条書きにしたいときは文章の前に　－　をつける

- 箇条書き


追記）
https://qiita.com/Qiita/items/c686397e4a0f4f11683d
ここに書き方のまとめが書いてる
→投稿やコメントの右上にある？から飛べる
Dockerで環境変数を扱う

# この記事は
指定した環境変数をPythonプログラム上で使用する方法です。

# 環境変数のおさらい
以下のようなPythonプログラムがあるとします。
単に、環境変数を取得して表示しているだけです。
```python:main.py
import os

value = os.environ["value"]
print(value)
```

特に何もせずに`python main.py`を実行すると、結果は`None`になります（指定した環境変数が取得できなかった）。これは想定通りです。
```shell
$ python main.py
None
```
環境変数を定義してから再度同じプログラムを実行させます。設定された値が表示されることがわかります。
```
$ export value=1234
$ python main.py
1234 # 設定した値が表示された!
```

# Dockerで同じことをする
2つの方法があります。
* ENVに固定値を設定する
* ARGを設定し、ENVでARGに設定した値を設定する。

順に見ていきます。

## ENVに固定値を設定する
以下のようなDockerfileを作成します。この時、`ENV=1234`として、変数を指定します。これがプログラム上から読み込むことのできる変数です。
```docker
FROM python:3.9

COPY . .

ENV value=1234

ENTRYPOINT ["python", "main.py"]

```

<details><summary>pythonコード（再掲）</summary>

```python:main.py
import os

value = os.environ["value"]
print(value)
```
</details>

`Dockerfile`と`main.py`があるディレクトリで以下のようにDockerのビルドと起動をします。想定通り指定したENVの値が表示されます。
```shell
$ docker build -t sample_docker .
$ docker run sample_docker
1234
```

## ARGを設定し、それをENVで読み込む
次に、ARGを設定する方法です。
以下のように、DockerfileにARGで値を設定し、設定したARGをさらにENVに指定しています。
この時、`ARG VALUE=default`は、`--build-arg`（後述）で値が別に指定されなかった場合のデフォルト値を指定していることを意味しています。
```docker
FROM python:3.9

COPY . .

ARG VALUE=default

ENV value=${VALUE}

ENTRYPOINT [ "python", "main.py" ]
```
この状態で、先ほどと同様にビルドと実行をします。
```
$ docker build -t sample_docker .
$ docker run sample_docker
default
```
ARGで設定した`default`という文字列が出力されるのがわかります。

次に、`build-arg`を用いてARGの値を上書きしてみます。

```shell
$ docker build -t sample_docker . --build-arg VALUE=my_value
$ docker run sample_docker
my_value
```

想定通り、`my_value`を表示できました。
AWS SES でコンソールからメール送信する ( AWS CLI のコマンドを実行 )

# 準備

IDの作成をする
入力したメールアドレスに認証メールが届くので認証する

<img width="1191" alt="image" src="https://user-images.githubusercontent.com/13635059/230554343-2b65b236-0bb2-4c32-8aa7-a361a8219c81.png">


# コンソールで

アクセスキーとシークレットがありリージョンを指定すればメール送信が出来る
AWSの認証方法は何でも良いがここでは環境変数で直接指定する

```
export AWS_ACCESS_KEY_ID="*********" && export AWS_SECRET_ACCESS_KEY=""*********"
```

リージョンを適宜指定してメール送信

```
aws ses send-email --from "example@example.com" --to "example@example.com" --subject subject --text body --region=ap-northeast-1
```


成功

```
{
    "MessageId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
```

# メールの確認

Gmailで受信すると警告が表示されたが受信は出来ていた

<img width="1122" alt="image" src="https://user-images.githubusercontent.com/13635059/230554915-1502d1ef-57de-4d1a-9dae-146644ed3251.png">


# 送信数

<img width="1011" alt="image" src="https://user-images.githubusercontent.com/13635059/230554521-5e8ca4a1-9a4f-4e75-a869-e9c32e305dbf.png">



# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# Twitter

https://twitter.com/YumaInaura
SetROBOのV2.0を試してみた（記録編）

# はじめに
こんにちは。社内PCのキッティングを担当している@Cheeeeeseです。

前回、 [SetROBO新バージョンV2.0がリリース](https://qiita.com/Cheeeeese/items/1c3db91286c5ecc0f694)された件を投稿しました。
ということで、今回はV2.0で新しく追加された<b>「UI記録モード」</b>を試してみたので紹介します。

前のバージョンでは、Windowsの設定画面とかアプリのインストールとか、ほぼ記録が取れないので、自分でコーディングしなきゃいけない状態でした。（過去記事参照→「[記録が取れない時の対処法](https://qiita.com/Cheeeeese/items/57905e67e8bdb575d301)」）
V2.0で新しく追加された<b>「UI記録モード」</b>では、ちゃんと記録が取れるようになっていたので、過去記事で記録が出来なかった「Acrobat Reader」のインストールのコードを改めて記録してみました。

# 環境
- Windows 11 Pro 22H2 (64bit)
- SetROBO for Kitting V2.0

# 今回やること
V2.0でAdobeの「Acrobat Reader」のインストールの操作を記録する。

「Acrobat Reader」のインストーラーはここから入手。
👉https://get.adobe.com/jp/reader/?promoid=TTGWL47M

# V1.9.5の場合
この画面がさっぱり記録が取れなかった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830862/5531dced-d261-f8e7-3e83-5768ed39bfca.png)

# V2.0で記録してみた
![記録.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830862/a0ed97be-14ad-bbf2-13cb-665487eabbe6.png)

<b>前のバージョンでは記録できないところも記録が出来ました！！</b>


コードを確認しましたが、ちゃんとコントロールの種類もCheckBoxとButtonで記録が取れていました。

ただ、今までだと記録したコントロールは「UI」が付かないらしいのですが、今回は付いてますね。
新しい「UI記録モード」だからでしょうか。

最後の「UIPane」は初めて見るコントロールでした。
Conductorのヘルプ曰く
> Paneコントロールは、複数のコントロールの枠組みとなるコントロールです。

とのこと。

ちょっとどういう時に使うか分からないけど、複数コントロールを選択する時に使うのかな。。。？
時間があったら使いところを調査してみます。

ただ、今回はEdgeを閉じたいだけなので、Closeで閉じたほうが良さそう。

## 記録したコードはきれいにしました
```ruby:SetROBO
using System;
using System.Drawing;
using Arp.Prova.ImageLibrary;
using Arp.Prova.CsvLib;
using Arp.Prova.PlayLib;

// CodeClass
public class CodeClass
{
	public static int CodeStart()
	{
		int waitTime = 1000;
		
		// 「Adobe Acrobat Reader DC」インストーラーを起動
		PlayLib.ProcessStart(@"C:\Tools\readerdc\readerdc64_jp_xa_crd_mdr_install.exe");
		
		if(PlayLib.WaitWindow("Adobe Acrobat Reader DC インストーラー", true, 60) == false)
		{
			PlayLib.TestLogError("「Adobe Acrobat Reader DC インストーラー」が起動できませんでした");
			return -1;
		}
		
		//「終了」ボタンが表示されるまで待機（Max 3分）
		if(PlayLib.Window("Adobe Acrobat Reader DC インストーラー").WaitUIControl("UIButton", "終了", true, 180) == true)
		{
			//「Adobe Acrobat Reader DC を起動」にチェックが入っているか確認
			if((int)PlayLib.Window("Adobe Acrobat Reader DC インストーラー").UICheckBox("Adobe Acrobat Reader DC を起動").GetProperty("Checked") == 1)
			{
				//「Adobe Acrobat Reader DC を起動」のチェックを外す
				PlayLib.Window("Adobe Acrobat Reader DC インストーラー").UICheckBox("Adobe Acrobat Reader DC を起動").Click();
				PlayLib.Sleep(waitTime);
			}
			
			//「終了」ボタンをクリック
			PlayLib.Window("Adobe Acrobat Reader DC インストーラー").UIButton("終了").Click();
			PlayLib.Sleep(waitTime);
			
			//ブラウザで「Adobe - Adobe Acrobat Reader DC をダウンロード」が起動されたら閉じる
			if(PlayLib.WaitWindow("Adobe - Adobe Acrobat Reader DC をダウンロード*", true, 60) == true)
			{
				PlayLib.Window("Adobe - Adobe Acrobat Reader DC をダウンロード*").Close();
			}
			
		}
		else
		{
			PlayLib.TestLogError("「Adobe Acrobat Reader DC」のインストールに失敗しました。")
			return -1;
		}

		return 0;
	}
}
```

# 最後に
V2.0の新しい「UI記録モード」はだいぶ強化されていました。
前のバージョンだとアプリのインストールとか設定は基本記録できなくて、ほとんど手動でコーディングしていたので、この強化は本当に助かります。


# 参考にしたサイト
- [SetROBO入門ガイド](https://www.setrobo.jp/entry_guide/files/SetROBO_EntryGuide_Ver3.0.pdf)
- Conductorヘルプ
RubyでAtCoder ABC287(A, B, C, D)を解いてみた

# はじめに
Webエンジニアを目指して、RubyやRailsをいじってます。
今回は、RubyでAtCoder ABC287のA, B, C, Dを解きました。備忘録として解き方をまとめていきたいと思います。

# A - Majority
https://atcoder.jp/contests/abc287/tasks/abc287_a
```ruby:a-287.rb
n = gets.to_i
array = Array.new(n){ gets.chomp }
puts array.count("For") > n / 2 ? "Yes" : "No"
```
## 解説
与えられた文字列で"For"が何回出現したかを調べ、過半数ならYesを、そうでなければNoを出力します。

# B - Postal Card
https://atcoder.jp/contests/abc287/tasks/abc287_b
```ruby:b-287.rb
# 修正前
n, m = gets.split.map(&:to_i)
s_array = Array.new(n){ gets.chomp }
t_array = Array.new(m){ gets.chomp }

ans = 0
n.times do|i|
    m.times do|j|
        if s_array[i][-3..-1] == t_array[j]
            ans += 1
            break
        end
    end
end
puts ans
```
```ruby:b-287.rb
# 修正後

n, m = gets.split.map(&:to_i)
s_array = Array.new(n){ gets.chomp }
t_array = Array.new(m){ gets.chomp }

puts s_array.count{ |s| t_array.include?(s[-3..])}
```
## 解説
問題文の通りに実装していけば解くことができます。

# C - Path Graph?
https://atcoder.jp/contests/abc287/tasks/abc287_c
```ruby:c-287.rb
n, m = gets.split.map(&:to_i)
array = Array.new(n + 1){[]}
m.times do
    u, v = gets.split.map(&:to_i)
    array[u] << v
    array[v] << u
end

if m != n - 1
    puts "No"
    exit
end

for i in 1..n
    if array[i].size > 2
        puts "No"
        exit
    end
end

searched = Array.new(n + 1, false)
searched[1] = true
stack = [1]
while node = stack.pop
    array[node].each do|factor|
        if !searched[factor]
            searched[factor] = true
            stack << factor
        end
    end
end

for i in 1..n
    if !searched[i]
        puts "No"
        exit
    end
end
puts "Yes"
```
## 解説
（公式解説を参考にしました）

<方針>
パスグラフとなるには、以下の３つの条件を満たす必要があります。
１）M=N-1である。
２）すべての頂点の次数が2以下である。
３）連結成分が１つである。

<実装方法>
上記の３つの条件を満たすかどうか順に調べていきます。なお、連結成分が１つであるかどうかは、DFSを使って調べています。

# D - Match or Not
https://atcoder.jp/contests/abc287/tasks/abc287_d
```ruby:d-287.rb
s = gets.chomp.chars
t = gets.chomp.chars
lt = t.size

pre = 0
lt.times do|i|
    break if s[i] != "?" && t[i] != "?" && s[i] != t[i]
    pre += 1
end

s = s.reverse
t = t.reverse

suf = 0
lt.times do|i|
    break if s[i] != "?" && t[i] != "?" && s[i] != t[i]
    suf += 1
end

(lt + 1).times do|i|
    if pre >= i && suf >= lt - i
        puts "Yes"
    else
        puts "No"
    end
end
```
## 解説
（公式解説を参考にしました）

preを先頭何文字までが一致させられるか、sufを末尾何文字までが一致させられるかを記憶する変数として更新していきます。最後に、preが先頭i文字より大きく、かつsufが末尾|T|-iよりも大きかったらYesを、そうでなければNoを出力します。

# 終わりに
C問題とD問題は、より条件やTLEを気にしながら実装していく必要があり難しかったです。

https://qiita.com/shoya15/items/50fd17e37d7c64f2b166
削除できない謎のVPCエンドポイントへの対応


AWS費用削減のため未使用のVPCエンドポイントを確認していたら、作った覚えのない謎のインターフェース型VPCエンドポイントがありました。

エンドポイントのNameタグは空欄で、インターフェース型で、サービス名は以下形式のものです。
```
com.amazonaws.vpce.ap-northeast-1.vpce-svc-XXXXXXXXXXXXXX
```


上記の形式だと、VPCエンドポイントのサービス名から用途を類推することができません。
さらに私の環境の場合デフォルトVPCのサブネット3つにENIを出しているようでなかなかの金食い虫構成です。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/6b6bab0b-f51e-e0fb-03b4-979ae429615e.png)

VPCエンドポイントの作成日から考えると、私がDB系サービスの検証を網羅的に実施していた時期です。
個人環境なので気軽にVPCエンドポイントを削除しようとしたところ、以下のエラーが出て削除に失敗しました。

```
vpce-XXXXXXXXXXXXXXX - Operation is not allowed for requester-managed VPC endpoints for the service com.amazonaws.vpce.ap-northeast-1.vpce-svc-XXXXXXXXXXXXXX.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/3566c6fb-8230-f585-1dce-99163d8a9ff4.png)

この操作を行ったIAMユーザの権限はAdministratorAccessであり、SCPの制限も入っていないので、権限上は問題ないはずです。

Webでエラー文字列を検索するとこのVPCエンドポイントは、AWSサービスがバックグラウンドで作成する「**リクエスタマネージド型VPCエンドポイント**」のようです。これはVPCの画面からではなく、**各利用サービス側のリソースを削除することでエンドポイントが削除される**もののようです。

https://repost.aws/ja/knowledge-center/vpc-delete-requester-managed-endpoint

> このタイプのエンドポイントを削除するには、エンドポイントを作成した AWS マネージドサービスを特定する必要があります。サービスを特定したら、エンドポイントを削除する前に、そのリソースを削除する必要があります。


上記サイトには作成後90日以内であれば、CloudTrailのイベント履歴からCreateVpcEndpoint API コールを検索することで、どのAWSサービスが作成したものであるか調べる方法が記載されています。

私の場合VPCエンドポイントは90日以上前の作成だったので、ClouTrailのイベント履歴では追うことができません。
代わりにAthenaやCloudtrail Lakeが必要になってきます。Athena系はちょっと面倒なので二の足を踏みます。

そこで上記サイトを見ていくと、現時点で「リクエスタマネージド型ネットワークインターフェイス」を作成するAWSサービスは以下の4種類のAWSサービスであるといえそうです。

- Network Firewall
- Aurora Serverless
- RDS Proxy
- Redshift

たしかにVPCエンドポイントの作成日時にこれらのサービスを触っていましたが、どれであるかは思い出せません。

いずれにせよ幸いなことに上記すべて検証は完了しており、サービスを消しても問題なかったため、上記サービスのリソースを一通り削除してみました。が、VPCエンドポイントが消えません。

AWSサポートに問い合わせをすると、 **Redshift Serverlessが怪しそう**との回答がありました。

マネジメントコンソールからRedshiftを開き、[Redshift Serverless]ダッシュボードに切り替えてから「ワークグループの設定」を見たところ、たしかにVPCエンドポイントの記載がありました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/ae6f4e80-f2d0-58fc-db4e-63eff46ffb0c.png)

たしかにRedshift Serverlessは、サーバレスと謳いながらもVPCのENIなどが必要になるサービスです。
当時触ったことも事実で、初期セットアップ時に「デフォルト設定を使用」したため、デフォルトVPCを対象にしたワークグループが作成されたようです。

ということで犯人がわかったので、defaultワークグループを削除してみます。
ワークグループの一覧画面に戻って対象のワークグループを選択し削除します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/8cf18821-2b08-760b-dd5d-5ed048770e48.png)

削除の際、チェックをいれることでワークグループが紐付けられた名前空間のほうも削除できるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/c15f1fed-8137-d20c-3794-c18e773fc79f.png)

ワークグループを削除すると、件のVPCエンドポイントも削除されます。

※なお、私は最初ワークグループの削除がいつまでたっても完了せず、数時間後にrootのメールアドレス向けにAWSサポートセンターから、内部エラーにより削除できなかった旨と、修正の連絡がきました。その後削除したところ、ワークグループの削除が正常に完了しました。(workgroup削除と同時にnamespaceの削除も実施していたのですがそちらは失敗しました）

Redshift Serverlessを長期的に使用しないときは、将来的な再利用状況を鑑みながら、workgroupを削除するという手もよいかと思います。
（それにしてもRedshift Serverlessはサーバレスと謳っておきながら、VPCリソースやコストを何かと消費しますね・・）

以上、同様の事象に遭遇した方向けのメモでした。
rust data struct説明--HashMap

# rust data struct説明--HashMap
New HashMap



#  std::collections::HashMap使用方法
### １create new hashmap
```rust
use std::collections::HashMap
let mut scores = hashmap::new()
```


### 2 insert value into hashmap
```rust
scores.insert(key,value)
```


### 3 get value
```rust
onwerkey = String::from(key)
scores.get(&onwerkey)
```
### 4 Hash Mapの完全使用方法
```rust
use std::collections::HashMap;


//#[derive(Hash)] attribute is used to automatically generate a Hash implementation for a struct or an enum.
#[derive(Hash)]
struct Persion {
    first_name: string,
    last_name: String,
}

impl Person {
    fn new(first: &str, last: &str) -> Self {
        Person {
            first_name; first.to_string(),
            last_name: last.to_string(),
        }
    }
}

fn main() {
    let mut book = HashMap::new();
    book.insert(Person::new("John","smith","521-8976"));
    book.insert(Person::new("sandra","dee","521-9655"));
    book.insert(Person::new("Ted","smith","418-4165"));

    let P = Person::new("John","Smith");
    if let Some(phone) = book.get(&p) {
        //find the book that person haved
        println!("Phone number found: {}", phone);
    }
    //delete recode
    book.remove(&p)

    println!("Find Key:{}",book.contains_key(&p));




}

```




### ５ entry Apiの使用と基本使用の違う
```rust
// 基本使用,2回操作、１キーの確認２キーを増加
if map.contains_key(key) {
    map.insert(key,value);
}

//entry apiの使用
map.entry(key).or_insert(value)
```


### 6 hashmap中にキーの変更がダメです
```rust
use std::hash::{Hash, Hasher};
use std::collections::HashMap;
use std::cell::Cell;

#[derive(Eq, PartialEq)]
struct BadKey {
    value: Cell<i32>
}

impl BadKey {
    fn new(v: i32) -> Self {
        BadKey { value: Cell::new(v)}
    }
}

impl Hash for BadKey {
    fn hash<H: Hasher>(&self, state: &mut H){
        self.value.get().hash(state);
    }
}
fn main() {
    let mut map = HashMap::new();
    map.insert(BadKey::new(1),100);
    map.insert(BadKey::new(2),200);
    for key in map.keys() {
        key.value.set(key.value.get() * 2);
    }
    println!("Find key 1:{:?}",map.get(&BadKey::new(1)));
    println!("Find key 2:{:?}",map.get(&BadKey::new(2)));
    println!("Find key 4:{:?}",map.get(&BadKey::new(4)));

}
//response
//Find key 1:None
//Find key 2:None
//Find key 4:None
```
結果;変更のキーと変更しないのキーは探さない、その原因でkeyはdynamic    keyはダメです

### ７     存在のリストを付いて、Hash Mapを作成します


###                                                                                                                                                  
```rust
use std::collections::HashMap;
let solar_distance = HashMap::from([
    ("Mercury", 0.4),
    ("Venus", 0.7),
    ("Earth", 1.0),
    ("Mars", 1.5),
]);
```
8別の方法



```rust
use std::collections::HashMap;
let a  = HashMap::new()

a.is_empty()
a.len()
//
a.drain().drain()(.take(1)
a.keys()
a.containers_key()
a.remove(&mut value) --> Removes a key from the map, returning the value at the key if the key was previously in the map.
a.remove_entry(&Mut value) -->Removes a key from the map, returning the stored key and value if the key was previously in the map.


```
Githubからクローンしたリポジトリを削除する方法（VB.NET）

# やりたいこと
Githubからクローンしたリポジトリを削除したい。

# 回答
1. ソリューションエクスプローラーを確認する。
2. 削除したいソリューションまたはファイル上で右クリックする。
3. 「エクスプローラーでフォルダを開く」を選択する。
4. エクスプローラー上の関連するファイルを削除する。
![無題5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3079289/d67fcaf2-6b3b-8d4d-4192-fccfc6bae8e0.png)
![無題4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3079289/fe5c89d9-1c94-f6b8-e3d2-5045d068d53f.png)
※削除する際、対象のファイルがvisual studioやその他のソフトで開いている場合は、閉じてから削除する必要があります。

# 注意事項
- Githubからクローンしたファイルが対象になります。
- そのためGithubからクローンしていないファイルを削除した場合、ファイルを復元できなくなる恐れがありますので、最新の注意を払ってください。

# この記事は誰向けの記事か？
- クローンしたリポジトリを削除したい人

# 環境
- OS:Windows10
- 開発プラットフォーム:visual studio 2022

# 本記事における注意事項
- 本記事は、備忘録としてまとめたものになります。
- 他の方の参考になる可能性も踏まえて、一般公開も行なっております。
- また記載内容はすべて、正しい内容が記載されているとは限りません。
- 誤った内容を見つけた場合は、ご指摘をお願いいたします。
FortiGate config自動バックアップ

# 設定方法
- GUIの設定例を記載
- 参考元: https://community.fortinet.com/t5/FortiGate/Technical-Tip-How-to-send-automated-backups-of-the-configuration/ta-p/198364

### Fortigate
1. ダッシュボード -> オートメーション
2. 新規オートメーションスケッチ
    - 名前: 任意
    - トリガー: スケジュール
        - 頻度、トリガー時刻/分を設定  
    - アクション: CLIスクリプト
        - 最小間隔(秒): 0
        - CLIスクリプト
            - 名前: 任意
            - Administrator profile: 実行時のユーザ名
            - スクリプト(tfptの場合): `execute backup config tftp backup.config {TFTP server ip}` 
                - tftpサーバには、 `backup.config` をあらかじめ作成しておくこと
3. OK押下


### その他
- バックアップ方法には、TFTP以外もFTPなどあり
参考: https://docs.fortinet.com/document/fortigate/6.4.0/best-practices/262994/performing-a-configuration-backup

---
Java 型変換とキャスト

## 型変換とキャスト
・原則異なる型同士の演算はできない
EX)　int a + long b　みたいなん
int a = 10;
long b = 20;
System.out.print(a+b);
→ビット数が合わないから
　　intは32ビット、longは64だからintが32ビット分足りてない
「暗黙の型変換」ってのがあるんやとさ
コンパイラが足りてないビットを追加して自動的に大きいほうに型を変えるから
ちょっと詳しくゆうと
　2進数の世界では、
　　０は値がない
　　１は値がある
　だから32ビットしかないintに不足してる32ビット分の０を足しても元の値が変わることはない
だから↑のa + bは成立する
ただビット数の大きいlongに合わせてるからlongで返ってくる（下の例）
＜error case＞ int + long = int
int a = 10;
long b = 20;
int result = a+b; ←エラー（intでは返せません）

## 型変換とキャスト２
・キャスト：プログラマーがビットを削っても問題ないと保証する
→変換したい型を（）でくくる
long a = 1;
int b = (int) a; ←longをintにしてる
Java 基礎

## クラス
- 1つのプログラム部品
- 1クラスにつき1ファイル

→クラスがいくつも連携して大きいプログラムを動かしてる

## コード
コードには

- 実行するための命令：ステートメント
- 実行中に扱うデータ：リテラル

の2種類がある
ステートメントは；で終わらす

## 変数
- 変数名：メモリアドレスにつける別名（人間にわかる）
- 変数：変数名を使って実際のメモリアドレスを参照し、そこにあるデータにアクセスする仕組み
- メモリ：データサイズに対して大きすぎるもの（宇宙の感覚）

この大きい空間を碁盤の目のように区切ってる感じ
でそこにメモリアドレスという場所を特定するものがふられている
ーこんな感じー
メモリってゆう大きい場所があって、そこにデータをほり込める
そのメモリは大きすぎるからすぐデータの場所がわかるようにメモリアドレスがある
でもそのメモリアドレスは意味の分からない英数字であらわされてる
それはさすがにわかりにくいから、変数名ってゆう人間が名前を付けましょう！って感じ
→どんなデータかわかりやすいですよね
こうやって人間が名付けた名前でデータを読んできて、データをゲットする仕組みを変数という

### 変数のデータ型
変数を扱うとき
→データ型と変数名をセットで宣言
EX)3と3.0はコンピュータの立場では別物
```
3   11
3.0 10000000000100000000000000000.....
```
2進数であらわすからこのくらい違う
データ型の種類
- プリミティブ型
- オブジェクト型
- 列挙型

### 変数の宣言
#### 変数の宣言とは
→どんな名前の変数を使うつもりなのか（変数名）
　その変数名がつけられたメモリ領域に入った値をどのように扱うのか（データ型）
変数は宣言しただけでは使えない
　　値を代入してやっと使える
#### 変数の初期化
→変数に初めて値を代入すること

### 文字列
String：何文字でも
char：1文字だけ
代入する文字列は""でかこむ

#### 定数の宣言
定数：値を変更できない変数
→finalを使う

＜case1＞値変更
```java
public static void main(String [] args){
   final int num = 10;
   num = 20;　　　　←エラー（値の変更はできない）
}
```
↑の場合 num = 20でコンパイルエラー
finalで修飾してるnumの値を変更しようとしてるから

＜case2＞宣言だけ記述
```java
final int num;
num = 20;　　　　　←初期化なのでエラーにはならない
num = 10;　　　　　←エラー）
```
↑の場合num = 10でコンパイルエラー
初期化せずに宣言だけを記述することはできる
一度初期化したらそれ以降は値を変更できない

↓↓↓↓↓↓↓ あなたの記事の内容

───────
## 型変換とキャスト
- 原則異なる型同士の演算はできない

EX)　int a + long b　みたいなん
```java
int a = 10;
long b = 20;
System.out.print(a+b);
```

→ビット数が合わないから
　　intは32ビット、longは64だからintが32ビット分足りてない
「暗黙の型変換」ってのがあるんやとさ
コンパイラが足りてないビットを追加して自動的に大きいほうに型を変えるから
ちょっと詳しくゆうと
　2進数の世界では、
　　０は値がない
　　１は値がある
　だから32ビットしかないintに不足してる32ビット分の０を足しても元の値が変わることはない
だから↑のa + bは成立する
↑↑↑↑↑↑↑ 編集リクエストの内容
ただビット数の大きいlongに合わせてるからlongで返ってくる（下の例）

＜error case＞ int + long = int
```java
int a = 10;
long b = 20;
int result = a+b;    ←エラー（intでは返せません）
```

### 型変換とキャスト２
- キャスト：プログラマーがビットを削っても問題ないと保証する

→変換したい型を（）でくくる
```
long a = 1;
int b = (int) a;   ←longをintにしてる
```
【S3】レプリケーションに失敗したオブジェクトはどうなる？

# その他
レプリケーションに失敗したオブジェクトはどうなる？
・再試行されずに失敗ステータスを維持
・バッチレプリケーションで対処する
・イベント通知は発行される
> 以前にレプリケートに失敗したオブジェクトをレプリケートする
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/replication.html#batch-replication-scenario
オブジェクトをアップロードした後で、オブジェクトのレプリケーションに失敗した場合、レプリケーションを再試行できません。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/replication-status.html#replication-status-overview
>オブジェクトをレプリケート先バケットに手動でコピーした場合、Amazon S3 インベントリレポートと HeadObject API 呼び出しは、レプリケート元バケットのオブジェクトに対して FAILED レプリケーションステータスを引き続き返します。
https://repost.aws/ja/knowledge-center/s3-list-objects-failed-replication
>Amazon S3 イベント通知によるレプリケーション失敗イベントの受信
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/replication-metrics.html#replication-metrics-events


ソースでオブジェクトアップロード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969819/9d55f51f-fde5-8c54-6519-db2c86d34386.png)
失敗（機能しないIAMロールをレプリケーションルールに設定した）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969819/c5655f59-febb-d7f1-f8a2-8cb55abef95d.png)
バッチオペレーションを設定してジョブ完了後に再度オブジェクトを確認
※バッチオペレーション用のIAMロールの作成をしておいてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969819/b183c62a-39f3-d878-868f-742bac4c351a.png)

ターゲットのバケットを確認するとREPLICAとステータスに記載されておりレプリケーションされていることがわかる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969819/04660626-8a55-0c46-94fd-b720c53bd4d0.png)
kotlin serializationでMapをdecodeする

Jsonをデコードする記事はたくさん有りましたが、今回は`Map`を`data class`に変換する方法を紹介します。

Mapを変換するために基本の依存関係の他に以下の依存関係が必要です。

```gradle
implementation "org.jetbrains.kotlinx:kotlinx-serialization-properties:バージョン"
```

Mapから以下のようなモデルに変換する例を考えます。
```kotlin
@Serializable
data class Employee(val id:Int, val name: String) {}
```

デコードするためのコードはこんな感じ。

```kotlin
val map:Map<String,Any> = mapOf(
    "id" to 1,
    "name" to "sekitaka_1214"
)
val employee = Properties.decodeFromMap<Employee>(map)
Log.d("Employee",employee.toString())
```

以下のようにMapが意図したEmployeeクラスのインスタンスにデコードされています。

```
Employee(id=1, name=sekitaka_1214)
```
MACアドレステーブルとARPテーブルの違い

# MACアドレステーブル
- MACアドレスとポートを結びつけるテーブル
- L2レベルの制御を行う機器に搭載
- スイッチング機能を持つ機器に搭載される

# ARPテーブル
- IPアドレス↔MACアドレスの対応表
- L3ルーティングを行う機器に搭載
[PHP]文字列の連結

# 文字列連結の方法
phpでの文字列の連結は、**.(ドット)**を用いる。
```php:例
$x = "PHP";
echo $x."入門";
//結果：PHP入門

//以下の通り、省略して書くことも可能
$x = "PHP";
$x = $x."入門";
→ $x .= "入門";
echo $x;
//結果：PHP入門
```

<strong>""(ダブルクォーテーション)</strong>で囲んだ文字列の中に、
{}で囲った変数を記載すると、変数の内容が代入されている値に置き換わる。

```php:例
$name = "太郎";
echo "私の名前は、{$name}です";
//結果：私の名前は、太郎です
ギフティファイの概要

Giftify（ギフティファイ）とは
Giftify（ギフティファイ）は、東京都豊島区に拠点を置くギフトキュレーション・サイトソリューション事業などを行っている。


公式ホームページはこちら
https://giftify.jp/

概要
サービス名：Giftify（ギフティファイ）
創設年：2021年（令和3年）7月

沿革
2021　7/10　Giftify 百貨店・総合スーパーのギフトキュレーションサイト開設
2021　10/17　月間5,000PV突破
2021　10/27　月間10,000PV突破
2021　12/11　月間20,000PV突破
2021　12/15　月間30,000PV突破
2021　12/20　月間40,000PV突破
2021　12/24　月間50,000PV突破
2021　12/29　月間60,000PV突破
2022　2/4　１日に5,936ユーザーが訪問と高騰！
2022　2/5　１日に8,033ユーザーが訪問と高騰！
2022　2/6　累計200,000PV突破！
2022/8/30　WEBサイトが月間85,000PVを突破！
2022/9/30　WEBサイトが月間180,000PVを突破！
2022/10/30　WEBサイトが月間500,000PVを突破！
2022/10/30　WEBサイトの月間アクティブユーザーが200,000人を突破！
2022/12/30　WEBサイトが月間700,000PVを突破！



事業内容
Giftifyはお中元（夏ギフト）・お歳暮（冬ギフト）・母の日・父の日・敬老の日などのプレゼントから内祝い・出産祝い、季節催事ではクリスマス、おせち、恵方巻、バレンタイン、ホワイトデー、ランドセルまでシーンごとのおすすめ・人気の商品・カタログギフトを提供する百貨店・総合スーパーで取り扱うギフトキュレーションサイト。
今月の何週目かを月曜日起点に計算する


+ [数式項目　今月の何週目かを月曜日起点で表したい](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000OtNJlSAN)

```
CASE(WEEKDAY(DATE(YEAR(TODAY()),MONTH(TODAY()),1)),
2,
IF(DAY(TODAY()) -1 <=6 ,  1,
IF(DAY(TODAY()) -1 <=13 , 2,
IF(DAY(TODAY()) -1 <=20 , 3,
IF(DAY(TODAY()) -1 <=27 , 4,
5)))),
3,
IF(DAY(TODAY()) -1 <=5 ,  1,
IF(DAY(TODAY()) -1 <=12 , 2,
IF(DAY(TODAY()) -1 <=19 , 3,
IF(DAY(TODAY()) -1 <=26 , 4,
5)))),
4,
IF(DAY(TODAY()) -1 <=4 ,  1,
IF(DAY(TODAY()) -1 <=11 , 2,
IF(DAY(TODAY()) -1 <=18 , 3,
IF(DAY(TODAY()) -1 <=25 , 4,
5)))),
5,
IF(DAY(TODAY()) -1 <=3 ,  1,
IF(DAY(TODAY()) -1 <=10 , 2,
IF(DAY(TODAY()) -1 <=17 , 3,
IF(DAY(TODAY()) -1 <=24 , 4,
IF(DAY(TODAY()) -1 <=31 , 5,
6))))),
6,
IF(DAY(TODAY()) -1 <=2 ,  1,
IF(DAY(TODAY()) -1 <=9 ,  2,
IF(DAY(TODAY()) -1 <=16 , 3,
IF(DAY(TODAY()) -1 <=23 , 4,
IF(DAY(TODAY()) -1 <=30 , 5,
6))))),
7,
IF(DAY(TODAY()) -1 <=1 ,  1,
IF(DAY(TODAY()) -1 <=8 ,  2,
IF(DAY(TODAY()) -1 <=15 , 3,
IF(DAY(TODAY()) -1 <=22 , 4,
IF(DAY(TODAY()) -1 <=29 , 5,
6))))),
IF(DAY(TODAY()) -1 <=0 ,  1,
IF(DAY(TODAY()) -1 <=7 ,  2,
IF(DAY(TODAY()) -1 <=14 , 3,
IF(DAY(TODAY()) -1 <=21 , 4,
IF(DAY(TODAY()) -1 <=28 , 5,
6)))))
)
```

# 考え方

月の1日が何曜日なのかによって、1日と各日の差が各週で最大値が違うことを利用する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/f9eaedbe-f999-c3b8-94c5-d294e895a17f.png)
Node.js: ChatGPT API の使い方

こちらと同じことを Node.js ES Modules で行います。
[Python3: ChatGPT API の使い方](https://qiita.com/ekzemplaro/items/3337acb377427337dd8b)

## プログラム

```js:fujisan.mjs
#! /usr/bin/node
// ---------------------------------------------------------------
//	fujisan.mjs
//
//					Apr/07/2023
//
// ---------------------------------------------------------------
console.error ("*** 開始 ***")

import { Configuration, OpenAIApi } from "openai";

const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});

const openai = new OpenAIApi(configuration);

const completion = await openai.createChatCompletion({
  model: "gpt-3.5-turbo",
  messages: [{role: "user", content: "富士山の高さは"}],
});
console.log(completion.data.choices[0].message);


console.error ("*** 終了 ***")


// ---------------------------------------------------------------
```

実行スクリプト

```bash:fujisan.sh
export OPENAI_API_KEY="sk-"11******************************************"
./fujisan.mjs
```

## 実行結果

```text
$ ./fujisan.sh
*** 開始 ***
{ role: 'assistant', content: '3776メートルです。' }
*** 終了 ***
```

## 確認したバージョン

```text
$ node --version
v19.8.1
```
[TouchDesigner] Dependency Objectsを使ってPython内の数値をネットワーク側に同期する

## はじめに

私は展示系インスタレーションなどのシステム開発をTouchDesignerで行うことがありますが、状態管理や外部との通信等のためにPythonを書くことが多いです。

（以前は `Table DAT` に必要な情報を全部入れていたのですが、システムが複雑になると管理がしんどくなってしまいました。）

そんな中、Python Class内の変数を、ネットワーク側のoperatorにも反映したくなることが多く発生します。 

:::note warn
この場合、例えば `Evaluate DAT` などでClass内の変数を引っ張ってくることができますが、これだけだと、変数の値が変更されたときに、ネットワーク側でcookが走らず、値が同期できなくなってしまいます。（なんて不便だ…）
:::

これまでは、下記みたいにPython側に直接オペレータを変更する処理を書いていましたが、

```py
op('text1').text = self.property
```

これだとノードが繋がらないため、あとでネットワークを見返した時に
- このoperatorが一体どこのスクリプトで変更されているか？

がわかりにくく、確認に時間がかかってしまいます。

数個だけなら良いですが、大量に発生すると、メンテナンスする場合にとても大変です。

## 解決策

すばり、 `Dependency Objects` というものがあります。

https://docs.derivative.ca/Dependency

>Because Python does not inherently have a procedural mechanism, Dependency Objects in TouchDesigner allow python data to cause downstream cooking when that data is changed.

`Dependency Objects` を使うことで、Python側の数値が変わったときにダウンストリームのcookが走るようになる。と書いてあります。ありがたい。

## サンプルプログラム

こちらにアップしました。
https://github.com/genkitoyama/TD_Dependency_sample

## 使い方

ネットワーク側で参照したい変数に、 `tdu.Dependency(初期値)` と入れればOKです。これだけ。

例えば、 
- `int` なら `tdu.Dependency(0)`
- `string` なら `tdu.Dependency('')`
- `list` なら `tdu.Dependency(['hoge', 'fuga'])`

などなど。

中身の値を get/set するには、 `val` メンバを使います。

```py
hoge = tdu.Dependency(0)
print(hoge) #type:Dependency val:0
print(hoge.val) #0

hoge.val += 1
print(hoge.val) #1
```

下図は、サンプルプログラムをキャプチャしたgifです。
ボタンを押すと、Class内の変数がインクリメントされ、Textportに出力されます。

![output-palette.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109722/a7463e83-c0ed-2129-d072-dd17b7e7b269.gif)


右下に `Evaluate DAT` が2種類あり、Dependencyの使用有無で比較をしています。

-  `parent().ValueA` （Dependencyを使っていない）
    -  ❌ ボタンを押しても数値が増えず、値の同期ができていません。

- `parent().ValueB.val` （Dependencyを使っている）
    - ✅ ボタンを押すと数値が増え、値の同期ができています。

## callbacks

TouchDesigner2022以降では、新たに `callbacks` メンバが追加されたので、値が変更された時に、簡単にコールバック関数を設定することができます。

まだあまり触れていないので、これから触ってみようと思います。

https://docs.derivative.ca/Dependency_Class

## 注意点

:::note warn
`list` , `dictionary` , `set` などの mutable なオブジェクトに関しては、個々の要素を変更しただけでは、cookが発生しません。
その場合は、 `modified()` というメソッドを呼ぶ必要があります。

:::

以下、公式からの引用です。

https://docs.derivative.ca/TDStoreTools#Deeply_Dependable_Collections

>Dictionaries, lists, sets, and other objects with changing contents are called "mutable" objects. If the contents of a mutable object changes, a dependency wrapping it does not automatically register as being modified! You must either use a deeply dependable collection or call the .modified() function manually as in the example below:

```py
op('comp1').Scale.val = [1,2,3]
op('comp1').Scale.val[1] = 15 # changing the contents doesn't update dependency!
op('comp1').Scale.modified() # this call notifies TouchDesigner that the value has changed
```

## おわりに

ぶっちゃけ、きっちりPython書かずとも、ノードベースで実装してしまえば良いのでは？ってなるのですが、

例えば常設のアプリケーションなど、後々のメンテナンスが必要なものにおいては、
なるべくコードベースで実装した方が、メンテナンスやデバッグが楽になるのでは？という気持ちです。

（遥か昔にエイヤで作ったノードまみれのシステムの不具合修正をしようとした時は、なかなかに絶望を感じました、、）

時と場合に応じて、使い分けると良いのかなと思いました。

## おまけ
ちなみに、上記デモでは、TDの `Comp Extension` という機能を使っています。
個人的にはわかりやすいです。

詳しくは下記を参考にしてください。

https://qiita.com/joe0hara/items/de0f91e8886c116504b3
[PHP]改行の方法

# はじめに
PHPにて改行をする際の方法について、まとめる。

# 改行について
ソースコード上での改行と、ブラウザ上での改行で方法が違うことに注意。
基本は、
```「"\n"」の挿入でソースコード上の改行```
```「"<br/>"」の挿入でブラウザ上での改行```
を指す。

```php:改行方法
for ($i=1; $i<=100; $i++){
 echo $i."<br>";
}

もしくは

for ($i=1; $i<=100; $i++){
 echo $i;
 echo "<br>";
}
```

省略して書く方法や、ソースコードでもブラウザでも両方に改行を改行を反映させる方法など、色々あるが、詳しいところは下記記事を参照してください。

https://it-kyujin.jp/article/detail/1066/#:~:text=%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E4%B8%8A%E3%81%A7%E3%81%AE%E6%94%B9%E8%A1%8C%E3%81%AB%E3%81%AF%E3%80%81PHP%E3%82%B3%E3%83%BC%E3%83%89,%E3%81%95%E3%81%9B%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82
LF-TBAC 方式のクロスアカウント共有のバージョン3ではAWS RAMを使用するようになった

## バージョン2まではどうだった？
クロスアカウント共有をしたい場合、バージョン2までは以下の方法でした。
- 名前付きリソース方式：AWS RAMを**使う**
- タグベース：AWS RAMは**使わない**

バージョン3からは、タグベースでもRAMを使うようになりました。
それ以外の変更点などについては、以下の公式ドキュメントに詳しく載っています。

https://docs.aws.amazon.com/ja_jp/lake-formation/latest/dg/optimize-ram.html#:~:text=%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%A7%E3%81%99%E3%80%82-,%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%203,-%E5%90%8D%E5%89%8D%E4%BB%98%E3%81%8D%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9

## タグベースのクロスアカウント共有する際の手順は、バージョン3でどう変わる？
大きく2点です。
1つ目は、Glue Data Catalog リソースポリシーの修正が不要になります。
公式ドキュメントで言うと、以下の部分です。

https://docs.aws.amazon.com/ja_jp/lake-formation/latest/dg/cross-account-prereqs.html#:~:text=LF%2DTBAC%20%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%9F%E3%83%87%E3%83%BC%E3%82%BF%E5%85%B1%E6%9C%89%E3%81%AE%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6

こちら、ポリシーを自分で変更する必要がなくなるのはかなり嬉しいですね。

2つ目は、今回の記事にあるAWS RAMの承認の実施です。
データ共有を受けた方（Consumer側）は、AWS RAMのコンソールから「承認」する必要があります。
以下は、実際にAWS RAMで承認を実施した時の画面です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2949357/a009c5f1-983c-c141-ea1e-f1e9f63bd472.png)

## おわりに
これを知らずに、なぜかクロスアカウント共有してもConsumer側で共有したリソースが見れず、30分くらい格闘していました･･･。
ただ、知ってしまえばRAMでささっと承認するだけなので楽ですね。
データメッシュなどのアーキテクチャが流行ってくると、クロスアカウントでのデータ共有は必須になってくると思うので、Lake Formationはもうちょっと色々試してみたいと思います。