OmniAICreator/Qiita-1.07M huggingface dataset:
ユニバーサルロボットへのRTDE通信の実例(JAVA)

# はじめに
世界で多くのシェアを誇る協働ロボットであるユニバーサルロボットは、ロボットの座標値やスピードスライダ値などの取得、または設定できるデータ通信機能を実装しております。
Real-Time Data Exchange(RTDE)通信と呼ばれるもので、ポート30004番にアクセスすることでリアルタイムのデータの取得・設定を可能とします。内容についてはEtherNet/IPの内容と同等なのでPLCがあればこの通信を使うことは不要ですが、ラズパイや外部パソコンからデータ通信を行うときに非常に役立ちます。

https://www.universal-robots.com/articles/ur/interface-communication/real-time-data-exchange-rtde-guide/

RTDE通信の仕組みは次の投稿で詳細にご説明しますが、まずここではどういうことができるのかを知っていただくために、JAVAで通信できるライブラリをOSSでGithubに公開しておりますので、それを使用した例を示します。

# RTDE通信のプログラム例
Githubの公開アドレス

https://github.com/tke-project/RTDESocketClient

App.javaのファイルを開いていただけると、今回作成したライブラリの使い方の例が載っております。
```java
        RTDESocketClient client= new RTDESocketClient("192.168.83.129", 20, "myApp") {

            @Override
            public void onReceive(Object[] values) throws IOException {
                double[] pos = (double[])values[0];

                System.out.println(pos[0]*180/Math.PI);
            }

            @Override
            public Object[] onSend() throws IOException {
                return null;
            }
            
        };

        client.addOutput(RTDEOutput.actual_q);
        client.start();

```

`RTDESocketClient`クラスのコンストラクタに、ロボット（ここではオフラインシミュレータ）のIPアドレスと、送受信の1秒間当たりの回数、通信ヘッダを設定します。通信ヘッダはログ用なので空欄でも構いません。
今回はロボットのジョイント角度の取得を行ってみます。そのため、末尾に`addOutput`メソッドで`RTDEOutput.actual_q`というのを指定しています。これはユニバーサルロボットの資料を見ればわかりますが、`actual_q`というのはジョイント6軸すべての値を示します。
実際に取得できたかどうかを確認するために、`onReceive`という抽象メソッドを明確に定義することでデータ受信直後の処理を書きます。ここではvalues[0]をdouble配列にキャストして、ロボットのベース軸の角度を表示するコードを書いています。
これを実行すると、下図のようにロボットのベース座標と、取得した値が合致することがわかります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565600/93181480-fbb7-213f-6d5c-7f6a185ca348.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565600/48b1f3aa-8724-4568-6412-7dda834ee96d.png)
BL652モジュールにはWaitやdelayみたいな待機処理がないので作った

現在開発のご依頼を頂き、BL652というなかなか日本での解説記事が少ないモジュールで
開発をしているんですが、なんとこのモジュールには、Waitやdelayみたいな、
プログラムの待機処理がない。。。
（このくらいは作って欲しいよ。）

なので今回は自作したので以下に記載。

```vb
DIM startTick
startTick = GetTickCount()
WHILE GetTickSince(startTick) < 200   //200ms待機
ENDWHILE
```

もし日本で開発されている方がいらっしゃれば、
参考にされてください。

地味につかう処理なので。
【FormRequest】バリデーションエラー時に任意のURLにリダイレクト

親クラスであるFormRequestクラスの「getRedirectUrl」をオーバライドして以下のようにTestファイル内に記述する。

    protected function getRedirectUrl()
    {
        return route('xxxxx.show', ['id' => $this->route('id'), '#commentArea']);
    }

・ユースケース
詳細画面にコメント機能があり、コメントの入力でバリデーションエラーが起きた際にコメントエリアのアンカーリンク付きのURLにリダイレクトさせる時など。
【自動化】麻雀の成績管理シートを作成したので公開する

普段固定メンバーで麻雀を打つ中で、長期的に成績を管理できるものが欲しくなったので、スプレッドシートを用いて作成しました。せっかくなので、公開しようと思います。
<br>

1半荘終わりの得点を入力するだけで、
- 30000点返しとウマを含んだ清算を自動で行い、
- 総合成績と当日成績に反映する

という特徴があります。
<br>

**↓スプレッドシート(閲覧のみ)**

https://docs.google.com/spreadsheets/d/1cHjgzEfywq4t4-P6taEn5blFH8bXteUJQies2Z0TkTA/edit?usp=sharing

## ダウンロード
こちらからダウンロードできます
https://docs.google.com/spreadsheets/u/1/d/187EWZpvF5d7TZxw0RqlPe6Q235rciJ5VTwxGPsZC_aA/copy


## 機能
- 総合成績シート
    - 総合順位と総合成績を確認できる。
    - その他、トップ率や平均順位もわかる。
    - 成績をつけるメンバー(雀士)は増やすor減らすことができる。
- 当日成績シート
    - 1半荘終わりの得点から自動で清算を行う。
    - 当日のトータル成績を表示する。
    - さらに、総合成績に自動で反映する。

## 使い方
こちらにまとめています。
https://docs.google.com/spreadsheets/d/1cHjgzEfywq4t4-P6taEn5blFH8bXteUJQies2Z0TkTA/edit#gid=1435940976

## 清算について
本管理シートでは、30000点返しのウマが`1位：+50, 2位：+10, 3位:-10, 4位:-30`で行っています。しかし、スプレッドシートに付随したGAS(Google Apps Script)のコードを変更することで、清算の計算方法を変えることができます。

例えば、順位ウマはcalc.gsにて変更可能です。

```javascript:calc.gs
// 登録画面から情報を取得し、順位通りのリスト(["名前","成績"])を返す
function get_name_and_score() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('当日成績');
  const rank_uma = [50, 10, -10, -30]; // 順位ウマ
  
  let registration_list = sheet.getRange('A3:C6').getValues();

  // 点数と順位からリストの並び替え
  let registration_list_sorted = registration_list.sort((a, b) => b[1] - a[1] || a[2] - b[2]);

  // 順位から成績を計算し、順位を除く
  let result_list = registration_list_sorted.map(
    (item, index) => [item[0], (item[1] - 30000)/1000 + rank_uma[index]]
  );

  return result_list
}
```

その他具体的なコードはGitHubからも確認できます。

https://github.com/Koichi73/Mahjong-Score-Sheet

## 今後の改善点
- エラー発生時の処理を追加する
- ドキュメントの充実化
- 清算方法をスプレッドシートから設定できるようにする
iOS, Androidエミュレータにファイルを転送するにはドラッグアンドドロップすればOK

ふと試したらあっさりできて感心した
一応仕事でスマホアプリ開発していたこともあったのに初めて知りました  

# Android
Downloadsにコピーされる
![スクリーンショット 2023-10-01 22.33.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352532/e9fea86c-3d58-7977-6974-dea33d24f038.png)
![スクリーンショット 2023-10-01 22.34.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352532/431cd565-5c89-d5b1-0cd5-79d68e63c3a4.png)
![スクリーンショット 2023-10-01 22.34.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352532/5af3eda7-1498-6925-9f6a-b9ab0f5e1ba0.png)

# iOS
コピー後自動でAll Photosが開く
![スクリーンショット 2023-10-01 22.36.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352532/e2074d17-ac5f-cccb-3283-1f95f162b270.png)
![スクリーンショット 2023-10-01 22.36.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352532/af6a754b-822f-406f-41d0-f8353104fe54.png)
再帰処理って何？

# 初めに
再帰処理を学んだので自分のためにメモとして残しておきます
あまり使ったことが無かったので新鮮でした

### 再帰処理とは

再帰処理とは、ある関数の中で、関数自信を呼び出すこと
再帰関数は、再帰処理が書かれている関数のこと

簡単な例です(サンプルプログラムを載せていますが、これだけでは動きません)

```sample.java
class Main{
    public static int Sample(int day){
        
        if (day <= 0) {
            return 1;
        }

        return Sample(day -1) * 2;
    }
}
```
日ごとに2倍になっていくプログラムです

day = 3の時を考えます

Sample(3)
の場合

if文の条件には引っかかりません

return Sample(2) * 2となります

Sample(2)
の場合

if文の条件には引っかかりません

return Sample(1) * 2　となります

Sample(1)の場合

if文の条件には引っかかりません

return Sample(0) * 2となります

Sample(0)の場合

if文の条件に引っかかります
この時、1を返します

Sample(3)をもう一度考えると

Sample(3) = 
Sample(2) * 2 = 
Sample(1) * 2 * 2 = 
Sample(0) * 2 * 2 * 2　=
1 * 2 * 2 * 2 =
8

となります

### 気を付けること

再帰処理をするときに気を付けることがあります

①　if文の条件を書くこと
②　2個目のreturn文でdayを1引くこと

①
if文の条件で (day<=0)としています
もし、この条件が無かった場合はどうなるでしょうか

```sample.java
class Main{
    public static int Sample(int day){

        return Sample(day - 1) * 2;
    }
}
```

dayは永遠にマイナスをされます
Sample(0) * 2 * 2 * 2 * 2
Sample(-1) * 2 * 2 * 2 * 2 * 2
Sample(-2) * 2 * 2 * 2 * 2 * 2 * 2
...

と永遠に続いてしまいます
どこかで再帰処理を終わらせる必要があります

②
①と似ていますが、day-1をしないことでif文の条件に引っかからず永遠と続いてしまいます

