OmniAICreator/Qiita-1.07M huggingface dataset:
mobxで深い階層の変更を検知する

mobxで、深い階層のデータをobserveしようと思って、うまくいかないことがあったので調べてみました。

#1. mobx.observeは、深い階層のデータを検知しない

意外かもしれませんが、mobx.observeでは直接的には、深い階層のデータの変更を検知することはできません。

たとえば、以下のようなオブジェクトを考えます。

```js
class Sample {
  @observable // @observe.deepと同じ
  array = [];
  @observable // @observe.deepと同じ
  object = {};
}
```
ここで、以下のようにobserveしても・・・

```js
mobx.observe(sample, "array", change => console.log("observe array"));
mobx.observe(sample, "object", change => console.log("observe object"));
sample.array.push(1);
sample.object.a = 10;
```
何も表示されません。

observableは、observable.deepと同じはずなのに、
全然deepじゃないじゃん。
そうなんです。

Angulara1.xに存在したwatch deepみたいな挙動を期待してはいけないということです。

#2. でも、検知する方法はある

いや、これでは納得いきませんね。
じゃぁ、observable.deepって何なんだよとなります。

そこで、以下のようにcomputedで、array.slice()で、arrayの中身の全コピーを作ってみます。
また、objectについては、Object.valuesで、全データを取得してみます。


```js
class Sample {
  @observable
  array = [];
  @observable
  object = {};
  @observable.ref
  objectRef = {};
  @computed
  get _array() {
    return this.array.slice();
  }
  @computed
  get _object() {
    return Object.values(this.object);
  }
  @computed
  get _objectRef() {
    return Object.values(this.objectRef);
  }
}

const sample = new Sample();
mobx.autorun(() => {
  sample.array;
  console.log("autorun array");
});
mobx.autorun(() => {
  sample._array;
  console.log("autorun _array");
});
mobx.autorun(() => {
  sample.object;
  console.log("autorun object");
});
mobx.autorun(() => {
  sample.objectRef;
  console.log("autorun objectRef");
});
mobx.autorun(() => {
  sample._object;
  console.log("autorun _object");
});
mobx.autorun(() => {
  sample._objectRef;
  console.log("autorun _objectRef");
});
mobx.observe(sample, "array", change => console.log("observe array"));
mobx.observe(sample, "_array", change => console.log("observe _array"));
mobx.observe(sample, "object", change => console.log("observe object"));
mobx.observe(sample, "objectRef", change => console.log("observe objectRef"));
mobx.observe(sample, "_object", change => console.log("observe _object"));
mobx.observe(sample, "_objectRef", change => console.log("observe _objectRef"));

sample.array.push(1);
// autorun _array
// observe _array

sample.object.a = 1;
// observe _object
// observe _object

sample.objectRef.a = 1;
// 何も表示されない

```

期待どおりの結果が得られました。
ちなみに、computedを使わず、autorunの中で直接、this.array.slice()や、Object.values(this.object)した場合も同じです。
ただ、observeの場合は、computedと併用するしかなさそうです。

#3. 結論

たしかに、observable.deepは、深い階層まで検知する仕組みを提供しています。
ただ、observeが、それに対応してないだけです。
autorunとかcomputedで、明示的に深い階層を参照してやれば、深い階層の変更を検知することができます。

さて、上の例では、this.array.slice()や、Object.values(this.object)のような重い操作をして実験しましたが、
通常はこういう操作をする必要はないはずで、
mobxにまかせてしまえば、最適なタイミングで更新処理がかかるはずです。
たとえば、this.array[3]と書けば、配列の3番目が更新されたときだけ、それに依存する処理が走るわけです。

基本的な考え方としては、
1. observeは、深い階層の変更を検知しない
2. observeで、深い階層の検知をしたくなったら、そもそも設計が間違っている可能性があるので、設計を見直そう。
という話ではないかと思います。

個人的には、observe.deepみたいな機能があっても良いのではないかとは思うのですが・・・。
FirefoxでさくらのVPSとかGoogleとかにログインできなかったのはFirefox Lockwiseの相性

2019年半ばからのここ数ヶ月、Firefox上でどうにもさくらのVPSのコントロールパネルとかGoogleの各種サービスとかにログインできないことが多発して困っていた。ログインできないというか、フォームにIDとパスワードを入れることはできるのだが、送信ボタンを押してもなにも反応しない。サーバから返答がないとかではなく、それ以前の段階で、ブラウザ側がアクションを起こさないのだ。

今年の春ぐらいからだろうか、Firefoxのコンテンツブロッキング機能やDo NotTrackの設定を有効にしたりしていたので、そのせいなのか？ Firefox Multi-Account Containersを使い始めたり、あるいは以前から使ってきた1Password(のコンパニオン機能拡張)やNoScriptが引っ掛かっているのか？ と疑っていた。

ちょこちょこその辺の設定を変えては結局ログインのアクションが起きないなあと悩んでいたものの、重い腰を上げて丁寧に一つ一つ有効無効を切り替えて調べたところ、Firefox Lockwiseを無効にして問題が解決した。他のものたちは無罪。

![拡張機能 Firefox Lockwise.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100338/c997b51a-f74e-10dd-d01c-64a2e64ad144.png)


Lockwiseのなにがアカンのかまでは追求していない(仕切れない)。まさかMozilla純正でンなことはないだろう、という慢心が発見を遅らせた感じ。いまバージョン見たら 2.2.5-alpha だった。まあアルファじゃしょうがないよな……。

[追記]
Firefox70あたりでLockwiseが拡張機能からFirefox内蔵パスワード保存機能に統合された模様。そのせいで症状が復活。差し当たりFirefoxの設定で「ログイン情報とパスワードを自動入力する」をオフにして回避している。
【52日目】掲示板にタグを設定しよう①！（seedについて、チェックボックスの作成、タグの保存・表示）

# 掲示板へのtag付け機能の追加
seedを使ってTagを追加しておく

#### seedとは
seedとは初期データのことで、システムの運用前に初期データとなるマスターデータ（あらかじめ用意するベースとなるデータ）を投入したり、開発段階で動作確認のために初期データを投入したりします。
他にもユーザー登録機能のある場合に、アドミン権限をユーザーにrollカラムをつける形で付与している場合などには、データベースをリセットする場合に備えて初期データとしてユーザー情報を投入する場合があるようです。

seedファイルはdb/seeds.rbにcreateメソッドやrubyスクリプトによって記述します。
データベースに投入するにはrails db:seedをすれば良いです。

#### seedファイルを使ったTagの初期データ作成
createメソッドでは、配列 []の中にハッシュ{key: 値}形式で複数のデータを同時に作成することができる。

```ruby:seeds.rb
Tag.create([
  { name: 'Ruby' },
  { name: 'Ruby on Rails4' },
  { name: 'Ruby on Rails5' },
  { name: 'Python2' },
  { name: 'Python3' },
  { name: 'Django2' },
])
```

 seedsファイルが編集できたらデータを投入する。

