OmniAICreator/Qiita-1.07M huggingface dataset:
[Ruby]破壊的メソッドと値渡しについて

# はじめに
本記事では、新しく知った`破壊的メソッド`と`値渡し`についてを記述します。
新しく知ったのでアウトプットしますが、
間違えている点があればご指摘いただけますと幸いです。

# 破壊的メソッド
レシーバになる`オブジェクトの値そのものを変更する`メソッド。
それ以降については、
オブジェクトの値が変更され、設定していたオブジェクトの値は破壊されてます。

例

```ruby
food = "apple"
p food.upcase!
p food

# 結果は「APPLE」
```

# 値渡し
私が学習している`Ruby`は`値渡し`になります。
(追記)
コメントにてご指摘いただきましたので、変更します。
正しくは、`参照の値渡し`です。


`値渡し`とは、変数の値をコピーする渡し方になります。
メソッド内で値を変更しようが、結果は変わらないです。

```ruby
def food(name)
    name = orange
    puts name # 結果「orange」
end

name = apple
food(name)

puts name # 結果変わらず「apple」
```

## 破壊的メソッド実行
その場合は、`結果は変わります`。
Ruby自体、一連の流れ全てがオブジェクトであり、
オブジェクトとしてメソッドに引数を渡すためです。

```ruby
def food(name)
    name.upcase!
    puts name # 結果「ORANGE」
end

name = apple
food(name)

puts name # 結果「ORANGE」
```

以上です。

# 終わりに
私的に、かなり難しいところで、
記事にするための理解で、何時間もかけてしまいました。

また、`.object_idメソッド`を使用することで、
オブジェクトが変更されているかどうかを確認することができました。

破壊的メソッドでは、値は変更されているものの、オブジェクトは変更されておらず、
非破壊的メソッドでは、値は変更されていないが、オブジェクトは変更されているという結果でした。

ここについては、もう少し深堀したいと考えています。(沼にハマらない程度に)

以下参考サイトです。
[値渡しと参照渡しの違いを理解する](https://magazine.rubyist.net/articles/0032/0032-CallByValueAndCallByReference.html)
[【Ruby】メソッドの引数は参照の値渡し？](https://kimromi.hatenablog.jp/entry/2015/05/11/204954)
[Rubyの破壊的メソッドについて](https://qiita.com/tentom/items/0164b68dff94702e3880)
[Rubyの破壊的メソッドについて現役エンジニアが解説【初心者向け】](https://techacademy.jp/magazine/19934)

明日も頑張ります！
JP-Secure SiteGuard Lite (SiteGuard Server Edition)の管理コンソールのパスワードをリセットする

JP-Secure SiteGuard Lite（SiteGuard Server Edition）の管理コンソール（WebUI）のユーザー名やパスワードを忘れてログイン出来ない場合は、以下の方法で認証情報をリセットすることができます。

```
# cd /opt/jp-secure/siteguardlite/conf
# cp siteguardlite.htdigest.org siteguardlite.htdigest
# service siteguardlite_admin restart
```

または、以下のコマンドを実行します。
（上記でうまくいかない場合はこちらを実行）
```
# cd /opt/jp-secure/siteguardlite/conf
# make resetadmin
# service siteguardlite_admin restart
```

上記実行後、 https://`SiteGuardのIPアドレスまたはホスト名`:9443/ へアクセスすると、デフォルトのユーザー名(admin)とパスワード(admin)でログインすることができます。 ログイン後、ユーザー名とパスワードの再設定を求められ、各設定を変更することが出来ます。




ユーザー名やパスワードを設定または変更後に設定したはずの認証情報で認証が行えない、前任者の引き継ぎ漏れや情報の記録漏れにより認証情報が無い場合にお試しください。
Ubuntu 20.04LTSでブリッジ接続を作成する


# １．bridge-utilsのインストール
```
$ sudo apt update && sudo apt install -y bridge-utils
```

# ２．Netplanの設定
```bash:/etc/netplan/00-installer-config.yaml
network:
  ethernets:
    eth0:
      dhcp4: false
      dhcp6: false
      accept-ra: false
  bridges:
    br0:
      interfaces: [eth0]
      addresses: [192.168.1.100/24]
      gateway4: 192.168.1.1
      link-local: []
      nameservers:
        addresses: [192.168.1.1]
      parameters:
        forward-delay: 0
        stp: no
      optional: true
  version: 2
```

# ３．設定の適用
```
$ sudo netplan apply
```
qmailに届いたメールをGmailに転送してもSPFで弾かれないようにする



#かくかくしかじか
メールをqmailで受け取り、Gmailに転送して運用しているのですが
qmailに届いたメールによっては転送するとGmailでは弾かれたり
SPFがFAILとなったりしてイマイチ不安定だったので、
そのあたりまだ緩いY!mailにも転送して、メールが届いてないかな？ってときは
Y!mailを見ていたりする運用をしていたのですが
まあこんな運用いまいちでしてね・・
昨今はGmail側の迷惑メールのフィルターがすごく激しいので
そろそろ自前ドメインのメールをGmailで受けたいなら
Google workplaceに移行でもしないといけないんかな？なんて思っていたところでした・・
(むかーし無料で配布してた無料版Google workspace(旧Gsuite)の100アカウント版持ってたりするんですけどねｗｗｗ)

#構成
[送信元メールサーバ]　ー> [自前サーバ qmail]　ｰ>　[Google Gmail]
自前サーバにはqmailが入っており.qmail-defaultですべてをGmailに飛ばしてる

#探す。そしてなんと解決！
そんなこんなで調べてみたら
「Envelope From」というキーワードをみつけ、
qmailでどうしたらいいのかな？と更にググってたらビンゴな記事を発見

https://ameblo.jp/halki/entry-12292187843.html

記事内容もまさしくコレ！

実際には、
・自前ドメインのTXTレコードにSPF設定を書き
・~alias/.qmailファイルに（自分は.qmail-defaultファイルに）

```qmail
|/var/qmail/bin/qmail-inject -f error@mydomain.com -a myaddress@gmail.com

```

error@mydomain.com は転送元のsmtp.mailfromメールアドレス（Return-Pathにもなるので、受け取れるメアド@自身のドメインでいいと思いますが、
default@はやめておきましょう。メールキャッチボールします（泣））
myaddress@gmail.com は転送先の運用しているGmailアドレス

としましたところ、
Gmailに転送したメールのSPFもDKIMもPASSとなりました！

![スクリーンショット 2021-09-23 0.34.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19107/d28541db-b222-0baf-339d-221fcfe516be.png)

これで懸念だった転送でのSPF問題も解決できたんじゃないかな！！
超うれしい！
railsチュートリアル第五章 Userコントローラ

##ユーザー登録: 最初のステップ
この節では、レイアウトとルーティングの取り組みにおける頂点として、```ユーザー登録ページへのルーティングを作成```
```2番目のコントローラを作成```することになります。
これは、```Webサイトでユーザー登録を行えるようにするため```の最初の重要な一歩となります

次の一歩であるユーザーのモデリングは第6章で行い、第7章でユーザー登録が完成。

###Userコントローラ
最初のコントローラである```StaticPagesコントローラを作成```
2番目のコントローラである```Usersコントローラを作成```
```generate```を実行して、現時点での要求である```新規ユーザー用のユーザー登録ページ（スタブ）を持つ```、最も簡単なコントローラを作成
Railsで好まれているRESTアーキテクチャの規約に従い、```新規ユーザー用のアクションをnew```を使う。
```generate controllerの引数にnew```を渡して、```自動的にアクションを作成```してみましょう。
####Usersコントローラの生成

```rb
ubuntu:~/environment/sample_app (filling-in-layout) $ rails generate controller Users new
Running via Spring preloader in process 4431
      create  app/controllers/users_controller.rb
       route  get 'users/new'
      invoke  erb
      create    app/views/users
      create    app/views/users/new.html.erb
      invoke  test_unit
      create    test/controllers/users_controller_test.rb
      invoke  helper
      create    app/helpers/users_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    scss
      create      app/assets/stylesheets/users.scss
```
```new ```アクションを持つ```Usersコントローラ```と、```スタブのユーザービューを作成```します。

####newアクションを持つ最初のUsersコントローラ

```rb
class UsersController < ApplicationController

  def new
  end
end
```

####Users用の最初のnewアクション

```rb
<h1>Users#new</h1>
<p>Find me in app/views/users/new.html.erb</p>
```

####Userページ用の最初のテスト

```rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest

  test "should get new" do
    get users_new_url
    assert_response :success
  end
end
```

```
ubuntu:~/environment/sample_app (filling-in-layout) $ rails t
Running via Spring preloader in process 3245
Started with run options --seed 838

  8/8: [==============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.31576s
8 tests, 17 assertions, 0 failures, 0 errors, 0 skips
```

####演習
1.users_new_urlではなくsignup_pathを使えるようにしてみてください。

```rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest
  test "should get new" do
    get signup_path
    assert_response :success
  end

end
```

2.先ほどの変更を加えたことにより、テストが red になったことを確認してください。

```
ubuntu:~/environment/sample_app (filling-in-layout) $ rails t
Running via Spring preloader in process 3790
Started with run options --seed 60200

ERROR["test_should_get_new", #<Minitest::Reporters::Suite:0x00005650d43b6368 @name="UsersControllerTest">, 1.309806750000007]
 test_should_get_new#UsersControllerTest (1.31s)
NameError:         NameError: undefined local variable or method `signup_path' for #<UsersControllerTest:0x00005650d43b28d0>
            test/controllers/users_controller_test.rb:5:in `block in <class:UsersControllerTest>'

  8/8: [==============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.39807s
8 tests, 16 assertions, 0 failures, 1 errors, 0 skips
```
```NameError: undefined local variable or method `signup_path'  ``` 
signup_pathがないらしい。
ぷよぷよの画像からぷよぷよを抽出する

# ぷよぷよの画面から何色のぷよぷよを抽出する

使いたい人がいるかもしれないのでコードの共有が目的
抽出した上でやることは秘密だが、この記事とコードでできることは

プレイ画像(動画) -> 配置されているぷよぷよのnumpy配列(横6×縦12×色)のplayer数分

# github
https://github.com/daikiclimate/PuyoExtractor
細かいところはこれからリファクタリング。
実装途中なので細かい修正はあるかも
基本的にはこのまま使うことはあまり考えていない。使う動画や画像に合わせて修正してほしい

#　結果
入力
![sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404462/72c90f49-5e55-1fbc-5e25-f7a80fd289bf.jpeg)
プレイヤー１
![player1_img.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404462/631d8812-75a8-692e-56e5-842bbae1037e.jpeg)
プレイヤー２
![player2_img.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404462/b0e165a4-5699-74e0-2242-2440e9fd1334.jpeg)

大体あってそうですね
みどりぷよのテンプレートを適当に選んじゃったからいつも横と繋がっているみたいになっちゃってますねw

# 実装の工夫
こんなぷよの分類如きに機械学習は使えないのでルールベースでノイズに強いうまく行く方法を考えた。

##　色の分類
当初はテンプレート画像との差分でいけるという話があったが、あまり頑健でなかった。
というより差分画像だとピクセル単位の位置ずれが起きた時にがっつり数字が変わってしまうので少し扱いづらかった気がする

そこで、画像をHSV空間に変換し、色をHで表現してから、cv2のcalcHistとcompareHistを使うことで、Hueをヒストグラム化して色を分類します。

上記の方法でタチが悪かったのが、背景。
背景は一定かと思いきや、実はキャラクターが書かれているんですね。

![スクリーンショット 2021-09-22 23.38.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404462/b46e1cb7-b5ec-6fa1-db62-972c1e7381fd.png)

背景にバッチリアミティがいますね。この背景がなんとなく色相を持っていることでたまに誤りが発生します。

どうしようかと思いましたが、背景は色合いは確実に薄いのでHSVのSaturationの薄いのではと思いましたが、背景はSaturationで閾値を切れでした。
ということで、背景だけSaturationで分けています。

## ×の抽出
ぷよが出てくる位置に×があって、しかもこのバツくるくる回るんですよね。テンプレートマッチングだと検出できないし、色だと赤ぷよとかぶるのでどうしたもんかと。今回はこの下にぷよがなければとりあえず空白になることになっていますが、後半のバツの下にぷよがある様なシーンではバグ実装になってます。仕方ない
ESP-WROOM-32　Bluetooth　シリアル接続　備忘録

ESP-WROOM-32をArduinoでパソコンとBluetoothシリアル通信するときの備忘録

使用するスケッチは…例から

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2064800/e408e814-e67c-ae7b-c075-b80fe652b839.png)

「SerialToSerialBT」を選択し、コンパイル、ESP32へダウンロードする。

ソフトの変更部分は無し。

PCとESP32をペアリングする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2064800/fc1d7fea-fd4b-682b-2571-bbaae5a93563.png)
その他のBluetoothオプションを押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2064800/6355e00c-dfd8-517e-db86-0af27b5143d8.png)
すると「Bluetooth設定」がお出まし。
COMポートにESP32を設定する。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2064800/a8e3fbe2-36b9-84bc-527c-c1b4ffc758d5.png)
9600bpsで接続する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2064800/75cc7386-ec38-bca4-7e87-d38ab1f4467f.png)
文字列を送信すると…![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2064800/b6e963e3-e35e-1888-1cb4-ebdbca6a069b.png)
あっさりとBluetoothでPCと接続できた…SHとかR8Cとかだとレジスタ設定からチマチマコーディングしなけりゃ動かなかったのが
嘘みたいに簡単に動くではないか。
AtCoderログ：0120 - ABC 092 B

