OmniAICreator/Qiita-1.07M huggingface dataset:
baserCMS版カスタムフィールドでオリジナルフィールドタイプを作る

2012年の [baserCMS アドベントカレンダー](https://adventar.org/calendars/5393) の7記事目としての投稿です。

2020年12月16日に [キャッチアップ](https://catchup.co.jp) から、baserCMS版カスタムフィールド「 [CuCustomField](https://github.com/ecatchup/CuCustomField) 」 をオープンソースとしてリリースしましたので軽く紹介したいと思います。


# 標準のフィールドタイプは14種類

標準では、次の14つのフィールドタイプを持ちます。

- テキスト（１行）
- テキストエリア（複数行）
- 日付（年月日）
- 日付（年月日時間）
- チェックボックス
- マルチチェックボックス
- 都道府県リスト
- ラジオボタン
- 関連データ
- セレクトボックス
- ファイルアップロード
- Google Maps
- Wysiwyg Editor
- ループ

# さらにフィールドタイプをプラグインとして追加できる

このフィールドタイプは、baserCMSの別プラグインとして独自で追加できるように設計されています。

作るものは４つです。

1. フィールドタイプの名称などを定義する設定ファイル
- フィールドタイプを定義するための入力画面用のテンプレート
- 上記画面を制御するためのJS
- 記事編集画面での入力機能と記事表示画面での表示機能を実装するヘルパ

詳細については、GitHub の [オリジナルのフィールドタイプを作成する](https://github.com/ecatchup/CuCustomField/blob/master/docs/CREATE_FIELD_TYPE.md) に書かれていますので、参考にしてみてください。

PHPとJavascriptが少しわかる人であれば、比較的簡単に作れますのでチャレンジしてみてください。

# 補足

ブログ記事で登録したデータを、実際にテンプレート上どうやって表示させるかについて情報が足りなかったので補足します。

GitHub の [cu_custom_field_block.php](https://github.com/ecatchup/CuCustomField/blob/master/View/Elements/cu_custom_field_block.php) が参考になるのですが、ブログテンプレートの `single.php` で次のような関数を書くことで表示できます。

```php
$this->CuCustomField->get($post, 'example_field_name');
```
では色々試してみてください！
Stable Rust にもうすぐやってくる (min) const generics とは

## はじめに
この記事は「限界開発鯖アドベントカレンダー」23日目の記事です。
いやー、明日はクリスマスイブ、明後日はクリスマスとはなんとも信じがたいです。2020年は過ぎるのがとても早かった。。。

この記事では、stable rustでもうすぐ使えるようになるかもしれない**const generics**と呼ばれる機能について、簡単に解説したいと思います。

なおこの記事でお話しするのは `min_const_generics` feature の範囲です。`const generics`はとても大きな機能なので、分割されています。今回安定化されるかもしれないのは最低限の部分です。

## TL;DR
- constな限定された一部の型の値をgenericsとして持たせることができるようになります。
- rust v1.50-stableでstabilizeされるかもしれません: [PR](https://github.com/rust-lang/rust/pull/79135)
- すでにnightlyでは使用可能です。 `#![feature(min_const_generics)]`

## 対象読者
- RustのGenericsについて理解している方
- Rustのconstについて理解している方

## 本題

2x3の行列を表すstructを考えてみましょう。普通に書いてみます。

```rust
struct Matrix2x3 {
    matrix: [[f64; 3]; 2],
}
```

これを拡張し、2x3ではなく、任意のサイズの行列を定義してみましょう。(サイズは固定とします）

```rust,no_run
struct Matrix {
    // ここでmatrixの長さを指定する方法がない！
    matrix: [[f64; ???]; ???],
}

impl Matrix {
    fn new_zeroed(cols: usize, rows: usize) -> Self {
        Self {
            // 配列の長さの指定には定数を使う必要がある！（変数は使えない！）
            matrix: [[0.0; rows]; cols],
        }
    }
}
```

このコードはコンパイルが通りません！今まではVecなどを使ってヒープアロケーションするしか方法がありませんでした。
const genericsを使うとこれがとても簡単に実現します。

```rust
struct Matrix<const COLS: usize, const ROWS: usize> {
    matrix: [[f64; ROWS]; COLS],
}

impl<const COLS: usize, const ROWS: usize> Matrix<COLS, ROWS> {
    fn new_zeroed() -> Self {
        Self {
            matrix: [[0.0; ROWS]; COLS],
        }
    }
}
```

このように、定数をジェネリクスに持たせることが出来るようになります！
使うときはこのように書けます。

```rust
fn main() {
    let matrix = Matrix::<2, 3>::new_zeroed();
    //もしくは
    let matrix: Matrix<2, 3> = Matrix::new_zeroed();
}
```

本当にジェネリクスの一部となっているのがお分かりいただけたと思います。
implでの応用例を行列の掛け算の実装で見てみましょう。

行列の掛け算は、a × b をするとき、aの列数とbの行数が等しくないと定義されません。
これを今までのRustでは表現できませんでしたが、const genericsを使うと型レベルで表現できます。

```rust
impl<
    const COLS: usize,
    const ROWS: usize,
    const K: usize,
> std::ops::Mul<Matrix<K, ROWS>> for Matrix<COLS, K> {
    type Output = Matrix<COLS, ROWS>;

    fn mul(self, rhs: Matrix<K, ROWS>) -> Self::Output {
        // ...
    }
}
```

これにより、a × b をするとき、aの列数とbの行数が一致しないときはコンパイルが通らないようになります！

```rust
fn test() {
    let matrix = Matrix {
        matrix: [
            [1,  2, -5, 2],
            [3, -1,  7, 3],
            [1,  0,  4, 4],
        ],
    };

    let _ = matrix * matrix;
}
```

このコードはコンパイル時にこんなエラーを吐きます。

```
error[E0308]: mismatched types
  --> src/lib.rs:61:22
   |
61 |     let _ = matrix * matrix;
   |                      ^^^^^^ expected `4_usize`, found `3_usize`
   |
   = note: expected struct `Matrix<4_usize, {_: usize}>`
              found struct `Matrix<3_usize, 4_usize>`

error: aborting due to previous error
```

今までは実行時にエラーハンドリングをしなければならなかったのを、コンパイル時に解決できます。素晴らしいですね！

## おわりに
おそらくこれからどんどん`const generics`の機能が実装されていくと思います。楽しみですね！

明日の限界開発鯖アドベントカレンダーは、くもことなおこさんの担当です。お楽しみに！
DOMの監視をJavascriptでやる方法 


[MutationObserver](https://developer.mozilla.org/ja/docs/Web/API/MutationObserver)を使うことでやりたかったことができた。

以下DOMを監視して変化したときに関数を実行したい。

```javascript


//監視対象のDOM
<p id="target">2020-12-23 11:00</p>

//監視ターゲットの取得
const target = document.getElementById('target')

// オブザーバーの作成
const observer = new MutationObserver(records => {
    //実行したい処理
})

// 監視の開始
observer.observe(target, {
   //今回はtarget配下の要素が変化した時なのでchildListを指定
   childList:true
}

//アロー関数で書くことでもう少しコンパクトになった
new MutationObserver(() => {
      //実行したい処理
    })
.observe(target, {childList: true});
```

## 参考
[JavaScriptのMutationObserverでDOMの変化を監視する方法](https://qiita.com/munieru_jp/items/a6f1433652124a2165e4)
「OpenSSH keys only supported if ED25519 is available」のエラーの解決方法

# 目標

エラーを解決してデプロイを完了させる。
# 本番環境
・Ruby: 2.5.7
・Rails: 5.2.4
・AWS：EC2
・OS: macOS Catalina

#解決方法
開発環境にてGemを追加

###1.`Gem`を追加

```ruby:Gemfile
gem 'ed25519'
gem 'bcrypt_pbkdf'
```

bundleインストールを行い、再度capistranoの自動デプロイコマンドを実行する

```java:ターミナル
$ bundle install
$ bundle exec cap production deploy
```

エラー文にもこのgemファイルを入れろという指示があったので試してみたら治りました。
配属直後に go get したのに command not found で躓いた話

## 忙しい人向け

#### 解決策①： パスを通す

```
$ export GOPATH=$HOME/go;
$ export PATH=$PATH:$GOPATH/bin;
```

単純にパスが通っていないだけであればこれで解決するだろう。

#### 解決策②： go get で `$GOPATH/bin` に落としたバイナリを `$GOROOT/bin` にコピーする

しかし、こっちが本記事の結論。

今回は、Goのバージョンを切り替えやすくするために $GOROOT にシンボリックリンクを貼っていて、実行環境にバイナリ(実行ファイル)を go get できていなかった。

[解決策はこちら](https://qiita.com/y-kaanai/items/7a1d274a441003144106#%E8%A7%A3%E6%B1%BA%E7%AD%96)

## 経緯
当時の私は新たな MacBook PRO を手に入れ（貸与され）、配属先でGo製のAPIを開発することになっていたため、ワクワクしながらGoの開発環境を構築していた。

（後々躓くことも知らずに）言われるがままに環境構築を行い、ビルド、手元でユニットテストを回した。
通った！
「よし、これで開発者としてチームの一員になれるぞ...」
その日はそのまま退勤。「明日から頑張るぞ！」という決意と共に会社を後にした。

さらに、参画することになっているプロジェクトでは `sqlboiler` というORMを使っているという情報を手に入れていた。生のSQLならまだしも、ActiveRecord以外のORMをまともに扱ったことがないこともあり、早速 `sqlboiler` を触ってみようと考えていた。

## やってみる

- [volatiletech/sqlboiler](https://github.com/volatiletech/sqlboiler)

上を参考に、必要なパッケージを go get していく。

```cli
$ go get -u -t github.com/volatiletech/sqlboiler
$ go get github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql
```

あらかた必要になりそうなもののインストールは済んだので、 sqlboiler コマンドの実行!


```
$ sqlboiler psql
```

しかしここでなぜか sqlboiler がないと怒られる

```cli
$ sqlboiler psql
-bash: sqlboiler: command not found
```

command not found ... ？
んん ... ？ `go get` したんだけどなと思いつつ確認する。

```cli
$ ls $GOPATH/bin
sqlboiler-psql*    sqlboiler*    その他省略
```

あれ？あるぞ？なぜ？
その後もあーだこーだしてるうちに時間が溶けてゆく。

## 原因を探る

まずはGOPATHの確認

```cli
$ echo $GOPATH
/Users/y-kanai/go
```

ふむ。
GOROOTは？

```
$ echo $GOROOT
/Users/y-kanai/goroot
```

goroot...? ってのはなんだ？
そんなもの知らんぞ？ってことで、

```
$ ls -la
drwxr-xr-x   5 y-kanai  931577470     160  7 19 23:21 go/
drwxr-xr-x@ 21 y-kanai  931577470     672 12 23 16:51 go1.13.14/
lrwxr-xr-x   1 y-kanai  931577470      25  8  4 12:11 goroot@ -> /Users/y-kanai/go1.xx.xx/
```

あ！シンボリックリンクを貼ってるやんけ！完全理解！！！

ってことは、$GOROOT/bin に sqlboiler がないんだろうな..と

```
$ ls $GOROOT/bin
シーン ...
```

つまり、今回の原因はGoのバージョンを切り替えやすくするために $GOROOT にシンボリックリンクを貼っていて、実行環境にバイナリを go get できていなかったことにありました。

用意されたドキュメントをコピーしてペロって貼ってると中々気づかないものですね。

## 解決策

`go get` で `$GOPATH/bin` に落としたバイナリ（実行ファイル）を、`$GOROOT/bin` にコピーする！

```bash
$ cp $GOPATH/bin/sqlboiler $GOROOT/bin/
$ cp $GOPATH/bin/sqlboiler-psql $GOROOT/bin/

$ ls $GOROOT/bin/
go*             gofmt*          sqlboiler*      sqlboiler-psql*
```

sqlboiler コマンドを実行！

```
$ sqlboiler psql
.
.
.
```

よし、ひとまず解決！
ここまで辿り着くのに1時間近くかかってしまいました。

## 振り返り

脳死コピーはダメですね。反省。

Goの複数バージョンを切り替えながら使っている方、
他に良い方法があれば、ぜひご教授願います。以上！
一人システム開発だけど、Azure Boardsを使ってみる②

#概要
前回の記事「[一人システム開発だけど、Azure Boardsを使ってみる①](https://qiita.com/yossihard/items/0d5b40b1ba6e810bef6c)」では、
Azure BoardsにEpic、Future、User Story、Taskを登録するところまで行いました。
今回はその続き。
いろいろ触ってみて、どんなことができるのか確認していきます。

#ユーザーの追加
いや、一人で開発なんですけど、
来るべき時に備え（笑）、ユーザーを追加する方法を調べておこうと。

左側メニューから歯車マークのProject Settingsを選択し、
その後「Permissions」を選択します。
以下のような画面が表示されるので、「（プロジェクト名） Team」（ここではAzureBoardsTest Team）を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/2209ddab-29d6-c1fb-ce5c-3c3f81a43acc.png)

次に「Members」タブを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/0a6b1a36-7153-eebd-2988-a724dbb81635.png)

以下のような画面が表示されるので、「Add」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/857585ec-a961-1d9d-917b-543d7b5b31f1.png)

以下のような画面が表示されます。検索枠の中に追加したいユーザーの名前を入力します。
※ユーザーが予め同じテナントに属している必要があります。
表示されたユーザーから、追加したいユーザーを選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/e11e1dc2-8343-83d6-6324-2fbf80d9b1e2.png)

以下のような表示になります。「Save」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/8c68f2d0-093b-b7fc-b635-0f11ca557e1e.png)

ユーザーが追加されました！
これで将来開発メンバーが増えても安心・・・なはず（笑）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/717e3501-8963-3f68-06a3-228967991866.png)


#Boardメニューを触ってみる（状態管理、表示の変更）
Azure DevOpsにログインして、左側メニューからBoards⇒Boardsを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/39b1e67e-a387-fcba-898a-138da18b81d8.png)

登録したFutureがカンバン方式で表示されますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/90d6437b-4d06-f38c-2614-2dae486de8c0.png)

各Futureは、作成された時点では「New」の列にあります。
これらはドラッグ＆ドロップで「Active」、「Resolved」、「Closed」に移動させることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/f5b1b3aa-637a-2c63-4e11-a04936a97107.png)

担当者の名前が表示されます。
ここをクリックすると、アサインする担当者を変更できます。

上部にあるコンボボックスを変更すると、Boardsに表示するものを
FeatureからUser Storyに帰ることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/360ecbf5-b510-2695-824e-347c27b13cab.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/2e627112-4106-d0fd-2652-64e901f1476d.png)

User Storyも、もちろんドラッグ＆ドロップで状態を変更することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/aae717ce-3372-7129-5ea7-e30167d477eb.png)

