OmniAICreator/Qiita-1.07M huggingface dataset:
ダイアログの表示

下の画像のようなダイアログの表示方法に関しての説明になります。

例として今回初期の値増加アプリを改造し、ダイアログを表示し値を増加させるか確認を取るアプリを作成していきます。
<img width="400" alt="ダイアログ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607760/b96451aa-7709-b528-7d1b-f1a198dca002.png">


# ダイアログの追加

下記のようなコードを追加します。

```dart

  Future<void> dialog() async {
    return showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (_) {
        return AlertDialog(
          title: Text('増減確認'),
          content: Text('値を増やしますか？'),
          actions: <Widget>[
            FlatButton(
              child: Text("はい"),
              onPressed: () => _incrementCounter(),
            ),
            FlatButton(
              child: Text("いいえ"),
              onPressed: () => Navigator.pop(context),
            ),
          ],
        );
      },
    );
  }
```

`AlertDialog`を使用するために適当な関数を作成します。
`barrierDismissible`をfalseにすることで、FlatButtonでのみダイアログが閉じるようになります。
`builder`でダイアログの生成を行います。
`title`で見出しを作成、`content`でtitleの下に表示される内容を作成します。

今回は、はいを選択した場合値が1増加するようになっています。
また、いいえを選択した場合はダイアログが閉じるようになっています。

# 完成

最終的なソースコードはこちらになります。
`_incrementCounter()`にも、数値の増加処理が終了したらダイアログが閉じるように`Navigator.pop(context);`を記述しました。

```dart

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ボタン配置',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
    Navigator.pop(context);
  }

  Future<void> dialog() async {
    return showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (_) {
        return AlertDialog(
          title: Text('増減確認'),
          content: Text('値を増やしますか？'),
          actions: <Widget>[
            FlatButton(
              child: Text("はい"),
              onPressed: () => _incrementCounter(),
            ),
            FlatButton(
              child: Text("いいえ"),
              onPressed: () => Navigator.pop(context),
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ダイアログ'),
        actions: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () => dialog(),
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '+ボタンでダイアログが表示されます',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
    );
  }
}
```

選択肢が今回のように2択ではなく、複数ある場合は`AlertDialog`よりも`SimpleDialog`を使用するほうが良いようです。

お疲れ様でした。
ここまでご覧いただきありがとうございます。
Debugオペレータで変数を監視するのは危険（1敗）

- [UniRxの「Debug」オペレータが便利](https://qiita.com/toRisouP/items/5abe4ac264fecaa148e9)

上の記事にある通り、Debugオペレータはとても便利なやつです
ですが、調子に乗って変数を見に行こうとするとハマるかもしれません

フォーマット文字列でDebugオペレータに変数をあげてはいけません（戒め）
#問題のコード
```C#:文字列補完でDebugにTime.timeを読ませている
void Start()
{
    this.UpdateAsObservable()
        .Debug($"Time.time is: {Time.time}")
        .Subscribe(x => Debug.Log($"Time.time is: {Time.time}"));
}
```
##実行結果
![debugoptime.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611062/61c8ab8d-9662-c7f7-227b-6ae26b57a946.gif)
`Subscribe()`内では正常に取れているのに、`Debug()`内では`Time.time = 0`のまま！！！

つまり、`Start()`時点での`Time.time`を参照してしまっているわけです(^q^)
#それでもしたい
```C#:力が欲しいか？
void Start()
{
    this.UpdateAsObservable()
        .Select(_ => Time.time)
        .Debug("Time.time is: ")
        .Subscribe(x => Debug.Log($"Time.time is: {Time.time}"));
}
```
やってやれないことはありません
observable経由で値を渡せるようにすればよいので、`Select()`で無理やり作ってやります
##実行結果
![debugoptime_select.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611062/0ceeaa9f-ab38-176a-5096-70e91ab88b3e.gif)
`OnNext()`のかっこ内に正しく`Time.time`が出力されています
#結論
力技なんでやめといたほうがいいと思います
フォルダ容量測定powershell

# フォルダ・ファイル容量測定Powershell
指定ディレクトリ内のフォルダ・ファイル容量（1MB以上）をすべて算出するPSスクリプト

```powershell:Get-DirectoryVolRer.ps1
function getFuckinHeavyWeightData($path) {
    $children = Get-ChildItem $path `
        | Select-Object `
            Name, `
            @{ name = "Size"; expression = { `
                [math]::round((Get-ChildItem $_.FullName -Recurse -Force `
                    | Measure-Object Length -Sum `
             ).Sum /1MB ) `
            } }, `
            FullName `
        | Where-Object {$_.Size -gt 1}


    foreach($e in $children) {
        if ($e.Size -gt 1) { # 1MB以上をチェック対象とする
            Write-Output ($e.FullName + ',' +  $e.Size)
            if (Test-Path $e.FullName -PathType container) {
                Set-Location $e.FullName
                getFuckinHeavyWeightData($e.FullName)
            }
        }
    }
}

# 対象フォルダ指定
$root = "\\192.168.3.30\book"

# 出力ログパス指定
$outlogpath =  "D:\home\logs\Get-DirectoryVolRecRec_$times.log"

# 時刻取得
$times = Get-Date -Format "yyyyMMddhhmmss"

# ログ出力
getFuckinHeavyWeightData($root)  | Tee-Object -FilePath $outlogpath