# 問題

https://atcoder.jp/contests/abc092/tasks/abc092_b

## 問題文
> ある合宿におやつとしてチョコレートが何個か準備されました。合宿は $N$ 人が参加して $D$ 日間行われました。$i$ 人目の参加者 $(1 \le i \le N)$ は合宿の $1,A_i
+1,2A_i+1,\ldots$ 日目にチョコレートを $1$ 個ずつ食べました。その結果、合宿終了後に残っていたチョコレートは $X$ 個となりました。また、合宿の参加者以外がチョコレートを食べることはありませんでした。
> 合宿開始前に準備されていたチョコレートの個数を求めてください。

## 制約
> ・$1 \le N \le 100$
> ・$1 \le D \le 100$
> ・$1 \le X \le 100$
> ・$1 \le A_i \le 100~(1 \le i \le N)$

## 収録されている問題セット
- [AtCoder Problems Boot camp for beginners Easy 100](https://kenkoooo.com/atcoder/#/training/Boot%20camp%20for%20Beginners/1) [#023]

# 回答
## 回答1 (AC)
$i$ 人目の参加者は合宿の $1,A_i+1,2A_i+1,\ldots$ 日目にチョコレートを $1$ 個ずつ食べるので、合宿全体で食べたチョコレートの個数は $\lceil D/A_i \rceil$ 個となります。ここで $\lceil x \rceil$ は実数 $x$ の小数点切り上げを表します (天井関数)。この個数を $i$ について合計すれば全員が食べたチョコレートの合計数になるので、合宿開始前に準備されていたチョコレートの個数はこれに $X$ 個を加えた個数となります。コードは以下のようになりました。

```C++:abc092b-1.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int n, d, x;
  cin >> n >> d >> x;

  vector<int> a(n);
  for ( int i=0; i<n; i++ ) {
    cin >> a.at(i);
  }

  int eaten = 0;
  for ( int i=0; i<n; i++ ) {
    eaten += ceil((float)d/a.at(i));
  }

  cout << x+eaten << endl;
}
```

## 回答2 (AC)
回答1のコードをちょっとだけ改良します。改良1のコードは i に関するループが2回ありますが、今回はまとめることが可能です。コードは以下のようになりました。

```C++:abc092b-2.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int n, d, x;
  cin >> n >> d >> x;

  int a;
  for ( int i=0; i<n; i++ ) {
    cin >> a;
    x += ceil((float)d/a);
  }

  cout << x << endl;
}
```

# 調べたこと
## [AtCoder の解説](https://atcoder.jp/contests/abc092/tasks/abc092_b/editorial) → [コンテスト全体の解説](https://img.atcoder.jp/arc093/editorial.pdf)
回答1と同じ方針でした。

# リンク
## 前後の記事
- 前の記事 → [AtCoderログ：0119 - ABC 219 B](https://qiita.com/RoadLynton/items/b8b6db2682f975ee2945)
kintoneで添付ファイルをルックアップ？する方法（その１）

今回は、kintone で添付ファイルをルックアップするように呼び出してみよう！という方法です。
ファイルはコピーせずに、ファイルを保存しているアプリから情報を取り出して表示するというアプリです。

# 出来上がりイメージ

<iframe width="480" height="360" src="https://www.youtube.com/embed/mLAAHrcrW2I" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# アプリの準備

**ファイルを保存しているアプリのフィールド**

| フィールド種類 | フィールドコード | 備考 |
|:-:|:-:|:-:|
| 添付ファイル  | 添付ファイル  |   |
| 文字列(1行)  | 説明  |   |

**👀ルックアップで呼び出す側のアプリ**

| フィールド種類 | フィールドコード | 備考 |
|:-:|:-:|:-:|
| ルックアップ| ルックアップ  | 設定は後述  |
| 数値 | 画像レコード番号  |   |
|スペース|sp||

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/2cee0842-0920-7f06-0419-1766c5f834a2.png)

**👀ルックアップの設定**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/caa6ce46-75a7-7998-3a15-fe93c7c0bcbe.png)

# JavaScript
**kintone REST API Client**を使います。
`https://unpkg.com/@kintone/rest-api-client@latest/umd/KintoneRestAPIClient.min.js`

## 初期表示

詳細画面、新規登録画面、編集画面表示後のイベントで、ファイルの初期表示します。
画像以外だと壊れますが、気になる方はなにか対策してください👀💦

```js
// 初期表示
kintone.events.on(
  [
    "app.record.detail.show",
    "app.record.create.show",
    "app.record.edit.show",
  ],
  (event) => {
    // スペースフィールドに色々仕込む
    const sp = kintone.app.record.getSpaceElement("sp");
    const img = document.createElement("img");
    const brtag = document.createElement("br");
    const atag = document.createElement("a");
    img.id = "img";
    atag.id = "atag";
    img.style.width = "200px";

    sp?.appendChild(img);
    sp?.appendChild(brtag);
    sp?.appendChild(atag);

    // 画像表示
    showImg(event.record.画像レコード番号.value);

    return event;
  }
);
```

