OmniAICreator/Qiita-1.07M huggingface dataset:
【Android】加速度センサーの値を表示する方法

#プログラミング勉強日記
2021年1月21日
Androidに搭載されている加速度センサーを使ってx軸、y軸、z軸の各軸にかかる加速度を測定する方法をまとめる。


#加速度センサーについて
　加速度センサーは以下のようにx軸、y軸、z軸のそれぞれに対して単位Gの値として情報を素得できる。垂直で縦に持つ場合は、以下のようになり-y方向に重力加速度がかかるので、加速度センサーの値は(x, y, z)=(0, -1, 0)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/1d9727b1-c6d6-0b13-fe68-e6abf6677ccb.png)
[](https://blog.smartdrive.co.jp/b548607cc5ad/)
出展: iOSイベント処理ガイド

#加速度センサーの値を表示するプログラム

```java:MainActivity.java
package sample;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;
import android.view.View;

public class MainActivity extends Activity implements SensorEventListener {

    private TextView myText;
    // SensorManagerインスタンス
    private SensorManager sma;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myText = findViewById(R.id.my_text);
        // SensorManagerのインスタンスを取得する
        sma = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        sma.registerListener(this, sma.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_FASTEST);
    }


    // センサーの値が変化すると呼ばれる
    public void onSensorChanged(SensorEvent event) {
        double x = event.values[0];
        double y = event.values[1];
        double z = event.values[2];

        String str =   " X= " + x + "\n"
                     + " Y= " + y + "\n"
                     + " Z= " + z;
        myText.setText(str);
    }

    // センサーの精度が変更されると呼ばれる
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

}
```

```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


        <TextView
            android:id="@+id/my_text"
            android:layout_width="343dp"
            android:layout_height="119dp"
            android:paddingLeft="30dp"
            android:paddingTop="25dp" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/cbef62a2-22c2-b652-64d2-c1458e5591b5.png)



#参考文献
[スマートフォンの加速度センサについて](https://blog.smartdrive.co.jp/b548607cc5ad/)
M5PaperのEPDドライバ 使用法（画面更新モード)

M5PaperのE-ink画面への表示はEPDドライバ経由で利用すると思いますが
結構ハマったので使用法を記載します。
(開発はPlatformIOが対象)

#Hello World
SampleのHello Worldは

```C++
M5EPD_Canvas canvas(&M5.EPD);
canvas.drawString("Hello World", 45, 350);
canvas.pushCanvas(0,0,UPDATE_MODE_DU4);
```
ですが、この後16階調のテストをしてハマりました。
次に記載する表示モードの指定が重要となります。
UPDATE_MODE_DU4は4階調モードなので16階調表示が出来ません。

#画面更新モード
EPDドライバには8種類の画面更新モードがあります

|モード|ゴースト|色モード|更新速度|使用用途|
|:---:|:---:|:---:|:---:|:---:|
|INIT|なし|なし|2000ms|表示初期化|
|GC16|超低|16|450ms|16階調画像表示|
|GL16|中|16|450ms|白背景の更新の少ない場合|
|DU4|中|4|120ms|速度が必要なページめくり等|
|DU|低|2|260ms|タッチ入力、メニュー選択|
|GLR16|低|16|450ms|白背景のテキストの更新|
|GLD16|低|16|450ms|白背景のテキスト及び画像の更新|
|A2|中|2|290ms|高速な更新、フラッシュ無し|
|NONE|-|-|キャッシュ転送のみ|-|
仕様書によると**INIT,GC16,GL16,DU4**を通常使用すれば良いようです。
以下に各モードの特徴を記載しておきます。
##INIT (UPDATE_MODE_INIT)
この更新モードを使用すると画面が初期化されます。

##GC16 (UPDATE_MODE_GC16)
<font color="Blue">ゴースト：超低　更新速度：低</font>
16階調の画像表示などを表示する場合に使用します。
画像表示ではこれを使うといいでしょう。

##GL16 (UPDATE_MODE_GL16)
<font color="Blue">ゴースト：中　更新速度：低</font>
アンチエイリアシングされたテキスト等を16階調が必要だが白い背景の更新率の低い画面更新に使用。
画面フラッシュがすくニアがゴーストは中程度発生する

## DU4 (UPDATE_MODE_DU4)
<font color="Blue">ゴースト：中　更新速度：高</font>
更新速度が速く4階調が使用できます
色指定No. 1, 6, 11, 16が使用できます。

## DU (UPDATE_MODE_DU)
<font color="Blue">ゴースト：低　更新速度：中</font>
DUでの画面更新は中間色から白or黒への更新のみです。
画面ボタン選択時の色反転等に使用。
このモードで中間色の画面を更新すると表示されません。

##GLR16 (UPDATE_MODE_GLR16)
<font color="Blue">ゴースト：低　更新速度：低</font>
白背景のテキストの更新に使用する。

##GLD16 (UPDATE_MODE_GLD16)
<font color="Blue">ゴースト：低　更新速度：低</font>
白背景のテキスト及び画像の更新に使用する。

## A2 (UPDATE_MODE_A2)
<font color="Blue">ゴースト：中　更新速度：中</font>
画面フラッシュを行わないため高速な白黒アニメーションなどに使用できます。
16階調→白黒の遷移時にA2で白画面を挟むと高速でゴーストの少ない遷移が可能です。
Laravel6で、ログインしていない状態でも特定のページにアクセスしたい

Laravel6でコマンドによる認証機能実装後、リンクを作成してローカルで確認すると、ログインページにリダイレクトされる。
ログインしないとアクセスできない仕様になっていた。

Laravel6のコマンドによる認証機能実装
・https://qiita.com/ucan-lab/items/bd0d6f6449602072cb87

# 未ログインでも特定のページにアクセスする方法
app/Http/Controllers/Auth/HomeController.php

HomeController.phpは下のような内容になっていると思います。

```HomeController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
        return view('home');
    }
}
```

今回は`about.blade.php`にログインしていなくてもアクセスできるようにしたいと思うので、
以下のように`->except('about')`を追加。

```HomeController.php
public function __construct()
    {
            $this->middleware('auth')
                 ->except('about');
    }
```

この1行を追加するだけでした！
2つ以上指定したい場合は、`->except('about','index');`

参考記事
・https://qiita.com/NAKKA-K/items/c50b199857467cd7c127
[追記]Go言語でチャンネル書込がブロックされるのを無理矢理解決した話

# 概要

 Go言語のチャンネルは非常に便利なんですが、正直複雑すぎてあまり使いこなせていません。

 この記事は、**チャンネルの受信側のgoroutineが先に死んだ場合に、送信側のチャンネル書き込みが永久にブロックされるのを無理矢理解決**した事例です。

 正直こんな書き方がGo言語的に良いのかどうかわからないので、ご意見等いただけると嬉しいです。

 **バッドノウハウかもしれないのでご注意を！！**
 **[追記]もしかしてBind呼ぶたびに終わらないgoroutineが増えるかも？**

## 参考

Go言語のチャンネル書き込みの時に…

| 条件 | 結果 |
|:----:|:----:|
| 受信ルーチンが生きていてチャンネルが満杯でない | ブロックしない |
| 受信ルーチンが生きていてチャンネルが満杯 | チャンネルが空くまでブロック |
| 受信ルーチンが死んでいる | 永久にブロック |
| チャンネルにnilが代入されている | 永久にブロック |
| チャンネルがcloseされている | panicする |

# panicするコード
[Playgroundへのリンク](https://play.golang.org/p/KwX4nRdm2cG)

```go:panic.go
package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	fmt.Println("Hello, playground")
	ch := make(chan string)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer func() {
			wg.Done()
			fmt.Println("Writer Finished")
		}()
		ch <- "AAA"
		time.Sleep(time.Second * 6)
		ch <- "BBB"
	}()
	wg.Add(1)
	go func() {
		defer func() {
			wg.Done()
			fmt.Println("Reader Finished")
		}()
		for i := 0; i < 5; i++ {
			select {
			case v := <-ch:
				fmt.Printf("OK:%v\n", v)
			default:
				fmt.Println("NG")
			}
			time.Sleep(time.Second)
		}
	}()
	wg.Wait()
}
```

受信側のルーチンが5秒で死んで、送信側が6秒後に新しい値を書こうとしてpanicします。

# 無理矢理解決したコード
[Playgroundへのリンク](https://play.golang.org/p/A6LBCh3xoVZ)

```go:muriyari.go
package main

import (
	"fmt"
	"sync"
	"time"
)

var m sync.Map

func main() {
	fmt.Println("Hello, playground")
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer func() {
			wg.Done()
			fmt.Println("Writer Finished")
		}()
		m.Store("AAA", "BBB")
		m.Store("CCC", "DDD")
		time.Sleep(time.Second * 6)
		m.Store("EEE", "FFF")
	}()
	wg.Add(1)
	go func() {
		defer func() {
			wg.Done()
			fmt.Println("Reader Finished")
		}()
		ch := make(chan interface{})
		Bind(ch, "CCC")
		for i := 0; i < 5; i++ {
			select {
			case v := <-ch:
				fmt.Printf("OK:%v\n", v)
			default:
				fmt.Println("NG")
			}
			time.Sleep(time.Second)
		}
	}()
	wg.Wait()
	fmt.Println("Main Finished")
}