#余談
デフォルトでは、BoardにNew、Active、Resolved、Closedの4つの列が表示されますが、
設定で変更することができます。

歯車のアイコンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/115441b1-149a-e142-b072-978a3ae1ecca.png)

以下のような画面が表示されるので、「Columns」タブをクリックします。
「＋Column」をクリックすると列が追加できます。
各列の「・・・」をクリックすると、「Remove」メニューが出てきて、削除できます。
設定変更が終わったら、最後に「Save and close」をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/12769b86-c3fa-e9de-d2f5-1147e75a5cac.png)
[初学者向け]Django・Reactおすすめ書籍まとめ

こんにちは、文系からITのお仕事について1年目のくまきちです。
初心者の方向けに、何を学べるかを書きながら、特によかった本やウェブサイトを紹介していきます。

勉強を始めたばかりの大学時代は、周りにITに造詣の深い友人や先輩がいなかったため本やウェブサイトに頼りきりでした。
知識がなかったので、やりたいことと関係のない本もたくさん買ってしまいましたね・・・。
皆さんにはお金を無駄にして欲しくないので(笑)、ちょこっと概要も書きつつおすすめを紹介していきます。勉強し始める方の一助になりますように！



#Django 
####Djangoを始める
:notebook_with_decorative_cover: 掌田津耶乃 [「Python Django3超入門」 ](https://www.amazon.co.jp/Python-Django3%E8%B6%85%E5%85%A5%E9%96%80-%E6%8E%8C%E7%94%B0-%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798061921/ref=tmm_hrd_swatch_0?_encoding=UTF8&qid=1608704262&sr=8-5)
一番初めにやりました。Djangoでどうやってインストールするの？から教えてくれる。
知識ゼロからウェブサイト作成まで。

:computer:  Django Brothers https://djangobrothers.com
ブログ、メモアプリを作れる無料チュートリアル。
構造を理解しやすくて勉強になるので個人的にはおすすめ。

####少し応用「会員機能」「デプロイ」
:notebook_with_decorative_cover: Django Brothers[「Django チュートリアルで学ぶ Web 開発入門 」](https://djangobrothers.booth.pm/items/1564348)
会員機能、カスタムユーザーモデルを用いた会員機能について学べる。ECショップの作り方も。

:notebook_with_decorative_cover: 大高 隆[「動かして学ぶ! Python Django開発入門」](https://www.amazon.co.jp/%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E5%AD%A6%E3%81%B6-Python-Django%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-NEXT-ONE/dp/4798162507/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=django%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80&qid=1608702455&sr=8-1)
会員機能、Bootstrapの導入方法、Django + AWSでのデプロイについて勉強できる。SES導入なども。
（AmazonLinux2AMIになり、Let’s Encryptの部分のコードはそのままでは実行できなくなってしまった。)



####番外編
:computer: Django Narito Blog https://blog.narito.ninja/
わからないことはまずこちらのブログで検索（笑）。
DRF + Vue.jsの情報もたくさん。

:notebook_with_decorative_cover: 森本哲也,中野正輝,池 徹, 岡田幸大[「できる 仕事がはかどるPython自動処理 全部入り。」](https://www.amazon.co.jp/%E3%81%A7%E3%81%8D%E3%82%8B-%E4%BB%95%E4%BA%8B%E3%81%8C%E3%81%AF%E3%81%8B%E3%81%A9%E3%82%8BPython%E8%87%AA%E5%8B%95%E5%87%A6%E7%90%86-%E5%85%A8%E9%83%A8%E5%85%A5%E3%82%8A%E3%80%82-%E3%80%8C%E3%81%A7%E3%81%8D%E3%82%8B%E5%85%A8%E9%83%A8%E5%85%A5%E3%82%8A%E3%80%82%E3%80%8D%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E6%A3%AE%E6%9C%AC%E5%93%B2%E4%B9%9F/dp/4295006254/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=python+%E8%87%AA%E5%8B%95%E5%87%A6%E7%90%86&qid=1608702772&sr=8-1)
Python ファイル書き込み、読み出しなどの編集などの自動処理。使えると便利！

#React
####Reactを始める
:computer: たかぴろ(@takapiro_99) [「React ハンズオン 2020 夏」](https://github.com/takapiro99/tech-quiz)
知識ゼロから外部APIの利用とSPAの構築、firebaseでデプロイするところまで学べる。
環境構築についてもとても参考になるので、他の言語を学び始める人もぜひ参考に！

:computer: React Tutorial https://react-tutorial.app/ 
Reactの基礎について手を動かしながら学べる。後半は有料だけど、無料枠が充実している。おすすめ！

####DRF + ReactでSPAを作る
:computer: CodingEntrepreneurs [「Create a Twitter-like App with Python Django JavaScript and React. Full TUTORIAL」] (https://youtu.be/f1R_bykXHGE)
YouTubeのチュートリアル動画。DRF + Reactでツイッターみたいなサービスの構築。
（途中のdjango埋め込み認証機能はそのままではうまくいかなかった。できた方いたら教えて欲しいです。）

####番外編
:computer: JavaScript | MDN https://developer.mozilla.org/ja/docs/Web/JavaScript
JavaScriptについてわかりやすく説明してくれる。
React以前の問題だ・・・と感じた時に。

---
まだまだちゃんと理解している部分は少ないですが、できることを増やしつつ理解していけたらなと思っています！
もっと知識をつけて本を間違って買わないようにしたいですね・・・:laughing:
GitHub Desktopでお目当てのリポジトリがクローンできないとき

**GitHub Desktop**を愛用しています。
すごく便利なツールなのですが、困ったことが起きました。
それは、GitHubで作ったリポジトリをクローンしようとしたときに、お目当てのリポジトリではなく、ほかのリポジトリがクローンされてしまうということなのです。

GitHub Desktopのバージョンは2.6.1です！

この症状は、以下のケースで起きるようです。

* GitHubにAというリポジトリを作り、それをクローン
* ローカルのAを削除
* GitHubのAをBにリネーム
* 新しくGitHubにAを作り、それをクローン
* なぜかAではなくBがクローンされる

この症状の原因がGitHub側にあるのかGitHub Desktopにあるのか切り分けるために、GitのCLIで同様のことをやってみましたら、問題なく期待どおりの動作となりました。
するとGitHub Desktopに何らかの問題があると推測できます。
ワールドワイドで使われているアプリケーションに、このようなベーシックな問題があるとは考えにくいのですが、ここは理性的に立ち向かうことにしました。

試行錯誤の末たどりついた仮説は、こんな感じです。

* GitHub Desktopは、リポジトリ名に何らかの識別子を作っている
* リポジトリ名が同じなら同じ識別子となる
* 識別子ごとに、GitHub側のリポジトリとURLを何らかの形で記憶している
* この対応は変更されない
* 同じリポジトリ名＝識別子なら、常に最初に記憶されたGitHubリポジトリ名とURLを使う

識別子ではなく、リポジトリ名でも同じかもしれません。
つまり最初に、「このリポジトリ名だったら、GitHubのこのリポジトリを引っ張ってくるからね！」というのが決まっていて、それはあとになっても変わらないのではないか？ということなのです。

いろいろ解決の策を練りましたが、手っ取り早いのは**GitHub Desktopをアンインストール**して、``%%Profile%%AppData¥Roaming¥GitHub Desktop``以下に残っているファイルもすべて削除、ということです。
根本的な解決にはなっていませんが、これでお目当てのリポジトリを無事クローンできました。

Aというリポジトリを作って試行錯誤的に作業していたけど、だいたいうまくいくようになったので改めてやり直したい。だけど保険のためにAを残しておきたいので、とりあえずBにリネーム、Aを新しく作り直す、というのはわりとあり得るケースだと思います。

もしかしたら同じくはまっている人がいるかも？ということで書いておきました。
【Swift】Qiita 週間いいね数ランキング【自動更新】

# タグ別

[`Android`](https://qiita.com/items/8b3af051428d746f26c5) [`AWS`](https://qiita.com/items/e24b6279326a462d456c) [`初心者`](https://qiita.com/items/402899ec543aff109505) [`Docker`](https://qiita.com/items/ae11fca7d2eba445b037) [`Git`](https://qiita.com/items/74eacdbf363e260981c3) [`Go`](https://qiita.com/items/49d4537d95f878b3e91a) [`iOS`](https://qiita.com/items/e61a29a383d0403e92fc) [`Java`](https://qiita.com/items/4c3f84836bfdbb137226) [`JavaScript`](https://qiita.com/items/eaa7ac5b62a0a723edbb) [`Linux`](https://qiita.com/items/362e81e53c3f9dee22f1) [`Node.js`](https://qiita.com/items/66ed7ad8f7c9673e9d50) [`PHP`](https://qiita.com/items/3318cbdbc45c6ebd4014) [`Python`](https://qiita.com/items/9d7f2ffeafb36cf59a77) [`Rails`](https://qiita.com/items/93b9e7f7d143e9ce650e) [`React`](https://qiita.com/items/f9712f8acace22815b99) [`Ruby`](https://qiita.com/items/72c3d2e896bdc3e1a6b3) [`Swift`](https://qiita.com/items/e2b6f0645e29f0e2b761) [`TypeScript`](https://qiita.com/items/25b7c0870afa6d41d19b) [`Vim`](https://qiita.com/items/f5361177baef95e447d1) [`Vue.js`](https://qiita.com/items/2774e02c6eea5c830d99) 

# 集計について

- 集計期間: 2025-06-16 ~ 2025-06-23
- 条件: ストック数が **2** 以上の記事


# GitHub

https://github.com/koki-develop/qiita-ranking

スターをもらえるととっても励みになります :bow:

# いいね数ランキング

ランキングに入る記事が見つかりませんでした。
npm バージョン7 にアップデートできるよ【2020年10月20日〜】


# npm バージョン7 について

>GitHubは米国時間2020年10月13日、Node.jsのパッケージマネージャーであるnpm(Node Package Manager)バージョン7が近日中にリリースされるNode.js 15.0.0に含まれることを公式ブログで明らかにした。npm バージョン7は2020年5月に発表し、10月12日にリリースされた。これまで数百万人の開発者が月間130万パッケージを750億回ダウンロードしている。
>
>npm バージョン7は複数のパッケージを操作するワークスペースの拡充や、依存関係の最適化、パッケージロック機能を更新するためのyarn.lockのサポートを含んでいる。特に依存関係はnode_modulesツリー内にある依存関係にあるパッケージを正しく検出するアルゴリズムが組み込まれた。Node.jsに含まれる安定版(バージョン12、本稿執筆時点の最新版はバージョン14)のバイナリーでは古いバージョンになるため、npmインストール済み環境で「npm i -g npm@7」を実行すると、npmをバージョン7に更新できる。
>
>阿久津良和(Cactus)

引用元：[ワークスペース拡充のnpm v7.0](https://news.mynavi.jp/article/20201016-1418462/)

# npm バージョン7 の変更点

@watilde さんという方がまとめてくださっています。

> [npm v7の主な変更点まとめ](https://blog.watilde.com/2020/10/14/npm-v7%E3%81%AE%E4%B8%BB%E3%81%AA%E5%A4%89%E6%9B%B4%E7%82%B9%E3%81%BE%E3%81%A8%E3%82%81/)

いくつか破壊的な変更があるので peerDependency や npx を使用している環境では注意してください。

# npm のアップデート

実際にアップデートしたいと思います。

```shell:動作環境
$ node -v
v15.5.0
```

```shell:アップデート
$ npm install -g npm@7
```

```shell:確認
$ npm -v
7.3.0
```

2020年12月23日現在は `v7.3.0` が最新のようです。

# 今すぐにバージョンすべきか？（2020年12月23日時点）

2020年12月23日時点では、Node.js の安定版は 14.15.3 (同梱 npm 6.14.9) であるため、今すぐにバージョン 7 にアップデートする必要はないですが、あらかじめ対応しておいたほうがあとあと楽かと思います。

- 安定版の確認
  - https://nodejs.org/ja/download/
- Node.js と npm バージョン対応表
  - https://nodejs.org/ja/download/releases/
GitHubActionsでPHPInsightsの自動コメント投稿する

こんにちは。M2です。
[GitHub Actions Advent Calendar 2020](https://qiita.com/advent-calendar/2020/github-actions)の22日目を隙間埋めで担当させていただきます。

# 概要

公式まんまですが、reviewdogなど使おうとして遠回りしたので備忘録がてらに。

## 対応方法

```yml
#.github/workflows/pr.yml
name: CI
on:
    - pull_request
jobs:
    phpinsights:
        runs-on: ubuntu-latest
        name: PHP Insights checks
        steps:
            - uses: actions/checkout@v2
            - uses: shivammathur/setup-php@v1
              with:
                  php-version: 7.3
            - run: composer install --prefer-dist --no-progress --no-suggest
            - run: vendor/bin/phpinsights -n --ansi --format=github-action # format=github-actionをつける
```

すると自動コメント投稿が

[![Image from Gyazo](https://i.gyazo.com/71d340fcbe4b29b33159bc12063b8a72.png)](https://gyazo.com/71d340fcbe4b29b33159bc12063b8a72)

## 参考

[PHP Insights公式ドキュメント](https://phpinsights.com/continuous-integration.html#github-action)
[Rails]本番環境のデータベースをリセットする方法(Capistrano版)

## はじめに
####前提
・Railsを使用してアプリケーションを開発
・Capistranoでの自動デプロイを実装している
・AWSのEC2にてサーバーを構築している
・RDSでMySQLを使用している

## 背景
私は開発環境では、rails db:migrate:resetにていつもデータベースを作り直していましたが、本番環境ではどのようにすればいいのかという疑問から実装しました。

### 本番環境のデータベースをリセットする
まずはターミナルを用いてEC2で自分のアプリケーションフォルダの階層まで進む。
Capistranoでの実装をしているのでミスをしないように基本的にはcurrentディレクトリで作業するようにする。

```ruby:terminal
[ec2-user@ip-222-22-2-222 アプリ名]cd current
```

```java:current
RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rails db:drop
```
これでデータベースが消去することができました。


###データベース再度作成する
今回はRDSにMySQLを導入している前提でお話していきます。
まず引き続きターミナルは同じディレクトリでmysqlに接続します。

`mysql -u （マスタユーザ名） -p -h （エンドポイント）`
「-u」はユーザ名、「-p」はパスワードの入力、「-h」は接続先の情報を表すオプションです。
なお、「エンドポイント」は、RDSメニューで確認し、入力してください。
「パスワード」は、「マスターパスワード情報」で設定したパスワードを入力します。

```ruby:terminal
mysql -u root -p -h rds-mysql-server.xxx.ap-northeast-1.rds.amazonaws.com
```
このコマンドを打つとpasswordの入力が求められるので入力すると無事mysqlに接続が完了しました。

削除したアプリケーション名と同じ名前をつけデータベースを再度作成します。

```ruby:terminal
mysql> create database アプリケーション名;
```

再度本番環境でmigrateを実行します。

```ruby:terminal
bundle exec rails db:migrate RAILS_ENV=production
```

これでデータベースのリセットは終了です。
あとは本番環境でcapistranoのコマンドを叩いて終了です

```ruby:terminal
bundle exec cap production deploy
```

お読みいただきありがとうございました
datetimeを使って、本日の注文件数を表示したい。


##まずは「$ rails c」でdatetimeの動きを見てみる。

```pry:irb
#現在時間の取得
[1] pry(main)> dt=DateTime.now
=> Wed, 23 Dec 2020 05:34:58 +0000

#parseで日付情報だけに加工
[2] pry(main)> today=Date.parse(dt.strftime("%Y/%m/%d %H:%M:%S"))
=> Wed, 23 Dec 2020


#ある日付を定義
[3] pry(main)> dt2 = DateTime.new(2020,12,23,5,6,7)     
=> Wed, 23 Dec 2020 05:06:07 +0000

#parseで日付情報だけに加工
[4] pry(main)> the_day=Date.parse(dt2.strftime("%Y/%m/%d %H:%M:%S"))
=> Wed, 23 Dec 2020

#today == the_day
[5] pry(main)> the_day == today
=> true


```

##本日の注文を表示させたい時

**状況設定**
　ECサイトの管理者top画面で本日の注文件数を表示させたい。
　注文に関するモデル：Order

```rb:orders_controller

    def top

        dt=DateTime.now  #まずは、今日の日付を取得。こんな感じ（=> Wed, 23 Dec 2020 05:34:58 +0000）
        today=Date.parse(dt.strftime("%Y/%m/%d %H:%M:%S"))  #日付だけ取り出す
        
        
        orders=Order.all  #とりあえず、注文情報全て引っ張ってくる。
        
        @sum=0  #合計件数用。　※別に初期化しているわけではないことに注意
                #@sum = @sum + 1 とした時　「@sumって何？」というエラーが出る。

        orders.each do |x|   #理系ですので、xと定義させていただきます。
            dt2=x.created_at
            the_day=Date.parse(dt2.strftime("%Y/%m/%d %H:%M:%S"))
            
            if today == the_day
                @sum = @sum + 1
            end
            
        end
        
    end
```
【AWS】Lambdaの実行ログから指定した文字列を検知してslackに通知 

今回は自分が触れる機会の多いAWSのサービスを使用して運用の際などで利用できる通知方法の一つをご紹介します。

#使用サービス
+ Lambda
+ CloudWatch
+ Slack

#実装フロー
1.Lambda関数からCloudWatch Logsへログを出力
2.CloudWatch Logsから指定した文字列を検知
3.検知された文字列をトリガーにSlack通知用のLambda関数を発火
4.通知用LambdaでSlackへ文字列を通知

#フロー詳細
実際に上記のフローで実装していきます。

__手順１__

今回は「test」という文字列でCloudWatch Logsへ出力します。
![スクリーンショット 2020-12-23 16.14.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404045/b67ff6df-0bcf-b9d5-c88e-d034741ec338.png)

__手順２__

文字列検知にCloudWatch Logsのサブスクリプションフィルター（Lambda）を使用していきます。
![スクリーンショット 2020-12-23 16.25.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404045/dacabb2d-fd89-71d4-9688-b6aacca66287.png)


__手順3__

通知用Lambda関数とログ形式およびフィルターパターンを設定します
![スクリーンショット 2020-12-23 16.35.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404045/9c099fd1-d022-0c29-6011-5e585edef755.png)



__手順４__

環境変数「WebhookURL」には通知したいslackのWebhook URLを設定しています。

```notifi.py
import json
import os
import urllib.request
import slackweb

def lambda_handler(event, context):
    decoded_data = zlib.decompress(
        base64.b64decode(event['awslogs']['data']),
        16+zlib.MAX_WBITS
    )
    json_data = json.loads(decoded_data) 
    print(json_data['logEvents'])
    for i in json_data['logEvents']:
        test = i['message'] # ログの内容
        slack = slackweb.Slack(url=os.environ['WebhookURL'])
        slack.notify(text=test)
```

先ほど設定した文字列を検知してslackに通知されることを確認しました。
![スクリーンショット 2020-12-23 16.48.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404045/d7293077-6dda-43a4-d71d-f41ef9c3ac46.png)

#まとめ
上記以外にも通知方法は複数ありますが、なるべく簡潔にまとめていった方が管理しやすいので簡略化できるところはどんどん楽にしていきたいですね。
マンガでわかる羊頭狗肉(ウルフチケット)ミニアンチパターン

![A89BD06C-57D3-48DC-8109-10E3B0AB28E7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11525/cf0c25eb-1835-e296-ccb1-5c9654d22643.png)
http://wiki.c2.com/?WolfTicket

羊頭狗肉の「狗」というのは食用犬のことです。羊の頭を飾った露店で犬の肉を売る行商人のような、使い物にならない中身に一人前の宣伝をつけて売り込むベンダーに騙されるのがこのパターンです。

英語ではウルフチケットと呼ばれています。こちらは、ダフ屋のライブチケットの意味があります。転売チケットを使うと、実際に入場するとき無効化されていたりしますが、ダフ屋はそれを承知で、とぼけて売りつけてくるというのを表現しています。

もし間違って採用してしまうと、その大部分が [ボートアンカー](https://qiita.com/tanakahisateru/items/b0a6193615af08f19743) になるか、あるいは、その困った実態に [
ベンダーロックイン](https://qiita.com/tanakahisateru/items/3855ab2b2516ceb5ff8a) されてしまいます。

たんに品質の低いソフトウェアであれば評判も上がらず、すぐにボロが出るのでよいのですが、いかにも業界標準らしい言葉を使っていながら、ベンダー独自の解釈が隠されているといった種類の嘘になっていた場合、かなり厄介です。広く使われてしまうと、間違った意味のほうがデファクトスタンダードになってしまいます。

キャッチすぎる宣伝文句には気をつけて、言葉の意味が本当かを考える知恵をつけましょう。また、間違いを間違いだと批判する声を上げることも大切です。ベンダーに「やっぱりちゃんと勉強して正直にやったほうがいいな」と思わせてやるのは、ユーザーが行使できる正当な権利ですね。
VMware NSX-T 3.0  同一ホスト内のTEP間通信

VMware NSX-T 3.0までのホスト内のTEP通信では、**<font color="red">ハイパーバイザとEdgeのTEP</font>**は基本的に**<font color="red">別サブネット</font>**である必要がありました。NSX-T 3.0の環境で、**<font color="red">同一サブネットで動作させる</font>**為の構成を検証した為、まとめました。

# マニュアル記述：NSX-T Data Center インストール ガイド
<font color="grey">NSX-T Data Center インストール ガイド　> NSX Edge のネットワーク構成　より<font color="grey">


準備が完了した NSX-T Data Center ホストに NSX Edge 仮想マシンをインストールして、トランスポートノードとして設定することができます。展開には 2 つのタイプがあります。

* **<font color="deepskyblue">NSX Edge 仮想マシンを展開するには、VSS/VDS がホスト上の個別のpNIC を使用する VSS/VDS ポートグループを使用します。</font>**ホストトランスポートノードは、ホストにインストールされた N-VDS に対して独立した pNIC を使用します。ホストトランスポートノードの N-VDS は、VSS または VDS と共存します。いずれも、独立したpNICを使用します。**<font color="deepskyblue">ホストTEP (Tunnel End Point) およびNSX Edge TEPは同じサブネットに配置することも、異なるサブネットに配置することもできます。</font>**

* NSX Edge 仮想マシンを展開するには、ホスト トランスポート ノードの N-VDS 上の VLAN によってバッキングされている論理スイッチを使用します。ホスト TEP および NSX Edge TEP は異なるサブネットに配置する必要があります。

# 検証環境

* ホストTEPとEdge TEPが動作するVSS/VDSが異なれば、同一サブネット及びVLANで動作可能
* 実行例：ホストのTEPは[DVS]上で稼働、192.168.40.3
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/45d2f2fa-765f-bf6f-9e8b-799029e41381.png)

* Edge TEPは[DVS2]上で稼働
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/6deddd32-c380-5b31-9d4a-ecc3cb01a9bc.png)

# 動作確認

* 同一サブネット(192.168.40.0/24)にて、ホスト(40.3)とEdge(40.4)のGeneveのリンクが動作
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/b7b0da22-2169-35db-2b5f-e779e65cc9b8.png)

# 結論
NSX-T 3.0でも、ホストのTEPとEdgeのTEPが動作するVSS/VDSが異なれば、同一ホスト内で同一サブネット及びVLANで動作する事が出来る


# 関連リンク
https://qiita.com/m-eno/items/c3fa75c7a04fc967342e
GASでAdminSDK APIを使って退職処理

Gsuiteの退職処理の自動化で、GoogleのAPIの使い方でつまづいたので備忘に

#やること
・退職者のパスワードを変更
・Googleグループから退職者を削除


#退職者のパスワードを変更

ランダムパスワードの生成
変数「S」で指定した文字列から「N」個抜き出してパスワードを作成します。

```Code.js
const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const N=10;
const password = Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('');

```

Admin SDK APIでユーザーのパスワードを変更
passwordに変更したいパスワードを
userKeyにパスワードを変更するユーザーのアドレスを入れる。

```Code.js
const userKey = "パスワードを変更するユーザーのメールアドレス"
const payload = {"password": password};
AdminDirectory.Users.update(JSON.stringify(payload),userKey);

```


全体はこんな感じ

```Code.js
function myFunction(){
    const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    const N=10;
    const password = Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('');

    const userKey = "パスワードを変更するユーザーのメールアドレス"
    const payload = {
    "password": password,                            
    };
    AdminDirectory.Users.update(JSON.stringify(payload),userKey);
};
```

#Googleグループから退職者を削除
userKeyで指定したユーザーが所属しているグループの一覧が、
json形式でビャッと出てきます。


```Code.js
AdminDirectory.Groups.list({userKey:"ユーザーのアドレス"});
```
指定したグループから指定したユーザーを削除します。

```Code.js
AdminDirectory.Members.remove("グループのアドレス","ユーザーのアドレス")
```


取得したリストからグループのメールアドレスを取り出して、
ユーザーをグループから削除します。
グループのメールアドレスはgroupList.groups.emailに入っています。

```Code.js
const groupList = AdminDirectory.Groups.list({userKey:"ユーザーのアドレス"});
for (let i = 0; i < groupList.groups.length; i++) {
    const group = groupList.groups[i];
    AdminDirectory.Members.remove(group.email,"ユーザーのアドレス");
};
    
```
うちでは退職者のGsuiteアカウントは3か月保管するので、
ついでに退職者用のグループに追加します。

```Code.js
    
AdminDirectory.Members.insert({email:"ユーザーのアドレス",role:"MEMBER"},"退職者用のグループのアドレス");
```
ゼロトラスト設計例

###ゼロトラストの設計例
コロナ禍でDXの社会的な期待が増し、セキュリティではzerotrustが次世代の手法として注目されていますが、その設計を具体的にどうすれば良いか？については悩んでいる人が多いと思います。
ここではその設計例を公開します。

#####【目次】
1. “セキュリティ”が経営に及ぼす影響
2. 設計
  1)手法の選択
  2)機能別設計
3. 期待される効果

#####＜リンク＞
https://www2.slideshare.net/MMurakamiMurakami/ss-240476781
#####＜抜粋＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585213/c1879241-fec9-f274-b911-f2b85bebb9cc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585213/e4fb44f4-a9fa-c8d0-1a5c-51b0ac7ce70e.png)
Opencvについて①

Opencv3系についてのメモ

基本的には公式ドキュメントを確認しながらの物。

####環境
windows10 home 64bit
Python 3.7.9
opencv 3.4.2.17
Jupyter Notebook使用(Anaconda不使用)

---
###使用画像について
画像については自分で撮影した肉を使用。
形式としては.pngを使用。
.jpgでも問題なし

```python:opencv_test.ipynb
cap_dir = "使用画像を保存したフォルダを指定"
```
以後はすべてこの変数を使用しています。

###①画像読み込みについて
1)cv2.imread(第1引数、第2引数)

```python:opencv_test.ipynb
img1 = cv2.imread(cap_dir,0)
img2 = cv2.imread(cap_dir,1)
img3 = cv2.imread(cap_dir,-1)
img4 = cv2.imread(cap_dir,cv2.IMREAD_GRAYSCALE)
img5 = cv2.imread(cap_dir,cv2.IMREAD_COLOR)
img6 = cv2.imread(cap_dir,cv2.IMREAD_UNCHANGED)
```
第1引数:読み込みたい画像
第2引数:画像に関するフラグ

普通に使用するだけなら0 or 1で問題ないと思います。
読み込み後の処理でどうするかなのでカラーで使うかグレーで使うかでいいと思います。

むしろアルファチャンネルなる物は初めて見ました。
画像みてもわかりません。

![スクリーンショット (333).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/a4de070e-d31c-1fc2-b539-84b632e6ff83.png)![スクリーンショット (334).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/7d8a0e9c-b3c9-2127-0002-a144c87919ea.png)![スクリーンショット (335).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/4dec51c0-557c-4a08-95f1-36b345a6646d.png)

---
###②画像表示について

1)cv2.imshow(第1引数、第2引数)
2)cv2.waitKey(第1引数)
3)cv2.destroyAllWindows(第1引数)
4)cv2.namedWindow(第1引数、第2引数)

```python:opencv_test.ipynb
img = cv2.imread(cap_dir,1)

cv2.namedWindow('niku',cv2.WINDOW_NORMAL) 
cv2.imshow('niku',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
```
1)cv2.imshow(第1引数、第2引数)
第1引数:表示ウインドウ名(文字型で入力)
第2引数:表示したい画像

2)cv2.waitKey(第1引数)
第1引数:キーボード入力待ち時間(ms単位)
ウエイトと思っていたが、公式曰く入力受付時間らしい
0にしておけば無制限入力待ちとなる。

3)cv2.destroyAllWindows(第1引数)
第1引数:未入力なら開いているすべてのウインドウを閉じる。
複数開いているときに閉じたいウインドウ名を入力すると
入力した名前のウインドウを閉じる

