OmniAICreator/Qiita-1.07M huggingface dataset:
初めてのGitHub(ターミナルでの操作)

#はじめに
こんにちは！
Python初学者です。
現在pythonを勉強中なのですが、言語を勉強していく中で、QiitaやGitを使ってアウトプットをしていきたいと思います。

#GitHubの操作

-GitHubの操作(ターミナルを使う)
1. ブラウザでリポジトリを作る
2. ターミナル上でgit clone urlを入力
3. cd ファイル名でカレントディレクトリに移動しておく
4. touch ファイル名で新規のファイルを作る
5. 新規のファイルをエディタで開き、適当なコードを入力する
6. git add 新規のファイル名でインデックスに追加
 (インデックスとは新たに書いた内容や追加変更した内容を一時的に保存する場所と理解している)
7. git commit -m "追加変更した内容を書く"
(この段階でインデックスに追加し内容をローカルリポジトリに保存すると理解している)
8. git push -u origin master
 (8の作業でマスターリポジトリに内容を反映させる)

大事なのは6,7,8の手順で
[add　→　commit → push]の手順を踏むことで自分が編集したファイルをリポジトリに保存できるということ

現在はこの方法でGithubの操作を理解しています

#マークダウンの練習
- この投稿も含めて簡単なマークダウンの練習をしています

**コード**

```python:Python
class Human:
    
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_name(self, name, age):
        print('{} {}'.format(name, age))

human = Human('山本', 30)
human.say_name('山田', 22)
```
**結果**

```python:Pyton(2)
山田 22 (say_name関数を呼び出した結果{}の中に 山田と22 が入る)
```
上記はコードを挿入する練習ですので中身は特に意味はありません

#感想
Qiitaに投稿する、GitHubをターミナルで操作する,マークダウン記法で記述する
ということを今までは知識で知ってはいましたが、今までは少しアレルギー反応を示していました。
ですが、実際にやってみると思ったよりは難しくなかったので、少しずつできることを増やしていきたいと思えました。
EC2でDocker＋Rails+pumaでアップしたときにWebpacker can't find applicationを解決した

ローカルではきちんと動いていたのに、EC2に上げたら

```
Webpacker can't find application
```
と出てしまったときにどうやって対処したのかを話していきたいと思います。

#結論

ローカルできちんと動いていたのは、Dockerを乗せる前にきちんとWebpackerやyarnなどのパッケージが入っていたからだと思います。
しかし、EC2はローカル環境とは全く違うので、そういった事は起きません。
なので、Dockerfileの中にnodeやyarnを入れてあげる記述をしてあげれば大丈夫です。

Dockerfile(before)

```
FROM ruby:2.6.5

# 必要なパッケージのインストール（基本的に必要になってくるものだと思うので削らないこと）
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev \
                       yarn \        
                       nodejs           

# 作業ディレクトリの作成、設定
RUN mkdir /app_name 
##作業ディレクトリ名をAPP_ROOTに割り当てて、以下$APP_ROOTで参照
ENV APP_ROOT /app_name 
WORKDIR $APP_ROOT

# ホスト側（ローカル）のGemfileを追加する（ローカルのGemfileは【３】で作成）
ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

# Gemfileのbundle install
RUN bundle install
ADD . $APP_ROOT
```

Dockerfile(After)

```
FROM ruby:2.6.5

# 必要なパッケージのインストール（基本的に必要になってくるものだと思うので削らないこと）
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev 
                       
# yarnパッケージ管理ツールをインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
  curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
  apt-get update && apt-get install -y yarn
# Node.jsをインストール
RUN curl -sL https://deb.nodesource.com/setup_7.x | bash - && \
  apt-get install nodejs        

# 作業ディレクトリの作成、設定
RUN mkdir /app_name 
##作業ディレクトリ名をAPP_ROOTに割り当てて、以下$APP_ROOTで参照
ENV APP_ROOT /app_name 
WORKDIR $APP_ROOT

# ホスト側（ローカル）のGemfileを追加する（ローカルのGemfileは【３】で作成）
ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

# Gemfileのbundle install
RUN bundle install
ADD . $APP_ROOT

```
以下のように、きちんとパッケージ管理ツールをDocker上でインストールするということがわかると思う。
これをしたあとに

```
docker-compose build
```

をしてあげれば、完成しました。

もし皆さんの約煮立てれば幸いです。
Pyinstaller でsklearnモジュールが入ると動作しない（exeファイルが起動しない問題）

＜要約＞
scikit-learn==0.24.1 python 3.8.9 の環境下で作成したプログラムを
pyinstaller==4.3　でEXE化しようとしました。

exeは出来るのですが、プログラムが起動しないという状況にありました。

状況確認の結果2点問題がありました
>1)exe化時にPower shellで
>warningでsklearn.neighbors.typedefs　が not found　
>と出力されていました。

>2) power shell でexeファイルを起動すると、
>ModuleNotFoundError: No module named 'sklearn.utils._weight_vector'
>と出力されていました。


改善は下記2点を実行して解決しました。その結果 exeファイルは予定通り起動しました。
>1)の問題は
>Lib\site-packages_pyinstaller_hooks_contrib\hooks\stdhooks\hook-sklearn.metrics.cluster.py
>を訂正する事で改善しました。
>こちらは、リンクを参照ください。
>https://qiita.com/niwataka/items/7712c38d287b50479cd4

>2)の問題はpyinstaller 時に
>pyinstaller ****.py --hidden-import=sklearn.utils._weight_vector
>とhidden importで事前にimportする事で解決しました。
toCサービスにおけるQAとは

こんにちは、株式会社DiverseでQAを担当している村上です！

QAを担当して1年ちょい経って、学びが多かったのでこれを機に書こうと思います。

:::note info
インフォメーション
自社サービスとしてのQA経験を踏まえて書いているのでタイトルには「toCサービスにおける」と記載しています。
:::

## QAとは

QA = Quality Assurance(品質保証)

直訳すると品質保証になります。
ではそもそも品質とはなんでしょうか？

## Quality(品質)とは

- 要求に対する適合(Crosby, 1926/6/18 - 2001/8/18)
- 誰かにとっての価値(Weinberg, 1933/10/27 - 2018/8/7)

上記で書いてあるように品質の定義というのは時代や人によって様々ですが、ユーザーファーストな現代においては誰かにとっての価値であることの方が重要なのかなと思っています。


## 上を踏まえて品質保証とは
会社によっては品質を保証するための役割もしくはその活動のことを指したりします。

個人的に品質保証とは、**対象物が「期待されている要求やニーズを実現したサービスであること」を保証する活動**だと考えています。

上は以下2つを保証していることを意味します

1. 機能要件そのものの品質保証
1. 実装された機能がユーザーのニーズに合致していることの保証

## 品質保証のための活動
ではどのように品質を保証しているかというとプロダクト品質とプロセス品質という2つの観点があります

ここではプロダクト品質とプロセス品質の2点があります。


**プロダクト品質**
成果物をテストして顧客に提供できるようにする活動であり、できたものがどれだけ要求と合致しているかを確認し、不具合があれば手直しをして品質を高める方法です。
これは結果系と言われています。

**プロセス品質**
一方、プロセス品質とは企画や開発の段階から品質の高いものを作り出すプロセスを整えることで品質を高めるという方法です。
これは要因系と言われています。


### 具体的な活動例
- **プロダクト品質**
    - コードレビュー
    - 静的解析ツールの運用
    - テスターによる機能テスト ←QAチームが担当している部分
    - 動的テストツールの運用
- **プロセス品質**
    - CI/CDツールの利用
    - ワークフローの運用
    - 開発ブランチのルール設定

一見QAと聞くとテストを思い浮かべがちですが、こうして見てみるとテストというのは品質保証活動の中のほんの一部だということがわかります。

`要求に対する適合`でも出てきたクロスビーさんも**重要なのは予防策であり、検査や手直しではない。**と言っているようにプロダクト品質(検査)よりもプロセス品質(予防)の方が重要であるという考えもあります。


## まとめ
- 品質の定義というのは時代や対象物によってそれぞれだが、現代のソフトウェア品質においては**誰かにとっての価値**というのが重要
- 品質保証のための活動としてはプロダクト品質とプロセス品質の2種類がある
  - `プロダクト品質` 成果物をテストして顧客に提供できるようにする活動
  - `プロセス品質` 成果物が出来上がるまでのプロセスを整えることで不具合が出る可能性を減らす活動

※勉強途中のため修正あればコメントください

