OmniAICreator/Qiita-1.07M huggingface dataset:
ファイルの分割出力

読み込んだテキストの内容を別のテキストに出力する。
出力ファイルの上限サイズを設定し、上限サイズを超える場合は別ファイルに分割して出力する。
分割出力する場合はファイル名の末尾に連番を付与する。           


```C#
            List<string> dataList = new List<string>();

            using (StreamReader sr = new StreamReader(@"C:\Work\test\src.txt", Encoding.GetEncoding("UTF-8"))) {

                while (sr.Peek() >= 0) {

                    dataList.Add(sr.ReadLine());
                }
            }

            var outPath = @"C:\Work\test";
            var outFileBaseName = "out";
            var outFileName = "";

            var dataLen = 0;
            var dataMaxLen = 50000;

            var fileCnt = 0;
            var row = 0;

            var splitFlg = false;

            while (true) {

                splitFlg = false;

                outFileName = outFileBaseName;

                if (fileCnt > 0) {
                    outFileName += "_" + fileCnt.ToString("000");
                }

                using (StreamWriter sw = new StreamWriter(Path.Combine(outPath, outFileName + ".txt"), false, new UTF8Encoding(false))) {

                    dataLen = 0;

                    for (int i = row; i < dataList.Count; i++) {

                        var rowData = dataList[i];
                        dataLen += Encoding.GetEncoding("UTF-8").GetByteCount(rowData);
                        dataLen += 2; // 改行コードの分

                        if (dataLen > dataMaxLen) {
                            fileCnt++;
                            row = i;
                            splitFlg = true;
                            break;
                        }

                        sw.NewLine = "\n";
                        sw.WriteLine(rowData);
                    }

                    if (!splitFlg) {
                        break;
                    }

                }
            }

```
Prophetを使ってみた（予測）

前回はAnacondaのインストールからjupyter notebookが起動するところまで実施しました。今回はProphetを使った時系列データの予測をやってみたいと思います。

前回の記事はこちらから

https://qiita.com/nw-engineer/items/d167e9e193124bacfb96

## 1. データダウンロード

下記サイトより為替レート(日本円からドル)のグラフをダウンロードします。

https://fred.stlouisfed.org/series/DEXJPUS

ダウンロードしたデータは作成したDataディレクトリに保存しておきます。

## 2. 事前準備
ここからはJupyter notebook上での作業となります。必要となるライブラリをインポートしておきます。

```bash
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from fbprophet import Prophet
```

CSVデータを読み込んで6行分表示してみます。

```bash
df = pd.read_csv("./data/DEXJPUS.csv")
df.head(6)
```

データが読み込まれている事が分かります。また、元データの6行目の値がperiod(．)になっている事が分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335933/5ce10fb3-c165-844d-23b3-7d312be7649e.png)

データの型を確認してみます。

```bash
df.dtypes
```
DATE/DEXJPUS列がobject型になっています。このままではprophetを使うことが出来ませんので、データクリーニングを実施します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335933/ab85b1d5-ae1f-80a2-22e5-f35438ba35b8.png)


下記コマンドを実行して型を変更し、periodはNoneに変更します。 

```bash
df["DEXJPUS"] = df["DEXJPUS"].replace(".", None)
df["DEXJPUS"] = df["DEXJPUS"].astype(np.float64)
df["DATE"] = pd.to_datetime(df["DATE"])
df.dtypes
```

型が変換されたことがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335933/444d30ca-7996-5a0f-f870-2674fbf44e1f.png)


## 3. Prophet実行
prophetは日付と値から成るデータであれば実行する事が可能ですが、カラム名がそれぞれds, yにセットされている必要があります。よってDATEをds、DEXJPUSをyとします。

```bash
df["ds"] = df["DATE"]
df["y"] = df["DEXJPUS"]
```
実際に未来予測がどれくらい正確なのか見ていきたいと思います。Prophetでは訓練データを読み込ませる形になりますので、訓練データとテストデータを定義します。

```bash
df_train = df[df["DATE"] <= "2021-11-19"]
df_test = df[df["DATE"] >= "2021-11-20"]
train_size = len(df_train)
test_size = len(df_test)
```
Prophetは新しいオブジェクトをインスタンス化することでモデルを適合させます。fitメソッドを呼び出して、データフレーム（訓練データ）を渡してあげます。

```bash
m = Prophet()
m.fit(df[:train_size])
```

## 4. 予測

予測してみましょう。訓練データ日以降の60日分の予測値がどのような結果を示すのか見たいと思います。

```bash
future = m.make_future_dataframe(periods=60, freq="d")
forecast = m.predict(future)
forecast
```
forecastにはProphetの学習結果からトレンドや予測値の上限/下限(yhat_upper/yhat_lower)などの値を確認する事が出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335933/002bfe49-5351-cd0d-24f5-424975b72793.png)


予測結果をグラフ化してみます。

```bash
m.plot(forecast)
plt.plot(df[train_size:train_size+test_size].ds, df[train_size:train_size+test_size].y,".",color="red", alpha=0.6)
```

少しわかりずらいですが、2021-11-20 ~ 2022-02-13まで青線がProphetの予測値、赤い点は実際の値になります。おおむね、予測した値通りに推移している事が分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335933/996b9e91-4fa5-c174-fcdb-dcee106f9ced.png)


## 5. その他
次回は異常検知について見ていきたいと思います。
Prophetを使ってみた（事前準備）

Prophetを利用した時系列データの予測などやってみたので、備忘録も兼ねて

## 1. 環境
- OS: CentOS 7.9
- kernel: 3.10.0-1160.53.1.el7.x86_64
- Anaconda: 3-5.3.1
- jupyter: 4.4.0

## 2. OS最新化＆ツールインストール
いつものようにOSは最新化します。また開発ツールパッケージも入れましょう。

```bash
yum update -y
reboot

yum groupinstall 'Development Tools' -y
yum install wget -y
```

## 3. Anacondaインストール
下記サイトからAnaconda（Linuxインストール用スクリプト）をダウンロードしてインストールします。

https://repo.anaconda.com/

wgetでダウンロードし、実行します。

```bash
wget https://repo.anaconda.com/archive/Anaconda3-5.3.1-Linux-x86_64.sh
bash Anaconda3-5.3.1-Linux-x86_64.sh

Please, press ENTER to continue
>>>                                           # Enterを押す

Do you accept the license terms? [yes|no]
[no] >>> yes                                  # yesとタイプしてEnterキーを押す

  - Press ENTER to confirm the location
  - Press CTRL-C to abort the installation
  - Or specify a different location below

[/root/anaconda3] >>>                         # Enterを押す

in your /root/.bashrc ? [yes|no]
[no] >>> yes                                  # yesとタイプしてEnterキーを押す

Do you wish to proceed with the installation of Microsoft VSCode? [yes|no]
>>> no                                        # noとタイプしてEnterキーを押す


source ~/.bashrc
jupyter --version
```

## 4. 仮想環境の作成
Python環境に影響を与えずにモジュールの追加・入れ替えが可能となる仮想環境を利用します。取り急ぎ、作業用ディレクトリを作成します。 

```bash
mkdir -p workspace/prophet
cd workspace/prophet
mkdir data
```

下記コマンドから仮想環境を作成しましょう。

```bash
conda create -n prophet python=3.8 jupyter
conda activate prophet
```

## 5. Prophet関連モジュールのインストール
Prophetを利用する上で必要となるモジュールをインストールしておきます。

```bash
conda install -c anaconda pandas
conda install -c anaconda matplotlib
conda install -c anaconda scikit-learn
conda install -c anaconda plotly
conda install -c conda-forge fbprophet
```

## 6. jupyter notebookの起動
下記コマンドを実行してjupyter notebookを起動させましょう。

```bash
firewall-cmd --add-port=8888/tcp --permanent
firewall-cmd --reload
jupyter notebook --allow-root --ip=*
```

## 7. ブラウザからアクセス
無事アクセスできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335933/7e37d0ab-832e-08f4-3532-5e948b0c22f8.png)

## 8. その他
次回はProphetを使った予測をしていきます。
データ基盤のワークフローエンジン選定


データ基盤はサービスにあるすべてのデータを集めて、分析できるようにする基盤です。
例えば、Mysql、Postgresqlにあるようなマスターデータだったり、Nginxのアクセスログだったり、Google Speadsheetにあるデータとか、
SalesForceにあるデータを集めて、中間テーブルを作ったり、Tableauで可視化するためのデータを作ることがあります。

ワークフローエンジンとしてはいろんな候補がありますが、現職の会社の要件に合ったワークフローを選定したいです。

## 要件
私は２年間データ分析基盤のワークフローをDigdagで安定運用していたので、
そのノウハウから安定稼働するために必要な要件を下の表にまとめて、各ワークフローエンジンを調べました。

 要件 | Digdag | Airflow | Luigi | Perfect | Argo
