OmniAICreator/Qiita-1.07M huggingface dataset:
pythonでC/C++から文字列配列を持ってくる

以下見てもわからなかったので
https://qiita.com/everylittle/items/6e18ba23f38502c18f3e


## C/C++だとこんな感じのをpythonから見れるようにする
```cpp
char** list = 0;
int32_t column_num = get_list(&list, false);
print_list(list, column_num);
if (list) free(list);

void print_list(char** list, int column_num) {
    FILE* fp = fopen("D:\\test.txt", "w");
    if (!fp) goto error;
    for (int i = 0; list[i]; i++) {
        fprintf(fp, "%s", list[i]);
        if (i % column_num == (column_num - 1)) {
            fprintf(fp, "\n");
        }
        else {
            fprintf(fp, "\t");
        }
    }
error:
    if (fp) fclose(fp);
}
```

## pythonの場合
```python
pp = c_char_p()
# argtypeやrestypeの指定は必要無かった
column_num = g.dll.get_list(byref(pp), c_bool(False))
out_data = cast(pp, POINTER(c_char_p))

count = 0
flag = True
while(flag):
    for i in range(column_num):
        p = cast(out_data[count], c_char_p)
        if p.value is None:
            flag = False
            break
        # バイト配列を一回変数に落とさないと自動変換が走ってしまう
        b = p.value
        print(b.decode(), end='\t')
        count = count+1
    print("")

# pythonから解放は無理っぽい
if pp.value is not None:
    g.dll.allocate_free(pp)
```

pythonの方が冗長だけどプロの人が書いたらもっとすっきりするんだろう。たぶん
Ruby-FFIでポインタのポインタを作る

勘違いしていたので反省を込めて短い記事。

Ruby-FFIで関数に渡すポインタのポインタを作るときは、

```ruby
ptr_ptr = FFI::MemoryPointer.new(:pointer)
```

の１行だけでOK。このとき、ptr_ptr は「ヌルポインタ」へのポインターである。

```ruby
ptr = FFI::MemoryPointer.new(:pointer)
ptr_ptr = FFI::MemoryPointer.new(ptr)
```

とすると、ヌルポインタのポインタのポインタになってしまうので間違い。
Azure DevOps Serverにおけるlog4j脆弱性の影響について

# 概要

Azure DevOps Serverにおけるlog4j脆弱性の影響について記載します。

公式ブログによれば、次の通りです。

https://devblogs.microsoft.com/devops/azure-devops-and-azure-devops-server-and-the-log4j-vulnerability/

- Azure DevOps（クラウドサービス）は影響を受けない
- Azure DevOps Server（オンプレミス版）では次の場合に限り対策が必要
    - バージョンが、2018、2019、2020
    - 検索機能を構成している（ブラウザからの検索機能）
        - 検索機能を構成した際に利用するElasticSearchが脆弱性の影響を受ける
- 手動での対策を行う。手動での対策を簡素化するためのパッチの生成を検討している
    - 追加情報は、[公式ブログ](https://devblogs.microsoft.com/devops/azure-devops-and-azure-devops-server-and-the-log4j-vulnerability/)に記載される

# 対策

- Java VMを最新にアップグレード
- JndiLookupをjarファイルから削除する

## 詳細

[公式ブログ](https://devblogs.microsoft.com/devops/azure-devops-and-azure-devops-server-and-the-log4j-vulnerability/)よりリンクされた次のサイトを参考に実施する。

https://jessehouwing.net/azure-devops-patch-for-log4j-vulnerability/

### 補足1

- Azure DevOps Server 2020で別サーバーに構成した場合は、手順で移動するElastic Searchのインストール先は別の場所 `C:\ES\elasticsearchv6.2\lib` でした。

```bat
cd "C:\ES\elasticsearchv6.2\lib"
..\bin\elasticsearch-service stop
"c:\Program Files\7-Zip\7z.exe" d log4j-core-2.9.1.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
..\bin\elasticsearch-service start
```

### 補足2

- Elastic Searchを停止後、7zによるJndiLookupをjarファイルから削除を実施してもエラーが表示されることがありました。3回試行後、7zが作成していたtmpファイル3個を一度削除し、再度「7zによるJndiLookupのjarファイルからの削除」を実施すると正常に終了しました。

```bat
Updating archive log4j-core-2.9.1.jar

Error:
7-Zip cannot delete the file
log4j-core-2.9.1.jar
プロセスはファイルにアクセスできません。別のプロセスが使用中です。
```

## 対策を実施した環境

- Azure DevOps Server 2020.0.1 + Patch 1

# 参考

- https://devblogs.microsoft.com/devops/azure-devops-and-azure-devops-server-and-the-log4j-vulnerability/
- https://jessehouwing.net/azure-devops-patch-for-log4j-vulnerability/
複数のGitリポジトリをまとめてPullするBat

batファイルが置かれているフォルダは以下のすべての
Gitリポジトリに対して、stash　→　pull　→　stash pop　を行う。

```pull.bat
@echo off

rem batファイルが置いてある場所のすべてのフォルダパスを取得
for /d %%d in (%~dp0*) do (
  echo "start------------>"%%d
  cd %%d

  echo "stash------------>"
  git stash
  echo.

  echo "pull------------>"
  git pull
  echo.

  echo "pop------------>"
  git stash pop
  echo.

  echo "------------>end"%%d
)

pause
```

すべてのローカルブランチに対して行う。
（サブモジュールにも対応）
```pull.bat
@echo off
setlocal enabledelayedexpansion

call:log "Start!"

REM フォルダ内のすべてのフォルダ名を取得
for /d %%i in (%~dp0*) do (
  REM 取得したフォルダ名のフォルダへ移動
  call:log "Processing folder: %%i"
  cd %%i
  cd

  REM 移動したフォルダがGitリポジトリの場合、Pullする
  if exist ".git" (
    call:gitPull
    for /f "delims=" %%s in ('git submodule status --recursive') do (
    REM サブモジュールのフォルダ名を取得
      for /f "tokens=2,3" %%c in ("%%s") do (
        call:log "Processing submodule: %%c"
        REM サブモジュールのフォルダへ移動
        cd %%c
        cd
        if exist ".git" (
          call:gitPull
        ) else (
          call:log "Not a Git repository. Skipping."
        )
      )
    )
  ) else (
    call:log "Not a Git repository. Skipping."
  )
)
call:log "End."

endlocal
pause

:gitPull
  REM 今ある変更をStashへ移動
  call:log "git stash"
  git stash

  REM 在のブランチ名を変数へ保存
  for /f "delims=" %%b in ('git branch --show-current') do set current_branch=%%b
  call:log "Current branch: !current_branch!"

  REM リポジトリ内のすべてのローカルブランチを取得
  for /f "delims=" %%a in ('git branch --format="%%(refname)"') do (
  
    REM 取得したローカルブランチへチェックアウト
    call:log "git checkout: %%~na"
    git checkout "%%~na"

    REM チェックアウトしたブランチに対してPull
    call:log "git pull"
    git pull
  )

  REM 変数へ保持したブランチへチェックアウト
  call:log "git checkout: !current_branch!"
  git checkout "!current_branch!"

  REM Stashした変更を適応
  call:log "stash pop"
  git stash pop
exit /b

:log
echo.
echo ------LOG------ %~1
echo.
exit /b

```
Chromebook で Spring Boot を使う

実行時の様子
![spring_dec22_aa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/fd6ee5f2-2c63-f623-c782-8308c1923f5f.png)

インストール

```bash
sudo apt install zip
sudo apt install default-jdk
#
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install springboot
#
```

バージョンの確認

```text
$ spring version
Spring CLI v2.6.2
```

サーバープログラム

```groovy:app.groovy 
@RestController
class ThisWillActuallyRun {

    @RequestMapping("/")
    String home() {
        def str_out = "Good  Morning!<br />\n" + "Hello World!<br />\n" \
        + "Hello World!(Groovy version : ${GroovySystem.version})<br />\n"
        return str_out
    }

}
```

サーバーの起動

```bash
spring run app.groovy
```

クライアントで
http://localhost:8080/
にアクセス

```text
$ http localhost:8080
HTTP/1.1 200 
Connection: keep-alive
Content-Length: 83
Content-Type: text/plain;charset=UTF-8
Date: Wed, 22 Dec 2021 03:44:11 GMT
Keep-Alive: timeout=60

Good  Morning!<br />
Hello World!<br />
Hello World!(Groovy version : 3.0.9)<br />
```
ビッグデータにおけるPostGIS ST_withinクエリの性能評価

#概要
ビッグデータのテーブルでPostGIS関数を使用すると時間がかかりすぎることがあります。この投稿ではST_withinクエリの最適な速度性能を評価します。


#1. PostGIS概要 : InputテーブルとGeometryカラム

```
アプリGPS対策テーブル、日本全国 : gps_log (1113万件)
市区町村テーブル : city_master (1909件)
```

Geometry系のカラムは主にEWKT形式で書いています

アプリGPSデータ : ポイント系 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168196/df4d2ae8-0e7a-c368-66fe-8ab956d08733.png)


市区町村テーブル : ポリゴン系
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168196/215c60d0-fadb-1172-682f-deadbe3ac17c.png)