# ホームディレクトリに戻って終了
Set-Location $HOME
Write-Host ("処理を完了しました。")
```
NaNはNumber型

# tl;dr
NaN(Number.NaN)は数値ではありませんがNumber型です。

# 説明
数値、またはNaNの変数は以下のように定義します。

```Typescript
const foo: number = 123;
const foo: number = Number.NaN;
```

以下のような定義はできません。

```Typescript
const foo: number | NaN = Number.NaN;
```

NaNのチェックには`isNaN()`を使います。

```Typescript
const foo: number = parseInt('a');
const bar: number = Number('a');
console.log('foo', foo, isNaN(foo));
console.log('bar', bar, isNaN(bar));
```

実行結果

```
foo NaN true
bar NaN true
```

単純に真偽値でチェックすると0との区別がつかないので注意！

```Typescript
const foo: number = parseInt('a');
const bar: number = parseInt('0');
console.log('foo', foo, !foo);
console.log('bar', bar, !bar);
```

実行結果

```
foo NaN true
bar 0 true
```

以上。
CentOS7 英語キーボードから日本語キーボードに交換

交換した日本語キーボード：jp-OADG109A
コマンド
setxkbmap -query
setxkbmap -model jp-OADG109A -layout "jp(OADG109A)"
setxkbmap -query
localectl status
localectl --no-convert set-keymap jp106
localectl --no-convert set-keymap jp-OADG109A
localectl --no-convert set-x11-keymap jp jp106 "" terminate:ctrl_alt_bksp
localectl status
cat /etc/X11/xorg.conf.d/00-keyboard.conf

以下のサイトを参考に該当のxmlを編集する。<default → jp>
【CentOS7 ibus-kkc,ibus-Anthy キーボードが英語になる】
https://qiita.com/Ikumi/items/5ca713683b785ca24c8b
AzureとAWSの比較

###AzureとAWSの比較(2020/2月現在)###

特徴
---
**Azure**はマイクロソフト製品(o365、Windowsなど)との親和性が高いです
裏を返すとこれらの製品にずっと縛られる、ライセンス値上げに悩まされる
という可能性があります

**AWS**はとにかく幅広いサービスを提供します
例えばPaaSタイプのオラクルDBはAzureにないですが、AWSにはあります

AWSはユーザの事を第一に考えている印象があり、機能改善の90%はユーザ要望からです
利用ユーザ、ナレッジ、情報量はAzureよりも優位性があります

ビジネスのモデル
---
**Azure**というよりマイクロソフトとAWSに大きな違いとして
マイクロソフトはライセンスビジネスを軸にしています
ただ、昨今はクラウドにも注力しAWSを追従しています

CEOがスティブバルマーからサティアナデラに変わって
ビジネスの方向性や風土も変わったと聞いたことがあります

**AWS**はサービスは幅広く様々な分野で利用可能です
サービス開始から15年以上の運用実績があります

AWSにライセンス販売というものは一切なく、サービスを使ったら使ったけだけ
お金を支払うという課金モデルが基本で、RI(今はSaving plansも)という先払いの形態もあります
(Azureというかクラウドサービス全体が課金モデル)


#Qiita内のキーワード検索数
* **Azure** 4,317
* **AWS** 23,486





市場のシェア
---
* **Azure** 15%
* **AWS**　33%


※調査会社によってパーセンテージは多少前後します



コンプライアンス(第三者認証)
---
どちらもほぼ同等です

* [Azureコンプライアンス](https://docs.microsoft.com/ja-jp/azure/compliance/)
* [AWSコンプライアンス](https://aws.amazon.com/jp/compliance/programs/)


準拠法・裁判所比較
---
**Azure**は日本法・東京地裁
**AWS**は準拠法・裁判所いずれも米国だが、日本法・東京地裁に変更可能

リージョン
---
* **Azure** 66
* **AWS**   81

※AzureとAWSではリージョンの概念が異なる

日本国内のリージョン
---
**Azure**は東日本/西大阪
**AWS**は東京のみで、大阪ローカルリージョンの利用は承認制
※2021年AWS大阪リージョンが開設

IaaSのバックアップサービス
---
どちらもある
※AWSは2019年に「AWS Backup」がローンチ
(以前はAWSにバックアップのサービスが無いことをよく比較された)

コスト
---
どちらもほぼ同等です
※サービスによっては優位性がどちらかにあることもあります

・価格の競争はいたちごっこで
どちらかが価格を下げると、それに合わせて一方が下げをしています

・価格はオンプレになかったクラウドならではのコスト設計が重要です
使い方次第で、高額になることもありますし、安くできることもあります


最後に
---
**AzureとAWSどちらがいい？**と言うのは
・トヨタと日産どちらがいいか？
・NTTドコモとauどちらがいいか？
これらの比較と同じようなものだと思います

重要なのは、なぜAzureを使うのか、なぜAWSを使うのかだと思います
「〇〇さんがああいったから、Azureを選択した、AWSを選択した」ではなく
自身で調査して納得した上で、クラウドプロバイダーを選ぶべきだと考えます

究極を言うと、やりたい事が満たせるか+コストだと思います
WebViewにJavaScriptを注入してAndroid側に値を渡す

WebViewで表示されているHTMLの内容から、特定の値をAndroid側で取得します。

考え方としてはHTMLページ内にAndroidからJavaScriptを注入して、JavaScriptからAndroidのコードを実行することで値のやり取りをします。

まずメインとなるコードから。

```kotlin
val webView: WebView = requireActivity().findViewById(R.id.web_view)
webView.webChromeClient = WebChromeClient()
webView.webViewClient = CustomWebViewClient()
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(WebAppInterface(requireContext()), "Android")
webView.loadUrl(url)
```
- webChromeClientをセットしないとJavaScriptが実行できない（？）みたいです
- webViewClientにはカスタマイズしたWebViewClientをセットします
- webView.settings.javaScriptEnabledでJavaScriptを有効にします
- webView.addJavascriptInterfaceでJavaScriptからAndroidの名前でWebAppInterfaceのメソッドが呼べるように定義します

次に、WebViewからAndroidへJavaScript経由で実行できるメソッドを定義します。

```kotlin
class WebAppInterface(private val context: Context) {
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(context, toast, Toast.LENGTH_SHORT).show()
    }
}
```

ここでは渡された値でトーストを表示しているだけですが、ちゃんと値が渡されればもちろん他の用途にも使えます。
JavaScriptから呼べるようにするメソッドには@JavascriptInterfaceアノテーションを付ける必要があります。
また、メソッドはPublicにする必要があります。

次に、カスタマイズしたWebViewClientです。

```kotlin
class AuthWebViewClient : WebViewClient() {
    override fun onPageFinished(view: WebView?, url: String?) {
        val script = "javascript:Android.showToast(document.getElementsById('hoge').value);"
        view?.loadUrl(script)
        super.onPageFinished(view, url)
    }
}
```

ここではonPageFinishedをオーバーライドして、JavaScriptコードを注入しています。
例のJavaScriptコードはページ内にid="hoge"が存在する前提でvalueを取得します。
それを上で定義したAndroid.showToastに渡しています。

以上で、HTML上のid="hoge"のvalueでToastが表示されます。
kerasのCSVLoggerでflushする方法

# CSVLoggerの問題点
kerasのCSVLoggerは学習途中のログをcsv形式でファイルに書き出してくれる優れものですが、一点不満があります。それはファイルに書き出されるタイミングが学習終了後だということです。これは出力がバッファに溜まって、いっぱいになるまで書き出されないのが原因です。このため、学習処理がクラッシュしたときに内容が失われることがあり、困っていました。通常、この問題を解決するには書き出したいタイミングで出力ストリームに対してflush()をすれば良いのですが、CSVLoggerを改造するのはやっかいです。

# 簡単な解決法
簡便な方法を見つけましたので、共有します。実際にはflush()するわけではありませんが、結果的に解決する方法です。

```python
csv_logger = CSVLogger(file_name, monitor='val_loss', save_best_only=True)
csv_logger._open_args('buffering') = 100
```
バッファのサイズを設定する方法をソースコードから見つけました。上記のようにして設定します。ここでは100バイトに設定しています。ちなみに0バイトを指定するとエラーになります。

以上です。
【Rails6】JavaScriptによる「入力フォームの内容のリアルタイムチェック機能」の実装例

※2022年から技術系の記事は[個人ブログ](https://yamaday0u.net/category/programmer/)に投稿しております。ぜひこちらもご覧ください→[yamaday0u Blog](https://yamaday0u.net/category/programmer/)

JavaScriptで以下のような「入力フォームの内容のリアルタイムチェック機能」を実装してみたので、その方法を共有します。

<a href="https://gyazo.com/f5ac85d4bb2a19586f14f596c00517ba"><img src="https://i.gyazo.com/f5ac85d4bb2a19586f14f596c00517ba.gif" alt="Image from Gyazo" width="722"/></a>

ポイントは、jQueryのようなライブラリは使用せず、そのままのJavaScriptで実装したことです。

ネットで検索してみると、意外とそのままのJavaScriptで実装した例を紹介する記事がヒットしなくて、jQueryを使った例ばかりだったので、今回記事にしてみようと思いました。

## 実行環境
Rails 6.0.3.4

## 各ファイルの記述
今回のメインである「入力フォームの内容のリアルタイムチェック機能」を実装する記述です。

正規表現の`/^[\A\d+\z]/`について補足します。半角数字にマッチする正規表現である`\A\d+\z`を`^[]`で囲うと、半角数字でないものにマッチする正規表現になります。

```app/javascript/validation.js
const validation = () => {
  //半角数字でないものにマッチする正規表現を変数化
  const pricePattern = /^[\A\d+\z]/;
  //エラーメッセージを表示する要素を変数化
  const priceErrorMessage = document.getElementById("price-error");
  //フォームの値が入る要素を変数化
  const itemPrice = document.getElementById("item-price");

  //キーボード操作がされた時にイベント発火
  addEventListener("keyup", e => {
    if (pricePattern.test(itemPrice.value) || itemPrice.value == "") {
      //item-priceが半角数字か空白ならエラーメッセージは表示しない
      priceErrorMessage.style.visibility="hidden";
    }else {
      //item-priceが全角数字ならエラーメッセージを表示する
      priceErrorMessage.style.visibility="visible";
    }
  })
}

window.addEventListener("load", validation)
```
application.jsに上記のvalidation.jsを読み込みます。

```app/javascript/packs/application.js
require("@rails/ujs").start()
require("@rails/activestorage").start()
require("channels")
require("../item_price")
require("../validation") //validation.jsを読み込む
```
ビューファイルの抜粋です。

```erb:app/views/items/new.html.erb
<%# 販売価格 %>
<div class="sell-price">
  <div class="weight-bold-text question-text">
    <span>販売価格<br>(¥300〜9,999,999)</span>
    <a class="question" href="#">?</a>
  </div>
  <div>
    <%# validation.jsで以下の1行を操作する %>
    <div id="price-error" class="price-error">価格は半角数字で入力してください</div>
    <div class="price-content">
      <div class="price-text">
        <span>価格</span>
        <span class="indispensable">必須</span>
      </div>
      <span class="sell-yen">¥</span>
      <%= f.text_field :price, class:"price-input", id:"item-price", placeholder:"例）300" %>
    </div>
    <div class="price-content">
      <span>販売手数料 (10%)</span>
      <span>
        <span id='add-tax-price'></span>円
      </span>
    </div>
    <div class="price-content">
      <span>販売利益</span>
      <span>
        <span id='profit'></span>円
    </div>
    </span>
  </div>