4)cv2.namedWindow(第1引数、第2引数)
第1引数:ウインドウ名(文字型)
第2引数:デフォルトで`cv2.WINDOW_AUTOSIZE`が設定されている。
通常開いた画像はウインドウの大きさは固定だが
第2引数に`cv2.WINDOW_NORMAL`と入力すると
マウス操作で大きさを任意で変更出来る様になる。

ただ注意として、第1引数をimshowの第1引数で設定した名前と違う名前にすると
画像のない別ウインドウが立ち上がって意味がなくなる。

下記は名前が一致しない場合
ウインドウの大きさがいじれるのは左側
![スクリーンショット (338).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/3457e9e1-6783-2590-aaa1-cff0563cd86f.png)


---
###③画像保存について

1)cv2.imwrite(第1引数、第2引数)

```python:opencv_test.ipynb
cv2.imwrite('test.png',img)
```
第1引数:保存画像名
第2引数:保存画像

これで画像を保存するとスクリプトがある場所と同じ所に画像が保存される。


```python:opencv_test.ipynb
save_dir = "画像を保存したいディレクトリ"
save_name = "画像名"
save_ext = ".png"

cv2.imwrite((save_dir + save_name + save_ext),img)
```
とすれば指定した場所に画像を保存する事が出来ます。

