OmniAICreator/Qiita-1.07M huggingface dataset:
CDNとは？初学者大歓迎

#本記事の内容
本記事はCDNに関して記述しています。
加筆・修正等ございましたらお手数ですがご連絡いただけますと幸いです。
#CDNとは
Content Deliverry Networkの略称。
ウェブコンテンツを効率的かつスピーディーに配信できる様に工夫されたネットワークのこと。
略称だけ見るとデリバリーってあるからピザの配達とかのイメージ・・・？コンテンツをデリバリーするネットワークの仕組み的な・・・？ってイメージでした。
取り合えずさらに深堀っていきます。
#なぜ必要？
>インターネットを利用するユーザーが急激に増加していることに加え、動画配信などインターネットに流れるウェブコンテンツが大容量化する昨今、ネットワークの負荷は高くなり続けています。アクセスが集中して、ホームページが表示できなくなったり、コンテンツのダウンロードに時間がかかったりすることも少なくありません。
そういった問題を解決するために作られたネットワークの仕組みがCDNです。CDNではウェブコンテンツ配信用にネットワークを最適化することによって、アクセスが集中したりコンテンツが大容量化したりしても、ホームページの表示やコンテンツの配信に問題が起こらない様にすることが可能です。

ほうほう,,,
みんながスマホやPC持ってるし一気に一つのサイトにアクセスしたらそりゃ情報捌き切れませんな。オリンピックの抽選の時みたいな感じかな？
言ってることはわかるけど実際どうやってるん・・・？


#どうやっている?
ズバリ、<font color = "red">キャッシュサーバー</font>というものを使用しています。キャッシュサーバーとは、世界中に配置された代理サーバーのこと。

オリジナルのサーバー（オリジンサーバー）のみだとアクセスの過多等に影響を受けやすい、、、だったらアクセスを分散させるためにサーバーも分散させてしまえば、オリジンサーバーのみの時と比較して負荷を軽減させることができるんじゃない？？ってことかな...っていうことはキャッシュサーバーにはオリジナルのウェブコンテンツのコピーがありますよねえ

#CDNののもう一つの仕組み
キャッシュサーバーは世界中に配置されている。ユーザーは近くのキャッシュサーバーにアクセスすることで快適にウェブコンテンツを取得することができる。

#最後に
頭の中を整理するためにも少し会話チックに書いてみました。
お読みいただきありがとうございました。

#参考にした記事

https://www.kagoya.jp/howto/network/cdn/
Rubyの乱数関数がめっちゃ分かりやすい件

Rubyの乱数関数がめっちゃ分かりやすいです。
他の言語ならクラスをimportして使うのですがRubyは
例えば21から30までの乱数を表示する場合

```Ruby
puts rand(21..30)
```

こんなに分かりやすいです。
[Laravel] 本番環境で環境変数をテストするアイディアを考察

