OmniAICreator/Qiita-1.07M huggingface dataset:
[SwiftUI] 画面の幅、高さを取得する方法

# 概要 

タイトルの通り、SwiftUI で、画面の幅と高さを取得する方法についての記事です。 
なお、「いや、UIScreen.main.bounds で取得できるやろ、何言っとるんやお前」と思われた方もいらっしゃると思いますが、`UIScreen.main.bounds` だと、iPadOS で SplitView した際などに画面幅の変更が反映されないため、本記事を書きました。 

# ざっくりとした実装方針 
1. 画面サイズをグローバルで保存するため、ObservableObject に準拠したクラスを作成
1. ContentView で、子 View を GeometryReader でラップ
1. `.onChange` モディファイアを使って、GeometryReader を使って取得した画面サイズを1. で作成したストアに保存



# 1. 画面サイズを保存するクラスを作成 

ObservableObject に準拠した、画面サイズを保存するクラスを作成します。 `update` 関数を作ったのは、使用側で ScreenSizeStore を書き換えるような書き方をしたくなかったのが理由です。

```swift: ScreenSizeStore.swift
import Foundation
import Combine
import SwiftUI

final class ScreenSizeStore: ObservableObject {
    @Published private(set) var screenWidth: CGFloat
    @Published private(set) var screenHeight: CGFloat
   
    // 一旦デフォルトの画面サイズで初期化
    init() {
        self.screenWidth = UIScreen.main.bounds.width
        self.screenHeight = UIScreen.main.bounds.height
    }
    
    func update(width: CGFloat, height: CGFloat) {
        self.screenWidth = width
        self.screenHeight = height
    }
}
```

# 2. ContentView に GeometryReader 設置 

ContentView に、GeometryReader を設置します。ContentView の親となる App に置くと色々面倒そうだと思ったので、ContentView にしています。 

```swift: ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        GeometryReader { rootViewGeometry in
          // 子 View
          HogeView()
        }
    }
}
```

# 3. `.onChange` を使い、`ScreenSizeStore` を更新 
ContentView に、`@EnvironmentObject` の宣言の設置と、子 View への `.onChange` モディファイアの追加を行います。 

```swift: ContentView.swift
import SwiftUI

struct ContentView: View {
    @EnvironmentObject var screenSizeStore: ScreenSizeStore
    
    var body: some View {
        GeometryReader { rootViewGeometry in
            RootView()
                .onAppear {
                    screenSizeStore.update(width: rootViewGeometry.size.width, height: rootViewGeometry.size.height)
                }
                .onChange(of: rootViewGeometry.size) { updatedValue in
                    screenSizeStore.update(width: updatedValue.width, height: updatedValue.height)
                }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(ScreenSizeStore())
    }
}
```

最後に、App 側で、`ContentView` に `.environmentObject` モディファイアを追加する必要があります。(追加しないとエラーが出る) 

```swift: Hoge_App.swift
import SwiftUI

@main
struct Hoge_App: App {
    var body: some Scene {
        WindowGroup {
          ContentView()
              .environmentObject(ScreenSizeStore())
        }
    }
}
```




# 使用例

使用側は、`@EnvironmentObject` の宣言を追加してやるだけです 

```swift: CousinView.swift
import SwiftUI

struct CousinView: View {
    @EnvironmentObject var screenSizeStore: ScreenSizeStore

    var body: some View {
        VStack {
            HStack {
              (中略)

            }
            .frame(maxWidth: screenSizeStore.width)
        }
    }
}
```

# 最後に 

いかがでしょうか？ 
もし、「もうちょっといいやり方あるぞ」などのご意見あれば、お気軽にコメント欄に投稿していただければ幸いです。
Music.app の「アルバムの評価」をJXAで一括変更する (灰色の★対策)

# 「アルバムの評価」がすごく邪魔
**(macOS の話です)**
Music.app でお気に入りの曲には、星 (<font color="crimson">★</font>) で評価をつけているのだが、
何年か前に「アルバムの評価」という項目ができて、勝手に灰色の星 (<font color="grey">★</font>) が付くようになった。

これが厄介なのは、スマートプレイリストに「評価：<font color="crimson">★★★</font> より大きい」などと設定したとき、
「曲の評価」が未設定 (<font color="crimson">⭐︎⭐︎⭐︎⭐︎⭐︎</font>) でも「アルバムの評価」が <font color="grey">★★★★</font> だと条件成立してしまうことだ。

# <font color="grey">★</font> の評価を止める対策
残念ながら灰色の <font color="grey">★</font> を無効化する設定が見つからなかった。

仕方がないので、予め全部のアルバムに評価を付けてしまい、自動評価されないようにする。
これを手作業で設定するのは少し面倒なので、JXAを使って一括処理してみる。
(AppleScript は...文法がよくわからない...)

下記をファイルに保存して実行権限を付与し、Music.app が起動している状態で実行すると、
全部の曲の「アルバムの評価」が10になる。
※ albumRating の設定値は0〜100の範囲で、20で<font color="crimson">★</font>1つ分。0が「未評価」になるようだ。(10だと<font color="crimson">★</font>0.5個分?)

```javascript
#!/usr/bin/osascript -l JavaScript

var MusicApp = Application("Music");
const NEW_RATING = 10;

// ライブラリの全部の曲に対して (処理速度は1000曲/秒くらい)
for (var track of MusicApp.tracks()) {
    if (track.albumRating() != NEW_RATING) {
        console.log(track.name());          // 曲名
        console.log(track.albumRating());   // アルバムの評価
        try {
            track.albumRating = NEW_RATING;
        } catch {
            // ストアから未ダウンロードの曲など、設定ができないとき。
        }
        console.log(track.albumRating());   // アルバムの評価(確認)
    }
}
```

「アルバムの評価」欄は20単位に切り下げて表示するようなので、
1〜19 の適当な値を付けておけば<font color="crimson">★</font>が表示されず邪魔にならないと思う。
JavaScript_var、let、const変数宣言違い

- var,let,constの違いは
 ブロックスコープ : let, const
 関数スコープ : var
※ブロックは{}

例：
```js
<body>
	<button onclick="testVar()">click me</button>
	<p id="test1"></p>
	<p id="test2"></p>
	<script>
	function testVar(){
		if(true){
		   var varTest = "you are the best";
		   let letTest = "have a good night"; 
		   }
        // you are the best出力
		document.getElementById("test1").innerHTML = varTest;
        // エラー：Uncaught ReferenceError: letTest is not defined
		document.getElementById("test2").innerHTML = letTest;
	}
	</script>
</body>
```
- let,constの違い
定数宣言：const
非定数宣言：let
※varも非定数宣言可能だが、より狭いスコープでは可読性高いし、バグが少ない

例
```js
<body>
	<button onclick="testLet()">click me</button>
	<p id="test1"></p>
	<p id="test2"></p>
	<script>
	function testLet(){
		const constTest = "you are the best";
		let letTest = "have a good night"; 
        // have a good night出力
		document.getElementById("test1").innerHTML = letTest;
        // エラー：Uncaught TypeError: Assignment to constant variable.
		constTest = "have a fun day";
		document.getElementById("test2").innerHTML = constTest;
	}
	</script>
</body>
```

- 注意：

例：
```js
<body>
	<button onclick="testConst()">click me</button>
	<p id="test1"></p>
	<p id="test2"></p>
	<script>
	function testConst(){
		const constTest = {
 			a : "hava a fun day",
			b : 2
		}
        // hava a fun day出力
		document.getElementById("test1").innerHTML = constTest.a;
		constTest.a = "kitty is lovely";
        // kitty is lovely出力
		document.getElementById("test2").innerHTML = constTest.a;
	}
	</script>
</body>
```
【#52 エンジニア転職学習】goqueryでhref情報をスクレイピング

# はじめに
富山県に住んでいるChikaといいます。
毎日投稿を目標に、バックエンドエンジニア転職に向けた学習内容をアウトプットします。

 引き続きスクレイピングのプロジェクトをつくっていきます。

バックエンドエンジニアになるまでの学習内容は以前投稿した以下の記事を基にしています。


