OmniAICreator/Qiita-1.07M huggingface dataset:
IAMの運用プラクティス

AWSブラックベルトオンラインセミナーを参考に、IAMの運用についてのプラクティスをチェックリストにして残しておきます。

## 認証情報の管理
* ルートユーザーを使用しない
* IAMユーザーには強力なパスワードポリシーを設定する
* アクセスキーを複数人で共有しない
* 次のような場所にアクセスキーを置かない
    * Githubリポジトリ
    * AMIの中への埋め込み
    * 設計書等のドキュメント内
* 特権ユーザーに対してはMFAを必須化するポリシーをアタッチする
* EC2で実行するアプリケーションからAWSへのアクセスにはアクセスキーではなくIAMロールを使用する

## アクセス権限の管理
* メンテナンスが発生するインラインポリシーではなく再利用性の高いカスタマー管理ポリシーを利用する
* ポリシーのCondition要素によってポリシーが有効になる条件をさらに絞り込む
    * リクエストを許容するソースIPアドレスの範囲
    * 日付または時間の範囲
    * MFAデバイスでの認証の要求
    * SSL使用の要求
* IAMポリシーを作成する場合、権限は最小にする。広範囲な権限を後から絞るのではなく、最小限のアクセス権限から開始し、必要に応じて権限を追加する
* IAMユーザーへの権限はIAMユーザーに直接付与するのではなくIAMグループを使用する

## 認証情報とアクセス権限の継続的な評価
* CloudTrail、AWS Config、S3等のロギング機能でAWSアカウントのアクティビティを監視し、権限が適切かどうか継続的に評価する
    * アクション日時
    * アクションのソースIP
    * 不適切なアクセス許可のために失敗したアクション等
* IAMのアクセスアドバイザーを使い、IAMエンティティ（ユーザー、グループ、ロール）の最終アクセス日時を確認し、不要な権限が与えられていないか確認する　
* IAMの認証情報レポートを使い、不要な認証情報を削除する
* 認証情報（IAMユーザーのパスワード、アクセスキー）は定期的にローテーションする
    * IAMユーザーにはパスワードに有効期限を設け、ユーザーに定期的にパスワード変更を求める
    * アクセスキーは2つまで作成できる。安全なローテーションのため、新しいものを作成し、古いキーは認証情報レポートでアクセスがないことを確認してから無効化する

## 参照
* [【AWS Black Belt Online Seminar】AWS Identity and Access Management (AWS IAM) Part1](https://www.youtube.com/watch?v=K7F5yTThynw&ab_channel=AmazonWebServicesJapan%E5%85%AC%E5%BC%8FAmazonWebServicesJapan%E5%85%AC%E5%BC%8F)
* [【AWS Black Belt Online Seminar】AWS Identity and Access Management (AWS IAM) Part2](https://www.youtube.com/watch?v=qrZKKF6V6Dc&ab_channel=AmazonWebServicesJapan%E5%85%AC%E5%BC%8FAmazonWebServicesJapan%E5%85%AC%E5%BC%8F)
* [IAM でのセキュリティのベストプラクティス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html)
* [AWS アクセスキーを管理するためのベストプラクティス](https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-access-keys-best-practices.html)
JP1/AJSのジョブエラー（実行結果）をJP1/IMを使わずに監視する

JP1/AJSのジョブエラー（実行結果）を監視する場合、JP1/IM-Managerを利用してJP1イベントを監視しているケースが多いと思います。
ですが、システムのクラウド移行等を契機として、クラウドのマネージドサービスやZabbix等のJP1以外のツールに監視を移行するケースが多くなっていると感じます。
そこで、ジョブエラーをJP1イベントではなく、一般的なログ監視（ログファイルやイベントログ）で監視する方法をまとめます。
***

#◆スケジューラーログを監視する場合
スケジューラーログとは、JP1/AJS3のスケジューラーサービスが出力する運用管理者向けのログ情報のことです。
ジョブネットやジョブの実行開始・実行終了および操作に関する情報が出力されます。

ログのパス等の情報は以下マニュアルを参照。

http://itdoc.hitachi.co.jp/manuals/3021/30213D2620/AJSK0009.HTM

###設定方法
デフォルトで出力されるため、結果出力についての設定は不要です。
監視したい実行結果のメッセージID（ジョブ異常終了であればKAVS0265-E）を検知対象とすればOKです。
スケジューラーログは2つのログファイルがローテーションする仕組みとなっているので、監視側の設定で注意が必要かもしれません。
***



#◆イベントログ・syslogを監視する場合
Windowsはイベントログ、Linuxはsyslogにジョブ実行結果を出力させて監視することができます。

###設定方法
AJSの環境設定パラメーターの設定で、監視したい実行結果を出力するように設定します。
異常や警告、遅延など、監視したい結果を指定して出力設定する必要があります。
監視したい実行結果のメッセージID（ジョブ異常終了であればKAVS0265-E）を検知対象とすればOKです。

ジョブの監視とジョブネットの監視はそれぞれ別に設定が必要です。

シングル構成、スケジューラーサービス名がAJSROOT1の場合の実行例は以下です。
異常終了、警告終了以外を出力させたい場合はマニュアルを参照してパラメータを追加して実行してください。


####①ジョブの異常終了と警告終了をイベントログ・syslogに出力させる場合

```
>jajs_config -k [JP1_DEFAULT\JP1AJSMANAGER\AJSROOT1] "JOBSYSLOG"="abend:warning"
KAVS1100-I コマンド（jajs_config）が正常に終了しました
```
#####マニュアル参照箇所
http://itdoc.hitachi.co.jp/manuals/3021/30213D2420/AJSX0332.HTM#d37e79624


####②ジョブネットの異常終了と警告終了をイベントログ・syslogに出力させる場合


```
>jajs_config -k [JP1_DEFAULT\JP1AJSMANAGER\AJSROOT1] "NETSYSLOG"="abend:warning"
KAVS1100-I コマンド（jajs_config）が正常に終了しました
```
#####マニュアル参照箇所
http://itdoc.hitachi.co.jp/manuals/3021/30213D2420/AJSX0332.HTM#ynetsyslog




###補足
jbsgetcnfコマンドでも設定は可能です。（パラメータを記載した定義ファイルを作成する必要あり）
jajs_configはパラメーター名および定義内容に誤りがある場合にコマンドが異常終了するのでこちらを推奨します。
python実行環境の構築メモ


# pythonのインストール

## pyenvのインストール
```
$ brew install pyenv
```

## pyenvのバージョン確認
```
$ pyenv -v
```

## bashかzshかを判断
```
$ echo $SHELL
※この実行結果が/bin/bashであれば、.bash_profileを設定し、
 /bin/zshであれば、.zshrcを設定する。
 以降では.bash_profileを前提とする。
```

## .bash_profileの設定 & ロード
```
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT:$PATH"' >> ~/.bash_profile

$ source ~/.bash_profile
```

## pythonインストール
```
$ pyenv install --list

$ pyenv install 3.7.7  （←バージョンは適宜変更）
```

## pythonバージョン切り替え
```
$ pyenv versions
* system
  3.7.7

$ pyenv global 3.7.7

$ pyenv versions
  system
* 3.7.7

$ python -V
Python 3.7.7
```

# 仮想環境構築

## 仮想環境作成
```
$ cd <リポジトリフォルダ>

$ python -m venv myVenv
```

## 仮想環境の有効化
```
$ source myVenv/bin/activate
```

## 仮想環境へ requirements.txt のパッケージインストール
```
$ cd <該当フォルダ>
$ pip install -r requirements.txt
```

## インストール済みのパッケージの確認
```
$ pip list
$ pip freeze  # requires.txt 形式で表示する
```

## VScodeのpythonデバッガから仮想環境のpythonから実行したい場合、コマンドパレットを開き、Select Interpreterを実行
![スクリーンショット 2024-04-29 12.15.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397664/5f7a8811-26ce-14a4-8da6-a2d7f9105cb5.png)


## 仮想環境の無効化
```
(myVenv) $ source deactivate
```

## tips
### pycharmの仮想環境設定
[PyCharm]-[Preferences]-[<該当プロジェクト>]-[Python Interpreter]
にて、
<仮想環境を作成した該当リポジトリ>/myVenv/bin/python
を指定する。
column does not exist

このようなエラーメッセージが出ました。
![スクリーンショット 2021-07-06 17.50.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1286751/05662d6b-9949-68b9-7ff4-d405e5c0a94b.png)

family_name_kanaが存在しないと仰ってます。familyname_kanaが関係しているのではないかとも仰ってます。

とりあえずmigrationファイルを確認。


```20257608489764_create_customer.rb
class CreateCustomers < ActiveRecord::Migration[6.0]
  def change
    create_table :customers do |t|
      t.string :email, null: false
      t.string :family_name, null: false
      t.string :given_name , null: false
      t.string :familyname_kana, null: false
      t.string :given_name_kana, null: false
      t.string :gender 
      t.date :birthday
      t.string :hashed_password

      t.timestamps
    end

      add_index :customers, "LOWER(email)" ,unique: true
      add_index :customers, [ :family_name_kana, :given_name_kana]
    
  end
end
```

```20257608489764_create_customer.rb
class CreateCustomers < ActiveRecord::Migration[6.0]
  def change
    create_table :customers do |t|
      t.string :email, null: false
      t.string :family_name, null: false
      t.string :given_name , null: false
      t.string :family_name_kana, null: false   #<=修正
      t.string :given_name_kana, null: false
      t.string :gender 
      t.date :birthday
      t.string :hashed_password

      t.timestamps
    end

      add_index :customers, "LOWER(email)" ,unique: true
      add_index :customers, [ :family_name_kana, :given_name_kana]
    
  end
end
```
修正して rails db:resetコマンドを入力するも、解消せず。同様のエラーメッセージが。

vscodeの検索窓でfamilyname_kanaを入力。複数のファイルが引っかかったのですべて修正して再びresetコマンドを入力。無事に解消しました。
Discovery Premiumやってみた　〜タイトルにリンクをつける編〜

Watson DiscoveryのPremiumプランで提供されているコンテンツ・マイニングに関する記事です。
今回はタイトルにリンクをつける方法の話です。

# やりたいこと
タイトルをクリックして
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/e288400a-2809-643d-8c4d-0a363e74c87f.png)

このページに飛びたい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/b6840601-fd22-df0e-5fca-fa7abe247414.png)

