OmniAICreator/Qiita-1.07M huggingface dataset:
Emotionはいいぞ

# はじめに

実際にEmotionを使ってみて、良かったと感じた部分をまとめて、Emotionを布教できればなーと思い記事を書いて見ました。
この記事を読んで、Emotionを導入する際の参考にしてもらえればありがたいです！

Emotionの導入・使い方についてはこちらで解説しているので、是非参考にしてください。
[Next.js 10 + TypeScriptでEmotionを使う](https://qiita.com/282Haniwa/items/243f00c39ee7c992d7f7)
[Emotionを使いこなす](https://qiita.com/282Haniwa/items/7248bed02a1b5b66579f)

# Emotionの良い点

## 全てのクラス名が一意になっている

全てのクラス名に一意なhashが付加されているので、ほかのCSSライブラリやグローバルなクラス名と衝突する心配がありません。

開発時はcssをバインドした変数名やコンポーネント名などがクラス名の後ろに付加されて、`css-11z9jyx-hoge`のようなクラス名になり、本番ビルドでは`css-11z9jyx`のようなクラス名になります。

CSS設計を行う上で、クラス名がかぶらないようにというのを様々な方法で実現しようとすると思いますが、Emotionを使っていればクラス名がかぶることがないので安心して使うことができます。

参考: https://emotion.sh/docs/@emotion/babel-plugin#labelformat


## CSSがどこで生成されたのかわかりやすい

コンポーネント名や変数名が生成されるクラス名に含まれているので、どこで生成されたCSSかが分かりやすくなっています。
設定によってはファイル名やディレクトリなどもクラス名に含むようにできるので、必要に応じて設定することもできます。

全てのCSSが一意であるという特徴と合わさって、一意かつ分かりやすいクラス名が生成されているのでデバッグのしやすさとバグの起こりにくさが両立されていると思います。


## CSSの定義する順番に左右されずに実装できる

場合によってはCSSに記述順や詳細どなどによってどれが適応されるか考慮しなければならないことがあります。
しかし、Emotionでは全て一つのクラスとしてCSSの内容を合成して生成するので、クラスの定義順などには左右されずに、そのcssテンプレート文字列の中の記述の順番だけを気にすれば良くなります。

このリンクの解説でとてもわかりやすく解説してあるので、読んでみてください。

参考: https://emotion.sh/docs/composition


## TypeScriptで補完が効く

css modulesだと、importしたら型が

```ts
const styles: {
    readonly [key: string]: string;
}
```

になっていて何のクラスがあるのかわかりません。

Emotionだと変数にバインドするだけなので、こう言った問題は起こりません。

```ts
const classes = {
  hoge: css``,
  fuga: (theme: Theme) => css``,
}
```

```ts
const classes: {
    hoge: SerializedStyles;
    fuga: (theme: Theme) => SerializedStyles;
}
```

CSSを定義したファイルと行き来しながらクラス名をコピペしてみたいな作業をしなくてよく、VSCodeの定義にジャンプできる機能なども使えるので、作業の効率が上がります。


## 過去のCSSをコピペで流用できる

CSS in JSのライブラリではオブジェクト形式で記述するものもありますが、Emotionはテンプレート文字列にCSSをかけるので、コピペで流用することができます。

css modulesからの移行をする場合でも、ある程度コピペするだけで移行できるので、オブジェクトに修正しないといけないCSS in JSライブラリなどと比較すると簡単にできます。


# Emotionの良くない点

## SSRの環境での導入の仕方が少しややこしい

SSR環境に導入する方法が少しややこしいです。

また、Emotionを使っている記事などがまだそこまで多くないので、少しニッチな使い方をしたい場合に検索しても出てこない場合があります。

Next.jsをTypeScriptで使ってそこにEmotionを入れるというだけでも日本語の記事はほとんどないので、導入やトラブルシューティングには少し苦労するかもしれません。


## どんどんアップデートされていく

これは良い点でもあるのですが、どんどんアップデートされて改善されていきます。

Emotion 10からEmotion 11に上がったときに導入の方法も少し変わっていたりするので、数ヶ月時間が経つだけでいろいろ変わってベストプラクティスも変わってくる可能性があります。

当然その分使いやすくなっていたりはするのですが、古い記事などを参考にできなくなって行ったりするので自分で調べたりドキュメントを読む能力が必要になるかもしれません。


# まとめ

最後まで読んでいただきありがとうございます。

いかがだったでしょうか？
個人的にはEmotionをとても気に入っているので、皆さんにも是非導入して試してもらいたいと思いました！
【kintone】テーブルの中で入力禁止にしたりフィールドにエラー出したりの小技

こんにちは！
おそらく私の今年のkintone記事マラソンは今日でおしまい✨

今年最後はテーブルのフィールドのちょっとした小技を書いて終わりたいと思います。
devnetをサーフィン（？）していてチラホラ見かけた質問たちが元ネタであります・・・。
答えを書いてしまいたいけど、コミュニティの方針上、答えをズバリと書くのは良くない気がしていまして。
「困った」で検索して引っかかるような、ちょっとしたTips記事になったら良いなぁみたいな淡い欲望から書いております。

# アプリの準備

アプリストアから「案件管理」をサンプルデータ込みで追加して試してね。
活動履歴テーブルを使うよ！

# テーブルのフィールド編集不可にする

1行目のメモ（フィールドコードは**文字列__1行__2**）を編集不可にするにはこう。
**disabled**を**true**にすると編集不可になります。

```js
//編集不可
kintone.events.on(['app.record.edit.show', 'app.record.create.show'], event => {
    //1行目
    event.record.活動履歴.value[0].value.文字列__1行__2.disabled=true;
    return event;
}); 
```


全部の行のメモを編集不可にするには、こう（1例です）！

```js
kintone.events.on(['app.record.edit.show', 'app.record.create.show'], event => {
    //全行
    event.record.活動履歴.value.forEach(v =>{
        v.value.文字列__1行__2.disabled=true;
    });
    return event;
}); 
```

# テーブルのフィールドに値が入っていたらエラーを出す

保存のタイミングで値が入っていたらエラーをだしたいという場合はこんな感じ。
**error**になにか文字列を入れておきましょう。

```js
//入力禁止
kintone.events.on(['app.record.edit.submit','app.record.create.submit'],event=>{
    //2行目
    if(!!event.record.活動履歴.value[1].value.文字列__1行__2.value){
        event.record.活動履歴.value[1].value.文字列__1行__2.error = "入力禁止";
    }
    return event;
})
```

全部の行チェックしたい場合はこう！

```js
kintone.events.on(['app.record.edit.submit','app.record.create.submit'],event=>{
    //全行
    event.record.活動履歴.value.forEach(v =>{
       if(!!v.value.文字列__1行__2.value){
           v.value.文字列__1行__2.error = "入力禁止";
        }
    });    
    return event;
}) 
```

# そしてこうなった！

八方塞がり＼(^o^)／

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/3a2a72f3-9548-ad6f-2b97-665f6884db1c.png)

# まとめ
使い方を誤ってしまい、何もできないアプリを作り出してしまった・・・。
JavaScript使うときはこんな事にならないように気をつけましょうっ！

というわけで、良いお年をお過ごしください＾０＾！
ceres-solver を android ndk　でビルドする備忘録

## 概要
android ndk で ceres-solver をビルドし、liceres.a が欲しかった。
それをアプリケーションにnative build する際にリンクさせる必要があった。


## ceres-solver バージョン確認
ceres-solver は様々なバージョンがあり、1.12, 1.13, 1.14, 2.0 系などがある。
それらをgithubのリリースから取得。


## ndk バージョン確認
ndk download と検索し、ndk(現在は22系が正式サポートされていた)
の必要なバージョンをダウンロードする。（前のバージョンはunsupported versionみたいなところから取得できる。現在は10e系が一番古いものとしてアーカイブに残っていた。）


## ceres-solver のバージョンと　ndkのバージョンを確認
試した感じでは、

ceres 1.12 系は、ndk 10系あたりだとビルドできた。
ceres 1.14 系は、ndk 14系あたりだとビルドできた。 
ceres 2 系は、   ndk 21系あたりでビルドできた。

## 使うアンドロイドデバイスのプロセッサーを確認
たとえば自分のデバイスは `arm64-v8a` が必要、など。

## 依存関係であるEigenをインストール
インストールしておく。

## ビルド時のプロセッサーを指定
上で確認した自身のつかうABIを、
ceres-solver の jni の中にある　`Application.mk` ファイルの最後の、
`APP_ABI`にて指定する。

これをやっておかないと、そもそも所望のABI用のビルドが行われないので、
それの結果をandroid studio から読ませようとすると、
`file wrong format` 的なエラーが出る。

## ndk-build を使ってビルド
ceres-solver のjniフォルダに入ったあと、

`EIGEN_PATH=/usr/local/include/eigen3 ~/Downloads/android-n
dk-r10e/ndk-build`

などのコマンド　
第一引数はeigenへのパス、
第二引数はndkフォルダの中のndk-build へのパス

を書き、実行する。


これを実行することにより、
objフォルダ内にABI名のフォルダが作られ、
その中にビルドされた`libceres.a`バイナリが作られているはず。


## ceres-solver-2.0.0 

`mkdir build && cd build` 

```
cmake -DCMAKE_TOOL_CHAIN_FILE=~/pathto/android-ndk-r18b/build/cmake/android.toolchain.cmake -DANDROID_ABI="arm64-v8a" -DANDROID_STL=c++_static  -DCMAKE_BUILD_TYPE=Release -DANDROID_NATIVE_API_LEVEL=android-24 -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_JAVA=OFF -DBUILD_ANDROID_EXAMPLES=OFF -DWITH_CUDA=OFF  -DENABLE_PRECOMPILED_HEADERS=OFF ..
```

## static が欲しい

`DBUILD_SHARED_LIBS=OFF`
をcmake のフラッグとして追加。


## 最終備忘

`Application.mk`

```
APP_BUILD_SCRIPT := $(call my-dir)/Android.mk
APP_PROJECT_PATH := $(call my-dir)

APP_CPPFLAGS += -fno-exceptions
#APP_CPPFLAGS += -fno-rtti
APP_OPTIM := release

APP_CPPFLAGS += -frtti
# Use libc++ from LLVM. It is a modern BSD licensed implementation of
# the standard C++ library.
APP_STL := c++_static
APP_ABI := arm64-v8a
```

そして `assert_ndk_version.sh` を無効化。

ceres-solver-1.12.0 を使い、

```
EIGEN_PATH=/usr/local/include/eigen3 ~/Downloads/android-ndk-r21d/ndk-build
```

にてビルド。
面倒な正規表現をRubularで簡単に

\![macOS-11.1](<https://img.shields.io/badge/macOS-11.1-brightgreen>) \![ruby-2.7.2p137](<https://img.shields.io/badge/ruby-2.7.2p137-brightgreen>) 


# Preface (はじめに)

欲しい文字列を抽出するのに便利なのが正規表現です.
しかし, 正規表現は覚えることが多く, 使いこなすのに手間がかかります.

そこで[Rubular](https://rubular.com/)というRubyでの正規表現をしてくれるツールが役に立ちます.


# rubular

上の`Your regular expressio`は正規表現を入れる場所.

左の`Your test string`は文字列を, 右には抽出結果が出ます. 

下にある`Regex quick reference`は正規表現の例がいくつか示されています. 


## Example

まず, test文字列

```
#+qiita_id: helloworld
```

を`Your test string`に入れます.

では, ここからhelloworldを取り出します.
`Your regular expression`に

```
(.+)
```

と打ちます.これはany single character を表す`.`と, 一つ以上を表す`+`を, 
capture everything enclosed を表す`(...)`で囲います.すると, 

-   Match result:

```
#+qiita_id: helloworld
```

-   Match groups:

```
1. #+qiita_id: helloworld
```

helloworldだけを取り出すには

```
:(.+)
```

で, できます.

正規表現はなかなか覚えにくいので, rubular をつかって以後も学んでいこうと思います.


# 参考資料

[チャート式ruby-appendix-V(rubular)](https://qiita.com/daddygongon/items/5e2de543400fe56be768)
毎日空を眺めませんか？

[Aizu Advent Calendar2020](https://adventar.org/calendars/5037)の11日目の記事です。
知り合い([@flying_hato_bus](https://twitter.com/flying_hato_bus))や知り合い([nozomoto](https://adventar.org/users/30623))からの熱い誘いがあったのでもう年末ですがしれっと書いてみました。何気にAdvent Calendarは初です。

ちなみにサラッと書いた技術の無駄使いの宣伝なのでそこまで期待せずに覗いてもらえれば幸いです苦笑

# TL;DR
https://wancom.github.io/Cloud/
（自分の気が変わって公開終了していなければ）多分ここを見るとこんな感じの雲の写真が現れます。正確には写真ではなく生成された画像ですね。
![latest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155364/5fe94a78-ee75-b4ef-929e-8245637f0b26.png)

# 使用している技術
使っている主な技術はパーリンノイズとBMP、GithubActionsです。
（他にももちろんC言語やImageMagickなとがありますが割愛します）

## パーリンノイズ
パーリンノイズは自然な感じのノイズを生成するアルゴリズムです。
ただの乱数だと飛び飛びで不自然な感じになりますが、パーリンノイズは「事前に計算された勾配に従って内挿を行い、時間的/空間的に擬似乱数的に変化する値を生成する」ことで滑らかで自然な雰囲気を持たせることができます。
https://ja.wikipedia.org/wiki/パーリンノイズ

説明できるほど深くまで理解はできていませんがイメージとしては一定間隔に乱数で山や谷を作り、その間の値を計算で滑らかに埋めるという感じです。

```
      __
     /  \__    /
    /      \__/
   /          
__/

**~~~~**~~~~**~~....
*が乱数で生成した値
~が*の間を埋める値
```
(アスキーアートにしたらわかりやすくなるかと思いましたが、むしろ余計にわかりにくくなってしまった気が…)

実際のコードはこちらです。自分で理解するために書いたこともあり、あまり綺麗なコードではありません…
https://github.com/wancom/Cloud/blob/master/cloud.c

## BMP(Microsoft Windows Bitmap Image)
BMPは主にWindowsで使用されている画像の保存形式です。
https://ja.wikipedia.org/wiki/Windows_bitmap

圧縮せずに1ピクセルずつ情報を保持しているため構造はシンプルです。
あまり何も考えずに作ったためとりあえず簡単に生成できるということで使用しました。
なお生成した画像はその後ImageMagickを使ってPNGに変換しています。

BMPファイルの生成には下記のサイトを参考にしました。
https://www.ruche-home.net/program/bmp/struct

## GitHub Actions
実は毎日雲の画像を生成しているため上記のサイトは毎日雲の画像が変化します。これはGitHub Actionsを使用して実現しています。

GitHub Actionsは通常特定ブランチにマージされた時をトリガーにしてデプロイするなどに使いますが、cronのように指定した日時をトリガーにして何かをするということもできます。
今回は毎朝7時に雲の画像を生成し、リポジトリに追加してPushするという操作を自動で行うようにしています。

（ちなみに余談ですが、GitHub Actionsで実行されるコンテナーにはデフォルトで様々なツールがインストールされていますが、まさかImageMagickのconvertコマンドも入っているとは思いませんでした。）

# コードはこちら
https://github.com/wancom/Cloud
雲のWebページはこちら
https://wancom.github.io/Cloud/

# あとがき
今回は完全に思いつきでチャチャっと作ったものなのでこんなこともできるんだなと感じていただければ幸いです…
そのうち気が向いたら曇りや夕方も再現してみようかなと思います。
RaspberryPiのバックアップ

#1.この記事の内容

Raspberry Piに使用しているSDカードのバックアップ方法について，

* SDカードを丸ごとバックアップする方法
* 必要な領域のみをバックアップする方法【T.B.D】  
※Windows Subsystem for Linux環境での実施を予定していましたが，LinuxファイルシステムをWSL上で読み込めない問題を解決できず保留です．Linux PCで実施すれば，[Raspberry Piを極限まで無駄なくバックアップする](https://dev.classmethod.jp/articles/raspberry-pi-dump-and-restore/) に記載の手順でバックアップできると思います．

の~~2つ~~1つの方法を紹介します．
参考にさせていただいた記事はページ下部の関連リンクに記載しています．

|方法|メリット|デメリット|
|---|---|---|
|SDカードを丸ごとバックアップする方法|バックアップの方法が容易|SDカード内の空き領域に対する読み書きが生じ，バックアップ時間及びストレージの効率が悪い．また，SDカードの寿命が縮みやすい|
|必要な領域のみをバックアップする方法|バックアップ時間及びストレージ効率が良い|バックアップ手順が少し複雑（ファイルシステムの基礎知識が必要）|


##1-1.使用環境

###1-1-1.SDカードを丸ごとバックアップする方法

* Windows 10
* バックアップツール：Win32 Disc Imager

###1-1-2.必要な領域のみをバックアップする方法

* Windows 10 (Windows Subsystem for Linux; Ubuntu 20.04)
* バックアップツール：Linux標準コマンド（parted, dd, dump）

#2.背景

Raspberry Piで作業する際のバックアップ方法を記事に残しておこうと思い立ちました．
HDDやSSDが安価になってきているので，32GB/64GB程度のサイズなら，丸ごとバックアップしても困ることはないと思いますが，空き領域をバックアップ対象にすべきではない為，必要な領域のみのバックアップ方法も紹介します．

#3.SDカードバックアップ

##3-1.SDカードを丸ごとバックアップする方法

1. Win32 Disc Imager を起動する
2. SDカードをPCに挿入する
3. バックアップするファイル名を指定する（ImageFile欄）
4. 「Read」ボタンをクリックしてバックアップを実行する

バックアップしたファイルをSDカードに書き込む際は，「Write」ボタンをクリックする．

##3-2.必要な領域のみをバックアップする方法

1. バックアップするSDカードをマウントする  
※SDカードがドライブで認識されたかは都度確認する
<pre>
$ sudo mkdir /mnt/d
$ sudo mount drvfs D: /mnt/d
</pre>
★ドライブのマウントはできるが/bootしか見えないので，断念．

#3.さいごに

Raspberry Piに使用しているSDカードのバックアップ方法を紹介しました．
手元にLinux環境がなく，WSLでのバックアップを試みましたが，調査したもののLinuxファイルシステムが見えない問題が解決できずに断念しました．
解決策が見つかる，または，Linux環境を整えたら，必要な領域のみをバックアップする方法についても追記したいと思います．

調査した内容（記事）は関連リンクに貼っています．

#4.関連リンク

* [【Raspberry Pi】SDカードを丸ごとバックアップする方法について](https://raspi.taneyats.com/entry/backup-sdcard)
* [Raspberry Piを極限まで無駄なくバックアップする](https://dev.classmethod.jp/articles/raspberry-pi-dump-and-restore/)
* [Win32 Disk Imager](https://sourceforge.net/projects/win32diskimager/)
* [WSLでSDカードをマウントする](https://qiita.com/moyashimanjyu/items/baf6ebcdd69b24bb5cf2)
* [Windows subsystem for linuxで始める組み込みlinux ラズパイ3のブートイメージを作ってみる](https://www.slideshare.net/ToyohikoKomatsu/windows-subsystem-for-linuxlinux-3-72589938)
* [[Ubuntu]Raspberry Pi Zeroのバックアップ](https://tech-drive.net/raspi/2018070701/)
* [WSL 2（Ubuntu20.04）にアップデートしてRaspberry Piのイメージをmountする](https://gan0803.dev/2020-06-06-wsl2-and-mount-raspbian-image/)
* [Windowsで読めないext4ファイルシステムを「WSL 2」経由でマウント～ Dev版「Windows 10」Build 20211が公開](https://forest.watch.impress.co.jp/docs/news/1276395.html)
* [WSL 2 (プレビュー) で Linux ディスクのマウントを開始する](https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-mount-disk)
改行させない方法

よくtableタグやulタグを使って表やリストを使った時変なところで折り返してしまう時ってありませんか？
今回はそんな時の対処法をご紹介します。

CSSでwhite-space:no-wrap;を使う。
これは改行させたくないクラスに対して付け加えるだけで改行されません。

簡単でしたね。
roman_numerals



# 問題

アラビア数字(arabic numerals)を受け取って，ローマ数字(roman numerals)を返すmethodを書きなさい.


# code

```ruby
def roman_numerals(input)
    numerals = [[1000,"M"],[900,"CM"],[500,"D"],[400,"CD"],[100,"C"],
    [90,"XC"],[50,"L"],[40,"XL"],[10,"X"],[9,"IX"],[5,"V"],[4,"IV"],[1,"I"]]

    result = ""

    numerals.each do |arabic,roman|
	while input >= arabic do
	    result += roman
	    input -= arabic
	end
    end

return result
end

input = ARGV[0].to_i
print "#{input} : #{roman_numerals(input)}\n"
```


# 簡単な解説

以下のサイトを参考にrubyで書いた.

<https://eigo.rumisunheart.com/2018/03/30/integer-to-roman-number-converter/>


# メソッド内の宣言

まず,アラビア数字とローマ数字の対応表を配列(numerals)にする.変換したあとの結果(result)を宣言しておく.このresultにローマ数字を足していく形で答えを作る.


# while内でアラビア数字をローマ数字に

入力されたアラビア数字(input)から対応表の配列(numerals)のアラビア数字部分(arabic)を引いていき、引いたアラビア数字に対応するローマ数字(roman)を結果(result)に足す.


# 結果

    1 : I
    2 : II
    4 : IV
    5 : V
    6 : VI
    9 : IX
    10 : X
    11 : XI
    14 : XIV
    15 : XV
    19 : XIX
    38 : XXXVIII
    42 : XLII
    49 : XLIX
    51 : LI
    97 : XCVII
    99 : XCIX
    439 : CDXXXIX
    483 : CDLXXXIII
    499 : CDXCIX
    732 : DCCXXXII
    961 : CMLXI
    999 : CMXCIX
    1999 : MCMXCIX



------
 - **source** ~/grad_members_20f/members/yoshida/roman_numerals.org
オートメーションに直に処理を書き込むんじゃあないッ

ショートカットのオートメーションは、リンク作れない(→共有できない)・複製できないという仕様なっているため、

- 同じような処理を複数のオートメーションで使いたい
- 作ったオートメーションを他人に共有したい

といった場合に**大変不便です。**

# 解決策
**通常のショートカットに処理を書いてオートメーション内で`[ショートカットを実行]`**です。つまり、

![通常のショートカット](https://user-images.githubusercontent.com/66032272/103321328-72f52280-4a7c-11eb-82b6-b231a3ea9d7e.jpeg)
こうして↑ こう↓~~(語彙力)~~
![オートメーションの編集画面](https://user-images.githubusercontent.com/66032272/103321315-640e7000-4a7c-11eb-8d59-27e9aa28a9d3.jpeg)

# おまけ
ショートカットを流用するがオートメーションごとに処理を分けたい場合
例)`Appを開く`で開くアプリに対応した通知を出す

ショートカット側: `{ショートカットの入力}`を使う
![image](https://user-images.githubusercontent.com/66032272/103330114-6f28c680-4aa3-11eb-9693-ab144634f3c1.jpeg)

オートメーション側: ショートカットに変数を渡す
![image](https://user-images.githubusercontent.com/66032272/103331390-12300f00-4aa9-11eb-86c0-aa974c34e427.jpeg)

※ オートメーションの処理部分は「どのAppを開いたときか」を取得できる手段がありません。手動で記述する必要があります。
25日目：Room設定変更

この記事はアドベントカレンダー「[【VCI】テーマパーク を作ろう](https://adventar.org/calendars/5855) 」の25日目の記事です。

# 今回の内容
テーマパークもいろいろとものも充実してきたため、今回は他の人もテーマパークに来れるようにRoomの設定を変更したいとおもいます　
今回は以下の内容を学びます。
　・Roomの設定の内容

# はじめに
アドベントカレンダー執筆中にルームが3つ公開可能になりました。
手順については1つの場合と特に変わりません

# Roomの設定変更
Roomの設定については以下の公式ページにのっています。正直これらページ見れば十分理解できると思います。
　・[ルーム情報の設定](https://virtualcast.jp/wiki/virtualcast/room/release/make/config)
　・[ユーザー種別](https://virtualcast.jp/wiki/virtualcast/room/authority)
　
上記ページを参考に今回は以下のように設定を変更しました。
<img width=90% src="https://cdn.discordapp.com/attachments/407897870876934147/793691039449612288/unknown.png">

代替上記ページの説明どおりですが、数点今回のポイントとしたところをピックアップします。

**ルーム公開**
　限定公開と公開を選べます。不特定たくさんの人に来てほしい場合は公開にしましょう。

**オーディオの減衰**
　最小が何mから減衰が始まるか、最大が何mで聞こえなくなるかです。
　今回は遊園地が広いため大きめの値に設定してます。
　企画背景などで特に減衰が必要ない場合は両方大きい値を設定しておけば大丈夫だと思います。

**VCI生成権限**
　どの権限レベルのユーザーまでVCIを出すことが出来るかの設定です。
　後述の設定で部屋を出た際にVCIが消えるように出来るため、ここは広めにとってる人が多い気がします。
　今回は遊園地に合うものを自由に置けるようにするため、シードユーザーにしています。

**ルーム入室の可能範囲**
　どの権限のユーザーまで入室できるかです。
　今回はシードユーザーにしました。ここは別に全ユーザーでも良かった気がします。

**ルームのロケーションを変更できるユーザー**
　どの権限のユーザーまでロケーション(背景)を変更できるかの設定です
　基本的には何らかの意図を持って背景VCIを作りロケーションに設定しているため、
　「管理者」か「オーナー」にすることが多いと思います。

**オーナー・管理者以外のアイテムの寿命**
　ユーザーが出したVCIがそのユーザーが退出時に消えるかどうかの設定です。
　今回は遊園地に合うものを自由に置いていけるようにするため広めに「ルームに紐づく」設定してますが、
　基本的には「参加者に紐づく」にしてあまり散らからないようにしているルームが多いのではないかと思います。

これで保存をすれば完了です。

# 終わりに 
これでテーマパークをつくろうのアドベントカレンダーは終了となります。いかがでしたでしょうか？
いままで学んだことをまとめると以下のようなります。
<img width=50% src="https://media.discordapp.net/attachments/407897870876934147/793708732332376064/unknown.png">

まだまだここで紹介していない難しめの技術などはありますが、基本的な部分は網羅できたのではないでしょうか。

最後に、アドベントカレンダーはこれでおしまいですが、今回作成したテーマパークは今後もどんどん拡張していく予定です。
もしよかったら是非VirtualCastへ見に来てください。
<img width=100% src="https://cdn.discordapp.com/attachments/407897870876934147/793709309732847636/2020123012171869.jpg">
新型コロナウイルス接触確認アプリのダウンロード数と陽性登録件数をスクレイピング

厚生労働省の[新型コロナウイルス接触確認アプリ（COCOA) COVID-19 Contact-Confirming Application](https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/cocoa_00138.html)のグラフの左上のダウンロード数と陽性登録件数をスクレイピング

![index.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36151/f77fda68-f706-dfc8-7d0c-f273dee00b7f.png)

# プログラム

## スクレイピング
```python
import base64

import requests
from bs4 import BeautifulSoup

url = "https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/cocoa_00138.html"

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"
}

r = requests.get(url, headers=headers)
r.raise_for_status()

soup = BeautifulSoup(r.content, "lxml")

img_tag = soup.select('div.m-grid > div.m-grid__col1 > img[src^="data:image/png;base64"]')[1]

img_b64 = img_tag.get("src").replace("data:image/png;base64,", "")

png = base64.b64decode(img_b64)
```

## 画像を保存
```python
import pathlib
import datetime

# 今日の日付
dt_today = datetime.date.today()

# 日付を文字列に変換
s_today = dt_today.strftime("%Y%m%d")

# PATH指定
p = pathlib.Path(f"../getIMG_pool/cocoa_info{s_today}.png")
p.parent.mkdir(parents=True, exist_ok=True)

with p.open(mode="wb") as fw:
    fw.write(png)

png_path = str(p)
```

## OCR
### tesseract-ocrをインストール
```
!add-apt-repository ppa:alex-p/tesseract-ocr -y
!apt update
!apt install tesseract-ocr
!apt install libtesseract-dev
!tesseract -v

!apt install tesseract-ocr-jpn  tesseract-ocr-jpn-vert
!apt install tesseract-ocr-script-jpan tesseract-ocr-script-jpan-vert
!tesseract --list-langs
!pip install pytesseract
```

![index2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36151/20b070b4-9b59-640c-2860-676fdec0f2be.png)

### 画像から文字を抽出
```python
import pytesseract

import cv2
import numpy as np

from google.colab.patches import cv2_imshow

# グレースケールで読み込み
img = cv2.imread(png_path, 0)

# 画像切り抜き
img_crop = img[55:150, 55:300]

# リサイズ1.2倍
height, width = img_crop.shape
imgx12 = cv2.resize(img_crop, (int(width * 1.2), int(height * 1.2)))

# 画像を確認
cv2_imshow(imgx12)

txt = (
    pytesseract.image_to_string(imgx12, lang="jpn", config="--psm 6")
    .strip()
    .replace(",", "")
    .replace(".", "")
).splitlines()

print(txt)
# ['12月28日現在', 'ーダウンロード数 : 約2245万件', 'ー陽性登録件数 : 5566件']
```

## 文字抽出
```python
import re

# 更新日

m = re.search("(\d{1,2})月(\d{1,2})日", txt[0])

dt_update = dt_today

if m:
    year = dt_today.year
    month, day = map(int, m.group(1, 2))

    dt_update = datetime.date(year, month, day)

    if dt_today < dt_update:
        dt_update = datetime.date(year - 1, month, day)


def str2num(s: str) -> int:

    m = re.search("(\d+)(万)?件", s)
    n = 0

    if m:
        n = int(m.group(1))

        if m.group(2) == "万":
            n = n * 10000

    return n


# ダウンロード数
download_cnt = str2num(txt[1])

# 陽性登録件数
patients_cnt = str2num(txt[2])

print(dt_update, download_cnt, patients_cnt)
```
最新のJupyter Notebookとnbextensionsの組み合わせでうまく動かない場合の解決法

# 最新のJupyter Notebookとnbextensionsの組み合わせがうまく動かない

現在、1週間前の2020/12/23に`Jupyter Notebook`がバージョン`6.1.6`にアップデートされたのですが、それに伴い拡張機能`nbextensions`(`0.5.1`)が使えなくなっているようです。さらに、単純に`Jupyter Notebook`のバージョンを`6.1.5`に落としても、`nbextensions`は使えるようになりますが、オートコンプリート(自動補完)の拡張機能`Hinterland`が使えません。これの私の場合の解決策を残しておきます。

# 環境
|||備考|
|:----:|:------:|:------:|
|OS|Windows10||
|conda|4.9.2|Anaconda Promptで`conda -V`|
|Python|3.9.1||

# 問題の再現

Anaconda Promptで次のよう新しい環境`nbex_test`を作って、`jupyter`と`nbextensions`をインストールしてみます。

```bash
(base) $ conda create -n nbex_test
(base) $ conda activate nbex_test
(nbex_test) $ conda install jupyter
(nbex_test) $ conda install -c conda-forge jupyter_contrib_nbextensions
```

この状態で、スタートメニューの`C:\Users\ユーザー名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Anaconda3 (64-bit)`に`jupyter notebook (nbex_test)`のショートカットが作られます。しかしこのショートカットから`jupyter notebook`を立ち上げてみると、下図のように`Nbextensions`のタブが現れません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/466677/92bd0a6d-b53e-1c9f-4f61-ff59c9df28a9.png)

# 解決策

Jupyter Notebookのバージョンを`6.1.5`に下げるとうまくいきました。Anaconda Promptで下のように、バージョンを指定してインストールし直すことで、Jupyter Notebookのバージョンを`6.1.5`に下げられます。

```bash
(nbex_test) $ conda install notebook==6.1.5
```

無事、下の図のように`nbextensions`のタブが現れました。これでオートコンプリート以外の拡張機能が使えるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/466677/5d1cbd14-5581-122e-72aa-fbf142aabf69.png)

# オートコンプリートが使えない

しかし、これだけだとオートコンプリートの拡張機能だけ使えません。`nbextensions`のタブから`Hinterland`をオンにすれば、下の図のように`imp`まで入力した時点で`import`とサジェストしてくれます。`Python`にもとからある構文だけでなく、後からインストールしているモジュールに合わせてオートコンプリート機能が働いてくれます。が、これが使えません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/466677/d48e3391-70e8-0409-1795-592469abbd5a.png)

## 解決策

`Jupyter`についてくるオートコンプリートの`Python`モジュール`jedi`のバージョンを下げてるとうまくいきました。現在インストールされていた`jedi`のバージョンが`0.18.0`だったので、それを`0.17.2`に下げました。

```bash
(nbex_test ) $ conda install jedi==0.17.2
```

# 感想

バージョンを落とせば使えるようになるだろうことは解っていましたが、どのモジュールをどのバージョンに落とせばいいのかわからないので困りますね...。というかAnacondaって依存関係をよしなに解決してくれるのがウリなのでは？なんにせよこれ以上これに苦しむ人が増えないことを願います。(私は半日食われました)
私がC言語を習い始めの頃に勘違いした引数渡し

他の記事に触発されて記事を書きました。

C言語を習い始めた1990年頃のこと・・・
以下ような例題がありました。（当時こんな書き方はできなかったはずですが・・・）

```c:C言語
#include <stdio.h>

void sub(char c)
{
    printf("%c\n", c);
}

int main(void)
{
    char message[] = "Hello, world!";
    sub(message[0]);
}
```

ちょっと改造しようと思い、講師に変な質問をしてしまいました：
「`sub`関数に `"Hello, world!"` の `H` を渡したのですから、隣の `e` も簡単に取り出して表示できますよね？」と。
引数`c`の文字`'H'`は `"Hello, world!"` の `'H'` なのだから、`sub`関数で `(&c)[1]` とすれば隣の文字 `'e'` を取り出せるんじゃないかと思ってしまったのです。
「なんでできないの？」としばらく思っていました。

### 変数の値をコピーして引数に渡す（値渡しする） C言語 では無理

実際には、`"Hello, world!"` の 文字`'H'` のデータをコピーして `sub`関数の引数`c`に代入されているので、変数`c`の文字`'H'`から元の`"Hello, world!"`の`'H'`の位置を特定することはできません。

このような処理を行いたい場合、呼び出し元からデータの位置（アドレス）を渡せばできます。

```c:C言語
#include <stdio.h>

void sub(char *c)  // *でデータ位置を指し示す（ポイントする）引数になる
{
    printf("%c\n", c[0]);  // [0] で先頭文字 'H'
    printf("%c\n", c[1]);  // [1] で隣の文字 'e'
}

int main(void)
{
    char message[] = "Hello, world!";
    sub(&message[0]);  // &でデータ位置（アドレス）を渡す
}
```

### 変数領域への参照を引数に渡せる（参照渡しできる） C++言語 なら可能

ちなみに、変数領域への参照を引数に渡せる C++ 言語であれば、私が最初に思ったことを実現できます。

```c++:C++言語
#include <stdio.h>

void sub(char &c)  // &で呼び出し元の変数に別名を付けて呼び出し元の変数領域を使う
{
    printf("%c\n", c);        // 変数cは普通のchar型変数として扱える
    printf("%c\n", (&c)[1]);  // 変数cのアドレスは呼出し元変数のアドレスなので次の文字を取り出せる
}

int main(void)
{
    char message[] = "Hello, world!";
    sub(message[0]);
}
```

```text:実行結果
H
e
```

### さいごに


講師の方々、いろいろ間違った理解をする受講生がいますので、温かいサポートをお願いします。（自戒も含めて）
Djangoのチュートリアルで躓いた時にやってみたこと

Qiitaは非常に便利ですが、投稿通りのコードを書いても上手くいかないことがあります。
私は下記の記事を参考にコードを書いてみましたが、エラーが起こりました。

Pythonさえ分かればDjangoでWebアプリは作れる
https://qiita.com/Taro_man/items/19613663ef7bb3c57d4f


記事下部のように出来上がったサイトに接続しようとするとエラーが発生しました。
![スクリーンショット (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831747/d701ddd9-3431-2307-350b-6704d753f802.png)

先に申し上げておきますが、記事はとても有用な内容でした。

初心者ならではの、立ち回りですが躓いた時の備忘録として投稿させていただきます。

### まず結論
エラーはhtmlファイルを保存するフォルダ名称を変更することにより解消いたしました。
エラー　　　　　：　template
エラーが起きない：　templates

ちなみに私の環境は以下の通りです。
Windows10 64bit バージョン2004
Python 3.8.6
Django 3.1.4

エラー　＝　ミス　とは考えていません。様々な要因があると思いますので、エラーとなった一例として捉えていただければと思います。

### では私がやったこと
まず目を付けたのはココ！
【Exception Type:	TemplateDoesNotExist】
内容としては「テンプレートが見つからないよ」ってことです。

①
./my_project/settings.pyのINSTALLED_APPSにアプリケーションの登録
調べると大多数がこのエラーです。けど記述は問題ありません。

②
./my_project/urls.pyの記述
公式チュートリアルにて、includeをインポートする記述を見逃しエラーとなったことがありました。
今回は問題ないようです。

③
./sample_app/urls.pyの記述
viewsのインポートの記述もあるし、受け取ったURLからviewsを呼び出す記述も問題なさそう。
さてはviewsの中身か？

④
./sample_app/views.pyの記述
受け取ったURLから関数を呼び出しreturnで返す。
う～ん、シンプル・・・。何故だ？分かんねー。

⑤
./sample_app/template/sample_app/post_list.htmlの記述
そもそもhtmlおかしいんじゃね？
　→開くと普通に見える。
初心に帰ってファイル名は？
　→間違ってない。

⑥
記事の環境と私の環境が違うのでは？
同じ環境でDjango Girlsのチュートリアルやってみる。
　→問題なく動くじゃねーか。

⑦
Visual Studio Code（VS Code）のコードチェック！
flake8インストール、すげーエラー出るじゃねーか。
　→pep8の書式チェックばかりでした。
※そもそも解決方法が大きくずれている・・・。

⑧
Django Girlsのチュートリアルと見比べてみる。
①～⑤を見比べるが問題なし。・・・でも違和感が。
Django Girlsのhtml保存先ってtemplatesになってない？
ここ！？こんなこと！？バージョンによって違うのかな。
・・・
・・
・
半信半疑で記事のフォルダ名を変更すると、動くやん。

以上

実際、エラー内容のどこを見ると一番効率よくトラブルシュートできたのか今でもわかりません。

### 得られたスキル
・Djangoの挙動についてコードを追いながら確認することにより、アプリケーションの仕組みがより理解できた。
・ネットの情報はほどほどに。
・意味を考えながらチュートリアルをやると、今まで理解できていなかった内容がなんとなく分かってくる。

そして、このトラブルが解決したときのスッキリ感！
# たまんね！
【PHP】DatetimeImmutableのコンストラクタはタイムゾーン判定において文字列を優先する


この前ハマったので。
結論を先に言うと、`new DateTimeImmutable($time, $timezone);`では`$timezone`よりも、文字列の`$time`のタイムゾーンを優先して判定するということらしいです。

# 想定通りに動作したケース

```PHP:new_jst_datetime_immutable.php

<?php

/**
 * JSTのDateitmeImmutableを返却する
 *
 * @param string $time
 * @return DateTimeImmutable
 * @throws Exception
 */
function new_jst_datetime_immutable(string $time)
{
    $timezone_jst = new DateTimeZone('Asia/Tokyo');
    return new DateTimeImmutable($time, $timezone_jst);
}
```

```PHP:without_timezone.php

<?php

require_once('new_jst_datetime_immutable.php');

// timezoneが付いていない文字列
$datetime_utc = new DateTimeImmutable('2020-12-30T12:00:00');
$datetime_jst = new_jst_datetime_immutable('2020-12-30T12:00:00');

var_dump($datetime_utc);
var_dump($datetime_jst);
```

```bash

$ php without_timezone.php 
object(DateTimeImmutable)#1 (3) {
  ["date"]=>
  string(26) "2020-12-30 12:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}
object(DateTimeImmutable)#3 (3) {
  ["date"]=>
  string(26) "2020-12-30 12:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}

```

# 想定通りに動作しないケース

```PHP:with_timezone.php

<?php

require_once('new_jst_datetime_immutable.php');

// timezone付きの文字列
$datetime_utc = new DateTimeImmutable('2020-12-30T12:00:00+0000');
$datetime_jst = new_jst_datetime_immutable('2020-12-30T12:00:00+0000');

var_dump($datetime_utc);
var_dump($datetime_jst);
```

```bash

$ php with_timezone.php
object(DateTimeImmutable)#1 (3) {
  ["date"]=>
  string(26) "2020-12-30 12:00:00.000000"
  ["timezone_type"]=>
  int(1)
  ["timezone"]=>
  string(6) "+00:00"
}
object(DateTimeImmutable)#3 (3) {
  ["date"]=>
  string(26) "2020-12-30 12:00:00.000000"
  ["timezone_type"]=>
  int(1)
  ["timezone"]=>
  string(6) "+00:00"
}
```

`$datetime_jst`の`timezone`が`Asia/Tokyo`でなく`+00:00`となっています。

# 動作確認用

https://github.com/kumackey/php-ruby/tree/main/DatetimeImmutableTimezone

```bash

% php -v
PHP 7.3.24-(to be removed in future macOS) (cli) (built: Nov 23 2020 06:45:16) ( NTS )
```
rails で〇〇 2.rbのファイルができた時に行った対処法

# railsで〇〇 2.rbのファイルができた時に行った対処法

## 環境

```
Mac Book Pro 2019 のローカル環境
VS code で編集
```

## 原因

よくわかっていません。同じ現象に合われた方で原因がわかった方は教えていただけると幸いです。

## 対処法

### 現在のgitの状態を確認 


```
$ git status
```

で、現在のgitの状態を確認します。

### 自分の編集したファイルをgit add


```
git add 自分で編集したファイル
```
で〇〇 2.rbの以外のファイルをaddします。

この時に間違えて〇〇 2.rb等のファイルをaddしないように気をつけてください。

### 削除するファイルの確認


```
git clean -n -d
```

で、削除するファイルを確認します。

### ファイルを削除

```
git clean -f -d
```

で、ファイルを削除します。

### 削除後のgitの状態を確認

```
git status
```

で、いらないファイルが削除できているか確認します。（〇〇 2.rb等）

### 必要があれば、コミットする

```
git commit -m "remove 2 file"
```
で、コミットしたい人はしても良いと思います。
[AWS]IAMとは？

<h2>IAMとは</h2>
<p>AWS Identity and Access Manegement の略でAWSを安全に操作するために権限を認証、認可する仕組み<p>
<h4>用語</h4>
ポリシー
ユーザー
グループ
ロール
<h2>ポリシー</h2>
どのAWSリソースに、どのリソースに対して、許可するかどうかを定義する
AWSで操作できる権限をjsonファイル形式の設定ファイルで記述できる
<h2>ユーザー</h2>
個々のアカウントのユーザー
ルートユーザーとIAMユーザーに大別される
ルートユーザーはフル権限で操作でき、IAMユーザーはここで作成できる。
IAMの認証情報は、プログラムアクセスとAWSマネジマントコンソールで操作できる
<h2>グループ</h2>
IAMユーザーの集合体で複数のユーザーに権限を付与でき、アクセス許可の作業を簡素化することができる
<h2>ロール</h2>
一時的に、権限を付与することができ作業が終わったら権限を削除する。
シークレットアクセスキーを必要とせずに操作ができるため、漏洩のリスクがなくなる。
Amplifyのschema.graphqlの@authでカスタム属性を使う方法

## 困ったこと
下記のように、Amplifyのschema.graphqlの権限にカスタム属性を使おうとしても使えなかった。

```text:schema.graphql
type Article
@model
@auth(rules:[
  {allow: owner, identityClaim: "custom:company", ownerField: "companyId", operations: [read]}
])
{
  id: ID!
  title: String
  body: String
  companyId: ID!
  createdAt: AWSDateTime!
  updatedAt: AWSDateTime!
}
```

## 解決法
Amplify（AppSync）でCognito User Poolのカスタム属性を使いたい場合は、GraphQL APIリクエスト時のAuthorizationヘッダーにIDトークンを入れる必要がある。（デフォルトではアクセストークンが入る）
実装としてはAmplify.configure()の引数を下記のように設定するイメージ。

```ts
import Amplify, { Auth } from 'aws-amplify'
import awsconfig from '../src/aws-exports'

Amplify.configure(Object.assign({}, awsconfig, {
  API: {
    graphql_headers: async ()=>({
      Authorization: (await Auth.currentSession()).getIdToken().getJwtToken()
    })
  },
}))
```

## 懸念点
https://github.com/aws-amplify/amplify-js/issues/1772
上記の公式Issueでも言及されているが、APIリクエスト時のAuthorizationヘッダーにIDトークンを入れるのはセキュリティ上の懸念があるため基本的には非推奨。

## 代替案

上記懸念点を踏まえて代替案を考える。

### 代替案① Cognito User PoolのGroupに対象IDを入れる
下記のようにowner指定ではなくgroups指定にして、IDをCognito User PoolのGroupに入れてしまう。

```text:schema.graphql
- {allow: owner, identityClaim: "custom:company", ownerField: "companyId", operations: [read]}
+ {allow: groups, groupsField: "companyId", operations: [read]}
```

もし、Groupを使って管理者と一般ユーザーを分けたい場合にも、下記のように併記させることで共存することが可能。
もちろん、この場合はcompanyIdに"ADMIN"という文字列が定義されないことが前提となる。

```text:schema.graphql
{allow: groups, groupsField: "companyId", operations: [read]},
{allow: groups, groups: ["ADMIN"], operations: [read, create, update, delete]},
```

この案を採用する場合にはCognitoの各種上限に注意。

* 各ユーザーが所属できるグループの最大数: 100
* ユーザープールごとのグループの最大数: 10,000

これらの条件をクリアできるのであれば採用するメリットはある。

### 代替案② Lambdaリゾルバーを使う

https://docs.amplify.aws/guides/api-graphql/lambda-resolvers/q/platform/js#creating-the-functions

上記参照URLの通り、Lambda関数を作って、その中でDynamoDB等からユーザー情報と対象IDを取得し、比較することで権限を判定。
自由度の高い方法だが、こうなってくるとREST APIと開発工数的にほぼ変わらないので、一長一短。
MariaDB(MySQL)「#1034 - 表 'db' の索引ファイル(key file)の内容が不正です。修復を試行してください。」 への対処方法

検索してもなかなか出てこなかったので記事を書きます.

# 前提
環境は以下の通りです.

* OS:　Windows10(64bit)
* 実行環境:　XAMPPのphpMyAdmin

# エラーメッセージ
phpMyAdminのデータベースの特権からユーザアカウント追加を行った後に以下のようなエラーメッセージが出ました.

```
#1034 - 表 'db' の索引ファイル(key file)の内容が不正です。修復を試行してください。
```

調べると以下のようなサイトが出てきて全て試したのですが, どれもエラーが直りませんでした.

* [https://tt-computing.com/mariadb-repair-global-priv](https://tt-computing.com/mariadb-repair-global-priv)
* [https://oc-technote.com/mysql/mysql%20%E3%82%A8%E3%83%A9%E3%83%BC%201034%20-%20incorrect%20key%20file%20for%20table%20try%20to%20repair%20it/](https://oc-technote.com/mysql/mysql%20%E3%82%A8%E3%83%A9%E3%83%BC%201034%20-%20incorrect%20key%20file%20for%20table%20try%20to%20repair%20it/)

# 私が行った対処方法

　まず, データの損失を防ぐため, 一応, phpMyAdminの__mysql__データベースの__db__テーブルのバックアップを取る(コマンドラインから以下のコマンドを実行する)

```
  mysqldump -u ユーザー名 -p mysql db > (バックアップするファイル名).sql
```
　次に, phpMyAdminの__mysql__データベースをGUIで開いて, テーブル一覧で__db__テーブルにチェックを入れる.

　  最後に, テーブル一覧下部にある__チェックしたものを：__で__テーブルを修復する__をクリックする.

# 参照
上記で示したサイトを再掲します.


* [https://tt-computing.com/mariadb-repair-global-priv](https://tt-computing.com/mariadb-repair-global-priv)
* [https://oc-technote.com/mysql/mysql%20%E3%82%A8%E3%83%A9%E3%83%BC%201034%20-%20incorrect%20key%20file%20for%20table%20try%20to%20repair%20it/](https://oc-technote.com/mysql/mysql%20%E3%82%A8%E3%83%A9%E3%83%BC%201034%20-%20incorrect%20key%20file%20for%20table%20try%20to%20repair%20it/)
怠けて怠けて、やっと応用情報技術者試験合格

# はじめに
令和元年 秋季の応用情報技術者試験に合格することができたので、合格に至った勉強法を展開します。
これから受ける方に、少しでも参考になればうれしいです。
# テスト結果
スマホ撮影です。画像汚くてすみません。
![goukaku.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/989811/91f83bf3-f9cc-c401-9dc6-c4d15e711bfa.png)
ギリギリにもほどがありますよね。。。
というわけで、１問、もしくはあと１文字でもミスってたら不合格という点数での合格でした。
100点でも60点でも合格には変わりないので、ある意味効率的でした！（開き直り）
# 投稿者の紹介
- 新卒2年目（当時）
- SE（組み込み系）
- メイン言語はC++
- 基本情報は取得済み
- 何事も形から入るタイプ

実は、2回の不合格を経てやっと今回合格しました。。。
”効率的”と言っていましたが、全然そんなことないですね。。。
# 合格に至るまでに実施した勉強
### 午前試験対策
応用情報技術者試験の午前問題は4択問題が80問で形成されますが、およそ半分が過去問から出題されるとよく言われます。
その割合は、実際に3回受験した（いわば常連）の私が持った感覚とも一致しています。
そのため、午前試験は**過去問の網羅率**がカギになると考えました。

そこで、[応用情報技術者試験ドットコム](https://www.ap-siken.com/)を利用し、とにかく網羅的に過去問を叩き込みました。
通勤時間を利用し、試験当日までには掲載されている問題の8割は正解できる状態になっていたと思います。

過去問だけでは6割取れないので、[応用情報技術者試験 テキスト&問題集](https://www.amazon.co.jp/%E6%83%85%E5%A0%B1%E5%87%A6%E7%90%86%E6%95%99%E7%A7%91%E6%9B%B8-%E5%BF%9C%E7%94%A8%E6%83%85%E5%A0%B1%E6%8A%80%E8%A1%93%E8%80%85-%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88-%E5%95%8F%E9%A1%8C%E9%9B%86-2021%E5%B9%B4%E7%89%88/dp/4798168629/ref=pd_sbs_5?pd_rd_w=XykUZ&pf_rd_p=c295905f-82f9-4d73-8142-c393a4211258&pf_rd_r=Q1GC27JDW0N18PEHGE91&pd_rd_r=c642b03b-081b-47c3-bf0c-d6dceaff327c&pd_rd_wg=EpoiW&pd_rd_i=4798168629&psc=1)で、不足している知識を補いました。
人に教えられるかと言ったら怪しいですが、本に載っている単語はすべて知っている状態になるよう、読み進めました。

### 午後試験対策
午後試験は問1を除いて選択です。
選択必須の情報セキュリティを除き、以下の10問から4問を選択して回答します。

- 経営戦略
- プログラミング
- システムアーキテクチャ
- ネットワーク
- データベース
- 組込みシステム開発
- 情報システム開発
- プロジェクトマネジメント
- サービスマネジメント
- システム監査

午後試験は、**文章読解**と**時間との勝負**という側面があり、いかに問題に慣れているかがポイントとなります。
私は、過去4年分の問題集を購入し、繰り返し（3周）問題を解きました。

どの問題を選択するかは、特に決めていませんでした。組み込み系エンジニアなので、組込みシステム開発は取る前提でしたが。
正直、どの問題も回に応じて難易度が変わってくると思います。そのため、どの問題を選択するかは、試験中に実際に問題を見て決めました。


# 合格できた要因
過去2回の不合格は、言うまでもなく勉強不足でした。
そのため、今回合格できた要因は紛れもなく勉強時間にあると思います。
ただ、合格した回も、決して満足に勉強していたわけではなく、どちらかというと不安な気持ちのままテストに臨みました。

不合格の時と違った点を挙げるとすれば、比較的メリハリをつけて勉強できたかな、といったくらいです。

あとは、試験本番で最後まであきらめないことですね。
私は、試験終了まであきらめず1点でも多くとろうと見直しを徹底しました。午後試験は見直しの時間なんてなかったですが。。
ただ、その姿勢が合格につながったと思います。

# 最後に
試験を受けると決めたら、早めに申し込んでください。
受けざるを得ない状態と、試験から逃げられる状態とでは、勉強するときのモチベーションが大きく違うと思うので。

読んでいただき、ありがとうございました。
VueRouterで動的にpathを取得（検知）する方法

皆さんこんにちは！！

今回は、タイトルの通り「VueRouterで動的にpathを取得する方法」をご紹介します！


なぜ、このような記事を書くかと言うと、下記のコードを一旦見て下さい。

```App.vue
<script>
export default{
    computed: {
      getPath() {
        return this.$router.currentRoute.path;
      }
    },
  }
</script>
```

これ実は、反映されないんです。

computedって値の変更を検知してくれますよね？

なのに、値の変更が検知されない。。。

なぜかと思い、色々調べたところ、公式サイトにはこのようなことが書かれていました。

`ルートのパラメーターを使う際に特筆すべき点は、ユーザーが /user/foo から /user/bar へ遷移するときに同じコンポーネントインスタンスが再利用されるということです。 両方のルートが同じコンポーネントを描画するため、古いインスタンスを破棄して新しいものを生成するよりも効率的です。しかしながら、これはコンポーネントのライフサイクルフックが呼ばれないことを意味しています。`

要は、VuRouterはページ遷移の際にURL自体は変わるのですが、”ライフサイクルフック”は呼ばれないということです。

だから、値の変更を検知してくれなかったのか。。。

そこで、同じ問題に直面してる方の助けになればなと思い、解決策をお教えします！！！

公式の[ドキュメント](https://router.vuejs.org/ja/guide/essentials/dynamic-matching.html)にも記されているので、ぜひご覧ください。

#watchメソッドでpathを取得#

以下のコードを追加し、値の変更を検知します。

今回は``Header.vue``（グローバルナビゲーション）で値の変更を検知します。

```components/Header.vue
<script>
export default{
    data() {
        return {
          curretPath: "",  //現在のパス
        }
    },
    methods: {
        detectPath(path) {
          //pathの上書き
          this.curretPath = path;
        }
    },
    watch: {
      $route (to, from) {
        //遷移先のpathを取得
        this.detectPath(to.path);
      }
    }
  }
</script>
```

#computedで値の変更を検知#

今回は、正規表現を使って``/signup``にアクセスしているかの確認を行います。

```components/Header.vue
<script>
export default{
    data() {
        return {
          curretPath: "",  //現在のパス
        }
    },
    //追加
    computed: {
      signupPass() {
        // /signupにアクセスしているかの確認
        return /\/signup\/*/.test(this.curretPath);
      },
    },
    methods: {
        detectPath(path) {
          //pathの上書き
          this.curretPath = path;
        }
    },
    watch: {
      $route (to, from) {
        //遷移先のpathを取得
        this.detectPath(to.path);
      }
    }
  }
</script>
```

これで、pathの変更を動的に検知することができます！！！

解決するのに結構時間がかかったので、一苦労でした。。。

以上、「VueRouterで動的にpathを取得（検知）する方法」でした！！！

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい！！

Thank you for reading
raspberrypi bluetooth

raspberrypi bluetooth

```
$sudo hcitool dev
Devices:
        hci1    FF:FF:FF:FF:FF:FF
        hci0    FF:FF:FF:FF:FF:FF

$sudo hciconfig
hci0:   Type: Primary  Bus: USB
        BD Address: FF:FF:FF:FF:FF:FF  ACL MTU: 310:10  SCO MTU: 64:8
        UP RUNNING
        RX bytes:136012 acl:0 sco:0 events:4029 errors:0
        TX bytes:1017 acl:0 sco:0 commands:45 errors:0

hci1:   Type: Primary  Bus: UART
        BD Address: FF:FF:FF:FF:FF:FF  ACL MTU: 1021:8  SCO MTU: 64:1
        UP RUNNING
        RX bytes:3551 acl:82 sco:0 events:187 errors:0
        TX bytes:2754 acl:68 sco:0 commands:120 errors:0
```
Golang StatikでViperのConfigファイルとかFirebaseのCredentialとかをまとめてビルド



## Viper
GoのConfig用ライブラリ。プロジェクトの外に置いて監視するつくりっぽいが、バイナリに含めたかったため調べて実装した。
Firebaseの方はセキュリティ的にどうなんだろうか・・・
https://github.com/spf13/viper

## 全体構成

``` 
プロジェクトルート/
　├ asset/
　│  ├ environment/環境名.yaml
　│  └ firebase/クレデンシャル.json
　│
　├ statik/statik.go
　├ config/config.go
　├ firebase.firebase.go
　└ main.go
```
<br>

##### statikを導入
```
$ go get github.com/rakyll/statik@v0.1.7
```

<br>
##### プロジェクトルート/assetの下にenvironment/環境名.yamlとfirebase/credential.jsonを設置する。

<br>
##### コマンドを実行。statik/statik.goが生成される。
```
$　statik -src asset
```
<br>

######以下Staticでファイル読むための処理
 
``` main.go
package main

import (
  "flag"

  "プロジェクトルート/config"
  "プロジェクトルート/firebase"
  "github.com/facebookgo/grace/gracehttp"
  "github.com/labstack/echo/v4"
  //statik バイナリ化したassetファイルのPATH
  _ "プロジェクトルート/statik"
)

func main() {
  // 環境設定取得 パラメータ名, デフォルト, 説明
  env := flag.String("e", "develop", "環境名")
  //flag.Parseで値が入る
  flag.Parse()
  //パラメータを渡してconfigの初期化を行う
  config.Init(*env, "environment")
  //Firebase初期化
  firebase.Init()

  // サーバースタート(echo)
  e := echo.New()
  e.HideBanner = true
  e.HidePort = true

  e.POST("/", handler.function)

  e.Server.Addr = ":8000"
  e.Logger.Fatal(gracehttp.Serve(e.Server))
}
```
<br>

``` config.go
package config

import (
  "bytes"
  "io/ioutil"

  "github.com/rakyll/statik/fs"
  "github.com/spf13/viper"
)

var c *viper.Viper

// Init Configの初期化
func Init(env string, path string) {
  //Viperという設定ファイル用ライブラリを使う
  c = viper.New()
  c.SetConfigType("YAML")
  // statik
  fileSystem, _ := fs.New()
  //statikコマンド時に指定したディレクトリ以下を指定
  f, err := fileSystem.Open("/" + path + "/" + env + ".yaml")
  if err != nil {
  }
  // fileを読んでbyteに 
  r, _ := ioutil.ReadAll(f)
  if err := c.ReadConfig(bytes.NewBuffer(r)); err != nil {
    panic(err)
  }
}

// GetConfig returns config
func GetConfig() *viper.Viper {
  return c
}
```
<br>

``` firebase.go
package firebase

import (
  "context"
  "io/ioutil"

  firebase "firebase.google.com/go"
  "firebase.google.com/go/auth"
  "firebase.google.com/go/messaging"
  "github.com/labstack/echo/v4"
  "github.com/rakyll/statik/fs"
  "google.golang.org/api/option"
)

var fb *firebase.App
var message *messaging.Client
var authenticate *auth.Client

//Init firebase 初期化
func Init() {
  c := config.GetConfig()
  ctx := context.Background()
  config := &firebase.Config{ProjectID: プロジェクトID}
  //
  fileSystem, _ := fs.New()
  f, openErr := fileSystem.Open(firebase/クレデンシャル名.json)
  if openErr != nil {
    panic(openErr)
  }
  // 同じくbyteに
  r, readErr := ioutil.ReadAll(f)
  if readErr != nil {
    panic(readErr)
  }
  // WithCredentialsJSONでClientOptionを生成
  opt := option.WithCredentialsJSON(r)
  var err error
  fb, err = firebase.NewApp(ctx, config, opt)
  if err != nil {
    panic(err)
  }

  message, err = fb.Messaging(ctx)
  if err != nil {
    panic(err)
  }
  authenticate, err = fb.Auth(ctx)
  if err != nil {
    panic(err)
  }
}
```
【Ruby/Rails】正規表現内でパスワードを生成する方法

# さいしょに

下記の記事を参考にゲストログイン機能を追加しました。

[簡単ログイン・ゲストログイン機能の実装方法（ポートフォリオ用）](https://qiita.com/take18k_tech/items/35f9b5883f5be4c6e104)

大変参考になる、とてもわかりやすい記事でおすすめです！

それで、この記事の本題なのですが、私のポートフォリオサイト場合でセキュリティ強化のため、パスワードでバリデーション機能を追加していたので、上記記事そのままで実装するとエラーが出ましたので、その対応をまとめていきたいと思います。


# エラー内容

![スクリーンショット 2020-12-30 11.15.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261027/15f51933-e199-89f4-dac2-07b23228f3ea.png)


エラーの内容は、生成されてたパスワードが、正規表現外の文字列となってしまっていたようです。

```log:logs/development.log
ActiveRecord::RecordInvalid (バリデーションに失敗しました: パスワードの長さは6文字以上で、大文字、小文字、数字、特殊文字がそれぞれ1つずつ含まれている必要があります。):
  
app/models/user.rb:41:in `guest'
app/controllers/users/sessions_controller.rb:18:in `new_guest'
```




パスワード生成は、```SecureRandom.urlsafe_base64```で生成されていたのですが、こちらを修正する必要がありました。

```ruby:app/models/user.rb
  def self.guest
    find_or_create_by!(email: 'guest@example.com') do |user|
      user.password = SecureRandom.urlsafe_base64
      user.name = "ゲスト様"
      user.username = "guest"
      user.confirmed_at = Time.now
    end
  end
```

バリデーションは正規表現で下記のようにしていました。

```ruby:app/models/user.rb
  validate :password_complexity
  def password_complexity
    return if password.blank? || password =~ /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,70}$/
    errors.add :password, 'の長さは6文字以上で、大文字、小文字、数字、特殊文字がそれぞれ1つずつ含まれている必要があります。'
  end

```

# 前提/実装環境

- 開発環境はDockerを使用
- Ruby:2.6.3のDockerイメージを使用
- Ruby on Rails:6.0.3
- devise (4.7.2)

# 修正内容

修正方法は、下記のようにパスワードを生成して対応しました。
0-9、a-z、A-Z、特殊文字の配列を```sample```メソッドでランダムで4文字ずつ配列で抽出
　▼　▼　▼
それぞれの配列を```sum```メソッドで連結
　▼　▼　▼
```shuffle```メソッドで配列内の値をシャッフル
　▼　▼　▼
```join```メソッドで配列文字列を文字列に連結

```ruby:app/models/user.rb
  def self.guest
    find_or_create_by!(email: 'guest@example.com') do |user|
      user.password = [
        [*0..9].sample(4),
        [*'a'..'z'].sample(4),
        [*'A'..'Z'].sample(4),
        ['#', '?', '!', '@', '$', '%', '^', '&', '*', '-'].sample(4),
      ].sum([]).shuffle.join

      user.name = "ゲスト様"
      user.username = "guest"
      user.confirmed_at = Time.now
    end
  end
```
# さいごに

最初は、```SecureRandom```メソッドで正規表現内でパスワードを生成する方法がないか調べたのですが、見つからず上記のような方法に至りました。
ちょっと無理やり感がある？ような感じがありますが、正規表現内でパスワードを生成できるようになったので良かったです。
〇〇の非staticメソッド〇〇をstatic参照することはできません

前のプログラミングで使っていた関数を新しい環境で使おうとしたときに詰まったのでメモ

いつものように以下のように書いていたらEclipceに怒られました。

```Java:関数
msgStr = MsgList.getMsg("hoge", hoge);
```

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/818309/517b41ac-30d9-d773-9ec1-3ef79b9fb48d.png)

Static参照したい訳じゃないんだけど、消えないこのメッセージ。

解決法調べてみたら簡単な凡ミスからくるエラーでした。

<h1>解決法</h1>
関数をインスタンス化する。
インスタンス化した関数を使う。

```Java:インスタンス化した関数
MsgList msgList = new MsgList();
msgStr = msgList.getMsg("hoge", hoge);
```

これで解決しました。

凡ミスですが、多分ほとんどの方が通る道ではないでしょうか？
私は解決まで結構時間とられましたが、参考になれば幸いです。
JSFで動的にメソッドを呼び出す

#はじめに

画面上にあるデータの主キーをセッションに格納してから画面遷移したい。
どの画面からどの画面へ遷移するときも、必ず自画面のManagedBeanの(キーをセッションに格納する)メソッドを呼び出したかったのでそのときやった実装方法をメモ。

#環境

Eclipse4.8 Photon
JavaEE6(Java7)
JSF2.0
GlassFish 3.1.2.2
Windows10

#ui:paramタグ

```HTML:sample.xhtml
<h:body>
 <ui:param name="targetView" value="#{sampleView}"/>
  <!--省略-->
</h:body>
```

```Java:sample.java
@ManagedBean(name = "sampleView")
public class sample{
 public void setMoveKey(ActionEvent event){
  //セッションにキーを格納
 }
}
```

Jsfページにui:paramタグを追加します。
valueにはManagedBeanのnameを書いておきます。
これを画面遷移時にキーをセッションに格納させたい全ての画面に追加しておきます。
※メソッド名setMoveKeyは各ManagedBeanで揃えておきます。


```HTML:header.xhtml
<h:body>
 <h:form>
  <div>
   <ui>
    <li><h:commandLink value="サンプル"
         actionListener="#{targetView.setMoveKey}"
         action="画面遷移メソッド(省略)"/></li>
　　<li><h:commandLink value="サンプル２"
         actionListener="#{targetView.setMoveKey}"
         action="画面遷移メソッド(省略)"/></li>
　　<li><h:commandLink value="サンプル３"
         actionListener="#{targetView.setMoveKey}"
         action="画面遷移メソッド(省略)"/></li>
  <!--省略-->
</h:body>
```

画面遷移のh:commandLinkタグにactionListener属性を追加して
"#{ui:paramのname.呼び出したいメソッド名}の形式で記述します。

これで完了です。
サンプル画面からサンプル２画面へ遷移するときは、
画面遷移メソッドが呼ばれる前にsampleView(sample.xhtmlのManagedBean)のsetMoveKeyが実行され、
サンプル２画面からサンプル画面へ遷移するときは、
sampleView2(sample2.xhtmlのManagedBean)のsetMoveKeyが実行されるようになりました。
prometheus, grafanaでいい感じに監視を可視化する

## 概要
`prometheus`と呼ばれる監視ツールと、`grafana`というグラフなどを GUIのデータで可視化してくれるフレームワークを利用して、サーバーの監視を行います。

## 環境構築
基本的にdockerをフル活用します。

```docker-compose.yml
version: '3'
services:
  prometheus:
    image: prom/prometheus
    container_name: prometheus
    volumes:
      - /var/app/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - 9090:9090
  grafana:
    image: grafana/grafana
    container_name: grafana
    ports:
      - 3000:3000
    env_file:
      - /var/app/prometheus/grafana.env
```























### prometheus
**※ golangが必要**
方法１: ソースからビルド
注: 少し時間かかる

```bash:
$ mkdir -p $GOPATH/src/github.com/prometheus
$ cd $GOPATH/src/github.com/prometheus
$ git clone https://github.com/prometheus/prometheus.git
$ cd prometheus
$ make build
$ ./prometheus -config.file=your_config.yml
```

方法2: 公式からダウンロード
こちらからbuild済みのバイナリーをダウンロードすることも可能です。
https://prometheus.io/download/

```bash:
$ cd prometheus-2.14.0.darwin-amd64  #macの場合
$ ./prometheus -config.file=your_config.yml
```

使う時は、your_config.ymlの部分書き換えていきます。

```config:


```
### grafana
公式のdocker imageが公開されているので、そちらを使うのが一番楽チンです。

```bash:
$ docker run -d -p 3000:3000 grafana/grafana
```


## 参考
https://grafana.com/docs/grafana/latest/installation/docker/
dgraphメモ

## 概要
ubuntuにdgraphをinstallして軽く使って見るところまで。

## 動作環境
Raspberry Pi 4
Ubuntu 20.04.1 LTS
go1.15.2.linux-arm64

## インストール
```sh
curl https://get.dgraph.io -sSf | bash
```

簡単！！
と思いきや、

```sh
dgraph
-bash: /usr/local/bin/dgraph: cannot execute binary file: Exec format error
```

Raspberry Pi 4にinstallしたubuntuは、arm64なので、下記のように自分でソースからクロスコンパイルを行う。

```sh
git clone https://github.com/dgraph-io/dgraph.git
cd dgraph/dgraph # ここにmain.goが入っている
GOARCH=arm64 go install 
```

自分で`go install`すると生成されたgolangのバイナリーファイルは、基本的に`$HOME/go/bin`以下に保存されます。

```sh
$ which dgraph
/home/ubuntu/go/bin/dgraph
```
vim メモ

## 概要
自分用によく使うけど忘れがちなvimの設定を残しておきます。

### bufferの移動
```
:ls 
:b {番号} 
:bnext
```

```vim
" move among buffers with CTRL
map <C-J> :bnext<CR>
map <C-K> :bprev<CR>
```

### terminalとvim間の移動
```
Ctrl+Z
fg
```

### ctags
```vim
ctags -R
# 関数ジャンプ
ctrl ]
```

### tab
```
:tabe
gt
```
Spring Data JPAのソート

findAll()メソッドでのソートに苦労したので備忘録を残しておきます。

# 単一条件でのソート方法

## クエリメソッド
クエリメソッドで実装する方法。
JPAにはRepositoryインターフェースに、命名規則に従ったメソッド名を書くとSQLを自動生成する機能があります。
この方法で実装するのが一般的なようです。

```java:Repositoryクラス
@Repository
public interface xxxRepository extends JpaRepository<xxxEntity, Integer> 	
	public List<xxxEntity> findAllByOrderByBigId();
}
```

## サービスクラスで実装
サービスクラスでソートを実装する場合は下記のようになります。

```java:Repositoryクラス
@Repository
public interface xxxRepository extends JpaRepository<xxxEntity, Integer> {
	public List<xxxEntity> findAll();
}
```

```java:Serviceクラス
    @Autowired
    private xxxRepository repository;

    public List<xxxEntity> accountFindAll(){

    	return repository.findAll(
    			Sort.by(Sort.Direction.ASC, "bigId")
    }
```

### PageRequest.ofでソート

public static PageRequest of(int page, int size, Sort sort)

```java:Serviceクラス
Pageable pageable =
  PageRequest.of(0, 3, Sort.by("bigId"));
```

# 複数条件でのソート方法

## クエリメソッドで実装

```java:Repositoryクラス
@Repository
public interface xxxRepository extends JpaRepository<xxxEntity, Integer> {
	public List<xxxEntity> findAllByOrderByBigIdNormalIdSmallId();
}
```

##@Queryで実装

クエリメソッドでは、メソッド名が長くなりすぎてしまう場合は＠Queryでの実装も検討します。

```java:Repositoryクラス
@Repository
public interface xxxRepository extends JpaRepository<xxxEntity, Integer> {
	@Query(value = "SELECT xxxEntity s FROM s order by s.bigId, s.normalId, s.smallId")
	public List<xxxEntity> findAllOrderByAllId();
}
```

## サービスクラスで実装

```java:Repositoryクラス
@Repository
public interface xxxRepository extends JpaRepository<xxxEntity, Integer> {
	public List<xxxEntity> findAll();
}
```

```java:Serviceクラス
    @Autowired
    private xxxRepository repository;

    public List<xxxEntity> accountFindAll(){

    	return repository.findAll(
    			Sort.by(Sort.Direction.ASC, "bigId")
    			.and(Sort.by(Sort.Direction.ASC, "normalId"))
    			.and(Sort.by(Sort.Direction.ASC, "smallId")));
    }
```

### PageRequest.ofでソート

```java:Serviceクラス
Pageable pageable =
  PageRequest.of(0, 5, Sort.by("bigId").descending().and(Sort.by("normalId")).and(Sort.by("smallId"));
```

# 参考サイト
[Spring Data JPA でのクエリー実装方法まとめ](https://qiita.com/shindo_ryo/items/af7d12be264c2cc4b252)
[SpringBoot JPA Sort 書き方が変わった？](http://blog.tomehachi.net/java/springboot-jpa-sort-code-changed/)
[Spring Data JPAを使用したページ区切りとソート](https://www.codeflow.site/ja/article/spring-data-jpa-pagination-sorting)

# 環境
Java:11
SpringBoot：2.3.1