多分、上記の様な使い方がデフォになると思います。

---
###④まとめ
他の物も順次まとめていく予定です。
変数の中身を意識したい！

## 言語
Ruby

## 対象読者
- 変数の型を意識していない方。
- プログラミングを勉強したばかりの方
- 変数がよくわからない方
- RubyでNoMethodErrorが発生した際に原因がパッと思い浮かばない。

## 記事の内容
変数や型の難しい箇所（参照型、値型、null、スコープなど）は説明せず、変数のイメージをつかんで頂けるように作成しております。

## 最低限知って欲しいこと

### 変数の中身を意識すること！

これだけです。これは静的でも動的でも変わらず重要なことです。
イメージ図はこんな感じです。

![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155615/aec0d4b9-685b-d2c1-19d9-e1b0cbabb564.png)

変数の中身を意識しないプログラマーは往々にしてこのような書き方をします。

```
変数さん！　料理をしてください！！
```

![スクリーンショット 2020-12-23 16.27.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155615/cf32ee6c-15ea-1e09-3d26-10569ca0cd33.png)

変数の中身が大工さんだと知らないんですね。なので、大工さんに対して料理をしてと言ってしまいます。
コード的に説明すると以下の感じです。

```
#大工クラスを定義しています。
class Carpenter
#建設メソッドを定義しています。
  def construction
    puts "建設します"
  end
end
#大工クラスを実体化しています。
hennsuu = Carpenter.new
#大工クラスに定義されていないメソッドを呼び出しています。
hennsuu.cooking
```