-|-|-|-|-|-
SQLの実行しやすさ | ◎ |o|x|x|x(コンテナ専用)| 
ワークフロー記述しやすさ | ◎ |o|x|△（機械学習なら良いかも)|-
backfill機能 | ◎ | x (aws maaw) |-|-|
複数人が同時に開発できるか | ◎ |x |-|-|-
Operator, pluginの豊富さ | ◎ |o|-|-|-
Embulkとの相性がいいこと | ◎ |o|-|-|-
安定稼働 | ◎ |o|-|-|-
依存関係が見やすい| ✖︎ |◎|-|-|-
 -| 採用 | 不採用 | 不採用 | 不採用 | 不採用


上の表の要件に沿って、上から各ワークフローエンジンを比較しました。
重要な要件が上にあるので、上から罰が多いと、下の要件を調べません。
私はDigdagに詳しいのでDigdagを調査する必要はなかったのですが、
結果的に1年半前にワークフローエンジンを選定する時と、結果が変わらずDigdagを作用することにしました。

##
C++でcsv fileを要素数指定なしの2D vectorに格納する方法の備忘録


1D vectorであれば要素数指定なしで以下のようにpush_backすれば、自動的に要素数の上限が増えていくので、初期化時に要素数の上限指定なしで大丈夫。

```
{1,2,3,4,5,6,7,8,9,...}
```

```C++:C++
vector<string> vec1D;
vec1D.push_back(strbuf);
```
2D vectorの場合、中に入っている要素vectorの要素指定なしでは、いくつかひっかかったところがあった。

```
{ {1,2,3,...},
  {1,2,3,...},
  {1,2,3,...},
  {.........} }
```

1. **以下のように始めに空の1D vectorを追加しないと、index out of rangeになる。**

    ```C++:C++
vector<vector<string>> vec2D;
vec2D.push_back(vector<string>());
    ```

2. **以下の方法で要素数を指定して、push_backしないと追加されない。**

```C++:C++
vec2D.at(i).push_back(strbuf);
```
  

## サンプルコード

要素指定は.at()を使いましょう。。。
```c++
vec2D.at(i).at(j)
```

```C++:C++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <time.h>
#include <vector>
#include <regex>

using namespace std;

auto split = [](string src, auto pat){
    vector<string> result{};
    regex reg{pat};
    copy(
        sregex_token_iterator{src.begin(), src.end(), reg, -1},
        sregex_token_iterator{},
        back_inserter(result)
    );
    return result;
};

int main(int argc, char *argv[]){
 
  const char delimiter = ',';//カンマ区切り
  long long i = 0;
  string input_csv_file_path  = argv[1];
  ifstream ifs_csv_file(input_csv_file_path);
  string str_line_buf;
  str_line_buf.reserve(1000);
  string str_sep_buf;
  vector<vector<string>> vec2D;//csvファイルの中身を格納する2D vector, 容量指定なし
 
  while (getline(ifs_csv_file, str_line_buf)){
      i = 0;
      istringstream line_sep(str_line_buf);
      while (getline(line_sep, str_sep_buf, delimiter)){
        //始めに空vectorを追加するため、例外処理を挟む
        try{
          vec2D.at(i).push_back(str_sep_buf);//2D vectorのi番目の要素に文字列を追加する
        }catch(out_of_range& e){
          cerr << "Out of Range: " << e.what() << endl;
          vec2D.push_back(vector<string>());//始めに追加する場合は、空の1D vectorを追加する
          vec2D.at(i).push_back(str_sep_buf);
        }
        ++i;
      }
  }
  //標準出力
  for (long long j = 0; j < vec2D.size(); j++) {
      for (long long k = 0; k < vec2D[j].size(); k++) {
          cout << vec2D[j][k] << endl; //j=row number, k=column number;
      }
  }
}
```

## まとめ
C++でcsvファイルをreadして、要素数指定なしの2D vectorに格納する方法を紹介した。より簡潔な方法を知れば更新していく。
WorkatoのSalesforceコネクターのコネクションでSalesforceと接続しているユーザーの情報を取得する


# 手順
## １．Salesforceコネクターをアクションに追加し、「Custom action」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/cb76621c-eb2f-bd19-4823-6f997eb9b061.png)

## ２．次の通り設定します。
Method `GET`
Path `/services/data/v24.0/chatter/users/me`
また、必要に応じてResponse body等も設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/6206e68d-3c9c-25b0-9617-1259c6938898.png)

※その他の項目は、デフォルトのままとします。

# 結果
以下のようにSalesforceと接続中のユーザー情報を取得することが出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/acfcea47-6dad-0e80-d8dd-27aed10856ff.png)

# 参考

https://developer.salesforce.com/forums/?id=906F00000009A0RIAU
【Swift】XcodeのシミュレータでPush通知をタップした場合のテストを簡単にやりたい

# どういうことか

Push通知をタップするとAppDelegateの `UNUserNotificationCenterDelegate` が働くが、こちらの中身がちゃんと動いてるかなどを確認したい。
**シミュレータでのチェックを想定している**。実機でチェックする方法はここでは省く（手間がかかる方法しか見つからなかった）。

## apnsファイルを作りましょう

Json形式でこんなものを作る。

```json:test.apns
{
    "Simulator Target Bundle": "アプリのBundle Identifier",
    "aps":{
        "alert":"テストです",
        "sound":"default",
        "badge":1
    }
}
```

保存して `.apns` という拡張子で保存する。
作ったファイルをシミュレータにドラッグドロップすると通知が出てくるので、タップして動作を確認しよう。
ちなみに `"badge"` を100にするとアイコンに表示されるバッジ数は100になるけど届く通知は1つだけ。

## 参考

コマンドラインから送る方法もある。

https://qiita.com/koogawa/items/85c0dd0abd2f1970c5fc
javascript61_thisの使い方

ブラウザが関数を呼び出す度に、関数内部に一つの隠している引数を渡します。
この引数はthisです。thisはオブジェクトを指しています。

オブジェクトとは、関数実行時のオブジェクトになります。
関数の呼び出し方の違いによって、thisが指しているオブジェクトが違ってきます。

1 関数の形で呼び出す時、thisはwindowになります。
2 メソッドの形式で呼び出す時に、thisはメソッドを使うする対象となる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/2b006993-ba1e-563a-b109-133580a2018a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/22697222-244f-1ff1-26a9-abf8a393bb9d.png)
ValueError: Memory growth cannot differ between GPU devices への対処方法

前提
python3.x
tensorflow2.x
複数のgpuデバイスがある環境

## 症状：
```
    raise ValueError("Memory growth cannot differ between GPU devices")
ValueError: Memory growth cannot differ between GPU devices への対処方法
```
のエラーが出る。
## 対処方法
`set_memory_growth()`を実行している箇所を見付ける。

変更の差分

```
-if len(physical_devices) > 0:
-    tf.config.experimental.set_memory_growth(physical_devices[0], True)
+for i in range(len(physical_devices)):
+    tf.config.experimental.set_memory_growth(physical_devices[i], True)
```    
複数のphysical_devicesがあるのに、１つしか設定していないことで生じていた。
Expoのapp.jsonを並び替える「sort-expo-appjson」を作った

Javascriptで開発する際には切っても切り離せない `package.json` を綺麗に並び替えるために fixpackを使っている人も多いのではないでしょうか。

https://github.com/HenrikJoreteg/fixpack

そんなみんな大好きfixpackにならって、Expoで使われる `app.json` を並び替えるための「sort-expo-appjson」というCLIツールを作りました。

https://github.com/KazuyaHara/sort-expo-appjson