→ 東京都 渋谷区のGPSデータを `ST_Within` で取得する

#2. PostGIS SELECT Queries評価

日本全国アプリGPSデータから渋谷区のデータをカウントするQueryをいろいろやってみる


```
INPUT DATA :
- アプリGPSデータ: gps_log (1113万件)
- 対象エリア 東京都 渋谷区 : polygon_shibuya
```


##　2.1. 緯度経度カラムをgeometryを作る
```sql:St_within_Select_1
SELECT COUNT(*) FROM gps_log
WHERE ST_WITHIN (ST_POINT(longitude,latitude)::geography::geometry,(SELECT geom FROM polygon_shibuya));
```
→結果 : 1分ぐらいで `count=167918` が表示された

##　2.2. GPS Pointのgeoｍetryカラムを使う
```sql:St_within_Select_2
SELECT COUNT(*) FROM gps_log
WHERE ST_WITHIN (geom,(SELECT geom from polygon_shibuya));
```
→結果 : 40秒 でできた！ 前より速い！

アプリGPSデータのテーブルのgeomカラムにSpatial indexを追加したら、もっと速いかもしれませんのでやってみます

参考 : https://www.postgresql.jp/document/8.3/html/textsearch-indexes.html

```sql:add_index
CREATE INDEX gps_log_spatial_index ON gps_log USING GIST (geom);
```

そしてまた上記のQueryとして

→結果 : 42秒でできました...変わらないですね...

##　2.3. GPS Pointのgeometryカラムと市区町村のEWKT形式をテキストで記入

手動でDatabaseに渋谷区のポリゴンをEWKT形式でコピーして、以下みたいにPasteする。
`ST_ASEWKT(geom)`でgeometryをEWKT形式でも取得できます。

```sql:St_within_Select_3
SELECT COUNT(*) FROM gps_log
WHERE ST_WITHIN(geom,'SRID=4326;MULTIPOLYGON(((139.716558418 35.673433081,139.716560078  {...他のたくさん座標...} 35.675078081,139.715462309 35.673890306,139.716558418 35.673433081)))')
```
→ 7秒 でめっちゃ速くてできました！！！

#3. ST_WITHIN の SELECT Queries 評価まとめ

アプリGPS dataのgeomカラムにIndex付き/なしでもやってみて、処理時間を以下の表でまとめました。

- Polygon カラムをgeometryではなくEWKT形式でテキストで記入したらPerformanceが結構上がる
- 推定と違くて、gps tableのgeomカラムにIndexが付けても、なくても変わらなそう
- 今回は1市区町村だけにテストしましたが、今度全市区町村でどうやってReverse Geocodingやるべきかを調べます。

|Method<br>`SELECT COUNT(*) FROM gps_log WHERE...`|Indexなし|Index付き|
|:--|:--|:--|
|2.1. アプリGPSデータの緯度経度カラムベース <br>`ST_WITHIN(ST_POINT(gps.longitude,gps.latitude) ::geography::geometry,shibuya.geom)` |61秒|58秒|
|2.2. アプリGPSデータのGeomカラムベース <br>`ST_WITHIN(gps.geom,shibuya.geom)` |40秒|42秒|
|2.3. GeomカラムをEWKT形式でテキストベース <br>`ST_WITHIN(gps.geom,'SRID=4326;MULTIPOLYGON(((139.716558418 35.673433081,139.716560078  {...他のたくさん座標...} 35.675078081,139.715462309 35.673890306,139.716558418 35.673433081)))')` |7秒|6秒|

 :dog2: 以上
HDFS上に配置したcsv/tsvファイルのTimestampカラムを読み込む方法

# 前提
クエリエンジンとしてHiveを使用しています。
Timestamp型文字列のカラムを含んだcsv/tsvファイルをHDFS上に配置しテーブルに読み込んだのち、そのカラムを別テーブルに挿入する処理が必要になった時の話です。
csv/tsvファイルのTimestamp型文字列をそのままHiveのTimestamp型として読み込めるかと思ったのですが、少し工夫が必要でした。その具体的な工夫について記載します。

# 結論
1. Hive 1.2.0 以降を使用しているなら、https://issues.apache.org/jira/browse/HIVE-9298 に記載がある通りTimestampのフォーマットをテーブルごとに設定します。
2. それ以前の場合、Timestamp型文字列のカラムを含んだcsv/tsvファイルを外部テーブルとして読み込むにはunixtimeに変換して出力すると良いです。そして別テーブルに挿入する際にHiveのクエリでTimestamp型に変換してやります。

# 方法
## 1. Hive 1.2.0 以降を使用している場合

テーブルが作成されている場合、以下のようにcsv/tsvファイルを読み込むテーブルのserdepropertyとしてTimestampの形式を設定してやります。

```hql
ALTER TABLE hogehoge_table SET SERDEPROPERTIES ("timestamp.formats"="yyyy-MM-dd HH:mm:ss");
```

※ただし `"yyyy-MM-dd HH:mm:ss"` の部分はcsv/tsvファイルに出力したTimestampの形式に応じて設定する

これからテーブルを作成する場合、DDLで設定するのが良さそうです。

```hql
CREATE EXTERNAL TABLE hogehoge_table (
  column_1         STRING,
  column_2         INT,
  target_column    TIMESTAMP
)
PARTITIONED BY (
  partition_column STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'
WITH SERDEPROPERTIES (
  "timestamp.formats"="yyyy-MM-dd HH:mm:ss"
)
LOCATION '<your hdfs file location>'
;
```

いずれにしてもtarget_columnはHiveのTimestamp形式としてSELECTできます。

## 2. それ以前の場合
csv/tsvファイルを読み込むテーブルの作成時、Timestamp型文字列ではなくunixtime形式で設定しておきます。

```hql
CREATE EXTERNAL TABLE hogehoge_table (
  column_1         STRING,
  column_2         INT,
  target_column    BIGINT
)
PARTITIONED BY (
  partition_column STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'
LOCATION '<your hdfs file location>'
;
```

別テーブルへの挿入時のSELECT文で変換してやります。

```hql
INSERT OVERWRITE TABLE fugafuga_table
PARTITION (partition_column = 'aaa')
SELECT
  column_1
  ,column_2
  ,CAST(FROM_UNIXTIME(target_column, 'yyyy-MM-dd HH:mm:ss') AS TIMESTAMP) AS target_column
FROM hogehoge_table
WHERE partition_column = 'aaa'
```

# 最後に
HiveのTips記事でした。
Timestamp型と上手く付き合っていきたいですね。

# 参考
https://issues.apache.org/jira/browse/HIVE-9298
https://stackoverflow.com/questions/62607279/how-to-load-a-csv-file-containing-time-string-value-to-timestamp-in-hive
https://community.cloudera.com/t5/Support-Questions/timestamp-not-supported-in-HIVE/td-p/242638
【Rails】タイムゾーンを日本時間に変更する

##時間を変更する意味
RailsのデフォルトタイムゾーンはUTC(協定時)となっているため、``created_atメソッド``や``updated_atメソッド``を使って時間を表示させてみると日本時間から９時間差の表記になってしまう。この差分を埋めるため、時間設定をする必要がある。

##手順
config/application.rbファイルに``config.time_zone = 'Tokyo'``を記載。

```
class Application < Rails::Application
  config.time_zone = 'Tokyo'
end
```

上記の設定が完了したら、一度アプリケーションを落とし再起動すると日本時間での表記になる。

ただし、このままだと表記されると
``Tue, 30 Jul 2019 00:12:19 +0000``のようになり、少し見にくくなってしまう。

##読みやすい日時フォーマットにする
※ネットで検索すると以下のようにstrftimeメソッドを使って検索する方法がよく出てくるが、他にも日時を表示するViewがあると、
``'%Y/%m/%d %H:%M:%S'``のような書式文字列を繰り返し書かないといけないため、コードがDRYにならない。DRYでないコードは変更に弱いコードため、あまり**おすすめできない**。