```sample.java
class Main{
    public static int Sample(int day){
        
        if (day <= 0) {
            return 1;
        }

        return Sample(day) * 2;
    }
}
```

Sample(3)とした場合
Sample(3) = 
Sample(3) * 2 = 
Sample(3) * 2 ...

となってしまい続いてしまいます



このように条件を間違えれば永遠と再帰処理が続いてしまうため(無限ループ)、使用する際は終了する条件が揃っているか注意する必要があります
[Unity]3Dオブジェクトの手前と奥にUIを配置する方法

# 0.はじめに
この記事はUnityのuGUIを3Dオブジェクトの奥と手前に表示する方法についての自分向けの備忘録代わりの記事となります。
また、記事の作成や文章を書くのが久しぶりのため、読み辛いことこの上ないかと思いますが、受け入れてください。
（コメントとかで、色々と指摘してもらえると嬉しいです。）

今回はLineRendererが背景よりも後ろで描画される問題に遭遇しました。
その問題解決のための前テストとして、表題の内容について色々と試しつつ調べました。

使用しているUnityのバージョンは[Unity 2021.3.25]です。

# 1.2Dオブジェクトのみ（UIのみ）の場合
1.SortingLayersから、レイヤー層を作成する
（上に置いてあるほど奥に表示され、下に置いてあるほど手前に表示される）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384169/1f05c1c1-42c7-e418-fdc0-77e91cd1aeca.png)
2.キャンバスから、[Sorting Layer]に割り当てをする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384169/8b16c9e7-412a-d74b-cbfa-a7e33313e4cc.png)
これで、同じSortLayer毎にまとめて前後表示の設定をすることができる。
MeshTextの設定でもできるが、コードからになるため今回は割愛。

# 2.3Dオブジェクトの手前と奥にUIを置く場合
1.[Render Mode]を[Screen Space - Camera]に変更をする
2.[Plane Distance]の値を調整し、3Dオブジェクトを挟む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384169/4dd37cce-b612-30b2-e910-787c098612a9.png)
これで、3Dオブジェクトの手前と奥にUIを置くことができる。
テスト的に作った画面だとこんな感じになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384169/87f1feab-f1e4-3818-a35b-a3da858b8356.png)

# 3.[2]の注意点
Hierarchyタブからカメラを選択した時、Sceneタブでカメラの範囲が表示される。
これよりもUIで作成したCanvasが奥過ぎたり手前過ぎると、UIは表示されなくなる
UIの奥と手前はこの範囲に限定される。->配置できる3Dオブジェクトもこの間に置かないといけない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384169/40532f5c-c4db-dd91-31e6-eecc5876c23e.png)


# 4.おわりに
今回得た知見を基に、次はLineRendererでの問題解決をやってみようと思います。
Canvasの原理的にも、問題はないはずだけれども、ダメだったら辛いなあ......。

# 5.参考URL
 - 特になし
プログラムを書いて趣味環境をちょっと幸せにした話〜初めてのスクレイピング〜

私は趣味でとあるカードを集めているのですが、
その数が70枚くらいを超えてから、
どのカードが獲得済みなのか、実際に今何枚なのかを把握するのが大変になってきました。

2年前の私だったら、何度もカード見返したり数えたりすることしかしなかったのでしょうが、
今の私は「情報をどっかから取得して自分のほしい形に整形して管理すればいいじゃない」と言えるようになりました。

そこで、今回は私の集めているマンホールカードの情報を検索サイトの情報を参考にCSVファイルにまとめていきたいと思います。


まさかマンホールカードを知らない人はいないと思いますが、もし万が一知らない場合はこちらのサイトを見てみて下さい。きっと虜になるはずです。

https://www.gk-p.jp/mhcard/?pref=zenkoku#mhcard_result

さて、今回は下水道広報プラットフォームの情報を取得し、データを整形、CSV出力していきたいと思います。

なお、今回は個人利用かつ勉強目的でスクレイピングを行っていますが、営利目的で使用する場合などはスクレイピングは行わなず、「マンホールカードのデータ利用に関するガイドライン」に従ってデータの提供を申請するようにして下さい。
>マンホールカードのデータ利用に関するガイドライン（以下、単に「ガイドライン」と言う。）は、マンホールカードに関する著作権を有するＧＫＰ（下水道広報プラットホーム）と地方公共団体（マンホールカードを製作している地方公共団体を指し、かつ、著作権の対象物は製作したマンホールカードに限るものとする）が、第三者から営利を目的としてマンホールカードに関するデータ（以下「ＭＣデータ」と言い、画像、及び座標等の電子データを指す）の提供を求められた場合に、公共性及び公平性の観点から円滑に手続きを進められることを目的として作成するものです。
なお、当該地方公共団体が自らのマンホールカードのみに関するデータを利用する場合のほか、第三者のうち、企業・団体ではない者が非営利の目的で利用する場合や、報道機関等への提供など明らかにマンホールカード自体のＰＲに資する場合は、ガイドラインの対象外とします。

https://www.gk-p.jp/wp-content/uploads/2021/06/MCGuideline210614.pdf

コードは以下のとおりです（開発言語はRuby）。

下水道広報プラットフォームのウェブサイトのHTMLを取得し、Nokogiriを使ってHTMLを解析、データを整形し、CSV出力する。というものです。

```ruby
#
# マンホールカードのまとめサイトにアクセスしてhtmlを取得し、データをCSVで出力
#
def send_csv
    # ウェブサイトのURLを指定
    url = URI.parse('https://www.gk-p.jp/mhcard/?pref=zenkoku#mhcard_result')

    # HTTPリクエストを送信してHTMLを取得
    response = Net::HTTP.get_response(url)
    return unless response.code == '200'

    html_content = response.body
    csv_data = create_csv_data(html_content)

    # エクセルで開くと文字化けするのでSJISでエンコード
    send_data csv_data.encode!(Encoding::SJIS, undef: :replace, replace: '?'),
            type: 'text/csv',
            filename: 'data.csv'
end

#
# CSVデータを作成
#
def create_csv_data(html_content)
    # html_contentをNokogiriで解析
    doc = Nokogiri::HTML.parse(html_content, nil, 'utf-8')

    # 各マンホールカードの情報はtr要素に入っている
    trs = doc.at('table').css('tr')

    csv_data = CSV.generate(row_sep: "\n", encoding: Encoding::UTF_8) do |csv|

      trs.each do |row|
        tds = row.css('td')
        next if tds.blank?

        array = tds.map(&:text)

        # 外部へのリンク
        link = tds.last.css('a').attr('href')&.value
        array.push(link)

        # マンホールカードのイメージソース
        image = row.css('img')[0]['src']
        array.unshift(image)

        csv.add_row(array)
      end
    end
end
```

出力結果のcsvファイル↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2644489/1de512ab-ea05-d15e-8d0a-6b3561f3ca3b.png)


始めてNokogiriを使ってみましたが、htmlの解析がこんなに簡単にできるんだ！と驚きました。

プログラミングに触れて、身近な問題の解決策の幅が広がってきた気がして、楽しいなぁと思います。

これで私のマンホールカード収集環境が少し幸せになりましたが、まだまだやりたいこと（slack上で簡易なマンホールカードクイズをするとか、旅行先を入力したら道中で取得できそうなマンホールカードを提案してくれる機能とか...）があるので、
これからもプログラミングの力で趣味環境を整えていきたいと思います。
マルウェア、さまざまな攻撃

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/2ec2fab1-04bc-c24e-d809-8a4c0db23144.png)
１：クロスサイトリクエストフォージェリ（CSRF）
　CSRFは、攻撃者がハイパーリンクなどを利用してログイン中の利用者に自らが意図しない要求を送信させる攻撃です。利用者は、商品の購入、ユーザ設定の変更（退会処理、商品の送り先の変更など）、掲示板への書き込みなどの操作を意図せず行なってしまいます。
　CSRFでは、攻撃者が用意したハイパーリンク中に、不正データを送りつける仕掛けを設けます。そして、そのハイパーリンクを利用者にクリックさせることで、あたかも利用者が自らの意思で不正データを送ったかのように見せかけます。
　攻撃者が用意したハイパーリンクは、データを受理するページに直接リンクしていることが多いのです。したがって、システム側で規定のページを経て、データを受理するページに移ってきたのか、規定のページ経ずにデータを受理するページに移ってきたのかを判定し、後者の場合は、データの受理を拒否すればCSRFの攻撃を防ぐことができます。

２：ディレクトリトラバーサル
相対パスでファイル名を入力することによって、非公開のファイルを盗み出す攻撃です。ユーザが入力したファイル名をそのまま利用してパスを生成するとディレクトリトラバーサルの被害を受けます。
　”/home/public/”というディレクトリで公開用のファイルを管理しているシステムを例にしてみます。ユーザは、ファイル名を入力してダウンロードするファイルを指定することができます。今、ユーザはファイル名として、"report.pdf"を入力しました。この時、システム内ではユーザが入力したファイル名の前に"/home/public/"をつけて、/home/public/report.pdfというパスを生成します。先頭に"/home/public/"をつけることで、このディレクトリ以外のファイルにはアクセスできないようにしようとの考えがあります。
　しかし、ユーザが"../../etc/password"のように、相対パスでファイル名を指定すると"/home/public"ディレクトリ以外のディレクトリにもアクセスできてしまいます。この場合、システムで生成されるパスは、/home/public/.../../etc/passwordとなりますから、/etc/passwordとなりますから、/etc/passwordにアクセスします。（/etc/passwordはパスワードが登録されているファイルです。）

