OmniAICreator/Qiita-1.07M huggingface dataset:
Node.jsを勉強する⑦ - npmモジュールyargsを使った引数のオプションの処理

##はじめに
前回は[コマンドライン引数の受け取り](https://qiita.com/arata0520/items/9acf3d56f78d5a056ce1)についてまとめました。
今回はnpmモジュールyargsを使ったオプションの変換を記事にします。yargsを利用することで、ターミナルでコマンドを実行する際に、引数のオプションを入れた場合、それを引数として変換できます。

##教材
Udemy
The Complete Node.js Developer Course (3rd Edition)
https://www.udemy.com/course/the-complete-nodejs-developer-course-2/

##解決したいこと
processを用いることで、コマンドライン引数の受け取りはできましたが、引数のオプションを入力しても上手く変換されません。
まずは、こちらから見ていきましょう。app.jsというファイルを作成し、コマンドライン引数がconsoleに表示されるようにコードを書きます。

```javascript:app.js
console.log(process.argv[2])
```
それでは、こちらのファイルを引数addBookとオプション title="Kokoro"を入れて実行してみます。

```terminaml:ターミナル
node app.js addBook --title="Kokoro"
```

結果は以下のようになり、オプションである"Kokoro"をうまく読み込んでくれません。（--title="Kokoro"全体が２つめの引数として認識されている）

```terminaml:ターミナル
addBook
```

こちらをnpmのyargsを使って解決します。
##yargsのインストール

ターミナルにてインストールのコマンドを実行します。npm公式のyargsのページは[こちら](https://www.npmjs.com/package/yargs)

``` terminal:ターミナル
//npmを開始していない場合はこちらも実行
 npm init
 npm i yargs
```
次に、requireを用いて、yargsをapp.js内で使えるようにします。コマンドライン引数をyargs.argvを用いて表示してみます。

```javascript:app.js
const yargs = require('yargs')

console.log(yargs.argv);
```
同じ引数を入れて、ファイルを実行すると、以下のように出力されます。

```terminaml:ターミナル
node app.js addBook --title="Kokoro"

{ _: [ 'addBook' ], title: 'Kokoro', '$0': 'app.js' }
```

引数 addBookに対して、titleをプロパティとして認識し、Kokoroを値として出力できています。
ちなみに、'$0'は実行されたファイル名を指します。app.jsが実行されていることがわかります。
#コマンドをカスタマイズする

次に、yargsのコマンドをカスタマイズしてみます。

```javascript:app.js
const yargs = require('yargs')

// 以下のコマンドを作作成します。
yargs.command({
     //コマンド名
     command: 'addBook',
     //以下で、コメントを残しておきます。
     describe: 'Add a new Book',
     //以下の処理を実行します
     handler: function(){
     console.log("Add a new book!");
     }
})

//yargsを実行する　
yargs.parse()
```

コマンドが作成されているかターミナルから確認してみます。

```terminaml:ターミナル
node app.js --help

Commands:
  app.js addBook  Add a new Book

Options:
  --help     Show help                                                 [boolean]
  --version  Show version number                                       [boolean]

```

コマンドに、 addBookと設定したコメントが追加されています。

実際に、コマンドを実行してみましょう。

```terminaml:ターミナル
node app.js addBook


Add a new book!
```

処理には、"Add a new book!"をコンソールに出力する処理を書きましたが、無事に表示されました。

##コマンドに引数のオプションを追加する

作成したコマンドには、builderを用いて、オプションを追加できます。

```javascript:app.js
const yargs = require('yargs')

yargs.command({
     command: 'addBook',
     describe: 'Add a new Book',
    //オプション引数を追加
     builder: {
       title:{
          //処理の内容をメモ
          describe: "Add a book title",
          //必須の引数にするか指定
          demandOption: true,
         //データ型を指定
          type: "string"
       }
   },
     //オプションを表示するように変更する
     handler: function(argv){
     console.log("Adding " + argv.title);
     },
})

yargs.parse();
```
ターミナルでオプションのtitleを入れてファイルを実行します。

```terminaml:ターミナル
node app.js addBook --title="Kokoro"

Adding Kokoro
```

オプションが上手く変換されて、コンソールに出力されました。
Rails辞めたい人にこそ伝えたいRailsのビューコンポーネント化の概念

**<a href="https://soeno-onseo.medium.com/rails%E8%BE%9E%E3%82%81%E3%81%9F%E3%81%84%E4%BA%BA%E3%81%AB%E3%81%93%E3%81%9D%E4%BC%9D%E3%81%88%E3%81%9F%E3%81%84rails%E3%81%AE%E3%83%93%E3%83%A5%E3%83%BC%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E5%8C%96%E3%81%AE%E6%A6%82%E5%BF%B5-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-ea08377322ea">※ 記事の内容はMediumに移動しました</a>**
# 筆者連絡先
**https://twitter.com/soeno_onseo**
サーバーを立ち上げる際のエラーについて

サーバーを立ち上げる際にエラーが起きてしまった場合

```
[vagrant@localhost test]$ rails s -b 0.0.0.0
=> Booting Puma
=> Rails 5.2.4.4 application starting in development 
=> Run `rails server -h` for more startup options
A server is already running. Check /home/vagrant/work/test/tmp/pids/server.pid.
Exiting
```
という状態に陥ってしまった。
これはもうすでにサーバーが立ち上がっている状態なためあたらしくサーバーが立ち上げられない状態に陥っている。

解決方法

```
lsof -i:3000
```
上記のコマンドを利用して解決した。
利用する場面としてlocalhostの状態を一度EXITで抜けてそこでコマンドを利用すること。そうすることで

```
test@testMacBook-Pro vagrant % lsof -i:3000
COMMAND     PID       USER   FD   TYPE            DEVICE SIZE/OFF NODE NAME
VBoxHeadl 32798 okadayuuki   16u  IPv4 0x63928d0dd139ed1      0t0  TCP *:hbci (LISTEN)
```
このような返答が返ってくる。
この中のPIDに対応する数字のサーバーを切る必要がある。

```
kill -9 32798
```
上記のコマンドの３２７９８の部分をPIDの数字に当て流ことでこのサーバーを切ることができる。

これにより再度サーバーを立ち上げることでエラーは解決する。
【GAS】lengthの意外？な使い方【JS】

```javascript
let arr = [1, 2, 3, 4, 5];
console.log(arr.length); //5
```
配列の要素数を知りたい時に使う「length」。ちょいちょい使うので単純にそういうもんだとばかり思っていたら、それだけじゃなかった。

意外ではなく常識なのかもだけど、lengthプロパティが上書きできると知って驚き！こういう時、ちゃんと書籍とかで勉強しといた方が良いんだろうなって思う^^;

例えば、要素数５の配列のlengthを、あとから２にすると・・・。

```javascript
let arr = [1, 2, 3, 4, 5];
console.log(arr.length); //5
  
arr.length = 2;
  
console.log(arr.length); //2
console.log(arr);        //[ 1, 2 ]
console.log(arr[3]);     //undefined
```
`arr[2]`以降は消滅！要素数２の配列になってしまった。`arr.length=2`を受け付けちゃうなんて、とても柔軟でホスピタリティ溢れる対応 ﾜｧ(・∀・)ｵ

だから、こんなこともできちゃう。

```javascript
let arr = [4, 3, 1, 2, 5];
arr.sort().length = 3;
console.log(arr); // [ 1, 2, 3 ]
```
配列内の小さい数字BEST3を抽出^^)b

こんなことも。

```javascript
let arr = [1, 2, 3, 4, 5];
arr.length = 0;
console.log(arr); // []
```
`arr.length=0`で配列をリセット！こんな書き方する機会があるかは知らないけど^^;

###おしまい
オブジェクト指向の基礎をまとめてみる

仕事でご一緒にした方に毎回同じようなOOPの解説をしているので、改めてまとめてみることにしました。
あくまで基礎の話なので悪しからず。

# 対象者
* クラスって必要なの？と感じている方
* OOPよく知らんという方

# OOPでは何がありがたいのか？
これの答えはシンプルで 
**変数とロジックを同じ場所(オブジェクト)に置きアクセス制限をかけることで、副作用を限定し使い回しをよくする** 
ことだと思っています。

俗に言うクラスのカプセル化ですね。
逆に、多態性や継承などは基礎の段階ではあまり知らなくていいと思います。

今回は、レッスンの予約システムを例に考えていきたいと思います。

# OOPを使わない場合
以下は適当に作ったレッスンの予約処理です。
レッスンに参加する生徒と講師のIDとレッスンの開始時間、終了時間がunixtimeで入ってきます。
そしてどのキャンペーン対象かをチェックして予約処理をおこないます。


```typescript

  function reserve(studnetId:Number, teacherId:Number, lessonStartTime:Number, lessonEndTime: Number, lessonType:Number):void {

      // バリデーション処理
      if(lessonEndTime < lessonStartTime){
          new Error("おかしい時間")
      }

      if (lessonType === 0){
          // キャンペーン対象じゃないので普通の予約処理

      }

      if(lessonType === 1){
          // 通常キャンペーン対象なので通常キャンペーン処理して予約

      }

      if(lessonType === 2){
          // 特殊キャンペーン対象なので特殊キャンペーン処理して予約
      }

  }
```

## この書き方の辛いところ
* reserve関数の引数の型が全てNumber型なので、引数に入れる順番を間違える可能性がある
* バリデーションの処理が増えるとif文も増えてどんどんわかりにくくなる。
* lessonTypeのマジックナンバーが変更になった時に全部書き換えないと行けなくなる
* コメント描かないと何をしているのかわかりにくい

# OOPを使う場合

```typescript
class Lesson {
    #studentId : Number;
    #teacherId : Number;
    #lessonStartTime:Number;
    #lessonEndTime: Number;
    #lessonType:Number;

    constructor(studnetId:Number, teacherId:Number, lessonStartTime:Number, lessonEndTime: Number, lessonType:Number) {
        this.#studentId = studnetId;
        this.#teacherId = teacherId;
        this.#lessonStartTime = lessonStartTime;
        this.#lessonEndTime = lessonEndTime;
        this.#lessonType = lessonType;
    }

    validate():void{
        if(this.#lessonEndTime < this.#lessonStartTime){
            new Error("おかしい時間")
        }
    }

    isNormalLesson(){
        return this.#lessonType === 0;
    }

    isCampaignLesson(){
        return this.#lessonType === 1;
    }

    isSpecialLesson(){
        return this.#lessonType === 2;
    }
    
}

  function reserve(lesson:Lesson):void {

      lesson.validate();

      if (lesson.isNormalLesson()){
          // キャンペーン対象じゃないので普通の予約処理

      }

      if(lesson.isCampaignLesson()){
          // 通常キャンペーン対象なので通常キャンペーン処理して予約

      }

      if(lesson.isSpecialLesson()){
          // 特殊キャンペーン対象なので特殊キャンペーン処理して予約
      }
  }
```

## OOPに変えた結果
* バリデーションをクラス内に移動したのでバリデーション処理が増えても、reserve関数自体にif文が増えず処理が読みやすい
* LessonTypeの比較をクラス内に移動したので、例えば特殊キャンペーンに入れたい値が増えても、lesson.isSpecialLesson関数内を変更するだけ良い
* Lessonクラスをつかい回せば、LessonTypeの判定処理とバリデーション処理を全ての処理で使いまわせる。

#まとめ
クラスを使うとなると継承とか、多態性が一番にきて、結局何がありがたいのかわからないと思いますが、
一番の本質的なことは上記のようなメンバ変数とそれに付随する処理をクラスに隠蔽して、クラスを使う側の処理の可読性をあげることだと思います。

それ絵はよきOOPライフを
Ubuntu 18.04にsnap経由でVLCをインストールするとミスった話

### はじめに
Ubuntu 18.04を使っていてVLCメディアプレイヤーをsnap経由でインストールしようとすると

```ruby:error
$ sudo snap install vlc
error: snap "vlc" has "install-snap" change in progress
```
と表示されてしまう。

###解決方法
少し調べたらこのサイト([https://askubuntu.com/questions/1029117/unable-to-install-package-snap-package-has-install-snap-change-in-pro](https://askubuntu.com/questions/1029117/unable-to-install-package-snap-package-has-install-snap-change-in-pro"参考サイト") )に解決方法が載っていた、``` snap changes```でsnapプロセスの動作を確認して、VLCのプロセス番号を確認、そのプロセスを捨ててもう一度インストールをコマンドを打てばインストールできるらしい

```ruby:
$ snap changes
ID   Status  Spawn               Ready  Summary
13   Doing   today at 12:12 JST  -      Install "vlc" snap from "latest/stable" channel
```
13番のプロセスでVLCのインストールが動いてるらしい、13番だから```snap abort 13```してプロセスを一旦破棄させる。

```ruby:solve
$ snap abort 13
ID   Status  Spawn               Ready               Summary
13   Error   today at 12:12 JST  today at 12:25 JST  Install "vlc" snap from "latest/stable" channel
```
エラーが起きてるがここはプロセスが止まったことは確認できたからOK。
このあともう一度```sudo snap install VLC```を実行すればインストールできる。
ローカル環境でDrupalを起動するなら、quick-startコマンドが便利

Mac OSのローカル環境でDrupalを起動する最速の方法が見つかりました。

### 要件

PHP 7.3 以上

### ダウンロード

まずComposer経由でDrupalをダウンロードし、作業ディレクトリに移動します。Composerはバージョン1でも2でもどちらでも可能です。

```bash
$ composer create-project drupal/recommended-project my-drupal
$ cd my-drupal
```

### インストール＆ログイン

作業ディレクトリに移動したら、以下のコマンドを打ちます。

```bash
$ php web/core/scripts/drupal quick-start standard     # composer未使用の方は→ php core/scripts/drupal quick-start standard
```

他に引数に指定できるのは `demo_umami`(フードマガジンのデモサイト)、`minimal`(最低限のモジュールがインストールされたもの)があります。

ブラウザで自動的に http://localhost:8888 にアクセスされます。これで気軽にDrupalを色々いじれますよ！

![Screen Shot 2021-01-09 at 13.32.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79577/46ed4475-a853-5c3b-4348-a7753c8eeb94.png)

Composerが未インストールの方は、Drupal.orgからZipファイルをダウンロードすることで同様のことが実現できます。Composer経由でダウンロードする場合とZipファイルではドキュメントルートが異なるので、quick-startコマンドを実行するパスにご注意ください。

```bash
$ mkdir my-drupal
$ cd my-drupal
$ curl -sSL https://www.drupal.org/download-latest/tar.gz | tar -xz --strip-components=1
$ php core/scripts/drupal quick-start standard
```

今までローカルで簡易的にDrupalを起動するのにPHPのビルドインの簡易サーバー(php -S)を直接実行していたのですが、うまくいかない部分があったので、やはりコンテナ(DDEVやLandoなど）使うしかないのか・・でも少し立ち上げに時間がかかるしなぁ・・と感じていていて色々ググったところ、[quick-start](https://www.drupal.org/docs/official_docs/en/_evaluator_guide.html)コマンドを見つけました。と言っても、このコマンドが紹介されている[Evaluator Guide](https://www.drupal.org/docs/official_docs/en/_evaluator_guide.html)は前から知っていたのですが、Umamiデモサイト以外のインストールプロファイルを選択できるのは知りませんでした！

web/core/scripts/drupal ファイルから、web/core/lib/Drupal/Core/Command/ ディレクトリのQuickStartCommand.phpファイル、InstallCommand.phpファイルが順で呼び出されます。
インストールプロファイルは、 web/core/profiles/ ディレクトリと web/profiles/ ディレクトリ(ディストリビューションのプロファイルはこちらのディレクトリに格納される)内から探索され、隠しプロファイル(xx.info.ymlのhidden値がtrueとなっているもの)を除いたインストールプロファイルが選択可能と見なされます。
Gitでコミットしたメッセージを変更する方法

【ひとつまえのコミットのメッセージの変更方法】は下記になります。

手順１：
  ❶　vi エディターで変更する方法
     $ git commit --amend
　　   ⇨　キーボードのiキーでinsert モードを切り替える　
　　   ⇨　メッセージを変更する
　　   ⇨　変更したら esc + :wq! で保存する
  ❷　直接変更する方法
 　  $ git commit --amend -m "変更したいメッセージを書く"


手順２：
  メッセージが変更できるかどうか確認する方法
　  $ git log --oneline

手順３：
  強制的にリモートにプッシュする方法
 　 $ git push -f origin master


:point_right_tone1:これでみっともないコミットのメッセージを変更できますよー
新規作成で作成するファイルを追加する[Win10]

# TL;DR
これをカスタマイズします．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228086/fa0b3032-b2b0-a1f0-c268-1a0098c98955.png)

# 追加方法
早速，やっていきます．
例として，Pythonファイル（`.py`）を追加してみます．

## 1. レジストリエディタを開く

`Win+R`からファイル名を指定して実行を立ち上げ，検索欄に「regedit」と入力するとレジストリエディタが起動します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228086/b139c41a-bbb2-ced1-fb8a-4e85229616d0.png)

## 2. 拡張子を探す

レジストリエディタの「HKEY_CLASS_ROOT」のツリーから追加したいファイルの拡張子を探します．今回は`.py`を探します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228086/9894dbff-430b-1383-24db-3f9cc11ba442.png)

## 3. 新規作成に追加する

この拡張子に「ShellNew」というキーを追加し，更にShellNewキーに文字列値「NullFile」を追加することで，新規作成に追加されます．
最終的にはこんな感じになります．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228086/2621f0d6-a72d-9fc8-db23-61bd31ffc5a7.png)

これで新規作成欄に追加されていると思います．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228086/d551ca4b-4f0f-ab6b-f3db-5c823802024e.png)
物理エンジンで複数の物体を自由落下させて、衝突時に音を鳴らすようにしてみた


