OmniAICreator/Qiita-1.07M huggingface dataset:
リハビリアプリを作ろう（その５）

こちらの続きです

https://qiita.com/t-yamanashi/items/5a88ed8dc0e8d0daf04c

1-25の数字押していくゲームを作りたいです
このコラムの範囲
- 時間測定

# 時間の表示
全問クリック後時間を止める

```elixir:lib/num_web/live/user_live/index.ex
defmodule NumWeb.UserLive.Index do
  use NumWeb, :live_view

  @impl true
  def mount(_params, _session, socket) do

    Process.send_after(self(), :update, 25)

    num =
    1..25
    |> Enum.shuffle()

    questions =
    num
    |> Enum.shuffle()

    questions = questions ++ [nil]
    [question | questions] = questions

    start_time = DateTime.utc_now()
    socket = assign(socket, num: num)
    |> assign(ok_num: [])
    |> assign(questions: questions)
    |> assign(question: question)
    |> assign(start_time: start_time)
    |> assign(time: nil)

    {:ok, socket}
  end

  @impl true
  def handle_info(:update, socket) do
    start_time = socket.assigns[:start_time]
    ok_num = socket.assigns[:ok_num]

    time = if length(ok_num) < 25 do
      Process.send_after(self(), :update, 25)
      DateTime.diff(DateTime.utc_now, start_time, :microsecond) / 1000000
    else
      socket.assigns[:time]
    end

    socket = assign(socket, time: time)
    {:noreply, socket}
  end

  @impl true
  def handle_event("buttno", %{"no" => no}, socket) do
    question = socket.assigns[:question]
    |> Integer.to_string()

    socket = if question == no do
      ok_num = socket.assigns[:ok_num] ++ [no]
      [question | questions] = socket.assigns[:questions]
      assign(socket, ok_num: ok_num)
      |> assign(questions: questions)
      |> assign(question: question)
    else
      socket
    end

    {:noreply, socket}
  end

end

```

```elixir:lib/num_web/live/user_live/index.html.heex
<div  class="w-28 h-28 m-2 bg-red-200 text-center">
  <p class="pt-7 text-5xl text-red-700 "><%= @question %></p>
</div>
<p class="pt-7 text-5xl text-red-700 "><%= @time %></p>
<br>
<%= for {s, i} <- Enum.with_index(@num, 1) do %>
  <%= if Enum.find_index(@ok_num, fn x -> Integer.to_string(s) == x end) == nil do %>
    <div phx-click="buttno" phx-value-no={s}  class="float-left w-28 h-28 m-2 bg-blue-200 text-center ">
      <p class="pt-7 text-5xl text-blue-700 "><%= s %></p>
    </div>
  <% else %>
    <div  class="float-left w-28 h-28 m-2 bg-blue-100 text-center ">
    </div>
  <% end %>
  <%= if rem(i, 5) == 0 do  %>
    <br>
  <% end %>
<% end %>
```

# 現時点結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/f28a7ca1-0536-e16a-4f35-9e0c5426dac6.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/b868cd39-abcd-94eb-cf2a-72e3ea3cb903.png)


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

今回はここまで

今後の課題
- リセットボタン
[App Service] Flask Socket.ioでリアルタイム通信を安定化させる方法

## はじめに

Azure App Serviceを使用してFlaskとSocket.ioベースのリアルタイム通信アプリをデプロイするとき、通信が不安定になる・すぐに接続が途切れるといった問題に直面しました。本記事では、この問題の解決方法について解説します。

## やりたいこと

以下のようなコードで作成されるFlaskアプリをAzure App Serviceにデプロイすることを想定します。基本的なSocket.ioによる通信が含まれています。

```py
# startup.py
from flask import Flask
from flask_socketio import SocketIO

app = Flask(__name__)
socketio = SocketIO(app)

@app.route('/')
def index():
    return 'Hello world'

@socketio.on('message')
def handle_message(message):
    print('Received message: ' + message)

if __name__ == '__main__':
    socketio.run(app)
```

```shell
# スタートアップコマンド
# Azure Portalで 設定→構成→全般設定→スタックの設定 から設定できる
gunicorn --bind=0.0.0.0 --workers=2 startup:app
```

開発環境

- Azure App Service (Free F1プラン・Linux)
- Python 3.10
- Flask 3.0.0
- Flask-SocketIO 5.3.6
- gunicorn 21.2.0

## 問題

ページ読み込み直後はSocket通信によってデータを受信できましたが、その後すぐに通信が切断されます。クライアントのコンソールには以下のようなエラーメッセージが大量に表示されていました。

:::note alert
GET https ://example.azurewebsites.net/socket.io/?EIO=*********　400 (Bad Request) polling.js:298
:::

## 解決方法

スタートアップコマンドにEventletを用いることによってリアルタイム通信が十分に機能するようになりました。具体的には以下のようにファイル・コマンドを変更しました。


1. **スタートアップコマンドの修正**
    
    ```shell
    # スタートアップコマンド
    # Azure Portalで 設定→構成→全般設定→スタックの設定 から設定できる
    gunicorn --worker-class eventlet -w 1 startup:app
    ```

2. **Pythonコードの修正**
    
    ```py
    # startup.py で以下を変更
    # これは明示的に同期方法を指定している。あくまでおまじない。
    socketio = SocketIO(app, cors_allowed_origins="*", async_mode="eventlet")
    ```

3. **必要なライブラリをインストールさせる**

    ```text
    # requirements.txt に以下を追加
    gunicorn
    eventlet
    gevent
    ```

### 試したけれど問題解決に繋がらなかったこと

1. App Serviceのプランをアップグレードする：最上位プラン（Premium v2 P3V2）を1時間借りて試しましたが、パフォーマンス・安定性の向上に繋がりませんでした。上記の対策後は無料プラン（Free F1プラン）でも快適に動作しています。
1. Azure PortalからCORSの設定を変更する：一切触らなくても通信の安定化はできました。

### なぜこの方法で解決できたのか？

デフォルトのGunicorn設定では、同期ワーカーが使用されます。同期ワーカーでは、1つのワーカーが一度に処理できるリクエストが1つだけです。1つの接続がブロックされると、他の接続も待たされることになり、通信の不安定さにつながります。そのため、長時間接続を維持するSocket通信には不適切です。

一方、`gunicorn --worker-class eventlet -w 1 startup:app` コマンドを使うと、Eventletがワーカークラスとして設定されます。Eventletは非同期I/Oをサポートし、一つのプロセスで複数の接続を並行して処理できるようになります。これにより、同時に多くの接続を処理できるため、リアルタイム通信の安定性が向上できたというわけです。

なお、`-w 1`はワーカーの数を1に設定しますが、一般的には`(2*コア数)+1`に設定することが推奨されているようです。

## 結論

Socket通信を扱う場合、非同期処理をサポートするEventletのようなワーカークラスの使用が推奨されます。これにより、多数の同時接続を扱う際のパフォーマンスと安定性が向上します。