## 参考記事
- [JSTQB FL シラバス 2018v3.1J02](http://jstqb.jp/dl/JSTQB-SyllabusFoundation_Version2018V31.J02.pdf)
- [トヨタ自動車の大規模リコール (2009年-2010年) - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A8%E3%82%BF%E8%87%AA%E5%8B%95%E8%BB%8A%E3%81%AE%E5%A4%A7%E8%A6%8F%E6%A8%A1%E3%83%AA%E3%82%B3%E3%83%BC%E3%83%AB_(2009%E5%B9%B4-2010%E5%B9%B4)
)
- [フィリップ・クロスビー ＺＤ（欠陥ゼロ）運動 - ダイヤモンド・オンライン](https://diamond.jp/articles/-/3273)
- [ソフトウェア品質管理とは - 株式会社イデソン](https://ideson-worx.com/%e3%82%bd%e3%83%95%e3%83%88%e3%82%a6%e3%82%a7%e3%82%a2%e5%93%81%e8%b3%aa%e3%81%a8%e3%81%af/)
centos6でのSSHのポート変更

centos7に慣れてきて、centos6を忘れてかけてきた今こそメモ

SSHのデフォルトポート22を3000に変更してみる

#sshd_configの設定
`$ vi /etc/ssh/sshd_config`

```
#Port 22
Port 3000
```

#iptablesの設定
バックアップをとる
`$ iptables-save > 20210428_iptables.txt`

コピーを作成して、編集する
`$ cp 20210428_iptables.txt 20210428_iptables_new.txt`
`$ vi 20210428_iptables_new.txt`
22の部分を3000に変更（詳細は略）

修正をiptablesに反映
`$ iptables-restore < 20210428_iptables_new.txt`
`$ /etc/init.d/iptables save`

```
iptables: Saving firewall rules to /etc/sysconfig/iptables: [  OK  ]
```

サービスを再起動して実反映
`$ service sshd restart`
`$ service iptables restart`

※注意。このあと、最終確認するまで作業中のターミナルは閉じないこと。
失敗した場合にログイン出来なくなるため。

別ウィンドウ、新しいポートでのSSH接続を試行する
Kubernetes の CronJob で多重起動とリトライを制限する

## 毎分実行するようなバッチをKubernetesで動かしたい
Kubernetesでは、Linuxのcronと同じように、時間を指定してJobを実行するCronJobという仕組みがあります。
メール送信や重い処理など非同期で行いたい場合、キューを使ってバックグラウンドプロセスに渡して処理をするのが一般的ですが、古いシステムの場合、cronで毎分バッチを実行させてこういった処理を行うことをやっている場合もあります。
今回は、そういった古いシステムをKubernetesに移行する場合のCronJobの設定について調べてみました。

## ECインスタンスなどでバッチの突き抜けをしない工夫

"突き抜け"とは、バッチ処理で規定の時間内に処理が終了しないことを指します。
例えばデータ量が増えたため、夜間バッチが朝の業務時間までに完了しなかったために業務に支障が出たり、
処理が完了しないままに後続のバッチが実行されて不具合が発生したりすることです。

今回のように毎分実行する設定になっているバッチの場合、処理するデータ量が多すぎるなどの理由でバッチ処理が完了する前に次のバッチが実行されると二重に処理が行われてしまう可能性があります（メール処理の場合だと二重に送信されてしまうとか）

こういった処理の場合、多重起動されないようにバッチ処理側でロックファイルの処理を追加したり、 以下のように`flock` コマンドを使ったりして前の処理が実行中の場合は起動しないように設定することが多いかと思います。

```shell
$ crontab -l
* * * * * /usr/bin/flock -w 0 /tmp/batch.lock /usr/local/project/bin/batch.sh
```

ただ、これらの処理は同じインスタンス内で実行されるのが前提の対応方法で、KubernetesのJobは実行される度に新しいコンテナインスタンスが作られるため、上記のような方法を取ることができません。

## KubernetesのCronJobについて

基本的にLinuxのcronと同じような仕組みなので、ドキュメントの記載もあるように以下のような制限があります。

> cronジョブは一度のスケジュール実行につき、 /おおよそ/ 1つのジョブオブジェクトを作成します。ここで /おおよそ/ と言っているのは、ある状況下では2つのジョブが作成される、もしくは1つも作成されない場合があるためです。通常、このようなことが起こらないようになっていますが、完全に防ぐことはできません。したがって、ジョブは /冪等/ であるべきです。
https://kubernetes.io/ja/docs/concepts/workloads/controllers/cron-jobs/

本来は公式ドキュメントにも強調されているように、バッチ処理は何度実行されても結果が同じになるように冪等性を持つようにするべきなのですが、ロックファイルで多重起動されないようにすることで回避しているバッチの場合、KubernetesのCronJobで多重起動を回避する設定する必要がありそうです。


## Kubernetes でバッチの同時実行を許可しない設定

まず、KubernetesのCronJob独特の特性として、100回以上スケジュールが失敗していると、以下のようなログが記録され、ジョブは開始されません。

> Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.

この 100回 を記録する時間範囲ですが、`spec.startingDeadlineSeconds`で設定できます。デフォルトは`nil`なので、累計100回以上失敗するとそのCronJobは再作成しない限り実行されなくなります。

* https://v1-18.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#cronjobspec-v1beta1-batch

また、`spec.concurrencyPolicy`を`Forbid`に設定すると、前回のバッチが実行中の場合、新しいバッチは実行されません。
ただし、この場合も失敗とみなされるため、100回以上カウントされると停止してしまいます。
この場合は、`spec. startingDeadlineSeconds`を適切に設定して回避します。


```yaml:設定例
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: batch
spec:
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 12000
```

## KubernetesのJobについて

古いシステムのバッチの場合、自前でロック処理を行なっていて、なんらかの理由で停止した場合に原因を調査してから手動でロックファイルを削除して再実行を行うような運用の場合があります。
KubernetesのJobでは、デフォルトの設定の場合、実行に失敗すると再実行が行われるので、この場合は実行に失敗すると素直に停止したままにしておく設定がよさそうです。

## KubernetesのJobでバッチ実行が失敗した場合に再実行しない設定
KubernetesではCronJobがスケジュールされた時間になると定義されたJobを生成してバッチ実行されます。

Jobの`jobTemplate.spec.backoffLimit`は、実行に失敗すると指定回数だけ再実行を試みます。
デフォルトは`6`なので、単純に再実行を行わないようにするには、この値を`0`に設定するだけです。

* https://kubernetes.io/docs/concepts/workloads/controllers/job/

また、Job が失敗した場合に再実行するかどうかは、`jobTemplate.spec.restartPolicy`の設定にも依ります。
例えば、この値が`OnFailure`の場合、実行に失敗すると再実行されます。
この際、back-off delay (10s, 20s, 40s,...) が加算され、最大5分まで再実行の開始を遅らせます。
また、10分間エラーが発生しなければ、この遅延実行時間はリセットされます。

* https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy

もし、バッチの実行時間にタイムアウトの設定を入れたい場合は、`jobTemplate.spec.activeDeadlineSeconds`を指定します。
実行開始から指定時間を過ぎると実行が停止され、エラーとして記録されます。

* https://v1-18.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#jobspec-v1-batch
Ruby 西暦を和暦に変換する方法

# 西暦を和暦に変換する方法

```ruby
require 'date'

date = Date.new(2021, 4, 24)

NENGO_EIJI_KANJI = { 'M' => '明治', 'T' => '大正', 'S' => '昭和', 'H' => '平成', 'R' => '令和' }
nengo = date.jisx0301.sub(/\A(.)0*(\d+).+/){ NENGO_EIJI_KANJI[$1] + $2 }

puts nengo
```
先日、私の拙い記事に対して、コメントをいただき、西暦を和暦に変換する方法について教えていただいたので、学習した内容を整理するために記事にしてみました。
教えていただいたコードは上記のとおりで、Dateクラスとsubメソッドと正規表現を使用したものです。

# subメソッド
[subメソッド](https://docs.ruby-lang.org/ja/latest/method/String/i/sub.html)は、文字列の中に指定した文字列や正規表現でマッチした文字列が存在した場合に、指定した文字列で置換を行ってくれます。
返り値には置換後の文字列が返されます。

```ruby
str = "HTML Ruby CSS"
puts str.(/CSS/, "JavaScript")
# HTML Ruby JavaScript
```
コードの可読性を上げるために、置換する文字列についてブロック形式で記載する方法もあります。

```ruby
str = "HTML Ruby CSS"
puts str.(/CSS/){"JavaScript"}
# HTML Ruby JavaScript
```

# 正規表現

正規表現とは[公式リファレンス](https://docs.ruby-lang.org/ja/latest/doc/spec=2fregexp.html)によると、

```
正規表現は文字列のパターンを記述するための言語です。
また、この言語で記述されたパターンも正規表現と呼びます。
正規表現を用いると、文字列が指定したパターンを含んでいるかどうかを判定し、また含んでいるならばそれが文字列中のどの場所であるかを知ることができます。
```

と記載されています。
このリファレンスマニュアルをもとに、今回ご教示いただいた

```ruby
nengo = date.jisx0301.sub(/\A(.)0*(\d+).+/){ NENGO_EIJI_KANJI[$1] + $2 }
```

を紐解いていきました。
間違っていたら、ご指摘願います。

まず、jisx0301メソッドで、西暦を和暦の英字表記に変換しています。

```ruby
date.jisx0301
# R03.04.24
```

この「R03.04.24」に対して、subメソッドを使って、日本語表示の和暦に変換してあげます。
そのときに、正規表現にマッチした`(/\A(.)0*(\d+).+/)`を`{ NENGO_EIJI_KANJI[$1] + $2 }`に変換するようになっていますが、正規表現を使う場合は、//で囲ってやり、その中に正規表現を定義してあげます。
以下、`\A(.)`と`0*(\d+)`と`.+`に分けて正規表現を解説していきます。
### \A(.)
`/A`は「文字列の先頭」を意味しており、今回の場合は「R」の部分に相当します。
`()`はキャプチャする際に使われ、後ほど`$1`と記載された特殊変数によって呼び出すことができるようにするために使われます。
`.`は任意の１文字を意味しています。
ここまでをまとめると、`\A(.)`で「先頭の文字列をキャプチャ化した」という意味になります。

### 0*(\d+)
`*`は直前の文字を０回以上繰り返す意味をもち、`0*`で「0が0回以上繰り返す」ことを意味しています。
`()`は先程説明したキャプチャ化のためのもので、後に出てくる`$2`でこの部分を呼び出すことができます。
`\d`は[0-9]の整数を意味しており、`+`は１回以上繰り返すことを意味しているので、`\d+`で0~9の整数が１回以上繰り返されることを意味しています。

### .+
最後に`.+`についてですが、すでに説明した通り、`.`任意の１文字、`+`１文字以上繰り返されるということで、`.+`で「何か文字があるもの全て該当する」という意味になります。

###(/\A(.)0*(\d+).+/)の意味
以上のことをまとめると、先の

```ruby
date.jisx0301 
# R03.04.24
```
について、先頭の文字「R」をキャプチャ化して、年数である「03」の部分については「0」が入っている場合は、その部分はキャプチャ化せずに、つまりは、「03」、「02」等の場合は、「0」の部分は無視して、１以上の連続した整数についてはキャプチャ化しなさいということを`(/\A(.)0*(\d+).+/)`は意味していることになります。

###{ NENGO_EIJI_KANJI[$1] + $2 }の意味
`$1`や`$2`については、特殊変数と呼ばれ、パターンマッチしたときに、情報をセットすることができるものです。
前述のとおり、`()`を使うことで、キャプチャ化することができ、最初にキャプチャ化したものを`$1`で呼び出すことができます。
2番目にキャプチャ化したものは`$2`で呼び出されるわけです。
以上を踏まえると、`{ NENGO_EIJI_KANJI[$1] + $2 }`の意味は以下のように書き換えることができます。

```ruby
{ NENGO_EIJI_KANJI["R"] + "03" }
```
という意味になり、

```ruby
nengo = date.jisx0301.sub(/\A(.)0*(\d+).+/){ NENGO_EIJI_KANJI[$1] + $2 }
# (/\A(.)0*(\d+).+/) => "R03.04.24"
# { NENGO_EIJI_KANJI[$1] + $2 } => "令和３"
puts nengo 
# "令和３"
```
に変換され、年号の部分のみ西暦を和暦に変換できるコードが完成しました。
【Redis】DockerのLaravel環境にRedisとphpredisを導入する

##初めに

[Laravelの日本語ドキュメント](https://readouble.com/laravel/6.x/ja/redis.html)をもとにDockerのLaravel環境にRedisを導入しようとしたところ、細かいところで追加の操作が必要だったので導入方法を備忘録として残しておきます。

## 環境

* Docker `v20.10`
* Laravel `v6.20`
* Redis `v6.2`
* phpredis `v5.3`

## ディレクトリ構造

```
├── laravel
├── infra
│   └─ php
│       └─ Dockerfile
└── docker-compose.yml
```

## docker-compose.ymlにredisコンテナを定義する

```yml:docker-compose.yml
version: "3.8"
volumes:
  php-fpm-socket:
services:
  app:
    build: ./infra/php
    volumes:
      - php-fpm-socket:/var/run/php-fpm
      - ./laravel:/work/laravel
  redis:
    image: "redis:latest"
    ports:
      - "16379:6379"

```

## appコンテナでphpredisを有効化する

phpredisのパッケージをpecl経由でインストールしappコンテナで有効化します。

```dockerfile:Dockerfile
FROM php:7.4-fpm-buster

RUN apt update && \
  apt -y install && \
  pecl install redis && \
  docker-php-ext-enable redis

WORKDIR /work/laravel
```

ここで気を付けなければならない点は、`pecl install redis`をした時点ではphpredisが有効になっていません。

この状態でlaravelのRedisファサードを使用しても

```php
PHP Error:  Class 'Redis' not found in 
/path/to/laravel/vendor/laravel/framework/src/Illuminate/Redis/Connectors/PhpRedisConnector.php on line 75
```

のように`\Redis`クラスが見つからないというエラーが発生します。

なので、`pecl install redis`を実行した後、忘れずに`docker-php-ext-enable redis`を実行しましょう。

## .envのREDIS_HOSTを設定する

.envのREDIS_HOSTにRedisコンテナ名を指定します。

```:.env
APP_NAME=Laravel
...
REDIS_HOST=redis
```

## app.phpのエイリアスからRedisを削除

```php:app.php
<?php

return [
  //  
  'aliases' => [
        'App' => Illuminate\Support\Facades\App::class,
        //
        'Redis' => Illuminate\Support\Facades\Redis::class,  // これを削除かコメントアウトする
        //
    ],
];
```

## laravelからRedisにデータを追加できるか確認

まずはappコンテナからRedisにデータを追加します。

```bash
$ docker-compose exec app bash
:/work/laravel php artisan tinker
>>> use Illuminate\\Support\\Facades\\Redis;
>>> Redis::set("str", "test");
```

redisコンテナのRedisに上記のデータが保存されていたら設定完了です。

```bash
$ docker-compose exec redis bash
:/data redis-cli
> keys *
1) "laravel_database_str"  # デフォルトで"laravel_database"がプレフィックスとして付加される
> get laravel_database_str
"test"
```
JavaScript基礎メモその5

#クラスのエクスポート、インポート
クラスの定義の後で『export default クラス名』とすることで、そのクラスをエクスポート（出力）し
、他のファイルへ渡すことができる。
>```javascript:animal.js
class Animal {
  //
}
export default Animal;
// Animalクラスを他のファイルでも使用できるようにする設定
```

『dog.js』内でAnimalクラスを使用できるようにする。
他のファイルで定義されているクラスを使用するにはインポート（読込）をする必要がある。
使用するファイルの先頭で『import クラス名 form "./ファイル名"』と書くことでインポートすることができる。
ファイル名の拡張子の『.js』は省略できる。
>```javascript
import Animal from "./animal";

#値のエクスポート、インポート
エクスポートする場合は『export default 定数名』
インポートする場合は『import 定数名 form "/ファイル名"』
>```javascript:sample1.js
const text = "Hello World";
export default text;
```

>```javascript:sample2.js
import text from "./sample1";
console.log(text);
```

結果
Hello World

#デフォルトエクスポート
『export default』はデフォルトエクスポートと呼ばれ、そのファイルがインポートされると自動的に『export default 値』の値がインポートされる。そのためエクスポート時の値の名前と、インポート時の名前に違いがあっても問題ない。
>```javascript:dogData.js
const dog = new Dog("reo", 4, "チワワ");
export default dog;
```

>```javascript:script.js
import doggy from "./dogData"
// 名前に違いがあっても定数dogの値が入っている (dog → doggy)
doggy.info()
```

デフォルトエクスポートは1ファイル1つ飲み使える。
>```javascript:dogDate.js
const dog1 = new Dog("han", 3, "柴犬");
const dog2 = new Dog("ryo", 2, "プードル");
>
export default dog1;
export default dog2;
// export default dog2; はエラーになる

#名前つきエクスポート
名前付きエクスポートは複数の値をエクスポートできる。またデフォルトエクスポートと違い、複数の定数やクラスを指定してエクスポートできる。
『export{名前１, 名前2}』という形で書くことにより、1つのファイルから複数のエクスポートができる。
>```javascript:dogData.js
const dog1 = new Dog("han", 3, "柴犬");
const dog2 = new Dog("ryo", 2, "プードル");
>
export { dog1, dog2 };
```

>```javascript:script.js
import { dog1, dog2 } from "./dogData";
dog1.info();
dog2.info();
```
Kintone Portal Designerをアプリで管理

kintone portal designerのアイコン、スペース、お知らせを
アプリのレコードで管理する機能を作り、公開しました。

[非公開記事を公開しました](https://qiita.com/zackey_/items/e8ad27eb4268186a09f0)
Cypressで躓いた所　クロスドメイン・iframe

## はじめに
E2Eテスト自動化でCypressを使った際に躓いてこう解決したを備忘録として残しておく。

## Cypressとは
[ここを参照。](https://www.cypress.io/)
[Cypressを使ってみるにはここ。](https://docs.cypress.io/guides/getting-started/installing-cypress)

## クロスドメイン
E2Eテスト自動化で躓くポイントの1つであるクロスドメインの部分でやはり躓いた。

### 躓きポイント
Cookieを用いて認証情報をやり取りしていたが、それが引き継がれない事でデータが取得できなかったり、画面が描画されなかったりという事が起きた。

### 原因
Cypressでは、[デフォルトで新しいテストが開始される前にすべてのCookieが自動的にクリアされる](https://docs.cypress.io/api/cypress-api/cookies#Preserve-Once)仕様になっているため。

### 解決策
`before`でログイン処理を行い、そこで取得したCookieを`beforeEach`にて`Cypress.Cookies.preserveOnce`で設定する。
※システムによっては`Cypress.Cookies.preserveOnce`ではうまくいかず、`Cypress.Cookies.default`も併用するとうまくいく事もある。

```js:cookie.js
  before('ログインする', () => {
    // カスタムコマンド
    cy.login('id', 'password')
  })

  beforeEach('Cookiesの設定', () => {
    const cookies = Cypress.env('COOKIES')
    
    cookies.forEach(cookie => {
      Cypress.Cookies.preserveOnce(cookie.name)

      // preserveOnceでうまくいかない時はこっちも併記するとうまくいく場合もある
      // Cypress.Cookies.default({ preserve: `${cookie.name]`})
    })
  })

  it('do something', () => {
  })

  it('check something', () => {
  })
```

```js:command.js
Cypress.Commands.add('login', (id, password) => {
  // Cookiesの初期化
  cy.clearCookies();

  cy.visit('/login');
  cy.get('#id').type(id);
  cy.get('#password').type(password);

  // ログイン後にCookiesを取得し、それをCypressの環境変数'COOKIES'に設定
  cy.Cookies().then(cookies => {
    Cypress.env('COOKIES', cookies);
  });
})
```

### 参考文献
- https://docs.cypress.io/api/cypress-api/cookies#Preserve-Once

## iframeが使われているWebサイトの操作
### 躓きポイント
iframeを使っている場合、通常の実装方法では要素を捕捉できず`type`や`click`ができなかった。

### 解決策
```js:iframe-test.js
cy.get('#iframe') // ←ここはiframeのDOMを指定するセレクタであればOK
  .its('0.contentDocument').should('exist')
  .its('body').should('not.be.undefined')
  .then(cy.wrap)
  .as('iframe')

cy.get('@iframe')
  .find('#name')
  .type('hogehoge')

cy.get('@iframe')
  .find('#submit')
  .click()
```

### 参考文献
- https://www.cypress.io/blog/2020/02/12/working-with-iframes-in-cypress/#clicking-on-a-button-inside-the-iframe

## 関連記事
- [CypressでのE2Eテスト自動化で学んだ事](https://qiita.com/yuta-katayama-23/items/127f078abeb9c9dfebaa)
GCP Cloud Source Repositories の GitHubミラーリングでリポジトリ接続エラー

# Cloud Source Repositories の GitHub リポジトリのミラーリング
- https://cloud.google.com/source-repositories/docs/mirroring-a-github-repository?hl=ja
- GitHubのPrivateリポジトリをミラーリングできるので、ここからAppEngineにデプロイしたら楽そうと思ってやってみました。

## 会社のリポジトリの場合
- とりあえずオーナーにお願いして、対象のリポジトリのAdmin権限をもらう
- 上記の公式の手順通りにやると、途中で organization リポジトリへのアクセス許可リクエストボタンがあるのでそれをクリック→pendingと表示される
- ひとまずそのまま承認して進める
    - この時点では一覧には自分のアカウントで作ったリポジトリしか表示されない
- もう一回オーナーに連絡してリクエスト許可してもらう
- ミラーリング対象のリポジトリ一覧に、organizationのも表示される
    - Admin権限持っているリポジトリだけがチェック可能になっている

# リポジトリに接続できませんでした
- あとはリポジトリ接続ボタンクリックで終わりと思いきやエラーになる
- タイムアウトかと思って何回かやったけどダメだったので、試しに空のリポジトリを作る
- もう一回リポジトリ接続すると成功した
- たぶん一度空のリポジトリを作ったことで、何かがenableになったんだと思うのですが、なんかバグっぽいですね
AlmaLinux8.3にvue-cli@2.9.6をインストールしてVue.jsを動かすまで

# やりたいこと

* AlmaLinuxでVue.jsが使えるようにする。


# 前提

* AlmaLinuxが最小構成以上でインストール済みであること。
* root権限でSSH接続できること。
* 任意のユーザとホームディレクトリが作成済みであること。

# サーバ環境

* VirtualBox
* AlmaLinux-8.3-x86_64-dvd.iso

# インストール

root権限で下記コマンドを実行する。

```
dnf update -y
dnf install nodejs -y
npm install -g @vue/cli
```

# バージョン確認

```
node -v
npm -v
vue --version
```

# Vueプロジェクト作成

任意のユーザ（ここではyamato）のホームディレクトリ配下に作成する。

```
su yamato
cd /home/yamato/
```


プロジェクト名は vue_test_210428a とする。

```
vue create vue_test_210428a
```

実行後、

```
  Your connection to the default npm registry seems to be slow.
   Use https://registry.npm.taobao.org for faster installation? (Y/n) Y
```

と表示されるので、Y を入力する。エンターキーを押してからしばらく待つ。

# ポート開放

vue起動時のデフォルトポート番号8080を開けておく。
root権限で実行する。


```
firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --reload
```

# 起動


```
cd vue_test_210428a/
npm run serve
```

# 画面表示

サーバのIPアドレスにhttpにてポート8080でアクセスして下記が表示されたらOK。

`http://XXX.XXX.X.XX:8080/`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/b7aa7fb0-a86f-1b45-d92b-744f36b20aa4.png)
【メモ】切断されたネットワークドライブでExplorerがフリーズする問題

# 移民にとって厄介な問題
異なる環境で、それぞれ別のネットワークドライブを使いたい。毎回接続するのは面倒だから、エクスプローラーにマッピングしておきたい。しかし、切断されているネットワークドライブがエクスプローラーのリスト（左側）に常に存在することとなる。

### 死のパターン
ドラッグ＆ドロップするとき、切断されたネットワークドライブの上を通過しようとした瞬間、エクスプローラーはフリーズする。正確には長時間の待ち状態になるため、2分ほど「応答なし」となる。

中途半端な位置でファイルのアイコンが止まってしまったら、（変な場所にファイルが移動しないように）フリーズ解除までマウスボタンを放さず自分も「フリーズ」して待つことを強いられる場合がある。

# 解決例
### 例１
まさにこの問題についての解決法として、下記の記事が見つかる。

[ネットワークドライブが切断されている時にExplorerがフリーズする問題を解決する](https://nellab.net/archives/00586/)

レジストリの```HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters```に```TcpMaxDataRetransmissions```を作成し、```DWORD(32ビット値)```に```0```をセットするやり方。

残念ながら私の環境では効果が認められなかった、他の条件との組み合わせが良くないのかもしれない。

### 例２
直接的な解決ができなくても、フリーズしたエクスプローラーを捨てて別のウィンドウを立ち上げることができればとりあえず作業を継続できる。それには、次の記事のやり方が役に立つだろう。

[エクスプローラーの「応答なし」がデスクトップ全体を道連れにしないようにする【Windows 10トラブル対策】](https://www.atmarkit.co.jp/ait/articles/2101/21/news013.html)

こちらはエクスプローラーで```別のプロセスでフォルダーウィンドウを開く```の設定を有効にする方法。

ひとまず作業を継続できるため、今のところこの方法でしのいでいる。
vue-routerでリファラを取得する

前のページ情報を取得したいときに、vue-routerを使ってリファラを取得する方法について書きます。
ちなみにvue-routerを使ってるとJSのdocument.referrerでは正しい値を取れません。

## 本文

ルーティングファイル内でbeforeEachを定義して、リファラとなる値を保存する。

```src/router/index.js
const router = new VueRouter({
  // ...
});

router.beforeEach((to, from, next) => {
  router['referrer'] = from;
  next();
})

```

そうすると使いたいコンポーネントでリファラの値を取得することができる。

```Hoge.vue
<script>

export default {
  method: {
    hoge () {
      let referrer = this.$router.referrer;
      console.log(referrer);
    }
  }
};
</script>

```

## 補足

ちなみに特定のページでリファラを取得するこっち↓のやり方もある。
[Vue.jsでアクセス元（referrer）を取得する │ エトセトラ備忘録](http://note.favorite-color.net/2021/202102102168/)
[データ取得 | Vue Router](https://router.vuejs.org/ja/guide/advanced/data-fetching)

## 参考
[Vue.js - VueRouterで前のページのURLを取得することは可能でしょうか？｜teratail](https://teratail.com/questions/144729)
[ナビゲーションガード | Vue Router](https://router.vuejs.org/ja/guide/advanced/navigation-guards.html)

ナビゲーションガードについてもうちょっと勉強せねば・・・
間違いなどあったらご指摘ください。

おしまい
ReaderWriter問題 - inheritという言葉の功罪

皆さんはReaderWriter問題をご存知でしょうか？
オブジェクト指向の話題でたまに出る話で、ReaderクラスとWriterクラスがあるとき、それらを組み合わせてReaderWriterクラスを作るとしたら、どのように設計すべきか？という問題です。
ふと思い出したので、私なりの答えを紹介したいと思います。
なお、ここで話を楽にするためにReaderもWriterもファイルを扱うものとします。

※なお、クラスは「単一責任」であるべきだ！二つの機能を持ってしまったら単一責任ではないので、そのようなクラスは作るべきではない！なんていわないでくださいね。
理由を述べると、「責任」を何とするかがポイントで、「ファイルアクセス」を責任と捉えるならば、立派な単一責任になります。（GOFのFacadeパターンとか勉強してもいいかもです）何より、多くの人はオブジェクト指向をしたいのではなく、オブジェクト指向を活用して生産性を上げる（コードを再利用したり、可読性を上げたり、テスタビリティを向上させたり、等）事が目的だと思います。稀にオブジェクト指向をすることが目的になっている方がいますが、そのような方は是非ご自宅でして頂きたいと思います。

#さて、ではどうしようか？
C++言語だったら「多重継承」という機能があるので、以下のようにしちゃえば一発です。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/14f1d19a-5813-7477-d7a8-4a5cb99bb3dc.png)
しかしながら、だいぶ大古の時代より「多重継承ってよくないよね～」と言われており、Javaを含む最近の言語（Javaはもはや最近じゃないか）では多重継承はできなくなっています。

ならどうしたらいいでしょう？
Readerクラスを継承して、ReaderWriterクラスにWriterと同じものを書く？
それともWriterクラスを継承してReaderWriterクラスにReaderと同じものを書く？
どっちもコードの再利用ができないですね。

#継承(inherit）という言葉の功罪
オブジェクト指向を語るとき、必ず継承というワードが出てきます。C#なんかだと、inheritsというキーワードもありますよね。
しかし、これは問題があると思っています。
オブジェクト指向設計での「継承」とオブジェクト指向言語での「継承」は分けて考えるべきです。
実際、Javaが継承という言葉を使わず、extends（拡張）という言葉を使っている理由は、ここにあると思います。

#縦の継承と横の継承
こういわれてピンとくるでしょうか？
下の図の左側は縦の継承です。馴染みのあるやつですね。Javaならextendsです。右側は横の継承です。ChildがParentを所有して、ラップしています。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/9d8a686e-d634-84ea-3927-b80418c622e7.png)
Child#method()の中身は、

```
method() {
  parent.method()
}
```

のようになる事でしょう。
method()の中に親のmethod()を書く手間がありますが、コードの再利用は達成できています。
（引数とか戻り値に変更あったら変えなきゃだけど、全部書くよりははるかにましです）

これを使うと、ReaderWriterクラスのクラス図は以下のようになります。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/cfd3bf3e-cf61-e6b5-e118-a6bd2e683a0d.png)

#もうちょっと綺麗にしたい。
このクラス図だと、コードからはReaderを継承している事が読み取れますが、Writerについては読み込まないとわかりません。
何より、

```
Reader r = new ReaderWriter();
```
はできても、

```
Writer w = new ReaderWriter();
```
はできません。

#インタフェースを分離しよう
ここでの問題は、具象クラスとインターフェースが切り離されていないことにあります。
以下のようにインターフェースと具象クラスを分ける事ができます。
Concreteは具象クラスという意味です。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/322ff2b7-5c8f-b128-c7fd-aae22101c393.png)
操作（インターフェース）と実装（具象クラス）を分ける事で、コード上必要なものがなんであるのか、わかる、という利点があります。

例えば、JavaなんかだとListインタフェース（順番を保ったオブジェクトの集合）があり、その具象クラスにはArrayList（配列による実装）、LinkedList（次要素へのリンクを保持するタイプの実装）があります。ArrayListは間にオブジェクトを挿入しようとすると、後ろを全部ずらさなければいけないが、LinkedListはリンクの付け替えで済むため、どのような使われ方をするかによって、具象クラスを選びます。
が、もしそこで必要とされている事がListで表現されるもののみであり、どのように実現されているかはどうでもいい場合は、変数はインタフェースにした方が、後々改変する際に改変の自由度が上がります。

#それを踏まえて、答え
はい、私の答えは以下のようになります。
ReaderWriterはReadableに直接矢印がつながっていないですが、つなげてしまっても問題ありません。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/88922240-ed31-cc60-f2f1-08dce4ecdfda.png)
このようにしておけば、

```
Writable w = new ReaderWriter();
```

も問題なく行えます。

#最後に
という訳で、私なりの答えを紹介させて頂きましたが、これが別に絶対的な正解というつもりはありません。
この記事で一番言いたかったことは、「継承」という言葉に惑わされるな、という事です。
そして、このような設計も、あくまで「自分たち（またはお客様含む関係者）にとって有意義であるか？」が一番大事なことだと思います。
複雑なプログラムをこのような手法で単純化できるのであればどんどんやるべきだと思いますし、逆に複雑になって大変だよ…というならやらないほうがいいです。

あくまでケースバイケースなのですが、このような整理法もある、という事は知っておいて損はないと思います。
fvmでインストールしたパッケージを再インストールする

パッケージの中身まで間違って文字列置換したときに、fvmでインストールしたパッケージを再インストールしたいと思ったのですが、どうすればいいかわからず苦労したのでメモ。

知ってさえいれば簡単で、Flutterプロジェクト直下にある`.fvm/flutter_sdk/packages`を消去して、プロジェクトをリビルドすればOK。

Android Studioで見るとこんな感じです

![スクリーンショット 2021-04-28 11.08.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164020/9c40f211-49ed-108f-69ca-8141aeae4082.png)
つくれぽ｜会社を作ってみた

## はじめに（コメント）

[マネーフォワード会社設立](https://establish.moneyforward.com/)をつかって、会社を作ってみました。おおむね手順通りやればOKで、とても便利だったんですけれども、

- いくつかのハマりどころがあった

のと、

- だれか図解してくれ

とおもったので、図解してみました。

> 技術情報じゃないけど、いちおう技術系（のこともやる予定）の会社なので...


## 環境

ここでの作図は、下記のバージョンで実施しました。

- [plantuml-stdlib/C4-PlantUML](https://github.com/plantuml-stdlib/C4-PlantUML#layout-options) v2.2.0
- PlantUMLエクステンション 2.14.5
- Visual Studio Code バージョン: 1.55.2
- PlantUML V1.2021.1 (2 Feb, 2021)+ Graphviz
- macOS BigSur 11.1
- MacBook Pro (13-inch, 2020, Four Thunderbolt 3 Ports)

## 前提

- 合同会社をつくりました。
- [マネーフォワード会社設立](https://establish.moneyforward.com/)をつかいました。
- 電子定款にしました。
- マネーフォワードクラウドに有料登録しました。

## おおまかな流れ


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184128/5a65d292-e777-3548-40a3-201895521e34.png" width=60%>

- system（ブルー）はおもに`マネーフォワード会社設立`でのタスク
- external_system(灰色）はおもに`マネーフォワード会社設立以外`でのタスク
- person（濃紺）はおもに手作業
- `STEP1 / STEP2`は`マネーフォワード会社設立`でのステップ
- 設立後のタスクは記載していません

## ハマりどころ

ふつうはハマらないところかもしれないけど、じぶんがハマったところ

- `印鑑証明`は、てっきり`会社実印の印鑑証明`だと思い込んでしまった
    - `個人の印鑑証明`でよい
    - `法人実印`を作成→届くのを待つ（タイムロス）→役所で思い違いに気づくという愚かさ
    - でも法務局に提出する書類に`法人実印`は必要なので、つくっておいてよかった

> よくよく考えれば、法人がまだできていないのに、印鑑証明を取れるわけがなかった。子どもが生まれてないのに、戸籍謄本を取ろうとしているようなもので。いちど思い違いをすると、訂正するのはむずかしいものだなー

- 時間がかかるところ（ボトルネックプロセス）の誤解
    - `印鑑購入`が時間がかかると思っていたが……
    - `定款作成`に時間がかかる（標準5営業日）
        - 電子認証には機材が必要（な様子）で、ここは行政書士の先生に依頼しなければならない
        - 特急料金を支払って短縮した（3営業日）
    - `登記完了`に時間がかかる
        - `設立日`は法務局への提出日だが、`完了日`は5営業日ぐらいかかる
        - オンライン申請できたかもだが、`マネーフォワード会社設立`の指導に従った

## 依存関係

なんども会社を作ったりもしないと思うけど、主な依存関係（ミスるとハマりそうだったところ）をメモ

- 会社名が決まらないと、次のステップに進めない（定款をつくるとか、印鑑を購入するとか）
- 資本金の額も決めないと、次のステップに進めない（定款をつくるとか）
- 代表社員の印鑑証明書に記載の住所と、合同会社の住所は厳密に同じでなければならない
- 電子定款はCD-Rなどのメディアに記録しなければならない
    - USBメモリとかでもいいみたいだけど、CD-Rが安い
    - CD-Rは百均で、2枚で100円だった
    - CD-Rの書き込みができるドライブを捨てずに置いておいて助かった
- 大きな声で言えないけど、行政書士の先生には、うまくお願いしたら早く終わらせていただけた
    - 特急料金はもちろんお支払いした
    - 3営業日の予定が、即日納品していただけた（ミンナニハナイショダヨ）
iRICのSDK

# 既往の問題点

iRICソフトウェア（以下iRIC）上で動作する解析プログラムなどを開発する場合、iRIC本体とデータをやり取りするためのライブラリ等(Software Development Kit:SDK)を利用する必要があります。これまでそれらSDKは、iRICのインストーラーとは別途iRICのWebサイトから配布されていましたが、バグ修正や関数追加の度に新しいバージョンのSDKに手動で更新する必要があり、SDKが最新版に更新されていないために「あれっ、マニュアルに記述されている関数が利用できない」などの問題が多々生じていました。


# SDKがiRICの更新と同時に更新、配布されるようになりました

この度、iRICを更新するとSDKも一緒に更新、配布されるようになりました。iRICの更新方法は以下の記事を参照ください。なぞに2回も同じような記事を書いてしまっていますが、、、

- [iRIC ソフトウェアの更新方法](https://qiita.com/Kazutake/items/a71eaaa8f83a9e5caf1d)
- [iRICを最新版にする方法　～マニュアルと差異があったらまず試すこと～](https://qiita.com/Kazutake/items/ecdae6d99018a2c39ea4)


iRICをインストールするとSDKは以下の場所にインストール（更新）されます。

（iRICのインストールフォルダ）/guis/prepost/sdk/

デフォルトインストールだと以下。
C:/Users/{ユーザー名}/iRIC/guis/prepost/sdk/

今回バージョンより、従来のfortranだけでなく以下の言語のSDKを配布しています。

- c
- fortran
- python

お試しください。
Keynoteにmp4→「デコードできません」表示が出る

Keynoteにmp4を入れたい。
ただ「デコードできません」と表示が出て挿入できない:frowning2:

#フレームレートの問題だった
貼れない動画はiPhoneの画面収録のmp4を編集したもの
↓
もともとの画面収録mp4素材のフレームレートがめっちゃ高
↓
**MEなどでフレームレートを下げて(29.97)レンダリング**
↓
貼れました。


Keynoteのバージョンの問題かと思ったら、
高すぎるフレームレートが問題だったようでした。
盲点！

※一緒に調査してくださった会社の方に感謝:pray:
Contentful REST API で記事一覧/記事の取得

# 概要

Contentful のコンテンツ配信 API のチートシート、必要最低限のもののみ記載。
参考: https://www.contentful.com/developers/docs/references/content-delivery-api

# 事前準備

1. Contentful のアカウント作成
1. スペースの作成 (アプリごと)
1. コンテンツモデルの作成 (記事のテンプレート)
1. コンテンツの作成 (記事のこと, エントリーとも呼ばれる)
1. Settings -> API keys から access_token を作成

# 必要情報

|名前|取得方法|
|---|---|
|space_id|spaceページURL|
|environmnet_id|未設定なら master|
|access_token|API keys の Content Delivery API - access token|
|entry_id|contentページURL or 記事一覧APIのレスポンス

# 記事一覧の取得

```
https:// https://cdn.contentful.com/spaces/{space_id}/environments/{environment_id}/entries?access_token={access_token}
```


# 記事単体の取得

記事一覧取得APIのレスポンスの `items.sys.id` にあるものが `entry_id` になる。

```
https://cdn.contentful.com/spaces/{space_id}/environments/{environment_id}/entries/{entry_id}?access_token={access_token}
```

# まとめ

ドキュメントを読むと他にも「検索」や「タグ」などでより便利に使いまわせそう、学びがあればまた追記する。
ローカルマシンにPHPもcomposerもインストールしない人のLaravel Sailの使い方

---------------------------------------
2024/04/08 追記
「使いづらい点」を主に紹介していましたが、改善できましたので、「使い方」を紹介します。
記事に至らない点があったのですが、ただの批判記事のまま放置になってしまっておりました。
すみませんでした。

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

お疲れ様です。
新しくプロジェクトを構築しようと思い、新し目のソリューションのLaravel Sailを利用してみました。
使ってみた結果、整理のためにまとめます。

公式：
https://readouble.com/laravel/8.x/ja/sail.html
https://readouble.com/laravel/9.x/ja/sail.html
https://readouble.com/laravel/10.x/ja/sail.html
https://readouble.com/laravel/11.x/ja/sail.html

# 良い点

### ローカル環境が爆速で構築できる
すでにDockerを利用している人なら数ステップでローカル環境が構築できます。
docker-compose.yml等を書く必要がありません。

# 使いづらい点
~~全体的に、プロジェクト作者のためだけの設定になっているように思います。~~
~~これをリモートリポジトリにpushし、他の人がcloneする際に問題が発覚します。~~

### ~~./vendor/bin/sail が .gitignore で無視されている~~
~~vendor配下はデフォルトで.gitignoreに記載されているので、git cloneした人はsailコマンドを打つことができません。~~
~~また、`./vendor/bin/sail`の他に`./vendor/laravel/sail`も必要です。~~

~~他の開発者にもLaravel Sailを利用してもらうなら（普通はそうしたいと思いますが）、~~
~~vendorの一部のファイルの無視設定をやめるため、vendor配下に以下のファイルを追加するなどの対応をする必要~~があります。~~
~~つまり依存パッケージ(sail)をリモートリポジトリにpushできるようにしてしまっているのでいまいちです。~~

### ~~composerのscriptが実行された前提になっている~~
~~プロジェクトをcloneした人は自前で以下のコマンドを打つ必要があります。~~
~~（もちろんMakefileなどで管理するのでも良い）~~
~~（composer createでプロジェクトを作成した人は、composerのscriptが実行されるので何もしなくてもOK）~~

~~上記が無いと、コンテナ構築の際に.envの設定を読めないため、~~
~~コンテナを作り直さないと`role "root" does not exist docker`(PostgreSQLの場合)とか言われてDBに接続できなくなったりします。~~
~~upする前に上記コマンドを打って、必ず.envを用意してください。~~

~~上記が無いと、`No application encryption key has been specified.`とか言われてLaravelが実行できません。~~

### ~~composer installされている前提でwebサーバーが動く~~
~~プロジェクトをgit cloneしてきたらまず、依存パッケージをインストールするため、composer installしないとLaravelが動きません。~~
~~そのためにsail upしますが、webサーバーはcomposer installされていないとエラーを吐いて接続できなくなります。~~
~~`up -> composer install -> 接続`~~
~~としたいですが~~
~~`up -> composer install -> down -> up -> 接続`~~
~~としないと接続できません。~~

# Dockerイメージをダイレクトに実行する

## composer install
composerが自分のマシンにインストールされていなくても、
以下のコマンドを実行して、composer installを実行できます。
``` shell
docker run -it --rm -v "$PWD":/app composer install
```
これでvendor配下の依存ファイルがインストールされます。

## php artisan sail:install
PHPが自分のマシンにインストールされていなくても、
以下のコマンドを実行して、sail:installを実行できます。
```shell
docker run -it --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp php:8.2-cli php artisan sail:install
```
これでdocker-compose.ymlが出来上がります。

## sail up
そして、sail upが実行できます！
```
./vendor/bin/sail up
```

Docker最高！
Bitcoin core Signet ノードの構築

bitcoin testnetは，マイニングマシンのテストなどに利用されているためにブロック生成時間が異常に変動したり，ブロックの再編成が頻発するなど使いにくい状態が続いています。このため，PoA 方式で安定的に利用できるsignetが研究や開発には適しています。

## bitcoin core のinstall

### MacOSXの場合

[Bitcoin core ダウンロードサイト](https://bitcoin.org/ja/download)から dmg 形式のインストーラをダウンロードする


#### インストーラで bitcoin core をインストールしてアプリを起動

* ドラックアンドドロップでアプリフォルダーにインストール
* 「システム環境設定」の「セキュリティとプライバシー」で，ロックのアイコンをクリックしてパスワード入力
*  bitcoin coreに対して「ダウンロードしたアプリケーションの実行許可」を与える

#### アプリ起動

「アプリケーションフォルダ」の bitcoin core アプリ起動

### ubuntu 20.04LTSの場合

#### snap でインストールする

```bash
sudo snap install bitcoin-core
```

#### bitcoin-qt アプリを起動

```
bitcoin-core.qt
```

#### snap で bitcoin core をインストールしたときのコマンドインターフェース

* bitcoin-core.daemon : デーモン起動 (bitcoind)
* bitcoin-core.cli : クライアント (bitcoin-cli)


## bitcoind 設定ファイルを編集（作成）

snap でインストールしたときの設定ファイル

```
 ~/snap/bitcoin-core/common/.bitcoin/bitcoin.conf
```

### bitcoin-qt (GUI) から設定ファイル (bitcoin.conf) を編集

* MacOSX :「ファイル」メニューの 「preferences」.. 
* ubuntu: 「設定」メニューの「オプション」
* 「設定ファイルを開く」ボタンをクリック
* 設定ファイルを以下のように作成して保存

```
signet=1
txindex=1
daemon=1
server=1
rest=1
[signet]
rpcuser=hoge
rpcpassword=hoge
rpcport=38332
port=38333
fallbackfee=0.0002
```

* 「OK」をクリック

## bitcoin core を一旦終了して再度起動

アイコンが薄い黄緑になっていれば成功

* bitcoin-qt で自分のワレットの作成しておく

## signet のテスト用コインの入手

[https://signet.bc-2.jp/](https://signet.bc-2.jp/)
tailwind対応のVueのコンポーネントライブラリ

みなさんtailwind使ってますか？
tailwindの良さって、CSSを書かずにクラスで定義できるので、CSSの運用コストや設計に時間が取られなく済むことですよね。

一方、その反面でVuetifyみたいにコンポーネント用意してくれていないため、結構1からコンポーネント作成することが多いと思います。
tailwind対応のコンポーネントないのかなと思って、探したけど見つける結構大変だったのでまとめていきます。
あんまり、見つけられなかったのでテンプレート集も追加しています。

## コンポーネントフレームワーク

### [DaisyUI](https://daisyui.com/)

少し丸み帯びた可愛らし目のデザイン。Vue2にも対応しているためNuxtユーザーもで利用できます。

![スクリーンショット 2021-04-28 10.17.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106459/2ff04ebb-b9f9-b324-fe78-0479debe9af6.png)


### [VueTailwind](https://www.vue-tailwind.com/)

~~Vue3対応なので、Nuxtユーザーはそのままでは使用できません。~~

![スクリーンショット 2021-04-28 10.20.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106459/e08edf46-e0a9-b326-fa3f-3a46fd59902a.png)

## テンプレート集

### [Tailblocks](https://tailblocks.cc/) 

tailwindベースのテンプレート集。LPなど作るときに便利

![スクリーンショット 2021-04-28 10.23.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106459/a5e3c4d1-f5ae-ef93-c6c2-3de670f3e9ee.png)

### [Tailwind UI Kit](https://www.tailwind-kit.com/components)

tailwindベースのテンプレート集。Tailblocksよりもボタンやカードのパーツが多め

![スクリーンショット 2021-04-28 10.26.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106459/586dee42-aba9-a133-6f0c-4f7da5ec9567.png)

### [tailwind components](https://tailwindcomponents.com/)

tailwindのサンプル集。自作したテンプレートをアップロードできるので、様々なテンプレートを見つけることができる。

![スクリーンショット 2021-04-28 10.27.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106459/006f3a83-e753-489b-169c-664cf2e09de8.png)
macでxamppが開けない

## 状況
chromeからOSXのxamppをインストールしようとしたが、zipファイルが開けない。アーカイブほにゃらら〜を繰り返す。

## 解決方法
「safariからインストールする」
※chromeからだとなぜかうまくいかないぽい。
【Javascript】Promiseをいろいろ試してみた(No.１）

# 背景
Ruby on Railsの非同期通信を学習していて、そこから非同期処理&Promiseというキーワードが出てきたのでその辺りについて調べてみることに。合わせて同期処理と非同期処理の概念も学習した内容として纏めました。

※内容に間違いなどがある場合はご指摘をよろしくお願いします。

# Javascriptの同期処理と非同期処理
同期処理はプログラムを書いた順番に実行される処理のこと、非同期処理は順番を待たずに処理を行うことです。
同期処理の場合、重いプログラムを読み込む際にはローディングに時間が掛かってしまいます。その待ち時間の間に何かしらの処理を挟むことができないか。そこから生まれたのが非同期処理。また、とあるタイミングで処理を実行したいという場合に非同期処理を利用する場面もあるみたいです。
![スクリーンショット 2021-04-28 9.00.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966135/e19fee9a-ed1b-19da-0d9c-8afd78c4f3f9.png)

# Promise
非同期処理の最終的な完了または失敗した時の結果を戻り値として返します。このpromiseには３つのstateがあります。
①pending: 初期状態
②fulfilled: 完了（処理が成功）
③rejected: 拒絶（処理が失敗）
成功した時と失敗した時をそれぞれ分けていることがポイントです。

# Promiseの基本的な書き方
まずはfunctionを用意してその中にPromiseを定義。以下のような形式で書きます。stateは成功と失敗の２つ用意することができますが、シンプルに成功した場合だけにしました。

```javascript
new Promise( state名 => { 
             何らかの処理;
             state名(処理の結果）;
            })
```

HTMLの中にscriptタグで囲み、testPromise()という関数を定義。戻り値としてPromiseを返します。その結果をconsole.logで出力してみました。state名は何でもいいですが、state名とその後の処理に使われるstate名は必ず一致する必要があります。ここではresolveにします。

```javascript

 <script>
    function testPromise() {
      return new Promise(resolve => {          //state名はresolve
        resolve(1);           //state名(処理の結果）; という形で処理の結果の値を表す
      })
    }

    console.log(testPromise());   //testPromise()関数を実行した結果をconsole.logで表示
  </script>
```
consoleの結果を見てみると、fulfilledというstateとその結果の値'1'が表示されました。このようにPromiseはState(状態）とResult(結果）になっていることが分かります。


![スクリーンショット 2021-04-28 9.34.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966135/f8468490-5466-922a-2e51-69a4793deaf8.png)

# 関数のパラメーターを指定した場合
実行する関数に引数を入れてその結果を表示させることもできるので試してみました。

```javascript
 <script>
    function testPromise(input) {
      return new Promise(resolve => {
        resolve(input);
      })
    }

    console.log(testPromise('プロミスです。'));
  </script>
```
testPromiseという関数にパラメーターとしてinputを指定し、引数'プロミスです'を渡しその結果をみると
![スクリーンショット 2021-04-28 9.48.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966135/f2d920f9-c834-d00c-a292-e70975410000.png)
fulfilledと成功したstate(状態）と引数として渡した"プロミスです"が結果として表示されています。

# stateが失敗（reject)の場合
処理が失敗した場合にconsoleでどういう結果が出るのか試してみました。

```javascript
  <script>
    function testPromise(input) {
      return new Promise((resolve, reject) => {   //stateにreject（失敗）を追加します。
        if (input) {              //inputの値があった場合はresolve
          resolve(input);
        } else {                 //inputがない場合はreject
          reject('失敗です')
        }
      })
    }

    console.log(testPromise());
  </script>
```
inputというパラメーターを取る関数testPromiseにわざと引数なしでconsole.logで出力してみます。
![スクリーンショット 2021-04-28 10.11.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966135/221f7f10-2928-c6a6-416f-6633df5a106d.png)
Promiseのstateはrejected(失敗）になり、結果として"失敗です"という文字列が出力されました。仮にinputに何かしらの引数を渡せば処理が成功しresolveが発動します。


# 参考サイト
https://qiita.com/kiyodori/items/da434d169755cbb20447
https://qiita.com/Takagi_/items/84b4a2184f42ee77867c
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises
https://hirocorpblog.com/post-383/
Windows10でコンピュータ名やワークグループ名を確認する方法

・「Windowsキー」＋「R」で、「ファイル名を指定して実行」の画面を出す。
・「名前」に「control system」(間にスペースが必要です)と入力して「Enter」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483166/c0abe164-ff35-32da-a88f-26e33c54a14b.png)
画面で確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483166/ab0aae4e-5edb-dba3-9ff8-f979b6e3d17c.png)

なお「control userpasswords」でユーザー名が分かります。

コマンドプロンプトから
「set|find "COMPUTERNAME"」でコンピュータ名を出せます。
「set|find "USERNAME"」でユーザー名を出せます。
setコマンドでワークグループ名は取得できません。
Mac でDocker上にXサーバーを立ち上げる際に詰まった箇所

# MacでDocker上にXQuartzを立ち上げてGUIアプリを表示したい
上記を試みた際にめちゃくちゃハマってしまったため、個人的な覚書として書いておきます。
Qiitaでもいくつか記事が出ていたのですが、それでもハマってしまったポイントをメモ。

まずXQuartzをmacへインストールします。
```brew cask install xquartz```
↓
XQuartsの環境設定->セキュリティ->ネットワーク•クライアントからの接続を許可にチェックを入れる
↓
一度XQuartzを再起動する

後はdocker -runの際にdisplayにlocalhostのアドレスを指定したらいいだけ、、、、
```docker run -it --rm --name $containerName \
       -e DISPLAY=docker.for.mac.localhost:0 \```

のはずだったのですが、自分の環境では何回やってもこれでXサーバーが立ち上がりませんでした。
さまざまな記事をみながら格闘して１日。。。結局原因として分かったのは、**macとXQuartzへのパスが通っていなかった**ためでした。
Dockerを立ち上げる際に、shell script もしくはterminalから以下のコマンドを打つことで解消しました。
```export PATH=$PATH:/usr/X11/bin
export CPATH=$CPATH:/usr/X11/include
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/X11/lib
xhost + 127.0.0.1```

同様に詰まっている方の参考になれば幸いです！
Python3.9 でのdatetimeの扱いのチートシート的メモ書き

Python3.9でdatetimeの扱い方法のチートシート的メモ書き

```py
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

### 生成 ###

## 現在時刻
zone = ZoneInfo("Asia/Tokyo")
now = datetime.now(zone)
print(f'現在時刻: {now}')

# >>>>>>>>>>>>>>>>>
# 現在時刻: 2021-04-07 11:34:20.971192+09:00
# >>>>>>>>>>>>>>>>>

## 指定時刻
first_day_2021 = datetime(2021, 1, 1, 0, 0, 0, tzinfo=zone)
print(f'指定時刻: {first_day_2021}')

# >>>>>>>>>>>>>>>>>
# 指定時刻: 2021-01-01 00:00:00+09:00
# >>>>>>>>>>>>>>>>>

## unixtimestampからの生成
from_unixtimestamp = datetime.fromtimestamp(0, tz=zone)
print(f'unixtimestampから: {from_unixtimestamp}')

# >>>>>>>>>>>>>>>>>
# unixtimestampから: 1970-01-01 09:00:00+09:00
# >>>>>>>>>>>>>>>>>

### 演算 ###

## 10秒足す
add_10sec = first_day_2021 + timedelta(seconds=10)
print(f'10秒足す: {add_10sec}')

# >>>>>>>>>>>>>>>>>
# 10秒足す: 2021-01-01 00:00:10+09:00
# >>>>>>>>>>>>>>>>>

## 10分引く
minus_10mins = first_day_2021 - timedelta(minutes=10)
print(f'10分引く: {minus_10mins}')

# >>>>>>>>>>>>>>>>>
# 10分引く: 2020-12-31 23:50:00+09:00
# >>>>>>>>>>>>>>>>>

## 1時間足す
add_1hour = first_day_2021 + timedelta(hours=1)
print(f'1時間足す: {add_1hour}')

# >>>>>>>>>>>>>>>>>
# 1時間足す: 2021-01-01 01:00:00+09:00
# >>>>>>>>>>>>>>>>>

## 1日引く
minus_1day = first_day_2021 - timedelta(days=1)
print(f'1日引く: {minus_1day}')

# >>>>>>>>>>>>>>>>>
# 1日引く: 2020-12-31 00:00:00+09:00
# >>>>>>>>>>>>>>>>>

### 文字列出力 ###

## 2021-01-01 00:00:00
print(now.strftime("%Y-%m-%d %H:%M:%S"))

# >>>>>>>>>>>>>>>>>
# 2021-04-07 11:34:20
# >>>>>>>>>>>>>>>>>


## 2021/01/01 00:00:00
now.strftime("%Y/%m/%d %H:%M:%S")
print(now.strftime("%Y/%m/%d %H:%M:%S"))

# >>>>>>>>>>>>>>>>>
# 2021/04/07 11:34:20
# >>>>>>>>>>>>>>>>>

```

# 参考

https://docs.python.org/ja/3/library/datetime.html
XplentyでYoutube Report APIデータを取得する

XplentyではYoutubeのReport APIに対してさまざまなパラメータを指定し、データを取得することが可能です。
今回はXplnetyを使用してYoutubeデータを抽出する方法について解説します。

#テンプレートを使用する
Xplentyでは、簡単にパイプラインを作成できるテンプレートについても提供しており、これらを使うことで手軽にパイプラインを作成することが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/bceb44ad-2573-b2f9-9dd7-3882676a6710.png)

##コネクションを定義
Youtubeからデータを取得する際に、あらかじめConnection画面でYoutubeの接続を定義する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/31db3e89-68ea-c921-15b6-e052d3bd255f.png)
Google アカウントを指定し、Xplentyからのアクセスを許可します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/5b20ee66-2210-0b36-8306-dff27c975c7f.png)


##テンプレートを使用したパッケージ作成
接続が作成できたら、「YouTube [Top Videos Report]」というテンプレートを使って設定方法を見ていきます。

パッケージを作成する際にテンプレートを指定すると、すでに構築済みのパイプラインが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/aa7c9a9d-709d-1449-2be9-5f725f6ddab7.png)

テンプレートの場合、基本的に変更しなければならない部分は以下の２点です。
**1. パッケージ変数**
Rest APIコンポーネントのURLパラメータにセットされる変数値を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/f82a1ca7-ebab-6cac-5c02-416ba7f10c99.png)

（設定例）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/a0c115cb-205f-e727-f74b-13f20b278441.png)



**2. 送信先のコンポーネント**
全てのテンプレートパッケージの送信先はRedshiftが設定されています。自身のユースケースに合わせて送信先をデータベースやBigQuery、Snowflakeなどのデータウェアハウス、もしくはS3やGoogle Cloud Storageなどのストレージを宛先に変更する必要があります。

#テンプレートで取得できるデータが欲しいデータに対応していない場合
この場合、テンプレートを参考に、リクエストURL部分を変更する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/08161c7a-f442-4c13-00ec-81050f6bb940.png)

URLでは、以下のように変数（**太字**部分）が設定されています。取得したいメトリックの項目を変更したい場合は、<font color="#ff0000">赤字部分</font>を変更する必要があります。
`https://youtubeanalytics.googleapis.com/v2/reports?dimensions=video&endDate=`**$EndDateByRange**`&ids=`**$ReportsChannelID**&<font color="#ff0000">metrics=views%2CredViews%2Ccomments%2Clikes%2Cdislikes%2CvideosAddedToPlaylists%2CvideosRemovedFromPlaylists%2Cshares%2CestimatedMinutesWatched%2CestimatedRedMinutesWatched%2CaverageViewDuration%2CaverageViewPercentage%2CannotationClickThroughRate%2CannotationCloseRate%2CannotationImpressions%2CannotationClickableImpressions%2CannotationClosableImpressions%2CannotationClicks%2CannotationCloses%2CcardClickRate%2CcardTeaserClickRate%2CcardImpressions%2CcardTeaserImpressions%2CcardClicks%2CcardTeaserClicks%2CsubscribersGained%2CsubscribersLost%2C</font>`&startDate=`**$StartDateByRange**`&sort=`**$TopVideoSort**`&maxResults=`**$TopVideomaxResults**



Metric部分も含め、URLをカスタマイズして設定したい際に、URLの生成およびテストも含め、以下のサイトで確認しながら進めるのが便利です。

https://developers.google.com/youtube/analytics/reference/reports/query?apix=true

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/92316846-9d5b-ecbc-386d-47ea98cacb2f.png)

リクエストURLが生成されるので、Xplenty側のリクエストURLをこちらを参考に設定してみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/bf9e0ad5-bfed-d5d0-a0b8-ed89afa39d01.png)
ひと昔前のインフラ構築(LB冗長化／DB冗長化)

# はじめに

様々なシステムを構築する際、そのシステムを稼働させるインフラ環境を構築する必要がありますが、ここ数年で、だいぶ作り方が変わってきました。
現在(2019年)は、

  - Amazon Web Services(AWS)
  - Google Cloud Platform(GCP)
  - Azure(MicroSoft)
 
などのクラウドサービスを利用して構築することが主流となってきており、
各クラウドから提供しているWebコンソール上でマウスを動かすだけで大抵の作業が完了します。
これに対し、ひと昔前までは必要なソフトウェアはすべて自分達でインストール・設定する必要がありました。
尚、本記事ですが「今は簡単だけど昔は大変だった」と武勇伝的なことを伝えたいのではなく、現在の各クラウドから提供されている様々な技術(サービス)を簡単に利用することができる状況ですが、それらの技術(サービス)の裏では、下記に書かれているような様々な技術がふんだんに利用されています。

「**技術に対する好奇心**」は、技術者にとって最も必要な要素だと思っていますので、各サービスを利用する際、その裏ではどのような技術が使われているのか？
など、少しでも考えるきっかけになっていただければと思います。
で、今回は、
　- ロードバランサ(LB)
　- DBの冗長化
の、ひと昔前の構築方法について紹介します。
※いづれも各クラウドでは、Webコンソール上で、数クリックするだけで実現可能。

#ロードバランサ(LB)を構築する
※ロードバランサは、いろいろな用途で利用可能ですが、ここではWEBロードバランサを前提として記述します。

- LBに対する要求
  - ①分散処理を実現する
  - ②LBの高可用性

- ①分散処理を実現する
  Linuxサーバで負荷分散を実現するもっともポピュラーな方法である「LVS(LinuxVirtualServer)」を利用します。
  LVSは、IPVS(IP Virtual Server)とipvsadmユーティリティから構成されていて、Layer4での負荷分散を実現します。
  ただし、LVSにはバランス先のWEBサーバの状態を監視する機能が備わっていないため、「Keepalived」というソフトウェアと組み合わせて、L4負荷分散を実現します。

- ②LBの高可用性を実現する
  上記①の対応で、LBまで来たHTTPリクエストを、LB配下にある複数のWebサーバにバランスすることができましたが、
  この状態では、もしLBサーバが不能な状態に陥った場合、そのWEBシステムに接続できなくなってしまします。いわゆる単一障害です。
  そこで、単一障害を回避するために「LBの高可能性」の実現です。
 もし、LBが不能となった場合、その変わりを担うLB(Standby機)を予め準備しておきます。
  そして、VRRPというプロトコルを利用して、LB(Active)が正常稼働しているかどうかを都度チェックし、LB(Active)の異常を検知した場合、瞬時にstandby機をActiveにフェールオーバー(昇格)するように設定します。

これで、高可用性なLBサーバの構築が完了となります。

# DBの冗長化

DBにはRDBMSやNoSQLなど様々な種類がありますが、今回はRDBMSであるMYSQLを例に話します。
システム仕様にもよりますが、DBは負荷上昇しやすいインフラパーツのため

> ・適切なSQLでデータの登録・取得を行う
> ・格納するデータ容量は可能な限り少なくする(DBデータ容量はディスクだけではなくメモリ使用量にも大きく影響するため)
> ・適切なメモリの割り当てを行う(my.cnf設定)
> ・参照用のスレーブDB(リードレプリカ)を作成し、データ参照による負荷を分散する

などを丁寧に行う必要があります。
また、DBはシステムの生命線でもあるため、DBが不能となった場合を考慮した仕組み・構成にする必要があります。
例えば、
> ・マスターDB×1台
> ・スレーブDB×1台

という構成に対して、冗長化構成を考える場合ですが
スレーブDBの冗長化は、スレーブDBを複数台構成にするだけで対応ができます。
※MYSQLで、複数のスレーブDBを作成するのは、MYSQLの標準機能で実現可能
※複数台構成のスレーブDBのうち１つが不能となった場合、そのDBをシステムから切り離す(参照しないようにする)必要があり、その場合はLBやHaProxyなどを利用することになりますが今回は割愛します。
 
それに対してマスターDBは、スレーブDBのように簡単に冗長化構成にすることはできません。
まず、ネットワークを通じてハードディスク(ブロックデバイス)をリアルタイムに複製(同時複製)することができる「DRBD」というソフトウェアを利用します。
MYSQLに登録したデータは、そのDBサーバのDISKに保存されますので、そのDISKに保存されたDBデータをDRBDを利用して、他サーバ(マスターDB(standby))にリアルタイムで同期(複製)します。
次に、マスタDB(Active)が不能になった場合、それを検知して、あらかじめデータ同期しておいたマスタDB(Standby)をActiveにフェールオーバ(昇格)させる必要があります。
それを実現させるためには、まず「Corosync」を利用してActive機の異常を検知し、「Pacemaker」がフェールオーバー(Standby機をActive機に昇格させる)を行います。
これで、高可用性のDBの構築が完了となります。

# 最後に
上記に記述した内容ですが、まずは概要を知っていただきたいと思って記述した文章となるため、
技術的な詳細部分についてかなり省略していたり、狭義の意味で間違っている記述もあると思いますがご容赦ください。
また、本記事ですが「ひと昔前」とは書きましたが、現在でもお客様都合でクラウド利用不可でオンプレサーバ(物理サーバ)で構築する必要がある場合もありますので、上記した技術は廃れた技術ではなく、現在でも世の中の多くのシステムで利用されています。
[Rails]simple_calenderの導入と実装

# はじめに
簡単なカレンダー機能を実装したのでこちらでアウトプットします。
なお、コントローラやモデル、入力フォームといった部分は割愛しております。
# 成果物イメージ
![スクリーンショット 2021-04-28 9.25.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1128031/1e42cd4e-40cc-9389-e50f-4fa8e6737ca9.png)

#実装

今回は、Simplecalendarというgemを導入して実装していきます
早速はじめていきましょう

まずはgemを導入します。


```Gemfile
gem 'simple_calendar', '~> 2.0'
```

※bundle installを忘れずに！！！

実は、極論これで完成といっても過言ではないです（笑）

ビューには以下を記載します。

```index.html.erb
<%= month_calendar events: @events do |date, events| %>
  <%= date.day %>
   <% events.each do |event| %>
    <div class="title">
      <%= event.title%>
    </div>
  <% end %>
<% end %>

```

一行目のmonth_calendarの部分は月間カレンダーであることを示します。
ここを、週間カレンダーにしたい場合はweek_calendarとしてやればOKです。

なお、これらの詳しいビューについては以下のコマンドを叩くと生成できます。

```terminal
rails g simple_calendar:views
```

個人的には、Googleの検証モードを使用してみてあげるのもわかりやすいかなと思います。

あとは、bootstrapの導入と個人的にCSS等で調整してあげれば成果物のような物が完成します。

# 個人的に苦しんだ部分を共有（CSS)

以上のようにカレンダー機能自体を実装することは簡単なのですが、デザインのところでかなり苦戦してしまいました。というのも、予定を入力した場合に、各日付のセル幅が、予定の内容の長さによって変わってしまい、カレンダーのデザインが崩れてしまいました。固定幅にするだけ！と鷹を括っていましたが、なぜかうまくいかず、、、

いろいろ調べてみたところ、以下のようにすると解決できました！！

```calendar.scss
.table {
　#省略
  table-layout: fixed;
}
```
table-layoutでtableの列幅を指定します。fixedとするとことで、各列を均等の幅にしてくれます。

#おわりに
ざっくりとした導入の流れを記載いたしました。おそらく、触りながらの方が理解が早いかと思いますので、まずは実際にやってみることからお勧めします！