# できたもの

グラフは、衝突した物体のペアの番号をプロットしたものです。（0番は地面）

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


鳴ったり鳴らなかったり…

スマホでは、ユーザー操作がないと鳴らせないらしい。
 [ブラウザアプリでのサウンド再生（苦悩編） | スマゲー作るよ](http://jquerystudy.info/wordpress/?p=6647)

【追記：コメント頂いた内容を参考に、ボタンで開始するようにしてみました。】

あと、画面外だと鳴らないっぽい（？）

# 効果音を連続的に再生させるやりかた

再生が完了しきらないうちに、次の再生を設定・要求すると、まともに再生できなかったので、
再生中の場合には、下記のように先頭から再生しなおしています。

```js

audio.pause();
audio.currentTime = 0;
audio.play();
```

# 参考サイト

## cannon.js - 衝突検出

 - [Three.jsとCannon.jsを使ってだるま落としをつくってみた | 株式会社ジュニ](https://junni.co.jp/release/56/)
 - [cannon.jsの衝突イベントのはまりどころメモ - Qiita](https://qiita.com/kob58im/items/3ef951ec2183021b3025)

## 音を鳴らす

 - [CodePen： HTML+JavaScriptで波形生成して聴覚テストっぽいものをつくってみた - Qiita](https://qiita.com/kob58im/items/47475706032938995462)
 - [video要素、audio要素をJavaScriptから操作する－HTML5のAPI、および、関連仕様](http://www.htmq.com/video/)


## chart.js

 - [Getting Started · Chart.js documentation](https://www.chartjs.org/docs/latest/getting-started/)
 - [JavaScript - chart.jsでグラフのデータを動的に切り替えるにはどのようにすれば良いでしょうか？｜teratail](https://teratail.com/questions/216345)
 - [Chart.jsで散布図を作る - Qiita](https://qiita.com/berry-clione/items/e46fd122cd3f75cedd5e)
 - [Chart.js 軸ラベル等の設定＜Chart.js＜Javascript＜木暮仁](http://www.kogures.com/hitoshi/javascript/chartjs/scale-label.html)
【Railsトリビア】blog_path(@ blog)の代わりにblog_pathと書いても自動的にidが補完される

たとえば、RailsのViewファイルとして次のような`blogs/edit.html.erb`があったとします。

```erb
<h1>Editing Blog</h1>

<%= render 'form', blog: @blog %>

<%= link_to 'Show', blog_path(@blog) %> |
<%= link_to 'Back', blogs_path %>
```

5行目の`blog_path(@blog)`は`/blogs/10`のようなパスを生成するヘルパーメソッドです。（ここでは`@blog.id`の値が10だった場合を想定）

このように`blog_path`の引数には通常、`@blog`のようなActiveRecordのインスタンスを渡すと思います。
ですが、`blog_path`は次のように引数無しで呼びだしてもエラーにはなりません。

```erb
<%= link_to 'Show', blog_path %> |
```

生成されるパスは先ほどと同じく`/blogs/10`になります。

Viewだけでなく、コントローラ内でも同様です。

```ruby
def update
  if @blog.update(blog_params)
    # blog_path(@blog) と書かなくても /blogs/10 にリダイレクトされる
    redirect_to blog_path, notice: '...'
  else
    render :edit
  end
end
```

## Why?

上記のような編集画面は通常、以下のようなパス（URL）で呼び出されると思います。

```
/blogs/10/edit
```

`blog_path`のように引数無しでヘルパーメソッドを呼び出した場合、リクエスト時のパスに含まれるid（ここでは10）を引き継いで `/blogs/10` が生成されます。

## 引数がないとエラーになるケース
rails consoleなどで`app.blog_path`と書いた場合は引き継ぐidがないのでエラーになります。

```
> app.blog_path
Traceback (most recent call last):
        1: from (irb):2
ActionController::UrlGenerationError (No route matches {:action=>"show", :controller=>"blogs"}, missing required keys: [:id])
Did you mean?  blog_url
               blogs_url
               blogs_path
               new_blog_url
```

## 引数無しの`blog_path`を使うのはアリか、ナシか？

個人的にはナシだと思います。

暗黙的にidが決まってしまうより、明示的に「このメソッドで生成したいパスはこのid（このオブジェクト）」ということを示すために`blog_path(@blog)`のように書く方が安全だからです。

また、引数を省略するクセが付いていると、Viewのロジックによっては意図しないidでパスが生成されたりして思わぬ不具合を起こすかもしれません。

## ドキュメントはどこ？

`url_for`メソッドのドキュメントにそれらしき記述がありました。

https://edgeapi.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html#method-i-url_for

> Missing routes keys may be filled in from the current request's parameters (e.g. `:controller`, `:action`, `:id` and any other parameters that are placed in the path).
>
> （筆者訳） 不足しているルーティングのキーは、リクエストパラメータ（例 `:controller` や、 `:action`、 `:id` 、その他パスに含まれる各種パラメータ）から取得されます。

上のドキュメントに書いてあるとおり、たとえば `blog_path` の代わりに `url_for(controller: 'blogs', action: 'show')` のように書いても同じように `/blogs/10` が生成されます。（もっと言えば `url_for(action: 'show')` だけでもOK）

## 他にもこんなケースが

ネストしたルーティングの場合も困った動きをします。たとえば、`/blogs/10`のようなページにおいて、以下のようなコードを書くとどんなURLが生成されるかわかりますか？

```erb 
<%# 間違ったコード例 %>
<%= link_to 'コメント編集', edit_blog_comment_path(comment) %>
```

これは次のようなURLを生成します。

```
/blogs/(commentのid)/comments/(現在表示しているblogのid)/edit
```

たとえば、`comment`のidが3だったとすると、こんなURLになります。

```
/blogs/3/comments/10/edit
```

なぜならURIパターンが次のようになっているからです。

```
/blogs/:blog_id/comments/:id/edit
```

`/:id`の部分は「リクエスト時のパスに含まれるid（＝現在表示しているblogのid）」をRailsが自動的に補完してくれます（補完しなくていいのに！）。そして、引数として与えた`comment`のidが`/:blog_id`として使われます。その結果、以下のURLが生成されるわけです。

```
/blogs/(commentのid)/comments/(現在表示しているblogのid)/edit
```

が、これは意図したURLではありません。erbのコードは本来こう書くべきです。

```erb 
<%# 正しいコード例 %>
<%= link_to 'コメント編集', edit_blog_comment_path(blog, comment) %>
```

こうすれば以下のように適切なURLが生成されます。

```
/blogs/(blogのid)/comments/(commentのid)/edit
 ↓
/blogs/10/comments/3/edit
```

引数が1つだけでもエラーにならず、一見ちゃんと動いているように見えるのがこの問題のややこしいところです・・・😭

## 確認したバージョン

- Rails 6.0.3.3
Homebrewでgit origin remoteの設定がおかしいよと言われた時の解決策

`% brew update`がおかしくなってなかなか苦労したので、解決までの経緯をここに。

#どんな問題がおきた？

>```:ターミナル
>Warning: Suspicious https://github.com/Homebrew/homebrew-core git >origin remote found.
>The current git origin is:
>  https://github.com/Homebrew/brew

>With a non-standard origin, Homebrew won't update properly.
>You can solve this by setting the origin remote:
>  git -C "/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core" >remote set-url origin https://github.com/Homebrew/homebrew-core

`% brew doctor`による診断はこんな感じ。

言われるままにディレクトリのgit remote設定を行っても、このエラーが消えてくれない。結論から言うとこの作業は意味がなかったので、詳細については省略。

なんだろうと思っていたら、[似たような悩み](https://discourse.brew.sh/t/error-no-formulae-found-in-taps/8331/5)をようやく見つけた。どうも、HomebrewとGitそれぞれの設定がおかしくなっているらしい。参考にした回答はこれ。https://discourse.brew.sh/t/error-no-formulae-found-in-taps/8331/7

#解決した方法

結局のところ、Homebrewをしっかりアンインストールするのが楽だよとのこと。

つまりはHomebrewのアンインストールが完了した後で最後に言われる、




>```
>The following possible Homebrew files were not deleted:
>######

>/対象のディレクトリ

>######
>You may wish to remove them yourself.
```


これらの自動で消してない箇所についても、ちゃんと手動で消してみてねということ。対象のディレクトリ部分にはもちろん、`/usr/local/Homebrew/`などの対象ディレクトリが入る。

手動での削除作業もおこない、Homebrewを再インストールしたところ、ようやく正常に機能した。

ちなみにインストール、アンインストールのコマンドについては[Homebrewの公式サイト](https://brew.sh/)を参照。また、下記に示すのは自分が行った手動での削除方法だけれど、ここについてはまずTimeMachineなどでバックアップをとって、他の記事を参考にした方が速いかも。

自分はもうターミナルを触りたくなかったので、Finderで`/usr/local`の階層を探し、指示されたディレクトリを一つずつ消した。これらのディレクトリ削除には管理者パスワードを求められた。



#終わりに

今日はpython環境構築のやり直しをやっていたのだけれど、いろんなエラーが出て大変だった。この記事で取り上げた問題についてはあまり日本語の記事がなかったので、だれかの参考になれば。
素数を求める

#コード
既出とは思いますが、自分でも考えて作ってみたので備忘録として残しておきます。

```primenumber.cpp
#include<iostream>
using std::cin; using std::cout; using std::endl;

bool primeNumber(int n)
{
    if ( n < 2 )
        return false;
    else if ( n == 2 )
        return true;
    else if ( n % 2 ==0 )
        return false;
    else
     {
        int i=0;
        for ( i = 3 ; i < n ; i+=2){
                if( n % i ==0 )
                        return false;
        }
        return true;
      }
}


int main()
{
    int num;
    cout<<"Input Num. -->";
    cin>> num;

    if(primeNumber(num))
        cout<<"This is primenumber."<<endl;
    else
        cout<<"This is not primenumber."<<endl;

    return 0;
}
```

##説明
今回はすべてをmain関数内で処理するのではなく、素数かどうかを判定するbool型関数を作成してみました。

###primeNumber関数
この関数を用いて、入力された値が素数かどうかを判定します。
素数となる条件は、**「2以上の数字」かつ「1と自分自身でしか割り切ることができない」**というものです。(例えば、5,7,11,13...など)

素数でない値ならば false, 素数の値ならば true を返すようにします。

```cpp
    if ( n < 2 )
        return false;
    else if ( n == 2 )
        return true;
    else if ( n % 2 ==0 )
        return false;
```

初めは単純な処理です。
2より小さい値は素数ではない、2は素数、2の倍数は必ず2で割り切れるので素数ではない、といった具合です。
ここで、先に入力された値が2かどうかの判定を行うことで、2の倍数かどうかの判定のときに、
その値が2であったとしても誤ってfalseを返さないようにしておきます。

では次に、3以上の奇数の判定にいきましょう。

```cpp
    else
     {
        int i=0;
        for ( i = 3 ; i < n ; i+=2){
                if( n % i ==0 )
                        return false;
        }
        return true;
      }
```
for文を使います。(おそらくもっといい方法があると思います...)

3以上の奇数を判定していくので、ループのはじめの値は `i=3`、増分は`i+=2`としています。

**もし入力された値が、自分より小さい値で割り切ることができたら、falseを返します。**
(素数ではないということ)

例えば、n=9 であるとします。9は3で割り切ることができる、つまり素数ではないのでfalseを返します。

そして、ループの中で一度も割り切ることができなかったら、素数であると判断できるので、ループ後にtrueを返します。


###main関数
main関数では、上記のprimeNumber関数を利用します。

```cpp
if(primeNumber(num))
        cout<<"This is primenumber."<<endl;
    else
        cout<<"This is not primenumber."<<endl;
```

primeNumber関数はbool型なので、if文を用いた判定に利用できます。



##終わりに

以上で、「素数を求める」プログラムの解説を終わります。
もっと効率の良い、素早い計算のできるコードがあるかもしれません。
そういう点も勉強していかないとダメですね...。

閲覧ありがとうございました。
何か間違いがあればコメントお願い致します。
メンバーをBANする方法 Discord.js（GBAN前編）

#サーバーから人をBANする方法
荒らす人、イライラする人にはBANができます。
その動作をBOTにさせる方法を教えます！
##動作環境

| バージョン | サービス 
|:-----------|:------------
| v.12以上   | Glitch       

##document
公式ドキュメント（英語）：[Link](https://discord.js.org/#/docs/main/stable/class/GuildMemberManager?scrollTo=ban)

##動き方

`!ban メンションorID`
↓
理由を聞く（BANをする理由）
↓
BAN完了！

##コード
```js
const discord = require("discord.js");
const client = new discord.Client();
const prefix = "!";//ここは好きに変えてもいいです

client.on("message", async message => {
if (!message.content.match(/^!/)) return; //!が最初にない場合は処理をしない
var command = message.content;
command = command.replace("!", "");
if (command == "ban") {
if (message.mentions.members.size == 1) { //メンションがあるかを確認

          const member = await message.mentions.members.first();//メンションの内容
          const id = member.user.id; //ユーザーID
          const mee = await message.channel.send({ //あとで編集などができるようにawait（非同期処理）をつける
            embed: {
              color: 16757683,
              description: "60秒以内に理由を入力してください。"
            }
          });
          const filter = msg => msg.author.id === message.author.id;
          const collected = await message.channel.awaitMessages(filter, {
            max: 1,
            time: 60000
          });
          const response = collected.first();
          if (!response)
            return mee.edit({
              embed: {
                description: "タイムアウト…"
              }
            });
          mee.edit({
            embed: {
              description: "BANしています…"
            }
          });
          message.guild.members.ban(id, { reason: response.content });
          mee.edit({
            embed: {
              description: `<@${id}>をBANしました。`
            }
          });
        } else { //IDの場合
          let me = message.content;
          me = me.replace("!ban ", "");
          let id = me
          const mee = await message.channel.send({
            embed: {
              color: 16757683,
              description: "60秒以内に理由を入力してください。"
            }
          });
          const filter = msg => msg.author.id === message.author.id; //検索内容を設定
          const collected = await message.channel.awaitMessages(filter, {
            max: 1,
            time: 60000
          });
          const response = collected.first();
          if (!response)
            return mee.edit({
              embed: {
                description: "タイムアウト…"
              }
            });
          mee.edit({
            embed: {
              description: "BANしています…"
            }
          });
          message.guild.members.ban(id, { reason: response.content });//BANをする
          mee.edit({
            embed: {
              description: `<@${id}>をBANしました。`
            }
          });
       
        }
}
})
```
## 動かない場合
①使うBOTに管理者権限、BANの権限、チャンネルへのアクセス権がない
②tokenが間違っている
③BOTが招待されていない
④コードが間違っている
など…
##使う上での注意
<p style="color: red;">※サーバーにいないひとをBANする場合、IDでしかBANができません。一応反応はします。（エラーを出すこともできるけど→別の記事で）サーバー内にいる人ならメンションでOKです。</p><br>

##質問など
Discordの場合：ライグー/racoon#1010
コメントでもOKです
GitとGitHub 〜よくあるエラーの解消方法〜

プログラミング学習の備忘録としてアップします。




#目的
* Switch Branchの使用方法を理解する
* コンフリクトが起こる操作ミスを学ぶ
* 簡単なコンフリクトの解消方法を学ぶこと
* revertの使用方法を理解する
* Switch Branchの使用方法を理解する

#内容
* Switch Branchの使用方法を理解する
　ブランチを作成し忘れたときや、別のブランチに書き込んでしまった場合、ブランチを元に戻すには
　1.書いているコードを別のブランチに引き継ぐか、　→Bring Change To
　2.書いているコードを一旦保留して０からブランチを作り直すか　→Leave On Change My Master
Leave On Change My Master
　一旦作業を中断したいときなどに用いる操作、０からブランチを作成するため作業内容が０に戻ってしまう。中断した作業を元に戻すには
マスターブランチに戻る
Started　Changes を選択する
Resteore を選択する
ことでコードを復旧する
￼

* コンフリクトが起こる操作ミスを学ぶ
　コンフリクト　複数人が各々のブランチで作業し、マスターブランチにPushする際、それぞれが同じファイルを編集してしまった場合、AにはIndexがありNewがないBにはIndexがなくNewgがあるといった辻褄が合わない状態が生じる。
* 簡単なコンフリクトの解消方法を学ぶこと
　間違った情報をPushしてしまった場合
￼
上記画面のResolve conflictを選択
コードをあるべき内容に修正する
Mark As Resolved を選択
上記をそれぞれに行う。
コンフリクトが起こった際の注意点
・別々に作業を始める前に、打ち合わせを行い事前にコンフリクト になりそうな箇所を押さえておく
・最新のマスターブランチの情報をpullしてから作業に取り掛かること
・なぜコンフリクトが発生したかを明確化すること
・コンフリクトの解消は独断で行わず、メンバーと打ち合わせて解消する

* revertの使用方法を理解すること
間違った情報をpushしてしまった場合
一度リモートディポジトリにpushしたcomment情報は戻すことはできない。注意してpushすること。
戻すことはできないが、リモートディポジトリのcommitは削除できる。→Revert
・Historyの中にあるcommit一覧の中の削除したいものを選択し右クリック
　「Revert Changes In commit」を行う　
　push Origin

間違った情報をcommentしてしまった場合
pushする前であれば、commentの下にあるUn doでコメントは取り消せる

#総括
複数人で作業をする際には個人のエラーが全体のエラーにつながってしまう。
・push・comment等をする際にはエラーがないことを確認して行う。
・間違えてしまったら正しい手順で解消し、チームメンバーには相談、報告を行う。
・解決できない時は分で解消しようとしないでメンバーに相談する。
おやっ！？MySQLに急にログインできなくなった時の対処法

皆さんこんにちは！

今日は、セキュリティが頑固なMySQLのコマンドラインで急にログインが出来なくなったので、その解決方法の備忘録として書いていきます。

MySQLって厄介で、一度何かの設定をミスると再インストールなんてことも珍しくありません。ちなみに僕は２回再インストールしています。

ただ、毎回そんなことをしては時間はかかるし、**エンジニア**らしくありませんよね？

MySQLは同じエラーでも人によっては解決できない場合もあります。

なので、このコマンドを打ったら解決できたよ！っていうよりかは、どのようにして解決できたかを書いていきたいと思います。

同じようなエラーに引っかかった人、違うエラーに引っかかった人にも手助けになればなと思うので、是非ご参考程度にご覧ください！

MySQLのコマンドプロンプトからパスワードを入力してもできない場合は、Windowsであればコマンドプロンプト、Macならターミナルから操作します。なので、各自、MySQLの環境変数を設定してください。

#MySQLのユーザー情報を取得#

コマンドプロンプト、もしくはターミナルで以下のコマンドを実行してください。

```
mysql select Host, User FROM mysql.user
// パスワードも取得したい場合
mysql select Host, User, authentication_string FROM mysql.user
```

<font color="red">※注意</font color>コマンドプロンプト、ターミナルからMySQLコマンドを実行する時、末尾にセミコロン（;）は付けないでください！

すると、このようにユーザー情報が出てくると思います。

```
+-----------+------------------+
| Host      | User             |
+-----------+------------------+
| localhost | mysql.infoschema |
| localhost | mysql.session    |
| localhost | mysql.sys        |
| localhost | root             |
+-----------+------------------+
```

ここで、rootユーザーが無い場合は、``create user``で作成して見て下さい。

#ユーザーにアクセス#

```
mysql -uroot -hlocalhost -p
```

と入力し、以下の画面が出てきます。

```
Enter password:
```

僕は、ここで何も入力せずにEnterキーを押すと入れました！

これでも入れない場合は、新しいユーザーを作成してください(``create user``)

#パスワードの設定#

次に、パスワードの設定を行います。``'YOUR_PASSWORD'``で自分のパスワードを入力してください。

```
set password = 'YOUR_PASSWORD';
```

#権限を付与#

```
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'YOUR_PASSWORD';
flush privileges;
```

#MySQLコマンドプロンプトでアクセス#

MySQLのコマンドプロンプトを開き、先ほどせってしたパスワードを入力してください。



```
Enter password: ****************
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 22
Server version: 8.0.22 MySQL Community Server - GPL

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

このような画面になれば成功です！

#port番号にミスアリ！？#

ここからは番外編です。

このような流れで大体の人はアクセスできると思うのですが、僕はアクセスできなかったんですよね。。。

ここで、先ほどWindowsのコマンドプロンプト、Macのターミナルからアクセスし、以下のコマンドを実行してください。

```
mysql> status
```

すると、色々な情報が出てくると思います。

```
TCP port: 3306
```

ここで、ポート番号出てきます。

そしたら、MySQLの``my.ini``を開いたところ（一応、my.iniの場所を載せておきます。人によっては、my.iniが無い場合もあるそうです。なので、ご参考までに。C:\ProgramData\MySQL\MySQL Server 8.0\）

```my.ini
[client]

# pipe=

# socket=MYSQ

port=3308

[mysqld]

# The next three options are mutually exclusive to SERVER_PORT below.
# skip-networking
# enable-named-pipe
# shared-memory

# shared-memory-base-name=MYSQL

# The Pipe the MySQL Server will use
# socket=MYSQ

# The TCP/IP Port the MySQL Server will listen on
port=3308
```

２回再インストールしていたので、port番号は(3308)になっていました。

なので、これを3306に変えたところ無事入れました！

いかがだったでしょうか？

MySQLのエラーは本当に厄介です。時間も潰れます。ですが、自分なりに考えたり、調べたりして頑張って解決してください！

応援してます！！

以上、「MySQLに急にログインできなくなった時の対処法」でした！

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい！！

Thank you for reading
Laravel シーディングのやり方

#環境
・Mac OS catalina
・Laravel6系
・phpMyAdmin
・MAMP
・Visual Studio Code

#シーディングとは
初期状態で、いくつかのダミーコードを作成すること。

#手順
###①シーダーファイルの作成

```
php artisan make:seeder （大文字）名前TableSeeder
```

###②記述（シーディング処理）
`public function run()`
の中にダミーデータを記述していく。

```
public function run(){

//1レコード目
    $param = [
         'name' => 'taro', //キーがカラムにあたる
         'mail => 'taro@yamada.jp',
         'age' => 12,
    ];
    DB::table('（小文字）テーブル名')->insert($param);

//2レコード目
    $param = [
         'name' => 'hanako', //キーがカラムにあたる
         'mail => 'hanako@flower.jp',
         'age' => 34,
    ];
    DB::table('（小文字）テーブル名')->insert($param);
}

```

###③シーダーファイルの登録
`DatabaseSeeder.php`のファイルに、今から行うシーダーファイルを登録しておく必要がある。
`public function run()`に以下を記載する。

```
$this->call(（大文字）名前TableSeeder::class);
```

`$this->call(シーダークラス::class);`という形で記載する。

###④シーディングを実行する

```
php artisan db:seed
```

とターミナルで入力すると実行される。
(小ネタ)TypeScriptで型定義ファイルが無いモジュールの読み込み方法

# はじめに
ml5を利用しようとしたところ、型定義が無く、エラーが発生したため、型定義が無い時のモジュールの読み込み方法について調べました。
＊なお、ml5の型定義は現在draft版を作成中。
https://gist.github.com/dikarel/38a12ce263199a41ad67c15eac7f4b45

# 型定義が無い時のエラー
ml5を以下のようにモジュールをインポートするとエラーとなる。

```typescript
import * as ml5 from "ml5";
```

具体的には以下のようなコンパイルエラーが発生する。

```console
Could not find a declaration file for module 'ml5'.
Try npm install @types/ml5 if it exists or add a new declaration (.d.ts) file containing declare module 'ml5';
```

# 型定義が無い時の読み込み方法
型定義ファイル( `d.ts` )を自作すれば良いが、面倒な時は`require` でモジュールを読み込みます。
暗黙的に `any` 型になるので、型定義ファイル `d.ts` が見つからないエラーは消えます。

```typescript
const ml5 = require('ml5')
```

ただし `TSLint` の設定によっては `[tslint] require statement not part of an import statement (no-var-requires)` という警告がでます。
`// tslint:disable-next-line:no-var-requires` とコメントで `require('ml5')` についてだけ警告を無効にします。

```ts
// tslint:disable-next-line:no-var-requires
const ml5 = require('ml5')
```

この状態では `ml5` が `any` 型になり、型チェックも賢い補完も行われません。
自分の使う API だけ型付けして置くと、開発がスムーズです。

```typescript:App.tsx
type ImageClassifierOptions = {
  alpha: number;
  topk: number;
  version: number;
};

interface IMl5 {
   imageClassifier(
    model: "MobileNet" | "Darknet" | "Darknet-tiny" | string,
    callback?: (error: any, result: any) => void
  ): undefined | Promise<any>;
  
  imageClassifier(
    model: "MobileNet" | "Darknet" | "Darknet-tiny" | string,
    options?: ImageClassifierOptions,
    callback?: (error: any, result: any) => void
  ): undefined | Promise<any>;
  
  imageClassifier(
    model: "MobileNet" | "Darknet" | "Darknet-tiny" | string,
    video?: MediaElement | HTMLVideoElement,
    options?: ImageClassifierOptions,
    callback?: (error: any, result: any) => void
  ): undefined | Promise<any>;

}
const ml5 = require('ml5') as IMl5;
```

# 終わりに

以上。
韓国の競プロ Baekjoon Online Judge 登録方法とはじめてのsubmit

<font color=red>1/9のコンテストに出るための記録</font>なので、後日加筆します


英語がないので登録の仕方から、submitまで簡単に紹介
<font color=red>以下、chromeで自動翻訳ONの状態のスクリーンショットなどです</font>


# 登録
まず、TopPageにアクセスします。
https://www.acmicpc.net/

会員登録を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/62f87629-08f8-fa6d-19a0-9157f52d45b5.png)

次のページは記入欄も少ないので簡単に記載できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/b7f96cef-3ea3-7269-11e3-3aaf5c80009d.png)

そのままではメール認証が来ないので
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/3dceefe8-84be-25e3-ea5d-02f64a8f6b09.png)
[このページ(上記の「ここ」です)](https://www.acmicpc.net/email/auth)を開きます。

さらに表示される`ここ`を押します。(これは個別のリンクなので、Qiitaからリンクは張りません)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/edc16c05-4cd8-b711-f61f-8782072d17b1.png)

するとメールが届きますが、<b><font color=red>gmailでは「迷惑メール」に判定されました。</font></b>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/c2877253-93ba-5b72-33ff-18bc3b147ded.png)

そのなかのリンクをクリックして認証を完了させます。(これも個別のリンクなので、Qiitaからリンクは張りません)


# はじめてのsubmit
認証を済ませるとこのようにloginができているはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/f1fcd229-26d1-2436-f89e-5c606d18cb49.png)
以下のような問題へのリンクがあります。今回はSTEP問題を解いてみます。
[STEP問題集](https://www.acmicpc.net/step)
[問題集一覧](https://www.acmicpc.net/workbook/top)
[大会一覧](https://www.acmicpc.net/contest/official/list)

例えば、[A+B](https://www.acmicpc.net/problem/1000)を開きます。左上に提出があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/46ec5d54-6d70-8b78-b9bf-33be7a3e4edb.png)
C++17, Python3, PyPy3などはあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/5245b8af-7e3d-4972-9088-7042de2b71e7.png)

AtCoderとちがい、WAしたときは何ケースACしたなどは出ないようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/95615a30-4738-9bd7-86f6-74488d8c0213.png)

(随時加筆します)
リダイレクト先に値を渡す（メッセージ表示）


リダイレクト先でメッセージを表示させたかったが少し手こずったので学習記録用記事


###リダイレクトで遷移先に値を渡す

値を渡す側のコントローラー👇

```Controller.java

@RequestMapping(value="/create", method=RequestMethod.POST)
public String create(RedirectAttributes redirectAttributes){

    redirectAttributes.addFlashAttribute("message","リダイレクト先に表示したいメッセージ")

    return "redirect:/index"
}
```

値を受け取る側のコントローラー👇

```Controller.java

@GetMapping("/index")
public String getIndex(@ModelAttribute("message") String message,Model model){
    model.addAttribute("message",message);
    return "/index";
}
```
<a href="https://spring.pleiades.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/support/RedirectAttributes.html">インターフェース RedirectAttributes</a>


**addFlashAttribute()**は、
実際には属性をフラッシュマップに格納
**addFlashAttribute()**のの利点は、フラッシュ属性にほとんどすべてのオブジェクトを格納できること

**addAttribute()**は
基本的に、からリクエストパラメータを構築
属性とリクエストで目的のページにリダイレクト
**addAttribute()**を使用すると、追加したオブジェクトが取得される。通常のリクエストパラメータに変換すると、Stringやプリミティブなどのオブジェクトタイプにかなり制限される。
【ミライトデザイン社内勉強会#11】「IDDD本から理解するドメイン駆動設計」輪読会~第2章「ドメイン」「サブドメイン」「境界づけられたコンテキスト」を読み解く~

### 前回の記事
[【ミライトデザイン社内勉強会#10】「IDDD本から理解するドメイン駆動設計」輪読会~1章「DDDへの誘い」~](https://qiita.com/tkek321/items/4a2b62db7219a510dedd)

## [実践DDD本 第2章「ドメイン」「サブドメイン」「境界づけられたコンテキスト」を読み解く (1/4)：CodeZine（コードジン）](https://codezine.jp/article/detail/9744)



###  コアドメインが分割されることとかあるの？１アプリケーション１コアドメインではないの？
- 1システム1コアドメインが理想。マストではないけど。

> 「コアドメイン」とは事業的に最も重要で戦略的に不可欠な部分です。
> IDDDでは、1つの「コアドメイン（もしくはサブドメイン）」に、1つの「境界づけられたコンテキスト」が対応している状態が最適だとされています。

### 「商品」という言語が、2つ以上の意味を持たないように「境界づけられたコンテキスト」を適切に分割する、とあるが、入荷待ち、着荷品ごとにコアドメインを分けるの？商品に入荷待ちや着荷品という状態を持たすのではダメなの？

https://codezine.jp/article/detail/9744?p=2
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/371d3d4e-f33a-132e-dda1-f9a4ad832d57.png)

- 状態が違えばモデルをわけろっていう話ではない。
     - 例えば、有料会員、無料会員で対して違いがないのであれば、1つの会員モデルで状態が違うだけでも構わない。
     - 有料会員と無料会員で全く意味が変わって来るのであれば、それはモデルを分けるべき
     - 別の例いうと、管理画面で権限によって振る舞いが変わる場合、ユーザーがもってる権限オブジェクトが違うだけの場合もある。
        - この場合は、権限はただのユーザーの一属性。
        - 同じモデルなのか、別のモデルにした方がいいのかは考えないといけない。

- Amazonとかの規模がおおきいシステムでは予約管理システム、在庫管理システムのようにコンテキストを分けた方がいい場合がある。
    - この場合、在庫管理は在庫管理システムで管理して「商品」は在庫品としての意味しか持たない。
- 小さいシステムでは予約中の商品は、予約中商品モデルを作ればいい（もしくは、商品に状態をもたせるだけですむなら、予約中という状態で考えるか）
 - 言葉が大きくなりすぎないようにしないといけない
 - 端的にいうと同じ言葉なのに別の物を指してる状態にならないようにしようって話。
 - 1つのモデルが複数の意味を持たないようにする
 - 1つの呼び名が複数の意味を持たないようにする
 - 例えば、チーム内で「商品」という単語が出た時に、それが「在庫品」なのか「不良品」のことを指しているのかわからず、認識に齟齬が生じる状態にあってはいけない。

###  境界づけられたコンテキストで１アプリケーションとして成り立つ？
- 成り立つ。
- 必ずしも１アプリケーションという訳ではないけど。
    - パッケージを分けているだけの場合もある。
- 境界づけられたコンテキスト内で、別の境界づけられたコンテキストのモデルを呼ぶことはない
    - コンテキスト外とのやりとりは Published Languag のみでしかしてはいけない。

### 「支援サブドメイン」と「汎用サブドメイン」の違いは、支援サブドメインが業務的にコアドメインほど重要ではないもの。汎用サブドメインはシステム的には必要だけど、業務には関係ないものって認識でok??
- 業務に全く関係ないってわけではないかも
- 汎用サブドメイン：汎用的に使うもの、別のドメインでも必要となるようなもの
- 支援サブドメイン：コアドメインを支援する上で必須なもの

> <img width="600" alt="image.png (77.5 kB)" src="https://img.esa.io/uploads/production/attachments/14942/2021/01/07/67007/4a5d2aa2-3c65-4b17-8cc4-b1943f7ad8f6.png">

### コアドメインとサブドメインとではコードではどういうふうに分けるのか？ドメインの中でコアとサブがある？ 
- パッケージで分けてもダメではないけど、あまり分ける必要もないかも
- コアドメインやサブドメインの話は、あくまで、アプリケーションの構成の考えてコアドメインに集中するためのもので、コード上ではそこまで意識する必要はない

### ユビキタス言語はドキュメントか何かで管理するもの？
- ドメインモデル図がユビキタス言語を兼ねていることが多いかも
- 特に決まりがあるわけはない


## 次回
[【ミライトデザイン社内勉強会#12】「IDDD本から理解するドメイン駆動設計」輪読会~第3章「コンテキストマップ」~ - Qiita](https://qiita.com/tkek321/items/69080f47d8ae94766557)
【React】Hooks時代のディレクトリ構成案

## 注意

2021/1現在、本ディレクトリ構成にて運用中のため、適宜メリット・デメリットを追記してゆく。
また、本ディレクトリ構成での運用に不都合が発覚した場合、ディレクトリ構成は変更の可能性あり。

## 編集履歴
- 1/9 本記事を作成

## 対象のシステム

客先などに提供するため実装・試験まできちんとしなければならないが、アトミックデザインにするほどでもないな〜といった感じのシステム。
以下、作成するシステムの前提を記載。

- create-react-appする
- TypeScriptで実装する
- Reduxは使わないがuseReducerを使う
- レンダリングとロジックを分離させるため、PresentationComponentとContainerComponentは分ける
- ユニットテストは[jest](https://jestjs.io/ja/)を利用し、ContainerComponent、カスタムフックのC1カバレッジを100%にする
- メッセージはcomponentとは別のファイルで定義する



## ディレクトリ構成案

```
app/
 ┗ src/
    ┠ Components/
    ┃        ┠ Presentations/  //画面表示に関するファイル
    ┃        ┗ Containers/     //ロジックに関するファイル
    ┠ Hooks/                   //カスタムフック
    ┠ Actions/                 //useReducerで利用するAction
    ┠ Models/                  //複数のコンポーネント間で共有するinterface
    ┠ Message/                 //エラーメッセージなど
    ┠ App.tsx
    ┠ index.tsx
    ┗ Tests/                   //ユニットテストに関するファイル
```

## メリット

- ユニットテスト実行時はContainerディレクトリ、Hooksディレクトリ内のファイルを対象に行えばよい
- コンポーネント間でAction、Model(interfaceなど)を共有しやすい
- コンポーネント数がある程度大きくなっても対応できる

## デメリット（課題）

- ディレクトリ構成からはPresentationComponentとContainerComponentの関連が推測できない（ファイル名によって推測するしかない）
2021新春 Markdownエディタ選定

## はじめに

定期的にMarkdownエディタを変えたくなる私です。
2020年はBoostnote(旧)を使ってきましたが、少し使いづらさを覚えています。
新春ですし、新しいMarkdownエディタを探してみようと思います。


## Markdownエディタに求めるもの

- vimモードがある
  - カーソル移動、行選択、矩形選択は欲しい
- LivePreviewがある
- TeX数式が使える
- フローチャートとかクラス図が書ける（欲しい機能を以下に列挙）
  - [Mermaid](https://mermaid-js.github.io/mermaid/#/)
  - [PlantUML](https://plantuml.com/ja/)
  - [flowchar.js](https://flowchart.js.org/)
  - [js-sequence-diagrams](https://bramp.github.io/js-sequence-diagrams/)

:warning: 使用環境はMacの前提です。

## 調査したエディタ一覧

| エディタ名 | 選定理由 |
| --- | --- |
| [Boostnote(旧)](https://github.com/BoostIO/Boostnote) | 2020年に使っていたから |
| [BoostNote](https://boostnote.io) | 旧版を選定したついで |
| [Typora](https://typora.io/) | LivePreviewが魅力的なので |
| [Inkdrop(Demo)](https://www.inkdrop.app/) | 有料版の実力を知りたかったから |
| [Atom](https://atom.io) | 最近良くなった気がしたので（最後に触ったのが5年ほど前） |


## 比較表

- o : 機能あり
- \- : 機能なし
- (o): プラグインで追加可能


| 機能名\エディタ名 | Boostnote(旧) | BoostNote | Typora | Inkdrop | Atom |
| --- | --- | --- | --- | --- | --- | --- |
| vimモード | o | o | - | (o) | (o) |
| LivePreview | o | o | o | o | o |
| Tex数式 | o | o | o | (o) | (o) |
| Mermaid | o | - | o | (o) | (o) |
| PlantUML | - | - | - | (o) | (o) |
| flowchar.js | - | - | - | (o) | (o) |
| sequence | o | - | o | (o) | (o) |
| タグ付け | o | o | - | o | - |

- InkdropとAtomがいい勝負になったので、ファイルへのタグ付けを比較機能に追加してます。 
- Inkdropがすべて機能を持っている。さすが有料。
- Atomはタグ付け以外の機能を持っている。
- Typora, Atomは純粋にエディタという立ち位置。
- Boostnote, Inkdropはファイル管理機能も持っている。




## エディタ選定

- 個人的にはTyporaのLivePreviewが気に入っているが、他の要件を満たせていないので見送る。
- 純粋に機能だけ見ればInkdrop。買いきりだった選択したかもしれないが、月額制(4.99$/月)なので見送る。
- Atomは無料で、欲しい機能はすべて揃っている。他にも便利なプラグインがありそうということで選択。

ということで、今年はAtomを使います :smile:

## おまけ: スクリーンショット

### Inkdrop
![スクリーンショット 2021-01-09 12.13.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32410/2b96b41a-67e2-23d7-27d5-e82185b16753.png)

### Atom
![スクリーンショット 2021-01-09 12.13.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32410/b92c6923-6f50-76ed-ea77-0b0c1b79fd80.png)

- Inkdropはフォントとかサイズ感とかが好き。
- あと、コードブロックのタイトル付与とか細かいところに手が届いている感じがよいね。
- ファイル管理機能も欲しくなったらInkdropにしよう。
stackの問題：「20. Valid Parentheses」を解いてみる

# はじめに
stackの問題を解いたので，メモしました．

# 「20. Valid Parentheses」を解いてみる
問題へのリンク：
https://leetcode.com/problems/valid-parentheses/

解答：

```python

class Solution:
    def isValid(self, s: str) -> bool:
      if len(s)%2 != 0:
        return False

      dic = {'(':')','{':'}','[':']'}
      stack = list()

      for i, c in enumerate(s):
        if s[i] in dic: # verify only left-bracket
          stack.append(s[i]) # add only left-bracket
        else:
          if len(stack) != 0 and dic[stack[-1]] == s[i]:
            stack.pop() # remove a last element
          else:
            return False

      if len(stack) == 0:
        return True
```
この処理の流れについて説明します．

まず，左・右かっこをそれぞれペアとした辞書，stackというリストを作成します．

そして，入力の文字列を前から順に見ていき，左かっこの場合はstackに追加．(stackに追加されるのは左かっこのみ）
右かっこの場合はstackの最後の要素がペアとなる左かっこであればその左かっこをstackから除去．（そうでない場合はFalseを返し終了）

最終的に，stackの中身が空となればTrueを返します．
[.net、Java連携]C#.netで.jarを実行するンジャー

[本記事は、自分サイトのこの記事と同一内容です。](https://hagiayato.github.io/my-site/jekyll/update/2021/01/09/ConnectCsJar.html)

どうしてもJavaを使わないとできない処理をC#.netから起動する必要が出た場合、
その実装に困りますよね…
そこで参上!C#.netで.jarを実行するンジャー!!(ここまで茶番)
# Java側のプログラム
java側のプログラムはmainメソッド(関数)を入れます。
main関数のコマンドライン引数"args"でC#から値を受け取ります。
下記サンプルプログラムでは、1つの引数でメッセージ：msgを受け取り、それを表示しています。

```java:Sample.java
	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ
		if (args.length <= 0) {
			System.out.println("出力メッセージなし");
		} else {
			System.out.println("出力メッセージ：" + args[0]);
		}
		// Enterキー入力待ち
		// 参考：https://stackoverflow.com/questions/26184409/java-console-prompt-for-enter-input-before-moving-on
		System.out.println("Press \"ENTER\" to exit...");
		Scanner scanner = new Scanner(System.in);
		scanner.nextLine();
	}
```

プログラムができたら、実行可能な.jarファイルを生成しましょう。

# C#(.net)側のプログラム
C#側では、Processクラスのオブジェクトで.jarファイルを起動します。
.jarファイルは、C#(.net)の実行ファイルが生成されるDebug/Releaseフォルダに配置するとファイル指定が楽です。

```C#
// コマンドプロンプトを表示して実行する場合
Process.Start("java", "-jar (.jarファイル名orパス) (引数) (引数)…"))
// コマンドプロンプトを表示せずに実行する場合
Process.Start("javaw", "-jar (.jarファイル名orパス) (引数) (引数)…"))
```

.jarファイルの実行中にC#のプログラムを止めたい場合、WaitForExitメソッドを使いましょう。

```C#
// 終了待ち
jar.WaitForExit();
```

.jarファイルの実行結果は、ExitCodeメソッドで受け取れます。
※javaのmainメソッドが ```void main()``` なら、戻り値0で正常終了

```C#
// 結果取得(0:正常終了)
jar.ExitCode();
```
オブジェクトを作成しますので、usingやclose,disposeなどによるオブジェクト解放をお忘れなく。
参考：[確保したリソースを忘れずに解放するには？［C#／VB］](https://www.atmarkit.co.jp/ait/articles/1710/18/news022.html)
実装例↓

```c#:ConnectJar.cs
        /// <summary>
        /// .jar実行
        /// </summary>
        /// <param name="msg">メッセージ</param>
        /// <returns>True:成功/False:失敗</returns>
        public static bool Excecute(string msg)
        {
            bool result = false;
            Process jar = null;
            try
            {
                // .jarをプロセスとして起動
                using (jar = Process.Start("java", "-jar Sample.jar " + msg))
                {
                    // 終了待ち
                    jar.WaitForExit();
                    // 結果取得(0:正常終了)
                    if (jar.ExitCode == 0) result = true;
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("例外発生\n" + e.Message);
            }
            return result;
        }
```

あとはC#.netを実行するだけ…!
サンプルコード全体は下記リポジトリにあります。
[CSJarソースコード](https://github.com/HagiAyato/CSJar)

# 補足
 - 今回はC#で実装しましたが、VB.netでも同じ方法で.jarの実行ができます。
 - Processクラスを使うと、.jar以外にも様々なプロセスを実行できます。  
   [外部アプリケーションを起動する、ファイルを関連付けられたソフトで開く|dobon.net](https://dobon.net/vb/dotnet/process/shell.html)
macで外部SSDの名前が"~~ 1"となって空白が入ってしまって不都合な時

##環境
macOS Catalina 10.15.6

##結論

そのSSDの名前を空白を使わないものに変更するだけ。

##経緯

原因不明ですが、SSDを繋ぐと以前のSSD(名前をKAMISSDとします)に加えて新しく"KAMISSD 1"という名前でパスが表示されるようになっていました。

```shell
$ cd Volumes/
$ ls
KAMISSD			Macintosh HD
KAMISSD 1
```

といった形です。ターミナル内での操作なら[こちらの記事](https://qiita.com/fantm21/items/aab0b2e55b2ffb4681c4)のように、```/Volumes/"KAMISSD 1"/```などとすれば良いのですが、インストーラーなどでここにインストールしようとするなどすると不都合が生じてしまいます。


どうしたものかと思い、いろいろ調べたのですが、こんなニッチなことに関する記事は見つからず、**SSDの名前を変更したらいけるのか**と試したところ、うまくいったのでここにご報告させていただきます。また、SSDに限らず、他のHDDやUSBメモリなどでも起こり得るかもしれません。

```shell

$ cd Volumes/
$ ls
KAMISSD			Macintosh HD
KAMI_SSD
```

このように```KAMI_SSD```へ変更しました。

ただ、Finder上では認識されていない?```KAMISSD```なる領域があり、これについてはどう使われているか不明です。このような症状が出る前は```KAMISSD```として認識されていたので、その残骸なのでしょうか。

現在、Finderなどでやりとりしたデータは全て```KAMI_SSD```に格納されています。
CSSが理解できなかった私の奮闘記

Progate>youtubeなどの動画教材を見ながら模写コーディングは何度かやったものの、自分がデザインしたポートフォリオのサイトが欠片にできない！置きたいところに要素を配置できない！という壁にぶち当たりました。

コミュニティの先輩方からアドバイスを頂きながら、実行したことを記録したいと思います。

#大前提
・自分で調べるの大事。すぐに理解して覚えられなくてもいいから知識を拾い集めていく感じで。
・デベロッパーツールを駆使して、いいなと思ったサイトの中身を見ること。大量のコードにビビらない。


#フレックスボックスの概念が理解できずに、要素を置きたいところに置けない人向けの学習マップ

###①[フレックスボックスの概念が学べるカエルゲーム](http://flexboxfroggy.com/#ja)
align-itemsやjustify-contentなどを使って、カエルをハスの上に移動させていくというシンプルなゲームです。
後半に続くにつれ難易度が上がって行きますが、小一時間で終わるので、さくっと学習できます。
メソッド

##メソッドとは
一言で言えば処理をまとめて使えるようにするものです。
これだけでは意味がわからないと思うので実際にコードを書いていきます

こんにちはと出力する　hello というメソッドを定義したいとします。

```
def hello              #メソッドを定義するときはdefで始めてendで終わる
  puts "こんにちは"
end

hello                  #メソッドの呼び出し

こんにちは　　　　　　　　　#helloで定義したこんにちはが出力される

```

##戻り値

メソッドは最後の行が戻り値となります。

```
def number
  "1"
  "2"
  "3"         
end

puts number
3              #最後の３が出力
```

##returnを入力すると処理が途中でとまります。


```
def number
  "1"
return  "2" 
  "3"         
end

puts number
2             #途中の2が出力
```
##引数
メソッドなどに値を渡すことができる物
これにより、定義されたメソッドの外の値を持ってくることができるようになる。
（基本的に定義されたメソッドの外で定義された値はメソッド内で使うことができない、この範囲をスコープという）

メソッドを定義する時に記述し処理に使う仮引数と
メソッドを呼ぶ時に実際に仮引数に代入される値を実引数という。

```
def メソッド名(仮引数)
  # 処理
end

メソッド名(実引数)  # メソッドの呼び出し
```
```
def add_number(number)  # ②仮引数 number に実引数の 5 が代入
  puts number + number  # ③numberの値が5なので　5+5の計算がなされる。
end

add_number(5)  # ①実引数に値を入れて、定義されたメソッドを呼び出す
```

引数は複数の指定が可能

```
def メソッド名(第一引数, 第二引数)
  # 処理
end

メソッド名(第一引数, 第二引数)
```
###しかし実引数と仮引数の数は必ず同じである必要があります。

以上です。
インド人になる(Cycle-GANによるドメイン変換)

**はじめ**
インドで本場のカレーを堪能したいのですが、コロナ渦で中々それが叶わず。なので現地に行けないのなら、せめてインディアンスの顔がインド風になればそのカレー欲が満たされるのでは(？)と思いその仮説を検証することにしました。

**なかみ**
CycleGANでインド人の顔と日本人の顔のドメイン変換器を作ります。
以下を参考に実装しました。
[CycleGANをpytorchで実装してみた](https://qiita.com/raglag/items/cfbe89b87335237c5ff2 "Qiita")

学習に必要なインド人の顔の画像と日本人の顔の画像は以下を参考に取得しました。
[API を叩かずに Google から画像収集をする]
(https://qiita.com/naz_/items/efc296ae1bf0e62f6704)

```image_collector.py
class Google(object):
    def __init__(self):
        self.GOOGLE_SEARCH_URL = "https://www.google.co.jp/search"
        self.session = requests.session()
        self.session.headers.update(
            {
                "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) \
                    Gecko/20100101 Firefox/10.0"
            }
        )
```
の`self.GOOGLE_SEARCH_URL`は`"http://www.google.co.jp/search"`
に変えました。SSLの認証がうまく通らなかったようです。

蛇足ですが”日本人 男”などのワードで検索しても外人女性などの写真が多く紛れていて除去作業が大変でした。”海外女性と結婚する日本人男性の特徴5選！”のような記事の画像を入手していたっぽいです。
"indian men"とか"indian male"だとインド人男性と思われる顔を多く入手できるのですが。

顔の切り出しについてはopenCVのカスケード分類でやりました。
[PyTorchを使って日向坂46の顔分類をしよう！]
(https://qiita.com/coper/items/b1fd51062642d624e26f)

こんな感じの画像(128*128)がそれぞれ400枚ほど集まりました。

![0_287_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374876/902017ca-dd27-a75d-c4d2-867057d2e463.jpeg)

![a_190_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374876/bdda6626-6ee2-52f8-29b3-676cc35ea40c.jpeg)


こちらを学習させていきます。
200epochsほど学習させた生成器の出力はこちら

インディアンインディアンス
![A2B_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374876/78b97e70-c791-0456-aee4-5ff4c399c30a.png)

![A2B_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374876/68bc0329-c973-8c7f-76e9-ad5aae2fbf2e.png)

目的とは逸脱しますが、

インディアン菅総理大臣
![A2B_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374876/3616b161-c095-d71c-b478-7828c3c72134.png)

![A2B_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374876/13ae0315-75c2-3f41-9a9f-f16615f8d976.png)




インド人の色合いとテカリ、髭と目元のホリの深さ辺りのドメインは変換できていそうです。少しインド欲が満たされたような気がします。

**さいご**
データ量もepoch数も少なかったようで生成器から出力される画像が粗めでした。もう少し取り組もうかと思いましたが、お昼にもうやんカレーを食べることで当初の目的が達成されそうなので、雑多ではありますがひとまずここで終わらせていただきます。

ここまで読んでくださった皆様ありがとうございました。
Catalystでprimary paneのツールバーにアイテムを表示する

NSToolbarDelegateで、WWDC20で追加されたばかりの新規APIを利用します。

以下のように`primarySidebarTrackingSeparatorItemIdentifier`を配置してください。そうすると、システムがそこをprimaryとsupplementary(もしくはsecondary)の境界線だと認識するので、適切にアイテムを配置してくれます。

この時、上記のidentifierに対して`toolbar(_:itemForItemIdentifier:willBeInsertedIntoToolbar:)`では追加の記述は必要ありません。

```swift
extension ToolbarDelegate: NSToolbarDelegate {

    func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
        let identifiers: [NSToolbarItem.Identifier] = [
            .flexibleSpace,
            .itemPlacedInPrimary,
            .primarySidebarTrackingSeparatorItemIdentifier,
            .toggleSidebar,
            .flexibleSpace
        ]
        return identifiers
    }

    func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
        return toolbarDefaultItemIdentifiers(toolbar)
    }

```