分かるでしょうか？実際に存在しているクラスを用いてもう一度説明します。
RubyのStringクラスとIntegerクラスの公式ドキュメントをみてください。

- String
    - https://docs.ruby-lang.org/ja/latest/class/String.html
- Integer
    - https://docs.ruby-lang.org/ja/latest/class/Integer.html

サイトの中で検索（command + F）でupcaseと検索してください。
Stringではヒットしますが、Integerではヒットしません。
これはStringクラスではupcaseメソッドが定義されていますが、Integerクラスではupcaseメソッドが定義されていないことを示します。
では、実際にコードを実行してみましょう。

```
hennsuu = "abcdef"
puts hennsuu.upcase #"ABCDEF"
hennsuu2 = 12345
puts hennsuu2.upcase #NoMethodError
hennsuu.class # String
hennsuu.class # Integer
```

NoMethodエラーが出ましたね。これはIntegerクラスにはupcaseメソッドが定義されていないためにエラーが発生しています。
このように、変数の中身を意識せずもっと言えば変数の中身のクラスを意識せずにプログラムは書けないのです。

## まとめ
変数の中身を意識しないと思わぬバグやエラーに苦しめられることがあります。
変数の中身は意識するようにしましょう！

## 外部サイト
今回使用している可愛い絵に関しては下記サイトにて取得したものです。
素材に関しての著作権はみふねたかし様が保持されております。
https://www.irasutoya.com/
PythonでGoogleDriveAPIを使ってGoogle Driveにファイルを定期的にアップロードする

