OmniAICreator/Qiita-1.07M huggingface dataset:
【RSpec】重複するテストコードを共通化しよう

重複するテストコードはshared_contextやshared_examplesで共通化できる

##rails_helperの設定

下記の記述のコメントアウトを外す

```spec/rails_helper.rb
...
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
...
```

↑を忘れると下記のエラーで怒られる

```
ArgumentError:
  Could not find shared context "hogehoge"
```

##shared_contextで重複するコードをまとめる

spec下にディレクトリを作成して共通化したい処理を記述

```spec/support/contexts/login_setup.rb
RSpec.shared_context 'login setup' do
  before do
    Rails.application.env_config["devise.mapping"] = Devise.mappings[:user]
    Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
    sign_in user
  end
end
```

```include_context 'login setup'```に置き換えるだけ

```spec/requests/XXXXX_spec.rb
RSpec.describe "/XXXXX", type: :request do
  describe 'GET /XXXXX' do
    ...
    include_context 'login setup'
    ...
  end
end
```

##shared_examplesで重複するコードをまとめる

shared_contextのときと同じようにファイルを作成する

```spec/support/contexts/login_user.rb
RSpec.shared_examples 'login user' do  
  it 'ログインしているユーザーを取得すること' do
    expect(response).to have_http_status(200)
    ...
  end
end
```

```spec/requests/XXXXX_spec.rb
RSpec.describe "/XXXXX", type: :request do
  describe 'GET /XXXXX' do
    context '@user' do
      it_behaves_like 'login user'
    end
  end
end
```

##include_examplesとit_behaves_likeの違い
shared_examplesを使うときは```include_examples```より```it_behaves_like```を使うほうがよい

include_examplesは定義の上書きが起きてしまうらしい
詳しくは↓

https://qiita.com/shinoharat/items/0a481bc33d9a5e81012e


##参考

https://techtechmedia.com/it_behaves_like-rspec/

https://chaika.hatenablog.com/entry/2019/05/31/083000
【chart.js(v3.5.1)】でchartjs-plugin-colorschemesを使用する

v3.51では以下のようなエラーで上手く動作しなかった

:::note alert
 Uncaught TypeError: f.defaults.global is undefined
    <anonymous> https://cdn.jsdelivr.net/npm/chartjs-plugin-colorschemes@0.4.0:7
    <anonymous> https://cdn.jsdelivr.net/npm/chartjs-plugin-colorschemes@0.4.0:7
    <anonymous> https://cdn.jsdelivr.net/npm/chartjs-plugin-colorschemes@0.4.0:7
:::

``` javascript:chartjs-plugin-colorschemes.js
 // [1] 1070行目あたり
 var ColorSchemesPlugin = {
        id: 'colorschemes',

        beforeUpdate: function (chart, options) {
            // ↓の行を追加
            var options = chart.config.options.plugins.colorschemes;


/////////////////////////////////////////////////////////////////////////////////

 // [2] 1190行目あたり
 //↓元々の行
 /*Chart.plugins.register(ColorSchemesPlugin);*/
 //↓の行のようにする
 (Chart.plugins || Chart).register(ColorSchemesPlugin);

```

以上の2点を変更すると動作するようになりました。
自称「通販プラットフォーマー」が教えるEC通販サイトオンボーディング②～倉庫編～

この記事は何回かにわたって投稿します。

自称「通販プラットフォーマー」を自負する筆者が、これまでやってきた経験からECで通販やるなら
知っておくとスムーズに開発プロセスに進めることを紹介していきます。

##1回目が気になる方は以下より
https://qiita.com/emi-hayashi/items/ab6040ef70400bfad469

##本セクションでの内容
* 倉庫ロジスティクスシステム（WMS）
* ECと倉庫が連携するためのデータフロー
* 物流オペレーション
* おまけ（筆者流倉庫選定基準）

![通販オンボーディング2image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1706758/47131bbe-5be5-0b41-ac96-f617ed5eec38.png)


##倉庫ロジスティクスシステム（WMS）
「WMS」ってご存知ですか？

Warehouse Management System＝倉庫管理システム　と呼ばれます。
ECサイト側がtoCに向けてシステムを作るのと同様、倉庫側もtoC（実際は企業クライアントのB）に向けたシステムを持っています。

上部イメージのうち、物流倉庫側がもつシステムを指します。そのシステムの役割は以下です。

* クライアント（ECサイト側）との受発注を連携
* 配送業者側のシステムとの集荷・出荷の連携

通販サイトを作る側は、配送商業者側と連携する必要はなく、中間I/FとしてWMSがそれを担ってくれています。
そのため、契約する物流のWMSの仕様を把握する必要があります。

## WMSが提供している主流I/Fはなにか？
各社オリジナルで作るものがあればパッケージを導入していることもあります。
そのため、統一的なものはありませんが、筆者が経験したものだと以下のI/Fが多いと感じます。

* (s)ftpでのファイル送受信型
* http(s)でのAPI連携型
* 管理サイトによる画面操作型

その中でもファイル連携型が多い印象で、APIなどのリアルタイム通信連携は具備してあるが、
利用実績は少ない、というような傾向を感じます。

※ECが出店モールという形で提供している企業は、ほぼ管理サイトかAPIを提供しているので、あくまでこれは直接物流と連携する場合としてご留意ください。

## ECと倉庫が連携するためのデータフロー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1706758/d5748e36-d4e6-815e-7628-58d5fe6c4e7c.png)
ざっくりイメージとして、双方向で図のようなデータを連携していきます。
取り扱う商材や事業スキームに応じて、サイクルを決めていくような感じです。

物理的なモノがない商材の場合には、ECサイトの中だけでデータ管理でよいですが、
モノを保管する・配送する　というセクションが分かれる場合には、関連する全システムに対してほぼ同じデータを同期するような感じで構成していきます。

## 物流オペレーション
よく使う共通言語をご紹介します！倉庫選定のときやWMSと連携するときに知っておかないといけない単語です。

* 入庫/入庫確定
    * 商品を倉庫に納品すること。また納品したことの結果を返すこと
* 出庫 /出庫確定
    * 商品を配送したこと。
* 検品
    * 荷受け時にダンボールを開けて商品の状態のチェックや数をカウントすること
* 保管
    * 商品を倉庫に保管すること
* 伝票
    * 商品を入荷・出荷するときのダンボールに添付されている荷札のこと。
* 納品書
    * 出荷時のダンボールの中に同梱する商品の明細書のこと。
* その他プチノウハウ
    * パレット：商品保管時の尺度となる面積を指す。
    * 〇期制：保管時にかかるコストを月に分割して請求するサイクルのこと（3期制なら10日締めで3回にわたって請求）
 
その他にもいろんな用語がありますが、あとはぐぐれば解決です:point_up:

今回は倉庫周りのシステム連携とそれを決定するために必要なデータフローと用語をお伝えしました！
何か参考になると幸いです！。

第3回目は、実際にローンチするまでに必要な開発物とプロセスを説明していきます:writing_hand:


## おまけ　筆者流倉庫選定基準
ECサイトでCtoBtoCではないビジネスの場合、荷物を保管・配送するという物流倉庫との連携が必要不可欠です。
そのため、プランナーになった場合には、いかにビジネスに適合する物流倉庫の正しい選定が必要になります。

私自身も倉庫選びをする機会がいくつかあり、評価ポイントとして見えた点があるので、ご参考として紹介します。

### 選定優先度
①場所（高速に面しているか、働く人環境満足度が高いか）
②取り扱い商品の倉庫インフラ環境（清潔さ、温度湿度、クリーニング、補修）
③入庫出庫処理のオペレーション品質
④システム連携ができるロジスティクス化対応可否
⑤値段

のような感じです。
システムだけの世界を見るとついつい③④の自動化とコストに目線をあてがちですが、実際はその上位にビジネスが達成でき、長期間にわたり利用できるかの視点で見るべき、と考えています。
（方針や予算などにも左右されるので、ここは筆者の経験則とご理解くださいませ）
Postman でリクエストボディにファイルをアップロードできないときの対処法

# はじめに

Web API のテストクライアントサービスである Postman にて、ファイルをアップロードして POST しようと試みたが、ファイルの添付時に **Couldn't upload file** というエラーが発生した。本記事では、エラーを解消する方法を述べる。[ここ](https://github.com/postmanlabs/postman-app-support/issues/9361)を参考にした。

# エラー発生状況

以下のように Postman で POST メソッドを選択。Body タブの binary を選択し、`Select File` からファイルをアップロードする。

![post.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769545/17021c93-fbab-7c7a-a2c0-6e356a31064b.png)

しかし、**Couldn't upload file** エラーが発生。

![error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769545/41a31795-09a8-a470-6327-f2ca941b19af.png)

# 対処法 1（失敗）