# 設定の仕方
## 前提
DiscoveryのContent Miningというタイプのプロジェクトを作り、[ここから](https://www.rondhuit.com/download.html)ダウンロードできるlivedoorニュースコーパスのデータを投入済みです。

入れたデータはこんな感じです。
タイトルにリンクをつけたいURLがフィールドとして入っている事がポイントです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/4c852bf4-a15f-7984-e54d-2ef27d94b866.png)

## 初期状態
データをそのままContent Miningに入れるとこんな感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/83559370-d308-6c83-3b91-8829ad5e8f33.png)


Titleとして表示されている「【Sports Watch】宮里藍…」にリンクをつけたい。

## 鉛筆マークをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/7ca48739-9336-6adb-28e7-d3bf693f8d4e.png)


## 設定画面で設置
「URL:」のプルダウンメニューからURLが含まれているフィールドを選び「Apply」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/7eb448c0-af8c-f400-dc46-222cc46b4f98.png)

## 結果
こうなります。タイトルをクリックすると、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/0e9457b1-fc54-b946-6488-1c802fb38474.png)

該当のページに飛びました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/b6840601-fd22-df0e-5fca-fa7abe247414.png)

##補足
ちなみに、リンクをつける前もタイトルをクリックできるのですが、クリックすると詳細な分析結果が見えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/fab3d2e1-bf85-9d9f-d1fe-731f7ba870d2.png)

#まとめ
今回はURLを設定しましたが、設定画面にはImageもあったので、画像も表示できそうな雰囲気ですね。

#お断り
このサイトの掲載内容は私自身の見解であり、必ずしも所属会社の立場、戦略、意見を代表するものではありません。 記事は執筆時点の情報を元に書いているため、必ずしも最新情報であるとはかぎりません。 記事の内容の正確性には責任を負いません。自己責任で実行してください。
Notionのテーブルをかろうじてmarkdown形式に変換する方法(力技)

## 2021/11/18 追記

Notionより、simple table がリリースされましたね。
databaseではなくこちらを使えば、単純にコピーしたものがMarkdownのテーブルに変換されます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ついに、シンプルテーブルなテーブルが実装されました！ 🎉<br><br>データベースほどリッチじゃ無くてもいいけど、表組み表示をしたい。<br>そんなニーズに応えられるのがシンプルテーブルです！<br><br>/table からご利用ください🏓 <a href="https://t.co/CdSrd2TtUh">https://t.co/CdSrd2TtUh</a></p>&mdash; Notion Japan 🇯🇵 (@NotionJP) <a href="https://twitter.com/NotionJP/status/1460756835208609792?ref_src=twsrc%5Etfw">November 16, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

既存のdatabaseの表をsimple tableに移したい時には、以下の手順でコピーしたものをNotionにペーストすれば変換できるので、参考にしていただければ(コピーしたデータはTable Generatorを使わなくてもsimple tableに貼り付けできるし、ヘッダーなどはNotion上で作ればいい)。

## TL;DR

Notionは本当に素晴らしいが、一点だけ不満が残る点がある。Notionで書いたテーブル形式はコピペでmarkdownに変換できない。コピーするとテーブルではなくリンクになってしまうが、テーブル形式でコピーできる手順を見つけたのでメモしておく。

社内ドキュメントやQiita、ブログ記事等も徹底的にmarkdownで管理している中、テーブル形式でNotionのメモを書いてしまうとその他メモに転記できなくなってしまう。使い込んでいると、他の媒体にあげるとしてもNotionで描き始めた後、uploadしたくなる。若干手間だがかろうじて変換できる方法を見つけたのでメモしておく。

## 制約

ヘッダーはコピペできない。よしなにやる。
※わりと力技

## 手順

以下のようなテーブルがあるとした時、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224217/2f982a04-df22-a4c0-5986-0f701051442b.png)


1. `row1-col1`を選択、一回`esc`。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224217/e8c362e2-2af0-5d45-39e1-c62e747b19b0.png)


2. `shift(またはCommand)`を押しながら`カーソル移動`で、markdown化したい箇所を選択し、コピー

    (クリックでやっちゃうとrow全体 = 1行が1ページのリンクとしてコピーされてしまう)

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224217/6e4d972e-6fbc-3e23-0092-d0d78c9a6907.png)


3. [Table Generator](https://www.tablesgenerator.com/markdown_tables) を開き、`File > Paste table data...`によりペースト

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224217/ccdd6fbe-4ac7-12c1-4dd1-d68d45dd4f7e.png)


4. この状態だと1行目がヘッダー行になってしまうので、1行目を選択、`Row > insert above` でヘッダー行を作る。(値は自分でいれるしかない)

5. Generate!!!
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224217/d28809f2-7144-3fb5-56cd-515391f4afa1.png)

お疲れ様でした。

## おわりに

テーブルのmarkdown変換機能作るって公式が言ってるの見たけど2年くらい経過してた。はよ！と思うが微妙にロックインされる機能だから期待薄
オプショナルチェイニングのドキュメント見て、jsの関数ってオブジェクトだったわと再認識した

# はじめに
こんにちは。皆さん、箱アイス買いますか？夏になるとパッケージの裏がはがきになってて、懸賞応募できるようになっているアイスとかありますよね！私は毎年あずきバー食べて送ってます、筆者です:v:

(当たったことはないです。)

さて、たまにMDNのJavaScriptのドキュメントを眺めているのですが、**オプショナルチェイニング**ってご存じでしょうか？

これ↓

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining

ざっくり、存在しないプロパティのときにエラーにならないで**undefined**で済むうれしいやつです。
PHP使いの方には、**PHP8**で追加された**Nullsafe operator**とほぼ同じやつです:pray:

# サンプルコード
```js
const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};

const dogName = adventurer.dog?.name;
console.log(dogName);
// expected output: undefined
```

# 本題
**オプショナルチェイニング**ですが、関数にも使えるんです！
普段PHPばっかり書いている自分からしたら、一瞬ぽかんでしたが、そういえばJavaScriptって関数はオブジェクトだったということを思い出して、**そりゃいけるわ！**と納得しました :smile:

```js
console.log(adventurer.someNonExistentMethod?.());
// expected output: undefined
```

## JavaScriptの関数はオブジェクト

>JavaScript において、関数は第一級オブジェクトです。すなわち、関数はオブジェクトであり、他のあらゆるオブジェクトと同じように操作したり渡したりすることができます。具体的には、関数は Function オブジェクトです。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions

# おわりに
関数にオプショナルチェイニングを使用したときの、今まで見たことない `?.()` の部分の文字列の並びにちょっとしびれました。

それでは！
Mac miniに外部カメラを「EpocCam」

# Mac miniに外部カメラを「EpocCam」

## 背景

メインPCとして利用しているMac mini late 2012。
かなり古いですが、SSD換装、メモリ16GB搭載と、できる限りのカスタマイズをして愛用しています。
今まではあまり不便さを感じませんでしたが、ここ最近のテレワークで不便な点が見つかりました。

それはMac miniにはカメラがないことです．．．

## EpocCam

USBカメラなど、外部カメラとしては色々あるのは知ってますが、あまり投資せず解決したいと思い、手持ちのiPhoneやiPadをカメラとして使用する方法を調査。
そこで、「EpocCam」を知りました。

有償版と無償版がありますが、今回は無償版を利用してみました。

