OmniAICreator/Qiita-1.07M huggingface dataset:
文字列配列を任意の並び順で辞書式ソートした型を取得する方法

※配列をソートした結果 __ではなく__ 含まれている型をソートした結果を取得する方法です。
やりたいことの前準備で必要だったので、備忘録です。

```ts

// この型に文字列リテラルの配列を渡すと、ソートした結果の配列型を返す
type Sort<T extends string[]> = Joins<SortImpl<Separators<T>>>;

type Test = Sort<['AB', 'G', 'AE', 'G', 'BD', '2F', 'AC', 'B', '1F']>; 
//=> type Test = ["AB", "AC", "AE", "B", "BD", "G", "G", "1F", "2F"]


// 対象となる文字とその並び順
type Characters = [
    'A', 'B', 'C', 'D', 'E',
    'F', 'G', 'H', 'I', 'J',
    'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T',
    'U', 'V', 'W', 'X', 'Y',
    'Z',
    0, 1, 2, 3, 4,
    5, 6, 7, 8, 9,
];

type Character = Characters[number];

// ソートするために文字列を文字単位に区切る
type Separator<T extends string> = T extends `${infer A extends Character}${infer B}` ? [A, ...Separator<B> ]  : [];
type Separators<T extends string[]> = T extends [infer A extends string, ...infer B extends string[]] ? [Separator<A>, ...Separators<B>]: [];
// 区切った文字の復元用
type Join<T extends Character[]> = T extends [infer A extends Character, ...infer B extends Character[]] ? `${A}${Join<B>}` : '';
type Joins<T extends (Character[] | undefined)[]> = T extends [infer A extends Character[], ...infer B extends Character[][]] ? [Join<A>, ...Joins<B>] : [];

type Pop<T extends Character[]> = T extends [T[0], ...infer A] ? A : [];

// 対象となる文字を先頭から比較していき、どちらかがマッチするか調べる。
// どちらも引っかからない場合、次の対象文字で比較する。
// 同じ文字同士の場合、次の文字同士で比較する。
type Compare<T1 extends Character[], T2 extends Character[], C extends Character[] = Characters> =
    T1[0] extends T2[0] 
        ? [T1[0], T2[0]] extends [undefined, undefined]
            ? 0
            : Compare<Pop<T1>, Pop<T2>>
        : T1[0] extends undefined
            ? -1
            : T2[0] extends undefined
                ? 1
                : T1[0] extends C[0] 
                    ? -1
                    : T2[0] extends C[0]
                        ? 1
                        : Compare<T1, T2, Pop<C>>; 

// マージソートを行う
type Merge<T1 extends Character[][], T2 extends Character[][]> =
    T1[0] extends undefined 
        ? [...T2]
        : T2[0] extends undefined
            ? [...T1]
            : Compare<T1[0], T2[0]> extends -1 
                ? T1 extends [infer A extends Character[], ...infer B extends Character[][]]
                    ? [A, ...Merge<B, T2>] 
                    : []
                : T2 extends [infer A extends Character[], ... infer B extends Character[][]]
                    ? [A, ...Merge<T1, B>]
                    : []
;

type SortImpl<T extends Character[][]> =
    T[1] extends undefined 
        ? [T[0]] 
        : T extends [infer A extends Character[], ...infer B extends Character[][]]
        ? Merge<[A], SortImpl<B>>
        : [];
```
【React / Next】SWRのfetch中のチラつきをなくす

SWRのfetch中のチラつきをなくすための方法です。
公式に書かれていました。

# 環境
- React: 17.0.2
- Next: 11.1.3
- SWR: ^1.2.2

# サンプルコード
公式から拝借

```tsx:SWR
const useUser = (id) => {
  const { data, error } = useSWR(`/api/user/${id}`, fetcher)

  return {
    user: data,
    // ここ
    isLoading: !error && !data,
  }
}
```

```tsx:component
export const Component = () => {
  const { user, isLoading } = useUser(id);

  if (isLoading) {
    return <Spinner />;
  }

  return (
    // 省略
  );
};
```

# 参考
https://swr.vercel.app/ja/docs/getting-started#%E5%86%8D%E5%88%A9%E7%94%A8%E5%8F%AF%E8%83%BD%E3%81%AB%E3%81%99%E3%82%8B
【Laravel】Illuminate\Database\Eloquent\Collection::paginate does not exist.



## 環境
Laravel v9.5.1 (PHP v8.1.3)

## 状況
ページネーションを実装しようとしたらタイトルのエラー。

```controller.php
public function show()
{
   $readHistories = $request->user()->readHistories()->paginate(25);

   return new ReadHistoryCollection($readHistories);
}
```

```Models/User.php
public function readHistories(): Collection
{ 
   $hogeReadHistories = $this->hogeReadHistories()->get();
   $fugaReadHistories = $this->fugaReadHistories()->get();

   return $hogeReadHistories->merge($fugaReadHistories)->sortByDesc('created_at');
}
```
## 原因
```$request->user()->readHistories()```がcollectionで返っていて、```paginate```はクエリビルダかEloquentクエリのメソッドなのでエラーが起きていた。


## 解決法

```$request->user()->readHistories()```をbuilderで返るように修正する。
```Models/User.php
public function readHistories(): Builder
{ 
   return ReadHistory::where('hoge_id', $this->id)
                      ->orWhere('fuga_id', $this->id)
                      ->orderByDesc('created_at');
}
```
WSL上でDockerが起動しないときの対処

久々にwsl上でdocker起動しようとしたら動かなかったので対策をメモ

# 環境
ホストOS: Windows10
wsl上のOS: Ubuntu 20.04

# 現象
wslを立ち上げてdockerを起動する
```bash
$ sudo service docker start
 * Starting Docker: docker
```

起動したかと思いきや確認すると起動してない
```bash
sudo service docker status
 * Docker is not running
```

logを確認する
なんかErrorでてる
```
cat /var/log/docker.log
```
```
time="2022-06-30T16:46:34.468361900+09:00" level=warning msg="grpc: addrConn.createTransport failed to connect to {unix:///var/run/docker/containerd/containerd.sock  <nil> 0 <nil>}. Err :connection error: desc = \"transport: Error while dialing dial unix:///var/run/docker/containerd/containerd.sock: timeout\". Reconnecting..." module=grpc
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: iptables failed: iptables -t nat -N DOCKER: iptables v1.8.4 (legacy): can't initialize iptables table `nat': Table does not exist (do you need to iからmod?)
Perhaps iptables or your kernel needs to be upgraded.
 (exit status 3)
```

# 原因
もしやと思ったがどうやらWSL2が更新されておらず、1のままらしい
PowerShellで
```PS
wsl --list --verbose
  NAME            STATE           VERSION
* Ubuntu-20.04    Running         1
```

# 対処
PowerShellで以下を実行し、バージョンを2に切り替えようとするもできない

```PS
wsl --set-version Ubuntu-20.04 2
変換中です。この処理には数分かかることがあります...
WSL 2 との主な違いについては、https://aka.ms/wsl2 を参照してください
WSL 2 を実行するには、カーネル コンポーネントの更新が必要です。詳細については https://aka.ms/wsl2kernel を参照してください
```

下記から *wsl_update_x64.msi*を入手してインストール
https://docs.microsoft.com/en-US/windows/wsl/install-manual#step-4---download-the-linux-kernel-update-package

再びやってみる
```PS
wsl --set-version Ubuntu-20.04 2
変換中です。この処理には数分かかることがあります...
WSL 2 との主な違いについては、https://aka.ms/wsl2 を参照してください
変換が完了しました。
```

いけた！

この状態でもう一度wslから起動してみる

```bash
$ sudo service docker start
 * Starting Docker: docker
```

```bash
$ sudo service docker status
 * Docker is running
```

うごいたー
Rilasのform_withヘルパーメソッドでのエラー

ある特定の条件のユーザーのみ、railsで作成したルームからコメントを投稿しようとすると下記のようなエラーが出て、解決したのでアウトプットします。「undefined method `model_name' for nil:NilClass」

