OmniAICreator/Qiita-1.07M huggingface dataset:
Amazon Braketでの実機へのjobの投げ方

# Amazon Braketでの実機へのjobの投げ方

## 1 notebookの作成

Amazon Braketを立ち上げて、Notebooksを選択します
下画面右上のCreate notebook instance を押し、notebookを作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/761738/8caea84e-be65-099c-5cd7-c4150ad42468.png)


instanceの名前を聞かれるので、名前を入力し、create notebook instance ボタンを押して
インスタンスを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/761738/31ca42e7-b820-f224-cb31-d0f026fa2c5b.png)

## 2 バケットの作成
S3 に移動し、バケットを作成します(ここでは、amazon-braket-07f25cd5185b)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/761738/4f6677d5-946f-eb8f-e0f0-aac86d71d7ac.png)


作成したamazon-braket-07f25cd5185b内にフォルダを作成します(ここではaaaとしました)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/761738/1e60dbbd-6193-21e4-c621-938acdd93160.png)

## 3 実行
braketに戻り適当なnotebookを開きます
(ここでは、デフォルトで入っている　groverのnotebookを開きました)

### bucket名とfolder名の入力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/761738/7557a348-da56-07e7-a8e0-4f897aca2055.png)

参考PGM内では、bucket名とfolder名の入力する箇所があるので、2で作成したs3のbucket名とfolder名をそのまま入力します。

```
#  Enter the S3 bucket you created during onboarding in the code below
my_bucket = "amazon-braket-07f25cd5185b" # the name of the bucket
my_prefix = "aaa" # the name of the folder in the bucket
s3_folder = (my_bucket, my_prefix)
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/761738/618de9e6-09c0-81e7-2ebd-1c607c7fae1d.png)

そのまま実行していくと、この例ではionQにjobを投げるようです。問題なければ、Status of task: CREATED　表示されます。

その下の、statusを見るとStatus of (reconstructed) task: QUEUED　と表示されています。

### タスクの確認
Braketに戻ってTasksを見てみると、jobが実行待ちのようです。
ionqは日本時間の深夜が稼働時間なので、しばらく待ちます。
後ほど、2で指定したフォルダに結果がかえってくるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/761738/7187d08c-926a-0e46-8654-a73355b0da0a.png)
CMakeのコツ:ビルドエラーをログファイルに出力する

cmakeのビルド時のエラーはstdoutではなくstderrに吐き出されます。
そのため、以下のように明示することでログファイルに出力できます。

```
./build_with_cmake.sh > log/build_err.log 2>&1
```


# 必要性

エラーが１０００行くらいでるとターミナルのバッファを超えて読めなくなります。
しかも、大体同じエラーメッセージが機械的に繰り返されています。
ですので、**一旦ログファイルに落とし込み、正規表現などでエラーの原因の仮説検証**を進めましょう。

ライブラリのソースファイルのエラーも出てきますが、それを確認するよりも、
**自分の書いたコードのパスと、それに起因するエラーメッセージを抽出する戦略**の方が効率いいです。


また、**1000行のエラー読むとメンタルがイカれて、エンジニアを辞める原因になります**。
是非ともログファイルに出して欲しい情報だけ抽出する習慣をつけましょう。
【RSpec】モックとスタブについて

学習のアウトプットとして投稿しています。
何かご指摘がございましたらお願いいたします。

# モックとスタブとは
■　モック(mock)
モックは本物のオブジェクトのふりをするオブジェクトのこと。テストで用いられる。
これまで、FactoryBotや素のRubyを使って作成していたテストデータの代わりになるもの。
両者の違いはDBにアクセスするかしないか。モックはDBにアクセスしない。
そのため、**テスト時間を短縮できる**。
テスト対象でないオブジェクトなどは、モックで管理した方がよい場合がある。
この場合、テスト対象が関連オブジェクトの実装に影響されにくくなり、**管理しやすいテストコードとなる**。

■ スタブ(stub)
スタブはオブジェクトのメソッドを上書きして、事前に返して欲しい値を設定することができる。
なので、スタブはテストでダミーメソッドとして使うことができる。

# 使い方

```ruby:user.rb
class User < ApplicationRecord
  has_one :profile

  delegate :name, to: :profile
end
```

```ruby:profile.rb
class Profile < ApplicationRecord
  has_one :user

  def name
    [first_name, last_name].join(" ")
  end
end
```

ここでUserモデルのnameメソッドをモックとスタブを使ってテストした場合

```ruby:spec/models/example_spec.rb
RSpec.describe User, type: :model do
  # モックを使う
  let(:profile) { double("profile", name: "Test User") }

  it "ユーザーのフルネームを返す" do
    user = User.new
    # スタブを使う
    allow(user).to receive(:profile).and_return(profile)
    expect(user.name).to eq "Test User"
  end
end
```

profileにモックを使い、引数にメソッドが呼ばれた時の返り値も設定しておく。
userにはprofileを受け取ったら、モックのprofileを返すようスタブを使い設定する。

こうすることで、本来userオブジェクトはprofileを受け取ったらDBに問い合わせてprofile_idからprofileをとってくるところを、スタブを使いモックのprofileを返すと設定したのでDBに問い合わせることなく、実行スピードが早くなる。
さらに、このUserモデルのテストは、モックを使ったことでprofileのnameメソッドの実装に依存しなくなるため、profileモデルの実装から影響を受けにくくなり管理しやすいテストになった。

## 注意点
このテストは、モックのprofileしか見ていないため、例えばprofileモデルのnameメソッドの名前が変わったり、削除されたりしてもこのテストは通ってしまう。
コードの変更によるバグなどはテストでキャッチするべきなので、このままではよくない。
この問題に対処するため、検証機能付きのモックを使う。検証機能付きのモックは引数で与えられたモデルのインスタンスと同じように振る舞うので、nameメソッドがなかった場合はエラーを出してくれる。

先ほどのテストに検証機能付きのモック(instance_double)を使った場合
※引数のprofileは大文字で記載する

```ruby:spec/models/example_spec.rb
RSpec.describe User, type: :model do
  # 検証機能付きのモックを使う(引数のProfileは大文字で記載する)
  let(:profile) { instance_double("Profile", name: "Test User") }

  it "ユーザーのフルネームを返す" do
    user = User.new
    # スタブを使う
    allow(user).to receive(:profile).and_return(profile)
    expect(user.name).to eq "Test User"
  end
end
```

## まとめ
テストデータの作成はFactoryBotやRubyでしか作成したことがなかったので、テスト時間が早くなるということでオリジナルアプリのテスト作成にモック導入を検討してみようかなと思いました。ただテスト初心者なので使いやすそうなinstance_doubleからやっていきたいですね。。。

## 参考
[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)
[使えるRSpec入門・その3「ゼロからわかるモック（mock）を使ったテストの書き方」](https://qiita.com/jnchito/items/640f17e124ab263a54dd)
[Railsガイド 3.4 メソッドの委譲](https://railsguides.jp/active_support_core_extensions.html#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E5%A7%94%E8%AD%B2)
Homebrewを使った Macのbashバージョン変更

### きっかけ

declareコマンドの-Aオプションが使えない、、、

```
declare: -A: invalid option
declare: usage: declare [-afFirtx] [-p] [name[=value] ...]
```

同じエラーで質問している人がいました
https://stackoverflow.com/questions/6047648/associative-arrays-error-declare-a-invalid-option

ふむふむ、bashのバージョンをあげてみよう

変更前のバージョンは3.2.57でした

```
% bash --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin20)
Copyright (C) 2007 Free Software Foundation, Inc.
```

## Homebrewを使ってインストール

`% brew install bash`

インストール完了しましたが、このままだとdefaultで最新バージョンのbashが使えないようです


```
% which -a bash
/opt/homebrew/bin/bash
/bin/bash
```
/bin/bashが元々インストールされていた古いもので、
/opt/homebrew/bin/bashが先ほどインストールしたものです。

これをまず/usr/local/binに置いてみます
`sudo cp /opt/homebrew/bin/bash /usr/local/bin/bash`


bashのバージョンを見てみると、、

```
% which bash
/bin/bash
% bash --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin20)
Copyright (C) 2007 Free Software Foundation, Inc.
```
まだ古い方になっています



## chshでdefaultで使用するbashを変えてみる

```
chsh -s /usr/local/bin/bash
Changing shell for username.
Password for username: 
chsh: /usr/local/bin/bash: non-standard shell
```

non-standard shellだよ〜と怒られてしまって変更ができません

##/etc/shellsを編集

/etc/shellsを編集してみます

`sudo vi /etc/shells`

```
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

#/bin/bash #古いバージョンをコメントアウト
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
/usr/local/bin/bash #新しいバージョンを追加
```

## 確認
`chsh -s /usr/local/bin/bash`を再度実行し、bashのバージョンを確認します

```
$ which bash
/usr/local/bin/bash
$ bash --version
GNU bash, バージョン 5.1.8(1)-release (aarch64-apple-darwin20.4.0)
Copyright (C) 2020 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