[対処法 1](https://github.com/postmanlabs/postman-app-support/issues/9361#issuecomment-742363517) に沿って、画面右上 [Settings] -> [General] にて、**Allow reading files outside working directory** を On にした。しかし、再度アップロードを試みても同じエラーが発生。

![settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769545/d479d1c4-2fbc-a3d7-93a3-d7ac5b73c83e.png)

# 対処法 2（成功）

[対処法 2](https://github.com/postmanlabs/postman-app-support/issues/9361#issuecomment-745060213) にしたがって進める。上記画像を見ると、Location に `~/Postman/files` が指定されているが、実際に自分の環境を確認すると `~/Postman Agent/files` となっていた。ここのディレクトリ名を変更して、`~/Postman/files` と一致するようにしたところ、ファイルをアップロードすることができた。また上記対処法 1 で Allow reading files outside working directory を On にしていれば、自由なディレクトリからファイルも添付できる。

# おわりに

Postman にてファイルをリクエストボディに添付できない場合の対処法について述べた。Postman を使って、さまざまなリクエストのパターンを試していきたい。
算術計算


# 今回の授業のゴール

Python3での，算術演算子，変数を用いた基本的な計算を，
インタラクティブシェル(ターミナルやコマンドプロンプト等)で実行できる．


# この授業資料の前提

とある中学校の授業資料として作成しております．開発環境やカリキュラムなどに関しては[こちら](https://qiita.com/hakukikun/items/ce55fef4fa1ffefb4e6d)を参照してください．


# 加減乗除，剰余，累乗
|算術計算の種類|算術演算子|キー（JISの場合）|
|:-:|:-:|:-:|
|加算(足し算)|+|Shift +「れ」|
|減算(引き算)|-|Shift +「ほ」|
|乗算(掛け算)|*|Shift +「け」|
|除算(割り算)|/|Shift +「め」|
|剰余(余り)|%|Shift +「え・5」|
|累乗(同じ数字を掛ける)|**|Shift +「け」|


# 【実習】

### 【演習1】計算してみよう(授業資料1 演習)
[このページ](https://qiita.com/Rumi_Yamaguchi/items/c6fa44ca0168894ecca1)の演習を解き，スクリーンショットをまなBOXにて提出しましょう．
【JS】相対パスを正規表現でディレクトリ部分とファイル名部分に分ける

# 正規表現を使ってディレクトリとファイル名に分ける

おれおれ、かんたん忘備録。




## 基本

```javascript:script.js
const path = '/acchi/kocchi/socchi/test.html';
const dir = path.replace(/[^/]*$/, ''); // '/acchi/kocchi/socchi/'
const file = path.replace(/.*\//, ''); // 'test.html'
```

`replace(/[^/]*$/, '')` と `replace(/.*\//, '')` が味噌です。


## 応用
ある特定のディレクトリ内の*test.htmlを全てignoreしたい！ってときに、
ファイルを比較するのに使える。使った。もっといい書き方、あるかも。

対象:
 - /acchi/kocchi/socchi/*test.html
 - /acchi/kocchi/socchi/test.html

```javascript:script.js
const targetPathList = [
    '/acchi/kocchi/socchi/test.html',
    '/acchi/kocchi/socchi/_test.html',
    '/acchi/kocchi/socchi/OLDtest.html',
];
const ignorePathList = [
    '/acchi/kocchi/socchi/test.html',
    '/acchi/socchi/test.html', // 増やしていってもok
];

for (let i = 0; i < targetPathList.length; i++) {
    const target_dir = targetPathList[i].replace(/[^/]*$/, '');
    const target_file = targetPathList[i].replace(/.*\//, '');
    let flag = false;
    
    for (let j = 0; j < ignorePathList.length; j++) {
        const ignore_dir = ignorePathList[j].replace(/[^/]*$/, '');
        const ignore_file = ignorePathList[j].replace(/.*\//, '');
        const file_regex = new RegExp(ignore_file + '$'); // = test.html$

        const dir_check = (ignore_dir == target_dir);
        const file_check = target_file.match(file_regex);

        if(dir_check && file_check) {
            flag =  true;
            break;
        }
    }

    if(flag) {
        console.log(targetPathList[i] + 'は、ignore対象ファイルです。');
        // ～～～～～　以下に任意の記述　～～～～～
    }
}


```


### 背景

バックエンドPythonとフロントエンドJSを使って、JSベースの自作FTPソフトを作っているときに
FTP対象外ファイルを特定の条件ではじきたかったので上記に至った。
Salesforce画面に静的画像を表示する（静的リソース・リッチテキストコンポーネント）

# 概要
Salesforce（Lightning）画面中に静的画像を表示する。
<font color="red">本方法は動作保証対象外と思われるため、恒久的な対応として行う場合は別の対応方法を検討してください。</font>
![画面イメージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/ac487ad5-a094-19e0-3ef9-7eeea69cc5da.png)

# 背景
Salesforce の導入提案にて一部の AppExchange については提案採用後に購入となったため、それまでの代替として、該当機能の画面イメージを Salesforce 画面に埋め込み、動作イメージを紹介することとなった。
リッチテキストコンポーネントのみの Lightning コンポーネントを定義し、画像を表示させたが、その時の画像ファイルの扱いで、**表示する画像を静的リソースに登録しなければならない**という考慮事項があったため、備考として手順も含めを本投稿に記載する。

# Salesforceにおけるリッチテキストについて
オブジェクトのカスタム項目としてのリッチテキスト編集フォームには画像挿入ボタンが表示されているが、Lightningコンポーネントのリッチテキスト編集フォームには画像挿入ボタンが表示されていない。よって、そもそもLightningコンポーネントのリッチテキストでは画像表示には対応していないと思われる（動作保証対象外としているのはこのためです）。

##### リッチテキスト項目の編集フォーム
![リッチテキスト項目の編集フォーム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/2ed5b7a6-a42e-5d95-f97e-3f84385e2738.png)

##### リッチテキストコンポーネントの編集フォーム
![リッチテキストコンポーネントの編集フォーム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/922d26a9-aa3d-10c2-3e7e-9b93cc37d8a0.png)

# 静的リソース登録の必要性
リッチテキストフォームに画像を貼り付ける際に、静的リソースに登録したものではなく、ローカルドライブの画像などから貼り付けた場合、作業したアカウントと別のアカウントから参照した際に、正しく表示されない事があるため、静的リソースに登録する必要がある。

# 対応手順
### 1. 画像ファイルの登録（静的リソース化）
設定 → カスタムコード → 静的リソース より、静的リソース画面を表示し、画像ファイルをアップロード・登録する（この時、キャッシュコントロールを「公開」に設定する）。
![静的リソース画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/09745b10-a713-c346-f6b5-230bacca0548.png)

### 2. 画像のコピー
1.でアップロードした画像をブラウザで表示し、右クリックして「コピー」する。
![静的リソース画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/45d3f893-4126-c8dc-dbea-20c5473e94d1.png)
![ブラウザ画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/3fd7c2d6-83f7-1ed8-cd8a-f0b400eef303.png)

### 3. リッチテキストコンポーネントの作成
Lightningページレイアウト画面にて、リッチテキストコンポーネントを配置し
![Image17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/3a0b5db6-4cc8-f1fd-2681-fe0654193c1e.png)
編集フォームにて右クリックし「貼り付け」を選択し、2.にてコピーした画像を設定する。
![Image18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/830afe56-ddca-d480-a79a-72746cd7a7d3.png)
![Image19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/4544b945-fe20-177c-c109-37534f342fed.png)
以降、編集したページを保存し作業完了となる。

# 完成イメージ
![Image20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/dc045562-8593-214c-6fd8-6c2e63844635.png)

# 参考文献
[Salesforce の画面に画像を配置する方法　静的リソース＆リッチテキストコンポーネント](https://sf.forum.circlace.com/articles/627587414294007808)
roslaunchで引数からrvizを起動するかどうか切り替える

launchファイルはそれ単体で使う場合にはrvizを起動して、他のlaunchから呼ばれて、そちらでrvizをするというようなことが多く引数からオンオフの切り替えができると便利です。

以下のように引数guiを定義して、ifで条件分岐によってrvizの起動するかしないかを決めます。

```xml:rviz.launch
<launch>

  <arg name="gui" default="false" />
  
  <group if="$(arg gui)">
    <node name="rviz"
          pkg="rviz" type="rviz"
          args="-d $(find aaaa)/rviz/bbbb.rviz"/>
  </group>

</launch>
```

実行は次のように行います。

- from cmd line

```
# rvizを実行しない場合
roslaunch aaa_pkg rviz.launch 

# rvizを実行する場合
roslaunch aaa_pkg rviz.launch gui:=true
```

- from launch file

```xml:from_launch.lauch
<launch>

<include file="$(find aaa)/launch/rviz.launch">
  <arg name="gui" value="true"/>
</include>

</launch>
```
システムを開発するならスクラッチ開発を選びますか？

デジタル技術と科学技術の現在では、ソフトウェアシステム開発は非常に必要な活動です。ソフトウェアシステム開発とは、簡単に言うと、デジタル技術と情報技術（IT技術）を駆使して、システム・仕組みを構築することで、ユーザーのニーズ・アイデアをさまざまなソフトウェア製品に変換することです。**<a href="https://kaopiz.com/ja-news-system-development-process-2021/">システム開発**</a>は、ビジネスの効率化、時間とコストの節約、データの迅速かつ正確な処理の支援など、一般的なビジネスに多くの大きなメリットをもたらすのです。そのため、ソフトウェア開発はますます人気が高まっていて、デジタル・IT技術を活用した新業態もたくさん出現しています。
システム開発における「スクラッチ開発」という言葉を耳にするでしょう。
スクラッチ開発とはどういう意味か、スクラッチ開発のメリット・デメリットは何か、システム開発を検討している方に開発の形の選択方法を本記事ではご紹介させて頂きます。

**•　スクラッチ開発とは？**
スクラッチ開発とは、既存のシステムを使用せず一からオリジナルのシステムを開発する方法です。非常に柔軟性があり独自のシステムを構築できます。
スクラッチ開発ほどユーザーの希望を柔軟に取り込むのは難しいものの、既存のシステムをそのまま利用するよりは業務を行いやすいシステムに仕上がるでしょう。
また、パッケージ開発という別の開発方法があります。スクラッチ開発とパッケージ開発の違いについて「**<a href="https://kaopiz.com/ja-news-system-development/">スクラッチ開発とパッケージ開発の違いとは？選ぶ基準紹介</a>**」記事をぜひご査収ください。

**•　スクラッチ開発のメリット・デメリット**
スクラッチ開発は一から開発するため、唯一無二のシステムになります。他社との差別化をはかる上で有効な方法です。
・完全オリジナルのシステムができる
・何かの事情でシステムが使えなくなるという心配がない
しかしデメリットもあります。
・初期費用がかかる
・仕上がりまで時間がかかる
・要件定義に時間が必要で発注者側も負担が大きい

**•　システム開発で失敗しない発注先を見つける方法**
システム開発に明るい会社で自社開発が可能であれば問題ありませんが、他社に発注する場合どこへ発注すればよいのか悩む方が多いようです。
いざ稼働を開始してから「問題だらけで使いにくい」とならないよう、注目すべき点をご紹介します。
①　コミュニケーション力
スクラッチ開発では特に「要件定義」の段階に時間がかかり、発注者の希望をどれだけ正確に聞き出せるかによって開発内容や進捗に差が出ます。システムに関して知識の少ない相手でも、丁寧に説明し聞き取りにじっくり時間をかけられる企業の方がより信頼できる企業だと言えます。不明点や知りたいことがあれば、契約前でもどんどん問い合わせるとよいでしょう。その問い合わせに対しどの程度スピーディーで丁寧な対応ができるかも、コミュニケーション能力をはかる一つの方法です。
②　経験と技術力
自社が希望するシステムに近いものを開発した経験がある会社であれば、ある程度のノウハウが身についていると考えられます。また今までの実績を確認することで、現在どの程度の技術力があるかも把握できます。
複数の会社を比較検討するのであれば、各社のサイト内に挙げられている実績を見てみましょう。
③　コストを抑えるなら「オフショア開発」
コスト面が心配であれば「オフショア開発」を検討するとよいでしょう。
「オフショア開発」とは、海外の企業にシステム開発を依頼することです。
国内で開発するのと遜色のないシステムを低価格で実現できる点がメリットです。
「オフショア開発」に興味がある方はこちらで詳しく紹介していますのでご覧ください。
→「<a href="https://kaopiz.com/ja-news-offshore-onshore-nearshore-2021/">**【2021年版】オフショア開発とは？オンショア・ニアショアの違いと最新情報</a>**」

**•　「スクラッチ開発」を選びますか？**
では「スクラッチ開発」を選べばよいのでしょうか？
上記のメリット・デメリットから分かるように、スクラッチ開発は導入までにコストと時間がかかります。会社としての体力がそこまでなく、まとまった金額を用意できない、長くは待てない可能性もあるでしょう。
一方、既存のシステムでも優秀なものはあります。自社業務にあった既存システムがあるかどうかをまず調べてみる必要があるでしょう。パッケージ開発で十分対応可能であれば、コスト・納期の面からもあえてスクラッチ開発を選択する必要性はありません。
しかし、既存のシステムではどうしても対応できない場合に、スクラッチ開発を検討してみましょう。

**•　理想のシステム開発なら「カオピーズ」**
「カオピーズ 」はベトナムに本社を置くシステム開発の会社です。
オフショア開発のメリットである低コストはもちろん、多くの経験とエンジニアの豊富な知識で満足のいくシステムをご提供いたします。
国内では日本法人「<a href="kaopiz.com">**株式会社カオピーズ**</a>」がベトナムのエンジニアとお客様を仲介し、システム開発をお手伝いいたします。
もちろん日本語にも対応しコミュニケーションも問題ありません。
お客様のニーズに合わせ開発方法に関して適切なアドバイスをさせていただきます。

※弊社株式会社カオピーズは、2014年9月に創業してから7年目を迎え、お客様に高品質な<b><a href="https://kaopiz.com/ja-system-development/">システム開発</a></b>、<b><a href="https://kaopiz.com/ja-digital-transformation">DX推進支援</a></b>、<b><a href="https://kaopiz.com/ja-aws-cloud-service/">AWS導入支援</b></a>、<b><a href="https://kaopiz.com/ja-ai-image-recognition/">AI画像認識</b></a>等の<b><a href="https://prtimes.jp/main/html/rd/p/000000009.000071489.html">オフショア開発サービス</b></a>をリズナーブルな価格で提供しています。お客様のご要望をヒアリングした後、それに見合った開発基盤、プロセス、体制などを提案し、お客様と合意した上、システム開発を行なっていきます。多くの企業様を支え続けてきた300案件以上からくる<b><a href="https://kaopiz.com/ja-technology/">豊富な知見とノウハウ</b></a>で、ご要件に見合ったクラウドサービスの提案からクラウド環境の設計、構築、運用をまでサポートいたします。カオピーズにとって、案件の初期のみならず、開発期間中に出てくる課題を解決し、要件変更に対して最適な対策を取るなどが必要です。<br>
ご興味のある方は、ぜひお気軽にお問い合わせください。
いまさらだけどGithub actions上でseleniumを動かしたのでまとめてみた

今回はgithub actions上で「seleniumを動かすことだけ」にスポットを当てます。
用意するのはseleniumとgithub actionsワークフロー。

### selenium
アクセス先のURLを出力するだけです。

```python:test_selenium.py
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import chromedriver_binary

options = Options()
# ヘッドレスモード（Linux上で動かすとき必ずこのモードにしておく）
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.get('https://example.com')
print(driver.current_url)
```

### github actionsワークフロー
`test_selenium.py`を置いているリポジトリにGithub actionsワークフロー`.github/workflows/test_selenium.yml`を作成します。

```yml:test_selenium.yml
name: test-selenium
on: 
  push:
    branches:
      - master
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - 
        name: Checkout Repository
        uses: actions/checkout@v2
      - 
        name: Setup Python
        uses: actions/setup-python@v2
      - 
        name: Install Chrome
        run: |
          sudo wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
          sudo apt update
          sudo apt-get install google-chrome-stable
      - 
        name: Install Package
        run: |
          pip install chromedriver-binary==94.*
          pip install selenium
      - 
        name: Test Selenium
        run: python test_selenium.py
```
`commit`するとActionsでワークフローの実行が確認できます。
[Node.js] コマンドライン引数の取得

# コマンドライン引数の取得
Node.jsプログラムからコマンドライン引数を取得するには、**process**オブジェクトの**process.argv**プロパティを参照する。**process**オブジェクトはnode.jsの実行環境のグローバル変数の1つであるため、モジュールを**require**で読み込む必要がない。また、**process.argv**は、文字列配列になっている。

```javascript:sample.js
for(let i = 0; i < process.argv.length; i++){
     console.log('argv[' + i + '] = ' + process.argv[i]);
}
```
上記の「sample.js」に引数を指定して実行すると以下の結果となる。

```javascript:実行結果
$ node sample.js red green blue
argv[0] = C:\Program Files\nodejs\node.exe
argv[1] = C:\work\workspace\sample.js
argv[2] = red
argv[3] = green
argv[4] = blue
```
これより、argv[0]、argv[1]の要素にはnodeコマンドと実行されるスクリプトのファイルパス、<font color="Red">argv[2]以降の要素にコマンドライン引数が格納されている</font>。



# 「npm run」でのコマンドライン引数
さらに、「npm run」で実行する場合についてもあわせて紹介する。
まず、任意のスクリプトを「npm run」で実行する場合、package.jsonのscriptsプロパティでスクリプトを定義しておく必要がある。これにより「**npm run <スクリプト名>**」で実行できるようになる。

```javascript:package.json
"scripts":{
    "sample": "node sample.js"
}
```

この場合、sample.jsの実行時に引数を渡す場合は以下のように、スクリプト名の後に引数を指定する。

```javascript:
$ npm run sample red green blue
```
要件定義のもう一つの意味（開発者の教育）

# ユーザーからみた要件定義

要件定義は、予算や期間、技術の制約をベースに、顧客の要求から実現可能な項目を定めていくフェーズ。
ここでは、利用を想定したインフラのシステムの非機能要件（ビジネスロジック以外の基盤的な要件）も決める。

これは一般的な理解だと思う。
そして、これはユーザーサイドに立った、作業の意味。

ただ、実はもう一つの側面がある。


# 開発者がユーザーの業務を理解するフェーズ

要件定義は、単純にやることを決めるだけでなく、
事業や業務整理を通して、開発者がユーザーの事業や業務を理解するフェーズでもある。

つまり、開発者の教育期間でもある。

つくるシステムの理解なしに、システムは開発できない。


# ユーザーと開発者、双方の立場を考慮する

よく設計はユーザーの立場に立っていろいろ語られるが、
実は、そこには開発者の認識や行動も当然付随する。

こうした両面の考慮をもって工程を進めないと、ゆくゆく認識不足が考慮漏れをもたらし、
失敗をもたらす。
GASで動的ページのスクレイピング

忘れないようにする自分用のメモ

#はじめに
GASでは静的ページのスクレイピングは簡単にできるが、動的ページのスクレイピングに躓いたのでメモ

静的ページでは`UrlFetchApp.fetch`ですぐに取得できる
動的ページだとエラーが出るか、中身が無いhtmlだけ取ってくる
通常JavaScriptであればちょっと弄くれば動的ページも取得できる
しかしGASだけだと複雑なことは難しい

#なので
外部に頼るしかない

#使うもの
`GAS`, `Parser`, `PhantomJsCloud`, `スクレイピングをする動的ページ(今回はReality)`
テスト用にRealityで活動されているこまいぬさんのフォロワー数を取得してみる(特にファンとかではない)
(https://reality.app/profile/232c82a5)

##PhantomJsCloudとは
GASではできない動的ページのレンダリングを代わりにやってくれるサイト　
レンダリングされたページをスクレイピングして情報を受け取れるようにできる

#やってみる
・まず`GAS`で`Parser`を入れる
`ツール` -> `スクリプトエディタ`で`ライブラリ`を選んだら スクリプトIDを入れる

スクリプトID
`1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw`

・`PhantomJsCloud`に登録してAPIKeyをもらう [phantomjscloud.com](https://phantomjscloud.com/)

ココに書いてある
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2170587/aa730fd4-b9f3-38de-aff7-f117cc446e22.png)

・その後コードを書く

```javascript:forReality.gs
function getRealityFollower() {
  var today = Utilities.formatDate(new Date(), "JST","yyyy/MM/dd");
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName("TEST"); //スプレッドシートの指定
  var lastRow = sheet.getLastRow();
  var row = lastRow + 1;

  var URL = "https://reality.app/profile/232c82a5"; 
  var key = "APIKEY"; //もらったAPIKeyをここにいれる
  var payload = 
      {
        url:URL,
        renderType:"HTML",
        outputAsJson:true};
  
  payload = JSON.stringify(payload);
  payload = encodeURIComponent(payload);

  var url = 'https://phantomjscloud.com/api/browser/v2/'+ key + '/?request=' + payload;
  var response = UrlFetchApp.fetch(url);
  var json = JSON.parse(response.getContentText()); 
  var source = json["content"]["data"];
  var follower = Parser.data(source).from('<span class="profile__followlist--Number">').to('</span>').iterate();

  sheet.getRange(row, 1).setValue(today);
  sheet.getRange(row, 2).setValue(follower[1]);
  var diff = follower[1] - sheet.getRange(lastRow,2).getValue() ;
  sheet.getRange(row, 3).setValue(diff);
}
```

保存したら`トリガー` -> `トリガーを追加`で関数を選ぶ　今回は`getRealityFollower`を選択
そして`イベント`を`時間主導型`にして`日付ベースのタイマー`を選択する　時刻は`午前0時~1時`とした　これで24時間に1回、午前0時~1時の間に実行される
あとは保存して終了

さくっと取得できるようにしたら適当に体裁を整えて入力されるようにする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2170587/3c51627e-76aa-be38-aff2-669e67bc6f98.png)
最終的にこんな感じになった　もう少し凝ってもいいかなと思うが個人で使う分には問題ないと思う
転職活動管理アプリを作ろう⑤（laravel-admin 拡張〜テーブル作成）

前回は [コチラ]()

# laravel-admin 拡張
## Extension をインストール

1. [redis-manager](https://github.com/laravel-admin-extensions/redis-manager)
    - redis の中身を閲覧できる
2. [simplemde](https://github.com/laravel-admin-extensions/simplemde)
    - markdown editor
    - config
        - https://github.com/sparksuite/simplemde-markdown-editor#configuration

## セッション管理を redis に変更
1. ```$ composer require predis/predis```
2. .env ファイルを以下のように修正

    ```
    SESSION_DRIVER=redis
      :
    REDIS_HOST=redis
    REDIS_PASSWORD=null
    REDIS_PORT=6379
    REDIS_CLIENT=predis
    ```

3. サーバー再起動

## 発生したエラー
### Class "Redis" not found
- ```REDIS_CLIENT``` の値が phpredis のままだったため発生。predis に修正して対応した。
    - redis のまま対応しようとすると、Redis 拡張モジュールをインストールする必要がある？（未確認）
        - 詳細は [この方の記事](https://qiita.com/freeneer/items/8162c562337e304b4417) にあるっぽい


# テーブル作成
- テーブル定義については [コチラの記事](https://qiita.com/soso555/items/89acdd45e88d0e0251f1) を参照

## 作り方 (例：Agents テーブル)
1. ```$ php artisan make:migration create_agents_table --create=agents```
2. 作成されたマイグレーションファイルにて、定義したテーブルの構成を記載

    ```php
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('agents', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('top_url');
            $table->string('login_url');
            $table->boolean('active');

            $table->timestamps();
        });
    }
    ```

3. ```$ php artisan migrate```
4. ```$ php artisan make:model Agent```
    - モデルを作成
5. ```$ php artisan admin:make AgentController --model=App\\Models\\Agent```
    - コントローラを作成
    - grid() を [カスタマイズ](https://github.com/z-song/laravel-admin/blob/master/docs/en/model-grid.md) すると、一覧画面の見せ方を変更できる
    - form() を [カスタマイズ](https://github.com/z-song/laravel-admin/blob/master/docs/en/model-form.md) すると、新規登録／更新画面の見せ方を変更できる
6. app/Admin/routes.php に以下ルーティングを追記
    - ```$router->resource('agents', AgentController::class);```
7. http://localhost/admin/agents にアクセスし表示されれば OK



## メニュー追加
### 親メニュー
1. admin 内メニューの「Menu」へ移動
2. 以下内容を入力し submit
    ![hoge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167986/6961d8d6-879b-b7e7-7ce8-22c6e1e6e0fa.png)
3. 再ログインするとサイドバーに反映される

### 子メニュー
1. admin 内メニューの「Menu」へ移動
2. 以下内容を入力し submit
    ![hoge2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167986/43b2987e-a0ef-97ef-9435-a7900c0f1924.png)
3. 再ログインするとサイドバーに反映される
Discord.py ver2.0.0aでボタンの実装をしてみる

Discord.pyがまさかの更新停止。
でもDannyは残してくれた。alpha版のDiscord.py、バージョン2.0.0aを―――――しかし。

**Discord.py 2.0.0aの記事がどこにも載っていない！**

## すること

DiscordのUI機能？のボタンを作ってみる。


## インストールするもの

Python 3.9以上
Discord.py 2.0.0a (alpha)
その他プログラムに必要なものがあればインストール

## インストール手順

Pythonは割愛。

Discord.py 2.0.0aのインストール

```
pip install git+https://github.com/Rapptz/discord.py
```

でok。

## ボタンを作ってみる

とりあえず表示だけ。

```py
from discord.ext import commands
import discord

class ButtonBot(commands.Bot):
    def __init__(self):
        super().__init__(command_prefix="!")

class CreateButton(discord.ui.View):
    def __init__(self):
        super().__init__()
        self.add_item(discord.ui.Button(label="ボタンです"))

bot = ButtonBot()

@bot.command()
async def button(ctx: commands.Context):
    await ctx.send("下にボタンがあるはず", view=CreateButton())

bot.run('TOKEN')
```

こんな感じでできる。

ボタンを押しても何も無いのは寂しい（というか実用性がない）ので、メッセージを折り返し送ってみよう。

```py
class CreateButton(discord.ui.View):
    def __init__(self):
        super().__init__()

    @discord.ui.button(label="ボタンです")
    async def return_message(self, button: discord.ui.Button, interaction: discord.Interaction):
        await interaction.response.send_message("折り返しのメッセージだよ")
```

こんな感じ。

interactionについては
https://discordpy.readthedocs.io/ja/latest/api.html#interaction
を見れば詳しく書いてあります。

ちなみに

```py
await interaction.response.send_message("折り返しのメッセージだよ", ephemeral=True)
```

と、ephemeralパラメータをTrueにするとボタンを押した人だけに見えるメッセージが送られる。すごい。


他にもいろいろ機能があるので、ぜひDiscord.py 2.0.0aをインストールしてみてください！！！（懇願）
また、他の機能を試してみたいけどどうやってプログラミングするか分からないって方はコメントしていただければ反応できます。
Salesforce sObject すべて項目取得メソッド これでSELECT *

```java
/**
 * 共通処理クラス
 * 
 */
public class Utility {
  /**
   * 概要：カステム項目取得
   * @parameter Schema.SObjectType
   */
  public static List<String> getCustomFieldNameList(Schema.SObjectType sObjectType) {
    // 対象オブジェクトのカスタム項目を取得する
    List< String >  fieldNameList = new List<String>();
    
    Map< String, Schema.sObjectField > sObjectFieldMap = sObjectType.getDescribe().fields.getMap();
    System.debug('sObjectFieldMap Start');
     System.debug(sObjectFieldMap);
     System.debug('sObjectFieldMap End');
    for(String fieldName : sObjectFieldMap.keySet()){
      if(fieldName.endswith('__c')){
        fieldNameList.add(fieldName);
      }
    }
    
    return fieldNameList;
  }

  /**
   * 概要：全項目取得
   * @parameter Schema.SObjectType
   */
  public static List<String> getAllFieldNameList(Schema.SObjectType sObjectType) {
    // 対象オブジェクトの全項目を取得する
    List< String > fieldNameList = new List<String>();
    
    Map< String, Schema.sObjectField > sObjectFieldMap = sObjectType.getDescribe().fields.getMap();
    System.debug('sObjectFieldMap Start');
     System.debug(sObjectFieldMap);
     System.debug('sObjectFieldMap End');
    for(String fieldName : sObjectFieldMap.keySet()){
        fieldNameList.add(fieldName);
    }
    
    return fieldNameList;
  }
}
```

```java:＝＝＝＝＝＝　使用方法　＝＝＝＝＝＝
// 項目取得
List<String> fieldNameList = Utility.getAllFieldNameList(RenkeiAddressMaster__c.SObjecttype);

// 検索用SOQL文作成
String strQuery = '';
strQuery = strQuery + ' SELECT ';
strQuery = strQuery + String.join(fieldNameList, ',');
strQuery = strQuery + ' FROM ';
strQuery = strQuery + '     RenkeiAddressMaster__c ';

// SOQL発行
return Database.query(strQuery );
```
[Unity] 重なり合うボタンを処理する

全てのボタンが同じtransformの子になっている前提で、
重なり合っているボタンをこんな風に処理したい
![bandicam-2021-10-14-15-15-55-631.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/01b4b1d5-d3f4-3a2b-1238-508533672e87.gif)

一番簡単なのは、OnClick()で

```
button.gameObject.transform.SetAsFirstSibling();
```
のように最背面に移動してしまうことなのですが、これだと次のボタンが押せるようになる代わりに表示も背面に隠れてしまいます。

そこで、

```
for (int i = 0; i < transform.childCount; ++i)
{
    UnityEngine.UI.Image img = _transform.gameObject.GetComponent<UnityEngine.UI.Image>();
    if (transform.GetChild(i).Equals(_transform))
    {
        img.color = Color.red;
        img.raycastTarget = false;
        transform.GetChild(i).SetAsLastSibling(); // 今回選択したものを最前面に
    }
    else if (!img.raycastTarget) // 前回最前面だったものを最背面にする
    {
        img.color = Color.white;
        img.raycastTarget = true;
        transform.GetChild(i).transform.SetAsFirstSibling();
    }
}
```
のように、今回選択したものは　raycastTarget をfalseにして押せなくしつつ最前面に、前回選択していたものはraycastTarget を元に戻しつつ最背面に送ることで、選択したものを最前面に残しつつ、後ろのボタンを押せるようになります。
【Ruby】<%= %>内でstyle要素を書く方法

こんにちは！テックアカデミーのWebアプリコースを受講している駆け出し高校生エンジニアの安田駿介です。

今回は、「Ruby <%= %>内でstyle要素を書く方法」というテーマで解説していきます。

#誤った書き方

![097f29a41aed890a26dc39803fa42f2f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1903726/c35f761b-3012-36ca-7c76-555cc3ead75e.png)

このような書き方だと、エラーになってしまうはずです。

#正しい書き方
![85bbc78926158bb6f935b36ac0c654a0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1903726/addca737-5b93-91ec-658d-90b821330868.png)

style要素に注目すると、=ではなく、:を使っています。


#開発環境
・Cloud9
・Ruby 3.0.0
・Rails 6.1.3.1
指数バックオフ + ジッターをTypeScriptでやるだけ

## 導入

外部API叩く系のコード、テストで通ってもスケーリングしたら死ぬことがあります
単純に一回に大量に叩いたり、そうでなくてもエラーから復旧したりした時に全員が同期して叩くみたいなことがあると、それはもはやDDoS攻撃だからです

https://cloudplatform-jp.googleblog.com/2016/11/ddos-cre.html


それを解決するのが指数バックオフです
ミスったらいい感じに待ってやり直すというアルゴリズムです

その待つ時間まで一緒だと同期攻撃が繰り返されるだけなので乱数でずらします それがジッターです

GCPのCloudStorageのドキュメントとかでも自分で実装しろと書いてあります 悲しい

https://cloud.google.com/storage/docs/retry-strategy?hl=ja

## 本質

このアルゴリズムの本質は待ち時間です

```expBackoff.ts
    const MIN_OFFSET_MSEC = 2000
    await sleep( (2 ** retries) * MIN_OFFSET_MSEC * ( 1 + Math.random() ) )
```

今回は1回目は2〜4秒、2回目は4〜8秒、3回目は8〜16秒....の中で一様分布にしています
冪乗具合やrandom具合はお好みでアレンジしてください

## コード

関数と最大リトライ数を引数に、指数バックオフを実行する関数です

```expBackoff.ts
const MIN_OFFSET_MSEC = 2000 // 最初は2秒からスタート、それ以降は倍倍で増える

export const expBackOff = async (func: Function, MAX_RETRIES: number) => {
    let retries = 0 // やり直した回数
    let retry = true // やり直すかフラグ
    let res

    while(retry && retries < MAX_RETRIES){
        await sleep( (2 ** retries) * MIN_OFFSET_MSEC * ( 1 + Math.random() ) ) // 本質
        try {
            res = await func() // undefinedは返さないで
            retry = false
        }
        catch (e) {
            console.log("retryCount: ", retries + 1)
            console.log("expbackoff ERROR: ", e)
        }
        retries += 1
    }
    if(res == undefined) {
        throw new Error("Expbackoff failed. retryCount: " + retries)
    }
    return res 
}

async function sleep(msec: number) {
   return new Promise(function(resolve) {
      setTimeout(function() {resolve()}, msec);
   })
}
```

## 使い方

```index.ts
const fetch = require('node-fetch');

const fetchFunc = async () => {　// 指数バックオフさせたい関数を作る
    return await fetch(URL, options).then((response: any) => { // ちゃんとパースしてエラーに昇華させる
        if(isYokunai(response)){ // レスポンスがよくなかった時
            throw new Error(response)
        } else {
            return response
        }
    })
    // return await someGoodApi() // ちゃんとエラーハンドリングされるならこれだけでもよい
}

const response = await expBackOff(fetchFunc, 5).then((res: any) => { // 5回やり直す
    console.log("RESULT: ", res)
    return res
}).catch((e) => {
    console.log("ERROR: ", e)
    return new Error(e) // 好きにハンドリングしてください
})
```

## まとめ

良いスケーリングライフを！
.NETネットワーク1　メモ

参考資料
[.NETネットワーク・プログラミング入門　@IT](https://atmarkit.itmedia.co.jp/fdotnet/special/networkprog/networkprog_01.html)

# ネット基礎1
####WWW
 World Wide NetWorkの略。
WWWはWWWサーバーとWWWクライアントの二種類に分類される

__クライアントとは：__
お客さん。ブログやYouTube、ウェブサイトなどを観覧する側のこと。要するにいつもネットサーフィンをする僕らのこと。

__サーバーとは：__
提供者。ブログやYouTube、ウェブサイトなどを運営する側のこと。
ブロガーやGoogle社やFaceBookなどもサーバー側。

WWWサーバーはネットワーク上にドキュメントを公開。
WWWクライアントはネットワーク上に公開されているWWWサーバーのドキュメントを表示する。
ドキュメントにはテキスト、画像、音声などさまざまな形式のデータを組み込むことが可能となり、
ドキュメントからドキュメントへの移動はインターネット上のどんなドキュメントでも可能なためリンクをたどることで世界中のドキュメントを観覧することができる

####URL
Uniform Resource Locatorの略。
WWWサーバーのドキュメント中にリンク情報(URL)を埋め込むことによって、
世界中のドキュメントへ順次アクセスることができる。

####マークアップ言語
WWWサーバーのドキュメントの記述にはマークアップ言語を使用します。
マークアップ言語はHTML、XML、XHML、SGMLなどがある。
※代表的なのはHTML

マークアップ言語はWWWクライアント側でどのように表示されるかをブラウザに任せているため、
WWWサーバー側とWWWクライアント側での表示が異なる場合があります。
これを回避するためにスタイルシートという機能が追加されました。
スタイルシートの名前はCSS(カスケーディング・スタイルシート)
ホームページの見栄え(デザイン)を細かく指定することができます。

#ネット基礎2
####OSI階層モデル
Open Systems Interconnectionの略。
下記はOSI階層モデルと呼ばれるネットワークの階層構造です。
様々な通信機能を7つの階層に分類し、役割を明確化するためのモデルです。
※モデルとは：全体像を把握するために物事を抽象化して大まかに分類したもののことを言います
![2129583.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1724045/771ff146-0edc-e7de-99a0-ca7ed863d4c2.gif)

####プロトコル
プロトコルとはコンピュータ同士が通信するためのお約束事のことである。
このお約束事はOSI階層モデルの各階層ごとに存在する。
例としては以下の通りである

第7層　アプリケーション層：HTTP、POP、SMTP
第4層　トランスポート層：TCP、UDP
第3層　ネットワーク層：IP
第1層　物理層：イーサーネット

プロトコルはそれぞれの階層ごとにお約束の概念が異なり、
第7層ではデータの種類
第4層では通信方法
第3層ではネットワーク上の住所
これらのお約束事の総称をプロトコルと呼びます。

####TCP/IP
TCPとIPは二つで1セットとして覚えましょう。
IPはインターネット上の住所のことであり、ネットワーク層に当たります。
IPアドレスは各デバイス(パソコンやスマホのこと)ごとに設定されており、住所から住所へとデータを送り合うことによってネットワーク上の通信を行っています。

この送り合う行為に手を貸してくれているのがTCPです。
TCPはどのデータをどのIPアドレスへ届けるかを瞬時に判断して各データを分類してくれています。
現実世界でいうところの郵便局のような役割ですね！

トランスポート層にはTCPの他にもUDPというものがありますが、TCPのように多機能ではないです。
詳細は以下の通り↓
TCP：信頼性が高く、多機能、遅い
UDP：信頼性は高くない、少機能、高速
※この時の信頼性とはデータの順番がちゃんと送った順になっているか、いくつかのデータが紛失していないかなどを基準にしています。

__UDPの用途__
音声や映像などのリアルタイム通信
TPCはデータを送るたびに確認応答をする必要があるので、音声通話が途切れたり聞き取れなかったりすることが多い。
そのため、音声通話ではUDPの方が品質が高まります。

__TCPの用途__
リアルタイム通信を必要としないサービス
ホームページ、メール、ファイルなどのデータを送受信する際に用います。
UDPで通信してしまうとデータが損失してしまう可能性が高く、
リアルタイム通信程の頻繁な通信を必要としていないため、
リアルタイム通信以外の用途での使用ではデータ送受信の確実性がより高いTCPが利用されることが多いです。

続き↓

https://qiita.com/kohaku-dayo/items/c5c0acb5516def64e910
IBMCloud SSL-VPNアクセスできない時

##目的
IBMCloudに対してSSL-VPNで接続し各種設定等を行うが、SSL-VPN(Webブラウザ/MotionPro)できない時に試してほしい事を記載する。

##SSL-VPN接続先
基本的に東京リージョンを使う場合、下記エンドポイントに接続する事が多いと思われる。
しかし、この接続先（東京）は使用者が多く、接続できない事が多々発生する。

* vpn.tok02.softlayer.com

##回避策(1)
[IBMCloud Docs](https://cloud.ibm.com/docs/iaas-vpn?topic=iaas-vpn-available-vpn-endpoints#asia-pacific)には記載がないが、大阪リージョンのエンドポイントを利用すると接続できる場合が多い。

* vpn.osa21.softlayer.com
* vpn.osa22.softlayer.com
* vpn.osa23.softlayer.com

##回避策(2)
Webブラウザ経由で接続している場合、うまくいかない場合がある。
その場合、MotionProクライアントから直接接続する。
Webブラウザ経由でインストールした場合、接続先を指定できないことがあるので、[このページ](https://support.arraynetworks.net/prx/001/http/supportportal.arraynetworks.net/downloads/downloads.html)からダウンロードしてインストールすることで回避可能

[Mac用MotionProクライアント](https://support.arraynetworks.net/prx/001/http/supportportal.arraynetworks.net/downloads/pkg_9_4_0_421/MP_MacOS_1.2.13/MotionProSetup_mac.dmg)
[Windows用MotionProクライアント](https://support.arraynetworks.net/prx/001/http/supportportal.arraynetworks.net/downloads/pkg_9_4_0_421/MP_Windows_1.2.16/MotionProSetup.zip)
Visual StudioサブスクリプションのAzure特典クレジットを代替アカウントで使う

# Visual Studio サブスクリプションの特典クレジット

Visual Studio サブスクリプションには、月単位の Azure 特典クレジットが付与されています。
例えば、Visual Studio Professional なら6000 円/月、Visual Studio Enterprise なら 17000 円/月の特典クレジットを利用できます(2021/10/14現在)。


参考 :  [Visual Studio サブスクライバー向けの月単位の Azure クレジット](
https://azure.microsoft.com/ja-jp/pricing/member-offers/credit-for-visual-studio-subscribers/)



# 代替アカウントとは

代替アカウントを設定すると、Visual Studio サブスクリプションの ID とは別の ID で Azure や Azure DevOps の特典クレジットを利用できます。

例えば、会社のドメインの ID で、Visual Studio サブスクリプション を持っているけれど、それとは別のテスト用 Azure テナントの ID (@xxx.onmicrosoft.com)で Azure クレジット特典を利用したいというような場合に、有効な機能です。

参考 :  [代替 ID ](https://docs.microsoft.com/ja-jp/visualstudio/subscriptions/vs-alternate-identity)


# 代替アカウントでAzureクレジットを利用する手順
### 1. 代替アカウントを設定します
1. [Visual Studio サブスクライバー ポータル の「サブスクリプション」](https://my.visualstudio.com/subscriptions)へアクセスします
2. [代替アカウントの追加]をクリックし、メール アドレスを入力して [追加] をクリックします

### 2. 代替アカウントでAzureクレジットのアクティブ化を実施します

1. Microsoft アカウントや組織アカウントでサインインしているすべてのサービスからサインアウトします
2. ブラウザのキャッシュをクリアします
3. 該当するサブスクリプションのサイトへアクセスします
  * [Visual Studio Enterprise サブスクライバー向けの月単位の Azure クレジット](https://azure.microsoft.com/ja-jp/offers/ms-azr-0063p)
  * [Visual Studio Professional サブスクライバー向けの月単位の Azure クレジット](https://azure.microsoft.com/ja-jp/offers/ms-azr-0059p/)

4. 「有効化する」をクリックします
5. 設定済みの代替アカウント (@xxx.onmicrosoft.com) にてサインインします
6. 画面の案内に沿ってサインアップを実施します

> この手順を実行すると、サインアップで指定したアカウントの存在確認が内部的に実行され、代替アカウントとして紐づいている有効な Visual Studio サブスクリプションが確認できれば、サインアップ成功となるようです。

この結果、Azure ポータルの「サブスクリプション」から確認すると、「Visual Studio xxxxx サブスクリプション」というサブスクリプション名のサブスクリプションが追加されています。

>・Azure のサブスクリプション ID は、Visual Studio サブスクリプションのサブスクリプション ID とは同一ではありませんでした。
>・代替アカウントの特典クレジットは、Visual Studio サブスクリプションが有効である限り、「有効化」してからひと月ごとに自動的に補填されていくそうです。



# まとめ
 - Visual Studio サブスクライバーは、Azure 特典クレジットを利用できます。
 - 代替アカウントを設定することにより、 Visual Studio サブスクライバーとは別のIDでAzure 特典クレジットを利用することができます。
かんたんスマホ で着信音が鳴らなくなったら（サイレントモード解除法）

#現象

ワイモバイルのかんたんスマホ（705KC）で、着信音やメール等の通知音が鳴らなくなるという現象（相談）がありました。

もちろんマナーモードは切ってあります。

設定で着信音量と通知音量を変えようとするとグレーアウトになっていて変えられません。リセットしても駄目。

画面に進入禁止のようなマークがついていて、調べると「サイレントモード」になっていることが分かりました。

https://novlog.me/android/icon-silent-mode/

しかし、705KCはスマホ初心者の方などでも簡単に操作できるよう（そして変な設定をしないよう）省略化されているようで、少なくとも調査した時点でのソフトウェアバージョンでは、設定で変えられませんでした。困った。。

ぐぐってみると同じ現象も起こっているようです

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11242960736

#解決策

結構あれこれ試行錯誤したのですが、結局、**Googleアシスタントを起動して、「サイレントモードをオフにして」と言う**と、あっさりオフにすることが出来ました。:relieved:

なぜサイレントモードがオンになってしまったのかは分かりません。

・・・厳密には技術記事ではありませんが、Qiita初投稿の練習も兼ねて書いてみました。同じ現象で困っている方の参考になれば幸いです。
GitHubへのPushができない？

GitHubへのPushが出来なくなった。
訳も分からず調べていたらなんとなく解決できたのでまとめてみる。

**やりたいこと**
----
コマンドからGitHubへのPushを行いたい。

**エラー内容**
----
remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: Authentication failed for 'https://github.com/taiki003838/markdown-editor.git/'

**調査**
--------
ご丁寧にエラー文にリンクが貼られていたので飛んでみると

**今まではローカルでGitHubにアクセスするにパスワード認証を使用しておりましたが、脆弱性防止のために個人アクセストークン認証に変更になったそうです。**(2021年8月13日にパスワード認証が廃止されます)

![スクリーンショット 2021-07-28 23.02.14 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912560/47bc7595-fb03-8f30-8a9a-7604d3109128.png)

こういう風に変わったみたいですね。ふむふむ


**解決策*
----
GitHubで個人アクセストークンを生成する方法する。
↓こちらにGitHubで個人アクセストークンを生成するまでの手順が記載されています。
https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token

**参考**
---
https://zenn.dev/yuri0427/articles/9587ae6a578ee9

無事解決できてよかった～:relaxed:
ABC143 C - Slimes を解いた

https://atcoder.jp/contests/abc143/tasks/abc143_c

![abc142_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/3b08e9b5-7327-9701-5e80-e748372422b0.png)
![abc142_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/4f5db048-5331-32a7-29a1-ea7b0776bd26.png)

たぶん、そのまま書けば行ける。
イメージとしては S の中から重複をなくした
スリムな文字列を作り直し、その長さを応える

```abc142c_r1.py
N = int(input())
S = list(input())

s = S[0]
if N == 1:
    print(1)
else:
    lis = [s]
    for i in range(1,N):
        if s != S[i]:
            lis.append(S[i])
            s = S[i]
    print(len(lis))
```

以下の記述でも OK

```abc142c_r2.py
#重複要素をカウント
def solv1():
    N = int(input())
    S = input()
    num =""
    cnt = 0
    for s in S:
        if num == s:
            cnt += 1
        num = s
    return N-cnt

#非重複要素をカウント
def solv2():
    N = int(input())
    S = input()
    num =S[0]
    cnt = 1
    for s in S:
        if num != s:
            cnt += 1
        num = s
    return cnt

#print(solv1())#35ms
print(solv2())#31ms
```

やっていることは、S の整形をせずに、
重複、非重複部分だけをカウントするやり方。

このくらいサクッと出来ないとと言われると耳が痛いが、
冒頭の記述の方が楽で早く、正確に書ける気がした。
よく使いそうなDockerコマンドをまとめてみた

よく使うけど、たびたび検索してしまうようなコマンドをまとめていこうと思います。
(メモ帳とかでいいじゃんって思ったりはしますが・・・ｗ)

# 環境

- Windows(10, ホスト)
- Docker Desktop for Windows(20.10.8)

# コマンド
## run（コンテナ実行）
dockerコンテナの`create`と`start`を組み合わせたコマンド

使用例）

```
docker run -itd --name {コンテナ名} -p 9000:9000 {Dockerイメージ}
```

## pull（イメージ取得）
Dockerのイメージをローカルにダウンロードするためのコマンド

主にオフィシャルにあるやつを使ってます。
(それ以外ってどうやってやるんだろう・・・)
[公式イメージ](https://hub.docker.com/search?image_filter=official&type=image)ページ

使用例）

```
docker pull {Dockerイメージ}
```

## exec（コマンド実行）
Dockerコンテナでコマンドを実行するためのコマンド

bash使用例）
ssh接続した時みたいにコンテナ内でコマンドが実行できる

```
docker exec -it {コンテナ名} bash
```

bashしなくてもコマンドが実行できる
ls -la使用例）

```
docker exec -it {コンテナ名} ls -la
```

## cp（コピー）
ホスト⇔コンテナ間でファイルのコピーを行うためのコマンド
コンテナIDは後述の`ps`コマンドで確認する。

```
docker cp {ホスト側のファイル} {コンテナID}:{コンテナ内のパス}
```

## ps（コンテナプロセス確認）
起動中のコンテナの状態を表示するためのコマンド

```
docker ps
```

こんな感じ

```
CONTAINER ID   IMAGE                       COMMAND                  CREATED          STATUS          PORTS                                      NAMES
56819f2c7c72   sonarqube:7.9.5-community   "./bin/run.sh"           46 minutes ago   Up 45 minutes   0.0.0.0:9000->9000/tcp                     sonar
768aa709e478   swaggerapi/swagger-editor   "/docker-entrypoint.…"   5 months ago     Up 3 seconds    80/tcp, 8080/tcp, 0.0.0.0:8085->8085/tcp   wizardly_gagarin
```
ABC142 C - Go to School を解いた

https://atcoder.jp/contests/abc142/tasks/abc142_c

![abc142_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/55f5a872-c7a4-63c2-4714-ad2977cb35b4.png)
![abc142_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/6c321eea-03c9-b9eb-aff6-8d4b0c41ffbd.png)
![abc142_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/6ee967ed-171b-4122-0c12-6a7a1d00c800.png)

特に注意点はないと思う。
そのまま書けばok

```abc142c.py
N = int(input())
A = list(map(int,input().split()))
memo = {}
for i in range(N):
    memo[i] = A[i]
memo = sorted(memo.items(),key=lambda x:x[1])
#print(memo)
ans = []
for a,b in memo:
    ans.append(str(a+1))

print(" ".join(ans))#141ms
```

新規で enumerate を導入してみた。

https://qiita.com/kimisuke0619/items/623fd5eb066479d1b374

更に map も駆使したらすっきりした。

```abc142c.py
def solv():
    N = int(input())
    A = list(map(int,input().split()))
    lis = [0]*N
    for a,b in enumerate(A):
        lis[b-1] = a+1
    print(" ".join(map(str,lis)))

solv()#74ms
```
PHREEQC、PHREEQPYによる溶液の物理化学シミュレーション

#PHREEQC、PHREEQPYとは？
PHREEQC、PHREEQPYは、地球科学分野？で開発された、溶液内のイオンがどのような形態で存在するかをシミュレーションするためのソフトウェアです。
自分の研究の備忘録みたいな感じで作っている記事なので、あんまり親切じゃないかも:point_up:

PHREEQの後についているCとかPYとかは、動かしている言語の名前です。（Rもあります。）
多分PHREEQCが一番メジャーに使われていて、PYとかRは利用者が少ないのかな？なんてイメージですが、絶対RとかPythonでやったほうが便利なので、後々RやPyhton用に記事を発展させるつもりです。

PHREEQシリーズは熱力学的シミュレーションですが、シミュレーションするための基盤になる熱力学的なデータベースが何種類かあります。
多分データベースによって、計算値が若干異なりますが、簡単なシミュレーションをする分にはあまり困らないのでは？？
一応自分はminteq.v4.datを使うことが多いです。
##シミュレーションの例
例えば、リン酸、硫酸マグネシウム、炭酸カルシウムを水に溶かしたとしましょう。

まずpHはどうなるか？酸性のリン酸と弱塩基の炭酸カルシウムがぶつかるので、pHはリン酸と炭酸カルシウムの量によって決まりますよね？

こういったpHの変化はシミュレーション可能です。

またリン酸はカルシウムやマグネシウムと凝集し、塩をつくってしまうので、水溶液中にCaHPO4やMgHPO4が沈殿します。こういった沈殿の量も同時にシミュレーション可能です！

#諸々のソース
[PHREEQCの公式サイト](https://www.usgs.gov/software/phreeqc-version-3)
→PHREEQCはまずココから。
簡単なターミナルやコマンドプロンプトの知識があれば、ここからダウンロードしたPHREEQCで色々出来ます。
C言語知らない自分が使えるので、ご安心ください。

一応GUI付きのソフトウェアも入っているので、それが利用できればPCの知識0でも大丈夫かもしれません。（自分のMacでは動きませんでした。）
ちょっとPython触れるくらいの知識だと、PHREEQCを直接使うのは難しいかも。。

[PHREEQC公式の取り扱い説明書](https://water.usgs.gov/water-resources/software/PHREEQC/documentation/phreeqc3-html/phreeqc3.htm)
→PHREEQCとありますが、多分PHREEQPYもPHREEQRも一緒です。
中身に色々なコマンドがあるので、それを理解するためにコイツは必須です。
特に例1~22まである実装例が超役に立ちます。
最終的には、微生物の反応なども同時に記述できるらしい。。

[PHREEQPYの実装例](https://hatarilabs.com/ih-en/mixing-groundwater-and-seawater-geochemical-modeling-with-phreeqc-and-python-tutorial)
→ちょくちょくお世話になるhatari labのPHREEQPY解説です。ライブラリや使用したプログラムはサイト下部のリンクから手に入ります。
一応pip installでもphreeqpyは入手可能なんですが、このhatari labでは別のソースを使っている気がします。。。
[PHREEQPY公式ドキュメント(pip版)](https://www.phreeqpy.com/contents.html)

[PHREEQRの実装例](https://swilke-geoscience.net/post/phreeqc-mineral-solubility/)


#全体の流れ
以下のテストケースを、自分でなぞれるようになればOKです。
##環境構築
正直あんまり覚えてないですが、公式サイトの**Batch Versions of PHREEQC**からご自分の環境にあったプログラムをインストール、解答してください。多分問題は起きないはず。
####Run PHREEQC
インストールしたファイル内に、RUN PHREEQCというGUIが入っているので、動かしてみてください。（これが動けば一番ラクなはず。。。）
詳細は僕わかんないです。
もし動かない人は次の方法に行きましょう。
###Testフォルダ
このTestフォルダの中に、test.commandというコマンドがあります。
こいつをダブルクリックして動かしてみましょう。
これが、実際のPHREEQCの計算になります。
####test.command
test.commandの中身を理解できれば、もうPHREEQCは使えたも同然です。
詳しい解説はとりあえず飛ばしますが、流れとしては

- 最初に実行するための基本情報が記述してあり、（実行フォルダ名とか）
- 中盤の繰り返し部分では、スクリプトを読み込んで計算を実行しています。
- 計算を実行する部分では、利用データベースとか条件を記述したスクリプトを指定。
- 条件を記述したスクリプトは、test.commandの場合は、examplesフォルダのex1~ex22です。

###シミュレーション内容を記述
testフォルダ内のex1~ex22はシミュレーション内容を記述したテキストファイルです。
中身の詳しい説明は、[公式取説](https://water.usgs.gov/water-resources/software/PHREEQC/documentation/phreeqc3-html/phreeqc3.htm)の例1~22と対応しています。
今後は自分で、このシミュレーション内容を自分好みに書き換えればPHREEQCを使えたも同然です。

#シミュレーションファイルの記述
結局PHREEQCはPythonだろうがRだろうがCだろうが、シミュレーション内容が記述できれば、完了です。（RとかPythonなら結果はグラフにしやすい）
##SOLUTION
[公式](https://water.usgs.gov/water-resources/software/PHREEQC/documentation/phreeqc3-html/phreeqc3-48.htm#50593793_30253)まず何をするにしろ、最初に使用する溶液を定義する必要があります。
その際に使うコマンドがSOLUTION。

ただ全ての分子を記述できるわけではなく（例えばシュウ酸。データベースにない）、その場合は未知の分子として、なにかしらのパラメータを与えて記述する必要あり。
ちなみにminteqのデータベースは、クエン酸（citrate）のデータあり。
 
!　各元素の量を多くしすぎると、エラーが発生する。その場合は、-waterの量を減らす等で対応。
ABC141 C - Attack Survival を解いた

https://atcoder.jp/contests/abc141/tasks/abc141_c

![abc141_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/dd0a35ca-16e3-5acd-6564-ee9b4d96e2e4.png)
![abc141_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/fbbb254d-b83a-ca05-aed1-f9e48996f177.png)
![abc141_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/0bb923a4-6d56-4ea2-862f-c9a94a465e30.png)
![abc141_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/f24025c2-cb8b-8a5e-3854-94d58a77bcdf.png)
![abc141_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/518d3333-4081-a24f-246d-114ca07d5182.png)

そのまま解いた。

```abc141c.py
N,K,Q = map(int,input().split())

memo = {}
for i in range(N):
    memo[i] = 0

for i in range(Q):
    a = int(input())
    memo[a-1] += 1

#print(memo)
for i in memo.keys():
    if K-(Q-memo[i]) > 0:
        print("Yes")
    else:
        print("No")
#208ms
```

collections.counter を使うやり方も面白いが、
使い慣れていないとミスを誘発するかもしれません。
ただ、辞書と違い、counter の中にない値は
error を吐かず、0 を return する使用は便利だと
思いました。

少しずつ慣れていこうと思います。

```abc141c.py
def solv():
    N,K,Q = map(int,input().split())
    A = list(int(input()) for _ in range(Q))
    from collections import Counter
    A = Counter(A)
    for i in range(1,N+1):
        if Q-A[i] >= K:
            print("No")
        else:
            print("Yes")
solv()#194ms
```
Angular - How to Add Base URL?

If you want to setup the website URL like this https://example.com/aaa/bbb, you will get the resources not found problem. For this reason, we need to add base url prefix in Angular project.

# Solution
```js:package.json

// Add this --deploy-url /aaa/bbb/
// Add this --base-href /aaa/bbb/

... skip

ng build --optimization --build-optimizer --configuration production --deploy-url /aaa/bbb/ --base-href /aaa/bbb/

... skip

```

In your web service(IIS, NGINX, APACHE...etc) server, don't forget opened RW module.

# Reference
https://angular.io/cli/build
C#のWindowsフォームアプリケーション（.NET Framework）でコンボボックスの設定

# 概要
デフォルトの状態でコンボボックスを設置するとユーザーが編集可能なコンボボックスになってしまうので、ユーザーが選択だけできるコンボボックスを作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/ee6afc20-8d45-4f1d-eff0-ec3407f288b7.png)

## 作り方
### フォームにコンボボックスを配置
コンボボックスのNameは`EnableComboBox`
今回のコンボボックスの目的はユーザーに「有効」「無効」を選択させること。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/263473c4-97a8-173d-a5ba-663fc3384758.png)

### コード
コンストラクタに初期設定を記載。
SelectedIndexで初期値を設定。
`EnableComboBox.DropDownStyle = ComboBoxStyle.DropDownList;`は、編集不可の設定。

```C#:UserSetForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class UserSetForm : Form
    {
        public UserSetForm()
        {
            InitializeComponent();

            EnableComboBox.Items.Add("有効");
            EnableComboBox.Items.Add("無効");
            EnableComboBox.SelectedIndex = 0;
            EnableComboBox.DropDownStyle = ComboBoxStyle.DropDownList;

        }
    }
}

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/85aa5045-7bcb-264d-d57a-bed3c8a4bb5e.png)
Windows10でSSH鍵を作成して、GitHubに鍵を登録する

# 前提条件

gitをインストールしていること。
gitのユーザ名、メールアドレスをGitHubアカウントと合わせておくこと。

```shell:ターミナル
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
```

[Gitインストール参考サイト](https://prog-8.com/docs/git-env-win)

# WindowsにてSSH鍵を生成する

ターミナルを起動して、コマンド「**ssh-keygen**」を叩く。
3回ほど何も入力せずにEnterを叩く。

```shell:ターミナル
PS C:\Users\XXXX> ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (//.ssh/id_rsa): c:/Users/XXXX/.ssh/id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in c:/Users/XXXX/.ssh/id_rsa.
Your public key has been saved in c:/Users/XXXX/.ssh/id_rsa.pub.
The key fingerprint is:
48:--:--:--:0b:bf:0a:fd:ff:--:--:--:--:--:--:-- XXXX@YOUR SERVER NAME
The key's randomart image is:
+--[ RSA 2048]----+
|        略       |
+-----------------+
```

# SSH公開鍵をGitHubに登録する。

ターミナルを起動してコマンド**「clip < .ssh/id_rsa.pub」**を叩く。
（または、C:\Users\XXXX\.ssh\id_rsa.pubをエディタで開いて内容をコピーする。）

```shell:ターミナル
PS C:\Users\XXXX> clip < .ssh\id_rsa.pub
```

[GitHub](https://github.com)にアクセスして右上アイコンからsettingsをクリックする。
その後SSH and GPG Keys → New SSH Keyをクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533960/e8d49aa9-d5dc-36c6-7337-7b97e3c92d9e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533960/a0c7eed0-b6bb-02e5-c442-90c17b99498f.png)

Titleはわかりやすいクライアント名にする。
Keyにクリップした公開鍵を貼り付ける。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533960/5adf073b-0524-e34e-e6ba-9254eab51ffc.png)

以上でGitHubにSSH鍵の登録することができました。
クライアントで、Gitリモートリポジトリをクローンできるか確認してください。