OmniAICreator/Qiita-1.07M huggingface dataset:
Ionic ローカルでプッシュ通知のテスト （Capacitor）

#はじめに
プッシュ通知には２種類あります。
1つは全ユーザーに向けて発信するような、サーバーから送信されるもの。
→詳しくは[こちら](https://qiita.com/kokogento/items/c3390f16895a76dbde70)を参考に。

もう１つは、**リマインダーのようなローカル環境で完結する通知。**

今回はIonic + Capacitorでローカルプッシュ通知を出してみたいと思います！
[CapacitorのLocal Notifications API](https://capacitor.ionicframework.com/docs/apis/local-Notifications/) を使用していきます〜。


#push.service.tsを作成
`home.page.ts`にプッシュ通知関連のコードをかくと、ごちゃごちゃして見にくくなるので:expressionless:
serviceにまとめちゃいます。

```terminal
ionic g service service/push
```

これでserviceフォルダに、`push.service.ts`が作成されたかと。

#Local Notifications APIを使ってプッシュ通知を登録

```typescript:push.service.ts
import { Injectable } from '@angular/core';
import { Plugins } from '@capacitor/core';
const { LocalNotifications } = Plugins;

@Injectable({
  providedIn: 'root'
})
export class PushService {
  
  constructor() { }

  LocalNotifications() {
    // 5秒後に通知が来る
     const date = new Date(Date.now() + 1000 * 5);
    LocalNotifications.schedule({
      notifications: [
        {
          title: 'Title',
          body: 'Body',
          id: 1,
          schedule: { at: date },
          sound: null,
          attachments: null,
          actionTypeId: '',
          extra: null
        }
      ]
    });
  }
}
```

あとは`LocalNotifications()`を任意の`home.page.ts`にて呼び出しましょう。

```typescript:home.page.ts
import { PushService } from './../service/push.service';

constructor(
  private pushService: PushService
) {
 this.pushService.LocalNotifications();
 }
```

これでアプリを起動すると、5秒後に通知が来ます。

#毎日通知を出す

```typescript
LocalNotifications() {
    LocalNotifications.schedule({
      notifications: [
        {
          title: 'Title',
          body: 'Body',
          id: 1,
          schedule: { every: 'day' },
          sound: null,
          attachments: null,
          actionTypeId: '',
          extra: null
        }
      ]
    });
  }
```

`schedule`の所を変えれば定期的に出すことも可能。
`hour`なら1時間毎に。
<img width="641" alt="スクリーンショット 2020-01-15 20.44.22.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/796312ec-c32b-05af-a330-ff48fa5d7068.jpeg">

#疑問点

```typescript
    LocalNotifications.schedule({
      notifications: [
        {
          title: 'Title',
          body: 'Body',
          id: 1,
          schedule: { every: 'two-weeks' },
          sound: null,
          attachments: null,
          actionTypeId: '',
          extra: null
        }
      ]
    });
  }
```

`sound`はまぁなんとなくわかる。

でも`attachments`, `actionTypeId`, `extra`って一体何に使うの？？！

#ストップさせる方法
ローカルプッシュ通知をストップさせる方法について、以下の記事に書きました〜。
[ionic ローカルプッシュ通知をストップさせる](https://qiita.com/kokogento/items/66b192132a031dec6254)

もしローカルプッシュ通知関連のコードを消しても、通知がずっと来る場合の対処法です。

#指定の時間に通知を出す
[Ionic ローカルプッシュ通知を毎月指定の日に出す（Capacitor）](https://qiita.com/kokogento/items/97a9b4ee3da9a785b206)を参考に。

#

#最後に
全て公式ドキュメント通りでしたが、、、地味に`schedule`を`every`にできる事に気付かなかったので、誰かの参考になれば幸いです:sweat:

また、**Androidでも実装する場合は下記の記事も見てね〜！**
[Ionic Androidのローカルプッシュ通知はアイコンの設定が必要だった（Capacitor）](https://qiita.com/kokogento/items/760532d5879f3e70e385)
Pythonのloggingモジュールの使い方


pythonのlogginモジュールの設定を一元管理し、以下のように、日時、ログを書き出したファイル名、 ログレベル(debug, info, error, ...), 出力メッセージと、一貫したフォーマットで出力させる方法を記載します。

```bash
2020-01-15 16:54:52,751 [logTest.py:9] INFO     メッセージです。
```

※　とりあえず、使うことができれば良い人向けです。(ログをローカルに吐き出すなどの処理に関しては説明していません。)
Git: [k-washi](https://github.com/k-washi/stereophonic-Sound-System)
python ver: 3.6.8
## ログの設定

logConf.pyファイルで、formatやログレベルを管理します。

```python:utils/logConf.py 
import logging

format="%(asctime)s [%(filename)s:%(lineno)d] %(levelname)-8s %(message)s"

logging.basicConfig(level=logging.DEBUG, format=format)
```

#　ログの処理

ログ処理を行うファイルで、

1. logConf.pyまでパスを通す
2. __name__で使用するファイルにおけるロガーを設定

を行います。
そして、logger.nfoやlogger.errorによりロギングできます。

```python
import os
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

from utils.logConf import logging
logger = logging.getLogger(__name__)

logger.info("メッセージです。")
logger.error("エラーです。")

"""
2020-01-15 16:54:52,751 [logTest.py:9] INFO     メッセージです。
2020-01-15 16:54:52,751 [logTest.py:10] ERROR    エラーです。
"""
```

# まとめ

以上で、簡単なロギング処理が可能となりました。
もし、追加でログのファイル出力等を行う場合、logConf.pyファイルを変更していけば、追加機能も簡単に追加できます。
AndroidでBLEデバイスとの通信ログを見る（1）

# 

Jan 14 2020 更新

---

未知のBLE端末を、自前のオリジナルアプリから使う。っていうことをしたかったので、
BLEの仕様をハックするためにやったことの備忘録。

簡単にいうと、Androidでアプリの通信ログを取って、頑張って、どういう通信しているかを分析する。という感じになる。

## 準備

AndroidとMacを用意。

## Androidの設定

Androidの開発者向けオプションを開く。
開発者向けオプションの出し方は、ググればすぐに出るので、それを参照。

例えば↓
[[Android 4.2以降] 開発者向けオプションを表示させる方法@colorrabbit](https://qiita.com/colorrabbit/items/1416cf704bfa4a0c0a1a)

開発者向けオプションから、スヌープログを有効化、また、USBデバッグを有効化する。

スヌープログを有効化した瞬間から、ログが吐き出される。
一度オフって、もう一度オンにすると、新しいログが吐き出されているっぽい。


## Macの設定
まず、Terminalからadbコマンドを使えるようにする。
この記事を見て入れた。
[adbをMacのターミナルで使えるようにする@furusin_oriver](https://qiita.com/furusin_oriver/items/f956848788c7a63922bd)

※homebrewとかでも入れられるっぽい。

ちなみにADBは、PCに接続されたAndroidにTerminalからアクセスしちゃうぅ〜みたいなコマンド。

詳しい説明は、[ここ](https://android.keicode.com/devenv/what-is-adb.php)とかにありそう。
忙しい人用に、冒頭だけ引用↓
> Android Debug Bridge は adb と省略されます。 これは Android SDK の platform-tools に含まれるツールです。
>このツールを用いると、現在利用可能なデバイス・エミュレータの列挙、シェルコマンドの発行、ファイルの転送などが行えます。
> Eclipse から Android アプリをビルドしたときも、裏側で adb を用いてエミュレータまたは実際のデバイスに接続します。


次にWiresharkというログの解析アプリを入れる。

```
$ brew cask install wireshark
```

とすれば、Wireshark.appが自動でアプリケーションディレクトリに入っている。

## ログを解析する

まず、AndroidをMacに繋ぐ。

```
$ adb devices
```

にデバイスが出れば接続できてることが確認できる。
接続できたら、早速ログファイルを抽出する。

```
$ adb bugreport report
```

でzipをダウンロードする。
コマンド実行後、少し待つと、`report.zip`というフォルダが今いるディレクトリに落ちてくるので解凍する。
`report`の部分はフォルダの名前指定なので、なんでも良い。

その中の`bugreport-bluexxxx-XXXX.000000.000-2020-01-01-00-00-00.txt`的な奴を探す。

これにログがあるけど、Wiresharkでは読めない形式なので、変換する必要がある。

Googleがいい感じのスクリプトを配布しているので、それを使う。

[btsnooz.py](https://android.googlesource.com/platform/system/bt/+/master/tools/scripts/btsnooz.py)

`btsnooz.py`をログの`.txt`と同じディレクトリに作って、Python2で実行する。

自分は、何も考えずPythonで実行したら、環境がPython3だったので、エラー出て5秒ほどハマった。

実行コマンドはこんな感じでよしなに。

```
$ python btsnooz.py bugreport-bluexxxx-XXXX.000000.000-2020-01-01-00-00-00.txt > BTSNOOP.log
```
`BTSNOOP.log`の部分は任意で改名して良い。

ようやく。

Wiresharkを起動し、先ほどの`BTSNOOP.log`を開くと記述されているログを見ることができる。

Macならこんな感じでいきなり開ける気がする。

```
$ open -a wireshark BTSNOOP.log
```

一旦ここまで。
chrome80でSameSite=Noneを明記しないといけなくなった問題、djangoユーザーはみんなどうしてるんだろう。

# 背景
ウェブサイトの所有者は、ほかのサイトで使用可能なサードパーティCookieに明示的な属性を追加することを義務付けられるようになります。
参考：https://digiday.jp/platforms/what-is-chrome-samesite/

# django側の対応
```django/http/response.py
def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
                   domain=None, secure=False, httponly=False, samesite=None):
...
  if samesite:
    if samesite.lower() not in ('lax', 'none', 'strict'):
      raise ValueError('samesite must be "lax", "none" or "strict".')
    self.cookies[key]['samesite'] = samesite
...
```
参考：https://github.com/django/django/commit/b33bfc383935cd26e19a2cf71d066ac6edd1425f#diff-eeebc8cb56240caf13a9952431189fef

けど、これ最新の3.0.2に入ってない
Web系開発周りの自分用まとめ:学習:Ruby

# 0. 概要
Progateを使ってRubyを学んでみての感想をまとめてみた。
他言語と比較しての感想とかも混ざってる。
今後Rubyを書いてみて、あれ？これどうするんだっけ？といったときに見返す用。

# 1. 感想
- 演算子
  - インクリメント演算子++が使えない
- 条件式
  - elseifじゃなくてelsifなのにびっくり
- 括弧
  -  くくらない言語なのでちょっと違和感(VBAとかもだけど)
  - 加えて、多言語での中括弧終わりに相当するのが``end``
- 文字列内""くくりでの変数展開が#{}なのが他でみたことない感じ
  - 文字列と数値の＋での結合ができない言語なので上をつかわないとダメ
- each doに違和感
- 慣習(と思われるもの「)
  - 変数名はスネークケース
  - 配列は複数形、each doでのループ回す時のループ変数は単数形
  - タブ(インデント)は半角スペース2つ
  - bool値を返すメソッド名の末尾は?をつける
  - クラス名は大文字開始(というか言語仕様的に大文字以外できない)
  - メソッドはキーワード引数で定義して、呼び出し側の可読性を向上させるっぽい
- ハッシュ(連想配列)
  - 文字列と同じような(とはいえ異なる)シンボル(:<任意の文字列>)というものをキーにすることもできるがよくわからん・・・
  - シンボルはハッシュのキーとしてよく使い、通常は{"key1" => value1, "key2" => value2}と書かないといけないのを、省略して{key1: value1, key2: value2}と書くことができて楽。これは便利！
- nil
  - オブジェクトが存在しない、はRubyではnullではなくnil
  - 条件式でnilはfalseと扱われ、それ以外の値はtrueと扱われる仕様。へぇー
- メソッド
  - 定義するときには、``def``を頭につける。なんかCのマクロと混同しちゃう。
  - 定義時に引数の名前を``ref:``のように``:``で終わらせると、呼び出し側でその引数をつけて呼び出させることができる。キーワード引数と呼ぶ。呼び出し側のコードの可読性があがりそう！
    - ちなみに**メソッド内でのキーワード引数へのアクセスは:が取れてるバージョン**。へぇ・・・
    - 引数なしの場合は呼び出し時に()は不要。つけてもうごくけど
- クラス
    - クラス名
        - 大文字縛り。コーディング規約的に一般的なのでいいね。
    - メンバ
        - インスタンス変数と呼ぶ.変な感じ
        - ``attr_accessor :member1``のようにシンボル的な書き方をする.変な感じ。attr_accessorもなんか長ったらしー
        - インスタンス変数へのアクセスは``.member1``のようにする。**えぇ・・・``:member1``じゃないんかーい**
        - ※attr_accessor :member1はメンバ変数の宣言ではないらしい。こう書くと、``@member1``というインスタンス変数への以下のpublicなgetter, setterが定義される特殊な書き方らしい。だから、インスタンス化した後に``.member1``で外部からアクセスすると、C#やJavaのような言語でpublicなmember1というインスタンス変数へ直接アクセスするかのような使い方ができる、というものらしい。
            - ``def member1``
            - ``def member1=(引数)``
    - メソッド
        - メンバで``attr_accessor``だったから``attr_method``とか必要なのかと思いきや、ふつーに``def <メソッド名>``**ええ・・一貫性がなーい**
        - クラスに定義したメソッドはインスタンスメソッドという。**なんでなんや・・・クラスメソッドでいいじゃん。メンバの方もインスタンス変数だったけどクラス変数でいいじゃん・・・**
              - と思いきや！クラスにはインスタンス化した後に呼び出せるメソッドとインスタンス化前に呼び出せるメソッドがあり、まだ**インスタンス化していないときに使えるメソッドのことをクラスメソッドと呼ぶ！なるほど！**
                  - なお、インスタンスメソッドの主な使いみちは[インスタンス化する時に特別な処理をさせたい](https://blog.mah-lab.com/2014/11/13/instance-method-and-class-method-with-ruby/)とき、みたい。
        - 自身のインスタンス変数を使うときには``self.<インスタンス変数名>``。**なんで``this.<インスタンス変数名>``じゃないんや・・・**
          - 厳密
        - コンストラクタはinitialize()。クラス自身の名前じゃないんですねぇ・・・・
    - 継承
        - ``子クラス < 親クラス`` で定義する.``:``じゃなくて``<``なんですね。へぇー
        - 子クラスのメソッドの中で``super()``とすると親クラスの同名のメソッドを呼び出すことができる。子クラス側で親クラスと同じ処理になる場合は、こうしておくと処理の修正をしなければならないときに親クラス側を修正するだけで済みそう
- コンソールからの入力
  - gets.chompでやる。へぇー
  - gets.chompでうけとった値は絶対に文字列。なので、文字列以外の値として扱いたい場合は、メソッドを実行する。たとえば``get.chomp.to_i``みたいな。なるほどねー
- 日付を使いたい
  - Rutyで用意されているDateクラスを使う
  - ``require "date"``が必要。``require "./date"``ではダメ。へぇー
  - コンストラクタは(年, 月, 日)になっている。
  - Date.new()でなく、Date.todayとすると今日の日付でインスタンス化される。へぇー
BigQueryで1行ずつの累積を求めるメモ

## やりたいこと
日付と時間のデータを使って、1行ずつ累積して合計時間を算出したい。
毎回忘れるのでメモ
<img width="207" alt="スクリーンショット 2020-01-15 16.35.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333838/7d0f3944-c872-ff5c-7524-91b62df1c2a7.png">

## SQL
```SQL
SELECT
  date,
  minutes,
  SUM(minutes) OVER(ORDER BY date) AS total_minutes
FROM mytable
```
`ORDER BY date`でdateの順番に上から足してくれる
※ `PARTITION BY`していないので、全部の行を順番に累積

## 結果
<img width="336" alt="スクリーンショット 2020-01-15 16.35.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333838/c40cfe30-606e-f62c-1ba6-623162741c47.png">

## おまけ
`ORDER BY`を指定しないと全ての行の和を計算する

```SQL
SELECT
  date,
  minutes,
  SUM(minutes) OVER() AS total_minutes
FROM mytable
```
<img width="337" alt="スクリーンショット 2020-01-15 16.42.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333838/2b9958ae-ec90-9c57-ab87-348f5e71b257.png">
Android Studioのエラーメッセージをコピペする方法(Mac)

## 問題

最近Flutterで開発することが多くなってきました！
しかし、MacでAndroidStudioのエラーメッセージをコピペしようとするとできない・・・。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/81d023ab-0747-aaf5-d6d1-378badc9971f.png)

いろんな記事をみて、試してみたが筆者はコピペすることができなかった。

## 解決策

Dartのプラグインが入っている場合に限りますが、
AndroidStudioの画面左下に`Dart Analysis`があります！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/aa9ff4c6-3e90-88bd-a568-abf52afaccc0.png)

ここからエラーメッセージをコピペして検索！
万事解決ですね！
コマンドが失敗したときに異常終了しましたと表示させたいとき~errorlevel

#errorlevelとは
errorlevelは終了コードを取得する変数です。
0が正常終了。0以外が異常終了とします。

例えば、以下のバッチを実行すると

```例　test.bat
@echo off
rem　testディレクトリに移動
cd test
rem　エラーラベルを表示
echo %errorlevel%
```

結果は以下になります。

```例　test.bat~実行結果
指定されたパスが見つかりません。
1
```

testというディレクトリは例では存在しないので
指定されたパスが見つかりません。とエラーになります。
そして、エラーラベルは1が表示されていて異常終了していることがわかります。

#IF errorlevelとは
エラーラベルが0以外の異常終了の場合に次のコマンドを実行するバッチ作成をしたい場合にはif errorlevel を使用します。

例えば、test.batの
testディレクトリがないときは
指定されたパスが見つかりません。
ではなく
そのディレクトリは存在しません。と
表示させるようにしたいときは以下のように作成します。

```例　test2.bat
@echo off
rem エラーメッセージは空のファイルに出力
cd test 2> nul

rem もしエラーラベルが0ではないときはコメントを表示させる
if %errorlevel%  neq 0(
echo そのディレクトリは見つかりません。
exit /b
)

rem 正常終了した場合に表示
echo 正常に移動しました。

```
```例　test2.bat~実行結果
そのディレクトリは見つかりません。
```

これでtestディレクトリがない場合は、そのディレクトリは見つかりません。
と表示されるようになりました。
またtestディレクトリが存在している場合は正常に移動しました。と表示されます。

この**IF**を使った条件分岐の方法は使うことがよくあると思います。
**IF**にもいろいろ使い方があるのでぜひ調べてみてください。
Rails 初歩


![hair_long_manヒゲ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563190/9e5acc7b-a4ba-2894-909c-7b7738ebf79d.png)
　　　　　　　風邪ぎみザッキーさん

## Rails 学んでみて


### ターミナル実行を行うと、、
テキストの通り再現しているつもりでもlocalhost画面にエラーがよく出てしまいました。
それを正常にするのに手間取ってしまいました。

### 　　白い画面のエラー
　　👉ターミナル側に問題がある

### 　　赤い画面のエラー
　　👉VScode側に問題がある
 


### 　　（参考までに）自分が困ってしまった例db migrate
　　カラムを作成せずに、そのまま実行を押してしまった場合
　　　　　　　　　　　　　↓
　　カラム作成のコードを書いてからそのまま再実行しても正常に戻らない。。。


### 　　　　　　　解決策
　　rails db:migrate:status = マイグレーションファイルの状態確認
　　rails db:rollback = マイグレーションファイルをdownへ変更
　　rails db:migrate = マイグレーションファイルをupに変更
　　　　　　　　　　　　　↓
　　　　　　　　カラムを作成できた！


## 最後に
どの部分が原因のエラーなのか、わからないと詰まる。
それを分かって改善するためにはMVCをしっかり理解していないとできない。
gtag.js の eventコマンドを使用して Google アナリティクスにデータを送信してレポートを確認する手順

##実装方法

```
//リンクが押されたら計測する。
<a href="http://example.jp/" onclick="
gtag('event', 'アクション', {
'event_category': 'カテゴリ',
'event_label': 'ラベル',
'value': '値'});
">ここをクリック</a> 
```

####必須

* 【event】ここは必ず「event」。
* 【アクション】レポートでイベントアクションとして表示される文字列。カテゴリに対してユーザーの行動。

####任意

* 【カテゴリ】レポートでイベントカテゴリとして表示される文字列。分析対象を分類するために指定する名前。
* 【ラベル】レポートでイベントラベルとして表示される文字列。イベントをさらに細かく分類して計測したいときに使用する。
* 【値】イベントの値（価値）として表示される 0 以上の整数。数値別で計測したいときに使用する。（「値」の合計値出したり、平均値出したり、イベント発生セッション数をカウントしたり。）



##イベント数の計測方法
レポート画面で「行動」→「イベント」→「ページ」
イベントの状況を把握することができる。

* 合計イベント数
イベントが発生した数の合計。同じユーザーで2回イベントが発生した場合でも2回としてカウントされる。

* ユニークイベント数
カテゴリ、アクション、ラベルが同じイベントを計測しない指標。同じユーザーが何回も同じイベントが発生した場合には、すべて1として算出される。

* イベントの値
イベントに設定した値の合計値。「イベントに設定した値」×「イベントの発生回数」の合計値になる。
例えば、コンバージョンに1と設定し、10回イベントが発生した場合、イベントの値は10ということになる。

* 平均値
イベントに設定した値の平均値。


##おまけ
###gtag.jsのイベントトラッキングにはデフォルトで用意されているものもある

例　ログインを計測する

```
gtag('event', 'login');
```

```
<a href="http://example.jp/" onclick="
gtag('event', 'login');">ここからログイン</a>
```

詳細は、
[gtag.js の APIリファレンス]
(https://developers.google.com/gtagjs/reference/api?hl=ja)
SQLのjoinまとめ

#はじめに
業務で見かけたテーブルの結合を３つまとめてみました。

#具体的な内容
大きく分けてLEFT JOIN（外部結合）、INNER JOIN（内部結合）、CROSS JOIN（クロス結合）の３つを紹介します。
今回利用するテーブルは下記のpostテーブルのカラム名「post_user_id 」とそれにカラム名「user_id」で紐づくuserテーブルになります。



**userテーブル**

| user_id      |       user_name | 
|:-----------------|------------------:|
| 1            |              Aさん | 
| 2         |            Bさん | 
| 3           |              Cさん | 

**postテーブル**

| post_id     |post_user_id |    post_name    |
|:-----------------|:------------------|:------------------:|
| 1            |1  |      日記1      |
| 2           |2 |         日記2     |
| 3           |3|        日記3        |
| 4             | 1 |         日記4         |
| 5            |  |         日記5         |



###LEFT JOIN
LEFT JOINはFROM側のテーブルを軸に、紐づいていない（一致しない場合）もデータとして取得して結果を出力します。
今回の場合ですとpostテーブルのpost_idの5はuserテーブルと紐づいていませんが結果として、
userテーブルのuser_nameとuser_idがnullのまま出力されています。


```
select *
from sample_post
  Left join sample_user
  on sample_post.post_user_id = sample_user.user_id
```
結果

| post_id     |post_user_id |    post_name    | user_id      |       user_name | 
|:-----------------|:------------------|:------------------:|:-----------------|------------------:|
| 1            |1  |      日記1      |1            |              Aさん | 
| 2           |2 |         日記2     |2         |            Bさん | 
| 3           |3|        日記3        | 3           |              Cさん | 
| 4             | 1 |         日記4         |1            |              Aさん | 
|5            | |         日記5        |       |               | 



###INNER JOIN

INNER JOINはON で指定した条件で絞り込んだ結果を出力します。
今回は「on sample_post.post_user_id = sample_user.user_id」でそれぞれの紐づくidがあるものだけを
絞り込み出力するため,post_idの5は出力されません。

```
select *
from sample_post
  Inner join sample_user
  on sample_post.post_user_id = sample_user.user_id
```
結果

| post_id     |post_user_id |    post_name    | user_id      |       user_name | 
|:-----------------|:------------------|:------------------:|:-----------------|------------------:|
| 1            |1  |      日記1      |1            |              Aさん | 
| 2           |2 |         日記2     |2         |            Bさん | 
| 3           |3|        日記3        | 3           |              Cさん | 
| 4             | 1 |         日記4         |1            |              Aさん | 


###CROSS JOIN
あまり見かけないですが、CROSS JOINは2つのテーブルを交差結合させてデータを取得します。
全てのカラムをかけ合わせ、すべての組み合わせ作成するイメージになります。
以下の例でwhere句で絞り込まない場合、全パターンの20レコードが結果として出力されますが、長くなりすぎるのでpost_idが5の場合に指定した場合の結果を記載しました。post_idが5のレコードが5つ出来上がり、それぞれ異なるレコードになっているのがわかります。

```
select *
from sample_post
 CROSS JOIN  sample_user
 where sample_post.post_id = 5
```
結果

| post_id     |post_user_id |    post_name    | user_id      |       user_name | 
|:-----------------|:------------------|:------------------:|:-----------------|------------------:|
| 5            |  |      日記5      |1            |              Aさん | 
| 5           | |         日記5     |2         |            Bさん | 
| 5           ||        日記5        | 3           |              Cさん | 
| 5             |  |         日記5         |           |              | 



#終わりに
joinにもさまざまな種類があることがわかりました。
用途に分けて使い分けっていきたいですね。（個人的にはCROSS JOINの面白い使い方を考えたいところです。）
訂正点などあればご指摘いただけると幸いです。
Tidydata vs 神Excel: Rで1つのセルに入力された複数項目を分割処理する

エクセルなどで、1セル中に複数の項目が入ったデータを処理することがあると思います。基本的には`dplyr::separate`で処理すればいいと思いますが、格納されている数が不定な場合、また何個あるかわからない場合に上手く処理できる関数を知らなかったのです。

[r-wakalang](r-wakalang.slack.com)で聞いたら、Rおじさんたちが優しく教えてくれました。備忘録も兼ねて記事にしておきます。

```r
data.frame(Subject = c("A","B","C","D"),
   Value = c("a+b+c", "c","b+c", "a+b+c+d+e"))
```

# `str_split()`からの`unnest()`
```r
demo <- data.frame(Subject = c("A","B","C","D"),
				   Value = c("a+b+c", "c","b+c", "a+b+c+d+e"))
demo %>%
group_by(Subject) %>% 
mutate(Value = str_split(Value, '\\+')) %>% 
unnest()

# Groups:   Subject [4]
   Subject Value
   <fct>   <chr>
 1 A       a    
 2 A       b    
 3 A       c    
 4 B       c    
 5 C       b    
 6 C       c    
 7 D       a    
 8 D       b    
 9 D       c    
10 D       d    
11 D       e  
```

# `tidyr::separate_rows()`
```r
library(tidyverse)
demo <- data.frame(Subject = c("A","B","C","D"),
                   Value = c("a+b+c", "c","b+c", "a+b+c+d+e"))
​
demo %>% 
  tidyr::separate_rows(Value, sep = "\\+")

   Subject Value
1        A     a
2        A     b
3        A     c
4        B     c
5        C     b
6        C     c
7        D     a
8        D     b
9        D     c
10       D     d
11       D     e
```
教えていただいた方々、どうもありがとうございました！
Rails（Ruby）アプリからスプレッドシートに書き込み

## GCP側のでやること
GCPのコンソール（`https://cloud.google.com`）から、
・「Google Drive API」と「Google Sheets API」を有効化
<img width="300" alt="スクリーンショット 2020-01-15 15.49.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114895/37190f0a-4833-c22a-43d2-f359d53084c4.png">
<img width="300" alt="スクリーンショット 2020-01-15 15.48.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114895/19511ec6-ab09-afb2-2ebf-b9fa78b95f57.png">


・OAuth Client ID取得 
<img width="883" alt="スクリーンショット 2020-01-15 15.50.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114895/91d308a7-07f6-2849-3806-d845f83f9960.png">

### 注意：WEBとかiOSとかではなく、「Other」を選ぶ

## Rails（Ruby）側
gemを追加

```ruby:Gemfile
gem 'google_drive'
```

認証情報を記載するconfig.jsonを作成

```bash
touch config.json
```

```json:config.json
{
  "client_id": "xxxxxxx-xxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
  "client_secret": "xxxxxxxXXxxxxxxxxxxxxxxxxx"
}
```

キーからスプレッドシートを取得。スプレッドシートのURLのうち、`https://docs.google.com/spreadsheets/d/xxx`このxxxの部分がスプレッドシートのキーになる。

```ruby
require "google_drive"

session = GoogleDrive::Session.from_config("config.json")
sheet = session.spreadsheet_by_key("xxxxxxxxxxxxxxxxxxxxxxxxxxx").worksheets[0]

# 書き込み
sheet[1,1] = "From API"
sheet.save
```

実行すると、認証が始まるので表示されるURLに対象アカウントでログインして認証。

## 書き込まれるかどうか確認
<img width="683" alt="スクリーンショット 2020-01-15 16.12.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114895/161ee7da-9b90-9742-8c22-f457210ce099.png">
とりあえずのDockerfileを作成したい。

#応用範囲が広いDockerfileのテンプレートを書いた。
僕はArchliluxが好きだ。つまりpacmanとaurが好きだ。archwikiとpkgbuildが好きだ。
それをそのままDockerに応用したい。そしてserverでもそのまま使いたいし、開発にも応用したい。

```Dockerfile
FROM archlinux/base
ARG PKG="yay"
RUN curl https://www.archlinux.org/mirrorlist/\?country\=JP\&protocol\=https\&ip_version\=4\&ip_version\=6 > /tmp/mirrorlist && sed -e "s/^#//g" /tmp/mirrorlist > /etc/pacman.d/mirrorlist && yes "" | pacman -Syy git base-devel
RUN useradd -m -s /bin/bash docker
RUN echo 'Defaults visiblepw'            >> /etc/sudoers
RUN echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

USER docker
WORKDIR /home/docker

##RUN sudo pacman -S $PKG
RUN git clone https://aur.archlinux.org/$PKG.git && cd $PKG && yes "" | makepkg -si && rm -r ~/$PKG

ENTRYPOINT ["bash", "-c", "echo editThisCommand."]
```
##使い方
2行目を編集するだけ！AURじゃない場合普通にpacmanでインストールできます。コメントアウト等は適当に。
ARG PKG="samba"と書けばファイルサーバーになるし、ARG PKG="dnsmasq"と書けばDNSサーバーになります。

## 例えば
```Dockerfile
FROM archlinux/base
ARG PKG="samba"
RUN curl https://www.archlinux.org/mirrorlist/\?country\=JP\&protocol\=https\&ip_version\=4\&ip_version\=6 > /tmp/mirrorlist && sed -e "s/^#//g" /tmp/mirrorlist > /etc/pacman.d/mirrorlist && yes "" | pacman -Syy git base-devel
RUN useradd -m -s /bin/bash docker
RUN echo 'Defaults visiblepw'            >> /etc/sudoers
RUN echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

#USER docker
#WORKDIR /home/docker

RUN pacman -S $PKG
##RUN git clone https://aur.archlinux.org/$PKG.git && cd $PKG && yes "" | makepkg -si && rm -r ~/$PKG

##https://wiki.archlinux.jp/index.php/Samba を参考に
RUN  curl "https://git.samba.org/samba.git/?p=samba.git;a=blob_plain;f=examples/smb.conf.default;hb=HEAD" > /etc/samba/smb.conf
RUN  yes "password" | pdbedit -a docker -t
RUN chmod 0777 /home/docker
ENTRYPOINT [ "bash", "-c", "nmbd -D && smbd -F </dev/null" ] ]
```
企業やグループ内のslackでbotユーザーを作成する手順メモ


すでにslackでBotsが導入されている場合で、新たにbotユーザーを作成する場合の手順詳細を画面付きで残します。（文面での説明だけだとよく分からない。ということに陥る人向け）

## slack botアカウント作成手順
- [自分のslackページ](https://my.slack.com/apps)へアクセスする
- **マストアプリを追加**をクリック
 
<img width="５００" border="3" alt="slackbot1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/f9ccddfd-4108-ebe8-23cd-c4afc232816e.png">

- 検索窓から **Bots を検索しクリック**する 
<img width="５００" border="3" alt="slackbot2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/ee17d7af-4540-744a-2815-082ff772544c.png">

- Botsの画面で **slackに追加する** をクリック

<img width="５００" border="3" alt="slackbot3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/62214353-4491-89c4-1312-3eed557b6a7e.png">

- Bot のユーザー名を決めて **ボットインテグレーションを追加** をクリックする

<img width="５００" border="3" alt="slackbot4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/f613d04b-e131-3e96-d71b-6bb89715c65b.png">

- 設定画面に遷移するので、 **名前、アイコン、表示名、botの利用目的などを記入** して、 **インテグレーションを保存**をクリックする

<img width="５００" border="3" alt="slackbot5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/eb99d56d-dca5-0ca0-5097-008ecbdee101.png">

<img width="５００" border="3" alt="slackbot6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/4454bcf9-b9c5-1723-26e0-84e9004a2d17.png">

<img width="５００" border="3" alt="slackbot7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/49771a2a-9ac5-ba82-9087-b7a69ef75c33.png">

- botを追加したいslackチャンネルに移動して、右上の設定ボタンから、 **アプリを追加する** を選択
<br>
**※注意：botはユーザーではなくアプリという形でslackチャンネルに追加を行う必要がある**

<img width="５００" border="3" alt="slackbot8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/50e0795e-83b4-01e8-44c5-38f35f07efb3.png">

- 検索窓から作成した bot ユーザーを検索して、 **追加** をクリックして完了

<img width="５００" border="3" alt="slackbot9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242038/6eb90acd-9ca7-7f4d-463a-bd3b8654868c.png">
Go AWS Lambda の init, main, handler のライフサイクルについて

# はじめに
Go AWS lambdaでRDSに接続する処理を書く必要が出てきた際
connectionを作るのは `init()` で良いのですが果たして `close` をいつどこで行えば良いのか悩み、[公式のリファレンス](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-programming-model-handler-types.html
)を参照しましたが肝心のライフサイクルについて具体的な記載が見当たらないため、実際に各func内の実行回数のcountを取って調査しました。

# 検証

`init()` `main()` `Handler()` それぞれの実行countをグローバルに保持しその値を出力する検証コードを作成して実行してみます。

```Go
package main

import (
	"fmt"
	"github.com/aws/aws-lambda-go/lambda"
	"log"
)

var invokeCount = 0
var initCount = 0
var mainCount = 0

func init() {
	log.Print("init start")
	initCount++
	log.Print("init end")
}

func Handler() (string, error) {
	log.Print("handler start")
	invokeCount++
	log.Printf("invoke=%d, main=%d, init=%d", invokeCount, mainCount, initCount)
	log.Print("handler end")
	return fmt.Sprintf("invoke=%d, main=%d, init=%d", invokeCount, mainCount, initCount), nil
}

func main() {
	log.Print("main start")
	mainCount++
	lambda.Start(Handler)
	log.Print("main end")
}
```

実行結果は以下の通り

```
[1st request]
2020/01/15 06:34:47 init start
2020/01/15 06:34:47 init end
2020/01/15 06:34:47 main start
2020/01/15 06:34:47 handler start
2020/01/15 06:34:47 invoke=1, main=1, init=1
2020/01/15 06:34:47 handler end

[2nd request]
2020/01/15 06:43:17 handler start
2020/01/15 06:43:17 invoke=2, main=1, init=1
2020/01/15 06:43:17 handler end

[3rd request]
2020/01/15 06:43:19 handler start
2020/01/15 06:43:19 invoke=3, main=1, init=1
2020/01/15 06:43:19 handler end
```

初回起動で`lambda.Start(Handler)`を呼んだ際に`Handler()`をreceiverとしてserverに登録して、2回目以降は`Handler()`を呼ぶようになっているようです。

次にしばらく時間を置いて `"main end"` が呼ばれるかどうかを確認しましたがどうも出力されていないよう。
こちらはcontainer自体がkillされたのか、呼ばれているがログが出ていないのか判断がつかず。何かいい方法がないものか。

# 結論

* `init()`及び`main()`が呼ばれるのは初回requestのみ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536869/4a4b4a52-113d-b485-bc0a-bfb49a17ec18.png)

* 2回目以降は`Handler()`が直接実行される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536869/5406841c-ca94-8743-6ce1-bf0edc7eafa6.png)

* 一定時間経過後、container自体がkillされる？　`lambda.Start()` 以降の処理は実行されているかどうか不明。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536869/d068268a-e130-59b7-c77a-7f1ffdc700b0.png)

Database connection の close を呼ぶタイミングがなさそう・・・
呼ばれるかどうかは不明ですが、`main()`内で`lambda.Start()`を呼ぶ前にdeferで定義するのが良いんだろうか。
railsでレコードに保存している「連続した改行」を改行を保持したままviewに表示する

#環境
Rails 5.2.3
Ruby 2.6.3

#実現したいこと
例えばArticleモデルのcontentカラムに保存している、
「あいうえお

かきこけこ」
をviewに表示したい。

#解決策
safe_join関数を利用し、改行文字をbrに変換する。
simple_format 関数もあるのだが連続改行が認識されないため、
safe_join関数を利用する。


```haml:show.html.haml
= safe_join(@article.content.split("\n"), tag(:br))
```
#参照
https://api.rubyonrails.org/classes/ERB/Util.html#method-c-html_escape
CFEのfirmware update

BroadcomのmipsのSOCはCFEという独自のブートローダーが使われています。このブートローダーでfirmwareをupdateするときにはtftpが使えます。

tftpはサーバとクライアントの両方に対応しています。

クライアントの場合

```
CFE> flash -noheader 192.168.11.2:main.trx flash1.trx
```

サーバの場合

```
CFE> flash -noheader : flash1.trx
```

5秒くらいの間にputしないとerrorになりプロンプトに戻ります。

バラしてシリアル接続できる場合はいいのですが、バラさないでupdateする方法がないか長いこと考えていました。

電源投入後の起動時にこんなメッセージがでます。

```
Wait a few seconds for an image
Reading :: Failed.: Timeout occured
```

どうもなんかを待っているようです。

手持ちのcfeのソースをgrepしてみてこんなコードがありました。

```
    /* Wait a few seconds for an image */
    ret = ui_docommand("flash -noheader : flash1.trx");
```

タイミングよくtftpするとバラさずにupdateできるようです。

shスクリプトにしてみました。

```sh:cfeflash.sh
#!/bin/sh

IP=$1
FILE=$2

while [ 1 ]
do
ping -t 1 -c 1 ${IP} >/dev/null 2>&1
if [ $? == 0 ]; then
echo " Find target"
echo "bin
put ${FILE}
quit
" | tftp ${IP}
exit
fi
echo -n "."
done
```

ネットワークインターフェースにデフォルトのIPの同じサブネットのアドレスを設定かエイリアスしてスクリプトを実行してターゲットの電源を入れると焼けます。

```
% cfeflash.sh 192.168.11.1 main.trx
........ Find target
Sent 323584 bytes during 0.6 seconds in 633 blocks
```

CFEのnvramのlan_ipaddrに保存されてます。

猛牛さんのターゲットのデフォルトのIPは以下の記事に記載してあります。

https://qiita.com/yamori813/items/2b286c87564687bb777c

tftpコマンドだけでもできるようです。

BCM5301Xなターゲットの処理をRLoginのスクリプトで書いてみました。

```
function bcmflash() {
if(swait(100, "Fill test patnum:2") == 0) {
	key = ctos(0x03);
	sputs(key);
	sleep(1000);
	sputs(key);
	sleep(1000);
	sputs("flash -noheader 10.0.1.72:netbsd-WZR-1750DHP.trx nflash1.trx\r");
if(swait(100, "CFE>") == 0)
	sputs("reboot\r");
}
}
```
SATySFiでブラケット記法を表示するコマンドを作った

# はじめに
静的型付けによる一貫した構造的記述が可能な組版処理システム[SATySFi](https://github.com/gfngfn/SATySFi)。関数型プログラミングの知識が必要で少しハードルは高いですが、慣れると確かにTeXより使いやすいです。エラーの場所もわかりやすいし、コマンドのカスタマイズも簡単。  
今回は、物理（工）学者、量子コンピュータエンジニアが親の顔よりもよく見る[ブラケット記法](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%A9-%E3%82%B1%E3%83%83%E3%83%88%E8%A8%98%E6%B3%95)（${|\psi\rangle},\langle \phi|,\langle \phi|A|\psi\rangle$みたいなやつ） をSATySFiで表示するコマンドを紹介します。

# 動機
- [SATySFiの標準ライブラリ](https://github.com/gfngfn/SATySFi/blob/master/lib-satysfi/dist/packages/math.satyh) に入っていない
- 使う人はめちゃくちゃよく使う

# 方法
## コマンド定義
SATySFi文書のローカルのクラスファイル`local.satyh`（その文書でだけ共有されるクラスファイルはたいていこの名前）に以下を追記し、適用したいSATySFi文書にimportしてください。

```local.satyh
@require stdjareport
...
let-math \bra =
  math-paren (Math.angle-left 0.5pt) Math.abs-right


let-math \ket =
  math-paren Math.abs-left (Math.angle-right 0.5pt)


let-math \braket lst =
  math-paren-with-middle
    (Math.angle-left 0.5pt) (Math.angle-right 0.5pt)
    (Math.abs-right) lst
```

`let-math`は`数式コマンド`の定義宣言で、ここでは`\bra`、`\ket`、`\braket`コマンドを定義しています。
ブラケット記法内の`|`は本来`Math.bar-middle`を使う方が意図がわかりやすいんでしょうが、表示してみたところ字間が広く空いてしまったので`Math.abs-right`を使いました(本当は記号の流用は避けたい)。
ここで使った関数は[math.satyh](https://github.com/gfngfn/SATySFi/blob/master/lib-satysfi/dist/packages/math.satyh)で定義されているので参考にしていただければと思います。


## 使い方
例として以下のようにコマンドを使ってみました。

### SATySFiファイル
```braket-test.satyh
@import: local
@require: itemize

document(|
        title = {ブラケット記法のテスト};
        author = {Massn};
|) '<
         +listing{
         * ブラ：${\bra{\phi}}
         * ケット：${\ket{\psi}}
         * ブラケット：
                 **${\braket![${\psi};${\phi}]}
                 **${\braket![${\psi};${A};${\phi}]}
                 **${\braket![${\psi};${A};${B};${\phi}]}
         }
>
```

### 表示結果
```bash
$ satysfi braket-test.satyh
```
で`braket-test.pdf`を生成しました。タイトル以外の主要部分は以下のように表示されます。
![braket.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32066/08e5df4a-94b5-ffda-2bef-fd1c10d830b9.png)

$\langle\psi|A|B|\phi\rangle$という書き方に意味があるかわかりませんが、まあこういうのも書けますよということで入れておきました。

# 参考文献
* [The SATySFi​book](https://gfngfn.booth.pm/items/1046747)
* [SATySFi-Wiki](https://github.com/gfngfn/SATySFi/wiki/SATySFi-Wiki)
Typescriptチュートリアル

Typescriptを始めたいけどどこから初めて良いかわからないという方のために、TypescriptのWebサイトに掲載されていた同名のページを少しだけアレンジして紹介してみようと思います。

# 環境の準備
まず、node.jsがPCにインストールされている必要がある。インストールされていない場合、下記のページからLTSの最新版をダウンロードしてインストールします。
https://nodejs.org/en/



# Typescriptのインストール
下記のコマンドを実行して、typescriptのライブラリをダウンロードしてきます。会社の環境などで、プロキシサーバが間にある方は、プロキシありの方のコマンドを実行してください。

```sh:プロキシなし
npm install -g typescript
```


```sh:プロキシあり
npm install -g typescript
```



# 最初のTypescriptファイルの作成
まずテキストエディタを開いて、下記のJavascriptコードをgreeter.tsというファイルに保存してください。

```typescript:greeter.ts
function greeter(person) {
    return "Hello, " + person;
}

let user = "Jane User";

document.body.innerHTML = greeter(user);
```

# Typescriptコードのコンパイル
次にコマンドプロンプトを開き、さっき作ったgreeter.tsが保存されているパスに移動してから下記のコマンドを実行します。

```shell:コマンド
tsc greeter.ts
```

実行すると、同じコードが書かれたgreeter.jsというファイルが作成されます。

次に、Typescriptが提供している機能を使ってコードを改良します。

greeterの引数にstringの型定義を追加しましょう。

```typescript:
function greeter(person: string) {
    return "Hello, " + person;
}

let user = "Jane User";

document.body.innerHTML = greeter(user);
Type annotations
```

上記のコードによって、javascriptにはない型定義の機能を追加することができました。試しに、変数userに数値を入れて、greeterメソッドを呼んでみます。

```typescript:
function greeter(person: string) {
    return "Hello, " + person;
}

let user = 12345;

document.body.innerHTML = greeter(user);
```

結果はこの通り。エラーが出力されました。

```:
error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
```

この場合でもgreeter.jsは作成され、使うこともできますが期待通りに動作しない可能性があることをTypescriptが警告を出します。



## typeアノテーション

## インタフェース

今度は、firstNameとlastNameを持っているオブジェクトを表現するインターフェースを作成します。Typescriptでは、

```ts
interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person: Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "Jane", lastName: "User" };

document.body.innerHTML = greeter(user);
Classes
```


## クラス
```ts
class Student {
    fullName: string;
    constructor(public firstName: string, public middleInitial: string, public lastName: string) {
        this.fullName = firstName + " " + middleInitial + " " + lastName;
    }
}

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person : Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = new Student("Jane", "M.", "User");

document.body.innerHTML = greeter(user);
```


## TypescriptWebAppの起動

```html
<!DOCTYPE html>
<html>
    <head><title>TypeScript Greeter</title></head>
    <body>
        <script src="greeter.js"></script>
    </body>
</html>
```

# デバッグ
クロームで実行したJavascriptを実行し、デバッグをしてみましょう。
コードを止めると、javascriptのコードではなく、typescriptが表示されます。


# まとめ
これにより、typescriptの開発環境ができました。
typescriptはjavascriptと異なりコンパイルというプロセスが入るため、実行しなくても記述ミスや型の間違いを見つけてくれます。
記述量は多少増えますが、規模が大きくなればなるほど、開発を助けてくれるはずです。

# 参考
https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html
User-Agent がオワコンになりそう

Edge や Chrome のベースとなっている Chromium で、下記のような記事が投稿されました。

https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/-2JIRNMWJ7s/yHe4tQNLCgAJ

> Intent to Deprecate and Freeze: The User-Agent string
> User-Agent 文字列を廃止し凍結へ

> We want to freeze and unify (but not remove) the User Agent string in HTTP requests as well as in `navigator.userAgent`
> HTTP リクエストや `navigator.userAgent` に含まれる User Agent 文字列を凍結し統合する予定です（削除はしません）。

元々ブラウザの種類やバージョン、OSの種類などは User-Agent という文字列から取得し判定して、処理分岐を行っていました。

しかし、昨今の User Agent は謎文字列をずらずら並べるような状態になっていて使いづらいため、使わないよう持っていく方針になっているようです。

mobile かどうかの判定は例えば [User-Agent Client Hints](https://wicg.github.io/ua-client-hints/) API などが現在策定途中なので、そちら側で確認出来るように変えていこう、という話みたいです。

2020 年 6 月のM83 で文字列が凍結され、 2020 年 9 月の M85 では mobile/desktop 双方ほぼ同じような文字列に統一される見通しとなっています。

User-Agent や `window.navigator.userAgent` 文字列によって処理を分けている部分があるソフトウェアは、今後は別の API によって処理を分けるよう修正する必要がありそうです。そちらは M81 から deprecated 表記がなされるようです。

Safari は以前 User-Agent の凍結に[挑戦した](https://twitter.com/rmondello/status/943545865204989953)ことがあるみたいですが、色々あって失敗に終わったようです。

### User-Agent Client Hints API(client side)

```js
const ua = await navigator.getUserAgent()
{
  "brand": "Edge",
  "version": "72",
  "platform": "Windows NT 10.0",
  "architecture": "x86_64",
  "model": "Pixel 2 XL",
  "mobile": false
}
```

User-Agent Client Hints API では上記のようなスタイルでクライアント機種情報が取得出来るようになる予定です(草案のため変更される可能性もあります)。また、似たような HTTP リクエストヘッダも提供されるみたいなので、サーバサイドではそちらを用いて判定出来そうです。

### TL; DR

- 短期的に User-Agent がなくなるわけではない
- 多分 Chrome 85 でも 90 でも Android 版でも、 PC Chrome 85 と判別できるような文字列が返ってくるようになる
- 長期的には削除されるかも？
createJSのテキストとスペースについて

#概要
createJSのテキストにて

```js

var textObj = _lib.text;
var sentence = "テスト用の文章";
textObj.text = sentence;
```
上記のように、テキストを表示させる場合に、
半角スペース、全角スペースを入れると
文章の途中で勝手に改行されてしまうことがある
上記の解決方法について記載する

#原因
##drawTextの仕様
テキストへの文字表示はcreatejs内のdrawText関数で行われている
<details><summary>drawText (createJS)</summary><div>

```js

p._drawText = function(ctx, o, lines) {
        var paint = !!ctx;
        if (!paint) {
            ctx = Text._workingContext;
            ctx.save();
            this._prepContext(ctx);
        }
        var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
         
        var maxW = 0, count = 0;
        var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/);
        for (var i=0, l=hardLines.length; i<l; i++) {
            var str = hardLines[i];
            var w = null;
             
            if (this.lineWidth != null && (w = ctx.measureText(str).width) > this.lineWidth) {
                // text wrapping:
                var words = str.split(/(\s)/);
                str = words[0];
                w = ctx.measureText(str).width;
                 
                for (var j=1, jl=words.length; j<jl; j+=2) {
                    // Line needs to wrap:
                    var wordW = ctx.measureText(words[j] + words[j+1]).width;
                    if (w + wordW > this.lineWidth) {
                        if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
                        if (lines) { lines.push(str); }
                        if (w > maxW) { maxW = w; }
                        str = words[j+1];
                        w = ctx.measureText(str).width;
                        count++;
                    } else {
                        str += words[j] + words[j+1];
                        w += wordW;
                    }
                }
            }
             
            if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
            if (lines) { lines.push(str); }
            if (o && w == null) { w = ctx.measureText(str).width; }
            if (w > maxW) { maxW = w; }
            count++;
        }
         
        if (o) {
            o.width = maxW;
            o.height = count*lineHeight;
        }
        if (!paint) { ctx.restore(); }
        return o;
    };
```

</div></details>

上記コードの中の
テキストオブジェクトの幅(lineWidth)と文字の長さを比較して

```js

w = ctx.measureText(str).width; //今描画している文字の長さ
var wordW = ctx.measureText(words[j] + words[j+1]).width;　//次に描画する単語と次の次に描画する単語を合わせた長さ
w + wordW > this.lineWidth　//2つ先の単語まで描画したときにテキストオブジェクトの幅を超えてしまうかどうか
```
いまから描画しようとしている単語の次の単語を描画した場合に、テキストオブジェクトの横幅を超えてしまうようであれば今回の描画後に改行をする仕様になっている

```js

if (w + wordW > this.lineWidth) {
    this._drawTextLine(ctx, str, count*lineHeight); //改行は、テキストを描画する高さを変更する形で実装されている？
    count++; //超えてしまったら、改行する
}
```
##単語の定義
createJSでは単語を区切るのにスペースが使用されている

```js

var words = str.split(/(\s)/);
str = words[i];
```

#対策
対策としては主に2つある
・表示する文章データの中にはスペース(\s)は入れないというルールを決める
・テキストオブジェクトの横幅を長めに設定しておく
基本的には、文章中にスペースは使いたくなることが多いと思うので、
テキストオブジェクトの横幅を大きめにとっておく対策がいいかと思われる。
TypeScriptでobjectの値の参照渡しでコケた時の回避方法

# 何をしようとしたか
特定の項目が同じ値のままUserを使いまわそうとした。
今回の例だと `User.age`

```typescript:entities/User.ts
export class User {
  name: string;
  age: number;
}
```

```typescript/main.ts
import { User } from "./entities/User";

export const main = () => {
  let users: User[] = [];

  let user: User = { name: "alpha", age: 20 };
  users.push(user);

  // nameのみ変更
  user.name = "beta";
  users.push(user);

  console.log({ users });
};

main();
```

###求めていた結果

```shell
{ users: [ { name: 'alpha', age: 20 }, { name: 'beta', age: 20 } ] }
```

###実際に出てきた結果

```shell
{ users: [ { name: 'beta', age: 20 }, { name: 'beta', age: 20 } ] }
```

betaくんが２人になってる…

# 回避手段

調べたらjavascriptはobjectの場合、値の参照渡しを行うらしい

なら毎回新しいobjectを生成すればいいのかと思った

丁度nestjsを使用していたので、`class-transformer/plainToClass`を使用することにした

```shell
npm i class-transformer
```

```typescript:main.ts
import { plainToClass } from "class-transformer";
import { User } from "./entities/User";

export const main = () => {
  let users: User[] = [];
  let user: User = { name: "alpha", age: 20 };
  users.push(plainToClass(User, user));
  user.name = "beta";
  users.push(plainToClass(User, user));

  console.log({ users });
};

main();
```

実行結果

```shell
{  users: [ User { name: 'alpha', age: 20 }, User { name: 'beta', age: 20 } ]}
```

求めている形で返ってきた

# 感想

objectの取り扱いには気をつけよう
~~そもそも使いまわしたい部分に対して処理をちゃんと書けばこうならなかったはず~~
~~とりあえず動かしたい精神で書いてたら死んだ~~

# 参考

[[Qiita]JavaScriptに参照渡し/値渡しなど存在しない](https://qiita.com/yuta0801/items/f8690a6e129c594de5fb)
【PHP】コマンドでPHPファイルを任意のPHPバージョンを指定して実行する。

開発環境でxamppを複数バージョン使っているため、PATHを通したPHP以外で実行したいことが多々あるため、メモとして残します。


#■やり方

```shell

C:/xampp5.6.32/php/php.exe -f "C:projects/testproject/test.php"
```
はじめにPHP実行ファイルのフルパス。
`-f`オプションでファイル実行になります。
最後に実行するPHPファイルのフルパスを指定。
amplify add codegenでエラー

### 参考

https://github.com/aws-amplify/amplify-js/issues/2166

amplifyで同じアプリで一度amplify deleteしてから、amplify add codegenをしたら、下記のエラーが発生。

```shell
Codegen support only one GraphQL API per project
```

### 対応

.graphqlconfig.ymlを削除して、再度実行。
「This domain is not authorized to run this operation.」のデバッグ方法【Firebase】

はじめまして、ますみです！

[![自己紹介.png](https://www.umi-mori.jp/wp-content/uploads/2023/02/introduction_banner_2-2048x533.png)](https://bit.ly/banner_intro_masumi_creator_qiita)


# エラー内容
FirebaseのDatabase / Functions / Hostingを用いて、サービスをローンチしました。
しかし、deployした先のドメインでは、ログインの際に[CORS](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)のような認証エラーが起きました。エラー文は以下の通りです。

```
"This domain (sample.app) is not authorized to run this operation.
Add it to the OAuth redirect domains list in the Firebase console -> Auth section -> Sign in method tab."
```

# 対処法
リサーチした所、どうやらFirebaseのコンソールからデータベスへのドメイン認証の設定を変更する必要がありました。

(1) まずFirebaseのコンソールから、`Authentification` > `ログイン方法`へ行き、下へスクロールします。

<img width="1200" alt="スクリーンショット 2020-01-15 14.53.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240280/22991320-5c63-df7c-2720-33e5baea50ce.png">

(2) 最後に、承認済みドメインの`ドメインを追加`のボタンからドメインを追加し、権限を付与します。

<img width="1200" alt="スクリーンショット 2020-01-15 14.53.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240280/59a73bbb-ed12-3c8a-f447-f790936bd5eb.png">


# 最後に
最後まで読んでくださり、ありがとうございました！
いかがだったでしょうか？

この記事を通して、少しでもあなたの学びに役立てば幸いです！

:::note info
【仕事の相談はこちら】
お仕事の相談のある方は、下記のフォームよりお気軽にご相談ください。

[問い合わせフォームはこちら](https://forms.gle/G5g1SJ7BBZw7oXYA7)

もしもメールでの問い合わせの方がよろしければ、下記のメールアドレスへご連絡ください。

info*galirage.com（*を@に変えてご送付ください）

🎁 「生成AI活用の無料相談券」もしくは「生成AIの社内ガイドライン（無料PDF）」を『公式LINE』で配布中 🎁
『生成AIを業務に活用したいけど、どうしたらいいかわからない』といった声を多くいただきます。

Galirageでは公式LINEにて、チャットやオンライン会議で「完全個別の生成AI活用無料相談会」を実施しております！
（期間限定で実施しているため、ご興味ある方はお早めに以下のLINE公式アカウントをご登録ください^^）
https://lin.ee/rvz6lMN

※ 予告なく、キャンペーンを終了する可能性がございますが、ご了承ください。

【業務内容】
具体的には、以下のお仕事を中心に受け付けております！（[詳しくはこちら](https://galirage.notion.site/Galirage-AI-c2f3945fafb0433d8d2fd03e56927fd8)）
1. 受託開発（例：生成AIを使った社内システムの開発）
2. コンサルティング（例：技術戦略のアドバイス）
3. 講演（例：社内研修、イベント登壇）

※ 特に「生成AIを使ったシステム開発のご依頼」が多く、ご好評いただいております。

【これまでの相談事例】
以下のようなご相談が多くあります。

🔑 機密情報を漏洩させないための、生成AIのシステム構築をお願いしたい。
🤖 自社データを用いたFAQチャットボットの作り方を知りたい。
💡 ChatGPTを、自分たちの事業にどのように活かせるか、アドバイスやアイデアが欲しい。
:::
# おまけ
エンジニアの仲間（データサイエンティストも含む）を増やしたいため、公式LINEを始めました🎉

「**一緒に仕事をしてくれる方**」「**友だちとして仲良くしてくれる方**」は、友だち追加をしていただけますと嬉しいです！（仲良くなった人たちを集めて、「ボードゲーム会」や「ハッカソン」や「もくもく会」もやりたいなと考えています😆）

とはいえ、みなさんにもメリットがないと申し訳ないので、特典を用意しました！

友だち追加後に、アンケートに回答してくれた方へ「**エンジニア図鑑（職種20選）**」のPDFをお送りします◎

https://lin.ee/hFUN4TL

[![](https://www.umi-mori.jp/wp-content/uploads/2023/10/line_banner_v2.png)](https://lin.ee/hFUN4TL)
OSMC を iPhone から操作する

こちらの記事の続きです。

* [Raspberry Pi 3 Model B+ に OSMC をインストール](https://qiita.com/ancolin/items/ce3ac4e639383339ab89)

Raspi にセットアップした OSMC を iPhone から操作できるようにしました。
びっくりするほど簡単でした……。

## 手順
### 1. OSMC の設定を変更し、アプリから操作可能にする
OSMC で以下の通り設定を変更します。

1. Settings を選択する
2. Services を選択する
3. Control を選択し、右キーで右側のリストに移る
4. Allow remote control via HTTP を有効化する  
ひとまず Username と Password には osmc を設定しました。  
セキュリティを考えるなら全くダメです……。
5. Allow remote control from applications on other systems を有効化する

### 2. iPhone に Official Kodi Remote をインストールする
アプリ Official Kodi Remote で操作できるようなのでインストールします。

### 3. Official Kodi Remote をセットアップする
OSMC をインストールした Raspi と同じ LAN に接続している必要があります。

1. 左下の Add Host をタップする
2. Host : port に Raspi の IP アドレス、手順 1 の Allow remote control via HTTP で設定した Port を設定する  
Port は、デフォルトのままなら 80 です。
3. Username と Password を手順 1 の Allow remote control via HTTP で設定したものを設定する
4. Save をタップする

これで以降の操作はすべて iPhone 上から可能です。
以上です。
VBA 「プロジェクトがロックされています」パスワード入力ダイヤログが表示されないなら、ファイルが共有されているだけかも

# VBAでプロジェクトがロックされていたら、それは単にファイルが共有されているだけかも

- ISSUE
	- VBAのプロジェクトが表示されない
		- VBAのプロジェクト一覧から特定のプロジェクトをクリックすると、本来ならツリーが展開されるべきところ、以下のダイヤログが表示される
			- タイトル：プロジェクトがロックされています。
			- 本文：プロジェクトを表示できません。
- SOLUTION
	- エクセルなどのアプリへ戻って、共有を解除する
- BENEFIT
	- VBAのプロジェクトが表示される

--------------------

なにを当たり前なことを言っているのだこいつは、と思われる諸兄もおられるとは存じますが、私はこれで30分無駄にしたので、同じような方のためにメモしておきます。

なお、同じような現象でも、プロジェクトを開こうとしたときにパスワードを要求されたら、それはプロジェクトがロックされているということだと思うので、上記の方法では解除できないと思われます。
Azure ARM Templateをテンプレートに登録して、Azure Portalからデプロイする

ARM Templateをチーム内で共有して、GUI上で誰でも利用できる手段を探していたところ、テンプレート(プレビュー)が使えそうだったので利用方法を残します。

# ARM Templateを準備する

* ARM Templateはなんでもよいですが、今回は適切なNSG設定がなされたVMが作成できることを目的としました。サンプルコードは以下です。  
[VM Create ARM Template Sample](https://github.com/shingo-kawahara/azure-template/tree/master/vm-create)

# テンプレートを作成する

* Azureポータル左上のハンバーガーメニューを選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/82752d8e-4275-418d-692f-caa4e7dbab7e.png)

* [すべてのサービス]を選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/a11af3de-9312-4871-b7c2-d7941686b0ec.png)

* 検索バーに[テンプレート]と入力します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/ab2eef5d-b741-b187-8918-7d6509310b85.png)

* [テンプレート]を選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/b40c497c-17cd-34e4-bfa3-2c1ebc7a82f8.png)

* [追加]を選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/21c58ae1-7e76-4714-dd40-32f05522b691.png)

* まずは全般設定で、[名前]と[説明]を入力します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/ac900074-21c5-a120-5333-e6689537024a.png)

* 次にARM Templateを記載します。ここでは事前に準備したARM Templateを貼り付けます。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/36721e9d-aa8c-7314-1bd7-c140752059ed.png)

* しばらくするとテンプレートが作成されます。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/f8c9de36-2ba4-e173-3695-65397a1c71ff.png)

* 作成されたテンプレートを選択して、[展開]を選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/ab5da9c2-b126-7231-db79-37158e2bb481.png)

* あとはARM Templateの定義に従って値を入力してデプロイします。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/8e4a7e7e-3890-fdf7-7003-a585c33d362c.png)

# まとめ
ARM TemplateのjsonをAzure CLIなどでデプロイするのは簡単にできますが、Azure PortalというGUIを使ってだれでもデプロイできる手段として使えるのはよいですね。
テンプレートに登録されたARM Templateのjsonの履歴管理ができないので、Git管理しているjsonファイルとの整合性を保つ必要があります。
履歴管理はGitでやって、CIツールからAzure CLIでテンプレートを最新化する。ということができれば最高ですが、現時点ではできなそうです。
このあたりは今後に期待ですね。

以上
AWS EC2インスタンスの複製をしてみた。その２（備忘録）

前回の続きです。https://qiita.com/Bikeiken-IT/items/b0762a293e231898066f

![step5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550376/d2b7b81d-4d25-404f-b045-014c0530aef9.png)

続いてストレージに関する設定です。EC2はサーバーとして機能しますが、ストレージ機能も有しています。
そのため、サーバーの処理の結果などを保存するためのストレージの設定が出来ます。
このストレージの情報をsnapshotとして保存し、新しく作るEC2インスタンスに反映させることが、今回の目的です。

現時点では何もせずに、デフォルトの状態でEC2インスタンスのストレージを設定します。
![step6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550376/c0cebfe3-edcb-187e-b94b-0c6054ad0c6f.png)
タグ付けを行います。今回はタグのキーと値をEC2-test-1とします。
![step7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550376/5ea05861-66ad-7056-e786-53cf3e0febd1.png)
続いて、セキュリティグループの設定です。

ルールの追加というボタンを押し、デフォルトで設定されているSSHのほかに、HTTPとHTTPSを追加します。

ホストとクライアント間では、お互いを識別し合いデータを交換します。その住所としてIPアドレスがあります。
データを受け取るクライアントにはポートと呼ばれる窓口があります。
あるIPアドレスのデータは○○番のポートを通るようにと設定することで、通信の安全性が保たれます。
また、データの通信方法にもルールがあります。SSHやHTTP、HTTPSなどがあります。
このように、データの通信方法やポートの設定、どのIPアドレスとのアクセスを許可するのかなどのセキュリティの設定を行います（ファイアウォールの設定）。

今回はSSH、HTTP、HTTPSという通信方式を選択し、それぞれの通信方式に対応するポートが選択され（自動に選択されます）。IPアドレスについては0.0.0.0/0に指定されています。これはすべてのIPアドレスにアクセス許可をみとめるものです（フルオープン）。

![step8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550376/f869fde6-f4fb-5a30-d148-d338d0aaf38a.png)

これまでのEC2インスタンスの設定の確認画面です。問題が無ければ起動ボタンを押します。

![step9-keypair.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550376/135e2437-e5d9-f26c-b398-0edf3183a660.png)

キーペアの作成または選択を求められます。

AWSを利用する際に、自身のPCを使いEC２インスタンスを操作することになります。
これは事実上リモートアクセスとなるため、AWS側と自身のPCとのデータのやり取りは暗号化することが求められます。
暗号化されたデータにカギがかけられているイメージです。この鍵をあらかじめpemというファイル形式で手元に置いておき、自身のPCからEC2インスタンスにアクセスする際に鍵を提示して、はじめて操作が可能となります。
![step10-keypair.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550376/84a37b01-1ef0-38c3-ba51-a97f4ddb6ef6.png)

今回は、既に用意してあったtest123.pemというキーペアを利用します。
インスタンスの作成ボタンを押します。
![step-created.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550376/fd3a0998-a585-887a-d50f-d56e95817bdb.png)
EC2インスタンスが作成されました。分かりやすいようにEC2-test-1という名前を付けました。

次回以降、このEC2インスタンスの設定をTeraTermを用いてローカル環境からEC2にアクセスします。

その３に続きます。https://qiita.com/Bikeiken-IT/items/d8b1f731334c4f179447