OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonでフォントを編集する


# はじめに
PythonでM+FONTSをベースにM+A1というフォントを作成しました。
(https://microraptor.booth.pm/items/2347968)
備忘録と布教の意味を込めて、プログラムでフォントを読み込み、書き出す手順を説明していきます。

- 筆者の環境
 - anaconda + Python3
- 標準以外のライブラリ
 - defcon
 - ufo2ft
- 元となるフォント
 - OpenType形式で改変が許可されているフォント
 - TrueTypeでも専用のソフトでOpenTypeに変換すればOKです

# やりたいこと
いきなりOTF(OpenType)を読み込むことはできないので、UFO(Unified Font Object)形式に書き出してから、そのファイルを読み込んでOTFを作ります。

UFOって何？　となるでしょうが、難しいことはありません。単なるxmlファイルです。
フォントデータを構成しているベジェ曲線の制御点がxmlとして書き出されます。

# OTF -> UFO
defconでFontオブジェクトを生成し、extractUFOでOTF->UFOへ書き出します。

```python

import defcon
import extractor

IMPORT_FILE = r'C:\example\abc.otf'
EXPORT_FILE = r'C:\example\abc_ufo'
font = defcon.Font()
extractor.extractUFO(IMPORT_FILE, font)
font.save(EXPORT_FILE)
```

欧文なら数秒、日本語書体なら数分でエクスポートが行われます。

# 出力結果
- glyph
 - 文字ごとのパス情報がXML形式で書かれています。(形式は.gilf) 
 - contents.plist
 - 収録している文字のパスファイル(.gilf)のリスト
- fontinfo.plist
 - フォント全体の情報（フォント名、アセンダ・ディセンダ等数値、などなど）
- kerning.plist
 - カーニング情報
- layercontents.plist
 - よくわからん
- lib.plist
 - 収録している文字のパスファイル(.gilf)のリスト
- metainfo.plist
 - フォーマット、作者等

lib.plistとcontents.plistは似ているファイルなので、どちらを見ても参照してもよいです
（lib.plistはフォント名とフォントファイルの紐付けがないが、各.gilf内にもフォント名が書かれている。contents.plistは大量の.gilf内に存在するので探すのが手間。たいていはどちらでも構わないと思われる。）

# UFO -> OTF
```python
import os
import defcon
import ufo2ft
from xml.etree import ElementTree as ET

# Const
UFO_FILE = r'C:\example\abc_ufo'
GLIF_PATH = os.path.join(UFO_FILE, 'glyphs')

font = defcon.Font()

# 編集する文字を選択
glif_root = ET.parse(os.path.join(GLIF_PATH, 'a.glif'))
glyph = font.newGlyph('a')
glyph.unicode = int(glif_root.find('.//unicode').attrib['hex'], 16)
contour_list = glif_root.findall('.//contour')

# glifファイルから<contour>配下の<point>タグを順次読み込み
for tag_contour in contour_list:
    contour = defcon.Contour()
    point_list = tag_contour.findall('./point')

    for k, v in enumerate(point_list):
        # <point>にtypeが定義されていないならNone
        _type = v.attrib['type'] if 'type' in v.attrib else None
        # ポイントを設定
        contour.appendPoint(
            defcon.Point(
                (int(v.attrib['x']), int(v.attrib['y'])),
                segmentType=_type,
            )
        )
    # 1周分の輪郭を設定
    glyph.appendContour(contour)

# OTFに書き出し
otf = ufo2ft.compileOTF(font)
otf.save('new_font.otf')
```

これで、"a"のみが設定されたフォントファイルが出力されました。
パスを操作してから書き込めば、オリジナルのフォントが作れます。

# リファレンス・参考資料
UFO公式 : https://unifiedfontobject.org/
DEFCON : https://defcon.robotools.dev/en/latest/
https://qiita.com/irori/items/5518c242e0244838783b
https://blog.hgrs.me/20190806022544
https://irori.github.io/wapuro-mincho/
H2 Database のデータベースを任意の場所に作成する方法

<h1>はじめに</h1>
H2DBのデータベースを新しく作成するときに、システムデフォルトの
JDBC URL:　jdbc:h2:~/test　(C:\Documents and Settings\[username]\test )ではなく
直接アクセス可能なCドライブ直下のディレクトリに作成したいけど上手くいかない時の解決策です。
今回は「C:\data」のディレクトリに「test」という名前のデータベースを作成したいと思います。

<h1>解決策１</h1>
H2コンソールから　JDBC URL:に「jdbc:h2:C:\data\test」と入力し接続をクリック
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664939/b92121b1-fe6d-b90a-404e-e57a707c5df6.png)

※解決策１でデータベースが作成出来るかもしれませんが、変なエラーが出て作成出来ない可能性があります。
<h1>解決策２(解決策１でダメな時)</h1>
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664939/84e1a159-9962-cd36-749e-c152703534e8.png)
デスクトップ右下の「隠れているインジケーターを表示します」
→「H2 Database Engine」右クリック
→「Create a new database...」
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664939/2e755c7e-e887-0d58-8c5b-2cf95d15c3d9.png)

Database path:「C:\data\test」
Createをクリック
※Password:は入力が要求されるので適当なものを入れる
<h1>おわりに</h1>
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664939/3b127fe9-5220-81f1-379f-e8632034060c.png)
データベースが作成されました
<h1>補足</h1>
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664939/3a5620ab-5461-7215-62a5-3f545109d347.png)

解決策２を用いた場合、データベース接続後に、このSQLコマンドを実行すればパスワードが消せます。
「ALTER USER sa SET PASSWORD '';」
render後のビューの崩れの原因と解決策【備忘録】

# エラーの内容
送信フォームを押すと、画像のフォームが消えてしまう。

# 状況
フリマアプリのコピーアプリで、商品出品機能を実装中。
itemモデルのデータを登録できるようにフォームを作成し、バリデーションを設定した。
その後、商品画像であるimageモデルのデータも同時に登録できるようフォームを作成した。
データが登録されなかった場合は、元の画面に遷移するようにコントローラーにrender :newを記述。
バリデーションの確認のため、フォームを空白にして送信したところ、renderによってページが遷移したが、
画像の投稿フォームのみ消えてしまうという挙動を示した。

ブラウザの画像
https://gyazo.com/c0ba763dcc41e6b4d2fdbc8246208a32

遷移後のブラウザの画像
https://gyazo.com/bd1b0e347b3156486149240e7479af98

# コード

```items_controller.rb
class ItemsController < ApplicationController
  def index
  end

  def new
    @item = Item.new
    @item.images.new
  end
  
  def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to root_path
    else
      render :new
    end
  end

  private  
  def item_params
    params.require(:item).permit(:name, :description, :brand, :condition, :status, :shipping_costs, :shipping_from, :shipping_date, :price, images_attributes: [:src])
  end
end
```

```items/new.html.haml
.sell-items__top
  .sell-items__top__container
    = form_with(model: @item, local: true) do |f|
      %ul
        %li.sell-items__top__container__list
          = f.text_field :name, placeholder: "商品名"
        %li.sell-items__top__container__list#image-box
          = f.fields_for :images do |image|
            %div{data: {index: image.index}, class: 'js-file_group'}
              商品画像
              = image.file_field :src, class: 'js-file'
              %br/
              %span.js-remove 削除
        %li.sell-items__top__container__list
          = f.text_area :description, placeholder: "説明文", rows: "10"
        %li.sell-items__top__container__list
          = f.text_field :brand, placeholder: "ブランド"
        %li.sell-items__top__container__list
          = f.text_field :condition, placeholder: "状態"
        %li.sell-items__top__container__list
          = f.text_field :shipping_costs, placeholder: "送料"
        %li.sell-items__top__container__list
          = f.text_field :shipping_from, placeholder: "発送元"
        %li.sell-items__top__container__list
          = f.text_field :shipping_date, placeholder: "発送日"
        %li.sell-items__top__container__list
          = f.text_field :price, placeholder: "価格"
        %li.sell-items__top__container__list
          = f.submit "SEND"
```

# 解決策
createアクションに追記。

```items.controller.rb
  def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to root_path
    else
      @item.images.new ←
      render :new
    end
  end
```

# 原因
renderによって遷移した先の画面は、items/newに見えるが、パスを確認するとitems/createであることがわかる。
つまり、見かけはnewアクションだが、中身はcreateアクションのビューである。
そのため、createアクションでelseの後にインスタンスを作成しないとフォームが消えてしまう。
LaravelでGoogle App Engine Cloud Taskを実行する

# Cloud Tasksを使って好きなときに実行できるバッチを作りたい