https://qiita.com/Chika110/items/ef54dddd565a0193ef44

# 本日の学習内容
本日は1つ目のタスクとしてURL一覧を取得するハンドラー部分を作成しました。
* **goqueryでURL一覧を取得するハンドラー作成　←Topics!!**


# goqueryでURL一覧を取得するハンドラー作成
対象である「マイナビ転職」の求人一覧ページ（https://tenshoku.mynavi.jp/list/）から、各求人詳細ページへのリンクURLを取得するところから始めます。
添付画像の`/jobinfo ~ /`をHTMLから取得してきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2041988/93e6c9fa-5709-c39c-769d-7e71cbb0f333.png)

手順はざっくり以下のとおりです。
1. http.GetとBodyでページ情報を取得
1. goquery.NewDocumentFromReaderでインターフェース作成
1. FindとAttrでHTMLタグとhrefで取得したい情報まで指定
1. SplitAfterNで文字列を区切る

実行すると↓のように綺麗に抽出できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2041988/3f048390-a75e-39b0-2f74-156cbd204bf4.png)


```main.go
package main

import (
	"fmt"
	"log"
	"net/http"

	"strings"

	"github.com/PuerkitoBio/goquery"
)

func main() {
	url := "https://tenshoku.mynavi.jp/list/pg2/"
	res, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
	}

	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	doc.Find("div.cassetteRecruit").Each(func(i int, s *goquery.Selection) {
		href, _ := s.Find("p.cassetteRecruit__copy > a").Attr("href")
		href = string([]rune(href))
		mynaviurl := strings.SplitAfterN(href, "/", 3)
		fmt.Printf("Review %d: %s\n", i, mynaviurl[0]+mynaviurl[1])
	})
}
```

#### 使用している教材はこちら↓

https://www.youtube.com/watch?v=tYbVl6N8oxI&list=PLv6F1QYhG31S-sDkutIQjCnKjFTvjuUus&index=1&t=2049s

# おわりに
最後までお読みいただきありがとうございました。
アドバイス・応援コメント等いただけますと幸いです。
VueJS のイベント１つから複数の関数を呼び出す方法

# はじめに

VueJS に $event 変数が用意されている事を教わって、便利だったのでメモ。

- https://vuejs.org/guide/essentials/event-handling.html#accessing-event-argument-in-inline-handlers

Thank you! @hadakadenkyu 

# イベントに複数の関数定義

はじめに駄目な例です。

```html:
<button @click="foo" @click="baa" />
```

みたいな事が許されていない件への対処法です。

ネットで検索すると、以下の方法が見つかります。ですが、関数側は空のイベントが渡ってきて微妙です。

```html:
<button @click="foo(); baa()" />
```
foo や baa が引数無しで呼ばれるので、イベントの内容を見て判断できません。
特に、event.target が見れないので汎用的な関数にし辛いです。

## foobaa の対応

これに関しては。foo baa の両方の処理をする function foobaa(e) を作るのが良いでしょう。

```js:
function foobaa(e) {
    this.foo(e);
    this.baa(e);
}
```

```html:
<button @click="foobaa"/>
```

## foobaa でない対応 (邪道)

このケースだとお勧めできませんが、どうしても複数呼びたい場合、$event が使えます。

```html:
<button @click="foo($event); baa($event)" />
```

なお、即時関数を使えば、変数名を短くできます。 このケースだとあまり変わらず。

```html:
<button @click="e=>{foo(e); baa(e)}" />
```

# ユースケース

複数の関数を呼ぶのは上記のように、そもそも複数設定すべきでないで話が終わりますが、少し違った使い方で $event が便利な事があります。

## リセットボタン

例えばリセットボタンを実装するのに、以下のように代入と関数呼びをセットで行いたい場合が思いつきます。
これは foobaa 方式では解決できません。

```html:
<input type="range" min="-100" max="100" v-model="x" />
<button @click="x=0" @click="foo" /> Reset </button>
```

この場合は、$event が便利です。

```html:
<input type="range" min="-100" max="100" v-model="x" />
<button @click="x=0; foo($event)" /> Reset </button>
```

単に反映した後に固定の処理をしたい場合は、watch で x を見張って foo を呼べば良いのですが。どこの button からイベントが来たかは watch からは分かりずらいし、汎用的な関数にし辛いので、素直に foo($event) で呼べると楽です。
TomeeでDB接続


### JDBCで接続
```
    public String connect(){
        
        final String URL = "jdbc:oracle:thin:@localhost:1521/XEPDB1";
        final String USER = "hr";
        final String PASS = "hr";
        final String SQL = "select * from sample_table";

        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(DbCon.class.getName()).log(Level.SEVERE, null, ex);
        }

        try (Connection conn = DriverManager.getConnection(URL,USER,PASS);
                Statement smt = conn.createStatement();
                ResultSet rs = smt.executeQuery(SQL)){
            
            while (rs.next()) {
                System.out.println(
                    "ID:" +rs.getString("ID") + 
                    "VALUE:" +rs.getString("Value"));
            }           

        } catch (Exception ex) {
            Logger.getLogger(DbCon.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return "";
    }
```

### DataPoolを使って接続
1.「\<CATALINA_HOME>\lib」配下にjdbcドライバを配置する。

2.「tomee.xml」を修正する。
``` tomee.xml
  <Resource id="Oracle Database" type="DataSource">
    JdbcDriver  oracle.jdbc.OracleDriver
    JdbcUrl jdbc:oracle:thin:@localhost:1521/XEPDB1
    UserName    hr
    Password    hr
  </Resource>
```

```
    @Resource(name = "Oracle Database")
    private DataSource dataSource;
    
    public String connect(){
        final String SQL = "select * from 'sample_table'";
        
        try(final Connection connection = dataSource.getConnection();
            Statement smt = connection.createStatement();
            ResultSet rs = smt.executeQuery(SQL)){
            
            while (rs.next()) {
                System.out.println(
                    "ID:" +rs.getString("ID") + 
                    "VALUE:" +rs.getString("Value"));
            } 
            
        } catch (SQLException e) {
                    e.printStackTrace();
        }
        return "";
    }
```

なぜか怒られるので、原因を確認中
java.sql.SQLSyntaxErrorException: unexpected token: sample_table
Caused by: org.hsqldb.HsqlException: unexpected token: sample_table






参考

https://www.tomitribe.com/blog/configuring-jdbc-in-tomee/

https://tomee.apache.org/common-datasource-configurations.html
JSONファイルをローカル内で読み込もうとしたら、CORSエラー発生【Access to XMLHttpRequest at 'file:///〜】


[基礎から学ぶ Vue.js](https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6-Vue-js-mio/dp/4863542453/ref=as_li_ss_tl?SubscriptionId=AKIAJHZKRQV6NN3MBEUQ&linkCode=sl1&tag=cubix-22&linkId=3b2923e7c205b374c15b2e75bd173305)のP83【外部からデータを取得する】で下記エラーが発生。
解決したので記載します。

# 発生したエラー
![スクリーンショット 2022-08-17 19.44.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223785/442d00e0-e5aa-f33b-73e1-105d0a7b9390.png)

```
Access to XMLHttpRequest at 'file:///Users/〇〇/Desktop/JS/list.json' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
```

# この記事が役に立つ方
・上記エラーを解消されたい方
# 環境
```
- OS: macOS Monterey 12.5
- Vue.js: 下記CDNを利用　
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
- axios: 下記CDNを利用
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
```
# 解決
1.ターミナルを立ち上げ、ソースがある場所へcdコマンドで移動。
2.http-server を使って簡単に静的ファイル用のローカルサーバーを立てます。

コマンド実行。
```
npx http-server
```
以下、移動したディレクトリ内のファイルにアクセスできる静的サーバーが立ち上がります。
```
Need to install the following packages:
  http-server
Ok to proceed? (y) y
Starting up http-server, serving ./

http-server version: 14.1.1

http-server settings: 
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none

Available on:
  http://xxx.0.0.1:8080
  http://xxx.xxx.xxx.xxx:8080
Hit CTRL-C to stop the server
```
-o オプションをつければサーバー立ち上げと同時にブラウザでページを開きます。

