OmniAICreator/Qiita-1.07M huggingface dataset:
Jbuilderの基本

筆者はRails + Vue.jsを使っていますがjQueryでもJbuilderの設定は同じだと思います

Vueファイルにアソシエーション先の情報(今回はツイートの投稿者)を取ってきたいけど
JSでは以下(4行目)では取ってくることができません

```.vue
<template>
  <div id="app">
    <div v-for="e in tweets" :key="e.id">
      {{e.user.name}} <!--投稿者-->
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data: function() {
    return {
      tweets: []
    }
  },
  mounted() {
    axios
    .get('/api/v1/tweets.json')
    .then(response => (this.tweets = response.data))
  }
}
</script>
```
そこでjbuilderを使います


##コントローラー
筆者はapiを使っています
app > controller > api > v1 > tweet_controller
3行目はindex.json.jbuilderファイルを参照します、というような記述です

```.rb

def index
  @tweets = Tweet.all
  render 'index', formats: 'json', handlers: 'jbuilder'
end
```

##jbuilder
app > views > api > v1 > index.json.jbuilder
tweet.user.nameをvueファイル内ではxx.nameという記述で使えるようにする

```.rb
json.array! @tweets do |tweet|
  json.name tweet.user.name
end
```

##Vueファイル
これでe.nameで投稿者を表示できる

```.vue

<template>
  <div id="app">
    <div v-for="e in tweets" :key="e.id">
      {{e.name}} <!--投稿者(tweet.user.name)と同じ意味-->
    </div>
  </div>
</template>

以下略
```
「20分でLaravel環境を構築するDockerハンズオン」で作成した環境にphpMyAdminを導入する

