OmniAICreator/Qiita-1.07M huggingface dataset:
Ruby on Railsの理念を具体例を用いて解説

# DRY ~Don't Repeat Yourself~ 

>今回は前回の記事で紹介した[Ruby on Railsの理念](https://qiita.com/iamu_TECH_CAMP/items/0d0c9686a651b009f7a1#ruby-on-rails%E3%81%AE%E7%90%86%E5%BF%B5)に関する内容を深ぼっていきます。

**DRY**とは実際にどの様な場面で用いられているのか...

>[Don't Repeat Yourselfの略]
「同じ情報を繰り返し定義しない」

ここでいう`情報`とは、
`メソッド`であったり、配列などの`データの集合体`などを指します。
同じコードを繰り返し記述しないという捉え方で問題ないようだ。


## 具体例

上記の内容を見た時にすぐに浮かんだのはcontrollerの中のこの記述

```rb:items_controller.rb
  def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to root_path
    else
      render :new
    end
  end

  def show
  end

  def edit
  end

# ~省略~

  private # ここだよー
  def item_params
    params.require(:item).permit(:image, :product_name, :product_description, :product_price, :product_category_id,
                                 :product_condition_id, :delivery_charge_id, :prefecture_id, :days_to_ship_id).merge(user_id: current_user.id)
  end

  def set_item
    @item = Item.find(params[:id])
  end
```

着目したいのは`privateメソッド`

このprivateメソッドとても優秀

## privateメソッド

privateメソッドを使用する利点は以下の2点

---

- **Classの外部から呼ばれたら困るメソッドを隔離**
    - メソッドの中には、Classの外部から呼び出されてしまうとエラーを起こすメソッドも存在する。
    - プライベートメソッドはClass外から呼び出すことが不可能であるため、誤って呼び出してしまう等によるエラーを事前に防ぐことが可能となる。

>privateメソッドと呼ばれる由縁なのかな？なんだか独りで納得。

- **可読性**
    - Classの外部から呼び出されるメソッドを探す際に、private以下の部分は目を通さなくて良くなる。
    - 繰り返し使用するメソッドもprivate以下に集約でるので、コードがシンプルになる。

>`item_params`と`set_item`として切り出したのがまさにソレ!

---

```rb:恩恵を受けた部分
  def show
  end

  def edit
  end
```
上記の部分なんておかげで何も書いていない。

>今後大きなアプリケーションを作成する際に、このありがたみがわかるでしょう。

こんな事を学習開始当初
目にしていましたが、本当に助かってます。
<br>
~~当時は何もピンときてなかったけどね~~
１日目：Labelとボタンの表記を操るアプリ

#1日目のアプリ
ボタンを押すとテキストが変わる。というアプリ。

##画面キャプチャ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/959530/fde85dc8-eff7-60b9-9d65-f62f6d884d95.gif" width=50%>

##以下の流れで作りました。
1. storyboadにLabelとbutton2つ（文字を変えるボタンとリセットボタン）を配置
2. ボタンを端末が変わってもレイアウトが崩れないように設定
3. 上記要素をViewController.swiftへoptionドラッグして紐付ける
4. 紐付けができたら、ViewController.swiftでコードを書く

##できたこと
+ ButtonとLabelの配置ができるようになった
+ デバイスサイズが変わってもレイアウトが崩れないように設定できた
+ Labelの文字をViewControllerから変更できるようになった
+ Buttonの文字をViewControllerから変更できるようになった
+ ButtonのタップをトリガーにしてLabelの文字を変更できるようになった

##書いたコードを共有します！

```
import UIKit

class ViewController: UIViewController {

//  要素を紐付ける。見た目の要素の設定はここで行う。IBOutlet
    @IBOutlet weak var messageText: UILabel!
    @IBOutlet weak var changeButto: UIButton!
    
//   デフォルトで表示する言葉
    let initialWord: String = "今日から私は？"
    
//    はじめに画面に読み込まれるものたち
    override func viewDidLoad() {
        super.viewDidLoad()
        
//        ラベルの文字を変更させるのは .text
        messageText.text = initialWord
        
//        ボタンの文字を変更させるのは .setTitle以下のパターン。要素は紐付けておく。
        changeButto.setTitle("何者になる？", for: .normal)
        
    }
    
//    ↓ボタンをタップしたら、、、の設定はIBActionを使う↓
//    ボタンをタップしたらラベルの文字を入れ替える
    @IBAction func tapBluebutton(_ sender: Any) {
        messageText.text = "アプリ開発者！"
    }
    
//    リセットボタンをタップしたらラベルの文字を元の内容に戻す
    @IBAction func resetButton(_ sender: Any) {
        messageText.text = initialWord
    }
    
}

```

##感想
初日。楽しいね。
SalesforceエンジニアのおすすめVSCode拡張機能

#Salesforce Extension Pack
まあこいつは省いても良いかもですが一応。
もはやオススメというか必須。
https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/bdf73db3-f715-7aef-d184-649437e6a944.png)

#Salesforce Package.xml Generator Extension for VS Code
こいつを使えばPackage.xmlを簡単に更新できます。
https://marketplace.visualstudio.com/items?itemName=VignaeshRamA.sfdx-package-xml-generator
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/30588601-5975-053a-17eb-7cfbad6b4898.png)

#Bracket Pair Colorizer
スコープごとに色を変えてくれるので見やすくなります。
https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/518d7cf7-9460-be94-41b0-3ef36b9655cb.png)

#Git Graph
Gitの操作を簡単にしてくれるツール。
https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/87c338a5-bb8e-ddaa-7b5c-be56bf05d8aa.png)

#Apex PMD
Apexクラスを静的解析してくれるツール。
https://marketplace.visualstudio.com/items?itemName=chuckjonas.apex-pmd
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/6ec9c700-3bc7-1fd6-d4ad-a4b087b50898.png)

#競合の検出
こちらは拡張機能ではありませんが便利なので紹介。
Detect Conflicts At Syncを有効にすれば、組織のプログラムとの競合を検知してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/da14294d-2a7f-5eff-1d54-ce56e7de5f37.png)
AWS SESでSendTemplatedEmailのAPIを叩いてメール送信が成功するのに届かない原因

# 経緯
AWS SESでテンプレートを元にメール送信するために、APIの`SendTemplatedEmail`を叩く。

レスポンスで`メタデータが返却されて成功している...!と思ったらメールが届いていない...!`
この現象の解決に時間がかかったため、記載。

# 原因
`テンプレートのレンダリングのエラー`が原因で、この場合はメール送信エラーは返さない。既にSESはメッセージを受け入れているからだそう。
テンプレートが適切か見直そう。