Expoの[app.jsonのドキュメント](https://docs.expo.dev/versions/latest/config/app/)を見ると分かりますが設定できる項目が多岐にわたり、その順序を綺麗に維持しようとすると、いつもドキュメントとにらめっこをして「このプロパティはこれとこれの間に追加して…」という事をやっていました。

sort-expo-appjsonなら、そんな面倒な並べ替え作業が

```sh
$ sort-expo-appjson # or seajson
```

だけで完了です。Expoのドキュメント上で定義されている並びと同じ順番に並び替えてくれます。

## やっていること
**sort-expo-appjson** では、ExpoのAPIから `app.json` に関するJSONスキーマを取得してきて、そこから基準となるjsonのキー順序を理解します。あとはお手持ちの `app.json` を見比べて、正しい順序になるように並べ替えをするだけです。

いたってシンプルなCLIですが、いつもアプリをExpoベースで作ることが多かった自分にとっては `app.json` をきれいに保つことは地味に面倒な作業だったので、短縮された実作業時間以上に心理的な開放感から満足度が高いツールになりました。
【PHP】Laravelとは？？

## はじめに
Laravelの学習や開発経験のあるエンジニアです。
今までインプット重視になってしまっていた反省も踏まえ、アウトプットしていきたいと思います。
今回は、Laravelとは何ぞやという事について書いていきます

## PHPとは
前提としてLaravelはPHPのフレームワークです
PHPを簡単に解説します
> PHP (PHP: Hypertext Preprocessor を再帰的に略したものです) は、広く使われているオープンソースの汎用スクリプト言語です。 PHP は、特に Web 開発に適しており、HTML に埋め込むことができます。
引用(https://www.php.net/manual/ja/intro-whatis.php)

PHPはWebアプリケーションの開発を手軽に実現することができる、という特徴を持ったプログラミング言語です。
初学者にもおすすめなプログラミング言語だと思います

注意点としては動的なHTMLの生成もデータベースへの接続も柔軟にできてしまいます。柔軟ということは、整理されていないプログラミングが容易に出来上がってしまうという側面を持ちます。

## フレームワークとは
次にフレームワークとは何なのか。
フレームワークとは、プログラムの基本的な仕組み・機能を提供するものです。開発に必要な機能をあらかじめ用意されているので、ゼロから用意する必要がなくなります。
また、セキュリティを強化したり、データベースアクセスを効率よくプログラミングしたりするのに便利な、多種多様な機能がフレームワークには備わっています。

## Laravelの特徴
Laravelには以下のような特徴があります。

- 読みやすく、書きやすいコード
- MVCフレームワーク
- スケルトンコードの自動生成

PHPのフレームワークという事でとても柔軟です。簡潔なコードを記述することだできるヘルパ関数をLaraveでは多く提供しています。
Laravelのプロジェクト構成は、広く使われているMVCモデルに基づいた構成です。その構成要素は、コマンドラインインターフェースArtisanにより自動生成されます。



夜も遅くなってきたのでここら辺で終わらせていただきますzzz
'Tensor' object has no attribute 'numpy' を生じる短いスクリプト例


上記のエラーを生じる短いスクリプト例
## 前提
python3.x
tensorflow2.x
## スクリプト
```
import tensorflow as tf
tf.compat.v1.disable_v2_behavior()
c = tf.constant([[1.0, 2.0], [3.0, 4.0]])
d = tf.constant([[1.0, 1.0], [0.0, 1.0]])
e = tf.matmul(c, d)
print(c)
print(d)
print(e)
print("print numpy.array")
use_numpy_method = True
if use_numpy_method:
    print(e.numpy())
else:
    print(e.eval(session=tf.compat.v1.Session()))
```
## 生じたエラー
```
AttributeError: 'Tensor' object has no attribute 'numpy'
```
## 対処方法１
`e.numpy()`
を次のように置き換えると動作する。
`e.eval(session=tf.compat.v1.Session())`
Laravel 画像アップロード機能　手順

### 超わかりやすい記事

https://note.com/laravelstudy/n/n038bd68f53a7#BkZEu

この方の記事は非常にわかりやすくい。
具体的にいうと、ただコードを書くだけではなく
各コードに対してのポイントが書いてあるところが良い。

noteフォローしておこう。


### 参考にした記事

https://readouble.com/laravel/5.5/ja/validation.html#rule-image


https://webukatu.com/wordpress/blog/39841/
【swift】delegate実装を整理する

MVPモデルを学習中なので、protocolに続きdelegateについてもまとめておきたいと思います。


##delegateとは
delegateは英語で「人に任せる」という意味を持っており、「あるクラスから他のクラスに処理を任せる」という処理です。


##delegate実装の登場人物（役割）

###①プロトコル
delegateで使用するメソッドやプロパティを定義しておきます。
メソッドは定義するだけ。
プロトコルで定義されたメソッドはデリゲートメソッドと呼ばれます。
プロトコルについては↓
https://qiita.com/taro-ken/items/0ac5367e5696ef1491ec

###②処理を任せるクラス
処理を任せる相手を保持するプロパティ（プロパティ名はdelegateがよく使われる）を持つ。
処理の流れを実装する

###③処理を任されるクラス
プロトコルで定義されたデリゲートメソッドを実装します。


##実装の例

###プロトコル（protocol.swift)

```ruby:protocol.swift
protocol ActionDelegate {

    func attack()
    func defence()
    func magic()
}

extension ActionDelegate {

    // デフォルト実装
    // プロトコル適合先で実装しなくてもエラーにならなくなる
    func magic() {
        print("魔法を使う")
    }
}

```

###処理を任せる側（Human.swift)

```ruby:Human.swift

final class Human {

   weak var delegate: greetingDelegate?  // 処理を任せる相手を保持する
//循環参照を防ぐためweakをつける

    func greet() {
        guard let delegate = delegate else {
            // 処理を任せる相手が決まっていない場合
            print("No Person")
            return
        }
        if type(of: delegate) == Senshi.self {
            // 処理を任せる相手がSenshiクラスの場合
            // 攻撃力と守備力をログに出力
            delegate.attack()
            delegate.defence()
        } else if type(of: delegate) ==Mahotukai.self {
            // 処理を任せる相手がmahotukaiクラスの場合
            // 攻撃力と守備力と魔法力をログに出力
            delegate.attack()
            delegate.defence()
            delegate.magic()
        }

    }
}

```


###処理を任せる側（Senshi.swift、Mahotukai.swift)

```ruby:Senshi.swift
final class Senshi: ActionDelegate {

    func attack() {
        print("攻撃力は５０です")
    }

    func defence() {
        print("守備力は３０です")
    }
}

```

```Mahotukai.swift

final class Mahotukai: ActionDelegate {

    func attack() {
        print("攻撃力は1０です")
    }

    func defence() {
        print("守備力は2０です")
    }

 　　　　　　func magic() {
　　　　　　　　　　　　　　　　// デフォルト実装を上書き
        print("魔法力は4０です")
    }
}
```

##試しに動かしてみる

```ruby:ActionViewController.swift
import UIKit

final class ActionViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let　human =　Human()  
       // 処理を任せるクラス

        let　senshi =　Senshi()   
       // 処理を任されるクラス

        human.delegate = senshi

       human.greet()
    }
}
```


```ruby:出力結果
攻撃力は５０です
守備力は３０です
```

思った通りの結果が得られました。

##おわりに
MVPモデルで使う際はもう少し複雑になってくるので、基礎をしっかり固めつつ処理が多くなっても終えるよう学んでいきたいと思います。
【Rails】wheneverでcronを設定

## whenever、cronとは
#### cron
「クロン」または「クーロン」と呼ばれ``○時になったら○○のコマンドを実行``といった具合に、定期的にコマンドを実行するためにメモリ場で常に命令を待機しているプロセス。

この``cron``に対して命令を行うには、``crontab``に記述しておけば、決まった時間に実行してくれる。

※UNIX系のOSには、``cron``が標準で備わっている。

#### whenever
``cron``の設定を、``ruby``の簡単な文法で扱えるようにしたライブラリが``whenever``。

``whenever``を使えば``cron``に対して命令を行う``crontab``に記述する内容を``ruby``言語で書けるようになる。
つまり``cron``の設定を自分で書く代わりに、この``gem``を使用すると比較的簡単に定期実行ができる。

## wheneverでcronを編集
まずは``whenever``をインストールする。
Gemfileに記入後、``bundle install``を実行。

```Gemfile
gem 'whenever', require: false
```
※``require: false``は``Rails``の内部で``whenever``を使うわけではないため、``Rails``の実行時に読み込まないようにするため。

インストール後、下記を実行することで``schedule.rb``というファイルが生成されるので、ここに定期実行させたいタスクやその頻度などを記述して``cron``を編集する。

```
$ bundle exec wheneverize .

[add] writing `./config/schedule.rb'
[done] wheneverized!
```

``[done] wheneverized!``が出ればOK。
``schedule.rb``に記述していく。

```schedule.rb
# Rails.rootを使用するために必要
require File.expand_path(File.dirname(__FILE__) + '/environment')

# cronを実行する環境変数
rails_env = ENV['RAILS_ENV'] || :development

# cronを実行する環境変数をセット
set :environment, rails_env

# cronのログの吐き出し場所
set :output, "#{Rails.root}/log/cron.log"

#定期実行したい処理を記入
every :hour do
  rake 'article_state:update_article_state'
end
```

今回は定期実行の箇所に、``rakeタスク``の記事ステータスが1時間ごとに変更される記載がしてある。

また、``rakeタスク``実行時に``puts``などで文字列をログに出したい場合は、``rakeタスク``はデフォルトで``--slientオプション``付きで登録されるため、``job_type``の再定義を行う必要がある。

## job_typeとは
``whenever``では次の4つの``job_type``と呼ばれる4種類の処理を行うことができ、時間も好きに設定できる。

|job_type | 実行内容 |
|:-:|:-:|
|  command | bashコマンド実行  |
| rake  | rakeタスク実行  |
|  runner |  Rails内のメソッド実行 |
|  script | scriptの実行  |

これらはデフォルトでは以下のコードと同じ挙動をする。

```rb
job_type :command, ":task :output"
job_type :rake,    "cd :path && :environment_variable=:environment bundle exec rake :task --silent :output"
job_type :runner,  "cd :path && bin/rails runner -e :environment ':task' :output"
job_type :script,  "cd :path && :environment_variable=:environment bundle exec script/:task :output"
```

自分で``job_type``を作成することもでき、``your_job_name``の部分は自由に命名できる。

```rb
job_type :your_job_name, 'cd :path && export PATH=/usr/local/bin:$PATH && 
  :environment_variable=:environment bundle exec rake :task --silent :output'

every 2.hours do
  your_job_name "sample_task"
end
```

## crontabを実行
``whenever``で``cron``の内容を記述したので、この``cron``を実行させるために``crontab``コマンドを打つ。

```
# wheneverの設定更新
$ bundle exec whenever --update-crontab

# 設定内容にエラーがないか確認
$ bundle exec whenever

# 設定されているcronを見る
$ crontab -l

# crontabの設定削除（定期実行を辞めたいとき）
$ bundle exec whenever --clear-crontab
```

## 参考記事
[Railsでwheneverを使ってcronを設定する](https://qiita.com/Esfahan/items/e7a924f7078faf3294f2)
[gem whenever を使って Rails で定時処理を作る](https://qiita.com/hirotakasasaki/items/3b31966294a809b99c4c)
[]()
Terraformの公式ドキュメントの調べ方

## この記事の目的
Terraform、楽しいですよね？

私は最近入門したばかりでまだまだ表面的なところしか使えていないとは思いますが、
それでも楽しさを感じています。

そんなTerraform、公式ドキュメントが良い感じなのですが
最初は調べたいものをどこで調べればいいか戸惑いました。

なので私と同じように、Terraformに関して公式ドキュメントを読みたいけど
知りたいところを探すにはどうすればいいか分からない、といった人のために
この記事にまとめます。

逆に「公式ドキュメントのここ読むとTerraform偏差値高まっていいよ」というのがもしありましたら
ぜひ教えてください！

**なお以下の説明は2022/02/17現在での情報になります。
今後Terraform公式サイトのレイアウトや掲載情報など変わる可能性はありますので
その点はご了承ください。**

## まず結論
Terraformの公式ドキュメントのどれが何について書かれているのかは
公式ページの[**ここ**](https://www.terraform.io/docs)に実は記載があります。

すごく端的にまとめてくれていますので、まずはここを見ましょう。

以下ではその中でも良く読むことになるものを紹介します。

## Terraformの設定全般について調べたい時
[**Configuration Language**](https://www.terraform.io/language) というドキュメントを読みましょう。

例えば、depends_onというmeta-argumentsがありますが、
それについて調べたい時は以下のような流れで行います。

1. まずConfiguration Languageを開く。
1. 左のFilterと書かれている検索窓にdepends_onと入力する。
![lang01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523625/32089600-3d14-b37f-5fb8-afe7eaa29bb2.png)
1. その下に表示されている検索結果をクリックする。
![lang02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523625/80b64334-7790-b6ae-548f-7831136cfe95.png)

以上です。簡単ですね。

## Providerについて調べたい時
[**Terraform Registry**](https://registry.terraform.io/browse/providers)にあるドキュメントを読みましょう。

例としてここでは、AWSのresourceの1つである
aws_security_groupに関して調べたい場合の流れを書きます。

1. Terraform Registryを開く
1. AWSのリンクをクリックする。
![prov01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523625/4d5aabc1-2dc0-5665-6966-3cc847cead07.png)

1. 右上にあるDocumentationをクリックする
![prov02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523625/cf8cef00-888d-f48c-e63b-075acadd281b.png)

1. 左のFilterと書かれている検索窓にaws_security_groupと入力する。
![prov03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523625/6073392e-d576-cb59-89ea-004abfa168ec.png)

1. 下の検索結果に表示されている中から目的のものをクリックする。
![prov04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523625/9fce600f-b40e-4dc0-b235-36a34580b9e1.png)

以上です。

## Terraformのドキュメントを横断して調べたい時は？
Terraformのドキュメントの中央上あたりに表示されている検索窓に入力します。

![all1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523625/229c241b-0e7e-8deb-0e5d-cdfa2ab6b6d0.png)

そうすると横断的に検索してくれますが、
私個人的には対象となるドキュメントを開いた上でFilterを使うやり方の方をよく使います。

大抵はConfiguration Languageで検索すれば何とかなります。そう強く信じています。


**ちなみにこの横断検索の時Terraform Registry側にあるドキュメントは検索対象に入っていないので注意しましょう。**

つまりAWSの設定とかを調べたい時には使えません。

各ProviderやModuleについて横断的に調べたい時はTerraform Registry側にいって検索する必要があります。


## あとがき
分からないところがあっていろいろと検索したんだけどもドンピシャの情報がなくて
沼にハマりかけてたところで公式ドキュメント読んでみたら普通に書いてあった、
みたいなことはTerraformに限らず良くあることですよね。

英語だから読むのちょっと面倒だなあと思っていても、
結局はそれが最短だったりします。

エンジニアたるもの、英語だろうとまずは公式ドキュメントという姿勢は常に持っていたいですね。
自戒です。
言語設定で姓と名の位置が変わってしまうようです。


+ We couldn't find a user named 'Noah Larkin' with the configuration specified in the project. Be sure you have entered the user information correctly.


始めに日本語にしていて、あとで英語に切り替えてもうまくいかないことがあります。
その場合は姓と名の中身を入れ替えてみます。

[We couldn't find a user named 'Noah Larkin' with the configuration specified in the project. Be sure you have entered the user information correctly.](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000FWfSV)


これが後から英語にしたパターン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/9de65390-3388-cca0-1cab-edba76f1921a.png)

こっちは最初から英語にしたパターン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/5d33f91c-03d4-4085-1208-7f13cd5d1702.png)

+ [We couldn't find a user named 'Noah Larkin' with the configuration specified in the project. Be sure you have entered the user information correctly.](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007Yvi5G)

:::note
Swap the first and the last name works.

https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000A8b3wSAB
:::
【Figma】Bottom Navigationを固定したいのにFix position when scrollingが表示されない時・・・

# はじめに
👇を参考にしながらFigmaでアプリのUIデザインを作っているとき

https://yososhi.com/archives/figma-prototype-scrolling/

![スクリーンショット 2022-02-17 16.54.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/22d7dc52-5bf5-a453-1802-3c00d89aff31.png)

Bottom Navigationを固定しようとしたのですが、、、

![スクリーンショット 2022-02-17 16.56.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/c6565393-46bf-379a-4808-9f8214ad8eef.png)

**「あれ？？？`Fix position when scrolling`のチェックボックスがないぞ・・・」**

となりました、、、:sweat:

# Bottom Navigationはframeの直下にないといけない！
めっちゃ簡単な話でした。。。。

![スクリーンショット 2022-02-17 17.04.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/73c02cde-8707-ca6e-d112-073d9ce509d7.png)
👆の場合は`frame（iPhone 13）>base UI＞bottom nav bar`の順番になってます。
これだと「Fix position when scrolling」は表示されません。。

![スクリーンショット 2022-02-17 17.05.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/c610d162-20fe-7a81-8ec1-61c47e8a42b0.png)
👆の場合は`frame（iPhone 13）＞bottom nav bar`の順番になってます。
**これだと「Fix position when scrolling」は表示されます！！！！**




# 参考

https://forum.figma.com/t/how-can-i-get-fix-position-when-scrolling-option-under-constraints-section/3782

# 追記
知らぬ間に仕様が変わっていて、今は固定表示はプロトタイピングのとこから設定する仕様になったらしいです！
ご注意を！！

https://note.com/sin1lo/n/n5c92c6cf81a4
PowerAutomateでJSONによるワークフロー間データ連携を行う

PowerAutomateを業務の効率化／自動化のために活用する中で、複数のワークフローの間にデータによる依存関係が発生することがあると思います。
フローAの処理で作成したデータを元にしてフローBで別の処理を行うとか、あるいは、フローAの前回処理で作成したデータを元にしてフローAの今回処理を行うとか、そういうケースです。

# Problem

このような場合、こうしたフローを結びつける役割をするデータをどのように管理するかで、複数の選択肢があります。

簡単に思いつくのは（私が最初に思いついたのは）ExcelファイルやSharePointリストによるデータ管理です。
元々手作業で行われていた業務を効率化／自動化するという場合、従来手作業で参照更新されていたファイルやリストを元に考えるのは自然なことでしょう。

しかし、ExcelファイルやSharePointリストにデータの読み書きを行う場合、いくつかの課題が発生します：

* 読み書きが遅い。とくに書き込み（項目追加）がものすごく遅い。
* 書き込み中の「中途半端な状態」で参照されるリスクがある。
* 読み取ることのできる [行数に上限](https://qiita.com/mizukyf/items/1388d47f19562a3b123d) がある。
* 日時データの表現（シリアル vs. ISO8601）の [相互運用に関する難しさ](https://qiita.com/mizukyf/items/df3d9ac2cde34a6cf20f) がある。
* マップ（辞書）構造のデータを管理できない。
* レコードを構成するデータをカラム（列）毎にアクションの入力欄に設定するのが手間。

# Solution

これらの課題を解決する方法の1つは、Excelファイルやリストが存在するのと同じSharePoint上で、ドキュメントライブラリ内にJSONファイルを配置して、ここにデータを入れるというものです。

ExcelファイルやSharePointリストでデータを管理する方式と比べた時のメリット／デメリットは以下の通り：

|  項目 |  Excel/SPリスト  | JSON |
| ---- | ---- | ---- |
|  読み書きの速度  |  X  |  O  |
|  「中途半端な状態」で参照されない  |  X  |  O  |
|  読み取り行数に制限がない |  X  |  O  |
|  日時データ相互運用の考慮が不要 | X  |  O  |
|  マップ構造の管理 | X  |  O  |
|  アクションの設定が簡素 |  X  |  O  |
|  ファイルの可読性が高い |  O  |  X  |
|  Excelアプリで参照更新 |  O  |  X  |

## フロー実装方法

フローの実装は次のようになります：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/1955615a-9a3c-a294-a8e6-b890f0a85856.png)

__①パスに取るファイルメタデータの取得__

後続アクションで利用するIDを取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/08f288c6-db93-c384-b6bd-53cd2cabe907.png)

__②ファイルのチェックアウト__

これから更新するファイルをチェックアウトします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/7c58ea57-d878-516c-50d7-95d87f336516.png)

__③ファイルの更新__

保存したいデータを「ファイルコンテンツ」欄にJSON形式で記述します。
この時点ではファイルはチェックアウトされた状態のため、ユーザーや他のワークフローがファイル内容を参照すると、変更前の状態のデータが得られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/3af337fa-b9a8-8d5c-a573-86a58c894c15.png)

__④ファイルのチェックイン__

更新したファイルをチェックインします。
チェックインの種類には「Major version (publish)」を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/39a1b027-089b-fe1f-f712-454621521905.png)

__JSONファイルコンテンツの取得と利用__

ちなみにこうして更新したJSONファイルの内容は「ファイルコンテンツの取得」を使って読み取ることができます。
[別の記事](https://qiita.com/mizukyf/items/14d77e507d5277023689) でお話した通り、「コンテンツタイプの推測」は「いいえ」にしておくのがオススメです。
取得結果はJSON形式の文字列になるので、 `json(...)` 関数でデシリアライズして利用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/5b4bc4ca-483f-4a57-e7f2-55280db4cf38.png)
vimscriptでterraform fmt checkをかける



## 内容
- `system()`で外部コマンドを呼び出すことができる
- `expand("%:p")は開いているファイルのフルパスを取得する`

```vimscript
function! TFfmt()
  let result = system('terraform fmt -no-color -diff -check '. expand("%:p"))
  echo result
endfunction
```

## 実行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293481/8adbb1dd-76fc-81c6-78a2-67b7071b8550.png)
プロセス間通信の生まれた経緯がわかるとスッキリした。

Dockerを勉強するにあたって、プロセス間通信みたいな話が出てきて、厳密に理解できていなかったので勉強し始めたら、理解に苦しんだ。

ここでは細かい技術なんて説明しないし、正直理解にまでは及んでない。
それよりもなんでプロセス間通信が必要なんというところに着地したい。
多分、僕以外のエンジニアは当たり前じゃんと思うかも、、、

## 出てくる用語
- スレッドとプロセス
- プロセス間通信


## そもそもプロセス間通信とは何か、なんのために存在するのか
>プロセス間通信（プロセスかんつうしん、IPC、英: interprocess communication）はコンピュータの動作において、複数プロセス（の複数スレッド）間でデータをやりとりする仕組み。通信プロセスは、同一コンピュータ内で帰結するローカル、ネットワーク接続された別のコンピュータと相互にリモート、などのほかに多様な観点で分類され、スレッド間の通信帯域幅とレイテンシや扱うデータの種類も多種多様である。メッセージパッシング、同期、共有メモリ、RPCなどのメカニズムやプリミティブがある。
[Wikipedia プロセス間通信](https://ja.wikipedia.org/wiki/プロセス間通信)


僕は思った。意味がわからんし、なんでプロセス間通信っていう言葉が生まれたのか。
そもそもプロセスってなんで、なんで通信で直接通信ができないのか。

## プロセスとは何か、そしてよく出てくるスレッド
プロセスとは、**OSが管理する一つの処理**である。(ちなみにDockerのコンテナは一つのプロセスでしかない。)
それに対して、スレッドとはプロセスが管理するCPUが担当する一つの処理である。
ExcelとWordの関係ではなく、WordとWordの機能の関係である。
つまり図にすると下記であるが、レイヤーが違うことは認識しておきたい。
![スクリーンショット 2022-02-17 16.49.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/c5872895-06d1-ac49-482e-384ed0c4017e.png)

そして、プロセスにおいて重要な点がある。それは
**各プロセスはメモリを共有できない**
ということである。この原則はとても重要である。
OSが各プロセスに対してメモリの利用範囲を決める。そして、そのプロセスはそのメモリから外に出ることはできない。つまり、プロセスは与えられたメモリの中で完結する。これはDockerのコンテナが互いに干渉しないことの説明にもなる。
![スクリーンショット 2022-02-17 16.53.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/16b1dbf9-e2a2-e74b-130f-cb8e50b16686.png)

正直、この前提が抜けていたからプロセス間通信を聞いても意味不明だった。

ここを理解できれば、あとは手段の話である。

各技術に対しては随時勉強していくとして、ソケット通信や名前付きパイプなどの目的を理解すると勉強もしやすいと思っている。
![スクリーンショット 2022-02-17 16.55.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/65ebc76d-b5ff-3c18-a771-dc0024eea68c.png)

以上。
【未経験】1ヶ月半でフロンドエンドエンジニアの就活用ポートフォリオを作った話🎓

#はじめに
はじめまして！フリーランスマーケターをしながら独学でプログラミングを学んでいる「たくろー」と申します。

この記事では、
__未経験から1ヶ月半でフロントエンドエンジニアの就活用ポートフォリオを作成した話__
という結構ヘビーな内容をお届けしたいと思います。

約50日間でどのように学習を進めてきたのか、赤裸々に書いていきますので
ぜひご一読いただけますと幸いです。

##【目次】
[1. なぜエンジニアを目指したのか](#なぜエンジニアを目指したのか)
[2. 50日間で私がやったこと](#50日間で私がやったこと)
[3. そこから得られた3つの経験](#そこから得られた3つの経験)
[4. さいごに](#さいごに)

#なぜエンジニアを目指したのか

そもそもこの話をする前に、自分について簡単に説明したいと思います。

都内在住の大学3年生で、普段はフリーランスのコンテンツマーケターをしています。
メインでやっているのは映像編集で、YouTuberの編集からPR動画・教材ビデオ制作など
かなり多岐にわたって仕事をしてきました。

そのほかにもライターとして記事を執筆したり、Podcastの配信をしたりなど、
ざっくり言えば__なんでも屋のコンテンツマーケター__として活動していた感じです。

そんな私がなぜエンジニアのポートフォリオを作成したのか。
それはとあるクライアントとの出会いがきっかけでした。

その会社はAIを軸に事業を展開している会社で、
DXやAIの将来性・現状の日本におけるIT知識不足を指摘していました。

これらの話を聞くたびに
__どっかのタイミングでプログラミングを学びたいな〜__
と漠然と思っていたのですが、なかなかキッカケが見つからず…。

のらりくらりと生活する中で、自分の将来について考え直すきっかけが起こります。

誰もが通る道、__就活__です。

就活について周りが動き出す中、自分は一体何がしたいのか分からなくなりました。
沢山のことに興味があるが故、この社会人1年目という貴重な時間を何に使おうか
迷っていたのです。

その時に、知り合いのエンジニアに声をかけられました。

__「1度プログラミングを勉強してみない？」__

そんな会話からプログラミングを始めてはや50日。
振り返ると、そこからの時間の流れはとてつもなく早かったです。

#50日間で私がやったこと

それからというもの、毎日10時間以上プログラミングと格闘する日々が続きました…
<br>
<br>
というのは冗談で、 __実は50日間のうち、半分もプログラミングしていません。__

理由は主に2つあって、
・学生なので授業が忙しかった
・家賃などの生活費は全て自分で賄っているので、フリーランスの活動を続けざるを得なかった
というのがありました。

そのため、基本的にプログラミングの勉強は週3日分の時間にとどめつつ、
その中でできる限り効率的に集中して学習を進めていきました。

私のこれまでの学習の流れは、

・HTML & CSS & JavaScriptの勉強（約2週間）
・Vue.jsでの成果物作成（約3週間）
・ポートフォリオサイトの作成 & 成果物のデプロイ（約2週間）

このような感じでした。__今思えばマジで超特急です。__

正直、ポートフォリオのレベルとしては高いものではありません。

しかし、残された時間と作りたいものをしっかりと踏まえた上で、
__ゴールから逆算して成果物を作り上げたことは、とても達成感がありました。__

#そこから得られた3つの経験

この経験から得られたこととして、主に3つのことが挙げられます。

まず1つ目は、当たり前ですが__プログラミングスキル__を身につけたことです。

具体的には、Vue.jsというJavaScriptにおけるモダンなフレームワークを使って、
__フリーランス特化型タスク管理アプリケーション__を作成しました。

こちらの[ポートフォリオサイト](https://takuro-portfolio.net)から確認できますので、ぜひ一度拝見してみてください。

またこれに加えて、__Web知識全般__についても触れることができました。
この経験はエンジニア以外でも活用できそうな内容だったので、個人的にはとても満足しています。

そして、何よりも大きかったのが__プログラミングにおける考え方__を身につけられたことです。

プログラミングにおいて大切なのは、
__目標設定に対して適切なマイルストーンを設置し、
それらに必要な手段を取捨選択して、効率よく質の高い成果物を作ること__
だと思っています。

私はVue.jsなどのモダンなフレームワークを利用したいと考えていましたが、
1番に考えていたのは「作りたいものに対して必要な言語は何か」ということでした。

たまたま成果物の内容的にVue.jsを使うことになりましたが、
それらを選んだのも__目標に対して何が必要か__をしっかりと考えられたことが大きいと思っています。

おかげで、自分の中で迷うことなく、やるべきことに集中して取り組めました。

#さいごに

ここまで読んでいただき、本当にありがとうございました。
私の中でこの50日間は、ただプログラミングの勉強をするだけではなく
__物事に対する取り組み方を養った期間だと思っています。__

この大学生という時間を使って、プログラミングを勉強できて本当によかったです。

ここから自分の納得できるキャリアが歩めるように、
制作したポートフォリオを持ってさまざまな企業へ足を運びたいと思います。

それでは、1社目の選考に行ってきます！
ありがとうございました。
【ＰＨＰ】値があれば先頭にくっつける、無ければ何もしない

#コード
```PHP
<?php

$before = '55';

$before1 = '';

$start = 'bbb:';
 
echo str_replace($before, '攻撃力：'.$before.'/', $before);

echo str_replace($before1, '攻撃力：'.$before1.'/', $before1);

```

#結果
```
攻撃力：55/

※出力なし

```
iOS15/Safari html要素にposition: fixedを設定した状態でlandscapeへ画面回転するとinnerHeightがアドレスバー分小さくなる

##タイトル通り
iOS15/Safariにて
html要素に`position: fixed;`を設定した状態でlandscapeへ画面回転すると、
アドレスバーが非表示であるにもかかわらず、「innerHeightがアドレスバーが表示されている時の値になってしまう」バグがある。
ちなみに本記事はただの備忘録です。

##どうしたか
innerHeightを使用せず100vhを使用したところ、計算上は正しい値が得られたものの、
innerHeightを超過した分は見切れてしまった(winodw外ということ？)ため、
html要素から`position: fixed；`を外すことでしか対応できなかった。
API Gateway+LambdaでJSONを受け取る

#API GatewayでHTTP APIを使ってみた
API Gateway（HTTP API）は低レイテンシー、低コストということで使ってみた。
Lambda（Python）でJSONデータを受け取る場合に REST API と HTTP API で取り出し方が違うのでメモする。

```
#JSONデータ
{
    "operation": "PUT",
    "data": {
        "temp":"28.0",
        "humid":"41.2"
    }
}
```

##HTTP APIの場合
```
$ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"operation": "PUT", "data": {"temp":"28.0","humid":"41.2"}}' https://{API ID}.execute-api.{リージョン}.amazonaws.com/{リソースパス}
```
```
# Lambda関数（Python)
import json

def lambda_handler(event, context):
  operation = json.loads(event.get('body', '{}')).get('operation')
  data = json.loads(event.get('body', '{}')).get('data')
  temp = data['temp']
  humid = data['humid']

  print("operation: " + operation)
  print("temp: " + temp)
  print("humid: " + humid)

  return {
    'statusCode': 200,
    'body': json.dumps('OK.')
  }
```
```
# lambda_handler 引数eventの中身
event: 
{
    "version": "2.0",
    "routeKey": "POST {リソースパス}",
    "rawPath": "{リソースパス}",
    "rawQueryString": "",
    "headers": {
       省略
       :
    },
    "requestContext": {
       省略
       :
    },
    "body": "{\"operation\":\"PUT\",\"data\":{\"temp\":\"28.0\",\"humid\":\"41.2\"}}",
    "isBase64Encoded": false
}
```


##REST APIの場合
```
$ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"operation": "PUT", "data": {"temp":"28.0","humid":"41.2"}}' https://{API ID}.execute-api.{リージョン}.amazonaws.com/{API デプロイのステージ名}
```
```
# Lambda関数(Python)
import json