詳しい手順は割愛しますが、母艦であるMac miniとiPhoneやiPadにアプリをインストールします。
私の場合はiPadをUSBケーブルでMac miniと接続していますが、WiFi接続でも利用できるらしいですが、そちらは未検証です。

## 検証

よく利用するOffice365アプリの「Teams」でビデオ通話を試します。
![スクリーンショット 2021-07-06 17.57.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589067/e3ae9c56-148c-c399-7638-e57a9f0b3c9a.png)

「カスタムセットアップ」をクリックすると、オーディオデバイスの設定ができます。
ここに、「カメラ」があり、「EpocCam」が選択できるのです。

この状態で会議を開始すれば、Mac miniと接続されたiPhoneもしくはiPadのインカメラで自分を映し出し、ビデオ通話でその映像を相手に見せることができます。

しかもこのEpocCam、無償版とはいえ、背景をぼかすこともできます。

## おわりに

Mac miniでカメラがないことでお困りの方、ぜひお手持ちのiPhoneやiPadでお試しください。
追加投資無しで、Mac miniにカメラを増設させることができます。
Chrome の印刷で レイアウト のオプションが表示されず縦横選べないケース

いつからこうなってたのかわからない。
非表示になってしまうダメパターン

```css
@page {
    size: A4;
}

@page {
    size: landscape;
}
```

auto にすると表示される

```css
@page {
    size: auto;
}
```

参考

https://stackoverflow.com/questions/49018636/bootstrap-4-css-causing-chromes-print-layout-to-disappear
＃１はじめまして

```
Hello!world!
こんにちは！世界！
```

クロキンと申します。　 
プログラミングスクールを卒業しまして、只今転職活動真っ只中です。
スクール受講期間中はアウトプットする時間をチームメンバーで設けていましたが、今日からは毎日Qiitaにアウトプットしていきたいと思います。


主に、自分の1日の勉強のまとめ（最近はウェブの基礎知識を再勉強中です）としてQiitaを投稿していこうと思います。
今までは、エラー解決や実装のために、閲覧し参考にすることが多かったのですが、自分でも発信していこうと思います。

よろしくお願いいたします。
multipart/form-dataで個々のデータに対してContent-Typeを指定する。

# やりたいこと
multipart/form-dataでファイルを送信する際は、`FormData`オブジェクトを作ってそれをそのまま送信する。
`FormData`オブジェクトを作成するには`<form>`タグを引数に取って`FormData`を初期化しても良いし、
空の`FormData`オブジェクトを作ってそこにデータを足しても良いみたい。
(下記は空の`FormData`の例)

```javascript
// フォームにファイルを指定
let formData = new FormData();
formData.set("uploadFile", file, file.name);

// 送信
let xhr = new XMLHttpRequest();
xhr.open("PUT", "/your/api/url/");
xhr.send(formData);
```
ただ、こうすると送信時の`file`の`Content-Type`がブラウザで判定されたMIMEタイプで送信されてしまう。
これを明示的に指定したい。

# 対策
ファイルを`type`指定付きで`Blob`に変換することで、multipart/form-data送信時のContent-Typeも指定した値になる。

```javascript
let blob = new Blob([file], {type: "application/zip"});

// フォームにblobを指定
let formData = new FormData();
formData.set("uploadFile", blob, file.name);
```

これはファイルの場合ですが、textや他のデータでも`Blob`に変換すればContent-Typeを指定できるはず。
(記事末尾の参考記事も参考にしてみてください。)

# サンプルコード
```html:upload.html
<!DOCTYPE html>
<html>
<head>
<title>ファイルアップロードテスト</title>
</head>
<body>
<input type="file" onchange="fileChanged(this)"/>
<button id="button">send</button>
<script>
const button = document.getElementById("button");

let file;

// 送信ボタンクリック時
button.onclick = function() {

  let blob = new Blob([file], {type: "application/zip"});

  // フォームにblobを指定
  let formData = new FormData();
  formData.set("uploadFile", blob, file.name);

  // 送信
  let xhr = new XMLHttpRequest();
  xhr.open("PUT", "/your/api/url/");
  xhr.send(formData);
  xhr.onerror = function(e) {
    console.log("ERROR");
    console.log(e);
  }
  xhr.onload = function(e) {
    console.log("LOAD");
    console.log(e);
    console.log(xhr.status);
    console.log(xhr.statusText);
    console.log(xhr.responseText);
    alert(xhr.responseText);
  }
}

// ファイル選択時
function fileChanged(input) {
  file = input.files[0];
  console.log(file);
  console.log(file.name);
}
</script>
</body>
</html>
```

# 参考記事
https://www.rhymewithgravy.com/2019/03/28/Setting-Content-Type-for-multipartform-data-values.html

https://hakuhin.jp/js/file.html
自分に何かあった時の準備

自分に何かあった時の手順と情報を残すことを真面目に考えてみる。
死んだ時とか、自分が何もできない状態になった時に、どうやって対処するか。（自分以外の誰かが誰かにパスできるように）

**基本的にはソースコードと最低限の手順書・情報と必要なアカウント情報があればなんとかしてくれるでしょ(こまかな仕様はソース読むなり顧客やその他関係者に聞いてください)スタイル。**


## 前提

- 家族か誰か信用できる人がいる
- 案件や状況によっては、そもそもこんなこと考えずとも適切な権限管理と共有ができているべき
- 可能な限りアカウント自体の共有ではなく、権限を共有する
- 実際に事が起きた時にはパスワード管理ソフトに入れた手順書に従ってアカウントや権限を外部の業者等に共有してもらう
- 業務委託等の契約や費用の話は横に置いておく（エンジニアレベルでパスできる状態をゴールとする）

## 利用サービス等

- パスワード管理ソフト : プライベートな家族用、管理画面とかサーバ系以外の情報
- GitHub : ソースコード等
- GitHub の Secrets : デプロイ系の情報
- GCP の KMSで暗号化 : デプロイ系の情報の暗号化

### パスワード管理ソフト

- 共有可能である程度高機能なやつ使って信頼できる人に共有しとく
- 緊急時の手順書とかもここに入れておく
- 各サービスのアカウント等でサービスごとの権限管理で共有できるものはサービスごとに適切に権限を共有しておく。
- 各サービスの権限共有機能などでコントールできる物に関してはここで自分のアカウントは共有しない。
- サービスごとにサービスの権限管理の機能など利用して共有しとく場合、非エンジニアの場合どうやるんだっけ？ってなりそうなので手順書をそえておく。
- https://www.gizmodo.jp/2020/11/best-password-managers-wirecutter.html

### GitHub　/　GitHub の Secrets

