OmniAICreator/Qiita-1.07M huggingface dataset:
自分用　ViewBindingの基本

自分用にViewBindingの基礎知識を残します。

 ViewBindingとは？
アプリケーションのUIを結びつけるためのライブラリです。
具体的に言うと、XMLレイアウトとKotlin（java）コードでの双方向のバインディング（結びつけ）が可能となります。
結びつけ自体は、setContentViewやFindviewByIdでやったことがありますよね。
それでは、わざわざViewBindingを使うメリットは何でしょうか？

ViewBindingを使うメリット
①冗長なコードの削減
setContentViewやFindviewByIdを使わず、直接UIコンポーネントに操作を加えることができます。
XMLレイアウトとデータモデルを直接バインディングできるため、冗長で手動的なコードが減少します。
冗長なコードの例：
setContentViewでレイアウトを表示→変数を宣言→FindviewByIdで変数の中にviewと結びつける
→変数を経由してViewの操作（textViewのtextを変えるなど）
ViewBindingの例：
変数を用意→変数にsampleActivityBindingを代入 →変数名.ViewIdでViewの操作

②安全性の向上
ViewBindingはコンパイル時に検出されるため、XML内のデータバインディングが存在しないプロパティやメソッドにアクセスしようとするエラーを防ぐことができます。
これにより、実行時エラーやNullPointerExceptionのリスクを減少させ、安全性を向上させます。
変数名.ViewIdでViewIdがインフレートしたレイアウトに存在しない場合は、赤い波線が表示されて事前にエラーに気付くことができます。

ViewBindingの使い方
①ビルド環境の設定
②インフレート
③Listenerを設定

①ビルド環境の設定
ViewBindingを使用するにはbuild.gradleファイルに ViewBinding 要素を追加します。
この時点で、XMLファイルとKotlin（Java）コードとのバインディングが可能となります。

```
//一応これでできるらしいけどうまくできなかった謎
android{
    dataBinding{
            enabled = true
    }
}
//これで出来ました
buildFeatures {
    viewBinding = true
}
```
TIPS:Viewのバインディングの仕組み
ViewBindingが可能になると、プロジェクトの内部で「ある自動的な処理」が行われます。
ViewBindingライブラリは、「レイアウト内のビュー」と「データオブジェクト」をバインドするために
必要なクラス(バインディングクラス)を「自動的」に生成します。
このクラスは、レイアウトを繋ぐ架け橋だと考えてください。
このクラスのおかげで直接レイアウトを動的に操作することが出来ます。
自動生成されるクラスの特徴：
１バインディングクラスは、レイアウトファイルごとに生成されます。
２デフォルトのクラス名は、レイアウトファイルの名前がパスカルケースに変換され、
Binding サフィックス（後ろに文字がつく）が付加されたものになります。

例：
activity_main.xml
↓（ViewBindingを適用）
ActivityMainBinging（クラス）自動生成！

注意：クラスが生成されるだけで新しいファイルが作成されるわけではないです。



②インフレート
前提：activity_main.xmlにViewBindingが適用されたとします。
ActivityMainBinging（クラス）自動生成されています。

ViewBindingを使ったインフレートは非常にシンプルです。
1.Bindingクラスのインスタンスを生成
2.Bindiing.rootをreturnで返す

1.Bindingクラスのインスタンスを生成
インスタンス生成には、バインディングクラス.inflate(layoutInflater)を使います。
上記のメソッドは指定されたレイアウトを展開し、
それに対応するBinding クラスのインスタンスを生成します。

2.Binding.rootをreturnで返す
Binding.rootとは、生成されたBindingクラス内で定義されているプロパティで、
展開されたレイアウトのルートビューへの参照を提供します。
このプロパティを使うことで、バインディングされたレイアウトのルートビュー（Viewクラス）への参照を提供します。
そのため、onCreateやonCreateViewでBindiing.rootをreturnで返すことでViewを表示することができます。

```
private lateinit var binding : ActivityMainBinding
override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {   
    binding = ActivityMainBinding.inflate(layoutInflater)
    return binding.root
}
```



③Listenerを設定
インフレートが完了すると、そのレイアウトにあるViewに直接変更を加えることができます。
”Bindingクラスのインスタンス名”.”ViewのID”で変更を加えるViewを指定することができます。
例えば、activity_main.xmlにid="addButton"のButtonViewがあったとします。
先ほどのBinding.addButtonでViewを指定することができます。
Listenerを設定をするとしたら以下のような感じでできます。
```
private lateinit var binding : ActivityMainBinding
//省略 onCreateViewでbindingを初期化
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    binding.addButton.setOnClickListener { 
    //処理を書く
    }
}
```

最後に
Bindingは、便利！
匿名selenimu

## torという技術
私はよくわからない。なにそれ。

## torを有効にしたselenium
まず前提としてサーバでtorが動いていること。
あとは要するにseleniumで実行しようとしているブラウザのプロキシにtorのアドレスを設定する、
これはfirefoxでやる例。

```python:spy family


    from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
    from selenium.webdriver.firefox.options import Options

    options = Options()
    options.headless = True

    profile = webdriver.FirefoxProfile()
    profile.set_preference('network.proxy.type', 1)
    profile.set_preference("network.proxy.http", "127.0.0.1")
    profile.set_preference("network.proxy.http_port", "9050")
    profile.update_preferences()

    options.add_argument('--proxy-server=socks5://localhost:9050')
    binary = FirefoxBinary('/usr/bin/firefox')

    driver = webdriver.Firefox(firefox_binary=binary, options=options , firefox_profile=profile)

```

## そもそも
オニオンブラウザをseleniumで動かせるのかな？できない事はなさそう。torの匿名性に興味があるだけでディープウェブは興味ないからやらんけど。
消えゆく記憶から架空の人格を作る　その１

## 消えゆく記憶とは
消すと判断した情報が本当に消えた時に残った情報。インターネットアーカイブに昔のWebサイトの情報って残ってますよね。こう言うやつです。

https://archive.org/

## 「消えゆく記憶」を見つけるには
誰かが「消す」と宣言してインターネットにアップロードした情報はその可能性が高いと言えるのではないでしょうか。

## どこでどう探す？

### どこで
消すことを宣言しながら情報が上がる場所というと色々あるかと思いますが、検索機能がないとそもそも見つけられない。では検索方法があるサービスを上げるべき。

### どう探す
検索機能があるサービスを候補にするとしたが、今現在プログラムから呼べる形でAPIを無料公開しているような情報アップロードサービスは中々ない。その中で以下のWebサービスを見つけた。アクセスしてみて欲しい。

https://search.yahoo.co.jp/realtime/api/v1/pagination?p=後で消す

JSON形式で取得できた情報があるだろう。GETパラメータでキーワードを渡すと紐づいた結果が取得できるようだ。

**どこでどう探す？**　の回答は一旦　**上記APIでGETパラメータにキーワードをたわして探す**で考えようと思う。

```python:exapmle
import requests
import json
keyword = "xxxxx"
r = requests.get(f"https://search.yahoo.co.jp/realtime/api/v1/pagination?p={keyword}")
j = json.loads(r.text)
```

jsonのフォーマットなどは詳しく解析していないがどうやら画像など取れるよう。
一旦ここで今回は終わり。

## 残課題
### 課題１　「人格」の元になる情報の確定と作成方法
とりあえず二つあげる。一つ目はやはり**人物像と言われる画像**だろうか。そして二つ目は**発言（人格を感じる文章）** ではないかと感じる

#### 人物像と言われる画像へのアプローチ
二つ考える。
- 取得した画像に対しTensorFlowおよびTensorFlow Hubを使用し画像が「人物」であるか判定して使えるものを使う
- FaceAPPのような仕組みを用意し、取得した画像を全て「人物」に変換する。まあこれは「そもそもAIで人物像を作れよ」って声もありそうですけど。

#### 発言（人格を感じる文章）へのアプローチ
マルコフ連鎖やword2vecであったり、前回のアドベントカレンダーでも扱ったSeqGANなど候補はあるものの、2023年の今ならChatGPTにサンプル文章渡して喋らせるのが手っ取り早そう。トークンとシークレット取得すればAPIも動いた気する。
でもどうやって問いかけるかはコツがいりそうで **「ChatGPTを〇〇になり切らせる」** という試行錯誤一つで記事がいっぱいできそう。

### 課題２　「消えゆく記憶」が本当に消えたと確認するには
jsonからURL取れるし、これに定期的にrequestを投げて404が帰ってきた時点で「消えた」と判断すると良いのでは。


### 課題3 そもそも「消えゆく記憶」をどこに蓄積するのか
これは悩みどころ。サーバを用意すれば如何様にもなるが。

サーバレスでやった方が後世の再現性が高いと考えるが費用が気になる。
AWSに固めればストレージはS3とdynamoDBの組み合わせが王道な気がするし、LambdaとStepFunctionsで全部やってくれる様にもできそうではあるが、TensorFlowなど前提にしてしまうとLambdaじゃGPU使えないからインスタンス借りてそこでプログラム動かす必要があり料金が爆上がりしてしまいそう。

しかしなーGPUがなー、趣味でやるシステムにGPUインスタンスの料金とか払いたくないしVPSなどでやるべきなのかな（それも金はかかるんですけど）

# その１って書いたけど続き考えたらやるのめんどくさくなってきちゃった
後続研究をやってくれる人がいたら大歓迎
ファミコン互換機からパッキパキのピクセル抽出