```
<%= user.created_at.strftime('%Y/%m/%d %H:%M:%S') %>
```
**<おすすめ方法>lメソッドでDRYに書式を指定する**
*手順*
①日本語化設定
②フォーマット作成
③viewに記入

**①日本語化設定**
config/application.rbファイルに``config.i18n.default_locale = :ja``を追加。

```
class Application < Rails::Application
    config.i18n.default_locale = :ja
  end
```

**②フォーマット作成**
``config/locales/ja.yml``というファイルを作成し、以下のような設定を記述。

```
ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"
```

**③viewに記入**
lメソッドを使って書式を指定する。

```
<%= l user.created_at %>

例：
変更前　　30 Jul 2019 00:12:19 +0000
変更後　　2019/07/30 12:19
```

短く表記したい場合は、``formatオプション``を付けると、書式を切り替えることができる。

```
<%= l user.created_at, format: :short %>

例：
変更前　　30 Jul 00:12:19 +0000
変更後　　07/30 12:19
```
``:short``を指定したので、ja.ymlに対応する書式を設定。

```
ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"
      short: "%m/%d %H:%M"
```

**注意: View以外ではI18n.lを使う**
Viewでは``l``と書くだけ良いが、``lメソッド``がヘルパーメソッドとして提供されていない場所（Modelなど）では、``l``だけではNoMethodErrorになる。
その場合は、``l``の代わりに``I18n.l``と書けば、日時をフォーマットすることができる。

ちなみにlはlocalizeのエイリアスメソッドなので、localizeと書いても大丈夫。

以下の記述はどれも同じ結果になる。

```
= l user.created_at
= I18n.l user.created_at
= localize user.created_at
= I18n.localize user.created_at
```

##参考記事
[Railsのタイムゾーンを日本時間にする](https://qiita.com/katsutakashima/items/1453ed0c97a818ddd731)
[【初心者向け・動画付き】Railsで日時をフォーマットするときはstrftimeよりも、lメソッドを使おう](https://qiita.com/jnchito/items/831654253fb8a958ec25)
新人1ヶ月！ここまでの反省と心がけていること

## はじめに

2021年オープンロジアドベントカレンダー、23日目の記事です。

2021年11月からWebエンジニアとして働いています。
学生時代からのプログラミング経験はあるものの、業務で開発を行うのは初めての新人です。
この記事では、新人としてここまでの反省や気づきなどをまとめて書いてみます。

## ここまでの反省

ここまで1ヶ月で感じたことを自戒の念を込めて書きます。

### 反省①｜同じ場所で悩みすぎた

調べればなんとかなるでしょ！と色々調べているうちに時間が過ぎ、気づいたら同じ場所で1日使っていた…ということがありました。
新人として成長することはもちろんですが、自分に課せられたタスクはチームが担っているものであり、チームのスループットを意識することは必須です。

#### 対処法｜質問ルールを決める！

組織によって対応は異なるとは思いますが、他の方々も記事にされているように、自分で質問するルールを設定しておく必要があるなと感じました。私の場合、メンターの先輩と相談し、半日以上進まなかったり、自力で解決が難しいと感じたりした時にはすぐに相談するように意識しています。
まだ相談のタイミングについては自分なりの正解を持っていない状態ですが、トライアンドエラーでちょうどいいところを見つけていこうと思っています。

### 反省②｜何がわからないかわからないときの対処法を知らず、対応が後手になった

①と少し重複する内容です。

オープンロジではフロントエンド/バックエンドの役職による区別をせず、一人で両方やるというスタイルをとっています。
そんなわけで、Webの開発は初めてでしたが、LaravelとReactのチュートリアルを少しやった状態で業務に当たることになりました。
当然ですが、十分な前提知識がないと解決できない課題は多く、それに直面したときに何がわからないかわからず、どう質問するかを考えていたら時間が経過していたということがありました。

具体的に言うと、xxのライブラリを使った実装の仕方がわからないなど、自力で調べながらやるにしても要求される能力と乖離がある場合です。自分でわからないところを整理できるもの（e.g. エラーの対処）は質問で解決できる可能性が高いですが、このような問題の場合、「XXがわからないんですけどどうしたらいいですか？」という好ましくない質問になってしまうなと考え、どう質問しようかと悩んでしまった、という事例です。

#### 対処法｜ペアプロをお願いする！

リーダーから提案だったのですが、このような場合ペアプロで解決するのが良かったです。
ペアプロの目的を理解していなかったので、そもそも自分の不明点の解決に使うとは知りませんでした。
質問とは違い、対応者の負担が増えるのでお願いしづらいところはありますが、私自身非常にいい経験になりました。
直接指導を受けることはもちろんなのですが、経験のあるエンジニアから実際の手の動かし方を見て動かすだけでも新たな発見が数多くありました。抱えている課題の解決よりも大きな収穫があったように思います。

### 反省③｜会話の機会を有効に使えていなかった

メンターの方から指摘していただいたことです。
毎朝15分程度の1on1を実施しているのですが、相談事項がなく報告で終わってしまうことがしばしばありました。
自分の目の前のタスクに必死になり過ぎていて、今必要な情報以外の関心が薄れてしまっていました。

#### 対処法｜気になったことはメモしておく！

自分のために使ってもらっている時間は有効活用しなければ非常にもったいないなと感じました。
これから時間が経つにつれて、いろんなことがどんどん聞きづらくなってくるだろうと思うと、自分にとっても良くありません。
新人のうちは、進捗確認などを目的に、会話をする機会が定期的に組まれることが多いと思います。
よくわからないまま放置していたことは気づいた時にメモしておき、質問できる時に聞いておくことは非常に大事だと思いました。


## コミュニケーションで気をつけていること

私が社会人になる前に身につけておきたかったと感じた、人に相談するときに気をつけていることです。

前職はBiz職だったのですが、その頃から伝えたいことをうまく伝えられず困ることが多くありました。
Slackなどのテキストコミュニケーションが大幅に増えた今、できる限り受け手の負担を減らさなければ、双方のストレスになってしまいます。

### 前提・やりたいこと・ネクストアクションの３つに分ける

#### 前提
  
聞き手は話し手の情報をほとんどor全く知らないはずなので、ここをうまく伝える必要があります。
今何をやろうとしていて、どういう状況か、などの自分が直面している状態をできる限りわかりやすく伝えると、コミュニケーションの手戻りを最小限にできます。

> 例） XXというタスクをやっていて、YYというところでつまづいて、ZZについて調べていた

#### やりたいこと

実際にどうしたいのか、困っていることなど、相手に期待する回答は何かを伝えます。
単なる報告なのか、相談して回答が欲しいことなのかなど、相手が取るべきアクションがわかるようにするとよいです。

> 例） AAというところがわからないので教えてほしい / BBという方法で行おうと考えているが合っているか確認したい

#### ネクストアクション

コミュニケーションの後、単純な結果報告を除いて、次にどんなアクションを取るかの確認が必要な場合が多いです。
前職の話ですが、相談した後に「あれ？結局何やればいいんだっけ？？」となり、また相談し直すということがありました。
話の論点が正しく伝わっていないと、結論がブレることがよくあります。

いくら綺麗に自分で前提とやりたいことが整理できていても、相手に正しく伝わるかは別問題です。
これは仕方のないことなので、そのキャリブレーションの手段として、「次にXXをやろうと思いますが大丈夫ですか？」などと確認を取ると双方に高い安心感が生まれます。

## 最後に

### 日々勉強！それが楽しい！！

勉強の量で言うと、論文に追われていた学生の頃とほぼ変わらず、業務と自学の区別がほとんど付かなくなっています。
しかし、日々何かしらの新知識を得て、業務にフィードバックし、ひいては事業の成長に…と考えるとワクワクします。

技術力のないうちはコミュニケーションでカバーするしかないと思っています。
ひいてはそれが自分の成長になることは間違いないと思います。
そう信じて頑張るのみです！

ネット上にはエンジニアになるまでの情報は多い一方で、なってからの情報は少ない気がしたので、このような内容にしてみました。
同じ悩みを抱えていたり、これからエンジニアになったりする方にとって少しでもためになればと思います！ 

少し宣伝ですが、私の[YOUTRUST](https://youtrust.jp/users/riku929hr)では、隔週〜月一くらいのペースで業務日誌をつけているので、よかったら覗いてみてください！
KotlinのFlow.onEachの挙動

多分collectされるたびに別ストリームになるのでonEachは複数回呼ばれている

``` kotlin
class EventBus {
    private val _event = MutableSharedFlow<Int>()
    val event: Flow<Int> = _event.onEach { println("onEach $it") }

    suspend fun emit(i: Int) {
        println("EventBus emit $i")
        _event.emit(i)
    }
}

class A(val eventBus: EventBus) {
    suspend fun start() {
        println("A start")
        eventBus.event.collect {
            println("A collect $it")
        }
    }
}

class B(val eventBus: EventBus) {
    suspend fun start() {
        println("B start")
        eventBus.event.collect {
            println("B collect $it")
        }
    }
}

val eventBus = EventBus()
val a = A(eventBus)
val b = B(eventBus)

println("start")
runBlocking {
    val jobA = launch {
        a.start()
    }
    val jobB = launch {
        b.start()
    }
    delay(1000)
    eventBus.emit(1)
    jobA.cancel()
    jobB.cancel()
}
println("end")
```

期待してた動作

```
start
A start
B start
EventBus emit 1
onEach 1
A collect 1
B collect 1
end
```

実際の動作

```
start
A start
B start
EventBus emit 1
onEach 1
A collect 1
onEach 1
B collect 1
end
```

onEachが複数回呼ばれている
【VB.NET、C#】ラベルの文字色を変更する（ForeColorプロパティ）



#ラベルの文字色を変更する
ラベルの文字色を変更する場合、コントロールのForeColorプロパティに、Color構造体のプロパティを設定することで、文字色の変更が可能です。

color構造体のプロパティはMSDNで公開されています。

https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.color?view=net-6.0


#実行確認用テストフォーム
イベント発生時にForeColorプロパティに対して、Color構造体のプロパティを設定してみます。
「クリックイベント発生時に、ラベルの文字色を変更する」というテストフォームを作成して動作を確認してみます。

![20211222_001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2162017/ba8c4c50-f8ce-c8ed-396e-508f25abacbb.jpeg)

```vb
'【VB.NET】
Private Sub btnFontColorChange_Click(sender As Object, e As EventArgs) Handles btnFontColorChange.Click
    'ラベルの文字色を設定
    lblComment.ForeColor = Color.Red
End Sub
```

```c#
//【C#】
private void btnFontColorChange_Click(object sender, EventArgs e)
{
    //ラベルの文字色を設定
    lblComment.ForeColor = Color.Red;
}
```
実行すると、こうなります。
![20211222_002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2162017/07b38d39-12cf-b7b6-05c8-506090564796.jpeg)

「Hello.world!」という文字が赤色に変わりました。

#ラベルの文字色を元に戻したい（デフォルトに設定したい）場合
ラベルの文字色を変更した後、元に戻したい場合は、ForeColorプロパティに対して、SystemColorsクラスのControlTextを設定します。

```vb
'【VB.NET】
'ラベルの文字色を元に戻す
lblComment.ForeColor = SystemColors.ControlText
```

```c#
//【C#】
//ラベルの文字色を元に戻す
lblComment.ForeColor = SystemColors.ControlText;
```


#参考
https://docs.microsoft.com/ja-jp/office/vba/api/excel.forecolor

https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.color?view=net-6.0

https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.systemcolors?view=net-6.0

https://stellacreate.com/entry/vbnet-label-color-change

http://jeanne.wankuma.com/tips/vb.net/control/forecolor.html
QOI圧縮を検証

#QOI圧縮とは
QOI　は Quite OK Image の略らしいです（どうなんですかねこのネーミング）。
先日(2021/12/20)、ファイルフォーマット規定の ver1.0 が発表されました。
https://phoboslab.org/log/2021/12/qoi-specification
このQOI圧縮、なかなか興味深いので掘ってみます。

#特徴
この圧縮方式の特徴は、とにかく実行速度が速いこと。ソースを見ればわかりますが、非常にシンプルなプログラムで、一見して「こりゃ速いよね」とわかります。コードも小さいしワークも少ないので、広範囲のIoT機器で実装できそう。ゲーム実装でも有効な場面は多いと思います。

そんな話を適当に流したい、そんな方は
https://voicy.jp/channel/2462/252474
こちらに概要を伝えた10分弱の音声解説を置きました。（BGMは外せます。スマホでVoicyアプリを入れるのがよいでしょう）

#実験
githubより

https://github.com/phoboslab/qoi

を落として、そこにある qoi.h を #include してね、というそれだけ。Makefileすらありません。その qoi.h もシンプル。このオリジナルはＣ言語ですが、有志によりすでにさまざまな言語にポーティングされているようです。

#サイズ検証
サンプルの qoiconv をビルドして、いくつかの画像を試してサイズを確認しました。
使用した画像は https://samplelib.com/sample-png.html と[いらすとや](https://www.irasutoya.com/ "いらすとや")さんから拝借しています。最後のノイズ画像は自作です。数値はバイト数と圧縮率で、注目すべき圧縮率に<font color="Green">緑</font>・<font color="Blue">青</font>で色をつけています。

||無圧縮|<font color="Green">PNG</font>|<font color="Blue">QOI</font>|QOI(pre)|
|---|---|---|---|---|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90046/756a499d-ab4a-c863-dc6c-7a1e2efc5ef9.png)|1,440,000<br>100%|705,670<br><font color="Green">49%</font>|817,088<br><font color="Blue">56.7%</font>|897,536<br>62.3%|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90046/ddc2df3e-6e00-77ee-af50-dbcf13f35749.png)|360,000 <br>100%|195,459<br><font color="Green">54.3%</font>|256,683<br><font color="Blue">71.3%</font>|266,178<br>73.9%|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90046/47fec195-816a-d0b4-8d51-568998e8512b.png)|360,000<br>100%|124,864<br><font color="Green">34.7%</font>|156,769<br><font color="Blue">43.6%</font>|158,766<br>44.1%|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90046/7e5802a4-6d7d-0d72-8d8a-0af413c1fa9e.png)|360,000<br>100%|271,068<br><font color="Green">75.3%</font>|327,451<br><font color="Blue">91%</font>|359,014<br>99.7%|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90046/47fbdb82-bd7c-8800-ccfd-7bc0974f253c.png)|360,000<br>100%|153<br><font color="Green">0.04%</font>|1,959<br><font color="Blue">0.54%</font>|50<br>0.01%|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90046/a895d417-a6d7-b8ae-bfc4-badc5e690189.png)|2,934,080<br>100%|386,601<br><font color="Green">13.2%</font>|375,828<br><font color="Blue">12.8%</font>|384,337<br>13.1%|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90046/e8d052c7-ffaa-e8d5-a0a9-a9674dd2d773.png)|1,048,576<br>100%|1,050,600<br><font color="Green">100.19%</font>|1,309,682<br><font color="Blue">124.9%</font>|1,306,558<br>124.6%|

