OmniAICreator/Qiita-1.07M huggingface dataset:
Gitについて勉強する。第二弾 (Gitでよく使うコマンドにエイリアスを設定しよう 編)

# エイリアスって何？
いちいち git commit とか打つのがめんどくさいので、自分用のショートカットを設定することです。
単純にキーボード叩く量が少なくなるので、作業効率を上げることができます。

# エイリアスの設定の仕方(コミットの場合)
git --global alias.<自分の設定したいコマンド> commit

--global オプションは、PC全体でgitのエイリアスを設定することができます。
作業を楽にするのが目的なので、PC全体の設定にしておきます。

# 設定してみました(サンプル)
↓お手本にさせて頂きました
[Gitを使い始めたらやっておきたい便利な設定いろいろ - アシアルブログ](https://blog.asial.co.jp/845)

## コミット
$ git config --global alias.ci commit

## ステータス
$ git config --global alias.st status

## ブランチ
$ git config --global alias.br branch

## チェックアウト
$ git config --global alias.co checkout


# 実際にやってみた。
ちゃんと、「git st」でも「git status」でも同じ結果が出ているのが確認できますね！
<img width="678" alt="スクリーンショット 2019-09-26 23.08.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418332/5aba4555-bd50-9c0b-b5cc-b3dd51be8109.png">



# あとがき
git専用でもエイリアスの設定が可能なんですね。
私は、ふつうに.bash_profileで書き込むところでした。

ぶっちゃけgitって打つのも面倒だから 「alias git='g'」 みたいにエイリアス設定しようかな...

[次・第三弾](https://qiita.com/hajime_manul/items/f1b907c8408292fa072b)
CentOS 8にPython 2.7をインストール（AppStream）

# はじめに

Application Stream(AppStream)を利用してCentOS8にPython 2.7をインストール
親記事：そのうち作成予定
参考：[RHEL8のパッケージ構成 - BaseOSとApplication Stream - 赤帽エンジニアブログ]( https://rheb.hatenablog.com/entry/rhel8_baseos_and_appstream)

## サポート

本手法で導入した場合、[Red Hat Enterprise Linux 8 Application Streams Life Cycle - Red Hat Customer Portal](https://access.redhat.com/node/4079021)より、2024-06がEOLだと思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### インストール

```shell-session
# cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core)

# yum install -y @python27
... 略
```

### 各種確認

```shell-session
# which python2
/usr/bin/python2

# python2 -V
Python 2.7.15
```
vscode 言語、環境別おすすめ拡張機能リスト

# はじめに

言語や環境毎にvscodeのおすすめの拡張機能をまとめた。

https://github.com/kannkyo/vs-extensions

以下のコマンドにより、1つのリストからインストールできる。

```shell
cd lists
cat python.txt | xargs -L1 code --install-extension
```

以下のコマンドにより、複数リストから一括インストールできる。

```shell
cd lists/general
cat *.txt | xargs -L1 code --install-extension
```
Vue.js + Konva.js でテキストを囲んだブロックのカスタムエレメントを作成 + ドラッグ&ドロップ

自分が現在作成しているアプリの調査の一環で、Konva.jsのVue.jsのためのライブラリ [vue-konva](https://github.com/konvajs/vue-konva) について少し調べた。その中で、テキストを囲んだブロックのカスタムエレメントの作成法とそれをドラッグ&ドロップする方法がわかったので紹介。

#### カスタムエレメントの作成方法
単一ファイルコンポーネントによる書き方を紹介。[こちら](https://codesandbox.io/s/9j1zp)を参考にした。ちなみに、v-label要素に`draggable: true`を指定することで、ドラッグできるようにしている。

```vue:CustomBlock.vue

<template>
  <v-label :config="labelConfig">
    <v-tag :config="tagConfig" />
    <v-text :config="textConfig" :text-label="textLabel"/>
  </v-label>
</template>

<script>
  export default {
    name: "CustomBlock",

    data: function () {
      return {
        labelConfig: {
          x: 170,
          y: 75,
          opacity: 0.75,
          draggable: true
        },
        tagConfig: {
          stroke: '#555',
          strokeWidth: 5,
          fill: '#ddd',
          lineJoin: 'round',
          shadowColor: 'black',
          shadowBlur: 10,
          shadowOffset: 10,
          shadowOpacity: 0.5,
          cornerRadius: 10,
        },
        textConfig: {
          text: this.textLabel,
          fontSize: 20,
          fontWeight: 'bold',
          padding: 10,
          stroke: 'black',
          strokeWidth: 1,
        },
      }
    },

    props: {
      textLabel: {
        type: String,
        default: ''
      },
    },

  }
</script>
```


#### キャンバスとの組み合わせ方
先ほどのカスタムエレメントをKonvaのキャンバス上で使う方法は以下の通り。イベント設定など、ドラッグ&ドロップのより高度な設定方法は、[こちらのページ](https://konvajs.org/docs/vue/index.html)を参考にするとよい。

```vuejs:Konva.vue
<template>
  <v-stage
      id="canvas"
      ref="stage"
      :config="configKonva"
  >
    <v-layer ref="layer">
      <CustomBlock
          :text-label="text"
      />
    </v-layer>
  </v-stage>
</template>

<script>

  import Vue from 'vue';
  import VueKonva from 'vue-konva';
  import CustomBlock from './atoms/CustomBlock'

  Vue.use(VueKonva);

  const width = window.innerWidth;
  const height = window.innerHeight;

  export default {
    name: "Konva",
    components: {CustomBlock},
    data() {
      return {
        configKonva: {
          width: width,
          height: height
        },
        text: "TEST"
      };
    },
  };

</script>

<style scoped>
  #canvas {
    width: 800px;
    height: 800px;
  }
</style>

```

最終的な動作は下のようになる。
![konva_demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201540/50182efb-8b26-0483-bc0c-889a69d63dc7.gif)
TouchIDの失敗回数制限メモ

# TouchIDログイン失敗回数制限について

TouchIDの認証に失敗しても無限に試行できないかと調査。
が、TouchIDの失敗回数にはデフォルト制限がある。
3回連続失敗でプロンプトがいったん閉じ、5回連続でロックアウトする。
（おそらくロックアウト後は、パスコードなど別の認証方法で認証を通すまではロックされ続ける）
昔は```LAContext.maxBiometryFailures```というプロパティで調整できたっぽいが、deprecatedになったようなので、あんまりたくさん試行できるのは良くないよね、という思想のようです。



# 参考

- https://developer.apple.com/documentation/localauthentication/lacontext/1622330-maxbiometryfailures
(Ubuntu)Ruby on rails 6.0 環境構築

本記事について、問題点についてご指摘くださっている記事がございます。

もし検索エンジンからこちらの記事にたどり着いた方は
恐れ入りますが、以下の記事を参考いただければ幸いです。
[Ruby 歴 10 年の私が【絶対に】 gem install rails コマンドを実行しない理由](https://qiita.com/kaishuu0123/items/2a91495e7daa8c7783ed#_reference-676d3ce878ece19a21dc)

---

※以下、2019年9月26日時点作成の内容となります。

Ruby on Rails 6.0系のインストール手順をまとめました。
(2019年8月16日に安定稼働版がリリース)

# 環境
Ubuntu 18.04 LTS

# コンパイラおよび必要なツールをインストール

```bash
sudo apt update -y
sudo apt upgrade -y
sudo apt install build-essential -y
sudo apt install -y libssl-dev libreadline-dev zlib1g-dev
```

# Rubyをインストール

```bash
# rbenv（パッケージ管理ツール）をインストール
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
# 環境変数にPathを設定
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
# シェルを再起動
exec $SHELL -l

# ruby-buildをインストール
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

#Rubyをインストール
rbenv install 2.6.2
rbenv global 2.6.2
```

Rails 6を動かすには、Ruby 2.5以上が必要になります。


# Railsをインストール

Railsの全バージョンは[こちら](https://rubygems.org/gems/rails/versions)から確認できます。

※2020年7月21日更新
グローバルへのgem install railsは、推奨されないようです。以下、参照のこと。
[Ruby 歴 10 年の私が【絶対に】 gem install rails コマンドを実行しない理由](https://qiita.com/kaishuu0123/items/2a91495e7daa8c7783ed#_reference-676d3ce878ece19a21dc)

```bash
# railsをインストール
gem install rails -v "6.0.0"
# 動作確認
rails -v
```

# SQLiteをインストール

```bash
sudo apt install libsqlite3-dev
```

# Node.jsをインストール

[こちら](https://qiita.com/seibe/items/36cef7df85fe2cefa3ea)を参照しました。

```bash
# node.js, npmをインストール
sudo apt install -y nodejs npm
# n packageをインストール
sudo npm install n -g
# n packageを使ってnodeをインストール
sudo n stable
# 最初に入れた古いnode.js, npmを削除
sudo apt purge -y nodejs npm
# 再ログイン
exec $SHELL -l
# yarn packageをインストール
sudo npm install yarn -g
# node.js のバージョンが最新か確認
node -v
```

# Rails アプリケーションの作成

```bash
# 開発用フォルダ作成（任意）
mkdir dev
cd dev

# アプリケーションを作成
rails new [ AppName ]

# サーバーを立てる
rails s -b 0.0.0.0
```

サーバーが起動したら、表示されたURLにブラウザからアクセスすれば、ランディングページが表示される。

# 参考
[Rubyのインストール方法(Ubuntu編)](https://qiita.com/idaida_idaida/items/66db61b78c42a8521c3b)
[Ubuntuに最新のNode.jsを難なくインストールする](https://qiita.com/seibe/items/36cef7df85fe2cefa3ea)
Pythonでスタック

# 初めに
昔々のその昔(^^ゞ　
情報処理のお勉強をしていた時に理解していたのですが、意外と自分でスタックを扱うプログラムを組む機会はありませんでした。
現在ある個人プロジェクトを進めているのですが、その中でスタックを使う必要が出てきました。
ネタとしてちょうど良いとも思いますので、記事にしておこうと思います。

# スタックの概念
スタックは英語で stack と書き、日本語で「積む」という意味です。

例えばテーブルの上などに本を積み上げる事を想像して見てください。
その事自体、お行儀が良いとは言えませんが(^^ゞ
積み上げた本を崩さずにお行儀よく取ろうと思ったら上から順に1冊ずつ取り出すことになります。
この時、積んだ順番と逆の順番で取り出す事になります。
これをFILO（First In Last Out：＝先入れ後出し）あるいはLIFO（Last In First Out：＝後入れ先出し）と言います。
先に積んだ本が後から取り出される。あるいは後に積んだ本が先に取り出される様子を表しています。

スタックにデータを一つ格納する操作をPUSH（プッシュ）と言います。
PUSH操作を繰り返すことで、例えば本や、例えばデータが順に積み上げられていきます。
![push.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248779/9ee558b9-4049-5f66-4e5a-7843c703f683.gif)

PUSHとは反対に、スタックに格納された本やデータを一つ取り出す操作をPOP（ポップ）といいます。
POP操作を繰り返すことで、本やデータが順に取り除かれます。
![pop.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248779/6330d25b-bae1-6af1-db6b-7a8762f63aaf.gif)

例えばPUSHを５回行った後、POPを３回行い、さらにPUSHを1回行う。という事も出来ます。
この場合、最後にはスタックに３つの本やデータが積まれた状態になります。

# プログラム
ではここで Python を使用してスタックを取り扱うクラスを作成したいと思います。

```stack.py
class Stack:
    """Stack"""
    def __init__(self):
        self.Clear()

    def Clear(self):
        self.Stack = []

    def Push(self, dat):
        self.Stack.append(dat)
        return self.Stack

    def Pop(self):
        if len(self.Stack) == 0:
            return None
        return self.Stack.pop()

# test
stack = Stack()
stack.Push(1)
stack.Push(2)
stack.Push(3)
print(stack.Pop())
print(stack.Pop())
print(stack.Pop())

```
Python のリストを使用する事で、とても簡単に作成できました。

# 終わりに
と、簡単に解説してみました。
どこかで誰かのお役に立てれば幸いです(^^)/
WordPressのWP REST APIで画像をアップロード

あまりに情報がないので一応メモ。

#ポイント
- /wp-json/wp/v2/mediaにPOSTで投げる
- フォームの画像のキーは`file`
- Content-Typeは`multipart/form-data`
- 認証はJWTトークンでやる

https://ja.wordpress.org/plugins/jwt-authentication-for-wp-rest-api/

### CURL

```bash
curl -X POST \
  https://example.com/wp-json/wp/v2/media \
  -H 'Authorization: Bearer XXXXXXXXXXXX' \ # JWTトークン
  -H 'Content-Type: multipart/form-data' \ # バイナリでもできるけど今回はフォームで
  -H 'cache-control: no-cache' \ 
  -H 'content-disposition: attachment; filename=filename=name.jpeg' \ # nameは任意のファイル名に
  -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
  -F 'file=@/Users/user/Documents/name.jpeg' # ファイルのパス
```

### Python

```python
import http.client
conn = http.client.HTTPConnection("example,com")

payload = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"name.jpeg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--"

headers = {
    'content-type': "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
    'Content-Type': "multipart/form-data",
    'content-disposition': "attachment; filename=filename=name.jpeg",
    'Authorization': "Bearer XXXXXXXXXXX",
    'cache-control': "no-cache",
}

conn.request("POST", "wp-json,wp,v2,media", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
```

### Ruby on Rails
```ruby
require 'uri'
require 'net/http'

url = URI("https://example.com/wp-json/wp/v2/media")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Post.new(url)
request["content-type"] = 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
request["Content-Type"] = 'multipart/form-data'
request["content-disposition"] = 'attachment; filename=filename=name.jpeg'
request["Authorization"] = 'Bearer XXXXXXXXXXXX'
request["cache-control"] = 'no-cache'
request.body = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"name.jpeg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--"

response = http.request(request)
puts response.read_body
```
React RouterがHooks対応したので使い方を整理する

## 概要

- ReactRouterがv5.1でHooks対応しました
- 以前 [react-router + React Hooks](https://qiita.com/ozaki25/items/db58152f035b0b6ae781) という記事で [use-react-router](https://github.com/CharlesStover/use-react-router) の使い方を紹介しました
- ですが、ReactRouter本体がHooks対応したのでこれから作る場合はこの記事のやり方で実装していくことになります

## サンプルコード

- ReactRouterのHooksを使った簡単なサンプルです

```bash
mkdir react-router-with-hook
cd react-router-with-hook
yarn init -y
yarn add react react-dom react-router-dom
touch index.html index.js
```

```html:index.html
<div id="root"></div>
<script src="./index.js"></script>
```

```jsx:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {
  BrowserRouter as Router,
  Route,
  Switch,
  useParams,
  useHistory,
  useLocation,
} from 'react-router-dom';

function Hello() {
  const history = useHistory();
  return (
    <div>
      <h1>Hello</h1>
      <button
        onClick={() => history.push('/hello/react-router?message=hooks#test')}
      >
        Next
      </button>
    </div>
  );
}

function HelloSomeone() {
  const history = useHistory();
  const location = useLocation();
  const { name } = useParams();
  return (
    <div>
      <h1>Hello {name} !</h1>
      <p>pathname: {location.pathname}</p>
      <p>search: {location.search}</p>
      <p>hash: {location.hash}</p>
      <button onClick={() => history.goBack()}>Go Back</button>
    </div>
  );
}

function App() {
  return (
    <Router>
      <Switch>
        <Route path="/" exact>
          <Hello />
        </Route>
        <Route path="/hello/:name" exact>
          <HelloSomeone />
        </Route>
      </Switch>
    </Router>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
```

- 以下のコマンドで起動します

```bash
npx parcel-bundler ./index.html
```

![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/5f978710-ab55-47a2-5203-76140c039ef1.gif)


## コードの説明

### useHistory

- ページ遷移させるときに使う`history`を取得できます
    - `const history = useHistory()`
    - `history.push('/')`や`history.goBack()`といった具合で使います

### useLocation

- 現在のページのURLのpathやqueryなどの情報を取得できます
    - `const location = useLocation()`
    - `location.path`や`location.search`といった具合で使います
 
### useParams

- URLのパスの中で動的に変化する部分の値を取得できます
    - `<Route path="/hello/:name">`と定義したページにアクセスした際に
    - `const { name } = useParams();`とすると`:name`の部分の値を取得できます

## まとめ

- Hooksで書けると昔のHOCを使った書き方より見通しがよいですね
- 一時的に使っていた`use-react-router`と若干仕様は違いますが違和感なく使えそうです
Windows で File.expand_path("~") はどうなる


# 問いと短い答え

Linux や macOS では，ファイルパスの先頭の `~` は現在のユーザーのホームディレクトリーを指す。
けれど Windows はそうではない。これがしばしば，複数の OS で同じように動くべき何かを作るときに問題になる。

さて，Linux や macOS で，Ruby の

```rb
File.expand_path("~")
```

という式は，現在のユーザーのホームディレクトリーの**絶対パス**を返してくれる。

Windows でどうなるのだろう？

結論を先に書けば，Windows でも同じである。例えば以下のようになる。

```rb
p File.expand_path("~") # => "C:/Users/n-takemoto"
```

# 公式リファレンスマニュアルを見る

公式リファレンスマニュアルの [File.expand_path](https://docs.ruby-lang.org/ja/2.6.0/method/File/s/expand_path.html) を見ると，引数に与えた文字列の先頭の `~` は `ENV["HOME"]` を参照することになっている。

なるほど，そういうことであったか。
環境変数 `HOME` が適切に設定されている環境であれば，OS によらずに期待通りの動作となるわけだ。

だから，

```rb
File.expand_path("~/.ssh/id_rsa")
```

なんかも，期待どおり，ホームディレクトリーの下の `.ssh/id_rsa` の絶対パスを返してくれる。

# ~n-takemoto はどうなのよ

件のリファレンスマニュアルには気になる記述があった。
`n-takemoto ` というユーザーがいたとすると，`~n-takemoto` はこのユーザーのホームディレクトリーの絶対パスに展開される，というのだ。へ〜

macOS と CentOS で試すと確かにそうなった。
しかし，Windows 10 では

```
ArgumentError (can't find user n-takemoto)
```

のような例外が発生した。原因は判らない。

# Pathname#expand_path は？

[Pathname#expand_path](https://docs.ruby-lang.org/ja/2.6.0/method/Pathname/i/expand_path.html) は内部で File.expand_path を使っているので，当然，同じような結果になる。

```rb
require "pathname"
Pathname("~").expand_path # => #<Pathname:C:/Users/n-takemoto>
```

のように。

# （オマケ）n-takemoto って誰なのよ

野暮な質問は無し♡
正規表現について

subメソッドとsubメソッド

文字列の指定した部分を別の文字列に置き換える
subは最初の一文字
gsubは指定した全て

```ruby:qiita.rb
tel = '090-1234-5678'
tel.sub(/-/,'')
=>"0901234-5678"

tel.gsub(/-/,'')
=>09012345678
```

| メタ文字列 | 意味 |
|:--|:--|
| * | 0回以上 |
| + | 1回以上 |
| ? | 0回か1回 |
| {n} | n回 |
| {n,} | n回以上 |
| {,n} | n回以下 |
| {n,m} | n回以上m回以下 |
CentOS 8にperl 5.24をインストール（AppStream）

# はじめに

CentOS8にOS標準のperlをインストール
親記事：そのうち作成予定
参考：[RHEL8のパッケージ構成 - BaseOSとApplication Stream - 赤帽エンジニアブログ]( https://rheb.hatenablog.com/entry/rhel8_baseos_and_appstream)
　　　[Modular Perl in Red Hat Enterprise Linux 8 - Red Hat Developer] (https://developers.redhat.com/blog/2019/05/16/modular-perl-in-red-hat-enterprise-linux-8/)

## サポート

本手法で導入した場合、[Red Hat Enterprise Linux 8 Application Streams Life Cycle - Red Hat Customer Portal](https://access.redhat.com/node/4079021)より、2021-05がEOLだと思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## note

- なんだかすごい量の処理が走るので、perlに依存しているシステムへの影響が怖い

### インストール

```shell-session
# cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core)

# yum module enable perl:5.24
... 略

# yum --allowerasing distrosync
... 略

# yum module install -y perl:5.24
... 略
```

または

```shell-session
# cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core)

# yum install -y perl
... 略

# yum module enable perl:5.24
... 略

# yum --allowerasing distrosync
... 略
```


### 各種確認

```shell-session
# which perl
/usr/bin/perl

# perl -V:version
version='5.24.4';
```

### 通常に戻す

```shell-session
# yum module reset -y perl
... 略

# yum --allowerasing -y distrosync
... 略

# perl -V:version
version='5.26.3';
```

### アンインストール

```shell-session
# yum remove -y perl
... 略
```
【Discord】小技集【Python】

随時追加していきます。




## 新しいメンバーがジョインしたのをトリガーになんかしたい

```python
@client.event
async def on_member_join(member):
```

## user_idから名前を引っ張りたい


```python
message.guild.get_member(u).name
```

 ※v1以前の場合

```python
message.server.get_member(u).name
```
Dapper+SQLiteで自動型変換のエラーの対処法をgithubに投稿しました。

dotnet core 2.1でコンソールアプリケーションの勉強中に出くわしたので、せっかくなので公開しときます。
Dapper+SQLiteで快適に使っていたのですが、なぜかdouble?型への型変換が特定の条件で失敗してしまいます。恐らく他の型でもありえるエラーでしょう。
具体的には以下のようなエラーが発生します。double?型を使ってほしいのですが、勝手にInt32型と判断しているのですね。

> Unhandled Exception: System.Data.DataException: Error parsing column 1 (Column2=36 - Double) ---> System.InvalidCastException: Unable to cast object of type 'System.Double' to type 'System.Int32'.

SQLによっては発生しないらしく、order by の昇順降順を変更するだけでエラーが発生しなくなったりします。ややこしいエラーです。

対処法は SqlMapper の型別のハンドラを追加してあげる事です。以下の行をコメント解除する事でエラーが発生しなくなります。
`SqlMapper.AddTypeHandler(typeof(System.Double),new DoubleConverter());`

以下のページでも話題になっていました。
https://qrunch.net/@maccyo/entries/toOHtxzaj3Dy4QEp
https://github.com/StackExchange/Dapper/issues/642#issuecomment-435277926

1つ目のURLに解決方法が記載されているのですが、スペルの誤りがありましたので、せっかくですからちゃんと動く物をgithubに追加しておきました。
https://github.com/currysita/DapperCastErrorSample

ソース全文を以下に記載しときます。

```Program.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using Dapper;
using Microsoft.Data.Sqlite;
using Models.Dapper.Mapper;

namespace dapperSample
{
    class HogeEntity {
        public double? Column1 {get;set;}
        public double? Column2 {get;set;}
    }
    class Program
    {
        static void Main(string[] args)
        {
            // SqlMapper.AddTypeHandler(typeof(System.Double),new DoubleConverter());
            var sqlConnectionSb = new SqliteConnectionStringBuilder { DataSource = "sqlitedb.sqlite3" };
            using(var con = new SqliteConnection(sqlConnectionSb.ToString())){
                try{
                    con.Execute("drop table Hoge;");
                }catch{}
                var createSql = $@"create table if not exists Hoge (
                    column1 REAL,
                    column2 REAL
                    )";
                con.Execute(createSql);
                var insertSql = "insert into Hoge (column1,column2 ) values (1,36)";
                con.Execute(insertSql);
                insertSql = "insert into Hoge (column1,column2 ) values (2,NULL)";
                con.Execute(insertSql);
                insertSql = "insert into Hoge (column1,column2 ) values (3,36)";
                con.Execute(insertSql);
                var list = con.Query<HogeEntity>(
                    "select column1 Column1 ,column2 Column2 from Hoge where column1 in @Column1 order by column2 IS NULL DESC"
                    ,new {Column1=new List<int>{1,2,3}});
                foreach(var hoge in list){
                    Console.WriteLine(hoge.Column1);
                    Console.WriteLine(hoge.Column2);
                }
            }
        }
    }
}

namespace Models.Dapper.Mapper{  
    public class DoubleConverter : SqlMapper.TypeHandler<double>{  
        public override Double Parse (Object value)  
        {  
            return Convert.ToDouble(value);  
        }  
        public override void SetValue(IDbDataParameter parameter, double value){  
            parameter.Value = value;  
        }  
    }
}
```
PowerShellで指定フォルダ以下にあるファイルの一覧(長いパス対応)を得る

# 概要

ファイルサーバ内のファイルをうち、特定の物を処理するスクリプトをPowerShellで作ろうと、`Get-ChildItem *** -Recurse`したところ、階層が深くパスが260文字以上となるファイルがたくさんあることがわかった。

解決法は何通りかあるものの、`robocopy`を使うのが簡単そうだったので、長いファイル名のフルパスを得る関数を作ってみた。
ついでに、長いパス名のファイルが無い場合でも`Get-ChildItem`よりも2倍程度高速。

# つくったもの

~~~Powershell
function Get-ChildFilePath {
  [CmdletBinding()]
  [OutputType([String[]])]
  param (
    [Parameter(Mandatory,ValueFromPipeline)]
    [string]$Path
  )

  BEGIN {
    $notexist = 'C:\DOESNOTEXIST';
    while (Test-Path $notexist){
      $notexist += Get-Random $(48..57+97..122) | % {[char]$_}
    }
  }

  PROCESS {
    if( Test-Path $Path ){
      $list = robocopy.exe $Path $notexist /L /E /B /NP /FP /NS /NC /NJH /NJS /NDL |
        ForEach-Object { $_.Trim() } | Where-Object { $_.Length -gt 0 }
      @($list)
    } else {
      @()
    }
  }
}
~~~

※ PowerShellは管理者権限が必要

## 説明

`robocopy`で使っているオプションの説明は下記(`robocopy /?`から抜粋)。

~~~
/L :: リストのみ - いずれのファイルにも、コピー、タイムスタンプの追加、または削除を実施しません。
/E :: 空のディレクトリを含むサブディレクトリをコピーします。
/B :: バックアップ モードでファイルをコピーします。
/NP :: 進行状況なし - コピーの完了率を表示しません。
/FP :: 出力にファイルの完全なパス名を含めます。
/NS :: サイズなし - ファイル サイズをログに記録しません。
/NC :: クラスなし - ファイル クラスをログに記録しません。
/NDL :: ディレクトリなし - ディレクトリ名をログに記録しません。
/NJH :: ジョブ ヘッダーがありません。
/NJS :: ジョブ要約がありません。
~~~

以上のオプションとコピー先として存在しないフォルダを指定して`robocopy.exe`を呼び出すと、コピー元に指定したフォルダ以下にあるファイルの、フルパスのリストが出力されるので、無駄な空白を除いたものを出力する。

また結果が0行だったり1行だったりしたときに返り値の型が変わらないよう、`@()`で囲んで必ず配列が返るようにした。

## 使い方

単純に指定したフォルダ以下の文字列のリストを得る。

~~~Powershell
Get-ChildFilePath "C:\Users\hogehoge\Documents"
~~~

`Get-ChildItem`と同様に`System.IO.FileSystemInfo`を得るには、`Get-Item`に渡せばOK。

~~~Powershell
Get-ChildFilePath "C:\Users\hogehoge\Documents" | Get-Item
~~~

ファイル名でフィルタをかける場合は、先に文字列を条件に絞ってからGet-Itemをしたほうが高速(なので敢えて`Get-ChildItem`とは互換のないようにした)。

~~~Powershell
Get-ChildFilePath "C:\Users\hogehoge\Documents" | ? { $_ -like "*.png" } | Get-Item
~~~
【備忘録】Flowで、メールが可能なセキュリティグループ、Office365グループにメンバーを追加するアクション

Formsに書いてもらったら、〇〇する、というFlowは色々使えて便利。SharePointのカスタムリストに追加する、Teamsに投稿する、グループに追加する、なんてこともできる。

以前投稿した、[ライセンスを自動で割り当てる](https://qiita.com/hisssa0102/items/50eb2a7450c061ecd231)、ではメールが有効なセキュリティグループに追加する方法で書いた。

先日,Office365グループに追加する際に、ちょっと躓いたので、まとめてみた。

#基本的な流れ

Formsに書いたら、Flowでグループに追加する処理を作る。
[ Office365 - ユーザーの検索 ]で、ユーザーを検索する処理までは同じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/994fb050-615b-745d-6575-56cd1ef5071c.png)


#メールが有効なセキュリティグループに、追加する

まず、オブジェクトIDを先にメモしておく。Azure Active Directry で調べる。
ただ、AzureADからでないと表示できない。AzureADを開けない場合は、あきらめてシステム管理者に聞きましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/237698/654ce809-ef0d-04b1-cbc1-c483b60370a0.png)

アクション [ Azure AD -　ユーザーをグループに追加 ]を選ぶ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/850952bc-4c6d-5a39-56e9-72af4785fb7a.png)

グループIDに先ほどのオブジェクトIDを設定すればOK。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/56259fde-c6c6-393b-5b24-55313a97fa0b.png)

#Office365グループに追加する

こっちの場合は、オブジェクトIDは不要。

アクション [Office 365 グループ - グループへのメンバーの追加]を選ぶ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/fa0eea5f-411f-79a9-2273-a16b4e4df7e8.png)

グループIDと、ユーザープリンシパルネームを設定すればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/42cd4c93-1bc7-7916-a769-00d76dc6c2ac.png)


#まとめ
Teamsつくった場合は、Office365グループになる。オブジェクトIDもいらない。管理者にオブジェクトID教えてもらわなくていいから気軽に試せる。

ただ、どうしてもメールが可能なセキュリティグループを使いたい場合は、管理者に聞いたうえで作りましょう。
macOS Mojave (APFS) でハードリンクする（っぽいやつ）

macOS Mojave 以前のファイルシステム HFS+ ではディレクトリのハードリンクできたんだけど、Mojave で強制的に APFS に変換されちゃってできなくなった。

[bindfs](https://bindfs.org/) を使うとあるディレクトリを別のディレクトリにマッピングできるので、ハードリンクっぽいことができる。

https://stackoverflow.com/a/54121082

FUSE をつかった仕組みなので不要になったら `umount` コマンドで解除。

（Dropbox で Personal アカウントと Business アカウントの両方をつかってると、ディレクトリ名にスペースが含まれちゃって Bazel がエラー吐いちゃうやつの対策🤣）
CentOS 8にperl 5.26をインストール（OS標準）

# はじめに

CentOS8にOS標準のperlをインストール
親記事：そのうち作成予定
参考：[RHEL8のパッケージ構成 - BaseOSとApplication Stream - 赤帽エンジニアブログ]( https://rheb.hatenablog.com/entry/rhel8_baseos_and_appstream)

## LOG

### インストール

```shell-session
# cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core)

# yum install -y perl
... 略
```

### 各種確認

```shell-session
# which perl
/usr/bin/perl

# perl -V:version
version='5.26.3';
```
Bracketsでライブプレビューが動作しない

##この記事の要旨

Bracketsを使っていて、**ライブプレビューが動作しない**という事案が幾度となく発生しました。
色々調べて解決したので、同じことでつまずく人がいないように記事にしておきます。（こんな初歩的な事でつまずくのは私だけだ、なんて話は気にしない方向で）

記事に誤りがあれば、コメント下さい。


##ライブプレビューが動作しない原因と解決策

###原因1

Chromeとの連携がうまくいっていない

###解決策

Bracketsをctrl + F5,F5で読み込みなおす。

その後もう一度ライブプレビューを起動

参考記事↓
https://qiita.com/ko3n/items/71382c3f6418d9702ffd


###原因2

シンタックスエラーが発生している

<details><summary>シンタックスエラーとは</summary><div>

文法的な間違いです。下記のような例が挙げられます。

```html:example.html
<!-- 正 -->
<a href=“http://example.com#1”>URL</a>

<!-- 誤 -->
<a href=“http://example.com”#1>URL</a>

<!-- ""の中に#1が入ってないのが誤り -->
```


他にも、誤字のせいでシンタックスエラーになることもあります。
大体は何てことないミスなので、かえって見つけづらいです。</div></details>



###解決策

シンタックスエラーを探してなくす。

＊ただし、探すのは大変です。後述の拡張機能を入れることをお勧めします。

##シンタックスエラーに対する予防策

拡張機能　：　Interactive Linter　を導入する

この拡張機能はシンタックスエラーを見つけてくれます。

導入方法はこちらから(ほかにも便利な拡張機能が紹介されています)↓

[無料エディター『Brackets』を拡張機能でさらに使いやすく](https://www.doe.co.jp/hp-tips/software/%E7%84%A1%E6%96%99%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%E3%83%BC%E3%80%8Ebrackets%E3%80%8F%E3%82%92%E6%8B%A1%E5%BC%B5%E6%A9%9F%E8%83%BD%E3%81%A7%E3%81%95%E3%82%89%E3%81%AB%E4%BD%BF%E3%81%84%E3%82%84/)
R : ggplot2で欠損データを含むグラフで欠損値も欠損していることを明らかにしつつ描画する方法

#　こまったこと

```{r}
library(tidyverse)

dat <- tibble(
  bunrui1 = c("A","A","A","B","B","C","C"),
  bunrui2 = c("x","y","z","x","y","x","z"),
  value   = c( 10, 20, 30, 15, 34, 21, 33)
)
```

このようなデータをggplot2でグラフを描画した際に、

```{r}
ggplot(dat) + 
  geom_col(aes(x = bunrui1, y = value, fill = bunrui2))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143095/36abc78b-1ee5-0889-ad28-2b57f396f531.png)

と描画される。
x, y, zがそれぞれ一部欠損しているケースでそれが「ない」ことも含めて描画したい場合には、
自分の感覚的には、、


```{r}
dat <- dat %>% 
  mutate(bunrui2 = as.factor(bunrui2))

ggplot(dat) + 
  geom_col(aes(x = bunrui1, y = value, fill = bunrui2), position="dodge") +
  scale_fill_discrete(drop = FALSE)
```

`bunrui2`を因子型等に変えたうえで、`scale_fill_discrete`で`drop=FALSE`等と設定すれば、うまく行きそうであるが、実は、うまく行かない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143095/90304fe1-e20f-980c-c202-ea4219c75961.png)

おんなじ・・・

ここでdrop設定を色々といじってみたが、詰まったので発想を変えてみたらうまく行った。

## 解決法

こういうケースでは`tidyr::complete`関数を利用すると簡単に対応できる。

```{r}
library(tidyverse)

dat <- tibble(
  bunrui1 = c("A","A","A","B","B","C","C"),
  bunrui2 = c("x","y","z","x","y","x","z"),
  value   = c( 10, 20, 30, 15, 34, 21, 33)
)

dat <- dat %>% 
  tidyr::complete(bunrui1, bunrui2, fill = list(value = 0))

```

と、`tidyr::complete`で欠損しているグループの数値を強制的に0としてあげることで、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143095/42e308fd-43e4-e312-6b2d-8c36f53a6780.png)

こんな感じでうまい具合に埋めてくれるので、あとは普通にggplot2で描画すれば

```{r}
ggplot(dat) + 
  geom_col(aes(x = bunrui1, y = value, fill = bunrui2), position="dodge")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143095/3820e4af-d3f3-7c11-d2c6-54bee29c6647.png)

できあがり。
HTTP／HTTPS通信

初学者ですが個人的に復習、理解を深めるためにまとめてみました。
#HTTP／HTTPS通信の基礎

###HTTPとは

*HyperText Transfer Protocol*の略称。

**HyperText**は文書内の任意の位置や要素に、他の文書への参照（所在情報や識別情報）を埋め込み、複数の文書を相互に結びつけるシステムのこと。

**Protocol**はコンピューター同士が通信をする際の手順や規約などの約束事のこと言い、Webページを閲覧するためには、WebブラウザとWebサーバーとがやり取りするためのHTTPというプロトコルが使われる。

####Webサーバの構成

今日インターネットを利用でWebを閲覧するとき、ユーザーの手元には**Webブラウザ**と呼ばれるクライアント用ソフトウェアと、閲覧される情報であるWebページをを保管している**Webサーバ**が必要になる。
WebブラウザがWebサーバに要求する情報を**HTTPリクエスト**、またWebサーバがこれに応答し返信する情報を**HTTPレスポンス**といい、HTTPを用いて一つのコンテンツを表示をするには、このリクエストとレスポンスのやり取りが行われる。


####HTTPの種類とHTTPメソッド
ブラウザから送信されるのはコンテンツを取得するためのリクエストだけではなく、入力フォームにて入力されたデータやアップロードされたファイルなどの送信データも含む。
リクエストの用途にに応じてサーバに要求する処理内容も異なるので、HTTP通信では**HTTPメソッド**によって、リクエストの種類を判別する。
一般的に使われるHTTPメソッドとしては、リクエストしたURLのリソースを取得する**GETメソッド**、リクエストしたURLに対して入力データを送信する**POSTメソッド**がある。

####GETメソッドとPOSTメソッドの違い

見た目的な違いはURLである。

GETメソッドは渡す値をURLの後ろにくっつける。
(例)```https://www.google.co.jp/?param1=value1&param2=value2```

POSTメソッドは渡す値を人間の目には見えないところにくっつける。
(例)```https://www.google.co.jp/```

では、GETメソッドとPOSTメソッドどちらを使うほうがいいのかというとPOSTメソッドのほうがいい場合が多い。

理由としては、

1.**GETメソッドはPOSTメソッドと比べて渡せる情報量が少ない**
2.**GETメソッドは渡した値が履歴に残る**

と以下のことから、どちらを使うか迷ったときはPOSTメソッドを使うほうが良い。

###HTTPSとは

HTTPSもHTTPと同じプロトコルの一つだが、HTTPSでは**SSL（Secure Socket Layer）**というプロトコルが使用されている。
SSLは暗号化して送受信するプロトコルであり、HTTPとHTTPSの主な違いはSSLによる通信内容が暗号化されていないか、されているかの違いである。
厳密に言うと現在はSSLではなく**TLS（Transport Layer Security）**というプロトコルが使われているが、SSLという言葉が長い間使われてきたため、現在でもSSLと呼んだり、あるいは**SSL/TLS**と呼ばれたりする状況になっている。

また、Google ChromeではHTTPSに対応したホームページを開くとブラウザのURLの左の方に**「保護された通信」**、あるいは**「組織名」**が表示される。

![IMG_20190926_214832.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447762/850a6d62-fba8-1955-a274-1c3d155edb2c.png)
![IMG_20190926_214816.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447762/3ad2d847-04be-b1fa-4a05-a56396eb6984.png)

「保護された通信」、「組織名」ともに通信は暗号化されているが、組織名が表示されている場合は、暗号化されて通信しているだけでなく、ドメインの所有者が実在する組織であることを表し、サーバに設定されている**証明書の種類が異なる**。
この場合の証明書の種類は、通信先のサーバが正当な所有者のものであることを保証する「証明書（SSLサーバ証明書）」となり、**ホームページの所有者の正当性を確認することができる。**

つまり、

1.**通信の暗号化**
2.**サーバの所有者の正当性**

この二つがHTTPとの違いであり、HTTPSの重要な機能である。

近年はHTTPよりもセキュリティが優れていることから、HTTPSがスタンダードになってきている。

###参考文献
http://e-words.jp/w/SSL.html
https://cybersecurity-jp.com/security-measures/25772
https://www.724685.com/word/wd140305.htm
https://wa3.i-3-i.info/diff7method.html
https://kotobank.jp/word/プロトコル-8535
http://e-words.jp/w/ハイパーテキスト.html
http://e-words.jp/w/HTTP.html
河野吉男、原田勉、深澤博、丸山智久、後藤悠「Webアプリケーションサービス構築」エスシーシー (2013/3/1)
東浩二、山田祥寛「初めてのJSP＆サーブレット」 秀和システム (2016/10/1)
ディレクトリ配下に連番ファイルを作成するバッチファイル

#概要
各ディレクトリ配下に一ヶ月分の日付が入ったファイル名を用意できます。
開発時にファイルの読み込みテストに使用できるかもしれません。
#コード

```bash:create_file.bat
@echo off
setlocal enabledelayedexpansion
chcp 65001

for /f "usebackq delims=" %%i in (`dir /B /S "ファイルを作りたいルートディレクトリ"`) do (
for /l %%k in (1,1,30) do (
set num=0%%k
echo "%%i\\%date:~0,4%%date:~5,2%!num:~-2!.txt"
echo ファイルに書き出したい文言 > "%%i\\%date:~0,4%%date:~5,2%!num:~-2!.txt"
)
)
CentOS 8にOpenJDK 11をインストール（AppStream）

# はじめに

Application Stream(AppStream)を利用してCentOS8にOpenJDK11をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[RHEL8のパッケージ構成 - BaseOSとApplication Stream - 赤帽エンジニアブログ]( https://rheb.hatenablog.com/entry/rhel8_baseos_and_appstream)

## サポート

本手法で導入した場合、[Red Hat Enterprise Linux 8 Application Streams Life Cycle - Red Hat Customer Portal](https://access.redhat.com/node/4079021)より、2024-10がEOLだと思われる。
[OpenJDK ライフサイクルおよびサポートポリシー - Red Hat Customer Portal] (https://access.redhat.com/ja/articles/1457743)の内容とも合致する期日になっている。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

ただし、CentOS Linux8のEOLが大きく前倒しされたので、CentOS Linux 8にこの手法でインストールした場合は2021-12-31以降は更新されなくなる。

## note

- openjdkはmoduleではなく、今までのCentOSと同じようにバージョン+パッケージ名で別れているだけの模様

## LOG

### インストール

```shell-session
# cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core)

# yum install -y java-11-openjdk
... 略
```

### 各種確認

```shell-session
# which java
/usr/bin/java

# java -version
openjdk version "11.0.4" 2019-07-16 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.4+11-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.4+11-LTS, mixed mode, sharing)
```
CentOS 8にOpenJDK 8(1.8)をインストール（AppStream）

# はじめに

Application Stream(AppStream)を利用してCentOS8にOpenJDK8(1.8)をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[RHEL8のパッケージ構成 - BaseOSとApplication Stream - 赤帽エンジニアブログ]( https://rheb.hatenablog.com/entry/rhel8_baseos_and_appstream)

## サポート

本手法で導入した場合、[Red Hat Enterprise Linux 8 Application Streams Life Cycle - Red Hat Customer Portal](https://access.redhat.com/node/4079021)より、2026-05がEOLだと思われる。
[OpenJDK ライフサイクルおよびサポートポリシー - Red Hat Customer Portal] (https://access.redhat.com/ja/articles/1457743)の内容とも合致する期日になっている。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

ただし、CentOS Linux8のEOLが大きく前倒しされたので、CentOS Linux 8にこの手法でインストールした場合は2021-12-31以降は更新されなくなる。

## note

- openjdkはmoduleではなく、今までのCentOSと同じようにバージョン+パッケージ名で別れているだけの模様

## LOG

### インストール

```shell-session
# cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core)

# yum install -y java-1.8.0-openjdk
... 略
```

### 各種確認

```shell-session
# which java
/usr/bin/java

# java -version
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-b10)
OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode)
```
BitbucketのwebhookをGASで取得してチャットツールに投稿する


## 概要
SlackだとBitbucketのあれこれを通知してくれる便利な拡張機能があるそうですが、
Twistにはそういった拡張機能が無いようなので自作してみました。

webhookの受け口はサーバーレスで費用のかからないGASで実装することに。
このようなイメージです。
```Bitbucket -> webhook -> GAS -> twistAPI```

※ Bitbucketのwebhook取得について、LIGさんのこちらの記事を参考にさせていただきました。
[Bitbucketにpushしたらチャットワークに通知できるようにしてみよう](https://liginc.co.jp/341162)

## GAS (Google Apps Script)
まずは受け口のGASを構築。
doPost()という名前で関数を作っておくことでPOSTを受け付けた際に実行されるようです。
せっかくGASで実装しているのでスプレッドシートと連携して簡易的なログ吐き出しもしてみました。

~~~js
function doPost(e) {
  let post_data       = JSON.parse(e.postData.contents);
  let push            = post_data.push.changes[0];
  let repository_name = post_data.repository.name;
  let branch_name     = push['new']['name'];
  let user            = push['new']['target']['author']['user']['display_name'];
  let url             = push['new']['target']['links']['html']['href'];
  let message         = push['new']['target']['message'];

  // 取得した内容を元に各種WEBサービスへ送信
  // 今回はtwistのスレッドに投稿
  let content = url + "\n" + user + " " + message.replace("\n\n","\n");
  addCommentToTwist(9999,content);

  // シートにログに吐き出し
  let spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  let sheet       = spreadsheet.getActiveSheet();
  let debug_flg   = sheet.getRange(1,2).getValue();
  if(debug_flg == 1){
      sheet.appendRow([repository_name,branch_name,post_data,push]);
  }
}
~~~

~~~js
// twistに投稿する関数
function addCommentToTwist(thread_id,content) {
  let url = "https://api.twist.com/api/v3/comments/add";
  let apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
  let idAdmin = true;
  let recipients = [];
  
  let headers = {
    "Authorization": apiKey,
    "Accept": "application/json",
    "Content-type": "application/json"
  };
  
  let payload = {
    "thread_id":thread_id,
    "send_as_integration":idAdmin,
    "recipients":recipients,
    "content":content
  };
  
  let options = {
    "method": "post",
    "payload": JSON.stringify(payload),
    "headers": headers
  };  
  
  let response = UrlFetchApp.fetch(url, options);
}
~~~

コードを書いたら以下の手順でURLを取得します。
```公開 -> ウェブアプリケーションとして導入 -> 更新 ```
![gas1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503672/673bd2ea-c76a-c207-292a-81ccd03f6e1d.png)
プロジェクトバージョンはNewを選択しないとコードの変更が反映されません。
GASの地味に勝手が悪い仕様なので注意。
![gas2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503672/3319a65d-9fab-b0fb-3c4a-bffff23f954a.png)

## Bitbucket

続いてBitbucketのWebhooksに先ほど取得したGASのURLを登録します。
```設定 -> Webhooks -> Add webhook```
![bitbucket.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503672/90ab3334-a515-da02-6304-35754218cc6d.png)

## 結果
以上で準備が完了です。
実際にコミットしてみると・・・

無事、twistに投稿できました！
![twist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503672/12241646-d596-7a23-44e1-915db1ae1210.png)
デバッグモードもきちんと動いています。
シート内でフラグのON,OFFができるので、ビルド（Newバージョンで公開）の必要がありません。
![debug_mode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/503672/084b88cc-9a53-4e36-d755-4d78ac8e01d1.png)

## 所感
リポジトリやブランチ名が取得できたので、投稿するスレッドを切り分けもできそう。
ハードコーディングではなくて、スプレッドシートでマッピングを設定できたら便利ですね。

ちょっとした小物を作るのにGASはとっても便利。今後も色々勉強していきたいと思います。
[rails]formに簡単なvalidationをつける(データ送信前)

#この記事で説明すること
- 利用規約にチェックしない場合、このような表示が表れるようにしたい
- modelでのかっちりとしたvalidationをつけたくないとき
<img width="691" alt="DoFes.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384804/8822322a-a19b-c82b-a632-c8071f9f515e.png">

#入力方法
今回はチェックボックスを例にしています(他のform等でも可)
`required: true`を記述すると可能になります。

```
= f.check_box :agreememt, {required: true, checked: false, id:'agree',
 :type=> 'checkbox'} , "true" , "false"
label[for="agree"]
```



`:agreement`については
[こちらの記事](https://qiita.com/rkonno/items/b6c77a2e994c6e0e86df)
を参考にさせていただきました。

`checked: false`はチェックボックスの初期値を指定しています。
**false**でチェックなし
**true**ならチェックされた状態になります。

#参考
[新規ユーザ登録フォームの同意チェックボックス（バリデーション）を実装する](https://qiita.com/rkonno/items/b6c77a2e994c6e0e86df)
#Rails の #rspec で外部接続するテストを禁止するには webmock/rspec が良さげ？

```rb
# spec/spec_helper.rb
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
```

https://github.com/bblimke/webmock

[How to Stub External Services in Tests](https://thoughtbot.com/blog/how-to-stub-external-services-in-tests#disable-all-remote-connections)

外部接続した時に例外を出すこともできるし、スタブすることもできるように見える。

![image](https://user-images.githubusercontent.com/13635059/65679228-9f5b0b80-e08f-11e9-852a-4bf0f86ff1e0.png)


# Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2532








<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->
AWSのEC2 オンデマンドインスタンス制限仕様変更（インスタンス数→vCPU数）

しれっとアナウンスされている気がしますが、オンデマンドインスタンス制限の使用が変わるようです。

>現在の数量ベースの制限から、新たに vCPU ベースの制限に移行

## 対象
EC2オンデマンドインスタンス
※リザーブドインスタンス、スポットインスタンスは対象外

## 対象リージョン
AWS中国(北京と寧夏)リージョンを除くすべての商用AWSリージョンで利用

## スケジュール
2019年09月24日〜 vCPUベース制限を設定可能
2019年10月24日〜 インスタンス制限をvCPUに移行
2019年11月08日〜 現在の数量ベースのインスタンス制限は利用できなくなるか、サポートされなくなる。

## ポイント
・オンデマンドインスタンス制限数がインスタンス数から、vCPU数になる。
・vCPU制限をオプトインしても実行中のインスタンスには影響なし。
・引き続き同じ数のインスタンスを作成可能。
・設定変更等をせずに放置すると、2019年10月24日以降は自動的にvCPUベース制限に以降。
・引き続き数量ベースのインスタンス制限は使用出来ない。

## vCPUベース制限切替方法
[EC2ダッシュボード]-[制限]で、案内が表示されています。(2019/9/27 0:15現在)
<img width="1410" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254711/e65a5938-93f5-91ec-1a69-eb7637d0173d.png">

```
EC2オンデマンドインスタンスの制限を設定する方法を再設計しています

新しいvCPUベースのインスタンス制限には、オプトイン期間があります。
オプトイン期間中に、vCPUベースまたはカウントベースのインスタンス制限を切り替えることができます。 
vCPUベースのインスタンス制限を試して、どこで改善できるかをお知らせください。

締切日以降、オンデマンドインスタンスの制限はvCPUベースのみになります。もっと詳しく知る
```

切替を実施する場合は、右上の[Opt in]をクリックします。
下記の画面が表示されます。
<img width="605" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254711/c2d60842-4436-3378-af81-691035c3cf45.png">

```
カウントベースのインスタンス制限からvCPUベースのインスタンス制限に切り替えています。

締切日までカウントベースのインスタンス制限に戻すことができ、
それ以降はインスタンス制限はvCPUベースのみになります。

vCPUベースのインスタンス制限への切り替えを確認するには、
下のフィールドにオプトインを入力します。
```

切替を実施する場合は、Opt inを入力しましょう。

<img width="1209" alt="3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254711/c868096c-469b-589d-0bb0-657abbaabfd0.png">

```
オプトインのリクエストは成功しました
すべての地域で、アカウントをカウントベースのインスタンス制限からvCPUベースのインスタンス制限に切り替えるプロセスを進めています。
これが完了するまでに最大15分かかります。
```
切替には１５分ほど掛かるようです。

## 参考リンク
[よくある質問：EC2 オンデマンドインスタンス制限](https://aws.amazon.com/jp/ec2/faqs/#EC2_On-Demand_Instance_limits)

[Using new vCPU-based On-Demand Instance limits with Amazon EC2](https://aws.amazon.com/jp/blogs/compute/preview-vcpu-based-instance-limits/?mkt_tok=eyJpIjoiWVRrNE5EZzRZbVZoTVRjeCIsInQiOiJhVENJM3lCUkxvR3RhaGwwR0F2cVAzRmFVQVF6YTdXUnRtYVwvaDRxcmNpSUpuNlg4XC9Tc3lXVFU1KzFNV3A5MUE0RFhQa1g1M3ZGQmM1OXZZalBTVUN0eFRLeEduY1wvZ1IrKzZpUHRqRGZlT0F4cFVjOCtIb2E4emRiMHhmMGROYkJXajFpUHJ6d21XU2VOU2tMck9oVkE9PSJ9)

[Amazon EC2 で vCPU ベースのオンデマンドインスタンス制限が利用可能になりました](https://aws.amazon.com/jp/blogs/news/vcpu-based-on-demand-instance-limits-are-now-available-in-amazon-ec2/)
ルーターを2つ使ってサーバ公開用ネットワークを構築した話

###■まえおき
マインクラフトやTerrariaなどでModを使用する場合、個人でサーバを建てる必要がある。
最初はGoogle Cloud Platformで構築してたが24時間起動だとやはりコストがかかるため余ったPCを使って個人サーバを建てた。
公開するにあたって公開用のネットワークとプライベートネットワークを分ける必要が出たのでそのメモ書き。

###■ネットワーク構成図
Excelで簡単に記載した。
・Router1～Router2の間が公開用のネットワーク（左側の赤破線部分）
・Router2～がプライベートネットワーク（右側の赤破線部分）
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487535/ccb7f6dc-2e29-1f81-d7d4-0beb912c09b8.png)

ここで問題となるのがRouter2配下のVPNサーバの存在。
VPNサーバまでポート開放しなければいけないが、ポート開放の解説をしている大抵のサイトでは2重ルーターはポート開放できないとある。

###■2重ルーターでポート開放するには？
答えは簡単。
2つのルーターでポート解放してあげればいい。

ServerがTCP:7777、VPN ServerがTCP:8888を待ち合わせているとする。

①Serverのポート開放
・Router1配下に設置されているためRouter1でTCP:7777 IP:192.168.0.3とポート解放する。

②VPN Serverのポート開放
1.Router1からRouter2までポート開放
　→Router1でTCP:8888 IP:192.168.0.2
2.Router2からVPN Serverまでポート開放
　→Router2でTCP:8888 IP:192.168.1.2

このようにするとルーターが複数あってもポート開放ができる。
（間違っていたら教えてください。）
Node.js の環境を順番に整える nodist 編

# 概要
Node.js の環境を整える必要が出てきたため順番に試す。

# nodist ってなに？
windows で Node.js 自体のインストールやバージョン管理をするもの。
https://github.com/nullivex/nodist

# nodist のインストール
v0.9.1 を使います。
[リリースページ](https://github.com/nullivex/nodist/releases)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275553/78666a9e-b5f5-60dc-9cee-13d817dd5a2d.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275553/fadcffd0-ab34-d3d2-da1f-10d4ae7a26c1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275553/00b3f0ec-7022-1f4a-fa8f-de44545cc630.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275553/6190e4e3-3b47-6f34-b48c-2822cd9906f5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275553/9cf00258-6fd7-51ef-3bfa-2f07171fc750.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275553/a9f839bf-f8b9-61f8-8eb5-14045c579dcb.png)

## インストール確認
```
> nodist -v
0.9.1
```

# Node.js のインストール
利用可能バージョン確認

```bash
> nodist dist
  0.1.14

#~~~~
  12.10.0
  12.11.0
```

インストール

```bash
> nodist + 12.11.0

# バージョン確認
> nodist
  (x64)
> 11.13.0  (global: v11.13.0)
  12.11.0

# node のバージョンも確認
> node -v
v11.13.0

# npm バージョンも確認
>npm -v
6.9.0
```
Ruby逆引き。エクセル関数一覧

#1.はじめに

関数ならばわかるのにRubyになるとわからないことが多々あるので
逆引きの一覧表を作成してみました。

自分の備忘録的な内容ですが、もしエクセル関数が得意な人がいれば参考にしていただければと思います。

#2.関数一覧

##2-1.文字の書式変更

###VALUE関数
できること：**数値に変換する**
エクセルの場合：=VALUE(A1)
Rubyの場合  ：A1.to_i

###T関数
できること：**文字列に変換する**
エクセルの場合：=T(A1)
Rubyの場合  ：A1.to_s  


##2-2.文字のカウント

###FIND関数
できること：**指定した文字の順番を返す**
エクセルの場合：=FIND("指定の文字",A1)
Rubyの場合  ：A1.index("指定した文字")



###LEN関数
できること：**文字数を返す**
エクセルの場合：=LEN(A1)
Rubyの場合  ：A1.length


##2-3.文字の抽出

###MID関数
できること：**任意の位置から指定された文字数までの文字を抽出**
エクセルの場合：=MID(A1,1,5)
Rubyの場合  ：A1[0,5]
※Rubyの場合は0が1番目になるので注意

###TRIM関数
できること：**空白の削除**
エクセルの場合：=TRIM(A1)
Rubyの場合  ：A1.strip



##2-4.その他

###SUBSTITUTE関数
できること：**文字の変換**
エクセルの場合：=SUBSTITUTE(A1,"今日","明日")
Rubyの場合  ：A1[0, 2] = "明日"
※A1の文章が「今日の天気」を「明日の天気」に変える場合


###SPLIT関数
できること：**指定した文字で分割させる**
エクセルの場合：=SPLIT(A1,"/")
Rubyの場合  ：A1.split("/")
※スプレッドシートのみの関数

###ISBLANK関数
できること：**文字列が空白かどうか調べる**
エクセルの場合：=ISBLANK(A1)
Rubyの場合  ：A1.empty?


###IF関数
できること：**条件により処理を分岐させる**
エクセルの場合：=IF(A1="雨","外出はやめよう","出かけよう")
Rubyの場合  ：↓

```ruby:
if A1 = '雨'
  '外出はやめよう'
else
  '出かけよう'
end
```

#3.まとめ

まだまだエンジニア初心者なので記載できていない内容があると思います。
今後勉強して行く中で残しておきたいこととかがあれば追記すると予定です。。
Bazel で Mono で C# をビルド

https://github.com/siarhiej-karalenka/bazel-mono

ポイントは [`--spawn_strategy=local` オプション](https://docs.bazel.build/versions/master/user-manual.html#flag--spawn_strategy)。Bazel は OS の Sandbox 機構を利用してコンパイラ等のビルドプロセスを起動する。Bazel で C# をビルドするための [rules_dotnet](https://github.com/bazelbuild/rules_dotne) は Mono コンパイラ (mcs) を Sandbox 内にセットアップするとこまではやってくれないので (?) `--spawn_strategy=local` オプションを付けずに `bazel build` すると `mcs` が見つからなくてコンパイルエラーになる。つけるとシステムにインストールされてる mono が使われる。
iOS13で、presentで画面遷移をした際に遷移前の画面に戻れてしまう問題について

# 問題

iOS13で、```present```で画面遷移をした際、全画面表示されなくなっている。
そして、画面を下にスワイプすると、簡単に遷移前の画面に戻れてしまう。

<img width="375" alt="IMG_4871.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93963/6651f1ae-80e4-a79b-0fc0-bf34aa2ce997.png">

# 原因

iOS13では```present```でモーダル表示する際の```modalPresentationStyle```のデフォルト値が```.pageSheet```に変更されたため。

# 解決法

遷移先の```ViewController```の```modalPresentationStyle```に```.fullScreen```を設定すればOK。

```swift:ViewController.swift
let loginStoryBoard: UIStoryboard = UIStoryboard(name: "Login", bundle: nil)
let viewController = loginStoryBoard.instantiateInitialViewController()
viewController?.modalPresentationStyle = .fullScreen
self.present(viewController!, animated: true, completion: nil)
```

すると、以下のように全画面表示され、スワイプしても遷移前の画面に戻れなくなります。

<img width="375" alt="IMG_4872.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93963/4e877d6d-0c73-bc9a-1ffc-d77ae5d57e8e.png">

# 参考

[iOS - ios13のプロジェクトでpresentすると遷移前の画面に戻れてしまう｜teratail](https://teratail.com/questions/208529)
[viewcontroller - Presenting modal in iOS 13 fullscreen - Stack Overflow](https://stackoverflow.com/questions/56435510/presenting-modal-in-ios-13-fullscreen)
Rapid Home Provisioning改めOracle Fleet Patching and Provisioningのライセンス条件が変わった件 

# Fleet Managementとは

ここ数年のOracle Databaseの資料で、よく出てくるキーワードが**「Fleet Management」**だ。

もともとは業務用車両や船舶の維持管理を指す言葉で、

**企業内に複数存在するOracle Databaseのバージョン管理やアップグレード管理、デプロイメント管理などの維持管理作業を指している。**

たとえば、100個のOracle Databaseインスタンスがあり、それぞれがバージョンもパッチレベルもバラバラだと管理は大変になる。より少ないバージョン＆パッチレベルに収束した方が、バグに会う可能性は少なくなるし、その後のパッチマネージメントやアップグレードも簡単だ。

また、個別にパッチを適用するのではなく、ゴールドイメージ（マスター的なもの）を作成して配布したほうが、高効率＆高品質の可能性が高い。

**まあ日本の場合、中央集権的に管理できることは少ないし、システムをリリースしたら塩漬け傾向なので、簡単な話ではないのだけれどね。**

とはいえ、Oracleの新しいリリースモデルUpdate/Revisionは、あきらかにそれを指向している。


# Rapid Home Provisioning改めOracle Fleet Patching and Provisioningとは

前記のような状況で便利なのが、Enterprise Editionの有償オプション「Oracle Database Lifecycle Management Pack for Oracle Database」であり、その機能の1つであるRapid Home Provisioningだ。

なお19cから、Rapid Home Provisioning(RHP)はOracle Fleet Patching and Provisioning(FPP)という名前になった。

FPPを利用すると、パッチ適用済みのゴールドイメージを作成し、簡単な操作で配布できる。詳しくは、日本語マニュアルや英語のWebページがあるので参照のこと。

* [Oracle Fleet Patching & Provisioning](https://www.oracle.com/database/technologies/rac/fpp.html)


**FPP関連マニュアル**

* [フリート・パッチ適用およびプロビジョニングを使用したOracle Softwareのデプロイと管理 リリース19c](https://docs.oracle.com/cd/F19136_01/sprhp/index.html)
* [高速ホーム・プロビジョニングを使用したOracle Softwareのデプロイと管理 リリース18c](https://docs.oracle.com/cd/E96517_01/sprhp/index.html)
* [Oracle Database高速ホーム・プロビジョニングを使用したOracle Softwareのデプロイと管理 リリース12c リリース2](https://docs.oracle.com/cd/E82638_01/sprhp/index.html)

# ライセンス条件が変わった部分

いままでRapid Home Provisioning改めOracle Fleet Patching and ProvisioningにはLifecycle Management Packが必要だった。

しかし2019年9月の改訂で、RACのライセンスを持っていればFPPを利用できる。

**ライセンスマニュアルのNoteの記載**
> When deployed across multiple clusters, requires Oracle Database Lifecycle Management Pack for Oracle Database, Oracle Real Application Clusters, or Oracle RAC One Node on a per target basis 

つまりFPPを利用するには、以下のいずれかの条件を満たせばよい。また19cだけでなく、12cでも改訂されている。

* Oracle Database Lifecycle Management Pack for Oracle Database
* Oracle Real Application Clusters
* Oracle RAC One Node

日本語のマニュアルは「PDB3つまでOK」と同様に、キャッチアップできていない。

* [Database Licensing Information User Manual 19c （E94254-09） September 2019](https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/Licensing-Information.html#GUID-0F9EB85D-4610-4EDF-89C2-4916A0E7AC87)
![FPP01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266049/8e41adce-863b-52f7-5e94-0264efb19419.png)

* [データベース・ライセンス情報ユーザー・マニュアル 19c（原本部品番号:E94254-06） 2019年5月](https://docs.oracle.com/cd/F19136_01/dblic/Licensing-Information.html#GUID-0F9EB85D-4610-4EDF-89C2-4916A0E7AC87)
![FPP02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266049/92331eb0-7a62-f797-87ee-c4bb472c0a32.png)

# ひとりごと

Oracle Cloud Infrastructure Database(DBaaS)は、すぐに使えて、従量課金で便利なのだけれど、PaaSとして完成度がイマイチなんだよね。OCI-Cと比べても機能的に劣っているし、FPPやPreupgrade Tool/AutoUpgrade Toolなどの便利ツールが使えない。

まあ、DBaaSはPSU/Update相当しか適用できないので、One Offパッチ沼には陥らないのだけどね。でも12c to 19cのようなアップグレード機能は不足している。

DBaaSについては、今後の機能強化に期待かな。とはいえ、Autonomous Databaseに力を入れているので、DBaaSの進化速度は低いような...。
タイムボックス化して、アポイントメントを捗らせよう

転職活動など、人と会うときにアポ取りをする。

そのとき、候補日と時間を３つくらい挙げることが多いが、他の予定とバッティングするリスクを減らすためにどうすればいいか、考えてみた。

まず、候補日と候補時間帯を、３×３くらい、合計９つ用意する。

たとえばこんな感じ。

```
さっそくですが日程を調整させていただきたく、
以下の日程ではご都合いかがでしょうか。

10/1 10:00、14:00、16:00 スタート
10/2 10:00、14:00、16:00 スタート
10/3 10:00、14:00、16:00 スタート
※ 所要時間は60分程度を予定しております。

ご返信をお待ちしております。 どうぞよろしくお願いいたします。
```

アポイントのメッセージを送った時点で、上記９つの時間帯すべてをカレンダーに登録してしまう。

こうすると、確定してなくても、予定をざっくりとではなく、細かく把握することができるし、相手の反応によってスケジュールを再調整する負担が減る。

予定が確定したら、確保しておいたタイムボックスを消せばいいだけである。
(Powershell) ファイル内の文字列を置換してコピーするスクリプト


##  ディレクトリ内のファイルを読み込んで文字列を置換したコピーを作成するスクリプト

```powershell
Get-ChildItem .\ | ForEach-Object{
$target = $_
echo $target
Get-Content $target | %{$_ -ireplace "from", "to"} > .\20190926\$target
}
```

## 対象ファイル内の文字列を置換してコピーを作成する

```powershell
Get-Content .\FromFile.txt | %{$_ -ireplace "from1", "to1"} | %{$_ -ireplace "from2","to2"} > .\20190926\ToFile.txt

```

## 参照
下記のサイトにファイルの文字列置換についての記述を参考にさせて頂きました。
https://qiita.com/mindwood/items/66207426904d02bd3359

以上です。