</div>
```
ビューファイルのスタイルシートの抜粋です。

```app/assets/stylesheets/items/new.css
.price-error {
  visibility: hidden;
  color: red;
}
```

## 参考資料
[【Qiita】素のJavaScriptでFormバリデーション](https://qiita.com/zaburo/items/a8fdcb0e1237f6ef97ff)
LaravelのHTTPクライアント(Illuminate\Support\Facades\Http)でcURLのCURLOPT_SSL_VERIFYPEERをfalseにしたい。

# はじめに
こんにちは。Laravelで日に日に新しいもの出てきて(見つけて)、追いつくのに必死の筆者です。

先日cURLで書かれているコードをLaravelに踏襲する機会がありました。

その際に、Laravelで`CURLOPT_SSL_VERIFYPEER`のオプションの付加方法について、うれしいことに既にメソッドとして切り出して用意されていたので、共有します。

# cURLの場合
```php
$url = 'https://example.com';
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // ←ここ
$response = curl_exec($curl);
curl_close($curl);
```

# Laravelの場合
```php
$url = 'https://example.com';
$response = Http::withoutVerifying() // ←ここ
    ->get($url);
```

# おわりに
Laravelアローで繋げられるので個人的には好きですね。
使用頻度の高いオプションなのか、メソッドとして切り出してありましたが、別途細かいオプションも`withOptions`メソッドとかで与えられます。
それでは！
WebSocket)ブラウザのタイムスタンプ依存についてメモ

#WebSocketのクライアント（ブラウザ）から時間データを受け取った際の時刻が想定と違った

メッセージを送信した時間をサーバ側で処理

` クライアント：　action SEND_SOCKET_MESSAGE @ 17:08:22.708
サーバ：2021/02/07 17:08:22 &{test test body 2021-02-07 08:08:22.708 +0000 UTC}
`
サーバからメッセージをJSONオブジェクトにするためにjson.Unmarshalでパーシング。

なぜかGoからはクライアントの時間を**UTC**で処理していた模様、、、

調べたところ、goのjson規格としてRFC７１５９を使ってました。
https://tools.ietf.org/html/rfc7159

---
package json // import "encoding/json"

Package json implements encoding and decoding of JSON as defined in RFC
7159. The mapping between JSON and Go values is described in the
documentation for the Marshal and Unmarshal functions.

---

が、、特にtimeについて項目がなかったのでパス

#結局

クライアントからのメッセージでタイムスタンプがあったらreflectで日本標準時を入れてくれる処理をつけました。

```
import (
	"reflect"
	"time"
)

var loc *time.Location

const (
	location = "Asia/Tokyo"
)

func init() {
	var err error
	loc, err = time.LoadLocation(location)
	if err != nil {
		loc = time.FixedZone(location, 9*60*60)
	}
}

func Recongnizer(obj interface{}) {

	e := reflect.TypeOf(obj).Elem()

	for i := 0; i < e.NumField(); i++ {

		varF := e.Field(i)

		switch varF.Type {
		case reflect.TypeOf(time.Time{}):
			varValue := reflect.ValueOf(obj)
			vs := reflect.Indirect(varValue)
			v := vs.Field(i)
			t := v.Interface().(time.Time)
			v.Set(timeSet(t))
		}

	}
}

func timeSet(t time.Time) reflect.Value {

	return reflect.ValueOf(t.In(loc))
}


```

機能として、JSONにした変数を受け取り、メンバーフィルドにtime.Timeタイプがあれば日本標準時に変更して値を更新してます。

先のメッセージ
2021/02/07 17:08:22 &{test test body 2021-02-07 08:08:22.708 +0000 UTC}
これが

これになります。
2021/02/07 17:08:22 &{test test body 2021-02-07 17:08:22.708 +0900 JST}


## 課題

サーバのロケージョンを自動的に取ってきて時間データの設定はできないか？
実行時のコマンドライン引数で指定はできないか？
構造体内部にさらに構造体が含まれている場合に全てのtime.Timeについて処理はできないか？
ほとんどないかと思いますが、システム要件で複数の時間帯を使っている場合どうなるか？


### 最後に

WebSocketサーバのクライアントとの遅延時間を計算する目的でクライアントからのメッセージ送信を試しました。次回からはPingPongパケットで確認することに（笑）
Goは1.15.7を使いました
【Heroku】Herokuで環境変数を設定する方法

#プログラミング勉強日記
2021年2月7日
Herokuで環境変数を設定する方法を備忘録として残す。

#環境変数とは
　環境変数について調べると以下のように出てきた。

> 環境変数とは、OSが設定値などを永続的に保存し、利用者や実行されるプログラムから設定・参照できるようにしたもの。プログラムの実行時などに必要となる、利用者やコンピュータごとに内容が異なる設定値などを記録するために用いられる。
([環境変数 【environment variables】](https://e-words.jp/w/%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0.html))

　OSが提供するWindowsやiosだけでなく、Linuxなどにも存在する機能。要は<b>変数</b>である。変数名と値が入っていて、変数名を参照することで値が出てくる変数そのものである。


#環境変数の設定におけるHerokuコマンド

##環境変数を一覧で表示する

```
$ heroku config
```

##環境変数を指定して値を表示する

```
$ heroku config:get 環境変数名
```

##環境変数を設定する

```:環境変数を1つ設定する
$ heroku config:set 環境変数名=値
```

```:環境変数を複数設定する
$ heroku config:set 環境変数名1=値1 環境変数名2=値2
```
　(このとき、`=`の間に空白文字を入れない。)

##環境変数を削除する

```:環境変数を1つ削除する
$ heroku conig:unset 環境変数名
```

```:環境変数を複数削除する
$ heroku conig:unset 環境変数名1 環境変数名2
```

#サイトから環境変数を設定する

##1. Herokuにログインする
　 [サイト](https://dashboard.heroku.com/)からHerokuにログインして、対象のアプリを選択する。

##2. settingタブをクリックする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/f9cfd434-ff96-bb0f-cfe5-37aefe579b8d.png)

##3. Config VarsのRevel Config Varsをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/03052536-5957-762e-4a81-a79a1ecbee68.png)

##4. KEYとVALUEを入力して保存する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/6acea9e2-86ad-1859-03e7-535265ec6e89.png)
commitee-rails の `ActionDispatch::Http::Parameters::ParseError` の解決法

## エラー時の状況

### 環境

```bash
root@f374345b90f6:/myapp# ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]

root@f374345b90f6:/myapp# bundle exec rails -v
Rails 6.1.1
```

### ソースコード

```rb
RSpec.describe '/contacts', type: :request do
  describe 'POST /contacts' do
    let(:headers) { { "Content-Type" => "application/json" } }
    let(:params) {
      {
        contact: {
          name: '',
          email: '',
          title: '',
          content: ''
        }
      }
    }

    it 'スキーマ定義とAPIの挙動が同じであること' do
      post '/contacts', params: params, headers: headers

      assert_request_schema_confirm
    end
  end
end
```

### エラーメッセージ

```bash
 1) /contacts POST /contacts スキーマ定義とAPIの挙動が同じであること
     Failure/Error: post '/contacts', headers: headers, params: {contact: params}

     ActionDispatch::Http::Parameters::ParseError:
       809: unexpected token at 'contact[contact][name]=&contact[contact][email]=&contact[contact][title]=&contact[contact][content]='
     # /bundle/ruby/3.0.0/gems/rack-2.2.3/lib/rack/request.rb:69:in `fetch'
```


## 解決法

POST時に渡すパラメータはハッシュなので`to_json`してJSONにパースして上げる必要があります。

```rb
RSpec.describe '/contacts', type: :request do
  describe 'POST /contacts' do
    let(:headers) { { "Content-Type" => "application/json" } }
    let(:params) {
      {
        contact: {
          name: '',
          email: '',
          title: '',
          content: ''
        }
      }.to_json
    }

    it 'スキーマ定義とAPIの挙動が同じであること' do
      post '/contacts', params: params, headers: headers

      assert_request_schema_confirm
    end
  end