いちばん右のカラム QUI(pre) というのは、このフォーマットが規定するひと月ほど前に公開された前バージョンのQOI圧縮です。その時点でも調査していたので併記しました。1.0になってもコンセプトは変わりませんが、実装はかなり変わっていて、圧縮率にも差が出ています。

#考察
おおむね、写真のような画像に関してはPNGのほうが圧縮率が高いのがわかります。いっぽうでイラスト調の画像ではQOI圧縮に軍配が上がります。

赤のベタ塗り画像に関してはQOIはかなり負けています。これはQOIの前バージョンと大きく変わったポイントです。ベタ画像はそもそも縮むので、そこで極端に小さくしなくても良かろう、ということでしょう、同意できるところです。

ホワイトノイズに関しては、どの圧縮でも元の画像よりも大きくなりますね。意外とPNGは、ノイズでも大きくなりませんね。

#まとめ
圧縮率だけを比較すればPNGなのでしょうけど、encodeも含む速度で有利となると、QOI圧縮の利用範囲は広そうです。画像によってはPNGよりも縮みます。
なにより、この2021年になって新しい画像圧縮が、こんなシンプルな形で登場したことに驚きました。
[py2rb] slice の None

# はじめに
移植やってます
# None (Python)
```python
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print(a[2:])
print(a[2:-1])
print(a[2:None])

# [2, 3, 4, 5, 6, 7, 8, 9]
# [2, 3, 4, 5, 6, 7, 8]   
# [2, 3, 4, 5, 6, 7, 8, 9]
```
へぇー、`:None`だと末尾まで行くんだ。
# nil (Ruby)
```ruby
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

p a[2..]
p a[2...]
p a[2..-1]
p a[2...-1]
p a[2..nil]
p a[2...nil]

# [2, 3, 4, 5, 6, 7, 8, 9]
# [2, 3, 4, 5, 6, 7, 8, 9]
# [2, 3, 4, 5, 6, 7, 8, 9]
# [2, 3, 4, 5, 6, 7, 8]
# [2, 3, 4, 5, 6, 7, 8, 9]
# [2, 3, 4, 5, 6, 7, 8, 9]
```
`-1`とは異なり、`..nil`も`...nil`も同じ動作となります。
# メモ
+ Python の None を学習した
+ 道のりは遠そう
neosでlogix その30