# 背景
pythonでgoogle driveにファイルを定期的にアップロードするジョブのスクリプトが欲しい。
検索すると、PyDriveを使った記事が多いが、PyDriveはメンテナンスがあまりされていないし、Google Drive APIのv2を使っていて今使うのは得策ではなさそう。
今回は GoogleDriveAPIを使って実装してみる。

# 参考リンク集

#### [Python Quickstart](https://developers.google.com/drive/api/v3/quickstart/python)
- GoogleDriveAPIの雰囲気が掴める掴む
- この例では一度コンソールでログインする必要がある
  - 今回の仕様としては、コンソールログインなどのユーザーアクションなしで定期アップロードを行うようにしたいので別の認証を考える必要がある

#### [Upload file data](https://developers.google.com/drive/api/v3/manage-uploads#python)
- ファイルアップロードのサンプル
  - Pythonのサンプルが少なくてしっくりこない

#### [Lambda(Python)でGoogle Driveへファイルアップロード](https://qiita.com/w2or3w/items/b66a4a8e45e6001a9c46)
- GCPのサービスアカウントを使用したサンプルが紹介されている
- このqiita記事が一番参考になった

# 実装サンプル

```py:sample.py
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
from oauth2client.service_account import ServiceAccountCredentials
import os

def uploadFileToGoogleDrive(fileName, localFilePath):
    service = getGoogleService()
    # "parents": ["****"]この部分はGoogle Driveに作成したフォルダのURLの後ろ側の文字列に置き換えてください。
    file_metadata = {"name": fileName, "mimeType": "text/csv", "parents": ["****"] }
    media = MediaFileUpload(localFilePath, mimetype="text/csv", resumable=True)
    file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()

def getGoogleService():
    scope = ['https://www.googleapis.com/auth/drive.file']
    keyFile = 'credentials.json'
    credentials = ServiceAccountCredentials.from_json_keyfile_name(keyFile, scopes=scope)
    return build("drive", "v3", credentials=credentials, cache_discovery=False)


getGoogleService()
uploadFileToGoogleDrive("hoge", "hige.csv")
```
Go言語　学習記録

# Go言語とは
　2009年にGoogleが開発したプログラミング言語です。
　静的型付け言語であり手続き型言語に分類されます。

# 私がGo言語を学ぶ理由
　大きく分けて2つあります。

### 理由1. 違うタイプの言語に触れてみたいと思ったため
　私は今まで、いわゆる「オブジェクト指向」かつ「動的型付け言語」であるRubyとJavaScriptを主に勉強してきました。

○「動的型付け言語」と「静的型付け言語」の違いとは？
　ざっくり言うと、「変数を定義するとき、文字列型とか数値型などの型を指定するかどうかの違い」があります。
　動的型付け言語の場合、指定しませんが、静的型付け言語の場合だと指定する必要があります。

### 理由2.Goの将来性
　現在、Go言語の需要は伸びています。求人数などもそうですが、技術的に今後のトレンドになるみたいです。
　Goは、マイクロサービス（複数の独立したWebアプリケーションをまとめたWebサービス）開発向けの言語です。今、マイクロサービスが注目されており、今後Goが開発環境で使われていくことは間違いないでしょう。

# んで、実際に学んだことについて
いや、本当に初歩の初歩ですが、アウトプットさせてください。。

### Println（プリントライン)
Goが用意している「コンソールに出力せよ」という命令の1つ。

```
Println("こんにちは")
```

```
[コンソール]

こんにちは
```

### 変数定義
「var　変数名 データ型」で定義する。
変数を定義する時点で型を決めることは、自分にとって新鮮！

```
var name str = "佐藤"
var age int = 24
```

# 最後に
　こんな感じでアウトプットさせていただきました。なんか初心者の人が頑張っているなぁと思ってモチベーションを上がってくれたら幸いです。
Rails に CircleCIを導入する

# はじめに

Rails に CircleCI を導入して GitHub に push すれば自動でテストなどが走るようにしていきます。


## 手順