ファミコン互換機の眠い映像から、くっきりした画面を取得

![Capture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1395875/bc043274-8eac-67ea-f097-9e84be08ef05.png)


キャプボ（カメラ）から映像を取得してCanvasのImageDataを加工します

```js:AVerMedia.js
import {View} from './js/View.js'
 
class Pixelater extends View{
	constructor(from,to){
		super('Pixelater')
		this.delta = [10,0]//カーソルキーで微調整
		this.source = new AVerMedia(from,{loaded:e=>{
			console.log(e)
			this.in		= new Canvas(this.source.size,{willReadFrequently:true})
			this.out	= new Canvas(to,{willReadFrequently:true}).pixelate()
			this.add(this.source,this.out).update()
		}})
		document.addEventListener('keyup',e=>e.preventDefault())
		document.addEventListener('keydown',e=>{
			switch(e.key){
				case 'ArrowRight'	:this.delta[0]++	;break
				case 'ArrowLeft'	:this.delta[0]--	;break
				case 'ArrowDown'	:this.delta[1]++	;break
				case 'ArrowUp'		:this.delta[1]--	;break
				default				:return
			}
			console.log(this.delta)
			e.preventDefault()
		})
	}
	update(){
		var scale = [this.in.width/this.out.width,this.in.height/this.out.height]
		var delta = this.delta.map(n=>n/10)
		var a = this.in.drawImage(this.source).getImageData()
		var b = this.out.createImageData()
		var p = 0
		for(var y=0;y<this.out.height;y++){
			for(var x=0;x<this.out.width;x++){
				var sx = x*scale[0]+delta[0]
				var sy = y*scale[1]+delta[1]
				var sp = (Math.round(sx)+Math.round(sy)*this.in.width)*4//RGBA
				for(var i=0;i<4;i++){b.data[p++]=a.data[sp++]}
			}
		}
		this.out.putImageData(b)
		requestAnimationFrame(this.update.bind(this))
	}
}

class Canvas extends View{
	constructor(size,option){
		super('CANVAS').assign(size)
		Object.assign(this,size,{size})
		this.context = this.node.getContext('2d',option)
	}
	pixelate(){
		this.context.imageSmoothingEnabled = false
		return this.style`image-rendering:pixelated`
	}
	createImageData(){return this.context.createImageData(this.width,this.height)}
	getImageData(){return this.context.getImageData(0,0,this.width,this.height)}
	putImageData(v){this.context.putImageData(v,0,0);return this}
	drawImage(v){this.context.drawImage(v.node,0,0);return this}
}

class AVerMedia extends View.Dispatcher{
	constructor(size,...a){
		super('VIDEO AVerMedia').assign({autoplay:true,controls:true}).add(...a)
		this.size = size
		navigator.mediaDevices.getUserMedia({video:size,audio:true}).then(stream=>{
			this.node.srcObject = stream
			this.node.onloadedmetadata = e=>{
				this.size = {width:this.width,height:this.height}
				this.on('loaded',this)
			}
		}).catch(e=>console.log(e.toString()))
	}
	get width(){return this.node.videoWidth}
	get height(){return this.node.videoHeight}
}

document.body.append(
	new Pixelater(
		{width:1280,height:720},//240x3 = 720p
		{width:270,height:240}//8BIT COMPACT HDMI - VRAMイメージ
	).node
)
```


入力キャプチャボード：Live Gamer 4K（GC573）
https://www.avermedia.co.jp/product-detail/GC573
ファミコンHDMI出力：8BIT COMPACT HDMI
https://www.columbuscircle.co.jp/products/?id=1528173155-049425
カセット：キングオブキングス（ナムコ）
https://www.amazon.co.jp/dp/B000068H3C
この世の会社PC選定する人に見てほしい。メモリ４GBのPC使って分かったこと

# 初めに
えか太郎です。普段から記事を書いたことすらないのに、ここ一週間書いてばかりで頭がつかれたので、今回はポエムを書いて息抜きしたいと思います。先日ヤフオクで購入した中古Thinkpadがちょうどメモリ４GBだったので増設する前と後で変わったことを書きたいと思います。
# 感じたデメリット
一か月で感じたことを書こうと思いました。ちなみにThinkpadの機種は「ThinkPad13 Gen2」FullHDのIPS液晶とほぼ未使用バッテリーがついて6000円（送料込み）でした。すごくいい買い物をしました。
## Chromeでタブを切り替えるたびにリロード（再読み込み）が発生する。
よくChrome君はメモリーをバカ食いすると言われていますが最近のChrome君はメモリーセーバーという機能が実装されました。この機能は表示していないタブをメモリ不足解消のために自動で閉じるというものです（正確には開くタイミングで再読み込みをさせるもの）。
この機能によってタブを4つほどしか開いていない場合でも、今見ているタブと1つ前のタブ以外は再リロードが必要でした。２、３秒程度の待ちですが私が見たいのは今すぐなのです。さらにページによってはどこまでスクロールしたかもリセットされ散々でした。

## 裏でソフトウェアのインストール中は半強制休憩時間に
インストール作業とかは構成する過程で一時保存のためにメモリを結構食うみたいですね。インストールに失敗する原因にもなってインストール中は優しくしてあげないといけないのは非常に面倒です。また、メモリ不足の弊害として新しいタブを開くとき、Youtubeの再生が一瞬固まるなどといった動作の不安定さももたらします。
## デュアルディスプレイにしてもろくにアプリを開けないから意味なし
HDMIでモニターに外部出力したときの話をします。ディスプレイが二倍になるということは同時に2倍の表示ができるということです。しかし、アプリが2倍多く開けるわけではありません。一画面にVScode、もう一画面にChromeとDiscordやSlackなどを開くという使い方をするとさらにヤバいメモリ不足になります

# 結論
メモリーは８GBが最小要件だといえます。やはり４GBには人権がないといっても過言ではないかもしれません。さすがにChromeのタブを見るのをやめるたびにリロードが掛かるのはさすがに効率が悪すぎます。100%になることがないように抑えられていて素晴らしいととらえるべきかもしれませんが、メモリを４GBから８GBにするのにそこまで費用はかかりません。（法人向けだと自分で変えられない分高いかも...資本主義か）
aspidaで快適にAPIと通信する

# そのAPIの型、定義されていますか？

フロントエンドで「型定義」といったらTypeScriptが思い浮かぶ人がほとんどではないでしょうか
宣言されている変数は型定義のおかげで意図しない値の代入を防げているでしょう

**では、APIの返り値の型はどうですか？ anyにしてませんか？**

せっかく、TypeScriptを導入したにも関わらずanyを多用してしまっている...なんてことを防いでくれるライブラリ「aspida」を紹介します！

# Aspidaが担ってくれること

> パス・URL クエリ・ヘッダー・ボディ・レスポンス全てに型を指定可能
> FormData / URLSearchParams の内容にも型を指定可能
> HTTP クライアントは axios / fetch / node-fetch に対応
> 
> READMEより
> https://github.com/aspida/aspida/tree/main/packages/aspida/docs/ja#readme

実際にAspidaは以下のように型定義をしてくれます

```typescript
import type { DefineMethods } from "aspida";

type User = {
  id: number;
  name: string;
};

export type Methods = DefineMethods<{
  get: {
    query?: {
      limit: number;
    };

    resBody: User[];
  };

  post: {
    reqBody: {
      name: string;
    };

    resBody: User;
    /**
     * reqHeaders(?): ...
     * reqFormat: ...
     * status: ...
     * resHeaders(?): ...
     * polymorph: [...]
     */
  };
}>;
```

さらにaspidaではパスとディレクトリ構造を一致させてくれます
例えば、APIのパスが __https://example-api.com/users?limit={number}__ の形式の場合は
```
- myproject
    - api
        - users
            - index.ts
```
のようにディレクトリを作ります
これが、パスが増えていくと
```
- myproject
    - api
        - users
            - index.ts
        - content
            - index.ts
        - action
            - post
                - index.ts
        ・・・
```
のように複雑なパスになっても可読性を保ったまま開発することができます！

ちなみに最初の例の場合のAPIを呼び出すコードは以下の通りです
```typescript
// 公式サイトより
import aspida from "@aspida/axios";
import api from "../api/$api";
(async () => {
  const userId = 0;
  const limit = 10;
  const client = api(aspida());

  await client.v1.users.post({ body: { name: "taro" } });

  const res = await client.v1.users.get({ query: { limit } });
  console.log(res);
  // req -> GET: /v1/users/?limit=10
  // res -> { status: 200, body: [{ id: 0, name: "taro" }], headers: {...} }

  const user = await client.v1.users._userId(userId).$get();
  console.log(user);
  // req -> GET: /v1/users/0
  // res -> { id: 0, name: "taro" }
})();
```

このように型定義だけではなく、パスの面倒や毎回APIのURL/パスを書いて「どこかでタイプミスしてた...」なんてことからも救ってくれます

# Aspidaの導入手順

npmからインストールできます
手順は公式リポジトリのReadMeで解説されているので割愛します

https://github.com/aspida/aspida/tree/main/packages/aspida/docs/ja#readme

# Aspidaで気を付けること

## aspidaのビルドが必要

