OmniAICreator/Qiita-1.07M huggingface dataset:
【要約】デザイン科学概論 -多空間デザインモデルの理論と実践2章「デザイン思考モデル」


これはデザイン科学を読んだ時の備忘録として書いたものです。
興味がある方はぜひ購入してみてください
https://amzn.to/37KiFIb


# 要点

## デザイン思考のモデル
- 原因から結果を導く順問題(forward problem)と結果から原因を導く逆問題(inverse problem)がある。
- デザインは逆問題である
    - 逆問題の例
        - 症状から原因を推察する診療
        - 事件現場から犯人を推察する推理
- 自然科学は「物事がどうなっているか」を扱う
    - 一般機な論理体系からある推論が有効
- デザインは「事象がどうあるべきか」を扱う
    - ここでは特殊な推論が必要である
    - 逆問題に対応するためにデザイナーがする思考を「デザイン思考(design thinking)と呼ぶ」

## デザイン思考

### デザイン思考の概念
- デザイン思考は米国のデザインスタジオIDEO のケリーが2005年に発祥とされている
- ケリーがd.schoolで行ったデザイン思考の授業のプロセス  
    - 共感
        - 意味のあるイノベーションにはユーザー」を理解し関心を持つ必要がある
    - 問題定義
        - 正しい問題設定が正しい解決策をもつ
    - 創造
        - 可能な限りの可能性を考える
    - プロトタイプ
        - 考えるために作り、学ぶためにテストする
    - テスト
        - ユーザーと解決策について考える
- 上記のことからデザイン思考とは「問題を解決するアイディアを生み、形にする」ためのプロセス又はデザインに関わる発見と創造そして、その評価に関することである

### 発見と発明のための思考

- 人類史以来ものづくりにおけるものづくりから発見と発明に関する
思考を遡って学ぶことができる
- 過去推論に関しては様々な議論があった、その中で論証が強いとされてきたものが帰納と演繹である。
        - 演繹
            - 前提条件と規則からから結論を導く
            - 「ソクラテスは人である（前提条件）」と「全ての人は死ぬ（規則）」から「ソクラテスはひとでらるから死ぬ（結論）」を導出する
        - 帰納
            - 前提条件と結果を複数組み合わせることによって規則を導く
            - 「人であるソクラテスは死んだ」「人であるプラトンは死んだ」から「人は死ぬ」を導出する
        - 仮設形成
            - 結論と規則を用いて結論を説明する前提条件を出す
            - 「ソクラテスとは死んだ」「人は死ぬ」から「ソクラテスは人である」を導出する
## デザインのための思考
- 1962年のデザイン会議でのシステマティックデザイン3つのプロセス
    - 分析
        - 全てのデザイン要求をリストアップし理論的に矛盾しない性能仕様になるまで要求を減ずる
    - 総合
        - それぞれの仕様のための解決策を見つけ出来うる限りの妥協をしないアイディア案を複数作る
    - 評価
        - それぞれのアイディアを動作面、製造面、販売面などから仕様の達成度を評価して最終的なデザインを決定する
 
### AGEモデル
総合を発想に置き換えたモデル

デザイン問題があった場合帰納を使った分析又は仮設形成を用いた発想を行う。  
分析後は発想にやる解決策の模索を行い発想で得たアイディアは演繹によって評価する。この際評価が納得いくものでないのであればサイド分析を行い改善点を模索する。
【要約】デザイン科学概論 -多空間デザインモデルの理論と実践- 1章「デザイン科学の文脈」

これはデザイン科学を読んだ時の備忘録として書いたものです。
興味がある方はぜひ購入してみてください

https://amzn.to/37KiFIb

## 要点
- 有史以来素朴な人工物においては使う人と作る人は同じ人だったが、複雑化大規模化に伴って作る人が独立し役割も年々細分化されていった
- デザインの代表的分類に工業デザイン(INDUSTRIAL DESIGN)と工学設計(ENGINEERING DESIGN)が存在する
  - 工業デザインは芸術に視座を置き使用者や使用環境と人工物との関係性に注目したもの
  - 工学設計は自然科学や工学に視座を置き、昨日や人工物の性能に注目したもの 
- 2つのデザイン分野が生まれたのは背景に産業革命がある
  - ものづくりが機械化していった結果美しさが欠ける人工物が溢れた
  - アーツ・アンド・クラフツ運動により中世の手工芸が再評価され人工物に芸術的観点からの意匠が施される様になっていった
- 工学設計は戦乱時代に誕生したシステム工学(SYSTEM DESIGN)によって大きく進展する
- 1969年に広範囲的な研究を用いて行うシステマティックなデザイン手法である「デザイン科学(design science)」提唱される
- デザイン方法に関する会議の講演者リッテルによりトップダウン的手法を用いて問題解決ができるかどうかを定義できる「おとなしい問題(tame ploblem)」と定義ができない「意地悪な問題(wicked ploblem)」という
  - 意地悪な問題に対してはトップダウンではなくデザイナーがファシリテーターの立場を取りステークホルダーが自らデザインを行う手法を推奨しそれを「第2世代」区分し 旧来のトップダウン的手法を「第一世代」とした
  - デザイナーの直感、思いつきなどを包括した方法は「第3世代」した
  - 「世代」という表現は全世代を否定するのではなく 各世代が混ざり合って現在のデザイン活動があり、問題に応じて多様なアプローチをするため
- デザイン科学に関しての定義は年々議論されてきた
  - 2000年代には「デザイン行為における法則性の解明及びデザイン行為にもちいられる知識の体系化を目指す学問」としてデザイン学の中核をなすとしている
  - 同時にデザイン科学の枠組み(framework for design science)を定義している
    - デザイン知識(design knowledge)
      - デザインにおける色彩や形態に関する科学知識などの客観的知識とデザイナー個人の経験知もしくはそれに基づいたデザインに関するノウハウに関する主観的知識からなる

    - デザイン行為(designing)
      - デザイン実務
        - プロダクトデザイン、建築デザインなどのデザインの実践
      - デザイン方法
        - 目的の効果的効率的な達成手法を扱う
      - デザイン方法論
        - デザイン方法の特徴分析、選択指針の構築など複数のデザイン方法を扱う
      - デザイン理論
        - デザイン行為を説明する法則やデザインモデルを説明する理論を扱う
- デザイン科学の必要性
    - 協業しながらのデザインは未だに困難を極める
    - 物質的な豊かさにより消費者の価値観が物の充足から心の充足へと変化している
        - そのため高機能、多機能ではなく、新しい価値観や新しい体験が求められている
    - 以上の解決のためにデザインシンポジウムなどで議論されているが工学とデザインという異なる専門分野で発展した工学設計と工業デザイン統合は進んでいない
    - 2つの統合のために新しい基盤を探している
    - 近年人工知能の発展によりデザインのあり方においての議論が活発化している
        - デザインにおける発送は類比と類推と言われている。そのため多くのデータが扱えるAIがデザイナーの代わりに多様な発想を効率的にかつすばやく行えるのではないかと言われている
        - 分析と評価においては人間の研ぎ澄まされた感性や伝統的な美意識、または人間の経験によるデザインの意味付けはAIでは難しいので人間に分があるのではないかと言われている
    - 本書ではデザイン科学の先端的試みである「多空間モデル」とそれを用いたデザイン方法論である「Mメゾット」について説明していく
11年間メール製品のテストを行ってきた歩みとその歩みから感じる時代の変化