def lambda_handler(event, context):
  operation = event['operation']
  data = event['data']
  temp = data['temp']
  humid = data['humid']

  print("operation: " + operation)
  print("temp: " + temp)
  print("humid: " + humid)

  return {
    'statusCode': 200,
    'body': json.dumps('OK.')
  }
```

```
# lambda_handler 引数eventの中身
event: {"operation": "PUT", "data": {"temp": "28.0", "humid": "41.2"}}
```
【unittest】例外発生をテストする

Pythonのunittestで例外発生をテストする方法をメモしておく。

# 例外発生のテスト
## 基本的な書き方
例外発生をテストする場合、```assertRaises()```を```with```と一緒に使って以下のように書く。

```python
with self.assertRaises(<発生を期待する例外>):
    <例外が発生するテスト対象メソッド>
```

##サンプル
上記の例外発生をテストする場合の書き方を使って、簡単なサンプルを書いてみた。
テストを実行すると、テスト対象メソッドで```ValueError```が発生するのでテスト成功となる。

```python:calculation.py
class calculation:
    def multiply(self, a, b):
        if type(a) is not int or type(b) is not int:
            raise ValueError

        return a * b
```

```python:test_calculation.py
class TestCalculation(unittest.TestCase):
    def test_multiply(self):
        calculator = calculation()
        # テストコードの記述
        with self.assertRaises(ValueError):
            calculator.multiply("test", 1)