アプリケーションでaspidaを使うためにはaspidaのビルドが必要です
```json
// package.json
{
  "scripts": {
    "api:build": "aspida"
  }
}
```
```bash
$ npm run api:build

> api/$api.ts was built successfully.
```

:::note
Nuxtなどですでにnpmを使っている場合はpackage.jsonのscriptsの部分のdevなどにaspidaのコマンドを入れて
```json
{
  "scripts": {
    "dev": "aspida && nuxt "
  }
}

```
などにすると個々でコマンドを実行することができます！
:::

# aspidaを実際のプロダクトで使ってよかったこと

- 引数を入れ忘れない
- アプリケーション側のコードの可読性が上がった

aspidaがあることで引数が足りなかったり型が違ったりしたときにエラーを吐いてくれるのでAPI側でのエラーが大幅に減りました
また、それぞれの場所でaxiosでAPIコールするよりもaspidaを使ったほうが読みやすいです(切実)

# 終わりに

これからも便利なライブラリや新しい技術を積極的に取り入れて楽にコーディングをしたいですね！
PHP でも AWS Lambda って実行できるんやで　〜 lambroll を添えて 〜

# はじめに

この記事は NE Advent Calendar 2023 7日目の記事です。


# どうして PHP なのか

弊社は PHP をメインに開発を進めております。
Lambda が標準でサポートしている Golang や Python を日常的に使用している人はおらず、Lambda のためだけに新しい言語を導入するのは今後このコードをメンテ出来る人が減ってしまう。

なんとかして PHP で書けないものか...

# 救世主

https://bref.sh/

Bref は PHP を Lambda で実行するための環境を提供してくれるライブラリです。

# 早速使ってみる

### インストール

以下のような composer.json を作って、`composer install` を実行します。

```composer.json
{
    "require": {
        "bref/bref": "^2.1"
    }
}
```

### 中身を書く

あとは思い思いにプログラムを書いてゆく。

他のライブラリを使う必要がないのであれば composer が自動で作成する `autoload.php` を require_once する必要はないです。

```handler.php
<?php
declare(strict_types=1);

return function ($event) {
    // do something
};
```

### デプロイ

一旦動作するかどうか確かめるためにデプロイします。