func Bind(ch chan interface{}, key string) {
	go func() {
		for {
			if v, ok := m.LoadAndDelete(key); ok {
				ch <- v
			}
			time.Sleep(time.Millisecond * 200)
		}
	}()
}
```

 チャンネルに直接書き込むのではなく、sync.Mapに書き込んでるのでブロックしません。sync.Mapに書き込むところをきちんと型を書いた新たなfuncを定義してやれば、型チェックは働きます。上で定義しているBind関数も同様です。

 ちなみに最初はBind関数内でチャンネルを作ろうとしてたんですが、関数を抜けた時点で変数が解放されるのでpanic起こしてダメでした。

 パフォーマンスは計測してませんが、生のチャンネルを扱うよりは確実に悪いでしょう。sleepの時間調整である程度パフォーマンス調整が出来るかもしれません。

# 余談

 チャンネルの容量を増やすことで満杯になるまではブロックしない書き込みになるんですが、無限に増やすわけにもいかないのでこんな方法を考えてみました。

 設計が悪いのは明らかなんですが、一応なぜこんな設計を考えているのかの事情は説明しておきます。

 それは`Ctrl+C`やSIGTERMなどOSのシステムコール時に全てのGoroutineが後始末をしながら終了をして欲しいからで、その際にcontextを使ってルーチンを終了させているのですがルーチンの終了の順番が不定だからです。なので送信側ルーチンが先に死んで正常終了するケースと、受信ルーチンが先に死んで永久にプロセスが動き続ける場合がありました。

 これはあくまで本当に無理矢理やった例ですので、もっと洗練されたやり方があったり、ライブラリがあればご紹介いただければ嬉しいです。
TypeScript のユニオン型の最初に書くパイプ | の意味

TypeScript のユニオン型の最初に書くパイプ `|` が謎だった。次みたいなやつ。

```ts
type A = 
  | string // <- この | なに？
  | number
  | boolean;

type B = string | number | boolean; // <- これと同じなのに
```

この機能が入った時のissueがGitHubにあった。
[Suggestion: Allowing leading pipe in union type declarations · Issue #12071 · microsoft/TypeScript](https://github.com/microsoft/TypeScript/issues/12071)

見た目を整えるためのもので、なにか機能があるわけではない。 [Flow](https://flow.org/en/docs/types/unions/#toc-union-type-syntax)や[F#](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/discriminated-unions)由来の書き方とのこと。
Rubocopの導入

##はじめに
オリアプ制作で**Rubocop**を導入したので、載せておこうと思います。
( **Rubocop**：静的コード解析ツールのことで、余分なスペースやインシデントが揃っていないなどを指摘・修正してくれるGem )
オリアプのコードは誰かに直接見てもらう事はないので、導入する必要はないかなと思ったのですが、今後使用するかもしれないと思い、導入しました。


##1.Gemの導入・ファイル作成
**1.Gemfileにrubocopを記述し、bundle installする。
コードはgroup :development do  ~   end 内に記述する。**

```ruby:Gemfile
group development do
  #中略
  gem 'rubocop', require: false
end
``` 

**2.rubocopの設定コードを記述するファイル ( .rubocop.yml ) をプロジェクト直下に作成する。**

##2.Rubocopの設定
作成した .rubocop.yml に設定用のコードを記述していく。
以下のコードはあくまで一例であり、調べればまだある。

```ruby:.rubocop.yml
AllCops:
 Exclude:   # 除外するディレクトリ
   - "vendor/**/*" 
   - "db/**/*"
   - "config/**/*"
   - "bin/*"
   - "node_modules/**/*"
   - "Gemfile"

# メソッドの行数が多すぎないかをチェック
Metrics/MethodLength:
 Max: 30

# クラスの行数をチェック（無効）
Metrics/ClassLength:
 Enabled: false

# ブロック内の行数をチェックする
Metrics/BlockLength:
 Exclude:    # チェックの対象から除外
   - "spec/**/*"

# 下記項目を計算して基準値を超えると警告する（下記頭文字をとって'Abc'）
# ・Assignment: 変数への代入  ・Branch: メソッド呼び出し  ・Condition: 条件文
Metrics/AbcSize:
 Max: 50

# メソッドの中身が複雑になっていないか、計算して基準値を超えると警告する
Metrics/PerceivedComplexity:
 Max: 8

# 1行あたりの文字数をチェックする
Layout/LineLength:
 Max: 130
 Exclude:    # チェックの対象から除外
   - "Rakefile"
   - "spec/rails_helper.rb"
   - "spec/spec_helper.rb"

# 循環的複雑度が高すぎないかをチェック（ifやforなどを1メソッド内で使いすぎている）
Metrics/CyclomaticComplexity:
 Max: 10

# ネストが深すぎないかをチェック（if文のネストもチェック）
Metrics/BlockNesting:
 Max: 5

# メソッドパラメータ名の最小文字数を設定
Naming/MethodParameterName:
 MinNameLength: 1

# 日本語でのコメントを許可
Style/AsciiComments:
 Enabled: false

# クラス内にクラスが定義されていないかチェック（無効）
Style/ClassAndModuleChildren:
 Enabled: false

# 文字列に値が代入されて変わっていないかチェック（無効）
Style/FrozenStringLiteralComment:
 Enabled: false

# クラスやモジュール定義前に、それらの説明書きがあるかをチェック（無効）
Style/Documentation:
 Enabled: false

# ％i（）構文を使用していないシンボルで構成される配列リテラルをチェック（無効）
Style/SymbolArray:
 Enabled: false

# 空メソッドの場合に、1行のスタイルにしない
Style/EmptyMethod:
 EnforcedStyle: expanded
``` 

##3.Rubocopの実行
**以下のコマンドを実行することでRubocopが余分な箇所などを指摘してくれる。**

```言語:ターミナル
% bundle exec rubocop
```
**また、以下のコマンドを実行することでRubocopがさきほどの指摘を自動で修正してくれる。**

```言語:ターミナル
% bundle exec rubocop -a
```

以上、導入完了！！！

##最後に
rubocopの設定用のコードを覚えるのは無理なので、もし使う機会があれば、この投稿を参考に導入していきたいと思います。

##参考
以下の投稿を参考にさせて頂きました。ありがとうございました。
https://qiita.com/tomohiii/items/1a17018b5a48b8284a8b
ラズパイでマイクラサーバーを建ててMinecratf Serversに乗るまでに得た知見など

# 続編書きました
[ここから](https://www.inaridiy.tech/article/x38oupq57i24)読めます


あと記事をブログに移しました
[ここ](https://inaridiy.tech/article/x38oupq57i24)から読めます
【Redis】redis-cliの備忘録

# はじめに

Redisのコマンドや特徴などについてのマイ備忘録
随時更新する

# 基本操作

大文字が予約語

```bash
# 起動
$ redis-cli

# DB番号選択
$ SELECT 1

# key, valueをセット
$ SET key value

# key一覧取得
$ KEYS *

# value取得
$ GET key_name

```

# KEYについて

## key名変更

```bash
$ RENAME 古いkey名 新しいkey名
```

## 正規表現

Redisで使える正規表現は以下のみ

- ``[]``
    - カッコ内のいずれかの文字1つ
- ``*``
    - 任意の文字列
- ``?``
    - 任意の1文字

### 使用例

```bash
# 全てのKey取得
$ keys *

# 数字で始まるkey取得
$ keys [0-9]*
```

# マルチバイト文字列

普通に起動すると、マルチバイト文字列が文字化けすることがある

```bash
$ redis-cli

# ここでのvalueの中身は適当
$ get hoge
"\xe3\x81"
```

この場合、以下のように``raw``オプションをつけると良い

```bash
$ redis-cli --raw
$ get hoge
"ほげ"
```

> 参考
[redis-cliでマルチバイト文字列が文字化けする時はrawオプションを使う - Qiita](https://qiita.com/marqs/items/2ee8889832f958b062b4)
sdk-goでdynamoDBの複雑なstructの値をupdateする

## やりたいこと
GoでのdynamoDBに対しての操作は割とめんどかったりします。単純なStringやIntのものであればまだ良いのですが、ListのMapのなかのListとか、値の構造が複雑になってくると気が滅入ります。

```go
// シンプルなパターン
&dynamodb.UpdateItemInput{
  TableName: aws.String("sampleTable"),
  Key: // 省略,
  ExpressionAttributeValues: map[string]*dynamodb.Attribute{
   ":ssv": {
      S: aws.String("sampleStringValue")
    },
   ":siv": {
      N: aws.String(strconv.Itoa(0)) // ここもintをstringに型変換してそれをaws.String()してkeyにはNを指定って感じでわかりづらい。
    },
  },
  UpdateExpression: aws.String("set sampleStringValue=:ssv and sampleIntValue=:siv")
}