# unittest.main()で実行
if __name__ == "__main__":
    unittest.main()
```
AWS認定アカウント　ピアソンVUEでの決済エラーによる制限解除方法

### エラーメッセージが繰り返し3回以上表示されると自動的にアカウントが制限される
AWS認定クラウドプラクティショナー試験を、ピアソンVUEで受験しようと予約手続きをしていました。決済画面でエラーメッセージを3回以上繰り返してしまうと、自動的にアカウントが制限されてしまいます。再度予約を試みましたが以下のメッセージが表示されます。
```
Your access to the Pearson Vue website has been restricted. Please contact the Pearson Vue Call Center for assistance.
```
日本語訳
```
ピアソンVueのWebサイトへのアクセスが制限されています。サポートが必要な場合は、ピアソンVueコールセンターにお問い合わせください。
```
私の場合、英語部分をそのままコピペし、検索をかけるとピアソンVUEカスタマーサービスのサイトへいけました。[ピアソンVUEカスタマーサービス](https://www.pearsonvue.co.jp/Test-takers/Customer-service.aspx)

問い合わせフォームからアカウント制限解除の依頼をし、以下のメールが届きました。
```
この度は、ピアソンVUEカスタマーサービスにお問い合わせいただきまして、誠にありがとうございます。

お問い合わせの件につきまして、ご不便をお掛けしております。