1. CircleCI の自分のアカウントで CI を走らせたいリポジトリを選択
2. .circleci 以下に config.yml を作成
3. push する

## 実装

Rails のアプリ作成(MySQLで作ります。)



~~~
$ rails new circleci_practice -d mysql
~~~

scaffoldで適当なCRUDを作成します。

~~~
circleci_practic $ rails g scaffold users name:string email:string
~~~

Gemfile に rspec を追加し bundle install

適当な RSpec を一つ書いてみてください。

.circleci ディレクトリを作り config.yml を作成

そのままでは Docker上でMySQLに接続できないのでhostの部分を編集してください。
とりあえず以下のような感じでいいと思います。

~~~confg/database.yml
test:
  <<: *default
  database: circleci_parctice_test
  # ↓これを追加
  host:  <%= ENV['DATABASE_HOST'] } %>
~~~

~~~.circleci/config.yml
version: 2

jobs:
  test:
    docker:
      - image: circleci/ruby:2.7.1-node
        environment:
          RAILS_ENV: test
          DATABASE_HOST: 127.0.0.1 
      - image: circleci/mysql:5.7
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: true
          MYSQL_ROOT_PASSWORD: ''
          MYSQL_DATABASE: circleci_parctice_test
    working_directory: ~/circleci_parctice
    steps:
      - checkout
      - run:
          name: "bundle install"
          command: bundle install --path vendor/bundle
      # MySQLと接続できるまで待ちます
      - run:
          name: "waiting DB start"
          command: dockerize -wait tcp://127.0.0.1:3306 -timeout 1m
      - run:
          name: "initialize DB"
          command: |
            bundle exec rake db:create
            bundle exec rake db:migrate
      - run:
          name: RSpec
          command: bundle exec rspec


workflows:
  version: 2
  workflows:
    jobs:
      - test
~~~

これで push すると自動でCIが走ると思います。

# おわりに

他にも rubocop を入れたり、 slim-lint を入れたりするといい感じになると思います。
yarn install や bundle install で cache を使ったりすると速くなります。
[jQuery] URLの取得、パラメターの取得

URL、パラメターを取得したい場合

```javascript

$(function () {

    var url = location.host;
    var tech = getUrlParameter('parameter-name');

});

var getUrlParameter = function getUrlParameter(sParam) {
    var sPageURL = window.location.search.substring(1),
        sURLVariables = sPageURL.split('&'),
        sParameterName,
        i;

    for (i = 0; i < sURLVariables.length; i++) {
        sParameterName = sURLVariables[i].split('=');

        if (sParameterName[0] === sParam) {
            return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);
        }
    }
};
```
第9回



# assert\_equal

assert\_equalとは，equalか否かを確認する関数であり，今回はこの関数がどのような振る舞いをするのかを確認する．

```rub:assert_equal.rb
p 1==1

def assert_equal(expected,result)
  return expected==result
end

p assert_equal(1,1)
```

出力結果は以下のようになる．

    true
    true


# colorize

　ターミナルの出力に色付けするStringの拡張機能です．通常の色付け方法だとコードが覚えにくい，読みにくいというデメリットを抱えているが，このgemがあればそれらの問題を解決することができる．

```ruby:colorize.rb
require 'colorize'

def assert_equal(expected, result)
  if expected == result
    puts 'true'.green
  else
    puts 'false'.red
  end
end

p "assert_equal(1, 1)"
p "assert_equal(1, 2)"

assert_equal(1, 1)
assert_equal(1, 2)

```

講義ではassert\_equalの応用であるassert\_not\_equalを扱っていたので，colorizeを用いて実装しようかと思います．完成したコードは以下になります．

```ruby:assert_not_equal.rb

require 'colorize'

def assert_not_equal(expected, result)
  if expected == result
    puts 'false'.red
  else
    puts 'true'.green
  end
end

assert_not_equal(1, 1)
assert_not_equal(1, 2)

```


# メソッドassert\_equal\_fin

それではassert\_equalとcolorizeの内容を用いましてassert\_equalの完成度を高めたいと思います．名前はassert\_equal\_finとしました．初めに，入力と出力の部分を改良したいと思います．以下のコードは期待値と実際に出力された数値を表示する関数になります．

```ruby
def puts_vals(expected, result)
  puts "expected :: #{expected}"       #期待値
  puts "result   :: #{result}"　　　　 #実際に出力された値
end
```

次は関数assert\_equal及びassert\_not\_equalを改良します．期待値と出力値が一致(assert\_not\_equalでは不一致)していたらsucceeded in assert\_equal(assert\_not\_equal)を，不一致(assert\_not\_equalでは一致)ならば，faild in assert\_equal(assert\_not\_equal)と出力します．

```ruby
def assert_not_equal(expected, result)
  puts_vals(expected, result)
  print expected != result ?
  "succeeded in #{__method__}.\n".green :
    "failed in #{__method__}.\n".red
end

def assert_equal(expected, result)
  puts_vals(expected, result)
  print  case expected == result
     when true  ; "succeeded in #{__method__}.\n".green
     when false ; "failed in #{__method__}.\n".red
     end
end
```

別スクリプトで使用する場合，

```ruby
assert_equal(1, 1)
assert_equal(1, 2)
assert_not_equal(1, 2) 
assert_not_equal(1, 1)
```

の部分は不要となるので，

```ruby
if $PROGRAM_NAME == __FILE__
  assert_equal(1, 1)
  assert_equal(1, 2)
  assert_not_equal(1, 2)
  assert_not_equal(1, 1)
end
```

と記入することで，記入されたファイル名と実行されたファイル名が完全一致したときの場合だけ実行させることができる．

以上より，assert\_equal\_finは以下のようになる．

```ruby:assert_equal_fin.rb

require 'colorize'

def puts_vals(expected, result)
  puts "expected :: #{expected}"
  puts "result   :: #{result}"
end
def assert_not_equal(expected, result)
  puts_vals(expected, result)
  print expected != result ?
	  "succeeded in #{__method__}.\n".green :
	  "failed in #{__method__}.\n".red
end
def assert_equal(expected, result)
  puts_vals(expected, result)
  print  case expected == result
	 when true  ; "succeeded in #{__method__}.\n".green
	 when false ; "failed in #{__method__}.\n".red
	 end
end

if $PROGRAM_NAME == __FILE__
  assert_equal(1, 1)
  assert_equal(1, 2)
  assert_not_equal(1, 2)
  assert_not_equal(1, 1)
end


```



------
 - **source** ~/grad_members_20f/members/NobuakiMori/L09_assert_equal.org
NSX-T 3.1 Federation スタンバイ グローバル マネージャー機能

VMware NSX-T 3.1の新機能で、Federationのスタンバイグローバルマネージャ機能がありますが、検証したのでまとめてみました。

# NSX-T Federationとは

NSX-T Data Center はバージョン 3.0 から Federation という複数環境を単一のシステムで管理できる機能が追加されました。この Federation 機能により複数データセンターにまたいだ NSXの環境を、より簡単に管理する （例：グローバルTier0/Tier1/Firewall）ことができるようになりました。

フェデレーション環境は、2 種類のNSX-T Managerが存在します。

* グローバル マネージャ：複数のローカルマネージャと連携する NSX Manager
* ローカル マネージャ：1つの場所に対してネットワーク サービスとセキュリティ サービスを提供する NSX Manager

![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/e9f7707c-aff9-409f-d336-a611e8a624a5.png)

# スタンバイ グローバル マネージャ機能
グローバル マネージャで、アクティブ・クラスタとスタンバイ・クラスタを別の場所に配置できるようになりました。（同一サイトに構成するクラスタに加えて、別サイトにスタンバイを配置可能）アクティブ・クラスタとスタンバイ・クラスタ間の遅延は、150ミリ秒以下にする必要があります。

# Active/Standby グローバル マネージャの設定

NSX-T グローバル マネージャは、Active/Standbyともにサイズは「Medium」、ロールを「NSX　Global Manager」で展開する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/38a267e7-68b9-b538-cc11-2c21b2c34bbc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/2717d37b-a026-7bfb-65ed-1c3f85a45a42.png)

グローバル マネージャは、Active/Standbyともに仮想IPを設定し、Activeグローバル マネージャーの画面よりStandbyグローバル マネージャーを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/ebaafa10-5db9-12ca-8103-4d2cb4d36864.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/03e423b9-4d82-8a60-002f-d4896afad0f8.png)

Standbyグローバル マネージャー追加の際には、名前はスペースなしのものを指定し、サムプリントは下記のコマンドで取得します。また、Standbyグローバルマネージャーは、Activeグローバルマネージャーとは異なるコンピュートマネージャーの登録が必要で、かつ仮想IPの設定も必要となります。