// 複雑なパターン
&dynamodb.UpdateItemInput{
  TableName: aws.String("sampleTable"),
  Key: //省略,
  ExpressionAttributeValues: map[string]*dynamodb.Attribute{
    ":slv": {
      L: [
       {
         M: {
           ":smp": {
            aws.String("sample")
            }....
         }
       }
      ]
    },
  }
}
```

これを楽にやれるのがsdk-goのexpressionってやつです。

## expressionの使い方

```go
update := expression.UpdateBuilder{}.Set(
  expression.Name("sampleComplexValue"),
  expression.Value(sampleCompexValue)
)

expr, err := expression.NewBuilder().WithUpdate(update).Build()

&dynamodb.UpdateItemInput{
  TableName: aws.String("sampleTable"),
  Key: //省略,
  ExpressionAttributeNames: expr.Names(),
  ExpressionAttributeValues: expr.Values(),
  UpdateExpression: expr.Update()
}
```
上の例よりかなりスマートにかけますね。可読性も格段に上がります。
expression.Valuesの引数に複雑な（もちろん単純なものでもなんの問題もありませんが）値を渡すだけで、ここまでできるのは結構ありがたい。

もちろんexpressionは上記の例のような更新処理だけでなく、Query処理等にも使えるので積極的に使っていきたいところですね。
【ミライトデザイン社内勉強会#13】「IDDD本から理解するドメイン駆動設計」輪読会~第4章「アーキテクチャ」~

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

## [実践DDD本 第4章「アーキテクチャ」 ～レイヤからヘキサゴナルへ～：CodeZine（コードジン）](https://codezine.jp/article/detail/9922)

### ヘキサゴナルアーキテクチャのポート、アダプタは具体的にどんな実装になるのか知りたい
- ポートがインターフェースでアダプタが具象クラス
- 例えば、DBに登録する処理で、ポートとなるインターフェースを定義しておき、アダプタとなる具象クラスをモッククラスと実際にDBに登録するクラスとで差し替えることでテストなんかもしやすくなる
- コンセントで例えると穴がポートでプラグがアダプター
- インターフェースを定義していたらヘキサゴナルアーキテクチャ？
    - そういうわけでもない
    - ヘキサゴナルアーキテクチャーはポートとアプリケーションの層を分けたアーキテクチャ
    - 技術的な部分とアプリケーションを分離して、技術的な部分を差し替え可能にする
- クリーンアーキテクチャもヘキサゴナルアーキテクチャの一種
    - クリーンアーキテクチャの外側の話をしているのがヘキサゴナルアーキテクチャ、ヘキサゴナルアーキテクチャのアプリケーションの内側の話をしているのがオニオンアーキテクチャ、ヘキサゴナルアーキテクチャとオニオンアーキテクチャをたすとクリーンアーキテクチャ。みたいなイメージ。
    
### SOAを導入する = 境界づけられたコンテキストをそれぞれ独立したシステムになるように実装しようって話？
-  [サービス指向アーキテクチャ (SOA)とは｜「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word16261.html)

> システムを「1つのデカいシステム」と捉えるのではなく「それぞれの機能を提供する部品（サービス）の集まり」と見なす考え方

- SOAの考え方を意識すると、ヘキサゴナルアーキテクチャでサービス作る時にいい設計ができるよって話
    - ここではおそらくSAOでやらなきゃいけないっていうより、紹介程度な説明

### 境界づけられたコンテキストごとにヘキサゴナルアーキテクチャや他のアーキテクチャで実装されていて、それぞれが独立したシステムになっているのが理想ってこと？
- そう。
- 境界づけられたコンテキストごとに独立したアプリケーションになるイメージ
   
### `ical（一般的なカレンダー形式）のような汎用的なメディアタイプを使用することが推奨されています。`と書いてあるけど、どういう意味？
-  https://codezine.jp/article/detail/9922?p=3
- [メディアタイプ - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%82%BF%E3%82%A4%E3%83%97#:~:text=%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%82%BF%E3%82%A4%E3%83%97%EF%BC%88%E6%97%A5%3A%20%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E5%9E%8B,%E8%A1%A8%E7%8F%BE%E3%81%99%E3%82%8B%E8%AD%98%E5%88%A5%E5%AD%90%E3%81%A7%E3%81%82%E3%82%8B%E3%80%82)
- 公開用の独自モデルを設計 = 独自モデルをjsonで返す
- 汎用的なメディアタイプを使用する = imgaeとかで返す
- コアドメインをそのまま公開する = ドメインのオブジェクトを返すとか？？
- Restより前の技術の話をしている気がする
    - 今ではapi叩いてjsonで返って来るのが当たり前みたいになってるからここの話はイメージが付きづらいのかも

### CQRSで「コマンドモデル用データストア」と「クエリモデル用データストア」　を用意してデータソース分離するような設計ってイベントソーシングで実装したいとき以外にもすることはあるの？
- CQRSはデータソースは分離している。
- データソース分離しないとメリットとしてはCQSと同じ
- CQRSの方はコマンドとクエリを完全に分離するので、データソースも分離してある
    - CQRSが解決したいのは、書き込み前提で正規化されたデータを読み込むのが辛いので書き込みと読み込みでデータソースを分けようってこと
    - 書き込みのために正規化されたテーブルを読み込むと読み込みのたびに毎回joinとかをする必要がある
    - 読み込み用のテーブルを作っておくと読み込むたびにjoinする必要がない
    - データソースを分離しないとCQRSのメリットは活かせないと思う
- クエリ用のデータソースはコマンド用のデータソースと同期する必要がある
    - イベントソーシングはコマンド用のモデルの書き込みはイベントの保存のみを行っている
- 一般的なアプリケーションは書き込まれた瞬間に読み込み側に反映する必要があるのでちょっとめんどくさい処理とかが必要になる

### イベントソーシングでイベントストアに保存されるイベントは具体的にはどんなデータ形式で保存されているの？
- noSqlとか
- イベントのデータがクエリ用のデータと同じようにRDBに保存されているとは限らない
- CRADのイメージを1回捨てないとCQRSやイベントソーシングの考えを理解するのは難しいかも
- イベントソーシングはそれだけで1つ勉強会が開けるくらいボリュームがある話なので今回は割愛

## 次回
[【ミライトデザイン社内勉強会#14】「IDDD本から理解するドメイン駆動設計」輪読会~第5章「エンティティ」~ - Qiita](https://qiita.com/tkek321/items/cf4f49b8f994e6b85651)
複数の型要素を持った項目をソートする方法 (C#)

#背景
様々な要素が塊となった際にintの要素の項目でsort処理(昇順、降順)を行い、それに伴ってセットとなっている他の値も並び替える方法を探していた為。
アウトプットとしてまとめて使いやすくする為。

##環境
- Mac (macOS Big Sur 11.1)
- Visual studio for mac (8.8.5)



##使用言語
C＃

##メリット
ソート内容に沿って様々な型要素(例えばstring型やbool型、char型)を使用できる

##書き方(言語化)

#####1.項目をクラス化する
#####2.クラスにした内容をインスタンス化する
#####3.処理する際に使用するListの変数を用意する
#####4.クラス化した変数に入力したい情報を塊として入れていく
#####5.Listに要素を追加していく
#####6.int型の要素の差を比較する
#####7.その差を利用して要素を並び替える
#####8.(もし降順にしたい場合は逆にする※いきなり降順はこの場合は不可)
#####9.要素をforeachで出力する

##書き方(code)

```C#
using System;
using System.Collections.Generic;

namespace test_for_Paiza
{
    //ここのクラスに情報をまとめる
    class Info
    {
        public string name;
        public int age;

        //情報は直接変数にアクセスできない様にメソッド化する
        public Info(string name, int age)
        {
            this.name = name;
            this.age = age;
        }
    }

    class Program
    {
        static void Main()
        {
            //今回はコンソールから入力したものとする
            
            //入力回数を指定
            var N = int.Parse(Console.ReadLine());

            //Infoクラスをインスタンス化
            Info[] src = new Info[] { };

            //操作しやすい様に格納するListを生成
            var list = new List<Info>();

            //コンソールからの入力をForループで可変長にする
            for (int i = 0; i < N; i++)
            {
                string[] baseArray = Console.ReadLine().Trim().Split(' ');
                int intData = int.Parse(baseArray[1]);
                //Info型の変数srcに新規内容として格納
                src = new Info[] { new Info(baseArray[0],intData) };

                // listに要素を都度追加
                list.AddRange(src);
            }

            // listをソート
            var c = new Comparison<Info>(Compare);
            list.Sort(c);
            
            //降順にしたい場合はReverseメソッドを使用
            //list.Reverse();

            Console.WriteLine("以下からがソート済の内容です");

            foreach (Info i in list)
            {
                Console.WriteLine("name:{0}, age:{1}才", i.name, i.age);
            }

            
        }