### はじめに
現在Dockerを学習しているのですが、[「20分でLaravel開発環境を構築するDockerハンズオン」](https://qiita.com/ucan-lab/items/56c9dc3cf2e6762672f4)という記事に大変お世話になっています。
今回は上記の記事に従って構築した環境に、追加でphpmyadminを導入してみました。

dbコンテナを生成し、"[GitHubにpush](https://qiita.com/ucan-lab/items/56c9dc3cf2e6762672f4#github%E3%81%ABpush)"までのハンズオンを終わらせた想定です。



#### コンテナ構成

```
├── app
├── web
├── db
└── phpmyadmin <-今回追加
```

#### ディレクトリ構成

```
.
├── README.md
├── infra
│   ├── mysql
│   ├── nginx
│   ├── php
│   └── phpmyadmin <- 追加
│       └── sessions <- 追加
├── docker-compose.yml
└── backend

```

#### docker-compose.ymlに追記する
phpMyAdminコンテナを生成するための設定値を追記します。
`localhost:3000`でphpMyAdminの管理画面にアクセスできる設定です。
ユーザIDとパスワードは上記の記事のハンズオン環境のものを設定しています。

```docker-compose.yml
version: "3.8"
services:
  app:
    [省略]

  web:
    [省略]

  db:
    [省略]

  # 追記
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    depends_on:
      - db
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOSTS=db
      - PMA_USER=phper
      - PMA_PASSWORD=secret
    ports:
      - "3000:80"
    volumes:
      - ./infra/phpmyadmin/sessions:/sessions

volumes:
  db-store:

```

・phpMyAdmin公式のDockerイメージ
https://hub.docker.com/r/phpmyadmin/phpmyadmin/


#### ディレクトリを生成する
コマンドはハンズオンの記事と同じ場所(`docker-compose.yml`があるディレクトリ)で入力してください。

```
[mac] $ mkdir -p infra/phpmyadmin
[mac] $ touch infra/phpmyadmin/sessions
```


#### build & up

```
[mac] $ docker-compose down
[mac] $ docker-compose up -d --build
```

#### コンテナ確認

```
[mac] $ docker-compose ps
```
`app`,`db`,`web`,`phpmyadmin`の4つのコンテナがあるか確認してください。
`State`が全て`UP`であれば起動しています。

下記URLからphpMyAdminの設定画面が表示されれば成功です。
[http://localhost:3000/](http://localhost:3000/)

ハンズオンの通りに行うと、左に「laravel_local」というテーブル表示があリます。

### 終わりに
記事は以上となります。
ディレクトリを生成する部分は「セッション情報をボリュームに設定し、永続化させる」という設定のようなのですが、仕組みについて十分に理解できていないためDockerについての学習を進めて引き続き編集していこうと思います。

指摘点あればご教示いただければ幸いです。ありがとうございました。


### 参考
- 【超入門】20分でLaravel開発環境を爆速構築するDockerハンズオン
https://qiita.com/ucan-lab/items/56c9dc3cf2e6762672f4

- Docker Composeでphpmyadminを導入する
https://zukucode.com/2019/06/docker-compose-phpmyadmin.html
YouTubeをSaaSを使わずにCLIでダウンロード with Docker

YouTubeのダウンロード方法として、ブラウザからダウンロードできるサイトは毎回見つけては閉鎖されてたりしますよね

[pytube/pytube](https://github.com/pytube/pytube)はstart数が多く継続的に使えると思います。
[![GitHub stars](https://img.shields.io/github/stars/pytube/pytube.svg)](https://github.com/pytube/pytube)

今回はDockerfileを書かずに、pytubeを使います

## pytube imageの作成

```bash
docker run --name tmp-python python:latest bash -c "python -m pip install pytube"
docker commit tmp-python python-with-pytube
```

## ダウンロード
引数などは[pytube/pytube](https://github.com/pytube/pytube)のREADMEを見ましょ!!
カレントディレクトリに動画がダウンロードされてるはずです

```bash
docker run --rm -it -w /root -v $(pwd):/root python-with-pytube bash -c "pytube https://www.youtube.com/watch?v=2lAe1cqCOXo --itag=22"
```
【AWS】aws-sam-cli をWindowsでbashから使うメモ

# 問題

[AWS SAM CLI](https://github.com/aws/aws-sam-cli) のWindows版をインストールすると、バッチファイルの「sam.cmd」が追加され、PowerShellやコマンドプロンプトから利用できる。[^1]

[^1]: SAM とは「Serverless Application Model」。Lambda等のサーバーレスアプリのビルド、デプロイ機能に特化した CloudFomation の拡張サービスです。


でも普段、Windows上で GitBash や WSL2 等の bashをメインに使っていると、cmdファイルは実行できなくてイラっとすることに。

# 環境

* Windows10 Pro
* SAM CLI version 1.17.0


# シェルを作る

cmdファイル内は Python を実行しているだけ。


```batchfile:sam.cmd
@rem
@echo off

setlocal

"%~dp0/../runtime/python.exe" -m samcli %*
```
<br>
同等のシェルを作って、同階層に配置します。

デフォルトでインストールすると、「C:\Program Files\Amazon\AWSSAMCLI\bin」にsam.cmdがいるので、同じ場所に以下を「sam」という名前で作ります。

```shell:sam
#!/bin/bash

SELF_DIR=$(dirname "$0")

"$SELF_DIR/../runtime/python.exe" -m samcli $*
```

おしまい。
Reactでjson形式のapiを読み込む

## 動作環境

React 17.0.1
npm 7.5.2
OS catalina

## 二種類のやり方紹介

__axiosを使って読み込む方法__

まずaxiosをインストール

```
npm install axios
```

ソースコード

```react

useEffect(() => {
    axios.get('https://jsonplaceholder.typicode.com/posts')
        .then(res => {
        setPosts(res.data)
    })
})
```

__JavaScriptの fetchメソッドを使う方法__

```react

useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts', {method: 'GET'})
        .then(res => res.json())
        .then(data => {
            setPosts(data);
        })
    })
```
Pardot Lightning アプリケーションを使用したエンゲージメントの向上とリードの育成 ⇒ Pardot のリード育成 (Pardot Lightning アプリケーション) ⇒ リード育成の価値の理解

１．リード育成とは何ですか?
２．次のうち、リードの育成の実務の例はどれですか?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/6b1f8575-992c-397d-59c7-9e59ead5ca60.png)
ググり力を伸ばす３つのポイント ～新人向けに簡潔に説明したメモ～ 

※複数サイトを参考に自分の主観もいれつつ箇条書きしたメモ
# ググリカを伸ばす３つのポイント
 1. キーワードの選定
 - 検索結果の判断
 - エラーログ、Twitter、原典も参考にする

## 1．キーワードの選定
#### 単語で検索する（キホン）


#### 正常系、異常系の検索の区別
   - 正常系：本来の仕様、手法がヒットする
      - 例：「○○  使い方」「○○  example」
   - 異常系：ある状況のトラブルと解決策の例がヒットする
      - 例：「○○  動かない」「○○ not working」
    - ”使い方を知らない状態で適当にやって動かせない”からといって
      「○○  動かない」で検索してはいけない。まずは正常系を知る。
      （こういう人結構いる）


#### 検索結果をフィルタする観点
   - 汎用的な単語のみで検索しない
      - 例：C言語について調べたいとき、×「C」  ○「C lang」


   - 「解決したいこと＋自分の状況」で検索する
      - 例：「maximum recursion depth exceeded python3」
        ※エラーログ＋ソフト・ミドル・OSのバージョン）


   - 日付で絞る
      - 使っているソフトのバージョンが2020年に公開されたなら検索結果を2020以降で絞る等


   - エラーログは検索結果を絞れそうなものを取り出して検索する
      - 主に先頭か末尾の文章から抽出（先頭には表層原因、末尾には根本原因がありがち）

#### 英語でググる
   - 情報量が多い
   - トラブったときの便利組み合わせワード
    - 「not working, example, ignored, without, not runnning, vs, best practice, custom」



## ２．検索結果の判断
#### 見出しで推測
   - 自分と同じ状況か
      - OSは同じか、バージョンは同じか、エラーログは同じか・・・
   - QAサイトの自動翻訳ページではないか、オリジナルのページか 
      - 翻訳サイトを見ないほうが良い理由１：ソースコードなども翻訳されている可能性がある
      - 翻訳サイトを見ないほうが良い理由２：自動翻訳サイトが乱立しすぎて、実質同じ内容のページを何回も開く羽目になる
   - クソサイトを見分ける嗅覚
      - やたら広告が多いサイトは利用者も少ないはずだから情報の信憑性も怪しいのではないか等

   - 記事の投稿日
    - 古いページの情報は古い（真理）


#### 3ページ目以降の検索結果は参考にならない
   - ググリカが高いと検索結果の1,2ページ目で事足りる
    - ただし、大量にデータを集めたいときは数ページめくったほうがいい



## ３．エラーログ、Twitter、原典も参考にする
#### エラーログを冷静に読みなおす
   - そこにエラーの原因が書いてある。
    - エラーの原因が書いていないこともある  (`Д´)


#### Twitterで検索する
   - ピンポイントな情報が書かれていることが多い
   - 最新情報を追いたいときも有用

#### 原典（公式リファレンス・公式本）から見直す
   - 公式は嘘をつかない
      - ウソを付くこともある  (`Д´)

<hr>
### 参考
[【エンジニア必見】エンジニアやプログラマに必要なググり力の鍛え方](https://www.yamata-pgblog.com/entry/2020/08/10/164124)
[誰も教えてくれないググり方](https://qiita.com/hcl/items/da9367d76ff103fcb181)
[技術的なハマりパターンを分類・オサレに命名し、パターン毎に解決策(エンジニアのググり方・質問の仕方)を明示してみた](https://qiita.com/y_tom/items/1b54d4e1a9a5f3fab402)
[GCP基礎]GCPのVirtual Instanceに画像を処理

### 初めに

---
GCPとかクラウド関連のプラットホームは機械学習や深層学習に重要なツールになっています。性能の高いパソコンなくても、深層学習のプロジェクトも実行でき、コスパが高いです。

今回、初心者むけに簡単な例でGCPのVirtual Machineの使い方を説明します。

**目標**：クラウドにあるVirtual Machineにコードやデータをアップロードし実行、そして処理した画像をダウンロードする。

**内容**：
1.GCP設定の手順
2.簡単なGCPコマンド

-----
### GCP設定の手順

#### 1.最初の準備
- GCPをサインイン
- [GCP SDKをダウンロード](https://cloud.google.com/sdk/docs/install)
- GCP新規プロジェクトを作成
![create_project.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/f66f7f27-c7fe-f994-c01f-14428a7afac2.png)
![create_project2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/ae854733-5d61-844c-d1e5-fe83a38bb666.png)
- コマンドラインで初期化

```$ gcloud init```

[*詳しくはこちら*](https://cloud.google.com/sdk/docs/cheatsheet#introductory_primer)


#### 2.プロジェクトにVirtual Instanceを作成

- 全てのVirtual Instanceをリストにする
```$ gcloud compute images list```

- Virtual Instanceを詳細に見る
```$ gcloud compute images describe  ubuntu-1604-xenial-v20210203 \```
``` --project ubuntu-os-cloud```

- Virtual Instanceを作成と命名
```$ gcloud compute instances create my-test-vmins \```
```--image ubuntu-1604-xenial-v20210203  \```
```--image-project ubuntu-os-cloud```


- Virtual Instanceをローカルで起動〜

コンソールからコマンドをコピー~
![ssh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/666dbf90-1925-fcbb-b6fb-caaf2e54e035.png)

そして実行

```$ gcloud beta compute ssh```
```--zone "asia-east2-c" "my-test-vmins"```
```--project "vm-test-304203"```

```>>> ravenlee@my-test-vmins:~$```になるはOKです。

#### 3.コードとデータファイルをアップロード
他のコマンドラインを開けて

- コードをアップロード（シングルスクリプト）


```$ gcloud compute scp adj_image.ipynb my-test-vmins:~/```

- データをアップロード（ファイル）


```$　gcloud compute scp --recurse img_file/  my-test-vmins:~/```

- Virtual Instanceに環境をインストール

ローカルと同じです〜

- Virtual Instanceにコードを実行

ローカルと同じです〜

- 出力をダウンロード

アップロードと同じように、他のコマンドラインに ```gcloud compute scp```でダウンロード

### 最後に

GCPは各種のサービスを提供して、使用する場合にAPIやtutorialsを参照して、理解し安いと思います。
14rep - gRPC クライアントの keepalive 自分メモ用

# 書いた理由
- gRPC Serverは書いたがClientは書いたことないな....と思い自分メモ

## keepalive は 2 種類

### TCP レイヤーの keepalive
- これは TCP ソケットの SO_KEEPALIVE オプション
- 細かい設定をアプリケーションから行えないので、アプリケーションレイヤーの実装が提供されている

### アプリケーションレイヤーの keepalive
- http/2 の PING フレームを定期的に送ってタイムアウトしたら再接続する
- クライアントのバッテリーや DDos 配慮のため、アクティブなストリームがない場合は PING フレームを送らないオプションが提供されている

``` golang
package client

import (
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

const (
	// GRPCClientKeepaliveTime 活動がなくなってから PING を送るまでの間隔を表す。
	GRPCClientKeepaliveTime = 2 * time.Second
	// GRPCClientKeepaliveTimeout PING 応答を待つ時間を表す。
	GRPCClientKeepaliveTimeout = 2 * time.Second
)

// TLS通信しない
// GrpcCliCon gRPC Client 接続
func GrpcCliCon() (*grpc.ClientConn, error) {
	addr := "host.docker.internal:3010"
	conn, err := grpc.Dial(addr,
		grpc.WithInsecure(),
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time:                GRPCClientKeepaliveTime,    // 活動がなくなってから PING を送るまでの間隔
			Timeout:             GRPCClientKeepaliveTimeout, // PING 応答を待つ時間
			PermitWithoutStream: true,                       // アクティブなストリームがないときも probe を送るかどうか
		}),
	)
	if err != nil {
		log.Fatal("client gRPC Dial connection error:", err)
	}
	return conn, nil
}

```

# grpc.WithInsecure()

``` go
func WithInsecure() DialOption
```
- WithInsecure は、この ClientConn のトランスポートセキュリティを無効にすDialOption を返します。
- WithInsecure が設定されていない限り、トランスポートセキュリティが必要です。

# Client接続
- go用に吐かれたgRPCファイル内に{xxxxServiceClient}と書かれた内容を見つけてください

``` go
sc := pb.New{gRPCを参照}ServiceClient(conn) // 作成されたgRPCファイルを参照してください
req := &pb.HogeFugeRequest{
    RequestId: requestID,
    Name: c.Query("word"),
}

res, _ := sc.SampleHoge(ctx, req)
	if res.Status.Code != int32(pb.Code_OK) {
		c.JSON(getStatusCode(res.Status.Code), res)

		return
	}
```


# 参考記事
- [gRPCのkeepaliveで気をつけること
](https://christina04.hatenablog.com/entry/grpc-keepalive)
- [gRPC クライアントの keepalive の薦め](https://qiita.com/yoheimuta/items/623957ca41f662917e74)
任意の日付がその月の第何週かを求める python

スクレイピングでカレンダーをポチポチしたいとき。
自分はxpathを使います。
その時よくあるxpathのパターンが以下(日曜始まりのカレンダー）
//*[@id="cal"]/main/table/tbody/tr[3]/td[1]・・・第3週目の1日目
//*[@id="cal"]/main/table/tbody/tr[3]/td[2]・・・第3週目の2日目
//*[@id="cal]/main//table/tbody/tr[3]/td[7]・・・第3週目の7日目

カレンダーがきれいなテーブルになっています。
tr[3]がカレンダーの第3週目を表しています
もし、2021年2月28日をポチっとしたい場合（日曜始まりの場合)
xpathは
//*[@id="cal"]/main/table/tbody/tr[5]/td[1]・・・第5週目の1日目

ここで必要になるのがtr[5]の部分つまり、
任意の日付がその月のカレンダーの第〇週になるということです。

ネットで調べても難しかったので自分で関数を作成しました。

```python:任意の日付がカレンダーの第〇週かを求める
import datetime
def get_week(year,month,day):
　　""" 
   日曜始まりのカレンダーの場合任意の日付が第〇週かを求める
    Args:
        year:年
        month:月
        day:日        
    Returns
        第〇週
    """
    firstday=datetime.date(year,month,1)　#任意の日付の同月1日のdate型を作成する
    hizuke=datetime.date(year,month,day)　#任意の日付のdate型を作成する

    firstsyuu=firstday.strftime("%U")　　　#任意の日付同月1日がその年の何週目かを取得する
    nowsyuu=hizuke.strftime("%U")         #任意の日付がその年の第何週目かを取得する

    return int(nowsyuu)-int(firstsyuu)+1  #引き算して求める
```

strftime("%U")で年初からの週数がわかるのでうまく引き算をして求めます。
※1/1は第0週
※カレンダーが月曜日から始まる場合strftime("%W")

[strftimeの説明](https://docs.python.org/ja/3/library/datetime.html#strftime-strptime-behavior)
【Express】.envファイルとHerokuの環境変数を同期する方法

#プログラミング勉強日記
2021年2月8日

#前提条件

- すでにHerokuに環境変数が設定されている

まだ環境変数を設定していない場合は、[【Heroku】Herokuで環境変数を設定する方法](https://qiita.com/mzmz__02/items/64db94b8fc67ee0a9068) から環境変数を設定してみてください。

#Herokuの環境変数と.envファイルを同期する方法

##1. heroku-configをインストールする

```
$ heroku plugins:install heroku-config
```

##2. Herokuの環境変数を.envに同期する

```
$ heroku config:pull
```

```:値を上書きしたい場合
$ heroku config:pull --overwrite
```


```:.envを編集して環境変数を上書きしたい場合
$ heroku config:push
```
AWS利用費用を見直した話


AWSの1アカウントで個人サービスの運用をしているのですが、
想定より請求されているなと思い、ちゃんと請求の内訳を確認してみたという話。
こーゆーのはほっとくと無駄な出費になるので定期的に見直しが必要と肝に命じる。


### 対応1

・$0.005 per Elastic IP address not attached to a running instance per hour (prorated)
ElasticIPは1個は無料枠だったが、なぜ未使用のElasticIPを生成しており、その利用分が請求されていました

### 対応
インスタンスに紐づいていない Elastic IP を開放した
$3.72/月のコスト削減

### 対応2

EBSの新しいボリュームタイプの方がコスパが良いのでそっちに変更
https://dev.classmethod.jp/articles/ebs-volume-covert-gp2-to-gp3/
 
> $0.12 per GB-month of General Purpose SSD (gp2) provisioned storage - Asia Pacific (Tokyo) $2.40

gp2 -> gp3に変更

・gp2は、ストレージ料金 （月あたり、1GBあたり、東京リージョン）$0.12で、20GBをアサインしているので、
$2.4が請求額ですね。 

gp3にするだけで、20*0.096= $1.92になります。
$0.5/月コスト削減

### 対応3

t2.micro で運用してたのですが、こんなに長く運用している想定でなかったので、
オンデマンドでずっとやってたのを、リザーブドインスタンスを購入（-35%）。

## 感想

仕事だとサイジングやコストを意識しているのに、
私用だときちんと設定していないのは良くないなと反省（そんな費用かからないだろうから放置してしまう）
lsyncdで「Error: Temporary or permanent failure on startup of "/xxx/yyy/zzz/". Terminating since "insist" is not set.」が出た場合の対処

# エラー内容
lsyncdをインストールし、さて起動するかと思ったものの起動せず。
lsyncd.logには以下のエラーが出力されていた。

```
Error: Temporary or permanent failure on startup of "/xxx/yyy/zzz/". Terminating since "insist" is not set.
```

# 環境
- Amazon Linux 2
- lsyncd: Version: 2.2.2

# 対応
lsyncd.confに `insist = 1,` を追記することで起動できた。
## 変更前
```lsyncd.conf
settings{
        logfile = "/var/log/lsyncd.log",
        statusFile = "/tmp/lsyncd.stat",
        statusInterval = 5,
}
```
## 変更後

```lsyncd.conf
settings{
        logfile = "/var/log/lsyncd.log",
        statusFile = "/tmp/lsyncd.stat",
        statusInterval = 5,
        insist = 1,
}
```

# 参考
- [lsyncdをいれてみた](https://masalib.hatenablog.com/entry/2016/08/25/220130)
LOL xlsx

LOL xlsx

```
import urllib.request
import json
import xlsxwriter
print("アプリレビューを取得ツールです。")
page=1;
appid=input("アプリIDを入力してください:");
#appid=441668861
workbook = xlsxwriter.Workbook('アプリレビュー.xlsx')
worksheet = workbook.add_worksheet()
format=workbook.add_format()
format.set_border(1)
format.set_border(1)
format_title = workbook.add_format()    
format_title.set_border(1)   
format_title.set_bg_color('#cccccc')
format_title.set_align('left')
format_title.set_bold()    
title=['ユーザー','タイトル','コメント']
worksheet.write_row('A1',title,format_title)
row=1
col=0
count=0

#  
while page<11:
    #https://itunes.apple.com/jp/rss/customerreviews/id=441668861/sortBy=mostRecent/page=4/xml
    myurl="https://itunes.apple.com/rss/customerreviews/page="+str(page)+"/id="+str(appid)+"/json?l=en&&cc=jp"
    response = urllib.request.urlopen(myurl)
    myjson = json.loads(response.read().decode())
    print("データが読み込んでいる......"+str(page*10)+"%")
    if "entry" in myjson["feed"]:
        count+=len(myjson["feed"]["entry"])
        #第一列　ユーザー
        for i in myjson["feed"]["entry"]:
            worksheet.write(row,col,i["author"]["name"]["label"],format)
            row+=1
        #第二列　タイトル  
        row=1+(page-1)*50
        for i in myjson["feed"]["entry"]:
            worksheet.write(row,col+1,i["title"]["label"],format)
            row+=1
        #第三列　コメント
        row=1+(page-1)*50
        for i in myjson["feed"]["entry"]:
            worksheet.write(row,col+2,i["content"]["label"],format)
            row+=1
        page=page+1
        row=(page-1)*50+1
    else:
        print("もうすぐ完了......100%")
        break
if count==0:
    print("データが取得できません、アプリIDをもう一度確認お願い致します。")
else:
    print("xlsxファイルが生成出来ました,トータル"+str(count)+"条。")
workbook.close()
```
5日目：過ぎゆく時間を目視できるタイマーアプリ

#5日目のアプリ
過ぎゆく時間を目視できるタイマーアプリ

##画面キャプチャ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/959530/2bd9b5a9-c57d-b94c-4973-253444b4d6e0.gif" width=50%>


##以下の流れで作りました。
1. storyboadに1つのUIImageViewと1つのぼかし用VisualEffectView、3つUIButton、1つlabelを配置
2. 上記要素をViewController.swiftへoptionドラッグして紐付ける
4. 紐付けができたら、ViewController.swiftでコードを書く

##できたこと
+ タイマーを設置できるようになった
+ 時間はすぐに過ぎていくもの。ということがわかった。
##書いたコードを共有します！

```

import UIKit

class ViewController: UIViewController {

//    ボタン要素、ラベル要素を配置する
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var go: UIButton!
    @IBOutlet weak var pause: UIButton!
    @IBOutlet weak var reset: UIButton!
    
//    タイマーオブジェクトを用意
    var myTimer = Timer()
    
//    タイマーの表示初期設定
    var timeDisplay = 0
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = "Time is money"
    }
    
    @IBAction func startButton(_ sender: Any) {
        myTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(action), userInfo: nil, repeats: true)
    }
    
    @IBAction func pauseButton(_ sender: Any) {
        myTimer.invalidate()
    }
    
    @IBAction func resetButton(_ sender: Any) {
        myTimer.invalidate()
        timeDisplay = 0
        label.text = "0"
    }
    
    @objc func action() {
        timeDisplay += 1
        label.text = String(timeDisplay)
    }
}

```

##感想
5日目。時間をカウントすることができるといろいろ使える。使える。。
5日連続でできたので、次は１日に２個アプリを作るレベルに進む！
Swiftのイニシャライザについて表面さらっとまとめてみた

会社では、Javaを使っているのでSwiftをやり始めてもだいたいわかるだろ～な～
と思ったらいきなり聞いたことない単語が・・・

なんなんだこいつは。と思ったので自分のアウトプットのためにまとめる事にしました。
間違ってること言っていたら教えてください。。

### 結論

Javaでいう「コンストラクタ」である。（結論になってない。笑）
つまり、クラスのプロパティを初期化する役割を持つ。
形はメソッドだが動くタイミングが違います。

メソッド　→　任意のタイミングで呼び出す
イニシャライズ　→　インスタンス生成時に自動で動く

### なんのためにあるのか

しつこいようだけど、"プロパティの初期化"

なぜ、初期化しなければいけないかというところだけど

ざっくりいうと、
プロパティに何かしら代入されていないとインスタンスを生成することができません。
逆説的になるけど、プロパティがすでに初期化済みだった場合はイニシャライズは、書かなくてもいい。

書かなくてもいいだけで、必要です。←？？
矛盾してるようですが、説明は最後らへんに書くことにします。

### イニシャライザの書き方

いうより書くが易し
下記のように、"init"と書けばそれでいいです。

```swift
class Human {
	var name:String
	var age:Int

	init(name:String, age:Int){
		self.name = name
		self.age = age
	}
}
```

ここで書いているselfは、自分を指してます。
自分というのは、クラスではなく「インスタンス」であることに注意が必要

なお、引数名とプロパティの変数名が同じの場合は、上記のようにselfを書く必要がありますが
違う場合は、self書かなくても自動で判別してくれます。

呼び出し側は、こんな感じ

```swift
let user = User(name:"tanaka",age:28)
```

### 絶対イニシャライザは必要なのか

最初に書いたこの疑問・・・
その答えはYESであり、NOでもある。

必ず必要なのは間違いないです。

だけどプロパティの初期化が必要ない場合は、コードを書かなくても内部的にSwiftが下記のように補完してくれます。

```swift
class Human {
	var name:String = "絶対田中"
	var age:Int = 28
	//↓これ
	init(){
	}

}
```

デフォルトイニシャライザと言います

### まとめ

誰でも知っているようなことを簡単にまとめました。

自分による、自分のための記事になりましたが

どこかの誰かの参考になれば幸いです。
ERROR! The server quit without updating PID fileが出て、MySQLをアンインストールする前に確認して欲しいこと

## PIDがないとのエラー
```bash:terminal
Starting MySQL
.. ERROR! The server quit without updating PID file (/opt/homebrew/var/mysql/username.local.pid).
```

ググると、PIDファイルを作ったり、権限を与えたりしなさいという記事が多い。

しかし、PIDファイルを作って再起動した途端なぜかPIDファイルが消滅している。
何度作ってもまた消える逆ゾンビ状態に。
再インストールする前に以下を確認。

## エラーログの確認
`/opt/homebrew/var/mysql/username.local.err`
ここ
実際のパスは環境によって違いますがこのエラーファイルにエラー原因が出ているかもしれないのでlessで中身を確認。

```bash:terminal
$ less /opt/homebrew/var/mysql/username.local.err
.
中略
.
[ERROR] [MY-000067] [Server] unknown variable ‘timezone=UTC’.
[ERROR] [MY-010119] [Server] Aborting
```
結構ボリュームありますけど、**[ERROR]**の文字を探す。
多分最後の方にある。

私の場合は、直前にmy.cnfに追記した、
```timezone=UTC```
がまずかったみたいなので削除。

## 再起動
からの起動

```bash:terminal

Starting MySQL
.. SUCCESS!
```
MySQLを削除せずにすみました。
基本的にはまずエラーログを確認しよう。
データベースのデータを重複なしデータのに変更

//データベースのデータを取得
let somethings = DataManager.shared.getsomething()
//空の配列を作成
var uniqValue: [Something] = []
//ループで回す
for something in somethings {
     if !uniqValue.contains(where: { $0.abc == something.abc }) {
　　　　　　　　　 //データを追加していく
                uniqValue.append(something)
     }
}

これで重複なしの配列ができます
SSH鍵を指定してSSH接続を行う

# 目的

- SSH鍵を指定してSSH接続を行う方法をメモ的にまとめる

# 方法

- 下記コマンドを実行してSSH接続を行う。

    ```terminal
    $ ssh -i SSH接続に用いる秘密鍵までのパス 接続先のユーザー名@接続先のIPアドレス
    ```
Microsoft Store を使わず WSL を導入する

# Microsoft Store を使わず WSL を導入する

## 背景

Microsoft Store を使えない環境で WSL を使いたかったので、手順をまとめました。
また、 `Add-AppxPackage` コマンドでのインストールもできない環境だったので、半ばハック的にインストールしました。

## 手順

### 0. WSL を有効化
下記リンクの Step 5 までやる
https://learn.microsoft.com/en-us/windows/wsl/install-manual

### 1. ディストリビューションをダウンロード

```powershell:PowerShell
Invoke-WebRequest -Uri https://aka.ms/wslubuntu2004 -OutFile Ubuntu.appx -UseBasicParsing
```

上記は、 Ubuntu 20.04 の場合です。

### 2. 展開

```powershell:PowerShell
move .\Ubuntu.appx .\Ubuntu.zip
Expand-Archive .\Ubuntu.zip
```

### 3. ディストリビューションをインポート

```powershell:PowerShell
cd Ubuntu
wsl.exe --import Ubuntu-20.04 'C:\wsl\Ubuntu-20.04' .\install.tar.gz
# インポートできたか確認
wsl -l -v
```

* Ubuntu-20.04: (任意)ディストリビューション名
* 'C:\wsl\Ubuntu-20.04': (任意)インストール先

### 4. (optional) デフォルトディストリビューションの設定

```powershell:PowerShell
wsl --set-default Ubuntu-20.04
```

* 先に Docker Desktop をインストールしていた場合などで、既定のディストリビューションを変更したい場合

### 5. 作業ユーザー作成

このままだと root ユーザーを使用することになり、あまりよろしくありません。
作業用のユーザーを作成しましょう。

```powershell:PowerShell
wsl --distribution Ubuntu-20.04
sudo adduser takelushi
usermod -aG sudo takelushi
```
* takelushi: 任意のユーザー名。僕の場合は takelushi です。

### 6. ログインユーザー設定

1. レジストリ エディターを開く
   * `regedit.exe`
1. 以下から Ubuntu-20.04 のレジストリを探す
   > HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss

1. DefaultUid を 0x3e8 (1000) にする。UID が 1000 以外の場合はそれに合わせる。
プロファイラ HTTP Server-Timing のGoパッケージ go-server-timing

サーバサイドの処理のプロファイリングを`ChromeのDevtool`で可視化するライブラリです。
[go-server-timing](https://github.com/mitchellh/go-server-timing)

## そもそも論として

HTTPプロトコルヘッダーには、`Server-Timing` というものがあります。ここにサーバ側で計測した値をセットして、HTTPレスポンスに一緒に仕込んでブラウザに返せば、`Chrome Devtool`がいい感じに可視化してくれるわけです。

こんな感じに。

![Screen Shot 2021-02-08 at 14.21.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75231/d5ce5df0-daa2-8542-23d0-e6deb0d217f7.png)


## コードサンプル

実行したソースコードです。(README.mdからママ)

```go
package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"sync"
	"time"

	servertiming "github.com/mitchellh/go-server-timing"
)

func main() {
	// Our handler. In a real application this might be your root router,
	// or some subset of your router. Wrapping this ensures that all routes
	// handled by this handler have access to the server timing header struct.
	var h http.Handler = http.HandlerFunc(handler)

	// Wrap our handler with the server timing middleware
	h = servertiming.Middleware(h, nil)

	// Start!
	http.ListenAndServe(":8080", h)
}

func handler(w http.ResponseWriter, r *http.Request) {
	// Get our timing header builder from the context
	timing := servertiming.FromContext(r.Context())

	// Imagine your handler performs some tasks in a goroutine, such as
	// accessing some remote service. timing is concurrency safe so we can
	// record how long that takes. Let's simulate making 5 concurrent requests
	// to various servicse.
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		name := fmt.Sprintf("service-%d", i)
		go func(name string) {
			// This creats a new metric and starts the timer. The Stop is
			// deferred so when the function exits it'll record the duration.
			defer timing.NewMetric(name).Start().Stop()
			time.Sleep(random(25, 75))
			wg.Done()
		}(name)
	}
	// Imagine this is just some blocking code in your main handler such
	// as a SQL query. Let's record that.
	m := timing.NewMetric("sql").WithDesc("SQL query").Start()
	time.Sleep(random(20, 50))
	m.Stop()

	// Wait for the goroutine to end
	wg.Wait()

	// You could continue recording more metrics, but let's just return now
	w.WriteHeader(200)
	w.Write([]byte("Done. Check your browser inspector timing details."))
}

func random(min, max int) time.Duration {
	return (time.Duration(rand.Intn(max-min) + min)) * time.Millisecond
}
```

## コードの説明

41行目の 

```
defer timing.NewMetric(name).Start().Stop()
```

でプロファイラを仕込んでいます。

Goのdeferで、関数をチェインした場合、最後の`.Stop()`のみdeferされ、`.Start()`は即時実行されることに注意してください。

また、deferのスコープは関数になります。38行目の`go func`
Amplifyでデプロイしたがトップページ以外のURLがAccesssDeniedになってしまう件

## 背景
AWS AmplifyでReactのSPAアプリケーションをデプロイをした際に、発行されたURLのトップページは開いたが、それ以外のURLを叩くとAccesssDenied(403エラー)になってしまった。
![スクリーンショット 2021-02-04 17.48.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751681/a726c49f-3c7a-006b-da54-a3e427c6dab6.png)
その解決方法をメモがてら残しておきます。

ここでは解決方法だけ記載します。
原因など詳しく知りたい場合は以下の参考サイトに記述されていますので、そちらにお願いします。
参考サイト:
 [Amplify Console でデプロイした SPA が403エラーになった原因と対策](https://blog.serverworks.co.jp/tech/2020/03/02/amplify-console-redirect/
)
[[公式]AWSドキュメント Amplifyでリダイレクトのやり方]
(https://docs.aws.amazon.com/ja_jp/amplify/latest/userguide/redirects.html)
## 解決方法

1. AWS Consoleにログイン
1. サービスからAmplifyを選択
1. 対象のアプリを選択
2. 左側のメニューから「書き換えて、リダイレクト」を選択
3. 編集から以下のように設定する
    4. 宛先アドレス：</^((?!.(css|gif|ico|jpg|js|png|txt|svg|woff|ttf)$).)*$/>
    5. ターゲットアドレス:/index.html
    6. 入力:200(Rewrite)
![スクリーンショット 2021-01-05 16.33.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751681/f968807f-2b3c-ba2c-c222-f59ed4b2f4c2.png)

以上の設定でトップページ以外のURLにも遷移するようになる。
Couchbase Serverとは何か？〜歴史を交えた概観

# はじめに

本記事は、Couchbase Serverというデータベースを紹介するにあたっての導入となる役割を果たすものとして、適度な分量の記事となることを目指して、執筆されました。


# Couchbase Serverについて

Couchbase Serverは、ドキュメント指向データベースにカテゴライズされるNoSQLデータベースであり、シェアードナッシング型の分散アーキテクチャーを持ちます。

NoSQLと呼ばれるデータベースは、ビッグデータの集計処理や、IoTのようなリアルタイムかつ大量のデータ投入等、データベース毎にカバーするユースケースが異なっていますが、Couchbase Serverはデータに対して、リード/ライトの両方のアクセスを、低遅延で実現する必要のある（つまり、インタラクティブな）アプリケーションのバックエンドとして用いられるために設計（最適化）されています。

# Couchbase, Inc.の設立とCouchbase Serverの起源

Couchbase, Inc.は、2011年に、Membase社とCouchOne社の合併により設立されました。
Membase社は、[memcached](https://memcached.org/)プロジェクトのリーダーにより開発されたキーバリューストアMembaseを自社プロダクトとして持ち、CouchOne社は、ドキュメント指向データベース[Apache CouchDB](https://couchdb.apache.org/)プロジェクトの主要メンバーを擁し、CouchDBの商用サポートを提供していました。

2012年にリリースされたCouchbase Server 2.0により、Membaseを前身とするCouchabse Serverは、純粋なキーバリューストアから、JSONドキュメント指向データベースへと変化を遂げました。

ドキュメント指向データベースであるCouchbaseは、JSONデータを扱うために必要な機能を提供している一方、キーバリューストアとしての側面を持ち、JSON以外のデータをバリューとして格納することもできます。また、memcachedのように、メモリキャッシュとして利用する機能も提供していますが、デフォルトではデータはディスクに永続化されます。メモリファーストアークテクチャーが採用されており、データ操作（リード/ライト）において、ミリ秒以下の応答性能を実現しています。


# 二つのエディションとオープンソースプロジェクト

Couchbase Serverは、Enterprise EditionとCommunity Editionの、二つの形態でバイナリが提供されており、その基盤として[オープンソースプロジェクト](https://github.com/couchbase)が機能しています。

Community Editionは、Enterprise Editionのメジャーリリースと同時にリリースされますが、Community Editionのマイナーバージョンリリースは、数か月遅れてリリースされ、メンテナンスリリースについても、限定されています。

Community Editionは、Enterprise Editionとで、共通して利用することのできる主要な機能については、完全な移植性が確保されており、Community Editionで実行されているアプリケーションは、Enterprise Editionにコードを変更することなく、移行可能です。

# 最後に

以上、Couchbase Serverの概観を紹介しました。基本的な機能の紹介については、稿を改めて行いたいと思います。

# 関連情報

[Couchbase Offical Site - About Us](https://www.couchbase.com/about)

[Wikipedia Couchbase Server(英語)](https://en.wikipedia.org/wiki/Couchbase_Server)

[Wikiwand Couchbase, Inc.](https://www.wikiwand.com/en/Couchbase,_Inc.)

[](https://blog.couchbase.com/couchbase-server-editions-explained-open-source-community-edition-and-enterprise-edition/)
CloudWatch アラーム名を変更する

## 目的
わが社ではCloudWatchのアラームを利用し、AWSリソースのメトリクス・ログ監視を設定→チケットとして起票するシステムを採用しています。
その際、チケットの表題にはアラーム名が登録されるのですが、
つい最近登録したアラーム名に不満がでており、変更の希望がありました。
CloudWatchに設定したアラーム名は直接変更できないため、一工夫してアラーム名を変更します。

## 手順
アラームをコピーして、新しいアラームとして設定するだけ。

①コンソールから、名前を変更するアラームを指定し、アラームのコピーを選択。
②アラーム名のみ変更して、アラームを作成
③元のアラームを削除


簡単
WebサイトにLINEログインを組み込む（PHP）

参考
https://blog.sat.ne.jp/2020/10/22/php%E3%81%A7line%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F/

#１、LINE Developers にログイン

#２、新規チャンネル作成

新規チャンネル作成
![スクリーンショット 2021-02-08 13.42.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1061654/369d6860-f2f5-89dc-0fa4-80c529f01f2f.png)
↓
LINEログイン
![スクリーンショット 2021-02-08 13.48.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1061654/052a2e12-0263-6434-04ed-c9477e6309bc.png)

#３、コールバックURLを設定
Lineログイン設定から設定

#４、チャンネルIDとチャンネルシークレットを確認
チャンネル基本設定で確認

#５、アクセストークンを取得

```
$postData = array(
    'grant_type'    => 'authorization_code',
    'code'          => $_GET['code'],
    'redirect_uri'  => 'コールバックURL（Lineログイン設定）',
    'client_id'     => 'チャンネルID（チャンネル基本設定）',
    'client_secret' => 'チャネルシークレット（チャンネル基本設定）',
);
 
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_URL, 'https://api.line.me/oauth2/v2.1/token');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
 
$json = json_decode($response);
$accessToken = $json->access_token; //アクセストークン

```

#６、アクセストークンでユーザ情報を取得

```
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $accessToken));
curl_setopt($ch, CURLOPT_URL, 'https://api.line.me/v2/profile');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
 
$json = json_decode($response);
$userInfo= json_decode(json_encode($json), true); //ログインユーザ情報

//取得したユーザー情報を確認
print_r($userInfo);
```

#７、Lineログインボタンの設置
公式ドキュメント
https://developers.line.biz/ja/docs/line-login/integrate-line-login/#making-an-authorization-request


```
https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id={チャンネルID}&redirect_uri={コールバックURL}&state={乱数}&scope=profile%20openid&nonce=09876xyz
```
【Kotlin】リソースを利用したプログラムの書き方を覚えよう

#はじめに
Kotlinをはじめ、Androidのプログラミングにおいて文字列や数値、色などは直接指定せずにリソースに置いた物を参照する方法がよいとされています。

デザインレイアウトのプロパティ群に関してはリソースを参照した書き方にしないと警告が出ますが、プログラム上では警告が出ないこともあり直接値を書いている人も多いでしょう。

どちらが良いかは設計する人に任せるとして、ともかくリソースを利用したプログラムの書き方を覚えてみましょう。
使うか使わないかはあなた次第です。

レイアウトなどにリソースを使用する方法は下記を参考にして下さい。
[参照](https://qiita.com/vram/items/b9c7ed1d848b497ceefb)

#リソースとは
リソースとはプログラムとは区別されていて、様々な値を管理している部分になります。
プロジェクト上ではプログラム関係が**java**の下に配置されているのに対して**res**の下に配置されているのがリソースです。

strings.xmlがあるのもここですね。

#文字列
リソースに文字列を指定して、これを参照しましょう。
res > values > strings.xml が文字列を指定するリソースになります。

```strings.xml
 <string name="btStart">開始</string>
```
レイアウト設計をするときの textの値として @string/btStart

と指定すれば btStrt は値として **開始**　を参照することになります。
ではプログラムから参照する場合はどうすればよいのでしょうか？

まずはリソースIDを取得してみましょう

```sample.kt
  val id = R.string.btStart
```

リソースIDはリソースごとに異なる番号が割り当てられており、これを利用してリソースの値を取得する事が出来ます。

```sample.kt
  val id = R.string.btStart
  val str = resources.getString(id)
```

1行で書くと

```sample.kt
  val str = resources.getString(R.string.btStart)
```
#文字列の配列

同様に文字列の配列も取得してみましょう

```strings.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
    <string-array name="lvEntries">
        <item>1個目</item>
        <item>2個目</item>
        <item>3個目</item>
    </string-array>
</resources>
```

参照する場合は

```sample.kt
  val lvEntries = resources.getStringArray(R.array.lvEntrys)
 // 1個目 
 lvEntries[0]
```

このようになります。

#数値

今度は数値です。

```strings.xml
<resources>
   <integer name="intSampleValue">123</integer>
</resources>
```

```sample.kt
  val i =  resources.getInteger(R.integer.intSampleValue)
```

#色
文字列はstring.xmlに書いていましたが、色はvalues の直下に colors.xml を作成してそこに記述するのが一般的のようです。

```colors.xml
<resources>
   <color name="colorSample">#FF0000</color>
</resources>
```

```sample.kt
   // MainActivityの場合
   val col = getColor(R.color.colorSample)
   // フラグメントの場合
   val col = context!!.getColor(R.color.colorSample)
```

#MainActivi以外で使用する場合
MainActivityにプログラムを書く場合は**resources**が定義されているので、これを参照できるのですがクラスやフラグメントに各場合は使えません。その場合は

```sample.kt
  val res = context!!.resources
  val i =  res.getInteger(R.integer.intSampleValue)
```

コンテンツオブジェクト内にリソースがありますのでそれを参照して下さい。

#終わりに
残念ながらリソースを使用した方がソース量が増えます。
しかし頻繁に変更する可能性がある値は、プログラム上にあるよりもリソース上にあった方が修正が楽なのは明白です。
出来る部分だけでも良いので、少しずつリソース化してみるのはいかがでしょうか？
Rubyのeven?メソッド

参考：
[Ruby 3.0.0 リファレンスマニュアル  Integer#even?](https://docs.ruby-lang.org/ja/latest/method/Integer/i/even=3f.html)

## even?メソッドとは
偶数か奇数かを判別するメソッドです。与えられた整数が偶数であればtrue、奇数であればfalseを返します。
even?メソッドは整数(integer型)のみ判別できます。小数点がついたもの(float型)に使用すると、NoMethodErrorとなります。

```ruby:ruby
10.even?
# => true

5.even?
# => false
```

実際に使用してみるとこのようになります。


```ruby:ruby

num = 2

if num.even?
  puts "#{num}は偶数です"
else
  puts "#{num}は奇数です"
end

# => 2は偶数です


num = 3

if num.even?
  puts "#{num}は偶数です"
else
  puts "#{num}は奇数です"
end

# => 3は奇数です
  
# 条件分岐との組み合わせで結果を分けることができます。
```

float型にeven?メソッドを使用した場合


```ruby:ruby
num = 2.4

if num.even?
  puts "#{num}は偶数です"
else
  puts "#{num}は奇数です"
end

# => undefined method `even?' for 2.4:Float (NoMethodError)

```
Power Apps で Deep Link


#Power Apps の特定のスクリーンにダイレクトで飛べるようにするDeepLink の作り方

##App の OnStart に設定する

Parm を使用して、 DeepLink のパラメータを取得する

```
//Read ScreenID Parameter
Set(startScreen,Param("screenID")); 
If(startScreen <> 0, Switch(startScreen, "1", Navigate(Screen_1), "2", Navigate(Screen_2))); //Navigate to corresponding screen as per the ScreenID 
```

##アクセスリンク表記
DeepLink にはパラメータを指定する

```
https://apps.powerapps.com/play/<<ID>>?tenantId=<<ID>>?screenID=1
```
【個人開発】はじめての個人開発　〜企画編〜


# はじめに
エンジニア転職を目指して昨年の10月からプログラミングスクールに通っています。
一通りのカリキュラムを終え、ポートフォリオのためのアプリを作り始めました。
せっかくなので企画段階からの流れを投稿してみようと思います。


# 作ろうとしているもの
飲食店と顧客のコミュニケーションアプリ「ikitsuke」

# 実現したいこと

私自身が飲食店で勤めていることもあり、お客様に特別感のある情報を確実に届けられるようなツールがあったらいいなという思いから今回の企画に至りました。投稿機能は実装せず、メッセージ機能に重点を置いたものを作る予定です。

・お客様との密なコミュニケーションが取れる。
・店舗側の負担をできるだけ少なくする。（一斉送信機能の実装）

以上の二点を実現できるようなアプリケーションの作成を目指します。


### ペルソナ
#### 【飲食店経営者】
個人でワインバーを経営している。
高価なワインを仕入れてみたいが、一度開けると劣化してしまうため、リスクが高い。SNSで周知してみるも、いまいち反応がない。
常連さんは多いが、一人一人と連絡するのは時間もかかる。かつ連絡先を知っている人ばかりではない。
また、コロナ禍で常連のお客様にかなり支えられたこともあり、リピーターの重要性を身にしみて感じた。そういったお客様に特別メニューの提供を行いたい。

####  【顧客】
30代男性。渋谷付近で飲むことが多い。
毎回違う店に行くというよりかはお気に入りのお店に繰り返し行くことが多い。
SNSをやっている飲食店をtwitter等でフォローしているが、他の情報に埋もれてしまうことが多い。
いい食材やメニューが入ったときは試してみたい気持ちがあるので、確実に情報を得たい。
また、その日の気分で飲食店を選ぶことが多い。いいメニューがあるのであれば、友達も連れて行きたいと思っている。



# できること
### 飲食店側
・トップページにはフォローされているuser名、プロフィール画像、最後のトーク内容が一覧表示されている
・userとメッセージでやりとりができる。
・選択したユーザーに一斉送信をすることができる。
・userの詳細を見ることができる。
・フォロー申請を受理、拒否することができる。
・フォローを解除することができる。

### 顧客側
・トップページにはフォローしているshop名、プロフィール画像、最後のトーク内容が一覧表示されている
・shopとメッセージでやりとりをすることができる。
・shopの詳細を見ることができる。
・shopを検索することができる。
・shopにフォロー申請をすることができる。


# 機能
【user,  shop共通】
・メッセージ機能
	顧客側→各飲食店とのトークルームを持つ
	店側→顧客とのトークルームを持つ。選択したユーザーに一斉送信をすることが可能。
・ユーザー管理機能（user, shop）
・user, shop詳細表示機能
・友達申請、受理機能（顧客→店）
・予約機能

【shop側】
・user一覧機能（shop側トップページ）
	user名、プロフィール画像、最後のトーク内容を一覧表示

【user側】
・shop一覧機能（user側トップページ）
	shop名、プロフィール画像、最後のトーク内容を一覧表示
・shop検索機能


# スケジュール
2月1週目　企画、データベース設計（済）
2月2週目  フロント実装 (3割程度) 
2月3週目  メッセージ機能 、ユーザー管理機能、詳細表示機能、一覧機能
2月4週目  友達申請&受理機能、検索機能
3月前半　予約機能、フロント完成（余裕があればVue.jsでSPA化）

デプロイはHerokuで行った後に、余裕があればAWSに移行してみようと思います。

四月頭には完成予定！
[Kotlin]GSONを使ってみた。

#KotlinでGsonを使う方法(Jsonデータを変換する)

導入方法はこちらの記事が参考になった。
>https://qiita.com/naoi/items/6b184700b2a41fb46356

#自分のアプリの一部、テストで使用してみた結果

```kotlin
data class TestData (
    val quizID: Int,
    val question: String,
    val answer: String,
    val difficulty: Int,
    val commentary: String
)
```




fromJsonメソッドは第一引数にJSON文字列、第二引数にデシリアライズ後の型を受け取ります。

```kotlin
fun main() {
    val strJson = """{"quizID": 1, "question": "国鉄から分割民営化された際に発足したJRは全部で7社である。", "answer": "○", "difficulty": 2, "commentary": "7社です。"}"""
    val result = Gson().fromJson(strJson, TestData::class.java)
    println(result)
}
```


ログにはこう出る。

```kotlin
TestData(quizID=1, question=国鉄から分割民営化された際に発足したJRは全部で7社である。, answer=○, difficulty=2, commentary=7社です。)
```
ルーティングエラー原因の探し方の一例

Routing Error
No route matches[POST]"/tweets"

上記は、[POST]"/tweets"と一致するエラーがないという内容です。
まず、エラー画面の下部のパスで[POST]"/tweets"を確認します。
HTTPメソッドなど問題がなければroutes.rbで確認してエラーの原因を確認します。
【form_with】text_areaの入力内容の「改行」を反映させる

#はじめに
form_withを使って送信フォームを実装し、入力内容に改行を入れても、改行は反映されない。

（簡易的なメモアプリを例とする）

```new.html.erb
〜省略〜
<%= form_with model: @memo, local: true do |f| %>
<%= f.text_area :title, placeholder:"メモのタイトルを入力する" %>
<%= f.submit "記録する" %>
<% end %>
〜省略〜
```

```show.html.erb   
〜省略〜
<% @memos.each do |memo| %>
  <ul>
    <li>
      <% memo.title%>
    </li>
  </ul>
〜省略〜
```
・投稿作成画面の表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/6c07fb10-7e06-91c3-fc4a-9e05f24a25db.png)

↓ フォームの内容を送信すると「改行」が反映されていない。

・投稿画面の表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/25bae815-7c56-dc84-18fe-b229ef494b2b.png)


#記事の内容
form_withで送信するフォーム内の「改行」を反映させる。

（改行版）投稿画面の表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/39f42327-79cf-a860-b01f-56cfbea9fc89.png)

#改行を反映させる方法
show.html.erbの
<% memo.title%>
↓
<%= safe_join(memo.title.split("\n"),tag(:br)) %>
と書き換える。

```show.html.erb   
〜省略〜
<% @memos.each do |memo| %>
  <ul>
    <li>
      <%= safe_join(memo.title.split("\n"),tag(:br)) %>
    </li>
  </ul>
<% end %>
〜省略〜
```

#コードの意味
・split("\n")で改行コードを取得
・tag(:br))でbrタグに変更
Web制作用語　まとめ　入門①　

Web制作で使う用語をまとめます。
引用元はこちら
https://www.youtube.com/watch?v=zKPnqKlQ_Fk
非常に為になりました。


###サイトマップ
Webサイトの全体の構造を図式化または、リスト化したもの。
主に３つの種類がある、
・設計者が公開前に使用するサイトマップ
・ユーザーが見るためのリスト（階層的）
・検索エンジンなどのWebクローラーを対象とした構造化されたリスト

Webクローラーについて
WebクローラーとはWeb上に公開されているテキスト・画像・動画などの情報を自動で収集し、DBに保管するプログラム。「ボット（Bot）」、「スパイダー」、「ロボット」などとも言われる。


###ワイヤーフレーム
Webサイトのデザインをする前（プロジェクトの段階）に、ページの構成・レイアウトをどのように作るのか、
Webページの完成形を設計図にしたもの。ワイヤーは「線」、フレームは「枠」、基礎的なユーザーインターフェース。

###デザインカンプ
Design Comprehensive Layoutの略で、「デザインの完成見本」。
クライアントと製作側で共有する。

###３つのビジュアル　FV　MV　KV
FV：ファーストビュー（スクロールせずに最初に見える範囲）
MV：メインビジュアル（大きなビジュアル）
KV：キーヴィジュアル

###ランディングページ
「Landing Page」通称LP。
単一のWebページのこと、主に下記の意味として使われる。
１、縦長の広告ページ
２、着地ページ

###ECサイト
 electronic commerceの略
商品やサービスの販売サイト（E-コマースの略）
メルカリとかamazonとか楽天とかのやつ

###Photoshop
高精度な写真の加工やグラフィックのデザインの作成ができる。

有名どころは「Adobe Photoshop」、アドビが販売しているビットマップ画像編集アプリケーションソフトウェア

最近はXDというUIデザインツールを使用するケースが増えてきているが、
XDは写真やグラフィックの編集・加工に弱いので写真加工やグラフィックデザインにこだわる必要がある


###Illustrator
イラスト制作をはじめ、
名刺やロゴデザインなど、線のはっきりしたものをかくのに適したソフトウェア

有名どころは「Adobe Illustrator」、これもアドビが販売するベクターイメージ編集ソフトウェア

###レスポンシブwebデザイン
いまさら語ることもない

###ブレイクポイント
画面幅に合わせてレイアウトを切り替える時に使用する「切り替えポイント」のこと
レスポンシブwebデザインとセットで使われる。

###フレームワーク
「共通して用いることができる考え方、手法、戦略などの枠組み」のこと
効率的に制作ができる技術のこと。


###Webフォント
サーバ上の書体データを呼び出すことで文字を表示させる技術のこと。

#まとめ

今回リストアップされてる用語はなんとなくで意味を理解できてればいいかなって思います。
次は入門②をまとめますが、そっちがかなり重要だと思う。
Railsで複数行コメントアウトするときの、=begin~=endの使い方

Railsのビューファイル（ERB）のコードを複数行コメントアウトする方法が分からなかったので調べて出てきた構文を使ってみました。

複数行コメントアウトする方法についての記事はすぐに見つかったのですが、記事通りに実装してもうまくコメントアウトできずちょっと詰まったので備忘録も兼ねて記載します。

# 複数行コメントアウトする方法：=begin ~ =end

最初に目を通した記事は、「Railsドキュメント」の「[ビューについて](https://railsdoc.com/view "ビューについて")」というページです。

このページの真ん中あたりで、=begin ~ =end の使い方が紹介されています。

```ruby:comment_out.rb
<% =begin %>
<%= コメント %>
<% =end %>
```

「なーんだ、簡単じゃん」と思ってすぐ自分のコードに追加したのですが、まったくコメントアウトされません、、、

```ruby:bad_sample1.rb
<div class="container">
  <h3>サンプルページ</h3>
  <div class="contents">
    <%= form_tag('frm_1', name: 'frm_1', id: 'frm_1') do %>
      <% =begin %>
        このdivタグ以下をコメントアウトしたいのにコメントアウトされない！
        <div class="contents_row">
          <%= label_tag :input_date, '日付', class: 'mar_l_1em' %>
          <%= text_field_tag ::input_date, '', class: 'date_field_all calendar', maxlength: 10 %>
        </div>
      <% =end %>
      <div class="contents_row">
        <%= label_tag :prefecture, '都道府県', class: 'mar_l_1em' %>
        <%= select_tag :prefecture, options_from_collection_for_select(@prefecture, :prefecture_cd, :prefecture_name), class: 'text_field', include_blank: false %>
      </div>
      ~~~
<div>
```

色々コードをいじってみた結果、=beginと=endは、**インデントに関係なくエディタの行の先頭（左詰め）に記述しなければ動かない**ということが分かりました。

```ruby:ok_sample1.rb
<div class="container">
  <h3>サンプルページ</h3>
  <div class="contents">
    <%= form_tag('frm_1', name: 'frm_1', id: 'frm_1') do %>
      <% ↓改行
=begin %> ←「=begin」をインデントを無視して左詰めで記述するとコメントアウトされる
        <div class="contents_row">
          <%= label_tag :input_date, '日付', class: 'mar_l_1em' %>
          <%= text_field_tag ::input_date, '', class: 'date_field_all calendar', maxlength: 10 %>
        </div>
      <% ↓改行
=end %>
      <div class="contents_row">
        <%= label_tag :prefecture, '都道府県', class: 'mar_l_1em' %>
        <%= select_tag :prefecture, options_from_collection_for_select(@prefecture, :prefecture_cd, :prefecture_name), class: 'text_field', include_blank: false %>
      </div>
~~~
</div>
```

（なんでRailsドキュメントには他のコードと同様に「<% =begin %> ~ <% =end %>」で紹介されているのかがワカラナイ・・・）

# 終わりに
なぜ「=begin」を普通に「<%%>」で囲むとコメントアウトできないのか（改行して左詰めで書くとコメントアウトされるのか）の理由であったり、自分が↑で記述した内容に対してご指摘があればコメントにてご記載いただけると大変ありがたいです！
お読みいただきありがとうございました。
PowerShell で与えられたパスがフォルダかどうか判定する方法

PowerShell で、与えられたパスが存在するか否かの判定は `Test-Path` で行えるが、
与えられたパスがフォルダなのかファイルなのか判定する方法について書く。

# `(Get-Item $path) -is [System.IO.DirectoryInfo]`
ref: https://devblogs.microsoft.com/scripting/powertip-using-powershell-to-determine-if-path-is-to-file-or-folder/

`Get-Item` にフォルダを指定した場合、`System.IO.DirectoryInfo` のオブジェクトが返ってくることを利用した方法。

```powershell
PS C:\pstest> ls
    ディレクトリ: C:\pstest
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2021/02/08     12:09                testFolder
-a----        2021/02/08     12:10              0 testFile.txt
PS C:\pstest> (Get-Item .\testFolder) -is [System.IO.DirectoryInfo]
True
PS C:\pstest> (Get-Item .\testFile.txt) -is [System.IO.DirectoryInfo]
False
```

ファイルの場合は `System.IO.FileInfo` のオブジェクトが返るため、ファイルを判定する場合は `-is [System.IO.FileInfo]` を指定すればよい。

# `(Get-Item $path).PSIsContainer`
ref: https://bayashita.com/p/entry/show/229

ファイルシステムオブジェクトは `PSIsContainer` というプロパティを持っており、これは フォルダ(container) の場合に true になる。

```powershell
PS C:\pstest> (Get-Item .\testFolder).PSIsContainer
True
PS C:\pstest> (Get-Item .\testFile.txt).PSIsContainer
False
```
【SwiftUI】Textの一部だけ設定を変える

# どういうことか

例は用意してないんですが
『**詳しくはこちら**』という文章があったとき、『詳しくは』を下線付きの大きな文字で青色、『こちら』を少し小さい文字で表示したい。

## 設定して繋げる

設定した文字列をそれぞれ `+` 繋げていく。

```swift
Text("詳しくは")
    .foregroundColor(.blue)
    .font(.system(size: 18))
    .underline()
                            
    + Text("こちら")
        .foregroundColor(.black)
        .font(.system(size: 14))
```

以上。
PHPのアップグレード方法(php72->php73)

php7.2のセキュリティサポート切れを考慮した、phpのアップグレード方法。
php7.3のセキュリティサポート切れも見えてきたため備忘録を兼ねて投稿。
#本記事の環境
ホストOS：Windows 10
ゲストOS：CentOS 7
VirtualBox + Vagrant
epelリポジトリ
remiリポジトリ
Tera Term
php7.2

#アップグレード方法
* ホストOSからゲストOSを起動する。
<pre>
cd [vagrantホーム]
vagrant up
</pre>

* ゲストOSにアクセスするためTera Termを起動する。
* インストールされているphpのバージョンを確認する。
<pre>
php -v
yum list installed php*
</pre>
* 一旦phpを削除するため、必要ならphp.iniのバックアップをとる。
<pre>
find / -name php.ini -ls
cp -p [バックアップするphp.ini] [任意のバックアップ先]
</pre>
* インストールされているphpを削除する
<pre>
yum remove php*
</pre>
* epel-releaseをアップデートする。
<pre>
yum update epel-release
</pre>
* remiのリポジトリを確認する。
<pre>
ll /etc/yum.repos.d/ | grep remi-
</pre>

* remi-php73.repoがない場合は以下を実施する。
<pre>
yum install http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
</pre>

* php7.3をインストールする。
<pre>
yum -y install --enablerepo=epel,remi,remi-php73 php php-cli php-common php-gd php-imap php-mbstring php-mysql php-odbc php-pdo php-pear php-pgsql php-process php-snmp php-xml php-xmlrpc php-fpm php-mcrypt php-devel php-apcu php-psr php-sodium
</pre>
* インストールされたphpのバージョンを確認する。
<pre>
php -v
</pre>
* apacheを再起動する。
<pre>
systemctl restart httpd
</pre>

同じ要領で、php73->php74もできるかと。
未設定のラズパイにsshログインを行いコマンドを流し込む

同じネットワークにいる未設定のラズパイに ssh ログインします。

ssh の有効化

ラズパイOSが書き込こまれている SDカードを PC(ubuntu) に差し、ssh という空のファイルを作成します。

````bash
touch /media/$USERNAME/boot/ssh ;
````

ラズパイを起動してIPアドレスを調べます。

※ラズパイ側で ifconfig で DHCP な IPアドレスを取得できますが、キーボード等なるべく触りたくないので、ubuntu 側でIPアドレスを調べます。

IPアドレスの調査

````bash
sudo apt install -y arp-scan ;
ifconfig ; #インターフェイス情報の取得
sudo arp-scan --interface wlp5s0 -l ;
````

得られる情報

````bash
192.168.0.111	xx:xx:xx:xx:xx:xx	Raspberry Pi Foundation
````

ubuntuからラズパイにsshログイン

````bash
ssh pi@192.168.0.111 ;
````

ログイン情報

|項目  |値  |
|---|---|
|ユーザー名|pi|
|パスワード|raspberry|

ラズパイをまとめてインストールしたい場合に便利そうです。

wifiでの接続を行う場合は

````bash
HOGE=$(cat<<TEXT
country=JP
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
    ssid="ssidname"
    psk="password"
}
TEXT
)
echo "$HOGE" > /media/USERNAME/xxxxsd_card_idxxxx/boot/wpa_supplicant.conf ;
````

これでwpa_supplicant.conf ファイルに下記情報が書き込まれます

````
country=JP
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
    ssid="ssid_name"
    psk="password"
}
````

あとはsshでログインを行って好きなコマンドを流し込みます。

１つの端末向け

````bash
#!/bin/bash

ssh pi@192.168.0.111 <<EOC
hostname ;
wget http://hgoehoge.com/install.sh ;
bash install.sh;
EOC
````

複数の端末向け

````bash
#!/bin/bash

HOSTS="192.168.0.111 192.168.0.112 192.168.0.113"

for i in ${HOSTS}; do

ssh pi@$i <<EOC
hostname ;
wget http://hgoehoge.com/install.sh ;
bash install.sh;
EOC

done
````
4日目：画面をタップしたら背景画像がランダムで切り変わるアプリ

#4日目のアプリ
画面をタップしたら背景画像がランダムで切り変わるアプリ

##画面キャプチャ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/959530/0f856f4a-dea3-e2c6-735d-e473585afd54.gif" width=50%>

##以下の流れで作りました。
1. storyboadに1つのUIImageViewと、1つUIButtonを配置
2. ImageViewは画面全部になるようにAspect Fill に設定、UIButtonは画面全てをカバーできるように設定。
3. 上記要素をViewController.swiftへoptionドラッグして紐付ける
4. 紐付けができたら、ViewController.swiftでコードを書く

##できたこと
+ ランダムで数値を生成できるようになった
+ 生成した数字を文字に変換して、画像のファイル名に対応するように書き換えることができるようになった
##書いたコードを共有します！

```

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var changeImageButton: UIButton! //タップ用ボタンの配置と紐付け
    @IBOutlet weak var backGround: UIImageView! //背景画像の配置と紐付け
    
    override func viewDidLoad() {
        super.viewDidLoad()
        changeImageButton.setTitle("", for: .normal) //ボタンの文字を変更
        
    }

    @IBAction func changeButton(_ sender: Any) {
        let randomInt = Int.random(in: 1...4)  //１から４の数字をランダムに生成
        let changeInt:String = String(randomInt) //文字に変換
        backGround.image = UIImage(named: "pink0\(changeInt)")  //ランダムな数字をファイル名に埋め込み
    }
    
}

```

##感想
4日目。ランダムって、くじ引きやジャンケン、サイコロ以外にもいろいろ使えそう。。