#概要

niosでlogixやってみる。
Trajectoryノードを使ってみた。

#写真

![2021-12-22 00.32.46.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/7d7f0f80-4d69-e43e-f283-8529df7e8242.jpeg)


#Trajectory

```
Inputs
 	Float3	InitialVelocity
 	Float3	Gravity
 	Float	Drag
 	Float	Time
Outputs
 	Float3	Position
```

以上。
neosでlogix その29

#概要

niosでlogixやってみる。
Stopwatchノードを使ってみた。


#写真

![2021-12-22 00.22.07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/bdadeb1c-1774-8a97-f98f-b75bac680f7e.jpeg)



#Stopwatch

```
Inputs
 	Impulse	Start
 	Impulse	Stop
 	Impulse	Reset
Outputs
 	Impulse	OnStart
 	Impulse	OnStop
 	Impulse	OnReset
 	Float	Time
 	Bool	IsRunning
```

以上。
【Rails】seeds.rbファイルとは

##seedsファイルとは
初期データを生成してくれるファイルのことで、簡単にテストデータを作ったりできる。

##記入方法
```
#通常の場合
User.create(name: "Taro")
User.create(name: "Hanako")

#配列の場合
users = User.create([{name: "Taro"}, {name: "Hanako"}])

#大量のテストデータの場合(この場合は100回繰り返す)
100.times do |n|
  User.create(title: "test{n}人目", content:"test！#{n}回目")
end
```
内容を記入し終わったら、``rails db:seed``のコマンドを実行。

##Faker
このFakerを使うと、実在しそうなデータを生成してくれるgemがあるのでその手順や記入法も記載しておく。

**Gemfile**

```
gem 'faker'

↑この文を追加後、bundle installを入力。
```

**記入法**

```
10.times do
  User.create(
      first_name: Faker::Name.first_name,
      last_name: Faker::Name.last_name,
      email: Faker::Internet.email,
      password: 'abcde',
      password_confirmation: 'abcde'
  )
end

20.times do |n|
  Board.create(
      user: User.offset(rand(User.count)).first,
      title: "タイトル#{n}",
      body: "本文#{n}"
  )
end
```
上記のFakerで作成される人名は日本語設定になっていると、日本語の名前になったりする。

``User.offset(rand(User.count)).first``
offsetメソッドは、データの取得開始位置を指定する。
randメソッドは整数値を与えると、0からその整数値未満の整数を返す。
なので、User.offset(rand(User.count)).first は、ユーザーの数の中から（例えば10だとすれば、0から9）ランダムに数字を生成し、ユーザーテーブルのデータ取得開始位置を決めて、その最初のユーザーデータを返すように指示する。

[Fakerで利用できるデータ](https://github.com/faker-ruby/faker#generators)

##参考記事
[Fakerを使ってダミーデータを作る【Day 8/30 2nd】]
(https://note.com/kentarotawara/n/n240bf41b5a54)
[seeds.rbの使い方](https://qiita.com/ren0826jam/items/b589171502df87e40234)
Javaを使用してExcel画像を追加、読み取り、削除する方法

##背景
この記事では、JavaプログラムでExcelテーブルに画像を追加する方法を紹介します。画像を追加するときは、画像のサイズ、位置、回転、ハイパーリンク、オプションのテキストなどを設定できます。また、エクセルテーブル。

ツール：無料のSpire.XLS for Java（無料版）
注：パッケージを公式Webサイトから**[ダウンロード](https://www.e-iceblue.com/Download/xls-for-java-free.html)**して解凍し、libフォルダー内のjarファイルをjavaプログラムにインポートするか、Mavenウェアハウスからダウンロードしてインポートすることができます。

**Jarのインポートした結果：**
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/e8026e17-bdfe-5a71-db85-84d8c73f1ca6.png)

##Javaコード一覧
###画像を追加する
```
import com.spire.xls.*;

public class AddImage {
    public static void main(String[] args) {
        //ドキュメントをロードする
        Workbook wb = new Workbook();
        wb.loadFromFile("C:\\Users\\Administrator\\Desktop\\test.xlsx");

        //ワークシートを取得する
        Worksheet sheet = wb.getWorksheets().get(0);

        //画像を追加する
        ExcelPicture picture = sheet.getPictures().add(7,2,"C:\\Users\\Administrator\\Pictures\\tp.png");
        picture.setHeight(270);//画像の高さを設定する
        picture.setWidth(550);//画像幅を設定する
        picture.setRotation(20);//画像の回転角度を設定する
        picture.setAlternativeText("Picture1");//画像の選択可能テキストを設定する
        picture.setHyperLink("https://www.google.com/",true);//画像にハイパーリンクを追加する

        //ドキュメントを保存する
        wb.saveToFile("AddImage.xlsx", ExcelVersion.Version2010);
        wb.dispose();
    }
}
```
**画像を追加した結果：**
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/27f9996d-b00d-b135-900a-c346aaf81f38.png)
###画像を読み取る
```
import com.spire.xls.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class ExtractImage {
    public static void main(String[] args) throws IOException {
        //ドキュメントをロードする
        Workbook wb = new Workbook();
        wb.loadFromFile("AddImage.xlsx");

        //1番目のワークシートを取得する
        Worksheet sheet = wb.getWorksheets().get(0);

        //ワークシートの最初の画像を取得し、指定したパスに保存する
        ExcelPicture pic = sheet.getPictures().get(0);
        BufferedImage loImage = pic.getPicture();
        ImageIO.write(loImage,"jpg",new File("ExtractedImage.jpg"));
    }
}
```
**画像を読み取った結果：**
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/69a30a98-beb9-79c4-2ed3-d718608b73de.png)
##画像を削除する
```
import com.spire.xls.*;

public class RemoveImage {
    public static void main(String[] args) {
        //ドキュメントをロードする
        Workbook wb = new Workbook();
        wb.loadFromFile("AddImage.xlsx");

        //指定したワークシートを取得する
        Worksheet sheet = wb.getWorksheets().get(0);

        //指定した画像を取得し削除する
        sheet.getPictures().get(0).remove();

        //ドキュメントを保存する
        wb.saveToFile("RemoveImage.xlsx",ExcelVersion.Version2010);
        wb.dispose();
    }
}
```
**画像を削除した結果：**
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/6c2593b9-52be-73c7-89d6-d0ceec69fff8.png)

はい、おしまい、今回の記事の内容はここまでです、最後まで読んでいただき誠にありがとうございます。ではまた！
AWS CloudShell 上の vi エディタでインデントの自動挿入なしでペーストする方法

# はじめに

みなさん、こんにちは。AWS CloudShell を使っているとファイルにペーストした際、勝手にインデントが入って「あー！」っとなったことの一度くらいはあるのではないでしょうか。今回はそんなときの解決方法を紹介していきたいと思います。

![01-auto-indent.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613908/a054bed0-2fbe-7389-605a-934fc1d5b572.gif)

# インデントの自動挿入なしでペーストする方法

いくつか方法はありますが、解の1つは「ペーストモードを使う」です。編集モードへ入る前に `:set paste` もしくは `:set paste!` を実行しましょう。ペーストモードにすると出力例のようにインデントが追加されずに期待通りの動きになりますね。

![02-paste-mode.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613908/d3321b67-610f-797b-fac9-35eaf445c7d0.gif)

# 起動時に自動で設定する方法

とはいえ、エディタを起動するたびに毎回ペーストモードの設定をするのは面倒です。そんなときは `vim` の設定ファイルを作成しましょう。これでエディタが起動した際に自動で適用されるようになります。めでたしめでたし。

```txt:~/.vimrc
set paste
```

# 終わりに

いまさらの情報でしたがいかがだったでしょうか。もちろんコードを編集するときは自動でインデントを追加してくれるのはうれしいのですが、個人的には AWS CloudShell 上ではクリップボードからコピーしてくることも結構多いのでデフォルトペーストモードにしておき、必要に応じて  `:set nopaste` もしくは `:set paste!` しております。よろしければ参考にしていただければと思います。

以上、AWS CloudShell 上の vi エディタでインデントの自動挿入なしでペーストする方法でした。

---

- AWS は、米国その他の諸国における Amazon.com, Inc. またはその関連会社の商標です。
- その他、本資料に記述してある会社名、製品名は、各社の登録商品または商標です。
GKE クラスタ作成時のオプションで Anthos Service Mesh を有効化できるようになりました

# はじめに