# エラー発生の経緯
```ruby:show.html.erb(コメントを打ち込む場所)
<%= form_with(model:[@room, @comment], local: true) do |f|%>
<%= f.text_area :text, placeholder:"コメントを入力してください" ,class:"comment_box"%>
<%= f.submit '送信', class:"comment_bottom_r"%>
<%end%>
```
上記のような記述でコメント送信ボタンを押すと、undefined method `model_name' for nil:NilClassとエラーが出ます。投稿フォームでこのようなエラーが出る場合、考えられる原因を列挙すると、
・ルーティングがネストになっていないなどの不備がある。
・comment.htmlにスペルミスがある
・comment.htmlに遷移する前に、コントローラー内でコメントのインスタンスを発行していない。
・form_withに適切な変数を渡していない
などが考えられます。従って、routes、コントローラーを確認します。
```ruby:config/routes
Rails.application.routes.draw do
  resources :rooms do
    resources :comments,only:[:show, :create, :destroy]
  end
end
```
routesはちゃんとネスト等も組んでおり問題ありませんでした。次にコントローラーを確認します。コメント打ち込む場所がshow.htmlですので、そこに遷移するためのコントローラー内のshowアクションで、しっかりとインスタンス変数を生成しているか確認します。
```ruby:room_controller
def show
    @room = Room.find(params[:id])
    @comment = Comment.new
end
```
Comment.newで空のインスタンス変数は生成しています。以上より確認した場所では特に問題がないことがわかりました。
# エラーの内容
undefined method `model_name' for nil:NilClassをアバウトに日本語訳すると、nilclassであるmodel_nameというメソッドを定義していないというもの。model_nameというメソッドが原因なのはわかりましたが、そもそもmodel_nameなんてもの定義していませんし、触れてすらいませんし、検討がつきません。結局はいろいろなパターンを総当たりして、解決しました。
# エラー解決方法
結論から言うと、commentsのcraeteアクション内で、再度"@comment = Comment.new"を定義し直すと、エラーが出ないようになりました。

```ruby:comment_controller
class CommentsController < ApplicationController
  before_action :authenticate_user!
  
  def create
　　　　　　　　@comment = Comment.new　#この行を追記。
    @room = Room.find(params[:room_id])
    if find_user_positioin != nil
      if Comment.create(comment_params)
        redirect_to room_path(@room)
      else
        create_comments
        render 'rooms/show'
      end
    else
      create_comments
      render 'rooms/show'
    end
  end
```
確かな原因はわかりませんでしたが、送信する際にコメントのインスタンス変数をどこかに落としてしまって、エラーが出たのかなと思います。もしかしたら、sessionなんかが絡んでいるのかもしれません。
【SwiftUI】TimelineViewを使ってデジタル時計を作る

# はじめに
iOS15から`TimelineView`というものが登場していたらしいです
コンポーネント一覧を見ていたら発見しました笑
気になったので`TimelineView`を使用して簡単なアプリを作成しようと思います。

# 完成形
![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/257b146a-ef0d-f222-e6ed-69b67fab5b3f.gif)

# 完成コード
```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        GeometryReader { geo in
            ZStack(alignment: .center) {
                Color(red: 132/255, green: 209/255, blue: 78/255).edgesIgnoringSafeArea(.all)
                TimelineView(.periodic(from: Date(), by: 1)) { timeline in
                    VStack(alignment: .center, spacing: 10) {
                        Text("\(timeline.date.formatted(.iso8601.year().month().day().dateSeparator(.dash)))")
                            .fontWeight(.medium)
                            .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/15))
                            .foregroundColor(.black)
                            .background(
                                Text("8888-88-88")
                                    .fontWeight(.medium)
                                    .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/15))
                                    .foregroundColor(.black.opacity(0.1))
                            )
                        Text("\(timeline.date.formatted(.iso8601.time(includingFractionalSeconds: false)))")
                            .fontWeight(.medium)
                            .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/6))
                            .foregroundColor(.black)
                            .background(
                                Text("88:88:88")
                                    .fontWeight(.medium)
                                    .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/6))
                                    .foregroundColor(.black.opacity(0.1))
                            )
                    }
                }
            }
        }
    }
}
```

# 解説
## 時間の更新
今回のメインである`TimelineView`は以下のような使い方になります。
```swift
TimelineView(.periodic(from: Date(), by: 1)) { timeline in

}
```
`timeline`から`date`を取り出します
```swift
// 日付
Text("\(timeline.date.formatted(.iso8601.year().month().day().dateSeparator(.dash)))")

// 時間
Text("\(timeline.date.formatted(.iso8601.time(includingFractionalSeconds: false)))")
```
:::note warn
日本時間にしたい場合、timeZoneを設定してください
:::

## フォント
カスタムフォントを設定しています。
フォントサイズは適当なので各自調整してください。
一応、iPad 11inch, iPhoneSE, iPhone12で正常に表示される事を確認しました
```swift
.font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/15))
```
https://qiita.com/SNQ-2001/items/6531526ab3aa51855247

## 文字背景に焼き付きを表示
88を重ねることによってデジタル時計感が増します
```swift
.background(
    Text("8888-88-88")
        .fontWeight(.medium)
        .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/15))
        .foregroundColor(.black.opacity(0.1))
)
```


# おわり
iOS15以前だと定期的に更新するには`timer`や`onReceive`を使用するなど面倒でした。
しかし、`TimelineView`の登場により、定期更新が簡単になった気がします。

完成プロジェクトを置いときます。

https://github.com/SNQ-2001/Digital-Clock-Demo
設定ファイルの passwd と shadow の違い

## 勉強前イメージ

パスワードを暗号化してるかしてないかくらいしかわからん

## 調査

### /etc/passwd とは

linux系のOSでユーザのパスワードやログイン情報を記載したファイルで、CentOS7では以下のように見えています。

```
[root@localhost ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
.....
```

設定ファイルは一行ずつ記録されていて、 `:` で区切られています。
`root:x:0:0:root:/root:/bin/bash` であれば以下になります。

1. ユーザ名 : `root`
2. パスワード : `x`
3. ユーザID : `0`
4. グループID : `0`
5. コメント欄 : `root`
6. ホームディレクトリ : `/root`
7. ログインシェル : `/bin/bash`

また、/etc/passwdの権限は644になります。
一般ユーザにも読み取り権限がないとユーザ名が表示されなかったりします。

### /etc/shadow とは

パスワードを一般ユーザに見えないようにするための工夫で、このファイル自体はrootしか読み取り権限がありません。
元々は/etc/passwdに書かれていましたが、誰でも読み取りができてしまいました。
CentOS7では以下のように見えています。

```
[root@localhost ~]# cat /etc/shadow
root:XXXXXXXXXXX::0:99999:7:::
bin:*:18353:0:99999:7:::
daemon:*:18353:0:99999:7:::
....
```

/etc/passwd同様設定ファイルは一行ずつ記録されていて、 `:` で区切られています。
`root:XXXXXXXXXXX::0:99999:7:::` であれば以下になります。

1. ユーザ名 : `root`
2. パスワード(暗号化されています) : `XXXXXXXXXXX`
3. パスワードを最後に変更した日 : [空白]
4. 変更可能最短期間 : `0`
5. 未変更可能最長期間 : `99999`
6. 未変更時警告日 : `7`
7. ログインしない場合に無効になる日数 : [空白]
8. アカウントが失効になるまでの日数 : [空白]
9. フラグ : [空白]

また、2のパスワードに頭に `!!` を `root:!!XXXXXXXXXXX::0:99999:7:::` のようにつけるとアカウントがロックされログインできなくなります。

## 勉強後イメージ

一般ユーザに見えないようにshadowをつかってるのか・・・・
ちょっと不思議なのか以下のように/etc/shadowをllで見たとき何もついてないように見える

```
---------- 1 root root 684  5月 26 23:49 /etc/shadow
```

## 参考