- ソースコード　：　WEB系システム開発なのでほぼ１００％ GitHub でソース管理している
- GitHub Actions でのデプロイに必要な情報は Secrets に。 (GitHub actions でデプロイ前提）
- Account Settings > Successor settings がまさしくこの用途
    - https://docs.github.com/ja/github/setting-up-and-managing-your-github-user-account/managing-access-to-your-personal-repositories/maintaining-ownership-continuity-of-your-user-accounts-repositories
    - 問題は死亡じゃない何かの時にどうなるんだろうか？

### GCP の KMSで暗号化

- 用途としてGithub の Secrets とかぶるのでどちらかに寄せてもいいかもしれない
- KMS以外でも使えるツール : mozilla/sops
    - https://github.com/mozilla/sops
    - https://qiita.com/mumoshu/items/d5c8b93ed7898620930b
- GitHub　のシークレットに KMSの認証情報を入れて、 GitHub Actions で sops 使って複合化して使ってるけど、全部 Secrets に入れてもあんまりリスクは変わらない気はする。併用した方が日頃の手間は少ない気がする
namespace ランキング

namespaceについて調べたときに参考になった記事

#第1位
https://qiita.com/engineer_atsumi/items/d42daa13d9081f2f71c7
#第2位
https://qiita.com/kmnohno3/items/455c8bf6c3d0a3a994d8
#第3位
https://qiita.com/speedstar18fct/items/e5de4509afe7e831fc34
#その他
https://qiita.com/eve1224/items/022660c6c4f6ab5474e4

いつもわかりやすい記事ありがとうございます。
JavaScriptで漢数字や全角数字を半角数字に変換する

#概要
書き手が違うテキストを複数読み込むとき、全部半角数字にしてくれればいいのに、漢数字や全角数字が混在して困った経験はありませんか？私は大いに困りました・・・

Web上に落ちてないかな～って軽く探しましたが、長大なコードばかりで複雑だなぁと思ったので、練習がてらに自分で作ってみました。

自分ではそこそこ簡潔に作れたかなと思ったのでアップしておきます。

もしもっとこうしたほうが簡単だよ、とか、バグがあれば、教えてください！w

#サンプルコード

```javascript
function kan2num(str){
  // 
  // 漢数字を半角数字に変換する関数
  // cf.  二千三百五十 -> 2350
  //      五十六億二十万千二百三円 -> 5600201203円
  //      一五四七 -> 1547
  //      2547百万 -> 2547000000
  //      二十三万円はドルに直すと二千ドルくらい -> 230000円はドルに直すと2000ドルくらい
  //
  // str:String型
  // 

  const m0 = { 0: "0０〇零", 1: "1１一壱", 2: "2２二弐", 3: "3３三参", 4: "4４四",
               5: "5５五伍", 6: "6６六",   7: "7７七",   8: "8８八",   9: "9９九"}
  const m1 = {1: "十拾",  2: "百", 3: "千仟阡"};
  const m2 = {4: "万萬", 8:"億", 12:"兆"};
  let stack1 = [], stack2 = [], trans = []; // stack:計算式を格納、trans:変換後の文字列を格納
  let buf = str.split('');

  let get_num = ch => {
    for(let i in m0) if(ch.match(RegExp("[" + m0[i] + "]"))) return [i, "m0"];
    for(let i in m1) if(ch.match(RegExp("[" + m1[i] + "]"))) return [10**i, "m1"];
    for(let i in m2) if(ch.match(RegExp("[" + m2[i] + "]"))) return [10**i, "m2"];
    return [ch, "str"];
  }

  // stack 内の計算式を全て計算して計算結果を trans に格納
  let push_stack_to_trans = () => {
    if(stack1.length) stack2.push((stack2.length ? "+": ""), String(eval(stack1.join(""))));
    if(stack2.length) trans.push(String(eval(stack2.join(""))));
    stack1=[], stack2=[];
  }

  for(let i=0; i<buf.length; i++){
    let [ch, type] = get_num(buf[i]);
    let type_b = i ? get_num(buf[i-1])[1] : "";  // 直前の文字のタイプ、i=0を除く

    if(type != "str" && !stack1.length){ stack1.push(ch); continue;}

    // 数字以外の文字の場合：stack1, 2を計算
    if(type == "str"){
      push_stack_to_trans();
      trans.push(ch);
      continue;
    }
    // 0～9の場合
    if(type == "m0") (type_b == "m0") ? stack1.push(ch) : stack1.push("+", ch);
    // 十、百、千などの場合
    if(type == "m1") (type_b == "m0") ? stack1.push("*", ch) : stack1.push("+", ch);
    // 万、億、兆などの場合：stack1の計算結果に掛けてstack2に格納
    if(type == "m2"){
        stack2.push((stack2.length ? "+": ""), String(eval(stack1.join(""))), "*", ch);
        stack1 = [];
    }
  }
  push_stack_to_trans();
  return trans.join("");
}
```

#簡単な説明
数字の羅列を計算式に変換して、最後に計算させるというアルゴリズムです。

二千三百五十
=　2 * 1000 + 3 * 100 + 5 * 10
=　2350

五十六億二十万千二百三
=　(5 * 10 + 6) * 100000000 + 2 * 10 * 1000 + 1000 + 2 * 100 + 3
=　5600201203

一五四七
=　1 5 4 7
=　1547

2547百万
=　(2 5 4 7) * 1000000
=　2547000000

というように計算されています。
直前の文字によって + するのか * するのか、それとも何も演算子を入れないのかの分岐が若干ややこしくなっていますかね・・・

#追記
####2021年7月6日
早速修正。
``\s``が0と認識されていたようなので、文字として認識するよう修正しました。
####2021年7月7日
コメント頂いたコードを参考に、よりシンプルに修正しました。ありがたい。
特定のS3バケットにのみアクセス可能なIAMを作成する



ローカル開発用に特定のS3バケットにのみアクセス可能なユーザーを作成したかったのでメモ。

まずはIAMユーザーを作成しておく。
ポリシーを以下のJSONのように作成し、IAMユーザーにアタッチする。
`/*`はつけないとバケット配下のオブジェクトへの書き込みが403になる。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::<バケット名>",
                "arn:aws:s3:::<バケット名>/*"
            ]
        }
    ]
}
```

## 参考
* [Amazon S3: 特定の S3 バケットの管理を制限する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_s3_deny-except-bucket.html)
BootstrapをCDNで導入

##はじめに
私はフロントがすっごく苦手なんですが、そもそもセンスがないんですね、、、
おしゃれなデザインができるようになりたいーみたいなことを仲間内で話してたらBootstrapを勧められました。

そもそもBootstrapというもの自体何者かよくわかっていませんでしたので、簡単に概要をまとめました。
あと、CDN(インターネット経由で読み込む）で導入する方法も書いているのでよかったら参考にしてみてください。
それではよろしくお願いします。

##Bootstrapとは
Bootstrapはcssのフレームワークです。Twitter社が作ったもので、比較的簡単におしゃれで整ったページが作成できるそうです。

Bootstrapの最大のメリットとしてよくあげられるのは、レスポンシブWebデザインに対応しているという点です。
スマホやタブレットなど、それぞれ専用のページを作成すると手間がかかりますが、Bootstrapを使用すると、一つのWebページを作成すれば、ブラウザの横幅サイズを基準に、レイアウトデザインを調整することが可能になります。

私の場合、デザイン性の優れたコンポーネントを使用する目的で使用しますが、これもBootstrapの大きな利点といえるでしょう。サボりではないです！

Bootstrapを使用するには,BootstrapとjQueryを読み込まなければなりません。そこで、インターネット経由で読み込む方法と、ローカル環境にダウンロードして読み込ませる方法の2種類のやり方があります。

前者の方法をCDNと言います。こちらすごく簡単だったので紹介します。

##CDNでBootstrapを導入する
CDNはContent Delivery Network（コンテンツデリバリーネットワーク）の略です。
CDNはBootstrapで使用するファイルなどをローカルへダウンロードする手間などがなく、HTMLへコードを埋め込むだけでBootstrapを使えるようにできます。

いろんなサイトで「この記述をコピーすればOKです」ってコードを書いてくれていたりしますが、うまくいかなかったりしたので、公式へ行きましょう。
↓このリンクから飛べます。
https://getbootstrap.com/docs/4.1/getting-started/introduction/

一応私の方の記事でも書いときます。

私の場合、railsアプリにCDNで導入する方法なのでご注意ください。

viewファイルのlayoutsのapplication.html.erbに記述します。
全ページへ反映させるためです。

まず、＜head＞の中にこの記述を追加↓

```erb:
  <head>
    <%#　省略 %>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
  </head>
```

そして、＜body＞の中に↓の記述
私は終了タグのすぐ上に記述しました。

```erb
  <body>
    <%# 省略　%>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
  </body>
```

これだけで準備完了です。
あとはチートシートなどにある記述をコピペすれば使いたいデザインなどがすぐ使えます！

##最後に
Bootstrapめちゃめちゃ便利です！
ただ、これを使いすぎてオリジナリティがなくなったり、フロントに対してブラックボックスになるのが怖いのでほどほどにしようと思います。
rmtrashがHomebrewでインストールできないので代替パッケージのtrashをインストール

##環境
iMac (2017)
macOS Big Sir ver 11.4

##状況
rmtrashがをHomebrewでインストールするため、まずはパッケージの検索。

```Bash:terminal
% brew search rmtrash
==> Formulae
trash
```

rmtrashがヒットせず、trashがでてくる。
どうやら、rmtrashは使用不可らしい。
（参考：https://github.com/Homebrew/homebrew-core/pull/65438）

##rmtrashの代替パッケージをインストール
以下のrmtrashの代替パッケージtrashをインストールしました。
https://github.com/h-matsuo/macOS-trash

インストールの手順はインストラクション通り。
1, スクリプトファイルSource code (zip)をダウンロード
2, 展開した後に"trash"ファイルを/usr/local/bin に配置
　 (Finder→移動→/usr/local/bin と移動し、trashを手動で移動しました。) 
  

###動作確認→NG

テスト用のディレクトリであるTestFolderを作ってから、trashコマンドを試してみる。
※ ディレクトリの削除は-rオプションが必要。

```Bash:terminal
% mkdir TestFolder
% trash -r TestFolder
29:98: execution error: FinderにApple Eventsを送信する権限がありません。 (-1743)
```
エラー発生。どうやらmacOS Mojave（10.14）から新しく導入された「オートメーション」の機能より、制限を受けているらしい。

・「オートメーション」機能によるプログラムの制御を解除する方法
1.「システム環境設定」の「セキュリティとプライバシー」を開く
2.「プライバシー」タブをクリックし、画面左側の「オートメーション」をクリック
3.「ターミナル」の項目の「Finder」にチェックをつける

###気を取り直してもう一度、動作確認→OK

先ほど作ったテスト用のディレクトリであるTestFolderにtrashコマンドを試してみる。

```Bash:terminal
% trash -r TestFolder
```
ゴミ箱を確認してみると、TestFolderがありました。
現在，ゴミ箱からもとに戻す機能はサポートされていないそうです。
GUI を用いて戻すことができるとのことなので試してみたら、Finder.app を使用して手動でゴミ箱から出すことができました。

###最後にrmコマンドの代わりにtrashが動くように設定（エイリアスを切る）

```Bash:terminal
% echo "alias rm=trash" >> ~/.bashrc
% source ~/.bashrc
```
これにより、rmコマンドでファイルがいきなり削除されるのではなく、ゴミ箱に移動されるようになるはず。
テスト用のファイルnewfileを作成し、消してみます。

```Bash:terminal
% echo > newfile
% rm newfile
```
ゴミ箱を確認してみると、newfileがありました。
rmコマンドでtrashパッケージを動かせているようです。
これで安心してrmコマンドを打てます。

以上です。

##参考
以下の記事を参考にしました。
「Macでrmtrashの代替とLinuxでのrmの置き換え」

https://qiita.com/TomokiYamashit3/items/d0e0fbbc736400e0aa39
unity5.2の作法 その24 post

#概要

unity5.2の作法、調べてみた。
postやってみた。

#makefile

- menu->file->new sceneする。
- menu->file->save sceneでstartとする。
- menu->gameobject->create emptyする。
- menu->assetes->creates->c# scriptでstart.csつくる。
- ダブルクリックして、コードを書く。
- ヒエラルキーのgameobjectに、コードを重ねる。
- 三角で実行。

#サンプルコード


```
using UnityEngine;
using System.Collections;