```
docker-compose exec web rails db:seed
```

## フォームにチェックボックスを作成

```ruby:_form.html.erb
<div class="form-group">
  <span>タグ</span>
  <%= f.collection_check_boxes(:tag_ids, Tag.all, :id, :name) do |tag| %>
    <div class="form-check">
      <%= tag.label class: 'form-check-label' do %>
        <%= tag.check_box class: 'form-check-input' %>
        <%= tag.text %>
      <% end %>
    </div>
  <% end %>
</div>
```

まず、多対多アソシエーションの設定によって、boardオブジェクトにはtag_idsプロパティが、tagオブジェクトにはboard_idsプロパティが追加されている。
ここでは、boardに登録されているtagの数分のチェックボックスを作成することになるため、フォームヘルパーのcollection.check.boxesメソッドを用いる。

#### collection_check_boxesメソッドの構造確認

```
collection_check_boxes(第一引数, :第二引数, 第三引数, :第四引数, :第五引数 ) 
```

第一引数　データを保存するテーブル（フォームヘルパー内の場合省略可能）
第二引数　データを保存するカラム
第三引数　第二引数のカラムに渡してあげるデータのリスト
　　　　　ここで渡したデータの数分のチェックボックスが作成される。
第四引数　チェックボックスのvalueに設定されるプロパティ
第五引数　チェックボックスのラベル名に設定されるプロパティ(text)
=>この第二引数で渡したデータリストの数分、ブロック内が繰り返し実行される。
　 ブロック内にはチェックボックスを生成するHTMLが記述されていて、データの数分のチェックボックスが生成される。

```
<%= tag.label class: 'form-check-label' do %> # タグラベルでチェックボックスとラベル名を覆う
        <%= tag.check_box class: 'form-check-input' %> # チェックボックス自体
        <%= tag.text %> # 第五引数に指定したラベルが表示される
```

## boards_controllerを修正して、チェックボックスを保存できるようにする
ストロングパラメータに対して、チェックボックスのパラメータ（第四引数）に指定したtag_idsを許可すれば保存できるようになる。
ただし、tag_idsは配列の形で複数渡ってくるので、[]をつけて配列であることを明示する必要がある。

```ruby:boards_controller.rb
def board_params
  params.require(:board).permit(:name, :title, :body, tag_ids: [])
end
``` 

## 掲示板詳細画面で関連づけられたタグを表示する

```ruby:_detail.html.erb
<% @board.tags.each do |tag| %>
  <span class="badge badge-primary"><%= tag.name %></span>
<% end %>
```

アソシエーションにおいては、「親モデル.<has_manyアソシエーション名>」で、アソシエーション先の子モデルにアクセスできます。
つまり1行目はアソシエーションに設定したboard.tagsをループして、タグの数だけタグ名を表示するようにしている。
2行目では、ブートストラップのバッジのデザインを使用して、掲示板のタイトルの下に表示するようにしている。
UE4でホワイトボードを作る -改-