しかしながら、公式に記述されているデプロイ方法は [Serverless](https://bref.sh/docs/deploy) を利用した方法しか記述されていません。

https://bref.sh/docs/deploy

弊社では Lambda で動作させるコードは [lambroll](https://github.com/fujiwara/lambroll) を利用してデプロイするようにしています。今回も lambroll を使ってデプロイしたい。

# lambroll を利用した Lambda へのデプロイ

lambroll の詳しい使い方は公式を読んでもらえばと思います。

弊社が lambroll を採用している理由として、
- tfstate の情報を利用することができる
- Lambda のソースコードのみをデプロイすることができる

が挙げられます。

## デプロイ方法

まずは lambroll を利用するために必要な設定ファイルの準備をします。

```function.json
{
  "Description": "hoge",
  "FunctionName": "qiita-sample-function",
  "Handler": "handler.php",
  "MemorySize": 128,
  "Role": "{{ tfstate `aws_iam_role.qiita_sample_function.arn` }}",
  "Runtime": "provided.al2",
  "Layers": [
    "arn:aws:lambda:ap-northeast-1:534081306603:layer:php-82:49"
  ],
}
```

### Role

Role には Lambda を実行する時に assume する IAM ロールを指定する必要があります。

IAM ロールは Terraform によって事前に定義されているのでそれを参照するようにしたいです。lambroll には Terraform によって生成された [tfstate](https://developer.hashicorp.com/terraform/language/state) を読み込む機能があるのでそれを利用して参照します。

### Layers

Bref を利用する時には個々のパラメータが非常に重要になります。

Lambda におけるレイヤーとは、コードが動作するために必要な依存関係をアプリケーションコードに含めるのではなくパッケージとして分離するための概念です。これにより、Lambda のコードを小さく保ちながら、再利用性やメンテナンス性を向上させることができます。

Bref ではこのレイヤーを提供してくれています。
https://runtimes.bref.sh/

composer 経由によってインストールされるパッケージと Bref から提供されている専用のレイヤーを利用することことで PHP を Lambda で動作させることができます。

### デプロイの実行

ディレクトリ構成は以下のようなものを想定しています。
```
├── function.json
├── composer.json
├── composer.lock
├── handler.php
└── vendor
    ├── autoload.php
    ├── bref
    :
    :
```

lambroll は以下のようにして実行できます。

```shell
$ lambroll deploy --function=function.json --tfstate=terraform.tfstate
```

# おわりに

PHP が一番得意、業務上 PHP での実装を求められているなど Lambda を PHP で動かしたい人は少なくないと思います。

Bref を使いこなして良き Lambda ライフを！
TailwindCSS Technic

# Tailwind CSS<br />ﾁｮｯﾄﾃﾞｷﾙ<br />になろう

- - -

## なんでTailwindCSSを取り上げたの？

- - -

## 記事として取り上げやすそうだった

- - -

## メリット

- - -

#### レスポンシブ対応が楽
クラス指定でブレークポイントを
指定できるというのが非常に楽です。

- - -

#### 要素に対してホバーしたとき、<br />カーソルを合わせた時の指定も楽

同じくクラス指定で<br />ホバー時とカーソル合わせた時の<br />指定もできます。スゴイ

- - - 

#### 元のテーマカラーかがないため、<br />自分のオリジナリティを出しやすい
CSSライブラリやUIライブラリは<br />そのライブラリやフレームワーク側が既に決定したデザインで使用することになる。

- - -

サイト内の要素に対して指定できる<br />デザインの幅の限界を決めてしまう<br />ことになります。

- - -

Tailwindは既存のCSSプロパティの指定のパターンをそれぞれCSSクラス化だけに過ぎないので、<br />自分がしたいデザインを好きなように実装できるようになります。

- - -

#### ダークテーマ対応がしやすい
ダークモード対応の手順が<br />既にドキュメントに書いてあって、<br />それを使うだけでダークモード対応も可能。


- - -

## デメリット

- - -

### クラス指定で色々やるからひたすら<br />クラス指定しないといけない。

- - -

「クラス指定でなんでもできる」<br />それはつまり...

- - - 

**「クラス指定でなんでもやることがTailwindCSSの使い方」** であるため、TailwindCSSでスタイリングするとき、ひたすらドキュメントとにらめっこ<br />しながら書くことになる。これが正直めちゃくちゃ負担。

- - - 

## 規定されたクラスでは<br />できないこともある

- - - 

その上、規定されたクラスのみでは<br />できない指定があるので、<br />それを受け入れる必要がある。

- - - 

## CSSを普通に書くのはよくないの？

- - -

TailwindCSSに頼りきりじゃなくて
**CSS書けるようになろう。
それがベスト。**
その上でライブラリ選定をしてTailwindCSS使うならよし。
- - -

##### これから心がけていくべきこと
### クラス名を<br />だらだら書くの<br />やめようね

- - -

## そのためのテクニック

- - -

## `@layer`と`@apply`を使いこなす

- - -

以下のようにすると、<br />最初から要素にある程度の指定を<br />しておくことができます。

- - -

```css
@layer base {
    h1 {
        @apply text-2xl font-extrabold
    }
    p {
        @apply text-blue-600
    }
    html {
        @apply bg-yellow-100
    }
}
```

- - -

## `[]`を使った指定を行う

- - -

`border-[3px]`で<br />Borderのpixel数を3pxにすることが<br />できるように、特定の指定値を<br />`[]`を使って与えることができる。

- - -

### `tailwind.config.js`の<br />`theme.extend` で、
### どんどん好きな指定を<br />拡張していく！

- - - 

ここから先は<br />自分の手で試してみてほしいので、
参考になるリンクだけ<br />提供しておきます。
[TailwindCSS Play ~Typing Amimation~](https://play.tailwindcss.com/q6LZjWKr97)

- - -
# おしまい！
chrome拡張のmanifestをv3に更新したら　"Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist."　エラーが出た場合の直し方


## 結論

sendMessage 系が promise を返すようになってるので、ちゃんとエラーハンドリングしましょう

````javascript
let promise = chrome.tabs.sendMessage(tabs[0].id, msg);
promise.catch((error) => {
    //console.log(error);
});
`````
もしくはこう
````javascript
chrome.tabs.sendMessage(tabs[0].id, msg).catch((error) => {
    //console.log(error);
});
`````

サンプルなどでは省略されがちなのでなかなか気づけなかった
0の中身が点のフォント字形が好きじゃない人向けの対処法（リガチャの個別設定）

## やりたいこと

リガチャするフォントをプログラミングに使っている人は多いと思いますが、「この記号はリガチャして欲しくないんだよな」とか「この文字はこの字形の方がいいんだよな」っていう時に使える小技です。タイトルはかなり限定的な感じですが、結構汎用性ある技だと思います。

## JetBrains Mono好きすぎる

私は普段コードを書く時にJetBrains Monoを使っています。

https://www.jetbrains.com/lp/mono/

今までConsolas→Source Han Code JP→SF Mono→Fragment Monoを使ってきて、現在はJetBrains Monoです。マジで好きです。愛してます。こんな見た目になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/59619002-ee94-8857-ae60-24a30a35f101.png)

（流石に日本語はヒラギノ角ゴシックです）

![スクリーンショット 2023-12-07 0.05.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/458a4d08-d0f7-1549-5fcc-f6b55eb432b9.png)

しかしいただけないのは0とl（小文字）の字形です。中身が点なのと、lのしっぽが曲がっている？のは個人的好きではありません。これは良い悪いの問題ではなくただの僕個人の性癖です。

## リガチャ・別字形の設定を一覧で見る

フォントによってはOpenType featuresとかいう名称で書いてあります。![スクリーンショット 2023-12-07 0.09.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/039780a4-5832-bb76-34fc-5db3390d2399.png)

https://github.com/JetBrains/JetBrainsMono/wiki/OpenType-features

JetBrains Mono以外にも書いてあります。Fragment Monoの方はこちら

https://github.com/weiweihuanghuang/fragment-mono/blob/main/documentation/FragmentMonoSpecimenDocumentation.pdf

このList of featuresを見ると個別にリガチャの設定ができるようになります。例えば、`zero`のところにChanges 0 to slashed variant（0を斜線に変えるよ）と書いてあるので、以下のようにVSCodeの`settigs.json`に追記します。今回はlの字形もかえたいので`cv01`も追加します。

``` json:settings.json
{
    "editor.fontLigatures": "'zero', 'cv01'"
}
```
![スクリーンショット 2023-12-07 0.18.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/2be66ddf-5895-1590-ee5d-b5542f71264f.png)

↑変更前

![スクリーンショット 2023-12-07 0.17.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/e964cca7-5bb3-f15e-e8a2-5ff362e4c562.png)

↑変更後

私の好みの字形に変更することができました。他にも`ss19`を指定して等号のリガチャを無効化したりなど、リガチャの設定を細かく設定できます。

## 終わりに

こんな記事を読まなくても、フォントを導入する時にそのドキュメントを読めばわかる話なのですが、そもそもリガチャを個別で設定できるって知ってなかったらドキュメントを読もうって発想にならない人も多いと思うので（実際僕もそうでした）Qiitaに起こしました。

それではHappy VSCoding!!!

## 布教

ここからはただの布教です。なぜJetBrains Monoが好きなのか。

まず縦ラインが見やすい。他のフォントにはない独特の雰囲気があるのはおそらく縦のラインが綺麗だからだと思います。この徹底ぶりは0や括弧()までも及んでいて、ちょっと潰されてる？感じになっています。

これのおかげで、等幅フォントの良さを最大限享受することができます。プログラムって基本的に左右方向ではなく、縦のつながりを見ると思うので目が疲れずにおすすめです。

他にもウェイトが多かったり、斜体がただ斜めになっているだけでなくて、綺麗に調整されてたりと推しポイントはたくさんあります。

![スクリーンショット 2023-12-07 0.29.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/305332b0-2a73-b022-e4ac-64bc5d5c399d.png)

ターミナルなど、日本語フォントを個別で設定できない環境ではUDEV Gothicもおすすめです。

https://github.com/yuru7/udev-gothic

![スクリーンショット 2023-12-07 0.34.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/cde12c14-9975-da71-4fa1-b862820c2c35.png)

皆さんのおすすめフォントもコメントで教えてください！！
書籍【組込みソフトウェア開発のための構造化プログラミング】の写経をレゴマインドストーム（EV3）でやってみた

この記事は[設計こばなし Advent Calendar 2023](https://qiita.com/advent-calendar/2023/software_design_talk)の7日目（12/7分）です。

# 概要
書籍 **【組込みソフトウェア開発のための構造化プログラミング】** に記載の設計・コードに感銘を受けました。
このコードを写経し、組込み装置で実際に動かして書籍の設計を学びたいと思いました。
写経したコードで実際に動いている動画がつぎになります。

<iframe width="560" height="315" src="https://www.youtube.com/embed/DDmrg3R3WEk?si=o5TLIBLOOuwXC8vk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

※組込みソフトウェア開発のための構造化プログラミング

https://www.shoeisha.co.jp/book/detail/9784798147611

# 動作確認環境
書籍の対象ハードウェアはレゴマインドストームNXTでした。レゴマインドストームNXTは持っていないのでレゴマインドストームEV3を使うことにしました。

## EV3について
### EV3の組立て
EV3はETロボコンのロボット**HackEV**としました。
こちらが組立てたHackEVです。

![HackEV.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/adfc5f44-e647-ab6d-8458-9fad8a36acc1.png)

HackEVの組立てはこちらの組立て図を参考にしました。

https://github.com/ETrobocon/etroboEV3/blob/master/BuildingInstructions/HackEV_L8b.pdf

### ソフトウェア開発環境
こちらのETロボコンの環境を使わせてもらうことにしました。

https://github.com/ETrobocon/etrobo

#### OS
ソフトウェア開発環境でインストールされるRTOS TOPPERS/EV3RTを利用しました。
インストールされたバージョンは確認時点(2023/11/10)の最新版でした。

* バージョン: リリース1.1
* 最終更新日: 2021年06月25日

TOPPERS/EV3RTのリリース履歴はこちらに記載があります。

https://dev.toppers.jp/trac_user/ev3pf/wiki/Download


# コード
写経したコードはつぎのGitHubリポジトリに置きました。

https://github.com/grace2riku/sp_esd_book_copy_ev3


# 設計図
書籍で紹介されているのはライントレースを行うロボットです。
こちらはソースコードから作成した構造図はつぎになります。

![src.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/76eb9b35-f828-3150-e55c-7ac76a6e393f.png)

ざっくりとコードを説明します。

* tr_traceCourse関数:トップの関数
* cs_detectDifference関数: コースのズレを検知する
* nv_naviCourse関数: コースを決めてモータを駆動する

tr_traceCourse関数は20msごとに呼び出しています。

こちらは自然言語で書かれた構造図です。

![language.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/e8ec5ecf-5fea-4005-b6fb-e28095b99c8d.png)


# 書籍の設計の良いところ
書籍で紹介されている設計の良いところを紹介します。

## 深くまで追わなくても動きを予測できる
tr_traceCourse関数というトップ関数からは、3つの関数を呼び出しています。
コースのズレを検知して、コースをナビゲートして駆動するという動きを予測できます。
何をしているのか予測できる関数は理解しやすい良い関数です。

## 関数名のわかりやすさ
たとえばcs_detectDifference関数関数（コースとのズレを検出する）は動詞 + 目的語の組み合わせで簡潔に機能を示しています。

## 入力部と出力部が分割されている
ライントレースの入力は自然言語で書かれた構造図でいうとつぎになります。

* 【光センサ値を取得】する
* 光センサの濃淡から【路面の色を判定する】
* 判定した路面の色から【ラインズレを検知する】

出力はつぎになります。

* 進行方向を設定する
* 左右車輪を駆動する

真ん中の【走行コースをナビゲートする】はラインズレを入力として受け取り、進行方向を決めて出力部に渡しています。
左側から得た入力を真ん中で変換し、右側で出力するという流れでわかりやすい良い構造です。

## 単方向の依存性になっている
構造図を見ると上位の階層が呼び出している下位の階層のモジュールにだけ依存しているので良い設計です。

## 同じ階層での横のつながりがない
同じ階層で横のつながりがなく、良い設計です。

## モジュールで何をしているのかがわかる
自然言語の構造図のモジュール名は「〜を・・・する」という形式で書かれているため、一目で何をしているかがわかる良い命名です。
PowerShellでnlコマンド

PowerShellについてなにもわからないので手探りで使ってみました。
nlコマンドについては説明しませんが、詳細を知りたい方は[昔の記事](https://qiita.com/advent-calendar/2020/nl)をご覧ください。

# PowerShellのnlコマンドレット

残念ながらそんなものは無いようです。コマンドレット（Cmdlet）とはPowerShellで利用可能なコマンドのことです。`Get-Command`というコマンドレットを使えばシステムで利用可能なコマンドを取得できます。

`line`や`num`をキーワードに`nl`っぽいコマンドレットがあるか探してみましたが見つかりませんでした。`-Name`オプションにはワイルドカードを使用できます。また、PowerShellでは大文字小文字を区別しないようです。
```powershell
PS> Get-Command -Type Cmdlet -Name *num*
PS> Get-Command -Type Cmdlet -Name *line*

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Get-PSReadLineKeyHandler                           2.0.0      PSReadLine
Cmdlet          Get-PSReadLineOption                               2.0.0      PSReadLine
Cmdlet          Remove-PSReadLineKeyHandler                        2.0.0      PSReadLine
Cmdlet          Set-PSReadLineKeyHandler                           2.0.0      PSReadLine
Cmdlet          Set-PSReadLineOption                               2.0.0      PSReadLine
```

# nlコマンド相当の処理を書く

`ForEach-Object`という、行ごと（オブジェクトごと）に繰り返すコマンドレットがあるので、これを利用すれば簡単に実装できます。nlコマンド相当の処理は以下のように書きます。
```powershell
PS> ls | ForEach-Object -Begin { $count = 1 } -Process { "{0} {1}" -f $count++, $_ }
1 .vscode
2 3D Objects
3 Contacts
4 Desktop
…
```
どこかで見たことあるような構文ですが、これは`awk`ですね。`awk`で似たように書くとしたらこんな感じでしょうか。
```bash
$ ls | awk 'BEGIN { count = 1 } { print count++" "$0 }'
```

# もっと簡潔に書く

`ForEach-Object`には便利なエイリアスとして`%`があるようです。また、コマンドレットだけでなくオプションも簡単に書くことができます。

```powershell
PS> ls | % -b { $c = 1 } -p { "{0} {1}" -f $c++, $_ }
1 .vscode
2 3D Objects
3 Contacts
4 Desktop
…
```

このくらい短く書ければ普段使いもできそうです。

# オプションを再現する
よく使いそうなやつだけやってみます。

## -v
例えば`-v 2`
```powershell
PS> ls | % -b { $c = 2 } -p { "{0} {1}" -f $c++, $_ }
2 .vscode
3 3D Objects
4 Contacts
5 Desktop
…
```

## -i
例えば`-i 5`
```powershell
PS> ls | % -b { $c = 1 } -p { "{0} {1}" -f $c, $_ ; $c+=5 }
1 .vscode
6 3D Objects
11 Contacts
16 Desktop
…
```
`-f`オプションの中には代入式が書けないみたいです。インクリメントはできるのに。

## -s
セパレーターをタブに。

```powershell
PS> ls | % -b { $c = 1 } -p { "{0}`t{1}" -f $c++, $_ }
1       .vscode
2       3D Objects
3       Contacts
4       Desktop
…
```
タブは「`t」と書くようです。エスケープしにくいな...

## -n ln

デフォルトが左揃えなので、lnの書式ですね。

## -n rz / -w

5桁でゼロ埋めの右揃え。`:D5`を指定すればいいようです。

```powershell
PS> ls | % -b { $c = 1 } -p { "{0:D5} {1}" -f $c++, $_ }
00001 .vscode
00002 3D Objects
00003 Contacts
00004 Desktop
…
```

## -n rn / -w
5桁で右揃え。`,5`を指定すればいいようです。

```powershell
PS> ls | % -b { $c = 1 } -p { "{0,5} {1}" -f $c++, $_ }
    1 .vscode
    2 3D Objects
    3 Contacts
    4 Desktop
…
```

----
このへんでおしまい。
プロデルでデータベースSQLServer2022（ステップ２）トランザクションしてみる

# はじめに
今回はプロデルでデータベースSQLServer2022のステップ２で、トランザクションを構成してみます。

https://qiita.com/advent-calendar/2023/produire


## この記事内容の作業環境
Windows11 Pro 22H2
CPU Intel(R) Core(TM) i3-5005U 2.00 GHz
Produire Version 2.0.1197 for Windows
SQL Server 16.0.1000.6 Express Edition
SQL Server Management Studio	19.2.56.2

# お題のデータべース
こんなテーブル構成のデータベースを作成しております。
データベース名は「日本語プログラミング言語」です。
![db.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118036/5482f410-d924-34ca-392d-a26461bf624f.png)

# お題のソースコード
#### プロデル
```mind:mssq2.rdr
※コンソール
【conn:SQLServerデータベース】

【sql1:文字列】は、「INSERT INTO 後継言語  (言語ID,後継言語ID) VALUES (@p1, @p2)」
【sql2:文字列】は、「UPDATE 後継言語 SET 後継言語ID =@p1 WHERE 言語ID =@p2」
【sql3:文字列】は、「SELECT LN.言語名,LN.公開年,LN.よみがな,KLN.言語名 AS 後継言語名 
FROM 言語名 AS LN
LEFT JOIN 後継言語　AS KL ON LN.言語ID=KL.言語ID
LEFT JOIN 言語名　AS KLN ON KL.後継言語ID=KLN.言語ID
WHERE LN.言語ID =@p1」
param1は｛@p1=2,@p2=8｝
param2は｛@p1=6,@p2=2｝
param3は｛@p1=2｝

メイン実行する。

メイン実行する手順
	接続構成する
	connへ接続する
	connでトランザクションを開始する
	例外監視
		sql1をparam1としてconnで実行して、結果1とする
		sql2をparam2としてconnで実行して、結果2とする
   
	   	connでトランザクションを完了する
　　発生した場合
		connでトランザクションを取り消す
    監視終わり	
    
	connからsql3をparam3として取得して、結果3とする
	結果3の内容をコンソールへ表示して改行する

	connから切断する
終わり

接続構成する手順
	connというSQLServerデータベースを作る
	connのデータソースは「DESKTOP-078KPRA\SQLEXPRESS」
	connのデータベースは「日本語プログラミング言語」
	connのWindows認証は、○
終わり
```
https://produ.irelang.jp/docs/data/db/dbsqlserver.htm

↑こちらの詳しい説明を拝見して書いています。

# 実行結果
それでは実行してみましょう。コンパイルはプロデルデザイナで実行ファイルの作成するで行っています。

```
C:\produire\sample2>mssql2.exe
{{ＴＴＳ,2000,てぃーてぃーえす,プロデル},{ＴＴＳ,2000,てぃーてぃーえす,スミレ}}

C:\produire\sample2>
```

ちょっとわかりやすくするため結果を改行してみます。

```
{
{ＴＴＳ,2000,てぃーてぃーえす,プロデル},
{ＴＴＳ,2000,てぃーてぃーえす,スミレ}
}
```

# おわりに
次回は例外をスローしてロールバックを検証してみます。
ちゅらデータ秋の学生インターン参加記（社員側）

:::note info
この記事は[ちゅらデータ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/churadata) 15日目の記事です。
:::


# 概要
ちゅらデータは2023/09に学生向けのインターンを開催しました！

https://churadata.okinawa/jobs/summer-internship-2023/

データエンジニアコースの内容としては以下です
> オンラインでの講義
 実践ワークに向けてデータエンジニア入門として下記の技術トピックを学んでいただきます
■Githubでのチーム開発
■AWSでのサーバ構築
■Snowflakeでのデータウェアハウス構築
■dbt(Data Build Tool)でのデータパイプライン構築
>
> 沖縄本社での実践ワーク
データ基盤構築をチームで5日間かけて取り組んでいただきます

私はdbtの担当として後半の5日間メンターとして沖縄本社へと赴き、手伝いをしていました（私自身はちゅらデータ所属ではなく、親会社のDATUM STUDIOの社員ですが、、、w）。
今回は主に私が担当したデータエンジニアコースの後半5日間の実践ワークについて紹介します。

# 学生の皆さんの参加記
皆さんキャッチアップがものすごく速くて優秀な方ばかりでした。

https://note.com/author_11/n/n72595e8f521b

https://blog.powell1213.com/2023/09/26/churadata-intern/


# 課題内容
ECサイトを模したシステムに関してairbyteを使ってデータ取り込みからdbtでの変換、tableauでの可視化までを実施します。

## Extract/Load
上流は以下のような構成になっています（うろ覚え）。

![スクリーンショット 2023-12-07 0.34.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326612/43c82e90-fa92-4f69-874d-50c9e62545ea.png)

この上流システムからAirbyteを使ってSnowflakeへデータ取り込みを行います。本当はAirbyteは上流システムと異なるAWSアカウントの方が実務っぽいんですが、用意が面倒なので同じアカウントになっています。上流側のネットワークも触ることができるので、FWに穴をあけることも出来るのですが、学生の皆さんはきっちりVPC Peeringを設定して接続をしていました:tada:

（ちなみにデータはほとんどChatGPTが作ってくれました）

## Transform
Transform部分にはdbt cloud, BIにはTableauを使っています。
dbtはdimensional modelingを用いて以下のような指標群を作ってもらいました。

- 売上
- 在庫数
- 在庫回転率

ディメンションは以下を設定しました。

- 日付
- 倉庫
- 商品
- スーパーバイザー

![IMG_1367.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326612/1f8f665e-e97f-7623-3990-6f2ffd0b9ebc.jpeg)
これは @foursue と課題案を考えている時のホワイトボード

![IMG_1369.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326612/a867373c-b9b7-0da8-a57b-2548a7c1eb4d.jpeg)
こっちは学生の皆さんの考えた跡

## BI
Tableau部分に関してはそれぞれの指標とディメンションの対応を与えて可視化をしてもらいました。

![IMG_1375.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326612/5fe1b166-2380-e72e-4213-00ab073bd20d.jpeg)
（[どこかでみたことのある](https://arigato-java.download/newline/)改行）

# 設定した罠
いくつかのひっかけポイントを用意したのでここで種明かしします

## 1: マスタの更新漏れ
これは罠というより私のミスですw
1週間分のマスタデータの更新DMLを流し、AirbyteのCDC機能で取り込みを行ってもらったのですが、私が誤って1日飛ばしてマスタを更新してしまいました。ですが~~マスタの再構築がめんどう~~実務上はマスタの断面を元に戻すことは不可能なのでマスタの再連携はリジェクトしました。~~大人をなめるな。~~
在庫マスタだったので幸いにも出庫履歴と入庫履歴から在庫マスタの在庫数は算出できましたが、実際は万引きや在庫廃棄もあるので完全に復旧するのは難しいよ〜みたいなしみじみ話も混ぜてその場を乗り切りましたw


## 2: 倉庫IDの管理体系ズレ
販売システムと在庫システムで同じ`倉庫ID`というカラムを用意していましたが、これらは別システムなので当然似て非なる列です。本当は名前などで名寄せが必要なのですが、これに気づいた方はいませんでした。連番を適当にランダムソートしただけなので流石に気付けないですね:joy:

# おわりに
学生のインターンにメンターとして参加した話と裏話を書きました。参加された学生の皆さんが本当にレベルが高くて、自分もただ技術をキャッチアップするだけでなく、それを以って何を成すかまで考えないといけないなと襟を正されたような一週間でした。

![PXL_20230910_104302536.MP.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326612/bea72623-8d61-4b25-8ae9-197c730b24b7.jpeg)
これは0日目に飲んだビール（沖縄最強！！！:muscle::muscle::muscle:
ワンライナーなdo{}while()

イテレータを使う

- イテラブル(反復可能プロトコル)
    - `@@iterator` つまり `[Symbol.iterator]` がイテレータを返すメソッドである
- イテレータ(イテレータプロトコル)
    - `next` がイテレータリザルトを返すメソッドである
- イテレータリザルト
    - `value` が現在の値を表す
    - `done` が次の値を生成できないことを表す真偽値
        - trueのとき`value`は無視される

```js
({value:'hello',done:false});// イテレータリザルト
({
    next:()=>({value:'hello',done:false})
});// イテレータ (注:無限ループ)
({
    next:()=>({value:'hello',done:false}),
    [Symbol.iterable](){return this;}
});// イテラブル (注:無限ループ)
({
    [Symbol.iterable]:()=>({
        next:()=>({value:'hello',done:false})
    })
});// これもイテラブル (注:無限ループ) 勝手に@@iterableが挿入される?

// =*=*=

console.log([...{[Symbol.iterator]:_=>({next:_=>({value:Math.random(),done:Math.random()>.9})})}]);
// はこれと同等の処理
do{
    console.log(Math.random());
}while((!Math.random()>.9));

// つまり
()=>{let a=[];do a.push(hoge());while(fuga());return a;}
// は
()=>[...{[Symbol.iterator]:_=>({next:_=>({value:hoge(),done:!fuga()})})}];
// に置換可能
```
使い所があるのかないのか

参考文献:

https://qiita.com/kura07/items/d1a57ea64ef5c3de8528
システム運用保守における障害対応について

## はじめに
この記事ではメンバー目線（特に1、2年目）での障害対応の乗り越え方を書いてみました。
直接現場の先輩から教わったことや、自分なりにやってよかったことをまとめております。

大きく以下の3つに分けて書いております。
①通常時（障害が起きていないとき）
②障害発生時
③障害対応完了後

## ①通常時（障害が起きていないとき）
- 設計書や必要なドキュメントの場所を把握し、`何のための設計書か`を理解しておく
- 使用頻度が高いコマンドはすぐに使えるようにまとめておく（Grepコマンドやオプションなど）
- システムの概要を知っておく（データの源泉はどこなのか、何をやるシステムなのか）

上記3つをやっておくことにより、
障害対応する際に原因追及までの速度が各段に上がりました。
特に「`何のための設計書か`を理解しておく」がとても大切で、
必要な情報をすぐに追うことができるようになります。

## ②障害発生時
「どういった種類の障害」なのかをすぐに判断する必要があります。
バッチが落ちてしまったのか、APIの処理にて例外エラーが発生しているのか、
通常は10分で終わるバッチ処理が1時間以上たっても終わらないようなものなのか...

しかし、障害の種類さえわかってしまえば原因の追い方はそれぞれ似ています。
私のプロジェクト先では障害は以下の種類が多かったです。

<dt>バッチ処理が落ちた</dt>
⇒インプットファイルに問題がある、DBに登録されたレコードがおかしい、など

<dt>APIのレスポンスにて例外エラーで返ってくる</dt>
⇒リターンコードを見るとユーザーのデータに原因あり、など

障害が発生した時、最初は焦ってしまいがちですが、
改めて確認すると障害の種類は大体同じです。
種類さえ判別できたなら、あとは設計書と合わせて必要な情報をまとめていくことで原因が見えてくると思います。
また、過去に同じ障害や似た障害が起きていれば、
その時のメモや情報を参考にするのも手です。

## ③障害対応完了後
一度起きた障害は、よっぽど特殊なものではない限り2回目も起きる可能性があります。
本来は再発を防ぐための対策を取る必要がありますが、
顧客のヒューマンエラーによるものなどは難しい場合があります。
そのため、
- 何を使って調べたか（どの設計書を使った、など）
- どう対応したか（単純リランしたのか、ファイルを置き換えてもらったのか、など）
- 再発した時の対処法

など、メモなどに残すことで
<dt>次に対応する人が効率的に対応できるようにする</dt>

ことが大切です。

この行動はよく評価されやすいと現場リーダーの方にも教えていただきました。
私自身も過去対応のメモで助かった作業も多くありますので、
良い情報を残すことは非常に重要だと思いました。

## まとめ
障害対応は純粋な技術力に加え経験も必要です。
しかし、最初からいきなりスイスイとできるわけではないので、
日頃からできる準備や勉強、システムへの理解があることにより対応力が大きく変わってくると思いました。
経験が浅い中、障害対応に苦労している方で
この記事を今後どう乗り換えるかの参考にしていただければ幸いです。
モバイルエンジニアに英語が必要な理由〜個人見解〜

私は最近英語の勉強をし始めました。
今回はどういったことを考えて学習をスタートしたのかについて記載して行こうと思います。

## 忙しい人のために最初に結論
どうしてモバイルエンジニアに英語が必要かというと以下の理由があると思われます。
1. Google・Appleなどから提供される最新技術の情報は英語である。
1. 主要ライブラリーなどのissueを読む際などに英語が必要である。
1. カンファレンスなどで質問する際に英語でする必要がある。
etc...

上記以外にも理由になることはあると思いますが、メインは上記のことになるのではないかと思います。

# 以下私が英語学習を始めようと考えた経緯について

### 英語が大の苦手だった学生時代
私は学生時代、5教科の中でダントツで英語が苦手でできませんでした。(人生で最初で最後の赤点をとったのも英語でした。。。:scream:)

ここまで苦手であったのには理由があります。それは素直に全く勉強しなかったためです。
では、なぜ勉強しなかったのかについてですが、これにも理由があります。
1. **テストでいい点数をとるための勉強に意味がないと感じていた。(←一番の理由)**
1. 将来日本にいる限り日本語ができれば困ることないと考えていた。
etc...

私は将来に役立つ知識と感じたことにすごく興味を持つ学生でした。
なので、身の回りで役に立っていると感じることができる化学・物理・数学・国語などは、よく勉強もしましたし、楽しいと感じていました。

しかし、英語は私の身の回りではそこまで役に立っているという経験がない状態でした。
(留学や海外旅行などを経験したことがなかったのも理由の一つかもしれません。)

## 英語の必要性を感じ始めた社会人(1~3年目)
そんな考えを持っている中で、社会人でモバイルアプリの開発業務に携わり始めます。
この辺りについては以下の記事を見ていただけると幸いです。

https://qiita.com/minomusi/items/2e256e441654889bbbbd

仕事の中でわからないことなどを調べる際、Androidエンジニアの方々は多いと思いますが、
まず公式のdeveloperのサイトを読むことが多いかと思います。そして、Androidの公式サイトあるあるの一つですが、**日本語版の公式サイトと英語版の公式サイトでは記載されている内容が異なる**ことなどが多々あります:innocent:(英語版は最新情報で、日本語版は古い内容など。。。)

こうなってくると、英語版を読まざる得なくなります。
まだその程度であればGoogle翻訳などを駆使してどうにかなります。
しかし、私の新卒で入った会社は海外などにもお客様がいらっしゃったので、時々英語のコミュニケーションが必要になることがありました。
そんなこんなで英語の必要性を少しずつ感じていました。

### フリーランスになっても必要になる英語
現在私はフリーランスとして活動しています。そんな中でも英語が必要になることが多くあります。
まず英語ができなければ書類審査すらしてもらえない案件などがあります。
また、英語ができるということだけで単価が上がることすらあります。
こうなってくると、もう英語から逃げることが難しくなってきます。

仕事で最新の情報をキャッチアップしていくのにも英語が必要で、
案件の幅を広げるにも英語が必要、単価を上げるにも英語が必要という状況です。
そして、将来のなりたい自分像を考えると、英語の能力は必須だと考えるようになりました。

### 遂に学習開始:muscle:
まずはじめにやったこととして、自身の英語の能力がどの程度かの確認になります。
私の方法は適切かわかりませんが、全く勉強せずにTOEICを受けてみることにしました。(大馬鹿www)
結果は、**合計で195点**と逆に清々しい気持ちになるほどでした:yum:

![IMG_6027.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351982/8ed7a52d-3d20-1c0e-bf24-9e2319d1691e.jpeg)

まず中学生の英語から再勉強していく必要があるとわかりました。

### 今の主な学習方法
今のメインの学習方法は、まず英語に毎日触れるという目的でスキマ時間にDuolingをやっています。
そのほかにも英語のテキストの勉強なども実施中です。
そして、2024年にもう一度TOEICを受けようと考えているところです。(果たして点数が上がるのかわかりませんが。。。)

少し長くなりましたが、私が英語が必要と思う理由とそこに至った経緯などを記載してみました。
最後までお読みいただきありがとうございます！:bow_tone1:
では、また明日〜
ブロックエレベータに使われているブロックの寸法を書いてみた

# はじめに
ブロックエレベーターを作っている　熊秀創吉です。
いままで、エレベーターの構成図や図面などを書いてきました。
そこでエレベーターに使われているブロック等の部品の寸法を書いてみました。
ここに書いた寸法は組立図を書く時に使ったものです。
作図に使ったのはJW_CADです。
この寸法は個人で測ったもので多少の誤差があります。
ノギスでは測れない（先端が大きく膨らんでいたり幅が厚いので）寸法がありました。そこは感で書きました。

### 基本ブロックの大きさ
基本寸法はブロックのポッチ（上部のブロック同士をつなぐ突起）間隔が8mmで
高さは基本が9.6mmでプレートはその1/3の3.2mmのようです。
実際にはブロック同士の隙間が0.2mm（タテヨコ）程度あるのでその分小さいです。

### テクニック
基本は8mm角で長さが@8mmで孔の間隔も8mmのようです。
実際は丸孔の長さ方向が0.2mm程度、直径方向と長さが0.6mm程度小さいです。

### 寸法図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/fdc196af-9082-8a8c-147f-aedc191edd27.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/f15a9cca-6bba-ca73-9f6e-0909961ac56d.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/a2e0215f-a752-d95a-57f8-c4d93fe722cf.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/d8d49310-79db-8113-67c3-564515c06a75.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/b2e6f300-61ae-9f29-00b2-30e161c81845.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/96be5882-8a1d-7b02-2e3d-3b1250343b03.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/af6bc271-658f-7fcf-3757-b1e5c4dc786d.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/9947bdec-7b5e-5fb0-4965-acdf7c886096.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/350281d1-ddae-31e5-1fdc-117f8c6a03ce.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/e9907a4f-d960-5a01-9d18-d1ce75d7fd0f.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/007cfebf-c6d1-ebad-49bf-9aab9af847d4.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/6387a281-ea5e-8d89-d380-46a7e3abeafc.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/27ab8f36-b87a-7662-3471-341ce036f0c9.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3010655/57f8067c-7fb7-f3e3-8f2d-d840f4a2640f.png)
GoでよしなにJSONをパースする

# よしなにJSONをパース
Goは静的型付け言語であるため、JSONを利用するにも型を宣言しなければならない。
自らが作成したアプリケーションが返すJSONならば良いのだが、
頻繁に変更されるような外部のAPIや巨大JSONの型を都度宣言し直すのは嬉しくない。

そこで、型を宣言せずともJSONから値を取れるライブラリの紹介。

## jsonparser
https://github.com/buger/jsonparser

```bash
mkdir app/day6/ && touch app/day6/main.go
```

```go
package main

import (
	"fmt"

	"github.com/buger/jsonparser"
)
func main() {
    var jsonStr =`
{
  "name": "山田太郎",
  "age": 30,
  "isStudent": false,
  "address": {
    "street": "桜通り1-2-3",
    "city": "東京",
    "zip": "100-0001"
  },
  "hobbies": ["読書", "映画鑑賞", "旅行"]
}`

    name, err := jsonparser.GetString([]byte(jsonStr), "name")
    if err != nil {
        return
    }
    fmt.Println(name)

    age, err := jsonparser.GetInt([]byte(jsonStr), "age")
    if err != nil {
        return
    }
    fmt.Println(age)

    _, err = jsonparser.ArrayEach([]byte(jsonStr), func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
        fmt.Println(string(value))
    }, "hobbies")
    if err != nil {
        return
    }
}

```

都度エラーを評価しなくては行けないのが、若干面倒ですが、よしなにJSONをパースすることができました。
スクレイピングなどで役に立ちそうです。
初心者が複数回答のFormsとExcelをPower Automateで連携させた採点処理でハマったところ

この記事は、[Microsoft Power Automate Advent Calendar 2023](https://qiita.com/advent-calendar/2023/powerautomate) の
カレンダー1の 12月7日担当分の記事です。

# はじめに
こんにちは！[okaken](https://twitter.com/t_2_2_2_t_2)です。この記事がQiita初投稿となります。

2023年10月からPower Automateを仕事で使い始めた初心者ですが、
クイズ用のMicrosoft FormsとExcelを連携させる採点処理を作成した際、
苦労した点を共有したいと思います。

# ハマったところ：複数回答の選択肢を選んだ時の挙動
複数回答の選択肢に関する問題点を、架空の問題を用いて紹介します。
以下の例では、全ての選択肢にチェックを入れると正解となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/214ed0ff-c758-47dd-8553-baa39607d425.png)

複数選択肢の回答がクリックされた順番で記録されることに気づきました。
例えば、福岡→大阪→札幌→東京→京都の順でクリックした場合、下の画像のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/802e1f75-395c-eb02-079f-75a379c50f01.png)

この状態でもForms単独での採点機能では問題ありませんが、
業務上の目的でPower Automateを使って採点する必要がありました。

Power Automateでは、下の画像の順番とFormsに記入された回答内容の比較を行い、
正解か不正解かを判断する仕組みを採用していました。
このため、Formsの回答内容がクリック順に変わることが課題となりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/5a3e376d-f670-6da5-85bf-8cf8ddb51a93.png)

# 解決策
Power Automateで回答内容を並び替える方法が何かないかを検索し、
直接的な解決策は見つからなかったものの、
参考となる要素が記載されたページが見つけ、
その要素を組み合わせた以下の手順で問題を解決することができました。

## 1.Forms側で回答の先頭に数字を付け、ソートできるようにする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/518d79b6-931c-aaea-3489-c77c39eff8e9.png)

## 2.Power Automateで出力される形式を確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/89af8cc9-8f41-c3fc-5762-b5afb2c3cb4a.png)

## 3.SORT関数、SPLIT関数、REPLACE関数を使用して回答内容を配列変数に格納する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/5fb36ca9-8cfb-b39f-9cc6-7b2add970678.png)
```
sort(split(replace(replace(outputs('応答の詳細を取得する')?['body/r47470530c7a74a9f9adbf1d346816cb8'],'[',''),']',''),','))
```

## 4.JOIN関数で配列変数から文字列を作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/b9603066-6c77-da73-50eb-1ee118a14946.png)
```
join(variables('回答'),',')
```

## 5.Formsで実際に回答し、各アクションがどのように実行されるかを確認する。

3のアクションの実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/7177a1fd-87d8-9c8a-57f7-3350d47a3df8.png)

4のアクションの実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/48284180-eecd-c34d-3b5a-ce3a7b026215.png)

## 6.全体の流れを以下に示します（採点部分のアクションは省略）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416064/977bf3ee-c4d7-7497-d304-e0b8727586cd.png)

# 参考にさせて頂いたページ
・Formsの応答から回答を取得し、SPLIT関数で配列変数に格納する方法について

https://qiita.com/ksgiksg/items/abcf386cccb19d08aa19

・JOIN関数とREPLACE関数の使い方について

https://mocabrown.com/blog/archives/6512

・SORT関数の使い方について

https://blog-tips.sekenkodqx.jp/2023/10/08/power-automate-function-sort/

# さいごに
この記事が同じ問題で困っている方の参考になれば嬉しいです。
現在は、Power Platform関連の知識を深めるため、以下のコミュニティで学んでいます。
初心者の自分でも楽しく学べているので、興味がある方はぜひ参加してみてください！

https://kimamani.connpass.com/

https://oujasan-to-issho.connpass.com/

Microsoft Power Automate Advent Calendar 2023、
カレンダー2はまだ空きがありますので、参加者絶賛募集中です。

https://qiita.com/advent-calendar/2023/powerautomate
electron + reactの学習

# はじめに
この記事は2023年度の振り返りです。

[前記事](https://qiita.com/youfuku/items/30c444d5330ab0f7cf5b)の続きとなります
何でどんな形で作るかが決まったものの、使い方の調査から開始です。

# electronが初めての方へ
Electronは、Windows、macOS、Linuxのデスクトップアプリケーションを作成するためのフレームワークです。Electronを使うことで、ウェブ技術であるHTMLやCSS、JavaScriptで作ったコンテンツをデスクトップアプリケーションにできます。

electronl製で有名なものとして、「Visual Studio Code」「Discord」「Notion」などがあります。

参考
https://www.electronjs.org/ja/docs/latest
https://ja.wikipedia.org/wiki/Electron_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2)

# 学習
https://qiita.com/udayaan/items/2a7c8fd0771d4d995b69
上記の記事などを元に、TODO作成から練習していきました。

# さいごに
かなり短い記事となってしまいましたが、ここではelectronとはなんぞや？と学習でTODOを元にしましたという回でした。
ラムゼーの定理ゲームを作りました

これはSiv3D Advent Calendar 2023 7日目の記事です。

# はじめに
今年の6月に「ラムゼーの定理ゲーム」というものを作りまして、少し前にアップデートしました。
https://x.com/spinal_spiral/status/1726907476686381204?s=20
<br>
これはどういうゲームかというと、
・6頂点の完全グラフの辺を赤か青で塗る
・そのうち3頂点を結ぶ三角形で、3辺が同じ色で塗られたものを作ると負け
というシンプルなゲームです。

1ゲーム3分くらいで終わるのでぜひやってみてください。
1人用モードも2人用モードも用意しています。
<br>
作る過程で、いろいろな経験を活かせる機会があったので、裏話的な感じでお話ししようかと思います。

# ラムゼーの定理って何？
これを読んでください(丸投げ)
https://ja.wikipedia.org/wiki/%E3%83%A9%E3%83%A0%E3%82%BC%E3%83%BC%E3%81%AE%E5%AE%9A%E7%90%86

実は私もふわっとした理解しかしていないんです…
<br>
このゲームでは、ラムゼーの定理から導かれる、

「6頂点の完全グラフの辺を2色で塗り分けると、そのうち3頂点からなる完全グラフで、すべての辺が同じ色で塗られたものが存在する」

という事実を用いています。
<br>
要は引き分けがないということを利用しました。

# 盤面の管理
盤面は6頂点の完全グラフとなっていて、それぞれの辺を塗り分けていくわけですが、内部で盤面を管理するにあたっては6×6の2次元配列を用いて辺の色だけを記録しています。
頂点はこのゲームにおいては変化しませんからね。

color[i][j] = 頂点 i と頂点 j を結ぶ辺の色を表す。(i<j)
0：黒、1：赤、2：青

という感じです。
<br>
各プレイヤーが手を打つたびに3重ループで全三角形を探索して同じ色の辺で結ばれたものがないかどうかを探索します。
3重ループといっても、三角形は 6C3 = 20 個しかないので動作が重くなることはないと判断しました。

# コンピュータはどんな方針をとるの？
このゲームには1人プレイモードがあり、その場合コンピュータと戦うことになります。
<br>
通常、コンピュータは自滅しない手の中からランダムで選択します。
その手が最善であるかどうかは考慮しません。
<br>
というのも、Ver1.0では最善手が分かっておらず、指した手の良し悪しが判断できなかったんですね。
その上、テストプレイでもなかなかいい勝負になっていた（個人の感想です）のでこの仕組みを採ることにしました。
<br>
残っている辺すべてについて、赤や青で塗ったときに自滅するかどうか（同じ色の辺でできた三角形ができるかどうか）を調べます。
一番場合の数が多いのは先攻1手目で、辺の数15通り×色数2通り=30通りです。
<br>
全探索しても三角形は20通りなので十分高速ですが、辺を1つ決めているということは、負けの原因となりうる三角形の頂点のうち2つを決めているということになるので、3つ目の頂点を残り4つのうちから一つ選んで調べればよいということになります。
<br>
そうして自滅しない手の候補を絞り込めたら、それらを配列にまとめてランダムに一つ選びます。

# 必勝法
Ver2.0では、コンピュータが必勝法を使えるようになりました。
どのように必勝法を見つけているかというと、総当たりです。
ゲーム木の全探索です。
<br>
ただ、普通にゲーム中に全探索をしようとすると、盤面の場合の数がそこそこ大きいので時間がかかってしまいます。
<br>
各操作の列で考えると、1手目は30通り、2手目は28通り… となるので
30×28×26×…×2 = 42,849,873,690,624,000(通り)
<br>
操作の順番が関係ない(途中で勝敗が決まらなければ、操作を入れ替えても盤面と次の手番プレイヤーは同じになる)ことを利用すると、
辺の色だけに注目すればよいので、3^15 = 14,348,907(通り)まで減ります。
<br>
でもまだ多いです。ここから盤面遷移の辺を張っていくことを考えると頂点数が10^7あるのはまずいです。
<br>
そこで、各状態の到達可能性を考えました。
同色辺の三角形ができた時点でゲームは終了するので、例えば以下のような赤と青の三角形が両方できるようなパターンは考えなくてよいです。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3271775/ddd595e3-1f3b-c0b5-85d4-485a2ab2090c.png)


また、盤面遷移の辺の結び方を考えます。
この盤面について考えてみます。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3271775/eb8d102e-19d0-ba85-f1ce-c0bce99c796e.png)


ゲームが途中で終了する条件を考えると、下の(a)からは遷移できますが、(b)からは遷移できません。

(a)
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3271775/d30c0e6d-ae6e-026b-2d67-55bd32effde6.png)
(b)
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3271775/ed2b8113-c806-47f6-70a3-977225f15473.png)