先日は話題になりました[こちら](https://qiita.com/reopa_sharkun/items/f3819b2e8727728da82a#comment-e972a4d62e23ba15f076)の記事を拝見しまして、
色々考えることがありましたので、こちらの記事を書くことにしました。

## はじめに
Laravelの`facade/ignition`パッケージに脆弱性（CVE-2021-3129）が見つかり、
上記の記事では、筆者さんの注意喚起やご自身のマルウェア感染体験談を紹介してくれています。

しかし、こちらの脆弱性はちょっと特殊な事情があります。
脆弱性の対象が、本番環境では利用されるはずのないパッケージであることや、Laravelの環境変数の設定ミスを狙ったものになっています。
なので、公開サーバーにデプロイされているLaravelを適切に設定していれば、該当しないことが基本です。

Laravelに限った話ではありませんが、アプリケーションを本番環境にデプロイするのは、神経質な作業を伴います。
個人的なプロジェクトだったら良いのですが、会社などの組織としてクライアントやエンドユーザーに
製品として提供するアプリケーションの場合は、組織が全責任を負います。

厳密には、会社の代表を含む役員が責任を負うことになりますので、
リスクを回避する一定の仕組みを責任者が設ける必要があります。

私も極小規模の会社ではございますが、一応代表の立場ではありますので、責任者として仕組みを作れないかと思い、
アイディアレベルの内容ではありますが、こちらの記事に試作を紹介したいと思います。

## Laravelの環境変数をデプロイ時にテスト
早速ではございますが、下記にて試作を作ってみました。
[https://github.com/timoshka-lab/laravel-health-check](https://github.com/timoshka-lab/laravel-health-check)

現時点では、数時間で作れるクオリティーでしかないので、実践では使えません。
あくまでもアイディアの一つとして皆さんのご意見やフィードバックを頂くことが目的のプロジェクトになっています。

## パッケージの目的
* Laravelアプリケーションの環境変数が、本番環境に見合った内容で設定されているかをテストしたい
* Composerで `require-dev`のパッケージがインストールされていないかをテストしたい
* Laravelパッケージは汚したくない、独立した場所で検証したい
* コマンドラインインタフェースで操作したい
* テストケースを誰でも分かりやすい形にしたい（追加も安易）
* 可能な限りシンプルに作りたい（役割が限られているので、管理を踏まえて作り込みすぎない）

## PHPUnitを有効活用した試作
試作では、artisan風にコマンドラインから入力を受け取り、PHPUnitでテストケースを走らせる形にしました。
PHPUnitのテストケースであれば、誰でも理解しやすいし、管理もしやすいことが主な目的です。

## ご利用時の注意点
rootユーザーでの実行はお控え下さい。コマンド内部では、ターゲットになっているLaravelアプリケーションをブートしていますので、
場合によっては、ログファイルがroot権限で書き込まれてしまいます。phpの実行ユーザーでご利用下さい。

## プロジェクトの今後
使えそうなアイディアであった場合は、徐々にリファクタリングや機能の追加をしていこうと思います。
現時点では試作レベルのため、DBやキュードライバーの対応が不十分だったり、Redisなどの環境変数を検証できていなかったりなど、
まだまだ実践での利用からは程遠い状態です。

## 同様のプロジェクト
実は同様のプロジェクトが既にPackagistに登録されています。その多くは、Laravelの内部から利用するものなっていたため、
今回の目的に合いませんでした。（すべて確認したわけではありませんが。。。）

## ご意見ください
どんなご意見でもOKです。すべてのご意見を快く受け止めます。

## 番外編
Composerでインストールしているパッケージに脆弱性が含まれていないか、検証できるプロジェクトが存在しています。
ご存知の方も多いとは思いますが、一応記載しておきます。
[https://github.com/FriendsOfPHP/security-advisories](https://github.com/FriendsOfPHP/security-advisories)
VisualStudioで小さなテストプログラム群を1プロジェクトに

## SmallSharp

言語やプログラムの挙動チェックするために小さなプログラムを書くことがあります。
VisualStudioでやると`ConsoleApp1`など適当なソリューションが山のようにできてしまいます。
ソリューションにプロジェクトをたくさん作る手もありますが、若干煩雑です。

[SCOTT HANSELMANさんのブログ](https://www.hanselman.com/blog/tiny-toplevel-programs-with-c-9-and-smallsharp-and-visual-studio)で紹介されていた [SmallSharp](https://www.clarius.org/SmallSharp/)を使うと、このように、**csファイル1つが1プロジェクト**のように扱えるようになります。（作者HPから）
![sample](https://www.clarius.org/SmallSharp/img/SmallSharpDemo.gif)

## インストール方法

nuget で `SmallSharp` をインストールします。

以前動作しなかった方は、作者にバグ報告して直してもらったので(ver1.1.1)でもう一度試してみましょう。（プルリク書いたのに、すぐ直されてしまった・・）

## 使い方

csファイル1つで完結したプログラムを書きます。
アクティブドキュメントを切り替えると自動的にコンパイル対象が切り替わります。
あとは普通にF5等でBuild&Runしましょう。

C#9.0で導入されたトップレベルプログラム（namespaceやMain()なしで書く）と相性がいいです。C#10.0も待ち遠しいですね。
DataDog/dd-trace-goがgorm.ioをサポートしている話

# はじめに
さくっとした投稿です。
Gormメジャーバージョンアップがあり、importパスが`github.com/jinzhu/gorm`から`gorm.io/gorm`になったのはご存知の方も多いと思います。

DatadogのAPMを利用していた場合、gormとDatadogのトレースを紐づけるライブラリhttps://github.com/DataDog/dd-trace-go/ の開発が追いついておらず、`gorm.io/gorm`に対応できていなくて困っていた方も多いかと思います。

ふとレポジトリをあさっていたら、今までの
`gopkg.in/DataDog/dd-trace-go.v1/contrib/jinzhu/gorm`
ではなく、
`gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1`
に、3か月前の2021/1/5に爆誕しているのを発見しました。

こいつに触れてあげている情報がネット上だと皆無だったので、気づいてあげられるようブログにすることにしました。

# どう対応すればいいか
## As Is
今までは以下のようにしてました。

```golang
import (
  .
  .
  gormtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/jinzhu/gorm"
)
// アプリ起動時に一度だけ実行される
func Connection() *gorm.DB {
  .
  .      
  db, _ := gorm.Open("postgres", dsn)
  db = gormtrace.WithCallbacks(db)
  db.DB()
  db.DB().SetConnMaxLifetime(MaxLifetime)
  db.DB().SetMaxIdleConns(MaxIdle)
  db.DB().SetMaxOpenConns(MaxOpen)
  db.LogMode(Debug)
  return db
}
```

```golang
import (
  .
  .
  "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

// sql実行時
// txはConnectionの戻り値dbに対して、db.Beginしたもの
func ExecQuery(ctx context.Context, tx *gorm.DB) {
  tx = gormtrace.WithContext(ctx, tx)
  if result := tx.Save(&record); result.Error != nil {
  ..

```

## To Be

```golang
import (
  .
  .
  gormtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1"
)

// アプリ起動時に一度だけ実行される
func Connection() *gorm.DB {
  // gormtraceはOpenにのみ必要
  db, _ := gormtrace.Open(postgres.New(postgres.Config{
       DriverName: "postgres",
       DSN:        dsn,
    }), &gorm.Config{
       Logger: logger.Default.LogMode(LogLevel),
    }, gormtrace.WithAnalytics(true),
  )
  db, err := db.DB() 
  db.SetConnMaxLifetime(MaxLifetime)
  db.SetMaxIdleConns(MaxIdle)
  db.SetMaxOpenConns(MaxOpen)

```


```golang

// sql実行時
// txはConnectionの戻り値dbに対して、db.Beginしたもの
func ExecQuery(ctx context.Context,tx *gorm.DB) {
  // tx.WithContext(ctx)をすると、tx.Statement.Contextに格納
  // traceするときにtx.Statement.Contextをみてくれている。
  if result := tx.WithContext(ctx).Save(&record)result.Error != nil {
  ..

```

少しシンプルになっていることがわかる。
これはgormが`Begin`意外にもcontextを与えられるようになったからだろう。

ソースでいうとこの辺り

https://github.com/DataDog/dd-trace-go/blob/3b61a6b4186a87ec01745a241ca047c0b8748c11/contrib/gorm.io/gorm.v1/gorm.go#L107-L124

```golang
  // db.Statement.Contextから取得
  ctx := db.Statement.Context
  // クエリ実行前のcallbackにて値が格納されている
  t, ok := ctx.Value(gormSpanStartTimeKey).(time.Time) 
  if !ok {
    return
  }
  opts := []ddtrace.StartSpanOption{
    tracer.StartTime(t),
    tracer.ServiceName(cfg.serviceName),
    tracer.SpanType(ext.SpanTypeSQL),
    tracer.ResourceName(db.Statement.SQL.String()),
  }
  if !math.IsNaN(cfg.analyticsRate) {
    opts = append(opts, tracer.Tag(ext.EventSampleRate, cfg.analyticsRate))
  }
  // ctxからspanを取得可能
  span, _ := tracer.StartSpanFromContext(ctx, operationName, opts...)
  span.Finish(tracer.WithError(db.Error))
}
```
WindowsTortoiseGitで複数リポジトリをクローン出来ない時の対処法

# WindowsTortoiseGitで複数リポジトリをクローン出来ない時の対処法

## 目的
TortoiseGitを利用していると、`repository not found`などのエラーが発生することが多々あり、その都度Gitをデバッグモードで調査したりする為、対処方を残しておく

## 内容
### 1.認証準備
`tortoisegit > 設定 > Git > 資格情報 `

```
設定タイプ:グローバル
URL:{リポジトリのURL}
ヘルパー:manager
ユーザー名:{Git認証ユーザー名}
```

### 2.クローン
普通にクローンする（ユーザー名、パスワード等は指定しない）

```
URL:{リポジトリのURL}
```

## うまくいかない時

### Windows資格情報をクリアする
`資格情報マネージャー > windows資格情報` > うまくいかないGitのURL情報を削除

### Git認証情報を削除する
GitConfigの内容を確認し、`tortoisegit > 設定 > Git > 資格情報 `より削除する ※手動で削除しない方がよい

```
git config --global -l
git config --system -l
git config --local -l
```
### GitCloneをデバッグ実行する
`not found`などエラー内容が殆どわからないので、デバッグモードで内容を確認する
GitBashで確認

```sh
GIT_TRACE=1 GIT_CURL_VERBOSE=1 git clone https://github.com/xxxxxxx
```
【Devise】 ログイン失敗時の処理をカスタマイズ

## はじめに
現在カフェHPをRailsを用いて開発中です。管理者画面へのログイン機能をDeviseを用いて実装していく中で、`Devise` にデフォルトで設定されている挙動に苦しめられた為、`Devise`のカスタマイズ方法を備忘録として残しておきます。


## やりたいこと
ログイン画面ではヘッダーを表示させない。

## 現状
ログインフォーム未入力等でログイン失敗した際に、ログインページが再度読み込まれてヘッダーが表示される。

※ちなみにヘッダーはログイン画面で表示されないように以下の記述をしています。

```ruby:views/layouts/application.html.erb
<header>
  <% unless current_page?(admin_session_path) %>
    <%= render "layouts/header-img" %>
    <%= render "layouts/nav" %>
    <%= render "layouts/burger"%>
  <% end %>
</header>
```



## 原因
どうやらDeviseはデフォルトのコントローラー設定により、ログイン認証に失敗すると、自動でsessionコントローラ内の別のアクションにリダイレクトする模様。

しかしターミナル上でログインボタンを押した際の挙動を確認してみると`Completed 401 Unauthorized`のエラーが吐き出され、新規にログインページ`sessions#new`を読み込んでおり、これが原因っぽい。


## 解決方法
やり方として正しいかはわからないが、`sessions_controller`の`create`メソッドをカスタマイズして、リダイレクト先を指定することでヘッダーは表示されなくなった。

備忘録のため、以下にその際の手順を記載します。

### 【Devise】 createメソッドカスタマイズ方法

#### 1. Deviseデフォルトの設定を受け継いだコントローラーを作成

まずはDeviseデフォルトのコントローラー設定を継承したカスタムコントローラーを作成していきます。


```sql:ターミナル
rails g devise:controllers admins
```
- **コントローラー名なので複数形にする**  
- **`admins` の部分は作成したモデル名**  
  一般ユーザーしかいないなら`users`
  
  
- 作成されたファイルを確認。  
以下のファイルが作成されるので、カスタマイズしたいコントローラーを編集していきます。  今回は`sessions#create`をカスタムしたいので`sessions_controller.rb`をイジっていきましょう。

```sql:ターミナル
create  app/controllers/admins/confirmations_controller.rb
create  app/controllers/admins/passwords_controller.rb
create  app/controllers/admins/registrations_controller.rb
create  app/controllers/admins/sessions_controller.rb
create  app/controllers/admins/unlocks_controller.rb
create  app/controllers/admins/omniauth_callbacks_controller.rb
```


#### 2. `routes.rb`の修正

続いてカスタムコントローラーを読み込む為に`routes.rb`を以下のようにします。

```ruby:routes.rb
devise_for :admins, controllers: {
    sessions: "admins/sessions"
  }
```

#### 3. createメソッドをカスタマイズ


ここからcreateメソッドをカスタマイズしていきますが、まずは分かりやすいようにDevise デフォルトの設定をコピペしてきます。

```ruby:app/controllers/admins/sessions_controller.rb
def create
    self.resource = warden.authenticate!(auth_options)
    set_flash_message!(:notice, :signed_in)
    sign_in(resource_name, resource)
    yield resource if block_given?
    respond_with resource, location: after_sign_in_path_for(resource)
end
```
１行目に書かれている
`self.resource = warden.authenticate!(auth_options)`により、ログイン認証に失敗すると、自動でsessionコントローラ内の別のアクションにリダイレクトするようになっています。


デフォルトではnewに設定されていますが、今回は改めてリダイレクト先を設定してやりましょう。



書き換えるには`auth_options`の設定値を書き換えてやる必要があります。
`auth_options`自体はDeviseのデフォルトコントローラに定義されていて、直接はさわれないため、オーバーライドしてやります。


```ruby:app/controllers/admins/sessions_controller.rb
    def failed
        redirect_to new_admin_session_path
    end

protected

  def auth_options
    { scope: resource_name, recall: "#{controller_path}#failed" }
  end
```

`fail`アクションを用意して、認証失敗時にはそちらにリダイレクトするようにしています。
これでログイン失敗時にもヘッダーが表示されることはなくなりました！

## さいごに

今回はDeviseをカスタマイズすることで、とりあえずは表示に問題なくなりました。

しかしDeviseについては、まだまだ理解不足なところが多いため、デフォルトの挙動についても少しずつ理解していきたいと思います。

間違いなどがありましたらコメント頂けると嬉しいです。
npmでyarnを入れられなかったのでhomebrewでインストールした

npmでyarnを入れられなかったのでhomebrewでインストールしました。

# npmでyarnをインストール

```
% npm install -g yarn
```

```
added 1 package, and audited 2 packages in 1s

found 0 vulnerabilities
```

良さそう

```
% yarn -v
zsh: command not found: yarn
```

あれ :thinking: 
使えませんね。

homebrew経由でインストール

```
% brew install yarn
```

良さそう :clap: 

```
% yarn -v          
1.22.10
```

pathも大丈夫そうです。

```
% which yarn
/usr/local/bin/yarn
```

# 所感
何故npmでダメだったのかはわかっていない…
もしかしたら以前homebrewでインストールしてたのかな。
OSとAPIの関係

ソフトウェアには種類があります。

#　①応用ソフトウェア
これは、計算ソフトなどが挙げられます。ワープロなどもこの分類に入ります。
私達が、作っているアプリケーションもこの分類になります。(Javascriptとか)

#　②ミドルウェア
これは、データベース管理ソフトウェアや各開発支援用のソフトになります。
後で記載します基本ソフトウェアとの中継役を務めています。
また機能としては、データベースサーバー、アプリケーション・サーバー、Webサーバーがあります。このデータベースサーバーには、大量のデータベースがあります。応用ソフトウェアがデータベースを使いたいなとおもったら、ここにリクエストをおくります！(Node.jsとかMysqlとか)

#　③基本ソフトウェア
これは、ハードウェアとの中継役を担っています。ハードウェアが一生懸命入力してもOSがないと応用ソフトウェアには指示が行きません！
これをOSといいます。種類は、Windows,MacOs,MsDos,Linuxがあります！！

上記3つを総合して、ソフトウェアといいます👍

#基本ソフトウェア
さて、基本ソフトウェアにも種類があります😅

#　①制御プログラム
これはOSの中核を担っていますし、これがメインのお仕事です。

#　②言語プロセッサ
これは、プログラミング言語(JS,Ruby,Javaなど)をコンピュータ語に翻訳してくれます。

#　③サービスプログラム
これは、コンピュータの補助プログラムです！

Os結構大切な役割ですよね😁　そもそもこの子使えないとなるとCPUを作ったりしないといけないんですね😇


#インターフェイス
OSの大切なことがわかったところで、いくらOSが優秀でも、つなぐものがないと動きませんそれがインターフェイスです！！　国同士の外交官みたいなもんです。


#　ソフトウェアインターフェィス
見えないからなかなか曲者です。
これは、プログラム間をつないでくれます。データのやり取りなどを担います。
言語学習しているときに画面に最初に[Hello World]と出しますよね？？
これは、OSじたいが備えている機能になります。今回は、Macと想定しますが。MacというOSじたいが備えている機能をアプリケーションからよびだしています。これをAPIといいます。
techacademy Webアプリケーションコース 受講レビュー

### 独学6ヶ月からtechacademyでWebアプリを作るまで
昨年に初Macbook購入を機会にプログラミングに興味を持ち、Progateの有料コースで半年ほどRubyとRailsについて独学で勉強しました。
ある程度進んだところで、独学での限界を感じました…
そこで色々とスクールを探しましたが、どこもかなりの金額…
とりあえずレビューと無料メンタリングでの対応の良さ、あとは金額でtechacademyを選びました。
以下に受講内容とレビューを書いておきます（受講内容などで間違いがあってもお許しください…）
これから受講をお考えの方の参考になれば幸いです。
※作成したアプリへのリンク[(Groups)](https://www.groups-app.net/)も入れていますので、どの程度のものができるかの参考になればと思います。

####・受講コース【Webアプリケーションコース 8週間】
選んだWebアプリケーションコースは、全てのカリキュラムを合計１６０時間でやり切るというものでした。
私は平日2～3時間、土日に各5時間、週に20時間できると試算し、8週間コースに申し込みました。
契約後、思いがけないことが…！！（
なんと、開始日以前にカリキュラムの閲覧ができる！…（契約内容ちゃんと読まない悪い癖…）
自分は開始日の2週間ほど前に申し込みしましたが、振込後にすぐカリキュラムが見れました！
（キャンペーンかなにかかもしれないので、申込前に各自でご確認ください）  
契約期間の8週間は、【課題評価】【メンターへの質問】【メンタリング】ができる期間でした！  

####・カリキュラム内容
基本的にはサイト上のテキストを読み進めていき、課題をこなしていく形です。
####0. 事前準備
受講中に使うシステムへの登録や設定を行いました。画像付きの説明で、PCが得意でなくても簡単に進められると思います。  
#####1. Web開発のために学ぶこと
Webアプリケーションとはなにか！？インターネット通信とは！？という基本的な内容を学びました。  
#####2. HTML/CSS
Webページの構成、HTML・CSSの役割を学びます。  
#####3. Bootstrap
ＣＳＳを書かずに、Webページの見た目を整える方法を学びます。独学時はこんな素敵なものがある事も知りませんでした…  
#####4. ターミナル
ターミナルでの操作の基本を学びました。実行したい事をコマンドで指示するのですが、コマンドがなかなか覚えられず…。私は基本的なコマンドのみ覚え、あとは使いたいときに調べるようにしました。  
#####5～8. Rubyについて
プログラミング言語、Rubyとはなにか？に始まり、Rubyでの基本的な処理を学びました。ここまでは独学である程度万でいた為、スラスラと進めました。  
#####9～10. MySQL
データベースの取扱いについて学びました。独学時はデータベースをあまり意識する事はありませんでしたが、テキストを進めていく事でデータベースの重要性を学ぶことができました。  
#####11. Git/GitHub 
履歴の残し方や共有の仕方を学びました。最初は途中までのデータを残していくだけくらいの認識でしたが、最終的にアプリを作る過程で、バージョン管理がいかに大切かを気づかされました。
（ちなみにオリジナルアプリ作成時に、キー（他人に知られてはいけないもの）をGitHubにプッシュしてしまい、冷や汗をかきました…）  
#####12. インターネット通信の仕組み
インターネット通信が、どのような仕組みで行われているかを学びました。なんとなくのイメージはありましたが、学ぶことで知識となり、通信を意識できるようになりました。  
#####13. メッセージボード
Railsを使用したアプリ開発をしました。Railsとはなにか？どのように機能するのかなど、基本的な部分を学びながら、アプリを組み立てられました。
#####14. Heroku
作成したアプリをWeb上に公開する手法を学びました。思ったよりも簡単でしたが、実際にオリジナルアプリを公開する際に何度か躓きました（テキストをきちんと理解していなかった為）  
#####15. Twitterクローン
メッセージボードで学んだ基本を基に、ユーザー認証や、フォロー機能を学びました。ここでの1対多などの考え方で少し躓きましたが、メンターの方の説明で理解する事ができました。  
#####16. オリジナルアプリ作成
これまで学んだことを基に、オリジナルのWebアプリケーションを作成しました。どんなアプリを作るかを1から企画し、組み立てていきました。
基本的な部分はテキストを読み返しながら作成できましたが、この機能が欲しい！というものは基本的に自分で調べて実装します。メンターの方は、基本的にテキスト外の事は答えてくれませんが、的確なアドバイスや助言をくれます。これにより自分で解決する努力と能力が身についたと思います。

#####・作成したオリジナルアプリ
最終課題で作成したオリジナルアプリを公開しています。
[Groups](https://www.groups-app.net/)
私はこの程度までしか作れませんでしたが、やる気次第ではもっと完成度の高いアプリを作る事ができると思います！  

#####・週に2回のメンタリング
1人の現役プログラマーの方が専属となり、週に2回、各30分のメンタリングを実施しました。メンタリングではテキストで分からない部分を直接聞いて教えてもらったり、テキスト外の事にも親切に答えてもらいました。
自分がはまってしまった事も、違った視点から解決へのアドバイスをいただき、経験を積んでいる方は違うなと思いました。  
#####・チャットでのメンターへの質問
現役プログラマーの方々へ、直接質問ができます！困ったことをすぐに誰かに聞ける状況は、周りに聞ける人がいなかった私には一番ありがたい事でした。
時間が15時～23時となるので、午前中に質問したことは回答までに少し時間が空いた気がします。
メンターの方々には、訳の分からない質問ばかりしました…。見捨てずに気付きを与えてくださり、ありがとうございました！！  

####まとめ
テキストを読んで課題を進めていく形ですので、ご自身のやる気次第だと思います。
私は毎日できる限りの時間を費やし、夢の中でもエラーに悩まされました（笑）
この8週間は四六時中プログラミングの事を考え、集中して学習できたと思います。
せっかく学んだ技術を使っていけるよう、これからも学んでいきたいと思います！
個人的な最終目標は、プログラマーへの転職！！（30代半ばで難しそうですが…）

長くなりましたが、読んでいただきたいありがとうございました！
変わるためには「環境＞決意」大きくてふくよかなクマみたいな見た目のお兄ちゃんが激やせした話

# はじめに
知り合いの**大きくてふくよかなクマみたいな見た目のお兄ちゃん**が激やせしたという話を聞きました。
痩せた理由を聞いてみたところ

>元々筋トレは嫌いじゃなかった。
ジムまで行くのが面倒で色々な理由をつけて「○○だから（暑いから、雨だから...etc）しょうがないよね」ってやらない理由を探していた。
コロナを機に筋トレ道具を買ったら自然と筋トレが続くようになった。

なるほど。
今までずっと変わらなかった知り合いの変化を目の当たりにしてちょっと感動しました。
（そして逆に年々太っていっている自分が恥ずかしくなりました）

**「やらない理由なくす」**当たり前のことですけど結構難しいですよね。
その方法として**「環境を変える」**はまさに最善の方法だと思います。
私たちエンジニアの多くも**スキルアップが必要**と考えていても行動に起こせない人は多いのではないでしょうか？

# 「決意する（だけ）」は無意味
私は過去何度も**「決意」**をしてきました。

「スキルアップのために勉強しよう」

これが社会人になってから何度も**「決意」**して何度も無駄にしたものです。
結局**「決意」**しても何も変われなかったです。

# 環境を変える
ではどうすれば変われるのか。
その激やせした知り合いのように**「勉強する環境」**を整えたいと思います。
具体的には

## １．やらない理由を排除する
知人がとった方法ですね。
「○○だから（暑いから、雨だから...etc）しょうがないよね」をつぶしていく。
とにかくやらない理由をあげきって物理的につぶしていきます。

## ２．思いつくことを手当たり次第にやる
とにかくすぐ行動することで**「行動する環境」**を自分で作ります。
本を買ったりUdemyでセミナーを買ったりなんてすぐに出来ることです。

## ３．人に宣言する
上司にでも知人にでもいいです。Twitterで全世界に対してでもいいかもしれません。
とにかく**「人に宣言することで後に引けない環境を作る」**こと。
有言不実行は格好悪いですからね。

## ４．付き合う人を変える
付き合う人によって人間は思考が左右されます。
向上心の高い人と付き合えば自分も向上心が高くなります。
詳しくは**「ミラーニューロン」**で調べてみてください。

## ５．ルールを決める
これは若干**「決意」**のような気もしますが・・・
自分にルールを課します。
例えば「１日１時間を勉強の時間とする」とルールを決めて守っていく。
どんなに忙しくても、１日１時間くらいならば捻出可能です。
睡眠の時間を８時間としても、残り１６時間あります。
そのうち１時間は、約６％です。６％くらいなら絞り出せそうに思えますよね？

# さいごに
私は「Qiita投稿を週1回はする」と自分でルールを作り人に宣言することで変わりました。
前より格段に勉強する時間は増えています。
みなさんも環境を変えることで自らを変えてみてください！

以上です。
Rust でパラメタライズドテスト

今回、Rustでテストコードを書いていて、パラメタライズドテストを省エネで書きたくなった。テストコードにしたって、何度も同じタイピングなんてしたくない。コピペなんて言語同断。

# かなしいコード

例えば、こんなコード。

```rust
pub fn add(a:i32, b:i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_add_1_1_returns_2() {
        assert_eq!(add(1, 1), 2);
    }
    #[test]
    fn test_add_0_100_returns_100() {
        assert_eq!(add(0, 100), 100);
    }
}
```

テストケースごとに似たような見た目のテストコードが増えていくことが目に見えている。

Go言語では、テストケースを構造体にまとめて、テストコードを見やすくすることを推奨している。
[GitHub の Wiki にテーブル駆動テストという項目に掲載されている](https://github.com/golang/go/wiki/TableDrivenTests)。

また、Python の標準ライブラリの一つである [unittest にも SubTest という似たような仕組みがある。](https://docs.python.org/ja/3/library/unittest.html#distinguishing-test-iterations-using-subtests)

一方、Rustにはそのような仕組みがない。Rustは標準ライブラリが小さいことがよく知られているので仕方ない。だからといって、テストコードをコピペしてみっともない真似はしたくない。


# Crates.io から探さずに自作する

プログラミング言語には、標準ライブラリ以外にサードパーティライブラリがあるため、さほど困らない。Rust には crates.io がある。JUnit にインスパイアされたという [parameterized](https://crates.io/crates/parameterized) というライブラリを使うのが簡単そうだ。

Go や Python の例をみるとお分かりのとおり、シンプルな実装でできそうな気がしてくる。

Rust には強力な macro 機能がある。

# マクロで作ってみた

```rust
#[cfg(test)]
mod tests {
   macro_rules! test_add {
        ($($name:ident: $value:expr, )*) => {
            $(
                #[test]
                fn $name() {
                    let (lhs, rhs, expected) = $value;
                    assert_eq!(crate::sample::add(lhs, rhs), expected);
                }
            )*
        };
    }

    test_add! {
        add1: (1, 1, 2),
        add2: (0, 100, 100),
    }
}
```

macro_rules を使って `test_add` マクロを生成する。今回は シンプルなコードなのでマクロを作る手間のほうが大きいが、このコードがサクッとかけるとライブラリに頼ることなくコードがかける。

関数単位のテストケースをマクロで生成しているので、テストが失敗してもほかのテストケースのテストは続行される。


# なぜライブラリに頼らないか

ライブラリは便利だが、たくさんインストールすると依存性の解消や、脆弱性への対応などメンテナンスコストが高くなる。

ライブラリ選定に時間をかけるより、自分で書いたほうが早いということもある。盲目的に選択肢を絞るのではなく、いつでも多くの選択肢を見つけられるようにしておくとよい。

※とは言っても、マクロはデバッグが大変になるのであまりお勧めはしない。


# 実際にマクロを使ってコードを圧縮した事例

37行のコードが18行に圧縮された上、テストコードの意図が読み取りやすくなった。

https://github.com/tamanobi/rusty-journal/commit/baff80a05e19513a78316df3342b21c308ea6ddb
Qiitaのトレンドの週間と月間が消えていた件

## 気づいて公式blogをみると

以下[公式blog](https://blog.qiita.com/feed-update-202103/)のアップデート内容の引用です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260307/4fdef924-17db-001a-6b63-a346f5b7c844.png)

アップデート以降のホームは以下のようなものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260307/ce55a166-e115-eb57-fc5d-15929dd74f24.png)

つまり、アップデート後の
・ホーム：より興味がある分野のみAIが推奨してくれる。
・トレンド：一日のみ
となったようです。

## 代替策はあるかな

トレンドの週間と月間で幅広く、ほかの分野のおもしろい記事を探し出すのに、とても愛用していましたが、
機能として消えてしまいこまりました。。
ほかにも同じように使用していた方もいると思いますが、困っていないでしょうか（代替策があったらぜひ教えていただきたい。。）
Zoomミーティングをコマンドで起動する方法

プロジェクトの定例会議でZoomを使っているのですが、会議のたびに
>Zoom起動 → ID入力 → パスワード入力

を毎回やるのが面倒になってきたので、どうにか自動化できないかと思い調べました。
自分の備忘のために記録します。

※2022/4/26 PowershellでID/PWが書かれたCSVを読み込んで表示する方法について追記しました

## コマンド
Zoom会議に参加する時
`start zoommtg:"//zoom.us/join?confno=<会議ID>&pwd=<パスワード>"`

※\<会議ID>、\<パスワード>は読み替えてください
※自分がホストの時の会議はパスワードは不要です

これをBatで作るだけ。とっても簡単。

## 参考にしたサイト
以下のサイトを参考にしました。ありがとうございます。

https://note.com/pinponpin/n/n0776759526c6

## おまけ
タスクスケジューラで会議が始まる時間に勝手に起動させる方法は、[こちらのサイト](https://blog.isarver.com/automatically-join-zoom/) に載っていました。
起動時のオプションについては以下のサイトで語られていますが、特に気にしなくても上のコマンドだけで十分そうです。

https://medium.com/zoom-developer-blog/zoom-url-schemes-748b95fd9205

## 2022/4/26 追記 会議ID/パスワードが書かれたCSVを読み込んで表示するPowershellコマンド
Zoom会議毎にバッチを作るのはイマイチだったので、自分が使いやすいようPowershellで実装したものを共有します。プログラミングのセンスはないのでもっと良い感じに改造してあげてください。
また、利用する際は間違ってID/PWが漏洩しないようにCSVファイルの取り扱いには十分注意してください。（自己責任です）

#### 使い方
1. zoommtg.csvに会議名、会議ID、パスワードを記載する
2. zoommtg.csvをzoomJoin.ps1と同じフォルダに格納する
3. zoomJoin.ps1を起動する
4. zoommtg.csvの内容が一覧表示されるので、接続したい会議を選んでOKを押す
5. Zoom会議に接続される

```zoommtg.csv
name,id,pw
会議A,01234567890,111111
会議B,11111111111,222222
会議C,99999999999,999999
```

```powershell:zoomJoin.ps1
# [System.Windows.MessageBox]を使用するためにインポート。使わないなら不要。
Add-Type -Assembly System.Windows.Forms

# Zoom会議の情報を格納する配列、オブジェクトを定義
$zoomMtgs = @()
class CustomObject {
    [string] $name
    [string] $id
    [string] $pw
}

# Zoom会議一覧の取得
# zoommtg.csvはこのファイルと同じディレクトリに置くこと

if (Test-Path "${PSScriptRoot}\zoommtg.csv") {
    $csvFile=@(Import-CSV "${PSScriptRoot}\zoommtg.csv")
}else{
    $result = [System.Windows.MessageBox]::Show("プログラムと同じフォルダにzoommtg.csvが存在することを確認してください。","確認","OK","Warning")
    return
}

# csvデータをオブジェクト、配列に格納
foreach($data in $csvFile){
    $zoom = New-Object CustomObject
    $zoom.name  = $data.name
    $zoom.id    = $data.id
    $zoom.pw    = $data.pw
    $zoomMtgs += $zoom
}

if ($zoomMtgs.Length -eq 0) {
    $result = [System.Windows.MessageBox]::Show("会議情報がありません。","確認","OK","Warning")
    return
}

# Zoom会議一覧をGridViewで表示して、接続したい会議を選択してもらう
$selected = ($zoomMtgs | select name,id,pw | Out-Gridview -Title "zoom会議一覧" -OutputMode Single)

# 選択されたZoom会議の情報で接続する
if ($selected) {
    $name = $selected.name
    $id = $selected.id
    $pw = $selected.pw

    # 接続前に確認画面を出したかったら以下を使う
    # $result = [System.Windows.MessageBox]::Show("次の会議に参加しますか？`n  会議名：${name}`n  会議ID：${id}`n  パスワード：${pw}", "確認", "OKCancel", "Question", "1")
    $result = "OK"
    if($result -eq "OK"){
        start zoommtg:"//zoom.us/join?action=join&confno=${id}&pwd=${pw}"
    }else{
        return
    }
}else{
    return
}
```
docker-composeでMySQLレプリケーション構築

## ディレクトリ構成

```
.
├── docker/
│   ├── primary/
│   │   ├── Dockerfile
│   │   ├── init.sh*
│   │   ├── mysql.cnf
│   │   └── mysqld.cnf
│   └── replica/
│       ├── Dockerfile
│       ├── dockerize-linux-amd64-v0.6.1.tar.gz
│       ├── init.sh*
│       ├── mysql.cnf
│       └── mysqld.cnf
└── docker-compose.yml
```

## Primary

```config:mysqld.cnf
[mysqld]
:
log-bin=ON
server-id=1
gtid-mode=ON
enforce-gtid-consistency=ON
log-slave-updates=ON
```

```sh:init.sh
#!/bin/bash
set -xeo pipefail

mysql -uroot -v <<SQL
CREATE USER 'repl'@'172.16.0.0/255.240.0.0' IDENTIFIED BY 'replpass';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'172.16.0.0/255.240.0.0';
SQL
```

```dockerfile:Dockerfile
FROM mysql
COPY mysql.cnf /etc/mysql/conf.d/mysql.cnf
COPY mysqld.cnf /etc/mysql/conf.d/mysqld.cnf
COPY init.sh /docker-entrypoint-initdb.d/init.sh
```

## Secondary

```config:mysqld.cnf
[mysqld]
:
log-bin=ON
server-id=2
gtid-mode=ON
enforce-gtid-consistency=ON
log-slave-updates=ON
read-only=ON
```

```sh:init.sh
#!/bin/bash
set -xeo pipefail

dockerize -timeout 60s -wait tcp://primary:3306
sleep 3

mysql -uroot -v <<SQL
CHANGE MASTER TO MASTER_HOST='primary', MASTER_USER='repl', MASTER_PASSWORD='replpass', MASTER_AUTO_POSITION=1;
START SLAVE;
SQL
```

```dockerfile:Dockerfile
FROM mysql
COPY mysql.cnf /etc/mysql/conf.d/mysql.cnf
COPY mysqld.cnf /etc/mysql/conf.d/mysqld.cnf
COPY init.sh /docker-entrypoint-initdb.d/init.sh

# dockerize
COPY dockerize-linux-amd64-v0.6.1.tar.gz /tmp/
RUN tar -xzf /tmp/dockerize-linux-amd64-v0.6.1.tar.gz -C /usr/local/bin
```

## Docker Compose

```yaml:docker-compose.yml
version: '3'
services:
  primary:
    build:
      context: ./docker/primary
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
    ports:
      - 33061:3306
    volumes:
      - type: volume
        source: primary
        target: /var/lib/mysql
  replica:
    build:
      context: ./docker/replica
    depends_on:
      - primary
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
    ports:
      - 33062:3306
    volumes:
      - type: volume
        source: replica
        target: /var/lib/mysql
volumes:
  primary:
  replica:
```
EC2 Mac Instanceを起動した後のVNC設定を自動化するScript

EC2 Mac Instanceを起動した後に必要なVNC Serverの設定を自動化するScriptです。BigSurrで動作確認済です。Scriptは[こちら](https://gist.github.com/nateware/3915757)を参考に作成しています。
<br>

検証済環境

```console
$ uname -a
Darwin ip-172-31-30-21.us-east-2.compute.internal 20.3.0 Darwin Kernel Version 20.3.0: Thu Jan 21 00:07:06 PST 2021; root:xnu-7195.81.3~1/RELEASE_X86_64 x86_64
```


Script

```shell
#!/bin/sh
sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -configure -allowAccessFor -allUsers -privs -all
sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -configure -clientopts -setvnclegacy -vnclegacy yes
sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -configure -clientopts -setvncpw -vncpw $2
sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -restart -agent -console
sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -activate
sudo passwd $1
```

実行例

```console
$ ./vnc.sh ec2-user password
```
Git ローカルリポジトリの最新のコミットを取り消す

# 目的

- ローカルリポジトリの最新のコミットを取り消す方法を個人メモ的にまとめる

# 情報

- コミットをリバートするのではなく完全に取り消す方法を記載する。

# 方法

- 下記コマンドを実行して直前のコミットを取り消す。
    - 直前のコミットの修正内容をそのままにコミットだけ取り消したい時

        ```terminal
        $ git reset --soft HEAD^
        ```

    - 直前のコミットの修正修正内容も含めてコミットを取り消したい時

        ```terminal
        $ git reset --hard HEAD^
        ```
Lightning Experience の取引先と取引先責任者 ⇒ 取引先と取引先責任者のリレーションの説明

１．取引先責任者-to-複数取引先に表示できる、取引先と取引先責任者の 2 種類のリレーションはどれですか?
２．関係取引先はどのように設定しますか?
３．あなたの非公開のデフォルト取引先チームのメンバーとなるのは誰ですか?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/0b510d62-2eaa-e38d-aa6f-d497683dd0e8.png)
AWS Amplifyでの環境変数について（react）

AWSのamplifyで環境変数の設置が分からなかったので調べてみたことを備忘録として残しておきます。

##環境設定の方法

Amplifyコンソールのアプリ設定に環境変数があります。
そこにREACT_APP_を変数名にプレフィックスとしてつけて環境変数を設定します。

![スクリーンショット 2021-04-09 9.38.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233228/e4d11df3-f31e-4c5d-594a-04a3be19453b.png)

そしてビルドの設定画面で先ほど設定した環境変数を設定します。

```
build:
  commands:
    - npm run build
    - echo "REACT_APP_HOGE=$REACT_APP_HOGE" >> .env
```

このようにすればreactアプリから読み込めるかと思います。
他に設定方法がありましたらご教示いただけると幸いです。

参考記事
https://qiita.com/kojiisd/items/709557fa4649aa25b483
二分探索木の実装

# 二分探索木の実装
- 練習としてpythonで書いてみました
- [こちら](https://qiita.com/menon/items/657f67bb2817e25b2222)の記事を参考にさせて頂きました

```python:BST.py
import random

class Node:
    """
    nodeをクラスとして実装する
    """
    def __init__(self,value):
        self.value = value
        self.left = None
        self.right = None
        
class Binary_search_tree: #binary search tree
    """
    二分探索木本体の実装
    機能としては、挿入処理、検索（インターフェース）、検索機能本体、ソート出力
    """
    def __init__(self,value_list):
        self.root = None
        for value in value_list:
            self.insert(value)
            
    def insert(self,new_value):
        root_node = self.root
        if root_node is None:
            self.root = Node(new_value)
            return
        current_node = root_node
        while True:
            current_node_value = current_node.value
            if new_value < current_node_value:
                if current_node.left is None:
                    current_node.left = Node(new_value)
                    return
                current_node = current_node.left
            elif new_value > current_node.value:
                if current_node.right is None:
                    current_node.right = Node(new_value)
                    return
                current_node = current_node.right
            else:
                current_node.value = new_value
                return
            
    def search(self,value):
        is_value = self._is_value(value)
        if is_value is None:
            print('検索対象がありません')
        elif is_value:
            print(str(value) + 'が見つかりました！')
        elif not is_value:
            print(str(value) + 'は見つかりませんでした')
            
    def _is_value(self,value):
        root_node = self.root
        if root_node is None:
            return None
        
        search_list = []
        search_list.append(root_node)
        while len(search_list) > 0:
            node = search_list.pop()
            if node.value == value:
                return True
            if node.right is not None:
                search_list.append(node.right)
            if node.left is not None:
                search_list.append(node.left)
        return False
     
    def inorder(self,node):
        if node:
            self.inorder(node.left)
            print(node.value)
            self.inorder(node.right)
```

# テスト
```python:test.py
#整数列Aの生成---------------------------------------------
#ランダム整数列Aの生成
iarray_A = list(range(50))
random.shuffle(iarray_A)
print(iarray_A)
#---------------------------------------------------------

#テスト----------------------------------------------------
tree = Binary_search_tree(iarray_A) #配列から二分探索木生成し、treeに代入
tree.search(10)#１０がtreeに存在するか検索
tree.inorder(tree.root) #中順走査、１～順にソート
#---------------------------------------------------------
```

# テスト結果

```
10が見つかりました！
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
```
eclipseのショートカットキーメモ（自分用）

久々にeclipseを使うことが多くなったので、個人的なメモとして
ついでにかなり昔にqiita登録したのになんと初投稿
markdownの練習も込みで

##エディター
| ショートカットキー| 挙動              |
| -----------------| ---------------- |
| Ctrl + O         | アウトライン<br>　フィールドやメソッドの一覧が見れる |
| F3               | 宣言を開く        |
| Ctrl + T         | クイック型階層<br>(奥まで行ける) |
| Ctrl + Alt + H   | 呼び出し階層を開く |
| Ctrl + Shift + T | 型を開く          |
| Ctrl + ↑↓        | ???              |
| Ctrl + /         | 選択範囲をコメントアウト |
| Alt + Shift + J  | Javadocつける     |


##ウィンドウ操作
| ショートカットキー | 挙動               |
| -----------------| ------------------ |
| Ctrl + Pup, Pdn  | タブを右、左に移動   |
| Ctrl + F7        | 次のビューに切り替え |
| Ctrl + F8        | 次のパースペクティブに切り替え |

##実行/デバッグ
| ショートカットキー | 挙動               |
| -----------------| ------------------ |
| F5               | ステップ・イン(次の行の処理を実行、メソッドがあればメソッドの中に入る)   |
| F6               | ステップ・オーバー(次の行の処理を実行、メソッドがあっても中には入らない) |
| F7               | ステップ・リターン(現在のメソッドの実行を終えて、呼び出し元へ戻る)    |
| F8               | 再開(次のブレークポイントまで進める)                               |
| Ctrl + F2        | 再処理を中断                                                     |

参考
https://yulii.github.io/eclipse-shortcut-keys-20120814.html
https://qiita.com/ynxx/items/7734dfb755bc83373d7c
globで指定した全てのサブディレクトリ以下から特定の拡張子ファイルのパスを取得

# globで指定した全てのサブディレクトリ以下から特定の拡張子ファイルのパスを取得
Python3.5からglobで**を使った再帰的な処理がサポートされました。
 
 引数`recursive=True`として`/**/`を使うと、中間フォルダに存在するあらゆるファイルや0個以上のディレクトリおよびサブディレクトリにマッチさせることができます。
深い階層の中の画像やjsonファイルをごっそり一括で取得したいというときに非常に便利です！

（※引数recursive=Trueを指定しないと**のみでは再起的に取得しないので注意です）
 

```python
 print(glob.glob('temp/**/*.json', recursive=True))
 # ['temp/1.json', 'temp/2.json', 'temp/dir/hoge.json', 'temp/dir/sub_dir1/1.json', 'temp/dir/sub_dir2/2.json']
```


## 参考
- https://note.nkmk.me/python-glob-usage/
大学生になって、通学時間がバラバラになったので、電車時刻検索ショートカットLINEBot

##はじめに
どうもこんにちは、LINEBotをこよなく愛し、高校時代をLINEBotで遊びまくったようかんです。
今年のはじめから、最年少LINEAPIExpertとして活動しております。
Expertになって初めての記事ですw（もっと活動しろ

とはいえ、僕がLINEBotを作りたくなるのは、自分や身近な周りの人の課題を見つけ、それをLINEBotと僕の技術でさくっと解決できそうだなと思った時に開発を行います。

---
高校時代は卒業式でLINEBotを用いました。先日のJAWSDAYS2021でも登壇しました!!

<iframe width="560" height="315" src="https://www.youtube.com/embed/HilOP8QGAkg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

-----
そんな僕が、大学生になって、やっと身近な課題を見つけました。
大学生って、今までのように同じ時間の電車に乗って通学しないんですよね。
僕の今学期はこんな感じです。(オンライン授業はいつ受けてもいいため、表からは除いています。）

|| 月 | 火 | 水 | 木 | 金 | 土 |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|1時限|   | ○ |   | ○ |   |   |
|2時限| ○ | ○ |   |○  | ○ |   |
|3時限| ○ | ○ |   |   | ○ | ○ |
|4時限| ○ | ○ |   | ○ | ○ |   |
|5時限|   |   |   |   |   |   |

これからサークルやらなんやらで突如日が変わったりしたり、とにかく
<strong>毎日時間を授業開始時間を確認→その20分前には最寄り駅に到着したい→その電車に間に合う電車を検索したい→何分に家を出ればいいかすぐ知りたい</strong>
これをLINEbotに基本的な情報を覚えてもらっておいてもらって、ワンタップや時刻になったら通知してくれるシステムがあれば便利だし、いろいろ心配しなくてもいいなと思いました。
※ここ何日かの間にこの案に似たLINEbot(時刻表を参照して、家を出る時間を教えてくれる）を作っておられる方の記事を読んだ覚えがあるのですが、見つけられなかったです。

##時刻表検索のAPIを探す
今回のLINEBotの目玉機能となるのは、時刻表を検索してくれるところです。ですが、流石にいろいろなサイトやらをスクレイピングする訳にもいかないので、乗り換え案内などでAPIを提供されているところを探しました。

###[ジョルダン乗り換え案内オープンAPI](https://norikae.jorudan.co.jp/openapi/)

```
公式サイト一部要約(2021/4/9)
 - オープンに利用する用途のみ。
 - 経路検索結果に、APIから返される「ジョルダン『乗換案内』へのリンク」を必ずセットする必要がある
 - 2019年5月時点で、路線バスを利用した経路検索には非対応。
 - 経路検索結果は、1経路を返却いたします。並べ替えには対応してない。
 - 各アカウントに対して、APIの月間利用回数を設定。（1API3,000回、合計で10,000回）
 - 連続的な総当りアクセスなどに対しては、当社判断にて利用を停止する。
```

###[駅すぱあとWebサービス フリープラン](https://ekiworld.net/service/sier/webservice/free_provision.html)

```
公式サイト一部要約(2021/4/9)
 - 社内システムや個人用アプリ、チャットアプリなど、さまざまな形に活用。
 - ライセンスの表記は不要ですが、記載してもらえると嬉しいです。
 - フリープランでは鉄道・航空情報のみに対応。
 - 「情報編集」や「範囲探索」「駅情報」などは課金で使える。
 - リクエスト制限は今のところないが、常識の範囲内で。
```

といったところが大手そう。後者のフリープランに経路検索があるっぽいので、今回はそれを使うことにした。公式サイトから利用目的などを入力し、数日待つとアクセスキーが送られてきた。


##完成(v1)
https://twitter.com/inoue2002/status/1384733650734833665?s=20

##利用したAPI

[概要はこちら](https://docs.ekispert.com/v1/le/api/)
経路検索には`駅すぱあと for web URL生成`という経路検索の結果のURLを返してくれるというAPIです。
本来なら、全てLINE上でやりたかったのですが、仕様上仕方ない。

[経路検索API](https://docs.ekispert.com/v1/le/api/search/course/light.html) GET

```
https://api.ekispert.jp/v1/json/search/course/light?key={上記手順で取得}&form={駅コード}&to={駅コード}
```
という感じでリクエストを送ると以下のように検索結果のURLが取得できます。コレをLINEで送っています。

![スクリーンショット 2021-04-21 14.30.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/d955d21f-f084-26ee-2265-931f1eb49bcb.png)


ところで駅コードってなんだよって感じですね。


[駅コード取得API](https://docs.ekispert.com/v1/le/api/station.html)

```
https://api.ekispert.jp/v1/json/station?key={上記手順で取得}&name={駅名}
```
という感じで取得したい駅名をnameパラメータにつけてリクエストを投げると以下のように取得できます。

![スクリーンショット 2021-04-21 14.46.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/9ac5e7f9-67ab-edee-cf6b-2c42c9de4788.png)

###構成
AWSのLambdaとNode.jsを用いて構築を行いました。

###ソースコード(現在は非公開)
https://github.com/inoue2002/trainCommutingLinebot

###今後
まずは、「僕専用」の実装を行いました
今後反響があればいろいろカスタムも行いたいと思います

ごりごりすごいことをするのも楽しいけれど、最小限な実装でも便利になると気持ちの良いもんだなと思いました。
Flutterのビルドエラーを解決できるかもしれない方法



## 事象

VSCodeなどのデバッグビルドが突然、Exited (sigterm)で終了するようになり、原因が全くわからない状態
この事象にハマると平気で2〜3時間ぐらい毎回費やしてしまうので備忘。

```
Launching lib/main.dart on iPhone 11 in debug mode...
lib/main.dart:1
Xcode build done.                                           35.7s
Exited (sigterm)
```

詳細をみてみると、こんなエラーも起きてたりする。

```
flutter run --verbose
```

```
ios/Flutter/App.framework" failed: No such file or directory (2) done flutter
```


## 結論
`flutter pub get` で './pub-cache' にインストールさたモジュールをデバッグの際になんらかのタイミングで触ってしまい、
構文エラーが起きていることが多い。

## 解決に至るまで


### flutter clean (=> ダメ)
flutter cleanを行い、再度、flutter pub getを行う

```
flutter clean
flutter pub get
```

### flutterの再インストール (=> ダメ)

[fvm](https://github.com/leoafarias/fvm)を使っての再インストール

```
fvm remove 1.22.6(YOUR FLUTTER VERSION)
fvm install 1.22.6
```

### `flutetr run --verbose` で、以下のエラーログを発見

```
⣽[   +9 ms] executing: [/Users/hogehoge/works/project/app/ios/Runner.xcodeproj/] /usr/bin/xcodebuild -project /Users/hogehoge/works/project/app/ios/Runner.xcodeproj -scheme Runner -showBuildSettings
[        ] executing: [/Users/hogehoge/works/project/app/ios/Runner.xcodeproj/] /usr/bin/xcodebuild -project /Users/hogehoge/works/project/app/ios/Runner.xcodeproj -scheme Runner -showBuildSettings
⣽[ +821 ms] ../../../.pub-cache/hosted/pub.dartlang.org/state_notifier-0.5.0/lib/state_notifier.dart:116:19: Error: Expected an identifier, but got '\'.
[        ]       return true;\
[        ]                   ^
[   +5 ms] ../../../.pub-cache/hosted/pub.dartlang.org/state_notifier-0.5.0/lib/state_notifier.dart:117:5: Error: Expected ';' after this.
[        ]     }());
[        ]     ^
```

どうやら、
`../../../.pub-cache/hosted/pub.dartlang.org/state_notifier-0.5.0/lib/state_notifier.dart`
の構文でエラーが起きているっぽい。



## 解決策
構文エラーが起きている ./pub-cacheの中身のdartファイルを直接修正しても良いが、以下コマンドで解決する。

```
flutter pub cache repair
```

#### 参考
https://stackoverflow.com/questions/55399209/update-flutter-dependencies-in-pub-cache
「scroll-snap」ちょっとのスクロールで勝手にスクロール位置をコンテンツに合わせてくれるやつ

#どうも7noteです。画面スクロールをスライドショーみたいにちょうどいい感じの位置まで勝手にスクロールしてくれるアレ（スクロールスナップ）です。

語彙力なさすぎて悲しいのですが、**コンテンツに合わせてスクロール位置を自動調整してくれるプロパティ「scroll-snap」**を紹介しようと思います。
日本語でも普通にスクロールスナップと呼ぶのが一般的なんですかね、おそらく。

![sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/42ce7b7c-a441-6a34-539a-e9e4fb038e44.gif)

（軽いスクロールだけで自動的にコンテンツに合わせてくれる）

以前からあったようなのですがほぼすべてのブラウザに対応しているそうなので、採用してみようと思い調べてみることに。まぁ最悪スクロールの加減が変わるだけなので効かないブラウザがあってもそこまで大きな影響にはならなさそうですね。

## 使い方

```index.html
<div class="box">
  <section>1</section>
  <section>2</section>
  <section>3</section>
  <section>4</section>
  <section>5</section>
</div>
```

```style.css
.box {
  width: 200px;  /* 適度な幅を指定 */
  height: 200px; /* 適度な高さを指定 */
  scroll-snap-type: y mandatory; /* スクロールスナップの指定 */
  overflow: scroll;              /* はみ出た部分をスクロールできる要素にする */
}

.box section {
  scroll-snap-align: start; /* スクロールスナップの指定 */
  width: 200px;  /* 適度な幅を指定 */
  height: 200px; /* 適度な高さを指定 */
  text-align: center; /* 文字を左右中央にするため */
  line-height: 200px; /* 文字を上下中央にするため */
}
	
.box section:nth-of-type(even) {
  background: #ccc; /* 偶数番の背景色 */
}
.box section:nth-of-type(odd) {
  background: #aaa; /* 奇数番の背景色 */
}
```

![sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/42ce7b7c-a441-6a34-539a-e9e4fb038e44.gif)


## まとめ

他にも横方向用や、コンテンツとコンテンツの間でスクロールを止められるような設定など細かい設定ができますが、一番良く使われやすそうなものを紹介しました。

さらにくわしく知りたい方は参考サイトへどうぞ！↓

参考： 
https://www.mitsue.co.jp/knowledge/blog/frontend/202102/02_1740.html
https://developer.mozilla.org/ja/docs/Web/CSS/scroll-snap-type

#### おそまつ！
~ Qiitaで毎日投稿中!! ~
[【初心者向け】WEB制作のちょいテク詰め合わせ](https://qiita.com/7note)
ZYBO導入記録 (1) 開発環境の準備

#この記事について
FPGA前々から始めたいと思っていたところ、先日オークションサイトで開発ボードを安く手に入れることができたので、自分のメモ代わりにやったことをまとめることにした。

#開発環境
- 開発用PC : Windows 10 64-bit
    - Vivado 2020.2
    - Xilinx Vitis 2020.2
- ターゲットボード : ZYBO (旧ZYBO)
    - DIGILENT (https://reference.digilentinc.com/reference/programmable-logic/zybo/start)
 

#Vivadoのインストール
##ツールのダウンロード
Xilinxのサイト (https://japan.xilinx.com/support/download.html) から開発ツール (ivado Design Suite) をダウンロードする。ウェブインストーラとイメージ全部入り (43GB) があるがウェブインストーラは安定しないことがあるので、ダウンロードに時間はかかってもイメージ全部入りを選択。
![0-ダウンロードページ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/6651395a-853e-0a80-040f-2b043a5b6cd4.png)
途中会社名や住所等を入力するので正直に登録する。職務内容は今回は "Hardware Enthusiast/Pro-Hobbyist" を選択した。
![1-ファイルダウンロード用住所入力.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/6427d03a-ebc0-39ab-06c9-ff8853855f5b.png)
tar.gzファイルなので7-zip等で解凍する。解凍後のファイルももちろん巨大なので、空き容量に注意する。元のファイルを適宜削除しながら解凍すると良い。
![3-巨大ファイル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/b1fbaa7f-84d9-dddd-1327-c83a76b1f19e.png)

##インストール

フォルダを開き、xsetup.exeファイルを開く。
![5-xsetup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/004c83bf-ba59-9dc7-d4c1-bb7eaf50ac97.png)
インストーラが起動する。起動まで少し時間がかかる。
![6-インストーラ起動.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/94467af6-3a9e-b34e-3e76-1ce6d0ec961e.png)
Vitisを選択してNext。
![7-とりあえずVitisを選んでみた.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/247cb5f7-6f29-55e3-3f9e-390491984f7c.png)
ここは特に何もいじらずにNext。
![8-全部入りのインストール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/8ddc504b-cbf7-938b-a0ee-0a984da39cec.png)
全部チェックしてNext。
![9-全部チェック.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/378d02f1-91f7-6939-fcc9-4d65dc2a7aad.png)
標準では`C:\Xilinx`にフォルダが作成されインストールされる。
非常に空き容量が必要なので "Disk Space Required" をよく確認しておくこと。
（このバージョンでは95.56GB）
問題なければNext。
![10-すごい容量食う.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/bebc2e58-02a9-548b-5b32-6e1c880c70f0.png)
これまでの設定の確認画面。問題なければInstall。
![11-いざインストール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/6188dbd0-d065-9417-f587-306fdac48a40.png)
インストールが完了するとアイコンが作成される。
![12-アイコン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/16458edd-789f-8071-222b-1d6eb5ccd55b.png)

##Vivadoを起動する

Vivadoのアイコンをクリックすると、下記のような画面が開く。
![13-Vivado起動画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601058/620263c0-50eb-8d30-1203-89e7546545b9.png)

#ZYBOボードファイルの取得
標準のインストール状態ではZYBOボード固有の接続情報（ボードファイル）が入っていないので、別にダウンロードする必要がある。
標準のインストール先だと、ボードファイルは`C:\Xilinx\Vivado\2017.4\data\boards\board_files`に保存されている。

ZYBOのボードファイルをDigilentのGitHub (https://github.com/Digilent/vivado-boards/archive/master.zip) からダウンロードする。

解凍して`new`フォルダにある`board_files`フォルダの中身をコピーし、先ほどの場所に保存する。
これでVivado上でZYBOボードを使った開発ができるようになる。

#参考

下記のページが大変参考になった。
https://qiita.com/iwatake2222/items/966f252f6ca954aff08b

#おわりに
この記事はFPGAもZYBOも初心者の自分が色々調べながらメモ代わりに書いております。現状では動作することは確認できておりますが、間違いがあったり、後から記事を修正する場合があります。ご容赦ください。
もしも間違っている内容等ありますが、ぜひご指摘ください。
エディションのまとめ

[まとめページに戻る](https://qiita.com/Keiji_otsubo/items/60afc2a8d211b6f7d89f)

[まとめページに戻る](https://qiita.com/Keiji_otsubo/items/60afc2a8d211b6f7d89f)
[まとめA～M](https://qiita.com/Keiji_otsubo/items/f3ddedee6793bf1576f8)


+ [ユーザライセンスのまとめ](https://qiita.com/Keiji_otsubo/items/d203c6ff53e3a711e624)

+ [Group Edition と Professional Edition での Apex の使用](https://qiita.com/Keiji_otsubo/items/227fe24dd31920e1fe98)
+ [Essentialエディションの制約](https://qiita.com/Keiji_otsubo/items/d0be1795a527f9908fc5)
+ [販売終了した Salesforce のエディション](https://help.salesforce.com/s/articleView?id=sf.overview_other_editions.htm&type=5)

# Flow Builder License Limitation (5) in Professional Edition

+ [Flow Builder License Limitation (5) in Professional Edition](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000Jfzfn)

:::note
彼らは今日のリリース準備期間中にこの制限を提起しました...「お楽しみに」....だから私は夏の23日に期待しています:)。

https://www.salesforce.com/video/39964879/
:::

# Professional Edition ではカレンダーオブジェクトは使えない

+ [Custom Object in Calendar](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007XIIwm)

+ [カレンダのまとめ](https://qiita.com/Keiji_otsubo/items/f8f6cd8063ba048ff43d)

# Professional Edition ではMicrosoft Exchangeと接続できない？　API接続だから？

+ [Help setting up Einstein Activity Capture](https://qiita.com/drafts/3db45b4cbcd5e1fa445e/edit)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/b2b6cecd-7033-d59a-a34d-9c8749394178.png)

+ [Professional Editionを利用されているお客様の環境にてデータローダが使用可能?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KDqh7)

:::note
Bulk APIが使用可能になっていますね。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/b65063be-3e36-4dfc-894c-f6186db0f745.png)

https://help.salesforce.com/s/articleView?id=xcloud.overview_limits_professional.htm&type=5

2020年のヘルプではAPIアクセスは有償みたいですけど 
https://help.salesforce.com/s/articleView?id=000385436&type=1

Winter 25版の資料でも追加オプションですね。 
https://developer.salesforce.com/docs/atlas.ja-jp.api_rest.meta/api_rest/intro_rest_compatible_editions.htm
:::


# Salesforce Starter Edition 

+ [An error occurred while trying to update the record. Please try again. Setup Entity ID is invalid: null. ](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007eruQ5)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/3dcc40d4-3594-d8f2-323f-2217bf635a8c.png)

https://help.salesforce.com/s/articleView?id=xcloud.overview_limits_starter.htm&type=5

https://help.salesforce.com/s/articleView?id=000387291&type=1

+ [Is it not possible to create any type of Flow in the Starter Edition?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000SUcLU)

:::note
It seems you can't run the flow. 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/9fb04ea4-ad50-4ef3-b506-004b76b3505f.png)

https://www.salesforce.com/en-us/wp-content/uploads/sites/4/documents/starter/starter-prosuite-feature-comparison.pdf
:::
Anacondaの商用利用有償化に際して: pipでIntel Math Kernel Library (MKL)をサクッと入れる (Windows)

Anacondaの商用利用有償化された(商用の定義は割愛)．minicondaをインストールし，conda forgeをrepoに指定すると問題ないらしいが，面倒なのでもうpip(+ poetry / pipenv)でよいのではと思っている．

だがよく言われる通り，こと行列演算等の数値計算に関しては，Intel製 CPU向けのMKLをベースにしたnumpyのほうが高速なので，pipでもMKLを使いたい．

ということで，WindowsでMKL対応のnumpyをインストールする手順を簡単にまとめる．

## 手順(numpyのインストール)
[公式の手順](https://software.intel.com/content/www/us/en/develop/articles/build-numpy-with-mkl-and-icc.html)に則ると面倒なので，ビルド済みのバイナリをダウンロードして`pip install`する．

### 0. numpyのアンインストール
すでに入っているnumpyは消しておく: `pip uninstall numpy`

### 1. pipが対応するwheelを確認

``` py
from pip._internal.utils.compatibility_tags import get_supported
print(get_supported())
```

出力:

```
[<cp38-cp38-win_amd64 @ 2327399349312>, ...]
```

### 2. .whlファイルのダウンロード
[カリフォルニア大学による非公式リポジトリ](https://www.lfd.uci.edu/~gohlke/pythonlibs/)から，pipが対応する.whlを探してダウンロードする(非公式なので自己責任)．

`numpy-<バージョン>+mkl*`と書いてあるものがMKLとリンクしているnumpyで，`vanilla`とあるのは通常のもの．

私は，`numpy‑1.19.5+mkl‑cp38‑cp38‑win_amd64.whl`をダウンロードした．

### 3. pip install

``` sh
pip install numpy‑1.19.5+mkl‑cp38‑cp38‑win_amd64.whl
```

### 4. 確認

``` sh
>>> import numpy as np
>>> np.show_config()
blas_mkl_info:
    libraries = ['mkl_lapack95_lp64', 'mkl_blas95_lp64', 'mkl_rt']
    library_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/lib/intel64_win']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020/windows/mkl/include']
blas_opt_info:
...
```

ちゃんとmklとリンクされたnumpyが入っている．

---

同様にScipy等もインストール可能．

## 参考:
- [【Python】.whlファイルをpipでインストールしようとして「○○.whl is not a supported wheel on this platform.」エラーが出た時の対応](https://qiita.com/r-ngtm/items/f7dc53bf713d12f6be9d)
- [Python-Control 環境構築 ＆ whlファイル インストールエラー対応](https://note.com/tomtom_0301/n/na8fedf6f87ee)
sql出力とn+1問題

laravelでsqlのログを出力した際n+1問題が出てきた。

LaravelはデフォルトでSQLのログを出力してくれないので
SQLログを出力する方法を記載します。

App\Providers直下に記載します
デフォルトでは
use Illuminate\Support\Facades\Schema;
use DB;
が入っていない可能性があります。

```laravel:App\Providers
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Schema; <-ココ追記
use Illuminate\Support\ServiceProvider;
use DB;　<-ココ追記

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot() {
        # 本番環境以外だった場合、SQLログを出力する
        if (config('app.env') !== 'production') {
            DB::listen(function ($query) {
                \Log::info("Query Time:{$query->time}s] $query->sql");
            });
        }
    }
  
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register() {
        //
    }
}

```
一度画面の更新をすると
laravelファイル直下のstorage/logsに作成されます

この際sqlが大量に発行されて重くなるというのがn+1問題です。

sqlテーブルが2つあって片方を片方に代入するときに大量にログが出てしまいます。
これを解決するにはApp\Http\Controllersのsqlデータを呼び出している部分にwithを書くこと

```

$posts = Post::orderBy('created_at', 'desc')
    ->categoryAt($category_id)
    ->paginate(10);
 
↓↓↓↓↓ 以下に変更 ↓↓↓↓↓
 
$posts = Post::with('foods')        // ←★これ
    ->orderBy('created_at', 'desc')
    ->categoryAt($category_id)
    ->paginate(10);
```

筆者は1対多のsqlだったので、Post::with('foods')
foodsとsをつけて複数形にしています。
第二引数、第三引数があるのならば

```

$posts = Post::orderBy('created_at', 'desc')
    ->categoryAt($category_id)
    ->paginate(10);

↓↓↓↓↓ 以下に変更 ↓↓↓↓↓

$posts = Post::with(['foods', 'drink'])        // ←★これ
    ->orderBy('created_at', 'desc')
    ->categoryAt($category_id)
    ->paginate(10);
```
これでn+1問題が解決されます。

もっと詳しく見たい方はこの記事
https://qiita.com/TsubasaTakagi/items/8c3f4317ad917924b860