OmniAICreator/Qiita-1.07M huggingface dataset:
ドメイン駆動設計入門で学んだこと #1

# はじめに

本記事は以下の書籍を読んで学んだことをまとめることを目的としています。
[ドメイン駆動設計入門 ボトムアップでわかる! ドメイン駆動設計の基本 | 成瀬 允宣 |本 | 通販 | Amazon](https://www.amazon.co.jp/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80-%E3%83%9C%E3%83%88%E3%83%A0%E3%82%A2%E3%83%83%E3%83%97%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8B-%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E3%81%AE%E5%9F%BA%E6%9C%AC-%E6%88%90%E7%80%AC-%E5%85%81%E5%AE%A3/dp/479815072X/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=325IZJW8OL7R9&dchild=1&keywords=%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88&qid=1593219430&sprefix=%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3,aps,244&sr=8-1)

# DDDで出てくる概念

DDDで出てくる概念を整理します。

## ドメイン

プログラムを適用する対象の領域のことを指します。
通販サービスを例にすると、商品、買いかご、顧客、、など様々な通販サービスの概念があります。こういうものがドメインに含まれます。
ただ、ドメインが何かというより、何がドメインに含まれるかが重要です。
なぜなら、ドメイン次第で作成するシステムの規模やどこまで支援するサービスなのかが決まってくるからです。


## ドメインモデル

ドメインの概念をモデリングして得られたモデルがドメインモデルです。
そもそも、書籍にはモデルとは、
> 現実の事象あるいは概念を抽象化した概念

とありました。

通販サービスにおける顧客と、飲食店の予約サービスにおける顧客では必要な情報が違ってくるように、同じ顧客でも、ドメインの中でどのような役割があるのかによって表現する概念が異なってきます。
そのドメインの中で必要となる情報を抽出して象った（かたどった）ものをドメインモデルと呼ぶと理解しています。

## ドメインオブジェクト

上記で述べたようにドメインモデルの段階ではまだ、概念を抽象化した知識です。
ドメインモデルをソフトウェア上で動作するようにしたものがドメインオブジェクトです。
ここで重要なのが、利用者が何に課題を感じていて、何を解決したいのかを考えることです。
この2つを理解してソフトウェアに反映していく必要があります。

## 値オブジェクト

システムならではの値を表現したいときに使われるものと理解しています。
例えばstringでは表せない文字数制限や入力できる文字種の制限など、そのシステム固有のルールを表現をしたい、かつ値の性質を適用したいときに使われます。値の性質とは以下が挙げられます。

- 不変
- 交換可能
- 等価性による比較

この性質を持った値を使用したいとき、値オブジェクトが使われます。

実装上の注意

- 不変であるため、Changeメソッドなどは値オブジェクトに作成しません。
- stringやint型のような値型と同様に値の代入(交換)によってのみ値オブジェクトを変更します。
- 値型と同じように、値オブジェクト同士が比較できるようにEqualsメソッドをオーバーライドします。

値オブジェクトを使うメリット

- 値の意味や仕様がコードを読むだけでわかる
- ロジックの散在を防ぐ
- 不正な値を防ぐ
- 誤った代入を防ぐ

特に上2つのメリットが値オブジェクトを使う意味であると考えています。

- 値の意味や仕様がコードを読むだけでわかる

上記を言い換えると、コードがドキュメントになるということです。わざわざ仕様書や設計書を読まなくてもコードを見て理解できるなら、そんな楽なことはないと思います。値型を使っているだけではそうはなりません。

- ロジックの散在を防ぐ

これもとても大きな意味があると思います。単純に変更箇所が一か所になるからです。

# さいごに
ここまでお読みいただき、ありがとうございました。
長くなるので、#2に続きます。
Powershellを使用してWindowsをリモート操作する方法

##内容
Powershellを使ってWindowsServer1からWindowsServer2へリモート接続してコマンドを実行するといったことを実施したのでやり方をまとめておきます。

##前提
■リモート接続元、接続先の両方でPowershellのポリシーがRemoteSignedになっている
・実行ポリシーをRemoteSignedに変更

```powershell
set-ExecutionPolicy RemoteSigned
```

・実行ポリシー確認方法
コマンドの結果RemoteSignedと表示されれば問題無し。

```powershell
> get-ExecutionPolicy
RemoteSigned
```
■WinRMが要求を受け入れられる状態になっている。
・WinRMで要求を受け入れられるようにするコマンド

```powershell
Enable-PSRemoting
```

##リモートログイン
・SSH接続したようなリモートログイン
これでログインすると、接続先の情報がプロンプトの頭に表示される。
以降のコマンド実行は接続先のサーバで実行される。

```powershell
Enter-PSSession -ComputerName windowsserver2 -Credential [接続アカウント] [パスワード]
[windowsserver2]：PS C:\Users\~>
```

この場合、コマンドはすべてWindowsServer2で実行されているため、結果をWindowsServer1側に保存しようとして`Export`系のコマンドへパイプを使って結果を渡しても、WindowsServer2側で保存されてしまう。

##コマンドのリモート実行
`Invoke-command`を用いる。
その際、複数コマンドを実行する場合、リモートセッション情報を使い回すため、セッションを固定で生成しておくと
都度セッションを開く必要がないため楽だと思う。

```powershell
$S = New-PSSession -ComputerName windowsserver2 -Credential [接続アカウント] [パスワード]
Invoke-Command -Session $S -ScriptBlock {[実行コマンド]}
```
こちらの場合、コマンド結果をWindowsServer1で受け取るのでパイプでExportするコマンドへ渡してあげれば、結果をWindowsServer1側で保存できる。
※多数のホストからの情報収集等はこちらを利用すると良いと思う。

`Invoke-command`はコマンドだけでなく事前に定義したFunctionも実行可能。

```powershell
function func {hostname}
Invoke-Command -Session $S -ScriptBlock $function:func
```

コマンド実行が終わったらセッションをクローズする。

```powershell
Remove-PSSession -Session $S
```
UbuntuStudioにMicrosoft Teamsをインストールする。

Ubuntu Studio 20.04.2 LTS (Focal Fossa)
にて動作確認しました。

#パッケージを使用して Microsoft Teams をインストールする
https://aka.ms/getteams からDEBパッケージをダウンロードします。
ターミナルから、

```
sudo dpkg -i **teams download file**
```
と入力します。
Teams は、アクティビティから起動することも、ターミナルから「`teams`」と入力して起動することもできます。

#コマンドラインから手動でMicrosoft Teams をインストールする

```
sudo apt install curl
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -

sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/ms-teams stable main" > /etc/apt/sources.list.d/teams.list'

sudo apt update
sudo apt install teams
```


コマンド &apos;teams&apos; が見つかりません。次の方法でインストールできます:

<pre>sudo snap install teams</pre>

Microsoft Teams のクライアントを取得する
    2021/04/19
https://docs.microsoft.com/ja-jp/microsoftteams/get-clients
ActiveRecordのwith_lockをいつ使うか

- 気軽に使うのはおすすめできません

## with_lockの挙動

RDBの `SELECT FOR UPDATE` を使って実現されています
https://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

## 危険な使い方
- current_userのようなスコープが広いレコードをlockしてしまう

```rb
class HogeController
  def action_hoge
    current_user.with_lock do
      hoge_logic
    end
  end
end

class FugaController
  def action_fuga
    current_user.with_lock do
      fuga_logic
    end
  end
end

```

- この場合`action_hoge`と`action_fuga`を同時に処理できなくなってしまい、スループット低下のリスクがあります

## 回避方法
### lockするレコードを変えてスコープを小さくする

```rb
class HogeController
  def action_hoge
    hoge = Hoge.find(params[:id])
    hoge.with_lock do
      hoge_logic
    end
  end
end
```

## with_lockの使い所
### 口座.残高のようなDB設計のとき、安全に残高を更新する
- `残高を減らす` のようなとき、with_lockの後で残高を確認する必要があることに注意してください
- with_lockすると内部的にはreloadが行われ、最新のDBの状態に更新されます。そのため、with_lock前後で値が変わっている可能性があります

```rb
class 口座
  # OK
  def 残高を減らす(money)
    with_lock do
      raise 'error' if self.残高 < money
      self.残高 -= money
    end
  end

  # NG
  def 残高を減らす(money)
    raise 'error' if self.残高 < money # ここでOKでも...
    with_lock do
      # この時点で残高が足りていない可能性がある
      self.残高 -= money
    end
  end
end
```

## ActiveRecord.serializeを使っているとき、安全に内容を更新する
- https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html

```rb
# NG
class Hoge < ApplicationRecord
  serialize :preferences, Hash

  def update_a
    preferences[:a] = 1
    save!
  end

  def update_b
    preferences[:b] = 2
    save!
  end
end
```

```rb
Hoge.create!(preferences: {a: 0, b: 0})

# terminalを2つ開いて順に実行してみる

# terminal1
hoge.update_a
# => #<Hoge id: 1, preferences: {:a=>1, :b=>0}>

# terminal2
hoge.update_b
# => #<Hoge id: 1, preferences: {:a=>0, :b=>2}>
# terminal1の更新がなかったことになってしまう
```

```rb
# OK
class Hoge < ApplicationRecord
  serialize :preferences, Hash

  def update_a_with_lock
    with_lock do
      preferences[:a] = 1
      save!
    end
  end

  def update_b_with_lock
    with_lock do
      preferences[:b] = 2
      save!
    end
  end
end
```

## その他 個人的に使う別の手法

### optimistic lock(lock version)を使う
- https://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html

### Redisでロックする
- https://github.com/leandromoreira/redlock-rb
- Redisをすでに導入しているならおすすめの手法です
- ユーザーIDをキーの一部に組み込むことで、DBレコードがなくても排他制御できます

```rb
lock_manager.lock("lock_key/#{user.id}/some_logic", 2000) do |locked|
  if locked
    # critical code
  else
    # error handling
  end
end
```
【rails6】ransackを用いた複雑検索まとめ　〜絞り込み検索〜

こんにちは！
先日の続き、「ransackを用いた複雑検索」についての続きの投稿となります。
前回は「 フォームに入力して検索を実行する 」というものでしたが今日はもう少々複雑な検索を実装します。

では、今日やっていくことをまずはご覧ください
![59d2956b0f1ae045ae23d994858d08a0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1422262/e65c451a-b264-e515-74ba-7296f7f6570f.gif)

...一応解説しておきます。
すでに「 レディース 」のカテゴリー分けされている中から
**価格が2,000円以下**とさらに絞りこみをかけています。

では、実装していきましょう

### 環境
前回同様
https://qiita.com/Takahiro_Ito/items/a4a7042bb3e51561521b
こちらをご確認ください

## ① ビューに追加検索フォームを記述します
コントローラーは前回記述済みなので、いきなりビューからの記述になります。

```items/search.html.erb
<div class="search-bar">
  <%= search_form_for @q, url: items_search_path do |f| %>
    <div class="nav-title">詳細検索</div>
      <ul>
      <li class="search-nav-content">
        <p><%= f.label "カテゴリーを選択する" %></p>
        <%= f.collection_select :category_id_eq, Category.where.not(id: 0), :id, :name, include_blank: "カテゴリーの選択", class:"search-select-box"  %>
      </li>
      <li class="search-nav-content">
        <p><%= f.label "商品の状態" %></p>
        <%= f.collection_select :status_id_eq, Status.where.not(id: 0), :id, :name, include_blank: "商品の状態の選択"  %>
      </li>
      <li class="search-nav-content">
        <p><%= f.label "価格" %></p>
        <%= f.number_field :price_gteq, class:"input-price", placeholder: "¥Min" %> 〜
        <%= f.number_field :price_lt, class:"input-price", placeholder: "¥Max" %>
      </li>
      <li class="search-nav-content">
        <p><%= f.label "配送費" %></p>
        <%= f.collection_select :shippingfee_id_eq, Shippingfee.where.not(id: 0), :id, :name, include_blank: "配送費の選択"  %>
      </li>
      <%= f.submit '検索する' %>
      </ul>
    <% end %>
  </div>
```
これが完成形のビューです。

まず、

```
<%= search_form_for @q, url: items_search_path do |f| %>
```
ここは前回と同じです。すぐにご理解いただけると思います。
検索結果は同じページに表示するように指示しています。

以下、パーツごとに解説をしていきますね。

### セレクトボックスを作りたい
このフォームに存在している、「 セレクトボックス 」は下記のように作ります

```
<%= f.collection_select :category_id_eq, Category.where.not(id: 0), :id, :name, include_blank: "カテゴリーの選択", class:"search-select-box"  %>
```

下記のように理解すればわかりやすいかと思います。

```
:category_id_eq.               => category_idと一致するものを探したい（eqの為完全一致です）
Category.where.not(id: 0)      => Categoryからidが0以外のもの全てから選びたい（今回はActivehashを使用しています。）
:id                            => 結果はidを返します
:name                          => Categoryのnameを画面に表示してください
include_blank: "カテゴリーの選択"  => 初期表示は"カテゴリーの選択"と表示してください
```

###  ○○以上 △△未満 の絞り込みをしたい
では、金額の絞り込みをしていきましょう。
今回は、「 フォームに好きな金額を入力できるようにする 」 というものです。

下記のように記述しましょう。

```
<%= f.number_field :price_gteq, class:"input-price", placeholder: "¥Min" %>
<%= f.number_field :price_lt, class:"input-price", placeholder: "¥Max" %>
```

先ほど同様に分解していきます。

```
:price_gteq  => praiceについて入力値 以上 (grater than or equalの略）
:price_lt    => praiceについて入力値 未満 (less thanの略）
```

これで実装ができましたね。


今回は以上です。
今後はチェックボックスとラジオボタンの実装もしていこうと思います。

ではまたお会いしましょう！！
railsチュートリアル　３章の備忘録

３章を進めていく中で感じたことをまとめています。
Rails6を学びたての元陸上競技（110mH）選手です。

◾️学んだこと
・テストスイート（Test Suite）は回帰バグ防止とリファクタリングに便利。テスト駆動（TDD）と呼ばれるらしい。
・Do not Repeat Yourself.の概念
・コントローラーの作成、削除

```
rails destroy controller [コントローラー名][ファイル]→削除
rails generate controller [コントローラー名][ファイル]→作成
```

◾️詰まったこと
・herokuサーバーデプロイ


```
$git add .
$git commit -m 'wakaran'
$git push && git push heroku
```
こう記述すると、下記のようになりました

```
Could not detect rake tasks
remote:  !     ensure you can run `$ bundle exec rake -P` against your app
remote:  !     and using the production group of your Gemfile.
remote:  !     rake aborted!
```
こちらを参考にして下記のようにコマンドを打っても解決せず。
https://qiita.com/candallorjun1025/items/6def984b69db86afd0ae

```
$bundle update coffee-script-source
$bundle update execjs
$bundle update autoprefixer-rails
```
とりあえずデプロイは後回しにした。
追記：
（３章の最後に駄目元で＄git push herokuを叩いたら下記のようになってデプロイに成功した！感激！）

```
 Verifying deploy... done.
To ssh://heroku.com/still-ridge-42195.git
 * [new branch]      main -> main

```

<h3>まとめ</h3>
・アプリ作成時は最初にherokuにデプロイしておくと、logを残すことができるので、エラーが出てきたときに対処が楽になる。
・テスト駆動はマメに行うことでバグが出てきたときに細かく修正できるので作業が楽になる。
・デプロイする前に以下の通りにtestを走らせておくと作業を再開させる時に楽。

```
$rails test
$git push heroku
```
---参考文献---
https://railstutorial.jp
https://qiita.com/candallorjun1025/items/6def984b69db86afd0ae
テスト37


```vb

Sub ComBoEvent()

Dim c As Variant
'シート上にあるフォームコントロールのコンボボックスのマクロ(あらかじめ入力範囲を使って値を入れておく)
'ドロップダウンリストから選択した値を取得する
For Each c In ThisWorkbook.Sheets("コンボボックス").Shapes

    If c.Type = 8 Then   'コントロールがフォームコントロールの場合
    
        If c.FormControlType = 2 Then    'フォームコントロールがコンボボックスの場合
        
            MsgBox ThisWorkbook.Sheets("コンボボックス").DropDowns(c.Name).Value
            
            'いろいろ処理
            
'            Exit Sub
            
        End If
    
    End If

Next c


End Sub


Sub Num_Check()

'数字を含む値があるかのチェック
With ThisWorkbook.Sheets("コンボボックス")

    If StrConv(.Range("D10").Value, vbNarrow) Like "*[0-9]*" Then
    
        MsgBox "数字を含みます。"
    
    Else
    
        MsgBox "数字を含みません。"
    
    End If

End With


End Sub
STM32 CubeIDE

2019/5 printf

https://yukblog.net/stm32cubeide-printf-uart/

2020/5 ubuntu CubeIDE

https://rt-net.jp/mobility/archives/11816

https://rt-net.jp/mobility/archives/12122

ITM_SendChar() 

https://moons.link/post-544/

printf float

http://dad8893.blogspot.com/2021/01/stm32cubeide-uartprintf.html

SWVデバッグ

http://triode.dip.jp/honkytonk/2019/11/electronicworks/stm32cubeide-debug-swv.html

printf float

https://gsmcustomeffects.hatenablog.com/entry/2017/04/04/202648

scanf

https://garberas.com/archives/99
Cloud Composer環境、消したはずのサブディレクトリでIsADirectoryError

## 事象

Cloud ComposerのDAG内でファイル参照をしようとした所、IsADirectoryErrorにより弾かれた。

```  
IsADirectoryError: [Errno 21] Is a directory: '/home/airflow/gcs/dags/my_dag/file.txt'
```

`/home/airflow/gcs/dags/`の中身はGCS Fuseにより`gs://bucket-name/dags`と対応しているため、該当のGCSバケットを確認。
誤って`file.txt`という名前のサブディレクトリを作成してしまっていたことに気づき、これを該当バケットから削除。
→ファイルがあり、同名のサブディレクトリが消えたことを確認して再実行するも同様のエラーとなる。  

## 原因
該当ディレクトリでは、一度置いたファイル・ディレクトリの削除が行われない。
https://cloud.google.com/composer/docs/concepts/cloud-storage#data_synchronization

> Cloud Composer による dags/ フォルダと plugins/ フォルダの同期は、ローカルにコピーすることによって一方向に行われます。一方向に同期されるため、これらのフォルダのローカルの変更は上書きされます。
> data/ フォルダと logs/ フォルダの同期は、Cloud Storage FUSE を使用して双方向に行われます。

dagsフォルダの同期方法は「ローカルにコピー」なので、GCS側のファイル・サブディレクトリを「削除」しても反映されない。

## 復旧
ローカル側(つまり`gs://bucket-name/dags/`ではなく、`/home/airflow/gcs/dags/`の方)を直接お掃除して不要ディレクトリを削除。  

## 備忘
そもそもドキュメントには、タスクから読み取るデータは`py_file`を除き`/home/airflow/gcs/data/`を使った方がよさそうなことが書いてある。
処理名のサブディレクトリに必要リソースをまとめ`dags/`配下に配置する方法が見やすいので採用していたのだが、見直した方がよいかもしれない。(`data/`
ディレクトリの挙動は後日確認したい)

## おわりに
いざ問題を解消してみると、この世で他にここで躓く人が居るのだろうかと考えこんでしまうのが世の常。居ない気もする。
でももし居れば……どこかで誰かが同じことで惑う時間が3分の1になったり……したらいいなあ……。
phpを使ってアンケート等を作るとき

VS Codeを使って、Live Serverを利用していると、ついつい
"Go Live"を押してHTMLを表示する。

しかし、それだけではphpが正常に動作しないので、必ずアドレスバーも操作する。

Udemyではlocalhostでプログラミングを進めているので、
アドレスバーでも
"lovcalhost:----"と記述する必要がある。
AWS日記27 (AWS Secrets Manager)

# はじめに
今回は AWS Secrets Manager を試します。
パスワードを自動生成して Lambda 上で参照します。
[Lambda関数・SAMテンプレート]
(https://github.com/tanaka-takurou/serverless-secrets-manager-page-go)



# 準備
[AWS SAM の準備をします](https://qiita.com/tanaka_takurou/items/cae7c19fc3aaf0031abb)

[AWS Secrets Managerの資料]
[AWS Secrets Manager](https://aws.amazon.com/jp/secrets-manager/)


# AWS SAM テンプレート作成

AWS SAM テンプレートで API-Gateway , Lambda, AWS Secrets Manager の設定をします。

[参考資料]
[AWS SAM テンプレートを作成する](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/tutorial-lambda-sam-template.html)

### AWS Secrets Managerの設定は以下の部分
```yaml
  SampleSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      GenerateSecretString:
        GenerateStringKey: password
        PasswordLength: 32
        SecretStringTemplate: '{}'
        IncludeSpace: false
        ExcludeLowercase: false
        ExcludeUppercase: false
        ExcludeNumbers: false
        ExcludePunctuation: true
```


# Lambda関数作成

※ Lambda関数は aws-lambda-go を利用しました。
AWS Secrets Manager の操作は aws-sdk-go-v2/service/secretsmanager を利用しました。


### Secrets Manager のデータを取得するには GetSecretValue を使う
```golang
func getSecret(ctx context.Context)(string, error) {
	if secretsmanagerClient == nil {
		secretsmanagerClient = secretsmanager.NewFromConfig(getConfig(ctx))
	}
	input := &secretsmanager.GetSecretValueInput{
		SecretId: aws.String(os.Getenv("SECRET_ID")),
	}

	result, err := secretsmanagerClient.GetSecretValue(ctx, input)
	if err != nil {
		log.Print(err)
		return "", err
	}
	resultJson, err := json.Marshal(SecretData{
		SecretName: aws.ToString(result.Name),
		SecretString: aws.ToString(result.SecretString),
		CreatedDate: result.CreatedDate.Format(layout),
	})
	if err != nil {
		log.Print(err)
		return "", err
	}
	return string(resultJson), nil
}
```


# 終わりに
Secrets Manager のパスワードを自動生成し、Lambda上で参照出来るようにしました。


<details><summary>参考資料</summary><div>
[CloudFormationでパスワードを自動生成してテンプレート内で利用する](https://dev.classmethod.jp/articles/automatically-generate-a-password-with-cloudformation/)

[【初心者】AWS Secrets Manager と AWS Systems Manager Parameter Store を使ってみる](https://qiita.com/mksamba/items/0080a342de180120073b)

[AWS CloudFormation でのシークレット作成の自動化](https://docs.aws.amazon.com/ja_jp/secretsmanager/latest/userguide/integrating_cloudformation.html)

[GoでSecrets Managerに保存したSlackトークンを使ってSlackへメッセージ送る](https://dev.classmethod.jp/articles/golang-secrets-manager-slacktoken/)
Yamaha RTXで特定プロバイダからしか接続できないようにするメモ

自宅内でbitwardenのホストを始めた。一応DMZ内の設置で、自宅用セグメントからは分離してあるとは言え、パスワードを扱うサービスだけ合って管理は結構繊細である。そこで、自分が普段用いているプロバイダと、携帯キャリア以外の通信をFWで切っておく。

apacheのVirtualhost設定で行うつもりが、面倒だったのでひとまずルータ側で行う。簡単にin側のフィルタをかけるメモ。

手っ取り早くかけるためのざっくりとした対応をメモしておく。

## ipアドレスレンジを調べる

https://www.nic.ad.jp/ja/whois/ja-gateway.html　

から、検索キーワードにプロバイダ名、検索タイプにネットワーク情報を選択し調べる。今回は朝日ネットのレンジを調べてみた。

結果は、こんな感じ。
https://whois.nic.ad.jp/cgi-bin/whois_gw?codecheck-sjis=%82%C9%82%D9%82%F1%82%CB%82%C1%82%C6%82%ED%81%5B%82%AD%82%A2%82%F1%82%D3%82%A7%82%DF%81%5B%82%B5%82%E5%82%F1%82%B9%82%F1%82%BD%81%5B&key=%92%A9%93%FA%83l%83b%83g&submit=%8C%9F%8D%F5&type=NET-HOLDER&rule=

この辺も参考にした
https://www.its.ne.jp/2021/01/593/


## フィルタを作る

次に、結果をまるっとエクセルにコピーする。
データ→区切り位置からIPアドレスだけを抽出。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134622/f6c7e9a4-64de-90ba-6b45-a3124f009d36.png)

F1セルに「="ip filter "&A1&" pass "&C1&" 192.168.52.10 tcp * www"」と入れるとフィルタリストができる。オートフィルでガーッとコピーして一気にフィルタを作る。

I1に「=A1」、I2に「=I1&" "&A2」と入れ後はオートフィルでガーッと下まで。こうするとフィルタ一覧が作成される。

次に、telnetでルータに入って、フィルタを流す。

```
ip filter 20001 pass 110.5.0.0/18 192.168.52.10 tcp * www
ip filter 20002 pass 183.76.0.0/15 192.168.52.10 tcp * www
ip filter 20003 pass 110.4.128.0/17 192.168.52.10 tcp * www
```

以下略。


フィルタを適用する。例えばpp0にかけるとすると、I列の結果を使って、

```
ip pp secure filter in  1020 1030 1040 1041 1042 1043 1044 1045 1046 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 20001 20002 20003 20004 20005 20006 20007 20008 ....（略）
```

というような感じに。
memberpressでメールを2回入力させる


#目的
memberpressでメール確認のためにメルアド二回入力させたい時のメモ。
フックとかでも直せるのかもしれないが、わとりあえずその場しのぎだが、プラグインを直接編集して対応。


#サインアップのテンプレートの修正
メール確認のフォームを追加します。

```php:wp-content/plugins/memberpress/app/views/checkout/form.php
      <!-- 前半省略（変更なし）  -->
      <!-- input要素にmepr_emailクラスを追加した以外へんこうなし -->
       <div class="mp-form-row mepr_email">
        <div class="mp-form-label">
          <label for="user_email<?php echo $unique_suffix; ?>"><?php _ex('Email:*', 'ui', 'memberpress'); ?></label>
          <span class="cc-error"><?php _ex('Invalid Email', 'ui', 'memberpress'); ?></span>
        </div>
        <input type="email" name="user_email" id="user_email<?php echo $unique_suffix; ?>" class="mepr-form-input mepr-email-input" value="<?php echo (isset($user_email))?esc_attr(stripslashes($user_email)):''; ?>" required />
      </div>
      <!-- ↓↓↓↓↓メール確認のフォームを追加 -->
      <div class="mp-form-row mepr_email_confirm">
        <div class="mp-form-label">
          <label for="user_email_confirm<?php echo $unique_suffix; ?>"><?php _ex('Email Confirmation:*', 'ui', 'memberpress'); ?></label>
          <span class="cc-error"><?php _ex('Email Confirmation Doesn\'t Match', 'ui', 'memberpress'); ?></span>
        </div>
        <input type="email" name="user_email_confirm" id="user_email_confirm<?php echo $unique_suffix; ?>" class="mepr-form-input mepr-email-confirm-input" value="<?php echo (isset($user_email_confirm))?esc_attr(stripslashes($user_email_confirm)):''; ?>" required />
      </div>
      <!-- ↑↑↑↑↑↑メール確認のフォームを追加 -->
      <!-- 以下略（変更なし） -->
```

#サインアップのjavascriptのバリデーションの修正
2つのメルアドが一致してるときだけ送信できるようにします。

```javascript:wp-content/plugins/memberpress/js/signup.js
//前半省略（変更なし）
if ($(obj).attr('type')==='url' && $(obj).val().length > 0) {
        var validURL = $(obj).is(':valid');
        mpToggleFieldValidation($(obj), validURL);
      }
      //追加部分↓↓
      if ($(obj).hasClass('mepr-email-confirm-input')) {
        var confirmMatch = $(obj).val() === form.find('.mepr-email-input').val();
        mpToggleFieldValidation($(obj), confirmMatch);
      }
      //追加部分ここまで↑↑
      if ($(obj).hasClass('mepr-password-confirm')) {
        var confirmMatch = $(obj).val() === form.find('.mepr-password').val();
        mpToggleFieldValidation($(obj), confirmMatch);
      }
      //途中省略（変更なし）
}
//以下省略（変更なし）
```
Prism コードサンプル学習：11-UsingDelegateCommands

# Prism コードサンプル学習：11-UsingDelegateCommands

## はじめに

以下の記事の続きです。
<https://qiita.com/mngreen/items/049b4d181287b6815608>

## 11-UsingDelegateCommands

本サンプルでは、DelegateCommandsの実装方法を紹介しています。
Prismフレームワークでは、以下の4つの方法でコマンドが生成されています。

```cs
    public class MainWindowViewModel : BindableBase
    {
        ...

        public MainWindowViewModel()
        {
            // コマンド生成：①
            ExecuteDelegateCommand = new DelegateCommand(Execute, CanExecute);

            // コマンド生成：②
            DelegateCommandObservesProperty = new DelegateCommand(Execute, CanExecute).ObservesProperty(() => IsEnabled);

            // コマンド生成：③
            DelegateCommandObservesCanExecute = new DelegateCommand(Execute).ObservesCanExecute(() => IsEnabled);

            // コマンド生成：④
            ExecuteGenericDelegateCommand = new DelegateCommand<string>(ExecuteGeneric).ObservesCanExecute(() => IsEnabled);
        }

        ...
    }
```

- [コマンド生成：①](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/DelegateCommand.cs#L34)
  - 一番シンプルなコマンドで、実行する引数なしアクションと実行できるかを判定する関数を渡している
  - プロパティ変更通知が飛ばないため、IsEnabledプロパティのセッターの末尾で[プロパティ変更を通知するメソッド](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/DelegateCommandBase.cs#L55)の呼び出しが必要
- コマンド生成：②
  - コマンド生成：①を呼出し後に[ObservesProperty()](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/DelegateCommand.cs#L86)メソッドを呼び出している
  - プロパティ変更通知対象をFuncで指定するパターンで、プロパティ変更通知メソッドの呼び出しを代わりにしてくれる
  - プロパティ変更通知メソッドの呼び出しの代わりは[PropertyObserver](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/PropertyObserver.cs#L13)クラスで行っている
    - 実際にPropertyChangedイベントにハンドラを登録しているのは[ここ](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/PropertyObserverNode.cs#L31)で行われていることを確認できる。次のノードへの参照があることで、登録されているノードを次々と更新できる。
- コマンド生成：③
  - [Actionのみを渡すコンストラクタ](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/DelegateCommand.cs#L22)を呼び出し後に[ObservesCanExecute()](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/DelegateCommand.cs#L97)メソッドを呼び出している
- [コマンド生成：④](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/DelegateCommand%7BT%7D.cs#L43)
  - 上記でコマンド生成後に、[ObservesCanExecute()](https://github.com/PrismLibrary/Prism/blob/342689ba9a10e511418943438d0f5c57534ca623/src/Prism.Core/Commands/DelegateCommand%7BT%7D.cs#L133)を呼び出し
  - Genericで指定できる以外は③と変わらないように見える

## おわりに

今回はViewModelLocatorの型の解消の変更方法について、別途登録する方法を学習できました。
フレームワークとして様々な登録方法が用意されているのは勉強になります。
自分であれば、いずれかの方法で登録できれば良いと考えてしまいそうなので。。
次回、[12-UsingCompositeCommands](https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/12-UsingCompositeCommands)について見ていこうと思います。
【python】heliumでダウンロード先のフォルダを指定する


heliumでもseleniumと同じ指定の仕方で、ダウンロード先のフォルダを指定できます。heliumを使ってダウンロードしたcsvファイルなどでごにょごにょしたい時にダウンロード先を明示できるので便利です。

```python
from selenium.webdriver import ChromeOptions
from helium import *

options = ChromeOptions()
prefs = {"download.default_directory": r"C:\Users\user\Documents\helium_test"}
options.add_experimental_option('prefs', prefs)
start_chrome(options=options)
# heliumでダウンロード
kill_browser()
```

download.default_directoryで指定するフォルダ名はご自身の環境に合わせてください。
私はwindows10を使っておりますが、フォルダへのパスの最後の"\"は有っても無くても動作には影響がありませんでした。
AWS Cloud9の環境を複製する

既存のCloud9の開発環境と全く同じものをもう一つ用意したいときの手順です。

# Cloud9の新しい環境を作成
コピー先となる新しい環境をCloud9のコンソールから新規作成します。

## 新しい環境に紐づくEC2インスタンスを停止
新しい環境が作成できたら、EC2のコンソールから新しい環境に紐づくCloud9のインスタンスを止まってなければ停止させます。
EC2インスタンス名のプレフィックスが`aws-cloud9-[Cloud9環境名]`となっているのでそこで判断します。

この時、コピー元の環境のEC2インスタンスとインスタンスサイズが違ったら合わせておいた方が無難です。

# EBSの準備
コピー元環境のEBSを複製し新しい環境にアタッチします。

## 新しい環境に紐づくEC2インスタンスのEBSを削除
新しい環境のEC2インスタンスからEBSボリュームをデタッチします。
デタッチしたEBSボリュームは不要なので削除します。

## コピー元環境のEC2インスタンスのEBSのスナップショットを作成
コピー元環境のEC2インスタンスのEBSボリュームのスナップショットを作成します。

## スナップショットからEBSボリュームを作成
スナップショットから新しい環境用のEBSボリュームを作成します。
この時、EBSボリュームを作成するアベイラビリティゾーンは、新しい環境に紐づくEC2インスタンスに合わせます。

## 作成したEBSボリュームをアタッチ
作成したEBSボリュームを新しい環境に紐づくEC2インスタンスにアタッチします。
デバイスは`/dev/xvda`にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96507/31140cbf-809b-186d-95b8-a4056a263c0b.png)

# 複製完了
これで複製が完了しました。
改めてCloud9コンソールから最初に作成したCloud9環境を立ち上げます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96507/ba9daf53-f2a2-45ae-d726-68aec79aaf61.png)

警告が出ますが「Accept」で問題ありません。

複製した環境に問題がなければ、お金がもったいないのでEBSスナップショットは削除しておきましょう。
IBM Security Verify APIの利用を特定のIPアドレスのみに制限する

# はじめに
2021年4月のアップデートで、トークンの発行や使用を特定のIPアドレス範囲に限定したり、除外したりするIPフィルタリング機能が実装されました。

|![ipfilter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678692/6e87bb9a-0aee-0307-0d8f-9cf0bd54f44f.png)
|:--|

このため、アクセストークン発行のAPIを例に、どのように動作するか確認してみました。

# 1.APIクライアントのIPフィルタについて
APIクライアントの登録画面で、IPフィルタを設定できるようになりました。
![ipfilter2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678692/8cd4a51f-c655-19ea-eba1-a60797e45776.png)

- ホワイトリスト(許可)とブラックリスト(拒否)のいずれか指定可能
- IPアドレスはレンジでの指定可能(ポリシー設定と同様です。)
    - IPv4: 1.2.3.4、1.2.3.0/24、または 1.2.3.4-1.2.3.42
    - IPv6: 2001:db8::1 または 2001:db8:1234 ::/48 または 2001:db8::1-2001:db8::ff
  
マニュアルは、翻訳前のため、言語を英語に切り替えて参照ください。
https://www.ibm.com/docs/en/security-verify?topic=access-managing-api-clients

|![ipfilter4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678692/04ff8e43-9052-399c-c980-075a426d93b5.png)
|:--|

# 2.IPフィルタ設定時の挙動確認

APIクライアントに拒否リストとして、自分の端末のIPアドレスを指定した上で、アクセストークンを取得すると、アクセストークンが取得できませんでした。

このときの、レスポンスコードは401 Unauthorizedが返ってきました。
また、エラーメッセージは以下の通りです。
{"error_description":"CSIAQ0314E The request is denied because the request origin is not allowed.","error":"access_denied"}

```
curl -i -X POST https://<テナント名＞.verify.ibm.com/oidc/endpoint/default/token -H "Content-Type: application/x-www-form-urlencoded" --data-urlencode "grant_type=client_credentials" --data-urlencode "client_id=<クライアントID>" --data-urlencode "client_secret=<クライアント秘密鍵＞"
```

![ipfilter3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678692/abf177a1-9df4-e151-80b3-68bb557856b1.png)

# 最後に
これまでは、実行できるAPIの権限や、スコープを絞り込むという方法でセキュリティを確保するようにしていましたが、今回の新機能により、特定のサーバーからのみAPI実行を許可するといった不正利用を防ぐ手段が追加となりました。
色を変えて気持ち悪いインプットを作る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111652/72bb8cf0-f2ba-9699-bcb0-a1be20dbaf74.png)



## はじめに
HTMLの`input`タグで遊べないかなと思い、
CSSで色々といじくってみました。
仕上がりがなんとも気持ち悪い感じになりましたが、うまくやればオシャレになります。
きっと。ww


## 気持ち悪いフォーム

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="result" data-user="rm5912" data-slug-hash="MWpOxvd" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Input Caret Color">
  <span>See the Pen <a href="https://codepen.io/rm5912/pen/MWpOxvd">
  Input Caret Color</a> by morioka (<a href="https://codepen.io/rm5912">@rm5912</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


## フォームの中身

HTMLは単純なインプットフォームです。

```html:HTML
<p>入力してください。</p>
<input type="text">
```

CSSはこんな感じに

```css:CSS
input {
  caret-color: #ffb2c1; /* キャレット（入力時に表示される縦線）の色を指定しています。 */
  padding: .5rem;
  font-size: 3rem;
  background-image: linear-gradient(180deg, #4db1ec, #a2e29b); /* グラデーションさせています */
  color: #ffd673; /* 文字の色を指定しています。 */
}
```
<br>
#### `caret-color`
キャレットと呼ばれる。入力時に表示される縦線の色を指定することができます。

```css:非表示
caret-color: transparent;
```
`transparent`とすると非表示になります。

#### `linear-gradient`
`background`に**`linear-gradient`**で色を`,`で区切って指定することでグラデーションを実装できます。

```css:グラデーション
background: linear-gradient(色１,色2);
```

#### `deg`
また色の指定の前に`180deg`と入れていますが、
**`deg`**は角度の単位で今回は180°回転にしています。

<br>
## 終わりに
インプットも通常だと味気ないですが、少しいじるとずいぶん印象が変わります。
アプリケーションにあったインプットタグが実装できると楽しいですよ。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111652/206947f2-51be-53c3-fc3f-5b024e9b5092.png)




> 参考サイト

 - [MDN WebDoc](https://developer.mozilla.org/ja/docs/Web/CSS/caret-color)
 - [Webクリエイターボックス](https://www.webcreatorbox.com/tech/css-tips-4)
Microsoft Build 2021 の「The future of modern application development with .NET」を視聴して学んだこと

# はじめに

本稿は、Microsoft Build 2021 の「The future of modern application development with .NET」を視聴して学んだことを書きます。
以下のURLで視聴できます。
https://mybuild.microsoft.com/sessions/ff42f714-fa18-4504-bc00-522f7b91866e?source=sessions

# 学んだこと

概要として、.NET 6 がどれだけすごいかという点にフォーカスしたセッションでした。

## .NET のエコシステムがどれだけ凄いか

- コミュニティには5百万人以上のアクティブな開発者が Visual Stuido Familyに入っている
- .NET Coreは、Stack Overflowで最も好きな(Most Loved)フレームワークとして、2019年と2020年に1位
- C#はGitHubで最もホットな言語トップ5に入っている
- .NETはリリースごとに高速になっていて、ASP.NET Core は Node.js より10倍高速

## .NET Upgrade Assistant

- 古い.NETのソースコードを最新の.NETのバージョンに移行することを支援する
- 様々なプロジェクトの種類に対応(WPF、ASP.NET、コンソールアプリケーションなど)
- 現在プレビュー版で、11月に正式リリース予定

## .NET 6 の概要

- クロスプラットフォーム開発として、MAUIで1つソースコードでAndroid、iOS、Mac、Windowsの開発ができる
- Blazer を用いて Web クラウアントも開発できる
- クラウドネイティブアプリの開発も簡単にできる
- C# 10
- 開発生産性の向上のために、ホットリロードを強化
- .NET 5 と比較して、.NET 6 は処理速度が70%向上、メモリも40％くらい削減

## ASP.NET Minimal API

- たった4行のコードで "Hello World" のWebアプリが起動する
- 従来 MVC API で137行で書いていたコードが、 .NET 6 Minimal API なら30行で書ける

## MAUI

- 1つソースコードで様々なAndroid、iOS、Mac、Windowsの開発ができる(Xamarin.Formsの後継)
- 現在プレビュー版を公開中
- デバッグ起動中にxamlファイルを変更するとすぐに起動中のアプリに反映されるホットリロード
- Windowsの通知、Androidの通知、iOSの通知のように、それぞれのネイティブな機能も簡単に実装できる

## Blazer

- Blazorで開発したものを、MAUIに統合することで、Blazorを用いてデスクトップアプリ開発もできる
- Blazorにも、ホットリロード機能がある

## ホットリロードの補足

- あらゆる機能でホットリロードができるようにする
- これにより、ビルドして起動しなおす手間が削減できるため、大幅に開発時間は短縮される

# まとめ

.NET はどんどん進化しています。ホットリロード機能が強化されたのは嬉しいです。

なお、Microsoft Build の重要なポイントを日本語で、MSの社員やMVP(エキスパートの人)が、解説してくれるイベントが2021/6/12にあります。
[Microsoft Build Recap Community Day](https://msdevjp.connpass.com/event/213915/)
awsのEC2でのredash9.0の導入方法

サイズ：t3a.medium
Ubuntu 18.04  x86　を選択

```
$ sudo apt-get update
$ sudo apt-get install apt-transport-https ca-certificates cur gnupg lsb-release
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
$ sudo usermod -a -G docker ubuntu

ここでログアウトする

$ docker info
ubuntuでdockerを実行できているか確認
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose --version
$ git clone https://github.com/getredash/setup.git
$ sudo mkdir -p /opt/redash/
$ sudo sh /setup/setup.sh
$ docker ps
コンテナが動いているのを確認
$ docker stop $(docker ps -q)
$ vim /setup/data/docker-compose.yml
すべてを下記に書き換える

version: '2'
x-redash-service: &redash-service
  image: redash/redash:9.0.0-beta.b42121
  depends_on:
    - postgres
    - redis
  env_file: /opt/redash/env
  restart: always
services:
  worker:
    <<: *redash-service
    command: worker
    environment:
      QUEUES: "periodic emails default"
      WORKERS_COUNT: 1
  server:
    <<: *redash-service
    command: server
    ports:
      - "5000:5000"
    environment:
      REDASH_WEB_WORKERS: 4
  scheduler:
    <<: *redash-service
    command: scheduler
  scheduled_worker:
    <<: *redash-service
    command: worker
    environment:
      QUEUES: "scheduled_queries,schemas"
      WORKERS_COUNT: 1
  adhoc_worker:
    <<: *redash-service
    command: worker
    environment:
      QUEUES: "queries"
      WORKERS_COUNT: 2
  redis:
    image: redis:5.0-alpine
    restart: always
  postgres:
    image: postgres:9.6-alpine
    env_file: /opt/redash/env
    volumes:
      - /opt/redash/postgres-data:/var/lib/postgresql/data
    restart: always
  nginx:
    image: redash/nginx:latest
    ports:
      - "80:80"
    depends_on:
      - server
    links:
      - server:redash
    restart: always

$ cd /setup/data
$ docker-compose up --force-recreate --build -d
```
Oracle Analytics Cloud ことはじめ：エディション（利用できる機能）について

Oracle Analytics Cloud にはエディションと呼ばれる契約形態があります。
・Oracle Analytics Cloud - Professional Edition
・Oracle Analytics Cloud - Enterprise Edition
の２つです。
それぞれ、セルフサービスBIとエンタープライズBIと呼ばれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1251127/20a0d0b4-5bec-b389-bd4e-8e8e7e190ad4.png)


詳細の機能は以下の通りです。

Oracle Analytics Cloud - Professional Edition
Professional Editionでは、次のことが可能です:

- データの管理
- ビジネス・アナリティクス・アプリケーションおよびデータ収集のためのプロセスの作成
- 提供するデータに関する洞察を発見
- インタラクティブなデータ・フローを通じたデータの準備
- 文法ベースのビジュアル化によるデータの探索
- 部門または組織内のビジネス・アナリティクスの調整


Oracle Analytics Cloud - Enterprise Edition
Enterprise Editionには、Professional Editionのすべての機能が用意されており、さらに次のことも可能です:

- エンタープライズ・ビジネス・インテリジェンス環境でのデータ・モデル、レポート、およびアナリティク・ダッシュボードの構築
- 企業データからピクセルパーフェクト・レポートを設計して公開
- 既存のオンプレミス環境からのコンテンツの移行
- さまざまなデータ・シナリオをテストする感度分析を実行
- Oracle Analytics Day by Dayモバイル・アプリケーションを使用
- オンプレミスのデータ・ウェアハウスへのライブ接続および最適化された接続を維持

別の機会にさらに詳細についてお話しします。
テリトリー管理を使用した販売効率の向上 ⇒ 高度なテリトリー管理 ⇒ エンタープライズテリトリー管理のパフォーマンスの最適化

１．エンタープライズテリトリー管理を使用するときにパフォーマンスを最適化するためのベストプラクティスは、次のうちどれですか?
２．パフォーマンスの問題を回避するために、割り当てルールで検索条件を定義するベストプラクティスは、次のうちどれですか?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/e1ca3af2-682d-e727-d7a3-9f6550940204.png)
[RTX830]YAMAHAルーターでAWS-VPCに接続してVPNをはってみた

## やりたいこと
タイトル通り、AWS-VPCを作成しVPN接続する
*プロバイダーさんにインターネット接続できる状態にしてもらっている前提です。

#環境

`OS: Windows 10`
`Terminal: Tera Term`
`Settings: AWS`
`Router: YAMAHA RTX830`


#AWS VPC設定
### カスタマーゲートウェイ
AWSでカスタマーゲートウェイの作成をします。
契約したプロバイダーから教えてもらったグローバルIPアドレスを入力します。他に`名前`を任意で設定し、後は全てデフォルトのままにしました。
![スクリーンショット 2021-05-27 17.37.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1556887/9f8f14c1-d502-0dd0-c915-982714fe607f.png)

### 仮想プライベートゲートウェイ
AWS側の仮想プライベートゲートウェイは移転前の設定があったので、それを流用しました。

### サイト間のVPN接続
`名前タグ`・・・任意の名前をつけます。
`仮想プライベートゲートウェイ`,`カスタマーゲートウェイID`・・・VPNでつなげたいゲートウェイを入力します。
入力の際、IDや名前タグで検索すればタイポが無くなります。
![スクリーンショット 2021-05-28 17.22.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1556887/a76ef598-b34e-613e-593d-4abea3f1892e.png)

他にも入力欄はありましたが今回は全てデフォルトにしました。

### VPN設定のダウンロード
VPN接続の作成が完了したら画面上にある作成したVPN接続設定をダウンロードします。
![スクリーンショット 2021-05-28 17.31.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1556887/8dc838ac-0200-6d94-2547-052e608f17f3.png)


# RTX830設定
ダウンロードしたVPN設定のファイルを開き、tunnel毎に`ipsec ike local address`をプライベートIPアドレスに書き換えます。

```
tunnel select 1
 tunnel name hoge
 ipsec tunnel 1
  ipsec sa policy 1 1 esp aes-cbc sha-hmac
  ipsec ike keepalive log 1 off
  ipsec ike keepalive use 1 on heartbeat 10 6
  ipsec ike local address 1 XXX.XXX.XXX.XXX
  ipsec ike pre-shared-key 1 text hoge
  ipsec ike remote address 1 XXX.XXX.XXX.XXX
 ip tunnel tcp mss limit auto
 tunnel enable 1
```
書き換えたらコマンド形式になっているので、全てコピーします。*余計な部分は省略しています。

今回は`きしめん`でPCとルーターを接続します。
Tera Termを開くと、`シリアル`を選択できるので選択し、OKを押します。

ユーザー→アドミンの順でログインして先ほどコピーした内容を貼り付けて実行します。
実行後、`show config`コマンドで内容が反映されていることを確認します。
内容に問題なければ、`save`コマンドを実行します。これで設定は終了です。

### きしめんとは
USB-RJ45変換用のケーブルです。はじめて見ました。きしめんとも呼ばれているみたいです。
![71w8u+Y6YZL._AC_SX425_.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1556887/7b8f3cbd-eb25-0107-cb37-b056faac3f0a.jpeg)
Audio / Videoの操作について

## 概要

Audio / Videoの自動再生が含まれるコンテンツを作成したときに詰まったことをメモとしてまとめてみる。

## Firefoxでロード感知ができない？

動画が20MBくらいあったため `canplaythrough` でロードを感知し再生しようと思い、実装を進めていた。
ローカルでは特に問題なく動作していたため、サーバーへアップ。
アップ後に確認してみるとFirefoxで動画が再生できず途中で止まってしまう自体に…。
特にエラーも出ていない、ローカルで再度確認しても動いている…なぜだ…？

しばらくしてイベント周りが怪しいと感じ、すべてのイベントの動作を確認。
結果、Firefoxのみ、ローカルでは`canplaythrough`が発生しているが、サーバーでは発生していないことが判明。

ということで、`canplay` `canplaythrough` の両方で進めることで解決した。

## ユーザーアクションがあるのに自動再生できない？

ユーザーアクションがないと自動再生ができない、困ったブラウザさんがいる。
（ミュートならユーザーアクションなしで自動再生できるはず）

「このボタンを押下後、○秒後にこのmp3を再生する」という感じで実装を進めるが、特定の処理のみ、とあるブラウザで自動再生エラー。
「ユーザーアクションがないぞ！このやろー！」って怒られる。いや、ボタン押してるじゃん…。
その後、処理順を変えたり検証してみると、あぁ…なるほどね…という原因が判明。

まず、ユーザーアクション後にAudio / Videoと関係ない非同期処理を挟むと、ユーザーアクションとみなされない模様。
そして、ユーザーアクション後、ある程度時間が経ってしまうとユーザーアクションとみなされない模様。

APIを挟んでからAudio / Videoを生成・再生するような場合は、色々と気をつけなければならない。

ふと、以前投稿した「[Window.openによる遷移で詰まったこと](https://qiita.com/chelcat3/items/b46061d90b6675c34a67)」と状況が似ていると感じた。
ユーザーアクションに依存するものは、このパターンに当てはまりそうな気がする。

## その他

video要素に `preload = 'auto'` を入れておくこと。うっかり忘れるとびっくりするくらい途中で止まる。

## 所感

ブラウザって大変。

## 参考URL

[https://developer.mozilla.org/ja/docs/Web/API/HTMLMediaElement/canplaythrough_event](https://developer.mozilla.org/ja/docs/Web/API/HTMLMediaElement/canplaythrough_event)
pipでopen3dがインストールできない

<h2>概要</h2>
pythonで点群処理をするためのライブラリ<strong>open3d</strong>をインストールしたかった。公式チュートリアルによれば「pip install open3d」で良いらしい。しかし、以下のようなエラーが出た。

```
ModuleNotFoundError: No module named 'open3d'
```
open3dが無い？？
これを解決してopen3dを使いたい。

<h2>原因</h2>
pythonのバージョンが3.9だったことが原因だった。
open3d公式HPによれば、対応しているpythonのバージョンは3.8までらしい。
(3.5以前も対応していない。)（2021/5/31現在）

<h2>解決方法</h2>
python公式からバージョン3.8のものをインストール。
その後、PCにはpythonのバージョン3.9と3.8が両方インストールされている状態なので
適当な場所に以下のコマンドでバージョン3.8のpython 仮想環境を作成。

```
py -3.8 -m venv py38env
```
「py38env」の部分は任意の名前で良い。

以下のコマンドで仮想環境オン。

```
cd py38env/scripts
activate.bat
```

これによりpython3.8の環境に入ったので
あとは「pip install open3d」でインストールできる。
以下の場所にopen3dがインストールできたことが確認できる。
「py38env\Lib\site-packages\open3d」

仮想環境から抜け出すときは以下のコマンド

```
deactivate
```
JavaScriptのよく使う機能

JavaScriptのよく使う機能についてメモ
メモ書きです。参考にしないでください

const、let等の変数宣言
初期、varのみで変数を宣言していた。
ES6ででconst、letで変数を宣言する機能が追加。 

var、const、letのそれぞれの違い

変数が文字列や値の場合
var
変数の上書　可能
変数の再宣言　可能
let
変数の上書　可能
変数の再宣言　不可能
const
変数の上書　不可能
変数の再宣言　不可能

変数がオブジェクトや配列の場合
constで定義したオブジェクトはプロパティの変更が可能

上書き方法
オブジェクトの場合
const val1 = {
 name:"Raro",
 age: 28,
};
val1.name = "Akiko";

配列の場合
onst val2 = ['dog','cat']
val2[0] = "bird";
val2.push=("monkey");

余談・・・開発では大体constを使っていくよ

テンプレート文字列
const name = "Akiko";
const age = 28;

const massage = `私の名前は${name}です。年齢は${age}です。`;
console.log(massage);
結果
私の名前はAkikoです。年齢は２８です。

アロー関数
まず関数の定義の仕方をおさらい
function func1(str) {
  return str;
}
console.log(func1("func1です"));
結果
func1です

関数を変数に代入するやり方
const func2 = function func1(str) {
  return str;
}
console.log(func2("func2です"));
結果
func2です

上をアロー関数で書くと略して書ける
const func3 = (str) => {
  return str;
}
console.log(func3("func3です"));
結果
func3です

→引数が１つの時（）を省略できる
const func3 = str => {
  return str;
}
ESXi & PT2と Ubuntu20.04 & Dockerで Mirakurunインストール

####ESXi上に仮想マシンの作成
`CPU -> 2
メモリ -> 4GB
ハードディスク -> 32GB
PCIパススルー設定 -> ホスト -> 管理 -> Xilinx Multimedia controller -> パススルーの切り替え -> アクティブ
設定の編集 -> メモリ -> 予約 -> RAMと同量に設定 -> 起動
設定の編集 -> その他のデバイスの追加 -> PCIデバイス(PT2接続済)
設定の編集 -> その他のデバイスの追加 -> USBデバイス(カードリーダー接続済)`
####OSイメージからUbuntuのインストール
・OSイメージのアップロード
`ストレージ -> データストアプラウザ -> アップロード`
・設定
`仮想マシン -> 設定の編集 -> CD/DVDドライブ1 -> パワーオン時に接続 -> チェック(OSインストール後解除)`
・インストール
`パワーオン -> インストール開始
language -> english
keyboard -> japanease`
####IPアドレスの固定化
```
$ cd /etc/netplan
$ sudo cp 00-installer-config.yaml 99_config.yaml
$ sudo vi 99_config.yaml
-------------------------------------------------
network:
  version: 2
  ethernets:
    ens192:
      dhcp6: false
      dhcp4: false
      addresses: [192.168.0.{任意のアドレス}/24]
      gateway4: 192.168.0.1
      nameservers:
        addresses: [192.168.0.1, 8.8.8.8, 8.8.4.4]
```
####自動認識されたドライバの確認
```
$ lsusb (カードリーダー）
-> Bus 002 Device 004: ID 04e6:511a SCM Microsystems, Inc. VMware Virtual USB Mouse
$ ls /dev/dvb (PT2)
-> adapter0  adapter1  adapter2  adapter3
```

###Dockerのインストール
素晴らしい参考記事。
[Ubuntu 20.04へのDockerのインストールおよび使用方法](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04-ja)
[Ubuntu 20.04へのDocker Composeのインストールおよび使用方法](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-20-04-ja)

```
-> sudoなしでdockerコマンドを使用できるようにする
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ su - $USER
$ docker version
```
###Mirakurunのインストール
[Mirakurun 3.0.0 “Night Whale” リリースのお知らせとインストール方法](https://medium.com/chinachu/mirakurun-3-0-0-night-whale-23aaf2c23c25) -> インストールから参照

```
インストール
$ mkdir ~/mirakurun/
$ cd ~/mirakurun/
$ wget https://raw.githubusercontent.com/Chinachu/Mirakurun/master/docker/docker-compose.yml
# ここでデバイスを編集 (DVB デバイス以外を使用する場合)
$ vim docker-compose.yml
# Docker コンテナイメージの取得
$ docker-compose pull
```
```
初回セットアップの実行
$ docker-compose run --rm -e SETUP=true mirakurun
```
**Mirakurun作者様に大感謝！！**

###4ch化に対応(主さんありがとうございます)

270名無しさん＠編集中 (ﾜｯﾁｮｲ b702-OU2n)2021/02/10(水) 18:15:31.11ID:7pVWiPIi0
Mirakurunはdvbv5_channels_isdbs.confに以下を追加
[BS19_3]
DELIVERY_SYSTEM = ISDBS
FREQUENCY = 1394720
STREAM_ID = 18227
その後channel.ymlに追加
- name: Ｊ　ＳＰＯＲＴＳ　３
type: BS
channel: BS19_3
isDisabled: false
serviceId: 244
で行けました BS23も今年4ch化のようなので
[BS23_3]
DELIVERY_SYSTEM = ISDBS
FREQUENCY = 1471440
STREAM_ID = 18291
もついでに入れとくといいのかなぁ？

####追記 - Mirakurunのアップデート

いつからか視聴中に固まるようになりましたがアップデートで直りました！
作者様ありがとうございます

```
//update
$ cd ~/mirakurun/
$ docker-compose down --rmi all
$ docker-compose pull
$ docker-compose up -d
```
Laravel 6.x でのバリデーションの基本的な構文

#初めに
この記事は laravel6.x を前提に記述しています。

#記事を書いたきっかけ
公式の[ドキュメント](https://readouble.com/laravel/6.x/ja/validation.html)を見ると、バリデーションの記法はいろいろと存在し、実際に書こうとするとどのように書けばよいか迷うかと思います。
今回はそんな悩みに対して基本的な機能が大体は扱える記法を紹介したいと思います。


#構文
```php
use Illuminate\Support\Facades\Validator;

(中略)

Validator::make($request->all(), ・・・(1)
    [
        (パラメータ名) => [ ・・・(2)
            '(ルール1)',
            '(ルール2)',
            ...
        ], ...
    ], 
    [
        (パラメータ名.ルール) => '(カスタムエラーメッセージ)', ・・・(3)
        ...
    ]
)->validate();
```

#(1) 入力データ（必須）
ここはバリデーションの対象となるデータを入れます。
基本的には`$request->all()`で十分かと思います。

#(2) ルール配列（必須）
ルール配列の書き方は上記の他に

```php
[
    (パラメータ名) => '(ルール1)|(ルール2)|...',
]
```

のような`|(パイプ)`でつなげる書き方もありますが、
[クロージャを使ったカスタムルール](https://readouble.com/laravel/6.x/ja/validation.html#using-closures)を使うことを考えると、
配列内に一つずつルールを書くスタイルの方が便利です。

#(3) カスタムエラーメッセージ配列（任意）
基本的なエラーメッセージは言語ファイル内のメッセージが表示されますが、
「このバリデーションではこんなメッセージにしておきたい！」といった
即席の専用メッセージにする際などに使用すると良いです。
【GA4】Google Analytics Data API を使ってTEAMSやSlackに投稿


#1.はじめに
こんにちは。はぁこです。
今日は新しくなった「Google Analytics4（GA4)」のAPIを使ってみたいと思います。GA4用のGoogle Analytics Data APIは2021年5月現在、まだ技術ブログでの紹介も少なくてさぐりになりました。

以前のGAはTEAMSのコネクタや、スプレッドシートからのコネクタなどが提供されていてとても便利だったのですが、GA４はそういったものがまだ充実していないようなので今回は自作です。



#2.参考サイト
とはいっても、ばっちり参考にさせていただいたページはあります。
こちらです。丁寧に書いていただいてとても助かりました。

参考①
[GA4(グーグルアナリティクス4)のAPI使い方解説！GASのサンプルコード紹介](https://auto-worker.com/blog/?p=3250)

参考②
[GoogleAnalyticsの内容をSlackへGASで通知するBotの作成 2](http://developabout0309.blogspot.com/2015/10/googleanalyticsslackgasbot-2.html)

#２.環境
Google Apps Script（GAS）
Google Analytics Data API(GCP上のもの)
Teams,slackのIncoming Webhook

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299024/a5475c30-00fd-d075-feb9-3d459049eaf0.png)

#３．手順
基本的には[参考①](https://auto-worker.com/blog/?p=3250)に従って進めました。そのうえで行き詰ったところだけ紹介します。

##（１）スプレッドシートからGASを起動する
サンプルプログラムではメタデータをスプレッドシートに出力する処理があるのですがGASからエディタを起動してしまうとこの処理でエラーになってしまいました。そこで、スプレッドシートからGASを起動すると処理が正常に行われました。
スプレッドシートの「ツール」－「スクリプトエディタ」から起動できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299024/cc48375f-bdb9-6d9b-ae4d-c6ca418278d1.png)

##（２）データが取得できない？
[参考①](https://auto-worker.com/blog/?p=3250)の「runReport」のサンプルコードを実行すると正常終了するのですが、取得した値が「not set」になっていました。これは、エラーではなく、取得しようとした指標値がなかったということでした。

私が取得したかったのは、前日のアクティブユーザー数や新規ユーザー数だったので、ディメンションを指定せずに、以下のようにメトリクスのみを指定したらうまくいきました。

```javascript
　let setDimension = [];
  let setMetrics = [{'name': 'newUsers'},{'name': 'activeUsers'}];
  let setDateRange = {'startDate': 'yesterday','endDate': 'yesterday'};
```

##（３）TeamsやSlackへの投稿
Slackは[参考②](http://developabout0309.blogspot.com/2015/10/googleanalyticsslackgasbot-2.html)を参考にさせていただきました。

Teamsは同様にIncoming webhooksを表示したいチャンネルにインストールしてWebhookのURLを取得したました。そして、payloadは以下のように指定しました。あとはSlackと同様です。

```javascript
     let payload = {
      "text" :  text + "<br>", //text は表示する内容をセット
      "@type": "MessageCard",
      "themeColor": "0072C6",
      "title": "昨日のアクセス状況"
    }
```

##（４）定期実行はどうやるの？

GASのメニューの中に「トリガー」があるのでここで設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299024/c65309a7-4b0b-951d-cad6-0460ba4aa27e.png)

こんな感じで時間指定（１時間の幅あり）で設定してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299024/a7925b9e-7cdd-7a8b-130a-20d90d6d65eb.png)

#４．おわり
といった感じで、無事、GA4のAPIでSlackやTeamsに情報連携できるようになりました。
今回はシンプルな指標の取得だけだったのですが、また次の機会にはコホートなど少し複雑な値の取得について書こうと思います。（余裕があればですが）

それでは、最後までお読みいただきありがとうございました☺
CarrierWave使用でNameError: uninitialized constant User::ImageUploaderというエラー

##CarrierWaveを使用しデータを登録したら、下記エラーが発生

[経緯はこちら](https://qiita.com/tama_chi/items/44ec59e548bcfe14b4e7 "CarrierWaveを使ってseeds.rbで画像を登録する")
データが格納されたことを確認しようとしたところ、下記エラーが発生しました。


```:コンソール
irb(main):001:0> User.all
Traceback (most recent call last):
        4: from (irb):12
        3: from (irb):12:in `rescue in irb_binding'
        2: from app/models/user.rb:1:in `<main>'
        1: from app/models/user.rb:2:in `<class:Food>'
NameError (uninitialized constant User (call 'User.connection' to establish a connection)::ImageUploader)
```
##対処法
```ruby:config/environment.rb
# Load the Rails application.
require_relative 'application'

# Initialize the Rails application.
Rails.application.initialize!
require 'carrierwave/orm/activerecord' #これを追記
```

上記のように、`require 'carrierwave/orm/activerecord'`を追記することで解決。

```:コンソール
irb(main):002:0> User.all
   (1.8ms)  SELECT sqlite_version(*)
  User Load (0.7ms)  SELECT "users".* FROM "users" LIMIT ?  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, name: "John", age: 20, hobby: "walking", image: "John.jpg", created_at: "2021-05-31 06:22:26", updated_at: "2021-05-31 06:22:26">]>
```
データが格納されていることが確認できました。

##無事解決…と思ったらブラウザでエラー
ブラウザで開いたところ、ブラウザでも`NameError: uninitialized constant User::ImageUploader`とのエラーが発生。

ターミナルを2つ開いて1つをサーバー起動用にしていたので、
Ctrl＋Cで一度サーバーを落とし、再起動したところちゃんと表示されました。



####Initializeとは…？な私とあなたへ

https://guides.rubyonrails.org/initialization.html
整数のkeyでネストしているparameter


以下の`parameter`で`true/false`の部分を取得して整形したい

```ruby:許可したいparams
<ActionController::Parameters {"schedules"=>{"1"=>{"id"=>"true"}, "2"=>{"id"=>"false"},"3"=>{"id"=>"false"},...}, 
"controller"=>"foo", "action"=>"update", "event_id"=>"777"} permitted: false>
    
```

<a href="https://qiita.com/kymmt90/items/4ce8618ca8f537b2ef7e" target="_blank">ネストするStrong Parametersの書きかた</a>
この記事を参考にschedulesをネストの中身の全て許可する方法としては

```ruby
params.permit(schedules: { 1: [:id], 2: [:id], .....}
```
こんな感じ？ちょっとヘンテコすぎる...(笑)

## 解決法
色々探してみたけれど、<a href="https://railsguides.jp/action_controller_overview.html#strong-parameters" target="_blank">rails guide</a>にこんな表記

```ruby
#キーが整数のハッシュは異なる方法で処理されます。これらは、あたかも直接の子オブジェクトであるかのように属性を宣言できます。
#以下のデータを許可
{"book" => {"title" => "Some Book",
            "chapters_attributes" => { "1" => {"title" => "First Chapter"},
                                      "2" => {"title" => "Second Chapter"}}}}

-> params.require(:book).permit(:title, chapters_attributes: [:title])
```

というわけで、`key`が整数のときは子オブジェクトの`key`で直接許可できるんですね

なので僕のコードも

```ruby
params.permit(schedules: [:id])
```
これで済んでしまうというわけですね

## それだけの話
CarrierWaveを使ってseeds.rbで画像を登録する

##経緯
初期データを登録する際に、画像も一緒に登録できるようにしました。
その過程を残しておきます。

##手順１　gemのインストール

```:Gemfile
gem 'carrierwave'
```


```ターミナル：ターミナル
$ bundle install 
```

##手順２　アップローダーを作成する

```:ターミナル
$ rails g uploader image //imageは任意の名前
```
##手順３　モデルのアソシエーション

```ruby:user.rb
class User < ApplicationRecord
  mount_uploader :image, ImageUploader
end
```
##手順４　登録したい画像を用意
publicフォルダ配下にimagesフォルダなどを作成し、その中に画像を格納する。

##手順５　seeds.rbに記入しデータを作成

```ruby:seeds.rb
User.create!(
  name: 'John', 
  age: '20', 
  hobby: 'walking', 
  image:File.open("./public/images/John.jpg")
)
```

```:ターミナル
$ rails db:seed
```

これで画像の登録ができました！


＜コンソールやviewの表示などでエラーが出る際は下記参照＞

https://qiita.com/tama_chi/items/bd07366c76f548512a3f