みなさん、こんにちは。今回はタイトルに掲げたとおり、最近の更新で Google Cloud コンソール(GUI)から Google Kubernetes Engine(GKE) Standard クラスタを作成する際に、次のようなすてきなオプションがプレビュー機能として追加されました。今回はこのオプションを使ってどのような構成が作られるのか実験したので共有したいと思います。

![01-anthos-service-mesh-enable-option.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613908/df0d6d08-dd40-b020-f84b-338faaba0f92.png)

# いきなりですが、結論です！

## In-cluster とマネージドコントロールプレーンのどちらで構築される？

答え、マネージドコントロールプレーンにて構築されます。現時点でこの設定を変更することはできません。

## Anthos Service Mesh のバージョンは？

答え、GKE でリリースチャンネルを採用した場合は GKE と同じチャンネルになります。GKE で静的リリースを採用した場合は Reguler チャンネルとなります。現時点でこの設定を変更することはできません。

## カスタム CA を扱うことはできるか？

答え、扱えません。マネージドコントロールプレーンでの導入となるため、カスタム CA を扱うことができる Istio CA を選択することはできません。

## 限定クラスタにした場合は別途 15017/TCP を許可する必要があるか？

答え、不要です。ルールを追加しなくてもサイドカー自動インジェクションは問題なく動きます。

## Ingress ゲートウェイはデフォルトで作られるか？

答え、Ingress ゲートウェイはデフォルトでは作られません。別途ユーザにてデプロイする必要があります。

# 終わりに

今回は GKE クラスタ作成時の Anthos Service Mesh 有効化オプションの実験結果の共有でしたがいかがだったでしょうか。

GKE と Anthos Service Mesh で採用するリリースチャンネルを変えたい、カスタム CA を使いたいといったケースでは従来どおり CLI を利用する必要がありますが、これらの要件がなければ GUI からポチポチするだけでとっても簡単に環境を作れるようになりそうですね。

:::note warn
2021年12月時点では GUI の Anthos Service Mesh 有効化オプションはプレビュー段階であり、今回紹介した挙動から変わる可能性がありますのでご注意ください。
:::

---

- Google Cloud は、Google LLC の商標または登録商標です。
- その他、記載されている会社名および商品・製品・サービス名は、各社の商標または登録商標です。
AutifyのJSステップ間で値の引き渡しをする