## 画像表示
kintone REST API Client を使って画像表示します。

```js
// 画像を表示する
const showImg = async (rid) => {
  const appId = kintone.app.getLookupTargetAppId("ルックアップ");
  const client = new KintoneRestAPIClient();
  const res = await client.record.getRecord({
    app: appId,
    id: rid,
  });
  const data = await client.file.downloadFile({
    fileKey: res.record.添付ファイル.value[0].fileKey,
  });
  const img = document.getElementById("img");
  const atag = document.getElementById("atag");
  const b = new Blob([data]);

  img.src = URL.createObjectURL(b);
  atag.text = res.record.添付ファイル.value[0].name;
  atag.href = img.src;
  atag.download = res.record.添付ファイル.value[0].name;
};
```

## ルックアップで選択しているファイルを変更した時

```js
// ルックアップした時
kintone.events.on(
  [
    "app.record.edit.change.画像レコード番号",
    "app.record.create.change.画像レコード番号",
  ],
  (event) => {
    // ルックアップで取得し直した添付ファイルアプリ側のレコードID
    const rid = event.record.画像レコード番号.value;
    // 画像表示
    showImg(rid);
    return event;
  }
);
```

# まとめ
画像を表示するだけでAPIを贅沢に2回使ってしまいますが、
本来ならルックアップできない画像がルックアップできているように見えてなんとなく便利そうな気もします。
別アプリに保存した画像を表示したいという方はお試しを👀
【初心者向け】Google Mapの埋め込み

#はじめに
APIキーを取得せずに、WebサイトにGoogle Mapを埋め込む方法を説明します。

#手順
1) GoogleMapを開く。
2) 埋め込みたい場所を検索する。
3) 「共有」ボタンをクリックする。
4) ダイアログが出てくるので、「地図を埋め込む」をクリックする。
5) `<iframe src="https://~~~"></iframe>`というコードが表示されるので、「HTMLをコピー」をクリックして、コードをコピーする。
6) エディターを開き、以下のように地図を埋め込みたいにコードを貼り付ける。

```html:HTML(例)
<div class="map">
<iframe src="https://www.google.com/maps/~~~~~~~" width="600" height="450" style="border:0;" allowfullscreen="" loading="lazy"></iframe>
</div>
```

#おわりに
APIを取得するのに比べて簡単に地図を表示することができますね。
是非試してみてください。
VPSって別にAWSじゃなくてもよくない？①

# VPSって別にAWSじゃなくてもよくない？①

> ぼく「思考停止でAWSを使うのはよくないよね」
> ぼく「VPSプロバイダの視点で考えてみよう」

# 用語
- VPSプロバイダ: VPSサーバの物理的な資産を所有し、エンドユーザに機能を提供する業者
- エンドユーザ: VPSプロバイダにお金払う人
- ユーザコントロール:  利用者数・利用ケースの制限・統制。

# 目次

