OmniAICreator/Qiita-1.07M huggingface dataset:
【覚書】iOS アプリ Code keyboard のショートカット一覧

## アプリ紹介

https://apps.apple.com/gb/app/code-keyboard/id1097372399?l=ja

コーディングに特化した iOS キーボードアプリ。
このアプリ、詳細な説明書とか一切ないのだが、一応特定のキーを押しっぱなしにすると、
コーディングで使用率の高い記号や演算子などを素早く入力できるようになっている。

この記事では、それらを一個一個検証して調べてみた。


## `abc` モード

```
 q w e r t y u i o p
  a s d f g h j k l
[S] z x c v b n m [<x]
[123]🌐[       ][Ent]
```

|Q|W|E|R|T|Y|U|I|O|P|A|L|Z|M|🌐|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|1|2|3|4|5|6|7|8|9|0|-|=|&&|//|⚙️|
|!|@|#|$|%|^|&|*|(|)|+|==|&#124;&#124;|/*||
|{|}|||||||||*|!=|&|*/||
|[|]|||||||||/|<|&#124;|||
|||||||||||++|>|!|||
|||||||||||--|>=||||
||||||||||||<=||||

## `123` モード

```
  1 2 3 4 5 6 7 8 9 0
  - / : ; ( ) $ & @ "
[#+=].  ,  ?  !  ' [<x]
[abc]🌐[<-][ ][->][Ent]
```

|-|/|$|&|"|.|?|!|'|🌐|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|--|\|&#124;|&&|”|..|¿|!=|`|⚙️|
||//|&#124;&#124;||@"||||||

## `#+=` モード

```
  [ ] { } # % ^ * + =
  _ \ | ~ < > € £ ¥ •
[123].  ,  ?  !  ' [<x]
[abc]🌐[<-][ ][->][Ent]
```

|%|.|?|!|'|🌐|
|:-:|:-:|:-:|:-:|:-:|:-:|
|‰|..|¿|!=|`|⚙️|

## `Enter 長押し` モード

```
  { } [ ] ( ) < > #  =
  - + * / \ | & ^ ++ --
[\t] . , ? ! ' " @ [<x]
[123]🌐[         ][Ent]
```

|<|>|=|-|+|/|&#124;|&|!|🌐|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|<=|>=|==|--|++|//|&#124;&#124;|&&|!=|⚙️|


- このモードでは、タブ文字が打てる

## おすすめの使い分け

- 基本的には `abc` モードを使用
  - `A` `L` `Z` `M` キーで演算子を出せることを覚えておく
- 1 ～ 2 桁程度の数字は `QWERTYUIOP` で入力
  - 3 桁以上入力したいときは `123` モードを使用
- 括弧やちょっとした記号や演算子が欲しい時は `Enter 長押し`
  - 見つからないときは `#+=` モードを使用

このように、基本的には `abc` `123` モードを使い、
記号が欲しい時に `Enter 長押し` する感じで使えば、良い感じに使えそう。
英語が苦手な人のためのXcode便利機能 SpellCheck

# はじめに
こんにちは！スーパー超絶英弱エンジニアのせーたろです！
profileをplofileと書いてしまう英語力の僕が、インターン先のつよつよエンジニアから教えてもらった便利機能を紹介します。

# Spell Checkの使い方
Xcode11から導入されたSpell Checkという機能を使います。
![スクリーンショット 2021-01-22 1.00.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592713/2c22c240-1fa0-9ce2-227a-c5dadb91b391.png)

Edit < Format < Spelling and GrammarからCheck Spelling While Typingをオンにします。

これでスペルミスをしている箇所に赤い点線が表示されるようになります。
![スクリーンショット 2021-01-22 1.03.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592713/f456ab28-5753-3528-d6bb-f65f2fb0c033.png)

# 修正
ダブルクリックで修正できます。
![スクリーンショット 2021-01-22 1.04.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592713/a65525f2-22b1-0c8a-f5d7-e5b9cafe3a4c.png)

# ショートカットキー
command⌘ + ;でスペルミスの箇所がハイライト、
command⌘ + :でSpelling and Grammar のウィンドウが表示されます。
![スクリーンショット 2021-01-22 1.06.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592713/c267047f-8e4e-6946-b221-44d375a21845.png)

# さいごに
この便利機能のおかげで、レビューなどでスペルミスの指摘を減らすことができるのではないでしょうか！
よければ使って見てください！
ひかり電話をLAN経由で接続する

# ~~言い訳~~
プログラミングと直接関係無いものの、プログラマが生活環境を改善するためのライフハックの1つとしてQiitaに共有させていただきます。
技術的な記事という点ではコミュニティガイドラインに沿っているので許してちょんまげ

# 概要
NTT系のひかり電話を契約したけど、HGWの裏の電話線（モジュラージャック）を使うのは不便という時に使える技
※基本的に構成を記載しているだけなので、詳細な設定は参考URL等を見て下さい。（その気になったらまとめるかも知れません）

- NTT系（フレッツ・コラボ）で、ひかり電話を契約している
- 今まで使ってきた固定電話用の電話機をそのまま使いたい
- HGWとは別の部屋に電話機を置きたい
- モジュラーケーブルでは無く、Ethernet（有線LAN）を使う
- （Wi-Fi接続も可能とは思われるが検証していません）

# 必要な物
- VoIPアダプタ （この記事ではGrandstreamの物を使用する想定）
- 無線接続したい場合は更にWi-Fiブリッジが必要と思われる

# 手順
1. 固定電話にVoIPアダプタを接続
2. HGWの内線設定の情報を確認 （`http://192.168.1.1/`等のURL）
![HGW 内線設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224667/7acd0a23-01ea-6a5e-ce5a-013349331ed8.png)
3. VoIP側設定 （ルーターが`192.168.1.1`の場合は`http://192.168.1.2/`や`http://192.168.1.3/`等のURL）

- SIP Server: HGWのIPアドレス
- SIP User ID: 内線番号
- Authenticate ID: 「内線設定」に記載のID（下記注意点に合わせた場合は内線番号と同じ）
- パスワード: 「内線設定」に記載のパスワード

# 注意点
HGWの内線設定で、接続したい内線番号のユーザーIDが「0003」等になっている場合は頭の000を抜いて設定し直した方が良いです。
000がついていると何故かうまくいきません。
![内線設定 ユーザーID](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224667/6b68a80c-a901-608b-26d3-b023a8504b01.png)


VoIPルーター内の「Preferred DTMF method」は3つともIn-audioにした方が良いという情報がありました。
![VoIP設定画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224667/27e1fb34-b576-7b2b-80a1-1acdcd653fac.png)