　このようにして、システム中のあらゆる場所にアクセスする攻撃がディレクトリトラバーサルです。

３：標的型攻撃
　主に、電子メールを用いて特定の組織や個人を狙う攻撃です。電子メールと共に送られてきたマルウェアに感染し、情報の漏洩、システムの不正利用などを引き起こします。一般的に、長期にわたって機密情報（企業の営業秘密、技術情報）を盗み取ることを目的としています。
　メールの内容は、顧客からの苦情や問い合わせを装ったりしており、心理的に内容を見てしまいがちなものになっています。また、送られてくるマルウェアが標的の組織や個人用にカスタマイズされていて、ウイルス対策ソフトで検知しづらいと言った特徴もあります。
　対策としては、ファイアウォールで組織外に出ていくパケットの内容を監視し、機密情報が送信されていないかをチェックする方法が有効です。

４：ドライブバイダウンロード
　インターネット上で公開されているWebサイトを閲覧したときに、ユーザに気づかないようにPCにマルウェアをダウンロードし、PCをマルウェアに感染させる攻撃です。「ホームページを見ただけでウイルスに感染した」という状態になります。
　知らない人から送られてきたメールの本文にあるリンクを不用意にクリックしたり、怪しげなWebページを閲覧したりしないように、日頃から注意を払うことが大切です。
※試験の出題例：ドライブバイダウンロード攻撃は、「利用者が公開したWebサイトを閲覧したときに、その利用者の意図にかかわらず、PCにマルウェアをダウンロードさせて感染させる」攻撃である。

５：水飲み場型攻撃
　企業の一般社員や個人が日常的に利用するサイトのWebサイトのWebページを改ざんしてマルウェアを仕掛け、当該Webページを閲覧際に、PCをマルウェアに感染させる攻撃です。標的型攻撃の一環としてこのような攻撃が繰り広げられることもあります。

６：電子メールの第三者中継
　「外部から外部へ宛てたメール」を中継するメールサーバをオープンリレーのメールサーバと呼びます。宛先が自組織のメールサーバがこのようなメールを中継する必要はありません。したがって、通常は、メールサーバをオープンリレーにすることはありません。一方で、設定間違いなどが原因でオープンリレーとなっているメールサーバも多く存在しています。攻撃者はオープンリレーのメールサーバを踏み台として利用し、迷惑メールや標的型攻撃メールを送ってきます。これを、第三者中継と言います。
Hyper-VでのNATネットワーク作成方法

本当にうまくいかず苦労したので備忘録。

参考サイト
https://askme4tech.com/how-create-nat-switch-hyper-v

Windowsターミナルを管理者で立ち上げ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284920/5ed855c3-113e-ed22-dbfc-8c0614d3d12d.png)

Hyper-V に仮想スイッチを作成。
New-VMSwitch -SwitchName "VM-SW01" -SwitchType Internal

NATゲートウェイ作成。
New-NetIPAddress -IPAddress 172.31.1.254 -PrefixLength 24 -InterfaceAlias "vEthernet (VM-SW01)"

NATネットワーク作成
New-NetNat -Name NAT-NW01 -InternalIPInterfaceAddressPrefix 172.31.1.0/24


Ubuntuのネットワーク設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284920/0aeab7dd-291c-8928-bf6a-9768f471b36e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284920/c50d4c4b-2dc5-bad2-5816-bd8edccc1d3f.png)


sudo netplan apply

Windowsターミナルで以下のコマンドを打つ。
Get-NetNatSession

なにがしか表示が出れば正常に機能している。
出ないのであれば、設定がおかしい可能性がある。
自分は、Ubuntu側のデフォルトゲートウェイの設定をしていないことが原因でした。そりゃーインターネットにつながらんわ（笑）

あとはSSH接続もやりたいが今のところうまくいっていない
AtCoder ABC 322 B - Prefix and Suffix: Z-algo, ロリハ, 編集距離, Python rfindなどで解く

https://atcoder.jp/contests/abc322/tasks/abc322_b

文字列に慣れるためにいろいろな方法で解きます。

- 1: find, スライスで文字列比較
- 2: suffix検出をrfind
- 3: suffix検出をfindの開始位置指定
- 4: suffix検出をひっくり返してprefix判定にする
- 5: Z algorithm
- 6: ローリングハッシュ
- 7: 編集距離(レーベンシュタイン距離)

# 1: find, スライスで文字列比較
Pythonの`find()`は検出された最初の位置を返すので0か判定すればよいです。
Pythonの`str()[-k:]`は最後のk文字を取り出します。
答えの表示はboolでif分岐が妥当ですがじっと眺めると`0b11`をxorすればよいです。
```Python
n, m = map(int, input().split())
s, t = input(), input()
ans = 3
if t.find(s) == 0: ans ^= 0b10
if t[-n:] == s: ans ^= 0b01
print(ans)
```

https://atcoder.jp/contests/abc322/submissions/46142881

# 2: suffix検出をrfind
Pythonには`rfind`があり検出された最後の位置を返す関数があります。
```Python
if t.find(s) == 0: ans ^= 0b10
if t.rfind(s) == m-n: ans ^= 0b01
```

https://atcoder.jp/contests/abc322/submissions/46143793

# 3: suffix検出をfindの開始位置指定
Pythonのfindは`find(sub[, start[, end]])`の`start`で開始位置を指定できます。
```Python
if t.find(s) == 0: ans ^= 0b10
if t.find(s, m-n) != -1: ans ^= 0b01
```

https://atcoder.jp/contests/abc322/submissions/46143855


# 4: suffix検出をひっくり返してprefix判定にする
sがtのsuffixであるとは、sとtをひっくり返した状態でsがtのprefixであるか？と同値です。
Pythonでは`str[::-1]`で文字列をひっくり返せます。
```Python
if t.find(s) == 0: ans ^= 0b10
s = s[::-1]
t = t[::-1]
if t.find(s) == 0: ans ^= 0b01
```
https://atcoder.jp/contests/abc322/submissions/46142898

# 5: Z algorithm
Z algorithmは与えられた文字列におけるすべての位置$i$からの最長共通接頭辞長を$O(N)$で求められます。Z algorithmの典型としては文字列`abc`と`ababc`があるとき、出現しない文字列`_`などを用いて`abc_ababc`をつくりこれを入力とします。
```Python
z = zAlgorithm(s + "_" + t)
# t開始からの最長共通接頭辞長がn以上ならprefix
if z.res[n+1] >= n: ans ^= 0b10
# 最後からn文字前の最長共通接頭辞長がn以上ならsuffix
if z.res[n+1+m-n] >= n: ans ^= 0b01
```
https://atcoder.jp/contests/abc322/submissions/46143072

# 6: ローリングハッシュ
ローリングハッシュは`O(N)`の事前計算をしておくことである区間の文字列のハッシュを`O(1)`で求めることが出来ます。
```Python
rhs = rollingHash61(s)
rht = rollingHash61(t)
# それぞれ、[0,n)のhashが一緒ならprefix
if rhs.hash(0, n) == rht.hash(0, n): ans ^= 0b10
# ｓの[0,n)とtの[m-n, m)のhashが一緒ならsuffix
if rhs.hash(0, n) == rht.hash(m-n, m): ans ^= 0b01
```

https://atcoder.jp/contests/abc322/submissions/46143287

# 7: 編集距離(レーベンシュタイン距離)
二つの文字列を字の挿入/削除/置換で一方を他方に変形するための最小手順回数です。
prefixは`distance(s, t) == m-n`でいいように思えますが、`abc, aabc`のとき距離1になってしまうので"_"などの使われない文字をダミーで入れてやります。
```Python
if levenshtein_distance(s + "_"*(m-n), t) == m-n: ans ^= 0b10
if levenshtein_distance("_"*(m-n) + s, t) == m - n: ans ^= 0b01
```

https://atcoder.jp/contests/abc322/submissions/46143548
Qiita への記事投稿のおすすめのやり方

すでにやっている方にとっては意味のない記事ですが...


プログラムと実行結果を共に記事にするには，Jupyter Lab でプログラムを含む記事を書き，Save and Export Notebook As ... で Markdown を選び，出来上がった Markdown ファイルを Qiita の記事投稿ウインドウにコピペするのが最善。

見やすいし，プログラム片をPythonのREPLにコピペして（必要なら少し変更を加えて）実行してみることもできる。

以下は，つい最近投稿・公開された記事（プログラムと実行結果は画像として張り込まれている）を，上述のようなやり方で投稿するとどうなるかの実例である。

---

## 一次元配列の扱い

この部分に，解説記事を書く。

```python
import numpy as np
a = np.array([1, 2, 3])
a
```




    array([1, 2, 3])




```python
print(a)
```

    [1 2 3]



```python
type(a)
```




    numpy.ndarray




```python
a.shape
```




    (3,)



## 2次元配列の扱い

この部分に，解説記事を書く。


```python
b = np.array([[1, 2, 3], [4, 5, 6]])
b
```




    array([[1, 2, 3],
           [4, 5, 6]])




```python
b.shape
```




    (2, 3)



## 次元の変形

この部分に，解説記事を書く。


```python
c1 = np.array([0, 1, 2, 3, 4, 5])
c1
```




    array([0, 1, 2, 3, 4, 5])




```python
c2 = c1.reshape((2, 3))
c2
```




    array([[0, 1, 2],
           [3, 4, 5]])




```python
c3 = c2.ravel()
c3
```




    array([0, 1, 2, 3, 4, 5])