```
無事新しいバージョンに切り替わりました

### 参考にした記事
https://itnext.io/upgrading-bash-on-macos-7138bd1066ba
https://blog.tottokug.com/entry/2017/12/31/121231
forfilesコマンドのフォルダパスに日本語を含むと、無効な引数またはオプションだと怒られる

Windows環境で、特定の日数以前のファイルを削除するため、以下のようなバッチファイルを作成して実行したところ、、、

```bat:batch.bat
forfiles /p フォルダパス名(ここに日本語が含まれていた。C:user/デスクトップ 等。) /d -2020/12/31 /c "cmd /c /s /q del @file"
```

次のようなエラーが出ます。

```
エラー：無効な引数またはオプションです - '-2020/12/31'。
"FORFILES /?" と入力すると使用法が表示されます。
続行するには何かキーを押してください . . .
```

↑このエラーはbatch.batの最終行にpauseを記入している場合に目視確認できました。

cdコマンドでは当然日本語を含むフォルダパスに移動することができたので、日本語が弾かれているかもという意識が無く、
エラーの内容を読んで「/dオプションがおかしいのか？」と詰まってしまいました。

**フォルダパスを英数字に変更**することで解決。

forfilesコマンド詳細については、[こちら](https://qiita.com/gitya107/items/a11deb3d83d8d624175c)に分かりやすい記事があります。

せめて「フォルダパスが無効です」とか言ってくれたら良いのに...。
Hyper Protect Crypto Servicesを使ってみた　その２（ROKS連携）

「[その１](https://qiita.com/y_tama/items/5d2104dcc37b9463bb88)」で、HPCSの初期セットアップまで完了しました。

今回は、HPCS上に鍵を作成し、その鍵を使って他サービスのデータを暗号化してみます。

基本的には下記の手順に従います。
https://cloud.ibm.com/docs/openshift?topic=openshift-encryption#keyprotect

# HPCSにルート鍵を作成

HPCSの「KMS鍵」の画面で、「鍵の追加」を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/cf63d2e0-690d-333d-e3aa-edd1dcfe6e06.png)

ルート鍵を任意の名前で作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/02e467fd-b20c-04fc-9451-0de8e19d46a6.png)

ルート鍵が作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/f61e735e-e9d8-323f-f0e3-2aec3a1b6960.png)


# ROKSで鍵管理サービス（KMS）有効化

ROKSの「概要」ページから、鍵管理サービスの有効化を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/0567a7d3-4874-9d23-bd33-62a9e923e7d1.png)

:::note warn
注意書きに書いてあるように、この設定を一度有効にすると、再度無効にすることはできません。
:::

先ほど作成したHPCSサービスとルート鍵を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/e383b044-f87c-7e25-73b3-ab9ae902e446.png)

鍵管理サービスとの連携が有効化された状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/3cbff956-acaa-feeb-0c8e-be3e68fac4a5.png)

また、HPCSの「KMS関連リソース」の画面に、ROKSクラスターの情報が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/360f7227-239e-e98a-5751-a2325af02184.png)


# ルート鍵を無効化

上記までで設定は完了ですが、これだけだと、本当にHPCSとROKSが連携して動いているか、少し実感が湧きづらいです。
連携されていることを可視化する1つの方法として、HPCSのルート鍵を無効化すると、ROKSクラスターが正常に使えなくなるので試してみます。

HPCSのKMS鍵の画面から、使用しているルート鍵を無効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/87904798-06ff-8095-2648-d62264d6b749.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/81a69cbc-6e9f-a0bc-9b9f-07ac1a1ac8ba.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/fc191d06-f855-6ba0-0add-b39c0c393e7a.png)

ROKSのステータスが異常になっていきます。
（下記の状態になるまでに、多少時間がかかりました）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/5bb2bd73-aa75-4ec3-54d0-fc770bb1e82e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/9785d205-df38-94ff-1935-1594a6fffc4a.png)


# ルート鍵を再度有効化

ルート鍵を有効化すると、ROKSクラスターのステータスも正常に戻りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/1c4b2a53-2835-2d6f-0fe1-be9a34a87b73.png)

ルート鍵が再度有効化されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/4baa1cd7-9003-7c16-54ff-44522a81f2cf.png)

ROKSのステータスが正常に戻りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/a37df62f-8735-91a4-1436-090d6ea8a6d0.png)

以上です。
ElasticBeanstalkでnodeがinstallできないエラー

# 現象
下記コマンドが通らない。
`sudo curl -sL https://rpm.nodesource.com/setup_12.x | sudo bash -`

# 原因
Let'sEncrypt証明書を使用してサーバーに接続できないため。
rpm.nodesource.comドメインの証明書の期限が9/30で切れている。

# 解決策
https://aws.amazon.com/jp/premiumsupport/knowledge-center/ec2-expired-certificate/

取り急ぎ共有でした。
[メモ]Apache 2.4.49 でバーチャルホストをホストする。

## ステップ1

