OmniAICreator/Qiita-1.07M huggingface dataset:
Rails6でFontAwesomeを導入と表示

## はじめに
今回、オリジナルアプリにFontAwesomeを導入しました。
Rails6では、Webpackerというパッケージを使用します。

FontAwesomeとは、アイコンを簡単に導入できるというものです。
今回私のオリジナルアプリでは、いいね機能実装でいいねのボタン
に使用しました。

[![Image from Gyazo](https://i.gyazo.com/2a8ce5ee490c5e9a2646f935e86c441b.png)](https://gyazo.com/2a8ce5ee490c5e9a2646f935e86c441b)

導入すると左下のような、アイコンが使えるものになります。

## 導入手順

まず、yarnを用いてFontAwesomeをインストールします

```
% brew install yarn
% yarn add @fortawesome/fontawesome-free
```

次に、JavaScriptとStylesheetsでFontAwesomeを読み込む記述を追記します。

application.js

```javascript

import '@fortawesome/fontawesome-free/js/all';
```

 application.scss

```scss
 $fa-font-path: '@fortawesome/fontawesome-free/webfonts';
@import '@fortawesome/fontawesome-free/scss/fontawesome';
@import '@fortawesome/fontawesome-free/scss/solid';
@import '@fortawesome/fontawesome-free/scss/regular';
@import '@fortawesome/fontawesome-free/scss/brands';
@import '@fortawesome/fontawesome-free/scss/v4-shims';

```

こちらはデフォルトで、「application.css」になっているため、「application.scss」に名前を変更しましょう。

こちらで導入は完了です。

## 表示方法

まずは、FontAwesomeの[公式サイト](https://fontawesome.com/icons?d=gallery&p=2&m=free)から使いたいアイコンを探します。

[![Image from Gyazo](https://i.gyazo.com/c97db52b3e9cbb219efb38e8b8ddb546.png)](https://gyazo.com/c97db52b3e9cbb219efb38e8b8ddb546)

この時、左真ん中あたりのFreeの欄にチェックをつけるのを忘れずに！！

使用したいアイコンのページに移り、コピーします。

[![Image from Gyazo](https://i.gyazo.com/883807fc74eaa6c48a7d54030259be03.gif)](https://gyazo.com/883807fc74eaa6c48a7d54030259be03)

あとは、使いたいところに貼り付けるだけです。

```ruby
 <%= link_to  post_post_like_path(post, post.post_likes), {class: "like-link", method: :delete } do %>
      <i class="fas fa-grin-squint-tears unlike-btn"></i>
 <% end %>

```

link_toにdoをつけて~endで囲ってあげることにより、いいねボタンとして実装できます。

私は導入時、freeの以外から選んでしまい上手く表示されないというトラブルがありました。
それ以外の導入はスムーズに行きました。

freeの物だけでもたくさんアイコンがありますのでとても便利でした！！

初心者のため何か不備があるかもしれません。
その際はコメントいただけたら幸いです。

参考リンクは下記の文献です。
https://techtechmedia.com/font-awesome-rails6/
CKAD受験記(2021年2月)

# はじめに
kubernetesの資格であるCKADに合格したので、いろいろ書きます。
関連記事は沢山あるので、他であまり書かれていない事を主に書きたいと思います。

# やったこと
- k8s完全ガイド4~13章
- [CKAD-exercises(模擬問題)](https://github.com/dgkanatsios/CKAD-exercises)

CKAD-exercisesは隅々までやりましょう。
「めんどくさいなあ…」って思って飛ばした所が出まくりました。

CKAD-exercisesをやると「これkubectlワンライナー暗記試験じゃん…」って思ってしまうかもしれませんが、そうでもないです。
ワンライナーだと厳しい問題が沢山出るので、k8s完全ガイド4~13章の範囲の理解度が大事です。
とはいえ、時間はかなり短いので、以下みたいなのは空で打てるようになりましょう。

```terminal
$ kubectl run ngixn --image=nginx --restart=Never -n mynamespace --port=80 --dry-run=client -o yaml > pod.yaml
```

あと、CKAD-JPのチャットや問題は日本語なので、僕みたいな英語弱者でも大丈夫です。
ただし、試験監督は恐らく翻訳ソフトを使用する外国人なので、トラブル時に英語が必要になるかもしれません。
"I'm Japanese"程度が打てれば多分大丈夫です
(1回目は日本語で完結しましたが、2回目はいきなり中国語チャットが飛んできて色々ありました)。

# 受験までにやっておくこと
### Webカメラ用意
部屋や机を見せる必要があるので、内蔵カメラだと大変です。

### 机・部屋の片付け
「必要なもの以外は全て片付けてください」と怒られます。
片付けたつもりでいても「片付けてください」と言われてヘコむぐらい厳しいです。
私の場合、試験直前に机の物を全部床に降ろして、試験後に戻すという虚無な事をしてました。

### スペック・環境見直し
2時間ずっと画面とカメラを共有する必要があります。
ラップトップを使用するなら、冷却台を用意すると良いと思います。

# 試験中のアドバイス
### 序盤
`$kubectl edit`で使用するエディタはvimです
emacs勢は、始まったらすぐ以下を打ちましょう。

```terminal
$ export EDITOR=emacs
```

また、試験中に使えるメモ帳はガンガン活用すると良いです。
解けなかった問題の番号・配点・状況を書くと、後半で無駄に時間を溶かさずに済みます。

### 中盤
1周して、解けなかった問題のどれかを選ぶフェーズです。
配点と難易度は一致してないので、配点はしっかり見ましょう。
(序盤に簡単な高配点問題が集中してるイメージです)

### 終盤
完答できた問題の総点数よりも結果の方が点数が高かったので、恐らく部分点があります。
残り5分辺りからは、部分点で足掻くと良いでしょう。

また、2回目受ける前提で、分からなかった問題をザックリ覚えるのも良いかもしれません。
ほぼ同じ問題しか出ないので。
(CKADは1回試験料を払ったら2回受けられます)

# おわりに
そんなこんなで、1回目59点、2回目79点で合格しました。
あなたの参考になれば幸いです。
【Git】git pushでエラーが出た時の対処法。error: src refspec ブランチ名 does not match any

Git pushをしようとすると、`error: src refspec ブランチ名 does not match any`といったエラーが出る、、


`git remore -v`で確認すると、対象のリモートレポジトリはちゃんと登録されてる。。


##原因と対処法
自分のいるブランチを見てみる。おそらく送信したいブランチが存在してないはず、、

```
$ git branch
* master
```

もしくは、fetchしたブランチに移動した場合は、detached headになっている。

```
$ git branch
master
*(HEAD detached at origin/44382)
```

送信しようとしてるブランチがそもそも存在していない、、そりゃsrcがないと言われます、、


###対処法

```:ブランチの作成と移動
$ git checkout -b ブランチ名
```

detached headの場合は

```:ブランチの移動
$ git checkout ブランチ名
```

このあとで、git pushしたらうまくいく。
間違い探しでデザイン脳トレ＃1

## はじめに
こんにちは。株式会社ベーシックでデザイナー寄りのフロントエンドエンジニアをやっています。Uと申します。好きな食べ物はぶりしゃぶです。

UI/UXの重要性が説かれて久しいですが、社内のエンジニア仲間や個人開発をしている知人などから「自分はセンスがない」とか「お客さんに論理的な説明ができない」という悩みをよく聞きます。しかしですね。基本的な考え方をおさえれば、（"それなり"のレベルまでなら）誰でもデザインができるようになると私は思うのです！
ということで今回はそのような基本をお伝えするべく、よくあるWEBアプリケーションを例に「デザイン間違い探し」をつくってみました。いったいどこが違うのか？当ててみてください！

## 第1問 情報の順序を考えよう（例：乗換案内アプリ）

電車の乗換案内アプリはみなさん馴染み深いと思います。（弊社は2021年2月現在、テレワークメインになっているため乗車機会は減りましたが…）
こちらの画像も普段何気なく使っている乗換案内の画面のようですが、実はひとつ大きな間違いがあります。どこでしょうか？
![第1問](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53402/87ede2b6-25b0-2ab9-3542-c8e023a3aa72.png)

<details>
<summary>:triangular_flag_on_post:解答・解説はこちら</summary>
<div>

### 第1問 解答
出発・到着駅入力エリアと、日時等詳細入力エリアが上下入れ替わっている！
↓正解はこちら！
![第1問 - 解答](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53402/105d179d-b37b-14ad-afdc-4816c70cdfd5.png)
### 第1問 解説
こういった入力パーツは情報の優先度・重要度を整理して、高い順から配置するとよいでしょう。
ポイントは、 **ユーザーの思考順序を邪魔しない** ことです！
たとえばこんな風に、ユーザーの頭の中を想像して書き出してみるといいですね。

1. 「東京から新横浜の経路を知りたい」
1. 「来週末の朝10時に到着したい」
1. 「慣れていないので乗り換え時間を多くとりたい」
1. 「新幹線ではなく在来線で行きたい」

1と2の重要度の高さは明らかなので上部に配置！（1に関しては、入力パーツのサイズも大きくなっていますね）
3、4の優先度はほぼ同列でしょうか。細かい順序は気にしなくてよさそうです。
もし項目の数が多くて困ったら、無理に一つ一つの順序を考えなくてもよいです。
**重要度 高 ＞ 中 ＞ 低** の3グループに分類し配置するだけでもまずはオッケー！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53402/0c7d9537-6514-a96e-4714-e8cfae1a6a6c.png" alt="第1問 - 解説(BAD)" width="50%" style="display: inline-block;"/><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53402/172580ae-fc7e-9e21-8f29-58f22e6356b6.png" alt="第1問 - 解説(GOOD)" width="50%" style="display: inline-block;"/>
</div></details>

## 第2問 情報の強弱を考えよう（例：ECサイト）
外出を控えるようになって、みなさんもオンラインでのお買い物が増えたのではないでしょうか。お手軽すぎるあまり、深く考えずにポチっとしてしまうんですよね〜。気をつけなければ…。
ということで、2問目はよくあるECサイトの商品検索結果より問題です。間違いはどこだ？？
![第2問](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53402/9f48503f-e8b4-85b1-0e96-a487155bc719.png)

<details>
<summary>:triangular_flag_on_post:解答・解説はこちら</summary>
<div>

### 第2問 解答
使われているフォントのスタイル（大きさ、太さ、色）がすべて同じになっている！
↓正解はこちら！
![第2問 - 解答](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53402/c2ceba68-fed6-95a6-8b74-3bae631ecd25.png)

### 第2問 解説
「間違い」ではいろんな情報が一律で目に飛び込んできて、視線が迷子になりませんか？一方「正解」では、商品名・価格が瞬時に認識できます。
重要度の高い商品名と価格のフォントを濃く、その他の補助情報を小さく薄く表現する--つまり **情報に強弱をつける** ことで、ユーザーの認知負荷が軽減されているのです。
レイアウト都合で配置の自由が効かない（スマートフォンアプリなど特に）……という場面においても、このように強弱を意識してサイズや色を工夫すれば、わかりやすさを損なわずに情報を配置することができますね！
![第2問 - 解説](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53402/846468c0-79a6-3df4-f93c-3fbc96659113.png)

</div></details>

## 今回のポイントまとめ
今回のテーマは **「情報の優先度・重要度を考える！」** でした。ポイントをおさらいしましょう。

1. まずは要素を洗い出し、優先度をつけてみる
1. 優先度が高い順に配置する
1. 色や形で強弱をつける

これらを意識することで、ユーザーの操作手順・思考を邪魔しない、つまりストレスの少ないアプリになります:sparkles:もちろん、ポスターやPOP、スライド等にもこのポイントが使えますのでぜひお試しください。

いかがでしたか？今後も「間違い探しでデザイン脳トレ」シリーズとしてこのような問題を出していく予定です。ご期待ください！
readonlyしてないのにreadonlyエラー

# なんでだろう

~~~js
interface UserModel {
    name: string;
}
const newUser = Object.create(UserModel);
newUser.name = 'xxx' // ←TypeError: "name" is read-only
~~~

## 調べてわかったこと

* JavaScript組み込みオブジェクトのFunctionに[name](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/name)という名の
「読み取り専用」プロパティがある  
　　　　　↓  
* TypeScriptの言語的な特性上、その読み取り専用のプロパティに代入しようとしたと
判断されてしまった？

## どう対応したか

* UserModelのnameをuserNameに変更しました。

・・・ほかにも方法があったのか、詳しい人いたら教えてくださいまし m(__)m
文字列連結時、大量の場合➡効率について

文字列連結時、大量の場合➡効率について、以下は参考になります。
 
```sample.js
//効率悪い例：
　　detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
　　detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
　　detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
//改善例：
　　detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
           + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
           + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
```
```sample.cs
//効率悪い例：
　　detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
　　detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
　　detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
//改善例：
      StringBuilder retSb = new StringBuilder();
      retSb.Append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
      retSb.Append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
      retSb.Append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
//※大量の文字列に対し、正規表現を使わないように。非常に遅くなる。
```
JavaはC#と同様。
フォームデータの送信

###form要素
データを送信する方法を定義します。その属性すべてが、ユーザーが送信ボタンを押すと送信されるリクエストを調整できるように設計されています。



###action 属性
action 属性は、どこにデータを送信するかを定義します。値は妥当な相対/絶対 URL でなければなりません。この属性が与えられなかった場合は、フォームが含まれているページの URL にデータが送信されます。


###GET メソッド
GET メソッドは、サーバーに対して指定したリソースを返すよう求めるためにブラウザーが使用するメソッドです。


###POST メソッド
POST メソッドは少し異なります。これは、HTTP リクエストの本文で提供したデータを考慮したレスポンスの要求を、ブラウザーがサーバーに送信するためのメソッドです。
TypeScript - Learning Road Map

# 1. JavaScript ES6+
* https://ithelp.ithome.com.tw/users/20119924/ironman/2235
* https://es6.ruanyifeng.com/

# 2. TypeScript
* https://ithelp.ithome.com.tw/users/20119139/ironman/2121
* https://willh.gitbook.io/typescript-tutorial/
Twitter API 取得方法の参考サイト


##はじめに
これからtwitterAPIの取得を行うための
参考になりそうなサイトを集めました
※今後実際に行って、取得方法・KEY・トークン等の取得方法を
　まとめていく予定です


##TwitterDeveloper

Twitterのdeveloperサイト
https://developer.twitter.com/

##API 利用申請の方法(参考リンク先)

**Twitter API 利用申請の事例**
https://qiita.com/shozzy/items/13843dccf25c26f8ef51


**TwitterAPI登録申請から承認までの流れ。取得審査が厳しい【例文あり】**
https://engineer-step.com/entry/twitter-api-fail/

**Twitter APIの申請方法と英語の例文**
https://qiita.com/QrioYagi/items/6afcbfb7193562921ee6


**2021年度版 Twitter API利用申請の例文からAPIキーの取得まで詳しく解説**
https://www.itti.jp/web-direction/how-to-apply-for-twitter-api/
【HTML】div, p, spanタグの使い分け、どれをいつ使うのか？

個人メモです。

div, p, spanタグ、なんとなく使っているので、いつどれを使えばいいか迷う、、

目的を持って使えるようにする。


##ざっくり

| タグ | 用途 |
|:-:|:--|
| div  | 要素を囲むときに使う。wrapper  |
| p  | 文章に使う  |
| span  | 文字を修飾するときに使う。  |


コメント内容など文章をいれる場合は`p`タグを使う。

単語を挿入したい場合や、文章中の単語を色づけなど装飾したい場合は`span`タグを使う。

`div`はレスポンシブなどで、横並びにするなど動かしたい要素のまとまりを囲む。




<br>
##dlタグ

「〇〇とは？ ▲▲である」　といった１組の定義と説明を表す場合は、`dl, dt, dd`タグを使う。

divタグとpタグの組み合わせなどでも同じものが作れるが、HTMLを見たときに、内容をわかりやすくしておく。

・[dl, dt, ddタグとは？用途を実例で確認](https://qiita.com/yuta-38/items/cc6f1065c7fa9ab16004)



<br>
##まとめ
dispalyプロパティなどcssを使えばほぼdivだけで対応することもできるが、

**HTMLの可読性をあげ、メンテナンスをしやすくするために、タグの意味を理解して、目的にあったタグを使い分けることが重要**。


後からコードを直すときに、この配慮が結構効いてくる。
【Rails】seed_fuまとめ

## 何がうれしいか
- seedデータの一部を変更した時、変更したファイルだけを読み込み、データの更新や追加ができる
  - デフォルトの`seeds.rb`の場合、既存のデータを削除してから再度読み込まなきゃいけない
- 環境ごとにseedデータを分けやすくなる

## インストール
```rb:Gemfile
gem 'seed-fu'
```
```bash
$ bundle install
```
```bash
$ mkdir db/fixtures # 必須
# 以下、環境ごとにseedファイルを分けたい場合に作成
$ mkdir db/fixtures/development
$ mkdir db/fixtures/production
```

## データを読み込む
```bash
$ touch db/fixtures/development/01_user.rb #ファイル名は自由。アルファベット順に読み込まれる。
```
以下の2通りの書き方がある。データが多い時は2つ目の書き方の方が良さそう。

```rb:基本の書き方
User.seed do |s|
  s.id = 1
  s.name = '茂野吾郎'
  s.email = 'shigeno@example.com'
  s.password  = 'password'
end

User.seed do |s|
  s.id = 2
  s.name = '佐藤寿也'
  s.email = 'sato@example.com'
  s.password  = 'password'
end
```
```rb:この書き方でも良い
User.seed(
  {
    name: '茂野吾郎',
    email: 'shigeno@example.com',
    password: 'password'
  },
 {
    name: '佐藤寿也',
    email: 'sato@example.com',
    password: 'password'
  },
)
```
```bash
$ rails db:seed_fu
```

## 読み込むseedファイルを明示的に指定する
```bash
$ rails db:seed_fu
```
開発環境で上記のコマンドを叩くと、`db/fixtures/development`以下のファイルだけが読み込まれる。
他のディレクトリのファイルを読みたい場合などには以下のようにディレクトリを指定することができる。

```bash
$ rails db:seed_fu FIXTURE_PATH=db/fixtures/hogehoge
```

## 【注意】データの同一性はidで判断される
既存のデータが更新されたのか、新規データが追加されたのかは、idを元に判断されている。seedファイル内のデータにidを書かないと、既存のデータを更新したつもりでも新規データが追加されてしまう。

id以外の(ユニークな)データを基準にデータの同一性を判断させたい場合は、以下のように書く。

```rb:emailがユニークな場合
User.seed(:email) do |s|
  s.name = 'foo'
  s.email = 'foo@example.com'
  s.password  = 'password'
end

# or

User.seed(:email,
  {
    name: 'foo',
    email: 'foo@example.com',
    password: 'password'
  }
)
```

## 既存のDBからseedファイルを作成するrakeタスク
`SeedFu::Writer.write`を使ってseedファイルを作ることができる

```bash:
$ rails g task seed-fu-gen-user
```
```rb:lib.tasks/seed_fu_gen_user.rake
namespace :seed_fu_gen_user do
  desc 'usersテーブルのデータを元にseedファイルを生成'
  task create_seed_by_db: :environment do
    SeedFu::Writer.write('./db/fixtures/user_gen.rb', class_name: 'User') do |w|
      User.all.each do |x|
        w << x.as_json(except: %i[created_at updated_at])
      end
    end
  end
end
```
```bash
$ ./bin/rake seed_fu_gen_user:create_seed_by_db
```

※公式のREADMEを読むと、`SeedFu::Writer`にはもろもろ変更が入ったと注意書きがありますが、少なくともver2.3.9では上記の方法で動作確認ができました。

## 参考
- https://github.com/mbleigh/seed-fu
- [railsで初期データを入れる(seed-fuの使い方) - Qiita](https://qiita.com/ko2ic/items/be96e450a33d631e0059)
- [【Rails】seed-fu で user の seed を作りたい - Qiita](https://qiita.com/punkshiraishi/items/18c80475fcd50d708dc4)
- [【Rails】rakeタスクを実装する - Qiita](https://qiita.com/d0ne1s/items/eb83f44b6f9e23de0b0d)
making test data in js

If you want blank arbitrary number of Array data.

[...Array(n)] gives us undefined value of array with (n),

while [...Array(n).keys()], 1...(n) of array. 

```js
  const [data, setData] = React.useState(
    [...Array(167)].map((_, index) => ({
      x: index,
      y: Math.floor(Math.random() * 100) + 40,
      date: new Date(),
    })),
  )
```
4桁の整数の各桁の値を取り出す(C++)


AtCoder用備忘録

例えばx=1234を受け取って、以下のように各桁の値a=1、b=2、c=3、d=4を格納できます

```言語:main.cpp
    int x; cin >>x;
    //各桁の値をa,b,c,dとする
    int a=x/1000;
    int b=(x/100)%10;
    int c=(x/10)%10;
    int d=x%10;
```

参考問題
[abc079 C - Train Ticket](https://atcoder.jp/contests/abc079/tasks/abc079_c)
Dockerでwordpressを立てた時にメール送信ができなかった

# 問題
EC2インスタンス上でDocker(docker-compose)を使ってWordpressを構築した際に、Wordpressの設定変更や問い合わせなどでメール送信ができなかった。

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

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:5.6
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: {}
```

これだとdockerのwordpressイメージの中にメール送信機能(sendmail)が含まれていないため、メール送信ができない。


# 解決
※SMTPサーバーは使わずに、sendmailを入れてとりあえずメール送信ができるようにする方法です。


```Dockerfile:Dockerfile
FROM wordpress:5.6

# sendmailをインストール
RUN apt-get update \
 && apt-get install -y --no-install-recommends sendmail \
 && rm -rf /var/lib/apt/lists/*

# sendmailをphpで使うための設定
RUN echo "sendmail_path=sendmail -t -i" >> /usr/local/etc/php/conf.d/sendmail.ini

# エントリポイントとして使用するスクリプトを作成
RUN echo '#!/bin/bash' >> /usr/local/bin/docker-entrypoint-wrapper.sh \
 && echo 'set -euo pipefail' >> /usr/local/bin/docker-entrypoint-wrapper.sh

# 1. /etc/hostsにコンテナホスト名のlocalhostエントリを作成
RUN echo 'echo "127.0.0.1 $(hostname) localhost localhost.localdomain" >> /etc/hosts' >> /usr/local/bin/docker-entrypoint-wrapper.sh

# 2. sendmailの再起動
RUN echo 'service sendmail restart' >> /usr/local/bin/docker-entrypoint-wrapper.sh

# 3. docker-entrypoint.shを呼ぶ
RUN echo 'exec docker-entrypoint.sh "$@"' >> /usr/local/bin/docker-entrypoint-wrapper.sh \
 && chmod +x /usr/local/bin/docker-entrypoint-wrapper.sh

ENTRYPOINT ["docker-entrypoint-wrapper.sh"]
CMD ["apache2-foreground"]
```
[参考コード](https://github.com/hochzehn/wordpress-sendmail-docker/blob/master/Dockerfile)

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

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     build:
      context: .
      dockerfile: ./Dockerfile
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: {}
```
DCモータのPWM制御により曲を演奏する（matlab+mbed）


DCモーターのPWM制御を行うと、PWM周波数に従ってモーターから音が発生します。
これを利用して、モーターで音楽を演奏します。
mp3ファイルから読み込んだ音声を、FFTして周波数（時間変化する）に変換します。
得た周波数データを逐次マイコンに送信し、マイコンは受信したPWM周波数でモーターをPWM制御します。

以下のような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260065/a405f8c0-5853-9ceb-9185-53147d624422.png)

# 環境

- matlab
- マイコン：mbedLPC1768
- モータドライバ：自作（Hブリッジ式）
- モーター：マブチ rz-735

モータドライバは高い周波数に耐えられればなんでもいいんじゃないかな。
20 kHzくらいまで出せればオッケーなので、Pololuのやつなら何でもいいと思う。

https://store.shopping.yahoo.co.jp/suzakulab/pololu-2991.html

# matlabコード

matalab 2020以上でないとserialportが使えないので注意

読み込んだ音声ファイルを、区間NでFFTし、そのときの最大スペクトルを持つ周波数をモーターへの出力周波数としています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260065/76ead111-53b0-29b5-f931-565819240c3e.png)



```matlab
port_name = 'COM26'; %COMポート
filename = 'hogehoge.mp3'; %音声ファイル名

[audio,Fs] = audioread(filename);
T = 1/Fs;
L = length(audio);
start_sec = 0.1;
start = round(start_sec / T);
step = 1000;
N = 1000;
input_list = zeros(1, start+L);

for i = start : step : start+L-N
    % stepずつ走査し,区間NでaudioをFFT
    y = audio(i : i+N);
    Y = fft(y(1:1+N));
    P2 = abs(Y/N);
    P1 = P2(1:N/2+1);
    P1(2:end-1) = 2*P1(2:end-1);
    f = Fs*(0:(N/2))/N;
    [~,I]=max(P1);
    Ma=f(I);
    input_list(i) = round(Ma);

    % テスト用に音声を再現
    t = [T*1 : T : T*N];
    Ma = round(Ma);
    output(i-start+1 : i-start+N) = sin(2*pi*Ma*t);
end

% スピーカーに音声を出力
% sound(output(start:L-N), Fs);

% mbedに送信
s = serialport(port_name, 9600);

for i = start : step : start+L-N
    h = bitshift(input_list(i), -16); %上位ビット
    m = bitshift(input_list(i), -8);  %中位ビット
    l = bitand(input_list(i), 255);   %下位ビット
    %送信
    write(s,h,"uint8");
    write(s,m,"uint8");
    write(s,l,"uint8");
    % 待機
    pause(T*step);
end
write(s,0,"uint8");
write(s,0,"uint8");
write(s,0,"uint8");
clear s;
```

# mbedコード

以下のコードはコンパイル通らないので注意です。
MotorControlerクラスのところだけうまいこと書き換えてください。
シリアル通信のところはそのままで大丈夫です。
通信ずれると止まりますが、まあいいでしょう

```c++
#include "mbed.h"
#include "MotorControler.h"

int main()
{
    Serial matlab(USBTX, USBRX);
    MotorControler MOTOR(p21,p22,p23);
    MOTOR.setPwmFrequency(20000);
    MOTOR.enableDriver();
    MOTOR = 0.7;
    
    while(1)
    {
        if(matlab.readable() > 0)
        {
            int h = (int)matlab.getc();
            int m = (int)matlab.getc();
            int l = (int)matlab.getc();
            MOTOR.setPwmFrequency(h<<16 | m<<8 | l);
        }
    }
}

```

# 結果

ボッカデラベリタ
https://twitter.com/newlvAlPr/status/1363865660887998467

夜に駆ける
https://twitter.com/newlvAlPr/status/1364057845604343809
ローカルで作業中のリポジトリをGitHubの新規リモートリポジトリにプッシュする方法

ローカルリポジトリ開発を進めた
↓
そのあとにGitHubで新規リモートリポジトリを作る
↓
プッシュする

という流れを記しておきます。

##1.ローカルリポジトリで開発
###git init
でイニシャライズして開発を進めます。
プッシュ前には、branch　をmasterにしておきます。（わかりやすくするため）

##2.GitHubで新規リモートリポジトリを作る
GitHubの管理画面で、新規リポジトリhogeを作成する。
リポジトリのURLが発行される。

```
https://github.com/(user)/hoge.git
```

##3.プッシュする
プッシュする前に、先ほどのURLをもとに書きコマンドを実行しておく。
これで、今後はoriginという名前でGitHubリポジトリとやり取りできるようになる。

```
$ git remote add origin https://github.com/(user)/hoge.git
```

そのあと、下記コマンドでプッシュする。


```
$ git push -u origin master
```
origin：リモート名
master：ブランチ名
###-u
次回以降、origin master という状況ではgit pushのみでプッシュできるようになる。
初回プッシュするときに付けておくとあとが楽になります。


GitHub上でファイルがプッシュされていれば成功です。
以上です。
flutter_svg で No named parameter with the name 'nullOk'. エラー

# はじめに

flutter で SVG 画像を表示したいと思い方法を探していると、以下のパッケージを使っている記事が多かったので採用することに。（pub.dev で検索してみましたが、他に良さそうなパッケージもなかったこともあり）

https://pub.dev/packages/flutter_svg

## 環境

Flutter (Channel beta, 1.26.0-17.6.pre, on macOS 11.2.1 20D74 darwin-x64, locale en-GB)

## エラー内容

こちらが表示されて SVG 画像を表示することができませんでした。

```text
No named parameter with the name 'nullOk'.
```

## 解決方法

GitHub の Issue に同様の問題でやりとりされていました。

https://github.com/dnfield/flutter_svg/issues/479

私の環境では以下のように flutter_svg のバージョンをあげ、 nullsafety のパッケージに変更することで解消しました。

```yaml:pubspec.yaml
  flutter_svg: 0.21.0-nullsafety.0
```
【エクセル】固有オブジェクト

# Application
　全B00K中一つのみのオブジェクト
### プロパティ一覧
#### DisplayAlerts
　DefaultはTrue, Falseにすることでシート削除時の「削除しますか？」といったダイアログを
　非表示にすることができる。(マクロでダイアログを操作する手間を省ける。)
#### ScreenUpdating
　DefaltはTrue、Falseにすることでマクロ実行中の画面更新を止める。(実行速度の向上)
#### StatusBar
  文字列をBookの左下に表示することができる。

### コード例
```vb
  Application.DisplayAlerts = False
  Application.ScreenUpdating = False
  
  Application.StatusBar = "処理開始"
  With ThisWorkbook
    .Worksheets.Add after:=.Worksheets(Worksheets.Count)
    Worksheets(Worksheets.Count).Delete
  End With
  Application.StatusBar = "処理完了"
  
  Application.DisplayAlerts = True
  Application.ScreenUpdating = True
  Application.StatusBar = ""
```

# WorkSheetFunction
　SumやCountIfなど非VBA関数が使用できるようになる。

```vb
  With WorksheetFunction
    If .Sum(r.Offset(, 1).Resize(, 5)) >= 350 And _
       .CountIf(r.Offset(, 1).Resize(, 5), ">=50") = 5
```
【SwiftUI】Buttonにおける .padding() の位置を気をつけようという話

# どういうことか

`.padding()` の書く位置によって表示が変わる！

## どうなるのか

```swift
Button(action: {
    print("押された")
}){
    Text("ボタン")
        .fontWeight(.bold)
        .font(.system(size: 20))
        .foregroundColor(Color.white)
        .background(Color.blue)
        .frame(maxWidth: .infinity, minHeight: 48)
        .padding(.horizontal, 32)
}
```

これだとこう

![fdd2b92d411d864aa635d452befaf958.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413699/e6d13939-1a02-4d63-80f0-32de1d9b4432.png)

ちょっと見づらいが、想定通り画面端からの `padding` が効いている。
ところが

```swift
Button(action: {
    print("押された")
}){
    Text("ボタン")
        .fontWeight(.bold)
        .font(.system(size: 20))
        .foregroundColor(Color.white)
        .background(Color.blue)
        .padding(.horizontal, 32)
        .frame(maxWidth: .infinity, minHeight: 48)
}
```

![c4cbcd62d957f4de60f361e7a626e2eb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413699/47c93de8-72ab-64c2-52cc-710282c2b8a2.png)

`frame` の後ではなく前に書くと効かなくなってしまう。なんてことだ。
で、 `padding` だけじゃなくて `cornerRadius` とかも場所で効いたり効かなかったりが変わるっぽい。気をつけよう。
EC2 サーバー構築(基本設定)

# EC2 サーバー構築

## 概要
EC2インスタンスを初期構築する際に行うことをまとめてみました。
不定期ですが、更新する予定です。

## 環境

|項目|値|
|--|--|
|AMI名|al2023-ami-2023.4.20240611.0-kernel-6.1-arm64|
|インスタンスタイプ|t4g.small|

## システム設定

### パッケージ更新

```bash
sudo dnf -y update
```

### タイムゾーン・ロケール設定
#### タイムゾーン確認

```bash
timedatectl status
```

#### タイムゾーンを日本時間に設定

```bash
sudo timedatectl set-timezone Asia/Tokyo
```

#### ロケール確認

```bash
localectl status
```

#### ロケール変更

```bash
sudo localectl set-locale LANG=ja_JP.UTF-8
```

### ホスト名設定

現在の設定

```bash
[ec2-user@ip-10-0-10-255 ~]$ hostnamectl status --static
ip-10-0-10-255.ap-northeast-1.compute.internal
```

設定変更

```
[ec2-user@ip-10-0-10-255 ~]$ sudo hostnamectl set-hostname app01
```

設定確認

```
[ec2-user@ip-10-0-10-255 ~]$ hostnamectl status --static
app01
```

一度SSH接続を切って、再度SSH接続してみてください。
コンソールが指定したものに変わっていればOKです。

```
[ec2-user@app01 ~]$
```
Vue.jsの基本的な使い方について　2021/1/27

今日やること
vue.jsを使うにあたり必要なプラグインを知り、基本的な文法を使ってフロントエンド側でログインボタンを作る

#vue.jsのプラグインをインストール

veturをインストールしよう！

vue cliで新しいプロジェクトを作っただけではvscode上でうまく`.vue`ファイルを扱うことができないので、拡張機能のVeturを導入します。

![スクリーンショット 2021-02-25 9.49.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/ef124121-2bc6-8c66-6217-db290936f6cb.png)


#コンポーネントの作成

先ほぼの工程でvscode上でvue.jsファイルを扱えるようになったので次は新しいコンポーネントを作ってその中にfacebookのログインボタン機能を作っていく。


```js:FBButton.vue
<template>
    <button>Login with Facebook</button>
</template>

<script>
export default {
    name: 'FBButton'
}
</script>

<style>

</style>
```

ここではfacebookのログインボタンを作ったので次に`App.vue`に反映させていく。

基本的に他のファイルでコンポーネントを追加しても`App.vue`に反映を書かなければブラウザ上には表示されない。

ここで、Vueアプリケーション雛形を確認しておこう。

![スクリーンショット 2021-02-25 10.13.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/8a737cb8-14cf-da8c-b53a-af62ec3c96cd.png)


`FBButton.vue`があるのは`App.vue`の下の部分である。

```js:App.vue
<template>
 <div>
    <FBButton />
 </div>
</template>

<script>
export default {
    name: 'App'
    components: {
        FBButton
    }
}

</script>

<style>
</style>
```
するとこのようなページができる

![スクリーンショット 2021-02-25 10.18.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/501a5794-0de5-c6fa-437f-e1628a05cff9.png)


シンプルだが悪くないだろうw

#v-onディレクティブ

次にログインボタンをクリックすると実行される処理について書いていこう。

ここで躓きやすいポイントは`v-on`（ボタンをクリックして関数を実行する）について理解しているかどうか。

>イベント
>>Webアプリケーションにおいて（つまりDOM要素において）、ボタンをクリックしたり、マウスオーバーによる一連の挙動。そして、それら様々なイベントに対して発動させる処理のことを（つまり関数）を「イベントハンドラ」を呼ぶ


Vue.jsにおいて、イベントによってイベントハンドラを実行させるには、v-onディレクティブを利用します。

つまり今回の場合、ユーザーがfacebookボタンをクリックする→イベントが発生する→何らかの処理をするという流れになる。

```js:FBButton.vue
<template>
        //クリックのイベントを指定
    <button @click="open">Login with Facebook</button>
</template>

<script>
export default {
    name: 'FBButton',
    methods: {
 　　　　　　// openを定義
        open () {
            document.location.href="URL"
        }
    }
}
</script>

<style>

</style>
```

v-onディレクティブにクリック（click）のイベントを指定し、openという名前のイベントハンドラを設定しています。v-onは省略して下記のように記述することができます。

JavaScript
<button @click="showMessage">ボタン</button>
 
また、methodsオプションにて、イベントハンドラであるopenを定義しています。ちなみに、`document.location.href`というのはJavaScriptでURLを遷移させる方法です。

今日はここまで
jsPsych 6.3.0からファイルの事前読み込み方法が変わったよ

今から[jsPsych](https://www.jspsych.org/)を使い始めようという人も、いままで使っていたよという人にも重要な内容です。[2021/2/22にバージョン6.3.0が公開されました。](https://github.com/jspsych/jsPsych/releases)

今回公開された新機能は多岐にわたるのですが、そのなかでもファイルの事前読み込み方法が変わった点は注意が必要かと思い、以下にまとめておきます。

# そもそもファイルの事前読み込みはどうして必要なの？

画像や動画、音声ファイルを使った実験では、指定したタイミングでそれらを呈示したいというのが人情です。ところが事前読み込みをしていないと、呈示する段になってあわてて読み込むので、刺激の呈示タイミングに重大な影響を及ぼします。なので事前読み込みは必ずしましょうね、という話です。

# 6.2.0まではどうやってたの？

基本的にjsPsychは自動で事前読み込みをするように設計されていますが、これが適切に機能するには制約があります。例えばcode1は自動で画像ファイルを読み込んでくれますが、code2は自動で読み込みません。

```javascript:code1　自動で読み込む
var trial = {
  type: 'image-button-response',
  stimulus: 'file.jpg'
}
```

```javascript:code2　自動で読み込まない
var trial = {
  type: 'html-button-response',
  stimulus: '<img src="file.jpg">'
}
```

code2のようなケースでは次のような形で手動で読み込む必要がありました。

```javascript
var images = ['image1.png', 'image2.png', 'image3.png'];
 
jsPsych.init({
  timeline: [trial],
  preload_images: images, // The image data should be preloaded.
});
```

# 6.3.0以降で以下のようにやる

自動で読み込むときと読み込まないときがあると上で説明しましたが、それについては変更はありません。正直なところ、どういうときに自動で読み込むのかを完全に把握するのは結構な手間ですよね・・・。なので私は基本的に手動で読み込むようにしています。どんな場合も手動！　手動は自動を包含します。なので、ここでも手動での説明にしぼります。

まず、事前読み込み用のプラグインを使う点に注意してください。つまり、ファイルの冒頭に以下のような一文が必要になります。

```javascript
<script src="../plugins/jspsych-preload.js"></script>
```

それから、事前読み込み用の試行を設けます。

```javascript

    const images = [ // プログラム内で使用するファイル名を列挙した配列を作ります
        // この例ではimgフォルダに画像がまとまっていると想定していますが、まとまっていなくてもOK
        './img/image1.png',
        './img/image2.png',
        './img/image3.png'
    ];

    const sounds = [
        './sound1.wav',
        './sound2.mp3',
        './sound3.wav'
    ];

    const preload = {
        type: 'preload',
        images: images,
        audio: sounds
    }
```

そうして、initのtimelineにpreloadを含めます。このとき、実際にファイルを呈示する試行（練習や本試行）よりも前で読み込むように注意してください。

```javascript
jsPsych.init({
    timeline: [preload, practice, experiment, ... ]
})
```

# めんどくさくなってない？

事前読み込みの方法が、いままでよりも複雑になってないか？　そんな声が聞こえてきそうです。私もその点は同意します。ただ、こんなふうに変更した意味がもちろんあるわけですね。

具体的には動画を使うような実験で恩恵を受けると思われます。

動画の事前読み込みは、かなりの時間を要することがあります。私が以前に作ったプログラムでも、実験前に１分以上待つことがありました。あれ？　フリーズしてる？って思うくらい。

例えば動画ファイルを３つ使う実験だったとして、実験前に３つのファイルすべてを読み込むのが今までのやり方でした。6.3.0以降では、これを次のように書くことができるようになりました。

```javascript
jsPsych.init({
    timeline: [preload1, trial1, preload2, trial2, preload3, trial3]
})
```

つまり、呈示するファイルだけを呈示の直前で読み込むことができるんですね。こうすることで読み込み時間（待ち時間）を分散することができるようになったのです！　もちろん、いっきにすべてのファイルを読み込んでも問題がないときには、まとめて読み込むことも可能です。

説明は以上です。

ここで説明したことのほかにも、**事前読み込みに失敗しても実験を強行できるオプション**や、**エラーの詳細を画面に表示したりすることができる**ようになっています。詳しくは、公式の[事前読み込みの概要](https://www.jspsych.org/overview/media-preloading/)と[preloadプラグインの説明](https://www.jspsych.org/plugins/jspsych-preload/)をご覧ください。

なお、私が作成した[psychophysicsプラグイン](https://jspsychophysics.hes.kyushu-u.ac.jp/)を使うときにも事前読み込みを忘れずに行ってくださいね。
簡単イージング

なぜかUnityにはイージングを制御する関数がありません。
なので自分用にイージングを極力簡略化したスクリプトを組みました。

本来イージングには開始値、終了値、経過時間、トータル時間の4つが必要ですが、実際イージングは比率しか求めないので開始位置と終了位置は無視して経過時間/トータル時間のみを渡して計算してます。

このスクリプトは０～１までの値を渡して、その値にイージング処理した値を返すものです。

たとえばLerpのt値にこれらの処理を加えることでイージング効果がでます。

```C#:Calc.cs
    public static float EasyEaseIn(float t)
    {
        return   t * t;
    }
    public static float EasyEaseOut(float t)
    {
        return -1 * t * (t - 2.0f);
    }
    public static float EasyEaseInOut(float t)
    {
        t = t *0.5f;
        if (t < 1) { return 0.5f * t * t; }
        t = t - 1;
        return -0.5f * (t * (t - 2.0f) - 1) ;
    }

```
【エクセル】正規表現

# 下準備
　ExcelVBAで正規表現を用いた検索、置換を行うには、以下の参照設定にチェックを入れる。
　Microsoft VBScript Regular Expression X.X

# ソースコード
```vb
  ' 正規表現オブジェクト
  Dim r As New regExp

  ' 検索・置換のパターン(連続した数字3文字)
  r.Pattern = "[0-9][0-9][0-9]"
  ' 検索パターンに一致するか
  If r.Test("3x00") Then
    MsgBox ("一致")
  End If
  ' 文字列を置換する。(300XXX -> YYYXXX)
  MsgBox (r.Replace("300XXX", "YYY"))
  Set r = Nothing
  
```
Google Cloud認定資格の遠隔監視オンライン試験で日本の免許証が使えなかった話

# Google Cloud認定資格の遠隔監視オンライン試験
公式リンク
https://support.google.com/cloud-certification/answer/9907852?hl=ja&ref_topic=9433463

Google Cloud認定資格の遠隔監視オンライン試験ができるということで、受けてみることにした。
準備のポイントは以下の感じ。

- Webカメラ、マイクを用意。マイクはヘッドホンやイヤホン付きは不可
- 生体認証のアプリをインストール。※写真が変なタイミングで撮れてしまって不安だったので、1回サポートに依頼して取り直し実施
- 試験のカメラ、マイク、速度チェックを実施
- 机の上を片付ける。


# オンライン試験の良いところ
- 自宅で受けれる
- 受験時間の選択肢が多い。

特に後者は選択できる時間を見たところ、平日なら24時間の選択肢があった。
休日は埋まっていたのか選択肢は少なかった。
平日の遅い時間に受験できるのは、結構大きいメリットだと思う。

# 身分証明について
IDについての説明で、日本の運転免許証は日本語なので、不安を感じつつも、
追加の身分証明のクレジットカードと合わせれば、いけると思って、試験に申し込んだ。

公式リンク
https://support.google.com/cloud-certification/answer/9907450?hl=ja&ref_topic=9433463

>試験会場または遠隔地で実施される試験では、ご本人の氏名、顔写真、署名が記載された、政府発行のメインの身分証明書を提示する必要があります。たとえば、次のものをご用意ください。

>政府発行の運転免許証
米国国務省発行の運転免許証
国 / 州 / 郡の身分証明書
パスポート
パスポート カード
軍の身分証明書
外国人登録カード（グリーンカード、永住ビザ）
追加の身分証明書としてご利用いただけるのは、ご本人の氏名と署名が記載されているものです。たとえば、次のものをご利用いただけます。
米国の社会保障カード
デビットカード / キャッシュ カード
クレジット カード
学生証
メインの身分証明書

>注: Webassessor アカウントに登録されている氏名は、政府発行の写真付き身分証明書に記載されている氏名と一致している必要があります。不一致があった場合、試験を受けることはできません。また、受験料は払い戻されません。試験予定日より前に登録氏名を更新するには、サポートにお問い合わせください。

# 日本の免許証が使えなかった
オンライン試験当日の身分証明で、
日本の免許証とクレジットカードを見せたところ、

- クレジットカードはID確認として認めない
- 日本の免許証ではID確認できない
- Googleのサポートに問い合わせろ

ということでキャンセルになってしまった。

# Googleへの問い合わせ結果
Googleに問い合わせた結果、
日本の免許証と、追加の身分証としてクレジットカードでのID提示について、
IDとアカウントは一致しなければいけない、その責任は受験者にあるとしか回答してくれなくて、
追加の身分証としてクレジットカードが有効か無効かは、はっきり言ってくれなかった。
あと、このようなトラブルは珍しくないとのことでした。

追加のクレジットカードで身分証明したって主張して、
最終的に回受験の75%引きバウチャーをもらいました（これがGoogle側のポリシーで可能な最大の対応とのこと）。

IDを日本語に変えましょうか？
という提案ももらったけど、
今後の受験で日本語のIDが読めないとかそういうトラブルが起きると困るので、
パスポートで英語のIDで身分証明しますと回答した。

# パスポートを用意することにした
今後のトラブル回避のために、パスポートを取ることにしました。
日本の免許証にローマ字のIDが入っていればこんな苦労は不要だけど、しょうがない。
STM32L010のMbedでのHT16K33でナイトライダー


1.SCLとSDAを接続、プルアップも忘れずに
2.電源の接続
3.下記のソースコードを書き込む
4.コンパイル実行で表示されたら終了
5.おわり

注意
mbedのリビジョンは、125

参照
STM32L010のMbedでの液晶,AQM0802Aで表示(i2c)(STM32L010F4P6)
https://qiita.com/caa45040/items/4c7a55bfe837e4ff7bdf

忙しい人の為
https://os.mbed.com/users/caa45040/code/HT16K33_010_1/

```c++

#include "mbed.h"
//#include <stdio.h>
 
//Serial pc(USBTX, USBRX); // tx, rx
//Serial pc(SERIAL_TX, SERIAL_RX);

#define ADDR        (0x70<<1)  //  address
#define ADDR_LCD    (0x7C)  //  address

//I2C i2c(I2C_SDA, I2C_SCL); //767
//I2C i2c(dp5, dp27); //1114
I2C i2c(PA_10, PA_9); //010

char data_read[8];  //i2cバッファー
int ii;             //ループカウンター


int main() {
    
///*INIT start *********  
    
    data_read[0]=0x21;
    i2c.write(ADDR, data_read, 1);wait_ms(1);

    data_read[0]=0x81;
    i2c.write(ADDR, data_read, 1);wait_ms(1);

    data_read[0]=0xef;
    i2c.write(ADDR, data_read, 1);wait_ms(1);

    for(ii=0;ii<16;ii++){
        data_read[0]=ii;
        data_read[1]=0x00;
        i2c.write(ADDR, data_read, 2);wait_ms(1);
    }//for

    
    wait_ms(2000);
    
//*/  //INIT end *********    

    while(1){
        for(ii=0;ii<8;ii++){
        
        data_read[0]=0;
        data_read[1]= (1<<ii);
        i2c.write(ADDR, data_read, 2);wait_ms(1000);

        }//for
    }//while

} // main


```

![i2c_HT16K33_010_1_r.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/430d0d74-f6ab-5a52-a81d-8115b3e64e26.jpeg)
Javaで String (文字列) を昇順 (asc)・降順 (desc) にソートする  

#はじめに: 

##やりたいこと
文字列 (String) をソートしたい。
・ "627118" -> (昇順) -> "112678"
・ "627118" -> (降順) -> "876211"
・ "Hello World" -> (昇順) -> " HWdellloor"
・ "Hello World" -> (降順) -> "roollledWH "  

#考察

## 昇順ソート
・これはすんなりいった。「やったこと」 の"StringSortAsc"を参照ください。
　String => char[] => Arrays.sort() => New String()

## 降順ソート
・一旦 String を char [] にして

```
// char 配列に変換 
char[] charArr = str.toCharArray();
```

・Arrays.sort の第２引数に Collections.reverseOrder() を渡す

```
import java.util.Collections;
...
Arrays.sort(charArr, Collections.reverseOrder());
``` 

・Error!!

```
// Main.java:10: error: no suitable method found for sort(char[],Comparator<Object>)
```

#やったこと

```Java
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        System.out.println(StringSortAsc("627118"));
        System.out.println(StringSortDesc("627118"));
        System.out.println(StringSortAsc("Hello, World"));
        System.out.println(StringSortDesc("Hello, World"));
    }
    /** 
     * @param str  String value to asc-sort
     * @return     String sorted in ascending order 
     * String の昇順ソート
     */
    private static String StringSortAsc (String str) {
        // char 配列に変換 
        char[] charArr = str.toCharArray();
        // Arrays の sort APIを使ってソート
        Arrays.sort(charArr);
        // charArr から Stringに再変換
        return new String(charArr);
    }
    /** 
     * @param str  String value to desc-sort
     * @return     String sorted in descending order
     * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StringBuilder.html#reverse()
     * String の降順ソート
     */
    private static String StringSortDesc (String str) {
        // 一旦昇順にソート
        String sortedStr = StringSortAsc(str);
        StringBuilder sb = new StringBuilder(sortedStr);
        sb.reverse();
        return sb.toString();
    }
}
```

##結果
```Java
112678
876211
 HWdellloor
roollledWH
```

#終わりに
StringBuilder 使いましたがもっと早い方法があればコメントください。ありがとうございました。

## 関連記事: 
[JavaScriptで String (文字列) を昇順 (asc)・降順 (desc) にソートする](https://qiita.com/akiras7171/items/53f976212488525364cf)
Provisional headers are shown

# 事象 : Provisional headers are shown
アプリにログインしようとしたら反応しない・・・Chromeの開発ツールを見たら「CORS error」って言われている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/a7f52b38-7e42-4efd-2103-1db33bfb1b47.png)

[Headers]にある[Request Headers]を見たら「Provisional headers are shown」と言われている。日本語では「暫定ヘッダーが表示されます。」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/3064a20c-f3a1-4667-3697-7d19b6a40994.png)

## 原因 : Request URLを誤っているから
[Headers]にある[General]をみるとRequest URLになんか無駄な「/」がある・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/eb8cd419-dfc8-08b0-e924-794b0bd2d942.png)

### 対応 : 正しいURLを設定する
こんなことにはまる人はそうそういない、「CORS error」とか言われるとビビる。

## 原因 : キャッシュで通信しているから
今回は違うけどこちらの原因のほうが一般的なので、記録しておく。

> この「Provisional headers are shown」表示になり、一部のリクエストヘッダしか表示されなかったのは、キャッシュからファイルを取得しており、通信を行っていなかったことが原因でした。
> [情報科学屋さんを目指す人のメモ &raquo; 【Chrome DevTools】「Provisional headers are shown」が表示される原因と対策」が表示される原因と対策](https://did2memo.net/2017/01/23/chrome-devtools-provisional-headers-are-shown/)

### 対応 : Disable cacheをONにする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/d4f69b49-86b1-89b4-9ff9-e0735d5f7c00.png)


# CORSは、別のドメインにアクセスする権限を制御する・・・みたいな仕組みです。
なんとなくを通り越して「知らない」のでメモっておきます。

> CORS は日本語訳すると オリジン間リソース共有 でした。つまり CORS とは、あるオリジンで動いている Web アプリケーションに対して、別のオリジンのサーバーへのアクセスをオリジン間 HTTP リクエストによって許可できる仕組みのことを言います。
> [なんとなく CORS がわかる...はもう終わりにする。 - Qiita](https://qiita.com/att55/items/2154a8aad8bf1409db2b)
Hyper-VへマイグレーションしたらCentOSが起動しなくなった件

 対象
-----------
- Hyper-V へ virtual box などから移行した人
- HDDを認識せず、起動せず、dracut～というコンソールに突入してしまう


対応策
------------------

### CentOS 7/8


1. チェックポイントを作成する(念のため)
2. レスキューモードで起動する(grubでrescureとかがあれば、そのカーネルを選択する)
3. root権限でログインして以下のコマンドを実行する(数分かかる)

```sh
dracut --kver 4.18.0-147.3.1.el8_1.x86_64 -f \
 --add-drivers "hv_vmbus hv_storvsc hv_netvsc hv_utils hv_balloon hyperv-keyboard hyperv_fb hid-hyperv"
# note:
# -kverはカーネルバージョン、このオプションなしの場合、
#        現在のイメージ(レスキュー)に対して操作してしまうので、私は設定した。
#        指定しなくてもいいのかは、わからない。
# -f は存在しても上書きする。
```

参考文献
-----------------------
実は、以下にすべての解決策が記載されている。

- [CentOS 7 will not boot after migration to Hyper-V](https://forums.centos.org/viewtopic.php?t=61699) (CentOS Forum)
- [Updating the Linux boot image before migrating from VMWare to Hyper-V](https://mangolassi.it/topic/8351/updating-the-linux-boot-image-before-migrating-from-vmware-to-hyper-v)

その他
--------------------
dracutって、debianのdkmsみたいなものなのかな...？今度調べる。
続・CameraX でカメラを一時停止する方法

# 概要
[CameraX でカメラを一時停止する方法](https://qiita.com/inuko/items/869bbb8b39138bd7c41c)の続きとなります。

# 前回の課題
androidx.camera:camera-core:1.0.0-alpha05 までは onPreviewOutputUpdateListener を使ってカメラの映像を一時停止・再開することができた。
以降のバージョンでは、onPreviewOutputUpdateListener が廃止され、一時停止・再開させる方法を検討した結果、暗転せずに一時停止から再開する方法が見つからず困っていた。

## 検証環境
camerax-core:1.1.0-rc01

# 今回試したもの
[カスタムの LifecycleOwner の実装](https://developer.android.com/topic/libraries/architecture/lifecycle?hl=ja#implementing-lco)


CameraX は下記のコードでライフサイクルに合わせた処理を自動でやってくれるようになっています

```kotlin
cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview)
```
[サンプルコード全体](https://developer.android.com/training/camerax/preview?hl=ja#select-camera-bind)


preview を unbind / bind したり、 Preview.setSurfaceProvider() で一時停止・再開を試みましたが、再開時に画面が暗転してしまっていましたが、今回は lifecycle を利用することで実現することができました。

### カスタム LifecycleOwner の実装
まず LifecycleOwner を独自で定義します。

```kotlin
class CustomLifecycle : LifecycleOwner {
        private val lifecycleRegistry: LifecycleRegistry

        init {
            lifecycleRegistry = LifecycleRegistry(this)
            lifecycleRegistry.markState(Lifecycle.State.CREATED)
        }

        fun doOnResume() {
            lifecycleRegistry.markState(Lifecycle.State.RESUMED)
        }

        fun doStart() {
            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
        }

        fun doPause() {
            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
        }

        override fun getLifecycle(): Lifecycle {
            return lifecycleRegistry
        }
    }
```

続いて、cameraProvider に渡します

```kotlin
val customLifecycle = CustomLifecycle()

var camera = cameraProvider.bindToLifecycle(customLifecycle, cameraSelector, preview)
```

ここまでで、CameraX に独自のライフサイクルを伝えられるようにします。

## 一時停止させる場合
一時停止させたいタイミングで CustomLifecycle#doStop() を呼びます

```kotlin

customLifecycle.doStop()
```
LifecycleRegistory#handleLifecycleEvent() に対して、Lifecycle.Event.ON_STOP を指定することで ON_STOP 状態を通知することができます。
※ ON_PAUSE では CameraX は停止しませんでした。

## 再開させる場合
同じように再開させたいタイミングで CustomLifecycle#doStart() を呼びます

```kotlin

customLifecycle.doStart()
```
LifecycleRegistory#handleLifecycleEvent() に対して、Lifecycle.Event.ON_START を指定することで ON_START 状態を通知することができます。
※ ON_RESUME でも再開しました

### 注意点
CustomLifecycle の onResume, onPause などを Fragment もしくは Activity と同期する必要があるため、Fragment や Activity から doOnResume() などの呼び出しが必要となります。
※ これを忘れると CameraX 自体が起動しません

# 結果
前回のエントリでは完全に暗転した状態からカメラのプレビューが再開されていましたが、今回の方法では（一瞬、暗くはなるものの）自然な流れで再開することができました。
CentOS 8によるループバックデバイスを使ったLVM作成

###環境
OS：CentOS Linux release 8.3.2011

###作業内容
OpenStackのPackstackによる自動構築について検証する。PackstackではCinderによる外部ストレージを設定するが、本環境ではサーバー1台のオールインワン構成で検証しているため外部ストレージは存在しない。

そこで、Linuxのループバックデバイスを使ってボリュームを作成し検証を行う。
本記事ではCentOS 8.3におけるループバックデバイスの作成およびLVMボリュームの作成方法を記載する。

###ループバックデバイスとは
ファイルをファイルシステムのようにマウントできる機能のことである。

###手順
現在のループバックデバイスの状況を確認する。

```
# losetup -a
```

何も表示されない場合は/dev/loop0が利用可能なので、任意のファイルをループバックデバイス/dev/loop0に割り当てる。

```
# losetup /dev/loop0 /root/<File name>
```

ループバックデバイスにLVMパーティションを作成する。(以下はGPTで割り当てる場合)

```
# parted -s /dev/loop0 'mklabel gpt'
# parted -s /dev/loop0 'mkpart primary 0 -1'
Warning: The resulting partition is not property aligned for best performance.
# parted -s /dev/loop0 'set 1 lvm on'
# parted -s /dev/loop0 'print'
```

ループバックデバイス/dev/loop0にLVMボリュームを作成する。今回はvgcreateコマンドでボリュームグループを作成する際に、VG名に「cinder-volumes」を指定する。

```
# partprobe /dev/loop0
# cat /proc/partitions
(省略)

# pvcreate -y -ff /dev/loop0p1
 Physical volume "/dev/loop0p1" successfully created
# vgcreate -y -f cinder-volumes /dev/loop0p1
 Volume group "cinder-volumes" successfully created
```
以上

###補足
今回はCinderボリュームをループバックデバイスで用意しましたが、本番環境ではNovaが稼働するコンピュートノードに接続されたiSCSIやFC接続の外部ストレージを使用するのが一般的です。そのため本番環境ではループバックデバイスではなく、外部ストレージが提供するブロックデバイスに、CinderボリュームのLVMを作成する必要があります。
計算でじゃんけん

[[PHP]じゃんけんプログラム](https://qiita.com/ri_/items/5c73dca33cc8401c2600)と、[if文なしでじゃんけん](https://qiita.com/tadsan/items/65d91ba6b50535fc8815)を読んで自分でもやってみた。

まずは、じゃんけんの勝敗を表にする。
9パターンしかないので、そこに数値を入れると下のようになります。

|  | グー(0) | パー(1) | チョキ(2) |
|:-:|:-:|:-:|:-:|
| グー(0)  | あいこ(0)  | 負け(2)  | 勝ち(1)  |
| パー(1)  | 勝ち(1)  | あいこ(0)  | 負け(2)  |
| チョキ(2)  | 負け(2)  | 勝ち(1)  | あいこ(0)  |

縦軸を`$player_hand`、横軸を`$pc_hand`とすると、計算方法は下の通りです。

```php
$result = ($player_hand - $pc_hand + 3) % 3;
```

~~これを邪悪な三項演算子に放り込む。~~
配列だけ済むので修正しました。

```php
$judgment = $result == 0 ? "あいこ" : ($result == 1 ? "勝ち" : "負け");
```

特に目新しい物がないけど、こんな感じになる。

```php
<?php

const RPS = ["グー", "パー", "チョキ"];
const JUDGMENT = ["あいこ", "勝ち", "負け"];

foreach (RPS as $player_hand) {
    foreach (RPS as $pc_hand) {
        $judgment = janken($player_hand, $pc_hand);
        echo $player_hand, ":", $pc_hand, ":", $judgment, PHP_EOL;
    }
}

function janken(string $player_hand, string $pc_hand): string {
    $result = (array_search($player_hand, RPS) - array_search($pc_hand, RPS) + 3) % 3;
    return JUDGMENT[$result];
}
```

普通すぎる結果になりました。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77441/c73f991d-94c1-a6b7-59c3-c9f89f3fd100.png)
intellijでdoma2の実装クラスが自動生成されない場合の解決法

## 概要
通常doma2ではinterfaceを実装してbuildするとその実装クラスが自動で生成される。
しかしintellijのbuildボタン(緑のボタン)でbuildを行う場合、この自動生成が行われず、DBからのデータ取得に失敗する場合がある。

自分含めまわりで何人かハマっている人がいたのでその際の解決法を記載


## 原因と解決法
preference/Build,Execution,Deployment/Build tools/Gradle
でintellijのbuildボタンで行う処理が設定できる。
ここの
```Build and run using: ```

がデフォルトではGradleになっているが、既存ファイルを開いた時にIntellij IDEAになっている事があり、その場合はdomaクラスが自動生成されない。

その場合は画像のようにデフォルトのgradleに戻してから再起動すればdoma実装クラスが自動生成できる様になっているはず
![スクリーンショット 2021-01-15 17.45.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207872/fe776a91-ec4c-2595-25d1-96aefb183150.png)
Pythonで自動予約を作る

#はじめに
予約制の施設を使うことになったが、ネット予約がいちいち面倒なので、自動化を試みる。
使うのはPython3とseleniumである。

#環境
・MacOS High Sierra ver10.13.6
・Python3

#自動ログイン
先ずは予約画面に入るための自動ログインを行う。

```autolog.py
from selenium import webdriver
driver=webdriver.Chrome("パス")
url="https://www.google.com"
driver.get(url)
#urlを開く
login_id=driver.find_element_by_xpath("//input[@id='txtUserID']")
login_pw=driver.find_element_by_xpath("//input[@id='txtpassword']")
#IDとpassの打ち込みformの取得
userid="ID"
userpw="PASS"
login_id.send_keys(userid)
login_pw.send_keys(userpw)
#IDとpassを打ち込む
login_btn=driver.find_element_by_xpath("//input[@id='btnLogin']")
login_btn.click()
#loginを押す
```
パスは絶対パスでChromeのwebdriverがあるところを参照する。場所がわからないときはterminalでdriverのあるfolderまで移動してからpwdで確認する。
サイトURLは秘密なのでとりあえずGoogleの検索画面を貼っておく。

#予約状況の確認
自動ログインをしたら、予約状況の確認を行う。
このサイトは何故か
login→予約画面1→予約画面2→各曜日の予約指定画面
と言う謎の予約画面がある。
とりあえず各曜日の予約指定画面まで行き、各曜日の予約画面を開いて空き状況を確認する。

```aut.py
reserve1=driver.find_element_by_id("ctl00_ContentPlaceHolder1_link01")
reserve1.click()
#予約画面1
reserve2=driver.find_element_by_id("ctl00_ContentPlaceHolder1_lnkFree")
reserve2.click()
#予約画面2
counter=[0,0,0,0,0,0,0]
#予約が空いている曜日は0、空いてなければ1にする
for num in range(1,8):
    date="ctl00_ContentPlaceHolder1_lnkDate"
    date=date+str(num)
    date_page=driver.find_element_by_id(date)
    date_page.click()
    if driver.find_elements_by_id("ctl00_ContentPlaceHolder1_lblErr"):
        counter[num-1]=+1
    driver.back()
driver.quit()
print(counter)
```
for文の中を少し説明。
各曜日のidが{dateに代入した文章＋数字(1~7)}であったのでfor文を用いてタグを取得できるようにした。
予約画面では、予約が一杯の時にのみ表示される文章があるため、その文章のタグを用いて判定する。
具体的にはseleniumでの要素取得に置いてelementではなくelementsを用いた。elementsの場合はその要素の個数が返ってくるためif文の真偽判定に利用した。予約が一杯であればcounterの指定した要素が1になる。

#自動予約
予約状況の確認ができる=空きが判定できる
ということで空きがあるpageでの予約を行う。
しかし、空いている時のsource pageをまだ入手していないため、一先ずここで区切るとする。



#参考文献
[楽しいQiitaの使い方　壁１０罠6つ技7つ](https://qiita.com/kaizen_nagoya/items/7828d0b1d47ad21b45c6)
[PythonとSeleniumでブラウザを自動操作] (https://tanuhack.com/selenium/#WebDriver)
[Python + Selenium で Chrome の自動操作を一通り](https://qiita.com/memakura/items/20a02161fa7e18d8a693#%E8%A6%81%E7%B4%A0%E3%81%AE%E7%8A%B6%E6%85%8B%E3%82%92%E8%AA%BF%E3%81%B9%E3%82%8B)
AWSにデプロイ後、よく使うコマンド

##EC2にSSHで接続し、git pullしてからよく使うコマンドまとめました。
- 使用頻度が多いものを自分用に簡単にまとめました。


##環境
rails 5.2.4
ruby 2.6.3
Cloud9
MySQL

##AWSの構成

![スクリーンショット 2021-02-25 8.15.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/972403/e5ee3e19-f468-b43c-d43b-1b7fb902b197.png)





```
EC2にSSHで接続する方法
$ ssh -i ~/.ssh/キー名.pem ec2-user@xx.xx.xx.xx

Nginxの再起動する方法
$ sudo systemctl restart nginx

アプリ（puma）起動する方法
$ rails s -e production

アプリ（puma）停止する方法
$ kill プロセスID
どちらも同じです
$ kill $(cat tmp/pids/puma.pid)
```

```
本番環境でbundle installする時のコマンド
$ bundle install --path vendor/bundle --without test development

CSS・JS変更時に使うコマンド
$ bundle exec rails assets:precompile RAILS_ENV=production

本番環境でrails db:migrateする時のコマンド
$ bundle exec rails db:migrate RAILS_ENV=production
```



seeds.rb編集した場合は、本番環境のデータベースを削除してからやり直す方がいいです。
本番環境のデータはなくなるので使う際はご自身で判断してください。

```
$ RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rails db:drop
$ mysql -u root -p -h エンドポイント
mysql> CREATE DATABASE 作成したいデータベース名;
$ bundle exec rails db:migrate RAILS_ENV=production
$ bundle exec rails db:seed RAILS_ENV=production
```



```
nginxエラーログ確認するコマンド
$ sudo tail -f /var/log/nginx/error.log

railsのエラーログ確認するコマンド
$ sudo tail -f log/production.log
```
全体の把握に時間がかかりたくさんのエラーを経験し心が折れそうになりました。
何度もEC2作り直ししましたのでAWSの知識もより深まりました。
今回は、S3を使っていないため今度はS3を使って構成することを考えてます😄
duplicityでS3 Glacierにバックアップ

[duplicity](http://duplicity.nongnu.org/)はLinuxで差分バックアップを取るソフトです。
最近はクラウドストレージにも対応していて、Amazon S3にバックアップが置けます。
ここでGlacierを使うと容量料金が下がりますが、バックアップのバケット全体にGlacierに移動するLifecycleを設定してしまうと、メタデータまでGlacierに移動されてしまいます。次回のバックアップ時に、メタデータ取り出しのためにバックアップ開始まで何時間もかかる羽目になります。

容量を喰わないメタデータはS3のままで、データのみをGlacierに移動する方法がわかりました。

# データのみを区別するためのprefix
必須なのは```--file-prefix-archive```です。一つのバケット内に複数のバックアップを置くためにホスト名などをつける場合、```archive```のあとにつける必要があります。

```
OPTS="--file-prefix-archive=archive-${HOST}- --file-prefix-manifest=manifest-${HOST}- --file-prefix-signature=signature-${HOST}-"
```

# S3をバックアップ先として指定

```
export AWS_ACCESS_KEY_ID=……
export AWS_SECRET_ACCESS_KEY=……
OPTS="${OPTS} --s3-use-new-style --encrypt-key=${GPG_KEY} --sign-key=${GPG_KEY}"
duplicity $EXCLUDE $OPTS $SRC s3+http://teikan/duplicity
```

# バックアップ結果

こんな感じでS3にバックアップが取れました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335774/f5049a95-0224-c2b1-2736-619be92b8f3e.png)
データ(```archive```)5GBに対して、メタデータ(```manifest```+```signature```)は39MBなので、S3に置いたままでも惜しくありません。

# Lifecycleの設定

バケット名```duplicity```に加えて、```--file-prefix-archive```で指定した```archive```をプレフィックスに指定するのがポイントです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335774/36273fbb-1287-275d-697b-e8a3e5bb3052.png)

# 結果

数日後に確認すると、期待通り`archive`だけGlacierに移動されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335774/50d282d4-8688-0703-78ec-a7718dc962f6.png)

# 他の方法

Lifecycleに頼らず、```--s3-use-glacier``` というオプションでいきなりGlacierに置く方法もあるようですが、[ちゃんと動くのかよくわからないチケット](https://bugs.launchpad.net/duplicity/+bug/1875937)もあり、試していません。
JavaScript記述中に発生したバグの話

# はじめに
ポートフォリオ作成時、画像選択した際にプレビュー表示させる機能をJavaScriptで実装。
実装が完了し、アプリで実際に動作確認を行うと、プレビュー表示自体は実装できているが、他の項目編集を行うと、プレビュー表示が消えてしまうバグが発生しました。

# 行ったこと
プレビュー表示が既にされている場合、再度画像選択するとプレビュー画像を変更し元からあった画像を削除する機能も実装していたので、
この部分に記述ミス(取得ミス)があるのではと推測。(下記コード)

```JavaScript
    imageUser = document.getElementById('user_image')
    if(imageUser){
      document.addEventListener('change', (e) => {
      const imageContent = document.querySelector('img')
      if (imageContent){
        imageContent.remove()
      }
      const file = e.target.files[0]
      const blob = window.URL.createObjectURL(file)

      createImageHTML(blob)
    }

```

3行目の画像を変更する対象として「document」を指定していたので、ページ内全ての項目変更に対応してしまっていた模様。
下記記述に修正することでバグ解消を行う事ができました。

```JavaScript
imageUser.addEventListener('change', (e) => {
const imageContent = document.querySelector('img')
if (imageContent){
imageContent.remove()
}
```

# さいごに
このバグ解消に時間をかなり使ってしまいました。
解決してみると、とても初歩的なミスだと気付いたので、まずは初心に帰る事が大事だなと教訓を得ました。
まだまだJavaScriptについても学習していきます！