OmniAICreator/Qiita-1.07M huggingface dataset:
Amazon TranscribeとAgora.ioでリアルタイム文字起こしを試してみた

ビデオ通話や、ライブ配信で文字起こしをリアルタイムに行う要件が増えてきています。
この記事ではAmazon Transcribeを用いて、リアルタイムに文字起こしをする方法について解説します。

## Amazon Transcribe
AWSでは２パターンの文字起こしを提供しています。S3に保存されたファイルからjobで生成するパターンと、リアルタイムにストリームを流し込んで生成するパターンがあります。
今回の記事では後者を利用します。

## Amazon Transcribe ストリーミング Servic
公式のドキュメントは[こちら](https://docs.aws.amazon.com/ja_jp/transcribe/latest/dg/API_streaming_StartStreamTranscription.html)にあります。

>双方向 HTTP/2 を起動するか WebSocket ストリーミング。オーディオが Amazon Transcribe にストリーミングされると、テキスト文字起こしの結果がアプリケーションにストリーミングされます。

(公式ドキュメントから引用)

## WebSocket で Amazon Transcribe ストリーミングを使用する
WebSocketを用いた実装の解説が[こちら](https://aws.amazon.com/jp/blogs/news/transcribe-speech-to-text-in-real-time-using-amazon-transcribe-with-websocket/)に掲載されています。
この解説の中に実装例の[リンク](https://github.com/amazon-archives/amazon-transcribe-websocket-static)が貼られていました。

## Agora.ioと連携してみる
さきほどのサンプルコードでは、ブラウザのAPIを利用して音声をキャプチャしています。

```javascript:main.js
    window.navigator.mediaDevices.getUserMedia({
            video: false,
            audio: true
        })
        // ...then we convert the mic stream to binary event stream messages when the promise resolves 
        .then(streamAudioToWebSocket) 
        .catch(function (error) {
            showError('There was an error streaming your audio to Amazon Transcribe. Please try again.');
            toggleStartStop();
        });
```

この部分をAgoraのAPIに置き換えてビデオ通話＋文字起こしを実現します。
```javascript:main.js
const AgoraRTC = require('agora-rtc-sdk-ng');
const agoraEngine = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
//中略
    await agoraEngine.join(YOUR APP ID, YOUR CHANNEL ID, null, null);
    let localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
    await agoraEngine.publish(localAudioTrack);
    let mediaStream = new MediaStream();
    mediaStream.addTrack(localAudioTrack.getMediaStreamTrack());
    streamAudioToWebSocket(mediaStream);
    // window.navigator.mediaDevices.getUserMedia({
    //         video: false,
    //         audio: true
    //     })
    //     // ...then we convert the mic stream to binary event stream messages when the promise resolves 
    //     .then(streamAudioToWebSocket) 
    //     .catch(function (error) {
    //         showError('There was an error streaming your audio to Amazon Transcribe. Please try again.');
    //         toggleStartStop();
    //     });

```
音声キャプチャをAgoraのAPIに差し替えただけの簡単な修正になります。
AgoraのAPIで取得した音声ストリームについてはgetMediaStreamTrack()でアクセス可能です。

この修正だけでビデオ通話中のリアルタイム文字起こしが完了です。
ライブ配信の場合ですと、取得した文字をリアルタイムメッセージ等で視聴者にブロードキャストする方法等が考えられます。

## 最後に
[**agora.ioに関するお問い合わせはこちらから**](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=1718307c581f0e2c2cd5)
[<img width="912" alt="Agoraの詳細はこちら" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/5865441c-9d31-c702-3698-3aeb84927f28.png">](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=1718307c581f0e2c2cd5)
母ちゃん、俺ボクサーやめてエンジニアって職業で一人前になろうと思うんだ

---
23年卒エンジニアとして働く予定の大学4年生(男)です。
実は大学3年生頃までボクシングをしていました。
紆余曲折ほどではないですがボクサーをやめてエンジニアの道を選びました。

ボクサーからエンジニアになった経緯とエンジニア就活においてやったことを書いてみました。

ご興味ありましたら是非一読していただけると嬉しいです。
<img width="200px" alt="life" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1462552/70f77779-52a8-ab9d-6730-7a03f3c52642.png">







## 始まりは新型コロナウイルス
始まりはコロナの第1波でした。
通っていたジムは緊急事態宣言で2ヶ月ほど営業を停止し、公園で夜な夜な練習するということしかできませんでした。　
大学もオンラインになり、時間を持て余し、死んだ目をして本屋にふらっと行き、プログラミングに関する本を一冊買いました。　

一旦この本を読み終えたタイミングでジムが再開し、私にとって転機となる出会いがありました。　

ジムの更衣室で着替えていると、私の鞄から一冊のプログラミングに関する本が出ちゃって...
それを拾ってくれた方が実はWEBエンジニアをやってる方でした。(お互い全裸でした笑)

当時エンジニアってアプリ作る人でしょという知識しかなかった私に、エンジニアという職業について教えてくれました。

そこで思ったんです。　

###### 自分のホームページとか作ったり、もっと良い機能のついたwebアプリ作れば試合のチケットいっぱい売れるんじゃね！！！

と。

今思うと安易な考えすぎて笑えますが、当時の私は勢い任せに少ないバイト代を全て使ってパソコンを買い、勉強するためによりフィジカルに負荷をかけた生活を始めました。　
(絶対良くなかった。ずっとイライラしてた。友達できなかった。ぁぁぁぁぁぁ)

当時のスケジュール
↓
<img width="300px" alt="life" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1462552/f91bb3f5-242b-1e9d-0097-431702fe8014.png">


## 挫折
試合に向けてスパーリングを数こなしていた時期のある日
###### いつもより目に濃い黒い点々が浮かぶなぁ
と思い、病院へ行きました。　

医者から言われた言葉がこちら
### 「これ以上ボクシングをすることを許可できないね」

### 「.......................................................」
<br />
血がサァーと引いていく感じでした。
<br />
<br />
<br />
当時共感していて好きだったフレーズが心を抉ってきました。

##### 「東京という街の地面はね　夢破れた奴らの屍で出来てる」 (MOROHA / 「三問銭」)
<br />







今思うと、お前じゃ二兎は追えないよということだったのかもしれません。

しかし、落ち込んでいても何も始まらないということで、　エンジニアとして一人前になるぞと決意を固めました。
<br />
<br />
<br />

### 母ちゃん、そういうわけで俺は一人前のエンジニアになるぜ！！
<br /><br />
<br /><br />
## 決意してからまずこんなことをした
①作りたいものを決めた（俗にいうポートフォリオ）
②そのために何が必要かを調べた
③やるしかない
④勉強途中で思い浮かんだものを作る(寄り道)
⑤完成させる
⑥就活じゃ

## 具体的にこんな勉強をした
### HTML & CSS
- 1冊ですべて身につくHTML & CSSとWebデザイン入門講座
www.amazon.co.jp/dp/4797398892
これを一冊模写した。
### JavaScript & React
- progate
- モダンJavaScriptの基礎から始める挫折しないためのReact入門
https://www.udemy.com/course/modern_javascipt_react_beginner/
-モダンJavaScriptの基本から始める　React実践の教科書　（最新ReactHooks対応）
www.amazon.co.jp/dp/481561072X


HTMLから入ったせいか、異常に難しく感じた。なんだこれなんだこれのオンパレード(今でも)
完全に理解した → 全然わからない → 完全に理解した → 全然わからないの繰り返し

できないなりに動くアプリを１０個くらい作った。

### Python & Django
- Djangoのツボとコツがゼッタイにわかる本[第2版]
www.amazon.co.jp/dp/4798066052

簡単なwebアプリのサーバーサイドを実装するために使用した。

### その他
-ニュースペックテキスト 応用情報技術者 2022年度 (情報処理技術者試験)
www.amazon.co.jp/dp/4813299954

午前中のところだけ熟読した。

## 最後に
ここまで読んでくださりありがとうございます。
私は天才ではなく、おそらく脳筋タイプですが、早く一人前になることができるよう日々精進していきます。
【初心者必見】ハッカソンの魅力

## はじめに
これは大学からプログラミングを始めた文系学生が語るハッカソンの魅力である



## ハッカソンとは
ハック（Hack）と　マラソン（Marathon）を掛け合わせた造語です。

エンジニアやデザイナーなどの人たちが数日間から数時間の間マラソンのように開発期間を
走り続け、アプリケーションなどを開発し、成果を競う事からハッカソンと呼ばれています。


## 魅力

さあここからはハッカソンがいかに初心者に魅力的か語っていきましょう、、、。


## 0から開発する経験が得られる
これが1番の魅力だと思います。
プログラミングを始めたばかりの頃、基礎の勉強をひたすらする事が何より大切だと思っていました。サイトや参考書を使っての勉強をする。それももちろん大切だと思います。
僕が初めてハッカソンに参加したのはプログラミングを学び始めておよそ３ヶ月後の事でした。（大学生になったばかりだし文系だから授業でプログラミングなんてしていないので完全初心者です）
最初に参加したのは僕が所属する学生IT団体のハッカソンでした。
全く関わったことのない先輩と3人でチームを組み、どんな物をつくるか0から考えました。
使用する言語も自分たちで決め、デザインも自分たちで決める。文字通り「0から」の経験でした。

ここからは僕の2パターンの経験を分析していきます。

## 1.先輩がいるハッカソン
さあまずは先輩がいるハッカソンです。
簡潔に良い点から挙げていきましょう。

　　　　良い点
　　　　・自由に発言できる
　　　　・リードしてもらえる
　　　　・自分のレベルにあった作業をさせてもらえる
　　　　・時間の使い方を考えなくていい

自分より経験の多い先輩方は、ハッカソンの期間の中でどこまでが実現可能なのかがある程度わかっています。それが分からない分、初心者である自分たちはアイデア出しの段階で先輩よりたくさんのアイデアを出すことが出来ます。そこから実現できる方に寄せていってもらえます。
そして事前開発、当日、役割が明確に分けられ、当たり前ですが自分がするタスクが割振られます。
ですが心配ありません。先輩方は分かっています。後輩たちがどこまで出来るのかを。そして少しレベルの高い作業を割振ってくれます。ですので割振られたタスクをこなせば成長出来ちゃいます。
時間の使い方はとっても大事です。ですが経験のない僕たちには後どのぐらいタスクが残っているのかなんて分からないんです。だから割振られたタスクを黙々とこなすだけで良いのです。


## 2.初心者だけでのハッカソン
次にほぼ初心者だけでのハッカソンです。
良い点から挙げていきましょう。

　　　　良い点
　　　　・全ての段階を自分主体で経験できる
　　　　

これにつきます。
自分たちで作りたい物を決め、自分たちで使用するプログラミング言語を決め、自分たちで役割を決め、自分たちでタスクの進捗から残りの時間配分を決める。この経験が凄く大事です。全て主体となって、全てに責任を持ち取り組むことが成長するために最も大事だと思います。

## おわりに
ここまで読んで頂き有り難うございます。ハッカソンの魅力について伝わっていますでしょうか。
ハッカソンのことについてもっと知りたい！！と思ってもらえていれば幸いです。
[github/gitlab]リポジトリをクローン時に既存のリモートブランチをローカルに反映する方法

# 困ったこと
新しいPJに参画する際に既に着手しているリポジトリを `clone` して作業を開始することはどの場面でもよくあることだと思います。

そんな時、メンバーから
「このブランチで進めてねぇ～」と言われた場合ただ `clone` しただけだとブランチは恐らくひとつしかないと思います。

# 具体的な解決方法

以下のコマンドでリモートのブランチを確認。
```terminal
git branch -r
```

以下のような結果になると思う。
```terminal
origin/HEAD -> origin/master
origin/develop
origin/feature/hoge
```

その後、先ほど確認したリモートのブランチ名をコピーし、(ミスがないように)
ローカルにリモートブランチを反映する作業をする。

```terminal
//git branch <ローカルブランチの名前> <リモートブランチの名前>
git branch local-branch-name remote-branch-name
git branch feature/hoge origin/feature/hoge
```
【Swift】初心者のランダム通話アプリ開発奮闘記 #11

# 今日の積み上げ
買った本のXcodeのボタン等々の名称を説明してくれている項を読んでいる最中、、
書くことない、、

本業の方は、いい感じの忙しさ
リモートワーク中ずっと電話繋いでくれる女の子をGravityで発見！！
![2022:12:14.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/997222/aa43c592-8850-3ab5-1b31-a34433dfcb11.jpeg)
開発に転生したら未経験は俺だけだった件 1話「goodbye halcyon days.」

# はじめに
この記事は私が未経験中途で営業から開発に転職し、その経緯や一年経過した中で得られた知見を共有するためのものです。
かなり稀有なパターンからの転職だったので他の開発職を目指す方々へのアドバイスになるようなものではないことを先に断っておきます。

# 簡単なプロフィール
```
名前: saitoooooooo(記事中では私)
年齢: 26→27
学歴: 国立大学工学部中退
業務: 営業系3年くらい → PjM, 開発 (二年目)
```

# 無職の危機～カジュアル面談
2020年頃、私は派遣型の営業会社でブイブイ言わせていました。社内成績も常に上位におりあまり不自由なく生活をしていた記憶です。
1年も経つとCOVID-19の影響で徐々に営業活動が縮小していき、2021年の6月になったタイミングで自宅待機となりほぼ完全に仕事がなくなりました。
私自身正社員でなかったこともあり、派遣先が無くなる可能性もあったためかなり危機感を覚えていました。

そこで、大学時代の先輩、Y氏になんとなく「仕事無くなるかもだけどなんかないかなー」とボヤいてみたところ

「開発職興味ある？」
「あー・・・面白そうね」
「じゃあカジュアル面談セットしとくわ」
「お、おう」

みたいな適当なやり取りで気付いたら面談が決まりました。
この段階で私はコーディング知識はほぼゼロで、未経験で開発職とかムリだろ、と考えていたのでかなり話半分に考えていたと思います。

この会話の次の週くらいに当時の課長とカジュアル面談がセットされました。
新規プロダクトの立ち上げフェーズということもあり、マネタイズや市場規模、今後の展望について話をしたのですが、
かなり白熱してしまい1時間予定のところを1時間40分ほど話をしたことを覚えています。

「・・・思ったより本気じゃん」

Y氏がかなり推してくれていた事もありますが、想定より本気で採りに来ていたので驚きました。
その後書類選考に通り、二次面接に進む前に入社課題を解いて欲しいとのことでした。

# 入社課題
その日の内に入社課題が届きました。(カプセル化、ポリモーフィズム、継承を理解したうえでOOPで連結決算の計算をするコードを書けって感じだった記憶)
当時はOOP、カプセル化、ポリモーフィズム、継承などの単語が何一つわからない状態からのスタートだったためかなり焦りました。

そこから1週間程度、「調べもの」→「書く」→「調べもの」→「書きなおす」のループを1日8時間くらい繰り返しました。
Y氏から事前に『リーダブルコード』は読むように言われたので、後半は書籍や他社のコードから"正解"を求めてリファクタをし、自信はないながらも何とか提出をしました。（この時仕事がクソ暇じゃなかったら間違いなくここで落ちてた）

提出した課題は意外と好感触？で無事二次面接に進むことが出来ました。
（後から聞いた話だと、初学者が1週間でOOPをある程度理解した上でコードを提出してきたポテンシャルを汲んだそう）

二次面接は緊張しすぎてあまり覚えてないですが、私の提出したコードについての問答や、1日の勉強時間、
あとは「プログラミングは簡単か？」と聞かれたので「いいえ。クソムズかしいです。」と答えたのは覚えています。

翌日に内定の連絡が届きました。
そして同時に以下のメッセージがY氏から届きました。

「入社までにAtcorder100問をPythonでやっといて」

...?(一切意味を理解していない顔)

結論を言うとこの後速攻で会社を辞めて、内定承諾し、1か月ちょいで100問を終わらせました。

この「100本ノック」の話は私の苦労話で終わる為詳しく書きませんが
未経験でエンジニア目指すのならかなりおすすめです。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2748021/858074b8-7909-a2e4-f4e7-a34f0c2ccf29.png)

学習は数だよ、兄貴

# ~補足~
1話は業界未経験のペーペーがなし崩し的に開発職になった話ですが、実は内定を出した **株式会社フィエルテ(現:DIVA)** の開発部ではこんなことになっていました。

- COVID-19により生活環境が変化した上での複数メンバーの離職
- 外販フェーズに入った際に営業が出来る人材がいない
- 今まで研修を務めていたメンバーが離職し、今いるメンバーで研修コンテンツを組み直さないといけない

> COVID-19により生活環境が変化した上での離職


これについては当時様々な現場で起こっていたと思います。
うちの部は当時10人→7人(だった記憶)に減っていたため早急に人が欲しかったそうです。

> 外販フェーズに入った際に営業が出来る人材がいない

単純にスキルマッチングでした。
ちなみに色々あっておそらく私は今後営業することはないと思います。

> 今まで研修を務めていたメンバーが離職し、今いるメンバーで研修コンテンツを組み直さないといけない

- 既にY氏と関係値がある
- 未経験が実務に耐えうるレベルになるためのステップが全て網羅できる


等の理由で研修コンテンツ作りに色々と都合が良かったらしいです。

開発部では後にも先にも未経験を採用したことはないので、
友人から伸ばされた手をがっちりつかんだから、そして ***運がよかったから*** 、転職できた側面が大きいと思っています。

2021年はパチスロの調子が悪かったのでその反動で転職できたってことですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2748021/1f4cb716-1ed1-38b8-7489-226271849acb.png)
***...。***


# 次回予告 
2話「出会い」
食う者と食われる者。 そのおこぼれを狙う者。 牙をもたぬ者は生きては行かれぬ暴力の街。 あらゆる悪徳が武装するウドの街。 ここは百年戦争が生み落とした惑星メルキアのソドムの市。 キリコにしみついた硝煙の匂いに惹かれて危険な奴等が集まってくる。

次回「出会い」
キリコが飲むウドのコーヒーは苦い。

https://qiita.com/saitoooooooo/items/6327ccd15e49906d4aef
VBAで差分比較ツールを作ってみた


Excelのシートそれぞれのデータで、差分比較をしたい時がある。
一般的にはDiffツールを使うのだが、ルールが厳しくDiff自体が使えない場合もあるらしい。

そこで、差分比較ツールを作ってみた。

# 差分比較ツール

まず、今回は「同じブック内の別シートで差分比較をする」やり方で書いたが、
これをアレンジすれば別ブック内の別シートで差分比較をすることも可能だろう。

## プログラムの流れ
そして流れとしては

①ブックを取得
②差分比較をしたい元シートを取得
③差分比較をしたい別シートを取得
④差分があったらそれを出力するシートを作成
⑤　②と③のシート内データを１行ずつ比較していく
⑥差分があれば④で作成したシートに出力していく

という流れで行う。

注意点としては、**比較元データを基準に比較先データと比較し、比較元にしかないデータを出す**ということ。
つまり、比較先／比較元関係なく、AとBシート両方で異なるデータを出力するのはまた別のやり方になる。

その上で、コードを書いていく。

## 実際のコード

```vb
Option Explicit
Public Sub MainProc()
    Dim shtMain As Worksheet
    Dim motoName As String
    Dim sakiName As String
    Dim shtMoto As Worksheet
    Dim shtSaki As Worksheet
    Dim shtSabun As Worksheet
    Dim lastRowMoto As Long
    Dim lastRowSaki As Long
    Dim lastCol As Long
    Dim i As Long
    Dim j As Long
    Dim k As Long
    Dim blnSame As Boolean
    Dim blnExist As Boolean
    Dim nowRow As Long
    
    Set shtMain = ThisWorkbook.Sheets("メイン")
    
    '比較元シート（最終行・最終列取得）
    motoName = shtMain.Range("A3")
    Set shtMoto = ThisWorkbook.Sheets(motoName)
    lastRowMoto = shtMoto.Cells(shtMoto.Rows.Count, 1).End(xlUp).Row
    lastCol = shtMoto.Cells(1, shtMoto.Columns.Count).End(xlToLeft).Column
    
    '比較先シート（最終行取得）
    sakiName = shtMain.Range("B3")
    Set shtSaki = ThisWorkbook.Sheets(sakiName)
    lastRowSaki = shtSaki.Cells(shtSaki.Rows.Count, 1).End(xlUp).Row
    
    '差分シート
    Set shtSabun = ThisWorkbook.Sheets("差分")
    
    '差分シートクリア
    shtSabun.Cells.Clear
    
    '比較元シートのヘッダー行を差分シートにコピー
    shtMoto.Range(shtMoto.Cells(1, 1), shtMoto.Cells(1, lastCol)).Copy (shtSabun.Cells(1, 1))
    
    nowRow = 1
    
    '比較元シートと比較先シートを比較し、差分行を差分シートにコピー
    For i = 2 To lastRowMoto
        blnExist = False
        For j = 2 To lastRowSaki
            blnSame = True
            
            For k = 1 To lastCol
                If shtMoto.Cells(i, k) <> shtSaki.Cells(j, k) Then
                    blnSame = False
                    Exit For
                End If
            Next
            
            If blnSame = True Then
                blnExist = True
                Exit For
            End If
        Next
        
        If blnExist = False Then
            nowRow = nowRow + 1
            shtMoto.Range(shtMoto.Cells(i, 1), shtMoto.Cells(i, lastCol)).Copy (shtSabun.Cells(nowRow, 1))
        End If
    Next
    
    MsgBox "完了"
    
End Sub
```

本来は変数でもっと良い名前があるとは思うが、比較元と比較先がそれぞれ分かるようにとあえて「Moto」「Saki」とつけておいた。

### 問題点

今は、比較元シートのデータを基準に比較しているため、仮に比較先シートにしかないデータがあったとしても
それは出てこない。
（比較元シートにしかないデータは出てくる）

それはそれで正しいのだが、比較元比較先関係なく、AとBで異なるデータはどちらも出せるようにというのは今後また考えていきたいと思う。
React Hooksって何

# はじめに
Next.jsでアプリを開発する中で、ReactHooksを使用した。
使用する中で、ReactHooksのルールに違反してエラーが発生したり、更新のタイミングがわからず、画面に表示されたデータを更新することができなかった。
問題を解決するために調べたことをまとめる。

## 基本

ReactHooksとは、ざっくりいうと値を管理するためのもの

https://ics.media/entry/201106/

https://yutaro-blog.net/2021/09/15/react-hooks/

## カスタムフック

> 自分独自のフックを作成することで、コンポーネントからロジックを抽出して再利用可能な関数を作ることが可能です。
>公式ドキュメントより引用
https://qiita.com/cheez921/items/af5878b0c6db376dbaf0


## React hooksのエラー

よく違反してエラーをだしたルールは、
フックはメソッド内の一番外側でしか、呼び出すことができないルール

https://qiita.com/tatsumin0206/items/4e1076e2deedf20a9485

## Global State　アプリケーション全体での状態管理
https://weseek.co.jp/tech/2565/#3_Recoil

### React Context

https://dev-k.hatenablog.com/entry/nextjs-state-reactcontext-dev-k

### Recoil

https://maasaablog.com/development/frontend/react/3226/

https://qiita.com/zaburo/items/225d0731faeaa6966ea9#%E5%80%A4%E3%81%AE%E6%B0%B8%E7%B6%9A%E5%8C%96persist

## router ページ遷移

https://qiita.com/cheez921/items/6856c2c72bb2b7cd9aab

## Next.jsでのデータ取得機能 SWR

SWRとはデータ取得のための React Hooks ライブラリ
データの取得状態を表す、キャッシュをもちいてデータの表示を早くする、
オプションを設定することで実行タイミングを制御できる。

https://swr.vercel.app/ja/docs/api

https://qiita.com/irico/items/d8f0ed7887f3c490ffa1
TypeScriptでビルトインクラスの拡張メソッドを作成する

# はじめに

共通的に使える関数ではなく、ある特定の型に拡張してメソッドを生やしたい時ってありますよね。
JavaScriptのやり方はググったらちょくちょく出てきたんですけど、TypeScriptのナレッジがあまり見受けられなかったので備忘録を兼ねて。

## バージョン

```
Node 18.12.1
npm 8.19.2
"typescript": "^4.9.3"
```

# 実装

```StringExtension.ts
/**
 * このexportがないとglobalスコープを拡張できない
 * See https://www.web-dev-qa-db-ja.com/ja/typescript/%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97%E3%81%AE%E6%8B%A1%E5%BC%B5%E3%81%AF%E3%80%81%E5%A4%96%E9%83%A8%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%BE%E3%81%9F%E3%81%AF%E3%82%A2%E3%83%B3%E3%83%93%E3%82%A8%E3%83%B3%E3%83%88%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%AE%A3%E8%A8%80%E3%81%A7%E3%81%AE%E3%81%BF%E7%9B%B4%E6%8E%A5%E3%83%8D%E3%82%B9%E3%83%88%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%EF%BC%882669%EF%BC%89/810779808/
 */
export {}

// Stringクラスにはこんなメソッドが定義されていますよ、とコンパイラ? インタプリタ？　に教えてあげないといけない
declare global {
  interface String {
    /**
     * JavaやC#のformatメソッドのように、特定の文字列のプレースホルダを引数で渡された文字で置き換える
     *
     * 参考
     * - https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format/4673436#4673436
     * - https://trueman-developer.blogspot.com/2015/11/typescriptjavascript.html
     */
    format(...args: unknown[]): string
  }
}

String.prototype.format = function (...args: unknown[]) {
  let str = this.toString()
  for (const [i, arg] of args.entries()) {
    const regExp = new RegExp(`\\{${i}\\}`, 'g')
    str = str.replace(regExp, arg as string)
  }
  return str
}
```

## 使い方

```index.ts
// 必ずimportしてあげる
import './StringExtension'

'Hello {0}'.format('World!')
```

# 参考

https://www.web-dev-qa-db-ja.com/ja/typescript/%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97%E3%81%AE%E6%8B%A1%E5%BC%B5%E3%81%AF%E3%80%81%E5%A4%96%E9%83%A8%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%BE%E3%81%9F%E3%81%AF%E3%82%A2%E3%83%B3%E3%83%93%E3%82%A8%E3%83%B3%E3%83%88%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%AE%A3%E8%A8%80%E3%81%A7%E3%81%AE%E3%81%BF%E7%9B%B4%E6%8E%A5%E3%83%8D%E3%82%B9%E3%83%88%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%EF%BC%882669%EF%BC%89/810779808/
TailwindCSSのレスポンシブのブレークポイント詳細が確認できるツール

## 環境

+ Chrome
+ TailwindCSS `3`



## Demo

**Vue.js**を例として実装しました。  
スクリーンサイズの調整によって、ブレークポイントの詳細情報が確認できるようになります。[CodePen](https://codepen.io/kensoz/pen/QWrXmNv)を参照することがおすすめします。

🌐 [CodePen](https://codepen.io/kensoz/pen/QWrXmNv)

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="QWrXmNv" data-user="kensoz" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kensoz/pen/QWrXmNv">
  Vue-Tailwind-breakpoint</a> by kensoz (<a href="https://codepen.io/kensoz">@kensoz</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>



## コード

Vue.js

```vue
<template>
	<div id="app">
		<main class="flex flex-col m-5">
			<div class="flex flex-row mb-3">
				<div>{{ width }} x {{ height }}</div>
				<button class="ml-5 font-bold" @click="lai()">Get Codepen Screen Size</button>
			</div>

			<div class="flex sm:hidden md:hidden lg:hidden xl:hidden 2xl:hidden">
				<div class="mr-5">
					base/xs =
					<span class="font-bold">~639px</span>
				</div>
				<ul class="list-disc list-inside">
					<li>iphone se ↑</li>
					<li>iphone 7 ↑</li>
					<li>iphone 12 pro ↑</li>
				</ul>
			</div>

			<div class="hidden sm:flex md:hidden lg:hidden xl:hidden 2xl:hidden">
				<div class="mr-5">
					sm(min-width:640px) =
					<span class="font-bold">640px~767px</span>
				</div>
				<ul class="list-disc list-inside">
					<li>iphone se ←</li>
					<li>iphone 7 ←</li>
				</ul>
			</div>

			<div class="hidden sm:hidden md:flex lg:hidden xl:hidden 2xl:hidden">
				<div class="mr-5">
					md(min-width:768px) =
					<span class="font-bold">768px~1023px</span>
				</div>
				<ul class="list-disc list-inside">
					<li>ipad mini ↑</li>
					<li>ipad ↑</li>
				</ul>
			</div>

			<div class="hidden sm:hidden md:hidden lg:flex xl:hidden 2xl:hidden">
				<div class="mr-5">
					lg(min-width:1024px) =
					<span class="font-bold">1024px~1279px</span>
				</div>
				<ul class="list-disc list-inside">
					<li>ipad mini ←</li>
					<li>ipad ←</li>
					<li>ipad pro ↑</li>
				</ul>
			</div>

			<div class="hidden sm:hidden md:hidden lg:hidden xl:flex 2xl:hidden">
				<div class="mr-5">
					xl(min-width:1280px) =
					<span class="font-bold">1280px~1535px</span>
				</div>
				<ul class="list-disc list-inside">
					<li>ipad pro ←</li>
					<li>laptop 1366x768 ↑</li>
				</ul>
			</div>

			<div class="hidden sm:hidden md:hidden lg:hidden xl:hidden 2xl:flex">
				<div class="mr-5">
					2xl(min-width:1536px) =
					<span class="font-bold">1536px~</span>
				</div>
				<ul class="list-disc list-inside">
					<li>laptop　↑</li>
					<li>desktop　↑</li>
				</ul>
			</div>
		</main>
	</div>
</template>

<script setup>
import { ref } from "vue";
let width = ref(window.innerWidth);
let height = ref(window.innerHeight);
const lai = () => {
	width.value = window.innerWidth;
	height.value = window.innerHeight;
};
</script>
```
twitterに自動でTweepyを使用せず、Twitter API "v2"でツイートする方法

## ■やりたいこと
・自動でツイートする
・Tweepyモジュールを使用しない
・v2で行いたい
・APIキーは別ファイルで持っておきたい

## ■完成コード（とりあえず動かしたい人はここまで。メモ書きは後半。）
同一階層にinit.json, tweet.pyを格納
init.json には各種キーを格納。

```json:init.json
{
    "api_key":"キーを入れる",
    "api_key_secret":"キーを入れる",
    "access_token":"トークンを入れる",
    "access_token_secret":"トークンを入れる",
}
```

```python:tweet.py
from requests_oauthlib import OAuth1Session
import json

# Get request token
json_open = open('init.json', 'r')
json_load = json.load(json_open)

# Set request token
# [Hints] consumer_key = api_key, consumer_secret = api_key_secret
AK = json_load["api_key"]
AKS = json_load["api_key_secret"]
AT = json_load["access_token"]
ATS = json_load["access_token_secret"]

# Be sure to add replace the text of the with the text you wish to Tweet.
payload = {"text": "Hello!"}

# Make the request
oauth = OAuth1Session(
    AK, AKS, AT, ATS,
)

# Making the request
response = oauth.post(
    "https://api.twitter.com/2/tweets",
    json=payload,
)

if response.status_code != 201:
    raise Exception(
        "Request returned an error: {} {}".format(response.status_code, response.text)
    )

print("Response code: {}".format(response.status_code))

# Saving the response as JSON
json_response = response.json()
print(json.dumps(json_response, indent=4, sort_keys=True))
```


## ■めもとか
（そもそも）公式のコードだと長くてわざわざアクセスしてPINを取ってこなければならず、
必要最小限でツイートしたかった。
↓参照
https://github.com/twitterdev/Twitter-API-v2-sample-code/blob/main/Manage-Tweets/create_tweet.py

このinputで止まるのダルかった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107879/09370e2b-fa1f-0e96-2077-16839d81f029.png)


---
init.jsonに格納してあるキー情報を取得しそれぞれ格納する。
```python:tweet.py
# Get request token
json_open = open('init.json', 'r')
json_load = json.load(json_open)

# Set request token
# [Hints] consumer_key = api_key, consumer_secret = api_key_secret
AK = json_load["api_key"]
AKS = json_load["api_key_secret"]
AT = json_load["access_token"]
ATS = json_load["access_token_secret"]
```

---
ツイートしたい内容"Hello!"を入れる
```python:tweet.py
# Be sure to add replace the text of the with the text you wish to Tweet.
payload = {"text": "Hello!"}
```

---
キーをがっちゃんこして、エンドポイント（api.twitter.com/2/tweets）へ
```python:tweet.py
# Make the request
oauth = OAuth1Session(
    AK, AKS, AT, ATS,
)

# Making the request
response = oauth.post(
    "https://api.twitter.com/2/tweets",
    json=payload,
)
```

---
正常ならレスポンスコードは201が返ってくるはず。それ以外ならコードとエラー文を表示。

```python:tweet.py
if response.status_code != 201:
    raise Exception(
        "Request returned an error: {} {}".format(response.status_code, response.text)
    )
```


---
コード表示して、レスポンス表示(エラー時も出るんよね。ここいらんか？)
```python:tweet.py
print("Response code: {}".format(response.status_code))

# Saving the response as JSON
json_response = response.json()
print(json.dumps(json_response, indent=4, sort_keys=True))
```
CSSのfilter効果をさわってみる

[ひとりCSS Advent Calendar 2022](https://qiita.com/advent-calendar/2022/css-becolomochi) 15日目です。

filter 効果をさわってみます。

## filter プロパティ

https://developer.mozilla.org/ja/docs/Web/CSS/filter

filter プロパティは対象にグラフィック効果を適用できる。

## 値

- `blur()` - ぼかし
    - pxで指定。
- `drop-shadow()` - ドロップシャドウがつく
    - (x位置, y位置, ぼかす量, 色) で指定
- `opacity()` - 透過
    - opacityと似ているが、フィルターだと一部ブラウザで性能向上されてるらしい。
- `hue-rotate()` - 色相の回転
    - 0deg 変化なし
- `brightness()` - 明度を変化。
    - 0%だと黒。100%だと変更されない。100%以上だと明るくなる。
- `contrast()` - コントラストを変化。
    - 0%だとグレー。100%だと変更されない。100%以上だと高コントラストになる。
- `saturate()` - 彩度を変化。
    - 0%だと無彩色。100%だと変化なし。100%以上で彩度が増える。
- `grayscale()` - グレースケール化する
    - 0%だと変化なし。100%がMax。
- `invert()` - 色の反転
    - 0%だと変化なし。100%がMax。
- `sepia()` - セピア調にする。
    - 0%だと変化なし。100%がMax。

## 試してみた

```html
<p>default</p>
<p class="blur">blur</p>
<p class="drop-shadow">drop-shadow</p>
<p class="opacity">opacity</p>
<p class="hue-rotate">hue-rotate</p>
<p class="brightness">brightness</p>
<p class="contrast">contrast</p>
<p class="saturate">saturate</p>
<p class="grayscale">grayscale</p>
<p class="invert">invert</p>
<p class="sepia">sepia</p>
```

```css
p {
  font-size: 2em;
  color: #e91e63;
}

.blur{filter: blur(2px);}
.drop-shadow{filter: drop-shadow(2px 2px 2px rgba(0,0,0,0.5));}
.opacity{filter: opacity(25%);}
.hue-rotate{filter: hue-rotate(180deg);}
.brightness{filter: brightness(30%);}
.contrast{filter: contrast(30%);}
.saturate{filter: saturate(30%);}
.grayscale{filter: grayscale(100%);}
.invert{filter: invert(100%);}
.sepia{filter: sepia(100%);}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41088/74e2d829-0e2f-02f8-71a9-c79e42d9511f.png)

## 遊んでみた

セピア色のリンクを hover すると紫になる

```html
<a href="#">Link</a>
```

```css
a {
  filter: sepia(100%);
  color: #9c27b0;
  transition: 0.2s;
  text-decoration: none;
}

a:hover {
  filter: sepia(0);
  text-decoration: underline 1px;
  text-underline-offset: 0.2em;
}
```

![gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41088/2296d0de-0340-3e16-9a4f-59761d55b8ed.gif)

## サンプル

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="QWxXVwL" data-user="becolomochi" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/becolomochi/pen/QWxXVwL">
  scroll-padding-top</a> by Beco (<a href="https://codepen.io/becolomochi">@becolomochi</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## 感想

- invert がわりと使い道ありそう
    - 地の色が変わる箇所に乗る黒色のテキストに適用するとか

## 参考

- https://developer.mozilla.org/ja/docs/Web/CSS/filter
JavaScript素数判定

## Code

🌐 [live demo](https://codepen.io/kensoz/pen/zYjQJOY)

```js
const isPrimeNum = (n) => {
	for (let i = 2; i < n; i++) {
		if (n % i === 0) return false;
	}
	return true;
};

console.log(isPrimeNum(2));
// true
```
Kubernetes の構造化ログのフォーマットについて

## Kubernetes の構造化ロギングについて

Kubernetes ではロギングライブラリとして klog が使用されています。
https://kubernetes.io/docs/concepts/cluster-administration/system-logs/

そして、Kubernetes 1.23 から構造化ロギングが Beta に昇格して、各コンポーネントのログメッセージの大半が構造化されてロギングされるようになっています。詳細は [Kubernetes 1.23: Metrics Changes と SIG Instrumentation の変更内容](https://qiita.com/watawuwu/items/0a0496f415b818edcb4d) をご参照ください。

ログを構造化することで機械が処理しやすくなるので、他システムと連携して活用しやすくなります。なお、現在も移行中で全てのログが構造化されているわけではないので注意が必要です。

## 構造化ログのフォーマット

構造化ログの形式は以下のようになっています。

```
<klog header> "<message>" <key1>="<value1>" <key2>="<value2>" ...
```

以下は構造化ログの例となります。

```
I1025 00:15:15.525108       1 controller_utils.go:116] "Pod status updated" pod="kube-system/kubedns" status="ready"
```

公式ドキュメントでは詳細が割愛されていますが、**klog header** にはログレベルや時刻などのメタデータが含まれています。仕様はドキュメントされておらず、klog の[ソースコード](https://github.com/kubernetes/klog/blob/v2.80.1/klog.go#L626-L645)に直接記載されています。

```
Log lines have this form:

	Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...

where the fields are defined as follows:

	L                A single character, representing the log level (eg 'I' for INFO)
	mm               The month (zero padded; ie May is '05')
	dd               The day (zero padded)
	hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
	threadid         The space-padded thread ID as returned by GetTID()
	file             The file name
	line             The line number
	msg              The user-supplied message
```

ログレベルは先頭のアルファベットで表現される仕様です。`I`nfo、`W`arning、`E`rror、`F`atal のように各ログレベルの頭文字が使用されて ([ソースコード](https://github.com/kubernetes/klog/blob/v2.80.1/internal/severity/severity.go#L38-L39))、その後に Month と Day の4桁数字が続くような形式になっています。
小ネタ：Flutterで時刻を加算する方法

時刻を加算する方法を調べたので、小ネタとして記事にしておきます。
結論ですが時刻を加算する方法は二つあります。

# addメソッド
一つ目は`DateTime`の`add`です。
コンパクトにかけるのがメリットですが、日にちの加算までしかできず、年月の加算はできません。
もししたい場合は「普通に足す」の項をご参照ください。

下記のサンプルコードのように、`baseTime.add(Duration(),);`で足したい時間を加算します。
**時間の繰り上げも勝手にやってくれます**。（３０分４５秒に、２０秒を足すと３１分５秒となります）
```dart
void _incrementCounter() {
  final baseTime = DateTime(2022, 3, 10, 9, 30, 45);

  // 2022-03-10 09:30:45
  printTime(baseTime);

  final addTwentySec = baseTime.add(
    const Duration(
      seconds: 20,
    ),
  );

  // 2022-03-10 09:31:05
  printTime(addTwentySec);
}

void printTime(DateTime dateTime) {
  var dtFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
  print(dtFormat.format(dateTime));
}
```

# 普通に足す
二つ目の方法は愚直にたす方法です。
この方法なら年と月も足せます。
また`add`と同じように、**時間の繰り上げも勝手にやってくれます**。（２０２２年３月に１０ヶ月足すと２０２３年１月にしてくれます）
```dart
void _incrementCounter() {
  final baseTime = DateTime(2022, 3, 10, 9, 30, 45);

  // 2022-03-10 09:30:45
  printTime(baseTime);

  final addTenMonth = DateTime(
    baseTime.year,
    baseTime.month + 10,
    baseTime.day,
    baseTime.hour,
    baseTime.minute,
    baseTime.second,
  );

  // 2023-01-10 09:30:45
  printTime(addTwentySec2);
}

void printTime(DateTime dateTime) {
  var dtFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
  print(dtFormat.format(dateTime));
}
```


以上、小ネタでした！！
form_forで複数のcheckboxを保存する

## overview
- checkboxを複数作ってDBに保存する処理
- checkboxのデータはDBから取得ではなく、配列で直接指定する
- 配列で処理するのでちょいと特殊な処理が必要
   - 今回はDeviseを使ったUserModelで検証

## 配列のデータ
- 今回はjson形式で読み込んでます
   - new,edit両方で使うデータなので何かしら共通のファイルで管理したい
   - でもこれくらいのデータはModelを作るほどじゃない
   - っていうことでjsonで読み込みました

```ruby
<!-- 半角スペースを含むスペースを入力すると表示エラーになります -->

<% @q_from_data = [
'Instagram',
'Twitter',
'ネット検索',
'サークルまとめサイト',
'友達の紹介',
'その他',
]
%>
```

## FormでのCheckbox複数表示
###### app/views/devise/registrations/new.html.erb
```erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<div class="field py-3">
<%= f.label :q_from %>
<div>
<% random_str = display_random_string %>
<% @q_from_data.each_with_index do |item, i| %>
<div>
<%= f.check_box :q_from,
{
multiple: true,
id: "#{random_str}-#{i}",
class:"w-auto",
},
item, nil
%>
<label class='d-inline fw-normal' for="opions-<%= i %>">
<%= item %>
</label>
</div>
<% end %>
</div>
</div>
<% end %>
```

## Controller
- 配列を受け取る場合は処理をちょいと変える必要があるようです
- さらに、配列の最後に書かないとエラーになります（ここポイント）
###### app/controllers/application_controller.rb
```diff
def configure_permitted_parameters
-   devise_parameter_sanitizer.permit(:sign_up, keys: [:name_ja, :q_from)
+   devise_parameter_sanitizer.permit(:sign_up, keys: [:name_ja, q_from:[])
end
```

## Editページ
- editでは以下の処理を実装しなくちゃいけないですね
   - すでにチェックされたcheckboxにチェックを入れる
   - チェックされていないcheckboxを空にする

###### app/views/devise/registrations/edit.html.erb
```erb
<div class="field py-2">
<%= f.label :q_from %>
<div>
  <% array = @user.q_from.nil? ? [] : @user.q_from.delete('[' ']' ' ' '"').split(",") %>
  <% @q_from_data.each_with_index do |text, i| %>
  <div>
    <%= f.check_box :q_from,
    { multiple: true,
      checked: array.find { |n| n  == text }.present? ? true : false },
      text, nil %>
      <span><%= text %></span>
    </div>
    <% end %>
  </div>
</div>
```
Next.js TypescriptでWebアプリを開発する 2

# はじめに
Next.js TypescriptでWebアプリを開発するさいに調べたことをまとめます。
以下の記事の続き。

https://qiita.com/bear05/items/06f6d41e7e2c38640ded

## Next.jsの利点について

https://qiita.com/Yuki_Oshima/items/5c0dfd8f7af8fb76af8f

## 前ページ共通処理の実装方法

https://zenn.dev/catnose99/articles/2169dae14b58b6

## ページルーティング

https://zenn.dev/terrierscript/articles/2020-10-10-next-js-index-js

https://zenn.dev/pk_yakkun/articles/39dbcac61d569b#router.push()%E3%81%A7%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88

https://www.flock.codes/GXqbdmcKTDoEvx4jjdjD

## バリデーション

https://stackoverflow.com/questions/64020518/error-cyclic-dependency-with-yup-validation

## エラーページの作成

https://nextjs.org/docs/advanced-features/custom-error-page#404-page

## デザイン

https://qiita.com/kanye__east/items/66680f2c6a5929126ebd


### Material-UI

https://zenn.dev/longbridge/articles/c100d0311ed1be

MUIでのページネーション

https://zenn.dev/syu/articles/67c4c569fa5c16

MUIでのフォント設定

https://maku.blog/p/s6djqw3/

## APIレスポンス、axiosエラーがかえってきた場合の対策

https://zenn.dev/wintyo/articles/b417e310efc67e

https://dev.classmethod.jp/articles/typescript-typing-exception-objects-in-axios-trycatch/

## エラー
ハイドレーションエラーはサーバーのレンダリングとブラウザでのレンダリングが異なるときにおこる。
windows,localhostなどを使用するメソッドはuseEffectなどをもちいてサーバー側でのレンダリングを回避する。

https://nextjs.org/docs/messages/react-hydration-error

https://stackoverflow.com/questions/73343986/next-js-abort-fetching-component-for-route-login

Duplicate atom keyのエラー

https://zenn.dev/jordan/articles/e1fe03ee4bb4c5
Angularアプリの一部にIonicコンポーネントを使う方法

Ionic Frameworkは「モバイルアプリのUIをWebで再現できるUIフレームワーク」として多くのアプリ開発の現場で利用されているのは広く知られているところです。Ionic teamがIonic Conferenceの時にだした次のConference Appをみると、その再現度の高さに気づけるかと思います。

https://ionic.io/blog/building-the-eas-2021-conference-app

https://ionic.io/events/ioniconf-2022

しかしWeb開発の現場において、モバイルアプリ開発はニーズのごく一部に過ぎません。ストアに配信するアプリではなく、ブラウザのみで配信することも多いでしょう。そういう案件によっては、Human Interface GuidelinesやMaterial Designに沿って、プッシュ遷移・ポップアップ遷移までは求めることはありません。

しかし、近年モバイルUIとWeb UIの間の境はほとんどなくなってきており、Web UIにおいてもToastやAction Sheet、Floating Actionといったモバイル起源のUIを使うことも増えてきました。そこで、本記事ではアプリの一部にIonicコンポーネントを使う方法をご紹介しましょう。IonicのUIコンポーネントはすべてがLazyLoadingになっているので、追加してもアプリ立ち上げ時に必要なbundleサイズにはほとんど影響しません！

# はじめ方

## scriptタグではじめる

おそらく現代でフロントエンドを行うほとんどの人にニーズはありませんが、まずは懐かしのscriptタグではじめる方法を整理しましょう。

Ionicは導入検討をすばやく行うために、CDN「jsdelivr」を使ってコンポーネントの配信をしています。これをタグで読み込むだけで使い始めることができます。あなたのプロジェクトに追加するのは、以下のたったの3行だけです。

```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core/css/ionic.bundle.css" />
```

1行目は[type=module]ですので、ES Module対応ブラウザのためのものです。2行目はnomoduleですので非対応ブラウザのためのスクリプトを読み込みます。まぁ、こうやって対応ブラウザをみると2行目はもう必要ない気もしますが、公式ドキュメントに残っているので案内しておきます。

https://caniuse.com/es6-module

そして、3行目はIonicデフォルトのスタイルシートを読み込みます。これには以下のすべてが含まれています。

https://ionicframework.jp/docs/layout/global-stylesheets/

用意するのはこれだけ。簡単ですね。

## ng addではじめる

Angularだともっと簡単にはじめることができますね。

```bash
% ng add @ionic/angular
```

`ng add` で何が行われるかは以下の通りです。

- `@ionic/angular` を `dependencies` にインストール
- `@ionic/angular-toolkit` を `devDependencies` にインストール
- `ngModule` に `IonicModule.forRoot()` を設定（import）
- 必要なスタイルシートを `angular.json` に設定
- IonIconを使うために、必要なsvgファイルを `angular.json` に設定
- Ionic CLIを使うことができるように、 `@ionic/angular-toolkit` などを `angular.json` に設定

実際に何が行われるかを知りたい人は以下をご覧ください。

https://github.com/ionic-team/ionic-framework/blob/main/angular/src/schematics/add/index.ts

シンプルにはじめることができますよね。


# コンポーネントを使ってみる

それでは試しにコンポーネントの中にIonicのタグをつかってみましょう。比較的人気の高い、skelten-textを導入してみます。

```html
<ion-skeleton-text [animated]="true" style="width: 80px"></ion-skeleton-text>
```

これだけでアニメーションのついたスケルトンテキストが表示できました。簡単ですね。

![5fbb0f377966fc7cec3baa7b33d1ed82.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100460/f189c7db-de0e-fa0d-765a-3ccc931784fa.gif)

（このgifアニメーションは複数のskelten-textを組み合わせています）。

# おわりに

この設定だけで、以下のすべてのコンポーネントを利用することができるようになります。しかもUIコンポーネントはLazy Loadingなので、少ないコストで！！

https://ionicframework.jp/docs/components/

本稿では「一部にIonicコンポーネントを使う」としましたので、これだけだとAngularのRouterをハックしたプッシュ遷移などはできませんが、それでもIonicのコンポーネントのすばらしさと使いやすさを体験してもらえると思います。

ぜひAngularプロジェクトのUIコンポーネントのひとつにIonicを候補として検討してもらえればと思います。

それではまた！
ransack使用時の注意点

この記事は[Ruby on Rails Advent Calendar 2022](https://qiita.com/advent-calendar/2022/ruby-on-rails)の20日目の記事です。

# はじめに

ransackは検索機能を楽に実装することができるgemです。
https://github.com/activerecord-hackery/ransack 

スター数は執筆時現在5.3kで、今なお開発が続けられており、人気のgemと言って差し支えないでしょう。
しかし、そのセキュリティ上の問題点についてはあまり注目されていないように感じます。
実際、執筆時現在Googleで「ransack」と検索するとセキュリティについて触れられていない記事が散見されます。
この記事ではransackのセキュリティ上の問題点とその対策について紹介します。

# セキュリティ上の問題点

**デフォルトで任意のカラムに対して検索ができてしまいます。**[^1]
後述の手順によってカラムの中身を特定可能です。

## 具体例

usersテーブルにはカラムsecret_tokenが存在するとします。
以下のような実装で、後述する対策をしていない場合に情報漏洩が起きます。

```ruby
class UsersController
  # GET /users
  def index
    @records = User.ransack(params[:q]).result
  end
end
```

## 攻撃
ransackは多様なマッチャーを用意しており[^2]、[カラム名]_startというパラメータで前方一致検索が可能です。それを利用して以下のように検索を繰り返します。

GET /users?secret_token_start=a

GET /users?secret_token_start=b

GET /users?secret_token_start=c

…

レコードが取得できたならばパターンに合致していたということです。
あとはこの手順を繰り返します。
ここでは先頭の文字がaだったことにして続く手順を見ていきましょう。

GET /users?secret_token_start=aa

GET /users?secret_token_start=ab

GET /users?secret_token_start=ac

…

2文字目以降も全く同じ工程を繰り返します。
このように非常に単純な方法で、ユーザーに公開してはいけないはずのカラムの値を特定できます。

# 対策

下記のようにモデルに対して検索可能なカラムのリストを設定します。

```ruby
class User
  def self.ransackable_attributes(auth_object)
    %w(first_name last_name)
  end
end
```

こうすることで指定したカラム以外の検索を拒むことができます。

# まとめ

以上のように許可リストを設定しなければ、使い方によっては情報漏洩につながることを見ていきました。ransackをお使いの方は一度実装を見直してみるといかがでしょうか。

# 余談
上記で述べた脆弱性について手を動かして検証できるRailsアプリを作りました。良かったら遊んでみてください。
https://github.com/wonda-tea-coffee/vulnerable_ransack_app

[^1]: https://activerecord-hackery.github.io/ransack/going-further/other-notes/#authorization-allowlistingdenylisting

[^2]: https://activerecord-hackery.github.io/ransack/getting-started/search-matches/
MetabaseをGAEで動かしたいっ！

# はじめに
こんにちは。
これは、[2022年 NI+C Advent Calendar](https://qiita.com/advent-calendar/2022/niandc_teamgcp) 14日目の記事です。
いつもNode-RED絡めた記事ですが、今年はなんとNode-REDではない記事なのです！

# そもそもMetabaseって何？
[Metabase](https://www.metabase.com/)は、オープンソースソフトウェアのBI（データ可視化）ツール。LookerStudioとかRe:Dashとかと同じ類の製品です。様々なデータを参照/可視化し、分析に役立てることができます。他のOSSのBIと比べてグラフのデザインがキレイなこと、コード（クエリ）を直接書くこと無くクリックベースでグラフ等可視化できることが特徴です。

**画面イメージ**
![スクリーンショット 2022-12-14 17.03.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142110/c300060a-2c2c-7fb3-ec0f-d7b4fea2c909.png)


# なぜApp Engineで動かしたいの？
Googleマネージドで動くサーバーレス環境だから。この点でしょうか。利用の面でどうしても気にしないといけないのが、環境の運用面。障害対応とかもそうですし、負荷に対する対応なども気にする必要があります。こういったところをうまーくやってくれる（やりやすくしてくれる）のが、マネージドなサーバーレス環境の利点だと思うのです。
・・・と書きましたが、単にGAEが好きだから。それが大切なのですっ。

# どうやってApp Engineで動かすの？
GAE用のカスタムコンテナを作って、それをデプロイするだけ。文字にするとこれだけです。

#### 事前準備しておくもの
- （必須）App Engine Flexible 環境の有効化
- （必須）CloudSQL（MySQL）インスタンスの準備
- （任意）oAuthの有効化・・・MetabaseでGoogle認証を使いたい場合
    - oAuth同意画面の設定
    - 認証情報 ウェブアプリケーションのクライアントIDの作成

#### 定義ファイルの作成
- （必須）app.yaml・・・GAEの環境定義です。以下のような定義になります。
```yaml
service: default
runtime: custom
env: flex

manual_scaling:
  instances: 1

resources:
  cpu: 1
  memory_gb: 2
  disk_size_gb: 10

env_variables:
  MB_DB_TYPE: mysql
  MB_DB_DBNAME: metabase
  MB_DB_USER: <<CloudSQL DB User ID>>
  MB_DB_PASS: <<CloudSQL DB User Password>>
  MB_DB_HOST: 127.0.0.1

beta_settings:
  cloud_sql_instances: <<CloudSQL 接続名>>
```

- （必須）Dockerfile・・・GAE上で動かすカスタムコンテナ定義です。以下のような定義になります。

```dockerfile
FROM gcr.io/google-appengine/openjdk

EXPOSE 8080

ENV PORT 8080
ENV MB_PORT 8080
ENV MB_JETTY_PORT 8080
ENV MB_DB_PORT 5432
ENV METABASE_SQL_INSTANCE <<CloudSQL 接続名>>
ENV JAVA_OPTS "-XX:+IgnoreUnrecognizedVMOptions -Dfile.encoding=UTF-8 --add-opens=java.base/java.net=ALL-UNNAMED --add-modules=java.xml.bind"

ADD https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 ./cloud_sql_proxy
ADD http://downloads.metabase.com/<<Metabaseのバージョン>>/metabase.jar ./metabase.jar

RUN chmod +x ./cloud_sql_proxy

CMD ./cloud_sql_proxy -instances=$METABASE_SQL_INSTANCE=tcp:$MB_DB_PORT & java -jar ./metabase.jar

```

#### GAEへのデプロイ
- （必須）gcloudコマンドでGCP Projectへログイン
- （必須）上記の定義ファイルがあるディレクトリで、デプロイコマンドを実行
```bash
gcloud app deploy
```

これだけで、App EngineでMetabaseが稼働できるようになります。
Metabaseが起動した後は、Metabaseにログインして初期設定を行ってください。

# まとめ
いかがでしたか？？
Metabaseは、クエリ知識がなくともある程度使えるところが一番のポイント。また、サーバーレス環境であるGAEで動かすことで、運用の省力化にも繋げられます。
Google Cloudでのデータ可視化/分析ツールの一つの選択肢として、ご参考になれば嬉しいです！
三項演算子

どうも、豆腐メンタルなエンジニアです。
今回はJSにおける三項演算子について、学習したので記録していきます。
# 三項演算子って？
三項演算子とは、条件によって処理を分けることのできる演算子です。
また、三項演算子の別名は「条件演算子」とも呼ばれます。

「条件によって」「条件」と聞くと、頭に浮かんでくるのがif,elseの条件式ですよね...。
少しコードを見ていきましょうか。
```js
const pokemon = "ピチュー";

if (pokemon === "ピチュー") {
  console.log("ぴちゅぴ");
} else {
  console.log("ちょっげぷりいいいいい");
}
// 出力結果
ぴちゅぴ
```

例題は...他に思いつかなかったのはご愛嬌
普通のif文だとこのようになります。
正直長いです...

では、ここで三項演算子を使ってみましょう

```js
const pokemon = "トゲピー" ==="ピチュー" ? "ぴちゅぴ":"ちょっげぷりいいいいいいい";
console.log(pokemon);
// 出力結果
ちょっげぷりいいいいいいい
```
一行だけでif文と同じことができてしまいました。
「:」の両隣でそれぞれ処理するようにしているイメージですね。
:の左はtrue,:の右だとfalseを返すといったところでしょうか。

if文だと長くなりがちですが、こちらの三項演算子を使えばシンプルに一行に纏めることができます。

また、入れ子構造にもできるみたいですが、それはまた後の機会に記録していこうかなと思います。

短い内容ですが、今回はこの辺りで。ではまた次回。
RubyonRailsチュートリアル：実践メモ【第一章】

<br>
<b>Ruby on Rails チュートリアル</b>(Webサービス開発が学べる学習サービス)に従って、MVCフレームワークアプリケーションを作成していく。<br><br>

**使用ツール** ：
AWS Cloud９
### 【AWS Cloud９】<br>新しくターミナルを開く方法
1. タブバーの **＋** ボタンをクリック
1. メニューの中からNew Terminalを選ぶ<br>※もしくはalt + t
TypeScript文法(基礎編) オブジェクトとは何か？型の付け方

# オブジェクトとは何か？

この記事で紹介したプリミティブ型という基本的な7つの型以外のものを、
オブジェクトという。

https://qiita.com/reisuta/items/702b64d50bbbe6bdf36d

他の言語だと、オブジェクト = クラスなどに結び付けられがちだが、
TypeScriptの場合、クラスだけにとどまらない、広い範囲を含むのが特徴だ。

代表的なものとしては、
```
const val = {
    obj1: 123,
    obj2: 'hello'
}
console.log(val.obj1) // 123と表示される
```
定義方法としては、
{}の中に、**プロパティ名(キー):値**と定義する。

オブジェクトの中身(プロパティ)を得ている、
console.log(val.obj1)の部分は、
プロパティアクセスに当たる。

ちなみにプロパティアクセスの方法としては、
```
console.log(val["obj1"]) // 123と表示される
```
のような記法もある。


いくつかのデータをまとめて管理したいときなどは、
オブジェクトが利用されることが多い。

なお、省略記法もあり、
例えば次のようにかける。

```
//省略しない書き方
const hello = 'Hello World'
const user1 = {
    hello: hello,
    age: 28,
}
console.log(user1.hello)
```

```
//省略記法
const hello = 'Hello World'
const user1 = {
    hello,
    age: 28,
}
console.log(user1.hello)
```

変数名とプロパティ名が同じ場合、
**: 変数名**の部分を省略できる。


# オブジェクトの型

オブジェクトの型は、
例えば次のように記述する

```
const val: { 
    obj1: number,
    obj2: string
} = {
    obj1: 123,
    obj2: 'hello'
}
console.log(val.obj1) // 123と表示される
```

この場合、変数valの型は、
{ obj1: number, obj2: string }型となる。

複数の値に対してそれぞれ型を定義しているだけで、
一つ一つについては、プリミティブのときと同様。

ただ、記述が長くなるので、
type文を使うことが実際には多い。

```
type valobj = {
    obj1: number,
    obj2: string
}
const val: valobj = {
    obj1: 123,
    obj2: 'hello'
}
console.log(val.obj1)
```
type文は、上記のように、
型を宣言する構文である。

すでに存在する型に対して、
別名をつけることができる。

# 型引数
 上記のtype文と関連して、
型引数という、type文の中でだけ有効な型として
扱えるものがある。

```
type valobj<T> = {
    obj1: number,
    obj2: T
}
```
上記の型引数を持つ型は、
ジェネリック型という。

上記の型を実際に使う場合は、
引数に型を指定する。
```
const val: valobj<string> = {
    obj1: 123,
    obj2: 'hello'
}
```
この場合、
stringが引数に渡されているので、
valobj<T>型のTはstringとなり、
{ obj1: number, obj2: string }型と同様になる。

なお型引数を指定した場合、
呼び出し側で引数を指定しないと、
コンパイルエラーになる。

```
//型引数なしでは呼び出せない。
const val: valobj = {
    obj1: 123,
    obj2: 'hello'
}
```

そのため、省略可能なオプショナルな型引数にする場合は、
予めデフォルト値を定義しておけばいい。

```
type valobj<T = string> = {
    obj1: number,
    obj2: T
}
const val: valobj = {
    obj1: 123,
    obj2: 'hello'
}
```
この場合、型引数が指定されないと、
Tはstring型になる。


# 配列
配列は、[]で定義される、
複数のデータをまとめたデータ構造で、
オブジェクトの一種である。

```
const arr = ['hello', 'alice', 123]
console.log(arr)
```

配列の値にアクセスするには、
```
arr[0]
```
と番号を指定する。
0始まりからなので、先頭の要素は、
arr[0]というふうに取得する。

配列は、上記の番号、
0とか1といったプロパティ名を持つ、
オブジェクトともいえるので、
オブジェクトのプロパティアクセスの方法で
値を取得することができる。

### 配列の型

配列の型は、

```
const arr: string[] = ['hello', 'alice']
```

というふうに、型[]と記述する

もう一つの記法として、
```
const arr: Array<string> = ['hello', 'alice']
```
というのも存在する。こちらは組み込みのジェネリック型にあたる。

また、最初に定義したような、
複数の型が混ざった配列の場合は、
ユニオン型を使って、
```
const arr: (string | number)[] = ['hello', 'alice', 123]
```
というふうに記述する。
reactでuseStateを使用して簡単なカウントシステム実装

reactのフックスの学習でuseStateを使用して、ユーザー権限の変更機能を実装したので
まとめます。

①権限をuseStateで定義する。
```
       　変数　　　　右の変数の関数　　()の中は初期値
const [ authority, authorityEdit ] = useState(0)
```

②カウントアップ、ダウンのボタンを作成。
```
            <div>
                <button className="btn btn-primary  bg-primary" type="button"　onClick={ addition }>権限ダウン</button>
                <button className="btn btn-primary  bg-primary" type="button"　onClick={ subtraction }>権限アップ</button>
            </div>
```

onClickで③で記述する関数が実行されるように記述します。

③、①のstateの値を変更する関数を記述する。(stateの変数に直接変更を加えるのはNG)
```
    const addition = () => {
        authorityEdit(
            authority +1
        )
    }
    const subtraction = () => {
        authorityEdit(
            authority -1
        )
    }
```

以上。
Ledger CLIで家計簿をつけたい！実践4 株式取引

前編↓

https://qiita.com/kikeg/items/02542f17ca7dba226c99

# 注意

## 株式取引は原価と売価に注意

株式の取引に際しては重要なのは購入時の価格と売却時の価格を考えるのが大事です。更にこれを厄介で、アメリカの株取引はコストを平均せずいつ取得した株かで原価を考えます。12/2に買った株は原価100, 12/3に買った株は原価105みたいな感じです。一方日本で株取引の売却益に課税する基準は平均取得単価と言ってこれまですべての取得に要した費用を平均した額で原価を考えます。なので、家計簿レベルでつける際は実際の課税基準に合わせてつけるのか、税申告用はこのデータを使って(Ledger CLIでもCSV出力して表計算ソフトを使ってでも)別に処理して家計簿の中ではピュアな利益を計算するのか、折り合いを付ける必要があります。個人的には税申告用には色々と厄介な要素が多いので別に分けておくのがいい気がします[^tax]。

# 実用例

## RSU (ストックユニット)をもらった場合

まずあまり日本の会社では見ないですが、会社がストックをぽんとくれるタイプの給与があります。外資にはよくあります。ストックオプションと違ってただ株をもらえます。

```ledger:broker/okane/2022.ledger
2022/06/25=2022/06/29 * RSU income
    Assets:Brokers:Okane                          80 OKANE @ 100 USD
    Expenses:Tax:Income                         4000 USD
    Income:Stock                         (-120 * 100 USD)

```

100ドルの株を120株もらいましたが、33%の所得税天引きで80株しかもらえてないという計算です。ちなみに、この天引き日本勤務だとできないので、全額一旦自分の懐に入ってから自分で売却するなりキャッシュを用立てて支払わないといけません。あと、見ておわかりの通りLedgerはカッコで囲むことで四則演算を使うことができます。本当は関数を定義したりできるそうですが、まだ使ったことがないです。

## 通常通り株を購入した場合

普通通り株を買うのは外貨を買うのと大差ないです。

```ledger:broker/okane/2022.ledger
2022/03/22 * Buy stock
    Assets:Brokers:Okane                           1 VTI @ 225.78 USD
    Assets:Brokers:Okane                     -225.78 USD
```

株の場合も売買手数料がかかることがあるので、かかる場合は記録したほうがいいです。

## 株を売却した場合

### 利益が出た場合

さて、売却する際には原価を考えないと売買差損益が出ていることが正しく記録されません。RSUを値上がりして売却する場合を考えましょう。2割値上がりして1株120ドルになったとしましょう。

```ledger:broker/okane/2022.ledger
2022/09/10 * Sell out RSU
    Assets:Brokers:Okane                         -40 OKANE {100 USD} @ 120 USD
    Assets:Brokers:Okane                     4799.00 USD
    Expenses:Commissions                        1.00 USD
```

この記法は`{}`内に購入時の価格を、`@`以降にこの取引での値段（つまり売価）を書くというものです。こうすると当然売買差益が入ってないのでエラーになります。

```shell-session
 %  ledger -f broker/okane/2022.ledger bal
While parsing file "/home/kikeg/projects/ledger-codelab/broker/okane/2022.ledger", line 13:
While balancing transaction from "/home/kikeg/projects/ledger-codelab/broker/okane/2022.ledger", lines 10-13:
...
Unbalanced remainder is:
          800.00 USD
Amount to balance against:
         4800.00 USD
Error: Transaction does not balance
```

なのでこれを直します。

```ledger:broker/okane/2022.ledger
2022/09/10 * Sell out RSU
    Assets:Brokers:Okane                         -40 OKANE {100 USD} @ 120 USD
    Assets:Brokers:Okane                     4799.00 USD
    Expenses:Commissions                        1.00 USD
    Income:Capital Gain                      -800.00 USD
```

### 損失が出た場合

今度は同様に損失が出た場合です。

```ledger:broker/okane/2022.ledger
2022/12/10 * Sell out RSU
    Assets:Brokers:Schwab                        -40 OKANE {100 USD} @ 80 USD
    Assets:Brokers:Schwab                    3199.00 USD
    Expenses:Commissions                        1.00 USD
    Income:Capital Gain                       800.00 USD
```

記法は同様ですが`Income`がプラスということは実際には損失が出てることに注意してください。私はズボラなので売買差損益はどちらも`Income:Capital Gain`に組み込んでいますが、神経質な人なら`Loss:Capital Loss`とかにしたほうがいいかもしれません。ただ、そうすると項目が増えて面倒なんですよね。

今回は株の取引の記録方法を見ました。購入時の株式の値段を知る方法が面倒だなぁという感じでした。次回コマンドを調べてそれを楽に計算できるか探してみます。詳しい人いたら教えて下さい。

[^tax]: どちらにせよ私は税理士ではないので、税申告上の信頼ある情報をお伝えすることはできません。