さらに、同色辺の三角形ができている盤面からは辺を出す必要がありません。
<br>
これらを考えると、
状態数：3,731,613
辺数：32,030,880
まで減らすことができました。

また、この状態遷移グラフについては、プレイ中に変化することはない(「状態・盤面」はもちろん変化しますが、「盤面」と「操作」によって「次の盤面」は1通りに決まる)ので、ゲーム木をプレイ中に構成するのではなく、あらかじめファイルにまとめておいてそれを読み込むことにしました。

### 結局必勝法を使うとどちらが勝つの？
結論から言うと、先攻が勝ちます。
今回はGrundy数というものを使って最善手を取ったときの勝敗の判定を行いました。

ただ、今回に関してはGrundy数が1なのか2なのかにあまり意味がなさそうなので、単純に勝ち負けだけを記録していてもよかったかもしれません。
<br>
みんなもコンピュータの必勝法を研究して対人戦で無双しよう！！
<br>
なかなかまとまりのない記事になってしまいましたが、ここまで読んでくださりありがとうございました。
Unity で 2 files found with path 'META-INF/kotlinx_coroutines_core.version' となってしまう問題

Unity にて、　以下のようなエラーが出てしまった時の対処法を見つけたので紹介します。


# 前提
Unity 2023.1.15f1 にて、 Android ビルド（aab）作成時に以下のエラーが出てbuild できませんでした。