1.Apache をダウンロードする。
(https://www.apachelounge.com/download/)

## ステップ2

1.httpd-\2.4.49-win64-VS16 を解凍する。
2.フォルダー `Apache24\mysite\` を作成する。
3.`Apache24\conf\httpd.conf` で以下のテキストを追加する。

```conf
#61行目
Listen <ボート>

#213行目
<VirtualHost *:<ボート>>
    DocumentRoot "${SRVROOT}/mysite"
</VirtualHost>

#239行目
<Directory />
    AllowOverride none
    Require all granted
</Directory>
```

## ステップ3
1.`Apache24\bin\ApacheMonitor.exe` をクリックして、Apache24 を起動する。
2.ブラウザで localhost:<ボート> にアクセスする。
#SharePoint リストを使った #PowerApps や #PowerAutomate もソリューション化したいそんなあなたへ

# ソリューション

Power Platform にはソリューションという考え方がありまして、Power Apps のアプリや Power Automate のフローを、Microsoft Dataverse のテーブル構造などと共に一つのパッケージにしてまとめ、別の環境や別のテナントに丸ごとインポートする機能があります。

https://docs.microsoft.com/ja-jp/power-platform/alm/solution-concepts-alm

しかも、一つのパッケージにまとめるだけでなく「マネージド」とすることで、インポート先ではアプリやフロー、テーブル構造などを編集できなくするといったことができますので

開発用の環境でアプリやフロー、テーブルなどを作成
　↓
開発用の環境で少人数ながらでテスト
　↓
問題なければ、マネージドソリューションとしてパッケージ化
　↓
本番環境にインポート

という流れをくむことで、実運用のアプリは直接編集できず、ヒューマンエラーによる障害を回避することができます。

これ、本当に便利なんですよね。
ただ、Dataverseを使ってる環境ならいいんですけど、SharePoint を使っているところも結構多いはず。
SharePoint でも使いたい！
そう思う方も多いかと思います。

そんなあなたのために、SharePoint リストを使ったソリューションの作り方をまとめます(笑)

# 環境変数

ソリューションのコンポーネントには、環境変数というのがあります。
この環境変数は、Power Apps や Power Automate で使用できる独自の変数を定義し、インポート時に設定することで、インポート先で違う値を渡してそのまま利用することができます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/0bc9ffbd-7606-dbe7-51b9-79515ce97d1a.png)

ここで、データ型を「データソース」にし、コネクタを「SharePoint」とすると、パラメータに「サイト」と「リスト」を選ぶことができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/96faf21b-8f75-6641-a0dd-5f593d84112f.png)


サイトの場合は、SharePointサイトを選択することができ、
リストの場合は、SharePointサイトの環境変数で指定されたサイトのリストを選択することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/2567eaf3-0c52-c19e-3b2c-88c3f945a89c.png)

# Power Apps でのSharePoint環境変数の扱い

Power Apps を作成するときに環境変数を使う際は、データソースで SharePoint コネクタを選ぶと、詳細設定に作成した環境変数が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/2732dfda-7065-2efc-9322-75d9bf977c67.png)

また、リストも同様に、詳細設定から環境変数を呼び出すことができますのでそれを使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/03437314-4344-98b8-4d42-3a342e86bbca.png)

こうすることで、SharePoint の環境変数を使ったアプリを作ることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/95402349-dd17-4389-6635-51e8bb9fc991.png)

# Power Automate でのSharePoint環境変数の扱い

SharePoint のコネクタから、サイトやリストにカスタム値の入力をすることで、環境変数を指定することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/ac2767ad-f1b6-9fb0-cbe4-27a2d9d8ff03.png)

サイトとリストに環境変数を指定すると、問題なくテーブルの値を指定できるようになりますので、各項目に動的コンテンツを追加することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/81b49758-4386-657f-8bb1-e1b306daf8e1.png)

# 作成したソリューションを別環境にインポート

インポートを別の環境に行うと、インポートウィザードにて、使用する接続を求められます。
環境に既に存在する場合はそのまま利用できますが、そうでないときは新たに接続を作成することも可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/1b7259d6-831f-b9f9-60c5-33d10c613754.png)

また、環境変数の値を、ソリューションをエクスポートする前に削除すると、インポート時に選ぶことができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/164e73e8-deaa-3fc4-4b0c-9cc4d2125583.png)

なので、本番と開発でサイトを分けたいとか、別のテナントへ・・・なんかにも安心してインポートすることができます。

# 注意

当然ですけど、インポート先で別のリストを指定する場合は、作った時に使用したリストと全く同じ構造のものを作成しておく必要がありますのでご注意くださいませ＼( 'ω')／
パラメーターとparams

パラメーターとはリクエストに含まれる外部からくるデータのこと。URLに含まれるものやフォーム入力から渡されるパラメーターがある。
パラメーターはコントローラーにたどり着くとparamsというハッシュのような構造のものに格納される。

paramsとは送られてきたパラメーターをハッシュのような構造で格納したもの。フォーム入力でパラメーターが送られてきた場合

フォームで入力(入力されたものがパラメーター) → コントローラでパラメーターがparamsに格納され、パラメーターを取得するカラムを指定する → モデル → データベース内のテーブルへ

という流れでパラメーターをparamsに入れてデータベースへ運んでいく。
例えば、HTTPメソッドがpost、URL名がposts、コントローラ名がposts、アクションがcreateの場合ルーティングの設定は

```ruby
post 'posts', to: 'posts#create'
``` 

★createは保存のための newアクション→情報の記述→saveアクション の流れを引数を利用することで実行できるメソッドで　モデル.create(カラム名: 変数名)　で定義できる。

となりコントローラーでアクションと保存の処理を定義。contentカラムでパラメーターを取得したい場合

```ruby
def create
   Post.create(content: params[:content])
end
```


ビューファイルでブラウザに表示されるパラメーターを入力するためフォームを定義

```ruby
<%= form_with url:”/posts”,method: post, local: true do |form| %>
  <%= form.text_field :content %>  #入力されたパラメータをコントローラーで定義した変数[:content]へ代入するという記述   
  <%= form.submit ‘投稿する’  %>
<% end %>
```

といむ流れでパラメーターはデータベースへ保存される。
【PHP】多次元配列 カラム同士を計算

```PHP
<?php
$results = [
    ['name' => '田中',  'japanese' => 83, 'math' => 57, 'science' => 43,  'social' => 72, 'english' => 78],
    ['name' => '渡部',  'japanese' => 62, 'math' => 88, 'science' => 70,  'social' => 66, 'english' => 38],
    ['name' => '品川',  'japanese' => 23, 'math' => 33, 'science' => 53,  'social' => 17, 'english' => 07],
    ['name' => '滝谷',  'japanese' => 93, 'math' => 89, 'science' => 79,  'social' => 96, 'english' => 91],
    ['name' => '川原田','japanese' => 55, 'math' => 56, 'science' => 60,  'social' => 54, 'english' => 66],
    ['name' => '一色',  'japanese' => 27, 'math' => 47, 'science' => 100, 'social' => 42, 'english' => 38],
];
$value = array_map(function($num){
    return [$num['name'] =>  array_sum($num)];
}, $results);

var_dump($value);
```

#結果
```
array(6) {
  [0]=>
  array(1) {
    ["田中"]=>
    int(333)
  }
  [1]=>
  array(1) {
    ["渡部"]=>
    int(324)
  }
  [2]=>
  array(1) {
    ["品川"]=>
    int(133)
  }
  [3]=>
  array(1) {
    ["滝谷"]=>
    int(448)
  }
  [4]=>
  array(1) {
    ["川原田"]=>
    int(291)
  }
  [5]=>
  array(1) {
    ["一色"]=>
    int(254)
  }
}

```
オドメトリを使ってROS2とUnityの間でロボットの位置を同期させる

# はじめに

本記事では、ROS2で動くロボットの位置と角度(オドメトリ)をもとに、Unity側のロボットを同期して動かす方法について説明します。ROS2側でロボットを動かすと、オドメトリの値(自己位置)が変化し、それをUnityに送ることでUnity側のロボットを同期して動かします。

# 実行環境

本記事で用いる実行環境は以下の通りです。

- ubuntu 20.04.2 LTS
- ROS2 Foxy Fitzroy
- Unity 2020.3.17f1

[このページ](https://github.com/Unity-Technologies/ROS-TCP-Connector#installation)にしたがって、UnityにはROS2との連携に必要な`ROS-TCP-Connector`をインストールしておきます。また、動作確認の手順については以下の記事でも紹介しています。

https://qiita.com/sfc_nakanishi_lab/items/40ab94a2f19f375f42db

# コード
オドメトリ(`/odom`トピック)をサブスクライブして、そこに含まれる位置と角度をもとにUnityのオブジェクトを移動させるコードです。Unityで移動を実現するには様々な方法がありますが、今回は`transform`プロパティの値を書き換えることで移動させています。

`nav_msgs/Odometry Message`の構造は、以下のページから確認できます。オドメトリのメッセージには、現在のロボットの世界座標系における位置と角度を示す`Pose`メッセージが含まれています。

http://docs.ros.org/en/noetic/api/nav_msgs/html/msg/Odometry.html


また、最初に受信した姿勢を`initialPose`として保存し、以後受信した姿勢はこれとの差分を取っているので、Unity側のロボットは常に原点からスタートします。



```RobotController.cs
using UnityEngine;
using Unity.Robotics.ROSTCPConnector;
using Unity.Robotics.ROSTCPConnector.ROSGeometry;
using RosMessageTypes.Nav;

public class RobotController : MonoBehaviour
{
    [SerializeField] private GameObject robot;
    [SerializeField] private string topicName = "/odom";
    private ROSConnection ros;
    private Pose initialPose;
    private bool receivedFisrtMsg;

    void Start()
    {
        ros = ROSConnection.instance;
        ros.Subscribe<OdometryMsg>(topicName, ReceiveOdomMsg);
    }

    private void ReceiveOdomMsg(OdometryMsg msg)
    {
        Vector3 pos = msg.pose.pose.position.From<FLU>();
        Quaternion rot = msg.pose.pose.orientation.From<FLU>();

        if (!receivedFisrtMsg) {
            // 初期位置を保存
            initialPose.position = pos;
            initialPose.rotation = rot;
            receivedFisrtMsg = true;
        }

        // 原点をリセット
        Vector3 adjustedPos = pos - initialPose.position;
        Quaternion adjustedRot = rot * Quaternion.Inverse(initialPose.rotation);
        
        // ロボット用のオブジェクトにサブスクライブした位置と角度を設定
        robot.transform.position = adjustedPos;
        robot.transform.rotation = adjustedRot;
    }
}
```

# 動作確認

ターミナルからros_tcp_endpointとgazeboを起動しておきます。gazeboを起動すると、`/odom`トピックがパブリッシュされるようになります。

```
$ ros2 run ros_tcp_endpoint default_server_endpoint --ros-args -p ROS_IP:=<your IP address>
```

```
$ ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
$ ros2 topic list
/camera/camera_info
/camera/image_raw
/clock
/cmd_vel
/imu
/joint_states
/odom
/parameter_events
/performance_metrics
/robot_description
/rosout
/scan
/tf
/tf_static
```

上のコードを適当なオブジェクトにアタッチして、Unityのシーンを実行します。`/cmd_vel`を送ってgazeboのロボットを動かすと`/odom`トピックに含まれる姿勢が変化し、Unityのロボットもそれと同じように動きます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/p8irCW6Z0ww" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
オーケストラ専用出欠サイトの制作

## 目次
- 概要
- 機能説明
- 改善点
- あとがき

## 概要
DMM WEBCAMPの卒業制作として作った、オーケストラのための出欠管理サイト「Otoderu」についてお話させてください。
私が15年超、音楽団体に関わっている中で出欠連絡というシステムの不完全さが運営側、団員双方の負担になっていると思ったことが制作の動機です。
半ば諦められている、「面倒」「忘れてた」に対しての問題解決ツールとして提案したいものです。
サイト：[Otoderu](https://otoderu.com/)
GitHub：[コチラ](https://github.com/sagittarius3008/Otoderu)
[頂いているレビュー](https://docs.google.com/spreadsheets/d/1ncuHkSPa2KOA9w-8PAqZyYjZSPjEKQVvLZSY_Tatpx4/edit?usp=sharing)

### 出欠連絡の重要性について
一番の問題は**代わりがいない**ということに尽きます。
例えばサッカーであればあるポジションの人が休んだとしてもある程度の代替は可能かと思います。
ただ、楽器の場合そもそも音が出せないので全くがら空きになってしまうのです。
パズルのピースが欠けている状況ですね。
そんな理由から出席者により練習する曲、順番を決めるという習慣があります。
そのためにも団員の出欠の把握というのは音楽団体にとって、非常に大切なことなのです。

### 何が問題なのか
出欠連絡が行われない理由は大きく３点あります。
1. 私用が定まらず後回しにした結果、忘れる
2. 様々な連絡が流れてくる結果、埋もれる
3. 各団体により使用しているツールがバラバラで億劫（メール、ライン、スプレッドシート、システムetc）
これらを総合すると、意思決定に必要な情報をひとまとめにすれば自然とアクセスされ、出欠入力も行われるという結論に辿り着きました。

## 機能説明（課題解決に直結するもののみ抜粋）
### 特徴（README.mdより）

- ログイン後すぐに出欠入力が行える設計で団員の負担を最小限に。
- 複数団体に対応。スケジュール管理を一括で行えます。
- GoogleMapを使用しており会場までの道のりがすぐ分かります。
- 練習予定の登録には過去の練習予定を見ながら検討できます。

### 複数団体への対応
予定が定まらない一つのパターンとして、複数団体に所属していて練習日が重なった場合の優先順位です。
特にアマチュアでも賛助（=金銭の授受など特別待遇がある）として参加する団体には気を使って予定を立てている傾向があります。
そうした方への対応として複数団体の予定を一目で確認できるようにしておき、入力を同時に行える設計にしました。
![複数団体.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1518966/fd5b8868-99aa-9b97-d19a-a81080975ae4.png)

### 入力忘れ、漏れへの対応
出欠未入力件数の通知をハンバーガーメニュー内、マイページに配置。サイトを訪問したら必ず目に留まるようにしました。
入力したつもりだったけど更新出来ていなかったという可能性を排除しています。
![ヘッダー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1518966/49742bba-5c4f-f96d-b8e5-993ba14100b0.png)![マイページ1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1518966/f51487da-8ab7-0b9b-21dc-5dd748f86d69.png)

### SNS投稿機能
練習詳細画面ではTwitter、FacebookのSNSシェアボタンを配置しており、団員への周知が簡単に行えます。
一度入力を漏らしても目に触れる機会を複数作ればサイトへアクセスする可能性は高まります。
参考ですが、所属する団体では78名中65名（83%）がどちらかのSNSに登録していました。（実際にはもう少しいるはず？）
演奏会情報の確認のため、継続性、こだわりの強い人間が多いためオーケストラ団員はSNSを愛する傾向があると見ています。
![ツイッター.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1518966/be7b228c-2619-09e6-7c7e-1ae30919ecd6.png)

### 練習登録の効率化
団員に、如何に入力してもらうかという視点でお話をしてきましたが、練習登録、練習組みも面倒な作業です。
そのため登録画面ではモーダルウィンドウ＋マウスホバーによる表示を組み合わせ、他の練習予定を見ながら練習組みが出来る設計としました。
勿論コピー＆ペーストも行えるので使いまわしも簡単に行えます。
![モーダルウィンドウ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1518966/6a5126cc-7c63-1386-1b93-71cad12ffb3c.png)

## 改善点
### SQL発行の多さ
each文の中にeach文を…と重ねている部分が多々あり、結果として1ページ表示するのに数十回とSQLが発行されており、実データを受け入れるには改善が必要です。
SQLの勉強を行い、どういった形でデータを引っ張っているのか、検証していきたいと思います。

### 保守性（DRYの実践）
コードを書くには見やすさと、保守性を考えなければなりませんが、前者には留意したものの、後者には手を回すことが出来ませんでした。
今後益々勉強に励み、DRYの実践に身を入れていきたいと考えています。

### Slackbotとの連携（バージョンアップ）
サイトにアクセス後、如何に簡単に入力するか、という点に主眼を置いていましたが、**本当のゴールはサイトにアクセスしないこと**だと思っています。
所属団体では通常の連絡手段にSlackを使っているので、Slackbotと連携させ、bot側から出欠入力を行う、出席者を確認するといった簡単な動作をアクセスしないで完結することを目指しています。

## あとがき
音楽仲間に見てもらうと、「使ってみたい！」「これに変えようよ！」といった声をもらうことができ、制作して良かったと心から思うことができました。
小さな課題解決ではありますが、こうしたことがまわりまわって、音楽業界が盛り上がる一助になれば良いなと思っています。

最後までお読みいただきありがとうございました。
MSYS2 で clip して文字化けするとき

# はじめに
MSYS2 のオプション設定を変更することで `clip` したときの文字化けを回避することができます。

## 例

```shell
$ ll|clip
```

などとしてコマンドの戻り値をパイプでクリップボードに送り、エディタに貼り付けてみるとこのように文字化けしています。
![2021-10-01 11-04-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65963/42fa7508-b938-554d-198d-c5813f111d85.png)

## 対処

1. **[Options]** を選択します。
![2021-10-01 11-11-37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65963/ac636c19-388c-3c70-a675-727a18fb5646.png)

2. **[Options]** ダイアログで **[Text]** の **[Locale]** ドロップダウンメニューから「ja_JP」を選択し、**[Character set]** から「SJIS (Japanese)」を選択して **[Apply]** をクリックします。
![2021-10-01 11-13-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65963/70959dcd-4c4f-4a61-7607-65e01ecf8090.png)

3. MSYS2 を一度終了し、再度 `ll|clip` したテキストをエディタに貼り付け、文字化しないことを確認します。
![2021-10-01 11-23-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65963/56b169d0-3000-3929-2fae-2d8fb15d30e0.png)

# おわりに
最近は WSL2 の話題をよく見かけるので、MSYS2 の記事を書いてみました。
htaccessが動かない（アクセス制限）

IPアドレス制限を設定したhtaccessファイルを設置しているのに、VPNを繋がずとも閲覧できてしまう不具合が発生していた。大きな範囲から検証していく。
（リファラ制限も記述に含んでいるが今回検証範囲ではないので説明は割愛）

```
SetEnvIf Referer "^http[s]*://~~~~~~~~" chk_url

order deny,allow
deny from all
allow from env=chk_url
allow from XXX.XXX.XXX.XXX
```

###サーバーレベルのチェック
- 対象サーバーがApacheである
- 設定ファイル（httpd.conf）でhtaccessの設定を許可している
- パーミッションが604あるいは644などサーバーで推奨された設定となっている

☞ 問題なし。設定ファイルは発見できなかったが親ディレクトリに既存のhtaccessが設置しており、問題なく動いている。
子ディレクトリに対して追加の設定がされていれば別だが、考えにくい。

####ファイルレベルのチェック
- 名前が「.htaccess」、前後にスペースが含まれていない
- 文字コードがUTF-8,改行コードLF（厳密にはUTF-8である必要はないが念のため）
- 内容に全角が含まれていない
- 最後に空行がある

☞ サーバーにアップされているファイルを確認した。問題なし。

####記述レベルのチェック
- 検証用に簡易なリダイレクト設定のhtaccessファイルに差替え、ファイル自体の挙動を確認

```
RewriteEngine on
RedirectMatch 301 .* http://example.com/

```

☞ 問題なく動くので、htaccessの記述に問題がある可能性が出てきた。

####念のためのチェック
- ブラウザのキャッシュの削除とハードの再読み込み
- 大文字と小文字
- allow/denyの記述変更
- 記述を大枠でコメントアウトして挙動を確認
- 親ディレクトリのhtaccessの影響を受けない別ディレクトリで挙動を確認

☞ 意味なし。

###何？？
Apacheのバージョン2.4以降だとAllowディレクティブの記述が効かないからでした
社内サーバーも同じく2.4以降だったが、サーバーのモジュール設定で、2.4でもAllow系が使えるようになっていることもあるらしい。そのため、サーバーによっては無効な記述になることに気づかなかった……

https://pc-taskal.net/howto/blog/apache24-mod-authz-host

https://www.magata.net/memo/index.php?Apache2.4%20%A4%CERequire%A5%C7%A5%A3%A5%EC%A5%AF%A5%C6%A5%A3%A5%D6%A4%C7%A5%A2%A5%AF%A5%BB%A5%B9%C0%A9%B8%E6%A4%F2%B9%D4%A4%A6

https://memorva.jp/memo/linux/htaccess.php

Allowディレクティブの記述はdenyとallowがややこしかったが、Requireディレクティブではより明確な記法に統一されている。詳しくはリンク先参照。

```
SetEnvIf Referer "^http[s]*://~~~~~~" chk_url

<RequireAny>
Require all denied
Require ip XXX.XXX.XXX.XXX
Require env chk_url
</RequireAny>

```

学びを得た……
Apacheのバージョン違いで効かない記述があることもだが、
「アクセス制限」で検索してもAllow系の記述がよく出てくるので作成段階で気づかなかったことや、
Apacheの勉強用に購入していた書籍も古すぎて2.2と2.4のバージョンの違いについて載っていなかった

というのも教訓になった。ﾖﾎﾞ
#0.5 アプリ作成→GitHubへのPush

#はじめに
前回の[開発環境の準備](https://qiita.com/snow100/items/eabcee3d7aa7beeb0f98)に引き続き、<br>アプリ作成からGitHubへのPushの流れを記事にして行きたいと思います。

#目次
1.アプリ作成
2.リモートリポジトリ作成
3.リモートリポジトリへPush

#1.アプリ作成
```
username:~/environment $ rails new snow-review
```
rails new アプリ名でアプリケーションのひな形を作成する事ができます。

```
username:~/environment $ cd snow-review
```
3.リモートリポジトリへのPushの為にcdでアプリ上に移動しときます。

#2.リモートリポジトリの作成
- Your repositriesから[NEW]ボタンを押し、Create New Repositriesへ。
- Repository Nameを書き、その他お好みの設定をし、[Create New]ボタンで新規リポジトリ作成。
- 新しく作ったリポジトリのページに行き、[code]からリポジトリURLを確認
<br>(※リポジトリURlはHTTPSでは無く、SSHの方)

#3.リモートリポジトリへPush
```ruby:Terminal
username:~/environment/アプリ名 $ git init
username:~/environment/アプリ名 $ git add -A
username:~/environment/アプリ名 $ git commit -m "コメント"
username:~/environment/アプリ名 $ git branch -M main
username:~/environment/アプリ名 $ git remote add origin リポジトリURL
username:~/environment/アプリ名 $ git push origin main
```
- git init  =  開発環境のアプリをローカルリポジトリとして登録
- git add -A = 全てのファイル(-A)をインデックスに追加
- git commit -m "コメント" = メッセージをつけて、変更履歴を保存 (初回だと登録履歴を保存)
- git branch -M main = デフォルトでmasterブランチになっているのを、mainに変更
- git remote add origin リポジトリURL = originという名前でリモートリポジトリURlを保存する
-  git push origin main　= ローカルリポジトリの変更をリモートリポジトリへアップロードする
<br>(mainの部分はブランチ名なので、その都度アップロードしたいブランチ名を指定する)

#最後に
最後に草が生えてるか確認しましょう。
以上で、今回のアプリ作成➡︎GitHubへのPushが完了です。
ご覧いただきありがとうございました。
ruby-ffi や Fiddle で、環境依存でサイズが変わる構造体に対処する

## はじめに

FFIで環境依存で変化する構造体に対処することは難しい。Win, Mac, Linuxなど大雑把に場合分けすることも考えられるが、ここでは別の方法として、`sizeof(piyo)` の結果を定数に保存する小さなC拡張を作成することを考える。

## カレンダー日時を保存するtm構造体

ここでは、例として、日時を保存するtm構造体を扱う。tm構造体は、`time.h` をincludeすると使うことができる。
Rubyでは、ffiを扱うライブラリは`fiddle`と`FFI(Ruby-FFI)`の2種類があるが、下記はFiddleを使った例である。

```ruby
    TM = struct [
      'int tm_sec',
      'int tm_min',
      'int tm_hour',
      'int tm_mday',
      'int tm_mon',
      'int tm_year',
      'int tm_wday',
      'int tm_yday',
      'int tm_isdst'
    ]
```

この場合は、TMのサイズは4×9=36である。これはWindowsではうまく動作する。しかし、LinuxやMacでは必ずしもうまくいくとは限らない。

スタック・オーバーフローの質問 [Size of struct tm](https://stackoverflow.com/questions/13969210/size-of-struct-tm)
にあるように、Linuxでは、tm構造体には追加のフィールドが定義されてサイズは56である。TMのサイズは56にしなければならない。

Ubuntuでは `/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h` にtm構造体の定義がある。

```c
/* ISO C `broken-down time' structure.  */
struct tm
{
  int tm_sec;			/* Seconds.	[0-60] (1 leap second) */
  int tm_min;			/* Minutes.	[0-59] */
  int tm_hour;			/* Hours.	[0-23] */
  int tm_mday;			/* Day.		[1-31] */
  int tm_mon;			/* Month.	[0-11] */
  int tm_year;			/* Year	- 1900.  */
  int tm_wday;			/* Day of week.	[0-6] */
  int tm_yday;			/* Days in year.[0-365]	*/
  int tm_isdst;			/* DST.		[-1/0/1]*/

# ifdef	__USE_MISC
  long int tm_gmtoff;		/* Seconds east of UTC.  */
  const char *tm_zone;		/* Timezone abbreviation.  */
# else
  long int __tm_gmtoff;		/* Seconds east of UTC.  */
  const char *__tm_zone;	/* Timezone abbreviation.  */
# endif
};
```

Fiddleで書く場合は、たとえばこんな感じ。

```ruby
    TM = struct [
      'int tm_sec',
      'int tm_min',
      'int tm_hour',
      'int tm_mday',
      'int tm_mon',
      'int tm_year',
      'int tm_wday',
      'int tm_yday',
      'int tm_isdst'
      'long __tm_gmtoff',
      'const char *__tm_zone',
    ]
```

このような環境依存をきちんと解決するためには、C拡張ライブラリを作成して、

```c
#include "tmsize.h"

void
Init_tmsize(void)
{
  struct tm tm;

  rb_require("fiddle");
  ID sym_mFiddle = rb_intern("Fiddle");
  VALUE rb_mFiddle = rb_const_get(rb_cObject, sym_mFiddle);
  rb_define_const(rb_mFiddle, "SIZEOF_STRUCT_TM", INT2FIX(sizeof(s)));
}
```

とすれば、Fiddleモジュールに、定数 `Fiddle::SIZEOF_STRUCT_TM` が定義される。
この定数の値は、Windowsでは36、Linuxでは例えば56になる。
これを利用して

```ruby
require 'tmsize'

ptr = Fiddle::Pointer.new(Fiddle::SIZEOF_STRUCT_TM)
tm = LibUI::FFI::TM.new(ptr)
```

とすればよい。こうすれば、


```ruby
    TM = struct [
      'int tm_sec',
      'int tm_min',
      'int tm_hour',
      'int tm_mday',
      'int tm_mon',
      'int tm_year',
      'int tm_wday',
      'int tm_yday',
      'int tm_isdst'
      # 追加のフィールドは無視
    ]
```

で大丈夫。今回はFiddleで書いたが、ruby-ffiでもだいたい同じようなことができる。

この記事は以上です。
【ＰＨＰ】宇宙船演算子のやり方

```PHP
<?php
// 利益計算
$amounts = array(5000, 10000, 15000);  // 売上
$expense = 10000;  // 経費

foreach ($amounts as $key => $value) {
    switch ($value <=> $expense) {         // ⇐ これが宇宙船演算子
        case -1:
            echo '赤字:';  // 赤字
            break;
        case 0:
            echo '変化なし:';  // 利益なし
            break;
        case 1:
            echo '黒字:';  // 黒字
            break;
        default:
            break;
    }
    echo $value-$expense.PHP_EOL;
}

```

#結果
```
赤字:-5000
変化なし:0
黒字:5000
```
初心者向け）csvファイルをpandasで読み込もうとしたときに発生するエラーの解決方法

## 文字コードの違いなんてよく理解していないし、ほとんど気にもしてないのが初心者の本音（それは私です）

WindowsPCのExcelから出力したcsvファイルを、jupyter notebookなどを利用してpandasで読み込もうとすると'utf-8がなんちゃらで読めないよ'的なエラーが発生しました。

```
import pandas as pd
df = pd.read_csv('nantoka.csv')
```

'nantoka.csv'はExcelから入力してcsv形式で出力したファイルという想定です。
上記だとエラーになりpandasがcsvファイルを正しく読み込まれず、以下のようなエラーメッセージが表示されました。

```
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x95 in position 0: invalid start byte
```

原因はPythonでは'utf-8'という文字コードを扱うことが標準ですが、WindowsPCで動くOffice製品でつくらたファイルは文字コードが'932'だからという違いです。

そういう場合は以下のようなおまじないを付ければエラーになりません。

```
import pandas as pd
df = pd.read_csv('nantoka.csv',encoding='932')
```

###おまじないは encoding='932'
encordingではなくencodingです。（日本人的発音であるエンコードに引っ張られて'r'を付いた'encording'と思い込んでましたが、あくまでエンコディング(encoding)です。私はこのつづり間違いに気づかずしばらく悩みました）

文字コード指定を'encoding="SHIFT-JIS"'としてもうまく動いたりすることもありますが、経験則としてうまく動かない場合もありましたので、'932'と指定した方が確実だと思います。


ひさしぶりにpandasでcsvを扱おうとしたらエラーになったので、忘れないうちに書いときます。

同じ悩みを抱えている方の助けになればよいなーと。

###参考までに
使っているWindows環境の文字コードは何かの理由であえて別のものを指定していないならば、コマンドプロンプトから確認すると'932'と表示されます。

確認するためのコマンドは'chcp'

```
c:\>chcp
現在のコード　ページ: 932
```

現場からは以上です。
Rubyの特異メソッド、クラスメソッドを理解する

Rubyのリファレンスを見るとよく特異メソッドという言葉が出てくるので調べました。
クラスメソッドとも関連があるため併せて記載します。

動作確認環境 ruby 3.0.0p0

## 特異メソッド

まずはリファレンスから。

https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#singleton_method

>特異メソッドとはクラスではなくある特定のオブジェクトに固有のメソッドです。特異メソッドの定義はネストできます。

>クラスの特異メソッドはそのサブクラスにも継承されます。言い替えればクラスの特異メソッドは他のオブジェクト指向システムに>おけるクラスメソッドの働きをすることになります。

>特異メソッド定義式は、メソッド名を Symbol にしたオブジェクトを返します。


Rubyはオブジェクトに対して固有のメソッドを定義できるみたいですね。
犬のしつけに例えてサンプルコードを試してみました。

```ruby
class Dog
  def bark
    'わんわん'
  end
end

pochi = Dog.new
taro = Dog.new

# pochiにお手をしつけ
def pochi.ote
  'わん！（お手をする）'
end

# pochi
pochi.bark # => "わんわん"
pochi.ote # => "わん！（お手をする）"

# taro
taro.bark # => "わんわん"
taro.ote # => undefined method `ote` for #<Dog:0x00007fdb1fb2a730>
# お手してくれない。。

```

`def pochi.ote`のように`def obj.method_name`と書くことで、objに対してメソッドを追加することができる。
このようにオブジェクト特有のメソッドを特異メソッドというみたいです。

## クラスメソッド

Rubyのクラスメソッドは特異メソッドを応用しています。
クラスメソッドは`self.method_name`という形式で書くことが多いと思います。

```ruby
def User
  def self.adult_age
    20
  end
end

```

selfはクラス自身を指すので、Userに置き換えられます。

```ruby
def User
  def User.adult_age
    20
  end
end
```

この形をみると、Userオブジェクトに特異メソッドが追加されたことになります。
「あれ、Userはオブジェクトではないのでは？」と疑問が浮かんで調べてところ、実はUserはClassクラスのオブジェクトになります。

```ruby
User.class
=> Class
```

参考：[プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで Software Design plus](https://www.amazon.co.jp/dp/B077Q8BXHC/ref=cm_sw_em_r_mt_dp_JZBMENHNNT3NSBKK4RGV)　8.5.7 クラスやモジュール自身もオブジェクト

つまり、クラスメソッドとして使っている`self.method_name`の形式は自身のクラスの特異メソッドを定義していることになります。
これにより、Rubyはクラスメソッドを実現しているみたいです。


## 参考サイト

以下参考にさせていただきました。

https://qiita.com/ta-chibana/items/7965d246ab67e14c175a

https://qiita.com/k-penguin-sato/items/d637dced7af32e4ec7c0

https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html

http://blog.livedoor.jp/sasata299/archives/51497378.html

[プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで Software Design plus](https://www.amazon.co.jp/dp/B077Q8BXHC/ref=cm_sw_em_r_mt_dp_JZBMENHNNT3NSBKK4RGV)
8.5.7 クラスやモジュール自身もオブジェクト
機械学習結果を解釈する(PFI編)

# 機械学習を解釈する

機械学習を利用して仕事をしているが、例えばテーブルデータに対して、何らかのモデルを適用して、分類や回帰を行うこと自体は、ライブラリの助けを借りればそれほど難しくない。ただ、予測精度がいくら高くても、「どうしてこうなるの？」「この特徴量を動かしたら結果がどのように変わるの？」という質問には答えにくいところがある。この「結果に対する理由づけ」は近年重要になってきており、私はその技術を学びたかった。

そのとき、出会ったのがこの本。

https://www.amazon.co.jp/機械学習を解釈する技術〜予測力と説明力を両立する実践テクニック-森下-光之助/dp/429712226X/ref=sr_1_1?adgrpid=124198978555&dchild=1&gclid=Cj0KCQjwwNWKBhDAARIsAJ8HkhcI0A1Oif03J5TBn1SnxrVBHNfnMTJCt5KkZvfJgEwlvFzEq6eG8QwaAuxFEALw_wcB&hvadid=526946324295&hvdev=c&hvlocphy=1009279&hvnetw=g&hvqmt=e&hvrand=2093810430843123536&hvtargid=kwd-1305614279162&hydadcr=1979_11027474&jp-ad-ap=0&keywords=機械学習を解釈する技術&qid=1633050794&sr=8-1

めっちゃ良いので、ぜひ読んでください。これでこのエントリを終えても良いのだが、自分の勉強のため、その中で一番最初に出てくる PFI (Permutation Feature Importance) について、簡単に説明を試みる。

# Permutation Feature Importance とは

例えば次のようなデータを考えてみる。

| X1 | X2 | Y |
|-----------:|------------:|------------:|
| 1       | 3    | 10         |
| 2     | 2      | 20       |
| 3       | 5       | 30         |
| 4        | 3          | 40           |
| 5       | 1       | 50       |

X1, X2 から Y を予測するモデルを構築する。
実際にはデータはもっとたくさんあるものと思って欲しい。
Y = 10 * X1 という関係がありそうだ。一方で、X2 と Y の関係ははっきりしない。

このとき、このデータに対してロジスティック回帰なり、ランダムフォレストなり、Gradient Boosting なりの機械学習モデルを適用すれば、おそらくそのモデルは X1 を重視し、X2 をほとんど無視して Y を予測しようとするだろう。

そのことをどう確かめたらよいか？

X1,X2といった説明変数を1つずつランダムにシャッフルして、予測を行い、正解データとの食い違い（損失）を見るというのが一つの方法である。

たとえば X2 を次のようにシャッフルする。

| X1 | X2 | Y |
|-----------:|------------:|------------:|
| 1       | 2    | 10         |
| 2     | 4      | 20       |
| 3       | 1       | 30         |
| 4        | 5          | 40           |
| 5       | 3       | 50       |

だが、もともとX2とYの関係ははっきりしなかったので、こうやってランダムにシャッフルしたうえで予測を行なっても、正解データとの食い違い具合（損失）はたいして変わらないと予想できる（実際に試してみるとそうなる）。

一方でX1を次のようにシャッフルするとどうなるか？

| X1 | X2 | Y |
|-----------:|------------:|------------:|
| 2       | 2    | 10         |
| 5     | 4      | 20       |
| 1       | 1       | 30         |
| 3        | 5          | 40           |
| 4       | 3       | 50       |

いままで Y = 10 * X1 という関係があるように見えていて、それに基づいてモデルが作られていたのに、X1 の並びがランダムになったので、この関係を使って Y を予測できなくなってしまった。そのため、予測時に正解データの食い違い具合（損失）は大きくなることが予想できる（実際そうなる）。

こうやって、説明変数一つ一つについて、ランダムシャッフル前後の損失を比較して、その差（あるいは比）が大きいものほど、予測上、重要な変数だと考えることができる。

これが、PFI(Permutation Feature Importance) である。直感的でわかりやすい。

# 終わりに

数式やコードを持ち出さずに簡単な言葉だけでアイディアを説明してみた。繰り返しになるが、詳細は最初に紹介した本を読んでみてほしい。
LambdaEdgeを利用したcognitoのトークン検証

通常、認証確認処理はwebサーバーでやることが多いと思いますが、
グローバルサイトでかつ同時に多量のアクセスが考えられる場合、lambdaEdgeで認証チェックを実装すると
静的なサイト全体に認証がかけられ、かつWEBサーバーにアクセス負荷をかけずに認証確認処理を実装できます。

今回は、cognitoから発行されたid_tokenをでコードして検証するコードをご紹介します。

#Lambda関数でのnpm moduleの利用法
今回、実装するにあたって

- jsonwebtoken
- jwk-to-pem

これら２つのモジュールを利用しています。これらはnpmでinstallを行うのですが、Lambdaコンソール上ではもちろんnpmは実行できません。
このため、まずはlambdaのコンソールからアクション>関数のエクスポートでデプロイパッケージをDLしてください。
DLできたら解凍し、そこにpackage.jsonを追加するなどしてnpm installで上記の２つのモジュールをinstallしてください。

開発についてもローカル環境でエディタを使って行う方が圧倒的に効率的だと思います。

編集完了後は、再度zip圧縮を行いアップロード元から.zipファイルを選択し反映すればよいのですが、
この時、ディレクトリを圧縮するのではなく、エントリポイントになるindex.jsファイルがルート階層に来るように圧縮を行ってください。

#必要な情報
実装するにあたり

- userPoolId
- appClientId
- jwks.json

が必要になります。
cognitoにclientアプリ登録を行うなどして取得しておきます。

#cookieの取得
今回はcookieにid_tokenなどが格納されてくる前提で実装を行います。
まずは、requestから必要なcookieを取得します。

cookieの文字列中にCognitoIdentityServiceProviderの文字列が入っているものを探し、そのcookie文字列をパースしています。
※もし違うnameでcookieに格納している場合は検索する文字列を変更する必要があります。

```js
function parseCookies(request) {
    var list = {};
    var headers = request.headers;

    if (Object.keys(headers).indexOf('cookie') === -1) {
        return list;
    }

    var rc = false;
    for (let i = 0; i < headers.cookie.length; i++) {
        if (headers.cookie[i].value.indexOf("CognitoIdentityServiceProvider") >= 0) {
            rc = headers.cookie[i].value;
            break;
        }
    }
    if (!rc) {
        return list;
    }

    rc.split(';').forEach(function (cookie) {
        var parts = cookie.split('=');
        var key = parts.shift().trim();
        list[key] = decodeURI(parts.join('='));
    });

    return list;
}
```

#jwksからpemへの変換
トークンをデコードするにあたり、jwksをpemに変換する必要があります。
npm installしたjwk-to-pemを利用し、jwksをpemに変換しましょう。

```js
    var jwk = JSON.parse(fs.readFileSync('./jwks.json', 'utf8'));
    var pem = jwkToPem(jwk.keys[0]);
```

#id_tokenをデコードして、各種値を検証する
cookieからid_tokenの値を取得し、それをjwksから変換したpemを使いデコードします。
デコードにはjsonwebtokenを利用しています。

```js
    jwt.verify(id_token, pem, { algorithms: ['RS256'] }, function (err, decodedToken) {
        if (err) {
            // デコードエラー(id_tokenの期限切れはここに入ります)
            // TODO:認証ページなどへのリダイレクトレスポンスを返す
　　　　　   return;
        }

        // Audience (aud) 確認
        if (decodedToken.aud !== appClientId) {
            // clientIDの不一致(期限切れはここに入ります)
            // TODO:認証ページなどへのリダイレクトレスポンスを返す
            return;
        }
        // Issuer (iss) 確認
        if (decodedToken.iss !== 'https://cognito-idp.us-east-1.amazonaws.com/' + userPoolId) {
            // userPoolIDの不一致(期限切れはここに入ります)
            // TODO:認証ページなどへのリダイレクトレスポンスを返す
            return;
        }
        // token_use 確認(今回はid)
        if (decodedToken.token_use !== 'id') {
            // userPoolIDの不一致(期限切れはここに入ります)
            // TODO:認証ページなどへのリダイレクトレスポンスを返す
            return;
        }
    });
```
今回はこれらのチェックが通ったものを認証済みとして扱い、それ以外の場合は認証ページへのリダイレクトレスポンスを返します。

#まとめ
本来はrefresh_tokenを使い、id_tokenのリフレッシュまで実装するとなおよいかもしれませんが、
lambdaEdgeで複雑な処理を行うとエラーハンドリングが大変かつコストもかかってしまうので、
そのあたりはclientで実装してもらったりすると良いかと思います。
[リファクタリング]　ポートフォリオの検索機能　クラスメソッド

<h2>初めに</h2>
現役エンジニアの方からPFのフィードバックをいただきましたので、
リファクタリングを進めていきます。

実装している検索機能は以下の通りです。

![スクリーンショット 2021-10-01 10.08.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/e6268f8f-0d96-3c8a-dd44-e7ce53cfadef.png)

<h2>元々のコード</h2>

```ruby:models/post.rb
def self.search(search)
  return Post.all unless search
  Post.where('title LIKE(?)', "%#{search}%").or(Post.where('content LIKE(?)', "%#{search}%"))
end
```

<h2>修正後</h2>

```ruby:models/post.rb
def self.search(search)
  return all unless search
  where('title LIKE(?)', "%#{search}%").or(where('content LIKE(?)', "%#{search}%"))
end
```
どこが変わったかというとPOSTの記載はことごとく消えています。

まず```return Post.all unless search```ですが、
```return　all unless search```に書き換えられ、
```Post.where('title LIKE(?)```は```where('title LIKE(?)'```に
```or(Post.where```も、```or(where(```に書き換わっています。


search メソッドというのは、 Post クラスに定義されており、
search メソッドの実行主体は Post になり、実行主体が同じなので省略できます。
そしてsearch メソッドのなかで実行している 
where(...) というメソッドの実行主体も Post になりますので省略可能です。

今回の場合Postがクラスで、search メソッドも、whereメソッド（where(...)）も、
POSTを主体としたクラスメソッドになるため省略ができます。

<h5>他の箇所も同様に修正してみます</h5>


<h2>修正前</h2>

```ruby:group.rb
 # 検索
  def self.search(search)
    return Group.all unless search

    Group.where('name LIKE(?)', "%#{search}%").or(Group.where('introduction LIKE(?)', "%#{search}%")).
      or(Group.where('status LIKE(?)', "%#{search}%"))
  end
end
```
<h2>修正後</h2>

```ruby:group.rb
# 検索
  def self.search(search)
    return all unless search
    where('name LIKE(?)', "%#{search}%").or(where('introduction LIKE(?)', "%#{search}%")).
      or(where('status LIKE(?)', "%#{search}%"))
  end
end
```

<h2>修正後</h2>
```ruby:user.rb
def self.search(search)
    return all unless search
    where('name LIKE(?)', "%#{search}%").
      or(where('introduction LIKE ?', "%#{search}%")).
      or(where(department_id: search)).or(where(join_year: search))
  end
```
「use "resolutions" to ensure only one version is installed.」←これに従っても解決できない場合

下記のようなエラーがでたため、package.jsonに`resolutions:{}`追記したけど解決しなくて頭抱えてたけど、`NODE_ENV=staging`を消したら直った。
こんな事する人あんまいないと思うけど、全然ググっても出てこなかったのでメモ。

```
vendor.js?61b0eac928c8c8d9443a:65 Uncaught Error: Cannot use e "__Schema" from another module or realm.

Ensure that there is only one instance of "graphql" in the node_modules
directory. If different versions of "graphql" are the dependencies of other
relied on modules, use "resolutions" to ensure only one version is installed.
```
DataOpsとは？

分析プロジェクトが[本番フェーズに入ると（英語）](https://www.dataiku.com/stories/finexkap-from-raw-data-to-production-7x-faster/)、分析はパフォーマンスを担保しつつ、価値を示すことが求められます。価値を生み出すためには、最新のデータが不可欠です。新鮮なデータがなければ、[ダッシュボード（英語）](https://blog.dataiku.com/step-aside-dashboards-make-way-for-data-storytelling)は古い情報を示すことになり、モデルの予測結果もうまく活用できないものになるため、エンドユーザーの信頼を失うことになります。そのため、本番データを、必要なときにA地点からB地点に確実に届けるための分野が存在します。これをDataOps（データオプス）と呼びます。
<img width="450" alt="AB image_small.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196881/56db6c18-f5e6-5995-9e57-1e1b59fef4f5.png">

DataOps（data operationsの略で、[DevOpsと混同しないように気を付けてください（英語）](https://blog.dataiku.com/the-role-of-dataops-in-data-science)）とは、組織がデータを本番環境に格納して保持することを可能にする決まり事であり、プロセスであり、技術でもあります。通常は分析プロジェクトのためのものです。分析プロジェクトには、本番用のレポートやダッシュボード、予測を行うためのAIや機械学習などがあります。分析は、組織がより良い情報に基づいた意思決定を行い、より魅力的で価値のある顧客体験を実現するのに役立ちます。どのような種類の分析プロジェクトでも、データの品質と新鮮さが、分析結果の活用を成功させるには不可欠です。
[→Dataikuがどのようにして企業でのDataOpsをスムーズにするかについてはこちらをご覧ください。（英語）](https://blog.dataiku.com/cs/c/?cta_guid=c95eeb26-6aae-40b8-a5ec-7c73da25aeec&signature=AAH58kEsB21AIYSPBTwnMWvBaVGmDZZK8A&pageId=49286885180&placement_guid=ae98a096-1d42-4b98-b91d-4f79c6501db7&click=4941ad12-8be2-4450-848e-78c8131fe710&hsutk=7cf29593dfb8dfaddb981500e9719055&canon=https%3A%2F%2Fblog.dataiku.com%2Fwhat-is-dataops&portal_id=2123903&redirect_url=APefjpHxQWZ0mvFUUTHQlXg4L97jiV-mgg-Vs4LEIQb4tdMgAh6ZccFQj7dWr7Hg_snm_1tAB_PIazwu2A96HDLuEkerFP01YotaTZ2GRieuRhTogVKQ2ytnPM1IbJfaO-F6vSzAD0pQi5S0uD0M5iMbxgtoWQwerSTHyepbWs2UXk_kMPlQB-vUDfkLIu8UDUU17aRH-la5TnXwIptIiPAKytqIK24C0MENhPRXzAOSacdq-KcTwmvMUyZ0ec0NdhuXNH1R7SKA&__hstc=186155446.7cf29593dfb8dfaddb981500e9719055.1628397523657.1633047742668.1633050347262.191&__hssc=186155446.1.1633050347262&__hsfp=1616403723&contentType=blog-post)


##DataOpsの技術
技術的な観点から見ると、DataOpsは、本番環境で使う分析データの提供を自動化することです。ビジュアルやコードベースのデータパイプラインには、データの接続、結合、使いやすい形に変換するために、一つずつ処理するプロセスが含まれます。DataOpsは、これらのパイプラインのステップを繰り返し実行し、自動化された本番用のパイプラインにします。DataOpsの自動化では、データのばらつきやパイプラインの運用上の問題点を探すために、指標をシステム的に確認していく必要があります。

DataOpsの技術は、プロセスと実績があるものを活用すべきです。パッケージ化された機能や自動化ができれば理想的です。確立されたデータパイプラインの枠組みを活用することで、パイプラインの開発と自動化の作業を減らし、より再現性と信頼性の高い結果を生み出し、本番環境の問題によって生じるダウンタイムを最小限に抑えることができます。


##DataOpsとDataiku
[Dataiku](https://www.dataiku.com/ja/製品/)は、データサイエンスと分析のプラットフォームとしてよく知られています。さらに、Dataikuは、活用しやすいデータパイプラインを構築でき、本番環境で必要となるパイプラインの自動化を行う、[DataOps機能](https://www.dataiku.com/ja/製品/の主要機能/dataops/)が含まれています。
![automating and managing data pipelines with dataiku-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196881/fced2cf5-52bd-0b02-bbad-7cf422d604ea.png "Dataikuによるデータパイプラインの自動化と管理(DataOps)")
*Dataikuによるデータパイプラインの自動化と管理(DataOps)*

プロジェクトが開発から本番のフェーズに移行しても、Dataikuを使用することで、素早く本番データに対応付けすることができ、本番環境でプロジェクトをテストし、パイプラインがもつ指標を測定したりデータ品質をチェックしたりしながら、スケジュールや条件に基づくトリガーでパイプラインを実行できます。



##DataOpsについてさらに知りたい方はこちら
DataikuでのDataOpsについては、Dataikuの最高執行責任者兼収益担当者であるRomain Fouache氏によるプレゼンテーションをご覧ください。
[<font color="Teal">**視聴はこちら(英語)**</font>](https://medium.com/data-from-the-trenches/automatic-feature-engineering-an-event-driven-approach-b2ca09d166f)


<br>
原文： [*What Is DataOps?*] (https://blog.dataiku.com/what-is-dataops)
【エラー解決】ERROR in Entry module not found: Error: Can't resolve './src'

アプリ開発をしている中で、`yarn start`をしても下記のようなエラーが発生して`yarn start`ができなくなったので、解決法を探しました。

```
ERROR in Entry module not found: Error: Can't resolve './src'
```

# package.json
何かあれこれいじってるうちに、package.jsonのscripts部分を下記のように変更してしまっていました。

```
  "scripts": {
    "start": "webpack-dev-server --content-base src --mode development --inline",
  },
```

なので、[こちら](https://qiita.com/Yuki-k-lion/items/21e8f68489a6b1ca5565)の記事を参考に、

```
   "start": "react-scripts start",
```

このように戻し、`yarn start`すると、ターミナル上に「バグを直すためにこれこれやってください」という手順が示されました（スクショ撮り忘れました、、）。

# バグ修正でやったこと
ざっくりとですが、指示に書いてあった内容を振り返ると、

- `node_modules{のフォルダを削除
- `yarn.lock`ファイルを削除
- ターミナルで`yarn`を実行
- そして`yarn start`

以上のような手順でエラーを消すことが出来ました。

# まとめ
`package.json`まわりの理解がまだまだ乏しいので、しっかりどこで何が行われているのかを把握していきたいと思います。
e-statの研究 その3

#概要

官製lodのe-statを研究してみる。
sparql query editorを書いてみた。
我侭なsparqlを問い合わせて、テーブルで表示するやつ。

#sparql

山形県の総人口

```
PREFIX sdmx-measure: <http://purl.org/linked-data/sdmx/2009/measure#>
PREFIX sdmx-dimension: <http://purl.org/linked-data/sdmx/2009/dimension#>
PREFIX cd-dimension: <http://data.e-stat.go.jp/lod/ontology/crossDomain/dimension/>
PREFIX cd-code: <http://data.e-stat.go.jp/lod/ontology/crossDomain/code/>
PREFIX g00200502-dimension: <http://data.e-stat.go.jp/lod/ontology/g00200502/dimension/>
PREFIX g00200502-code: <http://data.e-stat.go.jp/lod/ontology/g00200502/code/>
PREFIX estat-attribute: <http://data.e-stat.go.jp/lod/ontology/attribute/>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

select *
where {
	?s sdmx-measure:obsValue ?o;
		sdmx-dimension:refArea / dcterms:identifier '06000';
		g00200502-dimension:indicator g00200502-code:indicator-A1101;
		cd-dimension:timePeriod ?time;
		estat-attribute:unitMult / rdfs:label ?unitMult;
		estat-attribute:unitMult / dcterms:identifier ?unitIdent;
		FILTER (lang(?unitMult) = 'ja').
}

```

#サンプルコード


```

function render(json) {
  var config = {
    "selector": "#result"
  }
  d3sparql.htmltable(json, config)
}

function exec() {
  var endpoint = d3.select("#endpoint").property("value")
  var sparql = d3.select("#sparql").property("value")
  d3sparql.query(endpoint, sparql, render)
}



```

#成果物

http://embed.plnkr.co/plunk/hQSnJCeW5UzSGV9F

以上。
エラーの対処方法: Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.

Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.

このようなエラーが出てアプリの実行ができなかった時の対処方法です。

- Android Studio にて「File > Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JDK」の設定を1.8から11に変更する
- Android Studioを再起動する

以上です。
#0 開発環境の準備

#はじめに
これから学習の復習＆マークダウン記法の学習の為に記事を投稿していきます。
誰かの助けれになれば幸いです。
今回はアプリ作成前の準備段階について記事にしていきたいと思います。

#目次
1.AWS Cloud9で新しくIDE(開発環境)を立ち上げる
2.AWS Cloud9での設定
3.GitHubとの連携方法


##1.AWS Cloud9で新しくIDE(開発環境)を立ち上げる
(1).Your environmentsにて[create environments]ボタンをクリック
(2).(Name),(Description)をそれぞれ記述し、[NEXT STEP]のボタンをクリック
(3).Configure settingsにてPlat form 「・Amazon Linux 2」を選択
(4).[NEXT STEP]からの[create environments]ボタンで作成

これでAWS Cloud9にて新しく開発環境を立ち上げる事ができました。
次は、AWS Cloud9での設定を行なっていきたいと思います。

##2.AWS Cloud9での設定
- Project Settingsにて、soft tabs:の値を2へ変更。
- Project Settingsにて、On Save,Strip Whitespaceを☑️追加。

以上が変更点になります。すぐ終わりましたね、では次はGitHubとの連携です！

##3.GitHubとの連携方法
###(1)ターミナルを開く
上部の「Window」→「New Terminal」をクリックする。

###(2)GitHubの接続に必要な鍵の作成
```ruby:Terminal
username:~/environment $ ssh-keygen
```
この後に入力を求められますが、全てEnterで通しましょう。
上記で、秘密鍵と公開鍵の作成ができました。

###(3)作成した公開鍵をGitHubに登録
```ruby:Terminal
username:~/environment $ cat ~/.ssh/id_rsa.pub
```
この下に文字列が表示されるので、最初から最後まで全てコピーしてください。

1.GitHubにて、[Settings]→[SSH and GPG keys]→[New SSH keys]
2.TitleとKeyを記述して、「Add SSH key」をクリックし、公開鍵を登録する

###(4)GitHubコントリビューション設定
```ruby:Terminal
username:~/environment $　git config --global user.name "GitHubのユーザ名"
username:~/environment $　git config --global user.email "GitHubに登録したメールアドレス"
```

こちらで、GitHubのマイページに草を生やす設定ができました。
(※Gitにマージする度にコントリビューションを表示する事ができる。それがいわゆる草が生えている状態に見えるため、草を生やすと言われている。)

##4.Rails、ImageMagick等のセットアップ
###(1)Railsのバージョンをアップする
```ruby
username:~/environment $ gem install rails -v 5.2.5
```
railsのバージョンをインストールし、

```ruby
username:~/environment $ rails -v
Rails 5.2.5
```
実際にインストールされていればOK。

###(2)ImageMagickをインストール
```
username:~/environment $ sudo yum -y install libpng-devel libjpeg-devel libtiff-devel gcc
username:~/environment $ cd
username:~ $ git clone https://github.com/ImageMagick/ImageMagick.git ImageMagick-7.0.11
username:~ $ cd ImageMagick-7.0.11
username:~/ImageMagick-7.0.11 $ ./configure
username:~/ImageMagick-7.0.11 $ make
username:~/ImageMagick-7.0.11 $ sudo make install
```
ImageMagickは画像処理のためのソフトウェアです。画像を扱いたい場合は挿入しましょう。

```
vocstartsoft:~/ImageMagick-7.0.11 (main) $ convert -version
Version: ImageMagick 7.0.11-14 Q16 x86_64 2021-05-17 https://imagemagick.org
Copyright: (C) 1999-2021 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP(4.5) 
Delegates (built-in): jng jpeg png tiff zlib
```
Version: ImageMagick 〜が表示されていれば、インストール完了してます。
以上で、Cloud9の開発環境の準備は終わりです。

##最後に
初めての投稿で見にくく、拙いところもあるかも知れません。
最後まで見てくださった方はありがとうございました😊