public class www2 : MonoBehaviour
{
    string json0;
    void Start() {
        StartCoroutine("ReadMemo");
    }
    IEnumerator ReadMemo() {
        string url = "http://ohiapp0.herokuapp.com/test3";
        WWWForm form = new WWWForm();
        form.AddField("hoge", "fuga");
        form.AddField("foo", "bar");
        WWW www = new WWW(url, form);
        yield return www;
        if (www.error == null)
        {
            json0 = www.text;
            Debug.Log(www.text);
        }
    }
    void OnGUI() {
        GUI.color = Color.yellow;
        GUI.Label(new Rect(10, 10, 300, 300), json0);
    }
}



```


以上。
"urllib.error.HTTPError: HTTP Error 403: Forbidden"の対処法

＜事象説明＞
"urllib.error.HTTPError: HTTP Error 403: Forbidden"エラーは
閲覧権限がないを意味する。
サイト側がブラウザ以外のアクセスを拒否する設定になっている場合に起きる。

＜解決方法＞
ヘッダーをつけて、ブラウザに偽装する

```python:＜コード例1＞.py
url = 'https://www.a.com/abc.csv'
save_path = '\savename.csv'
opener=urllib.request.build_opener()
opener.addheaders=[('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36')]
urllib.request.install_opener(opener)
urllib.request.urlretrieve(url, save_path)
```

```python:＜コード例２＞.py
opener = urllib.request.build_opener()
opener.addheaders = [('User-Agent',
                      'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36')]

urllib.request.install_opener(opener)

for img in img_nodes:
    if img.get('file') is not None:
        img_url = img.get('file')
        ID = img.get('id')
        line = str(count) + "\t" + img_url  + "\n"
        img_meta.write(line)
        try:
           #urllib.request.urlretrieve(img_url, "./output/image/" + str(count) + ".jpg") # 画像を一つづつダウンロード
           #urllib.request.urlretrieve(img_url, "./output/image/" + ID + ".jpg")
           urllib.request.urlretrieve(img_url, "./output/image/" + ID + ".jpg")
        except urllib.request.URLError:
            print("Error opening URL")
        count += 1
```
参考：https://blog.csdn.net/liumoude6/article/details/88074565
Railsで架空のCafeのHPを作ってみよう!【1日目】

#概要
基本Railsの記法に則り書いていきます！
１から全ての説明ではなく
その中であれ？どうやるの？と
疑問に思った点や実装に困った箇所を
ピックアップして紹介していきます♩

#設定と準備
・Rails
・HTML
・CSS
・Javascript(jQuery)

↑上記の言語とフレームワークを使い
架空(自分で考えたテキトーなもの)のCafeの
HPを作っていこうと思います！

#1日目で躓いた箇所:zap:
ホーム画面にモデル、コントローラーが必要ないのに
どうたってrootパス指定すればいいんだ...?

基本Railsにおいて最初に遷移させたい画面は

```config/routes.rb
ails.application.routes.draw do
  root 'コントローラー名#アクション名'
end
```

しかし今回はモデルもコントローラーも必要ない。
直接viewに遷移させるような記述はないだろうかと調べたが
答えは**「ない」**でした。

Railsでrootパスを書く場合
必ず、コントローラーからアクションという流れになる。
なので

```app/controllers/homes_controller.rb
class HomesController < ApplicationController

  def index
  end

end
```

このように無理矢理ホーム画面に遷移させるため
コントローラー(今回の場合homes)を作成し
アクション(index)を定義。

そして

```config/routes.rb
Rails.application.routes.draw do
  root 'homes#index'
end
```

rootパスに指定する。
このような流れで無事ホーム画面に遷移させることに成功。
垂直スクロールを水平に変換

## 垂直スクロールを水平スクロールへ変換

```javascript
// タブ部分を上下にスクロールすると左右に
document
  .querySelector("#tab-scroll-container")
  .addEventListener("wheel", transformScroll);