[ドキュメント](https://docs.aws.amazon.com/ja_jp/ses/latest/APIReference/API_SendTemplatedEmail.html)から、以下抜粋

>If your call to the SendTemplatedEmail operation includes all of the required parameters, Amazon SES accepts it and returns a Message ID. However, if Amazon SES can't render the email because the template contains errors, it doesn't send the email. Additionally, because it already accepted the message, Amazon SES doesn't return a message stating that it was unable to send the email.






送信エラーが出ないのは厄介すぎる。。。
やはり公式も、以下のようにレンダリング失敗イベントが発生したときに通知の設定をすることを強く勧めている
>For these reasons, we highly recommend that you set up Amazon SES to send you notifications when Rendering Failure events occur. For more information, see Sending Personalized Email Using the Amazon SES API in the Amazon Simple Email Service Developer Guide.
AtCoder 生活105日目

[前回](https://qiita.com/flandrescarlet237/items/8b3cd1da2f4d5dbcb69d)

#105日目
今日は、灰色2問と茶色1問を解きました。

|問題|難易度|自力で解けた|かかった時間|
|:-:|:-:|:-:|---|---|
|ABC097|B|◯|34分37秒|
|ABC049|B|◯|2分26秒|
|ARC109|B|✕|182分|

##[ABC097_B](https://atcoder.jp/contests/abc097/tasks/abc097_b)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

const int INF = 2e9;

int main(){
    int x;
    cin >> x;
    int ans = 1;
    for(int i = 2;i*i <= x;i++){
        int r = i*i;
        while(r*i <= x){
            r *= i;
        }
        ans = max(ans ,r);
    }
    cout << ans << endl;
}

```

解くのにかかった時間：34分37秒
感想：i=1の場合にwhileでTLEになることに全然気づけなかった。

##[ABC049_B](https://atcoder.jp/contests/abc049/tasks/abc049_b)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

const int INF = 2e9;

int main(){
    int h,w;
    cin >> h >> w;
    rep(i,h){
        string s;
        cin >> s;
        cout << s << endl;
        cout << s << endl;
    }
}

```

解くのにかかった時間：2分26秒

##[ARC109_B](https://atcoder.jp/contests/arc109/tasks/arc109_b)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

const int INF = 2e9;

int main(){
    ll n;
    cin >> n;
    ll m = sqrt(n+1) + 1;
    while((m + 1)*m/2 <= n+1)m++;
    cout << n - m + 2 << endl;
}

```

解くのにかかった時間：182分
感想：ある程度考え方は正しかったのにコードをうまくかけなかった。

###最後に
茶色の問題をせめて30分で解けるようにしておきたい。
python,javascriptでよく使う関数

#文字列検索
python

```
'a' in array
```

javascript

```
array.indexOf('a')
```

#置換
python

```
str.replace('before','after')
```

javascript

```
str.replace('/a/g,'-')
```

#文字列に変換
python

```
str(x)
```

javascript

```
String(x)
```
SalesforceエンジニアのおすすめChrome拡張機能

#ORGanizer for Salesforce

ログイン系の拡張機能を色々試しましたが、こちらが一番便利。
ログイン時に自動でユーザ情報を登録してくれます。
他にも機能がありますが、私はログイン機能のみを使ってます。
https://chrome.google.com/webstore/detail/organizer-for-salesforce/lojdmgdchjcfnmkmodggbaafecagllnh?hl=ja
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/5521eb6c-249e-8f03-975c-6fa7fcaddf9a.png)

#Salesforce advanced Code searcher
こいつはコードカバレッジを設定画面で一覧表示してくれる優れもの。
https://chrome.google.com/webstore/detail/salesforce-advanced-code/lnkgcmpjkkkeffambkllliefdpjdklmi

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/b9b6a6ae-744d-d208-d99a-374fc9bc175e.png)

#Salesforce Colored Favicons
ログインしている環境ごとにChromeタブのファビコンカラーを変更してくれるツール。
Salesforceエンジニアは複数の環境に同時に入って作業をする事も多いので、こいつがあれば誤操作が減ります。
https://chrome.google.com/webstore/detail/salesforce-colored-favico/peohlnebahcddpmfaplmilpkgbkkcdho
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/501461e7-7569-81fd-924c-056ebcbc8cb6.png)

#Salesforce DevTools
こいつは割と有名ですよね。
個人的には手軽にSOQLを発行してレコード一覧を見たい時に使ってます。
あとはER図を自動生成してくれるのも好き。
https://chrome.google.com/webstore/detail/salesforce-devtools/ehgmhinnhggigkogkbhnbodhbfjgncjf
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/1b32db29-7448-c9f4-9832-364a5ef50bcc.png)

#Salesforce inspector
Salesforceでは値を裏持ちしてなんて事はよくあります。
そいつらを引っ張るのは地味に面倒ですよね。
このツールがあれば簡単に引き出せます。
https://chrome.google.com/webstore/detail/salesforce-inspector/aodjmnfhjibkcdimpodiifdjnnncaafh
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239535/9767f803-79c5-254b-3473-224523a9a7ef.png)
NAT・NAPTとは？違いは？

## 勉強前イメージ

グローバルIPアドレスとローカルIPアドレスの変換
NAPTは1対複数？

## 調査

### NAT とは？

Network Address Translation の略で
`送信元のプライベートIP`をグローバルIPに変換する技術です。
プライベートIPとグローバルIPは1対1でしか変換できません

### NAPT とは？

Network Address Port Translation の略で
NATのプライベートIPとグローバルIPの変換に加えて、ポート番号も変換します。
プライベートIPごとに異なるポートに変換することで、
グローバルIPとプライベートIPを1対多で変換することができます。

### NAT と NAPT の違い

- 1対1か多か
	- NAT : 1対1
	- NAPT : 1対多
- 変換するもの
	- NAT : IPアドレス
	- NAPT : IPアドレスとポート

## 勉強後イメージ

1対1 と 1対多 は知ってたけど、理由が知らなかった。
NAPTすごい

## 参考

- [NAT・NAPTとIPアドレスって？](https://livra.geolocation.co.jp/iplearning/209/)
- [NAT （ Network Address Translation ）](https://www.infraexpert.com/study/ip10.html)
sass変数をcss変数（カスタムプロパティ）に渡す方法

##結論
```scss
--cssCustomeProperty: #{$sassVariable}
```
このsassの変数を`#{ }`で囲むのはcalcとかでも一緒ですね。
参考：[SCSSの変数と、CSS3のcalc()を併用するには](https://qiita.com/mtmtkzm/items/2e3aef1b504ebcde5311)
##用例
```scss
/* sass変数の定義 */
$color_primary: #f00000;

/* 疑似クラス:rootでcss変数の定義 */
:root {
  --color-primary: #{$color_primary};
}

/* 定義済みcss変数の利用 */
body {
  color: var(--color-primary);
}
```
##補足：sass関数にcss変数を入れるのは出来ない
```scss
body {
  color : darken(var(--color-primary), 10%);
}

/* 上記はエラーになる */
```
無理難題を言っているのかもしれないが、この辺りの融通が利けば助かるのに、と思うときが稀にある。
Rails：form_withのオプションや特徴について整理

※2022年から技術系の記事は[個人ブログ](https://yamaday0u.net/category/programmer/)に投稿しております。ぜひこちらもご覧ください→[yamaday0u Blog](https://yamaday0u.net/category/programmer/)

Railsでフォームを作成するときに使用する`form_with`メソッドについて整理しました。

## form_withメソッドとは
Railsで用意されているヘルパーメソッドの1つで、投稿ページなどでのフォームの実装に役立ちます。
ヘルパーメソッドとは主にビューでHTMLタグを出現させたりテキストを加工するために使用するメソッドの総称です。

### 基本構文
`form_with`の基本構文は以下の通りです。モデルかスコープかURLは必ず記述します。

```rb
form_with(モデル or スコープ or URL [, オプション])
```

### form_withに記述するオプション
他にもいくつかオプションはありますが、ここではよく使うもの紹介します。

|オプション|説明|デフォルト値|
|:--|:--|:--|
|:model|モデルを指定||
|:scope|スコープを指定||
|:url|URLを指定||
|:local|リモート送信の無効|false|
|:method|HTTPメソッドを指定|POST|
|:id|id属性を指定||
|:class|class属性を指定||

### localオプションについて

`:local`はリモート送信（Ajax通信のような非同期通信）によるフォームの送信の有効・無効を設定するオプションです。

JavaScriptでAjax通信によるフォームの送信を実装する場合などは、何も記述しなくてOKです。

逆に、ブラウザの読み込みをするような同期通信を実装する場合は、`local: true`と記述して、リモート送信をオフにします。

※参考記事:[Qiita【Rails】form_withに記述するlocal: trueについて]
(https://qiita.com/sho_tsuchida1105/items/b08df35fe3c45f3756da)

## form_withメソッドの便利な機能
form_withには、モデル（:model）で記述した場合の便利な機能があります。

引数として渡されたモデルクラスのインスタンスに中身が入っているか否かで、フォーム送信後のアクションをcreateかupdateに自動的に振り分けてくれることです。

インスタンスに中身が入っていない場合はcreateアクションへ、中身が入っている場合はupdateアクションへ処理を振り分けてくれます。

以下のような`fruits_controller.rb`があるとします。

```app/controllers/fruits_controller.rb
class FruitsController < ApplicationController
  before_action :set_fruit, only: [:create, :update]

  def index
    @fruits = Fruit.all
  end

  def new
    @fruit = Fruit.new# 空のインスタンスを生成
  end

  def create
    redirect_to root_path
  end

  def edit
    @fruit = Fruit.find(params[:id])# URLに含めたidから検索したレコードでインスタンスを生成
  end

  def update
    redirect_to root_path
  end

  private
  def fruit_params
    params.require(:fruit).permit(:name, :color)
  end

  def set_fruit
    @fruit = Fruit.create(fruit_params)
  end
end
```

以下の`new.html.erb`と`edit.html.erb`は、ほとんど同じ記述ですが、1行目の`@fruit`の中身の有無が異なります。

```erb:app/views/fruits/new.html.erb
<%= form_with model: @fruit, local: true do |form| %><%# @fruitの中身は空 %>
  <div class="">果物の名前</div>
  <%= form.text_field :name, class: "item-text-field" %>

  <div class="">果物の色</div>
  <%= form.text_field :color, class: "item-text-field" %>
  <%= form.submit "登録する", class: "submit-btn" %>
<% end %>
```

```erb:app/views/fruits/edit.html.erb
<%= form_with model: @fruit, local: true do |form| %><%# @fruitにはeditアクションで代入したレコードの値が入っている %>
  <div class="">果物の名前</div>
  <%= form.text_field :name, class: "item-text-field" %>

  <div class="">果物の色</div>
  <%= form.text_field :color, class: "item-text-field" %>
  <%= form.submit "更新する", class: "submit-btn" %>
<% end %>
```

`new.html.erb`では、フォームを送信すると`@fruit`の中身が空であることを読み取ってcreateアクションに振り分けてくれます。
一方、`edit.html.erb`ではフォームを送信すると`@fruit`に中身があることを読み取ってupdateアクションに振り分けてくれます。

URLを自分で指定する場合と比べて、**モデルのインスタンスを引数として指定することで、記述の手間が減り、フォームの作りをそのまま転用しやすくなります。**

## form_tagとform_forとの違いについて
従来のform_tag、form_forとの違いについては以下の方が詳しく解説してくれているのでご参照ください。

[Railsドキュメント フォーム(form)](https://railsdoc.com/form)
[Qiita【Rails 5】(新) form_with と (旧) form_tag, form_for の違い](https://qiita.com/hmmrjn/items/24f3b8eade206ace17e2)
そう言えば分からないRailsのsave・createメソッドの違い

## 開発環境
macOS Catalina
Rails 6v

自己開発をする上で、そう言えばこの処理の違いを言えと言われると答えることができないことに気付きました。
なので、まとめてみました！何かの役に立てればと思います。

## 結論
saveメソッド・createメソッドの違いは
メソッドの目的、それはDBにデータを保存することです。
メソッド名やちょっとした処理の内容は異なりますが、最終行われる処理の結果は同じです。
saveメソッドを使用すると少しコードの数が多くなりますが、createメソッドは短くすることができます。

## ActiveRecord
ActiveRecordとはMVCのM（モデル）に関連するテーブル操作に対して使用できるメソッドの総称のことです。
saveメソッドやcreateメソッドはデータベースの情報を円滑に受け渡しをすることのできるコードになっています。

## それぞれの使い方

### saveメソッド
```ruby
 @post = Post.new(title: "hello")
 @post.save
```
newメソッドでpostインスタンス変数を生成してから、saveメソッドでインスタンス変数を保存する必要があるので短くても２行になってしまいます。

### createメソッド
```
 @post.create(title: "hello")
```
createメソッドはインスタンス変数の生成と保存を行うので、１行で済むという違いがあります。

## まとめ
saveとcreateメソッドは処理の結果は同じでも内容が違うことです。
そのほかには、
saveメソッドcreateメソッドにはsave!・create!があります！

処理の中には、結果が同じでも内面的に違うことがあるので、知ってみると楽しいです。
それぞれの処理の内容の特徴を知ることで理解を少しは深めることができてきているなと思います。
ふと思ったことをまとめることができればと思います。

何かアドバイス等あればコメントお願いします！！

### 参考文献
railsガイド：https://railsguides.jp/active_record_basics.html
【ExcelVBA】Long(数値)型を文字列型に置き換えるメモ

####トピックについて
* 数値型を文字列型にする関数のメモ
* 単純な話`Trim(str(val))`で済む話だけど、個人的に見栄えを良くしたくて、いつも`Function`を作成している。

◎2021/02/06追記---->
@radames1000様よりご指摘いただきました。
わざわざ関数作る必要なかった！！！
`CStr(num)`
以下の関数じゃなくて↑を使います！！
-------------------<

####ソースコード・・・
```vb
'-------------------------------------
'引数に指定されたlong型の数値を文字列に置き換える
'[引数]
'num : 文字列に変換したいlong型の数字
'[戻り値]
'string : 引数を文字列に変換した値（スペースも取り除く）
'-------------------------------------
Function cnvStr( _
  ByVal num As Long _
) As String
'-------------------------------------
  cnvStr = Trim(Str(num))
End Function

```

####メモ
●　単純に`Trim(str(val))`ではなく`cnvStr(val)`が好きなだけです。関数名は短ければなんでもOKです。
●　`（`を出来るだけ少なくしたい。
●　A1C1形式で`Range`でアクセスするときに良く使っています。
[初学者] SQLとは？

#SQL「StructuredQueryLanguage」
RDBの操作を行う言語です。
データベースに対して命令（クエリ）を行います。
SQL(エスキューエル)とはクエリを書くための言語とも言えます。
命令は大きく以下の3つです。
データを定義する「DDL（Data Definition Language）」
データを操作する「DML（Data Manipulation Language）」
データを制御する「DCL　(Data Control Language)」

 DDL
データを定義するSQLです。以下のような作成/更新/削除などの命令文があります。

命令   |機能
------|------------------------
CREATE|データベースやテーブルの作成
ALTER |データベースやテーブルの更新
DROP  |データベースやテーブルの削除


 DML
データを操作するSQLです。以下のような登録/更新/削除/検索などの命令文があります。

命令   |機能
------|-----------------
INSERT|データの登録
UPDATE|データの更新
DELETE|データの削除
SELECT|データの検索

#SELECT,　　FROM
「SELECT」は「どのカラム（縦の列）を取得するか」を指定します。
SELECT name「nameカラムのデータを取得」

「FROM」は「どのテーブルから取得するか」を指定します。
FROM　users 「usersテーブルからデータを取得」

SELECT name, email　　「複数カラムの指定」はカンマで区切ります。
SELECT ＊　　　　　　  「全てのカラム指定」にはアスタリスクを用います。


#WHERE
「WHERE」「どのレコード（横の行）を取得するか」
WHERE name = "鈴木";　「usersテーブル、nameカラム,、鈴木レコード」
文の最後はセミコロンをつけてください。

SQLでも演算子が、下記２文のように使えます。
WHERE price >= 100　　　　　　　　「値段が1000以上」
WHERE created_at <= "2020-04-15"　「2020年4月15日以前」

特定の文字を含むデータを取得したい場合は、「〜のような」という意味を持つ「LIKE演算子」を使います。

「ワイルドカード」とは、どんな文字列にも一致することを指す記号です。LIKE演算子では「％」をワイルドカードとして扱います。

WHERE name LIKE "%鈴木%";　　　　「鈴木が文字列内に含まれるデータを取得」

WHERE name LIKE "鈴木%";　　　　　「鈴木で始まるデータを取得」　　　前方一致

WHERE name LIKE "％鈴木";　　　　　「鈴木で終わるデータを取得」　　　後方一致


「NOT演算子」は「NOT」を置くことで、指定の条件を満たさないデータを取得することが可能です。

WHERE NOT name = "鈴木";　　　　　　「スズキではないデータを取得」

WHERE NOT name LIKE "%鈴木%";　　　「鈴木を含まないデータを取得」

「IS NULL」「カラム名 IS NULL」で、　　「指定したカラムがNULLであるデータ」を取得する

「NULLではないデータ」を取得する場合は「IS NOT NULL」を用います。「カラム名 IS NOT NULL」

WHERE name is NULL;　　　　「nameカラムがnullのレコードを取得」

WHERE name is NOT NULL;　　「nameカラムがnullではないレコードを取得」


AND演算子　WHEREに複数の条件を指定することができます。「WHERE 条件１ AND 条件２」のようにすることで、条件１と条件２を共に満たすデータを検索することができます。

WHERE  name = "鈴木"
AND     age = 17;

OR演算子は、AND演算子と同様に、複数の条件を扱います。「WHERE 条件１ OR 条件２」のようにすることで、条件１または条件２のどちらかを満たすデータを検索することができます。

WHERE  name = "鈴木"
OR     age = 17;
ssh-keygenの公開錠コメントを変えたいやつなんていない

業務にて関係部署に公開鍵を渡す機会があることも、ないこともない！

しかし、公開錠のコメントを下のように私用のメールアドレスで作ってしまったそんなあなた！

```ssh:.pub
ssh-rsa AAAAB3NzaC1yXXXXXXXXXXXXXXXXXXXXXXXXX== stbtkhr@sample.com
```

もっとわかりやすく鍵の中身を変えずに、コメントのみを会社のメルアドとかにしたい！

いろいろ調べたが検索してみても欲しいやつがなかった。

とりあえずコマンドには、manや！

```ssh:
man ssh-keygen

Too many arguments.
usage: ssh-keygen [-q] [-b bits] [-C comment] [-f output_keyfile] [-m format]
                  [-N new_passphrase] [-t dsa | ecdsa | ed25519 | rsa]
       ssh-keygen -p [-f keyfile] [-m format] [-N new_passphrase]
                   [-P old_passphrase]
       ssh-keygen -i [-f input_keyfile] [-m key_format]
       ssh-keygen -e [-f input_keyfile] [-m key_format]
       ssh-keygen -y [-f input_keyfile]
       ssh-keygen -c [-C comment] [-f keyfile] [-P passphrase]
//以下省略

     -C comment
             Provides a new comment.

     -c      Requests changing the comment in the private and public key files.  The program will prompt for the file containing the private keys, for the passphrase if the key has one, and for the new comment.
```

と書いてあったので、以下を実行してみる。

```ssh:
ssh-keygen -c -C "test"
```

これだけ。そうすると以下のように変わるはず。


```ssh:.pub
ssh-rsa AAAAB3NzaC1yXXXXXXXXXXXXXXXXXXXXXXXXX== test
```

見事最後のコメント部分だけ変更になった。

vimで変更するのも考えたが、（ないと思うけど）破壊する恐れがあったので、ssh-keygenで行うのが安全かな？
ここまで至るまでmanをよく読まず適当に試した結果、今まで登録していた秘密鍵すら変更する失敗を犯しました。

そんな失敗をしないようにしましょうね！みなさま！
ShellScriptの実行をCtrl+cでキャンセルする

# シェルの実行を途中で止める

シェルを途中で止めたいときはだいたい何かしらの後処理が必要になる。
なので単純にKILLするのは危ない。
そこでCtrl+cを受ける処理を記述する。

# 割り込みのトラップ

```shell
#!/bin/sh

trap 'echo
      #割り込み時の処理
      exit' INT

while :
do
    #本処理
done
```

trapからINTまでがトラップの処理です。
Ctrl+cを押すとINTが送信されるので、
トラップできます。
[Rails] RailsにMySQLを導入する方法

mysqlがインストールされていることが前提となります。

#新規アプリ作成時に指定する
Railsのデータベースの初期設定は、 `SQLite` が設定されています。
なので、新規アプリ作成時のコマンドを実行する時に `MySQL` を利用することを設定します。

```
$ rails new アプリケーション名 -d mysql
```

#接続設定をする
先ほどのコマンドを実行すると、 `config` フォルダ配下に `database.yml` が作成されます。 `database.yml` はデータベースとの接続情報を設定するファイルです。既にオプションで指定したMySQLを利用するためのデフォルトの設定が記載されています。

```
adapter:   使用するデータベースの種類
encoding:  文字コード
reconnect: 再接続するかどうか
database:  データベース名
pool:      コネクションプーリングで使用するコネクションの上限
username:  ユーザー名
password:  パスワード
host:      MySQLが動作しているホスト名
```

#データベースを作成する
以下のコマンドを実行します。

```
$ rails db:create
```

railsのサーバーを立ち上げます。

```
$ rails s
```
PythonでライブラリTranslatorの使い方の簡単な説明

#translateの使い方
Pythonを使って、翻訳機を作るために利用したライブラリであるtranslateの使い方を書いておきます。他にも翻訳できる機能としてgoogletransがありますが、環境構築がうまくいかなかったため、すぐにインストールして簡単に使用することができたtranslateを使いました。

まず、以下のようにtranslateをインストールします。
```
$pip install translate
```
次に以下のようにライブラリをインポートします。

```
from translate import Translator
```
最後に以下のようにある言語からある言語へと翻訳したい二つの言語、翻訳したい言葉を決めれば完了です。変数resultに翻訳された結果が格納されます。（この場合、日本語から英語に言葉が翻訳されます。そして、英語で"Hello"という言葉が変数resultに格納されます。アルファベット二文字表記で日本語は"ja"と表し、英語は"en"と表します。）

```
translator = Translator(from_lang = "ja", to_lang = "en")
result = translator.translate("こんにちは")
```

あらゆる言語のアルファベット二文字表記は以下のサイトに載っているので、ぜひ参考にしてみてください。しかし、そのサイトに載っているすべての言語が利用できるわけではないので注意してください。
https://www.benricho.org/translate/languagecode.html
Salesforce Classic での Pardot を使用した対ビジネスマーケティングの自動化 ⇒ B2B Marketing Analytics のデータセットとダッシュボード ⇒ データセットについて

１．データセットとは何ですか?
２．データセットはどのようにして入力されますか?
３．データセットには、次のどの種別のデータを使用できますか?
４．Analytics アプリケーションにデータをインポートするにはどうすればよいですか?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/51095a5b-1b0a-902d-cb57-cd9c0464357b.png)
そうだ、SQLを勉強しよう①

#はじめに
Railsでアプリを作っている時も、Mysqlを使っていたが、
SQL文を理解せず、DBを扱っていたので、ちゃんと勉強しようと思い、本日から始めます。

##基本の4パターン

- SELECT文(検索する)
- UPDATE文(更新する)
- DELETE文(削除する)
- INSERT文(挿入する)

##基本ルール

- 文の途中に改行を入れることができる。
- 行の先頭や途中に半角スペースを入れることができる。
- 複数の文を区切るときは、文末に(;)
- コメントは１行のときは(--)複数行のときは(`/*ここにコメント*/`)


##最後に
結局、4パターンにどう修飾させていくのかが、難しいんだぁ。
まぁ、今日学んだことを列挙してみたが、うーん…って感じだな。
AWS プライベートサブネットのRDSに踏み台経由でプログラムからアクセスする

# はじめに

この記事は、社会人２年目 AWS、python初心者が勉強したことをまとめたものです。
間違っている点、改善点等ございましたご指摘いただけると大変ありがたいです。


# この記事について
踏み台サーバを経由してセキュアにRDSに接続する構成は多いのではないでしょうか？

プライベートサブネットに存在するRDSにパブリックサブネットの踏み台経由で
ローカルのプログラムからアクセスする方法がわかります。
似たような記事はたくさんありますが、自身の備忘録として投稿しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1066929/da3d1c0f-2181-3bf2-f8c6-a367e8969d7e.png)



# 手順

## 踏み台サーバにポートフォワーディングを設定

```terminal

ssh -i 踏み台サーバの秘密鍵 踏み台のユーザ名@踏み台のIP -L 任意のポート:RDSエンドポイント:3306

例：
ssh -i sushi.pem sushi@22.22.22.22 -L 13306:dev-rds-endpoint.ap-northeast-1.rds.amazonaws.com:3306

```

## 実行プログラム

pythonのpymysqlを使用してRDSに接続します。

```python:rds_access.py

connection = pymysql.connect(host = '127.0.0.1',
                             user = 'rds_user',
                             password = 'password',
                             db = 'sushi',
                             charset = 'utf8mb4',
                             port = 13306, # ポートフォワーディングで設定したport
                             cursorclass=pymysql.cursors.DictCursor)


with connection.cursor() as cursor:
    sql = "select * from SushiInfo"
    cursor.execute(sql)
    result = cursor.fetchall()
```

# 接続できました！！
深層学習の実装手順

#深層学習の実装手順
①データを取り込む
②データを加工
③学習モデルの作成
④コンパイル
⑤学習
⑥予測

##深層学習の実装手順　解説
__①データを取り込む__
DBやcsvファイルより訓練データとテストデータを読み込みます。

```「```:pandasを使用したcsvファイルの読み込み
import pandas as pd
train = pd.read_csv("./train.csv")
test = pd.read_csv("./test.csv")
```

__②データの加工__
正規化や正則化を行います。

__③学習モデルの作成__
通常の深層学習であるMLPの他に画像データに対してはCNN、時系列データに対してはRNNやLSTMの学習モデルが存在します。

```「```:学習モデル(例)
model = Sequential()
model.add(Dense(中間層の数,input_dim=入力層の数))
model.add(Activation('sigmoid'))#(例)活性化関数：シグモイド関数
model.add(Dropout(ドロップアウトを行う確率))#過学習抑制のため
model.add(Dense(output_dim=出力層の数))
model.add(Activation('softmax'))#(例)活性化関数：ソフトマックス関数
```

__④コンパイル__
最適化アルゴリズム、誤差関数(損失関数)、評価関数を決定します。

■誤差関数(損失関数)：学習モデルの出力結果と教師データのずれを表す関数
■最適化アルゴリズム：ニューロンの重さを変化させる値を決定
■評価関数：学習モデルの出力結果と教師データが一致している割合

```「```:学習モデル(例)
model.compile(loss='categorical_crossentropy',
optimizer='adam',#(例)最適化手法(どのくらい下降するか決定)：'adam'
metrics=['accuracy'])#(例)評価関数(正解率)：accuracy((TP+TN)/(TP+TN+FP+FN))
```

__⑤学習__
訓練データが入力された時に正しい教師データが出力されるように学習を行います。
学習回数やバッチサイズを入力します。

```「```:学習(例)```」
変数 = model.fit(訓練データ,教師データ,epochs = 学習回数,batch_size = バッチサイズ)
```

__⑥予測__
学習し終えたモデルに対してテストデータを入力し、予測出力結果を得ます。

```「```:学習(例)```」
変数 = model.predict(テストデータ)
```

#参考文献
[1]岡谷貴之，深層学習，2015 講談社サイエンティフィク， 講談社．

[2]詳解　ディープラーニング　TensorFlow・Kerasによる時系列データ処理
地理座標点データを GeoJSON 形式へ変換

こんにちは
地理座標点データ（CSV データの並び）を、GeoJSON 形式へ変換しました（```MultiPoint```として表現）。```jq```コマンドを利用しました[^1]。

[^1]: これは、「[csv 形式を配列へ変換（jq コマンド）](https://qiita.com/kkdd/items/6895e55a8c733e80aa54)」の応用です。

```console
$ cat points.csv
131.1111111,31.1111111
132.2222222,32.2222222
$ cat points.csv | perl -pe 's/.*/[$&]/' | jq -sc .
[[131.1111111,31.1111111],[132.2222222,32.2222222]]
$ cat points.csv | perl -pe 's/.*/[$&]/' \
 | jq -s '{"type": "FeatureCollection", "features": [{"geometry": {"type": "MultiPoint", "coordinates": .}, "type": "Feature", "properties": {}}]}' 
{
  "type": "FeatureCollection",
  "features": [
    {
      "geometry": {
        "type": "MultiPoint",
        "coordinates": [
          [
            131.1111111,
            31.1111111
          ],
          [
            132.2222222,
            32.2222222
          ]
        ]
      },
      "type": "Feature",
      "properties": {}
    }
  ]
}
```
【GAS】GoogleAppsScriptを用いてスプレッドシートのリストからClassroomで課題を配布する方法(CourseWork.create)

こんにちは！GoogleAppsScriptを使った業務効率化に夢中の初心者プログラマー、太郎です。

今日は**GoogleAppsScript(GAS)を用いてClassroomに生徒を招待する方法**について書いていきたいと思います。

初心者向けですが、javascriptの基本的なルールは押さえておいた方が理解はしやすいと思いますので、まだの方は[Progate](https://prog-8.com/dashboard)でサクッとjavascriptの文法を学習してみてください。

また、初心者が手当たり次第にコードを書いてみて詰まったところやその時の心の声などもつらつらと書いています。やり方のみさっと知りたい方は、最後の[まとめ](#まとめ-スプレッドシートのリストからclassroomで課題を配布する方法
)を参照ください。

また、`ReferenceError: Classroom is not defined`とエラーが出てしまった方は、[こちらの記事](https://qiita.com/coticoticotty/items/fd73cf477140349b2809)を参考にしてみてください。
では、早速トライしていきましょう！
##スプレッドシートから生徒リストを取得する
まずはスプレッドシートから生徒のIDを取得します。
![スクリーンショット (77).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108046/296dbc0e-e847-269f-505b-4f6227259686.png)
欲しいデータは
列の2行目から8行目に格納されているので、その範囲のデータを取得しましょう。

```javascript
function createCourseWork () {
  var studentListSheetUrl = '取得したいスプレッドシートのURL';
  var studentListSheet = SpreadsheetApp.openByUrl(studentListSheetUrl).getSheetByName('取得したいシート名');
  var studentList = studentListSheet.getRange(2, 1, 7, 1).getValues().flat();//取得したい範囲のデータを取得し、1次元の配列に変える
}
```
これで変数`studentList`に生徒のメールアドレスの配列を取得できました。studentListの中を出力すると
`[××××@gmail.com, ××××@gmail.com,・・・]`
こんな感じで1次元の配列になっています。
`flat()`を付けないと、`[[××××@gmail.com], [××××@gmail.com],・・・]`
こんな感じで配列の中に配列がある、2次元配列になってしまうんですね。課題を配布する生徒を指定する`studentIds`には1次元の配列を入れるので、そのままだと入れられないんです。そんなわけで`flat()`で1次元配列にしてあげましょう。
##取得した生徒IDから課題を配布する
さて、IDを取得出来たら、あとは[Classroomで指定した生徒に課題を配布する方法](https://qiita.com/coticoticotty/items/f2cf998961ed8ac77c4d)に書いた通りで良く、`studentId`に取得したstudentListを渡してあげれば課題が配布できます。

```javascript
function createCourseWork () {
  var studentListSheetUrl = '取得したいスプレッドシートのURL';
  var studentListSheet = SpreadsheetApp.openByUrl(studentListSheetUrl).getSheetByName('取得したいシート名');
  var studentList = studentListSheet.getRange(2, 1, 7, 1).getValues().flat();//取得したい範囲を指定

  var targetCourseId = '××××'//課題を配布するコースのIDを入力
  var createAssignment = {
    title: '××××',//配布する課題のタイトルを入力
    state: 'PUBLISHED',
    workType: 'ASSIGNMENT',
    materials: [{
      "driveFile": {
        "driveFile": {"id": "××××"},//課題として配布するファイルのIDを取得
        "shareMode": 'STUDENT_COPY'
        }
      }],
    assigneeMode: "INDIVIDUAL_STUDENTS",
    individualStudentsOptions: {
      "studentIds": studentList//スプレッドシートから取得した生徒のIDリストを渡す
    }
  };
  Classroom.Courses.CourseWork.create(createAssignment, targetCourseId);
}
```
以上、参考になれば嬉しいです。Google Classroom APIは日本語の情報が少ないため、今後も様々トライしてみた結果を記事に書いていきたいと思います！
Request Specについて

#目次
①Request Specとは
②Request Specの記述の流れ
③テストの実行



##①Request Specとは
コントローラのテストコードを書くために特化したもの。(RequestSpecを導入後使用可能)
基本的なテストの内容はcontrollerテストと同様だが、requestテストではパスを指定する。
get post patch deleteメソッドを実行するとテストの対象となるルーティングが行われ、対応するコントローラーのアクションが実行される。このときresponse.statusが200出なければいけないし、response.bodyに特定の文字列が含まれることをテストできる。


##②Request Specの記述の流れ

```php:spec/requests/clients_spec.rb

  describe ClientsController, type: :request do

   before do
    @Clients = FactoryBot.create(:client)
   end

   describe 'GET #index' do
   it 'indexアクションにリクエストすると正常返ってくる'
     get root_path
     expect(response.status).to eq 200
   end
   it 'リクエストに成功すると、投稿済みのメモが存在する'
     get root_path
     expect(response.body).to include(@client.memo) 
   end
   it 'リクエストに成功すると、投稿フォームが存在する'
     get root_path
     expect(response.body).to include('投稿') 
   end
  end
 end
```
上記のように記述をする。
get root_pathはどのように記述するかというとターミナル上でrails routesをすることでリクエストすべきパスが見つかるので、それを記述する。idが含まれる場合は get client_path(@client)などと記述する。

response.statusを実行することによって、レスポンスのステータスコードを教えてくれる。

<b><補足></b>
HTTPレスポンスステータスコードについて
①情報レスポンス(100~199)
②成功レスポンス(200~299)
③リダイレクト(300~399)
④クライエントエラー(400~499)
⑤サーバーエラー(500~599)

①情報レスポンス(100~199)
暫定レスポンスで、その時点までのすべてに問題なく、クライアントはリクエストを継続してよい、リクエストが完了している場合はレスポンスを無視していいことをしめす
②成功レスポンス(200~299)
リクエストが成功したことを示す。
③リダイレクト(300~399)
リクエストに対して複数のレスポンスがあることを示す。
④クライエントエラー(400~499)
構文が無効であるためサーバーがリクエストを理解できないことを示す
⑤サーバーエラー(500~599)
サーバー側で処理方法がわからない事態が発生したことを示す。


response.bodyを実行することによって、ブラウザに表示されるHTMLの情報を抜き出すことができるかできる。


##③テストの実行

```php:ターミナル
 bundle exec rspec spec/ファイル名
```
Amazon S3 が AWS PrivateLink をサポート

#概要#
今までは、Gateway型という形で用意されていました。
VPCの各種Gatewayと同じで、VPCひとつに設定してあげてそこから、S3にリクエストをとばしてあげるイメージです。
今まで例えば、DirectConnectでオンプレからAWSに接続している場合に、オンプレからS3にアップロードすることができませんでした。
AWS側で何かしらファイルを受け取って、S3に投げ込む必要がありましたが、今回、PrivateLink に対応したことにより、直接ファイルを投げ込むことができるようになりました。
また、PrivateLink ということはENIが作成されるので、セキュリティグループがつけられ、そこで通信の制御ができるという利点があります。今までポリシーベースで制御ができましたが、ネットワークレベルで制御できたほうがよい。

[[Feb 2, 2021]Amazon S3 now supports AWS PrivateLink](https://aws.amazon.com/jp/about-aws/whats-new/2021/02/amazon-s3-now-supports-aws-privatelink/)


#PrivateLink の作成#
	
[タイプ]に「Interface」ができています！
![スクリーンショット 2021-02-04 215905.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/b8032322-49a9-aca7-7dd3-c58721e8e89d.png)

エンドポイントを作成する VPC および AZ を選択します。
![スクリーンショット 2021-02-04 220044.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/d0b62d46-64e5-2360-8a9c-52718a9f4701.png)

インターフェース型のエンドポイントを作成する際はプライベート DNS を有効にしますが、 PrivateLink for S3 では利用できません。セキュリティグループを選択し [エンドポイントの作成] します。
![スクリーンショット 2021-02-04 220137.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/a0615e52-844f-bdfc-f8d0-df50ce53d3a9.png)


#確認#

前述のとおり、PrivateLink for S3 ではプライベート DNS 名が利用できませんので、エンドポイント固有の DNS 名を利用する。

```
$ dig vpce-xxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com +short
10.0.1.139
10.0.2.145
```

AWS CLI の場合、Interface VPC Endpoint の DNS 名を使ってくれるようにプログラム側で指示してあげる必要があります。

```
aws s3 --endpoint-url https://bucket.<エンドポイントごと固有の値>.s3.ap-northeast-1.vpce.amazonaws.com ls s3://<バケット名>/
```

#リンク#
[Amazon S3](https://qiita.com/leomaro7/items/e6bfc93f03ca4235f14a)
getServerSideProps内で、２バイト文字のパラメーター含むURLにgetしたらエラーになった

#状況
途中までuseEffectで実装してて問題はなかったのですが、SSRに変更したら２バイト文字を含むURLにgetした時だけエラーになることが分かりました

#環境
```
 "next": {
      "version": "10.0.5"
}
```

#ダメだった例
```js
  const url = 
    `${process.env.NEXT_PUBLIC_API_ENDPOINT}/api/${word}`
  ;
  return axios.get(url).then(({ data }) => ({
    posts: data.posts
  }));
```



#エラー文
```
Server Error
TypeError: Request path contains unescaped characters

This error happened while generating the page. Any console logs will be displayed in the terminal window.
```

どうやら２バイト文字はだめっぽい感じ（他の１バイト文字列のパラメーターだとこのエラーはでない）


#encodeすればいけるらしい
```
  const url = encodeURI(   
    `${process.env.NEXT_PUBLIC_API_ENDPOINT}/api/${word}`
  );
```

に変更したら動きました。
やったぜ！！！！

#一応全体を貼っておきます

```
const fetchData = async word => {
  const url = encodeURI(   
    `${process.env.NEXT_PUBLIC_API_ENDPOINT}/api/${word}`
  );
  return axios.get(url).then(({ data }) => ({
    posts: data.posts
  }));
};

export const getServerSideProps = async ({ params }) => {
  const data = await fetchData(params.word);
  return {
    props: {
      posts: data ? data.posts : []
    }
  };
};
```

#まとめ

にわかなのでこれから色々覚える所存！！！
Ruby on railsについて

#Ruby on railsとは？
MVCアーキテクチャ（論理的構造）に基づいて、構築されたフレームワークのこと

フレームワークとは**骨組み**のこと
Ruby on Railsはその名の通り、あらかじめ引かれた線路に沿って開発を進めていくことで、少ないコードで機能的なWEBアプリを作成できるのが特徴。

**M：Model（モデル）/ドメイン層**・・・主にデータベースとのやり取り（保存や読み込み）するクラスのこと。


**V：View（ビュー）/インターフェース層**・・・ページの見た目。HTMLファイルが複数ある。


**C：Controller（コントローラー）/アプリケーション層**・・・指令や指示を司る機能。コントローラーを経由してビューをブラウザに返す。
。

**ルーティング**・・・ブラウザとコントローラーを繋ぐ役割を担うもの。
　　　　　　　送信されたURLに対して、『どのコントローラーのどのアクション』で処理するか決める対応表のこと。

MVCとルーティングを簡単に表にまとめたもの
![スクリーンショット 2021-02-05 12.41.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005104/dbef3849-dcd7-3aef-09f1-ae803ada428b.png)
#まとめ
Ruby on railsの動きをざっくりとだが理解できた。これから細かいところに入っていくのでしっかりと勉強したい。
JavaScriptアウトプット1日目

JavaScriptnアウトプット1日目
1. [文字の出力](#anchor1)


<a>
##文字の出力
- 書き方

```
console.log('Hello World!!')
```
- 出力

```
Hello World!!
```
##変数名を利用した出力

- 書き方

```
item = 'Hello'
console.log(`${item} World!`)
```
-出力
```
Hello World!
```

##if文
- 書き方

```
const number = 1;

//偶数奇数判定
if (number % 2 === 0) {
  console.log("even number");
} else {
  console.log("odd number");
}
```
- 出力

```
odd number
```

- 省略形

```
(number % 2 === 0)?console.log("even number"):  console.log("odd number");
```
## for文
- 入力

```
for (let i = 0; i <= 10; i++) {
  console.log(i);
}
```
- 出力

```
0
1
2
3
4
5
6
7
8
9
10
```

## 関数

```
const add(a, b) {
  return a + b;
}
substitute_value = add(1,2)
console.log(substitute_value)
```

- 出力

```
3
```
- 省略形

```
const add = (a, b) => a + b;
```
JSON.parseができない時の解決方法

# はじめに
　エンコードした値をparseメソッドで連想配列に戻す際にSyntaxErrorが出て、解決に時間がかかったので備忘録として記事を書こうと思いました。何かアドバイスがあればコメントよろしくお願いします。

# やりたかったこと
phpのコントローラーでjsonエンコードした値をアサイン
↓
tplファイルのscriptタグの中で受け取る
↓
JSON.parseメソッドでデコードし連想配列に戻す

# 出たエラー
JSON.parseメソッドでデコードした時にSyntaxError

# 原因
JSON.parseメソッドは解析する文字に特殊文字が入っていたのが原因。
phpでは、controllersでアサインする時にクロスサイトスクリプティング(XSS)対策で特殊文字に変換されるらしい。今回は「""」が「&quot」に変換されていました。

# 解決方法
特殊文字が原因なので、アサインする時のエスケープ処理を無効化すればいいので、

```
  JSON.parse('{{デコードする値　nofilter}}');
```
でエラーがでなくなりました。

nofilterを記述することで、HTMLをそのまま出力することができる。

他にもreplaceメソッドで特殊文字を置き換える方法でも解決できるみたい。
vim（vi）の設定（Linux, Unix, macOS）

vim（vi）の設定の覚書き。
タブキー押下でスペース4個が入力されます。
タブ文字は使いません。

```vim:.vimrc

" シンタックスハイライト 有効
syntax enable

" 行番号表示
set number

" tab文字を使わず常に半角スペース
set expandtab
" 4個の半角スペースで1個のTabとカウント
set tabstop=4
" 改行時などのインデント=半角スペース4個分
set shiftwidth=4

" 改行時に自動インデント（字下げ）
set autoindent


```
関数型プログラミング(の、おいしいところ)を使う

関数型プログラミングの詳細については、1冊の本が書けるほどだし、自分もきちんと読んでいない。が、(自分なりの)要点は

#なぜ使うか
**テストが楽になるから。**
なるはず。なればラッキー。
コード書くのは好き。テストは嫌い。テストなんてやりたくない。

#成功するほうがまれ
**失敗しても気にしない。たまに成功すればそれでよい。失敗を恐れてやらないと、引き出しが多くならない。**
たまにしか成功しなくても、だんだん再利用可能なコードが貯まってくれば、他の人がすぐにできないことを、驚くべき速さ・かつ高品質で(他の人からみれば、まるで魔法のように)対応できるようになってくる。

>十分に発達した科学技術は、魔法と見分けがつかない。
>(A.C.クラーク)

十分に発達した=十分に再利用可能なコードが蓄積された。

#「関数」と言ってるでしょ？
**プログラミング言語の「関数」と、数学で言うところの「関数」が違うものだと思っている人が多い。**
同じなんだから同じ。「関数」型の「関数」とは、プログラミング言語の「関数」でもあるし、数学で言うところの「関数」でもある。

$f_1(x)$という「数学的な」関数があったとして、その実装が、

```C#:
public static int f1(int x)
{
	int result = x;
	if (x < 0)
	{
		result = -x;
	}
	return result;
}
```

であれば、同じでしょ？

#おいしいところを使う
ただし、やりにくいものを無理にやる必要はない。作成とテストを合わせて、楽になる(=拘束時間が短い、従ってコストも小さい)やり方を選ぶ。
そもそも、不変性を期待しない場合もある。例えば、何かを受信するReceiveという関数があったとして、これは引数が全く同じでも、同じ結果を期待していない。受信があればデータを返すし、無ければ返さない。こういうものに無理やり適用する必要はない。(部分的に出来るところはやるべきだが。)

#とはいえ、やらないと進まない
効果が発揮されるのは、テスト済みの$f_1(x)$や$f_2(x)$([成功例](https://qiita.com/somehiro/items/a9d3ca73bd98ad1f30cd#%E6%88%90%E5%8A%9F%E4%BE%8B%E3%81%BE%E3%82%8C)を参照)がたくさん貯まってきてからだが、やらないと貯まらない。たいていの場合は再利用されないゴミだが、それでもやらないと貯まらない。新しいファイルを作るのをためらわない。

#具体的になにをやるか
**関数型プログラミング(同じ入力なら必ず同じ結果を返す)を標榜するにはどうすればよいか。**
以下を守ってコードを書く。

* static関数で処理する。
* static変数を使わない(シングルトン含む)。
* 引数が多くなってもビビらない。引数が多いのは、たくさんのデータを処理しなければならないことの証左。それが具現化しただけ。

#成功例(まれ)
$f_1(x)$と$f_2(x)$というテスト済みの関数があった時、$f_1(x)$と$f_2(x)$を組み合わせた新たな$f_3(x,y)=f_1(x)+f_2(y)$を作っても、テストは1個やればOKでしょう、という目論み。

```C#:
public static int f1(int x)
{
	int result = x;
	if (x < 0)
	{
		result = -x;
	}
	return result;
}

public static int f2(int x)
{
	int result = x;
	if (x > 0)
	{
		result = -x;
	}
	return result;
}

public static int f3(int x, int y)
{
	// f1とf2がテスト済みなら、f3のテストは1個でいいでしょ？
	int result = f1(x) + f2(y);
	return result;
}

```
【初心者向け】アジャイル開発とは何ぞや？をまとめてみた

# 概要
「アジャイルとは何ぞや？」という問いに対して、まだまだ理解が散在していると感じたので、約２年半、そこそこ形式化された某大手のアジャイル開発専門部隊で仕事をしてみて得た知見から、初心者向けのアジャイルに関する情報をまとめてみた。
（あくまで個人的な備忘録）

# 前提
「ウォーターフォール開発」への基本的な理解がある事を想定
（無い場合は、ウィキペディア等で調べてみてください。）

# アジャイル開発とは？
正式名称： 「Agile Software Development ＝ アジャイルソフトウェア開発」
アジャイルは、「素早い」を意味する言葉である。
従来の開発手法よりも素早く開発するために生まれたもの。

『アジャイル開発とは何か？』についての定義は、ココに書いてあることが原則
👉 [アジャイルソフトウェア開発宣言](http://agilemanifesto.org/iso/ja/manifesto.html)

これまでのサービス開発が、答えが決まっているものを計画的に作る「計画重視」であったのに対し、
これからのサービス開発は、何が正解かは初めからわかない中で、フィードバックしながら効率的に作る「適応重視」が求められると考えから、
新たに生まれたのがアジャイル開発。

### そもそもアジャイル開発は「開発手法」ではない

「アジャイル開発」は、特定の一つの開発手法ではなく、「開発手法群」を指す。
アジャイルな開発を実現する様々な手法が存在し、それらをまとめたもの。

```bash:要するに、こういうこと
アジャイル開発 ─────────────┐
　|　スクラム、FDD          |
　|　XP、DSDM、ASD         |
　|　クリスタルクリア        |
　└───────────────────────┘
```

# アジャイルの定義を要約
「アジャイルソフトウェア開発宣言」を要約すると、以下。

#### 「4つの基本理念」

1. プロセスやツール ＜ 個人と対話
1. 包括的なドキュメント ＜ 動くソフトウェア
1. 契約交渉 ＜ 顧客との協調
1. 計画に従うこと ＜ 変化への対応

左記のことに価値を認めながらも、右記のことに、より価値をおく。

#### 「背景の12原則」
開発宣言の背後にある、従うべき原則👇
（かなり崩した日本語で要約したver.）

1. 顧客満足を最優先。「価値がある」サービスを素早く継続的にリリースしよう。
1. 仕様変更は後からでも歓迎。すぐに変化できることを強みにしよう。
1. 開発期間の１サイクル（2週間〜3ヶ月）を決定し、同じサイクルで定期的にリリースしよう。
1. 「真の開発」とは、経営側と開発側が、日々一緒に行うことだ。
1. 熱意ある人を集めよう。そして、環境を与えて、支援し、最後まで開発者を信頼すべき。
1. 結局、対面で話すことが、最も伝わるし、最も効率的。
1. 動く状態のソフトウェアを見ることが、開発進捗の真の"ものさし"である。
1. 開発サイクルは常に一定のペースを維持するべき。
1. 常に、技術・設計・効率化の情報に飢えることで、機敏な開発者になる。
1. シンプル・イズ・ベスト。
1. みんなが「自己組織化」すると、優れたチームになる。
1. 定期的に「振り返り」をすることで、チーム独自の開発力は洗練されていく。

# アジャイルに対する、よくある勘違い
下記は全てアジャイルに対する見方を誤っていると考えています

- 短いウォーターフォールでしょ？
👉違います。期間が短いだけでなく、開発に対するアプローチややり方、開発のゴール、組織の考え方などで相違があります。

- アジャイルは、設計書つくらないんでしょ？
👉違います。必要なドキュメントは用意します。

- 4つの基本理念の左側は、いらないんでしょ？
👉違います。「左側も必要だけど、右側がもっと大事」というだけです。

- ウォーターフォールよりも優れた開発なんでしょ？
👉違います。プロダクトや規模によって、得意不得意があります。アジャイルでは不向きなこと、ウォーターフォールで不向きなことがあります。

# まとめ
「どの開発手法が一番良い」ということはないですが、経験としてアジャイル開発の知識・経験が求められるシーンもこれから増えてくると思うので、機会があれば社内の小さなプロジェクト等でも良いからやってみるのが良い。
日記（エラー解決）

ポートフォリオ製作中に必ずエラーに遭遇しました。
全然大したことないかもしれないですが、アウトプットがてら投稿します。

##変数名is not a function
日本語に訳すと変数名は関数じゃないよという意味
その時私は変数名の隣に（）をつけてました。
（）をつけると関数扱いになるのでエラーが出ました。

##Your API key is invalid, please check you have copied it correctly.
これはAPIKEYが無効ですという意味。
plugins/firebase.jsにプロジェクトにアクセスするための設定を
記述するのですが、その時

```

const config = {
projectId: process.env.FIREBASE_PROJECT_ID,
apiKey: process.env.FIREBASE_API_KEY
};

```

記述するはずのapiKey: process.env.FIREBASE_API_KEYを記述し忘れました。たったのこれだけですがかなり迷いました。

そんなこんなで思ったのがエラーからは逃げてはいけない。現場に入ったら一日中エラーと戦わなければいけないのでこんなところでへこたれている場合ではないと思いました。これからも立ち向かっていきます。
[Python]bit全探索 ABC190C

#[ABC190C](https://atcoder.jp/contests/abc190/tasks/abc190_c)

$K$を筆頭に制約が狭いので、全探索の時間計算量を検討すると、$O(2^K(N+M+K))$で適合する。
bit全探索で $co[i]$ を皿 $i$ 置かれたボールの数として、実装する。

```python:サンプルコード
n,m = list(map(int,input().split()))
ab = [list(map(int,input().split())) for _ in range(m)]
k = int(input())
cd = [list(map(int,input().split())) for _ in range(k)]

ans = 0
for bit in range(1<<k):
  co = [0]*(n+1)
  for i in range(k):
    if bit&(1<<i):
      co[cd[i][0]] += 1
    else:
      co[cd[i][1]] += 1
  anss = 0
  for a,b in ab:
    if co[a]>=1 and co[b]>=1:
      anss += 1
  ans = max(ans, anss)
print(ans)
```
Drupal8、9のViewsの外部設置フィルターにAjaxが追加できない問題

Drupalでは`hook_form_alter`などで

```php
$form['my_field']['#ajax']  = [
  'event' => 'change',
  'callback' => 'my_callback',
];
```
と書くことで、フォームのフィールドに対して特定のイベントが発生した際に、ページをリロードすることなくコールバック関数を呼び出すことが可能です。

Viewsの外部設置フィルターもフォームの一種ですが、D8とD9ではイベントが発生してもコールバック関数が呼び出されないというバグがあります。

イシューはこちらです。
https://www.drupal.org/project/drupal/issues/2842525

今回は#83のパッチを貼って解決しました。

```json
"extra": {
  "patches": {
        "drupal/core": {
                "Ajax attached to Views exposed filter form does not trigger callbacks": "https://www.drupal.org/files/issues/2020-03-27/2842525-83.patch"
        }
   }
}
```
composer.jsonに上記を追加して`composer install`
Updating 360 video

Note of updating 360 video on Vimeo and youtube.
Almost 360 video does not be included the metadata, so need to inject it before updating.

I use this.
Metadata injector
https://github.com/google/spatial-media/releases

■Vimeo
Need "Top/Bottom layout" for uploading 360 video on Vimeo according to this article.
https://vimeo.zendesk.com/hc/ja/articles/115001877167-360-%E5%BA%A6%E5%8B%95%E7%94%BB%E3%81%AE%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064877/b912f143-26b5-fc46-112d-7950fb08d5eb.png)


But fail to process around the vertex like this. 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064877/2446fda3-dad5-2ddf-5ce6-fb1d1f81caeb.png)

I can't detect the cause of this processing error.
Then I ask Vimeo from its ask form.

■YOUTUBE
No need "Top/Bottom layout",
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064877/54610cbd-04d6-5d31-4006-b7eb5a780233.png)

youtube processing needs more time with Top/Bottom layout.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064877/e712c20a-85e3-2221-4495-d62f7b39e131.png)

And fail to process with Top/Bottom layout like this.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064877/8efdadff-f813-8f1c-a247-32d14db8eb19.png)
PHPUnit Laravelで実行されたクエリを確認する

## Laravelでどのようなクエリが実行されたのか確認する方法を明記します。

環境
OS: Mac OSX

```
php -v
PHP 7.4.11 (cli) (built: Oct  1 2020 19:44:09) ( NTS )
```

```
php artisan -v
Laravel Framework 7.30.4
```

クエリログの取得には以下の２つのコードをクエリ実行の`前後`に挟むかたちで記述します。

```
DB::connection()->enableQueryLog();
DB::getQueryLog();
```


```qiita:Laravel
<?php

namespace App\Model;

use Illuminate\Database\Eloquent\Model\Test;
use DB;

public function Test() {
    DB::connection()->enableQueryLog();      //ログ出力を許可
    $query = Test::select('id', 1)->first(); //クエリ実行
    $queries = DB::getQueryLog();            //出力ログを取得
    print $query;
}

```

```クエリ内容
select * from `test` where `id` is not null and `id` = 1 limit 1
```

こんな感じで取得できます。

参考記事
[Laravelで発行されたSQLをUnitテストで確かめる](https://qiita.com/mpls104/items/9905357dba17853b3f63)
【Capybara】Docker環境で擬似的に「save_and_open_page」を使用する

Dockerの開発環境へ移行した結果、Rspecでよく使っていた`save_and_open_page`に以下のエラーが発生するようになってしまいました。

```
Failure/Error: save_and_open_page

Launchy::CommandNotFoundError:
  Unable to find a browser command. If this is unexpected, Please rerun with environment variable LAUNCHY_DEBUG=true or th
e '-d' commandline option and file a bug at https://github.com/copiousfreetime/launchy/issues/new
```

htmlファイル自体は保存されるので、それを手動で開けば見れるのですが、毎回このエラーが出るのは都合が悪い…
ので、擬似的に`save_and_open_page`と同様の動きをする方法を考えてみました。

#前提

`save_and_open_page`で保存されるhtmlファイルがローカル環境であること。
（`docker-compose.yml`でローカルのディレクトリをマウントしていること）

```yml:docker-compose.yml
services:
  rails:
    build:
      context: .
      dockerfile: ./docker/rails/Dockerfile
    volumes:
      - .:/app
```

#1.`fswatch`のインストール

ファイルを監視するために`fswatch`を**ローカル環境**でインストールしてください。
Macの体で記載しますが、Windows、Linuxでも導入可能だそうです。

```
$ brew install fswatch
```

#2.監視コマンドの実行

以下のコマンドを実行してください。

```shell:
$ while true; do fswatch -e ".*" -i ".*\.html$" --event Created -1 <HTML保存先パス> | xargs open -a <ブラウザ>; done
```
`-e`：監視対象から除外
`-i`：監視対象のファイルを指定　←`-e`のオプションとの併用が必要
`--event`：監視対象のイベントを指定、`Created`でファイルが作成されたときのみコマンドを実行させる。
`-1`：1回のみ監視　←`while`の記述で繰り返し監視する

* `-1`を省いた`fswatch〜<ブラウザ>`のみのコマンドだと、htmlファイルを何回も開く挙動となってしまったため、`-1`をつけ繰り返す形としました。
* 終了するときは`ctrl + c`。

```shell:例
$ while true; do fswatch -e ".*" -i ".*\.html$" --event Created -1 ~/app/tmp/capybara/ | xargs open -a "Google Chrome"; done
```



#3.`save_page`の挿入
キャプチャを確認したい場所で`save_and_open_page`ではなく、`save_page`を挿入してください。
`open`しないので、エラーは発生しなくなります。

これで`rspec`をDocker環境上で実行しても、ローカル環境のブラウザが自動で開き、ページを確認できるようにできます。
scrollViewの中のViewにaddSubViewで追加されたViewをスクロールしたい

ScrollViewを作成

Viewを作成

この時のイメージはScrollViewのなかにがViewが入ってます。

ScrollViewとViewは上下左右Autoレイアウトを0で設定。

そしてScrollViewの中のContaint Layout GuideとViewをCenter Vertically in Content Guideで結びます。

次にScrollViewの中のFrame Layout GuideとViewをEqual widthで結びます。
  
そしてコードはこちら。

        containerView.frame.size.height = view.frame.size.height

containerViewがScrollViewの中のView

viewがaddSubViewしたビューです。

この時

        containerView.frame.size.height = view.frame.size.height

上記の場合だと表示はできますが、ScrollViewはスクロールできません。

なので、scrollViewの中のViewにaddSubViewで追加されたViewをスクロールしたい場合は

        containerView.frame.size.height = view.frame.size.height
上記を使いましょう。

もしかするとそれだけでは動かない可能性があります。

その時はこれを使ってください。

        containerView.translatesAutoresizingMaskIntoConstraints = true

すると動くかなと思います。

参考記事

https://fluffy.es/scrollview-storyboard-xcode-11/
rpc の Pid

# rpc の Pid

`rpc` では、実行ノードの違いによって実行プロセスが異なる。

|実行ノード|実行プロセス|
|---|---|
|ローカル|`rpc` を呼び出したプロセス |
|外部ノード|`rpc` 用に新しく作られたプロセス|

```erlang
(a@zeek)2> self().
<0.70.0>
(a@zeek)3> rpc:call(node(), erlang, self, []).
<0.70.0>
(a@zeek)4> rpc:call(b@zeek, erlang, self, []).
<7987.78.0>
```

rpc で作られたプロセスは実行が終了次第死ぬ。
UIStcrollViewはContentSize ではUIViewは？

↓変数名は自分で作成したもの
containerView.frame.size.height

containerView.bounds.size.height

boundsの場合だと取得できない場合があります。

なのでframeを使った方が良いかもです。

frameでやってみてダメならboundsでやってみるとかでいいかなと。
【rails】中間テーブルでのタグ機能の表示

##はじめに
先日、railsでタグ機能を実装しました
タグ機能に関しては
https://qiita.com/you8/items/b2394104c6f9865f5d46
以上の記事を参考にして作成しました

##問題
さて、先ほど紹介した記事にてDBのアソシエーションやら、コントローラー、モデルの記述をしてみたのですが、肝心なところでビューってどう表示するの？と思いました
タグ機能関連ってコントローラーやらビューやらDB設計はたくさんあるけど表示の部分っていくら検索してもHITしないんだよね


まぁ私が技術不足であると言われたらそこは否めないのですが💦


##解決方法
結論から言うとeachで繰り返された物をさらにeachで繰り返すと上手に表示されます

```
    - @posts.each do |i|
      .contents
        .contents__image
          = link_to post_path(i) do
            = image_tag i.image.url, class:"Image"
        .contents__title
          = link_to i.title,post_path(i),class:"text"
        .contents__tags
          - i.tags.each do |tag|
            .tag
              = tag.tag_name
```

これ私が個人的に現在作成中のギブアンドテイク（仮）アプリで実際に使われているコードです
![328cfa35feacfaa251f79d47fe75be90.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672760/a3fd3849-0324-a110-65f4-bb02574cd7bc.png)

これだと正常に表示されています
Sequel proを確認するとpost_idとtag_idで管理していることがわかると思います![スクリーンショット 2021-02-04 20.43.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672760/3daac13d-8b99-6662-29f3-5a591e0da4de.png)

つまり、@posts（特定のpost_id)の中のtagsをeach分で繰り返さないと野球とスポーツのタグは表示されないのです（表現が難しい💦）


以上が中間テーブルを使用したタグを表示する方法でした
最初はただeachで回せば大丈夫と思っていましたが全てのタグが表示されてタグ機能自体諦めようと思っておりましたが、ふと思った天啓によって思い通りの実装ができました

私のように思い悩んでいる方が解決できたら嬉しいです
【環境構築】Python編-初歩

##1.VScode拡張機能pythonを追加
こちらをインストール
![スクリーンショット 2021-02-04 10.35.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631096/f671e693-33c3-81ab-5b49-7ebf77ad52a3.png)

##2.anacondaをインストールする
こちらのサイトより自分のOSにあったものを
https://www.anaconda.com/products/individual/download-success

##3.pyファイルをつくる
○○○○○.pyというファイルをつくる
そしてその中にpythonのコードを記述していく

![スクリーンショット 2021-02-04 10.54.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631096/1898587c-791f-1130-56b1-22f7fafa9e7a.png)

##4.実行する
ターミナルを開く。
Macなら(Shift+control+@)コマンドでターミナルをひらく。
python + ファイル名 で実行する。
（例）
python python.py

![スクリーンショット 2021-02-04 10.55.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631096/14e9d842-9344-165f-7e81-4a705cf34dd3.png)
【HCL】TerraformでECRへPushする

## なぜやる
`Terraform` で環境構築時、コンテナイメージがないと、起動失敗しますので、そうなると、`ECR`作った直後に、コンテナイメージが作成できれば、後続のサービスも順調に作成できます。

## どうやる
`Terraform` は `provisioner` という機能が持っており、リモート／ローカルで任意のコマンドを実行できます。

## やってみる
まず `docker build` 用のスクリプトを作って、`Terraform` か呼び出し、プッシュしてもらう感じ、スクリプトが変更されたら、再実行できるトリガーも追加します。

```sh:dockerbuild.sh
#!/bin/bash

# Docker login
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

# Build image
docker build -t $CONTAINER_NAME .

# Tag
docker tag $CONTAINER_NAME:latest $REPO_URL:latest

# Push image
docker push $REPO_URL:latest
```

```terraform:main.tf
# ----------------------------------------------------------
# ECR
# ----------------------------------------------------------
resource "aws_ecr_repository" "frontend" {
  name                 = "frontend"
  image_tag_mutability = "MUTABLE"
  image_scanning_configuration {
    scan_on_push = true
  }
}

# ----------------------------------------------------------
# Null Resource
# ----------------------------------------------------------
resource "null_resource" "frontend" {
  triggers = {
    // MD5 チェックし、トリガーにする
    file_content_md5 = md5(file("${path.module}/dockerbuild.sh"))
  }

  provisioner "local-exec" {
    // ローカルのスクリプトを呼び出す
    command = "sh ${path.module}/dockerbuild.sh"

    // スクリプト専用の環境変数
    environment = {
      AWS_REGION     = local.region
      AWS_ACCOUNT_ID = local.account_id
      REPO_URL       = aws_ecr_repository.frontend.repository_url
      CONTAINER_NAME = "frontend"
    }
  }
}
```

```sh:dockerbuild.sh
#!/bin/bash

# Docker login
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

# Build image
docker build -t $CONTAINER_NAME .

# Tag
docker tag $CONTAINER_NAME:latest $REPO_URL:latest

# Push image
docker push $REPO_URL:latest
```

## 最後に
`provisioner` はたくさんのユーズケースに使えますので、詳しい情報は下記サイトまで。

https://www.terraform.io/docs/language/resources/provisioners/syntax.html#destroy-time-provisioners