- [「/etc/shadow」ファイル](https://linuc.org/study/knowledge/510/)
- [「/etc/passwd」ファイル](https://linuc.org/study/knowledge/509/)
- [6./etc/passwdと/etc/shadow](http://www.linux-beginner.com/linux_other6.html)
WBS作成でのポイント ＜初心者向け＞

## はじめに
業務で初めてWBSの作成を行ったため、その時学んだことや教えていただいたことを記録したいと思います。
WBS作成初心者の方の参考になれば幸いです。

## プロジェクトについて
今回、WMS（倉庫管理システム）の導入プロジェクトについてWBSを作成しました。
弊社ではWMSの標準パッケージを主力製品として持っており、あるお客さまに対してその導入を行うプロジェクトとなっています。

## 注意したポイント

#### 各フェーズの役割を明確に把握する
WBSの大分類は、

要件定義 → 内部設計 → 外部設計 → 開発 → 単体テスト → 結合テスト → 統合テスト（他システム連携） → UAT → 移行テスト → ユーザー教育 → 導入

となっています。（ウォーターフォールの場合）
各フェーズにおける成果物や担当者、目的を意識することで、自然と細分化されたタスクが決まってくるはずです。
また、なるべく各フェーズで被りがないようにタスクを振り分けることも大切です。

#### カスタマイズが必要な機能を明確にする
標準パッケージの導入プロジェクトにおいては、標準機能で事足りる要望とカスタマイズが必要になってくる要望を分けて考えることが重要です。
標準機能で事足りるタスクは設計や開発の負担が少なく済みますが、カスタマイズが必要な機能に関しては細かく設計やお客様との検討を行っていく必要があるためタスクを細分化する必要があります。

#### 開発フェーズのタスクは、エンジニアが開発しやすいような実装機能とする
エンジニアにとって、細分化された実装機能が提示されていると開発をスムーズに行うことができます。
内部設計までの段階でなるべく細分化された実装機能一覧を作成できるように、実際に開発が必要になりそうな機能をイメージしながら設計フェーズのタスクを考えていくことが大切だと思います。

#### 要件定義書の内容が全て網羅されているか確認する
WBS作成時に要件定義書がある場合は、自分が作成したWBSと要件定義書の内容を比較して漏れがないかをチェックしましょう。
これはPMに提出するタイミングで最終チェックとして行うと良いと思います。

#### PMと認識のずれがないか確認する
ほとんどの情報は要件定義書に網羅されていると思いますが、場合によってはPMの頭の中で構想が練られている事項もあるかもしれません。
また、プロジェクト参加の経験が浅いと（筆者は初めてでした・・・）要件定義書だけ見ても理解できないことが多いはずです。
認識のずれが生じたまま、WBSを作成してしまうと危険です。ずれに気付いたタイミングが遅ければ遅いほど軌道修正に時間がかかってしまいます。
このような事態を避けるためにも、筆者は疑問に感じた点があればすぐにPMに聞くように心がけていました。また、WBSの大まかな流れ（分類の割り振りなど）などの作成方針が決まったら一度確認してもらうのもいいと思います。

## 最後に
以上、初めてのWBS作成で筆者が気をつけたポイントです。
会社やプロジェクトによってWBS作成の方針も異なると思うので、この記事はあくまで参考程度に、具体的なことは上司やPMに確認していただければと思います。
最後までご覧いただきありがとうございました。
【Tailwind CSS】画像を真ん中に置きたいのに右にpaddingができた時

# はじめに
プログラミング学習中の[@kat_log](https://twitter.com/kat__log "Twitter")と申します。

自作アプリにてdiv内に、画像を真ん中に置きたいのに右に空白ができてしまい困っていたのですが、解決したため情報共有です。

（アプリはRuby on Railsを使って作成しているためRailsのコードで記載をしています）

![image.png](https://soco-st.com/wp-content/themes/socost/upload/11813_color.svg)

# 結論

画像の`class`に**container**を設定すると解決しました。

# 試したこと（解決せず）

まだまだ学習中で色々試しました💧

- object-center
- bg-center
- justify-center
- origin-center
- justify-items-center
- justify-self-center
- content-center
- items-center
- self-center 
- place-items-center
- widthいじり・・・

# ビフォー　（解決前）

画像が四角の中の左に寄っています↓

## 見栄え

![image.png](https://s3.us-west-2.amazonaws.com/secure.notion-static.com/3c258eea-3731-4ebf-a78e-5524fde3baeb/Screen_Shot_2022-04-03_at_11.52.56.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220630%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220630T094744Z&X-Amz-Expires=86400&X-Amz-Signature=b186ba0d4b221203c95c83db6bcf284943e7f4cc0dee39973da53b7a37a57d31&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22Screen%2520Shot%25202022-04-03%2520at%252011.52.56.png%22&x-id=GetObject)

## コード

`container`未指定です

```erb
<div class="m-5 rounded-xl ">
	<%= link_to image_tag(ranking['mediumImageUrls'][0],
		{ class:"rounded-xl hover:opacity-75"}),
		ranking.url, target: :_blank, rel: "noopener noreferrer" %>
</div>
```

# アフター　（解決後）

## コード

`container`を指定しています↓

```erb
<div class="m-5 rounded-xl ">
	<%= link_to image_tag(ranking['mediumImageUrls'][0], 
		{ class:"rounded-xl hover:opacity-75 container"}), 
    ranking.url, target: :_blank, rel: "noopener noreferrer" %>
</div>
```

## 見栄え

画像がちょうどいい感じに配置されています↓

![image.png](https://s3.us-west-2.amazonaws.com/secure.notion-static.com/68ae39fc-5ecc-4fbe-8bd6-1f41a8f7a776/Screen_Shot_2022-04-03_at_16.15.37.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220630%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220630T095129Z&X-Amz-Expires=86400&X-Amz-Signature=2d132fd93cef7d90104e7f43026156558ba23a8c87b40035bcc38d7e569c49bc&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22Screen%2520Shot%25202022-04-03%2520at%252016.15.37.png%22&x-id=GetObject)

以上です！

# おわりに
どなたかの参考になれば嬉しいです！
お読みいただきありがとうございました！😄

# 参考

https://www.twilio.com/blog/style-your-website-with-tailwind-css-jp
【PHPUnit】よりベターなテストの仕方

## 環境

Laravel v9.5.1 (PHP v8.1.3)

## 前提条件

PostモデルにUserで検索する```scopeByUser()```を定義している。
Unitテストでこのメソッドをテストしたい。

```php
public function scopeByUser($query, User $user): Builder
{
  return $query->where('user_id', $user->id);
}
```

## Before

```php
  /**
   * scopeByUser()のテスト
   * UserでPostを検索することができる
   */
  public function testScopeByUser()
  {
    $users = User::factory()->count(2)->create();
    $posts = Post::factory()->for($users[0])->count(5)->create();
    $otherPost = Post::factory()->for($users[1])->create();

    $results = Post::byUser($users[0])->get();
    $this->assertEquals($results->count(), $posts->count());
    $this->assertNotContains($otherPost->id, $results->pluck('id')->toArray());
  }
```

```scopeByUser()```の結果と用意したテストデータのpostの数が一致してることをテストしたいので一見良さそうだが、下記の2点でテストとしてはちょっと微妙。
- もしテストデータを変えてしまったときでも```$posts->count()```で都合よく計算されてしまう可能性がある
- (Laravelのメソッドなので大丈夫だとは思うが)```$posts->count()```が正しいかもテストしなくてはいけなくなる
```php
$this->assertEquals($results->count(), $posts->count());
```

## After



```php
  /**
   * scopeByUser()のテスト
   * UserでPostを検索することができる
   * 4人中3人のユーザーの投稿を検索した場合には、3人のユーザーの投稿のみが取得できる
   */
  public function testScopeByUserIds()
  {
    $users = User::factory()->count(2)->create();
    $posts = Post::factory()->for($users[0])->count(5)->create();
    $otherPost = Post::factory()->for($users[1])->create();

    $results = Post::byUserIds($userIds)->get();
    $this->assertEquals($results->count(), 5);
    $this->assertNotContains($otherPost->id, $results->pluck('id')->toArray());
  }
```
変更点
- コメントで前提条件と結果を書く
- 投稿数を実際の数字で明記


```php
$this->assertEquals($results->count(), 5);
```

テストとしてより正確で、仕様書としてもわかりやすくなった！
AWS SESメール受信先にSlackチャンネルを指定しようとしたがダメだった

## 概要

サンドボックス状態のAWS SESからのメール送信先として、Slackチャンネル宛のメールアドレスを登録しようとしたがうまくいかなかった、という記録です。

## 経緯

とある新規サービス立ち上げの際、Eメール送信に[AWS SES](https://docs.aws.amazon.com/ja_jp/ses/index.html)（Simple Email Service, 以降SES）を利用することにしました。

SESは不正利用防止のため、初期状態では機能制限がかかっています。
https://docs.aws.amazon.com/ja_jp/ses/latest/dg/request-production-access.html
> 不正使用や悪用を防止し、送信者としての評価を保つため、新しい Amazon SES アカウントには一定の制限が適用されます。
> すべての新しいアカウントが Amazon SES サンドボックスに配置されます。
> ...
> Eメールの送信先は、検証済み Eメールアドレスおよびドメイン、または Amazon SES メールボックスシミュレーターに制限されます。
> ...

今回は、サンドボックス解除申請と並行してサーバアプリケーション側からのメール送信機能テストを行うこととなったため、SESから送信されたメールを受信するためのメールアドレスを準備する必要がありました。

会社のメールアドレスやフリーのメールアドレスを個人で取得することも考えましたが、

- 受信内容を安全にチーム内で共有
- チームメンバーの変動にも高度な柔軟性を維持

できるとうれしいな、と思い方法を検討したところ、Slackチャンネルをメール受信先として設定して利用できないかと思い至りました。

**Slackチャンネルに直接メール送信するメリット**

- Slackチャンネルに紐づくメールアドレスは簡単に発行可能
    - 参考: [Slack にメールを送信する](https://slack.com/intl/ja-jp/help/articles/206819278-Slack-%E3%81%AB%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%92%E9%80%81%E4%BF%A1%E3%81%99%E3%82%8B#h_01F4WDZG8RTCTNAMR4KJ7D419V)
- Slackチャンネル側で受信メールにアクセス可能な人をコントロール可能
    - チャンネルへの招待=テストメール受信者への追加
 - 使わなくなったらチャンネルをcloseすれば良い
 - 元々社内でSlackは導入済みなので追加費用なし

## 結果

できませんでした。

### 没理由

SESからSlackチャンネル宛メールアドレスに送った検証用メールがなぜか届きませんでした。

検証メールが届かない 
=> SESの送信許可リストに追加できない 
=> 没

![example-ses-verified-ids.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434222/b7d55499-2349-448d-b22f-44b784ce629c.png)


### memo:検証メール受信に向けた足掻きと確認

- SESのコンソール上から再送信 => 不達
- 到達遅れているだけかも 
    - => 1時間以上待ってみたがやはり来なかった
- SES側の設定を間違えているのかも
    - 自身の社用メールアカウント（=Gmail）宛を同様の操作でSESに登録したところ、すぐに検証用メールが届き、メール内の案内に従った操作で検証に成功
- そもそもSlackチャンネル側の設定がおかしいのかも
    - 自身の社用メールアカウント（=Gmail） から送ったSlackチャンネル宛のメールは届いた
    - 元々 private channel 向けのメールアドレスを宛先に指定していたので、念の為 public channel 宛のメールアドレスも試してみたが、やはりダメ（不達）

なお、SES, Slack双方でメール送受信情報にアクセスできないため、実際にどこでDropしたのか調査もできませんでした。

### その他確認事項

一応、サンドボックス解除申請が通った後であれば、メール受信検証をしていない宛先にも送信できるので、Slackチャンネル宛メールアドレスにSESからメール送信することは可能でした。
（なぜ検証メールだけは届かないのか...）
Visual StudioでC++のbits/stdc++.hを使う

競技プログラミングでC++をやっている人にはおなじみかと思うbits/stdc++.hですが、
VisualStudioではデフォルトでは使うことができません。
そこで、VisualStudioでも使えるようにします。

:::note info
プロジェクト毎に設定するのが面倒なので、Visual Studio全体で使えるような方法で説明します。
:::
# そもそもbits/stdc++.hって何？
[こちら](https://qiita.com/hakatashi/items/f9d9abf05a002b5c4dc5)の記事にて詳しく書かれています。

# 設定方法
今回は**Visual Studio 2022**での設定方法を記載しますが、
他のバージョンでも同様の手順にて設定できるかと思います。

## includeのパスを探す
デフォルトでインストールした場合は
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\バージョン\include
がincludeのパスになります。

:::note warn
別途フォルダーにインストールした場合は適宜読み替えてください。
:::

パスのバージョンの所ですが、人によっては複数のバージョンのフォルダーが生成されていることがあります。
この場合は一旦適当なプロジェクトを作成し、.cppファイルを作成、

```cpp
#include <iostream>
```
と記述し、iostreamの個所にカーソルを置きF12キーで一旦iostreamを表示させます。
完全パスのコピーまたはこのアイテムのフォルダーを開く
にてiostreamの存在する場所を特定することで、バージョンが何であるかがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855481/56cb61fa-8b61-087b-1e64-1f25576c3870.png)

includeフォルダーにbitsフォルダーを作成します。

:::note warn
作成には管理者権限が必要となります。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855481/2fef6199-50ac-af29-7669-8d075744429d.png)

その中にstdc++.hを作成します。

:::note warn
こちらも作成には管理者権限が必要となります。
通常のテキストエディタで作成する場合は、
管理者権限で実行するか、一度デスクトップなどに作成した後にbitsフォルダーに移動してください。
※通常起動では保存する権限がないため弾かれてしまいます。
:::

stdc++.hの中身は[こちら](https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/precompiled/stdc%2B%2B.h)の内容を丸々コピーし、
#ifndef、#endifの個所を削除してください。



これで

```cpp
#include <bits/stdc++.h>
```
と書けば使えるようになります。

なお、これでエラーが表示されてしまう場合は、
bits/stdc++.h内、最初の方(#includeが書かれる前)に

```c++
#define _SILENCE_CXX17_C_HEADER_DEPRECATION_WARNING
```

を記載してください。
AWS Certified DevOps Engineer - Professional  (DOP-C01)に合格

# 最初に

AWS SAP-C01 に続いて、AWS DOP-C01 に合格したので、その時に勉強法だったり、意識した内容をまた記事にしてみました。

AWS SAP-C01 の過去記事は↓から。

https://qiita.com/leomaro7/items/d7f51d8f79df60bd244c


# 感想

個人的には、AWS SAP の時のよりも、比較的、問題がときやすかったと思います。

長文であることは、AWS SAP と変わりませんので、時間配分は1問２分のペースで解いていくことは同じです。

AWS DOP は、AWS SAP と比べて、比較的に解きやすかった理由は以下にあると思います。

- 個人で実際に触ってみれるサービスが多かった
- 実際に業務経験があるサービスが多かった

例えば、AWS SAP の場合、AWS Direct Connect は実際に個人で触ってみることはできないですし、AWS Organizations もアカウントを複数作らなければいけませんし、個人でアカウントを複数所持している人はそうそういないでしょう。

それに比べて、AWS DOP は、Code兄弟、CloudFormation など実際に自分で触ってみることもできますし、業務経験をあればなおプラスです。

業務経験によっては、AWS SAP の方が解きやすいという方もいらっしゃると思います。

纏めると大切なのは以下の通りです。

- 試験範囲を確認する。
- 実際に触ってみる。
- 業務経験


# 出題されるサービスが多かったもの

- Code兄弟
- CloudFormation
- Elastic Beanstalk
- AutoScalling
- DR対策
- Config
- CloudTrail
- SSM
- デプロイメント方式
- コンテナサービス(ECR,ECS,EKS)
- CloudWatch
- EventBridge
- SNS
- Trusted Advisor 

全てのサービスを知る必要はありますが、実際にあまり使われていなさそうなサービス、Qiitaなどでもあまり積極的に投稿されていないサービスは正直いって省いてもいいかもしれません。

例えば、OpsWorks においては、1問も出題されませんでした。
どのようなサービスで、どのようなことができるかぐらいは知っておく必要はありますが、詳細まで学習する必要はないと思います。


# 利用した教材

AWS SAP の時の経験を踏まえて、今回は無駄な教材は一つもなかったと思います。

## 公式サンプル問題 

AWS公式のサンプル問題集、10問です。

https://d1.awsstatic.com/ja_JP/training-and-certification/docs-devops-pro/AWS-Certified-DevOps-Engineer-Professional_Sample-Questions.pdf

## 公式練習問題集

AWS公式のサンプル問題集、20問です。

https://explore.skillbuilder.aws/learn/course/external/view/elearning/12514/aws-certified-devops-engineer-professional-practice-question-set-dop-c01-japanese?devops=sec&sec=prep

## デジタルトレーニング

AWS公式のデジタルトレーニングで、7時間ほどので、各項目には、確認テストも含まれています。

https://explore.skillbuilder.aws/learn/course/external/view/elearning/74/exam-readiness-aws-certified-devops-engineer-professional?devops=sec&sec=prep

## Udemy 

Udemy の問題集2つ、85問と150問です。
英語ですが、Google翻訳で十分にいけます。
セールの時に購入すれば、各1,600円程度で購入できます。

https://www.udemy.com/course/aws-certified-devops-engineer-professional-practice-exam-dop/

https://www.udemy.com/course/aws-certified-devops-engineer-professional-practice-exams-amazon/


## AWS Black Belt

定番ではありますが、ポイントがあります。

例えば、

- AWS CloudFormation
- AWS CloudFormation deep dive	

という Title のものがあります。
AWS CloudFormation だけではなく、AWS CloudFormation deep dive のものまで高度なものまで見るようにした方が良いかと思います。

https://aws.amazon.com/jp/aws-jp-introduction/aws-jp-webinar-service-cut/
WEB API アウトプット(前回の続き)

## はじめに

＊前回の続き
この記事は普段バックエンドエンジニアの方に作ってもらっている「WEB APIってそもそもなんだろう」という疑問から執筆しています。

「WEB API The Good Parts(水野　貴明　　著)」を参考に自分なりにまとめアウトプットしていきます。
ぜひフロントエンドエンジニアの方で同じ疑問を持っている方は読み進めてみてください。

## 対象者
この記事は下記のような人を対象にしています。

- WEB APIについて何も知らない方
- フロントエンドエンジニアでWEB APIの触りの部分しか知らない方

それでは早速アウトプットに入っていきます。

## レスポンスデータの設計

レスポンスデータ：リクエストの結果返されるデータのこと

まずデータフォーマットから見ていくらしいです。

- JSON
- XMLなど

ただ最近の傾向としてJSONのみに対応しているものも多くなってきており、必要に応じてXMLに対応するのがベストらしいです。

## データの内部構造の考え方

例えばSNSのようなアプリで友達一覧を取得するAPIを作るとき、以下のようなデータだとどうでしょう？

```json:ダメな例
{
  "friends": [
    1,
    2,
    3, ...
  ]
}
```

これだとIDのみしか取得できず、性別や名前、アイコンプロフィールなどを取得したいとき再度APIを叩かないといけなくなります。

```json:いい例
{
  "friends": [
    {
      "id": 1,
      "name": "Taro Tanaka",
      "profileIcon": "http://image~.png",
    },
    {
      "id": 2,
      "name": "Hanako Yamada",
      "profileIcon": "http://image~.png",
    },
  ]
}
```

上記のAPIだとアクセスも1回ですみます。

## 性別のデータをどう表すか
大きく分けて以下の2パターン
- "male"や"female"といった文字列として表現する方法
- 1なら男性、2なら女性といった数値で表現する方法

## エラー表記
|ステータスコード|　　　意味　　　|
|-------------|------------|
|100番台|情報|
|200番台|成功|
|300番台|リダイレクト|
|400番台|クライアントサイドに起因するエラー|
|500番台|サーバサイドに起因するエラー|

200番台のリクエストはクライアントからのリクエストが成功した場合しか返してはなら無いそうです。
# Web会議の品質をリアルタイムで確認したい！

# Web会議の品質をリアルタイムで確認したい！
Cisco Webex や Microsoft Teams で Web 会議の調子が良くない・・・。
そんなときに原因を確認できるように、ThousandEyesの新しい機能 **Automated Session Tests** 機能を試してみました。

# Automated Session Tests とは？
Endpoint Agent に設定されている端末から Cisco Webex, Zoom, Microsoft Teams で Web 会議を実施した場合、端末～宛先サーバ間の通信を識別・監視する機能です。
監視する端末とルールさえ事前に設定しておけば、Web 会議開始後自動で監視を開始することができます。

# Autometion Session Tests 設定
ThousandEyes の Dashbord 画面サイドメニューの ``Endopoint Agents`` > ``Test Settings`` を選択します。
``Test Settings`` の画面から、``Autometion Session Tests`` タブを選択し、``Add New Test``を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1953283/4f9546fd-e3a4-055a-f0c9-7a75fde319c3.png)

テストの登録画面が表示されるので、以下を設定します。
1. Test Name
テスト一覧に表示される名前
1. Target
監視する Application（Microxoft Teams, Cisco Webex, zoom）を選択
1. Agents
テストを実行する端末を選択（初期設定時はすべての Endpoint Agent が選択）

設定が完了したら、最下部の ``Save Test`` を押下すると設定が保存されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1953283/42997ee5-cbee-914d-1393-57a7b7570e77.png)

これでテストの登録が完了しました。
テストに登録した Web 会議（①）を登録した端末（③）で実施すると、自動で監視が始まります。

# トラフィック確認
設定したテストが実際に動いているかどうかを確認します。
Dashbord 画面サイドメニューの ``Endopoint Agents`` > ``Views`` を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1953283/1c5441a8-5958-69d6-8620-2b4dee3aa0da.png)


初期状態では Metric は ``Loss`` が表示されており、Web 会議中のパケットロス平均値が表示されます。
グラフ上で任意の時間を選択すると、選択した時間帯のネットワークの状態を参照することができます。
今回は、``Average Loss`` が高く表示されている箇所を選択します。

結果を確認すると、東京都町田市にある ``my.meraki.net`` という GW で 50% のパケットロスが検出されていることがわかります。
他の経路・宛先サーバに問題はないため、通信が問題なく通っているということもわかります。
このように問題がある箇所は赤く表示されるため、一目で問題個所を発見することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1953283/6acfc950-7f65-f405-0384-abc19e1c479e.png)

宛先ノードの情報も確認できるので、どのサーバへアクセスしているのかリアルタイムに確認できるのは面白いですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1953283/326046d5-4b6d-27db-a427-1ace14b6e67c.png)

# さいごに
ThousandEyes 新機能の Autometion Session Tests を試してみました。
Web 会議で調子が悪いなーと思うタイミングで、また中身を見てみようと思います。
MS-Teams のテスト設定もしているのですが、私の端末では現状うまく動いていないので、また様子を見てみようと思います。


# 参考サイト
https://docs.thousandeyes.com/product-documentation/end-user-monitoring/automated-session-test
Golangで認証ページを突破する

# きっかけ
　Golangを今年の2月始めて触ってみて、書きやすい言語だなと思って何かGolangを使って遊んで見ようと思いました。そこで、簡単にブラウザをGolangによって操作させて、自動でログインできるような機能を作ってみようと思いました。

# 何をしたのか
　今回は、私が普段大学で利用しているLMS（学習管理システム）を使いたいと思います。
　LMSはもちろんのことログインをするためにはユーザー名とパスワードを入力しなければなりません。これを Golangを使うことでログイン認証を自動化させてみようと思います。

# 下準備
　これらを実現させるために主に2つのフレームワークを使いました。
  　一つ目が[agouti](https://qiita.com/k-penguin-sato/items/deaeab18aa416496e273)です。簡単に言うとブラウザ操作を自動化させるためのフレームワークです。
　まずは下準備としてこれを`go get`によってインストールしましょう。
```
go mod init asobi  //go.modファイル生成
go get https://pkg.go.dev/github.com/sclevine/agouti 
```
これで下準備は完了です。

# 実践

インポートするのは以下の通りです。
```Golang
package main

import (
   "log"
   "time"
   "https://pkg.go.dev/github.com/sclevine/agouti"
)
```

ここからmainを書いていきます。
```Golang
func main() {
	driver := agouti.ChromeDriver(agouti.Browser("chrome"))
	if err := driver.Start(); err != nil {
		log.Fatalf("Failed to start driver:%v", err)
	}
	defer driver.Stop()

	page, err := driver.NewPage()
	if err != nil {
		log.Fatalf("Failed to open page:%v", err)
	}

	if err := page.Navigate("https://elms.u-aizu.ac.jp/login/index.php"); err != nil {
		log.Fatalf("Failed to navigate:%v", err)
	}

	// ID, Passの要素を取得し、値を設定
	identity := page.FindByID("username") 
	password := page.FindByID("password")
	identity.Fill("own id") //"own id"には自分のユーザー名を記述
	password.Fill("own password") //"own passwordには自分のパスワードを記述"
	// formをサブミットして、ログイン
	btn := page.FindByButton("Log in")
	if err = btn.Click(); err != nil {
		log.Fatalf("Failed to navigate:%v", err)
	}
	// 処理完了後、10秒間ブラウザを表示しておく
	time.Sleep(10 * time.Second)
}
```
# 解説
ではここから解説していきます。(コメントの部分、一部は省略します。ごめんなさい)

まず、ブラウザ操作を自動化するための宣言をしています。私は普段ブラウザはchromeを使っているので、ここではchromeを指定しています。
```Golang
driver := agouti.ChromeDriver(agouti.Browser("chrome"))
```


以下では、Navigate関数で指定したURLに遷移させています。
```Golang
page.Navigate("https://elms.u-aizu.ac.jp/login/index.php");
```

以下では、FindByIDというメソッドで、ユーザー名の入力欄を指定しています。
(手動の場合、自分のユーザー名を入力するときユーザー名欄にカーソルを合わせます。これと同じ感覚です。)

```Golang
identity := page.FindByID("username") 
password := page.FindByID("password")
```

また、FindByIDに指定する"username"はGoogleDeveoperToolで確認できます。

>1  GoogleDeveloperToolを開いたら、ユーザー名と書いてある入力欄にカーソルを合わせます。
![findbyid3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608429/c71643a5-f7c9-5d9d-c1ed-ab6d47b3c66f.png)

>2. カーソルはそのままにして、GoogleDeveloperToolのElementを見るとid="username"と書いてあります
![findbyid2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608429/c3f041ea-c7e6-d007-29b5-8dd625229740.png)

パスワードも同様です。

以下でも、GoogleDevoloperToolを開きボタンにカーソルを合わせて、Elementを見ると、
`<bottom type="submit ...>Log in</bottom>`と書いてあります。
この`Log in`を指定します。

```Golang
btn := page.FindByButton("Log in")
```

以下のように書くことで、クリックを自動でしてくれます。
```Golang
btn.Click()
```
そしてLMSのマイページに移動することができます

# まとめ
自分の書いたコードが自動で動いたということに素直に感動しました。
今回はLMSで実装してみましたが、これ以外のサイトでもできるのかなと思います。
間違っているところがあれば教えてください。
LINE Messaging APIのreplyTokenは何秒で無効になるのかを検証【2022年6月時点】

# はじめに

LINE Messaging API で応答メッセージを送るために必要な replyToken には有効期限があります。
以前は、以下の記事に記載の通りリファレンスには

> 応答できるイベントには応答トークンが発行されます。 応答トークンは一定の期間が経過すると無効になるため、メッセージを受信したらすぐに応答を返す必要があります。

と書かれており、実験的に30秒有効と判断されていました。
（自分も1年ほど前に必要があって同様の調査を行い、その時点では30秒であることを確認していました。）

https://blog.morugu.com/entry/check-line-reply-token-expire


ところが最近、改めてリファレンスを確認したところいつの間にか

> 応答トークンは、Webhookを受信してから1分以内に使用する必要があります。1分を超える場合の使用については、動作は保証されません。

のような表現に変わっていました。

https://developers.line.biz/ja/reference/messaging-api/#send-reply-message-reply-token


本当に30秒以上有効となっているのか、「1分を超える場合の使用については、動作は保証されません。」とあるが実際はどの程度使えるのかが気になったので、本記事で検証します。



# 検証方法

お手軽に検証したいので今回はGASを利用してWebhookイベントを受信し、指定秒数スリープしたうえで応答メッセージを送信します。

```javascript
//LINE Developersコンソールで取得したアクセストークンを入れる
const CHANNEL_ACCESS_TOKEN = 'YOUR_CHANNEL_ACCESS_TOKEN';

// Sleepする秒数
const SLEEP_SEC = 60

function doPost(e) {
  // Webhookイベントオブジェクトを取得
  const webhookEvent = JSON.parse(e.postData.contents);

  // 検証のためにSleepを入れる
  Utilities.sleep(SLEEP_SEC * 1000);

  // イベントを処理する
  for (const event of webhookEvent.events) {
    // メッセージだけ処理する
    const type = event.type;
    if (type !== 'message') {
      continue;
    }

    // 応答トークンとメッセージテキストを取得
    const replyToken = event.replyToken;

    // メッセージを返信    
    UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', {
      'headers': {
        'Content-Type': 'application/json; charset=UTF-8',
        'Authorization': `Bearer ${CHANNEL_ACCESS_TOKEN}`,
      },
      'method': 'post',
      'payload': JSON.stringify({
        'replyToken': replyToken,
        'messages': [{
          'type': 'text',
          'text': `${SLEEP_SEC}秒待機`,
        }],
      }),
    });
  }

  return 'ok';
}
```


# 結論

__5分以上使えたが、GASのタイムアウトに引っかかって本当の上限は分からなかった。。。__

以下、検証内容です。

まず1分以上使えるかですが、これはあっさりクリアしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/2e71ed99-1656-3efb-965d-a9e91d3ece5f.png)

次に上限を調べるために待機時間を増やしてみました。
600秒で応答がなかったのでおっと思ったのですが、これはGASのタイムアウト6分に引っかかっているだけでした。。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/3a7ce921-d2e3-edb6-3e9a-b65e40d76f2a.png)

本当に上限を調べたかったら、GASではなくちゃんとサーバーを立てて検証した方が良さそうです。


# まとめ

応答トークンの有効期限は本当に30秒以上に伸びていました。
今回の検証では本当の失効期限は分かりませんでしたが、基本的にはリファレンスに記載の通り1分以内に利用するのが良さそうです。
rails ＋ postgresql ＋ dockerでのコンテナ作成

初投稿のため、問題点、改善点などありましたら
是非ご連絡いただけると幸いです！

# 各ファイルコード
```Dockerfile:Dockerfile
FROM ruby:3.0.1

# yarnパッケージ管理ツールをインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && apt-get install -y yarn

RUN apt-get install -y nodejs npm && npm install n -g && n 14.19.1
RUN mkdir /app_name
WORKDIR /app_name
COPY Gemfile /app_name/Gemfile
COPY Gemfile.lock /app_name/Gemfile.lock
RUN bundle install
COPY . /app_name

RUN yarn install --check-files
RUN bundle exec rails webpacker:compile

# コンテナ起動時に実行させるスクリプトを追加
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Rails サーバ起動
CMD ["rails", "server", "-b", "0.0.0.0"]
```

```docker-compose.yml:docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=postgres
    ports:
      - "5432"
    volumes:
      - ./tmp/db:/var/lib/postgresql/data

  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app_name
    ports:
      - "3000:3000"
    depends_on:
      - db

```

```datebase.yml:datebase.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: postgres
  password: password
  host: db

development:
  <<: *default
  database: app_name_development

test:
  <<: *default
  database: app_name_test


production:
  <<: *default
  database: app_name_production
  username: app_name
  password: <%= ENV['APP_NAME_DATABASE_PASSWORD'] %>
```
# terminalでの操作

## コンテナイメージbuild
```
$ docker-compose build
```

## PostgresqlにDBを作成
```
$ docker-compose run app rails db:create
```

## コンテナ立ち上げ
```
$ docker-compose up
```

# エラー一覧
## 'docker build' error: "failed to solve with frontend dockerfile.v0"
```
sudo docker compose up -d --build
```

# 参考記事
[Dockerコンテナの作成、起動〜停止まで](https://qiita.com/kooohei/items/0e788a2ce8c30f9dba53)
[DockerでRails・PostgreSQL構築](https://qiita.com/jungissei/items/c58bd1158cd909540b26)
provideとyieldについて（railsチュートリアル第3章）

# provideとyieldについて（railsチュートリアル第3章）

## yieldの一つの使い方
##### htmlへviewsの各内容を反映させる
application.html.erb は、アプリケーション全体で使用されるデフォルトのレイアウト。

```application.html.erb
<!DOCTYPE html>
<html>
<head>
  中略
</head>
<body>

<%= yield %>

</body>
</html>
```
<%= yield %と記述された箇所に各ページの内容として別途作成されたhtmlファイルの内容を持ってくるわけである。
```home.html.erb
<h1>サンプルアプリ</h1>
<p>これはサンプルアプリです</p>
```

この場合<%= yield %の中にhome.html.erbの記述内容が反映される。

## 他ファイルの一部分を参照する場合
```application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>アプリ名 | <%= yield(:title) %></title>
  中略
</head>
　　　　後略
```
```home.html.erb
<% provide(:title, 'ホーム') %>
<h1>サンプルアプリ</h1>
<p>これはサンプルアプリです</p>
```
provideのtitleが呼び出されたらtitleとラベルの書かれた引き出しを開け”home”という値を
yieldのtitle：に渡し結果

#### アプリ名　｜　home
と表示させることが出来る。

# 参考文献
https://qiita.com/sanstktkrsyhsk/items/e7d27c6717b430302967

https://www.oiax.jp/rails/rails3/controller_and_view2.html#:~:text=application.html.erb%20%E3%81%AF%E3%80%81,%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%81%BE%E3%81%99%E3%80%82
【PHPUnit】例外のテストは$this->expectException()を先に書く

## 環境
Laravel v9.5.1 (PHP v8.1.3)

## 前提条件
```UserRole```が```1~3```までEnumで定義されてるところに、存在しない```4```を入れてエラーが起きることをテストしたい。

## テスト方法
**例外のテストは、テストしたいメソッドより先に書く。**

理由はテストしたいメソッドを先に書くと、そこで例外が発生しエラーで落ちるから。
少し考えれば当たり前なんだけど、普段テストを書きまくっているとつい最後に書くものと思い込んでしまっていた。
```expectException```「エラーを期待する」という命名からも先に書くことは予想できる。

```php
public function testError()
{
  $this->expectException(ValueError::class);

  UserRole::from(4);
}
```

## RailsのRSpecでは

```expect```のブロックの中にテストしたいメソッドを書くことで例外の発生をテストできる。

```ruby
subject { instance.method }
expect { subject }.to raise_error(MyError)
```

## 参考
https://www.engilaboo.com/laravel-php-exception-test/
BigQuery で配列の平均を計算する


# 目的

以下のように固定長の Array (`target_array`) を持つテーブルに対して、`id` 単位で同じ offset にある要素同士の平均を取りたい。

| Row | id | target_array |
|-----|----|--------------|
| 1   | 1  | [1, 2, 3]    |
| 2   | 1  | [2, 3, 4]    |
| 3   | 1  | [3, 4, 5]    |
| 4   | 2  | [4, 5, 6]    |
| 5   | 2  | [5, 6, 7]    |

```sql
SELECT 1 AS id, [1, 2, 3] AS target_array
UNION ALL
SELECT 1 AS id, [2, 3, 4] AS target_array
UNION ALL
SELECT 1 AS id, [3, 4, 5] AS target_array
UNION ALL
SELECT 2 AS id, [4, 5, 6] AS target_array
UNION ALL
SELECT 2 AS id, [5, 6, 7] AS target_array
```

## 望まれる結果

| Row | id | averaged_array  |
|-----|----|-----------------|
| 1   | 1  | [2.0, 3.0, 4.0] |
| 2   | 2  | [4.5, 5.5, 6.5] |

# 方法

以下のように、一度 array を offset つきで展開し、id と offset を元に GROUP BY し平均を計算する。そして、再度 array を生成する。

この時、ARRAY_AGG は順序を考慮しないため、offset を元に ORDER BY する必要がある。

```sql
WITH unnested_array AS (
    SELECT
        id,
        value,
        offset
    FROM `PROJECT_ID.DATASET.TABLE_NAME`, 
        UNNEST(target_array) AS value WITH OFFSET AS offset
)
, averaged_unnested_array AS (
    SELECT
        id,
        AVG(value) AS averaged_value,
        offset
    FROM unnested_array
    GROUP BY id, offset
)
SELECT
    id,
    ARRAY_AGG(averaged_value ORDER BY offset ASC) AS averaged_array
FROM averaged_unnested_array
GROUP BY id
```

# 結果

```sql
WITH input_table AS (
  SELECT 1 AS id, [1, 2, 3] AS target_array
  UNION ALL
  SELECT 1 AS id, [2, 3, 4] AS target_array
  UNION ALL
  SELECT 1 AS id, [3, 4, 5] AS target_array
  UNION ALL
  SELECT 2 AS id, [4, 5, 6] AS target_array
  UNION ALL
  SELECT 2 AS id, [5, 6, 7] AS target_array
)
, unnested_array AS (
    SELECT
        id,
        value,
        offset
    FROM input_table, 
        UNNEST(target_array) AS value WITH OFFSET AS offset
)
, averaged_unnested_array AS (
    SELECT
        id,
        AVG(value) AS averaged_value,
        offset
    FROM unnested_array
    GROUP BY id, offset
)
SELECT
    id,
    ARRAY_AGG(averaged_value ORDER BY offset ASC) AS averaged_array
FROM averaged_unnested_array
GROUP BY id
```

| Row | id | averaged_array  |
|-----|----|-----------------|
| 1   | 1  | [2.0, 3.0, 4.0] |
| 2   | 2  | [4.5, 5.5, 6.5] |



# 終わりに

BigQuery で潜在表現の average pooling を行いたい時などに使えるように思う。上記クエリの `AVG()` を `MAX()` に変更すれば max pooling を行うことができる。

# 参考

https://stackoverflow.com/questions/72161127/ordering-within-array-agg-after-group-by-in-bigquery
【Ruby】文字列を部分的に抽出・削除するメソッドまとめ

# String#[]

文字列の範囲を指定して部分文字列を取り出す。

```ruby
str = 'sample_string'
str[0] #=> "s"
str[1, 3] #=> "amp"
str[2..5] #=> "mple"
```

**String#slice**も同様。

```ruby
str = 'sample_string'
str.slice(0) #=> "s"
str.slice(1, 3) #=> "amp"
str.slice(2..5) #=> "mple"
```

正規表現による指定もできる。

```ruby
str = 'sample_string'
str[/a.+l/] #=> "ampl"
str.slice(/a.+l/) #=> "ampl"
```

# String#match

指定した正規表現にマッチした部分を取得する。
**正規表現マッチを扱うクラス(MatchData)のインスタンスが返却される。**
第二引数によりマッチの開始位置を制御できる。

```ruby
str = 'sample_string'
str.match(/s(.+)e/) #=> #<MatchData "sample" 1:"ampl">
str.match(/s(.+)e/)[0] #=> "sample"
str.match(/s(.+)e/)[1] #=> "ampl"
str.match(/s./) #=> #<MatchData "sa">
str.match(/s./, 1) #=> #<MatchData "st">
```

# String#sub

第一引数のパターンにマッチした最初の部分を第二引数の文字列で置き換える。

```ruby
str = 'sample_string'
str.sub('s', 'ex') #=> "example_string"
str.sub(/s./, 'exa') #=> "example_string"
```

# String#gsub

第一引数のパターンにマッチした部分全てを第二引数の文字列で置き換える。

```ruby
str = 'sample_string'
str.gsub('s', 'ex') #=> "example_extring"
str.gsub(/s./, 'exa') #=> "example_exaring"
```

# String#delete

引数に渡した文字を削除する。
**1文字ずつ判定がかかることに注意が必要。**

```ruby
str = 'sample_string'
str.delete('abcdefg') #=> "smpl_strin"
str.delete('a-g') #=> "smpl_strin"
str.delete('_string') #=> "ample"
```

※ 特定の文字列を削除したい場合は**String#sub**や**String#gsub**を使って空文字に置換すれば良い。

```ruby
str = 'sample_string'
str.sub('_string', '') #=> "sample"
str.gsub('_string', '') #=> "sample"
```
Apple キャンペーンリンクの作り方


### これは
本業でキャンペーンリンクについて調べたので、備忘録を兼ねてまとめます。

キャンペーンリンクとはアプリまでのURLに任意のパラメータを付与したもので、
そのパラメータごとのダウンロード数を見ることができます。

基本的にこちらを参考にしてますが、少しわかりづらいところもあるので、下に詳しく書きます。

[キャンペーンリンクとは](https://developer.apple.com/jp/app-store/measuring-app-performance/)
[キャンペーンリンクの作り方](https://help.apple.com/app-store-connect/?lang=ja#/itcfa7936330)


### 実際の作り方

1.App Store Connectにログインして、"Appアナリティクス"を選択します。

![スクリーンショット 2022-06-30 17.09.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393442/cd5a460a-6f72-47b0-45d3-e62ad2c1545a.png)

2.リンクを作成したいアプリを選択

3."獲得" -> "キャンペーン" -> "＋ボタン"を選択

![スクリーンショット 2022-06-30 17.17.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393442/b0af70ca-b3dd-2f60-4d73-7c8c9376b49b.png)

4.実際にリンクを作成

ここで作成したキャンペーンリンクをコピーしておくことをお忘れなく。
(見た感じ一回しか表示されないかも、、？)

![スクリーンショット 2022-06-30 17.19.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393442/cf96f0d0-ffa1-34c9-e029-d224d4ec76e9.png)

これで終わりです。

### 終わり

意外と簡単に終わりましたね。素敵な分析ライフを！
SOFA framework備忘録　vtkの使い方

Softrobot pluginを含めSOFAではvtkファイルを使う場面が少なくないのでSTL等の汎用的な3Dモデルファイルからの変換方法を記す

# 必要なもの
- FreeCAD https://www.freecadweb.org/?lang=ja
- Gmsh   https://gmsh.info/
- STLファイル

# 手順
FreeCAD
1) STLファイルをインポートする
2) ワークスペースを[Part]に切り替える
3) メニューバーの[パート]から[メッシュから形状を作成]
4) 新しく生成された形状を選択し、[パート]から[ソリッドに変換]
5) 4で生成された形状を選択し、.stepファイルにエクスポートする

Gesh
6) .stepをインポートする
7) 左のModules-Meshから3Dを選択しボリュームメッシュを生成する
8) VTKファイルでエクスポート