// 上下方向のスクロールをそれぞれ左右方向に変換
function transformScroll(event) {
  if (!event.deltaY) {
    return;
  }
  event.currentTarget.scrollLeft += event.deltaY + event.deltaX;
  event.preventDefault();
}
```
## 要素の最大スクロール値を取得する

```javascript
// 横スクロールの最大値を取得
const scroll_max = scroll_element.scrollWidth - scroll_element.clientWidth;
```
BurpSuite / Intruder の Grep - Extract で複数回一致

Burp SuitのIntruder機能の「Grep - Extract」を触っておりまして、最初のマッチしかExtractされないなあ、との疑問を抱き確認をいたしました。

## ドキュメント等
- [PortSwigger Documentation](https://portswigger.net/burp/documentation/desktop/tools/intruder/options)

Grep - Extractの　↓　の部分がわかりませんでした。
> If the same matching item is added multiple times in succession, then each server response will be searched for multiple occurrences of that expression, and the text immediately following each occurrence will be captured. This can be useful, for example when an HTML table contains useful information but there are no unique prefixes with which to automatically pick out each item.

- [（User Forumで発見したQ&A）Extended grep-extract in Intruder to cover more occurrences](http://wiki.genexus.jp/hwiki.aspx?%5bAdministrator+User+Name%5d+%e3%83%97%e3%83%ad%e3%83%91%e3%83%86%e3%82%a3,)

答え　↓　は「Duplicate」で同じパターンを追加するようです。
> You can use the "Duplicate" button to add the same matching item multiple times in succession. Each server response will be searched for multiple occurrences of that expression, and the text immediately following each occurrence will be captured.

## 動作確認
複数のhtmlファイルをPositions設定で順にリクエストし、存在するhiddenをextractという仮定の単純なサンプルにおいて、
![intruder01.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168717/8a68c45e-0874-b999-5be7-6477aa64449c.jpeg)
上記のパターンを1つ追加した状態では、1個しかマッチしません。
![intruder02.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168717/67ce0ec3-3be4-9954-723e-d52f61a0239f.jpeg)
同じパターンを「Duplicate」しますと
![intruder03.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168717/de0d57ed-160c-c268-826d-284c483d327a.jpeg)

下記のように1行（1レスポンス）の列方向に、複数回のマッチ結果が並びました。
![intruder04.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168717/b5259ae7-4995-715d-2be7-8d14930246ab.jpeg)

2021年07月06日　初版
Slackの会話データを日次で取得する基本的な方法

# この記事はなにか？

- この記事は、Slackデータを定期的（日次など）に蓄積していく処理の解説記事です
- すべてのソースコードは [こちらのGitHubレポジトリ](https://github.com/sota0121/slack-ingestion) にあります

# なぜこの記事を書いたのか？

理由は3つあります。

1. （私の観測範囲内ですが）データ基盤構築のお仕事需要が結構発生していると思われる
2. とはいえ、こうした地味な処理の記事ってガチ系 or 超入門ばっかりでちょうど良い難易度・ボリュームのものが少ない気がする
3. ちょうどいいネタを自分が持っているので、公開したら（上記2つの理由から）誰かに役立つかもと考えた

# 処理のポイント

処理詳細やスクリプトの使い方はREADMEに記載していますので、そちらをご覧ください。ここでは、「定期実行する」ために重要と思われるポイントを簡単にまとめます。

## ログファイル作るよ

### ■ なぜ重要なのか？

- ずばり、なにか問題が起きたときに原因を特定するためです
- 今回のような「所望のタイミングで一定量のデータを一気にロードする処理」はバッチ処理と呼ばれる類のものです
- バッチ処理で問題が発生したときはすぐに気付けるようにアラート機能を付けておくことは当然必要として、問題の原因特定のためにログファイルが必要です

### ■ どうやったのか？

- Pythonの標準モジュールである `logging` を使いました
- `logging` の使い方は、 [公式Doc](https://docs.python.org/ja/3/library/logging.html) をご覧ください
- ログファイル名は、`ingest_log_at` というプレフィックスのあとに処理開始時刻を付けたものにしています
- こうすることで、複数回実行した場合でもログファイルが上書きされません

## 出力フォルダ/ファイルの命名規則考えるよ

### ■ なぜ重要なのか？

- ずばり、出力したデータを破壊しないため・処理の冪等性を担保するため・所望のデータを探しやすくするためです（と私は思います）
- アンチパターンケース
  - 例えば、出力フォルダを `output` 出力ファイルを `slack.json` などと設定するケースです
  - この場合、対象とするデータを特定することができません。「いつの」という情報がないからです。
- 上記アンチパターンに該当する場合、複数回実行することで、以前取り込んだデータを上書きして消してしまうリスクが高まります。
- また、あとでデータを見たとき「○年○月○日のデータがほしい」という要望に応えられません。（再度取得すればいいかもですが、もう二度と同じデータが取れない場合もあります）

:::message
冪等性（べきとうせい）については、 [こちら](https://ja.wikipedia.org/wiki/%E5%86%AA%E7%AD%89) を参照
:::

### ■ どうやったのか？

- 取得対象とする日付ごとにフォルダを作成し、その配下にAPIリソースごとのJSONファイル、ログファイルを格納する（ちょうど以下のようなイメージです）

![data-eng_ingestion-script_slack_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181972/fdd08388-82c2-2735-19db-9ae2a3a17969.png)


## 使いやすい実行パラメータを考えるよ

### ■ なぜ重要なのか？

- ずばり、定期実行とは別にマニュアル実行（好きな日付を指定して実行）を自由にできるようにしたいからです。
- なぜマニュアル実行が必要かというと、今後スクリプトを継続的にアップデートする上で何度も挙動確認が必要になるからです。
- とりあえずちょっと動かしてみるか、ってときに正規の方法（例：実行時の属する日付のデータを取得）だけだと、本番データに影響が及んでしまいます
- パラメータを丁寧に設計しておけば、手動であれCIであれ、気兼ねなく挙動確認できる下地ができます。

### ■ どうやったのか？

- 引数なし（デフォルト）の場合は、スクリプト実行時の前日の1日分のデータを取得します。これは通常の定期実行用です
- 引数あり（任意の取得対象日付を指定）の場合は、指定した日付の1日分のデータを取得します。これはマニュアル実行用です

**おしまい**
RaspyerryPi4でYolov5を動かす


## やりたいこと
**最新のYolov5をRaspberryPiで動かしたい**
※最新はyolov5のバージョン5を指している(07/2021現在)

## 環境
Raspberry Pi 4B 32b OS
Python3.7
torch 1.7
torchvision 0.8.0

## 記事を書いた理由
簡単に環境構築できるYolov5だが、yolov5のバージョン**４**以降は、torch1.7以上が必要なため、raspberryPiで環境構築するときに工夫が要る。
いくつか参考になる記事はあったが、pytorch1.7では動かすことを諦めて、yolov5のバージョンをダウングレードしたりしてたので、この記事では**yolov5バージョン5をpytorch1.7を使って動かす方法**をまとめた。

## RaspberryPiでGithubのReadMe通りに環境構築するとどうなる？
偉大なる本家様→[【github】yolov5](https://github.com/ultralytics)

requrements.txtをpip3 installするとき下記エラーが発生する。

```
pip3 install -r requirements.txt
```

```
  Could not find a version that satisfies the requirement torch>=1.7.0 (from -r requirements.txt (line 10)) (from versions: 0.1.2, 0.1.2.post1, 0.1.2.post2)
No matching distribution found for torch>=1.7.0 (from -r requirements.txt (line 10))
```
torch1.7が見つからないみたい。
→torch1.7を別で用意する必要がある!!

## 参考にしたけど、うまくいかなかった記事たち

### その1
[Raspberry Pi上で物体検出（YOLO v5）をやってみる](https://qiita.com/hamanhuman/items/d9a52b0e23044cd820de)

この記事通りだと、とりあえずyolov5を動かすことはできるが、yolov5のバージョンを下げなければいけない上に、pytorchは1.6を使っている。
今回は、最新のバージョン(ver5 07/2021現在)を動かしたい。

### その2

[Install PyTorch on a Raspberry Pi 4.](https://qengineering.eu/install-pytorch-on-raspberry-pi-4.html)

pytorch1.7以降のバージョンはraspberry Pi OSの64bitではなければ難しいと書いてる。
試しに、64bit版でやってみたが、Yolov5を動かそうとすると他のところでエラーが出まくった。
32bitOSで動かしたい。


## 解決方法
下記githubでtorch1.7とtorchvision0.8.0のwhlを用意してくれてるので、こちらを利用する。
[pytorch-armv7l](https://github.com/Kashu7100/pytorch-armv7l)

## 手順
### まずはgithubからyolov5をclone
```
git clone https://github.com/ultralytics/yolov5.git
```

### pytorch1.7とtorchvision0.8をダウンロードする
下記サイトからダウンロードする。他のサイトのtorch1.7のwhlだと動かなかった。
[pytorch-armv7l](https://github.com/Kashu7100/pytorch-armv7l)

ダウンロードが終わったら、それぞれをpip3 installする

```
sudo pip3 install torch-1.7.0a0-cp37-cp37m-linux_armv7l.whl
sudo pip3 install torchvision-0.8.0a0+45f960c-cp37-cp37m-linux_armv7l.whl
```

### pytorchがちゃんと動くか確認

```
python3
> import torch
```
libopenblas.so.0がないと怒られる。

```
ImportError: libopenblas.so.0: cannot open shared object file: No such file or directory
```
インストールすれば解決

```
sudo apt-get install libopenblas-dev
```

再度動くか確認して、何もエラーがでなければtorchのインストールはOK
torchのバージョンが1.7になってることをpip3 listで一応確認はしておく

### ライブラリをrequerement.txtからインストール
requrement.txtからtorchとtorchvisionをコメントアウト


```
pip3 install -r requirements.txt
```

### とりあえず動くか確認する
yolov5s.ptは予めダウンロードしておく。
https://github.com/ultralytics/yolov5/releases

```
python3 detect.py --source data/images/ --weights yolov5s.pt --conf 0.4
```

ibcblas.so.3がないというエラーが出た場合

```
ImportError: libcblas.so.3: cannot open shared object file: No such file or director
```

下記をインストールして解決     

```
sudo apt-get install libatlas-base-dev
```

もう一度、detect.pyを実行して、daga/imagesの下の画像が、推論されて出力先に保存されればOK!!
Android Studioのビルドが終わったら通知音を鳴らす

Android Studioでビルドしている間に、PCから離れて他のことをしたいときにちょっと便利です。

# 設定方法
Android Studioの設定の、 "Appearance & Behavior" → "Notifications" を開き、"Gradle Build(Balloon)" を選択する。そこの "Play Sound" をチェックする。
これで、Gradleによるビルドが終わったら、PCの通知音が鳴るようになります。

![スクリーンショット 2021-07-06 16.12.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227196/fdc3a97b-edcc-d1eb-df6f-347c1dc06a6f.png)

同じくその1つ下の "Gradle Build(Logging)" を選択し、そこの "Play Sound" をチェックする。
"Gradle Build(Balloon)" は、アプリのビルドでは通知音がなるのですが、修正の反映やテストの実行時は通知音が鳴らなかったため、 "Gradle Build(Logging)" も設定しておきます。

![スクリーンショット 2021-07-06 16.12.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227196/687e14eb-2a2f-80af-217f-8592ad60ba7b.png)

これで、アプリのビルド、テストの実行などで通知音が鳴るようになります。
enebularでherokupostgres その7

#概要

enebularでherokupostgresに繋いでみた。
api、テストしてみた。


#html側

フォームをapplication/x-www-form-urlencodedにして、xhrでpostする。

#enebular側

postで受けて、そのデータをjson化して、jsonヘッダー付けて、返す。

#成果物

https://embed.plnkr.co/plunk/dlmnKjbe9izpIQST

以上。
先生、EC2インスタンスが起動しません！

# 結論だけ知りたい場合

AWSコンソール（Web）ではなく、AWS CLIでStopしてStartすると治ることが多い。

# ある日の自宅勤務で

ぼく「サーバーにログインして作業や、踏み台インスタンス立ち上げー」

AWSコンソールぽちぽち

ぼく「たぶん2〜3分で立ち上がるからMAMAのカップ麺でも作ろ」

3分後

ぼく「SSHログインっと…あれ？Operation timed out？」
ぼく「AWSコンソールどうなってんねやろ」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143240/d63186fa-c204-68dc-90ee-f98ccd2fbe99.png)

ぼく「え、Pending？」
ぼく「そういえば2012年頃のAWSでもこんな現象あったな、とりあえずAWSコンソールからStop→Startしとくか」

カップラーメン食べ終わって

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143240/d63186fa-c204-68dc-90ee-f98ccd2fbe99.png)

ぼく「え、 **また**Pending？先生助けてや…」

先生「こういうときは、CLIでサクッと解決！」

```
aws ec2 stop-instances --instance-ids 止めたいインスタンスID
# 止まったのを確認してから
aws ec2 start-instances --instance-ids 止めたいインスタンスID
```

ぼく「SSHっと…やったー！入れたわ先生！あんがと！！」

---

この現象、私が社会人になりたての2012年、前職でのプロジェクトでAWSを使っていた頃から起きてました。
どういうことなんでしょうかね。
PHPでCookieを保存するときの名前の注意点 (Cookieの名前にドットを使ってはいけない)

`setcookie` 関数で書き込んで、グローバル変数 `$_COOKIE[<Cookie名>]`でCookieの値を取得しようとした。

しかし、`$_COOKIE[<Cookie名>]` がNULL(存在せず)となってしまい、書き込みがうまくいっていないと思ったら、Chromeのサイト情報では確かにCookieは保存されていた。

`var_dump($_COOKIE)`を実行したところ原因が判明。
Chrome側でのCookie名でドットになっていた部分が、`$_COOKIE`のキー名ではアンダーバーに変えられてました。

**Chrome側**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230846/3d8dbcdd-8b8e-d320-cdb6-a46a5e718109.png)


**PHP var_dump**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230846/e496579a-db3d-0468-b37c-e6bba5dd3203.png)
Pure Rubyで漢字-->ローマ字（英字）変換(feat. suika, romaji)

# RubyやRailsで独立に使える漢字をローマ字に変換する方法

## 1. 序論
Rubyで漢字を、カナやひらを経って、ローマ字に変換するGemは、kakasi, miyabi, zipang, nattoのようにすでにいくつかあります。
しかし、これらは、古くて最新Ruby Ver.3でバグったり、CやJavaのような外部ライブラリを用いるのでインストールが面倒だったり、httpリクエスト通信するので遅かったりする問題があります。

そこで、最近Suikaという形態素解析器Gemを見つけました。
Suikaは漢字をカナに変換できるので、romaji Gemと合わせて、漢字をローマ字に変換する方法を思いついたので、共有を兼ねてメモします。

テスト環境：

```
ruby --version
# ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]
```

## 2. Gem紹介

### 2.1 Suika (last updated at 2021)
本家のブログ-->https://yoshoku.hatenablog.com/entry/2020/07/05/090000
Github-->https://github.com/yoshoku/suika

用途：**漢字-->カナ**

なぜか食べ物の名前をしているこのGemは、日本語の形態素解析器です。
Pure Rubyなので、外部ライブラリが必要なく、インストールが簡単です。
なお、Suika単体ではローマ字に変換はできないので、後にromaji Gemと合わせて使います。

#### 2.1.1 インストール方法：
```gem install suika```

#### 2.1.2 使用方法：
```
require 'suika'