```
npx http-server -o
```
![スクリーンショット 2022-08-17 21.18.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223785/e5d88471-26b3-097c-0261-52026cc892f0.png)

表示されました。

どなたかのお役に立てれば幸いです。
【脱Excel方眼紙】Wordの基本操作 ～これだけ覚えれば大丈夫～

# はじめに

システム開発では「プログラム言語」が重要ですが、
ドキュメント作成でも「ドキュメント形式」が重要です。

生産性／可読性／保守性に大きな影響を与えます。

# 現場の実情

Markdown／Word／Excel／PowerPointなど、
さまざまな選択肢から、ドキュメント形式を選定することが理想的です。

ですが、実情としては、すべてのドキュメントを「Excel」にしてしまうことも多いです。（Excel方眼紙）

選定した理由は、つぎのような理由が多いです。

- Excelに慣れているから。
- WordやMarkdownの使い方がわからないから。
- 既存のExcelのテンプレートを流用したいから。

このような観点も重要ですが、
つぎのように、それぞれの強みへ注目することも大事だと考えています。

- 関数で自動集計するために、Excelにする。
- スタイルで素早く文章作成するために、Wordにする。

# 記事の目的

「WordやMarkdownの使い方がわからない」というのは、
選択肢が狭まっていることになるため、勿体ないなと感じています。

選択肢を広げてもらいたく、
「Wordを使ったことがない人」をターゲットに、基本操作を説明します。

# 推奨の設定

慣れないうちは、すべての編集記号を表示することを推奨します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/a3268b41-ced2-f4b6-acdb-176c7504dc8b.png)

# 基本操作

## 改行と改段落

改段落で、意味のある単位で文章を区切って、読みやすくできます。
改行で、行を改めて、読みやすくできます。

| 操作   | キー                                | 編集記号 |
| ------ | ----------------------------------- | ------- |
| 改段落 | <kbd>Enter</kbd>                    | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/1e5825a9-aad8-c573-5690-9dcbf1ab5be3.png) |
| 改行   | <kbd>Shift</kbd> + <kbd>Enter</kbd> | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/517e3b28-8547-8393-f8d6-099d459f3d89.png) |

### 使用例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/3a82cd6d-8a23-b54c-c0cd-e347d176df8d.png)

## 改ページ

つぎのページに切り替えることができます。

| 操作     | キー                               | 編集記号 |
| -------- | ---------------------------------- | ------- |
| 改ページ | <kbd>Ctrl</kbd> + <kbd>Enter</kbd> | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/073c84c5-8c82-4ccb-0f1c-8fa3d1870da6.png) |

### 使用例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/5ec40a42-ae0e-3b59-3c24-e09f7fadf9f6.png)

## スタイル

スタイルを選択すると、段落の書式を切り替えることができます。

### 使用例

![スタイル.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/348c12fb-0ece-c046-8749-58a0997e930c.jpeg)

## ナビゲーション ウィンドウ

適切にスタイルを設定していれば、文章構成を表示できます。
クリックすると、該当箇所までスクロールすることもできます。

ドキュメントの情報量が多いときに便利です。

### 使用例

![ナビゲーション_ウィンドウ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/46c961c7-71f9-7966-3bce-432d1851b355.jpeg)

## 表

表が作成できます。

### 使用例

![表.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/4f7ef530-2200-eab4-9d6b-b7f0410b200a.jpeg)

## 箇条書き

箇条書きが作成できます。

### 使用例

![箇条書き.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/f654a2d8-d5ee-a4e9-cf04-957f647eab11.jpeg)

## コメント

コメントが作成できます。
残課題の表現などに便利です。

使用例では塗りつぶしていますが、コメントの上部に名前が表示されます。

### 使用例

![コメント.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/963434bf-7d39-efac-9f66-978137315ed2.png)

## 変更履歴

変更履歴を残せます。
レビューの指摘などに便利です。

変更箇所が赤くなるため、変更内容を把握できます。
変更内容に問題がなければ、承諾をして、本文に反映してください。

### 使用例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542211/2dd1ae3e-18cb-e5c0-3c65-611e398522ac.png)
URPでParticleを最前面に表示する


# TL;DR
通常の描画用と最前面に描画したいオブジェクトのみを描画するOverlayのカメラを作成し表示する

# 手順
- Layerの追加
- Cameraを追加＆設定変更
- オブジェクトのLayerを変更

## Layerの追加
前面に表示するオブジェクトをまとめるLayerを ```Edit > Project Settings > Tags and Layers``` から追加する
ここでは```MostFrontParticles```を追加した

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374827/7f75d1d3-5584-2c19-5e1d-c6ed39dacc1e.png)


## Cameraを追加＆設定変更
Hierarchyに新しくカメラを追加し、必要であれば現在のカメラの子要素に入れTransformをリセットする

その後、RenderTypeをOverlayに、Rendering > CullingMaskを先程追加したレイヤーに変更する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374827/80e8b695-6139-6294-55e7-816c9f11f98f.png)


シーン内のメインで使用するカメラの```Stack```に先程追加したカメラを追加する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374827/575e74a5-075a-f152-6f0e-04bec2eca3cc.png)



## オブジェクトのLayerを変更
前面に表示したいオブジェクトのレイヤーを先程追加したレイヤーに変更する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374827/a7ad8f12-c73e-d1e9-9b72-94f1cfc1b50e.png)


# 完成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374827/f9edf335-58fd-767c-61cc-820ee77370a5.png)
Ethereumのガス代について

![GAS1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524398/f1bc91ae-6007-e19f-c287-3c12256a3f3d.jpeg)
弊社では現在、メタバース事業を推進しており、日々奮闘している訳だが、最近になり「暗号資産」特有の扱いの難しさの直面している。
慣れない言葉や概念がいくつかあるが、その中でも今回は謎多き「ガス代」に関して記述していく。

## Ethereumとガス代
まずはガス代を語る上で欠かせない**Ethereum**についての説明を少々。
Ethereumではブロックチェーンの仕組みを利用しており、世界中のユーザーで同じ履歴(台帳)をそれぞれで所有し、安全性を担保している。
そしてビットコインのブロックチェーンとは違い、ブロックチェーン上にプログラミングを実装する事により、高度なやり取り（スマートコントラクト）を可能にし、現在では新しいプラットフォームの一つとなっている。
そしてそれは、世界中の人々がそれぞれ計算、承認(マイニング)を行う事により支えられている。
マイニングをする人(マイナー)は特定の企業の社員が給料を貰って働いていたり、ボランティアで行なっている訳ではない。
そんな彼らへのインセンティブ(燃料)になるのが『ガス代（ETH）』という訳だ。
![GAS2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524398/e5c839a6-2434-d5cf-b8ee-1f9d0620ba9f.jpeg)

## ガス代に関わる項目
利用者が支払うガス代は、いくつかの計算を経て決定されている。ただ、その計算に関わる項目は幾つかあるらしい。様々な資料を調査しているが、資料ごとに項目の呼び方が違っていたりするので、ここで一旦整理しようと思う。名称よりも何について説明しているのかを注視してほしい。
※計算にクリティカルに関わるものを抜粋

* **Gas Limit**
 ガス量、ガスユニットとも呼ばれ、トランザクションの種類により変化するGas量の目安であり上限。　(例：ETHの送金であれば約21,000unit)

* **Gas Price**
 1Gas(上記1unit)辺りの値段を自分で決める事が出来る。プライオリティフィーとも。高い程優先的に処理しされ、安すぎると処理が成立しない事態も発生する。単位はGwei（ギガ ウェイ）
　※1Gwei＝0.000000001ETH

* **Gas Fee**
  一般的に最終的支払うガス代の金額。単位が"円"であるか"ETH"であるかなどの確認は必要。