```text: error
unity android.bundle.enableUncompressedNativeLibs=false' is deprecated.
```


```text: error
2 files found with path 'META-INF/kotlinx_coroutines_core.version'
```

```text: error
unity A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction
```

# 対処法

## ①
`Player` > `Android` > `Publish Setting` の

`Custom Luncher Gradle Template` にチェック ✅

## ②
49行目に以下を追加

```gradle:launcherTemplate.gradle
**PACKAGING_OPTIONS**
   packagingOptions {
       exclude('META-INF/kotlinx_coroutines_core.version')
    }
```

## ③
`Android Reslver` > `Force Resolve` を実行

## 4 (追記)
Android Target バージョンを34にする

# 参考

https://forum.unity.com/threads/the-option-setting-android-bundle-enableuncompressednativelibs-false-is-deprecated.1492733/

https://forum.unity.com/threads/warning-the-option-setting-android-bundle-enableuncompressednativelibs-false-is-experimental.1206991/

https://forum.unity.com/threads/gradle-build-enableuncompressednativelibs-false-is-deprecated.1487610/

https://forum.unity.com/threads/error-related-to-levelplay-update-2-files-found-with-path-meta-inf-kotlinx_coroutines_core-version.1493180/
Mavenとは

# 1. はじめに
STS(Spring Tool Suite)の環境構築をした際に、Mavenプロジェクトを作成しました。
その際に、Mavenについて調べた内容を記事にまとめます。