```python
c4 = c2.flatten()
c4
```




    array([0, 1, 2, 3, 4, 5])


どうですか？

Save and Export Notebook As ... では，MTHL, LaTeX, PDF その他も選べます。LaTeX や PDF を使えば，「本も書けます」
[C#] C#でGET/POST要求を実装する

## そもそもHTTPとは
　HttpとはHypertext Transfer Protocolの略で、直訳するとハイパーテキストを転送するプロトコルないしは規格となります。これでは何を言っているのかがさっぱりなので例を挙げましょう。
　世界中には英語を筆頭に7000近い数の言語が存在すると言われています。しかしながら、7000近い言語をすべて習得するなんてことは到底できません。そこで、違う国の人同士で話すときには英語といった世界的に普及している言葉を使うのです。
　インターネット上でも、要求をするときの書き方がいくつもあっては通信なんて到底できません。そこで、httpという規格が存在しているのです。

目次
[GET要求](#get要求)
[POST要求](#post要求)

## GET要求
　GET要求はWebサーバにデータを送るように要求するものです。以下のような内容になっています。
```
GET https://example.com/ HTTP/1.1
Host: example.com
...
```
　これは example.com に対してGET要求を送るというものです。その結果として以下のようなレスポンスが帰ってきます。二行目以降はメッセージヘッダといって自分の言語や環境などの追加の情報が記述されています。これによって、スマホ版とPC版のサイトどちらを送るべきか、日本語と英語どちらで送るべきかなどを判断しているわけなのです。
```
StatusCode        : 200
StatusDescription : OK
Content           : <!doctype html>
                    
                    ~~~webサイトのコンテンツ~~~

RawContent        : HTTP/1.1 200 OK
...
```
　StatusCodeが200,300番台だと成功してます。逆に400,500番台だとエラーが起きています。
普段私達はContentに当たる内容を見ているわけです。
　これをC#で実装するとこうなります。

```C#:GET.cs
using System.Net.Http;
//System.Net.Http名前空間のHttpClientを使う

string uri = "https://example.com"; //uriを入力

HttpClient httpclient = new HttpClient();

HttpResponseMessage response = await httpclient.GetAsync(uri); //uriにGET要求
string responsestr = await response.Content.ReadAsStringAsync(); //レスポンスのContentをstring型として読み込む
Console.WriteLine(responsestr);
```
```html:出力
<!doctype html>
<html>
<head>
    <title>Example Domain</title>
    ...
```
　ここでは、responseが返答でありそのうちのContentをstring型として読み込んで出力しています。
## POST要求
　POST要求も同様にサーバーにデータを送るように要求するものですが、大きな違いがあります。それは、パラメータをメッセージヘッダに含ませるので、送信するデータ量に制限がないということです。
　C#で記述するとこうなります。
```C#:POST.cs
using System.Net.Http;
//Post要求でもSystem.Net.Http名前空間のHttpClientを使う

string uri = ""; //Uriを設定
StringContent content = ""; //送信するContentを設定
HttpResponseMessage response = await httpClient.PostAsync(uri,content);
```
　StringContent以外にも送信可能なデータの型はあります。詳しくは、[HttpContent/Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.net.http.httpcontent?view=net-7.0)を参照してください
## 参考文献
HTTP要求の詳しい内容についてはこのサイトがとてもわかり易いです。
>[HTTPの仕組み(パケットキャプチャで学ぼう！)](https://zenn.dev/masahiro_toba/books/3daf7dc0d5dfd7)

C#でのGet,Post要求の詳細やその他のhttp要求についてはMicrosoftドキュメントを参考にしてください。
>[HttpClient クラスを使用して HTTP 要求を行う](https://learn.microsoft.com/ja-jp/dotnet/fundamentals/networking/http/httpclient)

　ネットワーク関係の知識に疎いのでご指摘等ございましたら、コメントにてご教授ください。
Officeファイルを通じたマクロの実行

# はじめに
マルウェア解析の勉強の一環としてword文書を通じたマクロの実行について勉強しました。
随時更新予定です。

# 免責事項
詳細は存じ上げませんが、マルウェア等のコンピュータウイルスおよびその製法を不特定多数に配布することや不特定多数がアクセスできる場所に置くことは法律で禁止されているそうです。この記事は直接的なマルウェアの製法を掲載しているわけではないので大丈夫だとは思いますが...

※この記事が原因で生じた損害について、著者は責任を負いかねますのでご注意ください。

# マクロの準備
Wordファイルの場合、開発 → Visual Basic でマクロ編集画面に行けます。

# マクロ簡易まとめ

## 特定のOfficeファイルを開く
```
Sub openDoc()
    Documents.Open FileName:=".\path\to\the\officefile.docx", ReadOnly:=True
End Sub
```
.\path\to\the\officefile.docxファイルがが開かれます。

## Officeファイルを開いたときに特定のコードを実行する
```
Private Sub Document_Open()
    runCode
End Sub
```
runCode()メソッドが実行されます。

# 実践

## あるOfficeファイルを開いたら別のOfficeファイルが勝手に開かれる
マクロ編集画面に進み、Prpject\Microsoft Word Objects\ThisDocument に以下を記入。
```
Private Sub Document_Open()
    openDoc
End Sub

Sub openDoc()
    Documents.Open FileName:=".\path\to\the\poo.docx", ReadOnly:=True
End Sub
```
hello.docmを開いたらpoo.docxが勝手に開かれる。例えばpoo.docxをマルウェア実行ファイルに置き換えて実行した場合、コンピュータにウイルスがばらまかれてしまう。


# 参考
https://stabucky.com/wp/archives/3416
【Android】「" "(ダブルコーテーション)」を文字列に入力したい【Kotlin】



# 「\」バックスラッシュを使う
「\」を「"」の前に入れることでダブルコーテーションを表示できる
```
"\"Hello, world!!!\""　　　// "Hello, world!!!"
```
# 三連引用符を使う
ダブルコーテーション三つで囲む方法でも表示できる
```
""""Hello, world!!!""""　　　// "Hello, world!!!"
```

# おわりに
stringリソースを使えばこんなことしなくても良いような気もしますが、どうしてもリソースを使わずにダブルコーテーションを表示したい場合があるかと思い記事にしました。
簡単にではありましたが、軽いメモ的な感覚で今回の記事としておきます。
【Unity】Entities + MVPパターンの開発を中止した話【雑記】

# 自己紹介
Unity DOTSでVRゲーム作ればまぁそれなりに戦えるだろうと思った大学生。
グラフィックがボトルネックになるのは待ったなしだし、言っちゃいけない。

# 前置き
そもそも、MVP(Model View Presenter)もECS(Entity Component System)もアーキテクチャの一種であるため共存させようとした私がおかしいという話は一旦置いておくとして、失敗談を話す過程でECSの設計上の特徴を話せればなと…

# MVP
MVPはロジックを純粋なC#に起こせることと、UniRxなどを用いれば階層をいい感じに分けれることを特徴としているアーキテクチャ。
保守性に軸を置いている。
階層上の奴。

今回は、UniRxとVContainerを採用して構築していた。

# ECS
ECSはデータ指向のアーキテクチャ。
保守性というよりもメモリを中心としたパフォーマンスを軸においている。

# 合体！
これらを合体すればいいじゃない。保守性も、パフォーマンスも！！

結論から言えば、まぁ無理。

# アーキテクチャが破綻する理由
ここからはアーキテクチャが破綻する理由を書いていく
## MonoBehaviour（≠ECS）とECSの違い
ここが一番大きいが、MonoBehaviourは表に出るもの、MVPでいうとViewの部分のみを司る。
しかし、ECSではデータ（Entity）の管理もUnityが担うことになる。

つまり、MVPのModelの部分もUnityが持ってしまう状況になります。
これはPresenterの役割が全く持ってなくなることを意味します。
ModelとViewが同じ場所にあるのですから…

### それでも破綻していないのでは？
私がMVPアーキテクチャを学ぶときに読ませていただいた以下の記事には

Unity依存レイアと非Unity依存レイアを分けることは言っていない。
過度な期待だ

という用に記されています。

https://qiita.com/toRisouP/items/5365936fc14c7e7eabf9

つまり、Unityが担うことになってもMVPパターンは破綻しないということです。
確かに、これはそうだと思います。
MonoBehaviourの場合でもどうしてもUnityが提供するクラスは使うことになります。

しかし、例えばModelにロジックを司るSystemを持たせ、Viewに表示などを任せるSystemを置くということをするとPresenterが全くの意味をなしません。
つまり、MVPアーキテクチャが破綻するということになります。

# そもそもECSアーキテクチャとは本当にMonoBehaviourの代わりなのか
この記事を書いている途中に思ったのですが、これECSとMonoBehaviourの役割が明確に違うため発生している問題のなのではないかと思いました。
実態は、ECSはあくまでMVPにおけるModelでありViewではないということです。

これを前提とするとViewはUI関連のMonoBehaviourだけを格納する層となります。

## この前提はいつまで続くか
これは今ECSがアニメーションやパーティクルなどを管理できないから成り立つ前提な気がします。
アップデートでこれらが追加されればMVPでは分けれなくなります。

どういった形で追加されるかも気になるところですが、実際ECSの設計でSystemをView系とModel系で分けるのは普通に良さそうですよね。

# まとめ
結論は、ECSはECS層とMonoBehaviourのUI層で分けるのが良さそうということでした。

# さいごに
まぁ、アーキテクチャを勉強し始めてまだまだ浅いためこの結論が間違っている可能性は十分あります。
ただMVPパターンの限界をちょっと垣間見れたような気がしてよかったです。

ちなみに、今回ECSでVRゲームを作ろうと思って少し触っていました。
1~2ヶ月を目途にサクッとなにかを作ろうと思っているのでまたお楽しみに。

https://github.com/AtsuAtsu0120/VR-ECS_MVP

リポジトリでは、VContainerのECSを使っていたりしますよ…！
階層化アーキテクチャを知っていますか？

## 階層化アーキテクチャ
**階層化アーキテクチャ**は、ソフトウェアを役割と責任に応じて複数の論理的な水平階層に構造化するソフトウェアアーキテクチャです。 これは最も一般的に使われるアーキテクチャの 1 つであり、構造化された階層数に応じて**N層アーキテクチャ**とも呼ばれます。

## 階層の個数
階層数に定められた規則はなく、必要に応じてソフトウェアを複数の階層に構造化して設計することができます。 一般的には以下のように4階層に分けることが多いです。

**例)4階層**
![Screen Shot 2023-10-01 at 20.15.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3529304/96e63940-4ea3-7e7f-8581-3018e1f4ff35.png)

- **プレゼンテーション階層(Presentation Layer)**:ユーザーインターフェースと関連したことをします。
- **ビジネス階層(Business Layer)**:実際のビジネスロジックを処理します。
- **パーシスタンス階層(Persistence Layer)**:データを保存して読み込む作業を担当します。
- **データベース階層(Database Layer)**: 実際にデータを保存するデータベースです。

単純なソフトウェアであれば3階層、複雑であれば5階層に分けることも可能です。各階層をどのように設計するかは、ほとんど設計者やチームの決定次第なので、正解はありません。 あなたならどのように設計しますか？


## 階層の隔離
各階層が単一の責任を持つことは重要です。各階層は他の階層の内部動作を知る必要はなく、事前に定義されたインターフェース（または契約）を通じて相互作用します。もちろん、一つの階層が直接、関連性のない他の階層と相互作用することも可能です。しかし、その場合にはいくつかの問題が発生する可能性があります。どのような問題が発生すると思いますか？

1. **依存性**の増加します。一つの階層が複数の他の階層と直接相互作用すると、それらの階層間の依存性が高まります。これによって、一つの変更が他の多くの階層に影響を与え、システム全体が不安定になる可能性があります。
2. **拡張性**と**保守性**の低下されます。高い依存性は、新しい機能を追加したり既存の機能を修正する際に、その影響を完全に理解することが難しくなります。この結果、システムの拡張性と保守性が低下する可能性があります。

![Screen Shot 2023-10-01 at 20.39.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3529304/e68ce05e-69b3-3c32-d043-f95c6603a8e6.png)

## アンチパターン
階層化アーキテクチャを実装する際に注意する必要があるアンチパターンがいくつかあります。 
- **太った階層(Fat Layer)**: ある層があまりにも多くの責任を負うと、その層は「太く」なり、複雑になる。これにより、メンテナンスが難しくなる可能性があります。
- **漏れのある抽象化(Leaky Abstraction)**: 下位階層の詳細が上位階層に公開されると、階層間の独立性が崩れます。
- **シンクホール・アンチパターン(Sinkhole Anti-Pattern)**: 複数の階層を通過しなければならないのに、各階層で実際に何もせずに単に次の階層にデータを渡すだけであれば、これは不必要な複雑さとパフォーマンスの低下をもたらす可能性があります。

## 結論
ソフトウェアアーキテクチャは、どのようなアプリケーションを開発するにも重要な部分です。階層化されたアーキテクチャは、小規模でシンプルなプロジェクトには効果的ですが、アプリケーションの規模と複雑さが増すにつれて限界が現れます。 より複雑なプロジェクトではマイクロサービス、イベントベースのアーキテクチャなど、より柔軟なパターンを検討することをお勧めします。
野良ExcelVBA、野良RPAは要らない。

**結論からはっきり言ってしまうと、社内SEとして野良ExcelVBAはまだしも、野良RPAなんて悪夢でしかないんですよ。**

ボトムアップで導入される時はこんな感じ。

現場サイドの偉い人の鶴の一声で導入が決まり、
現場の原価管理とかしてるExcelチョットクワシイ人が四苦八苦しながらRPAをいじる。
もちろん、生産性は社内SEや外注より極めて低いし、本業をおろそかにできないから残業時間でRPAの勉強や制作を行う。
ライセンス費用を上回る節約効果を生むことができるかも怪しいし、
設計書もない。設計書を書くという発想すらない。
仮にライセンス費用をペイできても、残るのは誰も管理していない、トラブルが起きても責任が取れない野良ロボットの群れ。
それが万が一トラブルを起こしたら社内情シスに丸投げ。

じゃあトップダウンで入れるとどうなるかというと、外注するか、社内情シスが作るかになる。
トップダウンで入れた時に現場の人がRPAを作ることができるようになるなんてことはない。
じゃあそれって、高いライセンス費用払ってRPAでやらないといけないことなんですかね？
社内情シスが普段の改善に使っている言語でプログラム書いてやれば終わる話じゃないんですか？
仕様書しっかり作って外注したらRPAのライセンス料なしで片付くんじゃないんですか？

**RPAじゃないとできないこと**って物凄く範囲狭いし、簡単にノーコーディングで作れると言っておきながら多少複雑なことやろうとすると変数とかループとかプログラミング知識が必要になるし、
はっきり言って流行りに乗って猫も杓子もRPAだとしか思えないんですよね。

大企業がRPAで〇〇万円浮かせたって話は、RPA専用の人員を確保・教育できる体力と、未だに紙でやってる作業が大量にある（企業の規模が大きいほど、そういった作業も多い）ことによるものであって、
中小企業が我も乗り遅れるなと言う形でRPAを入れても何もいいことないんですよ。

それでもRPAを入れたいというのなら無料のPower Automateを使ってから言ってほしい。
Power Automateも管理・製作できない人にUipathやWinActorの面倒が見れるわけがない。

野良ExcelVBAも結局作った人が面倒みきれなくなって情シスに丸投げ。
情シスは別に門戸を閉ざしてるわけでもないし、頼まれれば作るのに、どうして……

で、結局そういう「ITわからないけどバズワードは好きだからAIとかRPAとか入れたがる上位者」を止めるためには、社内情シスにも政治力が要るんですよ。
単にITの専門家であるだけでは全体最適を実現できない。全く実現できない。
各部門が自分たちはあれしたい、これが使いたいと言い出すのを制御するだけの力が必要。
それは単に規則に「ITに関することは情シス所管とする」と書いてあるだけでは全く不十分で、
各部門からそういった話が内々で上がってくるようになっていて、それを止められるぐらいの力が要る。
それは一個人ではどうしようもなくて、情シスの部門長やさらにその上司(役員・部長クラス）が社内政治をやってくれないと、社内SE一人じゃ何もできないんですよね。
わかりきった破滅を、自分の仕事をしながら眺める羽目になる。

力が……力がほしい……

追記
**そもそもRPAで画面操作を自動化するというアプローチが間違いなんですよ。プログラムどうしでデータをやり取りして自動で処理すれば画面操作自体がいらない。そこをまずは目指すべきです。**
Rails7 link_toで"turbo-method: :post"を設定しているのにGETのHTTPメソッドとして認識される


## 環境情報＆前提条件
* Ruby 3.2.1
* Rails 7.0.0

## 解決したい内容
* 以下のように`turbo-method: :post`を設定している`link_to`タグにおいて`[POST]`メソッドではなく、`[GET]`メソッド扱いされる。

```erb
<%= link_to hoge_path(hoge), data: { turbo: false, "turbo-method": :post } %>
```

```
<!-- 画面に表示されるエラー -->
Routing Error
No route matches [GET] "/hoge"
```

## 解決手順
* `turbo: false`と`"turbo-method": :post`は共存できない。`turbo: false`の記述を削除して、以下のようにすることで解決。
```erb
<%= link_to hoge_path(hoge), data: { "turbo-method": :post } %>
```

## 補足
* link_toはデフォルトでは`[GET]`メソッド扱いされるので、`turbo: false`をつけている場合は他のプロパティに関わらずデフォルトの`[GET]`扱いとなるようである。
* turboによるトラブルを防ぐために、`turbo: false`のようなturboを無効化する処置を取るケースも多いと考えられる。
* 今回は`turbo: false`を消さなければうまく動作しないケースで、筆者は少しハマってしまったので記事化しました。
しょーもない話を意識高い系の言葉にしたらいいかんじの話になるんじゃね？って話

# はじめに
今日はしょーもない話をChatGPTで意識高い系の言葉にして見たら
おもろくなるんじゃね？って思ったのでやっていきます。
# お話
```
本読みながら階段降りたら、足掛けられてこけた。
```
(実話です。)
これを早速意識高い系の言葉にしていきます。
# 結果
```
書籍に没頭しながら、階段を下降中、私は足元に忍び寄る隠れた障害物により、転落を経験しました。
```
```
文献の研究に没頭中、階段を降りる過程で、足元におけるインフラクションに見舞われ、アクシデンタルなフロア・インシデントが生じました。
```
```
リーディングセッション中、ステアリングダウン・ザ・ステップの過程において、エビデンスが足元に潜むヒンドランスとなり、私はエクスペリエンスの高度なステージからのアンプラネッド・フォールを経験しました。
```
こんな感じになった。
どんどん外来語が出てきて面白かった。

今日は以上です。
それじゃ
LLMのための環境構築_PyTorch導入偏

前回からの続きになります。

https://qiita.com/umauma-hagihagi/items/130804c14ba09f716bfd

# やりたいこと

- フレームワークとしてpytorch入れる
- なんらかのLLMを入れて会話する

# PyTorchのインストール

こちらの記事の「PyTorchのDockerイメージのインストール」から開始します。

https://blog.shikoan.com/wsl2-ndivid-docker-pytorch/

コマンドプロンプトで ```wsl``` と叩くとwsl2から前回インストールしたUbuntuが起動します。

Ubuntu上から、
記載にあるコマンド ```docker run --gpus all -it --rm --shm-size=8g nvcr.io/nvidia/pytorch:XX.XX-py3``` を叩くとインストールが実行されました。
(XX.XX-py3のところに何を入れるかは記事参照のこと）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565603/7b01c5c5-42a1-1695-cbc6-1964b442405e.png)

ついでにDocker Desktopの方も確認。
imageが追加されていました。今後はこのイメージを実行すれば良さそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565603/d54e2a0b-981a-fb4c-b31f-4d0a552cfeaf.png)

pytorchからGPU認識できているのか確認のため、
Ubuntu上から ```python``` コマンドを実行してpython起動します。
(実はpythonをこんな風に起動するのはじめてなのでドキドキ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565603/6d73eee0-b249-a123-811b-251f90ef15f0.png)

そのあとは```import torch```実行してから```print(torch.cuda.is_available())```実行。
今回はTrueと出力されたので認識はされていると判断し、続いて```print(torch.cuda.device_count())```を実行。
1と出てます。数が合ってるので大丈夫そう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3565603/958eb60b-bdc6-7644-0824-efa64330fff9.png)

今回はここまで。
次回はいよいよLLM導入です。
Rails7 Bootstrap slimのfile_fieldの見た目をアイコンにする


This article shows how to change the appearance of slim's file_field to Bootstrap icon.

## 環境情報＆前提条件
* Ruby 3.2.1
* Rails 7.0.0
* Bootstrap 5.2.3
* テンプレートエンジンとしてslimを使用

## 解決したい内容
* formタグの中のfile_fieldの見た目を以下の①通常の見た目でなく、②アイコンの見た目にしたい

①通常の見た目
![通常のfile_field.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490253/67393a91-ff95-26cc-0d80-425007138bfd.png)

②アイコンの見た目（画像左のimageアイコンをクリックして画像添付としたい）
![iconに変更したfile_field.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490253/2ce9b0ff-ba0f-0a45-a402-95a20a246265.png)

* Userモデルのimageカラムに画像を添付するケースを想定する。

## 解決手順
* 以下のようにlabelの中にfile_fieldを内包させる。
* file_field自体は`style: "display: none"`して非表示にする。
* ※アイコン部分には`style="cursor: pointer"`を設定して、アイコンにホバーした際にポインタとなるようにしています。必須ではありません。

```slim
<!-- パスやモデルは適宜置換してください。 -->
= form_with url: users_path, model: User.new, data: { turbo: false }, locale: true do |f|
  = f.label :image
    i.fs-3.bi-image.text-primary[style="cursor: pointer"]
    = f.file_field :image, style: "display: none"
```
【Rails】devise-token-authを用いたユーザー登録機能の作成

devise_token_authを用いたログイン機能の実装を備忘録として残します。

・Gemファイルの準備/インストール
--
Gemfileにdevise_token_authをGemに準備する。
以下の内容をGemfileに記載し、`bundle install`を行う。

```ruby:Gemfile
gem "rack-cors"
gem 'devise'
gem 'devise_token_auth'
gem 'devise-i18n'
```

下記の２つのコマンドを実行することで、devise_token_authをインストールする。
```
rails g devise:install
rails g devise_token_auth:install User auth
```

・ログイン機能の実装
--
まずは認証のコントローラーを準備するため、下記のコマンドを実行する。
```
rails g controller api/v1/registrations
```
できたコントローラーファイルを下記のように修正する。
classを設定することで、DeviseTokenAuth::RegistrationsControllerを引き継ぐ設定ができる。

```ruby:api/v1/auth/registrations_controller.rb
class Api::V1::Auth::RegistrationsController < DeviseTokenAuth::RegistrationsController
  private

    def sign_up_params
      params.permit(:name, :email, :password, :password_confirmation)
    end
    
    def account_update_params
      params.permit(:name, :email)
    end
end
```
`sign_up_params`　⇒　サインアップ（新規登録）の際に必要とするデータ
`account_update_params`　⇒　登録情報の更新をすることができるデータ

・ルーティングを修正
--
api/v1/authでコントローラーを作成したため、ルーティングは下記のように書き換える。

```ruby:config/routes.rb
Rails.application.routes.draw do
  root to: "home#index"

  namespace :api do
    namespace :v1 do
      mount_devise_token_auth_for "User", at: "auth", controllers: {
        registrations: "api/v1/auth/registrations"
      }
      resources :articles
    end
  end
end
```
`namespace`は名前空間の中にルーティングをグループ化するときに使われる。
これにより、関連ルートが視覚的にグループ化され、コードの整理や保守がしやすくなる。
`mount_devise_token_auth_for 'User', at: 'auth'`とは
サインインやサインアウトなどの認証ができるようにするために書く必要がある。

・devise_token_auth.rbを修正
--
```ruby:config/initializers/devise_token_auth.rb
DeviseTokenAuth.setup do |config|
#中略
config.change_headers_on_each_request = false
#元々はtrueのものをfalseに書き換える
#リクエストの度に毎回ヘッダーの情報が変わらないようにしている

#中略
config.token_lifespan = 2.weeks
#トークンが有効な期間を2週間としている

#中略
config.headers_names = { 'access-token': "access-token",
                           client: "client",
                           expiry: "expiry",
                           uid: "uid",
                           'token-type': "token-type" }
#ヘッダーに書き込まれる情報を上記のように修正している。
```

以上で、ユーザー登録機能が実装できています。
Postman等で処理が通るか確認してください。
makoto-developer流 macOSでの開発スタイルを公開(🚧 作成・編集中)


# 入れるツール

Summary

- Jetbrains Toolbox
- AltTab
- Clipy
- Rectangle
- Karabiner-Element
- iterm
- Docker Desktop or Rancher Desktop

# macOSのウィンドウを手懐ける

- Rectangle
- AltTab

# ひとつの画面で開発

- 外部ディスプレイを使用している
  - Setting -> Display -> ミラーリングで設定
- ワークスペースを使わない
  - AltTabでウィンドウを全て表示させる
  - 画面が増えてきたら`Ctrl + Space`でアプリをスイッチ
- Chrome
    - タブグループ機能を使う
    - ショートカット機能を使って、専用アプリ化する

# エディタ

- Vimをマスターする
- Neovimを入れてプラグインでIDEとしてvimを使う
- Intellij製品も併用する
- Clipy
  - Snippet
- キーボード
  - ErgoDox EZ
    - レイヤーを活用する
  - US配列
- Karabiner-Element

# vimの設定をカスタマイズする

# Terminal

- iterm2(iterm2以外も良い。macOSに最初から入っているterminalは使うな。)
- Font
  - Hackgen
- シェル
    - Fish Shell
    - zsh
- dotfilesリポジトリを持つ

# ブックマーク検索

- Vimuimを入れる
    - Vimuimを入れればブックマーク検索アプリとか入れなくてもなんとかなる
    - bでBookmark検索(Bで新しいタブで開く)
    - oでtab, Historyを開く(Oで新しいタブで開く)
- chromeの昨日だけで検索もできる
![スクリーンショット 2023-11-22 9.51.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684725/8398061c-bb4b-0ca2-b82c-e971b858e437.png)

# Task

- Github Project
- リポジトリの`Readme.md`にFeatureを書く
- Notion

# バックアップする

- ソースコードはGithub/GitLabに入れる
- Google Drive/One Drive/Dropbox/Box Sync等を使う

# Setupの手順をGithubで管理

- pc-setupと言うリポジトリを持っておく

# macOSのデフォルトの設定を変更

- キーの反応を速くする
- caps lockは使わないのでctrlに変更
- 右command/右optionは使わないので英語/日本語モードの切り替えに変えてしまう(USキーボードの人向け)
- 

# 開発編

- テンプレートアプリを用意する
Goのfor文で詰まった話

# 詰まった点
user.NameでUser構造体のNameフィールドを書き換えているのに変更が正しく反映されない...
## ソースコード
```go
package main

import "fmt"

type User struct {
	Name string
	Age  int
}

func main() {
	users := [3]User{
		{"Alice", 25},
		{"Bob", 30},
		{"Charlie", 22},
	}

	for _, user := range users {
		user.Name = "太郎"
	}

	for _, user := range users {
		fmt.Println(user.Name)
	}
}
```

## 実行結果
```
Alice
Bob
Charlie
```


# 原因
定義された`User構造体の配列の要素`とfor文で回されている`User構造体の配列の要素`は実はメモリ内ではそれぞれ別の場所に保持されている、つまり全く別の変数として扱われているため


#  対処法
```go
for i := range users {
    users[i].Name = "太郎"
}
```
Rails7 Heroku OmniauthのGitHub認証の際に"OmniAuth::Strategies::OAuth2::CallbackError, redirect_uri_mismatch"エラーが発生する


This article shows how to resolve CallbackError when GitHub authorizing at Heroku.

## 環境情報＆前提条件
* Ruby 3.2.1
* Rails 7.0.0
* 開発環境
  * Docker使用
* 本番環境
  * Herokuにデプロイ(stack heroku-22)

### gem
* devise 4.9.2
* omniauth 2.1.1
* omniauth-github 2.0.1

## 解決したい内容
* ローカルの開発環境でOmniauthによるGitHub認証ができることを確認済み。
* Heroku本番環境においてGitHub認証を実行すると、以下エラーが発生してGitHub認証に失敗する。
  * `"OmniAuth::Strategies::OAuth2::CallbackError, redirect_uri_mismatch"`

## 解決手順
* `config/initializers/devise.rb`に以下を追記する。`https://hoge.herokuapp.com`の部分はご自身のアプリのURLに置換してください。
```rb:config/initializers/devise.rb
OmniAuth.config.full_host = 'https://hoge.herokuapp.com' if Rails.env.production?
```

## 参考記事
* 以下の記事を大いに参考にさせていただきました。
  * [ログインエラーが出ていたのを直した](ttps://oknm.jp/posts/5bb62bf1979c14551a6d99974483dbf2c00e933b)
  * [OmniAuth: ロードバランサ配下での redirect_uri_mismatch 問題](https://tkrd.hatenablog.com/entry/2017/07/17/140641)
Swift 4. SwiftPlayGround 

# 4. プレイグラウンド


## 4.6 複数のプレイグラウンドページで作業する

![代替](https://i.imgur.com/morX0aM.png)

## 4.7 Swift UI + ライブビュー

![代替](https://i.imgur.com/ekFQXAI.png)

## 4.8 PlayGround 要約

その結果を動的に見ることができる環境提供、
生成されたコードを**編集コンパイルおよび実行せずに**
IOS SDKに含まれた多くのクラスおよびAPIを実験し、言語の勉強に役立つ
【Shell Script】ディレクトリが存在するかを判定

# はじめに
ShellScriptでディレクトリが存在するかどうかを判定する条件式のやり方を調べたので記録しておきます。

# 実装
```shell
if [ -d ./sample/ ]; then
    echo "ディレクトリが存在します"
else
    echo "ディレクトリが存在しません"
fi
```

# おわり
最近XcodeCloudなどでshellをよく触るので勉強になります
SalesforceのLightningページでレポートを表示させよう～レポート表示方法

こんにちは
本橋です

今回はLightningページでレポート表示方法を説明します

例えば「取引先が関連する活動」レポートを事前に作成し
そのレポートえを取引先Lighningページに設定すれば、その取引先で何件活動があるかを視覚化できます

たとえばこの取引先では活動が３件あることが分かります。
![行動確認１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/16a86d57-9984-6d91-2d99-f2e30b476a51.png)

また、このレポートコンポーネントの「レポートを表示」をクリックすればこの取引先の活動一覧が表示されます

行動の説明（コメント）のような長文を一覧で見ることができるので便利ですね
![Aレポート１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/c5958735-67b9-4085-1620-9beccdc043c6.png)


では、こちらの方法を説明します


# ステップ１　レポート作成
まずは表示させるレポートを作成します
今回はレポートタイプ「取引先が関連する活動」　でレポート作成します
後程取引先Lightningページで表示させるために以下の設定に気を付けましょう

1 グループ化で「取引先ID」でグループ化
2 グラフを表示しましょう
3 保存時のレポートタイプは「非公開レポート」ではないレポートタイプで設定しましょう

![Aレポート２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/1aa2240f-8a27-7471-028d-92c9803a2cf2.png)


# ステップ２　Lightningページを編集しましょう

次にステップ１で作成したレポートを表示させるためにLightningページを編集しましょう

・レポートグラフ　コンポーネントを表示させます
・レポートはステップ１で作成したレポート名を入力
・フィルタ条件は「取引先ID」で設定
![Cレポート２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/454ce65a-7437-4f95-93e0-d49b25d19e75.png)


なお、この設定がうまくいかない場合、先ほどのステップ１でこちら３点確認しましょう

1 グループ化で「取引先ID」でグループ化
2 グラフを表示しましょう
3 保存時のレポートタイプは「非公開レポート」ではないレポートタイプで設定しましょう
Xserver共有レンタルサーバのSSHで好きなバージョンのPHPを使う

せこい貧乏人なので、相変わらず共有サーバを使ってます

Xserverの共有レンタルサーバはドメインごとにPHPのバージョンを設定できるが、SSHでログインした場合、デフォルトで5系の最新が動くように設定されており、色々と困る。
```
$ composer upgrade
Loading composer repositories with package information
Warning from https://repo.packagist.org: Support for Composer 1 is deprecated and some packages will not be available. You should upgrade to Composer 2. See https://blog.packagist.com/deprecating-composer-1-support/
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - twig/twig v2.9.0 requires php ^7.0 -> your PHP version (5.4.16) does not satisfy that requirement.
    - twig/twig v2.8.1 requires php ^7.0 -> your PHP version (5.4.16) does not satisfy that requirement.
    - twig/twig v2.8.0 requires php ^7.0 -> your PHP version (5.4.16) does not satisfy that requirement.
    - twig/twig v2.7.4 requires php ^7.0 -> your PHP version (5.4.16) does not satisfy that requirement.
```

今までずっと良くわかってなくて詰んでたんだけど

```
$ ps ux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
himitsu+ 122225  0.0  0.0  72336  4692 ?        Ss   19:35   0:00 /usr/libexec/openssh/sftp-server
himitsu+ 214016  0.0  0.0 115748  3832 pts/0    Ss   19:43   0:00 -bash
himitsu+ 290866  0.0  0.0 525952 87192 ?        S    19:58   0:02 /usr/bin/php-fcgi7.0
```

なんか　/usr/bin/php-fcgi7.0　とかいうやつがあるな

```
$ php-fcgi7.0 composer.phar
X-Powered-By: PHP/7.0.33
Content-type: text/html; charset=UTF-8
Warning: Composer should be invoked via the CLI version of PHP, not the cgi-fcgi SAPI
```

なんかおこられた

```
$ find /usr/bin/php*
/usr/bin/php
/usr/bin/php-cgi
/usr/bin/php-fcgi4.4
/usr/bin/php-fcgi5.1
/usr/bin/php-fcgi5.2
/usr/bin/php-fcgi5.3
/usr/bin/php-fcgi5.4
/usr/bin/php-fcgi5.5
/usr/bin/php-fcgi5.6
/usr/bin/php-fcgi7.0
/usr/bin/php-fcgi7.1
/usr/bin/php-fcgi7.2
/usr/bin/php-fcgi7.3
/usr/bin/php-fcgi7.4
/usr/bin/php-fcgi8.0
/usr/bin/php-fcgi8.1
/usr/bin/php-fcgi8.2
/usr/bin/php4.4
/usr/bin/php5.1
/usr/bin/php5.2
/usr/bin/php5.3
/usr/bin/php5.4
/usr/bin/php5.5
/usr/bin/php5.6
/usr/bin/php5.6-cgi
/usr/bin/php7.0
/usr/bin/php7.0-cgi
/usr/bin/php7.1
/usr/bin/php7.1-cgi
/usr/bin/php7.2
/usr/bin/php7.2-cgi
/usr/bin/php7.3
/usr/bin/php7.3-cgi
/usr/bin/php7.4
/usr/bin/php7.4-cgi
/usr/bin/php8.0
/usr/bin/php8.0-cgi
/usr/bin/php8.1
/usr/bin/php8.1-cgi
/usr/bin/php8.2
/usr/bin/php8.2-cgi
/usr/bin/phpize
```

なんや、揃ってるやん

```
$ php7.4 composer.phar
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 1.6.2 2018-01-05 15:28:41
```
Eclipseのgitのsquashで複数commitをまとめる

# 環境
Eclipse 4.26.0

# 手順
Historyビュー -> 対象のcommitを選択 -> 右クリック -> modify -> squashをする。以下例では```commit1```, ```commit2```, ```commit3```の三つを選択している。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31934/3cba1b07-22fe-52b6-aebe-80efd281595b.png)

squashを押すとコミットメッセージの編集画面が開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31934/428428df-44e9-090c-6cda-1893905f92fd.png)

適当にメッセージを編集してReword

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31934/e3c089e9-725a-1ec0-468c-5e5820518d51.png)

正常に終了すると下記のように一つのコミットにまとめられる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31934/049cfef3-cd1b-41d2-a737-4e1ff6f4e866.png)
社内SE、ラズパイ4を買う。パート4（ゴミ出しの日にライン通知する）

・前回

https://qiita.com/ra_risu/items/1afe8dd832e328c7429f

・今回やること
①ゴミ出しの日の前日にライン通知をする。
（住んでるところのゴミ出しルールが前日に出して、早朝に回収のため）

・解決したい課題
ゴミ出しの日を忘れがち
夏場は特に、出し忘れは不衛生に直結するのでよくない。
もう夏終わるじゃんという突っ込みは受け付けないものとする。

・技術的なポイント
①第〇水曜などがあるため、単純な曜日判定以外の処理が必要。
②Line連携が必要
③初めてPythonを書くためPythonに慣れる必要がある。

・主文

ライン連携については以下の記事を、

https://qiita.com/hiratarich/items/6594bc876a514f5d2a03

曜日判定については以下の記事をそれぞれ参考にさせていただいた。

https://qiita.com/sugo/items/420134139948f2881a2c

あとはこの二つのプログラムを組み合わせて、
自分の住む場所に合わせてIF文でメッセージだけ変更するだけなのだが、
Pythonの摩訶不思議な仕様（変数宣言なし、インデントでIFやWhileの範囲を決める、インデントの外で変数が使えない、代入は＝なのに比較演算子は==、andやorは小文字じゃないと認識してくれない等）に翻弄されながら簡単なIF文に1，2時間ぐらいかけてしまった。
最終的にできたものが以下。
なお、ゴミ出しの曜日は居住地バレの典型例であり、IF文の条件を隠しているのはご容赦いただきたい。

**gomidasi.py**

    def LinePrint(msg):
    import datetime
    
    today = datetime.datetime.now()
    
    day = today.day
    wd = today.weekday()

    #1週間前の日付が同月かどうか調べる -> 1日より前か後かで判別
    #dayが1日以降(同月)なら出現回数+1してdayに1週間前の日付を代入(-7する)、1日より前(前の月の日付)なら処理終了
    weeks = 0
    while day > 0:
        weeks += 1
        day -= 7

    if (wd == (燃えるごみの曜日)) or (wd == 燃えるごみの曜日):
        message = "明日は燃えるごみの日です。"
    elif (weeks ==(第〇週)) and (wd ==  (不燃ごみの曜日)):
        message = "明日は不燃ごみの日です。乾電池も捨てられます。"
    elif (wd == (第〇週)) and ((weeks == (第×週)) or (weeks == （紙資源ごみの曜日）)):
        message = "明日は紙資源ごみの日です。段ボールや雑誌を捨てる際は縛って出してください"
    elif (wd == (第□週)) and ((weeks == (第△週)) or (weeks == （プラスチック資源ごみの曜日）)):
        message = "明日はプラスチック資源ごみの日です。ペットボトルやプラスチックなどが出せます"
    else:
        message  = "今日はゴミの日ではありません"
    
    print(message)

    import requests
    token='（LINE Notifyのトークン）'
    headers = {'Authorization': 'Bearer %s'%token, }        
    files = {'message': (None, message),}
    r = requests.post('https://notify-api.line.me/api/notify', headers=headers, files=files)
    print(r,r.text)
    
    LinePrint('')

あとはこれをRaspberry Pi側でCronで定時実行してやればよい。
chmod 777 /home/(ユーザ名)/gomidasi.py
00 18 * *  * sudo /usr/bin/python3 /home/(ユーザ名)/gomidasi.py
※毎日18時に通知

・感想
前回よりすごく簡単だった。ほとんどPython上で完結したし。
使いやすい連携機能を用意してくれているLineにも感謝したい。

・次やりたいこと
たぶんCO2検知をやる。
Pythonで「月」「日」が分かれているExcel上のデータを「月日」に連結する時のメモ

# やりたいこと

Excelで上のようになっているデータを、PandasのDataframeで下のようにしたいんです。

```
              月      日
0    2018-10-01        1
1    2018-10-01        2
2    2018-10-01        3
3    2018-10-01        4
4    2018-10-01        5
```
```
            月日
0    2018-10-01
1    2018-10-02
2    2018-10-03
3    2018-10-04
4    2018-10-05
```

# ソリューション

ChatGPTさんとBingチャットさんの両方に聞きましたが、個人的にはChatGPTさんの方がしっくりくる答えをくれるような気がしました。

```Python:Sample.py

import pandas as pd

# 最初のDataFrame
data1 = {'月': ['2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01', '2018-10-01'],
         '日': [1, 2, 3, 4, 5]}
df1 = pd.DataFrame(data1)

# '月' 列をDatetime型に変換
df1['月'] = pd.to_datetime(df1['月'])

# 変換後のDataFrame
df2 = df1.copy()
df2['月日'] = df2['月'].dt.strftime('%Y-%m-') + df2['日'].astype(str).str.zfill(2)
df2.drop(columns=['月', '日'], inplace=True)

print(df2)
```

この書き方で動くのか？という感じでしたが、ちゃんと動くんですね。Pythonすごい。

（おわり）
リクエストされたページが見つかりませんでした。今日のfacebook error

facebookで表示がでた。

>ブラウザーをアップデートする必要があります
エンドツーエンド暗号化されたチャットは、このブラウザーでは表示されません。ブラウザーをアップデートするか、別のブラウザーを使用してください。
詳しくはこちら

「こちら」に飛んでみた。

https://www.facebook.com/help/597429858389632/


>MessengerとFacebookのエンドツーエンド暗号化に対応しているブラウザーを使用する
...
チャットがエンドツーエンド暗号化されているかどうかを確認する。チャットがエンドツーエンド暗号化されている場合、チャットリストでそのチャットの横に鍵lockが表示されます。鍵が表示されていない場合、チャットはまだエンドツーエンドで暗号化されていません。暗号化されると通知が届きます。

「エンドツーエンドで暗号化」をクリック。

>エンドツーエンド暗号化チャットを管理する
Messengerでエンドツーエンド暗号化チャットまたはエンドツーエンド暗号化通話を開始する
Messengerのエンドツーエンド暗号化チャットでメッセージが消えるように設定する
Messengerのエンドツーエンド暗号化チャットでキーをチェックする
エンドツーエンド暗号化チャットを利用できるログイン済みのデバイスを確認する
Messengerのエンドツーエンド暗号化チャットからデバイスを削除する
Messengerのエンドツーエンド暗号化チャットを報告する

そしてたどり着いたのが、

≫リクエストされたページが見つかりませんでした。

お疲れ様でした。facebook and/or messenger のバグ取りに付き合いました。
# 文書履歴(document history)
ver. 0.10 初稿 20231001
### 最後までおよみいただきありがとうございました。
いいね　💚、フォローをお願いします。
#### Thank you very much for reading to the last sentence.
Please press the like icon 💚　and follow me for your happy life.
Rails7 リファクタリング 複数Modelで共通した処理をModuleに切り出す


This article shows how to reafactor common methods across multiple models.

## 環境情報＆前提条件
* Ruby 3.2.1
* Rails 7.0.0

## 実装したい内容
* 以下の記事の続きです。
  * [Rails7 リファクタリング ポリモーフィックな参照を持つレコードの新規作成を共通化する](https://qiita.com/KOH6/items/68287399989bfcd9258d)
* ポリモーフィック関連の共通処理を例として記載しますが、複数モデルに同じ処理を記載している場合同様にリファクタリング可能です。
* 以下のように3つのモデルで全く同じ処理を記載しているケースを想定。（関係する処理のみ抽出して記載）

```rb:like.rb
class Like < ApplicationRecord
  # 複数モデルで以下のような全く同じ処理を記載している
  after_create :create_notification

  private

  def create_notification
    Notification.create(user: post.user, action: self, action_type: self.class.name )
  end
end
```

```rb:comment.rb
class Comment < ApplicationRecord
  # 複数モデルで以下のような全く同じ処理を記載している
  after_create :create_notification

  private

  def create_notification
    Notification.create(user: post.user, action: self, action_type: self.class.name )
  end
end
```

```rb:repost.rb
class Repost < ApplicationRecord
  # 複数モデルで以下のような全く同じ処理を記載している
  after_create :create_notification

  private

  def create_notification
    Notification.create(user: post.user, action: self, action_type: self.class.name )
  end
end
```

## 実装手順
* ※このリファクタリングにたどり着く前に、親クラスのApplicationRecordに共通処理を記述しましたがうまく動作しませんでした。
* ※そのため、Moduleとして共通処理を切り出す形で記載いたします。各Modelに共通の親クラスを設定している場合は、親クラスに共通処理を記載する形で問題ありません。

### 手順1
* `app/models/concerns/`配下にModuleファイルを作成する。Moduleファイルを配置するディレクトリは非常に大事です。左記の場所に配置してください。
* 今回は共通処理が通知レコードの作成処理なので。`NotificationCreator`というモジュールを作成することにいたします。

### 手順2
* 手順1で作成したモジュールに共通処理を移行
```rb:app/models/concerns/notification_creator.rb
module NotificationCreator
  # ３つのモデルで記述している全く同じ処理をモジュールに移植
  def create_notification
    notification = Notification.create(user: post.user, action: self, action_type: self.class.name)
  end
end
```

### 手順3
* 各Modelから共通処理を削除し、モジュールをincludeする。
* 3つのモデルは最終的に以下の形になります。

```rb:like.rb
class Like < ApplicationRecord
  # 処理を移行したモジュールをinclude
  include NotificationCreator

  after_create :create_notification
end
```

```rb:comment.rb
class Comment < ApplicationRecord
  include NotificationCreator

  after_create :create_notification
end
```

```rb:repost.rb
class Repost < ApplicationRecord
  include NotificationCreator

  after_create :create_notification
end
```

## 補足：なぜリファクタリングをする必要があるか
* 上記のようにリファクタリングをするメリットとして、共通処理を変更した場合にコードの変更箇所が１箇所で済むというものがあります。
* 例えば、`create_notification`の中で通知レコードを作成すると共に「メール送信を同時に行う」という改修が新たに必要になったとします。
  * 3モデルに同じ処理が書いてあった場合、この改修により3モデルすべての`create_notification`を修正する必要があります。3回同じ修正をする必要が出てきます。
  * 一方で、モジュールに共通処理を移行した場合、モジュールの`create_notification`のみを編集するだけで問題ありません。1箇所コードを修正することで、includeした3つのモデル全てに変更を適用することが可能です。