本記事はベリサーブ [Advent Calendar 2021のカレンダー | Advent Calendar 2021 - Qiita](https://qiita.com/advent-calendar/2021/veriserve)の22日目です。

21日目は@yamasaki696 さんの[ISO/IEC/IEEE 29119-2～4の改訂で新しくでてきたテストモデルについて少し調べてみた](https://qiita.com/yamasaki696/items/954d6d5655d544698238)でした。

さて今回は[前回のAutifyのJSステップを書いてみる](https://qiita.com/YamaAri/items/7c2e2987198c701d837c)にひき続き、AutifyのJSステップのお話です。
タイトルにもある通り、JSステップ間で値の引き渡しができるようになったので、そちらの機能を試していこうと思います。
これが使えると、シナリオ内で作った値を使いまわせたりするので、便利かもしれません。

また例の如く、私はJSはほぼ未経験のため、「もっとこう書いたほうがいいよ」などがあればぜひ教えてください。


# はじめに

## 環境について
前回と同じですが、一応一通り書いておきます。
Autifyを使用するには下記を準備する必要があります。

- Autifyのユーザー
- Google Chromeと拡張機能「Autify Recorder」

これらについては公式ページが詳しく書いていますので、[ユーザー登録と環境セットアップ](https://autify.zendesk.com/hc/ja/articles/900003243946-%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E7%99%BB%E9%8C%B2%E3%81%A8%E7%92%B0%E5%A2%83%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)をご確認いただくのが良いと思います。

また今回のテスト対象は[HOTEL PLANISPHERE](https://hotel.testplanisphere.dev/ja/)を使わせていただきます。
HOTEL PLANISPHEREはホテル予約サイトで、予約画面は下記のようになっています。
![Autify_テスト対象画面.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121319/1554a307-bc66-a565-d249-028f9b31c213.png)



## 実装するテストケースとデータ

下記はざっくりした手順です。

1. お得な特典つきプランのこのプランで予約を選択
2. 宿泊日を設定
3. 宿泊日数を設定
4. 人数を設定
5. 名前を入力
6. 確認の連絡を選択
7. **表示された金額を取得する**
8. 予約内容を確認するを押下
9. **表示された金額と前ページで表示されていた金額が同じであるかを確認する**


Autifyでのテストの実装については以前[Autifyでテスト自動化を試してみる](https://qiita.com/YamaAri/items/3f43ef80d5b32152c43d)で記載しましたので、不明な方はそちらをご参照ください。

今回は太字の部分をJSで実装していきます。
シナリオ全体はだいたいこんな感じになります。
![Autify_.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121319/72efd1c0-cedf-40dd-d0ac-148e6aa12511.png)


# 実装

## 表示された金額を取得する

```金額取得.js
function(){
  /* 金額の取得 */
  var totalBill = document.getElementById('total-bill').textContent;

  /* 他JSステップで値を使う場合はreturn必須 */
  return totalBill;
}
```

Autify上の画面だと下記のようになります。

![Autify_金額の保持.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121319/c297152c-eb57-bb55-0f67-73a04ed21de5.png)



## 表示された金額と前ページで表示されていた金額が同じであるかを確認する


```金額比較.js
function(totalBill){
  /* 表示金額の取得と不要な文字列の削除 */
  var checktotalBill = document.getElementById('total-bill').textContent;
  var checktotalBill = checktotalBill.replace('合計 ').replace('（税込み）');

  /* 取得していた値と画面上の金額が一致するかを確認する */
  if (totalBill !== checktotalBill) {

  /* 一致しない場合はエラーメッセージ */
    errMessage = 'Error: expected(' + totalBill + ') and actual(' + checktotalBill + ') are not same.';
    throw new Error(errMessage);
  }
}
```

totalBillには金額取得のjsでリターンしたものが入ってきます。
Autify上の画面だと下記のようになります。
![Autify_他ステップから引数.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121319/55c6c03a-bff1-2a75-2263-0a14f6ce50ff.png)


以上でテストを回せばパスするはずです！

試しに1JSステップ内で2つreturnし、次のJSステップで2つとも取得できるのかを試してみましたが、先にreturnされた1つだけがreturnされているようでした。
データ仮想化機能である Watson Query を有効化する。

# データ仮想化機能である Watson Query を有効化する。

# この記事の位置づけ

- この記事は、[IBM Cloud Paks Advent Calendar 2021 Advent Calendar 2021] (https://qiita.com/advent-calendar/2021/cloudpaks) の12/14分として記載します。
- 2022年１月時点でCP4DaaSハンズオン資料（[5.データ仮想化編](https://qiita.com/Shumpei_Kubo/items/a8e2418b171d60793cc6)）の記事として位置づけています。

#やりたいこと
- データ仮想化機能であるWatson Queryが先日GAしたので、それを有効化して使える状態にします。

# 手順

## 1.IBM Cloudの管理画面でサービスを検索する。

- ログイン後の[IBM Cloudの管理画面](https://cloud.ibm.com/)の画面左上の検索窓で、「Watson Query」と入力し、表示される「Watson Query」をクリックします。
![スクリーンショット 2021-12-22 10.37.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/a23ac673-473e-4427-cd04-98e8bbb7ab6e.png)

## 2.サービスの確認と同意のチェック
- サービスがデプロイされるロケーションの確認（仮にすでに他のCP4DaaSサービスをデプロイ済であるなら、そこに合わせる必要があります。ここではダラスを選択しています。）、及びプランがLiteプラン（30日間無償）である事を確認します。
- サマリーが右上にあるのでそこでも確認します。
- 画面右下の同意条件を確認の上、チェックボックスをチェックします。
![スクリーンショット 2021-12-22 10.41.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/653a851e-67ef-e9da-381c-d894fd88dad5.png)

## 3.サービスの作成
- 青くなる「作成」ボタンをクリックします。
![スクリーンショット 2021-12-22 10.42.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/ac2955aa-ee34-c4db-495f-00aa18880f10.png)


- 画面が自動的に遷移します。
![スクリーンショット 2021-12-22 10.43.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/00c88db4-1578-8caa-f94e-ec5f5b1e497c.png)

![スクリーンショット 2021-12-22 10.43.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/e05432a4-4875-dffc-cf96-66c7994f373a.png)

## 4.待機
- サービスが作成されるまでかなり時間がかかります。経験的には1時間、かかるときは数時間かかかりますので、タイマーでもかけてその間に別な事でもしましょう。

## ４．５ 失敗した場合

- 長時間経ってもアクティブにならない場合があり、翌日確認して状態が「失敗」となっている事などがあります。
- その場合はWatson Queryの欄の右端をクリックして「削除」をクリックしてこのインスタンスを一度削除し、もう一度作成しなおして下さい。（以下写真はWatson Queryがアクティブな場合ですが）
![スクリーンショット 2021-12-24 11.48.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/a15c030e-453c-298d-fa1f-457c1ba0263a.png)


## 5.サービス確認

- リソース・リストでWatson Query が「アクティブ」状態になっている事を確認し、クリックします。
![スクリーンショット 2021-12-23 0.08.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/1ab417f7-94ff-7f3d-3e59-7953358fb55d.png)

- 「Go to UI」をクリックします。
![スクリーンショット 2021-12-23 0.11.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/a05d19fb-6233-517d-b153-843a76b2aab4.png)

- 無事にWatson Query のサービスが出来ています。
![スクリーンショット 2021-12-23 0.19.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/20d4590e-345e-64f3-5589-ff716b0ce1dc.png)

- なお、CP4DaaSのハンバーガーメニューからだと、「データ」配下の「Data Virtualization」から入れます。
![スクリーンショット 2021-12-23 0.22.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295123/e9500cf4-98d4-4e19-c986-e7096a402099.png)

#この後できること。

[こちら](https://qiita.com/Shumpei_Kubo/items/ecd02072e6eb5f84b499)で、Db2とSnowflakeのデータを仮想化して結合するという手順を公開していますので、よければ試してみて下さい。
VMware HorizonのView ADAMデータベースへの接続方法

# この記事について
VMware Horizon環境でView ADAMデータベースを参照するための手順を記載します。
基本的にはVMwareのKnowledge Baseの[KB2012377](https://kb.vmware.com/s/article/2012377?lang=ja)と同様の内容をキャプチャを交えて記載します。

# View ADAMデータベースとは
VMware HorizonではVDIやRDSHプールの設定内容などを
Connection Server内部のView ADAMデータベースに保持しています。
通常はHorizon ConsoleのようなGUI画面からVMware Horizonに関する各種設定やデプロイなどを実施しますが
GUI画面ではできない細かい設定やトラブルシューティングの際にView ADAMデータベースを操作することがあります。

# 手順
## Step1, Connection Serverに接続する
リモートデスクトップなど任意の方法でConnection Serverへログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412227/3502f1c2-be19-0111-269a-2d9223d157be.png)

## Step2, ADAM ADSI Editを開く
Connection Server上でADSIエディターを開きます。
Windowsメニューを開いて「adsi」などで検索すると簡単です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412227/6ea1a77f-aa8f-a483-ba4e-1b56de98f4e0.png)

## Step3, 接続先情報を入力して接続する
ADSIエディターが開いたら、ADSIエディターウィンドウの中の「ADSIエディター」の文字を右クリックして「接続」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412227/4048ead7-b060-a128-6e9c-a372e10e06b9.png)

接続ウィンドウが開いたら下記を入力します。[ ]の中を入力
入力ができたら「OK」をクリックします。

* 名前: [View ADAM Database]
* 識別名または名前付けコンテキストを選択または入力する: [dc=vdi,dc=vmware,dc=int]
* ドメインまたはサーバーを選択または入力する: [localhost:389]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412227/39ad2398-8ee4-1f7d-af8d-e92a1368aecb.png)

## Step4, View ADAMデータベースの中を見てみる
問題なく接続が完了すると「ADSIエディター」の配下に「View ADAM Database [localhost:389]」が表示されます。
「View ADAM Database [localhost:389]」やその配下は入れ子になっており展開できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412227/7fe39173-4877-9313-fc0f-be33b2beb4a1.png)

## まとめ
今回はView ADAMデータベースの接続方法を記載しました。
この後、必要に応じてVMwareのKnowledge Baseなどに記載の設定を実施することができます。
SQL Server のストアドプロシージャ 備忘録


参考

https://sql55.com/t-sql/t-sql-create-stored-procedure-1.php

##テーブルの準備
```
DROP TABLE  IF EXISTS student_tb;
CREATE TABLE student_tb(
student_id INT,
first_name CHAR(20),
last_name CHAR(20),
birthday DATE,
gender CHAR(5)
);
```

|student_id|first_name|last_name|birthday|gender|
|----|----|----|----|----|
|1|太郎|山田|1980/2/15|男性|
|2|花子|田中|1979/12/30|女性|
|3|優子|鈴木|1979/7/7|女性|
|4|孝雄|佐藤|1980/3/12|男性|
|5|弘樹|髙木|1979/4/5|男性|
|6|優佳|木村|1981/3/27|女性|

```
DROP TABLE  IF EXISTS test_result_tb;
CREATE TABLE test_result_tb(
test_result_id INT,
test_id INT,
student_id INT,
score INT
);
```

|test_result_id|test_id|student_id|score|
|----|----|----|----|
|1|1|1|85|
|2|1|2|60|
|3|1|4|98|
|4|1|5|73|
|5|2|1|77|
|6|2|NULL|NULL|
|7|2|3|92|
|8|2|4|81|

##ストアド作成

```
CREATE PROCEDURE uspStudentDelete
    @StudentID      INT,
    @ErrorMessage   NVARCHAR(100) OUT
AS
BEGIN
    IF EXISTS
        (SELECT *
         FROM   TestResult
         WHERE  StudentID = @StudentID)
       BEGIN
        SET @ErrorMessage = N'TestResult が存在するので削除できません。';
       END
    ELSE
       BEGIN
        DELETE
        FROM    Student
        WHERE   StudentID = @StudentID;
       END
END;
```
|項目|説明|備考|
|----|----|----|
|<span>@</span>StudentID INT|@パラメータ + 型|ストアド作成時はDECLEAR不要|
|NVARCHAR(100) OUT|アウトプットパラメータの後ろには OUTPUT または OUT というキーワードをつける||
|END|BEGIN...END構文|入れ子も可能<br>https://docs.microsoft.com/ja-jp/sql/t-sql/language-elements/begin-end-transact-sql?view=sql-server-ver15|
|SET <span>@</span>ErrorMessage|SET 変数 = 代入||

##ストアドの呼び出し方
```
DECLARE @ErrorMessage NVARCHAR(100);

EXEC uspStudentDelete 6, @ErrorMessage OUTPUT;

PRINT @ErrorMessage;
```
|項目|説明|備考|
|----|----|----|
|DECLARE |DECLARE + 変数 + 型||
|EXEC|EXECUTE または EXECで呼び出し<BR>EXECUTE + ストアド + パラメータ|

##更新
* ストアドを選択して<b>Modity</b>をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637823/bc897608-3a8a-b9b8-08ff-c31f4c561557.png)

* <b>ALTER PROCEDURE</b> のステートメントがクエリー画面に出る。
* 更新後、<b>Execute</b> する。

##削除
```
DROP PROCEDURE uspStudentDelete;
```
新人エンジニアに役立った命名規則6選とアンチパターン3選

#はじめに

> 「関数は動詞で始める」
> 

というのは一般的な命名規則とされていますが、Boolean型の返り値を伴うメソッド名に見事にアンチパターンにハマったのでアウトプット。
※個人ブログから技術的アウトプットはQiitaへ引っ越ししたので、こちらは過去に書いたブログとなります。



# 一般的に良いとされている命名6選（Boolean型）

1/ is + 形容詞
2/ has or is + 過去分詞
3/ can + 動詞原型
4/ 三人称単数
5/ 三人称単数の動詞 + 名詞
6/ should + 動詞原型


## 1.. is + 形容詞

- 形容詞の状態であるかを尋ねる

```js
ex/ isEmpty , isNull
```

## 2.. has or is + 過去分詞

- 動詞の状態となったか（完了、経験、継続）を尋ねる

```js
ex/ hasSent , isChanged , isChecked
```

## 3.. can + 動詞原型

- 動詞の挙動が可能かを尋ねる

```js
ex/ canGet , canUpdate
```

## 4.. 三人称単数

- 動詞の状態かを尋ねる
- 後に続く名詞が明確な場合に利用

```js
ex/ exists
```

## 5.. 三人称単数の動詞 + 名詞

- 動詞の状態に名詞

```js
ex/ existsError , hasRequiredKeys
```

## 6.. should + 動詞原型

- 動詞の挙動を行うべきか尋ねる

```js
ex/ shouldContinue , shouldCollect , shouldRedirect
```

などが一般的。

# アンチパターン3選


1. check + Something
2. is + be
3. 動詞原形 + 名詞 文法的に誤り


## 1. . check + Something

- アンチ理由：**YesかNoかが不明確**

```js
ex/ checkPesonalityId , checkError
```

## 2. . is + be

- アンチ理由：**文法的にアウト**

```js
ex/ 
誤) isFail / isEnable
正) isFailed / isEnabled
```

## 3. . 動詞原形 + 名詞 文法的に誤り

- アンチ理由：これも文法的な誤り

```js
ex/ 
誤) existError
正) existsError
```

# 指針

> ifの仮定文の条件に入れたとき、「変数名 == true」をつけないと文章として成立しない変数名は避ける
> 

### **良い例**

```js
if (hasPersonalityId){
  return db.doc
}

> 訳) もしパーソナリティIDを持ってたら、DBのドキュメントを~~~返す
> 文章的に成立する
```

### **悪い例**

```js
if (personalityIdFlag){
  return db.doc
}

> 訳) もしパーソナリティIDフラグなら,DBのドキュメントを~~~返す
> 文章的に成立しない
```

```js
if (personalityIdFlag == true){
  return db.doc~~~
}

> 訳) もしパーソナリティIDフラグがtrueなら,DBのドキュメントを~~~返す
> 文章的に成立する.故に避けるべき変数
```

# 例外（関数名）

- 実務でハマったのが以下

```typescript
ischecked (channelId:string,personalityId:string){
  hoge
 }
```

- 意図していた命名の訳：パーソナリティIDがチャンネルIDと一致していたらtrueを返す。

↑の **2.. is + 過去分詞** を意識したが、これだと何がチェックされたかわからない。

つまり、ルールを意識しすぎた。

結果的に、このファイルが何のパーソナリティidが明確であったためにアンチパターンでもある

```typescript
checkPersonalityId(channelId:string,personalityId:string){
  hoge
 }
```

関数名だけではYesかNoか不明確だが、

ディレクトリ構成上で何がYes/Noか明確にされているなら特別にOKだよね

また、変数名ではなく関数名なので、過去分詞にしなくてOKだよねとなり、edは省いてもOK

とレビュー頂き、無事にapprove頂いた。

# まとめ

- 「関数は動詞で始める」が基本のキ
- 一般的に良いとされている命名6選は守ろう、アンチパターンも意識する
- 指針である「ifの仮定文の条件に入れたとき、『変数名 == true』をつけないと文章として成立しない変数名は避ける」
- 例外でアンチパターンがOKの場合もある

### 参考
- [プログラミング ワンポイント - boolean型の変数の名前は命題にする - 株式会社デーコム](http://www.dcom-web.co.jp/technology/pgpoint1/)
バッチファイルのダブルクォート地獄を回避する方法

バッチファイル（BATファイル）の実行時引数にスペースを含む場合、その引数をダブルクォートで囲む必要がある。
一方、実行されたバッチファイル内で別途実行するコマンドに受け取った引数を渡す場合、同様にダブルクォートで囲む必要がある。
しかし、以下のように単純にダブルクォートで囲むと、望み通りではない結果となる。

```a.bat
@call b.bat "Hi %1, How are you?"
```

```b.bat
@echo %1
```

上記a.batを実行すると、以下のようになる。

```
> a.bat "Hoge Fuge"
"Hi "Hoge
```

# 解決策

以下のように、引数をフィルタリング（？）することで上記問題を解決できる。

```a2.bat
@call b2.bat "Hi %~1, How are you?"
```

```b2.bat
@echo %~1
```

上記a2.batを実行すると、望み通りの結果となる。

```
> a2.bat "Fuge Hoge"
Hi Fuge Hoge, How are you?
```

----

自分は長年知らずに困ることがあったので、同様に困っている方に共有したいと思い、本記事を掲載することにした。
CVE-2021-44228 の Log4j 1.x への影響を調査しました

[Apache Log4j Security Vulnerabilities](https://logging.apache.org/log4j/2.x/security.html) で [CVE-2021-44228](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228) の Log4j 1.x への影響について追記がありました。脆弱性の影響があるかどうかの確認方法を調査したので、参考情報として掲載します。

Apache Log4j 公式サイトには、次の通り記載されています。

> Log4j 1.x does not have Lookups so the risk is lower. Applications using Log4j 1.x are only vulnerable to this attack when they use JNDI in their configuration. A separate CVE (CVE-2021-4104) has been filed for this vulnerability. To mitigate: Audit your logging configuration to ensure it has no JMSAppender configured. Log4j 1.x configurations without JMSAppender are not impacted by this vulnerability.
>
> "Apache Log4j Security Vulnerabilities". The Apache Software Foundation. Retrieved December 22, 2021.

Log4j 1.x にはルックアップがないため、リスクは低いです。但し、JNDI を使用するように構成している場合は、今回の攻撃に対して脆弱です。この脆弱性については、別途 [CVE-2021-4104](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-4104) が提出されています。

CVE-2021-4104 には、次の通りに記載されています。

> JMSAppender in Log4j 1.2 is vulnerable to deserialization of untrusted data when the attacker has write access to the Log4j configuration. The attacker can provide TopicBindingName and TopicConnectionFactoryBindingName configurations causing JMSAppender to perform JNDI requests that result in remote code execution in a similar fashion to CVE-2021-44228. Note this issue only affects Log4j 1.2 when specifically configured to use JMSAppender, which is not the default. Apache Log4j 1.2 reached end of life in August 2015. Users should upgrade to Log4j 2 as it addresses numerous other issues from the previous versions.
>
> "CVE-2021-4104". The MITRE Corporation. Retrieved December 22, 2021.

Log4j 1.2 の `JMSAppender` は、攻撃者が Log4j 構成への書き込みアクセス権を持っている場合、信頼できないデータの逆シリアル化に対して脆弱です。攻撃者は `TopicBindingName` および `TopicConnectionFactoryBindingName` 構成を提供して、`JMSAppender` に JNDI 要求を実行させ、CVE-2021-44228 と同様の方法でリモートコードが実行される可能性があります。

この脆弱性の影響があるかどうかは、Log4j で `JMSAppender` が構成されているかを確認してください。`JMSAppender` を使用しない構成では、この脆弱性の影響を受けません。`JMSAppender` を使用する構成では、構成の見直しが必要になります。尚、`JMSAppender` は、デフォルトでは構成されません。
NervesのIExをElixirカラーにしたい

[Nerves]のIExを[Elixir]っぽい色に変更してみました。

イメージ
![Screen Shot 2021-12-21 at 7.52.48 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82804/7b9222d2-0a61-ca40-2b4c-c703194d81a7.png)

[elixir]: https://elixir-lang.org/docs.html
[nerves]: https://hexdocs.pm/nerves/getting-started.html

## やりかた

- `rootfs_overlay/etc/iex.exs`ファイルで、オプションを指定して[`IEx.configure/1`]を呼ぶ。
- `inspect`の表示要素数（List等）の上限を上げておくと便利かも。

以下は一例です。

```elixir:rootfs_overlay/etc/iex.exs
IEx.configure(
  inspect: [limit: 5_000],
  history_size: 100,
  # 通常時の`iex>`をカスタマイズ
  default_prompt:
    [:light_magenta, "%prefix>"]
    |> IO.ANSI.format()
    |> IO.chardata_to_string(),
  # 分散ノード使用時の`iex>`をカスタマイズ
  alive_prompt:
    [:light_magenta, "%prefix(%node)>"]
    |> IO.ANSI.format()
    |> IO.chardata_to_string()
)
```

## 資料

- [Configuring the shell]
- [`IEx.configure/1`]
- [`Inspect.Opts`]
- [`IO.ANSI`]

```elixir
iex> h IEx.configure/1
```

おーはらさんのスライドに日本語で包括的に説明されています。Awesome!

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">本日の発表資料です<a href="https://t.co/IjC8wnIYIF">https://t.co/IjC8wnIYIF</a><a href="https://twitter.com/hashtag/tokyoex?src=hash&amp;ref_src=twsrc%5Etfw">#tokyoex</a></p>&mdash; おーはら (@ohrdev) <a href="https://twitter.com/ohrdev/status/1474968395087237121?ref_src=twsrc%5Etfw">December 26, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<!-- Links -->

[`.iex.exs`]: https://hexdocs.pm/iex/IEx.html#module-the-iex-exs-file
[`io.ansi`]: https://hexdocs.pm/elixir/IO.ANSI.html
[`iex.configure/1`]: https://hexdocs.pm/iex/IEx.html#configure/1
[`inspect.opts`]: https://hexdocs.pm/elixir/Inspect.Opts.html
[configuring the shell]: https://hexdocs.pm/iex/IEx.html#module-configuring-the-shell