ご依頼頂いておりました利用制限解除につきまして、弊社担当部署での解除手続きが完了致しましたことをご案内申し上げます。

現在、弊社ウェブサイトにログイン頂くことは可能でございますが、ご予約の決済を行う際、入力された情報に誤りがあり、エラーメッセージが繰り返し3回以上表示された場合は、自動的にアカウントが制限されますのでご注意ください。

その他、ご不明な点等ございましたらお気軽にご連絡ください。

どうぞよろしくお願い致します。
```
念の為、カスタマーサービスへ電話にて問い合わせをし、決済画面の入力方法について伺ったところ｢半角英数字、-(ハイフン)｣で対応してるとの事でした。対応してくださった方も親切でした。
WLS上からGoogleDriveが見えなくなった時に再度見えるようにする方法

WSL2からGoogleDrive上のファイルの読み書きをしているのだが、GoogleDriveのマウントが時々切断されてしまう。WSL自体を再起動して直していたのだが、通常のマウントコマンドでなおることがわかった。（以下はGoogleドライブがGドライブの場合）

```shell
sudo mount -t drvfs g: /mnt/g
```

参考：[Windows 10の「WSL」でネットワークドライブなどをマウントする：Tech TIPS - ＠IT](https://atmarkit.itmedia.co.jp/ait/articles/1806/08/news042.html)
【Power Automate】メール/Teamsメッセージ/Teamsアダプティブカード/Virtual Agentチャットで有効な改行一覧

# Power Automateで改行がうまくいかない

各種チャネルの詳細項目に文字列を展開しようとしたら、**変数内**の改行がうまくいかないという、ささやかでしみったれながら地味に腹の立つ問題に悩まれたのでまとめました。

- 「〇」改行できる
- 「-」改行できない（多分）確認実績なし
- 「×」改行できない。確認実績あり

| 変数内の改行 | 変更なし | Brタグ | スペーススペース改行 | 改行改行 |
|:-:|:-:|:-:|:-:|:-:|
|Plannerタスク詳細   |〇   |-   |-   |-   |
|メール   | ×  |〇 ※1  |×   |×   |
|Teamsメッセージ   |×   |〇   |×   |×   |
|Teamsアダプティブカード（PC）   |×   |×   |〇   |〇   |
|Teamsアダプティブカード（スマホ）   |×   |〇   |×   |×   |
|Virtual Agentチャット   |×   |×   |〇   |〇   |

# 対処方法
 改行のある変数を詳細で展開する場合は、全文用の変数を作り、全体に改行に対しreplaceをかけるのが一番スマートかなと思います。取りこぼしがないという点で。

# 注記
※１　メールのメッセージに変数を利用する場合、事前に変換処理するとbrタグが消滅してしまう症状がでた。メッセージアクションの中で変換処理をするとbrタグが有効となる。
React で AWS Cognito を使う

こちらの記事と同じことを、行ってみました。
[Amplify を使わず React で AWS Cognito 認証を使う](https://mseeeen.msen.jp/react-auth-with-ready-made-cognito/)

確認した環境

```text
$ uname -a
Linux iwata 5.13.0-27-generic #29-Ubuntu SMP Wed Jan 12 17:36:47 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