tagger = Suika::Tagger.new
tagger.parse('すもももももももものうち').each { |token| puts token }

# すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
# も      助詞,係助詞,*,*,*,*,も,モ,モ
# もも    名詞,一般,*,*,*,*,もも,モモ,モモ
# も      助詞,係助詞,*,*,*,*,も,モ,モ
# もも    名詞,一般,*,*,*,*,もも,モモ,モモ
# の      助詞,連体化,*,*,*,*,の,ノ,ノ
# うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
```

### 2.2 romaji (last updated at 2017)
Github-->https://github.com/makimoto/romaji

用途：**カナ/ひら-->Romaji**

割と認知度の高いGemで、漢字からの変換はできないけれど、かなやひらをローマ字に変換してくれます。

#### 2.2.1 インストール方法：
```gem install romaji```

#### 2.2.2 使用方法：
```
require "romaji"

Romaji.kana2romaji "スシ" #=> "sushi"
Romaji.romaji2kana "sushi" #=> "スシ"
Romaji.romaji2kana "sushi", :kana_type => :hiragana #=> "すし"
```

## 3. Suika X romajiサンプルコード
この二つを合わせて、漢字-->Romaji変換スクリプトを書きます。
例）

```
require 'suika'
require "romaji"
$tagger = Suika::Tagger.new # Slow why?

def kanji_to_romaji(word)
  kana_arr = $tagger.parse(word).map{ |w| 
    if w.split(',')[-1] != '*'
      w.split(',')[-1] # Get last kana
    else
      w.split("\t")[0] # Get original
    end
  }
  romaji = kana_arr.map{ |k| Romaji.kana2romaji(k) }.join()
  return romaji
end