# 2. Mavenとは
Maven(メイヴン)とは、Javaプロジェクトのビルド、テスト、デプロイなどのプロジェクトライフサイクル管理ツールです。

## 2.1 プロジェクトの構造
　 一般的にsrc(ソースコード)とpom.xmlからなるディレクトリから構成される。
  ```text:hello-worldフォルダの中身
hello-world/
|-pom.xml
`-src/
  |-main/java/
  | `-com/example/
  |    `-App.java
  `-test/java/
    `-com/example/
      `-AppTest.java
```

## 2.2 POM(Project Object Model)
Mavenプロジェクトは、プロジェクトの設定情報をXML形式で記述したPOMファイルを持ちます。POMはビルドの設定、依存関係、プラグインなどのプロジェクトに関する情報を定義します。
具体的には下記のような要素があります。

| 要素       |       意味 |
|:-----------------|:------------------|
| modelVersion     |POM のバージョン。 特に変更する必要はありません。 |
| groupId          |プロジェクトを一意に識別する名前。 プロジェクトのルートパッケージ名を指定するのが一般的です。 |
| artifactId       |プロジェクトの成果物の名前。 作成する JAR や WAR, EAR ファイルなどの名前に使用されます。|
| packaging        |作成する成果物のパッケージング・タイプ。 jar (デフォルト), war, ear などがあります。|
| version          |プロジェクトのバージョン。 |
| name             |プロジェクトの表示名。 ドキュメントを作成するときなどに使用されます。 |
| url              |プロジェクトのサイトの URL。 ドキュメントを作成するときなどに使用されます。 |
| dependencies     |プロジェクトが依存するライブラリの情報。 |

pom.xmlには、ソースコードの場所や結果の出力先など情報はありません。これらの情報はapplication.properties等に追記します。

# 3. Mavenのコマンド
pom.xmlの設定に従い、コンパイルやテストがコマンド一つで実行可能になります。


## 3.1 コンパイル

```
mvn compile
```
プロジェクトをコンパイルするには上記コマンドを実行します。
`src/main/java/`配下ソースのコンパイルが行われます。


## 3.2 テスト

```
mvn test
```

ユニットテストを実行するには上記コマンドを実行します

デフォルトでは、以下のパターンにマッチするファイルがテストとして実行されます。

- `**/Test*.java`
- `**/*Test.java`
- `**/*TestCase.java`


## 3.3 公開

```
mvn deploy
```

作成した JAR ファイルをリモートリポジトリに配備するには、上記コマンドを実行します。

# おわりに
Mavenについて調査した内容をこの記事ではまとめました。
Mavenの理解に役立てていただけら幸いです。

# 参考
https://maven.apache.org/what-is-maven.html

https://www.techscore.com/tech/Java/ApacheJakarta/Maven/1/#maven-1-1

https://qiita.com/ASHITSUBO/items/6c2aa8dd55043781c6b4