※STLのメッシュ数が多すぎるとシミュレーションが重くなる可能性あるので適宜メッシュの削減を

参考文献
https://www.sofa-framework.org/community/forum/topic/easiest-way-from-stl-to-vtk/
FragmentとActivity間のデータ受け渡し方法(Kotlin)

<h2>当記事でわかること</h2>
・FragmentとActivity間の値受け渡し(Kotlin)<br>
・擬似的なFragment間値受け渡し(Kotlin)

<h2>背景</h2>
Fragment関係のデータ受け渡しの理解とコーディングにハマりかけたため、
<br>
メモ感覚で今回の記事を書くこととしました。
<br>
初投稿なので、お手柔らかにお願いいたします。
<h2>大前提</h2>
Fragmentとは、Activity上にあるライフサイクルを持った<font color="Salmon">View</font>です。
<br>＊こちらの記事ではFragmentへの理解を前提としております。
<br>
また、OSバージョンやエディタ環境のテストは、

筆者環境以外では行っていないため留意ください。
<h2>コード</h2>
<h4>〜FragmentからActivityへデータ渡し〜</h4>
<h6>・Fragment側</h6>

```kotlin:MainFragment.kt
        val intent = Intent(activity, MainActivity::class.java)
        val test1 = "test1"
        val test2 = "test2"
        intent.putExtra("test1_key",test1)
        intent.putExtra("test2_key",test2)
```
<h6>・Activity側</h6>