$ node --version
v17.5.0

$ npm --version
8.4.1
```

ソースコードをクローン

```bash
git clone https://github.com/kenzauros/react-auth-with-cognito
```

.env.local の作成

```bash
cd react-auth-with-cognito
cp .env.local.example .env.local
```

編集します。

```text:.env.local
REACT_APP_AUTH_REGION=ap-northeast-1
REACT_APP_AUTH_USER_POOL_ID=ap-northeast-1_123abcdef
REACT_APP_AUTH_USER_POOL_WEB_CLIENT_ID=123456789abc***
REACT_APP_AUTH_COOKIE_STORAGE_DOMAIN=localhost
```
これらの値は、
AWS のコンソールにサインして
Cognito で確認します。

REACT_APP_AUTH_USER_POOL_ID
>![cognito_aa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/69ca885b-0d15-0863-2888-4e5640b057fe.png)

REACT_APP_AUTH_USER_POOL_WEB_CLIENT_ID
>アプリケーションの統合
>![cognito_bb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/01e51837-bc4b-7f94-2c67-5ed9119be937.png)


ライブラリーのインストール

```bash
npm install
```

サーバーの起動

```bash
npm start
```

クライアントで
http://localhost:3000/
にアクセス

![react_aa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/132974cd-577d-bf4c-edf6-dc63d0f2eb1a.png)

ログインをクリックすると
![react_bb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/d3f96ac6-2fd1-af47-f18a-8524044a4296.png)


ユーザー　に登録されているメールアドレスでログイン
![cognito_cc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/2717bfba-47a0-9531-e1a7-52e8de5f748f.png)

#ビルド#

```bash
npm run build
```

サーバーの実行

```bash
serve -s build
```
DjangoにおけるN+1問題について

# はじめに
Djangoを使ってWebアプリを開発しているときに、レスポンスタイムの悪化を引き起こすというN+1問題について気になってしまったので、自分の備忘録として残しておきます。

# 環境

```terminal:ターミナル
% python -V
Python 3.9.6

% python -m django --version
4.0

% sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H1715
```

検証に使う便利なライブラリをインストールしておきます。

```terminal
% pip install django-extensions ipython
```

settings.pyに記述して有効化します。

```python:settings.py
INSTALLED_APPS = [
    ...
    ...
    ...
    'django_extensions',
]
```

これでshell_plusコマンドが使えるようになり、アプリケーションで定義したモデルをimportした状態で対話型シェルを起動することができます。

# 検証に使うモデル
求人情報に対する応募モデルを定義しています。

```python:models.py
from django.db import models