## 計算方法
Gas Limit(21000unit(処理内容で変動)) × Gas Price(40Gwei(時価)) = Gas　Fee(840,000Gwei)
Gas Fee(840,000Gwei) × 0.000000001(GweiをETHに変換) = Gas Fee(0.00084ETH)
Gas Fee(0.00084ETH) × 1ETHの価格(300,000円) ＝ 252円（実際の支払い金額）
※ETHの送金の例(送金金額に依存しない)
> 参考：[DappsWith　/　【ガス代計算ツール有】ブロックチェーンゲームのトランザクション、ガス(GAS)代とは？](https://ethereumprice.org/gas/)

## 安くするには
今までの情報を加味し、コストを抑えるとすると、ETHが安い時に取引する、または任意で変える事の出来る値(Gas Price)は安く出来る事がわかる。（別の話だがレイヤー2を使うのもアリ）
Gas Priceは優先的に処理してもらい、早く取引を完了させたい時に高く設定するものなので、逆に急ぎでない場合や競争率の低い時間、つまり取引量の少ない時間帯に行う事で抑える事が出来る。
以下リンクは直近の取引の量を調べる事が出来るサイトだ。これを見る限り2022年8月現在は月曜日が取引量が少ないため、狙い目だ。
> 参考：[ethereumprice
](https://dapps.gamewith.jp/?p=90)

![周別の比較](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524398/8f5f7a1d-8f8d-0a75-d737-c59e4dd5121f.jpeg)


## まとめ
今回ガス代について調べてみて表現する名称などは、ソースにより区々であったが、根本的な概念については共通点は多かった様に思える。
現在EthereumはPoWからPoSへ移行予定のため、先述した求め方がいつまで利用出来るかは解らない。変わらない可能性もあるが、、
ただイーサリアムの概念、仕組みを理解しておく事で新しい仕組みが出てきた時の理解の助けになれば良いと思う。
ぼくの TypeScript

毎回調べるのが面倒なので備忘録。src 内のソースを呼んで dist に吐き出す nodejs 向けの設定例。

## インストール

```
npm install typescript --save-dev
```

参考: https://www.typescriptlang.org/

## tsconfig の作成

tsconfig.json とは tsc を入力ファイル無しで実行すると参照する設定ファイル。環境ごとのおすすめ設定を https://github.com/tsconfig/bases/ の中から選んで extends に指定するとおすすめを元に細かく設定できる。

```
npm install --save-dev @tsconfig/node16-strictest
```

tsconfig.json の内容例:

```json
{
  "extends": "@tsconfig/node16/tsconfig.json",
  "compilerOptions": {
    "incremental": true,
    "noEmitOnError": true,
    "outDir": "dist"
  },
  "include": ["src/**/*"]
}
```

* extends: おすすめ設定ファイルの名前。
* files: コンパイルするファイルを一つ一つ指定する。
* include: コンパイルするファイルやディレクトリをまとめて指定する。拡張子が指定しなければ ts, tsx, d.ts などを選んだ事にする。
* exclude: include の中で除外するファイルやディレクトリ。
* compilerOptions.outDir: .js ファイル出力先。無ければ .ts ファイルと同じ場所に出力する。
* compilerOptions.incremental: ビルドが速くなるらしい。
* compilerOptions.noEmitOnError: true だとエラー時 .js を出力しない。デフォルトは false

参考: 
* https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
* https://www.typescriptlang.org/tsconfig

## 構文検査ツール ESLint の設定

```
npm install eslint --save-dev
npm init @eslint/config
```

質問に答えると設定完了 (airbnb guide を選んでいます)

```
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · airbnb
✔ What format do you want your config file to be in? · YAML
```

参考:
* https://eslint.org/docs/latest/user-guide/getting-started

## TypeScript ESLint の設定

上記質問に Does your project use TypeScript? · Yes と答えたので typescript-eslint はインストールされている。.eslintrc.yml の extends に追加。これをやらないと TypeScript と ESLint が喧嘩する。

```yaml
  - plugin:@typescript-eslint/recommended
  - plugin:@typescript-eslint/recommended-requiring-type-checking
```

参考:
https://typescript-eslint.io/docs/

## コード整形ツール Prettier の設定

```
npm install --save-dev --save-exact prettier eslint-config-prettier
echo {}> .prettierrc.json
```
 
.prettierignore ファイルを作成して整形したくないディレクトリを書く。

```
.serverless/
node_modules/
dist/
```

.eslintrc.yml に extends: [ prettier ] を追加すると、ESLint と prettier が喧嘩しなくなる。最終的にぼくの .eslintrc.yml はこんな風になった。

```yaml
env:
  browser: true
  es2021: true
extends:
  - airbnb-base
  - plugin:@typescript-eslint/recommended
  - plugin:@typescript-eslint/recommended-requiring-type-checking
  - prettier
parser: "@typescript-eslint/parser"
parserOptions:
  ecmaVersion: latest
  sourceType: module
plugins:
  - "@typescript-eslint"
rules:
  no-console: off
```

todo: monorepo で husky を使う方法

参考
* https://prettier.io/docs/en/install.html

## package.json に登録する

あとは実行しやすいように package.json の script に書く。

```json
  "scripts": {
    "watch": "tsc --watch",
    "lint": "eslint . --fix",
    "prettier": "prettier --write ."
  },
```
PHP オブジェクトのクローン

# 概要

- オブジェクトのクローンについてまとめる

# やりたいこと

- Aクラスのオブジェクトを`$a`に格納してからそれを複製して`$b`に格納し別のオブジェクトとして扱いたい。
- 下記のようなコードを考えた。

    ```php
    <?php
    
    class A {
        public $str = 1;
        
        public function save($input){
            $this->str = $input;
        }
    }
    
    // インスタンス化してオブジェクトを$aに格納
    $a = new A();
    echo $a->str . "\n";
    
    $b = $a;
    $b->save('更新した文字列');

    // 「更新した文字列」が出力されてほしい
    echo $b->str . "\n";
    
    // この時 intの1が出力されてほしい
    echo $a->str . "\n";
    ```

- 下記のように出力された。

    ```
    1
    更新した文字列
    更新した文字列
    ```

- 期待と違う。本当は下記のように出力されてほしい。

    ```
    1
    更新した文字列
    1
    ```

- 厳密には異なるがオブジェクトは参照渡しのような振る舞いをするらしい。そりゃいくら途中で`$b = $a;`のように記載して`$b`にだけ変更を加えても`$a`にも影響が及ぶわけだ。
    - [https://www.php.net/manual/ja/language.oop5.references.php](https://www.php.net/manual/ja/language.oop5.references.php)
- 下記のようにcloneを用いることでオブジェクトの複製を作成する事ができる。

    ```php
    <?php

    class A {
        public $str = 1;

        public function save($input){
            $this->str = $input;
        }
    }

    $a = new A();
    echo $a->str . "\n";

    $b = clone $a;
    $b->save('更新した文字列');
    echo $b->str . "\n";

    echo $a->str . "\n";
    ```

- 期待する出力を得ることもできた。

    ```
    1
    更新した文字列
    1
    ```

- ちなみにクラスに `__clone()`のマジックメソッドを記載して中に処理を書くことでクローン時のみ実行される処理を記載する事ができる。

    ```php
    <?php
    
    class A {
        public $str = 1;
        
        public function __clone(){
            echo 'cloneしてます〜' . "\n";
        }
        
        public function save($input){
            $this->str = $input;
        }
    }
    
    $a = new A();
    echo $a->str . "\n";
    
    $b = clone $a;
    $b->save('更新した文字列');
    echo $b->str . "\n";
    
    echo $a->str . "\n";
    ```

- 下記のように出力される。

    ```
    1
    cloneしてます〜
    更新した文字列
    1
    ```


# 参考文献

- [https://www.php.net/manual/ja/language.oop5.cloning.php](https://www.php.net/manual/ja/language.oop5.cloning.php)
- [https://www.php.net/manual/ja/language.oop5.references.php](https://www.php.net/manual/ja/language.oop5.references.php)
- [https://www.php.net/manual/ja/language.oop5.magic.php]()
ガンダムはC言語で動いていた・・・のか？

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

MovieCodeなどを参考にロボットアニメなどのコードを調査しました
https://moviecode.tumblr.com/
ユーザー退会時にユーザー関連データを削除するには（Monaca/JavaScript編）


iOSアプリではユーザー退会時にユーザーデータを削除する要件があります（2022年06月30日より）。プライバシーに配慮した要件ですが、アプリ開発者にとっては追加の対応が求められます。

ニフクラ mobile backend（以下NCMB）を使っている場合、ユーザーの退会処理（物理削除）は簡単に行えます。しかし、ユーザーに関連したデータはどう削除したら良いでしょうか。

今回はMonacaやWebアプリケーションで、JavaScript SDKを使った関連データ削除方法を解説します。

## ユーザーデータの削除

ユーザーデータを削除するのは簡単です。必ずログインした状態で行う必要があります。

```js
const user = ncmb.User.getCurrentUser();
user.delete(); // ユーザー削除 = 退会
```

ただ、注意して欲しいのは `ユーザーデータの削除は関連データをすべて削除してから` にして欲しいということです。ユーザー関連データを削除できる権限がユーザー自身に紐付いている場合、データを削除すると削除権限がなくなる可能性があります。

## 関連データの検索

### ポインターの場合

大抵の場合、NCMBのポインター機能を使ってユーザーデータを紐付けているかと思います。そこで、データストアを検索して、そのユーザーに関連したデータだけを取得します。今回は例として、ユーザープロフィール `UserProfile` クラスにあるデータを検索する例を紹介します。

```js
const UserProfile = ncmb.DataStore('UserProfile');
const userProfiles = await UserProfile
	.equalTo('user', {
		__type: 'Pointer',
		className: 'user',
		objectId: user.objectId,
	})
	.fetchAll();
// 全削除
await Promise.all(userProfiles.map(u => u.delete());
```

ポイントは、ポインターを使っているカラムの検索です。 `equalTo('user', user)` のようにしたくなりますが、これでは検索ができませんので注意してください。ポインターの形に合わせて検索を行うことで、該当ユーザーのデータだけを取得できます。後はすべて削除すれば良いでしょう。

### リレーションの場合

関連データをポインターではなく、リレーションで保存している場合もあるかと思います。その場合は次のようになります。

```js
const UserProfile = ncmb.DataStore('UserProfile');
// 関連データの検索
const userProfiles = await UserProfile
	.relatedTo(user, 'user')
	.fetchAll();
// 全削除
await Promise.all(userProfiles.map(u => u.delete());
```

### 注意点

UserProfileクラスに入っているデータの編集権限がユーザーにある場合のみ、削除可能です。権限がないと削除はできませんので注意してください。

## デバイストークンの削除

Monacaアプリの場合、プッシュ通知を送信する際にデバイストークンを取得しているかも知れません。その場合、このデバイストークンも削除対象になります。

実装としては次のようになります。ncmb-push-monaca-pluginとJavaScript SDKの両方が必要です。

```js
// ncmb-push-monaca-pluginによる処理
window.NCMB.monaca.getInstallationId(async (deviceToken) => {
	// JavaScript SDKの処理
	const installation = await ncmb.Installation
		.equalTo('deviceToken', deviceToken)
		.fetch();
	// デバイストークン削除
	await installation.delete();
});
```

## ログアウトを行う

ユーザーデータを削除した後には忘れずにログアウトを行ってください。なお、この時SDKで提供されているログアウト処理は必ず失敗します。なぜならユーザーが存在しない状態になるとセッションが無効になるためです。

そのため、強制ログアウトが必要になります。これはlocalStorageの該当データ削除と、セッショントークンを空にすればOKです。

```js
localStorage.removeItem(`NCMB/${ncmb.apikey}/currentUser`);
ncmb.sessionToken = null;
```

これでログアウト状態になり、ユーザーデータがすべてなくなった状態になります。

## まとめ

今回は物理削除の例を紹介しました。論理削除の場合は別なやり方になるかと思いますので注意してください。

[ドキュメント : 開発者向けドキュメント | ニフクラ mobile backend](https://mbaas.nifcloud.com/doc/current/index.html#/JavaScript)
AWS SSOを使ってSonarQubeにSAML認証でログインする


IAM Identity Center（旧AWS SSO）でSonarQubeをアプリケーション登録する手順を確認した時のメモ。
作業手順としては以下のようになる。
1. AWSで雛形のアプリケーションの作成
2. SonarQubeの設定変更
3. AWSで残りのアプリケーション設定

なお、SonarQubeは構築済みであることが前提となる。

## AWS側作業その１
IAM Identity Centerからアプリケーションの割り当ての中のアプリケーションを選択し、「アプリケーションの追加」をクリックする。\
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/f5928495-da39-324b-40dd-c406e6ec6149.png" width="500">
「事前統合アプリケーション」でSonarQubeを入力し、出てきたものをクリックして次へを押す。\
「アプリケーションを設定」の表示名と説明を好きなもので埋めて一旦SonarQubeに移動する。

## SonarQube側作業
Administration > Configuration > General Settings > Security > SAML\
からSAMLの設定画面に飛び、Enabledを有効化する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/031bff4d-ba4f-dc7b-1e45-24093d4ff07d.png" width="500">

以下の項目を埋めていく(入力しない箇所はデフォルト値を利用)。

- Provider ID：`IAM Identity Center SAML 発行者 URL`のURL
- SAML login url：`IAM Identity Center SAML 発行者 URL`のURL
- Provider certificate：`IAM Identity Center 証明書`の内容から-----BEGIN CERTIFICATE----- と -----END CERTIFICATE----- を削除したもの
- SAML user login attribute：`login`
- SAML user name attribute：`username`
- SAML user email attribute：`email`

次に、Administration > Configuration > General Settings > GeneralでServer base URLの項目にSonarQubeのURLを設定する。
これを実施しないと、AWS SSOからログインする際に403が返ってログインできないので注意。

## AWS側作業その2
先程の画面から再開し、以下設定していく。
- アプリケーションのプロパティ
  - アプリケーション開始 URL：`https://<SonarQubeのURL>/sessions/init/saml?return_to=%2F`
- アプリケーションメタデータ
  - アプリケーション ACS URL：`https://<SonarQubeのURL>/oauth2/callback/saml`
  - アプリケーション SAML 対象者：`sonarqube`

設定が終わったら、「送信」ボタンを押してアプリケーション作成作業を完了させる。\
その後、アプリケーションに対してユーザを割り当てる。

## 確認
アプリケーションを割り当てたユーザでログインすると、以下のようにSonarQubeが表示される。\
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/86af5e31-4ba6-088c-2731-14aa277fc50a.png" width="500">

クリックするとSonarQubeにログインできる。\
また、SonarQubeにログインすると、SAMLによるログインボタンも表示されるようになる。\
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/1d4f855e-07d8-34a3-772b-a0308bb55f4a.png" width="500">
なお、従来のユーザでログインしたい場合はMore optionsをクリックすればログインできる。
ParameterizedTestを用いたテストコードサンプル

### パターン
- 基本形
- 区切り文字を指定（引数にカンマが含まれている場合に使用する）
- MethodSourceを使用（引数にオブジェクトやListを使う場合に使用）

### サンプルコード
```java
class HogeTest {

    @InjectMocks
    HogeServiceImpl service;

    @ParameterizedTest
    @CsvSource({"1, A", "2, B"})
    void test(int num, String str) throws Exception {
        doReturn(str).when(service).hoge(num);
        assertEquals(str, service.hoge(num));
    }

    @ParameterizedTest
    @CsvSource(delimiter = '|', value = {"1 | A", "2 | B"})
    void delimiterTest(int num, String str) throws Exception {
        doReturn(str).when(service).hoge(num);
        assertEquals(str, service.hoge(num));
    }


    @ParameterizedTest
    @MethodSource("methodSourceTestParams")
    void methodSourceTest(int num, String str) throws Exception {
        doReturn(str).when(service).hoge(num);
        assertEquals(str, service.hoge(num));
    }

    static Stream<Arguments> methodSourceTestParams() {
        return Stream.of(Arguments.of(1, "A"), Arguments.of(2, "B"));
    }
}
```
Windows App Certification Kitにてパッケージの複数回指定エラー

# エラーの状況

![2022-08-17 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729778/3b5ada17-4de6-8791-1421-236777d2fdc6.png)

'Microsoft.Web.WebView2'を用いたアプリについて、ストア提出用のパッケージを作成し、引き続き'Windows App Certification Kit'が起動されるが、テスト開始前の初期化時に上のダイアログが出てエラーとなる。

:::note info
'Microsoft.Web.WebView2'を用いるために導入した'Microsoft.UI.Xaml'パッケージのバージョンは、最新の安定版2.8.1
:::

# とりあえずの対処

nugetキャッシュの初期化、'bin','obj'フォルダのクリアなど、nugetまわりのトラブル対処法をいくつか試みるが、解消せず。

結果として、'Microsoft.UI.Xaml'の一つ前の安定版にダウングレードすることで対処。安定版2.8.1に内在する問題と考えられる。
(時間があれば、次のリリースを待ってもよいが...)
【随時更新】資料に使えるおしゃれなフリーイラストリンク集

# 紹介リスト
| サイト名称 | リンク |
| ---- | ---- |
| **shigureni** | https://www.shigureni.com/ |
| **ちょうどいいイラスト** |  https://tyoudoii-illust.com/ |
| **ガーリー素材** | https://girlysozai.com/ |
| **Loose Drawing** | https://loosedrawing.com/ |
| **ソコスト** | https://soco-st.com/ |
| **human pictogram 2.0** | https://pictogram2.com/ |
| **flaticon** | https://www.flaticon.com/ |
| **ハンコでアソブ** | http://hankodeasobu.com/ |
| **ICOON MONO** | https://icooon-mono.com/ |
| **SHILHOUETTE ILLUST** | https://www.silhouette-illust.com/ |
| **Linustock** | https://www.linustock.com/ |
| **ダ鳥獣戯画** | https://chojugiga.com/ |
| **SMALL ROAD** | https://small-road.com |
| **フキダシデザイン** | https://fukidesign.com/ |
| **Unsplash** | https://unsplash.com/ |
| **Pixtabay** | https://pixabay.com/ja/ |
| **お道具箱** | https://www.xn--t8ju21hu42avir.com/ |

# カラフル系
## shigureni
- かわいい手書き風イラスト
- 量は多くない
- ピンポイントな絵が多い笑

https://www.shigureni.com/

## ちょうどいいイラスト
- 仕事の際に使いやすそうな絵が多い

https://tyoudoii-illust.com/

## ガーリー素材
- 名前通りガーリーなイラスト
- 有料の素材もあるがタブが分かれているのでわかりやすい

https://girlysozai.com/

## Loose Drawing
- かわいい手書き風イラスト
- 色を変更できるのがポイントが高い！

https://loosedrawing.com/

## ソコスト
- かわいい手書き風イラスト
- 手書き風だが仕事でも使えるぐらいのフォーマル加減

https://soco-st.com/

## human pictogram 2.0
- 出口でおなじみピクトさんのイラスト集

https://pictogram2.com/

## flaticon
- ロゴやステッカー、アニメーションアイコンなどもある

https://www.flaticon.com/

# モノクロ系
## ハンコでアソブ
- イラストがかわいらしい
- ハンコのようにちょっとかすれている

http://hankodeasobu.com/

## ICOON MONO
- シンプルで使いやすい
- 6000以上もあるのできっと気に入るのもある...はず！

https://icooon-mono.com/

## SHILHOUETTE ILLUST 
- ICOON MONOよりちょっとカジュアル
- 10000以上もあるらしい

https://www.silhouette-illust.com/

## Linustock 
- 線画イラスト集
- 色付き、モノクロ両方を使える

https://www.linustock.com/

## ダ鳥獣戯画
- 獣戯画の甲巻を中心にベクターデータ化、カラー化した素材集

https://chojugiga.com/

# その他
## SMALL ROAD
- 配色の見本に使える
- 写真の中の配色を抽象的に表現してくれている

https://small-road.com/

## フキダシデザイン
- その名の通り多種多様な吹き出しの素材がある
- 吹き出しにこんなに種類があることに驚愕

https://fukidesign.com/

## Unsplash
- 資料中に使えそうな写真集

https://unsplash.com/

## Pixtabay
- 資料に使えそうな写真集
- ファイルサイズを細かく決められる

https://pixabay.com/ja/

## お道具箱
- 配色やフォント、素材などのリンクをまとめているサイト

https://www.xn--t8ju21hu42avir.com/
[Laravel]アクセサ、ミューテタの基本

# はじめに
今回はLaravelのアクセサ、ミューテタについて調べたので、備忘録として残しておきます。

# 環境
- Laravel：8.83.4

# 1. アクセサ、ミューテタとは
アクセサとは、データベースから取得したデータをフォーマットするためのメソッドです。
アクセサを使うことで、加工したデータをフロント側に返すことができます。

ミューテタは、インスタンスに値を設定する際にデータをフォーマットするためのメソッドです。

# 2. アクセサを使ってみる
下記のコードで`updated_atカラム`を取得する時に**getUpdatedAtAttribute**が実行されます。
下記は、DBにタイムスタンプ型で存在する`updated_atカラム`を「2022-08-03 20:00:00」のような書式で返したい場合のコードです。
引数の`$value(updated_atのデータ)`を加工して返します。
```php
public function getUpdatedAtAttribute($value)
{
    return Carbon::parse($value)->toDateTimeString();
}
```

下記のようにDBからデータを取得するときに、**getUpdatedAtAttribute**が実行され、データを加工して返します。
```php
$post = App\Post::find(1);

$updatedAt = $post->updated_at;
```
上記のように、アクセサはDBからのデータ取得時にデータを加工できます。

# 3. ミューテタを使ってみる
下記のコードの場合、`contentカラム`の保存時に自動で　**setContentAttribute**が実行されます。

```php
public function setContentAttribute($value)
{
   $this->attributes['content'] = strtolower($value);
}
```
このようにすると、 文字列で `"Content"`という値をインスタンスに設定するときに、 `"content"` と小文字に変換されてから保存されます。

```php
$post = Post::find(1);

$post->content = 'Content';
$post->save();
```

上記のように、ミューテタはデータをDBに保存する際にデータを加工することができます。

以上で実装完了です。
# 4. 参考文献

https://readouble.com/laravel/8.x/ja/eloquent-mutators.html
WireMockを用いたテスト実装サンプル

## 使用しているライブラリ
- com.github.tomakehurst:wiremock-jre
- io.rest-assured:rest-assured

JSONのアサーションには以下のライブラリを使用（サンプルではhamcrest-jsonを使用）
- skyscreamer のJSONassert(こっちのが差異があったときに何が異なるかわかりやすいらしい)
- hamcrest-json のsameJsonAs

## jsonファイルの置き場所
```
src/test/resources/__files
```


### テストクラスサンプル
```java
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
@ActiveProfiles("test")
class HogeTest {

    private final WireMockServer wireMock = new WireMockServer(
            WireMockConfiguration.options().port(8081).notifier(new Slf4jNotifer(true)));

    @BeforeEach
    void beforeEach() {
        this.wireMock.start();
    }

    @AfterEach
    void afterEach() {
        this.wireMock.stop();
        this.wireMock.resetAll();
    }

    @Test
    void test1() throws Exception {
        wireMock.stubFor(WireMock.get(urlPathEqualTo("/v1/test"))
                .willReturn(aResponse()
                       .withStatus(200)
                       .withHeader("Content-type", "application/json")
                       .withBodyFile("stub_response.json")));

        RestAssured.given()
                .param("param", "test")
                .when()
                .get("/v1/test")
                .then()
                .assertThat()
                .statusCode(200)
                .body(sameJSONAs(FileUtils.readFileToString(new ClassPathResource("__files/expected_test.json").getFile(), StandardCharsets.UTF_8)))
    }
}
```


### メモ
- spring-clod-contract-wiremock を使うともう少し実装がスマートになるらしい？
- パッケージ構成
    - test/java/xxx/xxxIntegrationTest.java
        - テストクラス
    - test/resource/integration_test/
        - stubs/__files
            - mockのレスポンスjsonファイルを置く
            - Wiremockの使用上、filesで指定したパス配下の__filesに置く
        - responses
            - レスポンスのjsonファイルを置く
- 一つテストメソッドを作ったあと、ParameterizedTestにしてファイル名やレスポンスを引数で渡すようにするとコードが煩雑にならない
JSX で強制的にレンダリングしたい

- [あるWebサービスの開発メモ・目次ページに戻る](https://qiita.com/ishi32/items/152a1ecf7eeee7a8fb57)
- [あるWebサービスの開発メモ・JSX チートに戻る](https://qiita.com/ishi32/items/ce86885045d69bbfb2c9)

### 本当に強制的にレンダリングしたいのか？

強制的にレンダリングしたいということではなくて、期待した再レンダリングが実行されなくて困っているのではないでしょうか？

何かの値が更新されたので、この値に従って再レンダリングして欲しかったのに、うまくいかないってこと、よくあります。

React は state や props の変更が再レンダリングのトリガーになります。通常はこれで十分な場合が多いのですが、うまくいかないときは useState() フックを利用すると再レンダリングが可能になります。

### useState() フックを利用する

更新のトリガーにしたいオブジェクトが更新されたとき、期待した再レンダリングが実行されない場合、このオブジェクトを useState() で保持すると、値の変化をトリガーして確実に再レンダリングされるようになります。

### それでも本当に強制的に再レンダリングしたい場合

公式に[「forceUpdate のようなものはありますか？」](https://ja.reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate)というページがありますので、この実装でできるはずです。
Github Actions の概要と設定ファイル【基礎】

## Github Actionsとは
- Github のCI/CDツール
- リポジトリと連携して、ビルド・テスト・デプロイを自動化できる
- push, pull request, webhookを使用した外部イベントなどをトリガーとすることができる
- 特定のブランチやディレクトリに対してトリガーを指定できる
- ワークフロー設定ファイル(.yml)を複数置ける
- テンプレートファイルが用意されている
- 実行結果はGithubの`Actions`タブから確認できる

## ワークフローの作成
- リポジトリに`.github/workflows` というディレクトを作成し、その直下に`.yml`または `.yaml`という拡張子でワークフロー設定ファイルを作成する
- Githubページの`Actions`タブの`New workflow`から作成することもできる

## 設定ファイル .yml
### 構造
```
name: sample-actions #ワークフローの名前
on: [push] #テストを実行するトリガー
jobs: #jobの定義
 job1: #jobの名前
    runs-on: ubuntu-latest #jobが実行される環境を定義
    steps: #jobが実行する処理
      - uses: #Actionを指定
      - run: #コマンドを指定
 job2:
  ...
```
### サンプル
サンプルの設定ファイルを例に、各記述について見ていく
このサンプルでは、コードがpushされるたびにコードをチェックアウトし、batsテスティングフレームワークをインストールし、bats -vというbatsのバージョンを出力するコマンドが実行される。
```yml:sample.yml
name: learn-github-actions

on: [push]

jobs:
 check-bats-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '14'
      - run: npm install -g bats
      - run: bats -v
```
[Github Docs サンプルワークフローを作成する](https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%83%AF%E3%83%BC%E3%82%AF%E3%83%95%E3%83%AD%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)

### name
- Githubリポジトリに表示されるワークフロー名
- 省略すると相対パスが名前となる

### on
- トリガーとなるイベントを指定する
- 複数指定する場合は以下のように配列で記述できる
```yml
on: [push, pull_request] #どちらかが発生したときに実行される
```
- 特定のブランチやディレクトリに対してのトリガーを指定できる
```yml
# masterブランチに対してpushした時に実行される
push: 
    branches: 
      - master
```

#### workflow_dispatch
- サンプルには記載されていないが、ワークフローを手動で実行できるイベントとして`workflow_dispatch`がある
- `Actions`のタブ画面で、実行ブランチを選択できるUIがデフォルトで追加される
- 実行時に独自のデータを付与することができる
```yml
#UIから name というデータを付与できる
on:
  workflow_dispatch:
    inputs:
      name:
        description: 'Name'
        required: true
```
```yml
#呼び出すときは以下のように記述する
run: echo "${{ github.event.inputs.name }}"
```

### jobs
- ワークフローファイルで実行されるjobを記述する
- デフォルトではjobは並列で実行される

サンプルでは`check-bats-version`という名前のjobを一つだけ記述しているが、もちろん複数記述も可能

### runs-on
- jobを実行する仮想マシンを設定する
- ubuntsu以外にもwindows-serverやmacosもサポートされている

### steps
- jobが実行する処理を記述する
- uses でActionを指定
- run でコマンドを指定
- Githubで表示される名前を`name`で指定できる。ワークフローのログに表示される。
```
steps:
    - name: First step
      uses: actions/checkout@v3
```

### Action
- stepを構成する最小単位で何かしらの一連の処理を表す
- Github Actionsが提供していたり、独自のアクションを作成することもできる
- パブリックリポジトリやDockerレジストリで公開されているActionを実行できる
```yml
#サンプルではactionsユーザーのcheckoutリポジトリというActionを指定している
uses: actions/checkout@v2
```

#### actions/checkout@v3
- リポジトリをランナーにチェックアウトするアクション
- コードに対してスクリプトや他のアクション（ビルドやテストツールなど）を実行できるようになる
- ワークフローがリポジトリのコードに対して実行されるときに使う

#### actions/setup-node@v3
- Node.jsの指定されたバージョンをインストールする
- `with` でバージョンを指定する(サンプルではv14)

## 参考
[Understanding GitHub Actions](https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions)
TypeScript で subpath export されたモジュールを import しようとすると `TS2307 Cannot find module` エラーになる問題の解決

## 概要

package.json の exports 記法 ([Modules: Packages | Node.js v18.7.0 Documentation](https://nodejs.org/api/packages.html#subpath-exports)) でエクスポートされた subpath モジュールを、 TypeScript 4.7.4 で import しようとすると、次のようなエラーになる。

```
import.ts:1:19 - error TS2307: Cannot find module 'exporter/foo' or its corresponding type declarations.

1 import {FOO} from 'exporter/foo'
                    ~~~~~~~~~~~~~~
```

## 問題の再現

### 1. import 対象のパッケージ(exporter)の作成

package.json の exports で `import ... from 'exporter/foo'` として import できるパッケージを作成してみる。

```sh

mkdir exporter
cd exporter

cat > package.json <<'EOS'
{
  "name": "exporter",
  "version": "1.0.0",
  "exports": {
    "./foo": {
      "import": {
        "types": "./dist/foo.d.ts",
        "default": "./dist/foo.js"
      }
    }
  }
}
EOS

cat > tsconfig.json <<'EOS'
{
  "compilerOptions": {
    "declaration": true,
    "outDir": "./dist",
    "rootDir": "./src",
  },
}
EOS

mkdir src
cat > src/foo.ts <<'EOS'
const FOO = 123
export {FOO}
EOS

npm i -D typescript
./node_modules/.bin/tsc
```

### 2. import 側のコードを作成

`exporter/foo` を import するコードを書く。

```sh
mkdir importer
cd importer

# 下の「4. Node.js で直接実行してみる」のための設定
cat > "package.json" <<'EOS'
{
  "type": "module"
}
EOS

npm i -D typescript ../exporter

cat > import.ts <<'EOS'
import {FOO} from 'exporter/foo'

console.log(FOO)
EOS
```

### 3. コンパイル

`exporter/foo` を TypeScript は見つけられない。

```
$ ./node_modules/.bin/tsc import.ts

import.ts:1:19 - error TS2307: Cannot find module 'exporter/foo' or its corresponding type declarations.

1 import {FOO} from 'exporter/foo'
                    ~~~~~~~~~~~~~~
```

### 4. Node.js で直接実行してみる

こちらは問題ない。

```
$ cp import.ts as-plainjs.js
$ node as-plainjs.js
123
```

## 解決方法

[typescript-subpath-exports-workaround - npm](https://www.npmjs.com/package/typescript-subpath-exports-workaround)

を参考に、 `typesVersions` の記述を exporter の package.json に導入する。

```js
{
  "name": "exporter",
  "version": "1.0.0",
  "exports": {
    "./foo": {
      "types": "./dist/foo.d.ts",
      "import": "./dist/foo.js"
    }
  },
  "typesVersions": {  // <=== 追加
    "*": {
      "foo": ["dist/foo"]
    }
  }
}
```
PANDASのURLをリンクチェックしHTTPステータスコードを返す

# はじめに
URLをPANDASのSeriesから、リンクチェックを行い、HTTPステータスコードを戻します。

# 変換元データ
```python
import pandas as pd
df = pd.Series(['http://base1.nijl.ac.jp/~image/', 'http://archives.ih.otaru-uc.ac.jp/', 'https://repository.dl.itc.u-tokyo.ac.jp/search?search_type=2&q=6092'], index=['1', '2', '3'])
```

# 変換
```python
import requests
from contextlib import suppress

def f_str(x):
    with suppress(Exception):
        r = requests.get(x)
        return str(r.status_code)

df['linkcheck'] = df.map(f_str)
```
# 結果
```python
print(df)
```

```
1                              http://base1.nijl.ac.jp/~image/
2                           http://archives.ih.otaru-uc.ac.jp/
3            https://repository.dl.itc.u-tokyo.ac.jp/search...
linkcheck          1     403
2    None
3     200
dtype: object
dtype: object
```
Nginx学習と整理

## 機能

+ 動的および静的リソースの合理的な割り当てる
+ プロキシでの開発とデプロイ。複数のサーバーがある場合は、負荷分散と呼ばれる
+ gzip。js と css などのファイルを圧縮する
+ 設定ファイルでは、変数によってサーバーを選択する
+ httpsの強制設定
+ リクエスト制限
+ アクセス制御





## 関連プログラム

- Tengine
- OpenResty
VSCode Remote SSHを用いた環境構築メモ

# はじめに

サーバー上で快適に作業するために、Vscodeからssh接続し、ファイル操作できる環境を構築したときのメモ。

# Remote SSH プラグインのインストール
VSCodeの左上からインストール。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599506/e44b0e28-495f-e829-80c2-8aeec2d34a35.png)

# ssh config に ssh 接続情報を追加

/Users/ユーザ名/.ssh/config に以下の内容を追加する。

```bash
Host [ホスト名]
  HostName [接続先]
  Port [ポート]
  IdentityFile [認証キーのパス]
  User [接続ユーザー名]
```

例えば、
```bash
$ ssh -p 30000 user@192.168.1.1 -i ~/.ssh/id_rsa_test
```
で接続する場合、ホスト名は仮にisuconとして、以下のように設定する。

```bash
$ sudo nano ~/.ssh/config
Host isucon
  HostName 192.168.1.1
  Port 30000
  IdentityFile ~/.ssh/id_rsa_test
  User user
```

configを記載すると、
```bash
$ ssh [ホスト名]
```
で接続できるようになる。

※ 鍵の作成は省略。参考：[Windows10標準機能でSSH認証用の公開鍵と秘密鍵を作成する方法](https://www.threestarsoftware.co.jp/gitlab/windows10%E6%A8%99%E6%BA%96%E6%A9%9F%E8%83%BD%E3%81%A7ssh%E8%AA%8D%E8%A8%BC%E7%94%A8%E3%81%AE%E5%85%AC%E9%96%8B%E9%8D%B5%E3%81%A8%E7%A7%98%E5%AF%86%E9%8D%B5%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99/)

# VSCodeでSSH接続

左下のアイコン→SSH Targetから、上記で接続したホスト名を選択する。新しいVSCodeのウィンドウが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599506/81dea787-7148-9658-695e-82d5158cdd42.png)

# max_user_watches の変更
このままだと大規模のファイルが見れなくなるので、max_user_watchesを変更する
```bash
$ cat /proc/sys/fs/inotify/max_user_watches
8192
$ sudo nano /etc/sysctl.conf
~~~ 追記
# max_user_watches
fs.inotify.max_user_watches=524288
```

```bash
$ sudo sysctl -p
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
fs.inotify.max_user_watches = 524288
```

```bash
$ cat /proc/sys/fs/inotify/max_user_watches
524288
```
監視できるファイルの最大数が524,288とのこと。
各ファイル ウォッチは 1080 バイトを使用するため、524,288 個のウォッチがすべて消費されると仮定すると、上限は約 540 MiB になるので、メモリ制約のある場合は数を減らしておく。

# VSCodeがサーバリソースを食い散らかす対策

複数人でVSCodeのRemote SSHで接続していたサーバーの反応が異常に遅くなることがあった。

[【VSCode使用者注意】サーバーリソース食い散らかすﾏﾝから解放される唯一の方法](https://blog.masuyoshi.com/%E3%80%90vscode%E4%BD%BF%E7%94%A8%E8%80%85%E6%B3%A8%E6%84%8F%E3%80%91%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E9%A3%9F%E3%81%84%E6%95%A3%E3%82%89%E3%81%8B%E3%81%99/)

記事によると、VSCodeでSSH接続する際にサーバーに負荷をかけてしまうことがあるらしい。まさにこの現象。

対策として、この記事に倣って、ファイル監視を無効化する。
1. 画面上部メニュー「ファイル」→「ユーザー設定」→「設定」を選択
2. 「設定の検索」に Watcher Exclude と入力
3. 「パターン追加」 から ** を追加する
4. 設定を反映させるために、VSCodeを再起動

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599506/e1ff9ceb-c182-d6d5-603c-4aaad8c083b0.png)

※ デメリットあり
> ただし、トレードオフとして、gitのファイル変更状況がリアルタイムで反映されなくなるので、自身でコミットなどをする際には、手動でファイルの変更を確認することになります。

# 最後に

間違っていること、問題点、もっといい方法など見つかれば修正します。
シェルの実行エラーについて ./initialize.sh: bad interpreter:

```
zsh: ./initialize.sh: bad interpreter: /bin/bash^M: no such file or directory
```

シェルを実行しようとすると、上記のエラーが表示された。
調べてみると、改行コードがCRLFのため、エラーが出ていたようだ。
改行コードをLFに修正すると、エラーが解消された。
Gemfile.lockのexclamation mark (!)

## Gemfile.lockのDEPENDENCIESの!
自前のgemサーバーを立てたプロジェクトでGemfileを修正したところ、Gemfile.lockのDEPENDENCIESに!が差分として入るようになったのでその理由を調べてみた。

一応それらしい情報は見つかったものの、説明にバラツキがあり今一つピンと来ない。
https://stackoverflow.com/questions/7517524/understanding-the-gemfile-lock-file

## Bundlerは一体何をやっているのか？
仕方ないのでbundlerのソースを確認してみると、こんな感じで!を付けてました。
```ruby:lib/bundler/dependency.rb
    def to_lock
      out = super
      out << "!" if source
      out << "\n"
    end
```

sourceの判定
```ruby:lib/bundler/rubygems_ext.rb
    def source
      (defined?(@source) && @source) || Gem::Source::Installed.new
    end
```

## なるほど・・・

つまりsourceの判定結果次第で!が付く仕組みになっているので、先ほど見たStack Overflowに書いてある内容はどれも概ねあっているけど実際の挙動と微妙に違う、ということになりそうな気がします。

気になるので、一応挙動を確認しておきます。

## 動作確認
Global Sourceで *rubygems.org* を指定た状態で、意味はないけどあえてインストールするgemに`source`オプションをつけて同じ *rubygems.org* を指定してみます。
```ruby:Gemfile.lock
source "https://rubygems.org"
gem 'rails', source: "https://rubygems.org" # あえてsourceオプションをつけてみる
```
この状態で`bundle install`すると、予想通り DEPENDENCIESに ! が付きました。
```ruby:Gemfile.lock
DEPENDENCIES
  rails!
```
bundler1.0.0の頃から変更は入ってないようなので大体どのバージョンでも同じ結果になると思います。

## まとめ
Global Source以外に`:source`オプションや`source`ブロックを使用すると、Gemfile.lockのDEPENDENCIESに!が付くようになる。