        static int Compare(Info a, Info b)
        {
            //xには２つの値の差が入る
            var x = a.age - b.age;
            return x;
        }
    }
}

```

##### <入出力結果>
```
5
hideki 40
koichi 25
yohko 18
ken 34
miho 29
以下からがソート済の内容です
name:yohko, age:18才
name:koichi, age:25才
name:miho, age:29才
name:ken, age:34才
name:hideki, age:40才
```



##まとめ
書き方については他の方の書き方を参考にさせていただきながら書いてみました。ありがとうございます。
クラス化させることで追加で要素を増やしたくなった場合にも柔軟に対応できるのかなと思いました
[Rails] gem jsonを使って、APIを叩く。

今回は、weather APIにしました。(https://openweathermap.org/)

## APIの取得方法

```ruby
gem json
```

```ruby
def home
    require 'net/http'
    require 'json'

    @url = 'http://api.openweathermap.org/data/2.5/weather?q=berlin,de&appid=ここにapikey。'
    @uri = URI(@url)
    @response = Net::HTTP.get(@uri)
    @output = JSON.parse(@response)
   end
```

output

```ruby
<%= @output %>
```

です。
VTuberのモザイクアートを作る

動画・配信のサムネイルを使って、VTuberのモザイクアートを作ります。
とは言えモザイクアートの作成自体はツールを使うだけなので、ここで書くのはYouTube Data APIの使い方ぐらいです。
流れとして、APIで動画データ取得→動画データ内のサムネURLから画像を取得→取得した画像を使ってモザイクアート作成といった感じです。

今回対象とするVTuberさんは不条 理さんです。
https://twitter.com/AyaFujo
https://www.youtube.com/channel/UCnWY-6vdj_UaKDwGI5oLDZw
素敵な個人Vさんで、おすすめです。

##動画データの取得
まず、YouTube Data APIを使って動画データを取得します。
準備方法は調べればいくらでも出てくるので、今回は割愛します。

```py
from apiclient.discovery import build
import json
import time

# APIを使うための情報を設定
DEVELOPER_KEY = "＊＊取得したAPIキー＊＊"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
    developerKey=DEVELOPER_KEY)

item_list = []
search = youtube.search().list(
    part = "snippet",
    # チャンネルIDはYouTubeホーム画面URLから取得、↓の***の部分
    # https://www.youtube.com/channel/***/featured
    channelId = "＊＊チャンネルID＊＊",
    maxResults = 50 # 一回で取得する動画情報数
)
data = search.execute()

# 一回じゃ取りきれないので何回か実行する
while True:
    item_list = item_list + data["items"]
    print(len(item_list))
    print(data["pageInfo"])
    # 取りきったら終了
    if "nextPageToken" not in data:
        break
    time.sleep(1)
    search = youtube.search().list_next(search, data)
    data = search.execute()

# 取得したデータを保存
with open('movieData.json', 'w',  encoding="utf8") as f:
    json.dump(item_list, f, ensure_ascii=False)
```

ざっくりこんな感じで動画データが取得できます。

##画像の取得
続いて画像の取得です。
先ほど取得したJSONファイルですが、snippet→thumbnailsにサムネの情報が入ってます。
画像サイズごと別れており、その下のurlに画像のURLがあります。
データ構造も調べれば出てくるので割愛します。

画像の保存方法としてurllibライブライを使います。
これを使うことで、URLを指定して画像を保存することができます。

```py
import json
import urllib.request

# さっきのJSONファイル
json_file = open('movieData.json', 'r')
data = json.load(json_file)
json_file.close()

i = 1
for line in data:
    # 画像ファイル名
    image_file = "samune/" + str(i) + ".jpg"
    i += 1
    # URLの場所を指定して取得
    url = line["snippet"]["thumbnails"]["medium"]["url"]
    print(line["snippet"]["title"])
    # urllibライブライを使って画像を保存
    tgt = urllib.request.urlopen(url).read()
    with open(image_file, mode='wb') as f:
        f.write(tgt)
```

##モザイクアートの作成
これもプログラムでサクッとできればよかったのですが、そこまでの技術は無いので...。
最後はツールの力を借ります。
AndreaMosaicというツールになります。
使い方はリンクを貼るのでそちらから。
https://aprico-media.com/posts/3742

##完成したモザイクアート
元画像はツイッターのアイコンを使いました。
小さくして薄目で見ると見える気がする！

元画像
![MXVZQepK_400x400.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209731/335d261e-8776-71ff-a85d-8128f08a9c40.jpeg)

モザイクアート
![モザイクアート.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209731/3090f9f0-6f29-e5a4-05d5-d2a3f2ca02cb.jpeg)

以上、ちょっと変わったファンアートを作りたくて遊んだ話でした。
[React] Reactの学習をします（2-1）レビューサイトを作ろう

#Reactの学習をします（2-1）

引き続き、Reactの学習をしています。

##教材

[Reactチュートリアル2：レビューサイトを作ろう](https://zenn.dev/likr/articles/react-with-heroku "Reactチュートリアル2：レビューサイトを作ろう")

引き続き、likr さんが公開している「Reactチュートリアル2：レビューサイトを作ろう」という記事を教材に学習させて頂きます。

素晴らしい教材をありがとうございます。

##成果物

このチュートリアル2につきましては、チュートリアルの通りに作成し既にNetlifyに公開させて頂いております。（素晴らしいチュートリアルをありがとうございます！！）

こちらです　→　[日大文理ラーメンレビュー](https://friendly-hopper-e0a9d5.netlify.app/ "日大文理ラーメンレビュー")


##学習日記

既に作成済ですが、私にはまだまだ内容が難しかったです。

少しずつ、チュートリアルの内容を読み返してみたいと思います。

※ 教材から箇条書き的に抜粋させて頂きます。

##Web サービスの計画

このWebアプリでは、３つのGETと１つのPOSTが必要です。

|メソッド     |パス                                |
|:------------|:-----------------------------------|
| GET         | /restaurants                       |
| GET         | /restaurants/:restaurantId         |
| GET         | /restaurants/:restaurantId/reviews |
| POST        | /restaurants/:restaurantId/reviews |

>・REST API において、これらの機能ごとの URI を エンドポイント と呼びます。

そして、実際に開発を進めていきます。（あらかじめいくつかのファイルを含んだリポジトリを用意して下さっています。）

>・作業用ディレクトリの名前を review-app とします。

mkdirコマンドで、ディレクトリを作成しました。

（つづく）
JP1の起動条件と実行登録方法について解説

# 条件を設定した際の実行方法について
# 環境
|Kind|Software|Version|
|:-:|:-:|:-:|
|OS|Windows Server 2008||
|JP1 Client|Automatic Job Management System 3 - View|10-10-03|

# 各設定について
## `.CONDITION(AND)`を使用した際
### `.CONDITION(AND)`とは
下記の設定です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676040/7d140071-7df7-5a96-483c-49e7eb7022cd.png)
どういった場合に使うかというと、実行した10分後にもう一度実行、その10分後にさらにもう一度実行、....したい、といった状況です。
それ以外にも『イベントIDを設定してコマンドで呼び出す』、『メールを受信した際に呼び出す』等があります。
とりあえずなにかをトリガーにしたいときに使うものと覚えましょう

実行間隔制御についてもう少し詳しく話すと「10分間sleep」する制御なので
例えば昼の`12時34分56秒`に実行を開始したとすると「10分間sleep」した結果、最初の実行は`12時44分56秒`、次の実行は`12時54分56秒`になります。
しかも実行は数ミリ秒ずつずれていきます。
なのでずれた結果、1日後には`12時35分00秒`に実行しているかもしれませんね。

### 実行登録方法
実行登録から[登録方法:即時実行]→[起動条件:設定されていれば使用する]を選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676040/972cc524-3417-9304-1738-9a0cde12c21d.png)
OKを押した瞬間に「実行を開始」します。
上の例だとOKを押した10分後に最初の実行が動きます。

## `スケジュール`を使用した際
### `スケジュール`とは
下記画像の設定です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676040/695e3dc7-45e4-c8e4-1f93-dfb8cd4aa621.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676040/b1dcd696-d4c0-7158-5d57-ec86afe108a4.png)
実行を開始する時間をあらかじめ指定しておいて、その時間がくるまでJP1を待機させるやりかたです。
どういった場合に使うかというと、例として毎日始業時間に処理を開始したい場合『9:00 AM JSTに設定しておく』等です。
上で説明した`.CONDITION(AND)`だと時間にはルーズ(少しずつずれるし)なのでこちらを使用したほうがより正確に実行できます。

ちなみに、`.CONDITION(AND)`と併用することもできます。
併用した場合`9:00 AM JSTから6:00 PM JSTまで10分間隔で起動する`といったことができます。
併用する際も下に記載した`スケジュール`の実行登録方法になります。
※`.CONDITION(AND)`で紹介した実行登録方法を使うと`スケジュール`が無視されて`24時間ずっと10分間隔で起動する`といった動きになるので注意

### 実行登録方法
実行登録から[登録方法:計画実行]を選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676040/accb1107-9346-d789-42f8-f65e630997e7.png)
ちなみに`デーモン起動時に予定時刻超過`と`実行登録時に予定時刻超過`にある`すぐに実行する` `次回から実行する`の違いに関しての説明もしておきます。
`デーモン起動時に予定時刻超過`とは、実行すべき時間が来た時にJP1のサーバーがJP1のソフトを落としていた場合
`すぐに実行する`ソフトを起動した時にすぐ実行する
`次回から実行する`次に実行すべき時間が来た時に実行する

`実行登録時に予定時刻超過`とは上記のOKボタンを押したタイミングで
`すぐに実行する`
`次回から実行する`
ということ

# おわり

(もっとわかりやすい表記にすれば良いのにね)
Djangoでローカライズされた時刻を取得

## 環境
- Django 3.0.3 [Time zones | Django documentation | Django](https://docs.djangoproject.com/en/3.0/topics/i18n/timezones/)

## タイムゾーンを設定
settings.pyで`USE_TZ`と`TIME_ZONE`を設定する必要があります。

```python:settings.py
USE_TZ = True # タイムゾーンを使う
TIME_ZONE = "Asia/Tokyo" # タイムゾーンを日本時間にする
```

## modelのDateTimeFieldから取り出してタイムゾーンの時刻を取得
Djangoではローカライズした時刻で保存してもデータベースにはUTCの時刻で保存されます。

```
from models import Product
from django.utils.timezone import localtime