kanji_to_romaji('Suikaは、まだ、遊んでみるには良いけど、使ってはいけない。')
# "suikawa、mada、asondemiruniwayoikedo、tsukaxtsutewaikenai。"
kanji_to_romaji('湯川 秀樹は、日本の理論物理学者。')
# "yukawa hidekiwa、nipponnorironbutsurigakusha。"
```

## 4. 今後
上記例文で、`if...else...`は、単語帳にない名詞などのカナが'*'になる問題があるので入れました。これはSuikaの機能が増えるとよりスッキリなると思います。
また、Suikaでromajiを取り入れると、もっと簡単に漢字にローマ字に変換してくれると思います。
【Google Cloud Day'21】「Google Cloud で Web の脅威に立ち向かう」を視聴して

Google Cloud で Web の脅威に立ち向かう
アプリケーションは様々なレベルで様々な脅威に直面します。このセッションでは、1 つの脅威を紹介し、Google Cloud サービスを利用してそれらを処理する方法をお話します。
https://cloudonair.withgoogle.com/events/google-cloud-day-digital-21/watch?talk=d3-sec-03

https://cloudonair.withgoogle.com/events/google-cloud-day-digital-21?talk=d2-pl-13 

<table>
	<tr>
		<th>質問</th>
		<th>返答</th>
	</tr>
	<tr>
		<th>Security Command Centerにて追加されるFindingについて追加されたことを知るための術として何か良い方法はないでしょうか？メール通知やRSS、リリースノートなど</th>
		<td>はい、Cloud PubSub と Cloud Functions を利用して API、Slack、SIEM や他の Finding を送信したい対象にメッセージを送ることができます。</td>
	</tr>
	<tr>
		<th>SOCの機能（自動）を有しているサービスと記載があります。脅威の検出ミス、対処ミスの責任は、SLAに含まれますか？</th>
		<td>SLA は稼働時間に適用されています。</td>
	</tr>
</table>

<table>
	<tr>
		<th>切り口</th>
		<th>内容</th>
	</tr>
	<tr>
		<th>reChapchaの歴史</th>
		<td>ver1は10年以上前。高度な攻撃者はver2も解いてくる。</td>
	</tr>
	<tr>
		<th>reChapchaのver3はエンタープライズ</th>
		<td></td>
	</tr>
	<tr>
		<th>フォームから入力があったデータを保護する</th>
		<td></td>
	</tr>
	<tr>
		<th>DDOS攻撃対策</th>
		<td></td>
	</tr>
	<tr>
		<th>脆弱性診断</th>
		<td>SecurityComandCeterの一部</td>
	</tr>
	<tr>
		<th>SecurityComandCeter</th>
		<td></td>
	</tr>
	<tr>
		<th>怪しいURLのチェック</th>
		<td></td>
	</tr>
	<tr>
		<th>不正なコードは使われてないか</th>
		<td></td>
	</tr>
	<tr>
		<th>セキュアなフォームまとめ</th>
		<td>ここにLookerを使えばSIEMも実装できる</td>
	</tr>
	<tr>
		<th>GCPのセキュリティサービス一覧</th>
		<td></td>
	</tr>
</table>

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/7edc1f97-3543-360e-d18c-fab2733bacf7.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/e40a8c65-c989-5549-b762-2acf1caa1ce3.png)
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/9408c2d3-a15d-81fa-15c3-b811b96e57d6.png)
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/dac8aec4-a81e-8c79-3e96-fbf7985cc532.png)
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/1e69f514-f651-7b83-59a7-5ed05359aef0.png)
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/b59bbb95-48bf-800a-1c8d-be8b80745a31.png)
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/258ab1e6-2234-869a-92fd-42350950853d.png)
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/ac68a743-a212-333d-0bb6-627a0c78c920.png)
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/998f8a86-b1e9-933f-7cd9-68f76f4dfe21.png)
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/5c49e966-5046-a916-87e6-e05b11716b2b.png)
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/ac7a0dda-46a5-721b-cbde-b11bbbb66cfe.png)
【Google Cloud Day'21】「パスワード リスト型攻撃からの守り方」を視聴して


多くのウェブサービスが認証機能を提供していますが、その多くが自動化されたログイン試行の攻撃に対して十分な対策を講じていません。今日ではボットネットを活用した大規模な攻撃も珍しくなく、攻撃の矛先が向けば単純な対策は破られかねない状況にあります。本講演では、pixiv の認証基盤の運用で行っている reCAPTCHA、BigQuery、Looker を活用した対策・監視の方法をご紹介します。
https://cloudonair.withgoogle.com/events/google-cloud-day-digital-21/watch?talk=d3-sec-04

<table>
	<tr>
		<th>質問</th>
		<th>回答</th>
	</tr>
	<tr>
		<th>reCAPTCHA Enterpriseで取得したスコアに基づいたアクション (ブロッキングなど) はどのように決めて運用されていますか？</th>
		<td>ブロッキングのしきい値としては、Googleが0.5をしきい値としてそれ以下をブロック、とするように推奨しており、ピクシブでもそれにならっています。0.5以下だった場合、現在はフォールバックとしてreCAPTCHA v2を表示し、チャレンジを解ければ利用できるようにしています</td>
	</tr>
</table>

<table>
	<tr>
		<th>切り口</th>
		<th>内容</th>
	</tr>
	<tr>
		<th>パスワードリスト型攻撃と事例</th>
		<td></td>
	</tr>
	<tr>
		<th>対策</th>
		<td></td>
	</tr>
	<tr>
		<th>認証ログの取り扱いについて</th>
		<td>一般的にはSIEMｗ使うが、Lookerでも十分情報は収集できる</td>
	</tr>
	<tr>
		<th>攻撃のブロック方法と課題</th>
		<td></td>
	</tr>
	<tr>
		<th>攻撃のブロック方法と課題に対する解決策</th>
		<td>どれだけbotっぽいかを計算できる機能もある。PIXIVはこちらを使った。ユーザーにチェックさせるような挙動は嫌だったため。</td>
	</tr>
	<tr>
		<th>攻撃のブロック方法と課題に対する解決策の課題</th>
		<td></td>
	</tr>
	<tr>
		<th>攻撃のブロック方法と課題に対する解決策の課題の解決策</th>
		<td>oauthを使う。</td>
	</tr>
	<tr>
		<th>上記を実装してよかった点</th>
		<td></td>
	</tr>
	<tr>
		<th>漏洩パスワードDBの活用</th>
		<td></td>
	</tr>
</table>

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/e7f0d46b-5403-7494-5114-c659eec0fe58.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/e971375b-30ed-95f1-51cd-b5d92797e8db.png)
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/bbb21fb8-9e73-2fb8-3da5-15967272dd01.png)
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/c79b5fef-76f1-dfab-f36e-77b4b31ddac9.png)
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/a896544a-241e-0eb1-264e-2a2281964e7c.png)
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/9e52eb08-f795-2b60-5d54-0924cab6dec8.png)
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/c8e9c950-c6ba-b848-dc86-674e5bfd2e6b.png)
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/f14333e3-51de-91c6-f10f-8db03de6053c.png)
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/b1b9c771-0a62-0add-da39-178cf761d503.png)
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/f2c7b218-8d8a-3c20-8f15-0c439b823691.png)
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/f0138dd8-c5a0-1acd-a4d9-5ed5e1c5d4f4.png)
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/ca91fdb7-1f2f-c5d8-f874-ed0b52237954.png)
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/9a19cdf8-990e-a446-cdb6-2116024351be.png)
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/4e343abd-1c49-8a5a-16b5-869b16639c49.png)
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/1f02dce3-0a16-e46c-8692-afbe608af93e.png)
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/b0020bed-e7ca-d35a-31a0-1a7cb2a2bc98.png)
【Rails6】text_areaのplaceholderにFontAwesome5を導入するまでに数時間費やした件。

##はじめに
今回はplaceholderにFontAwesomeを導入したら、予想以上にかなり時間がかかってしまった(3~4時間ほど)ので、知識定着のためにアウトプットします。


#### 以下完成イメージ

![test](https://gyazo.com/56badbb3505fdceb6bc97d34c12d8084/raw)


##そもそもFontAwesomeとは？

気軽にネット上でよくみかけるアイコンを気軽に使えるツールのことです。
無料なのに豊富なアイコンを使用することができます！（有料もあるヨ）
https://fontawesome.com/

▼無料版アイコンを一部抜粋しました。
![test](https://gyazo.com/95516fbeae0fa4b910347a2872333619/raw)



##結論、以下のようにFontAwesomeを導入する

```application.scss

 $fa-font-path: '@fortawesome/fontawesome-free/webfonts';
 @import '@fortawesome/fontawesome-free/scss/fontawesome';
 @import '@fortawesome/fontawesome-free/scss/solid';
 @import '@fortawesome/fontawesome-free/scss/regular';
 @import '@fortawesome/fontawesome-free/scss/brands';
 @import '@fortawesome/fontawesome-free/scss/v4-shims';

```

```html
<%= f.text_area :content, size: "90x5", maxlength: "1000", placeholder: "\uf036", class: "fa" %>

#classで"fa"を指定するのを忘れないで！！！
#placeholderを指定する際に "\uf036"の\uを忘れないで！！！
```
これだけで簡単にFontAwesomeに必要なcssを当ててくれる！！

![test](https://gyazo.com/63ec500cb2f8b219a74d24ac92b69d42/raw)

##ワイが長時間悩んだ場所

結論、FontAwesome導入時にapplication.jsに記述した内容で不具合を起こしました。

```application.js
import '@fortawesome/fontawesome-free/js/all';
// ↑この記述をコメントアウトしていなかった！！！！

```
▼こちらは、コメントアウトせずに表示されたtext_areaのviewはこちらです。(明らかにおかしい)
![test](https://gyazo.com/2cd9b950dd562c1eb741cc8db4e464bc/raw)


##なぜ```import '@fortawesome/fontawesome-free/js/all';```の記述があることでFontAwesomeが読み込まれなくなるのか？


[こちらのブログ](https://techtechmedia.com/slick-raills/)を参考にさせていただき、解決しました。

```application.js
import '@fortawesome/fontawesome-free/js/all';
```

application.jsの記述の意味として、
```node_modulesフォルダ```内の```@fortawesome/fontawesome-free/js/all```ファイルを参照しています。

>javaScriptで使用できるFontAwesomeのパッケージをインストールしていますが、この記述があるとアイコンによるスライド移動ができなくなってしまいます。

上記参考にした記事の内容を抜粋しました。
この内容から考え出した仮説としては、
**placeholderはドキュメントの内容を変更する性質を持ちます。（非同期でアイコンや文字が消える）
したがって、placeholderはjsの要素が含まれており、```application.js```に```import '@fortawesome/fontawesome-free/js/all';```の記述があることで動きのあるFontAwesomeのアイコンが使用できないので、表示ができなかったと思われます。**

## まとめ
ネットに書いてある記事を参考にして環境構築することは必要不可欠ですが、「なぜこの導入方法なのか？」を常に問いながら導入していくことの重要性を痛感しました。
また、さらに深掘りができる内容なので、また何かわかり次第Qiita更新します。（たぶん）

## 参考にさせていただいた記事
https://techtechmedia.com/font-awesome-rails6/
https://techtechmedia.com/slick-raills/
https://qiita.com/sasakura_870/items/7f72c80f89d66923f6f1
https://qiita.com/kou_pg_0131/items/9842a490936dff5b29ac
CKEditorのきほん

## 設定の書き方
###### その1（個別に直書きで指定）
```html
  <h1>テスト</h1>
  <textarea name="editor1" id="editor1">replaced</textarea>
  <script>
    CKEDITOR.replace('editor1', {
      customConfig: './ckeditor_config.js',
      // language: 'fr',
      // uiColor:'#9AB8F3'
    });
  </script>
```

###### その2（個別の設定ファイル）
```html
  <h1>テスト</h1>
  <textarea name="editor1" id="editor1">replaced</textarea>
  <script>
    CKEDITOR.replace('editor1', {
      customConfig: './ckeditor_config.js',
    });
  </script>
```
```js:/ckeditor/ckeditor_config.js
 CKEDITOR.editorConfig = function( config ) {
    config.language = 'fr';
    config.uiColor = '#AADC6E';
};
```

###### その3(全体の設定ファイル)
```html
  <h1>テスト</h1>
  <textarea name="editor1" id="editor1">replaced</textarea>
  <script>
    CKEDITOR.replace('editor1');
  </script>
```
```js:/ckeditor/config.js
 CKEDITOR.editorConfig = function( config ) {
    config.language = 'fr';
    config.uiColor = '#AADC6E';
};
```