#はじめに
>[UE4でホワイトボードを作る](https://qiita.com/Naotsun/items/fe7ab1a3586146db3f48)

昨日レンダーターゲットとかを使ってホワイトボードを作るという記事を書きましたが、そちらでご紹介した方法ではすばやく線を描いた際に下の画像のようになってしまいました。
![8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/65d079d2-fc2a-ecd6-8762-01c3950ca173.jpeg)
この記事ではこれを改善していこうと思います。作業は前回の「[UE4でホワイトボードを作る](https://qiita.com/Naotsun/items/fe7ab1a3586146db3f48)」の後から開始するので、先にこちらに目を通して頂くことを推奨します。

#どう改善するのか
前回の方法では、下の画像のように無数の円を連ねて線に見せかけていました。
![9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/bb72a4eb-d52c-a34f-30e4-1199ca9da70f.jpeg)
今回の方法では、直前のフレームでの位置から現フレームの位置までの線を連ねて線を描いているように見せるようにします。

#改良する
##マーカーのマテリアル
まずは、マーカーの役割をするマテリアルを作ります。名前は「M_WhiteBoardMaker_EX」としておきます。
![4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/52fa92b2-4242-f464-0a07-5b8b6008b7a4.png)
![5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/aa6f53ef-4791-5857-b9ae-3f15fc329151.png)
![6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/1a7c9bf4-742b-308f-4c9e-45a39c71496f.png)
このようにノードを組みます。これで二つの座標から線を描画するマテリアルが完成です。
パラメータは以下の通りです。
- FromLocation　・・・　線の始点
- ToLocation　・・・　線の終点
- Width　・・・　線の太さ
- EdgeFade　・・・　線の強さ

##ホワイトボードアクター
![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/dc700f50-128e-2423-4b02-2d1ec9d75f53.png)
このようにノードを組みます。DrawOnWhiteBoardEXイベントの引数はどちらもVector2D型です。その他の部分は前回と同様です。

##書き手のアクター
![2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/9f08af6a-ee8d-fe9e-6343-0d7b6d79177e.png)
前回作成した処理のキャスト以降をこのようにします。「BeforeLocation」は前フレームの位置を保持しておくVector2D型の変数です。
![3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/a35eac3d-687d-2628-b747-c399394ec6d3.png)
同じく前回作成したこちらの処理のボタンを離した際にEndDrawingイベントを呼び出すようにしてあげます。

なにやら面倒くさそうなことをしていますが、これは書き始めのフレームはBeforeLocation変数に位置を記録して次にフレームから処理をするということをしています。このようにしないと書き始めでBeforeLocation変数の初期値から線が引かれてしまうなど意図しない線が入ってしまいます。

##勢いよく線を描く
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325493/b504a319-0c8a-1a41-c4fa-f64d05aec302.png)
このようにきれいに補間されました。マーカーのマテリアルの「Width」と「EdgeFade」を調節するするといい感じの線になります。

#おわりに
昨日の記事のコメントにて[abey1192さん](https://qiita.com/abey1192)よりアドバイスを頂きました。ありがとうございました！
直線での補間、スプラインでの補間、Catmull-Romでの補間などがあるそうですが、今回は直線での補間を行いました。
また、FMathクラスにCatmull-Romの関数がありました（BPには公開されてない）ので気になった方は試してみてはいかがでしょうか。

#サンプルプロジェクト
今回作成したプロジェクトはこちらからダウンロードできます。
https://github.com/Naotsun19B/WhiteBoard
duplicate key value violates unique constraint "xxx_pkey"

PostgreSQLでInsertした際にprimary keyの重複を示すエラーが発生した。

```
pq: duplicate key value violates unique constraint "tablename_pkey"
``` 

### 原因
PostgreSQLではprimary key(column_name:id)にSERIAL型を指定したとき、シーケンスが自動生成される。シーケンスはcolumnのデフォルト値に連番を割り当てる。(MySQLのAUTO_INCREMENTよろしく)
しかし、シーケンスからの自動採番を使わないInsertにより、レコードとシーケンスにズレが生じ、primary keyが重複してしまった。

#### 確認
max > currvalの場合衝突する可能性がある

```sql
-- pkeyの最大値を確認する
SELECT MAX(id) FROM tablename;

-- シーケンスの現在値を確認する
SELECT currval('tablename_id_seq');
-- or ↓ のlast_valueでシーケンスの現在値を確認する
SELECT * FROM tablename_id_seq;
```

### 対応:シーケンスにpkeyの最大値をセットしてズレを解消する

```sql
SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename));
```
確認のsqlの出力が同じになっていることを確認する

### 参考
https://www.postgresql.jp/document/11/html/functions-sequence.html
Swift5.1 PropetyWrappersを使ったUserDefaultsの例

# はじめに
Swift 5.1から新しく追加された`PropertyWrapper`。
`@State`や`@Binding`などでお馴染みですが、カスタム属性を作成して使っているでしょうか?

今回は、[WWDC2019セッション](https://developer.apple.com/videos/play/wwdc2019/402/)に`UserDefaults`を使用したカスタム属性の内容が紹介されていたので、その例を備忘録兼ねて書こうと思います。

# PropertyWrappersとは?

Swift5.1で導入されている機能で、プロパティをラップしてアクセスパターンを指定することができる機能。
すごくざっくり言うと、「プロパティにアクセスする際の面倒な処理をまとめておいて、使う時に`@なんちゃら`で呼び出すことができるよ」というものです。

ちなみに`@State`や`@Published`なども`PropertyWrappers`です。

独自に定義する場合は`@propertyWrapper`を宣言して使います。


# 今まで
通常はこんな処理はしないと思いますが、例えばこのような処理があったとします。

```swift
struct Person {
    var name: String {
        get {
            UserDefaults.standard.object(forKey: "person_name") as? String ?? ""
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "person_name")
        }
    }
}
```

`Person`以外にも、このようなコンピューテッドプロパティを使用して`UserDefaults`にアクセスするような処理が複数あったらとても冗長です。

# PropertyWrappersを使う
ここで`PropertyWrappers`の出番です。
`PropertyWrappers`を使用して`UserDefaults`のアクセスパターンを定義してみます。
(ちなみにこの`UserDefaults`のアクセスパターンは冒頭で書いたように、[WWDC2019セッション](https://developer.apple.com/videos/play/wwdc2019/402/)で紹介されている例です。)

```swift
@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T
    
    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }
    
    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}
```
こう定義することで、外部から`UserDefault`でプロパティを宣言することが可能になりました。

今回の例だと、実際に`@UserDefault`で定義されたプロパティにアクセスした際はこの`wrappedValue`の中が呼ばれます。

# `@UserDefault`を使ってみる
実際に使ってみます。

先ほど`Person`クラスで定義していた処理がこんなにすっきり書くことができました。

```swift
struct Person {
    @UserDefault("person_name", defaultValue: "")
    var name: String
}
```
通常通り以下のように使用できます。

```swift
let a = Person()
a.name = "Taro"
```

ただ、nameプロパティも同時に初期化する場合、このままでは型が`UserDefault<String>`となっているので、明示的にイニシャライザを追加してあげる必要があります。

```swift
struct Person {
    @UserDefault("person_name", defaultValue: "")
    var name: String
    
    init(name: String) {
        self.name = name
    }
}
```

これで引数付きで初期化できるようになりました。

```swift
let a = Person(name: "Taro")
```

上の例は実用的でないので、実際に使う場合は以下のようにStaticで宣言して使う感じになるかと思います。

```swift
struct UserDefaultsConfig {
    @UserDefault("isLogin", defaultValue: false)
    static var isLogin: Bool

    @UserDefault("username", defaultValue: "")
    static var username: String

    @UserDefault("age", defaultValue: 1990)
    static var age: Int
}

```

すっきり。

```swift
    UserDefaultsConfig.isLogin = true
    UserDefaultsConfig.username = "Yamada Taro"
    UserDefaultsConfig.age = 24
```

# さいごに

どうでしょう。思ったより簡単でした。
`PropertyWrappers`を使用することで、プロパティのアクセスパターンを繰り返し書く必要がなくなります。
そして宣言的にカスタム属性を足すだけで、その定義をプロパティに当てはめられるようになるので、よりシンプルに、完結的な記述をすることができるようになりますね。

良かったらみなさんも使ってみてください。

以上、`UserDefaults`を使った例でしたが、まだまだ、理解できていないところもありますので、ご指摘やアドバイス等ありましたら、コメントしていただければと思います。

それではお読みいただきありがとうございました。
AWS 遊んでみる　teraterm ログインする方法

#１．teraterm ログインする方法

##Hostに下記のアドレスをコピーして、teraterm入力する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/af6c67b4-0466-1942-bf9c-348505c49740.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/46c6cc41-0ea5-09ac-4666-c4c6c43724a8.png)

##User nameに固定ユーザID：ec2-userを入力します。下記の画面のデフォルトを選んでから、ダウンロードしたLightsailDefaultKey-ap-northeast-1.pemを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/d7bd0314-268b-664c-68c5-a03a932c672a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/1b783f28-b413-16d1-6536-1baf2d3bee6f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/415ff113-5552-5d56-fe07-819a916d4376.png)

#２．sudo suでルート権限を入手
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/80afbb28-91b2-21f6-c11c-b0973ee82491.png)

#３．passwd ec2-userでec2-userのパスワードを変更できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/1638a923-b653-3212-65f9-6967a9c5c899.png)

#４．DB2 V11.5のインストール
wget https://iwm.dhe.ibm.com/sdfdl/v2/regs2/db2pmopn/db2_v111_devc/Xa.2/Xb.MmcrqOJZXZXM8_8BjqzSs390pMCnzvwLhZ645grj200/Xc.db2_v111_devc/v11.5_linuxx64_dec.tar.gz/Xd./Xf.LPr.D1vk/Xg.10404755/Xi.mrs-db2devcdbsrv/XY.regsrvs/XZ.p5nhMxqDqJIy6INL9qoCtxc5O6Q/v11.5_linuxx64_dec.tar.gz

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/c9cf2d4d-5860-c5a4-a1db-c01f335bb851.png)

##4.1 db2のインストール
###(1)下記のコマンドでダウンロードしたファイルを解凍します。
```
#gzip -dc v11.5_linuxx64_dec.tar.gz | tar xvf -
tar zxvf v11.5_linuxx64_dec.tar.gz
```
複数modelの関連づけに関するまとめ（rails アソシエーション）


今週のアウトプットはrailsでコメント投稿サイトを作っていた最中に苦戦したアソシエーションに関して復習ついでに書いていきたいと思います。

## 関連づけ（アソシエーション）とは

ユーザーがコメント投稿するようなサイトを例に説明すると、
誰が投稿したコメントなのかをデータとして保存しておく必要があります。

まずmodelでuserとpostcommentのカラムに以下の表のようにカラムを作ります。
現在のテーブル設計だけでは、投稿したユーザのIDはわかっても、そのテーブル内のユーザ名まではわかりません。
そこで、ユーザ情報のモデルと、投稿情報のモデルとを関連付ける必要があります。
user_id（投稿したユーザを識別するID）と、id（投稿コメントを識別するID）が、該当します。
また、1人のユーザが複数の画像を投稿できます。
これをモデルで表現すると、Userモデルで表される1人のユーザに対して、複数個（N個）のPostcommentモデルを関連付けることができます。

| User             | PostComment | 
|:-----------------|------------------:|
| **id**               |                id   |
| name          |            comment|
| email       |  **user_id**|
| password           |                   |
| １：Nの関係                   ｜
これを、1対Nの関係と呼びます。

## 1:Nの関係性をモデルに実装する（アソシエーションの実行）

### UserモデルにPostCommentモデルを関連付ける

まずは、Userモデルに対して、PostCommentモデルが1:Nになるよう関連付けます。

```ruby:app/models/user.rb
class User < ApplicationRecord
    has_many :post_comment, dependent: :destroy
end
```
※dependent: :destroyは親（この場合はUser）が消滅した時は子(この場合はpost_comment)も消滅するという意味

### PostCommentモデルにUserモデルを関連付ける

次に、PostCommentモデルに対して、Userモデルとの関係性を追加していきます。

```ruby:app/models/post_comment.rb
class PostComment < ApplicationRecord
    belongs_to :user
end
```
関連づけにより参照先(userモデル)から値を参照できます。

```
@post_comment.user.name
```

##  第3のモデルを追加する場合のアソシエーション

上記のmodelにコメント投稿機能以外に画像投稿機能を追加したい場合の関連づけとしてhas_many :throughを用いると便利でした。以下のようにmodelを追加します。

| User             | PostComment | PostImage｜
|:-----------------|------------------|------------------:|
| **id**               |                id   |id
| name          |            comment|image_name
| email       |  **user_id**|image_id(画像)
| password           | **post_image_id**            |
| １：  N   :   Mの関係                   ｜  |

User  :  PostComment  =  1  :  N
User  :  PostImage  =   L :  M
PostComment　 :  PostImage  =  1 :  M
であるのでmodelに関係性を追加していきます。

```app/models/user.rb
class User < ApplicationRecord
    has_many :post_comment
    has_many :post_image through: :post_comment
end
```
```ruby:app/models/post_comment.rb
class PostComment < ApplicationRecord
    belongs_to :user
    belongs_to :post_image
end
```
```app/models/post_image.rb
class PostImage << ApplicationRecord
    has_many :post_comment
    has_many :user through: :post_comment
end
```

has_many thoroughを利用することでpost_commentから直接Userモデルにアクセスすることができます。
IP設定値からネットワークアドレスとCIDR表記を導出するマクロ

# 概要
- [VBAでIPアドレス自動計算マクロを作ってみた](https://qiita.com/nn24nHi/items/86c4446ee4b2a8f562ac) を見た
- そういえばIPアドレスの計算って手計算でしかやったことないや、って思ったのでやってみた
- おまけで、VBAでビット演算ができることを知った

# 本文

## ビット演算
ビット演算とかどうしよう、2進数文字列に変換とかしないとだめなのか？とか思ってたが、調べてみたら拍子抜けした

```vb
    Dim numA As Integer, numB As Integer, numC As Integer
    numA = 10 ' 0b1010
    numB = 7 ' 0b0101
    numC = numA Or numB ' 0b1111 で 15
```

まさか「数字AND数字」「数字OR数字」で2進数に見立ててビット演算してくれるとは。
というかビット演算自体、プログラム入門とかやってた頃以来なので、他の言語でどうなのかは知らないのだけど。

## そんなわけでソース
- [IpAddressクラス](https://github.com/kumanobori/misc/blob/master/20191011_vba_ipaddress/src/autoIp.xlsm/IpAddress.cls)
- [標準モジュール](https://github.com/kumanobori/misc/blob/master/20191011_vba_ipaddress/src/autoIp.xlsm/autoIp_original.bas)
- [おすすめはしたくないxlsmファイル](https://github.com/kumanobori/misc/tree/master/20191011_vba_ipaddress/bin)

## 予防線
実用を想定していないこともあり、エラー処理を一切考慮してません。
【Conoha VPS】Karnel Panicの復旧に手こずった話

パブリッククラウドが全盛期となる今日、ほそぼそとVPSいじってましたところ、**Karnel Panic**が発生。
OSは起動せず、キー入力も受け付けず、レスキューモードにも入れず、途方に暮れる事態に陥りました。
ということで、解決までの紆余曲折をお話したいと思います。

##環境

|項目|値|
|:----:|:----:|
|VPS|Conoha|
|OS|CentOS 7|
|症状|Karnel Panic|
※Conohaでの解決方法です。

# 再起動でも直らない
Karnel panicとはOSで発生した致命的なエラーです。
多くの場合、OSのアップデートが上手くいかなかったときに出るエラーで、運が良ければ再起動でも直ることがあるそうです。  
参考
https://qiita.com/toshihirock/items/a0cd2e04e03d1f479e48
https://wa3.i-3-i.info/word13389.html

これで直れば御の字でしたが、何度再起動を試みても一向に回復する気配なし。
どうやら、手を施す必要がありそうです。

# キー入力ができない
Karnel Panicが発生した場合、というかOSに障害が発生した場合、まずはレスキューモードでOSを復旧するのが通常の流れになります。オンプレの場合、キー入力が効くはずなので、特定のコマンドで入ったり、起動オプションでレスキューモードを選択したりと、やりようがあります。
しかし、VPSはコントロールパネルのコンソールウィンドウからキー入力を行います。ところによりますが、再起動するとコンソールの接続が切れ、再接続するとまたしても**Karnel Panic**...。この状態ではキー入力を受け付けないので、復旧の仕様がありません。
どんなに優秀なインフラエンジニアでも、キー入力ができなければ無力なのです（私が優秀というわけではなく）。

# レスキューモードに入れない
実は以前、カーネルではない別のファイルシステムが破損したことがありました。ディスクをマウントするために、レスキューモードに入った覚えがあるのですが、そのときはちゃんと再起動時にOSの起動オプションが表示されていました。
しかし、今回は何度再起動してもKarnel Panicが表示されるだけで、一切のキー入力をすることができません。
ただ、Karnel Panicの画面を表示することになってしまった私のサーバ…
どうすることもできなくなった私は、最後の手段に出ることにしました。

# 泣く泣くサポートに電話
「技術者が何たる体たらく」と思われてもしかたありません。
そうです。最終的にConohaのサポートセンターに電話することにしました。
Karnel Panic自体の復旧はもちろんサポートしていませんが、レスキューモードに入る手順ぐらいは教えてくれるはず…
自身の責任でOSが破損したのは間違いないので、一切のサポートを受けられないことも覚悟していました。

# ちょっと手こずったが無事解決
サポートに電話すると、ほとんど待つことなくスタッフの方につながりました。
IDや名前を伝え、事情を説明するとレスキューモードの入り方を教えてくれました。
**非常に丁寧な対応でした。**（しかし、公式ヘルプに手順がないのはなぜ？）

## レスキューモード手順
手順としては簡単ですが、コツがいります。  
1. コンソール接続（Karnel Panic画面）
2. サーバ再起動
3. コンソールの再読み込みボタンを**タイミングよく押す**

### 1. コンソール接続
Conohaのコントロールパネルに入り、該当のVPSサーバを選択します。
下記のようなボタンが並んでいる場面で、コンソールボタンを押し、コンソールを起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267005/55d60613-26c3-9ca8-7bc3-8b21e47accb2.png)

実際のKarnel Panicのコンソール画面です。このままだと一切のキー入力を受け付けません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267005/d18b88bf-d128-0e2b-222c-125442b0ce09.png)


### 2. サーバ再起動
次にコントロールパネルに戻って再起動ボタンを押し、コンソールを注視しましょう。
左下の「接続完了」が「切断されました」に表示が変わります。


### 3. コンソールの再読み込みボタンをタイミングよく押す
ここが一番難しいです。コンソール右上に再読み込みボタンがあるかと思いますが、サーバとの接続が切れて数秒したら再読み込みボタンを押してください。早すぎると接続されず、遅すぎるとKarnel Panicが出てきてしまいます。私の場合、起動ボタンを押して２，３秒ぐらいでしょうか。

すると起動オプションが一瞬表示されるので、カーソルキーを連打すればオプション画面が継続されます。
ここまでくれば、レスキューモードでOSを起動できます。
うまくオプション画面が表示されなかったら、手順1に戻って何度か繰り返しましょう。サーバが復旧できると思えば、安いトライです。

# 今回の学び
### 再起動前にバックアップは取っておけ
まあ、当然といえば当然です。
正常なうちにバックアップはとっておく。それも定期的に
一度痛い目みないと、バックアップは怠りがちです。
大きなアップデート前はもちろんのこと、再起動時や何もないときであってもバックアップを取っておけば保険になります。
私の場合、最新のバックアップが１ヶ月前だったので、もし復旧できなかったらかなりのダメージでした。  

**バックアップ、超大事**
___
### 復旧手順は確認しておく
今回は手厚いサポートのおかげで円滑な復旧ができましたが、障害時の対応や復旧手順はきちんと知っておくべきです。障害が起きてから初めて調べるなど愚の骨頂。サーバ管理者の端くれにもおけません。
特にVPSやパブリッククラウドは独自の方法でレスキューモードに入ること場合が多いです。BIOSに入れなかったり、特殊キーが受け付けなかったり、とオンプレとは訳が違います。
つまるところ、**平時に障害時の準備をすること**が大切なのです。

# 補足：Karnel Panicの解決
大した解決方法ではないので、あくまで補足として
Karnel PanicはOSのアップデートで発生することが多いエラーです。
というわけで、レスキューモードで入り、OSの再アップデートをかけることで、解決しました。  
CentOSだったので、`yum -y update`ですね。

Karnel Panicになったとしても、**人間までパニック**になる必要はありませんでした。
初心者によるプログラミング学習ログ 123日目

# 100日チャレンジの123日目

Twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

123日目は、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">おはようございます<br>123日目<br><br>・メンターさんのjavascriptの課題<br>・ドットインストールで三択クイズ<br><br>仕事終わってから他にもやることができてしまい、あまり学習できなかったです<a href="https://twitter.com/hashtag/100DaysOfCode?src=hash&amp;ref_src=twsrc%5Etfw">#100DaysOfCode</a> <a href="https://twitter.com/hashtag/%E6%97%A9%E8%B5%B7%E3%81%8D%E3%83%81%E3%83%A3%E3%83%AC%E3%83%B3%E3%82%B8?src=hash&amp;ref_src=twsrc%5Etfw">#早起きチャレンジ</a><a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアと繋がりたい</a></p>&mdash; ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) <a href="https://twitter.com/yudapinokio/status/1181317059495391232?ref_src=twsrc%5Etfw">October 7, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

本業が忙しいのでよくよく学習できませんでした。
Ubuntu 18.04にOpenJDK 13をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu18.04にOpenJDK13をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2023-03がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

# apt update; apt install -y zulu-13
... 略
```

### 各種確認

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

# java -version
openjdk version "13" 2019-09-17
OpenJDK Runtime Environment Zulu13.27+9-CA (build 13+33)
OpenJDK 64-Bit Server VM Zulu13.27+9-CA (build 13+33, mixed mode, sharing)
```
Ubuntu 18.04にOpenJDK 11をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu18.04にOpenJDK11をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2027-09がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

# apt update; apt install -y zulu-11
... 略
```

### 各種確認

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

# java -version
openjdk version "11.0.4" 2019-07-16 LTS
OpenJDK Runtime Environment Zulu11.33+15-CA (build 11.0.4+11-LTS)
OpenJDK 64-Bit Server VM Zulu11.33+15-CA (build 11.0.4+11-LTS, mixed mode)
```
angular-splitを利用する最小サンプルコード

# angular-splitの日本語記事がない！
angular-splitとは、ビューを分割し、ドラッグで分割領域を調節することができるUIライブラリです。（ほぼ公式文直訳）

便利で欠かせないライブラリなんですけど、日本語で解説してる記事がどうやらない。
Qiitaも個人ブログの記事も見つかりません。
それなら集合知を高めるためにも、私が記事を書こうかな～と思った次第です。
しかし私も触ったばかりなので、他の人がangular-splitを使用するハードルを下げるべく**angular-splitを用いた最もシンプルなコード**を書こうと思います。


## まずはインストール
ng newでAngularの雛形を作成したら、

```
npm install angular-split
```
を実行。すぐ終わります。

## そしてモジュール追加

```typescript:app.module.ts
import { AngularSplitModule } from 'angular-split';
```
app.module.tsの先頭に↑を追加。

```typescript:app.module.ts
AngularSplitModule.forRoot()
```
@NgModuleのimportsに↑を追加。

## それからテンプレートを書く
```html:app.component.html
<div class="split-example">
  <as-split [direction]="direction">
    <as-split-area #area1="asSplitArea">
      <p id="left">左</p>
    </as-split-area>
    <as-split-area #area2="asSplitArea">
      <p id="right">右</p>
    </as-split-area>
  </as-split>
</div>
```

## 次はコンポーネントを書く
```typescript:app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  direction: string = 'horizontal';

  constructor() {}
}

```

## 最期にスタイルシートだ
```scss:app.component.scss
.split-example {
    height: 100px;
    width: 200px;
}

#left {
    background-color: #cbffff;
}

#right {
    background-color: #ffffc7;
}
```

## よし表示してみよう！
![1-split.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480762/dcdc092e-d1de-ecb5-26af-4537b11ccc33.png)
こんな感じになりますね。
中央の仕切りをドラッグして左右領域のサイズを変えることができますよ～。
angular-split様の御業です。

ただこれでは如何せんシンプルすぎない？という方は、
htmlに

```html

<br />
<button class="btn btn-warning" (click)="direction = (direction === 'horizontal') ? 'vertical' : 'horizontal'">{{ '現在の分割: ' + direction + '' }}</button>
```
を追加し、


スタイルシートの.split-exampleに

```scss

outline: 5px dashed red;
```
を追加してみましょう。

![2-split.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480762/7b79fd5b-3733-c955-d785-39a228eddef4.png)
枠ができてわかりやすくなりました。
ボタンを押すと分割方法が変わります。
![3-split.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480762/30b62890-57f2-49a1-51ab-cdb532ba27a0.png)
やったね！

この超ゆとり記事を足掛かりに公式のサンプルで勉強してくれたらうれしいです。
Ubuntu 18.04にOpenJDK 8をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu18.04にOpenJDK8をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2026-03がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

# apt update; apt install -y zulu-8
... 略
```

### 各種確認

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

# java -version
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (Zulu 8.40.0.25-CA-linux64) (build 1.8.0_222-b10)
OpenJDK 64-Bit Server VM (Zulu 8.40.0.25-CA-linux64) (build 25.222-b10, mixed mode)
```
DjangoでWebサイトを立ち上げてみる。Part2〜Hello Worldの表示〜

#この投稿について
作業進捗の備忘録、学習効果の向上を目的として記録していきます。
学習しながら作業を進めておりますので、情報が間違っている場合も多々あると思います。
正しい情報は公式ドキュメントを参考にしてください。

#開発環境
* macOS High Sierra 10.13.6
* Anaconda 3
* Python 3.7.7
* Djnago 3.0.6
* VisualStudioCode


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

前回、Djangoプロジェクト（first_project）を作成しました。
プロジェクト下にアプリケーションを作成してみます。
>アプリケーションとは、実際に何らかの処理を行う Web アプリケーションを指します。（公式ドキュメント一部抜粋）

作成方法は簡単で、プロジェクト直下で以下のコマンドを打つだけで、アプリケーションの作成に必要なファイル一式を自動生成します。

```:Terminal
first_project % python3 manage.py startapp first_app
```
アプリはどこに配置してもいいみたいですが、プロジェクトの直下にしました。app名は`first_app`にしました。

＊詳細は公式ドキュメント参照
https://docs.djangoproject.com/ja/3.0/intro/tutorial01/

#views.pyの編集
トップページにアクセスした際に表示させる内容を設定するために、first_appフォルダ内の`views.py`を編集します。

```py:first_app/views.py
from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def index(request):
    return HttpResponse('Hello World!!')
```

Hello World!!と表示させるレスポンスを返す関数を記述しています。

#アプリケーションの登録
次に、アプリケーションをプロジェクトの設定ファイルに登録してあげる必要があります。

```py:first_project/settings.py
# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'first_app',###
]
```

djangoにアプリケーションの存在を認識してもらうために必ず追記します。

#urls.pyの編集
続いて、first_project内の`urls.py`を編集します。
urls.pyはサイト全体の目次みたいなもののようです。

```py:first_project/urls.py
from django.contrib import admin
from django.urls import path
from first_app import views #first_app/views.pyの関数を読み込む。

urlpatterns = [
    path('',views.index),#indexページ（http://127.0.0.1:8000/）にアクセスした際にviews.indexのレスポンスを返す。
    path('admin/', admin.site.urls),
]
```

以上で下準備は完了です。

#Djangoのサーバーを起動して動作確認してみる
```:Terminal
first_project % python3 manage.py runserver
```
http://127.0.0.1:8000/ にアクセスして
`Hello World!`
と表示されていればOK

#URLを指定してみる
今までは`http://127.0.0.1:8000/`にしかアクセスしていなかったが、
新たにURLを作成し、アクセスできるように設定する。
ここでは`http://127.0.0.1:8000/first_app/`というURLを生成してアクセスできるようにし、
indexページと同じ内容を表示させるように設定する。

方法はいろいろあると思いますが、今回はfirst_appフォルダ内のurls.pyファイルを経由する方法で設定してみます。

##appフォルダ内にurls.pyファイルを作成

```py:first_app/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
```

##first_project内のurls.pyを編集
公式ドキュメント参照
https://docs.djangoproject.com/ja/2.2/intro/tutorial01/#write-your-first-view

```py:first_project/urls.py
from django.contrib import admin
from django.urls import path, include #includeを新たにインポート
from first_app import views #first_app/views.pyの関数を読み込む。

urlpatterns = [
    path('',views.index),
    path('first_app/',include('first_app.urls')),#http://127.0.0.1:8000/first_app にアクセスした際にfirst_appフォルダ内の`urls.py`のurlpatternsに記載された処理を返す。
    path('admin/', admin.site.urls),
]
```

追記した部分の処理は、http://127.0.0.1:8000/first_app/ にアクセスした際にfirst_appフォルダ内の`urls.py`のurlpatternsに記載された処理を返すという処理になります。
ｉｎｃｌｕｄｅ('first_app.urls')で関連付けをしています。

##Djangoのサーバーを起動して動作確認してみる
```:Terminal
python manage.py runserver
```
http://127.0.0.1:8000/first_app/　にアクセスして
`Hello World!`
と表示されていればOK

このように、指定したURLに指定したのアプリのURLパターンをツリー状に繋げることができます。
DjangoでWebサイトを立ち上げてみる。Part1〜環境構築編〜

#この投稿について
作業進捗の備忘録、学習効果の向上を目的として記録していきます。
学習しながら作業を進めておりますので、情報が間違っている場合も多々あると思います。
正しい情報は公式ドキュメントを参考にしてください。

#開発環境
* macOS High Sierra 10.13.6
* Anaconda 3
* Python 3.7.7
* Djnago 3.0.6
* VisualStudioCode

！Anaconda、エディタ等のインストールは済んでいる前提です！

#Anacondaで仮想環境を作成

自PCのroot環境を汚さないために
Django用の仮想環境を作成しておきます。
仮想環境上で作業すれば何か不具合があった場合でもすぐにリセットすることができます。

まずはターミナルを開いて以下のコマンドを入力します。

```:Terminal
conda create --name MyDjangoEnv python=3.7
```

`MyDjangoEnv`が仮想環境名
`python=3.7`でpythonのバージョンを指定しています。

仮想環境の作り方はこちらの投稿を参考にしました。
https://qiita.com/ozaki_physics/items/985188feb92570e5b82d

#仮想環境の起動
```:Terminal
conda activate ＭyDjangoEnv
```
これで仮想環境が立ち上がります。
コマンドラインの先頭に、かっこ書きで仮想環境名が書いてあればOK

```:Terminal
(ＭyDjangoEnv) user@MacbookAir dict %
```
以降は仮想環境下での作業となります。

#仮想環境へDjangoのインストール
仮想環境を起動したところで、Djangoをインストールします。

```:Terminal
% pip install django=3.0.6
```

ここでひとまず環境構築は終了です。
ついでにDjandoでサーバーを立ち上げるところまでやってみます。

#Djangoで新しいプロジェクトを立ち上げる
適当なところに作業用のフォルダを作成し、ターミナル上で移動しておきます。
ターミナルで仮想環境を起動します。

```:Terminal
conda activate myDjangoEnv
```

続いて以下のコマンドを入力して新しいプロジェクトを作成。

```:Terminal
% django-admin startproject first_project
```

すると、先ほど作成した作業用フォルダ内に自動的に必要なPythonコードが作成されます。
詳細については追々。ここではよく使うものをリストアップ。

* settings.py  
プロジェクトの設定などを記述するもの。
* urls.py  
リクエストと処理をむすびつける設定を書くところ。
* views.py
リクエストに対する処理を書くところ。
* manage.py  
サーバーの起動、スーパーユーザーの作成などに使用する。

#Djangoのサーバーを起動してみる
```:Terminal
% python manage.py runserver
```
ターミナル画面に以下のように出ればサーバー起動成功
http://127.0.0.1:8000/　にアクセスして
`The install worked successfully! Congratulations!`
と表示されていればOK

```:Terminal
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

September 25, 2019 - 14:15:05
Django version 2.2.5, using settings 'first_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[25/Sep/2019 14:15:18] "GET / HTTP/1.1" 200 16348
[25/Sep/2019 14:15:18] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423
[25/Sep/2019 14:15:18] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 85876
[25/Sep/2019 14:15:18] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 86184
[25/Sep/2019 14:15:18] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 85692
Not Found: /favicon.ico
[25/Sep/2019 14:15:18] "GET /favicon.ico HTTP/1.1" 404 1979
```
##補足

```:Terminal
% python manage.py runserver
```

上記コマンド実行時に以下のエラーが表示される場合、

```
  File "manage.py", line 16
    ) from exc
         ^
SyntaxError: invalid syntax
```

以下のコマンドで試してみてください。

```:Terminal
% python3.7 manage.py runserver
```
詳しい原因は調べていないのでわかりませんが、PC本体環境と仮想環境のPythonのバージョンが異なるから？
とりあえずは仮想環境上のPythonのバージョンに合わせてをpyファイルを実行する際は'python3'のコマンドを使用します。



また、以下のエラーが起きた場合は、
`conda install sqlparse`
と入力してモジュールを追加でインストールすればOKです。

```
ModuleNotFoundError: No module named 'sqlparse'
```
Ubuntu 18.04にOpenJDK 7をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu18.04にOpenJDK7をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2023-07がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

# apt update; apt install -y zulu-7
... 略
```

### 各種確認

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

# java -version
openjdk version "1.7.0_232"
OpenJDK Runtime Environment (Zulu 7.31.0.5-CA-linux64) (build 1.7.0_232-b6)
OpenJDK 64-Bit Server VM (Zulu 7.31.0.5-CA-linux64) (build 24.232-b6, mixed mode)
```
GASによるスクレイピングのために作った機能


Google Apps Scriptでスクレイピングをし易くするために、フロントエンドの方にお馴染みのDOM機能の一部のようなプログラムを作成。

```javascript

// XmlServiceを前提としたScrapingお助けProgram

/*
* Method: getElementById
* @param {XmlService.Element} element
* @param {string} idToFind
* @return {XmlService.Element} 
* Description: if id does not exist, null will return.
*/
function getElementById(element, idToFind) { 
  var descendants = element.getDescendants();
  for (i in descendants) {
    var elmt = descendants[i].asElement();
    if (elmt != null){
      var id = elmt.getAttribute('id')
      if (id != null) {
        if (idToFind === id.getValue()){
          return elmt;
        }
      }
    }
  }
}

/*
* Method: getElementsByClassName
* @param {XmlService.Element} element
* @param {string} classToFind
* @return {Array XmlService.Element[]}
* Description: if id does not exist, empty Array will return.
*/
function getElementsByClassName(element, classToFind){
  var classes = [];
  var descendants = element.getDescendants();
  for (i in descendants) {
    var elmt = descendants[i].asElement();
    if (elmt != null){
      var class = elmt.getAttribute('class');
      if (class != null) {
        if (classToFind === class.getValue()){
          classes.push(elmt.asElement());
        }
      }
    }
  }
  return classes;
}

/*
* Method: getElementsByTagName
* @param {XmlService.Element} element
* @param {string} tagToFind
* @return {Array XmlService.Element[]}
* Description: if id does not exist, empty Array will return.
*/
function getElementsByTagName(element, tagToFind){
  var tags = []
  var descendants = element.getDescendants();
  for (i in descendants) {
    var elmt = descendants[i].asElement();
    if (elmt != null) {
      var tag = elmt.getName();
      if (tag != null) {
        if (tagToFind === tag) {
          tags.push(elmt);
        }
      }
    }
  }
  return tags;
}
```
Ubuntu 16.04にOpenJDK 13をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu16.04にOpenJDK13をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2023-03がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.6 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

# apt update; apt install -y zulu-13
... 略
```

### 各種確認

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

# java -version
openjdk version "13" 2019-09-17
OpenJDK Runtime Environment Zulu13.27+9-CA (build 13+33)
OpenJDK 64-Bit Server VM Zulu13.27+9-CA (build 13+33, mixed mode, sharing)
```
Ubuntu 16.04にOpenJDK 11をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu16.04にOpenJDK11をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2027-09がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.6 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

# apt update; apt install -y zulu-11
... 略
```

### 各種確認

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

# java -version
openjdk version "11.0.4" 2019-07-16 LTS
OpenJDK Runtime Environment Zulu11.33+15-CA (build 11.0.4+11-LTS)
OpenJDK 64-Bit Server VM Zulu11.33+15-CA (build 11.0.4+11-LTS, mixed mode)
```
自分用メモ(ログイン・findとfind_byの違い・helperって何、使い方は？)


<h2>ログイン機構</h2>
ログインはセッションと言うリソースを作ることで実現する

sessionはcookieに保存する

ログインページへ(new)
↓
情報入力
ログイン情報送信
↓
セッションにユーザーIDが渡される
↓
ログイン完了(ユーザーIDをセッションの中から取り出せる状態)


その後は
<%=　@current_user.name %>
とかで取り出せるように

@current_userの有無・userによってデータ出力を変化させることができる



<h2>ログイン情報入力のフォームを作る</h2>
フォームはデータベースをいじるためのもの(なので基本モデルがある)(form_for(model) do |f|)

ユーザー登録との違いは、セッションにはセッションモデルが存在しないと言うこと
→@userと言うようなインスタンス変数は存在しない
↓
自分で追加情報を明示しないといけない(今回は変化を与えるリソースの名前と、対応するURL)

<%= form_for(:session,url: login_path) %>
f.email_form :e-mail: 
↓
送信されるデータは"session[:password]"
つまりparams[:session][:password]で受け取る

<h2>ログイン機能を作る</h2>
入力したsessionをparamsで受け取りemailを入力した[:email]から探す
↓
見つかったらuserに代入する
↓
userに入っているpasswordと
↓　セッション入力されたpasswordが一致するかauthenticateでチェックする
　　↓
(一致する？)
○ ×　→render new
↓　
session[:user_id]=user.id

セッションの[:user_id]にuser.idを代入する


session[:user_id]がnilなら「誰もログインしていない状態」
session[:user_id]に値が代入されているなら、そのIDを持つユーザーがログイン中


<h2>ログイン者取得の簡略化</h2>
ログインしているかいないか、しているなら誰かを確認の確認が今後必要になってくる。
これはsession[:user_id]で取得すれば良い。
何回も呼び出すのが面倒なので、メソッドで定義しておく。

def current_user
 @current_user =User.find_by(id: session[:user_id])
end
(findの場合、ユーザー存在しない場合エラーが発生してしまうので、今回はfind_byを使う)
(@current_userに代入することで、データベースを検索する回数を初めの一回だけにする。)


↓定義する前に、、代入演算子を理解しておく「||=」

<h3>  「||＝」と言う代入演算子   </h3>

a ||= xxx

aが偽か未定義の場合、aに　xxx　を代入する。


これを書き換えると、
a = a || xxx
これはつまり、aに値が入っている場合、aにaは代入されるのでそのままである。
つまり、aに値が入っていない(nil)、もしくはfalseの時だけaが代入される。


<h3>続き</h3>
現在ログイン中のユーザーを取り出す(いる場合)
def current_user
  if session[:user_id]
   @current_user = @current_user || User.find_by(id: session[:user_id])
  end
end

ログインしているユーザーが存在する場合(session[:user_id]がnilでない場合)で、
@current_userがnilもしくはfalseの場合、@current_userにsession[:user_id]を代入する。
(@current_userが存在する場合、そのまま)

「||=」を使って書き換え　↓
def current_user
  if session[:user_id]
   @current_user ||=  User.find_by(id: session[:user_id])
  end
end

<h2>findとfind_byの使い分け</h2>

find
→idのみ(idがわかっている時のみ使用可能)
idがわからない場合は使えない

find_by
→idじゃなくても良い(帰ってくる結果は一つだけ)
idがわからない場合(nilになる場合)こちらを使う

idがわかっているときはfind
idがわからないときはfind_byを使う


<h2>helperって何</h2>
Viewをシンプルに書くためのモジュール。
例としては form_for、link_to


基本的に、Viewでヘルパーメソッドを呼び出す

・Controllerで呼び出したい場合
→そのままでは呼び出せないので、application.controllerに一文加える必要がある

class ApplicationController < ActionController::Base
  include SessionsHelper     ←←　これ
end





・helperを作るには？
アプリ全体でhelperを使いたい場合
→app/helpers/application_helper.rb　に定義する
特定のモデルでhelperを使いたい場合
→app/helpers/(特定のモデル名)_helper.rb　に定義する

・具体的にどうやって書くの？(セッションモデルにのみ適用の場合)(今回はcontrollerで使用する)


module SessionsHelper
(ここに書く)
end
(中身)
def log_in(user)
 session[:user_id]=user.id
end

呼び出したいときは対象のコントローラーにメソッドを書くだけでok

<h2>pathにユーザー渡すのよくわからん</h2>
Ubuntu 16.04にOpenJDK 8をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu16.04にOpenJDK8をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2026-03がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.6 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

# apt update; apt install -y zulu-8
... 略
```

### 各種確認

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

# java -version
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (Zulu 8.40.0.25-CA-linux64) (build 1.8.0_222-b10)
OpenJDK 64-Bit Server VM (Zulu 8.40.0.25-CA-linux64) (build 25.222-b10, mixed mode)
```
Ubuntu 16.04にOpenJDK 7をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu16.04にOpenJDK7をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2023-07がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.6 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

# apt update; apt install -y zulu-7
... 略
```

### 各種確認

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

# java -version
openjdk version "1.7.0_232"
OpenJDK Runtime Environment (Zulu 7.31.0.5-CA-linux64) (build 1.7.0_232-b6)
OpenJDK 64-Bit Server VM (Zulu 7.31.0.5-CA-linux64) (build 24.232-b6, mixed mode)
```
Ubuntu 14.04にOpenJDK 13をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu14.04にOpenJDK13をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2023-03がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="14.04.6 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.6 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"

# apt update; apt install -y zulu-13
... 略
```

### 各種確認

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

# java -version
openjdk version "13" 2019-09-17
OpenJDK Runtime Environment Zulu13.27+9-CA (build 13+33)
OpenJDK 64-Bit Server VM Zulu13.27+9-CA (build 13+33, mixed mode, sharing)
```
Ubuntu 14.04にOpenJDK 11をインストール（Zulu Community）

# はじめに

Zulu Communityを利用してUbuntu14.04にOpenJDK11をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Download OpenJDK Java Linux Windows macOS Alpine Java 11 Java 8](https://www.azul.com/downloads/zulu-community/)

## サポート

本手法で導入した場合、[Zulu Community Java OpenJDK free download TCK tested](https://www.azul.com/products/zulu-community/)より、2027-09がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### レポジトリ登録

```shell-session
# apt update; apt install -y software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
# apt-add-repository 'deb http://repos.azulsystems.com/ubuntu stable main'
```

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="14.04.6 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.6 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"

# apt update; apt install -y zulu-11
... 略
```

### 各種確認

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

# java -version
openjdk version "11.0.4" 2019-07-16 LTS
OpenJDK Runtime Environment Zulu11.33+15-CA (build 11.0.4+11-LTS)
OpenJDK 64-Bit Server VM Zulu11.33+15-CA (build 11.0.4+11-LTS, mixed mode)
```