product = Product.objects.get(pk=1)

print(product.created_at)
# 2021-01-21 10:26:06.994428+00:00

print( localtime(product.created_at) )
# 2021-01-21 10:26:06.994428+09:00
```

UTCで保存されるのは、複数のタイムゾーンを利用するときに柔軟に時刻の変換ができるからです。

pythonで時刻を扱うときの概念、

- native(タイムゾーンなしの時刻)
- aware(タイムゾーンありの時刻)

が影響しています。
これらの概念は以下の記事が参考になります。
[基本的な日付型および時間型](https://docs.python.org/ja/3/library/datetime.html#aware-and-naive-objects)
[【Django】naive timeをaware timeに変換する方法](https://sleepless-se.net/2019/10/28/django-nativedatetimeto-aware-datetime/)


## viewでタイムゾーンの時刻を取得
```
from django.utils import timezone
from django.utils.timezone import localtime

print( localtime(timezone.now()) )
# 2021-01-21 10:26:06.994428+09:00

```

### localtime() で時刻をローカライズ
`django.utils.timezone`で時刻を呼びたしただけではローカライズされていません。

```
from django.utils import timezone

print( timezone.now() )
# 2021-01-21 01:26:06.994401+00:00
```

`settings.py`の`TIME_ZONE`で設定した時刻を取得するには、`django.utils.timezone.localtime`を使ってローカライズされた時刻に変換する必要があります。

### pythonのdatetimeはローカライズできない
当たり前ですけど、これはエラーになります。

```
from datetime import datetime

print( datetime.now())
# 2021-01-21 10:43:12.858566

print( localtime(datetime.now()))
# localtime() cannot be applied to a naive datetime
```


## templateでタイムゾーンの時刻を取得
```
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils import timezone


class DashboardIndexView(LoginRequiredMixin, generic.TemplateView):
    """ ダッシュボード """
    template_name = 'dashboards/index.html'
    raise_exception = True # 403ページの表示

    def get_context_data(self, **kwargs):
        now = timezone.now()
        print( now )
        # 2021-01-21 01:53:31.005940+00:00
        context = super().get_context_data(**kwargs)
        context['now'] = now
        return context

```

```html:dashboards/index.html

{{ now }}
<!-- 2021年1月21日10:53 -->
```

テンプレート側でタイムゾーンの時刻を取得する場合は`django.utils.timezone`でローカライズする必要はありません。
LIFF URLでクエリパラメータを使おうとするとliff.initが正常に動かなかった話

# 概要
業務の中でLIFF URL(https://liff.line.me で始まるURL)にクエリパラメータを付与して処理を行う、というケースがありました。
そこで困ったことがあったので共有しようと思った次第です。

#結論
LIFF URLにクエリパラメータを付ける場合、liff.initのあとにsetTimeoutなどのsleep処理を入れると動作する。（根本原因は不明です、、）

# 動作環境
LIFF： v2.4.1
クライアント： Nuxt.js（SPAモード）

# LIFFでクエリパラメータを使えるようになった
LIFF v2.3.0からhttps://liff.line.me?hoge=hoge のようにLIFF URLに対してクエリパラメータを使えるようになりました。
詳しくは以下をご覧ください。
https://developers.line.biz/ja/docs/liff/opening-liff-app/

# 困る前のクライアントのソース
liffのSDKを使用するには「liff.init」という関数を実行する必要があります。
今回はNuxtを使用していたので、pluginsにliff.initを実行する関数を書いて実行しました。
この段階では正常に動作することが確認されました。

```javascript:plugins/liffConfig.js
import liff from '@line/liff'
const liffConfig = async (_) => {

  await liff.init( { liffId:'xxxxxxxxxxxx' } )
}

export default liffConfig

```

# 困ったこと
要件の都合上、LIFF URLに対してクエリパラメータを付与してアクセスする必要があったため、LINE DevelopersコンソールでLIFF URLを作成しhttps://liff.line.me?hoge=hoge の形でアクセスしました。
するとliff.init前後の処理が無限ループされる現象が起きました。

# 原因を探す
liffのSDKはローカルホストでは動作しないため、chromeのデベロッパーツール等でデバッグすることが出来ません（ここが一番つらかった）。
そのため開発環境にひたすらalert関数を入れて、どこまで処理が走っているのか検証しました。

# 原因を発見（？）
alert関数を入れて打鍵しながらデバッグした結果、liff.init後のalertで数秒待ったのちに処理を続けると、以前と同様に動作することが確認できました。

```javascript:plugins/liffConfig.js
import liff from '@line/liff'
const liffConfig = async (_) => {

  alert('initする前')
  await liff.init( { liffId:'xxxxxxxxxxxx' } )
  // 打鍵時にここで数秒待つと動いた、、、
  alert('initした後')
}

export default liffConfig

```

# 最終的に
数秒待ったのちに動作するのであれば、そのような処理を追加したら良いのでは？と思い、setTimeoutを使用してsleep処理を追加しました。
根本原因は不明ですが、動くものを目指すという意味ではとりあえず良しとしました。

```javascript:plugins/liffConfig.js
import liff from '@line/liff'
const liffConfig = async (_) => {

  await liff.init( { liffId:'xxxxxxxxxxxx' } )
  await sleep(1000)
}
// setTimeoutをそのまま使うのはイケてないと思ったので、Promiseでラップしています。
const sleep = (msec) =>
  new Promise((resolve) => setTimeout(resolve, msec))

export default liffConfig

