OmniAICreator/Qiita-1.07M huggingface dataset:
GCEのファイアウォールでsshを制限しながら、GCPのブラウザからのsshもできるようにする

# やりたいこと
SSH制限されたNWなど、sshできないのでクラウド上のサーバーにアクセスできない。
そんな環境で役に立つのが、GCPのブラウザからのsshの機能。
GCPのコンソール画面から、GCEのWEBターミナルを起動できる。
GCPのこの機能は地味に気に入っている。

[公式](https://cloud.google.com/compute/docs/ssh-in-browser?hl=jacx)の画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161521/55fd5926-4819-5b6f-234d-fa1758f42f44.png)

しかし、この機能はssh許可やパブリックIP付与が必要になる。
セキュリティポリシーで、サーバーへのsshの制限が必要だけど、アクセス経路としてGCPのブラウザからのsshを使いたい場合のメモ。

（2020/11/13）もっと良い方法が出てきたので、[こっちの記事参照](https://qiita.com/henatyokotraveler/items/ada9c45ca4518ab3b726)

# GCPのブラウザからのsshの条件
GCPのブラウザからのsshの機能だが、
GCEのファイアウォールで外部のSSHを制限しようとしたり、
グローバルからアクセスできるパブリックIPアドレスのないノードだったりするとアクセスできない。
理由は下記のとおり
## [公式](https://cloud.google.com/compute/docs/ssh-in-browser?hl=jacx)からの抜粋
- ポート上の SSH アクセスを許可するファイアウォール ルールがない。 ポート 22 上の SSH アクセスは、すべての Compute Engine インスタンスでデフォルトで有効になっています。アクセスを無効にしている場合、ブラウザからの SSH は機能しません。22 以外のポートで sshd を実行する場合は、カスタムのファイアウォール ルールを使用してそのポートへのアクセスを有効にする必要があります。

- SSH アクセスを許可するファイアウォール ルールが有効になっているが、GCP Console サービスからの接続を許可するように構成されていない。ブラウザベースの SSH セッションの送信元 IP アドレスは、GCP Console によって動的に割り当てられ、セッションごとに異なることがあります。この機能を動作させるには、任意の IP アドレスからの接続、または[公開 SPF レコード](https://support.google.com/a/answer/60764?hl=ja)を使用して取得できる Google の IP アドレス範囲からの接続を許可する必要があります。

# [公開 SPF レコード](https://support.google.com/a/answer/60764?hl=ja)を許可IPにする
2019/9/3時点の[公開 SPF レコード](https://support.google.com/a/answer/60764?hl=ja)は以下のとおり。

```_netblocks.google.com
>nslookup -q=TXT _netblocks.google.com 8.8.8.8
"v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all"
```

```_netblocks2.google.com
>nslookup -q=TXT _netblocks2.google.com 8.8.8.8
"v=spf1 ip6:2001:4860:4000::/36 ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36 ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all"
```

```_netblocks3.google.com
>nslookup -q=TXT _netblocks3.google.com 8.8.8.8
"v=spf1 ip4:172.217.0.0/19 ip4:172.217.32.0/20 ip4:172.217.128.0/19 ip4:172.217.160.0/20 ip4:172.217.192.0/19 ip4:108.177.96.0/19 ip4:35.191.0.0/16 ip4:130.211.0.0/22 ~all"
```

GCEのファイアウォールルールに上記の_netblocks.google.comのセグメントと、_netblocks3.google.comのセグメントをssh許可にする。
※IPv6はGCEでサポートしていないので不要
 （[公式：IPv6 はまだ Compute Engine 上でサポートされていないため無効になっています。](https://cloud.google.com/compute/docs/images?hl=JA)）

# 所感
上記の対処だと、
- 許可しているIP範囲広い
- パブリックIPなしはブラウザsshできる踏み台（Bastion）サーバを利用する
- いつのまにか公開SPFレコード変わってたらやだなー
といったところが残り、解決しきった感はない。

GCEはデフォルトでSSH許可されているし（鍵認証で外部アクセスを制限）、
[BeyondCorp](https://cloud.google.com/beyondcorp/)な考え方がベースにあるのかなとも思うけど、
ブラウザからのsshはグローバルからではなく、GCPのプライベートから可能にするか、せめて許可IPの範囲を狭めてほしいなーと思う。
アニメで分かる「git pull —rebase origin develop」

#はじめに
トピックブランチに、最新のdevelop（開発用ブランチ）を取り込むとき、
``「git merge develop」ではなく、「git pull —rebase origin develop」を使うと良い！``
というページをちらほら見かけたのですが、何がいいのかいまいちピンと来ていない人のために、何が嬉しいのかgifアニメを作ってみました。
※イメージ出来ることが目的なので、詳しい説明は参考のリンクを見てみてください。


#git merge develop
左の黒がdevelopで、右の青がトピックブランチです。
イメージは、こんな感じ！
![gitmergedevelop.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321577/cfef9f73-6414-e14d-97a9-eba5bab78f05.gif)

Mっていうのはマージコミットのことです。トピックブランチにdevelopをマージするたびに、毎回このマージコミットが増えてしまいます。
#git pull --rebase develop
続いて、ウワサの「git pull --rebase develop」を使ってみます。
すると…
![gitpullrebaseorigindevelop.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321577/48f0ac4c-3ba2-383d-6195-615808a417a2.gif)

わかりますでしょうか？？
ポイントは、**マージコミットが生成されない！！！**ということです。
そのかわりに、コマンドを打ったタイミングで、developの最新からブランチを作ったようなコミット履歴になります。
これにより、
**無駄なコミットがなくなって、コミット履歴がとても見やすくなる！**
という効果があります。
マージリクエストを出したときも、コミット履歴が見やすいので、レビュアーがレビューしやすくなります。
# 注意
コマンドに「rebase」と入っている通り、トピックブランチにあるコミットをリベースするので、トピックブランチがローカルにしかない時にやってください。
# まとめ
簡単なgitアニメなので、すぐ作れるだろうと思ってたら、4時間くらいかかりました笑
繰り返しになりますが、ざっくりだったので、詳しく知りたい方は参考をご確認ください！

#参考
[git pull と git pull –rebase の違いって？図を交えて説明します！]
(https://kray.jp/blog/git-pull-rebase/)
↑↑丁寧でわかりやすいです！
[最新のdevelopの取り込みはgit pull --rebase派](https://qiita.com/yanutetsu/items/bdafe41594327a422b00) 
↑↑熱い気持ちの参考はこちら
pythonでサイコロをコロコロするアイディア

最近競技プログラミングにはまっています。


有名ですが、サイコロは結構おしゃれなつくりをしていて、対面の数字を足すと７になります。
きっと何かそれを使ったおしゃれな解き方をする問題にいずれ出会うのでしょうか？

余談でした。

で、本題です。
サイコロを振るという話ではなく、6面体をころころと転がすような処理の話です。  

といっても、転がり方については、
右回転を3回繰り返せば、左回転1回と同義なので、
実は3パターンで網羅可能。

そのパターンの１つが下みたいな感じ

```python
    # サイコロのそれぞれの面を配列で保持してみるアイディア
    now_pos = [1,6,2,5,4,3]
    # サイコロを開くとこんな感じ
    #    1
    # 4  2  3  5
    #    6

    # 1,6を軸に4,2,3,5を4→2の向きで回転するとき
    #    1           →     1
    # 4  2  3  5     →  5  4  2  3
    #    6           →     6
    for p in now_pos:
        if p == 4:
            new_pos.append(2)
        elif p == 2:
            new_pos.append(3)
        elif p == 3:
            new_pos.append(5)
        elif p == 5:
            new_pos.append(4)
        else:
            new_pos.append(p)
```
あと2パターンかいてあげれば、サイコロを転がす処理は網羅可能なはず。


なんとなく、3×2=6で、[boolean,boolean,boolean]を使えば、
かっこよく処理できる気もしたのですが、
やりたいことの可愛さに対して、考えるのに時間がかかりそうなのでやめました。

なにかいい案や、「普通はこうやるんだぜ」なものがあれば、教えてください。5万点さしあげます。

shiracamusさんからコメントいただいた内容をもとに考えてみました！
こっちのほうがおしゃれですね！
これをベースに考えてみるとより良いかもしれないです！

```python
    now_pos = [1,6,2,5,4,3]

    # [4,2,3,5]
    targets = [now_pos[4],now_pos[2],now_pos[5],now_pos[3] ]
    new_pos = []
    # 一つずらすパターン
    for i in range(len(targets)):
        new_pos.append(targets[(i+1) % len(targets)])

    # 動かない値を戻す。
    new_pos.insert(now_pos[0],0)
    new_pos.insert(now_pos[1],1)
```

---

競プロ関連のことってどの程度記事とかにしていいんでしょうかね。
塩梅がわからないので、こんなに書いちゃダメだよ！であればご指摘ください。
Go で Observer パターン

Note to self: ObserverパターンのGo実装。

イベント発火に対するアクション実行を発火側から分離したいときに検討するパターン。

https://ja.wikipedia.org/wiki/Observer_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3

## クラス図

<img width="591" alt="class.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34192/5a1eb20b-5061-1c52-6fce-f9d98fb302ff.png">



## Code

```golang

// Package main
package main

import (
	"fmt"
	"sync"
)

// Param ...
type Param string

// Observer ...
type Observer interface {
	Notify(p Param)
}

// Subject ...
type Subject struct {
	mu        sync.RWMutex
	observers []Observer
}

// AddObserver ...
func (nb *Subject) AddObserver(o Observer) {
	nb.mu.Lock()
	defer nb.mu.Unlock()
	nb.observers = append(nb.observers, o)
}

// DeleteObserver ...
func (nb *Subject) DeleteObserver(o Observer) {
	nb.mu.Lock()
	defer nb.mu.Unlock()
	obs := nb.observers[0:0]
	for _, v := range nb.observers {
		if v != o {
			obs = append(obs, v)
		}
	}
	nb.observers = obs
}

// Notify ...
func (nb *Subject) Notify(p Param) {
	nb.mu.RLock()
	defer nb.mu.RUnlock()
	for _, v := range nb.observers {
		v.Notify(p)
	}
}

// Something ...
type Something struct {
	Subject
}

// Observer1 ...
type Observer1 struct{}

// Notify ...
func (o1 *Observer1) Notify(p Param) {
	fmt.Println("obs1:", p)
}

// Observer2 ...
type Observer2 struct{}

// Notify ...
func (o1 *Observer2) Notify(p Param) {
	fmt.Println("obs2:", p)
}

func main() {
	s := &Something{}
	ob1 := &Observer1{}
	ob2 := &Observer2{}
	s.AddObserver(ob1)
	s.AddObserver(ob2)
	s.Notify("hello!")
	s.DeleteObserver(ob1)
	s.Notify("World!")
}
```

### 実行結果

```
obs1: hello!
obs2: hello!
obs2: World!
```
C# Winforms フォルダ選択ダイアログ

C# windows formsのフォルダ選択ダイアログについてです。
どうも標準のFolderBrowserDialogが使いにくかったので
調べてみたところファイル選択ダイアログでもフォルダ選択できるようなので
備忘録として残しておきます。

## 試したアプリ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168779/1e80ba59-03a9-a099-6248-2490b23cb1a0.png)
色々な方法がありましたが二つ分かりやすくて簡単なやり方を見つけたので紹介します。
上のような簡易アプリで試しました。

## 方法1
```cs
private void Button1_Click(object sender, EventArgs e)
{
    var dialog = new FolderSelectDialog
    {
        InitialDirectory = Environment.CurrentDirectory,
        Title = "Select a folder to import music from"
    };
    if (dialog.Show(Handle))
    {
        this.textBox1.Text = dialog.FileName;
    }
}
```
>FolderSelectDialogクラスはStackOverflowの[この投稿](https://stackoverflow.com/questions/15368771/show-detailed-folder-browser-from-a-propertygrid)で作成されているものです。

詳細は[リンク先](https://stackoverflow.com/questions/15368771/show-detailed-folder-browser-from-a-propertygrid)を参照ください。
リフレクションとか使ってある意味ハックしているような感じでしょうか。
フォルダをクリックしたら選択状態になるので直観的に使いやすくて好きです。


## 方法2
```cs
private void Button2_Click(object sender, EventArgs e)
{
    using (var ofd = new OpenFileDialog() {
        FileName = "Folder Selection",
        Filter = "Folder|.",
        ValidateNames = false,
        CheckFileExists = false,
        CheckPathExists = true,
    })
    {
        if (ofd.ShowDialog() == DialogResult.OK)
        {
            this.textBox1.Text = Path.GetDirectoryName(ofd.FileName);
        }
    }
}
```
こちらはOpenFileDialogのプロパティを使ってフォルダを選択するやり方です。
使い方は方法1とほとんど変わりませんが最下層でないフォルダを選択する時はクリック状態だけでは駄目で
開いた状態でないと選択できないのが少しだけ使いづらいです。
下記記事の方も言われていますが普段使いであればこれで十分だと思います。
>[OpenFileDialog でフォルダを指定する [ WinForms ]](https://qiita.com/souten21kobayashi/items/bb25ca320e49b269ef9e)



## 参考・出典元
https://qiita.com/souten21kobayashi/items/bb25ca320e49b269ef9e
https://qiita.com/otagaisama-1/items/b0804b9d6d37d82950f7
https://stackoverflow.com/questions/31059/how-do-you-configure-an-openfiledialog-to-select-folders
https://stackoverflow.com/questions/15368771/show-detailed-folder-browser-from-a-propertygrid
上記URLに感謝です。
SharePointでワークフローを作成する(リスト作成からFlowまで) 補足 申請後のアイテムを編集させない

# はじめに
[前回](https://qiita.com/fukasuke/items/84508933124a864fde3b)はSharePoint+Flowを使ってワークフローを作成しました。
本記事ではその後の修正箇所をダラダラ記載していきます。

# アイテムの編集制限
前回の設定のままだと申請者が申請したあとアイテムを編集できてしまいます。SharePointの設定では、新規追加はできるけど、編集は不可という権限設定ができません。

## Flowで権限を変更する
今回は以下のようにしました
1. 申請後、そのアイテムの共有を停止
2. 申請者に対して参照権限のみ付与
3. 承認者に対して編集権限を付与
これで申請者はアイテムの編集ができなくなります。

これで一旦共有を停止。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245365/1d3dcbc3-e224-cb73-2ce5-4acf91e54f4b.png)

そのあとに以下を設定。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245365/6a507789-2c74-690e-970a-1546d1dc9283.png)
「受信者」は申請者のメールアドレスを指定します。ロールは「Can view」。このアイテムの参照権限を付与したことをメールで通知できるようですが、それは不要なので「受信人への通知」は「いいえ」にしておきます。
これで申請者は参照のみできます。

次に承認者に編集できるようにします。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245365/d56b41cf-1247-2375-57c4-1976eb0ac83b.png)
ここでの「受信者」はその上で取得したGet managerの上司のメールアドレスです。ロールは「Can edit」にしています。

これでおしまいです。
rails console でrenderの返り値を確認する

# rails console でrenderの返り値を確認する

たとえば、`app/views/users/index.erb`を`render`した時の返り値のHTMLをrails consoleで確認したい場合

```ruby
view = ActionView::Base.new('app/views/user', {},  ActionController::Base.new)
view.render(file: 'index.erb')
```

## インスタンス変数を渡したい場合

`render`のキーワード引数`locals`で渡してあげる

```ruby
posts = Post.all

view = ActionView::Base.new('app/views/user', {},  ActionController::Base.new)
view.render(file: 'index.erb', locals: { :@users => [], :@posts => posts })
```


## jbuilderでもできる

`app/views/users/index.json.jbuilder`Pをrenderした時の返り値のHTMLをrails consoleで確認したい場合

```ruby
posts = Post.all

view = ActionView::Base.new('app/views/user', {},  ActionController::Base.new)
view.render(file: 'index.json.jbuilder', locals: { :@users => [], :@posts => posts })
```
[memo]Railsのモデルで使うクラスメソッドとscopeの違いを理解する

## 概要
Railsで開発している中で、「scopeと通常のメソッド定義では何が違うの？」となったので調べました。

結論としては、**「やることはどっちも同じ。どっちを使うかは好みの問題」**だそうです。

## RailsのModelで使うscope
[Railsガイド](https://railsguides.jp/active_record_querying.html#%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97)によれば
> スコープを設定することで、関連オブジェクトやモデルへのメソッド呼び出しとして参照される、よく使用されるクエリを指定することができます。スコープでは、where、joins、includesなど、これまでに登場したすべてのメソッドを使用できます。どのスコープメソッドも、常にActiveRecord::Relationオブジェクトを返します。このオブジェクトに対して、別のスコープを含む他のメソッド呼び出しを行なうこともできます。

`scope`を使ってメソッド定義することで、事前に`scope`内で指定した実行してほしいクエリを渡すことができます。

複数回使うようなクエリがある場合にはそれをモデル内で`scope`を使って定義しておけば、可読性や再利用性を高めることができます。

また、`scope`同士はメソッドチェーンで繋ぐことも可能なので、より小さなレベルで`scope A`と`scope B`に切り出しておき、それらをつなぎ合わせた`scope C`として定義することも可能です。

```ruby:/models/book.rb
# 出版済みの書籍絞り込み
scope :published, -> { where(published: true) } 

# 出版日を降順で並べ替え
scope :sort_publication, -> { order(publication: :desc) }

# 出版済みの書籍を出版日で降順に並べ替え
scope :get_published, ->{ published.sort_publication }
```

## scopeでの定義とクラスメソッドの定義の違い
>以下でもわかるように、スコープでのメソッドの設定は、クラスメソッドの定義と完全に同じ (というよりクラスメソッドの定義そのもの) です。どちらの形式を使用するかは好みの問題です。
引用: [Railsガイド](https://railsguides.jp/active_record_querying.html#%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97)

やってることは完全に同じです。
`scope`を使って定義しようが、`def ~ end`のクラスメソッドで定義しようが、違いはありません。

```ruby:/models/book.rb
(1)
scope :published, -> { where(published: true) }

(2)
def self.published
  where(published: true)
end
```

上記(1)と(2)は全く同じです。

ただ、個人的には`scope`を使えば1行で済むのでコードの可読性も上がりそうで好きなのと、クラスメソッドとインスタンスメソッドの区別をより明確にする意味も込めて、`def ~ end`のクラスメソッドで定義するよりも`scope`を使いたいなーという感じでいます。

この辺は企業によって違ってきたりするんでしょうか。

コントローラーでクエリを直接呼び出したり、さらにはクエリを複数繋いだりすると読みづらくなったりRailsのDRYの原則にも合わなくなってくると思うので、個人的には避けていきたいところです。

## 考慮しておくべき点
- `scope`にも引数を渡すことができますが、そのような場合にはRailsガイドではクラスメソッドを利用することを推奨しています

>スコープに引数を渡す機能は、クラスメソッドによって提供される機能を単に複製したものです。
したがって、スコープで引数を使用するのであれば、クラスメソッドとして定義する方が推奨されます。クラスメソッドにした場合でも、関連オブジェクトからアクセス可能です。

- `scope`内で条件文を使用する時、その評価の結果が`false`になった場合にはクラスメソッドは`nil`を返すが、`scope`では`ActiveRecord::Relationオブジェクト`を返す
  - 条件文の評価によって挙動を変える場合などにはこの点を考慮しておく必要がある？

## まとめ
- `scope`での定義とクラスメソッドでの定義には違いはない。使い分けは好みの問題。
- `scope`を使う方がよりシンプルで読みやすくかける
- `scope`は小さく切っておくことでコードの再利用性も上がる

## 参照
[Ruby on Rails ガイド/Active Record クエリインターフェイス](https://railsguides.jp/active_record_querying.html)


## 備考
- `scope`のメソッド名では、`in_***`という命名が割と多いらしい
- パーツとして再利用できそう/しそうなやつだけ`scope`で切り出すのが良い？
Amazon CloudWatch Events で AWS Lambda を定期実行する際に任意の入力パラメータを渡す

## 概要
- Amazon CloudWatch Events で AWS Lambda を定期実行する際に任意の入力パラメータを指定する
- パラメータがどのように渡されてきているかログに出力して確認する

## 確認用ソースコード

AWS Lambda 関数に入力するソースコード。
ランタイムは Node.js 10.x を使用する。

```js
exports.handler = async (event, context) => {
    // CloudWatch Events Message Event オブジェクトを event にセット
    const result = {
        event: event,
    };
    // 結果を Amazon CloudWatch Logs に出力する
    console.log(JSON.stringify(result, null, 2));
    return result;
};
```

## AWS Lambda 関数の管理画面からトリガーを追加する際には任意の入力パラメータを設定できない

AWS Lambda 関数の Designer からトリガーを追加する際には、関数に渡す任意の入力パラメータを設定することはできない。

<img width="1040" alt="cloudwatch-events-2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/d90da2bd-5e3c-2ad9-ffeb-98082e7a9935.png">

![cloudwatch-events-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/561c0607-9b57-5c01-f910-8d9e4f2a831b.png)

任意のパラメータを設定していないトリガーから実行した結果を Amazon CloudWatch Logs にて確認すると、以下のような CloudWatch Events Message Event オブジェクトが出力されている。

```json
{
    "event": {
        "version": "0",
        "id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "detail-type": "Scheduled Event",
        "source": "aws.events",
        "account": "XXXXXXXXXXXX",
        "time": "2019-09-03T13:24:00Z",
        "region": "ap-northeast-1",
        "resources": [
            "arn:aws:events:ap-northeast-1:XXXXXXXXXXXX:rule/my-foo-bar-every-minutes"
        ],
        "detail": {}
    }
}
```

## Amazon CloudWatch コンソールから追加・編集時に任意の入力パラメータを設定できる

[CloudWatch Management Console](https://console.aws.amazon.com/cloudwatch/home?#events:) からイベントを作成または編集するときには任意の入力パラメータ 「定数 (JSON テキスト)」 を指定することができる。

<img width="1162" alt="cloudwatch-events-3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/dee0d14f-0f56-2acc-b275-7b87e4b6dd63.png">

実行結果を Amazon CloudWatch Logs にて確認すると、設定した JSON が CloudWatch Events Message Event オブジェクトとして出力されているのがわかる。

```json
{
    "event": {
        "message": "Hello",
        "detail": "2分おき実行"
    }
}
```

## 参考資料
- [CloudWatch イベント のイベントパターン \- Amazon CloudWatch Events](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/CloudWatchEventsandEventPatterns.html)
- [Node\.js の AWS Lambda 関数ハンドラー \- AWS Lambda](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-prog-model-handler.html)
- [AWS CloudWach Events実行時に便利なConfigure input ｜ DevelopersIO](https://dev.classmethod.jp/cloud/aws/aws-cloudwach-events-configure-input/)
（Python Selenium）自動ダウンロードした PDFファイル名を取得する

## Python Selenium で自動ダウンロードした PDFファイル名を取得したい
クローリングで ダウンロードした PDFファイル名を取得したいと思います。自動化してPDFダウンロードした際の課題として以下のものをあげました。

- **1. ダウンロード先フォルダをプロジェクトのフォルダに指定したい。**

開発環境毎に異なるダウンロード場所になってしまうことを防ぐため、ChromeDriver の立ち上げの際に指定したダウンロードフォルダを設定します。

- **2. 処理がファイル名を直接指定して読み込めない**

ファイル名が分からないため、ダウンロード直後に、ファイル名をすぐ取得する処理ができないか検討をしました。

以上の２点を自動ファイルダウンロードの課題としてあげました。これらを解決してダウンロードしたPDFファイル名を取得したいと思います。

## WebDriver インスタンス作成時にダウンロード先を指定する
まず、ブラウザ でダウンロードした際のダウンロード先を、WebDriverインスタンスを生成するタイミングで設定します。（**Chromeドライバの場合、selenium.webdriver.ChromeOptionsインスタンスに**add_experimental_option()メソッドで**download.default_directoryの指定をします。**）

```WebDriverModule.py
from selenium import webdriver
from selenium.webdriver import ChromeOptions 
from selenium.webdriver.chrome.webdriver import WebDriver
import os

downloadsFilePath = './downloads'
class WebDriverModule(object):
    def getChromeDriver(self)->WebDriver:
        return webdriver.Chrome(
            options=self.getCoromeOptions()
        )
    def getCoromeOptions(self)->ChromeOptions:
        options = ChromeOptions()
        prefs = {
            "plugins.always_open_pdf_externally": True,
            "profile.default_content_settings.popups": 1,
            "download.default_directory": 
                    os.path.abspath(downloadsFilePath) + r"\\", #IMPORTANT - ENDING SLASH V IMPORTANT
            "directory_upgrade": True
        }
        options.add_experimental_option("prefs", prefs)
        return options

webDriverModule = WebDriverModule()
driver = webDriverModule.getChromeDriver()
```
![DownloadDemo1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342307/67b3ed46-14d8-b58c-6237-642c3a785fe5.png)



この時、**plugins.always_open_pdf_externally=True** を設定することで、PDFファイルにアクセスした際に、default の Viewer を使う処理を制御して、ファイルを直接ダウンロードできます。

```test.py
from WebDriverModule import WebDriverModule
webDriverModule = WebDriverModule()
driver = webDriverModule.getChromeDriver()
fileUrl = 'http://www.soumu.go.jp/johotsusintokei/whitepaper/ja/h30/pdf/30daijin.pdf'
driver.get(fileUrl)
```
![DownloadDemo2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342307/7ec32981-ea72-ee13-3259-46c727e59b3e.png)


## ダウンロードディレクトリ内の最新ダウンロードファイル名を取得する。
os と max(list, key）を使用して、ダウンロード先内の全ファイルから「最新の日付のファイル」という条件で1ファイル取得します。

```WebDriverModule.py
downloadsFilePath = './downloads'
class WebDriverModule(object):
    def getLatestDownloadedFileName(self):
        if len(os.listdir(downloadsFilePath)) == 0:
            return None
        return max (
            [downloadsFilePath+ f for f in os.listdir(downloadsFilePath)], 
            key=os.path.getctime
        )

```

```test.py
print(webDriverModule.getLatestDownloadedFileName())
```

```出力結果.txt
python .\test.py
./downloads/30daijin.pdf
```

これで、PDFファイル名を取得することができました。


## まとめ

- ChromeDriver 立ち上げ時に、ダウンロード先を指定する。
- ダウンロード先の最新のファイル名を取得する。


これで、ダウンロードしたPDFファイル名を取得することができました。

以上です。
Docker+Python+Django WEB開発ハンズオン受講メモ(2019/09/03)

## 参加の目的
[業務システム開発のための開発言語／フレームワーク情報収集(2019年版)](https://qiita.com/yet103/items/d5c8c90b40ef6b518d33)の一環。

## ハンズオンセミナ概要
[Docker+Python+Django WEB開発ハンズオン](https://oneforall.connpass.com/event/141400/) 2019/08/19開催分

- 対象者
 - Webアプリ作成したい人
 - PythonでWEBアプリを作成したい人
 - Dockerで環境構築したい人
 - Dockerの仕組みを理解したい人

- 主な内容
Docker環境でWEB+DBを構築してWEBアプリ作成までのハンズオン。
    1.各OSごとの環境設定
    2.Docker環境構築（Django付属WEBサーバー＋Postgresql）
    3.Djangoアプリ作成
    4.Docker概要
    5.認証機能（時間あれば）

## 学べたこと
- Windows10環境にDocker環境を構築、docker-composeを実行できた
- DjangoによるWebアプリケーションの構成ファイル概要と、構築手順の一連を知ることができた

## はまったこと
- プロキシ問題
    - 自社NW環境での作業だったため、各所でプロキシの設定が必要。
        - "Docker Desktop" → Setting → Proxyes
        - Dockerfile 
            - RUN pip install の実行の前に、プロキシ設定の記述が必要。
            - `ENV http_proxy http://xxxx:xxxx@proxy.example.com:8080`
            - `ENV https_proxy http://xxxx:xxxx@proxy.example.com:8080`
        
- Shared Drives問題
    - `ERROR: Cannot create container for service web: b'Drive sharing seems blocked by a firewall'`
    - ハンズオン中にググって調べたことを手当たり次第に試行。([こちら](https://mseeeen.msen.jp/cisco-anyconnect-blocked-shared-drives-setting-in-docker-desktop-for-windows/)に一通りの対応候補が記載されていたので参考
    - 結局どれもダメだったので諦めて帰社。
    - 社内LANにつないだ状態でShared Drivesの設定を行ってみたら、あっさり成功。はっきりした原因は不明のまま。
シェルソート

##メリット
処理速度が速い

```java:ShellSort.java

class ShellSort{
  
  public static void main(Srring[] args){
   
   //ソート前の配列
   int a[] = {10,4,2,1,3,8,5,6,};
   
   //グループ分けの間隔を半分にしていく繰り返し
   for(int step = a.length / 2 ; step > 0; sttep /= 2){
      // 間隔を開けて挿入ソート
      // 配列から順番に1つずつ「挿入する値」を取り出すことを繰り返す
      for(int i = step; i < a.length ; i++){
         //挿入する値を変数に入れて退避
         int tmp = a[i];
         // 取り出した位置から前に向かって比較を繰り返す
         int j = i ;
         for (j = i ; j >= step; j -= step){
            //もし挿入する値が小さければその値をステップ幅だけ後ろへずらす
            a[j] = a[j-step];
         } else {
           //もし挿入する値が小さくなければ、そこでずらす処理を止める
           break;
         }
       }
       //ずらす処理が終わったところに「挿入する値」を入れる
       a[j] = tmp;
     }
   }
   //ソート後の配列の表示
   for (int i:a){
      System.out.println(i);
   }  
 }
}
```

参考書籍
[楽しく学ぶアルゴリズムとプログラミングの図鑑](https://www.amazon.co.jp/%E6%A5%BD%E3%81%97%E3%81%8F%E5%AD%A6%E3%81%B6-%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%A8%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E5%9B%B3%E9%91%91-%E6%A3%AE-%E5%B7%A7%E5%B0%9A-ebook/dp/B01N56TW48)
[ADODB、VBA、VB6]「引数が間違った型、許容範囲外、または競合しています。」というメッセージが表示される

# TL;DR

- 今時あまり役に立たないかもしれないADODBのエラーに関する情報
- 「引数が間違った型、許容範囲外、または競合しています。」というメッセージは、直接的な原因でない場合に表示される場合がある。
- 事例を2つあげる

# 例1：定義されていない定数を使用した場合

基本的には、vbaの環境で発生する。

例えば、以下のようなコードでエラーとなる

```vb
With CreateObject("ADODB.Stream")
        .Type = adTypeBinary '→ここでエラー
End With
```

VBAでは、adTypeBinaryなどの定数は定義されていないため、自分で定義する必要がある。
定義していない場合は、adTypeBinaryはEmptyの値になり今回のエラーが表示される。

## 対処

変数を定義する。

また、先頭にOption Explicitを記述すると、未定義の変数をエラーにできる

# 例2：パラメータクエリで、エラーとなるようなオブジェクトの引数をパラメータに設定した場合

```vb
Dim Cn As ADODB.Connection
Dim Rs As ADODB.Recordset
Dim Cmd As ADODB.Command
（中略）
Rs.MoveLast
Rs.MoveNext
Cmd.Parameters("p1").Value = Rs.Fields("v1").Value '→ここでエラー
```

`Rs.Fields("v1").Value`は、本来は、BOFまたはEOFがTrueとなっている旨のエラーが出る。
しかし、これをパラメーターに設定すると、表示されるのは、今回のエラーとなるため、原因の把握がしづらい。

## 対処

ロジックを見直す。
GoogleのAPIを使わずにSpreadSheetsのシート名一覧を取得するメモ


スクレイピングなので恒久的ではないです。
`docs-sheet-tab-caption`というクラスが振られてたのでそこから抽出できました。

サッとシート名(タブ名)の情報だけ欲しい時に使えそう。

## 準備

環境はNode.jsとaxiosです。

```bash
$ mkdir myapp
$ cd myapp
$ npm init -y
```

axiosインストール

```bash
$ npm i axios
```

## コード

```app.js
const axios = require('axios');
const url = `https://docs.google.com/spreadsheets/d/xxxxxxx/edit#gid=xxxxxxx` // スプレッドシートのURL

axios.get(url)
    .then(response => {
        const regexp = /docs-sheet-tab-caption">(.*?)<\/div>/;
        const tabstmp = response.data.match(new RegExp(regexp,'g'));        
        const tabs = tabstmp.map(tab => tab.match(regexp)[1])
        console.log(tabs); //結果
    })
    .catch(error => console.log(error))
```

## 実行

```bash
$ node app.js

[ 'シート1', 'ピボット テーブル 3', 'ピボット テーブル 1' ]
```

シート名っていうのかタブ名というのか、下の項目です。

> <img src="https://i.gyazo.com/5b048ac93cb5ad2d033130ee98411802.png" width="500px">
Pixi JS tips

#####その１　Spriteのremove  どこに配置したか分からなくなってもremoveできる

通常の方法

```JavaScript

let gazou = new PIXI.Sprite(PIXI.Texture.fromImage("img/MenuButton.png"));
app.stage.addChild(gazo);
app.stage.remove()
```

同じブロック内にある場合、困ることは少ないのですが、、遠い場所に設置したスプライトの場合、removeに困ることが多々あります。setTag等で削除もできるのですが、順番通りにしかremoveできないため困惑しました。
以下の方法で自由にremove可能です。

```JavaScript
this.parent.removeChild(this);
```

``````JavaScript
myContainer.parent.removeChild(myContainer);﻿
```

#####その2 Chromeは WebGLをサポートしていない？
```JavaScript 
<script src="//cdnjs.cloudflare.com/ajax/libs/pixi.js/5.0.3/pixi.min.js"></script>
```

なぜかCDNからとってきてPixi v5以上で試したら
```
Uncaught Error: WebGL unsupported in this browser, use "pixi.js-legacy" for fallback canvas2d support.
    at Function.e.create (Renderer.js:49)
    at Vr (autoDetectRenderer.js:36)
```
環境は、Chrome　バージョン: 76.0.3809.132　とWebServer　Chrome 200 OKというクロームアプリで動かしています。
なぜか、バージョンを下げたらうまく動きました。4.9で一部描画にバグ、4.8で今のところ問題なく稼働しています。

```
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.8.8/pixi.min.js"></script>
```
Open project構築時にcannot create directoryで怒られた話

## 事象

[こちら](https://qiita.com/terukizm/items/c94f06a40be55f65d4f9)の記事を読みながらOpen projectをdockerで構築した際、こんなエラーが出た。

```shell-session
[root@centos openproject]# docker-compose up 
Creating openproject_postgres_1  ... done
Creating openproject_memcached_1 ... done
Creating openproject_web_1       ... done
Attaching to openproject_postgres_1, openproject_memcached_1, openproject_web_1
postgres_1   | LOG:  database system was shut down at 2019-09-02 23:27:01 UTC
postgres_1   | LOG:  MultiXact member wraparound protections are now enabled
postgres_1   | LOG:  database system is ready to accept connections
postgres_1   | LOG:  autovacuum launcher started
web_1        | WARN: You are using a legacy volume path for your openproject data. You should mount your openproject volume at /var/openproject/assets instead of /var/db/openproject.
web_1        | INFO: /var/openproject/assets is empty, so /var/openproject/assets will be symlinked to /var/db/openproject as a temporary measure.
web_1        | mkdir: cannot create directory '/var/db/openproject/files': Permission denied
web_1        | mkdir: cannot create directory '/var/db/openproject/git': Permission denied
web_1        | mkdir: cannot create directory '/var/db/openproject/svn': Permission denied
openproject_web_1 exited with code 1
```

## 原因

```shell-session
web_1        | WARN: You are using a legacy volume path for your openproject data. You should mount your openproject volume at /var/openproject/assets instead of /var/db/openproject.
```

書かれている通りなのですが、
「volume pathが古いので/var/openproject/assetsを指定してね」
と言っているので修正します。

## 解決策

webのvolumeを修正。

```yaml:docker-compose.yml
version: '3'

services:
  web:
    image: openproject/community:latest
    depends_on:
      - postgres
      - memcached
    ports:
      - "30080:80"
    volumes:
      - pg-data:/var/lib/postgresql/data 
#      - ./data:/var/db/openproject
      - ./data:/var/openproject/assets
    environment:
      DATABASE_URL: "postgres://opuser:oppassword@postgres:5432/openproject?pool=10&encoding=unicode&timeout=5000&reconnect=true"
      SECRET_KEY_BASE: openproject_secret_key
      CACHE_MEMCACHE_SERVER: memcached
      CACHE_NAMESPACE: openproject
  postgres:
    image: postgres:9.6-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: openproject
      POSTGRES_USER: opuser
      POSTGRES_PASSWORD: oppassword
  memcached:
    image: memcached:1.5.7-alpine
volumes:
  pg-data:
```

あとは`docker-compose up`するだけ。
Kotlinの拡張関数でハマったこと

[拡張 - Kotlin Programming Language](https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/extensions.html)
リファレンスにある拡張関数について、ハマったことがあったので、調べてみました。

# 追加したNull許容レシーバーの処理が実行されない

例えば以下のソースコードがあったとします。

```kotlin
class C {
    fun foo() { println("member") }
}

// 今回実行したいNull許容レシーバー
fun C?.toSring(): String { return "extension" }

fun main() {
    var c: C? = null
    
    println(c.toString()) // extensionではなくnullが出力されてしまう
}
```

# Null許容型と非null許容型は明確に区別される

リファレンスによると、

> 拡張は、null許容なレシーバの型で定義できることに注意してください。このような拡張は、その値がnullの場合でも、オブジェクト変数で呼び出すことができ、かつその本体内で this == null をチェックすることができます。

だそうです。例えば以下のコードのように、

```kotlin
class C {
    fun foo() { println("member") }
}

// 今回実行したいNull許容レシーバー
fun C?.toSring(): String { return "extension" }

// CクラスにあるメソッドをNull許容レシーバーとして追加
fun C?.foo() { println("aaaaaa") }

fun main() {
    var c: C? = null
    
    println(c.toString()) // extensionではなくnullが出力されてしまう
    c.foo() // aaaaaa
    
    var c2 = C()
    c2.foo() // member
}
```

cはnullなので、Null許容レシーバーとして追加した方が呼ばれ、c2はメンバメソッドが呼ばれます。

この理論でいけば、toString()なんかも同様の結果になるはずですが、実際はそうはなりませんでした。

# toString()はAnyクラスに存在する関数だから？

Kotlinにおいて、全てのクラスはAnyクラスの子クラスであります。
[Any - Kotlin Programming Language](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html)

Anyクラスは、toString()や、hashCode()などのメンバメソッドを持っています。
また、拡張関数の特徴として、

> もし、あるクラスがメンバ関数を持つうえ、さらに、同じレシーバ型、同じ名前を有し、与えられた引数を受容可能な拡張関数が宣言されると、 常にメンバが優先されます。

があります。
そのため、拡張関数toString()を追加したとしても、すでにAnyクラスに定義済みのtoString()が呼ばれてしまっているため、反映されないのではと考えます。

拡張関数の説明に関しては、以下のスライドがわかりやすいと感じました。
[みんな大好き拡張関数 #kotlin_sansan - Speaker Deck](https://speakerdeck.com/boohbah/minnada-hao-kikuo-zhang-guan-shu-number-kotlin-sansan)
仮想環境を構築してADの確認をする

■目的
・ADの仕組みを実感する
・仮想環境とは何かを理解する

■作業内容
・Win10でHyper-VをONにする
・OS(ubuntsuT18.40)をインストールする
・ip address show などでIPアドレスを調べてpingを通したら、
　OSから仮想環境にはpingが届いたようだけど、逆は無理だった。
neovim を Windows 10 にインストールする最も簡単な方法

いろんなインストール方法がありますが、neovim を最もシンプルに Windows10 へインストールする方法をご紹介します。

[![neovim を Windows 10 にインストールする最も簡単な方法](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491413/485f4a79-2eb5-8b9f-70e2-492b773bddad.png)](https://vim.blue/neovim-install-simple/)
**[neovim を Windows 10 にインストールする最も簡単な方法](https://vim.blue/neovim-install-simple/)**


素の neovim を使うだけなら、このまま利用すれば簡単です。設定ファイルと一緒にUSBメモリに入れて持ち歩けば、ポータブルな neovim として利用することも可能です。（必要性は別として）

また、こちらの記事も参考になる情報だと思いますので、ぜひ参考にして下さい。

* [neovimとdein.vimをWindows 10にインストールする簡単な方法](https://vim.blue/neovim-dein-windows-10-install-simple/)
* [neovim の init.vim と ginit.vim の違いは？](https://vim.blue/neovim-init-vim-ginit-vim/)
* [Windows 10 で neovim 起動時のウインドウサイズを指定する方法は？](https://vim.blue/neovim-windows-10-window-size-startup/)
* [Windows 10 で neovim の設定ファイルを配置する場所はどこ？](https://vim.blue/neovim-setting-file-path/)
* [Windows 10 のの neovim で任意の設定ファイルを指定して起動する方法は？](https://vim.blue/neovim-windows-10-startup-by-optional-setting/)
* [neovim を USBメモリに入れて持ち歩くには？](https://vim.blue/neovim-potable-on-usb-memory/)


皆様の快適な vim / neovim ライフのお役に立てばと思います。
python+kivyのアプリをiPhone実機転送する際の注意点

# 前提
[こちらの記事(筆者作成)](https://qiita.com/tea63/items/32c53896e3fb50993c72)で`pong.py`を一度実機転送したのですが、`pong.py`は`python2`系で書かれている為、`python3`系のアプリだとまた少し工夫が必要なようです。
本記事では備忘の為に、対処内容を記載します。
~~また、lightningケーブルの調子が悪く、`python3系`の自作アプリの実機転送はまだできていません。~~
~~しかし、`XCode`の`iOSSimulator`では起動が確認できた為、大体OKだと思っています。~~
実機転送できました！

## 注意点1 `toolchain.py`のシバンを修正する
`toolchain.py`のシバンには`#!/usr/bin/env python`と記載されています。
[※`toolchain.py`はこちら。](https://github.com/kivy/kivy-ios/blob/master/toolchain.py)
私の環境で`env python`を起動すると`python2.7.10`が起動しました。
![スクリーンショット 2019-09-03 21.57.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204486/28cafab1-1655-835f-ce4a-6378fefd4777.png)

つまり、`toolchain.py`を起動する`python`を指定しなければ、`python2`系で実行されてしまいます。

### 解決方法
`env python`を`python3`系にする~~のが面倒だった~~方法が分からなかったので、`toolchain.py`のシバンを直接`python3`系の`python`に修正しました。


## 注意点2 `.kv`ファイルのファイル名に大文字は使用しない
当初、`bigApple.kv`のように複数の単語をCamelケースで連結した`.kv`ファイルを使用していました。
これは`Mac`上の起動では何ら問題なく動くのですが、`iOSSimulator`ではうまく動きませんでした。
症状として、`iOSSimulator`でアプリ起動後に真っ黒な画面がずっと表示されたままになります。ログには特に変わったことは何も出力されず、結構困りました。。

### 解決方法
おそらく大文字を使用していることが問題なので、`bigapple.kv`や`apple.kv`などとすれば解決します。
※当然のことながら、`if __name__ == '__main__':`内で`run()`を実行するクラス名と一致させる必要があります。

### 備考
@dario_okazakiさんの[こちらの記事](https://qiita.com/dario_okazaki/items/7892b24fcfa787faface)で@taashiさんがコメントされている内容と同じだと思います。


## 注意点3 `.kv`ファイルでの`main`の`import`方法
@dario_okazakiさんの[こちらの記事](https://qiita.com/dario_okazaki/items/32446faff803a067d700)で、`bookSearch.kv`の1行目に`#: import main main`という記載があります。
これをすると、`.kv`ファイルから`.py`ファイルの変数や関数などにアクセスすることができます。
これも`Mac`上では問題なく起動するのですが、`iOSSimulator`では`main`モジュールがうまく探せないようで、動きませんでした。

### 解決方法
`#: import main __main__`とすることにより解決しました。

## 注意点4 自作モジュールのインポート
`main.py`以外に自作のモジュールを用意して読み込ませる方法ですが、`iOSSimulator`ではうまくいきませんでした。
パスが通っていないことが原因でした。

### 解決方法
テンプレートファイルを修正して、パスを通します。
修正ファイルの配置場所：
`.../kivy-ios/tools/templates/{{ cookiecutter.project_name }}-ios`

修正ファイル
![スクリーンショット 2019-10-17 20.25.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204486/d9d304c1-b144-75c5-96f9-c2cdbea88e2a.png)

修正箇所
![スクリーンショット 2019-10-17 20.28.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204486/25f8ac40-031e-36d7-9731-81fd32317a5c.png)

修正内容(`main.py`と同階層にあるモジュールを読み込みたい場合)
`%@/YourApp`を追加します。
![スクリーンショット 2019-10-17 20.31.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204486/ac0e1877-b62c-bc8c-585d-bc9195d4e945.png)

### 解説
`toolchain.py`で`XCode`プロジェクトを`create`すると、(少なくとも私の環境では)下記ディレクトリにファイルたちが作成されます。
`~/Library/Developer/CoreSimulator/Devices/[UUID1]/data/Containers/Bundle/Application/[UUID2]/[アプリ名].app`
※`[アプリ名].app`はファイルではなくディレクトリ

`[アプリ名].app`は上記の`main.m`で言う`%@`に当たります。よって、パスが通っています。
しかし、`[アプリ名].app`に入ってみても、`main.py`や`python`モジュールは見当たりません。

そこで`[アプリ名].app`内の`YourApp`に入ってみると、`main.pyc`や`python`モジュールを見つけることができます。
つまり、ここにパスを通せば読み込めます。

ちなみに、`kivy`のライブラリは`[アプリ名].app/lib/python3.7/site-packages/kivy`にあるので、パスが通っており使用できます。
＃`python`のバージョンは人によって異なるかもしれません。

他に何か注意点を見つけたら追記していきます。
phpを使ったjsonファイルの出力

# はじめに
jsonをphpで読み込んで表示する機会があったのでここに書き留めておきます。

# 概要
1. jsonファイルを用意
2. phpで読み込み
3. 出力
   
# 手順

## jsonファイル作成
まず、出力するjsonファイルを用意します。  
今回は`data.json`を作成します。

```json
[
    {
        "name": "Aさん",
        "department": "営業",
        "text": [
            "今月の売り上げは先月の2倍の見込みです",
            "先月の遅れを取り戻します"
        ]
    },
    {
        "name": "B",
        "department": "人事",
        "text": [
            "就活生とお話する機会があって楽しいです"
        ]
    },
    {
        "name": "C",
        "department": "経理",
        "text": [
            "1桁のミスも許されない世界です"
        ]
    }
]
```

## phpで読み込み

```php
<?php
$file = "./data.json";
$json = file_get_contents($file);
$data = json_decode($json, true);
?>
```

- `$file`：jsonファイルへのパス
- `$json`：`file_get_contents`関数を用いて、`$file`の内容を文字列として読み込む
- `$data`：`$json`の内容を連想配列として`$data`に格納する

`$data`にどのような感じでjsonデータが格納されているか挙げると、

```
$data[0]["name"] => "A"
$data[0]["department"] => "営業"
$data[0]["text"][0] => "今月の売り上げは先月の2倍の見込みです"
$data[0]["text"][1] => "先月の遅れを取り戻します"
$data[1]["name"] => "B"
$data[1]["department"] => "人事"
・・・
```

このように、phpの連想配列として`$data`に格納されています。  
`echo var_dump($data)`  
と打つと、連想配列の様子が確認できます。

## 出力
最後に、htmlとして出力をします。  
ここまでくれば、あとは連想配列の出力と同じです。

```php
<?php
for($i=0; $i<count($data); $i++){
    print<<<EOT
    <div style="margin: 10px; padding: 5px; border: solid 1px #000000;">
        <p>名前：{$data[$i]["name"]}</p>
        <p>所属：{$data[$i]["department"]}</p>
        <p>
EOT;
        for($k=0; $k<count($data[$i]["text"]); $k++){
            print<<<EOT
            {$data[$i]["text"][$k]}<br>
EOT;
        }
        print<<<EOT
        </p>
    </div>
    
EOT;
}
?>
```

`text`のところ(2回目のfor文)で、そこが配列になっているのでやや複雑になっています。  
結果をブラウザで見ると、

<img width="934" alt="json.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488154/73f05cd9-8eb5-b84e-6b58-d4c8450660e4.png">

と、出力されているのが確かめられました！

# 最後に
今回の場合はjsonファイルのデータ容量がそれほど多くなく、言ってしまえばphpに連想配列を直書きできる内容でした。  
もしそのような場合はjsonを使う必要はないと思います。  
ただ、データ数や項目数が多い場合は、データを格納する専用のファイルとしてjsonを使うのをお勧めします。  
(データベースの知識があれば、MySQL等を使ったほうが良いと聞くのですが、私はまだそのスキルが十分ではないのでjsonを使っています)
Vue.Use したのに動かない

# 概要
* Plugin を Vue.Use に渡したのに install が実行されていない挙動。
* 原因は require, import の挙動の違い
* [参考](https://qiita.com/rooooomania/items/4c999d93ae745e9d8657#export-default--require)

# require, import の違い
```javascript
const Vue = require('vue')  // 1
import Vue from 'vue'       // 2
const { default: Vue } = require('vue') // 2 と同じ
```

* 1 ではモジュール自体を Vue に格納している


```javascript
const Vue = require('vue')  // 1
console.log(Vue)
```

```console
Module {default: ƒ, __esModule: true, Symbol(Symbol.toStringTag): "Module"}
default: ƒ Vue(options)
Symbol(Symbol.toStringTag): "Module"
__esModule: true
__proto__: Object
```

# エラーが出なかったのはなぜ
* [PluginInstall するところ](https://github.com/vuejs/vue/blob/52719ccab8fccffbdf497b96d3731dc86f04c1ce/src/core/global-api/use.js#L15)
* plugin が undefined ではないので、undefined エラーが出ずにインストールした気になる。
9/3 Tech Expert (事前学習) day 4　/　もぅ時間気にするのやめて、ゆっくりやります。。。

【学習アウトプット】

##今日の目標(やること)
昨日勉強した分を復習
Rubyを終わらせる

#今日のまとめ
###定数
名前の最初を大文字
再代入NG

###返り値(戻り値)
関数や処理が呼ばれ、その一連の処理が終了したときに呼び出し元に返す値
応え。結果。
putsメソッドの返り値はnil


#アプリケーションの作成
VSCでファイル作成
〇〇App.rb　 （レビュー管理）アプリケーション　（例：reviewApp.rb）
sample.rb　 （レビュー管理）アプリケーション以外のソースコード（練習用？？？）


一度閉じたファイルの開き方
① テキストエディタを開く
② 「command ＋ o」 のショートカットキーを使い、ファイルを開く


###変数
区切り線でも変数使える
line = "---------------------------"




##STEP1 : レビューを表示させてみよう
コードを段階を追って短くシンプルにしていくよー
（確認したら、課題をもう一度やってみる）

###１，決められたレビューを表示
『文章をreviewAppに入力。ターミナル上で確認。　ターミナル　：　$ ruby reviewApp.rb』
(大ヒント：全行の最初にputsを。毎行すべて""で囲む。改行には""のみ。)


###２，ソースコードを改善
バックスラッシュ記法(またはエスケープシークエンス)
記法 意味 
\n 改行
\t タブ
\b バックスペース
\\ バックスラッシュ

""で囲む（'' NG）


puts "あかちゃんは可愛い"
puts ""
puts "身内が一番"
↓（同じ）
puts "あかちゃんは可愛い\n\n身内が一番"

『改行をバックスラッシュ記法で書き直してみる。』
（ヒント：\n使うと、感想が一行にまとまる。）

###３，変数
可読性（読みやすさ）を上げるため変数を使用。
『レビューの「ジャンル」「タイトル」「感想」「区切り線」をそれぞれ変数に入れて使ってみましょう』
(ヒント：最初に定義する。ジャンルとタイトルで＋を使う)

###４，式展開
書き方＝文字列中で#{式}
文字列、数値、変数、メソッド呼び出し、演算子式などのオブジェクト
式展開を使えば、文字列の中に式をそのまま入れることが出来る
文字列の結合をしてくれる。+で書くよりも楽
""で囲む（'' NG）

『文字列の結合を+から式展開に変更』
（区切り線を出力するコード変数もバックスラッシュ記法と式展開を使えば一行にまとめることができます。＝３行でまとまる。）


##STEP2 : レビューを書けるようにしよう

getsメソッド
ユーザーから入力された値を表示
入力が完了するまでプログラムの処理を中断する

chompメソッド
getsメソッドで返ってきた文字列オブジェクトは末尾にバックスラッシュ記法の\nをつけた状態で返してしまうため、文末で改行してしまう。
chompメソッドは、文末の改行を取り除く


##STEP3 : ハッシュを使って情報を整理しよう
（ダイジェストで学習中…）


##〜独り言(まとめやら感想やら)〜
一回目はさっと通して次々行くべき。分かってはいるが、理解出来なさすぎて、結局、このスピード。。。
短期集中コース、９割型諦めました苦笑
せっかくだから事前学習の期間でどこまですすめられるか試しながら、背丈にあったところを探そうと思います。
肩の荷が下りて、残りの期間は楽しめそうだ笑
RailsでURLをランダム文字列にする方法

# はじめに
この記事では、RailsでURLをランダム文字列にする方法をご紹介します。
よくUserモデルを作って、Userのプロフィールを表示するのにURLを`https://~~~.com/users/1`のようにidをそのままにして使用していると思います。

このidの部分をランダムな文字列にして実装しようと思います！

**前提：Railsの環境構築が終わっている**

# URL用のカラムを追加する
ここではUserモデルを使って説明していきます^^
まず、UserテーブルにURL用のカラムをターミナルで追加します。
URLのカラム名として`url_token`を指定しました。ここにidの代わりに使用するランダム文字列を入れます。

```
$ rails generate migration add_url_token_to_users url_token:string
$ rails db:migrate
```
忘れずにmigrateもしましょう!!

# routesにparams: :url_tokenを追記する
resouceを使っていることを前提とすると、下記のようになります。
resouceを使っていないのであれば、おそらく`get 'users/show/:id' => "users#show", param: :url_token`で動くのではないかと思います。(動作確認しておりません)

```
# config/routes.rb
resources :users, param: :url_token
```

# Userモデルにto_paramを追加する
`to_param`はリソースのユニークなURLを発行する時に使うものです。
この中にどのカラムをURLに使うか指定します。ここでは、`url_token`ですが、例えば`name`とかも指定できます。

```
# app/models/user.rb
class User < ApplicationRecord
  validates :url_token, presence: true, uniqueness: true

  def to_param
    url_token
  end
end
```

# コントローラーでUserの取得方法を変更する
以前まではUserを取得する時idを指定して取得していたとします。ここでは【前】の部分のコードになります。
【次】がurl_tokenを指定して、Userを取得する場合のコードになります。
取得方法は【前】と似ていますね^^
使い方に関しては問題ないかと思います。

```
# app/controllers/user_controller.rb
【前】
class UserController < ApplicationController
  def show
    user = User.find(params[:id])
  end
end

【次】
class UserController < ApplicationController
  def show
    user = User.find_by(url_token: params[:url_token])
  end
end
```

# Userモデルのurl_tokenのランダム初期値を設定する
またモデルに戻って、url_tokenのデフォルト値をランダムに設定します。

```
class User < ApplicationRecord
  attribute :url_token, :string, default: SecureRandom.hex(10)

  validates :url_token, presence: true, uniqueness: true

  def to_param
    url_token
  end
end
```

# 終わりに
以上が、RailsでURLをランダム文字列に設定する方法でした！
Railsに慣れてくると、少しずつ視野が広くなって、どんどん楽しくなっていきますね！
Twitterでも、技術に関することを毎日発信してるので、よろしければフォローしてください^^
Twitter → https://twitter.com/TakeshiOnaga
Leetcode #947: Most Stones Removed with Same Row or Column

```swift
func removeStones(_ stones: [[Int]]) -> Int {
    var xDict = [Int: [Int]]()
    var yDict = [Int: [Int]]()
    for stone in stones {
        xDict[stone[0], default:[]].append(stone[1])
        yDict[stone[1], default:[]].append(stone[0])
    }
    var visited = Set<[Int]>()
    var answer = 0
    for stone in stones {
        var count = 0
        if !visited.contains([stone[0], stone[1]]) {
            var xArr = [stone[0]]
            var yArr = [stone[1]]
            while !xArr.isEmpty || !yArr.isEmpty {
                if !xArr.isEmpty {
                    let x = xArr.removeFirst()
                    let ar = xDict[x]!
                    for num in ar {
                        if !visited.contains([x, num]) {
                            yArr.append(num)
                            count += 1
                            visited.insert([x,num])
                        }
                    }
                }
                if !yArr.isEmpty {
                    let y = yArr.removeFirst()
                    let ar = yDict[y]!
                    for num in ar {
                        if !visited.contains([num, y]) {
                            xArr.append(num)
                            count += 1
                            visited.insert([num,y])
                        }
                    }
                }
            }
            count -= 1
        }
        answer += count
    }
    return answer
}
```
【ポエム】AWSの"AMI"の読み方

**この記事は中身の薄いポエムです**
# この記事を書いたきっかけ
普段上司と話すとき、私はAWSのAmazon Machine Image、通称「AMI」のことを、"アミ"と発音していました。

ところが先日他部署の方と打ち合わせをした時、「アミ」と言うとポカンとされてしまいました。どうやらその方はAMIのことを「エー・エム・アイ」と発音するようでした。

AMIの読み方は「アミ」と「エー・エム・アイ」、どちらが正しいのか、どちらが主流なのか、気になったので軽く調べました。
# "アミ"か"エー・エム・アイ"か
[AWSの公式の資料](https://aws.amazon.com/jp/blogs/news/webinar-bb-ec2-2018/)によると、どちらでもいいみたいです。
> Q13. AMIの読み方は、エーエムアイ、アミ、どちらが正解でしょうか。
A. 正確には”Amazonマシンイメージ”ですが、略称としてはどちらでも呼ばれるケースがあります。

公式にはどちらでもいいことはわかりましたが、実際に人々はどのように発音しているのか調べてみました。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">It’s time again: How many syllables does AMI have?</p>&mdash; Corey Quinn (@QuinnyPig) <a href="https://twitter.com/QuinnyPig/status/1098511266002264066?ref_src=twsrc%5Etfw">February 21, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

2019年9月3日現在916人が回答した上のアンケートでは、8割以上の方が「エー・エム・アイ」と発音するようでした。
ただし、このアンケートは海外の方の声が大きく反映されている雰囲気であり、日本人がどのようにAMIを発音するのかには手掛かりにならなさそうです。
# 最後に
この記事を読んだ皆様が、AMIをどのように読んでいるのか気になります。コメント欄に書いていただけると嬉しいです。
PowerShellで.ps1ファイルをIncludeしたいのだけど

# 事の顛末
Windows Server上で動くプログラムが必要になったので、PowerShellで書いていたのですが、行数増えてきて見通しが悪くなって気持ち悪くなってきたのと、同じような処理をまとめたくなってきてしまったので、一部の処理を別の.ps1ファイルに分離したくなった次第であります。ちなみにPowerShellは初心者レベルですので、基本ググりながらがんばっている感じです。


# 結論
ピリオドの後に呼び出したいFileのPathを書けばいいようです。Includeっていうわけではないけれど、呼び出した.ps1が同一コンテキストの中で実行されたことになるみたい。

```
. ".\some-script.ps1"
```
ところで、このピリオド何の意味ですか? ああ、Unix系のOSのShellだと、これで普通なんですね。

# 結局どうしたかというと
いろいろコードをいじりまくった結果、class化したコードを外部のScriptに追い出して、そいつを . でIncludeしたみたいな形にしました。あー、スッキリ。

```some-class.ps1
class Something{
    SomeThing(){ ... }
    ToDo(){ ... }
}
```

```main.ps1
. ".\some-class.ps1"
$someClass = NewObject Something
$someClass.ToDo()
```

# 参考文献
[PowerShell再入門：13. 外部ファイルを実行する](https://m0t0k1x2.tumblr.com/post/122486987174/powershell%E5%86%8D%E5%85%A5%E9%96%8013-%E5%A4%96%E9%83%A8%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B)
[【 source 】コマンド／【 . 】コマンド――シェルの設定を即座に反映させる](https://www.atmarkit.co.jp/ait/articles/1712/21/news015.html#abs)
素のJavaScriptでアクションゲームを作ってみた

# はじめに
完全に素のJavaScriptで、アクションゲームを作ってみました。
（素というのは、jQuery等ライブラリを使用していないということです）
作ったのは去年なのですが、今更ながら紹介しようと思い立ち、記事にしました。

ゲーム本体
https://holothuria.github.io/js_game/src/

実装
https://github.com/holothuria/js_game/tree/master/src

# どんなゲームか

### 概要
主人公をジャンプさせてゴールに辿り着く、という、ただそれだけのゲームです。
（縦スクロール）


### 操作説明（クリックでもタップでも同じです）
##### ・基本
画面右半分をクリック：右側にジャンプ
画面左半分をクリック：左側にジャンプ
※空中では基本1回だけ空中ジャンプが可能
##### ・壁張り付き時
壁側の画面半分をクリック長押し：壁登り
壁と逆側の画面半分をクリック：壁キック


![画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451284/41faed2e-bb79-e492-be79-58c67247c8ce.png)


# 実装について
## 描画処理
描画は、htmlのcanvas要素を利用し、JavaScript側で描画しています。
canvas要素を重ね合わせ、レイヤー上にすることで、地形の描画と主人公の描画を分けています。

基本的に、常に描画し直したりはせず、変化があったところのみ描画し直すようにしています。
ただ、主人公は動き続けるので、45ミリ秒間隔で周辺のcanvas要素をクリアし、現在地に描画し直しています。

地形は、主人公が進んだ際に、描画し直します。
現在のcanvas要素全体を画像として取得し、取得した地形画像を下にずらしてcanvas要素に貼りなおします。
その結果上に空いた余剰スペースに、新たに見えるようになった地形を描画するようにしています。
ちなみに、下にずらした一番下の行は、canvas要素からはみ出た場所に貼られることになるので、勝手に消えます。


## 地形について
地形は、2重配列で持っています。
何もなければ0、壁なら1、という感じです。

また、特殊な地形に触れた場合、特定のメソッドが呼ばれるようになっています。
それによって、棘に触れたらゲームオーバー的処理や、ブロックによっては大ジャンプ、みたいな処理を実現しています。


# 終わりに
大したことのないゲームなので、ルールも実装も説明するような点もあまりありません。
暇つぶしにすらならない気がしますが、あまりにも暇な人がいらっしゃれば、ぜひやってみてください。
ただ、なぜか壁抜けバグ等が発生することがあり、突然予測不能な動きをするかもしれません。ご了承ください。
VimのESCキーの代わりになるキーバインドは何種類？

Vimでは、他のモードからノーマルモードに戻るためにESCキーを多用します。そのため、最も重要なキーの1つと言えます。

そのVimで、プラグインを使わずに「ESCキーの代わり」をするキーバインドについて紹介したいと思います。
興味のある方は、下記をご覧ください。

[[vim問題] ESCキーの代わりになるキーバインドは何種類？](https://vim.blue/vim-esc-altanative-keybind/)

覚えると重宝しますので、ぜひ正確に覚えましょう。

この他にも下記の情報も参考になるはずです。

* [インデントを1つずらすキーバインドは？](https://vim.blue/vim-indent-left-right/)
* [入力を補完するキーバインドは？](https://vim.blue/vim-complement/)
* [指定した範囲を大文字または小文字に変換する方法とは？](https://vim.blue/vim-upper-and-lower/)
* [ドットコマンドとは何か？](https://vim.blue/dot-command/)
* [「最近使ったファイル」を開く方法とは？](https://vim.blue/vim-oldfiles-browse/)

皆様が快適な vim / neovim ライフを送れますように。
【Unity】Viveで左右に別々の映像を表示する手順

Viveで左目と右目にそれぞれ別の映像を設定したいことがありましたのでまとめます。
なお、SteamVRのSampleSceneの動作までは確認できていることを前提とします。

## 環境
Unity 2019.1.12f
HTC Vive Pro
StermVR Plugin ver.2.3.2

# 手順
右目に赤のSphereを、左目に緑のSphereを表示させてみます。

1. Sceneを作成し、MainCameraを削除します。
1. SceneにEmpty Objectを2つ配置し、それぞれの名前を"CamRight","CamLeft"とします。
1. 先ほど追加した"CamRight","CamLeft"にAssets/SteamVR/Scripts/SteamVR_Cameraをアタッチします。
1. "CamRight"を開き、SteamVR_CameraのExpandをクリックします。すると"CamRight(origin)">"CamRight(head)">"CamRight(eye)"のように子オブジェクトが生成されるはずです。"CamLeft"に対しても同じ作業を行います。
1. ここで、Edit > Project Settings > Tags and Layers からUser Layerに"Right Eye"と"Left Eye"をそれぞれ追加します。先に言っておくとこのレイヤーがそれぞれ右目と左目の映像に対応します。
1. Sceneに戻り、先ほどの"CamRight(eye)"を開きます。まず、Target Eyeを"Right"に設定します。次に、Culling Maskから"Left Eye"を外します。
1. "CamLeft(eye)"に対しても**RightとLeftを反対に**、同様の作業を行います。
1. 次にそれぞれのレイヤーにSphereを追加します。まずAssets下にMaterialsフォルダを作り、赤と緑のマテリアルを作成します。次にSceneにSphereオブジェクト"SphereRed","SphereGreen"を配置し、赤と緑のマテリアルをそれぞれアタッチします。
1. "SphereRed"を開き、Layerを"Right Eye"に設定します。同様に、"SphereGreen"のLayerを"Left Eye"に設定します。
1. このままではCameraがSphereの中に入っているので、Sphereのz座標をそれぞれ2.0程度に設定してあげます。
1. Sceneを実行します。

以上の手順で右目に赤のSphere、左目に緑のSphereが表示されるはずです。（VR上では色が重なってチカチカして見えるはずです。）
右目、左目にオブジェクトを追加したい場合は、追加したオブジェクトにLayerを指定すればOKです。
![2019-09-03 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479958/49ad21ab-46e9-481b-2695-27203dee3372.png)


## 参考
https://forum.unity.com/threads/steamvr-how-to-display-content-in-left-right-eye.396236/
Laravelでキャッシュクリアできず.envを反映できない時の対応方法

- 環境
    - Windows 10
    - Laravel Framework 5.5.40
    - PHP 7.3.7 (cli)

# 事象 : .envでDBの設定を変更したのに`cache:clear`でエラーになる
.envに誤ったDB設定をしてしまったので書き直したが`cache:clear`でエラーになる。
同じ設定でDBのクライアントツールでも他の環境でも問題なく接続できる。

```bash
$ php artisan cache:clear

In  Oci8.phpline 464:

  ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。
```

# 原因 : キャッシュクリアができなくて.envの設定が反映されていないから
エラーを出している Oci8.php でデバックして確認するとDBの設定が古いままになっている。キャッシュがクリアできない!

## やってみて失敗したこと : storageディレクトリにあるキャッシュ用ディレクトリの中身を削除した
`php artisan cache:clear` ができないので[config/cache.phpで指定しているキャッシュのディレクトリ](https://qiita.com/ponsuke0531/items/f0b68546068ac8922665)配下を削除してみた

```bash
# storageディレクトリにあるキャッシュのディレクトリを確認する
$ cat config/cache.php | grep storage_path | grep framework
            'path' => storage_path('framework/cache/data'),

# 中身を全削除する
$ rm storage/framework/cache/data/*

# 事象は何も変わらなかった
$ php artisan cache:clear

In  Oci8.phpline 464:

  ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。
```

### 失敗の原因 : 削除するキャッシュのファイルが違うから
[.envファイルの読み込みは，php artisan config:cacheしていない場合にしか行われません！ - mpyw&#39;s tech blog](https://mpyw.qrunch.io/entries/nyuqLPQzA2ej0Bo1)
というのを読んで、`php artisan config:cache` をやめたらいいのかと思った。

[設定ファイルのキャッシュを止めることはできません。 - コードログ](https://codeday.me/jp/qa/20190119/154959.html)
というのを読んで、どうしたものかと思いとりあえずLoadEnvironmentVariablesをデバックしてみた。

そして、bootstrap/cache/config.phpというファイルがあれば.envは読み込まれず、なければ.envを読み込むことを知った。

```php:LoadEnvironmentVariables
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        if ($app->configurationIsCached()) { // <<< ここの中身をデバックして設定ファイルの場所がわかった
            return;
        }

        $this->checkForSpecificEnvironmentFile($app);

        try {
            (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
        } catch (InvalidPathException $e) {
            //
        }
    }
```

## 対応方法 : bootstrap/cache/config.php を削除する
```bash
# config.phpを削除する
$ rm bootstrap/cache/config.php
# うまく行った!
$ php artisan config:clear
Configuration cache cleared!
```