1. [お金の話](#お金の話)
1. [スケーリングの話](#スケーリングの話)
1. [サポートの話](#サポートの話)
1. [リージョンの話](#リージョンの話)
1. [所見](#所見)

# お金の話

- 仮説
    * VPSプロバイダはお金を稼ぐ
        + サービス開始時の膨大な投資を速やかに回収する
        + エンドユーザに感情的な思い入れは無く、常に高い方に寄せたい
        + 一度作ったら後は最小限のコストで運用したい
    * エンドユーザは安くVPSプロバイダを使う
        + 初期投資リスクをVPSプロバイダに最大限リスク転嫁する
        + VPSプロバイダに感情的な思い入れは無く、常に安い方に飛びつく
        + 一度作ったら後は最小限のコストで運用したい

- VPSプロバイダの収益確保
 * 投資資産の維持コストを支える持続的な収入
 * 相当規模の利用者数の確保、一定数以上の利用者流入、それを下回る利用者流出
 * 既存のサービスに影響が無い設備増強
 * 決済方法の一点集中

- エンドユーザの受益最大化
 * 最小限の投資コスト
 * 為替リスクの回避
 * ロックインの回避
 * サポート切れの回避
 * 決済方法の分散

# スケーリングの話

- 仮説
    * VPSプロバイダはサーバをスケールさせたくない
        + 集約（スケールイン）
            + 収益が減る
        + 転嫁（スケールアウト）
            + 他社に行かれると収益が減る
        + 上方修正（アップサイジング）
            + 収益が増えるが、増設可能な仕組みの維持が必要
        + 下方修正（ダウンサイジング）
            + 収益が減る
    * エンドユーザはサーバをスケールしたい
        + 顧客の事業規模変動への対応
        + 誤算（要件見誤り）への対応

# サポートの話

- 仮説
    * VPSプロバイダはサポートしたくない
    * エンドユーザはサポートしたくない

- VPSプロバイダがサポートをさぼるには
    * OSソフトウェア以上のレイヤのサポートをしない
    * サービスの言語・時差を考慮しない
        + 自社の都合の良い言語・時差に固定
    * エンドユーザ向けのサポートデスクを設置しない
        + オープンコミュニティやフォーラムに責任転嫁
        + QAボットを設置
    * DNSやLBなどのバンドルサービスをしない
        + サービス棲み分けする
        + 他社サービスに責任転嫁

- エンドユーザがサポートをさぼるには
    * 一度運用がスタートしたら、永久的にVPSプロバイダにサポートさせたい
        + 集団の力でVPSプロバイダを叩いてサポートさせたい
        + 法的にサポートから逃げられないようなVPSプロバイダを使いたい

# リージョンの話

- 仮説
    * VPSプロバイダはリージョンを一点集中したい
        + 分散すると管理がめんどくさい
    * エンドユーザはリージョンを分散管理したい
        + リージョンによる法的制約から退避する
        + リージョンによる災害からデータを守る

- VPSプロバイダがリージョンを一点集中するには
    * エンドユーザの住所を制限する
    * 特定のリージョンのIPアドレスに固定する
    * 特定のリージョンに対しての課金金額を操作する

- エンドユーザがリージョンを分散管理するには
    * 語学学習して言語フリーになる
    * 24時間働いて時差フリーになる
    * リージョン分散しているVPSプロバイダを選ぶ
    * スケールアウト先に他のリージョンの会社を選択

# 所見

皆が自前で自宅にサーバ立てて1世帯が1VPSプロバイダになって互いに利用し合えば
両方の立場のメリットを受けられるじゃん！
でも今の初期投資掛かりすぎてそんなん当分無理じゃん！
Pythonの関数名を強調表示し、ログを見やすくする

大規模なプログラムの開発をしていると関数の数が増えて複雑さも増すため、どこからどの関数が呼ばれてどんな処理をしているのか、分かりづらくなってきます。inspectモジュールのcurrentframeを使って工夫すると関数名やクラス名を強調表示し、ログを見やすくすることができます。

# サンプルコード
```python
import inspect

def add(a, b):
    # 関数名表示(start)-------------------------------
    func_name = inspect.currentframe().f_code.co_name
    print('\n' + '-' * len(func_name))
    print(func_name)
    print('-' * len(func_name))
    # -----------------------------------------------
    print('hello')
    c = a + b
    print('a={0},b={1},c={2}'.format(a, b, c))
    # 関数名表示(end)---------------------------------
    func_name_end = func_name  + '(end)'
    print('-' * len(func_name_end))
    print(func_name_end)
    print('-' * len(func_name_end) + '\n')
    # -----------------------------------------------

def multiply(a, b):
    # 関数名表示(start)-------------------------------
    func_name = inspect.currentframe().f_code.co_name
    print('\n' + '-' * len(func_name))
    print(func_name)
    print('-' * len(func_name))
    # -----------------------------------------------
    print('hello')
    c = a
    print('a={0},b={1},c={2}'.format(a, b, c))
    # 関数名表示(end)---------------------------------
    func_name_end = func_name  + '(end)'
    print('-' * len(func_name_end))
    print(func_name_end)
    print('-' * len(func_name_end) + '\n')
    # -----------------------------------------------

if __name__ == '__main__':
    add(1, 2)
    multiply(1, 2)
```

# 実行結果
```
---
add
---
hello
a=1,b=2,c=3
--------
add(end)
--------


--------
multiply
--------
hello
a=1,b=2,c=1
-------------
multiply(end)
-------------

```

この規模だとあまり見やすい！って感じでもないですね。飾り付け等、使い方にもう少し工夫の余地がありそうです。inspectモジュールの他の機能についても調べてみようと思います。
Rustのvectorとイテレータ

Pythonのリストと同じようなものかと思って舐めてかかったら、痛い目にあったのでメモ。

# Rustにおけるvectorとは
- **ヒープ**で確保できる。つまり可変長で値を確保できる。
- 同じ型の値のみで使える。

Pythonでいう``list``オブジェクトに近い？　ただしあちらはヒープでは無いらしい...。


# vectorの作り方

```rust:main.rs
let mut vec = vec![];
// 空のvectorを作成する
// vec! によってベクタ型を作成できる
```
このようにして、空のベクタを作成できる。
型を指定するやり方もあるみたいだが、値を入れればコンパイラが推論してくれるので、この書き方でもOKみたい。
<br>
そして、空のベクタに値を追加する。
``push``を使う。

```rust:main.rs
for i in 0..4
{
    vec.push(i);
}
// vec[0, 1, 2, 3]のように値が追加される
```
``0..4``のように、イテレータが使えると便利。

# vectorの要素へのアクセス
簡単にアクセスするには、添え字と参照でアクセスするのが楽

```rust:main.rs
println!("number is {}", &vec[1]);
// 出力:number is 1
```
<br>
ただし、この時範囲外の添え字を与えてしまうと、プログラムがパニックになり、強制終了してしまう。

安全にアクセスするのであれば、``get()``メソッドを使い、``Option<T>``を取得する。
``Option<T>``は、**値が入っているか(Some(T))、それとも入っていないか(None)**のどちらかを取る。


```rust:main.rs
let num = vec.get(1);
match num
{
    Some(result) => println!("number is {}", result),
    // Some(T)が帰って来たとき、そのTに値が入っている。(ここではresultとしている)
    None => println!("None!")
}

// 出力:number is 1
```

<br>
範囲外の添え字を指定した場合でも、プログラムが強制終了しないで済む。

```rust:main.rs
let num = vec.get(10);
match num
{
    Some(result) => println!("number is {}", result),
    // Some(T)が帰って来たとき、そのTに値が入っている。(ここではresultとしている)
    None => println!("None!")
}

// 出力:None!
```

# vectorのループ
インデックスを指定して回すより、``foreach``のようにして回すほうが楽だろう。
rustでは　

```rust:main.rs
for j in vec.iter()
{
    println!("{}", j);
}
```

```rust:main.rs
for j in &vec
{
    println!("{}", j);
}
```

のように、``iter()``でイテレータを作成するか、``&``で参照することによって``foreach``のようにループを回せる。

**ただし、以下のようにするとvectorの値が借用されて、再利用できなくなるので注意。**

```rust:main.rs
for j in vec
{
    println!("{}", j);
}
```
この文以降に、また``vec``を使おうとすると、エラーが出てしまう。
この形は使わないほうが無難かもしれない。

# 参考
https://doc.rust-jp.rs/book-ja/ch08-01-vectors.html
Azure Functionsでzipデプロイせずに、コンソールからnpmパッケージをインストールする方法 (2021年秋)

Azure Functionsでnpm等のライブラリを使う場合は、ZIPで固めてアップロードする[zipデプロイ](https://docs.microsoft.com/ja-jp/azure/azure-functions/deployment-zip-push)のが一般的ですが、Node.jsに限って言えば Azure ポータル上から直接npm install する方法があります。

※ [Azure Functions (Node.js) で npm パッケージを追加して利用する方法 (コンソール利用)](https://qiita.com/ma2shita/items/e6d395a986bab4d81624) の2021年版です

今回は[is-one](https://www.npmjs.com/package/is-one)という "それは '1' ですか？" を判定してくれるジョークライブラリを使ってみます。[^1]

[^1]: 他のライブラリへの依存が一切無いことから、サンプルとしては秀逸。

## 関数アプリの作成

Azure ポータルから関数アプリを作成します。
今回は `i-need-libs-iesae2cu` という名前で作成しました。以降はこの名前で解説します。

## 関数の作成

`i-need-libs-iesae2cu` 内に関数を作成します。
今回は「開発環境 = ポータル」「テンプレート = HTTP Trigger」で作成しました。そうすると `HttpTrigger1` という関数が作成されます。以降はこの名前を使って解説します。

## 関数アプリの「コンソール」で npm install する

`HttpTrigger1` が作成されたら `i-need-libs-iesae2cu` の管理画面に戻ります。
この管理画面には開発ツールのセクションに「コンソール」があります。これをクリックするとコマンドプロンプトが表示されます。このコマンドプロンプトが `i-need-libs-iesae2cu` 内の環境です。

`D:\home\site\wwwroot\` が `i-need-libs-iesae2cu` のルートとなっており、この中に関数名 = ディレクトリとして作成されています。

```
D:\home\site\wwwroot>ls -lF
total 1
drwxr-xr-x 1 Unknown+User Unknown+Group   0 Sep 22 13:49 HttpTrigger1/
-rw-r--r-- 1 Unknown+User Unknown+Group 141 Sep 22 13:47 host.json
```

ここで `HttpTrigger1` に移動して npm install すれば目的は達成です。

```
D:\home\site\wwwroot>cd HttpTrigger1
D:\home\site\wwwroot\HttpTrigger1>npm init --yes
D:\home\site\wwwroot\HttpTrigger1>npm install is-one
```

`npm init --yes` は `package.json` を作成するためです。関数実行時に `package.json` を求めらるため必須でが、初回の実行でOKです。
以降は `npm install <lib>` でインストール可能です。

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/0f805074-c819-c225-bfed-dc61acd7821b.gif)

### 検証用コードと実行結果

```javascript:index.js
const isOne = require('is-one');

module.exports = async function (context, req) {
    console.log(`1 is ONE? => ${isOne(1)}`);
    console.log(`2 is ONE? => ${isOne(2)}`);
    context.res = {status: 204};
}
```

```実行結果
2021-09-22T14:07:33.128 [Information] Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=XX)
2021-09-22T14:07:33.137 [Information] 1 is ONE? => true
2021-09-22T14:07:33.137 [Information] 2 is ONE? => false
2021-09-22T14:07:33.145 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=XX, Duration=17ms)
```

## ライブラリをメンテナンスする

これもコンソールから npm コマンドでメンテナンスできます。
初期化したければ `node_modules` `package.json` `package-lock.json` を削除すればOKです。

## コンソール操作でよくあるミスやちょっと不便なところ

- CTRL+W でウィンドウを閉じてしまう (^^;;;;
    - 速攻で CTRL+SHIFT+T でウィンドウ回復。
- ~~コピペができない (^^;;;;;~~ → CTRL+V (もしくは CTRL+SHIFT+V) でできる模様

こういった面倒なことがあるため、ある程度大きなものになるようだったらzipデプロイを検討しましょう。

## あとがき



EoT
Active Storageに画像を投稿させようしたらundefined method `upload' for nil:NilClassがでた

EC2にデプロイしたアプリケーションが、Active Storageに画像を投稿するアプリケーションでした。
なので、AWSのS3似画像と動画を保存させる必要がありました。

そこで、AWSのS3の設定をしよとしたら、見事につまりましたので、今回は備忘録として残していきたいと思います。

#現象

アプリにログインをして、画像を投稿しようとしたら、

```
undefined method `upload' for nil:NilClass
```

のエラー画面がでてきました。
Backしてみたところ、画像がリンク切れのときのマークが出てくるようになっている。

前回記事でS3についての物を出した時のように、きちんとAWSのアクセスキーとシークレットアクセスキーをセットして、きちんとS3のコンソールでパブリック・アクセスも許可していて、更にバケットポリシーも問題なく動いていました。

なんで詰まったのかなと思ったので、とりあえず

##アプリケーションそのものを再起動してみたところ無事解決しました。

一回設定したら再起動しないいけないのですね。
知らなかった。
Amazon S3 Access Pointとは

### Amazon S3 Access Pointについて備忘録
9/13の[週刊AWS](https://aws.amazon.com/jp/blogs/news/aws-weekly-20210913/)のアップデートにて、
>Amazon S3 Access PointでAmazon CloudWatchのリクエストメトリクスを利用可能に

という内容がありましたがそもそも**Amazon S3 Access Point**って何？となったので調査内容を備忘録として残します。

現状、担当する案件に大規模なものが無いため使用する機会はありませんが、今後必要となってくる知識だと感じました。

#### Amazon S3 Access Pointとは
>**S3に保存した共有データセットへのアクセス管理が簡単**
S3 の機能である Amazon S3 Access Points は、S3 にデータを保存するあらゆる AWS のサービスやお客様のアプリケーションのデータアクセスを簡素化します。S3 Access Points では、お客様はアクセスポイントごとに固有のアクセスコントロールポリシーを作成し、共有データセットへのアクセスを容易にコントロールできます。

参考サイト：[Amazon S3 Access Point](https://aws.amazon.com/jp/s3/features/access-points/)

#### S3における従来の課題
S3のアクセス制御を行う際は、基本的にバケットポリシーを設定します。
要件毎にバケットポリシーを設定していくと小規模であれば困りませんが、大規模になってくると管理の手間が大きくなります。
このような場合に活躍するのがAmazon S3 Access Pointとなります。

#### Amazon S3 Access Pointを使用するメリット
Amazon S3 Access Pointを使用することで、バケットポリシーを個別に設定するのではなく1つの共有アクセスポイントを作成することでグループ化を行うことが可能となります。
単純に作成の手間が省けるというメリットもありますが、修正を行う際に個別に対応する必要がなくなるというメリットもありますね。
【TypeScript】inferの使い方

inferの基本的な使い方です。

## infer でできること
infer の動作はRustやScalaのパターンマッチに似ています。
型変数を conditional type の extends の中で宣言できます。
宣言した型変数の値はTypeScriptが推論してくれます。

```TypeScript
type R<T> = T extends (...args:any[])=> infer V ? V:never;
```
上記の場合、Tが関数のとき `T extends (...args:any[])=> infer V` はtrueになります。
infer は戻り値の位置に書かれているのでVの値はTの戻り値の型になります。
infer を使って宣言した型変数はconditional typeのtrueの分岐の中で参照できます。

infer を記述する位置を変えることでキャプチャの対象を変更できます。
例えば以下のようにできます。

```TypeScript
// 関数の引数の型
type Args<T> = T extends (...args:infer A) => any ? A:never;

// タプルや配列の最初の要素の型
type First<T extends any[]> = T extends [infer F, ...any[]] ? F:never

// タプルや配列の最後の要素の型
type Last<T extends any[]> = T extends [...any[], infer L] ? L:never;

// Promiseの返値の型
type PromiseValue<T> = T extends Promise<infer V> ? V:never;

```

## infer が使える場所
conditional type の extends の後ろ、? より手前のところです。

```TypeScript
type A<T> = T extends `ここで使える` ? true:false;
```
conditional type 以外では使えません。

```TypeScript
// infer は conditional type の途中でしか使えないので下のような書き方は間違い
type Args<T extends (...args:infer P)=> any> = P
```
[Javascript] キーの同時押しを取得する

# はじめに
Javascriptでゲームを作っていた際、addEventListenerでユーザーのキー入力を取得していたのですが、同時に複数キーが押された時のキーの取得に詰まったので、メモとして残しておきます。

# もともとやっていた方法
addEventListenerについては[こちら](https://www.sejuku.net/blog/57625)

```javascript
//keyが押されたなら
document.addEventListener('keydown', (event) => {
    if(event.key == "ArrowUp"){
        //上キーが押された時の処理
    }
    if(event.key == "ArrowDown"){
        //下キーが押された時の処理
    }
    if(event.key == "ArrowLeft"){
        //左キーが押された時の処理
    }
    if(event.key == "ArrowRight"){
        //右キーが押された時の処理
    }
    
});
```
このままだと、押されたキーの取得は同時には1種類しかできません。

# 改良後

キーが押されているか変数で保持しておけばできます。

```diff_javascript
let up_push = false;
let down_push = false;
let left_push = false;
let right_push = false;

//keyが押されたなら
document.addEventListener('keydown', (event) => {
    if(event.key == "ArrowUp"){
        up_push = true;
    }
    if(event.key == "ArrowDown"){
        down_push = true;
    }
    if(event.key == "ArrowLeft"){
        left_push = true;
    }
    if(event.key == "ArrowRight"){
        right_push = true;
    }
    
});


//keyが離されたなら
document.addEventListener('keyup', (event) => {
    if(event.key == "ArrowUp"){
        up_push = false;
    }
    if(event.key == "ArrowDown"){
        down_push = false;
    }
    if(event.key == "ArrowLeft"){
        left_push = false;
    }
    if(event.key == "ArrowRight"){
        right_push = false;
    }
    
});

function move(){
    if(up_push){
        //上キーが押された時の処理
    }
    if(down_push){
        //下キーが押された時の処理
    }
    if(left_push){
        //左キーが押された時の処理
    }
    if(right_push){
        //右キーが押された時の処理
    }
}
```

お役に立てれば幸いです。
もっと簡単な方法があれば是非教えてください。
DMM WEBCAMPで習得できたスキル

# はじめに

ご紹介が遅くなりました、改めて簡単にわたしの自己紹介したいと思います:blush:
前職は航空業界に新卒で入社し、航空貨物業務に6年間携わり、2021年３月に退職しました。現在、DMM WEBCAMPを卒業し、転職活動をしております。
エンジニアを目指そうとなったきっかけは前職でITを用いた業務改善をいくつか取り組んできました。
*　車両点検電子化
*　ワークフロー電子化
*　iPadを用いた作業効率化

上記の業務改善に携わった際、自分の成果物が誰かの役に立ち、他者への貢献が自身への喜びに感じました。それ以来、「技術力を身につけよりよりものづくりがしたいと感じ世の中に新しいコンテンツを提供し、社会の課題をITを通じて解決していきたい」と思うようになり、この経験を通して2020年9月からプログミング学習を始めました。
今回はDMM WEBCAMPで習得できたスキルや、なぜDMM WEBCAMPを選んだかについて纏めたいと思います。

__※あくまでも個人的主観であるのでこんなことをやるんだなという程度に読んで頂けたら幸いです__

#対象者

* これからプログラミングスクールに通うか検討されている人
* DMM WEB CAMPに興味がある人
* 未経験から転職を考えている人

#DMM WEBCAMPについて

__"キャリアに寄り添うプログラミングスクール"__

https://web-camp.io/

国から専門技術コースは経済産業省の第四次産業革命スキル習得講座に認定され、
厚生労働省指定の専門実践教育訓練給付金制度の対象講座に認定されました。


条件を満たすことで支払った教育訓練経費の最大70％が教育訓練給付金として支給されます。


#何故,DMMWEBCAMPを選んだか

__1.現場で活きる実践的な知識・技術が身に付く__
DMMの特徴として、チームでの開発が期間、1ヶ月で実施できます。内容として、テーブル定義書、アプリケーション定義書、ER図を各々作成しチーム内ですり合わせて全員で議論して進めていきます。チームによって、得意分野不得意分野があるので個人が活かせる分野を引き出せるように一つのアプリを完成を目指します。また、チームでのGit運用もチーム開発ならではIssueやコンフリクトなど学習できた点は非常によかったと思います。

__2.転職成功率98％　学習後のキャリアUPも安心__
現時点ではまだ就職できておりませんが、就職サポートも手厚いと感じております。エンジニア転職のおいての本質的な欲求から探り、なぜエンジニアになりたいのかを自問自答し、将来のビジョンについて改めて考えることができたと思っています。プログラミングスクールと聞いて、ただプログラミング学習するかと思いますが、エンジニアになるまでのサポートがあるのが魅力だと感じます、結果的にこの数字に結びついてくるのではないかと思っております。

__3.未経験者のために開発されたカリキュラム__
独自のカリキュラムですが、３０００回のカイゼンを経て作成されたと伺っております。この数字はどう考えるのか、それは受講者の意見を素直に受け止めて受講者にわかりやすいにカリキュラムであるように最適であると考えられます。自分自身、
書籍だとイメージが掴めず途中でやめてしまうことがありましたが、カリキュラムではスライドや動画が盛り込まれておりとてもわかりやすい内容だと感じてます。また、利点として卒業でもカリキュラムは見ることができるので現在も学習教材として使用しております。

#どんなことを学んだか

__基本的なプログラミング言語への理解__

[フロントエンド]
HTML・CSS・Javascript・jquery

[バックエンド]
Ruby・Ruby on Rails・RSpec,API

[AWS]
VPC・サブネット・S3・CloudWatch・ALB

[AWS]
チーム開発の経験

[その他]
テーブル定義書、アプリケーション定義書、ER図、Git,GitHub,Ajax

#カリキュラムで辛かったこと。
やはりエラーで躓いて何も進まない時が辛いと感じましたね・・・最初はなんで起きているのかわからない状態でただ時間だけを過ごすこともありました。しかし、経験を積み重ねていく内にパターン化されてなんとなくアプローチ方法がわかる時もあります。プログラミングスクールではわからない時は質問環境であるのは非常にありがたいことではありますが、なんでエラーが生じているのか仮説を立ててエラー解決策を遂行しないと一向に上達はできないと感じたのでこれから始めると人はこちらを意識していただければ良いのかと思います:blush:

#最後に
これからプログラミングスクールを検討される方に、この時代情報は飽和されてどの情報が正しいかは個人の向き不向きによって判断されるのが良いかと思います。ただ、自分自身思ったこととして退職前にもっとプラグラミングに打ち込んでおけばよかったなと思うこともあります。退職前にはHTML・CSS・Ruby・Railsをプロゲートでやったことはあったもののエディターを使用して自分で作品をレベルを作れるまでの技術力ラインまで到達していたならもっと選択肢は増えていたのかと思う節々は感じました。最後まで閲覧していただきありがとうございました！！！
C#9のrecord型でEqualsの再定義

C#で独自record同士の比較をしたかったが、ちょっと詰まったので、メモ

## 通常のrecordの場合
基本的にrecordは値型っぽく扱えるので、フィールドが値型であれば、特に何もしなくても値型の比較をしてくれる

```C#

record XXX(int Hoge);
Assert.AreEqual(new XXX(3), new XXX(3)); // true
```

## recordのフィールドに参照型が混じった場合
この場合は、classの時と同様にちょっとコードを書く必要がある

```C#

record XXX(params int[] Hoges);
record YYY(params int[] Hoges) {
    public virtual bool Equals(YYY y) {
        if(y is null)  return false;
        return Hoges.SequenceEqual(y.Hoges);
    }
    public override int GetHashCode()
    {
        HashCode hashcode = new();
        foreach(var h in Hoges) {
            hashcode.Add(h);
        }
        return hashcode.ToHashCode();
    }
}
Assert.AreEqual(new XXX(3), new XXX(3)); // false
Assert.AreEqual(new YYY(3), new YYY(3)); // true
```

ポイントはEqualsはvirtualで定義するところ。なぜvirtualにするかは正直あんまり理解してない（調べてない）が、record型の都合なのだろうという解釈をとりあえずしている。
EC2インスタンス起動時にスクリプトを実行する方法

新しくEC2インスタンス起動した後は初期設定をすると思います。
もし毎回同じコマンドを実行するのが面倒な場合は、自動でスクリプトを実行させることができます。
「ユーザーデータ」というものを使えば良いです。その使い方を説明します。

# 1. スクリプトを作成する

初期設定のコマンドを実行するスクリプトを作成します。
今回は例としてwebサーバを立ててみます。

```bash
#!/bin/bash
apt update
apt install apache2 -y
echo 'hello world' > /var/www/html/index.html
```

ユーザーデータのスクリプトはroot権限で実行されるため、`sudo`等は必要ありません。

# 2. ユーザーデータとしてスクリプトを登録する

先ほど作成したファイルをユーザーデータとして登録しましょう。
以下のキャプチャにあるように、EC2インスタンス作成時に登録できます。

![2-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1884434/8a0265ad-4531-7b6d-9682-4d6131676a6a.png)

テキストをコピペして貼り付けても良いですし、ファイルをアップロードしても良いです。
どちらかの方法で登録してください。

また、今回のサンプルではwebサーバをインストールするため、その接続確認のため80番ポートは開けておきましょう。
セキュリティグループを以下のキャプチャを参考にして設定してください。

![2-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1884434/b268912c-5ee1-3b65-fbf3-582ec9b37656.png)

今回の起動時の主なスペックを以下にまとめておきます。
OSは`ubuntu 20.04`を使用しました。
その他の設定は無料枠に収まるようにしたかったため、
デフォルトの設定のままです。

|OS|instance type|storage|
|-|-|-|
|ubuntu 20.04|t2.micro|SSD 8GB(default)|

**この方法はあくまでサンプルです。起動前の確認で警告の表示が出ると思います。
試し終わったらインスタンスの削除等を行ってください!!　(後でその説明もあります)**

# 3. EC2インスタンスを起動する

準備万端ですので、さっそくEC2インスタンスを起動しましょう。
最後の「7. 確認」の画面で「起動」をクリックしてください。

![2-9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1884434/232ceea8-164a-b99d-82aa-6be296b67d74.png)


# 4. webサーバに接続してみる

起動した後はwebサーバに接続してみましょう。
まず`Public IP`を確認します。以下のキャプチャを参考にしてください。

![2-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1884434/dc09f5b0-1259-8148-c82a-9ddbfb5ebca1.png)


※この`Public IP`は毎回違う値が自動的にセットされます。
インスタンスを作り直した場合は、その都度値を確認しましょう。

次にブラウザに`Public IP`を入力して、webサーバにアクセスしましょう。

![2-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1884434/99798e66-7ee7-3b01-95a6-d221e6edda56.png)


無事にアクセスできました！`hello world`が表示されています。

# 5. インスタンスを削除する

ここ大事です。先ほど説明したようにこのwebサーバを放置するのはよろしくないので、インスタンスを削除しましょう。

まず該当のインスタンスのチェックボックスにチェックを入れ、「インスタンスの状態」をクリックします。

![2-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1884434/fe50a7b3-1337-d0c3-4580-7662032c243f.png)

次に「インスタンスの終了」をクリックしましょう。

![2-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1884434/1950c2aa-e4e9-dea5-56c9-541645695708.png)

最後に「終了」をクリックしましょう。これで終了できたはずです。

![2-19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1884434/0847b28c-f225-b750-934e-51dc75d71088.png)

# 参考

[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html)
python初心者はWordCloudで遊んでみたい4

#前回まで
WordCloudを軽くいじくった。書式もいじくって見やすくした。
しかし、日本語出力する時はデフォルトだと文字化けしてしまうので、今回は日本語を対応させていく。

参考にさせていただいたサイト↓
[WordCloud（ワードクラウド）を日本語で作成する【Python】] (https://self-development.info/wordcloud%ef%bc%88%e3%83%af%e3%83%bc%e3%83%89%e3%82%af%e3%83%a9%e3%82%a6%e3%83%89%ef%bc%89%e3%82%92%e6%97%a5%e6%9c%ac%e8%aa%9e%e3%81%a7%e4%bd%9c%e6%88%90%e3%81%99%e3%82%8b%e3%80%90python%e3%80%91/)
~~ほぼまるパクり~~

#日本語フォント
フォントを指定する際、PC内のフォントを使っている方が大半ですが、今回はサイトからインストールしたフォントを使用します。
今回使用したフォントはこちら↓
https://fontfree.me/2994

ダウンロードしたらフォントをGoogleDriveに入れ、パスを指定すれば使えます。

##とりあえずやってみる

```python
from janome.tokenizer import Tokenizer
from wordcloud import WordCloud
import re

f = open("sample.txt", "r", encoding="UTF-8")
text = f.read()

FONT_PATH = '/content/drive/MyDrive/Togalite-Regular.otf'
```

ここは毎度同じ。必要なライブラリをimportしてテキストファイル開いてフォントパスを指定する。

```python
def get_word_str(text):
 
    t = Tokenizer()
    token = t.tokenize(text)
    word_list = []
 
    for line in token:
        tmp = re.split('\t|,', str(line))
        if tmp[1] in ["名詞"]:
          word_list.append(tmp[0])
 
    return " " . join(word_list)
 
# 文字列取得
word_str = get_word_str(text)

wordcloud = WordCloud(font_path=FONT_PATH,
                      background_color="white",
                      colormap="summer",
                      collocations=False)
wordcloud.generate(word_str)
wordcloud.to_file("wc3.png")
```

参考にさせていただいたサイトとほぼ同じ。
```def get_word_str(text)```で形態素解析。
```re.split()```で文字列を分割。
```if tmp[1] in ["名詞"]```で名詞のみを指定。

wordcloudの方は、フォントの指定以外は前回と変わらない。

##実行結果
![wc3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2018163/27b8b9a2-4542-c212-e47a-439d7d9881fa.png)

しっかりと日本語になっている。おじいさんよりおばあさんが多いのはなんでだろう。

#まとめ
今回は、WordCloudを日本語対応させた。できたのはいいが、第1回と2回で勉強したやつ使ってなくね？調べても今回のようなコードだったので、もしかしたらただjanomeを遊んだだけの人だったのかもしれない。
思いつく限りのことはしたが、そもそも、コード同士を繋げたことがないので分からない。
できるなら勉強したコードを使いたかったけど、できたので結果オーライ!

（もしできる方法があり、わかる方がいたら教えてください...）
Error: You are using an unsupported version of Node. Please update to at least Node vの対処法

```
npm run watch
```

をして、JavaScriptをコンパイルして、JavaScriptの変更を反映しようとしたら、

```
Error: You are using an unsupported version of Node. Please update to at least Node v12.14
```

というエラーが出てきました。

その時に、調べていくと、どうもnpmのバージョンが古いみたいみたいだなと思ったので、

```terminal
npm run update
```

をしてみてもうまく行かないなと思いました。
ただ、エラーメッセージをよく見ていくと、

```
Please update to at least Node v12.14
```

ってことは、Nodeのバージョンを上げればいいのかなと思ったので、Dockerfileをチェックしてみました。そしたら、

```Dockerfile

COPY --from=node:10.22 /usr/local/bin /usr/local/bin
COPY --from=node:10.22 /usr/local/lib /usr/local/lib
```

となっていました。ここが古いのが原因なのかなと思ったので、ここの

```
node:10.22
```

となっているところのバージョンを、エラーメッセージにでている番号を変更しました。

```Dockerfile
COPY --from=node:12.14 /usr/local/bin /usr/local/bin
COPY --from=node:12.14 /usr/local/lib /usr/local/lib
```

二つとも、12.14にバージョンを変更しました。
変更した後に忘れずに

```Dockerfile
docker-compose build
```

をしたら、無事解決しました。

Nodeのバージョンが古いとバージョンアップをしないといけないのですね。
今回のことで学ぶことが出来ました。
よかったよかった。
単回帰分析

単回帰について調べる機会があったので自分用にまとめます。
間違いがあるかもしれません。（特に中心化については自信がないです）
ご指摘いただければ幸いです。

## 単回帰の定義
以下のように、二つひと組のデータの集合が与えられているとします。

```math
D = 
\left\{ 
  (X_n , Y_n )
\right\}_{n=1}^{N}
```
次に、このデータの集合$D$を以下のように単純化します。

```math
y = wx+b
```

このような定式化を**単回帰**と呼ぶことにします。

## 中心化

ある一組のデータ$(X_n, Y_n)$を単回帰に当てはめます。

```math
Y_n = wX_n+b
```

同様にデータ全体に対して適用し、平均をとります。

```math
\begin{align}

\frac{1}{N} \sum_{n=1}^{N}Y_n &= \frac{1}{N}\sum_{n=1}^{N}(wX_n+b) \\

& =
w\frac{1}{N}\sum_{n=1}^{N}X_n +b
\end{align}
```
上式から分かるように、$X_n$の平均$\bar{X}$と、$Y_n$の平均$\bar{Y}$が0になるようにデータ全体を平行移動することで、$b=0$にすることができます。
すなわち次のようにデータを変換します。

```math
D^{\prime}=
\left\{ 
  (X_n -\bar{X} , Y_n-\bar{Y} )
\right\}_{n=1}^{N}

```

このような操作を**中心化**と呼ぶことにします。
以降、中心化が行われた前提で単回帰を次のように表すことにします。

```math
{y} = w{x}
```


## 目的関数の決定

単回帰モデルの予測値$y_n$と、実際のデータ(目標値)$Y_n$の誤差の二乗を考えます。


```math
(Y_n-y_n )^2 = (Y_n-wX_n)^2
```

次に、この二乗和誤差をデータ全体に対して行い和をとったものを$L$で表すことにします。

```math
L = \sum_{n=1}^{N}
(Y_n-y_n )^2 =
\sum_{n=1}^{N}
(Y_n - wX_n )^2
```
この$L$を**目的関数**と呼ぶことにし、目的関数を最小化することを考えます。

## パラメタの最適化

目的関数$L$において、$Y_n$と$X_n$は任意の値で良いはずなので、$L$は$w$のみに依存した関数と言えます。
したがって、$L$を最小化する方程式は以下となります。

```math
\frac{\partial}{\partial w}
L = 0
```

では、方程式の解となるパラメタ$w$を求めます。
また、$u_n =Y_n - wX_n$と定義しておきます。

```math
\begin{align}
0 & = \frac{\partial}{\partial w}L = 
\frac{\partial}{\partial w}
\sum_{n=1}^{N}
(Y_n - wX_n)^2 \\

& = 
\frac{\partial}{\partial w}
 \sum_{n=1}^{N}
{u_n}^2 

=
\sum_{n=1}^{N}
\frac{\partial}{\partial w}
{u_n}^2 \\

& = 
\sum_{n=1}^{N}
\frac{\partial u_n}{\partial w}
\frac{\partial {u_n}^2}{\partial u_n} \\

& = 
\sum_{n=1}^{N}
(-X_n\cdot 2u_n)

= 
-2 \sum_{n=1}^{N}
X_nu_n \\

& =
-2 \sum_{n=1}^{N}
X_n(Y_n - wX_n) \\

& =
-2\sum_{n=1}^{N}
X_nY_n+2
w\sum_{n=1}^{N}{X_n}^2

\end{align}
```

以上よりパラメタ$w$は以下で決定されます。

```math
w = \frac{\sum_{n=1}^{N}X_nY_n}{\sum_{n=1}^{N} {X_n}^2}
```


## まとめ

1.データが次で与えられる。

```math
D = 
\left\{ 
  (X_n , Y_n )
\right\}_{n=1}^{N}
```

2.中心化を行う。

```math
D^{\prime}=
\left\{ 
  (X_n -\bar{X} , Y_n-\bar{Y} )
\right\}_{n=1}^{N}

```

3.ウェイト$w$を求める。

```math
w = \frac{\sum_{n=1}^{N}X_nY_n}{\sum_{n=1}^{N} {X_n}^2}
```
4.単回帰が求まる。

```math
{y} = w{x}
```

## 参考資料
https://tutorials.chainer.org/ja/07_Regression_Analysis.html

・中心化を行わずにパラメタを最適化する
https://ai-trend.jp/basic-study/regression/simple_linear_reg/
【Rails】form_withに関する基本知識

# form_withメソッドとは
フォームを実装するためのヘルパーメソッドです。
ヘルパーメソッドとは、主にビューでHTMLタグを出現させたりテキストを加工するために使用するメソッドのことです。
HTMLのformタグの代わりに使用できます。

**【例】HTMLのフォーム記述**

```
<form action="/posts" method="post">
  <input type="text" name="content">
  <input type="submit" value="投稿する">
</form>
```
↓↓これをこのように書き換えることができます
**【例】ヘルパーメソッドを用いたフォーム記述**

```
<%= form_with url: "/posts", method: :post, local: true do |form| %>
  <%= form.text_field :content %>
  <%= form.submit '投稿する' %>
<% end %>
```

ヘルパーメソッドはRubyとして取り扱われるので、表示するために`<%= %>`で囲んで使用します。
do〜endの間に、表示される投稿フォームの内容を記述していきます。

# form_withを使うメリット
1.パスの指定やRubyの埋め込みなどの記述がシンプルになる
2.セキュリティ上の問題を解消する

# form_withメソッドのオプション

```
<%= form_with url: "/posts", method: :post, local: true do |form| %>
```
・urlオプション：フォームの情報を送るリクエストのパスを指定。
・methodオプション：フォームの情報を送るリクエストのHTTPメソッドを指定。オプションの初期値は:postなので、postメソッドを指定する場合は省略することが可能。
・localオプション：リモート送信を無効にするかどうかを指定。trueにすると無効になる。

※urlオプションやmethodオプションは、先に説明したform要素のaction属性やmethod属性に対応するものです。

# form_withでのフォームの部品の作り方

form_withのブロック変数「form」とヘルパーメソッドを組み合わせることで、フォームに部品を追加できます。

```
<%= form_with url: "/posts", method: :post, local: true do |form| %>
  <%= form.text_field :content %>
  <%= form.submit '投稿する' %>
<% end %>
```
上記の例であれば、`<%= form.text_field :content %>`〜`<%= form.submit '投稿する' %>`という記述で、ビューに表示されるフォームの部品を生成しています。

**フォーム部品のヘルパーメソッド例**
・text_field：1行のテキストボックス
・password_field：パスワード入力ボックス（入力したテキストがアスタリスクなどに置き換えて表示される）
・check_box：チェックボックス（複数選択可能）
・radio_button：ラジオボタン（複数の中から1つしか選択できない）
・submit：送信ボタン

ここではsubmitを除いて、これらのヘルパーメソッドの引数には、name属性にあたる名前が入ります。(`form.text_field :content`でいうと、`:content`がname属性)

このname属性の値が、データを扱う際の名前(キー)になります。
つまり`form.text_field :content`でいうと、ユーザーが`form.text_field`に入力した値を管理するのが`:content`です。→ { content: ユーザーが入力した値} というハッシュ形式になるイメージ。


# modelオプションの値によるリクエストの自動判断

【modelオプションによる自動判断の例】

```
    <%= form_with model: @tweet, local: true do |form| %>
      <%= form.text_field :name, placeholder: "Nickname" %>
      <%= form.text_field :image, placeholder: "Image Url" %>
      <%= form.text_area :text, placeholder: "text", rows: "10" %>
      <%= form.submit "SEND" %>
    <% end %>
```
form_withは、modelオプションに指定された値の状態により、リクエストを送るアクションを自動で判断してくれます。

例えば「インスタンス変数@tweet」をmodelオプションの値としたとき、

①modelオプションが「@tweet = Tweet.new」という**空のインスタンス変数@tweetの場合**
 →「空ということは、次はcreateアクションだ！」と自動で判断してくれる。
②modelオプションが「@tweet = Tweet.find(params[:id])」という**値の入った@tweetの場合**
 →「値が入っているということは、次はupdateアクションだ！」と自動で判断してくれる。

そして②のように、modelオプションに指定したものが「すでに値が入っているインスタンス変数」であれば、その変数が持っている値を初期値としてカラムに入れておいてくれます。

<br>
以上、form_withに関する基本知識でした。
ご指摘などあれば、ご教授頂けると幸いです。
リモートデスクトップ接続のタイトルバー


リモートデスクトップ接続のここ↓を変更したくて、調べた結果を書く。

![図01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639468/e8341e6d-cba3-9f41-66a4-88e1a84b2fbd.png)


どうやら、ここには、コンピュータ名が表示されるみたい。

※コンピュータ名というのは、ここ↓

![図2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639468/93e27b71-57c7-e1ee-9304-28dce86d512b.png)


で、こういうrdpファイルを作ってリモートデスクトップ接続をすると

![図3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639468/dc6dbad2-fcc3-c8bd-15c7-03465a854fb6.png)


あら、不思議

![図4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639468/2df5a859-1b35-40ca-608e-ca30ef70a355.png)


rdpファイル名＋コンピュータ名が表示されました。

証跡を取ったり、テストをしている人にとっては、当たり前だったりするのだろうか。
Ebitenでのアイコン設定

　Ebitenでゲームを作ってますが、ウインドウのアイコン設定でハマったのでqiitaでソース残しておきます

```golang
import (
	"image"
	"image/png"
	"os"

	"github.com/hajimehoshi/ebiten/v2"
)
```
　golangの方のimageを使っているので、いつもの

```
	_ "image/png"
```
では無いです

```golang
func iconSet() {
	p, err := os.Open("icon.png")
	if err != nil {
		panic(err)
	}
	defer p.Close()
	
	img, err := png.Decode(p)
	if err != nil {
		panic(err)
	}
	
	ebiten.SetWindowIcon([]image.Image{img})
}
```
　Go言語って、配列の設定でメゾッドへ送れたんだな…
OSI参照モデル TCP/IPモデル の「層」って?

データ転送というものは、以下の通り。
> 送信側はデータに必要な情報を付加しながら上の層から下の層へと渡し、
最終的にネットワークに渡す。
> そして、受信側では受け取った付加情報を確認しながら下の層から上の層へと渡して、
最終的にデータを見る、、、

この層ってなんなんだ。


#プロトコル(protocol)
日本語で「約束」、「取り決め」、「通信規約」。
コンピュータ間でデータを送受信するためのルールを意味する。
コンピュータはこのルールに則って通信するため、
物理的にコンピュータ同士を接続しただけでは通信できない。


:::note info
かつては各OSで形成するネットワークごとに異なるプロトコルを使用していたため異種OS間での通信はできなかったそう。
:::

各ネットワークで使用されているプロトコルは一つではなく、
多数のプロトコルの中から必要なプロトコルを選択し使用している。
一般的には、各ネットワークで使用するプロトコルの名前には総称が付けられており、
プロトコルといった場合こちらを指すことも。


| ネットワークの種類 | プロトコルの総称 | 主なプロトコル |
|:-----------|:-----------|:-----------|
| インターネット       | TCP/IP        | IP、TCP、UDP、ICMP、HTTP、FTP |
| NetWare     | IPX/SPX       | IPX、SPX、NPC|
| Windows       | NetBEUI       | will         |
| Macintosh       | AppleTalk          | DDP、RTMP、AEP、ATP、ZIP |

###じゃあなんで層が必要なの
言語に、主語、述語、目的語などの役割がありそれらを組み合わせて一つの文章ができるように、
プロトコルにも各階層に役割があり、それらを組み合わせて一つのデータができる。
言語で文法が決まっているのと同様！

##OSI 参照モデル
そんなこんなで、1984 年、ISO(国際標準化機構)が、
DECNET、SNA、TCP/IPなどのネットワークの調査結果に基づいてき、OSI参照モデルを作成。
ベンダに提供した一連の規格。

上位層ほどソフトウェアに近づき、
一般的なエンドユーザアプリケーションはアプリケーション層に位置する。
逆に、下位層ほどハードウェアに近づき、ケーブルは物理層。

|層 |OSI参照モデル| 主な働き|
|:-----------|:-----------|:-----------|
|7 |アプリケーション層 |電子メール、ファイル転送などアプリケーション同士が相互にやり取りできるようにする|
|6 |プレゼンテーション層 |漢字コードなど OS や機種ごとに異なる形式を通信形式に変換したり戻したりする|
|5 |セッション層 |回線切断時の回復方法や、同期、フロー制御を行う|
|4 |トランスポート層 |送られたデータに抜けがないか、順序は正しいかをチェックする|
|3 |ネットワーク層 |どの経路を通って相手まで届けるのかを決定する|
|2 |データリンク層 |直接接続された機器間でのデータ転送と制御を行う|
|1| 物理層 |コネクタやケーブル、ネットワークカードの仕様を決める|

:::note warn
TCP/IPが急速に広まったため、現在ではほぼ使われていないが、
学ぶ必要がある超重要な仕組み
:::

##TCP/IP プロトコル群
アメリカ国防総省の実験から発表されたとかなんとか。
現在ネットワークプロトコルのデファクトスタンダード(事実上標準)となっている。

|OSI層| 層| TCP/IP| 役割| 主なプロトコル|
|:-----------|:-----------|:-----------|:-----------|:-----------|
|7,6,5|4| アプリケーション層|ユーザが使いたいアプリケーションのデータとアプリケーション間の通信手順を決める。|HTTP、FTP、SMTP、POP3、DNS、DHCP|
|4| 3| トランスポート層| アプリケーションの特定と通信方法を決める。 |TCP、UDP|
|3| 2| インターネット層| 宛先までパケットを届けるための経路制御を行う。 |IP、ICMP|
|2,1|1|ネットワークインターフェース層|ネットワークの機器、ハードウェアに関する取り決めをする。|(Ethernet)|

TCP/IPは狭い解釈では、トランスポート層のTCPとインターネット層のIPを指す。
これはTCPとIPを利用する通信が多いからだが
、実際にはアプリケーション層のHTTPやFTPなどのプロトコルも含めてTCP/IPと呼ぶケースが多く、
TCP/IP をとりまくプロトコル群の総称として使われている。

##まとめ
「層」はネットワーク分野を理解する上での重要な**地図**
CPUについて　[コンピュータの５大装置]

#CPUについて

####そもそもCPUとは

**中央処理装置**
つまり人間で言う脳の役割を持つ

が、脳と違いCPU自体が考えて処理を行うのではない

<font color="red">プログラムを読み込むことでCPUがそれに従い処理を行う</font>

#コンピュータの５大装置

◯制御装置
　→CPU内にある
　　プログラムの命令を理解しコンピュータ全体を制御
◯演算装置
　→CPU内にある
　　計算、データの演算処理を行う
◯記憶装置
　→主記憶装置（データを一時的に記憶する）と補助記憶装置（ハードディスクやCD-ROM）からなる
　　処理に必要な情報を保存・保持する
◯入力装置
　→キーボードやマウス
◯出力装置
　→ディスプレイやプリンタ

#ノイマン型コンピュータ

コンピュータの処理の流れは・・・

補助記憶装置で待機しているプログラムが実行されるときに、
**主記憶装置に移動（プログラム内蔵方式）**、<font color="red">つまり主記憶のメモリ上にロードする</font>
CPUがそこから**プログラム内の命令を一つずつ取り出してく（逐次制御方式）**

これを<font color="red">ノイマン型コンピュータ</font>という
これが今の主流


###主記憶措置のアドレス

主記憶装置に保管されているプログラムや多くの演算データから欲しいものを取り出すためには、
主記憶内に区画番号をつけて区別すればデータを簡単に見つけて取り出しやすくなる
この区画番号を<font color="red">アドレス</font>という

#CPUの命令実行とレジスタ

###レジスタとは
<font color="red">CPU内にある記憶装置</font>

CPUは取り出したい命令に必要な情報を一旦レジスタ内に保管

どんな情報かというと・・・
      　　　　　　　　　<font color="red">アドレス</font>

アドレスを記憶し実行するときにメモリから取り出せるようにする（**フェッチ**）


実行するときは、メモリから命令を引っ張り、同時に処理に必要な装置に動くよう指示する



>参考書籍
>>きたみりゅうじ　『キタミ式　イラストIT塾　基本情報技術者　令和０３年』 技術評論社 2020年
Google Play StoreとAppGallery両方にアプリをリリースした場合のアプリアップデート方法について考案しました

Google Play StoreとAppGallery両方にアプリをリリースした場合、アプリのアップデートロジックもそれに合わせてカスタマイズしなければなりません。

もし、アップデートのリンクをGoogle Play Storeに固定させてしまったら、Google Play Storeが入っていないHMS端末ではアプリがアップデートできないことが生じてしまいます。

# Google Play StoreとAppGallery両方にアプリをリリースした場合のアップデートのソリューション

### ソリューション１
GMSとHMSがあるかないかでアップデートリンクを分けます。

```kotlin
private fun updateAppByCheckingService() {
    // GMSの存在確認
    val googleResult = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this)

    // HMSの存在確認
    val huaweiResult = HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(this)

    // 先にGMSが使えるかどうか調べる
    // GMSが使える場合、Play Storeが存在するとみなす
    if (com.google.android.gms.common.ConnectionResult.SUCCESS == googleResult) {
        startActivity(Intent(Intent.ACTION_VIEW, https://play.google.com/store/apps/details?id={パッケージ名]}))
    }
    // GMSが使えない場合、HMSが使えるかどうか調べる
    // HMSが使える場合、AppGalleryが存在するとみなす
    else if (com.huawei.hms.api.ConnectionResult.SUCCESS == huaweiResult) {
        startActivity(Intent(Intent.ACTION_VIEW, market://com.huawei.appmarket.applink?appId={アプリID}))
    }
}
```

モジュールのbuild.gradle

```gradle:build.gradle
implementation 'com.google.android.gms:play-services-base:17.6.0'
implementation 'com.huawei.hms:base:6.0.1.302'
```

プロジェクトのbuild.gradle

```gradle:build.gradle
buildscript {
    repositories {
        ...

        maven {url 'https://developer.huawei.com/repo/'}
    }
    ...
}

allprojects {
    repositories {
        ...

        maven {url 'https://developer.huawei.com/repo/'}
    }
}
```


### ソリューション２
Google Play StoreとAppGalleryがあるかないかでアップデートリンクを分けます。

```kotlin
private fun updateAppByCheckingPackage() {
    // 先にPlay Storeが使えるかどうか調べる
    if (getApplicationInfo("com.android.vending") != null) {
        startActivity(Intent(Intent.ACTION_VIEW, {}))
    }
    // Play Storeが使えない場合、AppGalleryが使えるかどうか調べる
    else if (getApplicationInfo("com.huawei.appmarket") != null) {
        startActivity(Intent(Intent.ACTION_VIEW, {}))
    }
}

private fun getApplicationInfo(packageName: String): ApplicationInfo? {
    return try {
        packageManager.getApplicationInfo(packageName, 0)
    } catch (exception: PackageManager.NameNotFoundException) {
        null
    }
}
```

# GitHub
[https://github.com/Rei2020GitHub/MyPublicProject/tree/master/CheckUpdtae](https://github.com/Rei2020GitHub/MyPublicProject/tree/master/CheckUpdtae)

# 参考サイト
AppGallery：https://appgallery.huawei.com/#/Featured
AppGalleryの紹介：https://consumer.huawei.com/jp/mobileservices/appgallery/
HMS：https://developer.huawei.com/consumer/jp/
HuaweiApiAvailability：https://developer.huawei.com/consumer/jp/doc/development/HMS-References/huaweiapiavailability
Huawei Developers：https://forums.developer.huawei.com/forumPortal/en/home
Facebook Huawei Developersグループ：https://www.facebook.com/Huaweidevs/