```kotlin:MainActivity.kt
        val test1 = intent.getStringExtra("test1_key")
        val test2 = intent.getStringExtra("test2_key")
```
上記の方法はFragmentが乗っているActivityとは別のActivityへの
値受け渡しが可能です。
<h4>〜ActivityからFragmentへデータ渡し〜</h4>

<h6>・Activity側</h6>

```kotlin:MainActivity.kt
        val args = Bundle()
        args.putString("test1_key",test1)
        args.putString("test2_key",test2)

        val fragment = MainFragment()

        fragment.setArguments(args)
```
<h6>・Fragment側</h6>

```kotlin:MainFragment.kt
        val test1 = requireArguments().getString("test1_key")
        val test2 = requireArguments().getString("test2_key")
```
上記の方法はActivity上のFragmentへActivityから値渡しが可能です。
<h2>全体を通して</h2>
キー設定を行うことで値の受け渡しが簡単に行うことができます。<br>
フラグメント同士の値受け渡しも擬似的に実装することもできますので、<br>
この記事が値受け渡しの際のバイブルになると嬉しいです。

<h2>参考にした記事</h2>

[argumentsを使いactivityからfragmentにオブジェクトを渡す【Android】【kotlin】](https://qiita.com/yuta3737/items/67eace6c5dff4ea2ac7e)

[[Android & Kotlin ] アプリの画面遷移とActivity間のデータ転送](https://akira-watson.com/android/kotlin/activity-transfer-data.html)
ServiceNow PDIにHRを有効化する方法

## 背景・目的
ServiceNowの個人用開発環境（以下、PDI）でいろいろ実験していたところ、HR機能が使用できなかった。

## 解決策
なんてことはない、ただプラグインを入れればいいだけだったんですね。

当初、HR admin にユーザを切り替えられなかったが、、、
![スクリーンショット 2022-06-30 162604.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237493/784368fe-328d-05db-8e6d-8a86f816c411.png)

Syetem Definition > Plugins から、"human"で検索し、画像のものをInstallする。
![スクリーンショット 2022-06-30 163529.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237493/24121af1-ee7b-ffc5-6f00-78d345f74a0d.png)


そうすると、確認画面が出てきて、Demo dataにもチェックを入れ、Installを実行する。
![スクリーンショット 2022-06-30 163557.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237493/856b46b6-c7f3-6944-0904-f1901caec82a.png)

※これが重たいのか40分くらいかかりました。

余談だが、なぜか3つのアプリケーションがすでにインストールされてるといわれた（入れた覚えはない）
![スクリーンショット 2022-06-30 165017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237493/492537a2-e0af-c03b-a4a0-3f8af8e54800.png)

***ここでポイントになるのが、一度ログアウトしてから、再度確認を行うということです。***

そして、HR Adminを確認すると、、、
![スクリーンショット 2022-06-30 165154.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237493/67f66237-bf0e-4e6e-47e3-bbd4a75b2b20.png)


はい、ちゃんと入ってましたね。まずは、こんな初歩的なところから、備忘を兼ねてやっていきたいと思います。
【Vue.js】tableタグの中でcomponentが読み込まれない


# コンポーネントが正しく読み込まれない！

```html
<table>
  <v-hoge-thead></v-hoge-thead> 
  <tbody>
    <tr>
      <td>hogege</td>
    </tr>
  </tbody>
  ... ... 
</table>
```

このようなtableを作ってthead部分だけコンポーネント化していました。
そうするとコンポーネントが読み込まれているのですが、完全にレイアウトが崩れてしまいました。

調べたところ以下のドキュメントに記載がありました。

https://vuejs.org/guide/essentials/component-basics.html#dom-template-parsing-caveats:~:text=Element%20Placement%20Restrictions

```
Some HTML elements, such as <ul>, <ol>, <table> and <select> have restrictions on what elements can appear inside them, and some elements such as <li>, <tr>, and <option> can only appear inside certain other elements.
```
（引用タグがうまくできなかった）
つまり特定のタグ、例えばul、ol、tableなどは中に決まったタグしか受け付けないようになっているようです。

なのでそういう時は読み込み方をisに変えて

```html
<table>
  <thead is="vue:v-hoge-thead"></thead>
  <tbody>
    <tr>
      <td>hogege</td>
    </tr>
  </tbody>
  ... ... 
</table>
```

とするとうまく読み込まれました
linux(ubuntu)でもwindowsに近いことをしたい話

今回は…
---
ちょっと余談的な内容です。

ubuntuでwindowsのソフトを代用したい話
---
まずはこれを見てください
| windowsソフト | ubuntu代用 | 備考 |
|:-:|:-:|:-:|
| studio clip <br> FireAlpaca  | Gimp <br> pinta |   |
| burnaware free  | Brasero  | DVDを焼く場合は「DeVeDe」  |
| .exeを動かす  | Wine  | インストールがややこしいときがある  |
| mp3tag  | easytag  |   |
| CD音源を好みの拡張子で出す  | Asunder  |   |
| FFFTP  | Ubuntuのファイルブラウザーから可能  |   |

こんな感じで代用可能です。

このソフトは入れとけっていう話
---
このソフトは、あって損はないです
| ソフト名(コマンド) | どのように役に立つか |
|:-:|:-:|
| ``sudo apt-get install ubuntu-restricted-extras``  | 基本の動画コーデックパッケージを入れることができます<br>linuxでネット動画が見たい場合は必須  |
| ffmpeg  | 動画をlinuxで触るときは、これは必須でしょう  |
| VScode  | とりあえず入れておくNo1  |
| VLC  | DVD動画いろいろ動画ファイルを開きたい時はおすすめ  |
| Wine  | windowsアプリをとりあえず動かしたいときは必須<br>インストールが少しややこしい  |
| google chrome  | chrome信者は入れとけってだけ |

このソフトは、とりあえず入れとけっていうおすすめです

これだけでも最低限覚えとけっていうコマンドの話
---
アプリをインストール
``sudo apt install [ファイルパス or ソフトウェアの名前]``
ただし、これだけでは入れられないというややこしソフトが一部あり
``sudo apt upgrade``
ソフトや本体などをアップデートするコマンドlinuxer(?)には必須のコマンド

windowsと同じような漢字のコマンド
``ls`` (``dir``)
ディレクトリを表示するコマンド。「dir」でも動くが、「ls」のほうが色がつく
``cd``
カレントディレクトリ


まとめ
---
即興で書いたようなものですが、役に立てば幸いです

ご視聴ありがとうございました！