## 参考文献
[デプロイメント Flask-SocketIOドキュメント](https://flask-socketio.readthedocs.io/en/latest/deployment.html)
[設計 Gunicorn 21.2.0ドキュメント](https://docs.gunicorn.org/en/stable/design.html)
「PCリサイクルマーク」を使ってディスプレイをリサイクルに出した件

## はじめに

[ACCESS Advent Calendar 2023](https://qiita.com/advent-calendar/2023/access) の7日目です。

我が家ではPC用ディスプレイにレコーダを接続しテレビがわりに利用していたのですが、突然切れる、砂嵐のような画面になる、という症状が繰り返されたため買い換えることになりました。

ディスプレイは三菱のRDT232WLM(BK)で、私が大学生時代に購入したものなので10年以上は稼働していたことになります。

![2010-09-10 22.04.23.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42210/8997d310-ea27-d505-945b-dd6aa627e14e.jpeg)

これまでPCやタブレットの処分は行ったことがあったのですが、ディスプレイは初めてだったため、利用したことのないPCリサイクルを試してみることにしました。

以降、当記事でいう「PCリサイクル」は「一般社団法人パソコン３Ｒ推進協会」によるリサイクルを指すこととします。

## PCリサイクルマークとは？

「PCリサイクルマーク」は2003年10月以降に販売された家庭向けパソコンに貼付されているものであり、これが貼り付けられているPCやディスプレイは無料でPCリサイクルに出すことができます。

https://www.pc3r.jp/home/pcrecycle_mark.html

RDT232WLM(BK)の購入は2010年であることから、無料でリサイクルに出せると考えました。

## リサイクル手順

### 申し込み

しかし、私の購入したディスプレイにはマークがありません。別途シールとして同封されている場合もあるようですが、何せ10年以上前に購入したものであり、箱も紛失しているためなんともいえません。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42210/a13429cc-d62f-2ca1-7324-f8f6221f0720.png)

こういった場合でも、機種とシリアル番号を送付することでPCリサイクルマークを取り寄せることができるようです。ただ私の場合、マークを取り寄せる前に機種名とシリアル番号を入力したところ無償でリサイクルと表示が出たため、対象機種であれば表示がなくとも可能なようです。

![Untitled_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42210/6fae1277-946e-2015-e7d1-49a0c5a19c0d.png)

![Untitled_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42210/9671ee71-eb34-9eb8-64d1-71227121c932.png)

サイト上で申請すると、数日後にエコゆうパック伝票が届きます。集荷の申し込みも可能で、通常のゆうパックと同様にWebで申請が可能です。

### 梱包・発送

梱包はビニール袋（重ねがけ）かダンボールが勧められており、リサイクルに出す機種に付属するもの（ケーブルやスタンドなど）以外は同梱できません。これは後述するリネットジャパンなどのリサイクルとは異なる点です。

https://www.pc3r.jp/home/method.html

当初はダンボールによる梱包を検討しましたが、最終的にビニール袋による梱包を選びました。

![231123170954665.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42210/bd3ace50-74c8-25c7-5f2c-80a9798cec32.jpeg)

そして、通常の荷物と同様に集荷に出して終了です。

## 未来へ

無料でPCをリサイクルする、でPCリサイクル以外に挙げられるのは、やはり[リネットジャパン](https://www.renet.jp/)などの無料回収だと思われます。相違点としては、対象となるPCやスマホが1個でも入っていれば、140サイズのダンボール1箱にどれだけ詰めても無料という点です。

対してPCリサイクルは、対象機種のみの回収ではあるものの、逆に言えばディスプレイ1点のみでも可能であるところが強みでしょうか。私の場合でも廃棄まちのPCが~~なぜか~~いくつもあるため無料回収もできたかと思いますが、ディスプレイが入るダンボールの用意がネックでした。その点、ビニール袋の方が手に入りやすいということもあると思います。

電化製品はリサイクルに出す、というのが一般化して久しいですが、その時々の理由や条件に合わせて手段を選べるというのは役に立つかと思います。もしご家庭に処分まちのPCやディスプレイがあれば、PCリサイクルに出すことを検討してみてはいかがでしょうか。
それ，numpy で書かない？-- 1 --

## それ，numpy で書かない？

Python ではリストがよく使われる。また，for ループが遅いのでリストに特化したリスト内包表記も推奨されることが多い。

それなりの根拠があるからではあるが...

課題：$n$ 個の整数を要素とするリストが 2 対ある。対応する要素が等しいものと等しくないものの個数を求めよ。

テストデータとして以下のような，0 ~ 999 の整数要素からなる，2 個のリストを作る。


```python
from time import time
import random
n = 10000000
random.seed(123)
expected = [random.randint(0, 999) for _ in range(n)]
predicted = [random.randint(0, 999) for _ in range(n)]
```

for を使うとたしかに遅い。


```python
s = time()
correct = 0.0
wrong = 0.0
for i in range(n):
    if expected[i] == predicted[i]:
        correct += 1
    else:
        wrong += 1
print("@1", correct, wrong, time() - s)
```

    @1 10026.0 9989974.0 1.076805830001831


遅いのは遅いが，ちょっと無駄がある。同じものと違うものを別々に数えているが，これはどちらか一方を数えるだけで十分だ。


```python
s = time()
correct = 0.0
wrong = 0.0
for i in range(n):
    if expected[i] == predicted[i]:
        correct += 1
wrong = n - correct
print("@2", correct, wrong, time() - s)
```

    @2 10026.0 9989974.0 0.7036769390106201


実行速度は倍速になるかと思いきや，そうはいかない。

for が遅いのを，リスト内包表記でカバーするのが Python 流。
書き方は何通りかあるので，遅い順に挙げてみる。

まずは，for の前に判定条件を置く場合。判定結果は長さが同じ True/False からなるリストなので，同じものの個数は sum() で得られる。
最初のものに比べるとほぼ倍速になる。


```python
s = time()
correct = sum([expected[_] == predicted[_] for _ in range(n)])
wrong = n - correct
print("@3", correct, wrong, time() - s)
```

    @3 10026 9989974 0.49999427795410156


次は，for のあとに if を置く場合。結果は等しいものだけなので，同じものの個数は len() で得られる。
前のものに比べて少し速くなる。


```python
s = time()
correct = len([_ for _ in range(n) if expected[_] == predicted[_]])
wrong = n - correct
print("@4", correct, wrong, time() - s)
```

    @4 10026 9989974 0.43302106857299805


次は，イテレータに zip() を使う方法。
更に速くなる。


```python
s = time()
correct = sum([_1 == _2 for _1, _2 in zip(expected, predicted)])
wrong = n - correct
print("@5", correct, wrong, time() - s)
```

    @5 10026 9989974 0.2798469066619873


次は，イテレータに zip() を使う方法。
更に速くなる。最初の for を使った場合に比べてほぼ 4 倍速である。


```python
s = time()
correct = len([_1 for _1, _2 in zip(expected, predicted) if _1 == _2])
wrong = n - correct
print("@6", correct, wrong, time() - s)
```

    @6 10026 9989974 0.22952604293823242


---

ここまでにしようかとも思うが，観点を変えて numpy を使った場合を見てみる。

リスト内包表記を使った最後のプログラムは 1 行で書けるとはいえ，決して簡潔ではない。
numpy で書くと，簡潔に書くことができる（もちろん 1 行で）。
速度は，ちょっと遅い。

以下ではリストを numpy 配列にする処理を挟むが，最初から numpy で書けば，余分な処理をする必要はない。


```python
import numpy as np
expected2 = np.ravel(expected)
predicted2 = np.ravel(predicted)
```


```python
s = time()
correct = sum(expected2 == predicted2)
wrong = n - correct
print("@7", correct, wrong, time() - s)
```

    @7 10026 9989974 0.6402859687805176


内包表記で書いた場合の 2 倍ぐらいかかる。
ならば，内包表記で書く...

いやいや，待って。
長さが 1千万のリストで 0.27 秒かかるのが 0.51 なのだから，体感的にはそんなに差がないともいえる。

numpy を使うメリット，局面は他にもあるので，今回の課題にはこのやり方で十分だろう...
個人の感想です。
50点を超えるためのUIデザイン

以下、UIデザインを行う上で50点を取るためのルールです。
## ルールは３つ
1. 原則・法則を厳守し、アンチパターンを徹底的に避けること
2. 難易度の高いデザインを行わないこと
3. アプリケーションで扱う情報の全体像と、各情報の関連、そして、各画面で中心的に着目する情報を整理すること

上記を守れば 100点は取れないですが、50点を取るまでは可能です。

## 原則・法則の厳守とアンチパターンの回避
「近接」「整列」「強弱」「反復」の4原則は徹底的に守りましょう。
自分は「守破離」の「守」段階だと考えましょう。
アンチパターン同士が競合した場合は、そうなっている状況自体が何かおかしいと考えましょう。
どうしても解消できない場合は、いったんマシな方のアンチパターンを取っておきます。

以下書籍の紹介です。徹底的に守ってください。
- 「ノンデザイナーズ・デザインブック」（https://www.amazon.co.jp/dp/4839955557 ）
- 「インターフェースデザインのお約束」（https://www.amazon.co.jp/dp/4873118948 ）

以下Sociomedia社がまとめているガイドラインです。こちらも徹底的に守ります。
- 「ヒューマンインターフェース ガイドライン」（https://www.sociomedia.co.jp/category/shig ）

## 難易度の高いデザイン禁止
以下は禁止です。
- UIコンポーネントの自作
- 色の利用
- アニメーションの利用
### UIコンポーネント
自作はNGです。世の中のCSSフレームワークが提供しているUIコンポーネントを使いましょう。
できれば「マテリアルデザイン」を使いましょう（https://materializecss.com/ ）
できなければ「Bootstrap」を使いましょう（https://getbootstrap.jp/ ）
他にもたくさんありますが、欲をかいて手を出すのはNGです。
UIデザインを上記の規定のコンポーネントで表現できない場合は、それは何かがおかしいです。

### 色
色はNGです。白～黒のグレースケールで作成しましょう。
当然良いプロダクトは良い色を使っています。良いデザイナーは良い色の選択ができます。
ただし、初級UIデザイナーにとっては、色は邪魔です。
CSSフレームワークが提供するグレースケールのパレットから選択するようにしましょう。
どうしても色が欲しい場合は、UIデザインが完成したあとにちょっとだけ色付けする程度としましょう。

### アニメーション
アニメーションはNGです。魅力的に感じますが、それは罠です。
作成に時間がかかる割に、ユーザー体験を悪くする可能性が高いです。
「守破離」の「守」を守りましょう。
ただし、元々UIコンポーネントにあるアニメーションを消す必要までは無いです。（ローディングのUI等）

## アプリケーションで扱う情報の整理
情報はUIデザインの要です。
これまで記載したルールを守りつつ、アプリケーション上で扱う情報を過不足無く配置していきます。
これでようやく50点を取ることができます。
以下のプロセスを踏んでいきます。

### 用語の統一
「同じ情報は同じ用語で呼ぶ。違う情報は違う用語で呼ぶ」を徹底しましょう。
用語集（日本語、英語、説明）が無ければ作成しましょう。
アプリケーション上で使う用語は、用語集の中からのみ使えるようにしましょう。
用語集の作成は、マネージャー、クライアントにも協力してもらい、全員で共有しましょう。

### 情報同士の関連整理
いわゆる「ドメインモデル」や「情報モデル」「概念ER図」と呼ばれるような、四角が線で繋がれた一枚絵を探しましょう。
もし無ければ、マネージャー、クライアント、エンジニアと協力して作成しましょう。
これは用語集とセットで使います。
以下の点などを考慮しつつ作成します。
- 情報同士は、直接つながるのか間接的につながるのか
- 情報は別の情報に所属する属性か、別の情報の具体化／抽象化した情報か、別の情報の構成要素か
- 情報同士は、一対一でつながるのか、一対多でつながるのか、多対多でつながるのか
- 情報は「発生時刻」を伴う情報か否か
- 情報は更新されるか否か

### 画面への情報配置
情報同士の関連が整理ができた後は、アプリケーションで扱う情報を参照できる画面のUIを設計していきます。登録や更新フォームなどは後回しとします。
関連図の中で注目する情報をピックアップし、その情報の周辺の関連情報を関連を明示できるようにUIコンポーネントを使って表現していきます。

その後、アプリケーションに求められる機能に沿って、各画面で表示する情報の取捨選択します。

参考書籍
- 「オブジェクト指向UIデザイン」（https://www.amazon.co.jp/dp/4297113511 ）
- 「ユースケース駆動開発実践ガイド」（https://www.amazon.co.jp/dp/4798114456 ）



## あとがき
「デザインには正解が無い」と言われることがあります。
UIデザインにおいては「UIの表現方法の選択肢が無数に存在すること」がその要因の一つだと思います。
UIデザイン初学者はそのUIの表現方法の選択肢の多さに圧倒されてしまい、情報設計が不十分なままでアプリケーションのデザインを完了してしまいます。
そのような事態を防ぐことができればと思い、記事では厳し目のルールを記載しています。
画面への情報配置についてはかなり端折っています。書籍「オブジェクト指向UIデザイン」の中で、習得していただけると幸いです。
Pythonの__init__.py

## 対象の読者
この記事は、Pythonのパッケージ管理について学びたいと思っているPython初心者や中級者を対象としています。特に、\_\_init_\_.pyファイルの役割と使い方について詳しく解説します。

## \_\_init\_\_.pyとは？
Pythonでは、ディレクトリをパッケージとして扱うためには、そのディレクトリに\__init__.pyファイルを含める必要があります。このファイルがあることで、そのディレクトリ内のPythonモジュールやサブパッケージへのアクセスが可能になります。

## 役割
\_\_init\_\_.pyファイルは以下のような役割を果たします。

- パッケージの識別
\_\_init_\_.pyファイルがあるディレクトリはPythonパッケージとして認識されます。これにより、そのディレクトリ内のPythonモジュールやサブパッケージへのアクセスが可能になります。
- 初期化コードの配置
\_\_init\_\_.pyファイルは通常は空ですが、必要に応じて初期化コードを記述することができます。これは、パッケージのセットアップや設定を行うのに役立ちます。
- 名前空間の管理
\_\_init\_\_.pyファイルには、パッケージ内のモジュールやサブパッケージのインポート名を指定することができます。これにより、外部からのインポート時に名前の衝突を防ぐことができます。
- 相対インポート
\_\_init\_\_.pyファイルがあることで、そのディレクトリ内のモジュールやサブパッケージを相対インポートできるようになります。

## 使用例
開発環境ではlocalモジュールが呼び出され、本番環境ではproductionモジュールが呼び出されるような設定を_\_init\_\_.pyで行うことができます。

```settings/__init__.py
from .production import *

try:
    from .local import *
except:
    pass
```

## まとめ
Pythonの\_\_init\_\_.pyファイルは、パッケージ管理における重要な要素です。このファイルを通じて、パッケージの識別、初期化コードの配置、名前空間の管理、そして相対インポートが可能となります。これらの機能を理解し、適切に活用することで、Pythonのパッケージ管理を効率的に行うことができます。
Rubyのオブジェクトクラス10選とその例

# Rubyのオブジェクトクラス10選とその例

Rubyは、多種多様なオブジェクトクラスを提供するプログラミング言語です。これらのクラスは、データの操作や計算、表現に不可欠な役割を果たします。以下では、Rubyでよく使用される10種類のオブジェクトクラスとその具体的な例を紹介します。

## 数値クラスとその例
1. **`Integer`**: 整数を表します。
   - 例: `1`, `100`, `-50`
2. **`Float`**: 浮動小数点数を表します。
   - 例: `3.14`, `2.5e8`
3. **`Rational`**: 有理数を表します。
   - 例: `Rational(2,3)`, `1/2r`
4. **`Complex`**: 複素数を表します。
   - 例: `Complex(1,2)`, `1+2i`

## 文字列クラスとその例
5. **`String`**: 文字列を表します。
   - 例: `"hello"`, `'world'`

## 配列クラスとその例
6. **`Array`**: 複数の要素を順序付けて格納します。
   - 例: `[1, 2, 3]`, `['a', 'b', 'c']`

## ハッシュクラスとその例
7. **`Hash`**: キーと値のペアを保持します。
   - 例: `{ 'a' => 1, 'b' => 2 }`

## 範囲クラスとその例
8. **`Range`**: 数値や文字の範囲を表します。
   - 例: `1..10`, `'a'...'z'`

## シンボルクラスとその例
9. **`Symbol`**: 軽量な識別子を表します。
   - 例: `:name`, `:id`

## 正規表現クラスとその例
10. **`Regexp`**: 文字列のパターンマッチングに使用します。
    - 例: `/^[a-z]+$/`, `/^\d+$/`

## 真偽値クラスとその例
11. **`TrueClass` と `FalseClass`**: Rubyの真偽値を表します。
    - 例: `true` `false`

## ファイルクラスとその例
12. **`Class`**: クラス自身もオブジェクトになります
    - 例: `class HogeHuga`

## モジュールクラスとその例
13. **`Module`**: メソッドや定数の名前空間を提供します。
    - 例: `module MyModule`

## オブジェクトクラスの判定方法とその例
- **`class`メソッド**: オブジェクトのクラスを調べます。
  - 例: `"こんにちは".class # => String`
- **`kind_of?`メソッド**: 特定のクラスまたはそのサブクラスのインスタンスであるかを判断します。
  - 例: `[1, 2, 3].kind_of?(Array) # => true`
- **`instance_of?`メソッド**: 特定のクラスのインスタンスであるかを厳密に判断します。
  - 例: `123.instance_of?(Integer) # => true`

## まとめ
Rubyのオブジェクトクラスは、プログラミングのさまざまな場面で活用されます。これらのクラスを適切に使いこなすことで、Rubyでのプログラミングがより効果的かつ効率的になります。また、オブジェクトのクラスを判定するメソッドを活用することで、プログラムの柔軟性と堅牢性が向上します。
AWSのStepFunctionsにおけるInvokeで後続処理への値の引き渡し及び新規パラメータを追加する方法

# はじめに

StepfFunctionsのInvokeを知用し、LambdaA→LambdaBへとパラメータを渡す際に、Aの戻り値以外にも新たなパラメータを追加してLambdaBに渡したい事案が発生した。
その際にどうやるべきかを学んだのでここにまとめる。

# ステートマシーン構成図
構成は以下の図の通り。
LambdaA(=Lambda Invoke)、LambdaB(=Lambda Invoke 2)として説明する
![スクリーンショット (359) (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355599/c6143788-5d7d-c4c7-ec04-2d613e2a98b2.png)

# ソースコード

[Lambda Invoke]

```python
import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': 'Hello from Lambda!',
        'count_list': [
	                    {"count":"3"},
	                    {"count":"21"},
	                    {"count":"39"},
	                    {"count":"40"},
	                    {"count":"52"},
	                    {"count":"60"}
        ]
    }
```

[Lambda Invoke 2]

```python
import json

def lambda_handler(event, context):
    print(event)
```


# 実装方法

## StepFunctionsの機能であるペイロードを設定する

 StepFunctionsの設定のペイロードにて[Use state input as payload]を選択すると、前段の戻り値の値をそのままeventで引き継ぐ。しかし今回は新たに値を埋め込みたいので

① Lambda Invoke 2の設定で<strong>[Enter payload]</strong>を選択する。

② 以下の値を設定する。

```json
{
  "sampleKey1": "sampleValue1",
  "StateInput.$": "$",
  "key3": 100
}
```
:bulb:　固定値を追加で埋め込む場合は"sampleKey1": "sampleValue1”のようにKey:Valueで記述する。もちろん文字列だけでなく、数値も追加可能。

:bulb:「StateInput.\\$」はLambda Invoke 2がeventで受け取る時のKey名となる。またValueに「”$”」と記述することでLambda Invoke 1での戻り値がここに入る。

## 実行してログを確認
StepFunctionsを実行してログを確認してみた。

Lambda Invoke 2が受け取ったeventの中身は以下のようになった。

```json
{
	"sampleKey1": "sampleValue1",
	"key3": 100,
	"StateInput": {
		"statusCode": 200,
		"body": "Hello from Lambda!",
		"count_list": [
			{
				"count": "3"
			},
			{
				"count": "21"
			},
			{
				"count": "39"
			},
			{
				"count": "40"
			},
			{
				"count": "52"
			},
			{
				"count": "60"
			}
		]
	}
}
```

埋め込んだ値、及び前段の戻り値を後続のLambdaのパラメータとして渡すことができた。
Delta Live Tablesのチュートリアルを通じた新機能のウォークスルー

こちらのチュートリアルを実行しながら、最近の新機能をウォークスルーします。

https://docs.databricks.com/ja/delta-live-tables/tutorial-sql.html

以下のように、ノートブックでデータパイプラインを実装します。なお、[新たなセルUI](https://qiita.com/taka_yayoi/items/d08702b8e782e778121a)をオンにしています。

![Screenshot 2023-12-06 at 10.31.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/56fbeb70-32f1-1309-9ba2-f3813cd81ee3.png)

DLTパイプラインを作成します。
![Screenshot 2023-12-06 at 10.34.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/8fbbd74c-fce0-1f24-53fc-8374c171dc0d.png)

ここでいきなりパイプラインを実行するのではなく、新機能の検証(Validate)機能を試します。
![Screenshot 2023-12-06 at 10.34.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/67f6a78d-d383-ead0-d8b1-7a76085289fc.png)

https://docs.databricks.com/ja/delta-live-tables/updates.html#check-a-pipeline-for-errors-without-waiting-for-tables-to-update

検証を実行すると、ターゲットテーブルにデータを書き込まずにパイプラインのロジックの検証を行うことができます。
![Screenshot 2023-12-06 at 10.35.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/bb721219-4a6d-441a-89b1-e0882f188634.png)

検証がスタートします。
![Screenshot 2023-12-06 at 10.37.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/fa2ac80b-2ca5-5962-0e75-aeaea21b371c.png)

少し待つとパイプラインのグラフ構造が表示されます。
![Screenshot 2023-12-06 at 10.42.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/27c87f4a-3690-7945-252e-5af49d2cbf15.png)

指定したターゲットスキーマにもデータは書き込まれません。実際に処理を実行する前にロジックを確認する開発フェーズでは有用ですね。
![Screenshot 2023-12-06 at 10.45.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/b0f82ef3-4c2b-ef6a-e57c-2432661f5a18.png)

ちなみに**リスト**タブも追加されています。気付いてませんでした。パイプラインの各ステップを一覧することができ、フィルタリングも可能です。
![Screenshot 2023-12-06 at 10.45.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/08c1f044-25ef-8e7e-b36f-03a600b7ed91.png)

パイプラインを実行すると、リストビューでもリアルタイムで進捗を確認できます。
![Screenshot 2023-12-06 at 10.47.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/529e0755-4f25-febb-e006-c10cb2b07882.png)
![Screenshot 2023-12-06 at 10.48.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/270ef7b1-620d-ce85-2557-934ee0cd4dd0.png)

### Databricksクイックスタートガイド

[Databricksクイックスタートガイド](https://www.amazon.co.jp/dp/B09V1YXFVQ/)


### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)
Tapヘルパーを使って処理をまとめよう

## 概要
tapメソッドは第一引数を返却します。第一引数に何をしたいかは第二引数のクロージャに記述します



### 使用例1: ユーザーの名前を更新する
```php
//$userにはnameを'hoge'にした後の値が入る
$user = tap(User::first(), function (User $user) {
   $user->name = 'hoge';
   $user->save();
});
```

### 使用例2:<br/>第一引数から真偽値を返すメソッドを使用した後に第一引数から動的なメソッドを使用する
第一引数から動的なメソッドを発火したい場合は、第二引数を省略できます。

```php
//before
$user = User::first();
$user->update([
  'name' => 'hoge'
]);
$user->->notify('更新しました')

//after: update()は真偽値を返すが、更新後にUserモデルが持つ動的なメソッドを発火させる
$user = tap(User::first())->update([
    'name' => 'hoge',
])->notify('更新しました');
```

### エクストラ
Higher Order Messagesを使用した場合
> Higher Order Messagesについては別途記事を書きます。

```php
// before
$user = User::find(1);
$user->update($data);
$user->notify();

//after
tap(User::find(1))->update($data)->notify();
```


## 参考

https://readouble.com/laravel/10.x/ja/helpers.html#method-fluent-str-tap

https://suzumura-tumiage.com/php/518/
"「0.1+0.2≠0.3」だから浮動小数点数を扱うときには気を付けましょう" はいいんだけど結局どうしたらいいのかまでフォローしたほうが親切だと思ったので調べてみた

# 釣られた

https://qiita.com/higashi_nc/items/9a5ea00415a008f06843 に釣られて読みました。

2行でまとめると以下のような内容です。

1. 10進数の0.1や0.2は浮動小数点数で表すとピッタリ0.1や0.2にはならずに誤差が出る
2. 浮動小数点数の扱いを理解して、より堅牢なプログラムを作成することを願っています。

え？結局どうしたらいいの？という感想だけ残ったので各言語での浮動小数点数同士の比較の方法を ChatGPT に教えてもらいました。

# 結局どうしたらいいのか

基本的に浮動小数点数同士の差がある程度の誤差より小さければ同じ数である、という判定をすればいいようです。
今回の回答には含まれませんでしたが、おそらく言語によっては浮動小数点数同士の比較をする専用のライブラリなどもあるかと思われます。

## Python

```python
import math

a = 0.1
b = 0.2

if math.isclose(a + b, 0.3, rel_tol=1e-5):
    print("等しい")
else:
    print("等しくない")
```

## Go

```go
package main

import (
    "fmt"
    "math"
)

// FloatEquals 浮動小数点数が等しいかどうかを判定する
func FloatEquals(a, b, epsilon float64) bool {
    return math.Abs(a - b) < epsilon
}

func main() {
    var a, b float64 = 0.1, 0.2
    var result float64 = a + b

    // 0.3 と比較する場合
    if FloatEquals(result, 0.3, 0.0001) {
        fmt.Println("等しい")
    } else {
        fmt.Println("等しくない")
    }
}
```

## C

```c
#include <stdio.h>
#include <math.h>

int floatEquals(double a, double b, double epsilon) {
    return fabs(a - b) < epsilon;
}

int main() {
    double a = 0.1;
    double b = 0.2;
    double epsilon = 0.00001;

    if (floatEquals(a + b, 0.3, epsilon)) {
        printf("等しい\n");
    } else {
        printf("等しくない\n");
    }

    return 0;
}
```

## PHP

```php
function floatEquals($a, $b, $epsilon = 0.00001) {
    return abs($a - $b) < $epsilon;
}

$a = 0.1;
$b = 0.2;

if (floatEquals($a + $b, 0.3)) {
    echo "等しい\n";
} else {
    echo "等しくない\n";
}
```

## Java

```java
public class Main {
    public static boolean floatEquals(double a, double b, double epsilon) {
        return Math.abs(a - b) < epsilon;
    }

    public static void main(String[] args) {
        double a = 0.1;
        double b = 0.2;
        double epsilon = 0.00001;

        if (floatEquals(a + b, 0.3, epsilon)) {
            System.out.println("等しい");
        } else {
            System.out.println("等しくない");
        }
    }
}
```

## JavaScript

```javascript
function floatEquals(a, b, epsilon = 0.00001) {
    return Math.abs(a - b) < epsilon;
}

let a = 0.1;
let b = 0.2;

if (floatEquals(a + b, 0.3)) {
    console.log("等しい");
} else {
    console.log("等しくない");
}
```
ハイブリッドなエンジニア勉強会を主催するTips

これはVoicy Advent Calendar 2023の6日目の記事です。

こんにちは。VoicyでAndroidエンジニアをしている、おぎです。
最近では、オフラインとオンライン同時開催の勉強会など以前より多く見かけるようになってきましたね。
先日、オフラインとオンライン同時開催の勉強会を初めて主催して色々と準備が大変だったので、今後やる方の参考になればと思い配信環境の準備などについて書いていきます。

# 環境
以下の環境で開催しました。
* オフライン：会社オフィス
* オンライン：YouTube Live

基本的に登壇者の方は全員オフライン会場で発表して頂く形式にしました。

## 準備するもの
以下のような会場レイアウトで開催しました。
![potatotips会場設営図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618486/14025f2d-0dec-42e2-8b7e-60c4dc64f770.png)

それぞれの機材ついて説明します。
(同じことが出来れば以下の機材でなくてもOK)

* Mixcast4
    * 会場のマイク音声、BGMなどを配信用PCに取り込むためのオーディオインターフェース。Mixcast4はスマホとBluetoothで接続出来るのでBGMの操作などが楽でした。
* Elgato Game Capture HD60 S (キャプチャーボード)
    * 会場の発表者の発表画面を配信用PCに取り込むためのキャプチャーボード。これにHDMIケーブルを繋いで登壇者の方のPCと配信PCを繋ぎます。
* カメラ、三脚
    * 登壇者の方を配信画面に映すためのカメラ。
* 配信用PC
    * 配信に使うためのPC。配信は司会用などとは分けて専用機を用意した方が良いと思います。
* [OBS](https://obsproject.com/ja)
    * 配信するためのストリーミング配信ソフト。
* 配信用の背景画像など
    * canvaで作成しました。
* マイク、スピーカーなど
    * マイクは無線よりも有線の方が接続のトラブルなど防止出来て良いと思います。


# 開催してみてやっておいた方が良いと思った事
何よりも、オフライン会場に配信設備を想定通り配置出来るかを実際に配置して確認する事が重要です。
それぞれのケーブルの長さが足りるか、コンセントの位置は配信PCの場所から遠くないか、カメラの位置は発表の途中でも画角調整など出来る位置にあるかなど、実際に配置してみて気付くこともあるので実際に配置してみることをおすすめします。
今回の会場では、プロジェクターへ投影するためのHDMI入力口が音響などのPA機材から遠い場所にあり、移動させることが出来ない制約があったので、長いHDMIケーブルの用意などが必要でした。

また、配信用の背景画像を何種類か用意しておくと発表者の転換中に配信画面では転換用の画像を映しておくことが出来るのであると便利です。発表者の方が転換中に映したくない画面をスクリーンに映してしまっても、配信画面で転換中という画像を映しておけば配信事故を防止出来るので、おすすめです。
私は[canva](https://www.canva.com/ja_jp/)というサイトで作成したのですが、様々なフォントなどもありサックと作ることが出来ました。

もし、アーカイブ動画として残す場合は会場で流すBGMなども気を付けた方が良いかもしれません。著作権などにひっかからないBGMを用意する必要があります。私は音楽を作るDTMもたまにやっていたので、BGMを自作しようかと思ったのですがそこまでの時間がなかったので、[SOUNDRAW](https://soundraw.io/ja/)というAI作曲サービスを利用しました。こちらは著作権フリーの良い感じのBGMを生成してくれるサービスです。

最後に一番伝えたいことがあります！
Macでスライド投影している画面をOBSを使って配信しようとしている時に、配信画面が黄色くなってしまう事はありませんか？
登壇者の方に以下の設定をして頂く事で解決出来ます！
Macの設定>ディスプレイ>True ToneをOFFにする。

今回の記事ではOBSの具体的な配信方法などは割愛しますが、検索すると詳しい説明をしてくださっている記事が沢山出てくるので色々試してみてください！

実際のハイブリッド開催した勉強会の様子はこんな感じでした！
<iframe width="560" height="315" src="https://www.youtube.com/embed/dnbgmr11tuI?si=yo3jVwVSkCsFICgL" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

これから、ハイブリッド開催の勉強会をやりたいと思っているけど、どうやったら良いだろうと思っている方の参考になれば幸いです。

明日のVoicyアドベントカレンダーもお楽しみに！
エンジニアでなくてもスプレッドシートとGASでGoogle カレンダーのデータを取得

# はじめに
この記事は[リンクアンドモチベーション Advent Calendar 2023](https://qiita.com/advent-calendar/2023/lmi)の12日目の記事です。

Qiita初投稿となります、Daisukeです。
社内でDX推進を担っており、生産性向上のためにGAS(Google Apps Script)をメインに業務改善のツールを作成しております。

# 今回紹介する話
業務改善のツールのなかでも「部署のメンバーの時間の使い方を可視化したい」という要望をもらうことがあります。（例：部署内で顧客ごとに費やしている時間に偏りが無いか確認したい。） そのために現状の時間のデータを取得が必要となります。

この記事では、エンジニアでなくてもGASでカレンダー（カレンダー）のデータを取得できるツールを作成したお話をできればと思います。その過程で以下2つを解決しました。
**①　取得の対象者をプログラム側から変更→スプレッドシートから取得の対象者を変更**
**②　GASの実行時間制限の壁→時間制限前にトリガーを設定し制限を突破**
作成したスプレッドシート「[Google カレンダーのデータ取得](https://docs.google.com/spreadsheets/d/19P0yiwZTdrAB_sTpfGseNRHo8qYxG-Il6yZHozlvP1M/edit#gid=957678740)」をコピーすればユーザー自身でカレンダーデータを取得できる状態としました。

# ① スプレッドシートとの連携
今回はスプレッドシートにGASを連携させます。スプレッドシートでは取得対象者のGoogleのメールアドレスを以下画像のように記載します。

![スクリーンショット 2023-12-05 23.30.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2986701/cf86d83c-ac02-29fe-671c-13df7bf0409e.png)

ここで、A,B,C列はそれぞれ以下となります。B列には記入が必要です。
|列  |ヘッダー名 |情報  |記入者  |
|---|---|---|---|
|A  |データ取得日  |データ取得された日付が書き込まれます  |GASにより記入（課題②、細かい仕様にて詳細説明）  |
|B  |メールアドレス  |メールアドレスを記入ください（必須）  |ユーザーが記入  |
|C  |氏名  |ご自由に記入ください（任意）  |ユーザーが記入  |

このようにGoogleアカウントさえあれば、ユーザーが自身で環境構築をせずとも取得対象者のカレンダー情報がシート"取得データ"に書き出されます。

# ② 実行時間制限の壁を突破
ご存知の方もいらっしゃると思いますが、GASには**実行時間の制限（無償アカウント：6分、有償アカウント：30分）** があります。
今回のようなGASによるカレンダーデータの取得には時間を要します。
そのため[こちらのサイト](https://uncle-gas.com/avoid-timeout-error/)を参考に、時間制限の壁を越えるためのコーディングを施しました。簡単に言うと、時間制限がくる前に次の実行トリガーを設定するというものです。

しかし今回のGASにそのまま適用してしまうと、どの対象者までデータを取得できたのか、プログラム側で判別がつきません。
そのため、以下コードの110行目にて実行中に取得が完了した対象者がいる場合**シート"取得対象者"の対象者の行A列にGASの実行日が書き込むこと** で解決しました。

![ShiftIt-1.6.6 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2986701/f975214d-5f03-7697-b01b-ad7661c3dbb2.png)

# まとめ
今回は作成したスプレッドシート「[Google カレンダーのデータ取得](https://docs.google.com/spreadsheets/d/19P0yiwZTdrAB_sTpfGseNRHo8qYxG-Il6yZHozlvP1M/edit#gid=957678740)」をコピーすればエンジニアでなくてもユーザー自身でカレンダーデータを取得するために行ったことを紹介しました。

以下、取得したデータのイメージを記載します。※時刻・カレンダータイトルは仮で入力しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2986701/2f591c5f-7390-f901-1dda-2af68e3ae98c.png)

またこの記事ではデータを取得した後の活用に関する機能については、スコープ外としました。取得したデータの活用方法に関しては、いずれQiitaで続編を投稿予定です！

# （備考）ユーザーの使用手順
1. スプレッドシート「[Google カレンダーのデータ取得](https://docs.google.com/spreadsheets/d/19P0yiwZTdrAB_sTpfGseNRHo8qYxG-Il6yZHozlvP1M/edit#gid=957678740)」にアクセスしファイルを複製
2. シート名"取得対象者"のA列"データ取得日"のA2以降を空欄化、メールアドレスを指定)を1.で複製したスプレッドシートから設定
3. ["新しいカレンダーを作成する"](https://support.google.com/calendar/answer/37095?hl=ja)の"新しいカレンダーを設定する"から取得対象者をユーザーのカレンダーに登録
4. 1.で複製したスプレッドシートからメニューバーの"カスタムメニュー"→"カレンダーデータを取得"を選択し、GASを実行

# 参考資料
タイトル：GASおじさんのブログ/ GASでタイムアウトエラーを回避する方法【6分の壁/30分の壁】
URL：https://support.google.com/calendar/answer/37095?hl=ja

タイトル：Google カレンダーヘルプ/ 新しいカレンダーを作成する
URL：https://uncle-gas.com/avoid-timeout-error/
timm + SyncBatchNorm

### 結論
timm モデルを用いて SyncBatchNorm を実現する場合は以下のユーティリティを利用する。
```python
model = timm.layers.norm_act.convert_sync_batchnorm(model)
```

### 経緯
複数のGPUで学習を行う際に1GPUあたりのバッチサイズが小さいと学習が安定しないことがある。この対策として DistributedDataParallel であれば BatchNorm統計量を GPU間で同期させる SyncBatchNorm がレイヤーが存在する。

利用方法としてはモデルの BatchNormXd を SyncBatchNormXd に変更して、保持している統計量などのパラメータをコピーすればよい。また、torchvisionなどのモデルであれば下記のユーティリティが利用できる。
```python
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
```
https://pytorch.org/docs/stable/generated/torch.nn.SyncBatchNorm.html

この関数は model の中にある BatchNormXd のインスタンスを見つけると SyncBatchNormXd に置き換えるという機能を提供している。

### 問題
timm モデルにおいて torch の提供する convert_sync_batchnorm でモデルを変換すると学習ができなくなるという問題が発生した。本来 SyncBatchNorm が余計なことをしない Single-GPU の環境であっても生じたため SyncBatchNorm に変更する過程で何らかの問題が生じていることが判る。

### 原因
timm のモデルは BatchNormAct2d という形で BatchNorm2d を継承して Activation を統合したレイヤーを用いるようになっている。

先のユーティリティは model 内の BatchNorm のインスタンスを自動的に SyncBatchNorm に置き換えるものであり、isinstanceで検索されていることから継承したレイヤーも対象となる。timm のモデルの BatchNormActXd は BatchNormXd を継承したレイヤーとして作られているため、torch のユーティリティを使うと SyncBatchNormXd に置き換えられる。
結果として存在した Activation レイヤーが消されてしまい想定したモデルと異なるモデルになり、計算結果が破壊される。これは [issue](https://github.com/huggingface/pytorch-image-models/issues/1254) でも取り上げられていた。

timm.layers.norm_act.convert_sync_batchnorm(model) を使うと BatchNormAct2d が SyncBatchNormAct2d として変換されるので、こちらを使う必要がある。

:::note warn
この問題は本質的には timm 固有の問題ではなく
BatchNorm を継承したレイヤーを自分で定義した際にも起きうるため注意が必要。
:::

### PyTorch Lightningについて
lightning においても [layer_sync.py](https://github.com/Lightning-AI/pytorch-lightning/blob/4d154685557881b4ff47083267b5a6328b465d61/src/lightning/pytorch/plugins/layer_sync.py#L57) にて同じく torch のユーティリティを用いているので timm モデルを使用する場合は自前で変換が必要となる。

### 参考
* [timm](https://github.com/huggingface/pytorch-image-models) - GitHub
* [PyTorch Documentation](https://pytorch.org/docs/stable/index.html)
* [pytorch-lightning](https://github.com/Lightning-AI/pytorch-lightning) - GitHub
Windows11でタスクバーにbatファイルをピン止めできなくなった。無理やりピン止めする。

# 困りごと
Windows11のアップデートによりタスクバーに.batや.vbsをピン止めできなくなったと思われる。
ネットで検索すると従来の方法ではピン止めできず困っていた。起動するのがメンドクサイ。

# 模索
ピン止めしたものの実態?は以下場所にショートカットとして存在しているらしい。

```%USERPROFILE%\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar```

そこへショートカットを突っ込んで再起動しても認識してくれない。
「ピン止め」から「そこへ登録される」という順序が必要であるようだ。

存在しているショートカットのリンク先を変えると任意のプログラム(batやcmd経由のvbsも)が呼び出せることがわかった。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3512483/2e7deb31-24cb-ccb6-ebc5-06df547d8962.png) 
画像1

書き換えてしまうと元のアプリケーションを起動したときタスクバーのソコのアイコンへウィンドウがまとめられてメンドクサイことになることもわかった。
**リンク先を書き換えても元のアプリケーションの呪縛からは逃げられない模様。**

# 新しいexeを作って無理やり登録してしまう
1. デスクトップに適当にtxtファイルを作る。
2. 1のファイルをexeに変更(もちろんこのexeは起動できない)、ファイル名はわかりやすいものにする(このファイル名がタスクバーアイコンの名前として永続する)。
3. 2のexeを右クリックして「タスクバーにピン留めする」を行う。結果タスクバーにピン留めされる。
4. ```%USERPROFILE%\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar```を開き、新しくできたショートカットができているのでそのリンク先を書き換える。vbsを呼び出す場合は```C:\Windows\System32\cscript.exe "C:hoe.vbs"```のように呼び出しを行えばよい。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3512483/544cb9f6-a4b0-308a-6637-1945ccefe581.png)
画像2

5. 2で作ったexeは削除してしまおう。


以上 解決した!
マイコン使って「IoT機器でも作ってみない？」3+1日目 (補足記事)

# 補足説明

なんの説明もなく`server.send`を使ってしまって申し訳ないと思っています。
ちゃんと説明するます！

# コード

```cpp
server.send(200, "text/html", "LED ON<br><a href='/'>BACK</a>");
```

ではこちらについて説明させていただきます。

## `server.send( ... )`

クライアントに対してレスポンスを返す関数...それ以上でもそれ以下でもないです。

## `200`
200？何それ？って思った人もいるかもしれません。

この数字はステータスコードと言って、レスポンスに対して付けられる数字です。
`404 Not Found`とか有名ですよね。

https://developer.mozilla.org/ja/docs/Web/HTTP/Status

ステータスコードの全ては上に載っています。

とりあえず、200は`OK`、つまり正常にリクエストが成功したことを示すコードとだけ覚えておきましょう。

## `"text/html"`
これは、MIMEタイプと呼ばれる、コンテンツがどの種類のものなのかを示すものです。
例えば`text/html`であれば、「`text`の中の特に`html`という種類」ということを示しています。

https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types

これを適切に設定しないとブラウザ側で正常な表示が行われなくなる場合があります。

## `"LED ON<br><a href='/'>BACK</a>"`

これは、HTMLですね。
なんと！
私の！
アドカレ1日目から！
HTMLに関する記事もちょっとだけ紹介しているので！
ぜひそちらをご覧ください！

https://qiita.com/nattyan_tv/items/d3cd6b16b89b8287d2fe

# おしまい
そういえばここら辺説明していなかったなということで急遽書かせていただきました。
これを使うことで遠隔でなんかできますね！
では！！！
Amazon Cognitoで、未認証でのアクセスをログイン画面（Cognito Hosted UI）にリダイレクトさせるときのメモ

## やりたいこと

CloudFront経由で公開しているサイトにて、Cognitoの認証がされていないアクセスについてはログイン画面にリダイレクトさせたい。

### 使うもの

[awslabs/cognito-at-edge](https://github.com/awslabs/cognito-at-edge) 

### Cognito アプリケーションクライアント設定

1. OAuth 付与タイプを「認証コード付与」にする
2. コールバックURLにPathを含めない（例：https://example.com ）← cognito-at-edgeの仕様？ .com/のようにスラッシュも省くこと
3. 「ホストされた UI を表示」ボタンから飛べるHosted UIは使わない（使えない。ログイン後に502エラーとなる）

### Lambda@Edge設定
[awslabs/cognito-at-edge](https://github.com/awslabs/cognito-at-edge) をLambda@edgeに設定する。

```npm
npm install cognito-at-edge
```

Lambda@Edge用に関数を作成し、上記コマンドの生成物をLambdaにあげる。

### 動き

Pathを含めたURLでアクセスした場合もログイン画面にリダイレクトされる。ログイン後に、Pathを含めたURLにリダイレクトされる。  
https://example.com/page/test.html にアクセスしたとして、ログイン後も同じURLにリダイレクトされる。
[Vue Router] routesプロパティを分離してメンテナンス性を高める

:::note
ささやかな小ネタです
:::
## Vue Routerをインポート

まずはVue.jsをインストール
```sh
$ npm init vue@latest
```

src/router/index.ts　
が最初から作られている。

アプリのディレクトリ(/src/など)にrouter.jsを作成して、vue-routerをインポートする。

```router.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView
    },
    {
      path: '/about',
      name: 'about',
      component: () => import('../views/AboutView.vue')
    }
  ]
})

export default router
``` 

## RouteRecordRawを使用して分離

そのまま使用してもよいが、routesプロパティは分離した方がメンテナンス性が高い。
router.jsを修正する。

```router.js
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router';
import HomeView from '../views/HomeView.vue'

const routerSettings: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue')
  }
]
  
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: routerSettings
})

export default router
``` 
この時点では大して変わってないじゃないかと思われるかもしれないが、開発が進みページが増えていくと、ルートの配列を別のファイルとして分離することで管理しやすくなる。
大量の下層ページを作成しなければいけないときに、createRouteRaw関数などを自作して自動生成することも可能。

残念ながら私は増えた後に気づいてしまったので同じような状況の人たちのために。
Snowflake で1万"列"データを扱う技術

Snowflake をお使いの皆さん、これまでに **4XL のウェアハウスを OutOfMemoryError で止めた** ことがありますか？ 私はあります！

テラバイト級のデータ量も涼しい顔で捌くあの Snowflake を落とすなんて、一体どれだけ膨大なデータを食わせたんだ！？ と思うところですが、実際に止めたのは **わずか10行の CSV ファイル x 100ファイル** です。

そのデータというのは、だいたい以下のような感じです。

|  | col_1 | col_2 | col_3 | ... | col_15000 |
|---|---|---|---|---|---|
| **1** | 0.8 | 1.0 | 1.1 | ... | 0.4 |
| **2** | 1.0 | 1.2 | 0.9 | ... | 0.3 |
| **:** | : | : | : | ... | : |
| **10** | 0.9 | 1.2 | 1.0 | ... | 0.4 |

そうです。わずか10行しかないデータですが、 **15,000列** あります。

「それデータモデリングをミスってるやん」というツッコミはあると思いますが、今回はそのあたりについてはノーコメントです。諸事情ありこのようなデータを扱うことになりました。

今回は Snowflake でもなかなか扱うことが難しい **(横方向に) クソデカデータの世界** をご案内したいと思います。

## クソデカテーブルを作る
まずテーブルを作ります。

``` sql
CREATE TABLE foo (
  col_1 number,
  col_2 number,
  col_3 number,
    :
  col_15000 number
)
```

この時点でエラーが出てくれるならまだ良かった。。。

なんとこのクエリは問題なく実行でき、テーブルは作成できます。 **作成できてしまいます。** 作成できてしまったが故に、「さすが Snowflake、列数が多いテーブルも簡単に扱えるんだね!!」と高をくくってしまいます。

ちなみに PostgreSQL の場合は1テーブルあたり最大 1,600 列だそうです。まぁそれだけあれば普通は困らないですよね。

## クソデカテーブルを扱うということ
クソデカテーブルを扱うということはどういうことなのか、みなさんイメージがついていないでしょう。こういうことです。

#### (1) 全カラムを列挙するようなストアドプロシージャを作ろうとすると「関数定義が長すぎます」と怒られる
ストアドプロシージャの定義は (圧縮後のサイズで) 100KB までという制限があるらしいです。そんな制限があることすら知らない人が大半じゃないでしょうか。

#### (2) データ1000件 INSERT のクエリコンパイルが1時間経っても終わらずタイムアウト
「クエリ実行がタイムアウト」ではないですよ？ 「クエリコンパイルがタイムアウト」です。

詳しくはわからないですが、プルーニングのための何らかの処理が行われており、クエリコンパイルそのものではなくそちらの方に時間がかかるらしいです。

## どうしても取り込みたい
兎にも角にもデータが入れれないことには何もはじまりません。

ちなみに、なんやかんや試行錯誤の結果、データを入れさえすれば SELECT クエリは案外普通に通ることがわかりました。つまりはデータを入れてしまえば勝ちです。

しかし上述のとおりで MERGE だの INSERT だのを使用して他テーブルを結合してクソデカテーブルにデータ追加することは難しく、 **残す手段は「外部ステージからのデータロード」のみ** となりました。

あらかじめ結合しておいた15,000列の CSV ファイルを外部ステージ (Amazon S3) に100個ほど配置、COPY INTO を試みます。これが最後の頼みの綱です。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/fedfc44d-702a-6d85-0bad-79cf567197bc.png)

たのむ、成功してくれ・・・！

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/a6590dcd-a975-62ea-687a-7f121f57d6bd.png)

> ***OutOfMemoryError***

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/7a023311-5985-9f4d-6863-2d6057982aab.png)




おしまいです。

メモリ不足だというなら金で殴ればいいじゃない、ということで当時の最大サイズであった 4XL のウェアハウスを使ってやってみましたが、それでもダメでした。

## たったひとつのマッチョな解決策
もうこれでデータを投入するためのすべての手段が絶たれたかのように思われましたが、ここであることに気が付きました。

**「1ファイルだけなら COPY INTO が成功するっぽいぞ？」**

その当時はまだ理由がわかっていませんでした、1ファイルずつなら取り込んでいけることが判明しました。しかし取り込みたいファイルは100万以上あったため、1ファイルずつ取り込んでいくことは現実的ではありません。

ここで妙案を思いつきます。

**「1ファイルに固めてしまえるなら、10行でも100万行でも取り込めるのでは？」**

大量の行データを扱うのには強いはずの Snowflake です。可能性はあります。

試しに「10行 x 15,000列 x 10,000ファイル」を「100,000行 x 15,000列 x 1ファイル」に **気合いで結合** したので、これを先ほど同様に COPY INTO してみます。

。。。

**成功しました。**

かくして1.5万列データをテーブルに投入することができ、事なきを得ました。

あとからわかったことですが、Snowflake ではデータロードの際に複数スレッドで各ファイルを処理しており、その際に1.5万列分の巨大なメタデータがスレッドの数だけ読み込まれることによりメモリ不足に陥っていたようです。1ファイルに固めた場合は1スレッドしか動かずメタデータが1スレッド分しか読み込まれないため、メモリ不足に陥らずにデータロード処理を続行できたということですね。

## まとめ
Snowflake で1.5万列のテーブルを扱うのは不可能ではないっぽいですが、どう考えてもしんどいので真似しないほうがいいと思います。
技術書典(オンライン参加)でやることまとめ

この記事は[Code Polaris Advent Calendar 2023](https://qiita.com/advent-calendar/2023/code-polaris)の8日目の記事です。

### はじめに
今回初めて技術書典に参加しました。Code Polarisとしては2期連続です！
「今期もやりましょう！」という声がなかなか上がらなかったのですが、どうしてもやってみたかったので~~無謀にも~~音頭を取ることにしました。
　せっかくなので、やったこと(やってないけど、"やったほうがいいと思ったこと"も含みます)をまとめていきたいと思います。参考になれば嬉しいです。

### １．準備編
①希望者を募る
②キックオフ会をする(スケジュール伝える、趣旨、利益とコスト、ルール、オフラインをやるかなど)
③キックオフで下記の表を案内し、埋めてもらう(口頭での案内だと伝わってるかどうかも分かるので、できるだけ事前準備を行ってキックオフに臨むこと)

|No| 筆者（Xアカウント)|タイトル(案)|レビューNo| 筆者紹介 |振込希望方法|  
|----|----|----|----|----|----|
|aa |aa |aa |aa |aa |aa |


その他、スケジュールとステータスが分かる表も準備しておきます。


:::note info
スケジュールとタスクごとの締め切りを伝えることはとても重要になります！
:::

### ２．執筆、各原稿のマージ
・執筆勉強会(初心者向け)
・マージは1か月後で締め切り設定しておく(＋1週間バッファとして考慮した期限設定)
・応募、審査あり
・前書きの準備
・表紙検討
・(オフラインの場合のみ)印刷関連検討(サイズ、部数、紙質)

### ３．販売準備
・当落確認
・オンラインマーケットへの登録
・(オフラインの場合のみ)当日の準備そろえる
・宣伝検討

### ４．クロージング
・月末締めで収益計算、支払い
　　売上の集計は日単位ではなく月単位でしかでないので、利益の分配は前回同様、月末締め。
・コストを差し引いて利益分配
・お疲れさまでしたの会(振り返り)

### 最後に
GitHub、Gitも普段使わないため、初めてづくしの経験でした。Code Polarisの方々にいろいろ教えていただき、共に成し遂げることができてよかったです！
C#でSQL接続する方法

# C#でのSQL接続の記事が古すぎる！
まだひよっこエンジニアの自分はUdemyやら会社の先輩やらで聞いてなんとかなったけど、ネットの記事が古くて参考にならなかったので自分で作成することにしました。
特にやり方3はどこにも載っていなかったので、必見です。
結構テンプレのコードが続くので、このコードを丸ごと覚えるのでなく流れを覚えてください。
VisualStudio2019でやった。

# やり方1 (SqlConnectionの使用)
この接続文字列を使うやり方が一番オーソドックスですね
vbaマクロでもsqlサーバーに繋がられるそう
```c#
var builder = new SqlConnectionStringBuilder();
builder.DataSource = "サーバー名";
builder.InitialCatalog = "データベース名";
builder.IntegratedSecurity = true;
_connectionString = builder.ToString();
```
c#側で操作するのはdataGridViewの設定のみです。
下の関数で返り値をdataTableにし、dataGridViewのソースに繋げたらSqlの結果が画面に表示されます。
```c#
public static DataTable GetDataTable()
{
    var sql = @"select * from テーブル名";
    DataTable dt = new DataTable();
    using (var connection = new SqlConnection(_connectionString)
    using (var adapter = new SqlDataAdapter(sql, connection))
    {
        connection.Open();
        adapter.Fill(dt);
    }
    return dt;
}
```

# やり方2 (データソースの選択)
DataSetファイルを追加する。TableAdapterの使用
![IMG_2305.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/49fcbd09-8988-839c-e4f4-a854a493c6b0.jpeg)
TableAdapter構成ウィザードが表示されるので、クエリビルダーまで進めてください。
![IMG_2306.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/b0f92827-87a6-026b-b83f-ff2e25784c51.jpeg)
ここで表示したいデータテーブルの選択をする。ここでクエリービルダーをするとsql文の結果を返す。
![IMG_2307.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/f3cb7988-3c64-8db3-0e63-c87dc79b2e74.jpeg)
データテーブルがデータセット内に作られたらOK
ここからはDataGridView側の設定にうつる。データソースの選択でさっき入れたデータテーブルを選ぶ。
![IMG_2309.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/4611beb8-ef11-835e-1c82-8fe47dc8b488.jpeg)

# やり方3 (TableAdapterを使う)
データテーブルを作るまではやり方2の手順の通りで、TableAdapterインスタンスを生成する。ここではdgvMoneyと名付けたdataGridViewにテーブルを渡している。
```c#
using mta = プロジェクト名.MoneyDataSetTableAdapters;
public void update_table()
{
    mta.moneyDataTableTableAdapter adp = new mta.moneyDataTableTableAdapter();
    MoneyDataSet.moneyDataTableDataTable dt = new MoneyDataSet.moneyDataTableDataTable();

    adp.Fill(dt);

    this.dgvMoney.DataSource = dt;
}
```
さらにQueriesTableAdapterを使うと表示以外の削除(delete文)・挿入(insert文)・更新(update文)を導入できる。やり方2のTableAdapter,DataTableの並びにクエリがあるのでそれを選ぶ。
クエリ名を指定したら下のようなQueriesTableAdapterが作られる。

![IMG_2310.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/e5a03df3-8c25-4c1e-aaac-a1bdd4976cba.jpeg)
ここではたくさんあるけど、DeleteQueryを呼び出したい場合は下のように書く。
```c#
private void DeleteData()
{
    mta.QueriesTableAdapter qadp = new mta.QueriesTableAdapter();
    int nowRow = dgvMoney.CurrentRow.Index;
    int ID = int.Parse(dgvMoney.Rows[nowRow].Cells[0].Value.ToString());
    qadp.DeleteQuery(ID);
    update_table();
}
```
>dataGridViewに表示したい時はDataTableAdapterでいけるが、複雑なsql文を実行したい時はQueriesTableAdapterを使おう
>
Collectionに対する繰り返し処理にはeachを使おう

## 概要
Collectionを用いた処理を書く際に全てforeachで書いてませんか？
eachメソッドを使うと、引数に型宣言を行えるのでエディタの補完も効きやすくなります。

`Illuminate\Database\Eloquent\Collection`でも、
`Illuminate\Support\Collection`の両方で使えます。

> 私はPhpStormを使用しています。PhpStorm使おう

### foreachの場合
![スクリーンショット 2023-12-06 9.47.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/492d1a3a-2527-af3e-785b-612cf569cf9a.png)

### eachメソッドの場合
![スクリーンショット 2023-12-06 9.46.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/f5aa7c1a-b94b-6c19-82bb-4348a4fa01a5.png)


## 参考
https://readouble.com/laravel/10.x/ja/collections.html#method-each
LambdaLayerから独自ライブラリを呼び出す際のTips

# はじめに

ご無沙汰しております。うらさくと申します
この記事は[TechCommit Advent Calendar 2023](https://adventar.org/calendars/8839)の6日目の記事です
連投になりますが頑張ります

# 概要

今のプロジェクトでLambda関数からLambdaLayerを呼び出す際、「LambdaLayerにはないけどLambda関数側にあるライブラリを呼び出せないか？」という要求がありました

そもそもなぜこんな事になっているかというと、Lambda関数＋Layerで250MB制限を超えてしまったため、[こちら](https://yassanabc.com/2022/03/15/%E3%80%90lambda%E3%80%91efs%E3%82%92%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%80%90%E5%AE%B9%E9%87%8F%E5%88%B6%E9%99%90%E5%9B%9E%E9%81%BF%E3%80%91/)の記事を参考に、EFSを使って回避していました

（※私も最初勘違いしていたのですが、Lambda単体ではなく、Lambda＋Layer込で250MB以内じゃないといけないみたいです）

そうした構成にした場合に「LambdaLayerにはないけどLambda関数側にあるライブラリを呼び出す」という特殊なケースが出来上がりました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553852/70a138c7-4503-7e5f-cdf3-fed9bfdf06b9.png)

# 結論

**使えます！**

もちろんLambdaにEFSがマウントされている（もしくはLambda本体が持っている）場合のみです

仕組みとしては下記の公式の記載から読み取れます

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/chapter-layers.html


> 関数にレイヤーを追加すると、Lambda は関数の実行環境の /opt ディレクトリにレイヤーのコンテンツを抽出します。ネイティブにサポートされているすべての Lambda ランタイムには、/opt ディレクトリ内の特定のディレクトリへのパスが含まれています。これにより、関数はレイヤーコンテンツにアクセスできるようになります。

つまり、「**Layerを読み出す際は、Lambdaの実行環境上に物理的に抽出するから、Layer上にないライブラリでも実行元のLambdaが持ってれば実行できる**」という感じです（わかりにくいかも笑）

# まとめ

正直、運用として正しいのかと言われると結構怪しいと思っています

今回の場合EFSをマウントしていないLambdaから呼び出すとエラーになりますし、Layerとして公開している以上、利用側は何も気にすることなくLayer単品として利用できるというのがあるべき姿だと思います

ただ今回は小さいプロジェクトかつ、利用者も限られているということで暫定的にこの構成で運用しています

みなさんもLambdaの250MB制限に引っかかることはたまにあると思うので、何かの参考になれば幸いです

最後まで読んでいただきありがとうございました
金欠高校生はサクッとwebサイトを作ってみたい！

今年１年間で様々なwebサイト開発に携わる機会があったのでそれぞれのメリット、デメリットやどのくらいの規模に適しているかを個人的に見ていきたいと思います

今回、比較するのは以下の４つです

- Django(フロントエンド・バックエンド)
- Nuxt/Vue(フロントエンド)
- Hugo(SSG)
- 生のhtml

# TL;DR

初めてのデータベースを使うようなwebアプリ開発にはDjangoがおすすめ
APIが作れるならVue
サクッとブログを作るならHugo
１枚のページのwebサイトならフレームワークを使わずにhtmlのほうが書きやすい

# 比較

## Django

### 特徴

1. **ORM（Object-Relational Mapping）**
　データベースのマイグレーションの管理や操作までできる

2. **管理画面の提供**
　Djangoは自動生成される管理画面を提供してくれるので、コマンドを使わずにデータの編集や追加、削除ができる！

3. **フロントエンドとバックエンドの親和性が高い**
　変数を直接htmlに記述することができるからAPIを使う場合に比べて本当に手軽に動的なサイトが作れる

### ユースケース:

- **大規模なウェブアプリケーションの構築**
　Djangoは大規模なウェブアプリケーションの構築に適しています。セキュリティ、データベース、管理画面など、多くの機能が組み込まれているため、大規模で複雑なプロジェクトに対応できる。

- **初心者に優しい**
　APIを別で立てる必要もなく、ある程度のセキュリティも担保してくれるから初めてのwebサイト開発でも作りやすい
  (自分も最初に作ったwebアプリはdjangoで作りました...!)

## Nuxt.js

### 特徴

1. **Vue.jsを使っている人は気軽に移行できる**
　Nuxt.jsはVue.jsのフレームワークで、Vue.jsの機能を拡張してるからコンポーネントやデータバインディングなどの特徴をそのまま利用できる。

2. **SSRとSSGがサポートされてる**
　サーバーサイドレンダリング(SSR)は、サーバー上でページを事前にレンダリングし、クライアントにHTMLを返すことで、SEO対策や初回読み込みの高速化が期待できる。静的サイト生成(SSG)は、ビルド時に事前にHTMLを生成し、サーバーレスで配信できる。

3. **ルーティングを自動生成してくれる**
　ページとコンポーネントをプロジェクトに配置するだけで、Nuxt.jsが自動的にルーティングを生成してくれる(これ便利！)。

4. **プラグインとモジュールがある**
　Nuxt.jsには多くのプラグインとモジュールが提供されてるので、デフォルトで組み込まれているものや、サードパーティのものを簡単に利用できる。

### ユースケース:

- **中規模から大規模なウェブアプリケーションの構築**
　Nuxt.jsはVue.jsの力強さを活かしながら、構造や機能の整理が必要な中規模から大規模なウェブアプリケーションの構築ができる。

- **SEO対策も！**
　SSRがサポートされているので、検索エンジンがウェブアプリケーションのコンテンツを正確にクロールしやすくなる。

## Hugo

### 特徴

1. **高速なビルドとパフォーマンス**
　HugoはGo言語で開発されてるので、非常に高速なビルドとパフォーマンスを得られる。

2. **シンプルな構造**
　Hugoのプロジェクトはシンプルな構造を持っており、コンテンツやテーマの管理が容易です。ディレクトリ構造はフォルダとファイルのみで構成されているので、見やすい！

3. **静的サイト生成**
　Hugoはサーバーサイドの処理が不要で、静的なHTMLを生成してくれる！
　だからこそ、安定性が高く、ホスティングが簡単な静的なサイトを構築できる。
　ただ、動的サイトには向いてない

### ユースケース:

- **ブログや個人サイトの構築に向いてる**
　Hugoはコンテンツの管理がシンプルであり、個人のブログやポートフォリオサイトの構築に適してる。

- **高速で安定した静的サイトが手軽に作れる**
　サーバーサイドの処理が不要で、ビルドが非常に高速なので、**静的サイト**に向いてる。
 　特に大量のコンテンツを効率的に処理する場合に力を発揮してくれる。

## 生のHTML

### 特徴:

1. **原点にして頂点**
　ページのデザインや構造に完全な制御が可能
　手動でHTMLを書くことで、特定の要件に合わせて柔軟なデザインを実現できる。

2. **軽量**
　フレームワークやライブラリを使用せずに生のHTMLを書くことで、ページが非常に軽量になる。

3. **大きなサイトには向いてない**
　動的なサイトやページ数の多いサイトを作るのはちょっとしんどい...

### ユースケース:

- **簡単な静的サイトの構築**
　小規模でシンプルな静的サイトを構築する場合、迅速な開発とシンプルなメンテナンスが求められる場合に有用

- **デザイン要件にこだわりがある場合**
　フレームワークが提供するデフォルトのスタイルや構造を使用せず、独自のデザインを実現する場合


# 終わりに

それぞれのプロジェクトの必要とする要件と相談しながら楽してwebアプリ開発を楽しみましょう！
【TIPS】構造体とマップの相互変換

この記事は、[Elixir Advent Calendar 2023 シリーズ8](https://qiita.com/advent-calendar/2023/elixir) の12日目です

昨日は、私で [「【TIPS】構造体とマップの相互変換」](https://qiita.com/piacerex/items/2585d4569376dc92004a) でした

---

【本コラムは、1分で読め、2分で試せます】

[piacere](https://twitter.com/piacere_ex) です、ご覧いただいてありがとございます :bow:

Elixirの構造体って、マップのように使える割には、一方でMapモジュール関数が使えなかったりと面倒なこと、ありませんか？

そういうシーンで困らないように、相互変換をTIPSとしてまとめておきました … Elixirの様々な書き方のトレーニングにもなると思います

# マップから構造体へ変換

```elixir
struct(Range, %{first: 1, last: 10, step: 2})
```

```elixir
%{first: 1, last: 10, step: 2} |> then(&struct(Range, &1))
```

# 構造体からマップへ変換

```elixir
%Range{first: 1, last: 10, step: 2} |> Map.from_struct
```
【TIPS】マップ：文字列キーとアトムキーの相互変換

この記事は、[Elixir Advent Calendar 2023 シリーズ8](https://qiita.com/advent-calendar/2023/elixir) の11日目です

昨日は、私で [「【TIPS】マップ：文字列キーとアトムキーの相互変換」](https://qiita.com/piacerex/items/c283492fdd649cc73837) でした

---

【本コラムは、5分で読め、10分で試せます】

[piacere](https://twitter.com/piacere_ex) です、ご覧いただいてありがとございます :bow:

Elixirのマップって、文字列キーとアトムキーの2種類、定義があり、これを入れ替えるのが面倒なこと、ありませんか？

TIPSとしてまとめておきました … Elixirの様々な書き方のトレーニングにもなると思います

# 文字列キーからアトムキーへ変換

```elixir
%{"first" => 1, "last" => 10, "step" => 2} |> Map.new(fn {k, v} -> {String.to_atom(k), v} end)
```

```elixir
%{"first" => 1, "last" => 10, "step" => 2} |> Map.new(&{String.to_atom(elem(&1, 0)), elem(&1, 1)})
```

```elixir
for {k, v} <- %{"first" => 1, "last" => 10, "step" => 2}, into: %{}, do: {String.to_atom(k), v}
```

```elixir
%{"first" => 1, "last" => 10, "step" => 2} |> Jason.encode! |> Jason.decode!(keys: :atoms)
```

# アトムキーから文字列キーへ変換

```elixir
%{first: 1, last: 10, step: 2} |> Map.new(fn {k, v} -> {Atom.to_string(k), v} end)
```

```elixir
%{first: 1, last: 10, step: 2} |> Map.new(&{Atom.to_string(elem(&1, 0)), elem(&1, 1)})
```

```elixir
for {k, v} <- %{first: 1, last: 10, step: 2}, into: %{}, do: {Atom.to_string(k), v}
```

```elixir
%{first: 1, last: 10, step: 2} |> Jason.encode! |> Jason.decode!
```

---

明日は、私で [「【TIPS】構造体とマップの相互変換」](https://qiita.com/piacerex/items/2585d4569376dc92004a) です