Cloud Taskを受けるサービスの作り方は以下が参考になった。
[App Engine と Cloud Tasks で単発のバッチジョブを実行する](https://addsict.hatenablog.com/entry/2018/09/28/190113)
ただし、deployにCloud Buildを使っている場合、queue.yamlを利用するとIAMで権限付与が面倒。

### queueは1度作成すれば使い回せるので、gcloudコマンドで作った

```bash
gcloud tasks queues create キューの名前 --max-dispatches-per-second=1 --max-concurrent-dispatches=1 --max-attempts=1
```

これでタスクを登録したとき、1回だけ実行してくれるキューがつくれた。

## APIを使うのでcredentialファイルを取得

以下を参考に、キューを作成したプロジェクトで、サービスアカウントキーを作成。
[Google Cloud Platform のサービスアカウントキーを作成する
](https://www.magellanic-clouds.com/blocks/guide/create-gcp-service-account-key/)

ダウンロードしたJSONファイルは、Laravelの直下にでも配置しておく。

## タスクを登録するServiceクラスを作成

まずはライブラリをインストール

```bash
composer require google/apiclient:"^2.0"
```

次にServiceクラスを作成

```php:app/Services/GoogleApiClientService.php
<?php

namespace App\Services;

use Google_Service_CloudTasks_AppEngineHttpRequest;
use Google_Service_CloudTasks_CreateTaskRequest;
use Google_Service_CloudTasks_Task;

class GoogleApiClientService
{
  private $service;
  private const SCOPE = 'https://www.googleapis.com/auth/cloud-platform';
  private const PARENT = 'projects/[プロジェクト名]/locations/[asia-northeast1とかのリージョン名]/queues/[キューの名前]';
  private const RELATIVE_URL = '/api/hoge'; # 実行したいバッチプログラムの相対パス https://xxxx.appspot.com/api/hoge
  private const METHOD = 'GET';

  public function __construct()
  {
    $client = new \Google_Client();
    # ダウンロードしたサービスアカウントキーのファイルパスを指定する
    $client->setAuthConfig(base_path() . '/' . env('GOOGLE_CLOUD_KEY_FILE'));
    $client->addScope(self::SCOPE);
    $this->service = new \Google_Service_CloudTasks($client);
  }

  public function createSitemapTask()
  {
    $parent = self::PARENT;
    $request_body = new Google_Service_CloudTasks_CreateTaskRequest();
    $request_body->setTask($this->task());
    $this->service->projects_locations_queues_tasks->create($parent, $request_body);
  }

  private function task()
  {
    $app_request = new Google_Service_CloudTasks_AppEngineHttpRequest();
    $app_request->setRelativeUri(self::RELATIVE_URL);
    $app_request->setHttpMethod(self::METHOD);

    $task = new Google_Service_CloudTasks_Task();
    $task->setAppEngineHttpRequest($app_request);

    return $task;
  }
}
```

## cronの設定をする
ファイル名はcron.yamlにしないとエラーになる。
cloud buildでdeployしているなら、xxxxx@cloudbuild.gserviceaccount.comに対して、「Cloud Scheduler 管理者」を追加

```yaml:cron.yaml
cron:
  - description: "HOGE KING"
    url: /api/hoge
    schedule: every 24 hours
```

## Controllerの呼び出し部分を作成

```php:app/Http/Controllers/HogeController.php

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Services\GoogleApiClientService;
use Illuminate\Http\Request;

class HogeController extends Controller
{
  public function index(GoogleApiClientService $googleApiClient, Request $request)
  {
    # gaeのcronからアクセスは、X-Appengine-Cron=trueのヘッダが送信される
    if (env('APP_ENV') === 'production' && $request->header('X-Appengine-Cron')) {
      $googleApiClient->createSitemapTask();
    }
  }
}


```
C#でCSVを出力するときの注意

#CSVって便利だよね
Excel等の表計算ソフトにデータをエクスポートしたいとき、.xlsxを作ったりするの、できなくはないけど結構面倒ですよね。だから、CSVで出力したりする。
単に数字やアルファベットだけを取り扱うのなら、

```csharp:export.cs
File.WriteAllLines(FileName, CSVData);
```

みたいな形で書けます。
しかし、CSVDataにASCII以外の文字が入っていると、Excelは正確に認識してくれません。
UTF-8で書き出しているはずなのですが、ShiftJISとして読んだ結果文字化けします。
(Excelから開くことはできますが、CSVの関連付けで開くと文字化けします)

さて、解決方法です。日本語版のExcelですと、デフォルトはShiftJISとして認識します。一方、英語版だとANSIで認識します。一方、BOM付きのUTF-8で書き出すとExcelはちゃんと認識してくれて、UTF-8として読み込んでくれます。
BOMがついていない場合はデフォルトのコードページで、BOMがついている場合BOMに応じて読み込むそうです。

#UTF-8(BOM)付きで書き出す方法
あれ？と思うかもしれません。
確かに、File.WriteAllLinesには、Encoding引き数があります。しかし、デフォルトでUTF-8(BOM無し)で書き込むわけで、Encode引数指定なしでの書き出しはEncodingにEncoding.Text.UTF8Encodingを指定した書き出しが実行される様に思うじゃないですか。
そう、そんな風に考えていたことが私にもありました。


実はMicrosoftのサイトにはこういうことが書いてあります。
>メソッドの既定の動作で WriteAllLines は、バイトオーダーマーク (BOM) なしで utf-8 エンコーディングを使用してデータを書き出します。 ファイルの先頭に UTF-8 識別子 (バイトオーダーマークなど) を含める必要がある場合は、 WriteAllLines(String, String[], Encoding) エンコードでメソッドオーバーロードを使用し UTF8 ます。
[System.IO.File.WriteAllLines](https://docs.microsoft.com/ja-jp/dotnet/api/system.io.file.writealllines?view=netcore-3.1#System_IO_File_WriteAllLines_System_String_System_String___)

そう、Encoding.Text.UTF8Encoding指定とEncoding指定なしのメソッドは動作が違います。

そうです。

```csharp:export.cs
File.WriteAllLines(FileName, CSVData、Encoding.Text.UTF8Encoding);
```
が正しいのです。これで、Excelがちゃんと文字コードを認識して読み込んでくれます。めでたしめでたし。
numpy.random.poisson の実装メモ

# 背景

numpy.random.poission を C/C++ で実装したい.

C++11 であれば標準で poisson 分布が使えるが, 多少再現性が変わってもいいので, 並列で高速に計算したいとかで自前実装がほしい.

## 実装

`random/src/mtrand.pyx` にインターフェイスがあります.
実際の実装(`random_poisson`)は `random/src/distributions/distributions.c` にあります.

https://github.com/numpy/numpy/blob/f842c487da5096bd3e76a716ca033681981821c7/numpy/random/src/distributions/distributions.c#L547

The transformed rejection method for generating Poisson random variables (1993)

と古い文献を参考に実装してあります(マジックナンバーたくさんでわけわからぬ)

ちなみに libcxx の poisson 実装もマジックナンバーだらけです(かつコメントがまったくないのでなにを参考に実装してあるかわからぬ)

gnustl もよくわからぬ実装ですが, 一応参考文献のコメントがあります.

Non-Uniform Random Variates Generation(1986 と古い)

## さらなる高みへ(高速生成)

cuRand や rocRAND が参考になるかも(rocRAND はソースコード公開)
POSTMANのダウンロード～新規リクエストの作成（作成画面を出すところまで）

筆者自身、POSTMANに詳しいわけではないのですが、どうやらAPIを投げるためには非常に便利なツールのようです。

ということで筆者の会社では、IQ BotにAPIを投げるときはいつもPOSTMANを使っています。

このページでは、POSTMANを始める初歩の初歩、インストール～新規リクエスト作成画面を出すまでの手順を説明します。


##１．POSTMANのダウンロード

POSTMANを持っていなければ、以下のページからダウンロードしてインストールします。
https://www.postman.com/downloads/

諸々の初期設定（ユーザー登録）などがあった気がしますが詳しい手順は忘れました。。。
そんなに難しくなかった気がするので割愛します。。。

web版もあるようですが、筆者はダウンロードして使っているので、以下はダウンロード版前提での説明となります。

##２．リクエスト作成画面の起動

POSTMANのダウンロードができたら、リクエストの作成画面を起動します。

###2-1. 右上の「New」をクリックし、「Request」を選択します。
![01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/eed704c1-e809-9801-bb16-0fae94d0ce22.jpeg)

###2-2. リクエスト名をつけます。

以下のような画面が出てくるので、適当（適切）なリクエスト名をつけます。
※「適当（適切）」と書いているところは、どんな名前をつけても処理上は影響ないので自分で意味がわかる名前ならOKという意味です

例えば認証トークンを取得する「Get Token」としておきます。
![02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/c18153ca-4949-fb5e-a775-45d4bf21dc02.jpeg)

###2-3. リクエストを入れるコレクションを選択する

作成したリクエストをどのコレクション（フォルダみたいなもの）に入れるのかを選択します。

すでに作成したコレクションがある場合、下記の画像のイメージのようにフォルダのマークができているはずなので、できているフォルダから選びます。

新規にコレクションを作成する場合は「Create Collection」を押し、適当（適切）な名前をつけて右側のチェックボタンを押します。
![03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/572ba07e-196c-353b-1c49-a37e15f85881.jpeg)

###2-4. コレクションの作成

「Save to　（今作成／選択したコレクション名）」というボタンが活性化するのでそれをクリックします。
![04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/adb09917-0e4f-bfe5-c7f5-dcdf66e8cb7d.jpeg)

###2-5. リクエストの作成画面の起動！

すると、こんなかんじの画面が出てきます。
![05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/5f7b626b-3238-77d5-5460-6277af813873.jpeg)


この画面を使って、実際のリクエストを編集していきます。
PHPで連想配列を使う

#プログラミング勉強日記
2020年9月3日

#連想配列とは
　配列では各値は順番に0,1,2…と番号が割り振られていて、その番号を指定することでデータを指定することができるが、連想配列では、番号の代わりに名前をつけて管理することができる。
　配列も連想配列も使う箱は同じだが、連想配列ではデータを入れた後の扱いをわかりやすくするために、数字ではなく名前を指定することで、名前ごとのデータを取り出すことができる。

![0903.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/568a7350-eb9c-afbf-0b30-fd12cc7c5d26.png)

#書き方
　最初に、配列の書き方を復習する。

```php:配列
<?php
$sports = array("バスケ", "野球", "サッカー");
echo $sports[0];
?>
```
　
　basketaballとバスケ、baseballと野球、soccerとサッカーが対となる。この場合、配列のように0番目の値、1番目の値となるのではなく、basketballの値、baseballの値というようになる。

```php:連想配列
<?php
$sports = array(
  "basketball"  = "バスケ",
  "baseball" = "野球",
  "soccer"  = "サッカー"
);
echo $sports["basketball"];
?>
```

#参考文献
[連想配列](http://dp22265278.lolipop.jp/tutorial/php/hash.html)
[PHPで連想配列を使う方法【初心者向け】](https://techacademy.jp/magazine/4917)
プルリクレビュー時に私がやってること

知らない人もいるかも知れないプルリクのレビュー時のTipsです。

## 文脈がわからない変更履歴は、もうちょっと表示させる

変更履歴だけだと、上下が分からずどういう流れになってるのか分からない。そんなときは、以下部分をクリックするともっと表示できます。
![スクリーンショット_2020-09-03_18_59_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275926/59083641-d5e9-dd2a-df6d-fce427923039.png)

## 変更履歴が多いときは、コミットごとに見る

プルリク作成者がこまめにコミットしてくれてる必要がありますが、コミットコメントを見て特に必要そうな変更が入ってるコミットの変更履歴を見たりします。
プルリク画面内のコミットコメントクリックでも見れますし、 以下のようにFiles changedから all commitsをクリックしても選べます。

![スクリーンショット_2020-09-03_19_09_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275926/d2637033-7119-bc61-74c7-b8b3186179e6.png)

## 検索してみる

そのクラスや関数がどう使われているか、全体的に見たい時は検索してみます。
同じプロジェクトを開いていれば手元のVSCodeなどの環境で検索できますが、そうでない場合はGithubのリポジトリ内検索が使えます。
![スクリーンショット 2020-09-03 19.14.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275926/6bcea9b9-848b-485b-5776-ae4f65d32941.png)

## ファイル全体を見る

変更履歴のファイルを全部見たい場合は、右上にある「…」マークから View fileで見れます
![スクリーンショット_2020-09-03_19_22_38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275926/cb3821a7-c4a6-3be0-80e2-bdf63a956386.png)




以上、簡単なGithubのTipsでした。
北浜 本町 ヨガスタジオ | Yoga Vini Japan

ここに新しく、ヨガクラスに興味があるなら、北浜本町ヨガスタジオで 体験レッスン予約はこちら

Visit: 北浜・本町スタジオの予約
https://yogavinijapan.org/kitahama-honmachi/

![Capture.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619650/0b140779-a4bc-7421-c49c-883ba1faf50b.png)

![Capture1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619650/7a657b5d-9994-12a7-72af-03d914770f5d.png)



![Capture4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619650/e71d82c6-c719-ebf8-07e1-cf07481e697a.png)

![Capture6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619650/f94c485c-d63d-0889-b6bc-6b6f8bcf441d.png)
instantiateについて

#はじめに
ここでは技術的な部分で初心者の方向けになんとなくわかっていただけるように書いています。

###instantiate

一言で言うとゲームオブジェクトを生成する関数です。

使うところといえば、例えば敵を倒したらアイテムが出るようにしたり
銃を打ったら玉が出るようにするときに使います。


以上。


詳しく使い方を説明しているサイトがあったのでURLを貼っておきます。
https://www.sejuku.net/blog/48180
Railsのログでもちゃんとマスクする

# 結論

クエリログのマスクに関しては[こちらのコード](https://qiita.com/calorie/items/426fb4c23ab850122f93#activerecordlogsubscriber%E3%81%AB%E3%83%91%E3%83%83%E3%83%81%E3%82%92%E3%81%82%E3%81%A6%E3%82%8B)で `ActiveRecord::LogSubscriber` にパッチをあてる

# ログのマスクとは

ログ上の個人情報等の機密情報を特定の文字列に置換して隠すことを指しています。
マスクすることで機密情報の漏洩のリスクを低減させることができるため、マスクをすることは重要なことです。

# Railsでのログのマスク

Railsでのログのマスクというと `config.filter_parameters` があります。
マスクしたいパラメータを指定することで、リクエストのログ上のパラメータをマスクしてくれる便利な機能です。

ただし、[こちらのissue](https://github.com/rails/rails/issues/35421)でも挙げられているように、クエリログのマスクは対象外であり、
また、Railsはデフォルトでproductionでもログレベルがdebugであるため、
productionでもクエリログが流れて機密情報が露出してしまいます。

Rails 6では `filter_attribute` がリリースされ、inspectされてログに機密情報が露出する問題も防げるようになりました。
ただ、こちらでもクエリログをマスクすることはできませんでした。
(Active Recordの実装の仕方によってはマスクできるようになる可能性はあるのかもしれません)

# 解決策

クエリログのマスクについていくつか解決策を提示します。

## クエリログを出さない

そもそも `config.log_level = :info` にしてクエリログを出さないようにしてしまえば解決します。
ただ、全てのクエリログが見られなくなるため、できればこの方法を取りたくない場合もあると思います。

## blousonを使う

[blouson](https://github.com/cookpad/blouson) というgemがあり、特定のテーブルのクエリログをマスクする機能がある素晴らしいgemです。
ただ、マスクしたいテーブルの名前のprefixが `secure_` である必要があるのと、条件に引っかかったクエリログ全体をマスクしてしまいます。
そのため、筆者の環境では少し使いづらく、欲を言うと `filter_parameters` のように特定のカラムのみマスクしたいという思いがありました。

## ActiveRecord::LogSubscriberにパッチをあてる

提示する解決策の中で一番 `filter_attribute` の挙動に近いのがこちらではないかと思います。

以下のように、 `ActiveRecord::LogSubscriber` のログを出力する部分で、
特定の文字列のカラムをbindしているクエリログの場合は、bindしている値をマスクするようにします。

### MySQLの場合

[こちら](https://stackoverflow.com/a/43802405)で提示されている方法で、　`render_bind` の前段にマスクの処理を追加します。

```ruby:config/initializers/sql_log_filter.rb
module SqlLogFilter
  FILTERS = Set.new(%w(email password))

  def render_bind(attr, value)
    value = '[FILTERED]' if FILTERS.include?(attr.name)
    super(attr, value)
  end
end

ActiveRecord::LogSubscriber.prepend SqlLogFilter
```

### SQL Server(activerecord-sqlserver-adapter)の場合

直接sqlのログにパラメータが埋め込まれているため、該当箇所を正規表現で抜いて置換します。

```ruby:config/initializers/sql_log_filter.rb
module SqlLogFilter
  FILTER_REGEX = /email|password/i

  def sql(event)
    binds = event.payload[:binds]
    return super(event) if binds.blank?

    binds.each_with_index do |bind, index|
      next unless bind.name.match?(FILTER_REGEX)

      event.payload[:sql] = event.payload[:sql].gsub(/@#{index} = (N?'(.*?)'|\d+)/) do |_|
        "@#{index} = [FILTERED]"
      end
    end

    super(event)
  end
end

ActiveRecord::LogSubscriber.prepend SqlLogFilter
```

良いマスクライフを!
TransactionScopeでトランザクションが効かない件

#よくネットで見るやつ

TransactionScopeのインスタンスを生成後に、
SqlConnectionを接続する。

#それでもトランザクションが効かない場合

既に同一のDBに接続された、
SqlConnectionが存在してる可能性が高い。


#要するに
開きっぱなし、ダメ、絶対。

既存の仕組みのせいで、
最新技術の恩恵を受けられないなんて。。。
RSpec単体テストコード作成手順（ログインユーザー作成Ver.）

<h1>RSpec単体テストコード作成手順（ログインユーザー作成Ver.）

RSpecの単体テストコードの作り方を、ざっくり一眼でわかるようにまとめないと慣れるまで今後苦労すると思ったので、作成手順を自分なりにまとめてみました。

<h4>1.RSpecを使用するためのgem導入</h4>
・Gemfile

```ruby
group :development, :test do
  #省略
  gem 'rspec-rails'
end
```
・ターミナル
% bundle install

<h4>2.RSpecを導入</h4>
・ターミナル
% rails g rspec:install

<h4>3.テストコードの結果をターミナル上で可視化するための設定</h4>
/.rspec

```ruby
--require spec_helper
--format documentation
```
<h4>4.FactoryBotとFakerのgemを導入</h4>
・Gemfile

```ruby
group :development, :test do
  # 省略
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'faker'
end
```

・ターミナル
％　bundle install

<h4>5.ダミー用の画像を用意</h4>
public/images/ダミー画像

<h4>6.~モデルのテストコードを記述するためのファイルを生成</h4>
・ターミナル
% rails g rspec:model モデル名

<h4>7.FactoryBotを使用して、Userモデルのインスタンスを生成するための値を設定</h4>

(例）
spec/factories/users.rb

```ruby
FactoryBot.define do
  factory :user do
    nickname {Faker::Name.last_name}
    email {Faker::Internet.free_email}
    password {Faker::Internet.password(min_length: 6)}
    #6文字以下にする場合↑
    password_confirmation {password}
  end
end
```

<h4>8.FactoryBotを用いてインスタンスを生成</h4>

（例）
spec/models/user_spec.rb

```ruby
require 'rails_helper'

RSpec.describe User, type: :model do
  before do
    @user = FactoryBot.build(:user)
  end
end
```

<h4>9.exampleを整理する</h4>
(例文）
・nicknameとemail、passwordとpassword_confirmationが存在すれば登録できること
・nicknameが空では登録できないこと
〜
など。

<h4>10.exampleをテストコードのファイルに記述</h4>

(例)
spec/models/user_spec.rb

```ruby
describe 'ユーザー新規登録' do
    it "nicknameとemail、passwordとpassword_confirmationが存在すれば登録できること" do
    end
　　・・・
end
```

<h4>11.テストコードを記述,実行</h4>
・ターミナル
% bundle exec rspec spec/models/user_spec.rb

暗記せずともこれ見ればある程度作れるようになるといいな。
Composerを使ってみる

環境
初書：2020/09/03
PC：macOS 10.15.6
Composer： v1.10.10
php 7.4.9

# Composerとは
> Composerは、PHPプログラミング言語向けのソフトウェアおよび必要なライブラリの依存関係を管理する標準形式を提供するアプリケーションレベルのパッケージ管理システムである。

[wikipediaより](https://ja.wikipedia.org/wiki/Composer)

要するに、ライブラリの依存関係をまとめて処理してくれるものらしい。
逆に、ライブラリを使わないなら必要ないと思われる。（まぁ後に使う可能性があるなら先に用意しておくのもありかもしれない）

# インストール
homebrewを使う。

```zsh
% brew install composer
```
これだけ。ちなみにローカルとかグローバルとかは今は気にしなくていい（はず）
一応、```% composer -v```でバージョン情報が出てくるか確認する。

# 開発環境に追加する
作成するフォルダ直下に、```composer.json```ファイルを作成し、中身にとりあえず

```json:composer.json
{}
```
と記述する。（使うライブラリを決めているなら、この時点で記述しておくといいかも。）

次に、ターミナルから下記を実行

```zsh
% composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
```
これでvenderフォルダが作成されたはず。

# gitから除外する（任意）
vscodeで、gitを使っている場合、基本的に全てのファイル/フォルダがgitの対象になってしまい、うざいので除外していく。
まず、composer.jsonと同じ階層に```.gitignore```ファイルを作成する
このファイルに、gitから除外するファイル名等を記述する。
今回はvenderフォルダを除外したいので、

```text:.gitignore
vendor
.gitignore
```
を記述する（.gitignoreもgitから除外してもいいよね？）

# 完成
これで、準備は完了。あとはphpファイルで

```php
require __DIR__ . '/vendor/autoload.php';
```
を読み込むだけで、ライブラリを使う事ができる。

#.
と、まぁここまで書いたのはいいが、今のところ見た感じライブラリを使わない場合は全くもって必要がない。
ここで、composerのもう一つの役割を持ってくる。
それは、必要な時に必要な物だけ読み込んでくれるシステムである。

phpにはautoloadという機能があり、呼ばれたクラスが読み込まれていなかったらそれを読み込む。という機能がある。[^a1]

[^a1]: [クラスのオートローディング](https://www.php.net/manual/ja/language.oop5.autoload.php)、[spl_autoload_register](https://www.php.net/manual/ja/function.spl-autoload-register.php)、

これをcomposerがまとめて処理してくれるので、ライブラリの依存関係とかを処理できるらしい。

なので、自分が作ったクラスをcomposerに取り入れて、それを適当なタイミングで読み込んでもらえるように設定してみる。

# クラスの用意

とりあえず説明するためのクラスがなければ話が進まないので、適当にクラスを用意する。

```php:ShowText.php
<?php

namespace tes;

class ShowText
{
    public function EchoText(string $str) : void
    {
        echo htmlspecialchars($str);
    }
}
```
エスケープ処理して出力する関数があるクラス。
これを、phpフォルダを作成し、用意する。

```text
.
├── composer.json
├── vendor
│   ├── autoload.php
│   └── (その他フォルダ)
└── php
    └── ShowText.php
```
今こういう感じ。

# composerに追加する

composer.jsonに下記を追加する。
（json内部が空の場合は以下コピペ。書いている場合はrequireとかと同じ位置）

```json:composer.json
{
    "autoload": {
        "psr-4": {   
            "tes\\": "php"
        }
    }
}
```
ちなみに、psr-4という記述方式を利用するため、keyがnamespace（＋\\\\）、valueが該当ファイルまでのフォルダ構造、そしてクラス名とファイル名は同じでなくてはならない。らしい。

書き終えたら、

```zsh
% composer dump-autoload
```
を実行し、autoloaderに認識してもらう。

# 実行してみる

```php:index.php
<?php
declare(strict_types=1);

require_once __DIR__ . "/vendor/autoload.php";

$a = new tes\ShowText();
$a->EchoText("テスト表示");

```

```html
テスト表示
```

クラス宣言が長いと感じる時は、useを使うことも可能。

```php:index.php
<?php
declare(strict_types=1);

use tes\ShowText;

require_once __DIR__ . "/vendor/autoload.php";

$a = new ShowText();
$a->EchoText("テスト表示");
```
このように、autoloadの前にuseを記述しても、ShowTextファイルが読み込まれるのはnew ShowTextの箇所なので、useを使っても特に問題はなさそう。

# 終わりに
せっかくなのでライブラリをいくつか探してみようかな。。


# 参考サイト
[Composerの使い方](https://qiita.com/sano1202/items/50e5a05227d739302761)
[Visual studio codeで.vscodeディレクトリをgit管理から外す](https://qiita.com/EngTks/items/a4f875956f0b087668f6)
[自作のクラスを\`composer\`の\`autoload\`で自動読み込みする](https://labor.ewigleere.net/2019/08/06/composer-autoload/)
コンポーネントをスクリプトで無効・有効にする方法。(enabled)

#はじめに


ここでは技術的な部分で初心者の方向けになんとなくわかっていただけるように書いています。


###enabled

簡単にいうとゲームオブジェクトにつけれるコンポーネントを有効にしたり無効にしたりできる変数です。


![“shot” 2020-09-03 18.43.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688851/4e2490e5-d284-d3a7-84d3-13a86c1f2ea2.png)

画像のようにインスペクタービューの部分ですね。
コンポーネントは「Add Component」で追加できるものです。


例えば実行した瞬間「collider」コンポーネントを消すにはスクリプトでこんな感じに書く。

```
void Start(){
        var colliderTest = GetComponent<Collider>();
        colliderTest.enabled = false;
}
```

このスクリプトのvoid Start」のカッコの中を上から順番に説明すると

「colliderTest」という変数を作り、
そこに「GetComponent<Collider>」でこのスクリプトがついているゲームオブジェクトの「Cllider」コンポーネントを入れる。

最後に「colliderTest.enabled = false;」は
「colliderTest」は先ほど作った変数なのでそれを
「enabled = false」無効にしますよ、という意味です。


逆に

```
colliderTest.enabled = false;
```
の「false」の部分を「true」にすることで、コンポーネントを有効にすることもできます。

```
colliderTest.enabled = true;
```
変更後。



一応Unityの公式リファレンスを貼っておきます

[Unity公式リファレンスenabledについて](https://docs.unity3d.com/ja/current/ScriptReference/Behaviour-enabled.html)



また、今回はゲームオブジェクトについているコンポーネントを有効、無効にする方法でしたが、「ゲームオブジェクトそのもの」を有効、無効にする方法もあります。
「SetActive」というのを使うのですが、本日はここまで。
SetActiveについては公式のリファレンスを貼っておきます。

[Unity公式リファレンスSetActive](https://docs.unity3d.com/jp/current/ScriptReference/GameObject.SetActive.html)
RailsのViewに複数のsubmitボタンを設置して押されたボタンの値を取得する

# やりたいこと
以下のように、複数のボタンを設置して、押されたボタンの値を取得します
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199110/80cecc11-8244-5275-a531-c96d9eb6f2ea.png" width=50%>

# View
selectコントローラの、purposeアクションに飛びます

```button.html.erb
<%= form_tag({ :controller => 'select', :action => 'purpose' }) do %>
        <%= submit_tag "A" %><br> 
        <%= submit_tag "B" %><br>
        <%= submit_tag "C" %><br>
        <%= submit_tag "D" %><br>       
<% end %>
```
# Controller

params[:commit]にボタンの値が入ります

```ruby:select_controller.rb
class SelectController < ApplicationController   
    def purpose
        #A,B,C,Dなど、ボタンの値が入る
        @name=params[:commit]        
    end
```
PUSH通知 向いている要件

# 通知方法は大きく5パターン

- ローカルPUSH通知
- API PUSH通知
- mbass PUSH通知

# ローカルPUSH通知

### 向いている要件

- 決まった時間に通知したい場合
 - 例）19時ジャストにセール開始の通知をしたい

# API PUSH通知

### 向いている要件

- 大規模なPUSH通知
- データを用いた通知
 - 例）スコアがn以上の会員だけにキャンペーンを打ちたい
 - 例）購入回数が1回の会員に販促を打ちたい

# mbass PUSH通知

### 向いている要件

- スポットでの通知
 - 例)急に明日キャンペーンを打ちたいとなった


# 備考

他にも下記があります。

- API+ローカル PUSH通知
- mbass+ローカル PUSH通知


# まとめ

適したPUSH通知を設計するのが意外に大事...でした！
Web画面初心者の教育用メモ１[全般基礎]

#はじめに
会社でWeb画面作成の教育係になった。。ということで教育用メモ
（社内用の画面のためSEO対策などは不要）

#Web画面を作るにあたっての基礎知識
Web画面は基本的なものとして３つの要素から作られる
　・HTML　　　　…　表示するコンテンツの構造を定義するもの
　・CSS　　　　　…　表示するコンテンツのデザインの定義
　・JavaScript　　 …　コンテンツの動きを定義

#HTMLでできること（私がよく使うもの）
・文字の表示
・改行
・画像の表示
・表の作成
・コンテンツをグループ化してまとめる
・ボタンの表示
・クリックにて違う画面に遷移
・データを入力
　セレクトボックス、テキストボックス、ラジオボタン、チェックボックス等
・違う画面に遷移するときにデータを渡す
等

#CSSでできること（私がよく使うもの）
・背景の色を変更
・文字の色・サイズを変更
・要素のサイズを変更
・枠線を変更
・位置を変更
・表示非表示を変更
等

#JavaScriptでできること（私がよく使うもの）
・ユーザが入力したデータを参照
・テキストデータ書き換え
・HTMLの要素のプロパティを変更
等
MacでRedashを動かすときに参考にした記事


[mac CatalinaでRedash構築 - Qiita](https://qiita.com/nakan-D128/items/0a98d46e608b6a06b51a)

[Redash を Docker for Mac で最速で動かす - Qiita](https://qiita.com/Tomoyuki_Mikami/items/a06ee33b096bc5deb67b)

## 参考資料

以前までは[メインのリポジトリ](https://github.com/getredash/redash)だけで完結していましたが、最近セットアップを行うための[リポジトリ](https://github.com/getredash/setup)が作られました。

- https://github.com/getredash/redash
- https://github.com/getredash/setup

[Redash Instanceのセットアップについて](https://redash.io/help/open-source/setup#docker)

以下のドキュメントは開発者向けの環境構築手順です。
[Docker Based Developer Installation Guide](https://redash.io/help/open-source/dev-guide/docker)

Redashのイメージ一覧
[Docker Hub](https://hub.docker.com/r/redash/redash/tags)

## 詰まった箇所

複数バージョンの環境を試しているとpostgresで以下のエラーがでました。

```
DETAIL: The data directory was initialized by PostgreSQL version 9.6, which is not compatible with this version 9.5.23.
```

/opt/redash/postgres-dataにバージョン違いのデータが残ってたのが原因です。
`rm -rf postgres-data`でデータを削除したあとに以下を実行すると解決しました。

```
docker-compose run --rm server create_db
docker-compose up -d
```
VBAでユーザーフォムのコンボボックスのリストに値を追加する

##ユーザーフォムのコンボボックス
ユーザーフォム、コンボボックスを作成する。
（今回ボタンは使いませんがなんとなくつけてあるだけです。）
ID列のデータをすべてコンボボックスに追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700414/1dcecc3e-ce10-a25e-4cd8-8015bd4553e3.png)　　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700414/2e9ba693-09a2-172b-3432-8cfadecc5dfd.png)


```VB
Private Sub UserForm_Initialize()'コンボボックスのリストに作成シート名を追加する
    Dim IDCol As Long 'IDの列番号を格納する変数
    IDCol = WorksheetFunction.Match("ID", Master.Rows(1), 0) 'Match関数で列数格納
    Dim LastRow As Long '最終行を格納する変数
    LastRow = Master.Cells(Rows.Count, IDCol).End(xlUp).Row '最終行格納
    Dim i As Long
    
    For i = 2 To LastRow 'コンボボックスにIDをすべて追加する
        cmbID.AddItem Master.Cells(i, IDCol).Value
    Next i
    
End Sub
```
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700414/05324077-0a9b-620b-a602-427abc650ef9.png)

Initializeイベントに追加しておくことで
データが増えても毎回すべて追加されます。

##ワタシ流こだわり
ユーザーフォムのコントロールの名前付けは必ず以下のようなルールで:point_up:
フォーム：__frm～__
コンボボックス：__cmb～__
ボタン：__btn～__
【javascript】モジュールの使用

こちらの記事は以下の書籍を参考にアウトプットとして執筆しました。
[入門JavaScriptプログラミング](https://www.amazon.co.jp/dp/B07M8T65SM/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

Reactの勉強を指定いてimportとexportがよくわからなかったため

#モジュールの場所を指定
importを使うときは、「何を」、「どこから」を指定する。

```javascript:基本構文
import X from Y
```
|項目|説明|
|---|---|
|X|インポートするもの|
|Y|モジュールの場所|

例

```javascript
import myFunc from './my_format.js'
```
参考：[入門JavaScriptプログラミング](https://www.amazon.co.jp/dp/B07M8T65SM/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

モジュールの場所とモジュールから何をインポートするかを指定するには文字リテラルを使う

以下は無効

```javascript
const MyModule='./my.js'
import myFunc from MyModule;
```
ということで**Yのところは絶対に文字列**

以下は同等

```javascript
import myVal from './src/file'
import myVal from './src/file.js'
```
参考：[入門JavaScriptプログラミング](https://www.amazon.co.jp/dp/B07M8T65SM/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

##Yのファイル拡張子を省略した場合
ファイル拡張子が着いていないパスは、index.jsを含んでいるディレクトリを表す。

```javascript
// ./src/file.jsと ./src/file/indes.jsの両方と一致
import myVla from './src/file';

// ./src/file.jsのみと一致
import myVal from './src/file.js'
```
参考：[入門JavaScriptプログラミング](https://www.amazon.co.jp/dp/B07M8T65SM/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

まぁ絶対パスを書いていれば安心。

###ここまでのまとめ

構文は上記
モジュールの場所は文字列で指定
場所は拡張子の省略もできるけど、普通に拡張子まで書いたほうが今は無難

#モジュールから値をインポート

```javascript:./src/exam/file.js
export default function exam(num){console.log(num)}
```

```javascript
import A from './src/exam/file.js'
```
ここでは./src/exam/file.jsの関数をAという名前でインポートしている

デフォルトエクスポートは好きな名前でインポートできる。
ローカル環境で開発するときに使えるフリーソフト

# はじめに
私が担当しているPJはお客様都合で99%オフライン環境で開発を行っています。
当然会社としてはテレワークができる環境は整っていますが、お客様の特性上インターネット環境での開発が不可能です。

インフラを担当していることから、新しいPJに参画するとまず開発環境を確認し、適宜足りない部分を足したり、
一から作ったりということをオフライン環境で行っています(´；ω；`)ｳｩｩ

本記事については、オフライン環境でのPJのスタートアップ的な記事となります。

# 1. インフラ

今まで経験したPJでPJ開発用のサーバはさすがに用意されていました。ただし、自分専用のサーバまでは用意できないこともあったので、
そんな時は以下の2製品でサーバを構築して検証等を行ってました。
1. VMware Workstation Player
2. Virtual Box

どちらでも構築したことはありますが、個人的にはVM製品に慣れ親しんでいるので、VMware Workstation Playerのほうが好きです。

## 2. グループウェア
1. IP Messenger
2. GroupSession

オフライン環境でもグループウェアがないと指示が口頭になりがちで、あとで言った言わないともめる原因になります。
もう最小限の環境では、昔ながらのIP Messengerで情報共有したりしています。
[ダウンロード](https://ipmsg.org/)

余談ですが、利用するPCのIPmessagerのログを確認するといろんなことがわかって面白がっていましたが、
生々しいやりとりかもあって、そのうち見なくなりました(;^_^A

GroupSessionは最近愛用しています。フリー版でもメール、スケジュール管理、施設予約、掲示板機能があるので
グループウェアとしては問題ないです。サーバも低スペックのものでサクサク動くのでお勧めです。
[ダウンロード](https://groupsession.jp/)


## 3. その他
よくあるのが我々でDNSやNTPサーバを作って、上位のサーバと連携試験をしたいというときに簡単にいろいろな用途のサーバを模擬するソフトとしてはBlackjumboDogがおすすめです。
[ダウンロード](http://www.sapporoworks.ne.jp/spw/?page_id=361)

これのおかげでメールサーバをPostfixで作らなくていいし、DNSサーバをBindで作らなくていいので重宝します。
自分は使いませんがWeb/FTP/DHCPサーバも作れるので便利です。

Pukiwikiを使ってPJ wikiを運用していたこともありますが、なかなかオフラインwikiははやらず
有志がPJから離脱するともう存在が消えてしまいます…。
[ダウンロード](https://pukiwiki.osdn.jp/)

プロジェクト管理でredmine使ってたPJもありましたが、個人的にはあまり好きじゃなかったです。
[ダウンロード](https://redmine.jp/download/)

## あとがき
オフライン環境で使えるフリーソフトあれば教えてください！
はやくslackとか使って情報共有したり、zoomやteamでテレビ会議しながら開発したいな…。
後から.gitingnoreにファイルを追加する


#【1】 .gitignoreに除外したいファイルを記載
```
#.gitignore
/spec/examples.txt
```

#【２】除外したいファイルのキャッシュを削除

```
$ git rm --cashed spec/expamples.txt
```

#【３】.gitignoreをプッシュする

```
$ git add .gitignore
$ git commit -m "Add file to .gitignore"
$ git push origin master
```

#【4】githubにある除外したいファイルを削除
GitHub ActionsのAnnotationにテスト内容を反映する

## Annotationなるもの

GitHub Actionsでworkflowを開くとAnnotationsという欄があって、テスト結果とかが表示されるのですが、例えば漫然と`go test`を実行すると`Process completed with exit code 1.`しか出てこないんですね。ちゃんとテスト内容について言及してほしい。

## Problem Matcher

https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md
このProblem Matcherという機能を使うと、ステップ毎のログをスキャンして、正規表現にマッチしたものをannotationに変換してくれるようです。

```
$ go test 
--- FAIL: TestGcdNegative (0.00s)
    math_test.go:22: failed
FAIL
exit status 1
FAIL    _/home/username/src/actions-playground     0.003s
```

`go test`の出力はこんな感じですね。複数行のマッチもできるようですが、とりあえず`    math_test.go:22: failed`にマッチさせたい場合は以下のようになります。

```go-test-problem-matcher.json
{
    "problemMatcher": [
        {
            "owner": "go-test",
            "pattern": [
                {
                    "regexp": "^\\s*(.+):(\\d+): (.+)$",
                    "file": 1,
                    "line": 2,
                    "message": 3
                }
            ]
        }
    ]
}
```

数字は正規表現のグループ（それぞれの`(～)`）の番号ですね。`column`、`severity`、`code`といったオプションもあるのでリンク先を参照して頂ければと思います。

これをリポジトリの`/.github/`に置いて、ワークフローファイルの`steps`でテスト実行前にproblem matcherを有効にします。（`::`で始まるログ出力によってactions固有のコマンドが実行できます。[参考](https://github.com/actions/toolkit/blob/master/docs/commands.md#problem-matchers)）

```
      - name: enable problem matcher
        run: |
          echo "::add-matcher::.github/go-test-problem-matcher.json"
```

## 成果

以下のようにAnnotationsに表示されるようになりました。（実は下に元のエラーも残っていますが）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171370/bc9498ec-d0f4-d1a4-2259-84ada45693dd.png)

あとプルリクエストが存在する場合、以下のようにコードにも表示してくれるんですね。元のテストの出力次第ですが、problem matcherをちゃんと設定すればかなり便利になりそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171370/d9019763-b179-bbeb-3f23-61097498f6c8.png)
AWS Workspacesをpythonで追加する。

リモートワークでAWSのworkspacesを使う企業が増えてきていますが、workspacesのユーザを作るのをコンソールからだと面倒ですよね？
あまりAPI経由でやったという記事がなかったので参考までにのせます。今回はpythonで実施していますが、AWS cliからでも簡単にできると思います。

# 前提
以下の作業が完了していること
- EC2の立ち上げ および　python3,boto3のインストール
- Simple ADの作成
- WorkDocsの作成

#手順
1. EC2にRoleを付与  
注意したいのはここでユーザを作るのはworkdocsを使用するみたいです。
workdocsとworkspacesの2つをroleに追加しましょう。
![role.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702821/44d02b12-b326-6980-c372-294d7ef24942.png)

1. ADにユーザを追加  
pythonでADにユーザ追加します。1.で述べたようにworkdocs経由です。

```py
import boto3
client = boto3.client('workdocs',region_name='ap-northeast-1')
client.create_user(
    OrganizationId='d-XXXXXXXX', //ADのディレクトリIDを記載
    Username='kani',
    EmailAddress='XXXXXX@gmail.com',
    GivenName='ABCD',
    Surname='EFGH',
    Password='tesT1234',
)
```
うまくいかない場合はadのworkspaceが有効か確認してください。
![ad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702821/42c3e90e-feaa-2162-5d71-6fe9c584e77f.png)

1. workspaceを追加  
あとは追加するだけです。

```py
import boto3
client = boto3.client('workspaces',region_name='ap-northeast-1')

response = client.create_workspaces(
    Workspaces=[
        {
            'DirectoryId': 'd-XXXXXXXX',　//ADのディレクトリIDを記載します
            'UserName': 'kani',　　　　　　//2のUsernameとあわせます
            'BundleId': 'wsb-bh8rsxt14',　//追加したいworkspaceのバンドルIDを記載します
            'UserVolumeEncryptionEnabled': False,
            'RootVolumeEncryptionEnabled': False,
            'WorkspaceProperties': {
                'RunningMode': 'AUTO_STOP',
                'RunningModeAutoStopTimeoutInMinutes': 60,
                'RootVolumeSizeGib': 80,
                'UserVolumeSizeGib': 10,
                'ComputeTypeName': 'VALUE'
            },

        },
    ]
)　　
```

以上です。workdocs経由という点に気づかなくて意外に苦戦してしまいました。
よかったら参考にしてください。
Power Apps から Power Automate 経由で SharePoint の参照列登録が出来なかったお話

<h1> やりたかったこと</h1>
ダイエットのために食事記録をつけようとPower Appsアプリを作ってました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/51f37adb-7cd5-afa9-be3a-1dec1129c78b.png)


* SharePoint上にメニューマスタのリストを持ちカロリーやタンパク質などの情報保持
* 食事記録自体はドキュメントライブラリで作成
    * アプリ上で撮影した画像を保持
    * カロリーなどの情報は複数値の合計を自動算出
    * メニューマスタから複数の値を選択可能（参照列）

ロジックはシンプルなのですぐ出来るだろうと思ってました。

<h3>アプリ上で撮影した画像を保持</h3>
これは記事がたくさんありますよう、Power AppsからSharePointに直接画像のアップロードはできません。なのでPower Automateへイメージ情報を渡し、ファイルのコンテンツを
dataUriToBinary(イメージ情報)
と指定することでアップロード可能です。ここは問題ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/f94d3920-c2ea-196a-84c7-6ece726a4915.png)

<h3>カロリーなどの情報は複数値の合計を自動算出</h3>
前提として、SharePoint参照列の設定において複数値を許可しておきます。
あとはPower Appsの参照列の「OnChange」項目でForAll、要するにループして値をコレクトしていきます。参照列は直接読み取れなかったのでID列でLookUpして取得しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/8e407c97-78c4-cd38-9fa6-82f416259da3.png)

合計値はSum関数で算出できます。kcalは上述のコレクトする際に設定したプロパティ名です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/23b66f94-7a1e-7408-0281-8b9a981077e3.png)

<h3>メニューマスタから複数の値を選択可能（参照列）</h3>
問題はこいつです。Power Automateで参照列の複数設定、これは可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/f0c6fcfb-2529-9175-e5e7-33b21cff80ed.png)
JSON形式でIDを指定します。これだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/c6576438-8adc-7666-74e0-207cc8303a43.png)

この部分をPower Appsから渡せるようにフローを設定します。<font color="red">【Point】</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/004b3022-7a0f-424b-ca4f-b0950ee60116.png)

ではこれをPower Appsから渡しましょう。
やはりこれも参照列の「OnChange」項目に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/441701a7-3e8e-cfc4-625c-3ee2a2a1009e.png)

結果、jsonIDs に思った通りのJSON値が設定されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/bb168abf-1723-74d7-c971-c0d4440905c3.png)

さて、問題はここから。フロー上でJSON値を受け取るように設定すると（<font color="red">【Point】</font>）、Power Appsからフローを設定出来なくなります。下は登録ボタンにフローアクションを設定しようとした画面です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/b4a107b8-9c5d-ccbc-2597-2bab7a57670b.png)

エラーの詳細は開発ツールから確認できるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696735/85a8803d-5d53-c580-ce25-41a0aa45583f.png)
401やら404、400と色々エラーが出ますねぇ。。

わけわからん！という話でした。解決策分かる方いらっしゃいましたら教えてください。
FirebaseへiOSアプリのdSYMを手動でアップロードする


## 概要

Firebaseのクラッシュ解析用に、iOSアプリのdSYMを手動でアップロードする手順メモ。

現在のFirebase Crashlytics SDKを使った方法です。（旧Fabric SDKは非推奨）

## 手順

### dSYMをApp Store Connectからダウンロード

対象アプリのdSYM一式をダウンロードします。

1. **App Store Connectへアクセス**
2. **Appから対象アプリを選択**
3. **アクティビティを選択**
4. **iOSビルド一覧から、対象バージョンのビルドを選択**
5. **一般情報欄の「シンボルを含む」の「dSYMをダウンロード」**

完了すると、dSYM一式が入っているappDsymsがダウンロードされます。

### アップロード用スクリプト「upload-symbols」を用意

場所は、プロジェクトのpods内、FirebaseCrashlytics以下にあります。

```
/path/to/pods/directory/FirebaseCrashlytics/upload-symbols
```

### Firebase設定用の「GoogleService-Info.plist」を用意

こちらもプロジェクト内から持ってきます。

```
path-to-YourApp/YourApp/GoogleService-Info.plist
```

### 任意の場所に配置

各ファイルを直接指定しても良いですが、今回はコピーして任意の場所に配置します。

```
./
├── GoogleService-Info.plist
├── upload-symbols
└── v1.0.0
    └── appDsyms
```

### dSYMをアップロード

各ファイルを配置したディレクトに移動して、スクリプトを実行。

FirebaseにdSYM一式をアップロードします。

```shell
./upload-symbols -gsp GoogleService-Info.plist -p ios v1.0.0/appDsyms/
```

実行すると、つらつらと以下の様なメッセージが流れ

```
Fetching upload-symbols settings...
Successfully submitted symbols for architecture armv7k with UUID xxxxxxx in dSYM: ...xxxxx-xxx-xx.dSYM
...
...
```

```
Successfully uploaded Crashlytics symbols
```
と出たら完了です。


---
参考：
[Firebase Crashlytics SDK を使用して難読化解除されたクラッシュ レポートを取得する](https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports?platform=ios&authuser=0)
ネットワークを構成する装置、データの誤り制御　

#ネットワークを構成する装置
　ネットワークの世界で働く代表的な装置は、ルータやハブ、ブリッジ、リピータ等がある
#代表的な装置の性能

　**リピータ**
　ケーブルを流れる電気信号を増幅して、LANの総延遠距離を伸ばす中継機器
　<font color="#cc0000">※パケットの中身を解さず、ただ電気信号を増幅するだけなので、不要なパケットも中継してしまうところがある。</font>
　
　**ブリッジ**
　セグメント間の中継役として、ながれてきたパケットのMACアドレス情報を確認、必要であれば他方のセグメントへと
　パケットを流す機器
　中継パケットはCSMA/CD方式に従って送出するため、コリジョンの発生が抑制されて、ネットワークの
　利用効率向上に役立つ

　**ルータ**
　異なるネットワーク(LAN)動詞の中継役として、流れ的たパケットIPアドレス情報を確認した後に、最適な
　経路へとパケットを転送する。

　**ゲートウェイ**
　異なるネットワーク間で、プロトコル変換による中継機能を提供する装置
　ネットワーク双方で使っているプロトコルの際をこの装置が返還、吸収することで、お互いの接続を可能とする

#LANの装置とOSI基本参照モデルの関係
　ネットワークで用いる各装置はOSI基本参照モデルでいうとどの層に属しているかは以下の通り
　![図107png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/90a42de6-4fd5-d9ec-e886-1e5b176d0826.png)


#NIC(Network Interface Card)
　コンピュータをネットワークに接続するための拡張カードが**NIC(lANボード)**と呼ぶ

　**NICの役割**
　データを電気信号に変換してケーブル上に流すこと、そして受け取ることです。
　NICをはじめとするネットワーク機器には製造段階で**MACアドレス**という番号が割り振られている
　IEEEによって管理される製造メーカー番号と自社製品に割り振る製造番号との組み合わせで出来ていて
　世界中で重複しない一意の番号であることが保証されている

#データの誤り制御
　ケーブルの上を流れるのは電気的信号のみ、この信号の波形を「この範囲は0または１」と値に置き換えることで
　ビットの内容をやり取りしているが電気的信号なので、伝送距離が延びれば信号は減衰したり
　横から別の電気的な干渉を加えてやれば波形は乱れてしまいこうして生まれるのがデータの誤り
　これらを防ぐには**パリティチェック**やCRC(巡回延長検査)などを用いて、誤りを検出したり
　修正したりする。

#パリティチェック
　送信するビット列に対して、**パリティビット**と呼ばれる検査用のビットを付加することで、データの誤りを検出する
　![図108.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/6611c163-45dd-11b0-0c4f-176eca68dddb.png)

![図109.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/58b9f50e-bfcf-05cc-7784-6eb06b8e929e.png)


#水平垂直パリティチェック
　パリティビットは「どの方向に付加するか」によって**垂直パリティ**と**水平パリティ**に分かれる
　![図110.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/97957519-22df-90e6-7e18-44ce0968b21f.png)


#CRC（巡回延長検査)
　ビット列を到底の式(生産多項式)で割り、そのあまりをチェック用のデータとして不可する方法



#以上
文字列のフォーマット

#文字列のフォーマット

Pythonのようなスクリプト言語では、定型の文章の中に一部だけ任意の文字列を差し込む処理を多く実行します。
年賀状を印刷する時にメッセージの中の名前だけ変えて印刷するようなことがありますが、差し込み処理とはそのような処理を
指します。Pythonでは、差し込み処理を手軽に行うためにformat()メソッドが用意されています。
format()メソッドでは、波括弧（ { } ）で囲んだ文字列を使って、テンプレートのなかに文字列を差し込む位置を指定します。
{0}や{1.attr_a}のようなフォーマット文字列にオブジェクトを渡すと、波括弧で囲まれた部分に文字列を差し込んだ状態の文字列を返します。
format()メソッドを使うと、Webアプリケーションフレームワークなどで利用するテンプレートエンジンに近い
高度な文字列フォーマット機能を利用できます。
なお、Python2で使っていた%演算子を使う文字列フォーマット機能は、いずれ廃止されると予告されている機能です。
Python3.5までのバージョンではまだ利用できますが、先々のことを考えると、特別な理由がない限りformat()メソッドを使うようにしたほうが良いです。

＊フォーマットに要素を埋め込む＊
Jupyter Notebookを使って、format()メソッドを使った例が下記です。
format()メソッドなので、文字列のリテラルや変数から呼び出します。

【フォーマットに文字列を差し込む】
“{}” loves Python !”.format(‘Hatamoto’)
↓
Hatamoto loves Prthon !

ダブルクォーテーションで囲まれた部分が、テンプレート文字列になります。
波括弧で囲まれた部分に、format()メソッドに引数として与えた文字列を挿入して結果を返します。

波括弧の置き換え部分は、複数記述できます。
format()メソッドとリスト、for文を組み合わせて、Python関連サイトのリンクを作成する例が下記です。


【複数同時に差し込む】
linkstr = ‘<a href= “{}”>{}</a>’
for i in [ ‘http://python.org’,
	     ‘http://pypy.org’,
	     ‘http://cython.org’,]:
    print(linkstr.format(i i.replace(‘http://’, ‘’)))
↓
<a href= “http://python.org”>python.org</a>
<a href= “http://pypy.org’”>pypy.org’</a>
<a href= “http://cython.org”>cython.org</a>

最初の要素にはURLを、次の要素には「http://」を取り除いた文字列を指定しています。
このように、定型の文字列を簡単に作ることができるのがformat()メソッドの魅力です。


＊引数の順番を指定した置換＊
{0}のように波括弧の中に数値を入れると、format()メソッドに与えた引数を数値に応じた位置に埋め込むことができます。
番号は引数の順番で、０から始めます。フォーマット文字列に、同じ番号を複数記述すると、それぞれの場所に埋め込みが行われます。
同じ要素を複数埋め込むこともできます。波括弧内の数値の最大値が、format()メソッドに与えた引数より多い場合はエラーになります。

【数値で差し込み位置を指定する】
“{0} {1} {0}”.format(’Spam’, ‘Ham’)
↓
Spam Ham Spam


＊キーワード引数を指定した置換＊
{foo}のように波括弧の中に英数を書き込むと、format()メソッドに渡したキーワード引数をベースに置換を行います。

【キーで差し込み位置を指定する】
“{food1} {food2} {food1}”.format(food1 = ’Spam’, food2 = ‘Ham’)
↓
Spam Ham Spam


＊ディクショナリを指定した置換＊
{0[foo]}のようにすると、引数として渡したディクショナリのキーを指定した置換を行えます。
角括弧（[ ]）の中に指定するディクショナリのキーには、クォーテーションは必要ありません。
角括弧の中身が文字列として解釈され、ディクショナリのキーに割り当てられた値を取り出して置換を行います。

【ディクショナリで差し込み位置を指定する】
d = {‘name’ : ‘Hatamoto’, ‘birthyear’ :1995}
“{0[birthyear]} is {0[name]}’s birthyear.”,format(d)
↓
1995 is Hatamoto’s birthyear.
【体験記】AZ-900 Microsoft Azure Fundamentalsに合格した

はじめに
--

先日、[AZ-900 Microsoft Azure Fundamentals](https://docs.microsoft.com/en-us/learn/certifications/exams/az-900)の試験に合格しました。
当方所属プロジェクトにて試験代金を負担してくれたこと、業務で少しAzure環境に触る機会があり、更なる知識の習得のため受験しました。
少しでも、これから試験に臨まれる方の参考になれば幸いです。

試験概要
--

Azure認定資格は数々ありますが、「Azureとはそもそも何だ？」といった概要が問われる入門レベルに位置づけされた認定資格かと思います。
また、Azureの知識だけに関わらず、「クラウドコンピューティング」自体の知識・概念も問われるため、クラウドに関する体系的な知識の習得も可能かと思います。

* 問題数：40問ほど
* 所要時間：60分 (試験時間) + 20分 (ガイダンス時間)
* 合格点：700/1000点
* 出題形式：選択方式 (*)

(*) [詳しくはこちらを参照](https://docs.microsoft.com/ja-jp/learn/certifications/certification-exams#meo-section4)

勉強方法
--

インプットは主にMicrosoft Learnおよび書籍を使用して試験勉強をしました。
アウトプットは書籍の巻末についていた模擬試験をひたすら解きました。
勉強期間はだいたい1週間ほど。一日に2、3時間ほど勉強しました。

<dl>
 <dt>● Microsoft Learn
  <dd>○Microsoft公式のAzure学習モジュールになります。試験合格に必要な知識や概要のほとんどは<a href="https://docs.microsoft.com/en-us/learn/certifications/azure-fundamentals">Microsoft Learn</a>で習得できました。
  </dd> 
  <dd>○認定試験に合格することだけが目標であればアカウント作成やサーバーデプロイ実習はスルーで良い気がします</dd> 

 <dt>● 書籍<a href="https://www.amazon.co.jp/dp/B0872LVYD6/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1">『合格対策Microsoft認定AZ-900:Microsoft Azure Fundamentalsテキスト＆問題集』</a></dt>
  <dd>○2～3周ざっくり読み</dd>
  <dd>○各章末問題/巻末模擬試験を解き、苦手な分野があれば補う</dd>
  <dd>○説明が簡潔で分かりやすいため、本書のみで試験対策も十分にできます</dd>
</dl>

試験当日
--

コロナウイルスの影響もあり、最寄のピアソンテストセンターが開いていなかったため**自宅にてオンライン受験**(在宅受験)しました。
在宅受験は今回が初体験であり、試験が実際に始まるまでは不安でしたが、終えてからの感想は「テストセンターで受験するのとあまり代り映えなかったし、何より交通機関での移動が無いのは最高だなあ」といったところでした笑
今後、在宅受験がニューノーマルになるとかなり便利ですね。
詳細については別記事でまとめたいと思いますが、試験開始まではざっくり以下の流れです。


1. 試験予約完了時にピアソンより送付される予約完了メールから、**予約した試験開始時刻30分前**に試験ログイン用URLへアクセスする。
2. 試験ログイン用URLへアクセスし、予約した試験を選択したうえで「オンライン監督下の試験を開始する」をクリックします。
3. オンライン試験開始前のチェックインプロセスが開始します。チェックインプロセスでは運転免許証orパスポートの写真、試験受験に使用するPC周りの写真を提出します。
4. 各写真の提出後、問題点(*)があった場合に試験監督官がチャットで適宜指示を飛ばしてきます。指示に従って対処します。
5. 試験監督にて、問題点が取り除かれたことを確認次第、いよいよ試験がスタートします。

(*)使用PC裏のセカンドモニターが写真に写っていたため、タオル等を掛けて画面を覆うよう指示がありました。

受験をして
--

860点を取得し合格しました。合否は受験終了後即時で確認することができます。
試験時間は60分間設けられていますが、実際に使用した時間は見直しも含めて30分ほどで終了しました。
冒頭でもお伝えしましたが、入門レベルに位置付けられている試験であったため、応用を利かした問題等は無く、Microsoft Learnおよび書籍で得た知識がそのまま問われました。
これからAzureを使って業務される方、クラウドの基礎を勉強したい方にお勧めの資格であるかなと思います。
また、上位資格であるAZ-104 Azure Administrator Associate (AZ-103は2020年8末に終了)を受験する前のワンステップとして受験するのもよいのではないでしょうか。


最後まで読んでいただきありがとうございました。
【①】WindowsServer2016　FW編

##今回やること
業務でFWを触ることになりそうなので、勉強しつつ、まとめていこうかと思います。
FWの設定は大分奥が深いので、時間をかけて学習していこうと思っています。

##使用ツール
VMware Workstation 15 Player

##FW設定
古いバージョンだとFWがデフォルトで有効化しないみたいで、
セキュリティの構成ウィザードを実行したりしてFWを有効化していかないといけなかったらしいです。
ただwinSV2012 R2でセキュリティ構成ウィザードが廃止になったようです。

つまり2016ではデフォルトでFWが効いているみたいですね。
なのでガチガチに最初から固まっているので、ping通らなかったりするようです。

特定の使うサービスだけ手動で解除しますよー。が基本的な流れになるのかな？なんて予想してます。
（勝手な憶測ですので、間違っていたらごめんなさい。）

そして、更に詳細な設定ができるFWがあるらしい。

【コンパネ】⇒【システムとセキュリティ】⇒【Windowsファイアウォール】⇒【詳細設定】

これで【**セキュリティが強化されたファイアウォール**】が開けます。

![FW.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686507/d83d510f-aa28-e03d-f8b4-7280fbc7f082.jpeg)

ちなみにツールからも開けました。
![FW1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686507/e205f338-7210-6db0-b46f-8db78878cdcf.jpeg)

ツールから開くほうが早いですね。

##まとめ
今回はFWをどこで設定すればよいか、までまとめました。
触っていれば場所は覚えていくと思います。

次回は実際にFWの設定に手をだしたいと思います。

以上となります。
VSCodeのRemote containerを利用したLaravel on docker環境でHostからコンテナ内にアクセスができない場合に確認すること

VSCodeのRemote containerを利用したLaravel on docker環境でHostからコンテナ内にアクセスができない場合に確認すること

初PHP， Laravelにつき対処法のみメモ

# 環境
- VSCode Remote container使用
- Nginx, Laravel, DBの各コンテナで構成
- Host， コンテナの外からChromeでlocalhost:8000にアクセス

# 問題

VSCodeのremote containerでlocal（host）からのアクセスが
502 Bad Gatewayになる

`.devcontainer/devcontainer.json`

```json
// Use 'forwardPorts' to make a list of ports inside the container available locally.
	"forwardPorts": [
		8000
	],
```

上記設定済みで

local => コンテナ内のnginxまでは到達できている様子

# 解決

`.devcontainer/devcontainer.json`の`command`以下をコメントアウトする

```json
# Overrides default command so things don't shut down after the process ends.
# 以下をコメントアウト
# command: /bin/sh -c "while sleep 1000; do :; done" 
```

# 参考

[php - nginx does not open localhost with VSCode Remote Containers Extension (docker compose) - Stack Overflow](https://stackoverflow.com/questions/59368396/nginx-does-not-open-localhost-with-vscode-remote-containers-extension-docker-co)
GravioでChromecastにローカルにある動画を流す

CATTではローカル動画を再生できるので、Gravioでもできるかどうか試してみた。

CATTで動画を流すための準備はこちらを参照してください。

* [GravioでChromecastに動画を流す: Windows編](https://qiita.com/zrock/items/eccb40f9c96455a9e6a9)
* [GravioでChromecastに動画を流す: ラズパイ/Linux編](https://qiita.com/zrock/items/5ff126bf64d2716299a4)
* [CATTを使ってラズパイからChromecastやNest Hubに動画やWebサイトをキャストしてみる](https://qiita.com/zrock/items/522a629262222645aefe)

# 使用環境
- Windows 10 1909 (18363.1049)
- Python 3.8.5
- CATT v0.11.3
- Gravio HubKit v3.8.4550.0
- Gravio Studio v3.8.3606.0

# 方法
[GravioでChromecastに動画を流す: Windows編](https://qiita.com/zrock/items/eccb40f9c96455a9e6a9)で使った**play-video.bat**にパスを渡すだけだった。
ただし、相対パスの場合は、`C:\ProgramData\Gravio\action\data`が基準になりますので、個々にファイルを置けば、`gravio.mp4`などと引数に渡すだけで大丈夫。それ以外は絶対パスを書きましょう。

# これができれば・・・
いちいちYouTubeなどに置かなくても素材さえ作ってしまえばいろんな動画が再生できる。公開できないような動画（別にいかがわしくはなくても）などもIoTのセンサーと絡めて再生できるので、面白いことが色々できそう。