end
```
Phalcon PHPを今は新規で使うべきでない理由


こんにちは。
今日は業務で3年ほどPhalconを使っている私が今後Phalconを使おうと思っている人に向けて、使うべきでない理由をだらだら書いたものです。

## 注意

この話の前提として、現在(v4)の話をしています。
~~v5~~v6(PHPコード版)が開発され始めているのもあり、~~v5~~v6が出たらこの状況は変わる可能性があります。(v5はPHPコード版ではなくなりました、v6からPHPコード版になるようです)

## 本家のバグ修正が遅い、メジャーバージョンアップによるバグが多い

正直これに尽きる気がします。
Phalconは2021/02/07現在、4.1.0が最新です。
4.1.0は比較的最近出ているのですが、正直いま安定しているのは3.4.5です。
4系に上がったときにPSRに寄せようとしたり、色々と努力がされているのですがその際にバグが結構生まれてしまってるなと感じます。
特にPhalconはPHP拡張であるのもあってide-stubsという公式のIDE向けのhelperの利用は必須です。しかしこのide-stubsもzephirのコードをparseして生成しているのもあって漏れがあったり、そもそもzephir側の静的チェックが出来てなかったりします。(Interfaceの引数と実際のクラスの引数が異なっているなど、そもそもあってはならないようなことが普通にあります)

こういったいろんな問題があるにも関わらず、本家の対応が遅いため(そもそもzephirのコードをメンテする人がほぼいない)、ずっと根底にあるバグ(仕様)が残ったままバージョンが上がっていってます。
また、修正がされたとしてもパッチバージョンをあげてくれなかったりして、小さい単位でもパッチバージョンとして出してほしいなぁと思います。

## ドキュメントが正しくない

現在、公式ドキュメントは4.0と3.4が存在しますが4.0のドキュメントは実際には出来ないことがドキュメントに載っています。
これは3.4系では出来たのに4.0で出来なくなったもので、そもそもドキュメントの更新自体が追いついてない感じです。

## Phalconのクラスを拡張しようとするとバグ(仕様)にぶつかる

これは実際にやってて、PhalconのクラスをReflectionで取得してコンストラクタのデフォルトパラメータ(isDefaultValueAvailable)を確認しようとするとあるのにも関わらずすべてfalseで返されてしまいます。

issueは2015年に上がっていて https://github.com/phalcon/zephir/issues/1134 Cだから出来ないという結論で終わっています。

## 新しいバージョンが出たら、前バージョンのサポートがされない

これは結構つらくて、4系が出てから少しして3系のサポートがされなくなりました。
つまり、3系で直してほしくても4系に上げてくれって話になります。
最初に書きましたが、4系はv3.4.5に比べて明らかにバグが多いです。

## そもそも活発な人たちが少ない

あきらかにここは感じます。
周りのライブラリ開発者の温度感が低めです。Phalcon3系に対応しているのもそこまで多くないです。

ほかに、ppa:ondrej/phpなどのリポジトリの反映も遅れているようです。(公式が通知してない？)

## それでも今Phalconを選んだときのメリット

このような状況でありますが、Phalconを選んだときのメリットがあります。
それはOSS貢献を出来ることです。あきらかにバグがもりもりあるので、修正をすれば取り込まれる可能性は高いです。現に、筆者も何件かのPRを出して取り込まれましたし、issueもバグとして判断されたものもあります。

## 最後に

最初にも書きましたが、現在のv4の話なので、v5になったら突然なにか変わるかもしれません。
v5に期待しつつ、バグが減っていくと良いなぁと思います。
【Ruby on Rails】gem "refile" の画像アップロード先の変更と注意点

# 保存先の変更

Googleの`VisionAI`というAPIを用いてアプリケーションへ投稿画像に対する自動タグ付け機能を実装した際に、`refile`の画像ファイルの保存先を変更する必要があった為、備忘録として本記事を作成。
refileのデフォルトでの画像ファイルの保存先は`tmp/uploads/store`になっており、今回はその保存先を`public/uploads`に変更。

##`config/initializers`階層下にファイルを作成

`config/initializers内`に新たに`refile.rb`ファイルを作成し、以下の記述を行う。
これにより、アップロードされた画像の保存先を変更することが可能。

```rb:config/initializers/refile.rb
Refile.backends['store'] = Refile::Backend::FileSystem.new('public/uploads/')
```

## 既存のデータがあった場合、`tmp/uploads/store`から`refile.rb`に移す

すでにアップロードされた画像データが存在した場合、上記の手順でファイルの読み込み先を変更した為に、`tmp/uploads/store内`の既存のデータの読み込みが出来なってしまう。
その場合、既存のデータを`public/uploads`に移動させることによって、画像ファイルの読み込みが可能となる。
ただし`refile`の機能を新たに実装する場合には、こちらの手順は必要ない。

```terminal:terminal
$ cp tmp/uploads/store/* public/uploads/
```

`cpコマンド`を用いて既存のデータを`public/uploads`にコピーする。
これを開発環境、本番環境共に行う。

**記載内容に間違いがあった場合はご指摘頂けると嬉しいです。
ご連絡お待ちしております。**
【Go】mongo-go-driverのFindで特定フィールドのみ取得

## 概要
MongoDBでもRDBのSELECT分同様に、特定のフィールドのみ取得する機能が用意されています。[How to select fields in a document in MongoDB](https://learnwithparam.com/blog/how-to-select-fields-in-a-mongo-query/)の記事にある通り、findメソッドの引数で取得するフィールド名を指定します。
今回はこの操作をmongo-go-driverでどのように実装すれば良いかというのを書きます。

## 対応
[remove a field from mongodb query result in golang](https://stackoverflow.com/questions/64145702/remove-a-field-from-mongodb-query-result-in-golang)の記事にある通り、`options.FindOne().SetProjection`のメソッド（複数行取得の場合は`Find()`）に取得フィールドを指定します。指定した内容をFindOneメソッド（複数行取得の場合はFind）に設定します。

## 実装サンプル
上記記事のほぼコピペですが、コレクションから`name`と`email`のみを抽出するサンプルを記載します。なお、コレクション取得までの接続処理は割愛します。接続処理については[こちら](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo)のドキュメントを参考ください。

```go:sample.go
// コレクション取得までの接続処理は割愛・・
var result bson.M
// filter条件の設定
filter := bson.D{{Key: "_id", Value: ID}}
// 取得するフィールドを絞ってクエリ発行
err := col.FindOne(context.Background(), filter, options.FindOne().SetProjection({
	bson.M{
		"name": 1,
		"email": 1,
	},
)).Decode(&result)

```
『OpenCVによる画像処理入門』練習問題7.1(一部)

[『OpenCVによる画像処理入門』](https://www.kspub.co.jp/book/detail/1538290.html)の練習問題7.1をやった。

# 練習問題7.1
## 1 
Q. 図7.7にSobelオペレータを適用すると、どのような画像が得られるか。適用結果をグレースケール画像として表示するために必要な処理は何か。

図7.7
![20210207_160946.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581044/02e24650-5911-caf7-70ca-365f97639449.jpeg)
Sobelオペレータ：エッジ（輪郭）検出に使用するオペレータ。
グレースケール画像：色味のない明るさの度合いだけで表現した画像

図7.7の画像を表示するプログラム

```sub15_1.cpp
//画面表示
void display(cv::Mat image) {
    std::string windowName = "windowName";
    cv::namedWindow(windowName, CV_WINDOW_NORMAL/2);
    cv::imshow(windowName, image);
    cv::waitKey(0);
}

int sub15() {
    // 7×7の画像、1ch
    cv::Mat image = cv::Mat::zeros(8, 8, CV_8UC1);
    int cols = image.cols;
    int rows = image.rows;

    for (int j = 0; j < rows; j++) {
        for (int i = 0; i < cols; i++) {
            if((2<=j && j<=5) && (2 <= i && i <= 5))
            {
                image.at<unsigned char>(j, i) = 255;
            }
            else
            {
                image.at<unsigned char>(j, i) = 0;
            }
        }
    }
    //画面に出力
    display(image);
    return 0;
}
```
出力画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581044/4324b5e8-9ee9-b31b-3816-c8e8f147c88f.png)


図7.7にSobelオペレータを適用するプログラム

```sub15_2.cpp
//画面表示
void display(cv::Mat image) {
    std::string windowName = "windowName";
    cv::namedWindow(windowName, CV_WINDOW_NORMAL/2);
    cv::imshow(windowName, image);
    cv::waitKey(0);
}

int sub15() {

    // 7×7の画像、1ch
    Mat img_src = cv::Mat::zeros(8, 8, CV_8UC1);
    Mat img_tmp;
    Mat img_dst;

    int cols = img_src.cols;
    int rows = img_src.rows;
    for (int j = 0; j < rows; j++) {
        for (int i = 0; i < cols; i++) {

            if((2<=j && j<=5) && (2 <= i && i <= 5))
            {
                img_src.at<unsigned char>(j, i) = 255;
            }
            else
            {
                img_src.at<unsigned char>(j, i) = 0;
            }
        }
    }
    // Sobelオペレータ
    Sobel(img_src, img_tmp, CV_32F, 1, 0, 3);
    convertScaleAbs(img_tmp, img_dst, 1, 0);

    //画面に出力
    display(img_dst);
    return 0;
}
```
出力画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581044/f74bb832-4041-bbab-efd2-4ad6637e0510.png)
分裂してしまった。。
グレースケール画像の変換はパス。

## 2
Q. 図7.7に鮮鋭化オペレータを適用すると、どのような画像が得られるか。

鮮鋭化オペレータ：画像のエッジを強調するオペレータ。入力画像に対して、ある平滑化処理を施し、その結果を元の画像から引くことで適用される。

出力画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581044/98fcbe8a-3ea5-3b61-ac0c-7adcc1953e7f.png)
今度は膨らんだ。
alpine:3.13 armv7をRaspberry Piでrunしたらネットワークにつながらなかった

# はじめに

alpine:3.13ベースのDockerfileを作ってRaspberry Pi4上でビルドしたら`apk`が以下のようにコケた。

```shell
Step 6/12 : RUN apk add --update curl
 ---> Running in 65452985e0f9
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/armv7/APKINDEX.tar.gz
ERROR: https://dl-cdn.alpinelinux.org/alpine/v3.13/main: temporary error (try again later)
WARNING: Ignoring https://dl-cdn.alpinelinux.org/alpine/v3.13/main: No such file or directory
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/armv7/APKINDEX.tar.gz
ERROR: https://dl-cdn.alpinelinux.org/alpine/v3.13/community: temporary error (try again later)
WARNING: Ignoring https://dl-cdn.alpinelinux.org/alpine/v3.13/community: No such file or directory
ERROR: unable to select packages:
  curl (no such package):
    required by: world[curl]
```

Mac上ではビルド上手くいってるのになぜゆえ？
ちなみに現在リリースされている `3.13.0`, `3.13.1` 両方でこうなった。
おそらくarmv7アーキテクチャ版固有の問題かとあたりをつけた。

# 検証

```
docker pull alpine:3.13.0

docker run --rm --name alpine-test alpine:3.13.0 uname -a
Linux 36fb4d97261e 5.4.79-v7l+ #1373 SMP Mon Nov 23 13:27:40 GMT 2020 armv7l Linux

docker run --rm --name alpine-test alpine:3.13.0 ping -c3 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
ping: clock_gettime(MONOTONIC) failed

docker run --rm --name alpine-test alpine:3.13.0 date
Sun Jan  0 00:100:4174038  1900
```

というわけでクロック周りがあかんせいでネットワークにつながらない。いまはこれ以上深堀りしないが根本的にダメな感じがする。

[issue](https://gitlab.alpinelinux.org/alpine/aports/-/issues/12091)あがってました。

# 対策

バージョンを `3.12.3` に下げた

```
docker pull alpine:3.12.3 

docker run --rm --name alpine-test alpine:3.12.3 uname -a
Linux d05b91bab944 5.4.79-v7l+ #1373 SMP Mon Nov 23 13:27:40 GMT 2020 armv7l Linux

docker run --rm --name alpine-test alpine:3.12.3 ping -c3 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=116 time=24.230 ms
64 bytes from 8.8.8.8: seq=1 ttl=116 time=50.765 ms
64 bytes from 8.8.8.8: seq=2 ttl=116 time=38.202 ms

docker run --rm --name alpine-test alpine:3.12.3 date
Sun Feb  7 07:41:24 UTC 2021
```

問題なし。Dockerfileのほうも更新してビルドしたらapkも普通に通った。

# おわりに

いや〜今日もはまったはまった。
Railsで「undefined method `-' for "1":String Did you mean? -@」と表示された時の対処方法

<h2>発生した問題</h2>

ルーティング(routes.rb)とビュー(answer.html.erb)の間で値渡しを行うコントローラーを記述していた際に問題が発生しました。

エラーメッセージ
>undefined method `-' for "1":String Did you mean? -@

ネットでググり、「undefined method」これは初歩的なスペミスか、定義されていない変数やクラスへのアクセスなどの可能性がある、という情報は理解しました。しかし「-」(マイナス)がメソッドとして「undefined」という意味に戸惑い、状況が理解できませんでした。

```ruby:question_and_answers_controller.rb
  def answer
    @course = params[:course]
    @scene = params[:scene]
    question_number = (@course - 1) * 5 + @scene
    qa = QuestionAndAnswer.find_by(id: question_number)
    @answer = qa.answer
    @next_scene = @scene + 1
  end
```

```ruby:routes.rb
Rails.application.routes.draw do
  get 'answer/:course/:scene' => 'question_and_answers#answer'
```

なぜ数値の1が"1"と文字扱いになっているのかもわかりませんでした。
しかしここにヒントがありました。
単純にシンプルに理解し、文字と認識されているなら、と数値に変換してみました。

```ruby:question_and_answers_controller.rb
  def answer
    @course = params[:course].to_i
    @scene = params[:scene].to_i
    question_number = (@course - 1) * 5 + @scene
    qa = QuestionAndAnswer.find_by(id: question_number)
    @answer = qa.answer
    @next_scene = @scene + 1
  end
```

これでうまくいきました。ルーティングのアドレスで渡される値は文字として認識されているということがわかり、新たな気づきがありました。
今後は気をつけたいと思います。
[Python]平方根の誤差

# 概要

[ABC191 D - Circle Lattice Points](https://atcoder.jp/contests/abc191/tasks/abc191_d) で平方根を求める際に、浮動小数点数を返す `sqrt` は正方向に $1$ 以上の誤差を持つことがあり WA であった。
整数 $n$ に対して $a^2 \le n$ を満たす最大の整数 $a$ を得るには、`int(sqrt(n))` ではなく `isqrt` を使うと良い。

# 検証

手軽に計算可能な $1 \le n \le 10^7$ の範囲では `int(sqrt(n)) != isqrt(n)` である $n$ は見つからなかった。
調べたところ $2^{52}+2^{27} = 4,503,599,761,588,224$ が条件を満たす最小の数だそう。

>FWIW, assuming IEEE 754 semantics (with C's double matching IEEE 754's binary64), the smallest n for which int(math.sqrt(n)) doesn't give the right result is n = 2**52 + 2**27
>[Exactness of integer square root in Python - Stack Overflow](https://stackoverflow.com/questions/34918504/exactness-of-integer-square-root-in-python#comment105237340_34919877)

```python
n = 2**52 + 2**27
print(int(sqrt(n)), isqrt(n))
# 67108865 67108864
```
今回の問題の制約内では、 $|X| \le 10^5$ かつ、小数第 4 位を整数として扱うと $(10^9)^2=10^{18}$ の平方根を求めることがあり、この誤差を考慮する必要がある。

なお、$n$ がさらに大きくなると `1 < int(sqrt(n)) - isqrt(n)` となることにも注意したい。たとえば

```python
n = 2**109
print(n, int(sqrt(n))-isqrt(n))
# 649037107316853453566312041152512 2
```



# 参考
[math --- 数学関数 — Python 3.9.1 ドキュメント](https://docs.python.org/ja/3/library/math.html#math.isqrt)
M1 MacBookでFlutterの環境構築をするの巻

この記事はM1 MacBookAirを購入し、ほぼモバイルアプリ開発の技術がない状態からFlutterアプリを開発していく過程を記述した学習記録です

今回は、Flutter（フラッター）の環境構築をしていきます

環境構築とは、その言語を書くのに必要な道具を自分のPCに入れて使えるようにする作業のことです（私の場合Flutterを書くのに必要な道具ということですね）

正確な定義は知りません（）

~~細かいことは気にしないです使えればいいんです~~

では、私が行った環境構築の流れをざっと書いていきます

##動作環境

macOS Big Sur バージョン11.2
13インチMacBook Air（M1 2020）
8コアCPU 7コアGPU
メモリ 8GB
ストレージ　256GB

##Flutter SDKをダウンロード

Flutter公式
https://flutter.dev/

まずは公式からFlutterをダウンロードして任意のディレクトリに解凍

私のPCでは `/Users/haruaki/Developer/flutter/`

##パスを通す

次はパスを通すという作業をします

要はパスを通して使えるようにするということです（説明になってない）

```
vim .bash_profile
```

ターミナルを開き.bash_profileというファイルをvimで編集します

`i`でインサートモードに入ってFlutterSDKを置いたディレクトリのパスを書きます

```
export PATH="$PATH:/Users/haruaki/Developer/flutter/bin"
```

escキーを押して`:wq`で保存

私の場合はエラーが出て`:wq!`にしたら保存できました

どんなエラーだったかは忘れた（）

```
which flutter
```

でパスが通っているのを確認

ヨシ！

これでFlutterのダウンロードとFlutterのパスを通すことができました

##Android Studioをインストール

AndroidStudio公式
https://developer.android.com/studio

公式からAndroidStudioをダウンロードし、エミュレーターというのを使ってPC上にAndroidの環境を再現します

途中でFlutterプラグインをインストール（ちゃんとスクショを撮れ←）

2020/12/4にAndroidエミュレーターのプレビュー版が出ているようです

対応状況の変化がまだ過渡期って感じがして楽しい

Androidエミュレーター（プレビュー版）
https://androidstudio.googleblog.com/2020/12/android-emulator-apple-silicon-preview.html

こちらのYouTube動画を参考にインストール
https://youtu.be/YjUCXGGJu7E

![スクリーンショット 2021-02-08 10.10.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906378/84ecf20f-8500-77d3-cd3e-ba079b71fc56.png)
Androidエミュレーターのビルドができました

初期状態のアプリが表示されています（なんか押すと数字が増えます）

余談ですがAndroidはエミュレーター、iOSはシミュレーターと呼ぶらしいです

呼び方もエミュレーターなのかエミュレータなのかよく分かりませんがその辺調べてみると面白そうですね

##Xcodeをインストール

AppStore
https://apps.apple.com/jp/app/xcode/id497799835

次はiOSシミュレーターでiOSの環境を再現します

AppStoreからiOSシミュレーターのXcodeをインストール

![スクリーンショット 2021-02-08 10.46.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906378/2fc87f16-e2e8-67f8-b064-6dcce7850607.png)
そしてiOSのシミュレーターをデバック

これでAndroidとiOSのエミュレーター・シミュレーターをビルドすることができました

環境構築は完了です

自分お疲れ様でした（？）  

今回参考にさせて頂いた記事
https://zenn.dev/hndr/articles/14689ec937af1f

今回参考にさせて頂いた動画
https://youtu.be/kpvVENfDCRc

##最後に

M1 MacBookでの環境構築でしたが、Flutterはおおむね動作しているらしく、ここまでは割と順調に進めることができました

そして開発環境が整ったのでこれからアプリ開発ができる状態になりました

わーい

あとはエラーの時のスクショちゃんと撮っておかないと忘れますねあせあせ

（ちなみに巻っていうのはシリーズ化するのに分かりやすいかなって思っただけなのでとくに意味はないです）

次回はFlutterのWidget（ウィジェット）の使い方を学習する予定です
gitコミット時に間違ったコメントを直す

## まえおき

UdemyでVue.jsの講座を受けつつ、レッスン単位でコミットしていた。
が、あるコミットからコメントが間違っていた。
直前にコミットならサクっと修正できるけど、ちょっと前から連続している…。
この記事はそんなやらかしと修正の記録である。

## 状況

当該リポジトリで`git log --oneline`叩くと以下のようになる。

```terminal:terminal
% git log --oneline
f079f8e (HEAD -> master) lesson1-5 end // ここで間違っているのに気がついた
6317475 lesson1-4 end
85155b7 lesson1-3 end
29c35b0 lesson1-2 end
292fa3b lesson1-1 end // ここからlesson2-xにするつもりだった
8b895d5 lesson1-2 end
37ec038 lesson1-1 end
```

## やり方

1. どのコミットまで戻すか決定する
2. コミットごとにコメントを修正して、コミットする
3. すべてのコミットを修正して、`git push`して完了

## 1.どのコミットまで戻すか決定する

目的のコミット(292fa3b)は`HEAD`から数えて何番目か数える。
数える際は、HEADを1番目とするので、目的のコミットは5番目ということになる。

```terminal:terminal
% git log --oneline
f079f8e (HEAD -> master) lesson1-5 end // 1番目
6317475 lesson1-4 end // 2番目
85155b7 lesson1-3 end // 3番目
29c35b0 lesson1-2 end // 4番目
292fa3b lesson1-1 end // 5番目
8b895d5 lesson1-2 end
37ec038 lesson1-1 end
```
## 2.コミットごとにコメントを修正する

目的のコミットが何番目か確認したら、`git rebase`を叩く。
この時、`-i`オプションを設定し、`HEAD`から何番目かをチルダの後に示す。

```terminal:terminal
% git rebase -i HEAD~5    //HEADから5番目なので、チルダの後ろは5
```

すると以下のようにエディタが表示され、コミットごとに何をするか選択可能になる。
(エディタの操作方法については秀逸な記事がいくらでも転がっているので探してください)

```
pick 292fa3b lesson1-1 end
pick 29c35b0 lesson1-2 end
pick 85155b7 lesson1-3 end
pick 6317475 lesson1-4 end
pick de3921c lesson1-5 end

# Rebase 8b895d5..de3921c onto 8b895d5 (5 commands)
#
# Commands:
(以下略)
```

1コミットが1行として表されている。
編集したいコミットの行頭を`pick`から`edit`に書き換えると、修正可能となる。
上記を`edit`に書き換えた例は以下。

```
edit 292fa3b lesson1-1 end
edit 29c35b0 lesson1-2 end
edit 85155b7 lesson1-3 end
edit 6317475 lesson1-4 end
edit de3921c lesson1-5 end

# Rebase 8b895d5..de3921c onto 8b895d5 (5 commands)
#
# Commands:
(以下略)
```

書き込みを行ってエディタから抜けると、さっそく修正が行える状態になる。
下記例では、エディタから抜けた瞬間

```
% git rebase -i HEAD~5
Stopped at 292fa3b...  lesson1-1 end
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue
```


```
% git commit --amend -m "lesson2-1 end"
[detached HEAD 2693e0b] lesson2-1 end
 Date: Thu Jun 11 17:35:56 2020 +0900
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 lesson2.txt
```


```
% git log --oneline
2693e0b (HEAD) lesson2-1 end // 修正された
8b895d5 lesson1-2 end
37ec038 lesson1-1 end
```

コメントを変えたら次のコミットを修正するので、`git rebase --continue`で次のコミットに移動する。

```
% git rebase --continue
Stopped at 29c35b0...  lesson1-2 end
```

この後、`Stopped at 〜`で現在対象とするコミットが示されるので、「適切なコメントに修正→次のコミットに移動」を繰り返す。
最後まで修正すると、以下が表示される。

```
% git rebase --continue
Successfully rebased and updated refs/heads/master.
```
以上で修正は完了。おつかれさまでした。

## おまけ：直前のコメントを修正したい

`git commit`に`--amend`を付けて正しいメッセージを付与する

```terminal:terminal
% git log --oneline
fg79f8e (HEAD -> master) message2 // <- message3にするつもりが間違えた
9217475 message 2
85729b7 message 1

% git commit --amend -m "message3"

% git log --oneline
hg7g07e (HEAD -> master) message3 // <- 修正OK
9217475 message 2
85729b7 message 1
```
VS Code 1.53 の新機能 - すべてのタブを表示する機能

https://code.visualstudio.com/updates/v1_53#_wrap-tabs

エディタを同時にたくさん開いたときに、エディタ上部のタブが表示しきれなくなってしまう（スクロールしないと見れない）が、そんな時にタブをすべて表示してくれるオプションが追加された。
設定`workbench.editor.wrapTabs`を変更する。

![v1.53.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181011/07737c01-3864-34f2-e4ef-99d50670141a.gif)

タブの表示の工夫については、設定`workbench.editor.tabSizing`を有効にするとタブの横幅が縮小されるというのがあったが、タブが多いとタブ幅が狭くなり過ぎてファイル名が見にくくなってしまうが、ちょっと使ってみた限りでは今回の新機能のほうがファイル名が見やすそうな感じ。例えタブが複数段になってエディタの高さが多少狭くなってもファイル名が見たい場合があるので、そういう時に非常にありがたい機能。
Scannerクラス next,nextLine,nextIntメソッドの使い分け

#はじめに

* nextLine
* next
* nextInt

上記のメソッドはScannerクラスのメソッドであり、
入力された値をスキャンするために用いられる
（コンソールに入力した値を取得したりする際に用いられる）

#注目ポイント！

これらのメソッドが分かりづらいポイントは
**「改行までスキャンするか」**
にあると思う。本記事ではその点において主に記載していく


#結論

* **nextLine** <br>⇛改行をスキャンする<br>⇛改行のみでも改行（\n）としてスキャンしている
* **next** ※1<br>⇛改行をスキャンしない<br>⇛文字をスキャンするまで改行は無視される
* **nextInt** ※1<br>⇛改行をスキャンしない<br>⇛数字をスキャンするまで改行は無視される

※1 スペースは無視されず、スペースまでをスキャンする

#パターン

#####「パターン①」
* next()
* nextLine()

【input】
first
second
【output】
first

```java

Scanner scan = new Scanner(System.in);
String firstWord = scan.next();
//next()は文字のみをスキャンする。改行を無視
System.out.println(firstWord);
//output:first

String secondWord = scan.nextLine();
//改行もスキャンする
System.out.println(secondWord);
//output:なにも起こらないように見える
//input時、Enterを押すため、その「\n」が入って何も出力されない
```

#####「パターン②」
* next()
* nextLine()
* next()

【input】
first
second
【output】
first
second

```java
Scanner scan = new Scanner(System.in);
String firstWord = scan.next();
//next()は文字のみをスキャンする。改行を無視
System.out.println(firstWord);
//output:first

scan.nextLine();
//input時、Enterを押すため、その「\n」が読み込まれている

String secondWord = scan.next();
//文字のみをスキャンする。改行を無視
System.out.println(secondWord);
//output:second
```

#####「パターン③」
* next()
* next()

【input】
first
second
【output】
first
second

```java
Scanner scan = new Scanner(System.in);
String firstWord = scan.next();
//next()は文字のみをスキャンする。改行を無視
System.out.println(firstWord);
//output:first

String secondWord = scan.next();
//改行を無視して次の文字をスキャン
System.out.println(secondWord);
//output:second
//input時、Enterを押してもnext()は次の文字をスキャンしに行くので"word"がとってこれる
```

#####「パターン④」
* nextLine()
* nextLine()

【input】
first
second
【output】
first
second

```java
Scanner scan = new Scanner(System.in);

String firstWord = scan.nextLine();
//改行までスキャンする
System.out.println(firstWord);
//output:first

String secondWord = scan.nextLine();
//改行もスキャンする
System.out.println(secondWord);
//output:second
```
#####「パターン⑤」
* nextInt()
* nextLine()

【input】
3
ABC

【output】
3

A,B,C,

```java
Scanner scan = new Scanner(System.in);

int n = scan.nextInt();
//数字をスキャン。nextIntは改行をスキャンしない
String r = scan.nextLine();
//改行を取得
String word[] = scan.nextLine().split("");
//単語をスキャン。一文字ずつ配列の要素に格納。

System.out.println(n);
//出力結果：3
System.out.println(r);
//出力結果：
//※改行
for(int i = 0; i < word.length; i++) {
	System.out.print(word[i] + ",");
	//配列の要素すべての出力
	//出力結果：A,B,C,
	}
```
[Laravel] Eloquent::when() を使う

### when()メソッドを使う

例えば下記のように、if文を使ってクエリを書くコードがある。

```sample.php
$query = User::query();

if(request('filter') == 'likes') {
    $query->where('likes', '>' , request('likes_amount', 0));
}
```

上記コードを、when（）methodを使って、より見やすく、”Laravel Way”で書く。
when()メソッドの第一引数がtrueの場合実行される。

```when_sample.php
$query = User::query();

$query->when($request('filter') == 'likes', function($q){
    return $q->where('likes', '>', request('likes_amount', 0));
});

$users = $query->get();
```

###その他例
Query Builderでも使える。

```sample1.php

$users = DB::table('users')
          ->when($request->input('role'), function($q) use($role) {
              //イコールの場合、'='は省略可能
              return $q->where('role', $role)
          })->get();

```
### 参考
https://laravel.com/docs/8.x/queries#conditional-clauses
https://laraveldaily.com/less-know-way-conditional-queries/
きれいなコードを書くためにSOLID原則を学びました⑤ ~依存関係逆転の原則~

今回は**SOLIDの依存関係逆転の原則**についてまとめました。

その他の記事は以下。
<a href="https://qiita.com/suzuki0430/items/b13c7c1f637e7cd2146d">きれいなコードを書くためにSOLID原則を学びました① ~単一責任の原則~</a>
<a href="https://qiita.com/suzuki0430/items/b0e2a2c294d81ecdefba">きれいなコードを書くためにSOLID原則を学びました② ~オープン・クローズドの原則~</a>
<a href="https://qiita.com/suzuki0430/items/08fb861d79e13b62f828">きれいなコードを書くためにSOLID原則を学びました③ ~リスコフの置換原則~</a>
<a href="https://qiita.com/suzuki0430/items/040a186e5e8f51e6c11f">きれいなコードを書くためにSOLID原則を学びました④ ~インターフェース分離の原則~</a>

#依存関係逆転の原則
>High-level modules should not depend on low-level modules. Both should depend on abstractions.

直訳すると、**「ハイレベルのモジュールはローレベルのモジュールに依存するべきではない。両方とも抽象に依存するべきである」**という意味になります。

「ハイレベル」やら「ローレベル」やら「抽象」といった見慣れない言葉が出てきましたね...

例として、eコマースのアプリケーションを考えてみます。
![スクリーンショット 2021-02-07 9.57.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e6b0238c-9926-5172-753d-17fad7d2957c.png)

四角で囲まれているものがアプリケーションのそれぞれの機能を示しており、これを**モジュール**と呼びます。

PayPalやGooglePayのモジュールをみてみると、これらはPaymentProcessor（支払処理）の具体的なサービス名にあたります。このように、PaymentProcessorのような一般的な機能にあたる部分を**ハイレベル**、具体的な手段にあたる部分を**ローレベル**と呼びます。ただ、図のCommunicationのような、CustomerProfileからみたらローレベル、EmailSenderからみたらハイレベルみたいなモジュールもあるので、レベルというのはあくまで相対的な概念であるという点に注意が必要です。

![スクリーンショット 2021-02-07 9.57.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/27750529-b9fd-0eee-1109-c42e447a3df7.png)

言葉の定義がわかったところで図をみてみると、何か違和感を感じるはずです。
そうです。**「ハイレベルのモジュールはローレベルのモジュールに依存するべきではない。」という原則に違反してしまっています。**


##インターフェースの導入
ProductCatalogとSQLProductRepositoryの関係について、図の依存関係をコードにすると以下のようになります。
モジュールの依存というのは、要は他のモジュール内でインスタンス化を行っていることとみなすことができます。

```java
public class ProductCatalog {
  public void listAllProducts() {
    SQLProductRepository sqlProductRepository = new SQLProductRepository();
  }
}

public class SQLProductRepository {
  public List<String> getAllProductNames() {
    ...
  }
}
```

この依存関係を解消する方法は、インターフェースとインスタンス化を行うファクトリークラスを利用することです。

```java
public class ProductCatalog {
  public void listAllProducts() {
    ProductRepository productRepository = ProductFactory.create();
    ...
  }
}

public interface ProductRepository {
  public List<String> getAllProductNames();
}

public class ProductFactory {
  public static ProductRepository create() {
    return new SQLProductRepository();
  }
}

public class SQLProductRepository implements ProductRepository {
  public List<string> getAllProductNames() {
    ...
  }
}
```

インターフェースを挟むことでモジュール間の依存関係はなくなり、インタフェースが両モジュールから依存されるようになります。

このように、**依存される側だったSQLProductRepositoryが依存する側に逆転することから、依存関係逆転の原則と呼ばれます。**
![スクリーンショット 2021-02-07 15.55.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/b603abcc-697f-0959-cfdd-f4db4d1fdd54.png)


#おわりに
依存性の注入については、Laravelを例にしてまた別の機会にまとめてみたいと思います。

#参考書籍・動画
<a href="https://www.amazon.co.jp/dp/4048930656/ref=cm_sw_em_r_mt_dp_VTH6KQ7525WWPDZX78BY">Crean Architecture</a>　Robert.C.Martin
<a href="https://www.udemy.com/share/101KLUCEEZd11VRno=/">SOLID Principles: Introducing Software Architecture & Design</a>　Sujith George
【AWS】デプロイ後のエラー

## 内容：unicornの起動時にworking_directoryと表示される
エラー文を詳細に見ると
ArgumentError: config_file=config/unicorn.rb would not be accessible in working_directory=/var/www/current
との表示。

### AWSエラー文、解決の流れ
1. エラー文の内容を翻訳
1. エラー文をググる


## 1.エラー文の内容を翻訳　
エラー文の全容が分からなくてもOK。断片的な事から理解し紐解く。
app_pathとは　アプリケーションコードが設置されているディレクトリを変数に入れておく
事。
この時点で下記をapp_pathとして指定
app_path = File.expand_path(‘../../‘../‘, __FILE__)
working_directory　とは　アプリケーションの設置されているディレクトリ
working_directory "#{app_path}/current"　と指定

この場合のエラー文、
ArgumentError　：メソッドの引数が正しくない時や足りない時に発生するエラー
config_file=config/unicorn.rb　
would not be accessible in  ： アクセスできません。
まとめると　working_directoryの引数が正しくないからconfig/unicorn.rb
にアクセスできませんとなる。

## 2.エラー文をググる
AWS would not be accessible in working_directory　でググる
同様のケースと思われるtaratailを発見
私の場合、app_path = File.expand_path(‘../../../‘, __FILE__)
と指定されていたので、メソッドの引数が多い為
エラーが起こったと考えられる。
app_path = File.expand_path('../../', __FILE__)
と修正したら解決。

> 参考情報
以下のページを参照しました。ありがとうございました。
https://teratail.com/questions/229963
2021年10月版 WindowsでのPython仮想開発環境の構築方法

# やりたいこと
Windowsで複数バージョンのPython仮想開発環境を作成したい。

# インストール済みの Python バージョンを調べる
下記のコマンドをコマンドラインから実行します。エラーとなる場合はPythonがインストールされていません。`py` コマンドは、Windows環境で使えるpythonのランチャーコマンドです。`-0` のオプションは、現在インストール済みのPython一覧を表示します。 * のついているバージョンが最新のバージョンで、バージョンを指定せずに `py` コマンドを実行した場合、このバージョンのPythonが実行されます。

```
>py -0
Installed Pythons found by py Launcher for Windows
 -3.10-64 *
 -3.9-64
 -3.8-64
 -3.7-64
```

# 複数マイナーバージョンのPythonをインストール

https://www.python.org/downloads/windows/
から、必要なバージョンの Windows installer (64-bit) をクリックしてダウンロード。たとえば、2021年10月5日現在の代表的なマイナーバージョン Python 3.10、Python 3.9、Python 3.8、Python 3.7のWindows版の最新は、

- Python 3.10.0
- Python 3.9.7
- Python 3.8.10
- Python 3.7.9

となっている。マイクロバージョンのみが違うものをインストール済みの場合は、
インストール時に、(→ Upgrade Now) を選ぶだけで、自動的に上書きアップグレードされる。
例) 3.9.6 → 3.9.7 など。

# インストールしたPythonのバージョンを確認
`py -V` とバージョンを指定せず、`py` コマンドを実行した場合は、最新バージョンのPythonが使われる。

```
>py -V && py -3.9 -V && py -3.8 -V && py -3.7 -V
Python 3.10.0
Python 3.9.7
Python 3.8.10
Python 3.7.9
```
# 仮想環境の作成
venvというモジュールを使って、Desktopのappというフォルダ(場所は任意)に、.venvという名前(名前は任意)の仮想環境を作成する。Python 3.9.x以上の場合は、`--upgrade-deps` オプションをつけておくと、`pip` などのアップデートも行われる。Python 3.8.x, Python 3.7.x の環境を作りたい場合は、`py -3.8` ... のようにバージョンを指定する。

```
>mkdir Desktop\app
>cd Desktop\app

# Python 3.9以上の仮想環境を作成する場合
Desktop\app>py -m venv .venv --upgrade-deps

# Python 3.8の仮想環境を作成する場合
Desktop\app>py -3.8 -m venv .venv

Desktop\app>dir
<DIR>          .
<DIR>          ..
<DIR>          .venv
```

# 仮想環境をアクティベート
.venvフォルダ内の `activate` コマンドを呼び出して、.venv仮想環境に入ると、`python` コマンドで、指定のpythonが実行できる環境ができる。`pip` コマンドも、この環境内で閉じたものになり、他には影響しない。

```
Desktop\app>.venv\Scripts\activate
(.venv) Desktop\app>python -V
Python 3.8.10
```

# Visual Studio Codeで実行
Visual Studio Codeをインストールしておけば、`code` コマンドで、Visual Studio Codeを起動することができる。

```
(.venv) Desktop\app>code main.py
```
但し、起動後は、ファイル(F) - フォルダーを開く...メニューから、先ほどのDesktop\appフォルダを開きなおし、実行(R) - 構成の追加... メニューで、Python Fileを選択しておくと、main.pyを選択して、F5キーで、すぐに実行することができる。
Visual Studio Code内のターミナルを閉じて、再度開くには、[Ctrl] + [@] キーを使う。
プロンプトが、

```
(.venv) Desktop\app>
```
のようになっていれば、.venv仮想環境で実行できる。もし、仮想環境に入っていない場合は、Visual Studio Code 画面左下のPython実行環境のバージョン一覧から変更できる。
[1O1]ローカルで作成したファイルをgitでpushする流れ

# 目的
ローカルで作成したプログラムファイルなどをgithubのリモートリポジトリにpushする。
なおシェルはgit bashを利用する。

# git init
ローカルリポジトリを初期化する。
ソースファイルの置かれたフォルダをまるごとgitで管理するため、cdで目的のディレクトリまで移動する。
デフォルトだとbash上のカレントワークディレクトリ内に作成され、隠しファイルとして`.git`が作成される。

```
$ git init 
```



#git add
インデックスにファイルを登録(ステージング)する。
`git add .`でディレクトリ内のすべてのファイルを指定する。`git add 'ファイル名'`で特定のファイルのみ指定する。

```
$ git add .
```

#git commit
ローカルリポジトリにaddしたファイルをコミットする。引数にコミットメッセージを入力しないと完了できない。

```
$ git commit -m "first commit"
```

このとき
>Author identity unknown
>please tell me who you are

というエラーが出たらgitに個人情報が登録されていない状態なので以下のコマンドで名前とアドレスをconfigに登録し、再度commitをする。

```
$ git config --global user.name [hogehoge]
```

```
$ git config --global user.email [hoge@hoge.com]
```

# リモートリポジトリの登録
まずgithubのマイページにて新規リポジトリを作成する。プロジェクト名(リポジトリ名)を入力し、**初期化(init)の項目はいずれもチェックしないようにする。**
次に以下のコマンドでリモートリポジトリを登録する。URLを含むリポジトリ名をoriginとする。
`[注意]リポジトリのURLはSSHではなくHTTPSの方を選択する。(sshの場合リモート接続用のセキュリティを設定する必要があるため)`

```
$ git remote add origin https://github.com/[your-username]/[project-name].git
```

#git push
masterブランチをリモートリポジトリ(origin)にpushする。
初回の場合(?)githubへのサインインが要求される。

☆なお、originとはリモートリポジトリの名前であり、github側のリポジトリを指定していることになる。masterはブランチの名前であり、デフォルトではmasterだがブランチを切った場合は当然違う名前を入力することになる。

```
$ git push origin master
```

###変更ファイル、コミットのログの確認

```
#コミットのログ(メッセージや履歴など)を確認できる
$git log

#変更ファイルや新規ファイルが赤文字で出力される
$git status
```

#2回目以降にadd,commit,pushするとき
commitの-aコマンドではコミットしたことのあるファイルに変更があった場合そのすべてをインデックスにステージングする(要はcommitとaddを同時におこなう)。
その後は初回同様にgit pushをおこなう。

```
$git commit -a -m "second commit"
```

#リモートリポジトリのファイルを削除するとき

```
$git rm --cached [filename]
```
Mac上のSTSでLombokを使えるようにする

今回はMac上のSTSでLombokを使えるようにします。

# 環境
OS: macOS Big Sur バージョン11.1
STS: Spring Tool Suite 4 Version: 4.9.0.RELEASE

# Lombokのインストール
１、まずは[Lombokダウンロード](https://projectlombok.org/download)する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/7b8c4ff0-1ff5-29c4-afd5-813d12e41381.png)
2、STSを閉じる。
3、ダウンロードしたLombokを起動すると、「Can't find IDE」のダイアログが出るのでOKで閉じる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/b0f9c4ed-0f4c-182c-66e2-d605792db460.png)
4、その後、「Specify location...」を選択し、「SpringToolSuite4.ini」ファイルを選択する。
ファイルパスは「/Applications/SpringToolSuite4.app/Contents/Eclipse/SpringToolSuite4.ini」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/18de1216-1647-52d4-7680-6fc9828ea4a7.png)
「Install/Update」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/a0b9a1f5-d067-1efb-ed36-8eee36867322.png)
インストール完了！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/9940e7e0-b629-bcb5-5470-1d066c49d07c.png)

# ライブラリ追加
pom.xmlもしくはbuild.gradleに[mvnrepository](https://mvnrepository.com/artifact/org.projectlombok/lombok)からLombokのライブラリを追加する。

~~~groovy:build.gradle
implementation 'org.projectlombok:lombok'
~~~

# 参考
[Lombok Macにインストール(STS使用)](https://qiita.com/nakamura999/items/6a05fb9f05af14c2f8d5)
【Java】Comparatorを使ってListをソートする方法

#はじめに
本記事ではJava 8 以降可能となった、Comparatorを使ってListをソートする方法を備忘のためにアウトプットします。

#単項目でソートする

```java:文字列長でソート
List<String> playerList = Arrays.asList("Keisuke Honda", "Shinji Kagawa", "Marcus Tulio Lyuji Murzani Tanaka");
        playerList.sort(Comparator.comparingInt(String::length)
        .collect(Collectors.toList());
```

#複数条件でソートする

``thenComparing()``を利用することで、複数条件でソートすることが可能です。

```java:文字列長でソート後、アルファベット順でソート
List<String> playerList = Arrays.asList("Keisuke Honda", "Shinji Kagawa", "Marcus Tulio Lyuji Murzani Tanaka");
        playerList.stream().sorted(
        Comparator.comparingInt(String::length)
        .thenComparing(Comparator.naturalOrder())).collect(Collectors.toList());
```

#参考
[Java Platform SE8 #Comparator](https://docs.oracle.com/javase/jp/8/docs/api/java/util/Comparator.html)