# 参考URL
全て2021年1月22日アクセス
- [VoIPアダプタ(HT701)でIP電話を固定電話化することで家の電話代を0円に | IT EVANGELIST.NET](https://itevangelist.net/blog/voip-adapter-ht701-ip-phone-change-to-fixed-line-phone/)
- [ドコモひかり電話でGrandstream HT802を使うには・・・？？？ : てぃーふるの未来](https://t-furu70281.doorblog.jp/archives/18200612.html)
- [ひかり電話ルータにGXP1620を接続 | 社内SEの教科書](https://shanai-se.net/%E3%81%B2%E3%81%8B%E3%82%8A%E9%9B%BB%E8%A9%B1%E3%83%AB%E3%83%BC%E3%82%BF%E3%81%ABgxp1620%E3%82%92%E6%8E%A5%E7%B6%9A/)
JavaScriptで特定の条件を満たす場合にオブジェクト・配列に要素を追加する

JavaScriptで、特定の条件を満たす場合にオブジェクトにプロパティを設定し、満たさない場合にはプロパティ自体を設定したくない場合、以前は次のように後からプロパティを追加するしかありませんでした。

```javascript
const obj = {
  name:  'Sato'
};

if(someCondition) {
  obj.country = 'Japan';
}
```

ES2015からはスプレッド演算子を使って、次のようにまとめて書くことができます。
ややトリッキーですが、条件がtrueのときにオブジェクトを展開することで、外側のオブジェクトにプロパティを追加しています。

```javascript
const obj = {
  name:  'Sato',
  ...(someCondition && {country: 'Japan'})
};
```

配列の場合も同じようにスプレッド演算子を使うことで、特定の条件のときだけ要素を追加できます。

```javascript
const arr = [
  'America',
  'China',
  ...(someCondition && ['Japan'])
];
```

if文で後から追加する方法と比べるとごちゃごちゃしていますが、やはりオブジェクトの初期化時にまとめてプロパティを定義できるのは便利です。

## 参考
https://stackoverflow.com/questions/11704267/in-javascript-how-to-conditionally-add-a-member-to-an-object
Flaskで登録したエラーハンドラを無効化する方法

## はじめに
Flaskのアプリケーションでは [エラーハンドラ](https://flask.palletsprojects.com/en/1.1.x/errorhandling/#error-handlers) という機能を用いて例外発生時のレスポンスを制御できます。

```python
@app.errorhandler(errors.MyFlaskAppError)
def handle_my_flask_app_error(e):
    """エラーを補足し、専用のレスポンスを返す。"""

    return jsonify(e.response_body), e.response_status
```

ドキュメントにもこのことは詳しく記載されていますし、他にも技術系のブログや記事等でもたくさん紹介されているので、Flaskに詳しくない方でも調べればすぐにたどり着ける情報だと思います。
ただ、ハンドラの登録に対してハンドラの無効化に関する情報がなかなか見つからなかったので、メモがてら紹介します。

## 動作検証環境
* Python 3.9.1
* Flask 1.1.2

## 登録したハンドラを無効化する方法
[Flask.error_handler_spec](https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.error_handler_spec) に空の辞書をセットすることで無効化できます！

```python
app.error_handler_spec = {}
```

## 想定されるユースケース
わざわざ自分でハンドラを登録しておいて、それを無効化したいってどういうこと？と思われるかもしれませんが、別に変なことをしようとしているわけではありません。
ユースケースとして、単体テスト、特にホワイトボックス的な観点でのテスト（入力に対して開発者が想定している例外が発生していることの確認）を行う際には必要になってくる操作だと思っています。

例えば、実際にpytestを使ってテストする際には、下記のようなコードを書くことでテスト時にのみエラーハンドラを無効化できます。

```python
import pytest

from myflaskapp import create_app
from myflaskapp import errors


@pytest.fixture
def client():
    """テスト用のクライアントを生成する。"""

    # 初期化済みのFlaskアプリケーションを生成します。
    # 記事の本質からズレるので具体的なコードは省略しますが、中でエラーハンドラの登録も行っています。
    app = create_app()

    # テスト時には設定をテストモードに変更します。
    # 詳しくは公式ドキュメントを参照ください。
    # https://flask.palletsprojects.com/en/1.1.x/config/#TESTING
    app.config["TESTING"] = True

    # 例外を補足するために登録した、エラーハンドラを無効化します。
    # これを設定することで、通常であればハンドラで補足される例外を外まで通過させることができます。
    app.error_handler_spec = {}

    # テスト用のクライアントを返します。
    return app.test_client()


def test_request_parameter_is_empty(client):
    """パラメータが空の場合、意図した例外が発生することを確認する。"""

    with pytest.raises(errors.ParameterError):
        client.post("/", json={})
```
Rustを始めよう0章 - Hello worldしよう

# はじめに
今回は、Rustを使い小さなプログラムを書いてみようということでテキストのHello worldを出力するまでやってみようかと思います。
なお、公式のドキュメントを基にしています。

# Hello world
1.プロジェクトのディレクトリと「Hello_world」のディレクトリを作成します。

```
mkdir projects
cd projects
mkdir hello_world
cd hello_world
```

2.新しいソースファイルを作成します。
Rustでは、ファイルは常に.rs拡張子で終わります。
今回は、main.rsを作成します。

* main.rsを作成したら、下記のコードを書きます。

```main.rs
fn main() {
    println!("Hello, world!");
}
```

* 次に、以下コマンドを入力してファイルをコンパイルします。

```
rustc main.rs
```

* コンパイルが終わったら、実行してみましょう。

```
❯ ./main
Hello, world!
```

**これでHello, world!が見れたら成功です！**

# 解説
ここからが本題です。w
上の「Hello、world！」で何が起こったのかを理解していきましょう！

```main.rs
fn main() {

}
```

* ここでは、Rustの関数を定義しています。
* なお、main関数は、すべてのRustプログラムで実行される最初のコードです。
* main関数は、パラメーターを持たず、何も返さないという名前の関数を宣言しています。
* パラメータがある場合、( )に入ります。

**次にmain関数内の処理を見てきます。**

```main.rs
println!("Hello, world!");
```
これは、テキストを画面に出力します。

**ここで注意すべき4つの重要要素があります。**
1.Rustは、タブではなく4つのスペースでインデントする
2.println！は、Rustマクロと呼ばれるものを呼び出すようです。
どうやら(!)が、通常の関数ではなくマクロを呼び出すことを意味するようです
3."Hello, world!"という、文字列を引数としてprintln!に渡し、 この文字列が画面に表示されている。
4.行をセミコロン（;）で終了します。Rustコードのほとんどの行は、セミコロンで終わります。

**次にコンパイルについて見ていきます。**

* Rustプログラムを実行する前に、rustcコマンドを入力し、ソースファイルの名前を渡して、Rustコンパイラを使用してプログラムをコンパイルする必要があります。GCCっぽいですねw

* 正常にコンパイルされた後、Rustはバイナリ実行可能ファイルを出力します。

```
rustc main.rs
```

実行可能ファイルが生成されていることを確認する。

```
❯ tree .        
.
├── main
└── main.rs

0 directories, 2 files
```

# まとめ
今回は、Rustを使用しHello worldを出力してみました。
次回は、ビルドシステムおよびパッケージマネージャーのCargoを学んでいきます。

# 参考
https://doc.rust-lang.org/book/ch01-02-hello-world.html
MacPCでUSB用LAN変換アダプタが使えなくなったときの解決方法

BUFFALOの `LUA-U3-AGTE-NBK` USB3.0用LANアダプターがMacで使えなくなってしまった！
![ダウンロード.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605055/0d2032ef-dacc-5d85-88a7-215c9e7a0b09.jpeg)

いろいろ調べて **MacOSのBigSurに対応していない** のが原因だとわかりました。
解決方法もすでに記事があったので参考にやってみたのですが最後に一手間あったのでメモっておきます！

## 解決手順
1. 下記の「参考にしたサイト」の手順を実施
2. 作業が終わったらアドレス欄に「86886.jp/lua4-u3-agte/」と入力する
3. 最新のMac用ドライバをダウンロード
4. ダウンロードしたdmgファイルを開いてインストール実施
5. パッケージスクリプトを実行中に英語でモーダルが表示される
6. 「セキュリティとプライバシー」->「一般」->「許可」ボタンをクリック
7. モーダルのOKボタンをクリック
8. 処理が完了したらPC再起動

以上！助けになれば幸いです。

## 参考にしたサイト
https://obel.hatenablog.jp/entry/20201116/1605484800
https://www.tr-imagination.com/archives/6414　←動画あってわかりやすい
[git]GitとGitHubの使い方(初心者向け)

今回は、「Git」「Github」の使い方についてまとめます。
おそらく僕と同じく困っている方もいると思うので参考なればと。

※使用するためにセッティングが必要だと思いますが、今回、そちらは書いておりません。
 あくまで事前設定を終えた後からの話になります。

#GitとGitHubについて
###gitとは
ソースコードのバージョン管理を行うツールになります。
「いつ・誰が・どこを」等を記録し、管理する事ができるツールになります。
主に、共同開発時にこれがあるだけで管理がいやすくなるそうです。（まだ経験がないですw）

管理の方法としては、下記の2種類かと。
・ローカルリポジトリ　　自分のパソコン上に記録・保管
・リモートリポジトリ　　専用のサーバーで管理し、複数人で共有

###GitHubとは
Gitを利用した、開発者を支援するWebサービスです。


なので、GitとGitHubは、一緒に使うことが想定されますので、
使い方は覚えておくべきかと思います、（自分自身にも言い聞かせてます。）

<br>

次は、使い方を簡潔にまとめたいと思います。
#gitの超シンプルな使い方

###
文字でまとめると、個人的にはこのような流れでした。

**「手順一覧」**
1.ターミナルを開く
2.ディレクトリとファイルを作成
3.ローカル作成
4.GitHubでリポジトリ作成
5.作成/変更履歴をローカルに記録
6.ローカルとリモートの紐付け
7.GitHub（リモート）にアップロード

※作業時は、4〜5を繰り返す。   



**次は、もう少し詳しく、分解して表にまとめると、、、、、**

|No.| 操作場所| 手順 | 方法 |
|:-:|:-:|:-:|:-:|
|1| PC|ターミナルを開く  | PCで検索して開く  |
|2| ターミナル|ディレクトリ作成  |　% mkdir ______   |
|3| ターミナル|ファイル作成  |  % touch _____  |
|4| ターミナル|ローカル作成  | % git init  |
|5|　GitHub |GitHubでリポジトリ作成  | リモートリポジトリ作成＋URLゲット  |
|6|　ターミナル |　①作成/変更履歴をローカルに記録  | % git add ____(ファイル名)  |
|7|　ターミナル |　②作成/変更履歴をローカルに記録  | % git commit -m "____"  |
|8|　ターミナル＆GitHub　|　紐付け（コネクト） | % git remote add  origin (※リモートリポジトリURL)<br> ※5でゲットしたURL |
|9|　ターミナル |　ブランチ変更  | % git branch -M main  |
|10|　ターミナル |　リモートへアップロード  | % git push origin main |

以上を行うと、GitHubにファイルがアップロードされているかと思います。

その後、作業を行うかと思うので、
その際は、**6,7,10**を実行すると反映できるかと思います。

|No.| 操作場所| 手順 | 方法 |
|:-:|:-:|:-:|:-:|
|6|　ターミナル |　①作成/変更履歴をローカルに記録  | % git add ____(ファイル名)  |
|7|　ターミナル |　②作成/変更履歴をローカルに記録  | % git commit -m "____"  |
|10|　ターミナル |　リモートへアップロード  | % git push origin main |

以上、たにーでした。

#参考文献
1. [いまさら聞けないGitとGitHubの違いって何？](https://www.i3design.jp/in-pocket/3111)
Gitでタグ打ちの手順

## GnuPGをインストール

```
$ brew install gnupg
```

## 以下のURLの手順に従う
https://docs.github.com/ja/free-pro-team@latest/github/authenticating-to-github/generating-a-new-gpg-key

・GPGキーペアを作成 (emailはGitHubアカウントのemail)
・GitHubアカウントへのGPGキーの登録
・Gitに署名キーを伝える
・macOSの場合pinentry-macが必要になる

```
$ brew install pinentry-mac
You can now set this as your pinentry program like

~/.gnupg/gpg-agent.conf
    pinentry-program /usr/local/bin/pinentry-mac

$ echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
$ killall gpg-agent
```

・タグに署名する

```
$ git tag -s <tag-name>
$ git tag -s v1.13.6
```

・タグをpush

```
$ git push origin <tag-name>
```
ElastiCacheとは

## 勉強前イメージ

キャッシュするサービス
でもなんでDB系が多いんやろう

## 調査

### ElastiCacheとは

メインメモリキャッシュのデプロイ、運用、スケーリングをクラウド内で簡単にできるAWSのサービスです。
データをノードのメモリに保存するので、HDDやSSDへの書き込み読み込みより非常に高速なデータの出し入れが出来ます。
2種類のエンジンをサポートしています。

### そもそもキャッシュとは

一度アクセスしたwebページの情報をブラウザやノード側で保管し、次同じアクセスがされた際の表示を速くする仕組み・対象のデータのことを指します。
webページを表示するには毎回ダウンロードが必要になりますが、一時的にデータを保存(キャッシュ)することで表示スピードが向上します。
そのキャッシュをクラウド上でするのがElastiCacheのサービスになります。

### ElastiCacheの特徴

- インメモリデータストアの昨日

ディスク読み込みとメモリ読み込みでは10倍ほど処理速度に差が出ます。
その結果、インメモリにすることにより高速な読み書きが可能になりました。

- フルマネージドサービス

AWSではよく聞きますが、ハードウェアの準備や電源監視、ハードウェアの運用作業などAWSが請け負ってくれるサービス体系で、
サービスを利用するだけでElastiCacheが使えます。

### 2種類のエンジン

ElastiCacheには以下の2種類のエンジンが存在します。

- Memcached : シンプルなデータ構造で、スケールアウト・スケールインへの対応
- Redis : 複雑なデータ構造で、キーバリューストアとしても使用できる

#### Memcached

分散型インメモリキャッシュで、キーとバリューをシンプルな1対1で組み合わせて保存するキーバリューストアです｡
RDBと連携してデータベースの負荷を下げたり、
データを古い順から削除するような機構があります。
拡張性についてはスケールアウト・スケールインが出来ますが、クライアント側での制御が必要です。

#### Redis

キーとバリューを紐付けて管理するNoSQLデータベースです。
以下が主に使えるデータ型になります。

- Strings
- Lists
- Sets
- Sorted Sets
- Hashes

また、マスターと複数のスレーブで構成する事で負荷分散をする事が可能で、
メモリ上のデータをディスクに保存する事で､停電等の電源消失時にデータを失わずに保存することが出来ます。

## 勉強後イメージ

キャッシュするためのサービスもあるのか・・・
DB系が多いのはただ単にキャッシュとしてこのサービスを使いたい需要がDB多い的なあれ？

## 参考

- [Amazon ElastiCacheで高速DB実現](https://www.acrovision.jp/service/aws/?p=685)
- [【AWS】Amazon ElastiCacheとEC2の活用法](https://www.acrovision.jp/service/aws/?p=2304)
- [ElastiCacheの2つのエンジン ~RedisとMemcachedについて考える~](https://www.acrovision.jp/service/aws/?p=786)
- [Amazon ElastiCache](https://qiita.com/leomaro7/items/f031cfdd7d12d5d5ccc5)
- [ElastiCacheは良いサービス！！特徴や使い方をおさらいしましょ！]()
- [ElastiCacheは良いサービス！！特徴や使い方をおさらいしましょ！](https://dev.classmethod.jp/articles/elasticache-is-very-good-lets-review/)
- [AmazonElastiCacheのドキュメント](https://docs.aws.amazon.com/elasticache/index.html)
Swift Packageのバージョンを最新に解決できない

## 概要
iOSアプリ開発のCI/CDを行うときは、xcode buildやfastlaneでビルドやテストを行うと思います。その際にプロジェクト（ワークスペース）が参照しているSwift Packageがなかなか狙い通りに解決されなかったため、備忘録として残します。

## とりあえず結果
結論としてはxcodebuildやfastlaneのパッケージ解決の方法では難しいことがわかりました。Package.resolvedやDerivedDataを削除しても謎のファクターXを参照して、古いリビジョンを解決してしまいます。

### 最新リビジョンを取得するワークアラウンド
そのプロジェクトがSPMでない場合でも、Package.swiftを用意し、そこにSPMのパッケージを書きます。
で、アップデートして、Package.resolvedを移動させます。

```swift
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Hoge",
    platforms: [
        .iOS(.v14),
    ],
    dependencies: [
        .package(name: "Hoge", url: "https://xxxxxxxxx",
        .branch("master"))
    ]
)



```

```yaml
swift package update
mv Package.resolved Hoge.xcworkspace/xcshareddata/swiftpm/
bundle exec fastlane tests
```

この場合、Package.resolvedを参照してほしいので、fastlaneの場合は、disableAutomaticPackageResolutionを有効にします。

```Fastfile.
    run_tests(..,
              xcargs: "-disableAutomaticPackageResolution",
              ...
              )
```
これで最新版をなんとか解決できました！

## パッケージ解決に関与してそうなパラメータ
#### Package.resolved
`XXX.xcodeproj/xcshareddata/swiftpm/Package.resolved` というパスにあるファイルです。基本的にこのファイルがある場合は、このファイルに記載されているリビジョンのものがチェックアウトされますが、無視する設定もできるようです。

#### DerivedData
いわゆるビルド時の中間ファイルです。これがあると最新を取得してきてくれないことがあります。 このパスは指定することができるようで、fastlaneだと`cloned_source_packages_path`というパラメータで指定することができます。xcodebuildだと`clonedSourcePackagesDirPath`というパラメータです。

#### resolvePackageDependencies
`xcodebuild`コマンドに渡すパラメータです。このパラメータの挙動を正確に把握していないのですが、プロジェクトAがSwiftPackageBとSwiftPackageCを`xcodeproj`で参照しており、SwiftPackageBがSwiftPackageCを参照していた場合、プロジェクトAが参照するプロジェクトCのバージョンは、プロジェクトBのPackage.Swiftのバージョンになる、という挙動をしました。



というところまでは調べたのですが、ファクターXがなんなのかが結局わかりませんでした。
FirestoreでorderByすると、指定したfieldを持たない場合はクエリ結果に含まれないらしい

タイトルのままですが、、

https://firebase.google.com/docs/firestore/query-data/order-limit-data?hl=ja#limitations

> orderBy() 句は、指定したフィールドの有無によるフィルタも行います。指定したフィールドがないドキュメントは結果セットには含まれません。

例えば、

``` ts
firebase.firestore().collection("users").orderBy("name", "asc")
```

のようなクエリを以下のようなデータセットに対して投げた場合、

```
{
   id: "user1",
   name: "餃子"
   isFreelance: false,
   occupation: "restaurant owner"
},
{
   id: "user2",
   isFreelance: true,
   occupation: "Anonymous self-taught software developer"
}
```

`user2`はクエリ結果に含まれないらしい。

Firestoreのスキーマの変更で新しくフィールドを追加したけど、古いデータにはまだフィールドが存在していないとか、何かしらの理由でorderByで指定したフィールドを持たないデータがある場合に、orderByするとクエリの結果件数が減って焦る！みたいなことが起こらないようにメモ残しておきます
MVCモデルについて



学習を始めてしばらく経ったので復習をかねてアウトプットします。


# MVCモデルとは

モデル（Model）の頭文字の「M」、ビュー（View）の頭文字の「V」、コントローラー（controller）の頭文字の（C）から「MVC」と呼びます。
それぞれの役割ごとにModel, View, Controllerに分割して作業を行います。
それぞれの持つ役割とは，

- Model -->  アプリの中で扱うデータを保持、操作、システムの設計や機能をどうするか決める

- View  -->  HTMLなどの実際の見た目を司る部分。受け取ったデータを表示したり、入出力といった機能を処理する

- Controller  -->  ユーザーからのリクエスト（入力）に基づき，ModelとViewを呼び出して処理し、その結果を返す

概念図としては次の図１のようになります。（大枠）
図１
![MVC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/723715/90dd5bf2-e0bf-6d72-bb87-2b7a5d0227bf.png)



Rubyにおいて実装箇所がそれぞれ分かれているためそれぞれの役割と関連性を覚えることが重要です。

図２においてユーザーから入力があった時のMVCの流れを図にしました。


#MVCモデルでの処理の流れ

図２
![MVCの処理の流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/723715/530594b3-3180-e11e-3f09-e10ffbc8dcef.png)


- Model  -->  受け取った命令を元に実際にデータの処理を行う

- View  -->  処理を行ったModelの状態を表示する

- Controller  -->  ModelとViewに処理を分担する

例えば、ファーストフード店があったとします。
接客担当Vさん（View）がお客様（User）から受け取った注文を厨房に伝達する係員Cさん（Controller）に伝えます。そしてCさんが調理担当のMさんたち（Model）に注文がハンバーガーなのかポテトなのかを指示し、それぞれの担当のMさんが作った料理をVさん経由でお客様に渡すというのが処理の流れになります。

このような役割分担が各機能ごとに存在するので分業して作業をしやすく変更・修正があった場合の影響が出にくくなっています。

今回の記事は以上になります．

図解を作る技術が発展途上のため見ずらかったらすみません。
間違っている点や理解が不十分な点等あればご指摘いただけると助かります。．
React Developer Toolsのインストールを推されるコンソールメッセージ消してみた。

# 行いたいこと
ブラウザのコンソールに出てる以下の文章を本番環境で消したかった。
`Download the React DevTools for a better development experience: https://fb.me/react-devtools`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116515/ec999e51-0634-49db-9227-72d38e05a9c4.png)

# やったこと
```html
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  <head>
<!-- 略 -->
  </head>
  <body>
    <script>
      // 本番環境上でコンソール上のdevtoolに関するメッセージを本番環境では表示しないようにする。
      @production
        window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
          supportsFiber: true,
          inject: function() {},
          onCommitFiberRoot: function() {},
          onCommitFiberUnmount: function() {},
        };
      @endproduction
    </script>
    <div id="app"></div>
  </body>
</html>
```

# 解説
グローバル変数に以下の内容を代入すれば、devtoolに関するメッセージは消える
**※ただしバンドルされるコード内に記載しても意味がない。**

```javascript
  window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
          supportsFiber: true,
          inject: function() {},
          onCommitFiberRoot: function() {},
          onCommitFiberUnmount: function() {},
        };
```

# 補足（Laravel）
投稿主の環境はPHP(Laravel) + React.jsの構成になっている。
ちょうどいいからLaravelのBlade(テンプレートエンジン)上で、本番環境では、出ないようにするために、
`@production` と `@endproduction`で挟んでいる。

`@production`の解説
https://readouble.com/laravel/7.x/ja/blade.html#if-statements
Laravel Mix + glob => Pugのコンパイル自動化

# はじめに
LaravelにPugを導入する機会があり、いろいろと調査を重ね、
コンパイル時のパスマッピングを自動化することが出来たので載せてみました。

Laravel Mixの導入については[npmの公式サイト](https://www.npmjs.com/package/laravel-mix-pug)を参考にした。

# 前提
``npm`` と ``node`` がインストールされている前提です。

【参考までに私の環境↓】

|    |npm    |node    |
|:-: |:-:    |:-:     |
|Ver.|6.14.11|v10.23.1|

# 本題
調べてみるとSassやJsのコンパイル自動化は出てくるが、Pugについては見つからなかった。
それどころか諦めてしまっているものも見つかった。。
（もう少し早く辿り着けていれば助言出来たかも。。）
[【webpack】【Laravel】laravel-mix-pugで特定のディレクトリを再帰的にビルドする方法はないでしょうか？
](https://teratail.com/questions/196539)

SassやJsの自動化を**glob**を使用して自動化するという記事を参考にさせていただきました。
【参考】
[Laravel Mix+globによるコンパイルパスマッピング自動化](https://qiita.com/K-Shuuun/items/aca37ec1ecd1dbf0c439)
 
###globの導入

```console:bash
# webpack.mix.jsがある場所でインストール
$ npm install glob
```

次に``webpack.mix.js``を下記のように実装。

```js:webpack.mix.js
const mix = require('laravel-mix');
mix.pug = require('laravel-mix-pug');
const glob = require('glob');

/**
 * pugのコンパイル用パス自動マッピング
 */
glob.sync(`resources/pug/**/*.pug`).map(function(file) {
    const index   = file.indexOf('/pug');
    const dir     = file.slice(index + 1);
    const dirPath = dir.split("/").reverse().slice(1).reverse().join("/");
    // コンパイル先のフォルダ/ファイル名
    const pugPath = dirPath.replace('pug', 'resources/views');
    // コンパイル元のフォルダ/ファイル名
    const exPath  = pugPath.replace('views', 'pug');

    // マッピングパスを動的に生成
    mix.pug(file, pugPath, {
        ext: '.blade.php', // コンパイル時、〇〇.blade.phpの拡張子で生成
        excludePath: exPath
    });
});

```
これで完成です。

####注意点：
・**コンパイル元のフォルダと同じものがコンパイル先にないとエラーになる**
例：
``/resources/pug/test/hoge.pug`` をコンパイルしたい場合、
``/resources/views/test`` を作成してから行うこと。

・``excludePath``の指定を忘れると、ルートディレクトリ直下にファイル生成をしようとしてしまう。

・参考にさせていただいた記事の
**[ファイルごとにピックアップしてコンパイルしたいとき](https://qiita.com/K-Shuuun/items/aca37ec1ecd1dbf0c439#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%94%E3%81%A8%E3%81%AB%E3%83%94%E3%83%83%E3%82%AF%E3%82%A2%E3%83%83%E3%83%97%E3%81%97%E3%81%A6%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%81%97%E3%81%9F%E3%81%84%E3%81%A8%E3%81%8D)**
については実装していないため、コンパイル実行時ファイル数によっては時間がかかる。

# まとめ
調べても中々出てこなかったので苦労しましたが、自動化は便利だなと。。
やっぱりLaravelでは中々Pug使わないのかな。。
My Library機能を使ってよく使うWidget/Skin/Componentを保存・利用する方法

#目次
1. はじめに
2. 実装方法
3. 最後に

#はじめに
Visualizerには**My Library**という、よく利用するWidget/Skin/Componentを保存・利用できる
お気に入り登録のような便利な機能があります。

ライブラリに保存することで、よく利用Widget等を
異なるプロジェクトで簡単に再利用できるので、今回はその利用方法をご説明していきます:laughing:

#実装方法
まず、ライブラリを作成したことがない場合、ライブラリを新規で作成していきます。
例として、コンポーネントからライブラリを作成する方法を説明します。

##ライブラリを作成・保存
左メニューの**Templates**タブを開き、
**Components** ->ライブラリに保存したいコンポーネントを選択 -> **Add to Collection** を選択します。
作成済みのライブラリがある場合、ライブラリ名が表示されますが、
既存ライブラリがない場合、**New Library**を選択し、新規のライブラリを作成しましょう。

![スクリーンショット 2021-01-21 23.57.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/0ff446e8-7f14-8e18-7356-1efd4470868e.png)

ライブラリの名前を設定する画面が表示されますので、ライブラリ名とコレクション名を設定します。

今回はこのように設定しました。
![スクリーンショット 2021-01-21 23.10.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/aab9aeed-0330-8d3e-866f-737880ddf39c.png)

これでライブラリが作成・保存できましたので、
次回以降、よく利用するものはこちらのライブラリを選択することができるようになりました。

スキンをライブラリに保存したい場合、
**Skins**タブを選択し、保存したいSkinを選択、同様に**Add to Collection**を選択します。
作成済みのライブラリがある場合は、画像のように、
選択できるライブラリが表示されます。

今回は、`themes`を選択しました。
![スクリーンショット 2021-01-21 23.25.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/23cffac3-eba9-1ad4-28b6-f516528f244e.png)

ウィジェットの場合、
対象のウィジェットを選択し、同様に**Add to Collection**を選択します。
![スクリーンショット 2021-01-21 23.22.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/bfbf17a8-d48f-6bb9-f7e9-e2541ae321bc.png)

保存したものは、左メニューの**My Library**を選択すると確認できます。
コンポーネントとウィジェットは、**Collections**に保存されており、
![スクリーンショット 2021-01-21 23.34.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/b06b5a4d-4f3d-716d-e05c-5e1949731f3f.png)
スキンは、**Skins**に保存されました！
![スクリーンショット 2021-01-21 23.33.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/968faab0-c544-e0aa-2fa8-76c75795befb.png)

これで、ライブラリによく利用するものが保存できました。

##ライブラリからWidget/Skin/Componentを適用
次にライブラリに保存したものを別プロジェクトで適用していきます。

別プロジェクトを開くと、**My Library**に先ほど保存したものが表示されていることがわかると思います。

こちらからFormに追加したい場合は、
追加したい対象をドラックアンドドロップし、追加します。

<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://t.co/lWwg942Wjh">pic.twitter.com/lWwg942Wjh</a></p>&mdash; Kony (@Kony12763790) <a href="https://twitter.com/Kony12763790/status/1352265228713562114?ref_src=twsrc%5Etfw">January 21, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ライブラリからフォームにコンポーネントを追加できました！
ウィジェットも同じように追加できます。

Skinを適用したい場合は、
対象となるWidgetを選択し、適用したいSkinをを選択すればOKです:relaxed:
<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://t.co/SUE5dvP5bH">pic.twitter.com/SUE5dvP5bH</a></p>&mdash; Kony (@Kony12763790) <a href="https://twitter.com/Kony12763790/status/1352266044488912899?ref_src=twsrc%5Etfw">January 21, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#最後に
アラートポップアップなど、どのプロジェクトでも共通して利用するウィジェットやスキンがある場合、
My Libraryを利用することで簡単に再利用することが可能です！
便利な機能なので、日々の開発で是非活用してみてくださいね:relaxed::sunny:
Railsのルーティングについて学び直そうと思う…①

#はじめに
学習初期から、ルーティングについては、`resources`を使うことが多かったので、よく理解できていない部分が多々あったので、少しずつ調べて紹介していこうと思います。

##リソースを同時に定義する

```ruby
resources :users
resources :admins
```

```ruby
resources :users, :admins
```
上の2つは全く同じです。
`resources`を全て使うなら、まとめた方が読みやすいですね。
マジョカアイリスハック SAVIファイルから画像抜き出し

単純にSAVIファイルから画像抜き出しをします。
環境はUbuntu20です。

※画像を抜き出すだけの適当なものなので、あとで編集すると思います。

参考リンク
https://twitter.com/bakueikozo/status/1347204355582173184/photo/1
https://github.com/shikarunochi/Majokairis/blob/main/extract/extract.py

saviファイルから画像の共通ヘッダー抜き出し

````
hexdump -e ' /1 "%02x "' -v B018.savi -s 2336 -n 624 -v > header_image.hex ;
````

画像群を切り出し

````
hexdump -e ' /1 "%02x "' -v B026.savi | sed 's/ff da 00/\nff da 00/g'  | split -l 1 -d - image_ -a 3 ;
````

画像の共通ヘッダーと画像ファイルを結合

````
ls image_* | xargs -I IMAGE_FILE bash -c "cat header_image.hex IMAGE_FILE > combine_IMAGE_FILE" ;
````

JPG画像に変換

````
ls combine_* | xargs -I COMBINE_FILE bash -c "xxd -r -p COMBINE_FILE > COMBINE_FILE.jpg" ;
````

![EsGOkg_VcAEFZjt.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115935/de1806db-1290-6d4b-14c5-977f9bbde3db.jpeg)

おまけ：画像を動画に変換

````
ffmpeg -i combine_image_%03d.jpg -pix_fmt rgb24 -f gif output.gif ;
````

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115935/23b35152-9270-0a68-ff55-5ee7ebafac5c.gif)
robocopyでコピー済みのファイルを再度コピーしてしまう問題

##現象
robocopyコマンドで、フォルダ・ファイルをコピーするときに
コピー元のフォルダ・ファイルには一切変更していないにも関わらず、
コピー対象として認識されコピーしてしまう

例として、robocopyコマンドを、
1回目実行したとき：新規にコピーする
2回目実行したとき：スキップする
3回目実行したとき：コピーする　　　←本来はスキップされるべきがなぜかコピーする
4回目実行したとき：スキップする
5回目実行したとき：コピーする　　　←本来はスキップされるべきがなぜかコピーする
となり、実行都度交互にコピーが走ってしまう
（1回目～5回目のあいだ、フォルダ・ファイルには一切何も修正を加えていない）

##原因
robocopyコマンドのオプションに、/SECFIXを指定していた
答えは以下のサイトにあった
https://n-archives.net/software/robosync/articles/robocopy-security-information/

(抜粋)
/SECFIXは、セキュリティ情報をコピーする際、コピー先のMFT更新時刻をコピー元に合わせるのではなく、
コピー実行時の時刻に更新してしまうため、スキップ対象だったファイルが、次回の更新コピーにおいて全てコピー対象となってしまう

私が確認した際は、毎回ではなく交互という動きをするが・・・


##対処方法
詳しくはやはり上記のサイトに記載があるが、
/SECFIXは使わず、/COPYALLと/ITオプションを組み合わせるなどする
【React Native】ScrollViewとFlatlistの違い

#はじめに
現在UdemyでReact Nativeを勉強しています。
小さいことですが、なるほどと感じたことをこちらでアウトプットしようと思います。
ちなみに受講しているコースはこちら↓です。

React Native - The Practical Guide [2021 Edition]
https://www.udemy.com/course/react-native-the-practical-guide/

日本語字幕が無いのがちょっと残念ですが、分かりやすくゆっくり話してくれるのでだいぶ助かります。

#ScrollViewの書き方

```react
<ScrollView>
     {goals.map(goal => (
       <VIew key={goal}>
         <Text>{goal}</Text>
       </View>
     ))}
</ScrollView>
```
シンプルにmap関数で表示させているものをScrollViewタグで挟んであげる。

#Flatlistの書き方

```react
<FlatList
        keyExtractor={(item, index) => item.id}
        data={goals}
        renderItem={itemData => (
          <View>
            <Text>{itemData.item.value}</Text>
          </View>
        )}
      />
```
ScrollViewと書き方は全然違う。

#ScrollViewとFlatlistの違い
コース内ではパフォーマンスの観点でFlatlistをオススメされました。
ScrollViewは全てのデータをレンダリングしてしまうため、データの数が100,1000となるとパフォーマンスが下がってしまうようです。
一方Flatlistは画面に表示されるデータのみレンダリングするので、データ数が多くてもScrollViewと比べてパフォーマンスに影響しないようです。

#終わりに
今回はScrollViewとFlatlistの違いについて書きましたが、正直まだ自身を持って説明できません。
今後新たな学びがありましたら更新します。
もしご指摘等ありましたら、ご教示いただけると幸いです。
チラシの裏: Mac HomeBrewでなんかおかしくなってIntelliJ IDEAのremote script `idea` が動かなくなったのはpyenvのrehashで直った

## T/O

* エラーメッセージがfindのログで流れてしまって後の祭。
* `~/.pyenv/shims/pyenv` が、 `/usr/local/Celler/pyenv/1.2.22/bin/pyenv` を見つけられない、みたいなエラーメッセージだった。いや、あるし。なんでやねん。
* でhelp見てみたら答が書いてあった。

```shell
pyenv --help
-> Usage: pyenv <command> [<args>]

Some useful pyenv commands are:
   --version   Display the version of pyenv
   commands    List all available pyenv commands
   exec        Run an executable with the selected Python version
   global      Set or show the global Python version(s)
   help        Display help for a command
   hooks       List hook scripts for a given pyenv command
   init        Configure the shell environment for pyenv
   install     Install a Python version using python-build
   local       Set or show the local application-specific Python version(s)
   prefix      Display prefix for a Python version
   **rehash      Rehash pyenv shims (run this after installing executables)**
   root        Display the root directory where versions and shims are kept
   shell       Set or show the shell-specific Python version
   shims       List existing pyenv shims
   uninstall   Uninstall a specific Python version
   version     Show the current Python version(s) and its origin
   version-file   Detect the file that sets the current pyenv version
   version-name   Show the current Python version
   version-origin   Explain how the current Python version is set
   versions    List all Python versions available to pyenv
   whence      List all Python versions that contain the given executable
```
## やってみる

```shell
pyenv rehash
->
idea .
->
```
なおった。へぇーへぇー (2へぇ)
Androidのログ出力ライブラリ”Timber”

どうも”おもり”です。

今年はOSS活動に精を出していこうと考えています。
Android開発のOSS活動。力不足の自分でもやっていけそうなissueを探している中、
以下のようなissueを見つけた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276053/740e7726-d41d-ab53-11ff-20d8c9a3246a.png)

[https://github.com/uhooi/UhooiPicBook-Android](https://github.com/uhooi/UhooiPicBook-Android)より


"good first issue"のタグが付いているので、いざやってみようと思ったのですが、
Timberとは何ぞや状態だったので、色々ネットを調べあさりました。
調べた情報をまとめて、Timberの良いところをサクッと学べるような記事を書きます。

# Timberとは？

Timberとは、Androidのログ出力ライブラリです。

Androidのログ出力機能といえば、Androidのログ標準ライブラリ（Log）があります。

```Logライブラリ
Log.v("MainActivity", "verbose")
Log.d("MainActivity", "debug")
Log.i("MainActivity", "info")
Log.w("MainActivity", "warn")
Log.e("MainActivity", "error")
```

※各ロギングの意味合い
Log.v：VERVOSE（すべてのログ情報）
Log.d：DEBUG(デバッグ情報）
Log.i：INFO(情報）
Log.w：WARN(警告）
Log.e：ERROR（致命的な問題）

しかし、ログ標準ライブラリを使うと問題があります。

* リリースビルドなのにでバックログが出力される
* デバックビルドなのにCrashlyticsに出力される

特に、リリースビルドでもでバックログが出力されることが大きな問題です。

# Timberを使って嬉しいこと

* デバックとリリースでログ出力を切り替えることができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276053/c2cc754d-1696-007a-7144-71c06b8e4f5b.png)
※リリースの場合は、自分でロギングクラスを作成する必要がある。

* Logクラスの薄いラッパークラスなので導入が簡単

```
public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();

    Timber.plant(new DebugTree());
  }
}
簡単に導入できる。TreeをPlantして、初期化をする。
```

* 書きやすい

```
Timber.tag("hoge").i("piyo=%s", "fuga");
```
Logライブラリの使い方と使用方法がそこまで違わないので、使うのが簡単。

* リリース後のクラッシュログの収集ができる。

Crashlytics等を使ってリリース後のクラッシュログを収集することができるらしい。
自分のアプリを作る時に試してみよう。


# 参考資料

[Androidのログ出力をいい感じにする](https://speakerdeck.com/cutmail/androidfalseroguchu-li-woiigan-zinisuru-number-potatotips-9)
[AndroidのTimber(ログ系)とFabric/Crashlytics(クラッシュレポート)を使ってみた。](https://araiyusuke.hatenadiary.com/entry/2015/11/01/182339)
[[Android] Timber で debug ビルド時のみにログを出力する](https://qiita.com/hkusu/items/d4f24141d11e05f57451)
[（Android）ログ出力ライブラリ"Timber"を使ってみる](https://iyemon018.hatenablog.com/entry/2017/09/12/170201)
[Logcatのソースへ飛ぶ機能をTimberに盛り込んでみた。](https://qiita.com/shiraji/items/5815bfe667d042051119)
[TimberをLogの代わりに使用して快適ログ確認](https://archive-blog.yagi2.dev/2016/12/06/how-to-use-timber.html)
4.1より前のRedmineでもクリップボードから画像ペタリしたいです（Wikiページに）

#これをしたい！

![up.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/b47687a9-077d-150f-a8e8-7831295385be.gif)

Lightshotでクリップボードに保存した画像を ```Ctl + V``` だけでWiki記事にペタリ！

↓Lightshotについてはこっち。
[ウェ～イなPrintScreenを実現してくれる画面キャプチャソフト『Lightshot』](https://qiita.com/waokitsune/items/60115b8d343bcfc69468)

#4.1.0からはできますです

[Redmine4.1.0リリース情報](http://blog.redmine.jp/articles/4_1/redmine-4_1_0-released/) によると標準で搭載されてるようです。

あたいもDockerで4.1.0の環境作ってやってみたら実際できました。

でも宗教上の理由とかで中々アップデートできない事情もありまして…。
というわけで4.0系でもできる方法です。

#ビューカスタマイズプラグインにJavaScript

↓ビューカスタマイズプラグインってなんぞや？な時はこっち。
[CentOS8にインストールしたRedmineにビューカスタマイズプラグイン導入](https://qiita.com/waokitsune/items/d08c96bf44b32000981e)

作ったビューカスタマイズの設定がこれ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/3f949de4-2767-ec3c-7448-58aed0ef10a3.png)

```javascript:コード
$(function() {
	
	//実行トリガー
	//ファイル選択してアップロードするボタンがある場所限定ね！
	$('form div.box').has('input:file.filedrop').on('paste', copy_image_from_clipboard);

	//処理のエントリーポイント
	//この機能のコア
	function copy_image_from_clipboard(e) {
		
		//思わぬ悪影響が出ないように
		if (!$(e.target).hasClass('wiki-edit')) { return; } //Wiki編集画面だけね
		
		var clipboard_data_ = e.clipboardData || e.originalEvent.clipboardData

		//思わぬ悪影響が出ないように
		if (!clipboard_data_) { return; } //クリップボードが空なら何もしない

		//クリップボードデータ達の中から画像ファイルだけを処理するよ
		var items_ = clipboard_data_.items
		for (var i_ = 0 ; i_ < items_.length ; i_++) {
			
			var item_ = items_[i_];
			
			if (item_.type.indexOf("image") == -1) { continue; }

			var blob_ = item_.getAsFile();

			//2021年1月21日19時50分28秒にyukarinさんがpngを貼り付けたなら…
			//20210121_195028_yukarin.png
			var now_date_ = new Date();
			var filename_ = now_date_.getFullYear()
				+ ('0' + (now_date_.getMonth()+1)).slice(-2)
				+ ('0' + now_date_.getDate()).slice(-2)
				+ '_'
				+ ('0' + now_date_.getHours()).slice(-2)
				+ ('0' + now_date_.getMinutes()).slice(-2)
				+ ('0' + now_date_.getSeconds()).slice(-2)
				+ '_'
				+ ViewCustomize.context.user.login //※1
				+ '.' + blob_.name.split('.').pop();

			//※1 ビューカスタマイズプラグイン入れておくと、
			//『ViewCustomize』っていう名前のオブジェクトからログインユーザー情報取れるから便利だよ

			//Redmineの標準JavaScript関数使いながらファイルアップロード
			var file_ = new File([blob_], filename_, {type: blob_.type});
			var input_element_ = $('input:file.filedrop').first();
			handleFileDropEvent.target = e.target; //handleFileDropEvent()はRedmine標準のattachments.jsに
			addFile(input_element_, file_, true); //addFile()はRedmine標準のattachments.jsに
			
		}
	}
  
})
```

『誰がいつ？が分かるように』『複数人が同時に処理しても衝突しないように』を意識してファイル名をちょっと工夫。

#蛇足
少しでも使いやすくなるように小さい工夫をコツコツ。

#参考サイトさん
https://blog.enjoyxstudy.com/entry/2019/03/14/000000

#バージョン
CentOS Linux release 8.3.2011
Redmine 4.0.6.stable
Chrome バージョン: 87.0.4280.141（Official Build） （64 ビット）
PurgeCSS のシンプルな使い方

PurgeCSS はプロジェクトで使っていない CSS を削除してくれる便利ツールです。

本来は PostCSS や Gulp などのプラグインとして使うのでしょうが、コマンドラインで都度動かすこともできます。

とりあえず使っていないCSSを削除したいときや、簡単に試してみたい時にはこんな方法でいいかもしれません。

### 環境
- node v10.14.1
- purgecss v4.0.0

## デモンストレーション

### ディレクトリ構造
``` shell
index.html
style.css
```

下記、style.css と index.html を見ていただければわかるのですが、
index.html で使っている css は ```.mt-50``` のみです。

### style.css
``` css
.mt-10 {
  margin-top: 10px;
}
.mt-20 {
  margin-top: 20px;
}
.mt-30 {
  margin-top: 30px;
}
.mt-40 {
  margin-top: 40px;
}
.mt-50 {
  margin-top: 50px;
}
.mt-60 {
  margin-top: 60px;
}
.mt-70 {
  margin-top: 70px;
}
.mt-80 {
  margin-top: 80px;
}
.mt-90 {
  margin-top: 90px;
}
```

### index.html
``` html
<!DOCTYPE html>
<html lang="ja">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
  </head>

  <body>
    <p class="mt-50">test</p>
  </body>

</html>
```

デモではわかりやすく30行程度の CSS ファイルですが、
ユーティリティ系のスタイルをたくさん準備しておくと実際の作業では 数千〜数万行になることもあるのではないでしょうか。

おそらく全てのスタイルは使いません。

そこで PurgeCSS です。

### インストール
``` shell
npm i -g purgecss
```

### 実行
``` shell
purgecss --css style.css --content index.html --output style.purged.css
```

オプションの ```--css``` は元のCSSファイル、```--content``` は参照先のファイル、```--output``` は書き出しファイルです。
ファイル名、ファイルパスは作業環境に合わせてください。

### 書き出された CSS ファイル
``` css
.mt-50 {
  margin-top: 50px;
}
```
[Tableau] ランキングチャートの作り方 

#こんなときに

* 自社のアプリやサービスの MAU など、なにかのランキングの推移を作りたいとき

#完成イメージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/fbf04ef2-5a87-d809-c81e-4b98a01f3c1c.png)


#用意するもの

* データソース : サンプル - スーパーストア (Tableau に入っているサンプルです)

#環境

* Tableau Desktop : 2020.3.0

#準備

* ここまで作ってみましょう。

-列に月（2020年で）
-行に 売上合計
-サブカテゴリごと

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/1ab22a64-e9b3-aff6-e8be-e9fc3cef708b.png)

#Step1 (ランク設定)

* 売上を “簡易表計算” => “ランク” に設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/58c30b8c-9357-3c62-4c2c-5ddfb58756c5.png)

* 次に “次を使用して計算” => “サブカテゴリ” に

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/86c7f361-8023-4394-198e-f00b0d2dabb9.png)

* こうなる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/75df55ed-7f5a-3d01-9481-b5cde10674a5.png)

#Step2 (軸の編集)

* 縦軸を右クリック “軸の編集”
* 編集ダイアログのスケールの “反転” をチェック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/bf8863b2-2644-574b-c720-0d5884c763d7.png)

* 行に同じものを作る。（Ctrl 押しながらドラック＆ドロップ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/50b0496d-7d4b-8768-e65a-46dc110736a5.png)

* 二重軸にする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/6ad68c4b-ceb4-0e8c-25c3-7874fb28ae54.png)

* 反対側の軸も編集で”反転”する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/fdd1d02b-b762-b3e3-1a32-6a183fd50b03.png)

* こんな感じになる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/325b0e5d-2bbc-3c46-9e31-633d860fdcea.png)

#Step3 (整形・ランキング数値入れ)

* こいつをいじります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/55d530a1-8ff4-c4a0-bf38-3408dc5ccabf.png)

* 円を選択する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/a49284c7-4e69-ee83-124f-4a59351f877a.png)

* ちょっとズレてて気持ち悪いので、”軸の同期”

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/4f5535ff-91c0-a459-f048-dab0b2778106.png)

* 円のサイズを適当に大きくする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/2c0d07ca-f928-2f63-ba2d-c38d8d4cf70a.png)

* 左側の売上を Ctrl 押しながら ラベルへドロップ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/6bd32ebb-9779-84d8-61ee-abca9870e552.png)

* “ラベルがその他のマークをオーバーラップするのを許可” をチェック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/cb369f7d-6090-111f-c40f-e9648f43e75a.png)

* 数値が円に入るように適当に配置をいじる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/b22e82ab-de07-43e4-5edd-e59cd02b9edd.png)

#完成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/fbf04ef2-5a87-d809-c81e-4b98a01f3c1c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177920/cfba6935-6340-2888-364d-8f0814d9c8ae.png)


おつかれさまでした！
[Swift]型のプロパティについて

##Swiftの型
Swiftの型は、クラス、構造体、列挙型として定義できます。
クラス、構造体、列挙型の3つには、メソッドやプロパティなどの共通の要素が用意されています。
#宣言方法

```swift
struct 構造体名{
   構造体の定義
}
class クラス名{
   クラスの定義
}
enum 列挙型名{
   列挙型の定義
}
```
##インスタン化の方法
型をインスタンス化するには、以下のようにします。
その時同時にイニシャライザが呼び出されます。イニシャライザは、インスタンス化する時に呼びだれるメソッドです。


```swift
型名()
```

#プロパティ
型に紐づいた値のことをプロパティと呼びます。
より詳しくいうと、型の中で定義された変数をプロパティと言います。
#定義方法
プロパティはvarやletで定義します。

```swift
struct 構造体名{
   //再代入可能なプロパティ
   var プロパティ名:プロパティの型=値
   //再代入不可能なプロパティ
   let プロパティ名:プロパティの型=値
}
```
プロパティの整合性を保つために、インスタンス化するまでに全てのプロパティの型を決める必要があるのでプロパティに初期値を代入する必要があります。
構造体の中でプロパティを初期化するか、イニシャライザで初期化するかいづれかの方法でプロパティに値を渡します。

#プロパティの分類
プロパティには2種類あります。
1.インスタンスに紐づくプロパティ`(インスタンスプロパティ)`
2.型に紐づくプロパティ`(スタティックプロパティ)`
###インスタンスプロパティ

```swift
struct Area{
    var tate=10
    var yoko=10
}
var A_1=Area()//インスタンス化
var A_2=Area()//インスタンス化
A_1.tate=3
A_2.tate=7
print(A_1.tate)//3
print(A_2.tate)//7
```
インスタンス化とは実物にするということです。
つまり、A_1とA_2という二つの物体を生成したことになります。
そのA_1とA_2の縦を変更したので、それぞれ３と7になります。
じゃあ、__元々の縦を変更したい時にはどうすれば良いでしょうか。__
その時は、`staticキーワード`を使います
###スタティックプロパティ
```swift
struct Area{
    static var tate=10
    var yoko=10
}
var A_1=Area()
var A_2=Area()
Area.tate=3
A_1.tate=7//エラー
print(Area.tate)//3
```
A_1やA_2からtateにアクセスすることはできません。
そのかわり、型に直接3を代入することでtateを10から3に変更できます。

`スタティックプロパティにはイニシャライザみたいな初期化するメソッドがないため宣言文のなかで初期化する必要があります。`
図解するとVuexストアがはっきり理解できた件、

Vuexストアによる状態管理が十分理解できていなかったが、図解をすることでかなり理解できたからアウトプットしていく。

## Vuexストアの図解

（参考記事）
https://codezine.jp/article/detail/11994

![Vuex 【図解】.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1025574/af6d1008-4bb7-fb29-ec82-ebe9bd1e95d7.png)

【流れの説明】
1. コンポーネントがWebページの更新をするためにアクションを実行する（dispatch）。
2. アクションでWeb APIなどの更新に必要なデータを取得（１）。
3. アクションで取得したデータを状態に反映させるためにミューテーションを実行する（commit）。
4. ミューテーションでステートの変更を行う（Mutate）。
5. ステートでWebページ全体の状態を保持する（３）。
6. 変更されたステートに基づいてコンポーネントが画面を表示（Render）。

細く言えば、まだいろいろな要素が残って行っているけど、基本的にこの流れをおさえておけば、かなり理解できた。

## 実際に使用してみる

概念だけだとあまり定着できないことの方が多いから手を動かしてみる。

[花谷氏の「Nuxt.jsビギナーズガイド」](https://nuxt-beginners-guide.elevenback.jp/)を使って学習した内容をいかして、axios-moduleを利用しWordpressの記事情報を取得するときの状態管理を記述していく。

ヘッドレスのwpの導入方法については今回は割愛。

```store/index.js
// 5. ステートでWebページ全体の状態を保持する（３）。
export const state = () => ({
  posts: [],
})

// stateで保持しているデータを取得するために使用
export const getters = {
  posts: (state) => state.posts
}

// 4. ミューテーションでステートの変更を行う（Mutate）。
export const mutations = {
  setPosts(state, { posts }) {
    state.posts = posts
  }
}

// 2. アクションでWeb APIなどの更新に必要なデータを取得（１）。
export const actions = {
  async fetchPosts({ commit }) {
    const posts = await this.$axios.$get('https://~/wp-json/wp/v2/posts?_embed&tags=6')
    // 3. アクションで取得したデータを状態に反映させるためにミューテーションを実行する（commit）。
    commit('setPosts', { posts })
  }
}
```

```pages/index.vue
<template>
  // 6. 変更されたステートに基づいてコンポーネントが画面を表示（Render）。
  <div>{{ posts }}</div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  async asyncData({ store }) {
    // 1. コンポーネントがWebページの更新をするためにアクションを実行する（dispatch）。
    await store.dispatch('fetchPosts')
  },
  computed: {
    ...mapGetters(['posts'])
  },
}
</script>
```

今回は、直接`state`からデータを取得するのではなく、`getters`を経由して取得している。

順番がかなり前後しているが、このような流れを意識してVuexストアを記述すると初めてうまくいった。

## 図解はかなり効果的な定着方法

コードが複雑なればなるほど、頭の中でイメージを膨らませるのでが大切で、頭の中で整理できないものは実際に図などに落とし込みをするのがいいなと感じた。
Android11から地味に変わったパーミッション

# パーミッション is 何
Androidアプリはアクセスが制限されたサンドボックス内で実行されるため、アプリの外部にあるリソースや情報を使用する必要がある場合には、権限をリクエストする必要があります。この権限がパーミッションです。

Android5.1（APIレベル22）以前は、アプリのインストールと共に必要なパーミッションがアプリに付与されるため、特に権限のリクエストをする必要がありませんでした。

しかし、Android6.0（APIレベル23）以降からはガラッと変わり、パーミッションが必要なタイミングでユーザにリクエストをしなければいけなくなりました。例えば、現在地を取得する時に、端末に「位置情報の利用を許可しますか?」みたいな画面が出てくるようになりました。

# Android11からまた変わったパーミッション
Android11（APIレベル30）から、アプリが

- 位置情報
- カメラ
- マイク

に関するパーミッションをリクエストするたびに、「今回のみ」というオプションが表示されるようになりました。
ちなみに、iOSでは実はiOS13（正式公開日が2019/9/19）から追加されている機能ではあるので、既に見覚えがある方もいるのではと思います。

![スクリーンショット 2021-01-21 22.07.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645987/e58e178a-dd19-939a-5cbd-a0872d107455.png)
参照:https://developer.android.com/training/permissions/requesting?hl=ja

とはいえ、実は変わったのはこれだけではありません。地味に変わった & 重要なところがもう一つあります。

# パーミッションダイアログ表示の回数が減った?
デバイスにインストールされたアプリがパーミッションダイアログを表示し、ユーザが「許可しない」を選択後、再度ダイアログを開いて「許可しない」を選択した場合、同じパーミッションダイアログが再度表示されることがなくなってしまいました。
例えば位置情報に対して2度「許可しない」を選択すると、他の位置情報関係のパーミッションリクエストのダイアログが表示されることはありません。

今までのパーミッションダイアログの表示は次のような形でした。
![スクリーンショット 2021-01-21 22.38.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645987/656f057e-eb9a-c5bb-4f2a-169cfff4e142.png)
しかし、Android11からは次のように変更されています。
![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_645987_49829f35-9247-470c-fc37-a1d5adbe6fff.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645987/7f357966-cfa0-a9e5-0a80-98351bdac556.png)
2度目に表示されるダイアログの「許可しない（次回から表示しない）」が「許可しない」へ姿を変えています。

ユーザさんにとっては、「許可したく無いことに何度も出てくるダイアログうざい！」が解消されたのかもしれませんが、開発者側からすると、「許可の機会が減った！？」となりかねないような変更でした。

これからはユーザさんが選択する「許可しない（次回から表示しない）」のハードルが下がり、相対的に「許可する」のハードルが上がった形になると思います。
開発者からの「使って欲しいおすすめ機能」がパーミッション前提のものだと使ってもらえないなんてこともあるかもしれません。

# まとめ
パーミッションの仕様が地味に変わった話でした。
Androidはこのような仕様変更がちょいちょいあるみたいですので、今後もしっかりと確認していきたいと改めて思いました。

# 編集履歴
2020/02/18 パーミッション表示の回数を修正
Android11からパーミッションが一度しか表示されない → パーミッションを2度拒否すると表示されなくなる
Typescript Jestでaxiosをモック化する

## 概要

実現したかったのは主に２つ

- TypeScript x Jestでaxiosをモック化する方法
- ローカルに置いたJSONファイルを読み込んでAPIレスポンスとして扱う方法


## ライブラリバージョン

package.jsonより

- typescript: 4.1.3
- jest: 26.6.0
- axios: 0.21.1


## 実装

```json:data.json
{
  "name": "Taro",
  "age": 20
}
```

```ts:Person.ts
export class Person {
    name: string;
    age: number
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}
```

```ts:テスト対象のコード
import axios from "axios";
import { Person } from "./Person";

export class PersonConsumer {
    
    async get(): Promise<Person> {
        return (await axios.get<Person>('https://hoge.com')).data;
    }
}
```

```ts:テストコード
import axios from "axios";
import data from "../data/data.json";
import { PersonConsumer } from "./PersonConsumer";
jest.mock('axios');

describe('PersonConsumer', () => {

    it('return person from api', async () => {
        const mockedAxios = axios as jest.Mocked<typeof axios>
        mockedAxios.get.mockResolvedValue({data: data});

        const target = new PersonConsumer();
        const actual = await target.get();
        expect(actual.name).toBe('Taro');
        expect(actual.age).toBe(20);
    });

});
```
tidymodelsとworkflow（その２）

##1.問題点とエラーコード
前回は、tidymodelsのうち、workflowオブジェクトによる方法を行いました。
workflowオブジェクトは非常に便利で、まさに神！な感じです。
しかし、workflowオブジェクトを使った方法で、次のようなエラーがでました。

まず次のコードを実行します。

```{r}
library(tidymodels)
library(tidyverse)

d = mtcars

set.seed(12)
initial_split = initial_split(d, p = 0.75)
train_data = training(initial_split)
test_data  = testing(initial_split)

#前処理を作成　：目的変数を対数化
recipe1 = recipe(mpg ~., train_data) %>%
  step_log(mpg)

#モデルは線形モデル（lm)を指定する
model_lm = linear_reg() %>%  set_engine("lm")

#workflowオブジェクトを作成する
lm_workflow = workflow() %>% add_recipe(recipe1) %>% add_model(model_lm)

lm_workflow

$>══ Workflow ════════════════════════════════════════════════════════════════════════════
$>Preprocessor: Recipe
$>Model: linear_reg()
$>
$>─ Preprocessor ────────────────────────────────────
$>1 Recipe Step
$>
$>● step_log()
$>
$>─ Model ────────────────────────────────────────
$>Linear Regression Model Specification (regression)
$>
$>Computational engine: lm 
```
訓練データにfitさせて、検証データにpredictさせます。

```{r}
lm_workflow_fit = lm_workflow %>% fit(train_data)
lm_workflow_fit %>% predict(test_data)

$>エラー: Assigned data `log(new_data[[col_names[i]]] + object$offset, base = object$base)` must be compatible with existing data. x Existing data has 8 rows. x Assigned data has 0 rows. ℹ Only vectors of size 1 are recycled.
```


##2.解決方法
上記のようなエラーが出て、大変悩みました。
（上のコード事例ではありませんが、同じエラーがでました）

結局、今のところは、workflowオブジェクト独特の問題でrecipeの処理に目的変数を当てる（上記の場合、目的変数の対数化）てpredictをworkflowオブジェクトに当てると生じます。

そこで、解決方法です。

```{r}
#recipe1を使って直接データを前処理（目的変数を対数化）
train_data_juice = juice(recipe1 %>% prep())
test_data_bake = bake(recipe1 %>% prep(),test_data)
recipe2 = recipe(mpg ~., train_data_juice)

#recipeのアップデート:目的変数の対数化を取り消し
lm_workflow = lm_workflow %>% update_recipe(recipe2)

lm_workflow_fit = lm_workflow %>% fit(train_data_juice)
lm_workflow_fit %>% predict(test_data_bake)

$>.pred
$><dbl>
$>3.094213				
$>2.878973				
$>2.802263				
$>2.468462				
$>2.940477				
$>3.370685				
$>3.456701				
$>2.413078
$>8 rows
```
今のところ、目的変数を対象にrecipeを使って前処理をする場合、predictするときにworkflowではエラーがでるので一度、prep(),juice()で前処理を済ませてから、workflowにかける必要があるみたいです。
（目的変数は、前処理過程に入れられない）

##3.参考

[Predict doesn't work for recipe that modify outcome #63](https://github.com/tidymodels/workflows/issues/63)

#enjoy!
python-dotenvを使い環境変数を管理

# python-dotenvを使い環境変数を管理

## 初めに

API_KEYなどPythonファイルにベタ書きだと使い勝手が悪いため
python-dotenvを使い環境変数を設定する

```Python
from datetime import timedelta

import pandas as pd
import tweepy

API_KEY = "xxx"
API_SECRET_KEY = "xxx"
ACCESS_TOKEN = "xxx"
ACCESS_TOKEN_SECRET = "xxx"


def twitter_api() -> tweepy.API:
    auth = tweepy.OAuthHandler(API_KEY, API_SECRET_KEY)
    auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
    return tweepy.API(auth)
```

[python-dotenv](https://github.com/theskumar/python-dotenv)バッケージをインストール

```
$ pip install python-dotenv
```

## 使い方

.envファイル作成

```
API_KEY = "xxx"
API_SECRET_KEY = "xxx"
ACCESS_TOKEN = "xxx"
ACCESS_TOKEN_SECRET = "xxx"
```

settigs.py作成
.envからデータを読み込む

```Python
import os
from os.path import dirname, join

from dotenv import load_dotenv

load_dotenv(verbose=True)

dotenv_path = join(dirname(__file__), ".env")

API_KEY = os.environ.get("API_KEY")
API_SECRET_KEY = os.environ.get("API_SECRET_KEY")
ACCESS_TOKEN = os.environ.get("ACCESS_TOKEN")
ACCESS_TOKEN_SECRET = os.environ.get("ACCESS_TOKEN_SECRET")
```

元Pythonファイル変更

```Python
from datetime import timedelta

import pandas as pd
import tweepy

from settings import API_KEY, API_SECRET_KEY, ACCESS_TOKEN, ACCESS_TOKEN_SECRET


def twitter_api() -> tweepy.API:
    auth = tweepy.OAuthHandler(API_KEY, API_SECRET_KEY)
    auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
    return tweepy.API(auth)
```

## 何がいいか

環境変数が散らばると参照がし難い、セキュリティ的にも良くない
python-dotenを使うと.envに集約できる