```

# 最後に
根本原因を突き止めれなかったのは悔しいですが、何とか動くものが出来て良かったのかなと思います。
少しでも同じ現象に出くわした方の助けになれれば幸いです。
こうしたほうが良いのではなどのコメントがあれば、ぜひよろしくお願いします！
SpacemacsのClojure Layerで一旦LSPをやめる

※公式で修正されたので不要になった :tada: 

cf. https://github.com/syl20bnr/spacemacs/commit/4bf350148863a63ec1f961aad6cd248503026483

---

ひとまず


```diff:layers/+lang/clojure/funcs.el抜粋

  (defun spacemacs//clojure-backend ()
    "Return selected backend."
-   (if clojure-backend
+   (when clojure-backend
      clojure-backend
-     (cond
-      ((configuration-layer/layer-used-p 'lsp) 'lsp)
-      (t 'cider))
    ))

```

https://github.com/syl20bnr/spacemacs/blob/ba9e0afa34c97b310456d4352ec582d73411902b/layers/%2Blang/clojure/funcs.el#L12-L18


何の話題かはこれ見てどうぞ
https://note.com/v2okimochi/n/n0733089b5ee5
【AWS】EC2（Amazon Linux2）でGUIをインストールしてみる

# はじめに
Amazon Linux2でVNC接続の開発環境を構築したかったので、MATEデスクトップでGUIを表示できるように設定します。

# 設定方法

## EC2の準備
パブリックIPでSSHアクセスできるEC2インスタンスを準備してください。

## MATEデスクトップのインストール

まずは、パッケージ類のアップデートを行います。
`Is this ok [y/d/N]: `と表示されたら、`y`を入力してアップデートを続行します。

```bash:パッケージのアップデート
sudo yum update
```

その後に、MATEパッケージをインストールします。
`Is this ok [y/d/N]: `と表示されたら、`y`を入力してインストールを続行します。

```bash:MATEパッケージのインストール
sudo amazon-linux-extras install mate-desktop1.x
```

MATE をデフォルトのデスクトップとして定義します。
下記のコマンドでどのユーザーでもMATEを利用できるように定義します。

```bash:MATEの定義
sudo bash -c 'echo PREFERRED=/usr/bin/mate-session > /etc/sysconfig/desktop'
```

<details><summary>現在のユーザーに対してのみ定義する場合</summary><div>

```bash:現在のユーザーのみ有効なMATE定義
echo "/usr/bin/mate-session" > ~/.Xclients && chmod +x ~/.Xclients
```　
</div></details>

## TigerVNCのインストール

下記のコマンドでTigerVNCをインストールします。

```bash:TigerVNCのインストール
sudo yum install tigervnc-server
```

VNC固有のパスワードを設定します。

```bash:パスワード設定コマンド
vncpasswd
```

`Password:`と表示されたら設定したいパスワードを入力してください。
`Verify:`と表示されたら、パスワードを再度入力してください。
`Would you like to enter a view-only password (y/n)? `と表示されたら、`n`と入力してEnterキーを押してください。

VNCサーバーを自動起動したいので、下記のコマンドでSystemdユニットを作成します。

```bash:Systemd作成コマンド
sudo cp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@.service
```

sedコマンドを使用して、新しいユニットのすべてのUSERをec2-userに置き換えます。

```bash:ユーザー置き換えコマンド
sudo sed -i 's/<USER>/ec2-user/' /etc/systemd/system/vncserver@.service
```

Systemdの設定を再ロードし、サービスを有効にして起動します。

```bash:Systemdコマンド
sudo systemctl daemon-reload
sudo systemctl enable vncserver@:1
sudo systemctl start vncserver@:1
```

## VNCを使用してGUIに接続

TigerVNCを[公式ページ](https://tigervnc.org/)からダウンロードして、インストールします。

インストールが完了したら、起動します。
『インスタンスのIPアドレス:5901』と入力して接続します。
パスワードを聞かれるので、先ほど設定したパスワードを入力します。
LINEのchrome拡張機能をアプリではなくブラウザ上で開く + デスクトップショートカット呼び出し

# はじめに
LINEのchrome拡張機能は、クリックした瞬間にアプリ単体で立ち上がるため、

* ブックマークができない
* デスクトップのショートカットに置けない

という問題があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/9090b766-552d-8386-0f46-5d2f54513ff6.png)


[ユーザーレビュー](https://chrome.google.com/webstore/detail/line/ophjlpahpchlmihnnnihgmmeilfjmjjc?utm_source=chrome-ntp-icon)でもいくつか問題視されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/622e39d9-b467-d057-f835-24dc13a71a8f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/ba242e8d-a9f6-9dc8-e0dd-e6269c6d900f.png)

そこで、今回、ブラウザから開くことができるURLを調査し、ショートカットでも開けるようにしたので、情報共有します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/5f610981-ee73-dfbb-3906-dd1fa9df95bc.png)

# 各種情報
## 条件
・LINEの拡張機能がインストールされたユーザーであること

## ブックマーク用アドレス
```
chrome-extension://ophjlpahpchlmihnnnihgmmeilfjmjjc/index.html
```
## ショートカット用アドレス
```
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" chrome-extension://ophjlpahpchlmihnnnihgmmeilfjmjjc/index.html
```
- 「特定のURLで開くchromeのショートカット」です

# 起動画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/f7b35db7-39b4-3cb8-f353-5cf649ebc096.png)

私はWoX(キーボードランチャ)を入れており、chromeも立ち上がっていない状態からキーボードだけでこの画面に飛びます。

# 調査内容
着目したのは、「Googleアプリ」でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/9f04322f-8d72-12b4-e114-d03508aaeae6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/45a33483-db88-7df9-ee7c-d9e223ba96e3.png)


ここの「ショートカット作成」に着目。
「Googleスライド」を例にします。
ブラウザで開くと、一瞬この画面が表示されます(めっちゃスクショした)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/1215e57f-da0a-0f58-8fee-75d92fdf30ac.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/34a8860b-b82b-a6ca-17e0-651f04b27614.png)


よく見ると、
```chrome-extension://【ID】/xxxx.html
```

の形式であることが読み取れます。
そこで、LINEのIDである、 
```ophjlpahpchlmihnnnihgmmeilfjmjjc
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90313/950aaf42-af28-c2da-4bd4-ab557834c95e.png)

に置き換えて、いくつかそれっぽいファイル名で開いてみたところ、無事開くことができた、、というものです。(スライドはmain.htmlですし、LINEはindex.htmlですね)

# おわりに
- 今回のケースのように、拡張機能からしか開けないアプリがあれば、同じように使用できます。
- 上記レビューのように、LINEのchrome拡張にはそれ以外にもいろいろ問題があるようですが...私はショートカットが置ければとりあえず良いです
- ~~まぁ、LINEアプリ入れればいいじゃん、というのは置いておいて...~~
SSMS から Azure SQL Server へ接続する方法


1. SSMSを起動する。
2. Azure Portal から接続したいサーバー名を入力する。（大抵~.net）
3. SQL Server認証を選択してサーバーにログインするためのidを入力する。（Azureアカウントではない）
4. パスワードも入力したら接続を押下

サーバー側で接続許可をしている前提です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1025585/ce6addb0-4a03-e8e0-91ce-206a6a89a025.png)
create-react-app(CRA)でcssやめてscssを使う方法

# Reactのプロジェクトを作成
```bash
npx create-react-app myapp --template typescript
```

# Sassのビルドツールをインストール
```bash
npm i -D node-sass@4.14.1
```

`node-sass`のバージョン指定の理由は最新版の`5.0.0`をインストールすると下記のようなエラーが発生するからです

```bash
error Generating development JavaScript bundle failed 
Node Sass version 5.0.0 is incompatible with ^4.0.0.
```

# cssファイルをscssファイルにリネーム
`index.css`と`App.css`を`.scss`に変更
`index.tsx`と`App.tsx`のcssをimportしている箇所を`scss`に変更

# アプリを実行
```bash
npm start
```

# ブラウザデフォルトのcssをリセットしたい
`index.scc`の最初に下記を追記すればOK([公式](@import-normalize; /* bring in normalize.css styles */
))

```css
@import-normalize; /* bring in normalize.css styles */
```
PostgreSQLのJSONB型のデータで一部の要素を更新する


使用しているPostgreSQLはバージョン10です。

更新するJSONの例、『check_comment』の値をjsonb_setで更新します。

```JSON
{
    checked:{
        checker_name: "test", 
        checkdate: "2021/01/21",
        check_comment: "test1"
    }
}
```

jsonb_setの引数は対象となるJSONB、値をセットする対象の要素のパスをカンマ区切りで指定、セットする値です。

```SQL
UPDATE tbl_checked SET
checkedjson = jsonb_set(checkedjson,'{checked,check_comment}','"test2"')
WHERE checkedid = '1772377700a0817'
```

このSQLを実行すると『check_comment』の値が「test2」に更新されます。
[Vue3] v-onでKeyCodeが非推奨になりました

https://v3.vuejs.org/guide/migration/keycode-modifiers.html

Vue2系では

``` javascript
<input @keyup.13="submit" />
```

のようにキーコードを指定してメソッドを発火させることができたのですが
同じことをVue3系でやろうとすると

```
'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. 
Using 'KeyboardEvent.key' instead  vue/no-deprecated-v-on-number-modifiers
```

Lintの設定によってはエラーになってしまいます。
これはkeyCodeが非推奨になったためです。

Vue3系ではキーコードを直接使用せずにエイリアスを使うことが推奨されています。
先程の例だとkeyCodeの13はenterの入力なので
以下のように書いてあげるとLintで怒られなくなります

``` javascript
<input @keyup.enter="submit" />
```

ちなみにエイリアスの記法はケバブケースになります

``` javascript
// GOOD
<input @keyup.page-down="submit" />

// BAD
<input @keyup.pageDown="submit" />
```

## input type="number"でeを入力できないようにする
inputタグで数字だけを入力させたいためtypeをnumberにしましたが指数表記のeも入力できてしまいます

Vue2系であれば`e`のキーコード`69`をpreventでもLintに怒られませんでしたがVue3では怒られてしまいます

``` javascript
<input type="number" @keydown.69.prevent>
```


そのため69ではなく`e`を使用するかメソッド内で数字を判定してあげる必要があります

``` javascript
<input type="number" @keydown.e.prevent>

// もしくは

<input type="number" @keydown="checkNumber">
...
methods: {
  checkNumber (input) {
    if (!input.key.match(/[0-9]/)) input.preventDefault()
  }
}
```
複数の画像投稿/表示をさせたいとき(refile使用)

refileを使用して写真投稿/表示を行う方へ。
下記の手順で実装すれば、本来1枚しか投稿・表示できないところ、
複数の写真投稿/表示ができるようになります。

###1.rifleの準備
Gemfileに下記を記入後、「bundle install」ください。

```rb:Gemfile
gem "refile", require: "refile/rails", github: 'manfe/refile'
gem "refile-mini_magick"
```

###2.テーブルの作成
複数枚の写真を投稿・表示させる場合、
「写真を表示させるモデル」と「写真を保存しておくテーブル」が必要になります。


```db/schema.rb
create_table "shops", force: :cascade do |t|
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "shop_images", force: :cascade do |t|
  t.string "shop_image_id"
  t.integer "shop_id"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end
```

###3.モデルの設定
先ほど作成したテーブルのモデルにて、それぞれの関係を1:多の関係で紐付ける必要があります。
(ここでは例として、「shopモデル」に紐づく「shop_imageモデル」で解説しています。
「shop」はあくまで例ですので、ご自身の作成されているモデル名に合わせて修正ください)

```shop.rb
 has_many :shop_images, dependent: :destroy
 #1:多の紐付け
 accepts_attachments_for :shop_images, attachment: :shop_image
 #shop_image(_id)をshopのviewにて表示させるための設定
```

```shop_image.rb
 belongs_to :shop
 #1:多の紐付け
 attachment :shop_image
 #shop_image(_id)をshopのviewにて表示させるための設定
```

###4.コントローラーの設定
shopコントローラーのnewアクション(投稿)、showアクション(一覧表示)、
ストロングパラメータをそれぞれ記述していきましょう。(※shop_imageコントローラーは作成しません)

```shops_cotroller.rb
def new
  @shop = Shop.new
  #空データを作成
  @shop.shop_images.new
  #写真の空データを作成
end

def show
  Shop.find(params[:id)
end

private
def shop_params
  params.require(:shop).permit(shop_images_shop_images: [])
  #shop_images_shop_images → shop_imagesテーブルの複数のshop_imageを指定
  #: [] → 指定した複数のshop_imageを配列に入れます(配列に入れて管理するため)
end

```

###5.ビューの記述
投稿ページ(new.html)と投稿ページ(show.html)を記述していきましょう。

```erb:app/views/shops/new.html.erb
<%= form_with model: @shop, local:true do |f| %>
<table>
  <tbody>
    <td>
　　   <%= f.attachment_field :shop_images_shop_images, multiple: true %>
    </td>
  </tbody>
</table>
<% end %>
```
↑「multiple:true」にすることで複数投稿が可能になります。

```erb:app/views/shops/show.html.erb
<table>
  <td>
  <% if @shop.shop_images.present? %>
    <% @shop.shop_images.each do |shop_image| %>
      <%= attachment_image_tag shop_image, :shop_image, :fill, 200, 200 %>
    <% end %>
  <% else %>
    <%= image_tag "no_image.jpg", size: "200x200" %>
  <% end %>
  </td>
</table>
```
↑「no_image.jpg」は別途用意していただく必要があります。
`app/assets/images`に用意頂いたデータを入れてください。
[no_image_jpg](https://upload.wikimedia.org/wikipedia/commons/thumb/0/0e/No_image.jpg/795px-No_image.jpg) (urlを開いていただいた後、右クリックでダンロードください)


###6.ブラウザ上にて複数の画像を選択する方法
複数の画像をアップロードする場合は、「ファイル選択」をクリック後、
`Shift + クリック`, または`Ctrl + クリック`で画像を選択ください。

###参考にした記事
- [refileの基本と複数画像のアップロード](https://qiita.com/nekosuke_/items/501e7ba1b286c51c1be9)
- [【Ruby on Rails】refileで複数画像のアップロード](https://qiita.com/japwork/items/dcca7ead2d3c334b124c)
イベントドリブン・ビジネスロジック・プラットフォームとしてのCouchbase


# はじめに

本稿では、Couchbaseをイベントドリブン・ビジネスロジック・プラットフォームとして利用するための機能について解説します。

# Couchbase Eventingサービス

## Couchbase Eventingサービスとは

Couchbase Eventingサービスは、Couchbase内のデータに変更が生じたときに、ユーザー定義のビジネスロジックをトリガーするための機能です。

QueryやIndexなど、その他のサービス同様、クラスターを構成する特定のノードをEventingサービスに割り当てることができます。要件に応じて、複数台の専用ノードを用いたり、他のサービスとノードを共有することができます（マルチディメンショナルスケーリング）。つまり、Couchbaseの他のサービス同様、Eventingサービスは、高可用性を持ち、高パフォーマンス、かつスケーラブルです。

Couchbaseとネイティブに統合されているため、サードパーティソリューションを必要とせず、シングルプラットフォームで、イベント駆動型のビジネスロジックを簡単に開発、展開、保守できます。

技術的には、JavaScriptを用いて、処理を実装します。

なお、Eventingサービスは、Couchbase Serverのコミュニティエディションでは提供されておらず、Enterprise Editionにのみ存在する機能です。

## Eventingサービス利用例

RDBのトリガーの様に、データベース内のデータに対して作用することもできますし、外部システムへのコールを実装することも可能です。

### データベース内の変更
- データ更新に際して、サーバサイドでリアルタイムに情報を付与する
- データ更新によって生じた不整合な状態を回避するためにデータを削除する

### 外部システムとの連携
- データ変更を他のシステムに伝達する
- 特定の情報を監視し、閾値に達した際にアクションを実行する

## その他の特徴

- ビルトイン・デバッガーを備えており、デバッグが容易

# 最後に

Couchbase Eventingサービスを活用するための、多くのブログ記事が書かれているので、参考情報として紹介します。

## 参考情報

### Couchbase Blog
[Introducing Couchbase Eventing Service](https://blog.couchbase.com/eventing/)

[Using N1QL with Couchbase Eventing Functions](https://blog.couchbase.com/using-n1ql-with-couchbase-eventing-functions/)

[Couchbase Eventing: small scripts that solve big problems](https://blog.couchbase.com/couchbase-eventing-small-scripts-that-solve-big-problems-at-scale/)

[Eventing: Notification with Couchbase Functions](https://blog.couchbase.com/eventing-notification-couchbase-functions/)

[Eventing Service Best Practices](https://blog.couchbase.com/eventing-best-practices/)

[Eventing Service Improvements in Couchbase Server 6.5](https://blog.couchbase.com/eventing-service-improvements-in-couchbase-server-6-5/)

[Eventing Improvements (Timers, Handlers, and Statistics)](https://blog.couchbase.com/eventing-improvements-timers-handlers-and-statistics/)

[Using cURL with the Eventing Service](https://blog.couchbase.com/using-curl-eventing-service-update/)

[Real Time User Deduplication with Couchbase Eventing](https://blog.couchbase.com/real-time-user-deduplication-couchbase-eventing/)


### 公式ランディングページ・ドキュメント

[EVENTING SERVICE FROM COUCHBASE](https://www.couchbase.com/products/eventing)

[Eventing Service: Fundamentals](https://docs.couchbase.com/server/current/eventing/eventing-overview.html)
PowerAutomate 「条件」のチェックボックスって何のためにあるの？？

#条件アクションのチェックボックスとは？
PowerAutomateを触るようになると、ちょっと気になるこのチェックボックス。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322371/89d38d90-a579-5ad2-219b-8912179375e6.png)

チェックの有無に関わらず、条件通りの分岐処理がされるし、公式ドキュメントには何の言及もない。フローの実行には影響はなくても、何の影響があるのか分からないのって気持ち悪いですよね。

日本語で探してもヒントが無く、、PowerAutaomteCommunityのフォーラムで質問に回答されている記事がありましたのでこの際日本語で記事を書きたいと思います！

# 結論：チェックボックスで、まとめてグループ化できる。
以上。

# 使い方
例えば、上図の条件3と条件4はまとめてOR条件にしたいとき、
条件3と条件4にチェックを入れます。
すると、条件式右の「・・・」をクリックして表示したサブメニューに「グループの作成」というメニューが活性化された状態で出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322371/cc815372-0f65-b16d-fbc1-987543377e91.png)

「グループの作成」をクリックすると、チェックを入れた条件がグループ化される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322371/f639f911-40a3-0f5c-61a9-3852685c8c70.png)

あとは、条件を変更するだけ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322371/2c902e89-81a0-09c2-225c-e26c0867203b.png)

以上、PowerAutaomte「条件」にあるチェックボックスの存在意義でした。

###参考ページ
参考にしたPowerAutaomteCommunity フォーラムで質疑応答されていたページのリンクを張っておきます。
【Checboxes in the Condition action】
https://powerusers.microsoft.com/t5/General-Power-Automate/checkboxes-in-the-Condition-action/m-p/338253#
【React Native】icomoonのフォントを使う（通常の外部ttfを使う時のやり方も記載）【Expoなし】

icomoonは[react-native-vector-icons](https://github.com/oblador/react-native-vector-icons)にサポート内容が書いてあるけど、そもそも react-native-vector-icons
がちゃんと使える前提っていうね…  
当たり前のようにreact-nativeのバージョンとの相性でlinkコマンドやったりpodやった結果エラーで使えないとかあって情報が錯綜してる気がするので現段階のをまとめておく。(まぁ、こういう放置系の文化のおかげでFlutterの方が〜とか言われる原因の1つな気がする)  

環境は下記

```
react-native-cli: 2.0.1
react-native: 0.63.4
react-native-vector-icons: "^8.0.0"
```

## react-native-vector-icons を使える状態にする

なんで、こういう事書いてるか？って言うと、iosで「react-native link」やれとか、「いや、npx pod-install(cd ios & pod install)」とか書いてあるけど自分の環境下ではエラー起きてbuildでこけるから。（前提と同じ事書いてるけど、本当こういう文化なくした方がいいと思うんだけどなぁ）  

1. プロジェクト下に移動したらreact-native-vector-iconsをinstallする(npmやnpx使い人は置き換えてください)

```
yarn add react-native-vector-icons
```
1. podinstallをする(react-native linkはreact-nativeのバージョンが60以上なのでしない、やったら怒られる)
 
```
npx pod-install
```
1. ios下の「自分のプロジェクト名.xcodeproj」をXcodeで開く
1. 左側ペインのファイルディレクトリ一覧のところにResoucesフォルダがあって.ttfファイルが入っているか確認する
1. 左側ペインの一番上の自分のプロジェクトをクリック
1. メインペインの上部のBuild PhasesをクリックしてCopy Bundle Resourcesのicomoon.ttf以外のfontを「-」を押して消す(じゃないとビルドできない)
![スクリーンショット 2021-01-19 18.41.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043424/c9f12a22-44bb-5836-b01d-2e45c349ac63.png)

これでやっと「react-native run-ios」みたいなコマンドでbuildできるようになる。

## icomoonのttfを使えるようにする

1. とりあえずicomoonからDLしたら、zipファイルを解凍する
1. ios下の「自分のプロジェクト名.xcodeproj」をXcodeで開く
1. 左側ペインのファイルディレクトリ一覧のところにResoucesフォルダに1で解凍した中にあるttfをドラッグする
1. Copy itemps neededと Create groups と Add to targetにチェックがついてるか確認して追加
![スクリーンショット 2021-01-19 18.40.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043424/06a91d5f-6b49-282d-fada-57779b501b46.png)
1. Xcodeを閉じたら、App.jsのあるルートディレクトリ/ios/プロジェクト名/Info.plistファイルをエディタで開いてicomoon.ttfファイル名を追記する(react-native-vector-iconsの設定コピって真似すればOK)
1. App.jsと同じディレクトリに1で解凍した**selection.js**を配置する
1. iconを使いたいファイルを以下のようにしてiconが出れば成功(その際、icon-というプレフィックスはいらない)

```
import { createIconSetFromIcoMoon } from 'react-native-vector-icons'
import icoMoonConfig from '../selection.json'
const IcoMoon = createIconSetFromIcoMoon(icoMoonConfig)
/*~略~*/
  render() {
    return (
       <IcoMoon name='search' size={30} />
       <IcoMoon name='qr' size={30} />
       )
    }
```

## GoogleとかでDLできるフリーのfontを使う場合

1. App.jsと同じディレクトリ配下に /assets/fonts を作ってそこに ttfなどを入れる
1. App.jsと同じディレクトリ配下に react-native.config をなければ作る
1. react-native.configにassetsのキーを追加

```
module.exports = {
    assets: ['./assets/fonts/'],
};
```

1. react-native link をする
1. CSSでfontFamilyを指定すれば使えるようになる

```
<Text style={{fontFamily:'PixelMplus10 Regular'}}>TEST</Text>
```

これで上手く反映されない時は手動で追加する

1. ./ios/PROJECT_NAME.xcodeproj をXcodeで開く
1. .ttfファイルがResoucesフォルダに入っているか確認すること
1. Info.plistファイルに.ttfファイル名が書かれていること(プロジェクト名下のだけでOK)
1. Build Phases→Copy Bubdle Resourcesに.ttfファイル名が含まれていること
未経験・初心者のエンジニアがまず意識したほうが良いこと

## はじめに
初めまして。hiroseです。
ほぼプログラミング未経験でIT企業に入社してもうすぐ2年が経とうとしているエンジニアです。
この記事は、私が入社してから**重要に感じたこと**をまとめた記事になります。
同じように業界未経験や入社1年目の方々のお役に立てればと思います。

## 質問の仕方を考える
### 丸投げしない
質問は**分からないことの答えをそのまま教えてもらう**ためにするものではなく、
**問題を解決するための手助けを得る**ためにするものです。
必ず**自分の考え**を質問に付け加えるようにしましょう。  

何かを決めなければいけないときに自分だけの判断だけでは決めきれないというのは新人にとってよくあることです。
そんな時に先輩社員に質問をするときに
> AとBどちらにするか迷っているのですが、どちらが良いと思いますか？

と質問するのはNGです。

> AとBだとAの方がいいと思うのですが、どう思いますか？Aの方がいいと思う理由は…

というように理由もつけて自分の考えを相手に提示しましょう。

### 状況を報告する
技術的な質問をする際にはピンポイントで分からないことを聞くのではなく、
**自分の状況**も併せて報告するようにしましょう。  
具体的には

- 実現させたいこと
- 試したこと
- エラーが出ている箇所とエラーコード

等を質問に付け加えるようにします。
特にエラーに関してはまずは一度自分でしっかり調べることも大切です。
自分で調べてもわからなく、限界に到達した際に上記のポイントをまとめて質問をしましょう。


## 公式ドキュメントを読む
プログラミングで分からないものについて調べていると、Qiitaやその他技術的な記事をまとめているサイトがよくヒットします。
しかし、これらの記事を読む前に**必ずまず初めに公式ドキュメント**を読みましょう。
何故なら人が書いた記事というのは**例外なく公式ドキュメントに書かれてあることを基に書かれている**からです。
また、公式でないものに関しては**そこに書かれていることが100%正しいと言える保証がありません。**

プログラミングをしていく上で**分からないものを調べる力**は非常に重要です。
そして技術的な問題の答えのほとんどは公式ドキュメントに書いてあります。
読みやすいから、という理由で人が書いた記事に答えを求めているといつまでも調べる力が身に付きません。
 

プログラミングには英語のドキュメントしかないことも少なくなく、それらを読む訓練にもなります。
問題を解決する際は、まずは公式ドキュメントを読みましょう。

## コードを読むコツ
コードを読むときのコツは理解したい部分だけ読むのではなく、**視野を広げてコードを読む**ことです。  
具体的に言うと

- 理解したいコードが参照されているところ
- 他クラス、他メソッドとの関係性
- クラス名、メソッド名、変数名などの命名部分

等のこと指します。
書かれているコードにはプログラム上で必ず何かしらの役割があります。
その役割は何なのだろう、というのを意識しながら常にコードの全体像を把握するようにしてコードを読んでいくと、理解するまでのスピードが早まります。

## コメントを書く
コメントは**自分の為にも相手の為にも**書くようにしましょう。
自分が書いたコードというのは誰かが読むことになるし、もちろん誰かが書いたコードを自分が読むこともあります。
そんな時にコードにコメントが書かれているか書かれていないかでコードを読解するまでにかかる時間は大きく変わってきます。
自分が書いたコードですら、時間が経ってから読み返してみると他人が書いたコードの様に見えることもあります。

しかし、コメントは愚直に全てのコードに書けばいいというわけではありません。
わざわざ書かなくてもいいコメントを書いてもコードの可読性が落ちるだけです。
コメントを書くコツは**コードにはない情報を付け足す**ことです。  
例えば


```
// 名前を代入する
string name = "hoge";

// 年齢を代入する
int age = 24;
```


上記のように、コードを読めばわかることにわざわざコメントでコードの説明をするのはNGです。
なので


```
// 名前の最大文字数は10文字
string name = "hoge";

int age = 24;
```


等のように、コードだけでは読めない情報を付け加えてあげるようにしましょう。


## 最後に
エンジニアは日々学ぶことが多い職業です。
先輩から教えてもらった知識、自分で調べて見つけた知識など、得た知識は自分の言葉でまとめ直してメモしておくと物事の再理解に繋がります。
そしていつか、こういう記事を書こうと思った時のネタにもなるので、メモはしっかりしておきましょう。
VSCode でGrepした時、UTF8だけではなくSJISもヒットする拡張機能作ってみた

Visual Studio Code でファイルGrepした時に、UTF8だけではなくShift-JISも同時にGrepできる拡張機能を作ってみた。  
なお、Windows専用。  

VSCodeの拡張機能のところで「rg-sjis」と検索すると見つかります。

https://marketplace.visualstudio.com/items?itemName=komiyamma.rg-sjis
PHPで個人開発してたら、PHPがめっちゃ叩かれてて辛みだった話

この記事は [Railsで個人開発してたら、Railsがめっちゃ叩かれて辛みだった話](https://qiita.com/damuha/items/907f1ec24fa0ca96d3ff) のエイリアスです。