2019.11.14-15 開催された [JPAAWG 2nd General Meeting](https://meetings.jpaawg.org) で、弊社品質管理部主任吉川誠一により「11年間メール製品のテストを行ってきた歩みとその歩みから感じる時代の変化」というタイトルのセッションが行われました。その資料をこちらにも共有します。

[![thumbnail](https://speakerd.s3.amazonaws.com/presentations/2d0155d915f849d4a4e1ae3c06bf6588/slide_0.jpg?572683)](https://speakerdeck.com/yoshikawa01/11nian-jian-meruzhi-pin-falsetesutowoxing-tutekitabu-mito-sofalsebu-mikaragan-sirushi-dai-falsebian-hua)

弊社の別のセッション資料も合わせてご参考ください。
セッション資料：[メールセキュリティトレイルマップ～DMARCやその先にある世界～] (https://qiita.com/qualitia_cdev/items/a9d4fcc3ccf55154bf43)
Could not find value for key log4j.appender.A

```
+ exec /usr/java/jdk1.8.0_232-cloudera/bin/java -Djava.net.preferIPv4Stack=true -Djava.security.egd=file:///dev/urandom -cp '/run/cloudera-scm-agent/process/35-OOZIE.OOZIE_SERVER-test-db-connection:/usr/share/java/mysql-connector-java.jar:/opt/cloudera/cm/lib/postgresql-42.1.4.jre7.jar:/usr/share/java/oracle-connector-java.jar:/opt/cloudera/cm/lib/*' com.cloudera.enterprise.dbutil.DbCommandExecutor db.properties
log4j:ERROR Could not find value for key log4j.appender.A
log4j:ERROR Could not instantiate appender named "A".

2019-11-27 23:29:17,062 ERROR CommandPusher:com.cloudera.cmf.model.DbCommand: Command 55(OozieTestDatabaseConnection) has completed. finalstate:FINISHED, success:false, msg:Logon denied for user/password. Able to find the database server and database, but the login request was rejected.
2019-11-27 23:29:19,157 INFO scm-web-446:com.cloudera.enterprise.JavaMelodyFacade: Entering HTTP Operation: Method:POST, Path:/dbTestConn/checkConnectionResult
2019-11-27 23:29:19,172 INFO scm-web-446:com.cloudera.enterprise.JavaMelodyFacade: Exiting HTTP Operation: Method:POST, Path:/dbTestConn/checkConnectionResult, Status:200
2019-11-27 23:29:19,237 INFO CommandPusher:com.cloudera.cmf.service.AbstractOneOffHostCommand: Unsuccessful 'HueTestDatabaseConnection'
2019-11-27 23:29:19,238 INFO CommandPusher:com.cloudera.cmf.service.AbstractDbConnectionTestCommand: Command exited with code: 1
2019-11-27 23:29:19,238 INFO CommandPusher:com.cloudera.cmf.service.AbstractDbConnectionTestCommand:     return self._cursor()
```


Cloudera ManagerでCDPインストールしたら、上記エラーが出ました。
対策：
https://docs.cloudera.com/documentation/enterprise/latest/topics/cm_ig_mysql.html
MySQLのJava Connectorが古すぎるから。

```
sudo yum install mysql-connector-java* -y
cd ~/
mkdir tools/
cd tools/
wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.46.tar.gz
tar zxvf mysql-connector-java-5.1.46.tar.gz
sudo mkdir -p /usr/share/java/
cd mysql-connector-java-5.1.46
sudo cp mysql-connector-java-5.1.46-bin.jar /usr/share/java/mysql-connector-java.jar
```
[GCP]Google-managed SSL でFAILED_NOT_VISIBLEと出たときに確認したいこと

# はじめに

GCPからSSL証明書を作ろうとしたのですが、`FAILED_NOT_VISIBLE`というエラーが出てしまったので、解決するために確認・変更した部分をまとめておこうと思います。

大変参考になった記事↓
[GCP managed SSL(β版)を利用したhttpsサーバ構築](https://qiita.com/koshilife/items/a75a69d03e2524f33c8e)


# ①　DNSSECがONになっているか
調べている際に「DNSSECをONにしたら証明書が作れた」という記事があったので、参考にしました。

GCPメニューバーからネットワークサービス>Cloud DNSに行きゾーンのDNSSECをONにします。
![ 2019-11-28 13.08.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532695/80456224-db79-2dbc-422c-c624a3f0cd3c.png)


# ②　レコードセットのIPv4アドレスとロードバランサ（フロントエンド）のIpアドレスが同じになっているか

自分はこちらを直したら、解決できました。
GCPメニューバーからネットワークサービス>Cloud DNSに行きゾーンの詳細を見ます。
![ 2019-11-28 13.20.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532695/b7c63e39-db51-e7a1-a5c3-9dce561d0bda.png)
画像の赤色部分に表示されている番号と、GCPメニューバー>ネットワークサービス>負荷分散のロードバランサ詳細の下の赤色部分の番号が等しくなっているか確認します。違っていれば、ロードバランサの詳細のアドレスに合わせます。
![ 2019-11-28 13.14.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532695/b929e559-d58b-50fc-7ac5-2c7fbbe1ca7c.png)

この２つを修正し、解決することができました。
CentOS8で個人的に必要なrpmを作りました

* epelになかったため

## GitHub

* https://github.com/tukiyo/docker-centos8

## rpm 

* https://github.com/tukiyo/docker-centos8/releases/tag/rpms
    *  autossh
    *  ndjbdns
    *  nkf
    *  ranger
    *  sshfs
    *  tig
    *  w3m
Node.js + axios で 画像を mulitpart/form-data でアップロードしようとしてハマった話

# はじめに
この記事は [Goodpatch Advent Calendar 2019](https://qiita.com/advent-calendar/2019/goodpatch) の3日目です．

サービス開発でかかせないテストですが、テストデータの登録は数が多いと結構手間がかかったりします。
この記事では、Node.jsでテストデータを自動登録しようとしたときに詰ったポイントと、その解決の紹介をします。

# やりたかったこと
Node.jsのスクリプトでローカル上の画像ファイルを`mulitpart/form-data`で送信したかったので、
フロントエンドと同じaxiosを使おうとしました。

# 結論
最終的に動いたコードはこちらです。

```js
const fs = require('fs')
const axios = require('axios')
const FormData = require('form-data')

const uploadImage = async ({ auth_token, imageFilePath }) => {
  const form = new FormData()
  const file = fs.createReadStream(imageFilePath)
  form.append('image_file', file)
  const config = {
    headers: {
      'X-AUTH-Token': auth_token,
      'X-API-Token': env.API_TOKEN,
      ...form.getHeaders(),  // ← ← ← ← ここ!!
    },
  }
  const result = await axios.post(env.API_BASE_URL + 'api/v1/images', form, config)
}
```
# 経緯
## 目的
テスト用の大量のデータをフロント経由で準備するのが面倒だったので、API経由でデータを一括登録するためにNode.jsでスクリプトを作ろうとしました。

## 最初のコード
```js
const fs = require('fs')
const axios = require('axios')
const FormData = require('form-data')

const uploadImage = async ({ auth_token, imageFile }) => {
  const form = new FormData()
  const file = fs.createReadStream(imageFile)
  form.append('image_type')
  form.append('image_file', file)
  const config = {
    headers: {
      'content-type': 'multipart/form-data',
      'X-AUTH-Token': auth_token,
      'X-API-Token': API_TOKEN,
    },
  }
  const result await axios.post(env.API_BASE_URL + 'api/v1/images', form, config)
}
```

## エラー
```json
{
  "status": 500,
  "statusText": "Internal Server Error"
}
```

## なるほどわからん
どつぼにはまりました(1時間)

## 違いの比較
ちゃんと送信できているフロントエンドと比較するとRequest Headersがなんか違いました

Node.jsのヘッダー

```json
{
  "Content-Type": "mutlipart/form-data"
}
```

ブラウザが送信しているヘッダー

```json
{
  "content-type": "multipart/form-data; boundary=----WebKitFormBoundaryYtLvDL7BwH9NCRJja"
}
```

## `content-type` 消してみた
自前で付けている `'content-type': 'multipart/form-data'`消してみましたが駄目。
Request が `multipart/form-data`になってないことによるエラーが発生。

```json
{
  "status": 400
}
```
```json
{
  "Content-Type": "application/x-www-form-urlencoded" 
}
```

## 調べたら情報あった

`node.js axios FormData` でググったらバッチリでました。ありがたや。

https://qiita.com/itoudium/items/d9f04f379d34265387d8#axios%E3%81%A7formdata%E3%82%92%E9%80%81%E3%82%8B

## 要するに

`FormData`が自身の送信用headerを、getHeaders関数で生成してくれるので、それ使えやオラァ!ということです。

```js
 headers: {
   'X-AUTH-Token': auth_token,
   'X-API-Token': env.API_TOKEN,
   ...form.getHeaders(),  // ← ← ← ← ここ!!
 },
```

## 結論

同一コードでフロント上では問題なかったので混乱しました
最初からググって調べようね(教訓)
Laravel6.5 419 page expired にハマったとき

# Laravel6.5でセッションがうまく機能しなかったとき

419エラーの原因は大体、form内に"@csrf"を書き忘れている場合だと思います。

しかし、CRSF対策のコードを記述してもエラーが解決しなかったので頭を悩ませましたが、

```
{Laravel project}\storage\framework\sessions
```
のなかにあるセッションを保存しているファイルをすべて削除したら
正常に稼働しました。

ネット上ではいくら探しても見つからなかったため、記事に起こしておきます。
vim-lspの定義ジャンプとvim本来のtagジャンプを共存させる

[vim-lsp](https://github.com/prabirshrestha/vim-lsp)の定義ジャンプがいい感じなので`<C-]>`キーを上書きして使っているのだが、vimのhelpを見る際などに`<C-]>`が動作しなくて不便なことがある。

方針としてはまず`:LspDefinition`を実行してみて失敗したら本来のvimの定義ジャンプを呼び出すようなキーバインドを登録したい。

本質的にはvim-lspに限らず他のプラグインでも同様にできるはず。

```vim
nnoremap <expr> <silent> <C-]> execute(':LspDefinition') =~ "not supported" ? "\<C-]>" : ":echo<cr>"
```

エラーメッセージをマッチさせるという、かなりアドホックなやり方だが現時点ではちゃんと動く。
vim-lspの仕様が変われば動かなくなるかもしれない。

**2021/09/07追記:**

上の方法だと定義が新しいwindowで開かれてしまうので下の方法のほうが良いかもしれない。

```vim
  nnoremap <expr> <C-]> execute('LspPeekDefinition') =~ "not supported" ? "\<C-]>" : ":LspDefinition<cr>"
```

一度`LspPeekDefinition`を実行してみて大丈夫そうなら`LspDefinition`を実行する。
Raspberry Pi 4 でUbuntu Mateを動かす．

この記事は既に過去のもので，
今は普通にインスコできます。（2021/12/24）

# はじめに
 Raspberry Pi 4でもROS使いたいと思って，Ubuntu MATEをセットアップしようと思ったけど，そのままでは今のところできないらしいので，メモ．（2019/11/28）
 コミュニティの情報ではできるらしい・・・．

[参考ページ](https://ubuntu-mate.community/t/is-there-a-ubuntu-mate-for-raspberry-pi-4/19943)

 2019/12/25 追記
　どうやら、UnOfficialイメージというのがあるらしい！こっちの方が早いかも・・・。
[参考ページ](https://jamesachambers.com/raspberry-pi-4-ubuntu-server-desktop-18-04-3-image-unofficial/)

# 用意するもの
- Raspberry Pi 4
- Raspberry Pi 3(+でも何でも）
- microSD 2枚
- キーボード
- マウス
- パソコン

# Raspbian Busterをセットアップする
　空のSDカードにRaspberry Pi 4にRaspbian Busterをセットアップします．セットアップ方法は割愛．
1.　Raspian Buster の /boot パーティションを適当なところにコピー（バックアップ）します．
2.　Raspian Buster の /rootfs/lib/modules を適当なところにコピー（バックアップ）します．

#　Raspberry Pi3にUbuntu MATEをセットアップする
　Raspbian を入れたSDカードとは別のSDカードにRaspberry Pi 3にUbuntu MATE 18.04.2 (Bionic)をセットアップします．セットアップ方法は割愛．
1.　Ubuntu MATE の /boot パーティションをRaspian Buster のものと入れ替えます．
2.　Ubuntu MATE の /rootfs/lib/modules をRaspian Buster のものと入れ替えます．

# bootを編集する
  `/boot/cmdline.txt`
 を編集します。ほかのパソコン上で、
　　`ls -lF /dev /disk /by-partuuid`
 を実行し、Ubuntu MATEのPARTUUIDを取得し、その値を書き留め、
 cmdline.txtに反映します。
　`/boot/config.txt`
　の
　`dtoverlay = vc4-fkms-v3d`
  をコメントアウトします。

# Raspberry Pi 4 からUbntu Mateを起動する
　Raspberry Pi 3に色々セットアップしたmicroSDをRaspberry Pi 4に入れて起動する．

まだ試してないので，できたら画像UPします！

# 番外編！！（UnOfficialイメージを試す）
　そっこーできました・・・。(2019/12/25)
 Rasbian Busterを落として来て、SDに焼いて諸々アップデートします．

```
sudo apt-get update && sudo apt-get dist-upgrade -y
sudo rpi-update
```
[ここ](https://github.com/TheRemote/Ubuntu-Server-raspi4-unofficial/releases)から、serverを落としてきます．
新しいSDに焼いて、Raspberry pi 4にぶっさして起動します．

aptでアップデートすると調整したファイルが上書きされるから先にパッチ当ててねと書いてあるので、言われたとおりします．

```
wget https://raw.githubusercontent.com/TheRemote/Ubuntu-Server-raspi4-unofficial/master/Updater.sh
chmod +x Updater.sh
sudo ./Updater.sh 
```

あとは、ごにょごにょってやったら・・・・

 ```
sudo apt-get instsudo apt update && sudo apt dist-upgrade
sudo apt-get install mate-desktop-environment ubuntu-mate-desktop
```

できましたとさ！
![Screenshot at 2019-12-25 12-07-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531802/ec4a56b0-0e19-dbaa-37ff-6541d99083d7.png)
kubectl get ingress は通るのにkubectl describe ingress が通らなくなった話

# TL;DR
- 表題みたいなことが起こったら、kubectlのバージョンとkubernetesのバージョンを揃えよう。

# この記事は
ingress controllerの動きを確認するために、ingressの情報を確認しようとしたら、

```
$ kubectl describe ingress
Error from server (NotFound): the server could not find the requested resource
```

と出てきて困ったというお話

# 近況
久々の投稿です。最近は社内でもKubernetesやCI/CDを回してみたいという話が少しずつでてきており、その~~人身御供~~管理人として、Kuberentes周りの勉強をしています。

今回はingress controllerの動作確認で少しハマったので、記事として書いておきます。（同じような記事がQiitaには見当たらなかったので）

# やったこと
```
$ kubectl apply -f ingress.yaml
$ kubectl get ingress  -> OK
$ kubectl describe ingress ***
Error from server (NotFound): the server could not find the requested resource
```
- ingressは正常にできていて、リソースとしても登録されているのだけれど、describeできない
- namespace間違えているとか、そういう話でもない

# 結論
```
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.0", GitCommit:"2bd9643cee5b3b3a5ecbd3af49d09018f0773c77", GitTreeState:"clean", BuildDate:"2019-09-18T14:36:53Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.10", GitCommit:"37d169313237cb4ceb2cc4bef300f2ae3053c1a2", GitTreeState:"clean", BuildDate:"2019-08-19T10:44:49Z", GoVersion:"go1.11.13", Compiler:"gc", Platform:"linux/amd64"}
```

クライアントとサーバのバージョンが違うからおきている問題らしい。

https://discuss.kubernetes.io/t/kubectl-describe-ingress-not-working/7369

クライアントのバージョンを下げるか、サーバのバージョンを上げるかして対処しましょう、とのこと。
2019年秋、Dartに待望の「拡張関数」がやってきた

Kotlinを書いている人にとっては馴染みの深い「拡張関数」がDart 2.6で使えるようになりました。
（ちなみに、Dartでは "static extension methods" と呼ぶようです）
https://github.com/dart-lang/language/issues/41

## 拡張関数を書くには？

拡張関数の定義はトップレベルでのみ可能です。

```dart
extension StringExt on String {
  int toInt() {
    return int.parse(this);
  }
}
```

Dartの拡張関数は、extensionを定義したファイル全体に適用されます。

こんな書き方をする人は居ないとは思いますが、

```dart
main() {
  print("123".toInt() + "456".toInt());
}

extension StringExtension on String {
  int toInt() {
    return int.parse(this);
  }
}
```

このようにファイルの末尾に書いたとしても、同一のファイルに書いてあるなら

```
$ dart main.dart 
579
```

このように、拡張関数が適用されて実行されます。

Dartに限らず拡張関数というもの全般に言えることですが、「便利だから」といって拡張関数に依存した処理を書きまくるのは良い習慣ではありません。拡張関数は用途ごとにファイル別で定義し、使いたいファイルでのみimportして使うのがおそらく現実的な運用でしょう。

## dartx パッケージ

Androidアプリ開発では、KTXという拡張関数をつめこんだパッケージがありますね。
Dartだと2019年12月現在、公式（または準公式）の拡張関数パッケージはまだありません。

ただ、非公式で拡張関数をいろいろ詰め込んだ dartx というパッケージがあります。
https://pub.dev/packages/dartx

先にも書いたように、拡張関数に依存した処理を作り込みすぎるリスクがあるので、dartxパッケージを採用しようというケースは多くはないでしょう。しかしながら、拡張関数を書きたいとなったときに、dartxの書き方は大いに参考になります。

### 例: `?.let` in Dart

たとえば、Kotlinでよく使うイディオムで

```kotlin
selectedItem?.let{ showInImageView(it.imageUrl) }
```

のように nullableな変数に `?.let` を使うことがありますよね。
「これDartでも使いたいよね？」ってなったときにどう書けばいいでしょう？

Objectに `.let` のメソッドを生やせばいいわけですが、クロージャーに渡る引数はObject型だと困りますよね。

dartxのソースコードを見ると、こんな書き方をしている場所があります。

```github.com/leisim/dartx/lib/src/num.dart
extension NumX<T extends num> on T {
  ...
```

つまり、Objectを継承したT型に拡張関数を生やすとよさそう、ということがわかります。

```dart
extension ObjectExt<T extends Object> on T {
  ReturnType let<ReturnType>(ReturnType operation_for(T self)) {
    return operation_for(this);
  }
}

class MyValue {
  final int value;
  MyValue(this.value);
}

main() {
  final val1 = MyValue(3);
  final val2 = null;

  print(val1?.let((it) => it.value)); // => 3
  print(val2?.let((it) => it.value)); // => null
}
```


## まとめ

Dart2.6で拡張関数が使えるようになったことで、Kotlinのように可読性の高いメソッドを自由に生やすことが可能になりました。

現時点ではまだandroid-ktxのような拡張関数をつめこんだ公式パッケージはありませんが、今後そのような公式パッケージが出てきたら、Dart/Flutterはより書きやすい言語/フレームワークになっていくのではないでしょうか。
メルセンヌ・ツイスターは必要か？

#今回の話題
人類にMTはでかすぎる

#疑似乱数とは

コンピュータでは擬似乱数しか作ることは出来ません。
本の1ページ毎にランダムな数字が書いてあり、1ページずつ読み進める感じです。
もし同じ本を持っていて、同じページから進めれば、
全く同じ乱数となります。

レトロゲーのRTAだと状況再現と言うテクニックがあります。
電源ONからの入力を完全に同じようにすることで、同じページからすすめるようにして
敵の出現やダメージまで完全に過去と同じにする方法です。

#メルセンヌ・ツイスタとは

メルセンヌ・ツイスタ(以下MT)は今の所万能と言われる乱数です。
MTの乱数の本のページ数は2^19937-1、桁数にすると6000桁。
宇宙に存在する原子の数の予想が80桁ですから
宇宙が75個ほどあってようやく一周するほどの巨大な本です。

嘘です。
桁数同士の比較ですから、まだ5920桁個の宇宙が必要です。
でかすぎる！

#MTの大きさを考える

さて、大きすぎる数の話なので、2進数の桁数と言う意味でbitという表現を使います。
例えば、16bitなら(2進数で)16桁の数という意味です。

この巨大な本、読み始める場所が決まっています。
栞みたいなのが入っているページのみから始められ、大体32bit(42億)通りしかありません
この始めるページをシードと言います。

42億もあるんだから、十分じゃんと思うのですが、
同じページから始める人がいるかどうかだけなら6万回試行するだけでどこかは被ります

さらに、乱数を何個ぐらい使うかを考えると、
1秒に100個生成して1年使うと大体32bit程度の数になります。

つまり、入り口が32bitで使うページが32bitなので、
大体64bitのページを使うことになります。

これはMTの全体に対すると、1/(19873bit)です。
冒頭に上げたとおり宇宙の原子の数を持ってきても比較できないサイズなので、
人類が使っている部分なんてほんの一部…という表現も大きすぎるレベルです。。

つまり誰も見たこともないページがめちゃくちゃあるわけです。
そのために今何ページ目を読んでいるか、という情報が内部的にあるわけで、
具体的には19937bit分のデータ、ページ数を表すだけで約2.5kbのメモリを消費するわけです。

今のPCだと大した量ではないんですが、
使いもしないメモリを消費しているわけで無駄といえば無駄です。

#Xorshift128を使おう

そこで、何がおすすめなの？と言われれば、Xorshift128です。
周期が2^128-1、64bitの数として取り出しても64bitの周期があります。
メモリ消費も32byte、人類が使うであろうサイズにジャストフィットです。
ABC085B - Kagami Mochi

#問題
https://atcoder.jp/contests/abs/tasks/abc085_b
<img width="1109" alt="スクリーンショット 2019-11-28 12.01.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307024/83309a6b-8b62-b2d2-8846-d1d792e1fa63.png">

#1回目
##回答
重複削除のメソッドあるかな！って調べたらあったので一瞬で終わった。

```ruby
N = gets.to_i
D = N.times.map{gets.to_i}
puts D.uniq.size
```
##結果
<img width="1091" alt="スクリーンショット 2019-11-28 12.02.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307024/5bd8bed2-65c3-3829-3ac2-eea902d12c62.png">

#2回目
##回答
せっかくなので自力で重複削除してみた

```ruby
N = gets.to_i
D = N.times.map{gets.to_i}

for i in 0..(D.size-2)
  (D.size-1).step(i+1, -1) do |j|
    if D[j] == D[i]
      D.delete_at(j)
    end
  end
end

puts D.size
```
##結果
<img width="1088" alt="スクリーンショット 2019-11-28 12.04.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307024/5621cde7-75cf-6bc2-c08e-ea6a37c48446.png">
jQueryでシンプルなSPAのサンプルを作ってみた

#jQueryでシンプルなSPAを実装してみた

SPAを本格的に実装するのであればReactやVueを実装する方が良いのだろうけど、簡単なSPAであればjQueryでも実装は可能です。
完成イメージは下記のようになり、添付画像の黄色い箇所だけ切り替わります。
![spa2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10793/5aaf476b-c1df-de89-59e5-148866d0ac88.png)

##HTML
**spaクラス**がついているdivタグをjQueryで上書きします。

```html:HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>シンプルSPA</title>
<link rel="stylesheet" type="text/css" href="assets/reset.css">
<link rel="stylesheet" type="text/css" href="assets/spa.css">
</head>
<body>
<div class="wrapper">
  <header class="header">
    <nav>
      <a href="#page1">page1</a> | 
      <a href="#page2">page2</a> | 
      <a href="#page3">page3</a>
    </nav>
  </header>
  <main id="spa">
    <!-- ここが切り替わる -->
    <div class="spa"></div>
  </main>
  <footer class="footer">
    footer
  </footer>
</div>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="assets/spa.js"></script>
</body>
</html>
```

ハッシュに対応したファイル名のHTMLを用意しておきます。
例：ハッシュが**#page1**の場合、**page1.html**を読み込むようにします。
page2.html、page3.htmlも同様です。

```html:page1.html
<div class="page1 page">page1</div>
```


##CSS
説明は割愛。

```css:CSS
@charset "utf-8";

.wrapper {
    width: 600px;
    margin: auto;
}
.page {
    text-align: center;
    line-height: 300px;
    font-size: 24px;
    font-weight: bold;
}
.page1 {
    background: #f0e68c;
}
.page2 {
    background: #4169e1;
}
.page3 {
    background: #ffb6c1;
}
.header,
.footer {
    text-align: center;
    background: #ccc;
    padding: 20px;
}
```


##JavaScript

**hashchange**イベントを使用して、URLのハッシュが切り替わったら読み込むHTMLファイルを切り替えさせています。
イレギュラーなハッシュを叩かれた場合や初回アクセス時はinit()の方でpage1.htmlを読み込ませます。

参考:[MDN WindowEventHandlers.onhashchange](https://developer.mozilla.org/ja/docs/Web/API/WindowEventHandlers/onhashchange)

```javascript:JavaScript
;(function(){
'use strict';

//URLの一覧を配列で用意
const url_list = [
  'page1',
  'page2',
  'page3',
];

function init(){
  $.get('/page/page1.html').done((data) => {
      $('.spa').html(data);
  }).fail(() => {
      error();
  });
}

function hashchange(){
  const page = location.hash.slice(1);
  const in_url = $.inArray(page, url_list);
  if(in_url !== -1){
    $.get(`/page/${page}.html`).done((data) => {
      $('.spa').html(data);
    }).fail(() => {
      error();
    });
  }
}

function error(){
  $('.spa').html('読み込みエラー');
}

$(window).on("hashchange", () => {
  hashchange();
});

$(function(){
  init();
});

})();
```

##まとめ
URLの階層が深くなった場合や動的URLに対応していない最低限の実装ですがSPAのようなものが実装できました。
本格的に実装するのであれば大人しくVueなどのフレームワークを使用した方がバグも少なく実装できるかと思いますが、既存サイトの一部にちょっと実装する場合や個人サイト程度ならこのくらいでも十分かと思います。
PostgreSQLで和暦文字列をdateに変換する

探してみたら意外に見つからなかったので、関数を作ってみました。

```sql
CREATE OR REPLACE FUNCTION japanese_calendar_to_date(jp_date text) RETURNS date AS $$
DECLARE date_parts text[];
BEGIN
  date_parts := regexp_matches(jp_date, '(.{2})(\d{1,2})(.{1})(\d{1,2})(.{1})(\d{1,2})');
  CASE date_parts[1]
    WHEN '明治' THEN
      return make_date(1867 + date_parts[2]::int, date_parts[4]::int, date_parts[6]::int);
    WHEN '大正' THEN
      return make_date(1911 + date_parts[2]::int, date_parts[4]::int, date_parts[6]::int);
    WHEN '昭和' THEN
      return make_date(1925 + date_parts[2]::int, date_parts[4]::int, date_parts[6]::int);
    WHEN '平成' THEN
      return make_date(1988 + date_parts[2]::int, date_parts[4]::int, date_parts[6]::int);
    WHEN '令和' THEN
      return make_date(2018 + date_parts[2]::int, date_parts[4]::int, date_parts[6]::int);
    ELSE
      RAISE EXCEPTION '%は不正な和暦です。', jp_date;
  END CASE;
END
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
```

動作はバージョン11.5で確認しています。

```sql
# select japanese_calendar_to_date('昭和43年12月21日');
 japanese_calendar_to_date
---------------------------
 1968-12-21
(1 row)
```
Youtube　IFrame Player APIの使用方法について

先日とある案件で初めてYoutubeのIFrame Player APIを触ったのですが、
割と理解に時間がかかりましたので、備忘録的にまとめようと思います。
※IFrame Player APIはhtmlにyoutubeの動画を埋め込む為のApiです。

htmlへyoutube動画埋めこむ際、一番簡単な方法は、「静的にiframeタグを設置する」というものですが、
このやり方だと「ただ埋め込む」だけでして、
例えば再生ボタンでない任意のボタンから動画再生したり、ある程度ページの内容を読み進めた後で動画を自動再生したりといった小回りの効いた操作は出来ません。
→普通の再生ボタンじゃダサいから、独自の再生ボタンで再生できるようにしたいよ！とかニーズありそうですよね。

で、このApiを用いると、それが出来るということなんですね。
具体的には上記では静的にhtml上に設置していたiframeタグをjavascriptで動的に設置します。
この動的に生成したiframeタグはjavascript上では1つのオブジェクト扱いになっています。
そして「IFrame Player APIで定義されたメソッド」はjavascript上で当該オブジェクトに対し、例えば「再生してくれ」とクエリを送る事が出来まして、それによって「再生」を制御する事が出来ます。
つまりこのApiを用いると、javascriptで再生や停止、自動再生等の操作が制御出来るようになります。

※補足事項：静的に設置した後にjavascriptで操作すればいいのでは？と思ったらそれはノンノ
　　　　　クロスオリジン制限というブラウザのルールが邪魔して操作できません。

という事でもの凄く詰まったこちらのApiの解説が以下
[YouTubeIFrame Player API公式リファレンス]
(https://developers.google.com/youtube/iframe_api_reference?hl=ja)


```iframe.html
<body>
    <!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
    <!-- 以下のようにhtml上に設置したdivタグが、ページ読み込みの際、動的に生成されたiframeタグと入れ替わります。-->
    <div id="player"></div>
　　
    <script>
      // 2. This code loads the IFrame Player API code asynchronously.
      //以下ではscriptタグを生成・Youtube用のscriptファイルを読み込める様に編集、html上に設置ということをしています。
      var tag = document.createElement('script');

      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

      // 3. This function creates an <iframe> (and YouTube player)
      //これは②で作ったscriptタグの読み込みが終わったら自動で動くコードで、埋め込み用の動画を生成してくれます。
      //この時、どの動画をどんな画質で〜、、、という設定を書きます。そしてこの設定の通り後ほど埋め込み動画が生成されます。
      //で、これよく見ると新しく作ったオブジェクトが「player」という変数に入ってます。
      //    after the API code downloads.
      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
          height: '360',
          width: '640',
          videoId: 'M7lc1UVf-VE',
          events: {
            'onReady': onPlayerReady,
          }
        });
      }

      // 4. The API will call this function when the video player is ready.
      //これは動画プレイヤーが再生可能状態になったら、それがトリガーになって動くというfunctionです。
      //この中に書いてある「event.target.playVideo()」に注目
      //これを上で定義した「player」に対して、player.playVideo()という使い方をすると、playerで示された動画オブジェクトに対して再生や停止の命令を出す事が出来ます。
      //つまり、当初言っていたような事がしたい場合は独自の条件トリガーを作り、その中で「再生用メソッド等」を展開すればいいだけなのです。
      function onPlayerReady(event) {
        event.target.playVideo();
      }
    </script>
  </body>
</html>
```

ちなみにyoutube動画の再生ボタンダサいな、自分風にアレンジしたいなって時は
html上で動画の再生ボタン上にimgを重ねてください（z-index的な意味です）、
そして、あなたの作った再生ボタン（img）を押すと再生のクエリが飛ぶ様にjavascriptを書くのです。
Excellent!
curlのPOST -FをRetrofitで実装してみる


curlで実行しているスクリプトを Kotlin ＋ Retrofit で実装しようとしたら意外と時間がかかったので残しておく。

## 元のcurlスクリプト

```
curl \
  -F 'param=abc1234' \
  https://hoge.org/post
```

## Kotlin + Retrofitで実現したコード

Retrofit部分

```kotlin
interface ApiService {
    @Multipart
    @POST("post")
    suspend fun postData(
        @Part param: MultipartBody.Part
    ): Response<Output>
}
```

APIクライアント部分

```kotlin
class ApiClient {
    // apiService作成は省略
    suspend fun startPost(): Response<Output> {
        apiService = retrofit.create(ApiService::class.java)
        return apiService.postData(
            MultipartBody.Part.createFormData("param", "abc1234")
        )
    }
```

これでcurlと同じ動作をするはず。

まさかMultipart扱いになるとは知らず、MultipartBodyの作り方も知らなかったのでハマった。。。

## 参考

- Stack Overflow https://stackoverflow.com/questions/52926639/upload-file-with-json-object-using-retrofit

## オマケ

httpieで実行する場合

```
http -f POST \
  https://hoge.org \
  param1=abc1234
```
v-ifで同一のコンポーネントの表示を切り替えた時にcreatedが発火しない問題とその解決

たぶんすごい基礎中の基礎なんだろうけど、結構詰まったので。

## どういうことか

例えばこんなコンポーネントがあるとする。
渡したvalueを表示するだけのコンポーネント

```vuejs:TestComponent.vue
<template>
  <div>
    {{ value }}
  </div>
</template>

<script>
export default {
  props: {
    value: { type: String }
  },
  created() {
    console.log("created");
  }
};
</script>
```

こんな感じで利用する

```vuejs:Main.vue
<template>
  <div>
    <button @click="click">Click</button>
    <div>
      <testComponent value="a" v-if="testOpen"/>
      <testComponent value="b" v-else/>
    </div>
  </div>
</template>

<script>
import testCompoment from "TestComponent"

export default {
  data() {
    return {
      testOpen: false
    }
  },
  components: {
    testComponent
  },
  methods: {
    click() {
      this.testOpen = !this.testOpen;
    }
  }
};
</script>
```
クリックすると片方のコンポーネントが表示され、もう片方が消え、画面表示のa,bが切り替わる画面

## 予測していた動作

クリックをするたびにcreatedが呼ばれ、画面表示のa,bが切り替わる。

## 実際の動作

createdが最初しか呼ばれない。

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="7tsuno" data-slug-hash="wvvVJXj" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="wvvVJXj">
  <span>See the Pen <a href="https://codepen.io/7tsuno/pen/wvvVJXj">
  wvvVJXj</a> by 7tsuno (<a href="https://codepen.io/7tsuno">@7tsuno</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## なぜか

Vue は要素を可能な限り効率的に描画しようとする。
ここでは二つの`testComponent`要素が同じものだとされ、初期化処理が走らないようになっている。

## 解決策

コンポーネントに別々のkeyをつける。
keyをつけることでvueに"この2つの要素は完全に別個のもので、再利用しないでください"と伝えることが出来る。

```vuejs:Main.vue
<template>
  <div>
    <button @click="click">Click</button>
    <div>
      <testComponent value="a" v-if="testOpen" key="testA"/>
      <testComponent value="b" v-else key="testB"/>
    </div>
  </div>
</template>

<script>
import testCompoment from "TestComponent"

export default {
  data() {
    return {
      testOpen: false
    }
  },
  components: {
    testComponent
  },
  methods: {
    click() {
      this.testOpen = !this.testOpen;
    }
  }
};
</script>
```

## 参考
[公式 : 条件付きレンダリング](https://jp.vuejs.org/v2/guide/conditional.html)
CentOS上の文字化けしたファイルを削除する

# はじめに
CentOSにログインした時、`ls`で`/home`ディレクトリにゴミがあることに気づいた。
名前が`??x?・4a???`とよくわからないことになっている。
おそらくWindows OSで作成したデータファイルをteraterm や winscpでアップロードしてしまったのだろうと思われる。

## rm -f ??x?・4a???で消せない
そんなファイルはないと警告されてしまう。

## rm -rf * で消せない
rmで削除したいが`/home`には様々なファイルが置いてあるので `rm -rf *` が使えない。

## さて困った
qiitaなどで調べてみたが、意外とこのようなケースへの対処方法がない。

# 解決策

## 基本的なデータの管理について思い出してみる
Linuxはデータファイルが実際に保管される領域とinode(index node)領域というものにデータファイルの情報を保存/管理する。

### inodeとは
ファイルやディレクトリの所有者、更新日時、ファイルサイズなどを管理しているデータをinodeという。
inodeにはinode番号と呼ばれる管理番号が割り当てられており、これを指定することでデータそのものを操作することが可能になる。

## 解決手順

### 1.ファイルのinode番号を調べる
```console:inode番号を調べる
ls -li
合計 xxx
1234567890    test?_.txt
-----------------------------
inode number  file name
```

### 2.inode番号でファイルを検索し、削除
```console:inode番号で検索し、削除
find . -inum 1234567890 -exec rm -rf {} \;
```

## 補足

-execオプションを使うと、findで検索されたファイルに対して他の処理を行わせることができる。
``` -exec {} \; ``` はfindで検索されたファイルを個別に、ファイルの数だけ処理を行う。
``` -exec {} + ``` はfindで検索されたファイルをまとめて 1つの処理を行う。




# 参考
- https://qiita.com/nekkoneko/items/91bcdd687357ea5b58eb
- https://qiita.com/ironsand/items/0aed2ffca295706cab69
「汚いコード、綺麗なコードって何？」リファクタリングを考えてみる

# はじめに
## 汚いとか綺麗とか何？
よく「汚いコード」とか「綺麗なコード」あるいは「美しいコード」なんていう言葉が使われることがある。
汚い、綺麗、美しい、という言葉は元々個人の価値観に基づく感覚的な言葉だ。
処理の羅列であるソースコードに対して感覚的な言葉を使うのは、はたから見ると奇異な行為にうつるだろう。
一方で、プログラマは真剣に綺麗なコードを書くことに努力をしている。彼らは一体何をやっているのか、**「リファクタリング」**という行為から少し考えてみることにする。

## 想定読者
* リファクタリングって何？って人
* 大事なのはわかるけど進捗を産まないリファクタリングは現実的にできないよって人

# リファクタリングって何？
リファクタリングとは一言で言えば「お前の書いたその汚いコード、とりあえず動いているけどもっと綺麗なコードにしよう」ということである。
もう少しかっちりいうと**「コードの振る舞いを変えずに内部構造を変化させること」**だ。

## なぜやるの？
僕が思っている**リファクタリングをやらなきゃいけない**と思う理由は次の三つだ。

１、　変更に強くなりたい
２、　生産性を向上させたい
３、　楽しいコードにしたい！

それぞれ詳しく説明していく。

### 変更に強くなりたいってどういうこと？
「変更」「修正」「拡張」、システム開発をしていれば必ずよく聞く言葉になる。しかしエンジニアやプログラマの中にはこれらを親の仇のように憎む人もいる。僕たちは本当はこれらを憎みたくない。
**変更は歓迎すべきだ**。よりよりアイディアが浮かんだってことだから。
**修正も歓迎すべきだ**。不具合が見つかったんだから。
**拡張は大好きだ**。システムがもっといいものになるんだから。
変更も、修正も、拡張も新しい知見であり、いつだってwelcomeであるべきだ。

だから僕たちは**常に自分のソースコードが変更を受け付けられる**ようにするべきだ。
変更があったら、**即座にどの箇所を変更すればいいか見当がつく**。
拡張があったら、**どこのクラスに何を拡張すればいいかが見当がつく**。
そして僕たちはできるだけ、この変更によって**他の機能への影響を最小限にしたい**。

### 生産性を向上したいってどういうこと？
苛立ちを覚えるコードってないだろうか。
やたらと長い関数とか、ほぼコピペのコードが別メソッドに並べられているとか(しかもどこに差分があるのか一見わからない)、１箇所修正したら３箇所修正しないといけないコードとか。
僕たちがこれらにイライラするのは生産性を下げさせられているからだ。
長い関数は読むのに時間を食い、脳のリソースを奪い、結果得られる知識はわずかだったりする。
ほぼコピペのコードが散乱していて１箇所修正するのに３箇所が修正しなければならない日にはストレスを抱える。
僕たちはコードによって生産性を下げられることを憎む。
だからリファクタリングをして生産性をあげたい。

### 楽しいコードにしたいってどういうこと？
極度に洗練されたプログラムはポエムになる、という言葉がある。
もはや自然言語で記述されたかのように美しいプログラムがそこにあったとして、君はそこに手を加えることに興奮しないだろうか。
一方で、腐臭漂うスパゲッティコードがあって、今日から君はこれの改修を３ヶ月やってくれなんて言われた日には、楽しい気持ちなんて一切ないんじゃないだろうか。
どうせやるなら楽しくやりたい。楽しいコードはリファクタリングから生まれる。

# 具体的にリファクタリングって何をするの？
どのようにリファクタリングをすると、変更に強い/生産性の高い/楽しいコードになるのか。思いつく限りに書き殴ってみた。

* ロジックの流れが意味不明　→ ロジックの流れが明快
* 何が何に影響しているかわからない　→ 依存関係が明確
* 型がない → 型がある
* 膨大なコード　→ 小さく区切ったコード
* 何を書いてたか忘れる → 読めば意味のわかるコード
* 名前が体を表していない　→ 名前が体を表している
* １箇所の修正するのに何箇所も変更しなければいけない　→ １箇所の修正は１箇所でいい
* コピペがある(コピペ元とちょっと違うのかもしれない、という恐怖)　→ コピペがない。メソッドを使いまわしている。
* 全てが１つのクラスやメソッドに書かれていて、一から読まないと 変更箇所がわからない　→ 責務を意識したアーキテクチャ
* 頭の痛い問題(バグ)が放置されている　→ 割れ窓を放置しない
* 再利用するメソッドが使いにくい　→ 引数と出力が明快
* テストができない　→ 一目でわかりやすいテストがある
* 同じコードを何度も書いている　→ 共通化、継承
* 似たようなことを何回もしている　→　共通化、継承
* どこに何が書かれているかが明確　→ 責務が明快
* ロジックが複雑で何をしているか分からない → シンプルなロジック
* 長くて読むのがだるい → 短い
* ifばっかり → 条件分岐を少なくする
* 一眼で何をしているか分からない → 短いメソッド、適切な命名
* MVCが分けられていない　→　MVC
* その変数に何が入っているかが想像できない → わかりやすい命名、ドメインモデルの切り出しや統合

これらは抽象化すると３つにまとめられる。
１、可読性 : 読みやすい！
２、高凝縮 : まとまってる！
３、疎結合 : 緩くつながってる！

他にも次のような要素が必要なのが分かる。
* 再利用可能性
* オブジェクト指向(+DDD)
* テスト完備/テスト可能

これらの意識してコードをリファクタリングすることで「変更に強い/生産性の高い/楽しい」コードに近づくことができると思う。

# まとめ
* リファクタリングについて説明した
* リファクタリングの動機について説明した
* どんな具体例があるかを列挙した

今回は取り扱わなかったが、リファクタリングは方法論も研究されており、「こういう状態にはこういう手順でリファクタリングをする」のような手順書がまとまっていたりする。興味を持った人はコードスメル、リファクタリングという単語で動画や書籍にあたってみると良いと思います。他にもIDEや高機能エディタにはリネームやメソッド切り出しなどリファクタリングを支援する機能が備わっているので、そちらも触ってみてください。

それでは！
Macで音が鳴らなくなった時の対処法 (再起動不要)

## 問題
Macで突然音が鳴らなくなる (内蔵スピーカー，イヤホン問わず)。
再起動すれば直るがわざわざ再起動したくない。

## 対処法
方針：再起動せずにMacのオーディオドライバーをリロードする。

`Terminal.app`を起動し，以下のコマンドを入力して実行 (エンターキーを押す)する。

```sh
ps aux | grep 'coreaudio[d]' | awk '{print $2}' | xargs sudo kill
```

Macの管理パスワードが求められるので，入力してエンターを押す。


音が鳴るようになった♪
Amazon TranscribeにVirtual Insanityを文字起こしさせてみた

#TL;DR
曲の歌詞を抽出するのはダメそう。

#きっかけ
翻訳制度が高いというなら歌詞付き音楽もいけるんじゃないか？
とかふと思ったのでやってみた感

#選曲
とりあえず洋楽で好きなjamiroquaiのVirtualInsanityをチョイス。

#やってみた
MP3ファイルを用意する。
あとはAmazon Transcribeの説明の通りに翻訳にかける。

#言語:Englishで翻訳した結果
<img width="991" alt="Screen Shot 2019-11-28 at 11.23.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538639/51880efb-a85f-1940-2883-77e3fb2d080a.png">

#操作ミスで言語:日本語で翻訳させちゃった結果・・・
<img width="987" alt="Screen Shot 2019-11-28 at 11.08.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538639/6cfbdcba-14fa-cbc4-a140-9548572426c0.png">

#感想
なんか惜しいっておもうところもあるけど大抵ダメ。
音楽の歌詞翻訳は難しい・・・
※当初翻訳サービスだと勘違いしていたので文字起こしに改めて更新しています。

#VirtualInsanityの歌詞サイト
https://www.study-lyrics.com/entry/virtual-insanity
Redmineでの運用管理

フォトクリエイト カスタマーバリュー開発部 デザイングループの@harashima です。
この記事は フォトクリエイト Advent Calendar 2019 の 5日目です。

# はじめに

フォトクリエイトではイベントや学校行事で撮影した写真を、インターネットで写真販売しており、私の仕事は主にイベント参加者に配布するチラシや、イベントプログラムに載せる広告のデザインをしています。

デザイングループではタスク管理ツールとしてRedmineを導入していますので、使ってみた感想をまとめました。

[Redmineとは？](http://redmine.jp/overview/)

# メールでの依頼対応の課題

業務で紙媒体のデザイン対応をしていますが、主に営業サイドから依頼をもらい、進行を進めます。
以前はメールのみで依頼を受けていて、何をどのくらいこなしたのかさっぱりわからず、案件数の集計などは過去のメールを検索したりしてかなり大変でした。。。

そこでRedmineだと **「案件の集計もできる！」「ガントチャートでスケジュールの見える化もできる！」** これは良いと聞いてやってみることに。

 案件の集計
![レポート.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390042/c9329ece-c270-4dcf-9e4d-1d4c3fea02cd.jpeg)



ガントチャート
![ガントチャート.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390042/b81422fc-a7c4-bce7-2d60-1fa90b5983fb.jpeg)



# 導入したメール+Redmine運用時代

その当時、制作・営業サイドにはRedmineの馴染みがなく、メールがRedmineに自動登録される仕組みをエンジニアが開発していました。
メールで依頼することは従来のままそれは維持しつつ、Redmineで裏側処理することで管理できるようになりました。
依頼側はメールだけをみていて、依頼を受けた開発・デザイン側はRedmineのチケットとして進行管理していくスタイルです。

Redmineでは仕事（タスク）をチケットで管理しています。
チケットを作成してもらい、それに返信していくことになりますが、チケットは完了にしないと消えない・・・

 **営業側から制作依頼（Gmail）
　　　　　↓
Redmineでチケット化
　　　　　↓
制作後、営業側に確認連絡（Gmail）
　　　　　↓
営業側から「制作完了」の連絡（Gmail）
　　　　　↓
Redmineでチケット完了** 

営業側から「制作完了」の連絡（Gmail） をもらわないと、依頼を完了にできないことに今更ながら気が付きました。

# 結果どうなったか？

 **制作完了の連絡がいつ来るのかはわからない。
　　　　　↓
そして次の依頼はどんどん来る。
　　　　　↓
実際の作業は完了しているのに、完了連絡がこないので未完了となっているチケットが溜まっていく。
　　　　　↓
月一とかでまとめて消すようになる。
　　　　　↓
チケットを消すのがめんどくさくなる。
　　　　　↓
Redmineを見なくなる。
　　　　　↓
そしてGmailだけの運用に戻る。** 

Redmineでの運用をやめてGmailだけの運用に戻って暫くしたら、Googleフォームで集計できることを聞き、Googleフォームから依頼をしてもらうようにすることで、案件数の把握は出来るようになりました。

ただ、チケット管理の煩雑さはなくなったものの、依然メールでの運用課題は残ったままでした。

 **・メールは案件進捗が把握しづらい。
・誰がボールをもっているのか不明になり進行が止まるケースがある。
・案件の履歴が残しにくい。
・メールの宛先に入っていないメンバーは、案件把握ができない。** 

# 現在、オンリーRedmine運用開始

その後、紙媒体の制作グループとWEBデザイングループが一緒になりました。
紙媒体とWEBのデザイン連動の動きを強めるためにも、様々なメンバーで案件がみられる状態にする必要性も高まり、デザイングループでもRedmineを使っていたので、そのままRedmineでの運用となります。

以前使ったときは失敗したので、要は自分たちではなく、依頼者（営業側）にチケットを完了にしてもらえばいいってことでRedmine再びとなりました。

# うまくいったところ

- 営業側にもRedmineを導入してもうことでチケット完了の問題は解決でき、Redmineから返信すればよく、案件数の集計などができるようになりました。
- なかなかチケットを完了にしてくれない人もいるが、Slackを併用して進捗確認をしてもらっています。

[Slackとは？](https://slack.com/intl/ja-jp/help/articles/115004071768)

# 課題になっているところ

緊急の依頼が多いので、営業側に前もって依頼をチケット化してもらい、ガントチャートとして見える化することで、チーム内MTGで状況確認をおこなうことができるように営業側と調整してみましたが、営業側の繁忙期のためギリギリになってからしか動けず。。。今後の課題です。

# 最後に

いろいろ運用してみて都度課題はあるが、 初めてやるときは失敗することもあります。
何か問題が起きたら自分たちなりに使い方などを考えていけばいいかと。

フォトクリエイトでは自ら考えたり、問題解決に向けて前向きな気持ちで仕事ができる方を採用しております。
少しでもご興味をお持ちいただけたようでしたら、人事部門の担当者 [twitter: @tetsunosuke](https://twitter.com/tetsunosuke) へお知らせください。

明日の記事は @tetsunosukeito さんによる [LAPRAS/teratail でレベルアップ！採用担当は候補者に負けないスキルを維持せよ](https://qiita.com/tetsunosukeito/items/940a68ad7e00770b7b45) です。
MySQLのデータをダンプしてコンテナ上のMySQLにデータを取り込む方法

似たようなことを何回も調べてる気がするのでめも :pencil: 

### ダンプする

```
mysqldump -u [ユーザー名] -p -h [ホスト名] [DB名] >> dump.sql
```

### Dockerコンテナ上のmysqlにダンプファイルを読み込む

```
$ docker cp dump.sql sample_mysqldb_1:/tmp/
$ docker exec -it sample_mysqldb_1 sh
# mysql -u[ユーザー名] -p[パスワード] -h[ホスト名] [DB名] < /tmp/dump.sql
```
10AM


mvcモデルのそれぞれの役割について

ルーティング＝振り分けるやつ
コントローラー＝アクションを起こすやつ
モデル＝データベースの通り道
ビュー＝直接見える部分

モデルを介して得たデータを、
ビューで表示できるのは、
アコーディングレコード君の活躍のおかげ。
コントローラーの変数に入れておけば、
ビューファイルで表示してくれる。

だから、データの情報が
表示されているってこと！！！
Laravel 6.6 LTS セットアップ


１：インストール

```
% composer create-project --prefer-dist laravel/laravel spider
```

２：簡易サーバー起動、動作チェック

```
% cd spider
% php artisan serve
```

以下が表示されればオッケ。
![スクリーンショット 2019-11-28 9.37.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33953/459586fe-0899-245c-c821-b830d3f8d598.png)



３：SQLite 設定

```:.env編集
DB_CONNECTION=sqlite
#DB_DATABASE=laravel
```

```:データベース作成
% touch database/database.sqlite
```

```:マイグレーション
% php artisan migrate    
```

４：Laravel 初期設定

```:/config/app.php
    'timezone' => 'Asia/Tokyo',
    'locale' => 'ja',
```

５：git管理する。

```
% git init
```
Pipenv使い方まとめ（自分用）

#はじめに
なんか他の方の記事を見ると詳細がわかったりしていいのですが開発する際にどうすればなにができるかっていうのが一つの記事でわかるものが少ないなって思いました．
みんながわかるようにっていうわけではなく自分がこれがあれば十分かなっていうのをまとめてみました．

#環境
- Windows 10 Home
- Windows Subsystem for Linux
- Python 3.6.4

#導入
[Pipenvを導入するまで](https://qiita.com/mashita1023/items/10239f5621ef2fc8acb9)
これ見てください

#使い方

## 初期化
Python3系なら

```bash
$ pipenv --python 3
```

## パッケージのインストール

```bash
$ pipenv install django
$ pipenv install numpy==1.17.4  # バージョンを指定する 
```

```:Pipfile
[packages]
django = "*"
numpy = "1.17.4"
```

開発用パッケージのインストールをするときは

```bash
$ pipenv install --dev flake8
```

```:Pipfile
[dev-packages]
flake8 = "*"
```

## 環境の構築
bundle installをするぞ～

```bash
$ pipenv install
$ pipenv install --dev # 開発用
```

Pipfile.lockから環境を再現したいときは

```bash
$ pipenv sync
```

インストールされているパッケージのバージョンアップ

```bash
$ pipenv update
```



##スクリプト
自分で`Pipfile`を編集して下のように書く．

```:Pipfile
[scripts]
main = "python main.py"
```
scriptsの実行

```bash
$ pipenv run main
```

また，仮想環境に入らずに個別に実行することができる

```bash

$ pipenv run python main.py
```

## 仮想環境
`.venv`をプロジェクトディレクトリ配下に作りたくないですか？？？
ということで

```:.bashrc
export PIPENV_VENV_IN_PROJECT=true
```

あとは使い方

```bash
$ pipenv shell   # 仮想環境
$ exit           # 抜ける
$ pipenv --venv  # 仮想環境のパス
$ pipenv --rm    # 仮想環境の削除
```

#まとめ
もっとちゃんと書いてる人いると思うけど自分で使うやつをとりあえずまとめました．

正直Python始めたばっかなので調べてまとめただけですし割と適当に書いてるのでこんな感じくらいに思っておいてください．

真面目に言うとちゃんとわかってる人のやつをちゃんと読んで理解したほうがいいですよ（特大ブーメラン）


ちょっと話それますが早くWSL2の安定版でないですかね～
docker使いて～～
Prometheus client_python tips - 複数の対象のmetricsを作る方法

# Overview

Prometheusには様々なexporterがあるが、APIを作ったりして
独自のmetricsを生成したい場合は、client libraryを使うようになっている。

- https://prometheus.io/docs/instrumenting/clientlibs/

従ってpythonにもそれ用のlibraryがあり、それが[こちら](https://github.com/prometheus/client_python)。
基本的にはGaugeを使って作れば良いのでだいたい以下のようなコードになる。

```python
#!/usr/bin/env python3
# import os
import sys
import re
from logging import getLogger
from prometheus_client import generate_latest
from prometheus_client import Gauge
from prometheus_client import REGISTRY
logger = getLogger(__name__)

class MetricsGenetator(object):

    def __init__(self):
        self.g = Gauge('my_inprogress_requests', 'Description of gauge')

    def run(self):
        self.g.inc()      # Increment by 1
        self.g.dec(10)    # Decrement by given value
        self.g.set(4.2)   # Set to a given value
        metrics = generate_latest(self.registry)
        return metrics



def main():
    app = MetricsGenerator()
    metrics = app.run()
    print(metrics)

if __name__ == '__main__':
    main()
```

これを都度、実行しているならば良いが、
例えばself.g.setに渡されている4.2という値が以下のような使い方をしていると困ったことが起きる。

- snmpget cpu_usage from <ip> のように対象が分かれるだけでコード自体は使い回されている場合
- 上記のようなコードがモジュール化されており常時プロセスとしては起動し続けている
- 値が取得できたときは返すが、取得できない時にダミー値を入れない

具体的にはどのようなことが起きるかというと

1. Aサーバから CPU usageを得る - 50 とする
2. Bサーバから CPU usageを得ようとして失敗する (None)
   * この時に、Noneが返ってきた場合に self.g.set()自体をスキップする
3. するとBサーバの CPU usage が 50 として返る

つまり前の値がそのまま流用されてしまうのである。
上記のような構造の場合は CPU usage = None 等を明示的に入れれば良いのだが
Aサーバ、BサーバのCPU数が流動的で、 for cpu in cpu_num: というような形で
ラベルを動的に生成をしている場合はダミー値を入れることができない。

## どうするか

2019年9月に新しくcommitされた CollectorRegistryのtarget_infoを使う事で
Registryを分けることができるようになる。
こんな感じ。

  
```python
#!/usr/bin/env python3
# import os
import sys
import re
import click
from logging import getLogger
from prometheus_client import generate_latest
from prometheus_client import Gauge
# from prometheus_client import REGISTRY
from prometheus_client.core import CollectorRegistry
logger = getLogger(__name__)

class MetricsGenetator(object):

    def __init__(self, target):
        registry = CollectorRegistry(target_info={"target": server_name})
        self.g = Gauge(
                     'my_inprogress_requests',
                     'Description of gauge',
                     registry=registry)

    def run(self):
        self.g.inc()      # Increment by 1
        self.g.dec(10)    # Decrement by given value
        self.g.set(4.2)   # Set to a given value
        metrics = generate_latest(self.registry)
        return metrics


@click.command()
@click.option('-h', '--hostname', required=True, type=str,
              default="localhost", help="get metrics from hostname. (default: localhost)")
def cli():
    app = MetricsGenerator(target)
    metrics = app.run()
    print(metrics)


def main():
    cli()


if __name__ == '__main__':
    main()
```
A-FRAME: 物理演算でボーリングっぽい動きを実現してみる７（ピンのモデル）

A-Frameをつかって物理演算ができるようにしてみます。
前回、ピンの物理的な形状を設定し、sphereとcylinderにおいて中心の不一致が見られました。

今回は利用するモデルによって差があるのかどうかを確認してみます。

### 例１）pin1
以前から利用しているモデルです。
![pin1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/7b55c0a4-8af7-bb35-708d-44c4fc54226e.gif)[demo](https://k38.github.io/aframe/physics-system/pin_model_1.html)
中心がずれてます。

### 例２）pin2
これまでとは別のモデルを試してみます。
![pin2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/5f7795dd-49c3-134f-98ab-9dd18274a025.gif)[demo](https://k38.github.io/aframe/physics-system/pin_model_2.html)
これもずれてます。

### 例３）pin3
さらに別のモデルです。
![pin3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/632d9a4a-7023-4713-19b7-086bfe9f0cea.gif)[demo](https://k38.github.io/aframe/physics-system/pin_model_3.html)
ちょっと違うずれ方ですが、ずれてます。

### 例４）pin4
さらに別。
![pin4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/767d42ff-def3-f730-4e86-a9c5064d3182.gif)[demo](https://k38.github.io/aframe/physics-system/pin_model_4.html)
ずれてる。

## まとめ
４つのボーリングのピンのモデルを試してみました。
モデルにより物理的な形状とレンダリングされる形状の中心のずれ方は違いましたが、一致もしませんでした。

blender等を用いてモデル側で調整ができるかもしれないですが、
どうせモデルによってズレるのであればA-Frame側で調整する方が良いような気もします。

次はA-Frame側で物理的な形状とレンダリングされる形状の中心を揃えてみたいと思います。

## 参考
今回利用させていただいたピンのモデルです。
- [pin1 - Bowling Pin, MSerdar Tekin](https://sketchfab.com/3d-models/bowling-pin-028ccb945012460aa9056ffda5b53e20)
- [pin2 - Bowling pin, Vas3D](https://sketchfab.com/3d-models/bowling-pin-513488fe22524a9d93c550cf982ed2e9)
- [pin3 - Bowling Pin, Hassan Sheikh](https://sketchfab.com/3d-models/bowling-pin-88655efb4b834452a28173e5d1b7763b)
- [pin4 - Pin, Adam.Klimo](https://sketchfab.com/3d-models/pin-e6f79488d254497a8099ae400af131ae)
Sourcetrail(win版)をJavaコードで使ってみる

#Sourcetrail(win版)をJavaコードで使ってみる

https://qiita.com/cloudsnow/items/7d7f3b859186667379ef

macOS版を参考にしてやってみました。

最終確認してCreateを選択までしたのですが動作しなかったのでどこがおかしいか調べてみると
)

[![Screenshot from Gyazo](https://gyazo.com/cf8d09db0ae883cfa78194d9f5ba4092/raw)](https://gyazo.com/cf8d09db0ae883cfa78194d9f5ba4092)
Editにあるpreferencesを開くとjava Path(64Bit)のアドレスパスが違ったので
Example：C:/Program Files/Java/jdk1.8.0_102/jre/bin/server/jvm.dll　に変更したら動作しました
[![Screenshot from Gyazo](https://gyazo.com/cea32da054eb1d1f3af28ead73c76727/raw)](https://gyazo.com/cea32da054eb1d1f3af28ead73c76727)
各ブラウザを判定してCSSを適用する方法

ブラウザ毎にCSSのバグがあって、それらの対応方法を探していたら以下の記事が参考になりました。

ネタ元
https://www.kimagure-weblog.net/css/ie001/#IE


大きく問題になるのはIE10-11でしょうが。早く消えろって感じです。
以下がIE対応方法。メディアクエリと同様の書き方です。
ネタ元にはFireFox,Chrome,Safariへの方法も書いてあります。

```css
@media all and (-ms-high-contrast: none){
  /*ここにCSSを書く*/
}
```
Codeforces Round #578 A. Hotelier

Codeforces Round #578 A. Hotelier
===

https://codeforces.com/contest/1200/problem/A

珍しく極めて簡単な問題。言い換えというほどではないが以下のような問題。
- ホテルに部屋が直線的に10部屋ある
- L: あいている一番左端の部屋に人を入れる
- R: あいている一番右端の部屋に人を入れる
- 番号: その部屋の人をチェックアウトさせる

高々n < 10^5の試行回数なのでシミュレーションしてしまえばよい。
O(10 * 10^5) = O(10^6)におさまる。

試行回数が非常に多い場合は空いている左端と右端をプライオリティキューなど使って工夫すると良さそうか。

```Python
n = int(input())
s = input()
room = [0] * 10
for i in range(len(s)):
    if s[i] == "L":
        for j in range(10):
            if room[j] == 0:
                room[j] = 1
                break
    elif s[i] == "R":
        for j in range(9,-1,-1):
            if room[j] == 0:
                room[j] = 1
                break
    else:
        room[int(s[i])] = 0
res = map(str, room)
res = list(res)
print("".join(res))
```

https://codeforces.com/contest/1200/submission/58590944
バリデーションを設定しよう。 ❏Rails❏

#バリデーションとは
フォームから送られてきた値が正常な値であるか検証する機能です。
よく使う２つを見ていきましょう。

①`presence: true`
②`unique: true`

<br>
#①値が空ではないか
フォームが空の場合は保存できなくします。

モデルに
`validates :カラム名, presence: true`を追記

例）tweetsテーブルのtextカラムに入る値を入力必須にしたい↓

```ruby:tweet.rb
class Tweet < ApplicationRecord
  validates :text, presence: true
end
```
textを入力しなかった場合にエラーとなり、保存できません。
<br>
#②値が他と被ってないか
他のレコードに既に同じ値がある場合は保存できなくします。
唯一無二の値のことを、一意な値と言います。

モデルに、
`validates :カラム名, unique: true`を追記

例）tweetsテーブルのtextカラムに入る値を一意にしたい↓

```ruby:tweet.rb
class Tweet < ApplicationRecord
  validates :text, unique: true
end
```

まったく同じテキストは保存できなくなりました。
`おはよう`とツイートできるのは１人だけ。早いもの勝ちとなります。
<br>
#エラーメッセージを表示させる

保存できないことを知らせてあげましょう。

```ruby:new.html.haml
- if @tweet.errors.any?
  %div
    %h2= "#{@tweet.errors.full_messages.count}件のエラーが発生しました。"
    %ul
      - @tweet.errors.full_messages.each do |message|
        %li= message
```

<br>
もしエラーメッセージが表示されない時は以下を疑いましょう。
・`form_with`の`local: true`が抜けてる。

```ruby:new.html.haml
= form_with model: tweet do |form| #誤り

= form_with model: tweet, local: true do |form| #正しい
```

僕はエラーメッセージが表示されなくて、２日間悩みました。

<br>
ではまた！
ルーティングのネスト ❏Rails❏

#いつ使うの
ツイートにコメントできるようにする時を考えます。
コメントのparamsの中に、どのツイートに対してのコメントなのかわかるようにそのツイートのid情報を含めます。

#やり方→ルーティングをネストさせる
実際に例を見たほうが早いです。

```ruby:routes.rb
Rails.application.routes.draw do
  resources :tweets do
    resources :comments, only: [:create, :destroy]
  end
end
```

このように記述し、
`rails routes`を叩いてルーティングを確認すると、、、
<br>

```:ターミナル
tweet_comments POST   /tweets/:tweet_id/comments(.:format)                                                     comments#create
 tweet_comment DELETE /tweets/:tweet_id/comments/:id(.:format)                                                 comments#destroy
```

真ん中にtweet_idが含まれてるぅ！！！
これでどのツイートに対してのコメントかわかるようになったね！！！

#【注意】form_withでインスタンスを2つ渡す

```ruby:haml
- form_with model: [@tweet, @comment] do |form|
  = form.text_field :text
  = form.submit
```
@tweetを渡すことでtweet_idを渡せるようになります。
お忘れなく。

<br>
ではまた！