class Application(models.Model):

    class Meta:
        db_table = 'applications'

    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                                verbose_name='求職者',
                                on_delete=models.CASCADE)
    work = models.ForeignKey(Work,
                                verbose_name='仕事',
                                on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"apply to {self.work} by {self.user}"
```

# 検証
shell_plusでは、`--print-sql`オプションを付けることで、実行されるSQLを出力することができます。
(出力されるSQLの各カラム名をそのまま書くと冗長なため、`*`で置き換えています。)

```shell:ターミナル
% python manage.py shell_plus --print-sql

In [1]: apps = Application.objects.all()

In [2]: for app in apps[:100]:
   ...:     print(app.user.name)
SELECT * FROM "applications" LIMIT 100
SELECT * FROM "accounts_user" WHERE "accounts_user"."id" = 1 LIMIT 21
username1
SELECT * FROM "accounts_user" WHERE "accounts_user"."id" = 2 LIMIT 21
username2
SELECT * FROM "accounts_user" WHERE "accounts_user"."id" = 3 LIMIT 21
username3
SELECT * FROM "accounts_user" WHERE "accounts_user"."id" = 4 LIMIT 21
username4
SELECT * FROM "accounts_user" WHERE "accounts_user"."id" = 5 LIMIT 21
username5
...
...
...
SELECT * FROM "accounts_user" WHERE "accounts_user"."id" = 100 LIMIT 21
username100
```

この場合、applicationsテーブルから100件のデータを1回のSQL文で取り出しましたが、
そのあとでaccounts_usersテーブルからnameフィールドを取得するため、100回もSQL文が発行されています。
つまり、100件のデータを取得するのに 1+100 = 101回のSQL文を発行しています(このことから、1+N問題とも呼ばれるみたいです)。
これはクエリの呼び出しオーバーヘッドが大きく、深刻なパフォーマンスの低下を引き起こすため、Djangoでは`select_related`を使ってテーブル結合を行います。

```shell:ターミナル
In [3]: apps = Application.objects.select_related("user").all()[:100]

In [4]: for app in apps:
   ...:     print(app.user.name)
SELECT * FROM "applications"
INNER JOIN "accounts_user"
    ON ("applications"."user_id" = "accounts_user"."id")
LIMIT 100

username1
username2
username3
username4
username5
...
...
...
username100
```

`select_related`を使ったことで、発行されたのはたった1つのSQL文です! 発行されたSQLから分かるように、applicationsテーブルとaccounts_usersテーブルを事前にJOINしておくことで、100件のデータを1度に取得できています。

# まとめ
N件のデータを取り出すためにN+1件のSQLを発行してしまう、というところからN+1問題と呼ばれているのってなんか面白いですね。
対多の外部キーでクエリの最適化を行うには`prefetch_related`を使うそうです。以下の記事が参考になりました。

https://just-python.com/document/django/orm-query/select_related-prefetch_related

小規模なアプリケーションだとあまり気にならないかもしれませんが、発行されるSQLを意識せずにORMを使っていると、N+1問題に気付くのが難しそうです...
javascript59_関数機能スコープ

関数の機能スコープ：
関数を呼び出す時に関数スコープが生成され、関数の実行が終わったら、関数の機能スコープはなくなる。
関数を呼び出すたびに、新しい機能スコープが生成されます。お互い独立しています。
関数の機能スコープのなかに全領域の変数にアクセスすることができます。
関数の中に宣言した変数は関数の中でしか使用できません。
関数機能スコープのなかに変数を操作する時に、まず関数内で検索する。関数内でない場合は、関数外で調べる。
関数外で調べてもない場合は、ReferenceErrorが出ます。

関数の中で全領域変数をアクセスしたい場合は、windowを使用します。

関数機能スコープのなかにも、事前に変数宣言の特性があります。
varで宣言した変数は、関数が実行される前に宣言されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/ff958690-fee4-b721-cffa-cbb4e2004433.png)

注意：関数の中にconst/varで宣言していない変数はグローバル変数になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/8fd3da03-a6d8-6b84-698f-75d784572613.png)
注意：形式引数は関数の中に変数を宣言したと同じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/ff8c818b-fe0d-36d6-21f4-fd28048b7701.png)

実際コードを書くときに、関数内に変数宣言時にconstを書き忘れることが多いです。
それを防ぐために、JSの頭に'use strict' を書くことをお勧めします。
'use strict'書くと、エラーMSGを出してくれます。
wslでファイルが見つかりませんのエラーが出た時の対処法

## 環境
* Windows11
* WSL2
* Ubuntu-20.04

## 事象
WSLでUbuntu-20.04に色々インストールしまくって遊んでたところ、適当にやりすぎて環境が汚れたんで、一旦Windowsの設定側からUbuntuをアンインストール。
その後再インストールして起動しようとしたところ下記のエラー

```powershell
C:\Windows\system32> bash
指定されたファイルが見つかりません。
```

恐らくWSL側のconfigとかを考慮せずにUbuntuをアンインストールしたのが原因だと思われる。
初歩的過ぎて調べても特に同じ状況になっている人がいないのでとりあえず暫定の対処法で解決したので備忘録ついでに書いておきます。

## 対処
とりあえずWSL側で参照ができていないようなので、WSL側の登録を解除してあげれば大丈夫らしい。

とりあえず自分の規定になってるディストリビューションを確認。

```powershell
C:\Windows\system32> wsl -l
Linux 用 Windows サブシステム ディストリビューション:
Ubuntu-20.04 (既定)
```

僕の環境だとUbuntu-20.04なので、こいつの登録を一回解除して、関連ファイルとかを削除してあげる。

```
C:\Windows\system32> wslconfig /u Ubuntu-20.04
登録解除。
この操作を正しく終了しました。
```

今回はwslconfig使ったけど、``` wsl --unregister Ubuntu-20.04```とかでもできると思う。
恐らくアンインストールした前後でやらんといけないことありそうだけど、一先ずこれで最初のユーザー登録まで戻れたので対症療法としてはいい感じ。

## 結論
色々弄る前にしっかりと公式のドキュメントは読もう。
[デブサミ2022]「10年後もエンジニアが成長し続けるためにできることを、20年続く組織の中から考える」で学んだこと

# はじめに

以下のデブサミのセッションを視聴して学んだことを書きます。

セッション名：10年後もエンジニアが成長し続けるためにできることを、20年続く組織の中から考える
スピーカー：小田中 育生[ナビタイムジャパン]
概要ページ：https://event.shoeisha.jp/devsumi/20220217/session/3674/

# セッション内容

エンジニアは、以下の状態だと、いきいきしていると言える
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/704f979b-0fb6-ab6a-a6ea-bd68015427c1.png)

「ビジョン」を実現するためには「プロダクト」が必要で、「プロダクト」を作るためには「技術」と「育つ環境」が必要。

職種別のアンケートでは、エンジニアは、働きがいが低い数値になっている。

エンジニアがいきいきするために必要な要素は、そろっているが、いきいきできないエンジニアも多い。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/4b1e8fd5-f6a7-65e2-1ed0-32e959291d9b.png)

20年前は、「ケータイでナビができる」こと自体が価値だった。
魅力品質は、時とともに、当たり前品質になっていく。

ではどうすればいいか？
フィードバックに基づき品質を高めていくための持続的イノベーションが必要。
そして、組織が成熟するためには、環境の変化に合わせた勝ちパターンのアンラーニングが必要。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/d55fd6ac-777c-8593-b572-e8aaea4f60d1.png)

前提が変わるような変化に適応するには、アンラーニングが必要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/9f62a55f-32e8-3ccd-3df5-cf3a9f7dba1a.png)

アンラーニングすることは、エンジニアがいきいきすることに繋がる

変化を受け入れる仕組みを作るためには、どうすればいいか？

現場からボトムアップで小さく少しずつ変えていく
良い取り組みをすると、それは他の組織に伝搬していく
ただし、ボトムアップは気付きベースなので、組織はまだらに変わっていく

組織をいっきに変えるためにはトップダウンが必要
しかし、現状維持バイアスがかかり、変化への理解・納得感なしにトップダウンで意思決定がなされると人は現状維持の重力に縛られる

変化への納得感をうけいれやすくするための強力な手段が「OKR」
会社の目標を、個人の目標に落とし込む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/656530fe-7ced-28f3-0e27-3c776fb70d45.png)

OKRを使っても、対話が足りないと、うまくいかない
対話をする1つの手段として、インセプションデッキを作る
その時の対話のやり方は、「自分の考え方ではそうすべき」という話し方でなく「あなたにとってその方が良い」というアプローチの仕方をした方がいい


# 所感

「自分がいきいきするための活動を続ける」「相手にとってメリットのある言い方・アプローチをする」という考え方はとても共感しました。
c#でsqliteのハンドルが残ってファイルを削除できない

# 現象
`database2.sqlite`を削除したいが「ファイルは開かれているため、操作を完了できません」が出て出来ない。

# 対策
```c#
// 削除できる
using (var connection = new SqliteConnection($"DataSource=database.sqlite;Pooling=false")) {
    connection.Open();
}
// 削除できない
using (var connection = new SqliteConnection($"DataSource=database2.sqlite")) {
    connection.Open();
}
```
# 参考
https://docs.microsoft.com/ja-jp/dotnet/standard/data/sqlite/connection-strings
【Rails 】Rakeタスクとは

##Rakeタスクとは
``Rake``とは、``Ruby``で書かれたコードを``タスク``として作成しておき必要に応じて呼び出し実行する事が出来る機能。

この``Rake``が実行する処理内容を``Rakeタスク``と呼び、定義する場所を``Rakefile``と呼ぶ。

``rakeタスク``を利用する場面として、

・何かしらのデータの連携
・データベースのバックアップ
・定期的にデータを更新、削除する

などが挙げられる。
また``whennever``と組み合わせることにより、任意のタイミングで処理を実行することができる。
▷[【Rails】wheneverでcronを設定](https://qiita.com/mmaumtjgj/items/19e866f31541abb6c614)

##Rakeタスクの作り方
``Hello``と簡単な挨拶を表示するタスクを作成。
まず最初にタスクファイルを生成する。

```
$ rails g task ファイル名
↓
$ rails g task greet
```

上記のコマンドで、``lib/tasks``配下に``greet.rake``というファイルが生成されていて、ファイルの中身は以下のようになっている。

```lib/tasks/greet.rake
namespace :greet do
end
```

この中に処理を記入していくことで、定義が完了する。

```rb
#基本形
namespace :greet do
  desc "タスクの説明" #desc → description（説明）
  task task_name: :environment do #task_nameは自由につけられる
    # 実行したい処理を記述する場所
  end
end

#lib/tasks/greet.rake
namespace :greet do
  desc "Helloを表示するタスク"
  task say_hello: :environment do
    puts "Hello"
  end
end
```

記入し終えたら、タスクが表示されるか確認をする。

```
$ rake -T   #←現在のアプリに定義されているタスク一覧が表示されるコマンド

rake greet:say_hello                    # Helloを表示するタスク
```

今度はタスクを実行して確認。``Hello``が返ってきたら成功。

```
$ rake greet:say_hello
  Hello
```

##参考記事
[Rakeタスクの実装](https://qiita.com/yoshito410kam/items/26c3c6e519d4990ed739)
[【Ruby on Rails】Rakeタスクを使えば様々な処理が自動化出来る？（初心者向け）](https://opiyotan.hatenablog.com/entry/rails-rake-task)
Ruby - Hash を使った文字列のフォーマット

Ruby のコードをレビューしてるときに、文字列とHashを演算子`％`で処理しているコードに出会い、意味が理解できなくて苦労した。
具体的にはこんなコード。

```
$my_preference = {
    "seafood".to_sym => "salmon"
}

my_cheese = "I love %{cheese} cheese."
my_cheese %= $my_preference
```

おそらく、この `X %= B`は `X = X % B` の代入演算子なんだろうけど、それでは ％ は何をするのかと思って調べた。
この場合、`X`は文字列で`B`はHashなので、当然ながら整数除算の「あまり」の計算じゃないし…、Ruby って不思議すぎと思ったら、[Help and documentation for the Ruby programming language.](https://ruby-doc.org/)の[Class: String - str % arg → new_str](https://ruby-doc.org/core-2.5.1/String.html#method-i-25)に説明がありました。
`%{`と`}`で囲まれた文字列をキーとしてHashから取り出した値で、`%{`から`}`を置き換えてくれるようです。
以下にサンプルコードと出力内容を付けます。

```
$my_preference = {
    "meet".to_sym => "pork",
    "seafood".to_sym => "salmon",
    "vegitable".to_sym => "cucumber",
    "fruite".to_sym => "strawberry",
    "cheese".to_sym => "cheddar"
}

seafood_tmpl = "My favolit seafood is %{seafood}."
puts seafood_tmpl #=> My favolit seafood is %{seafood}.

my_seafood = seafood_tmpl % $my_preference
puts my_seafood #=> My favolit seafood is salmon.

my_cheese = "I love %{cheese} cheese."
puts my_cheese  #=> I love %{cheese} cheese.

my_cheese %= $my_preference
puts my_cheese  #=> I love cheddar cheese.
```

便利です。