```
get certificate cluster thumbprint
```
FQDN/IPには、Standbyグローバルマネージャの仮想IPを指定します。（ノードのIPを指定した場合は失敗します）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/8ec26b5d-6906-35fa-9d11-096f47ca5529.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/11c58479-f8a2-898e-abd5-93ddea899942.png)

# グローバルマネージャー障害試験（Active/Standby）

実際にActiveグローバル マネージャをシャットダウンして、Standbyグローバル マネージャーに切り替えてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/e9c4e008-1367-e4d7-dd25-a4183300a7dd.png)
Standbyグローバル マネージャーは切り替え前は、グローバルオブジェクトの変更はできません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/4534e9ef-c455-ae0f-dca3-ef9065bf91d2.png)

Activeグローバル マネージャの停止後、手動にてStandbyグローバル マネージャーをActiveに昇格します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/d8816ea3-f0e0-39d9-b967-f40274afdf5f.png)
旧Standbyグローバル マネージャーはActiveへの昇格後、グローバルオブジェクトの変更が可能になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/751e047e-50f6-8811-347e-7f8ab46c7c9d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251519/074e830f-d1fd-b08e-ca59-edd359843d04.png)
UbuntuにGoをインストールする方法


# 最新版を調べる

[https://golang.org/dl/](https://golang.org/dl/)

# インストール

```
cd ~
wget https://golang.org/dl/go1.15.6.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.15.6.linux-amd64.tar.gz
rm -f go1.15.6.linux-amd64.tar.gz
echo 'PATH="/usr/local/go/bin:$PATH"' >> .profile
source ~/.profile
```
# 確認

```
go version
```
Canon EDSDKでリモート撮影できないときに確認するポイント

Canon EDSDK（EOS Digital Software Development Kit）でカメラをリモート制御したいとき、いくつかハマりどころがあるので備忘録。

## (1) TAKE_PICTURE_CARD_NGで撮影できない

`kEdsPropID_SaveTo`を`kEdsSaveTo_Host`に設定すると、撮影された画像はホストPC側に転送されるにも関わらず、CARD_NGというエラーが出る。

このケースでは、SetCapacityを撮影前に行う必要がある。PC側に十分な容量があると教えてあげるためだと思われる。

```cpp
EdsCapacity capacity = { 0x7FFFFFFF, 0x1000, 1 };
err = EdsSetCapacity(camera, capacity);
```

## (2) ObjectEventだけが呼ばれない

ホストPC側に画像を転送する際、ObjectEventHandlerで非同期イベントを受信して画像を受け取るのだが、Windowsではこのイベントが全くこないことがある。（しかも、ParameterEventやStateEventはきちんと呼ばれるにも関わらず…。）

この場合は、リモートレリーズ制御直後に以下のコードを挿入し、Win32APIでメッセージ受信をする必要がある。（イベント自体は非同期でハンドラが呼ばれる。）

```cpp
#ifdef WIN32
		MSG msg;
		while (GetMessage(&msg, NULL, NULL, NULL)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
#endif
```

このwhile文中では、実際に欲しいイベント（例えばkEdsObjectEvent_DirItemRequestTransfer）以外にもいろいろ受信するので、実際に目的のイベントが受信されたら適宜breakする必要がある。

## 参考

- https://stackoverflow.com/questions/22965982/canon-edsdk-saving-image-in-my-pc/22976188
- https://stackoverflow.com/questions/6891693/taking-picture-with-edsdk-and-retrieving-it-immediately/10428426
- http://songuke.blogspot.com/2012/05/short-note-about-using-canon-edsdk.html

（この辺の情報、公式ドキュメントにも載せてくれたらホントにいいのになぁ…。）
Dockerイメージとオレオレ証明書組み込みレシピ その2

# SSL 自己署名証明書と Docker レシピ その２

日立グループ OSS AdventCalendar #2 24日目は、OSSソリューションセンタ 神山 から投稿します。

[前回記事(1枚目1日目)](https://qiita.com/hi-naoya/items/d325a57d24a39e4d03e8)にて、Dockerイメージに証明書を組み込む方法を記載しました。今回は、その応用編をいくつか書いていきたいと思います。

## 応用編: マルチステージ・ビルドで組み込む

マルチステージ・ビルドとは、Docker 17.05 移行で導入された機能で、イメージのビルドを複数のステージに分けて実行できる機能です。マルチステージ・ビルド登場以前は、ビルド時のみ必要なパッケージもイメージに含まれてしまい、イメージの小容量化が難しかったのですが、マルチステージ・ビルドでは、多段階にビルドを分離することで、最終的に配布するイメージ(実行環境)からビルド時にのみ必要なパッケージを分離することが容易となります。

どのOSにしても、**ca-certificates** パッケージなど、何かしら インストールする必要があり、最小主義で環境を作りたい場合は、ちょっと気持ち悪い状態になります。

そんな時は、Docker のマルチステージ・ビルド を用いるときれいなイメージを作れます。

```dockerfile
FROM ubuntu as certs
RUN apt-get update -y && apt-get install -y ca-certificates
COPY self-signed.cert /usr/local/share/ca-certificates/extra/self-signed.crt
RUN update-ca-certificates

FROM ubuntu
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
```

これは、1回目のビルドで通常通り証明書の登録ツール(**update-ca-certificates**)のインストールとオレオレ証明書のコピー及び登録をします。
そして2回目のビルドでは、**update-ca-certificates** の出力結果(**/etc/ssl/certs/ca-certificates.crt**) のみを新しいイメージに持ってきます。

最終的なイメージは、次のようなきれいなイメージになります。

- オレオレ証明書を含む、システムが信頼する証明書(**/etc/ssl/certs/ca-certificates.crt**) は存在する
- **update-ca-certificates** はインストールされていない
- 登録のための一時ファイル(**/usr/local/share/ca-certificates/extra/self-signed.crt**) は存在しない

マルチステージ・ビルドはきれいなイメージを作りたいにはとても便利です。

## 応用編: 初見のイメージに証明書を登録する(Jenkins イメージ の例)

ここまでOSやミドルウェアをベースとする場合に、どうやって証明書を登録するかを扱ってきましたが…
実際は特定のサービス（Jenkinsなど…）で証明書を登録したいことも多いかと思います。
自己流なのでもしかしたら効率悪い・邪道な方法かもしれないです。もしもっと良い方法があればコメントでご指摘ください！

試しに [Jenkins](https://hub.docker.com/_/jenkins) を例に、証明書を登録する方法を調べてみます。
証明書の登録は root 権限で行う必要がありますが、多くのイメージは、実行権限が root 以外になっています。

そこでまずはイメージの作りを見ていくのですが…
[Docker Hub で Jenkins](https://hub.docker.com/_/jenkins) を見ると Dockerfile が公開されておりません。

こういうときは、Docker イメージ自体を調べてみます。

```bash
docker pull jenkins
docker history jenkins | grep "USER"
```

このように、jenkins イメージを取得し、history コマンドでどのようにイメージが作られたかを確認します。
この際、今回知りたいコンテナの実行ユーザーを設定している "USER" で絞り込みます。

すると、

```
<missing>   2 years ago   /bin/sh -c #(nop)  USER [jenkins]   0B
```

このような形で、"jenkins" ユーザーに設定されていることがわかります。

次にOSを特定したいのですが、Dockerfile が公開されていない場合、あたりを付けて調べてみるしかないです。
私はよく、

```bash
 docker run jenkins apt
```

といった具合に、パッケージマネージャをたたいてみて、コマンドの有無で調べています。
たまにパッケージマネージャすらも消してしまうような、ギリギリを攻めたイメージがあり、この方法で特定できないこともありますが…経験的にはこの方法でほぼ特定できました。

ここまでわかると、後は 次のような Dockerfile を書いて、ビルドしてあげればOKです。

```dockerfile
FROM jenkins

USER root
COPY self-signed.cert /usr/local/share/ca-certificates/extra/self-signed.crt
RUN update-ca-certificates
USER jenkins
```

1. ユーザをroot に戻す
2. OSに合わせた方法で証明書をインストール(この場合は debian)
   * ここでは **ca-certificates** パッケージはインストールしていません(jenkins イメージではインストール済)
   * 多くの場合 **ca-certificates** パッケージが既にインストールされているため、まずは存在すること前提で呼び出すのがおすすめです。稀にインストールされていない場合がありエラーとなりますので、その場合は、 **ca-certificates** パッケージ のインストールも手順に組み込むと良いです
3. ユーザを元に戻す
   * 元のイメージと使用感を合わせるために、実行ユーザは元に戻しておいた方が良いです

これで、正しくオレオレ証明書を認識するイメージが出来上がります。

## さいごに

今回は、SSL証明書の登録を行う際に、イメージを小さくする方法や、初見イメージに対して組み込む流れを書いてみました。
この辺りは手探りな部分もあるので、もっと良い方法があれば、改善していきたいです。