【パーフェクト Ruby on Rails】を読む - その7

2-2-4

コールバックによる制御

必ず行いたい前処理後処理を書けます。

before_○○○○○
ahter_○○○○○
around_○○○○○←あんまり使わないかも

ログを残したい場合に条件を自分で設定し作成できます。

ログレベル

ログレベル 名称 内容
0 debug デバッグ情報など
1 info 一般的な情報
2 warn 警告
3 error 制御可能なエラー
4 fatal プログラムをクラッシュさせるような制御不可能なエラー
5 unknown 常に記録されるべき不明なエラー

logger(Rails.logger).leverで設定変更が可能。これは場所を問わない。これを書けば、ログが吐き出される。Railsの記載は省略でき、loggerからで良いです。

2-2-5

enum

数値のカラムに対して別名を与えます。なのでカラムを追加する必要があります。(もしくはmodelかmigrationファイルを作成する)

# db/migration内
add_column :hoge, :category, :integer, null: false, default: 0
# model内
enum category: { bug: 0, request: 1, others: 2 }

enumはモデルに対して定義する必要があります。

enumのメソッドでwhere.notはnotを記載する事で取得する事ができます。

# enumの書き方
Article.not_draft
# これが取得できます。
Article.where.not(status: draft)

2-3-2

フック

コントローラーのアクションの前後に処理を差し込むことができる(コールバックと似ている) onlyexpectを付けることで特定のアクションに対して実行できる。 berore_action :hogehoge, only: [:show, :update]

フック一覧 処理が実行されるタイミング
before_action Actionの実行前
after_action Actionの実行後(正常にActionが実行された場合のみ呼び出される)
around_action Actionの前後
フックのスキップ

例えば、ログインが必要なアプリの時にApplicationControllerに操作を制御するフックを記載した場合、継承先のcontrollerに、skipのクラスメソッドを使用する必要があります。 そうしないと、いつまでもログインができないので注意が必要です。

CSRF適用を無効にしたい時の記述

特定のアクションのみセキュリティトークンの検証を行わないようにするには、下記のように記述します。

protect_from_forgery except: :update

複数のアクションで用いたい場合は配列で記述します。 ですが、基本的によほどのことがない限りセキュリティに穴をあけることになるので無闇に記述するのは辞めましょう。

ひとこと

around_actionなんて知らなかった。

参考

意外と知らないかもしれないRuby, Railsのメソッドとか

https://qiita.com/hc0208/items/45cce0f3f3c843c03c01#increment-decrement
Railsenumを定義している時、自動でscopeが定義される
https://kossy-web-engineer.hatenablog.com/entry/2020/05/31/113615
RailsEnumってどんな子?使えるの?
https://qiita.com/ozackiee/items/17b91e26fad58e147f2e

【図解入門 TCP/IP】を読む - その3

1.2.2 プロトコルは階層で整理する

 プロトコルで定義されている色々な通信機能は、その処理に応じて階層構造になっています。

1.2.3

TCP/IP参照モデル

4層構造になっています。
下の階層によって役割分担がされています。
自分の層の処理画終わったら他の層の処理には関与しません

階層 階層名 役割
第4層 アプリケーション層 ユーザーに対して、アプリケーションを提供する
第3層 トランスポート層 アプリケーションの識別と、それに応じた通信制御を行う
第2層 インターネット層 異なるネットワークにいる端末との接続性を確保する
第1層 リンク層 同じネットワークにいる端末との接続性を確保する

私達的な覚え方・4321層の順番で頭文字を取って、アトイリと覚えます。

送る側 4 → 3 → 2 → 1 ↩️
受信側 4 ← 3 ← 2 ← 1

層によってプロトコル(約束事)はたくさんあります!その辺はまた後で


OSI参照モデル

7層構造になっています。
TCP/IP参照モデルと同じで階層によって役割分担がされています。
こちらも自分の層の処理画終わったら他の層の処理には関与しません

階層 名称 役割
第7層  アプリケーション層  ユーザーに対して、アプリケーションを提供する
第6層 プレゼンテーション層  アプリケーションデータを通信できる方式に変換する
第5層  セッション層  アプリケーションデータを送受信するため論理的な通信路(セッション)を管理する
第4層  トランスポート層  アプリケーションの識別と、それに応じた通信制御を行う
第3層  ネットワーク層  異なるネットワークにいる端末との接続性を確保する
第2層  データリンク層  物理層の信頼性を確保し、同じネットワークにいる端末との接続性を確保する
第1層  物理層  デジタルデータを電気信号や光信号、電波に変換して、ネットワークに流す

覚え方、7654321層の順の頭文字で、アプセトネ デブと覚えます。


PDUとは (Protocol Data Unit)

各階層で処理されるひとまとまりのデータ、つまりデータの単位のこと。


私たちなりの例え


本書で使用するモデルは以下のように二つのモデルをいいとこどりした5階層のものです。

各階層別のPDUの名称

階層 名称 PDUの名称
第7層 アプリケーション層 メッセージ
第4層 トランスポート層 セグメント(TCPの場合)、データグラム(UDPの場合)
第3層 ネットワーク層 パケット
第2層 データリンク層 フレーム
第1層 物理層 ビット

  • 今日の覚えること
    アトイリ
    アプセトネ デブ

参考

本書のみ

【図解入門 TCP/IP】を読む - その2

1.2 通信するときのルールがプロトコル

プロトコル(通信プロトコル) ネットワークの世界で、パケットを処理するときの約束事

  • https
    ブラウザのURLに書く文字で、httpsという約束事に基づき、暗号化してパケットを処理する

プロトコルで決まっていること

  • 物理的な仕様
    LANケーブルの素材やコネクタの形状、そのピンアサイン(ピン配列)に至るまで、ネットワークにおいて、目に見えるものは全てプロトコルで定義されています。
  • 送信相手の特定
    ネットワークの世界で、現実の世界と同じように住所を割り当てて、送信相手を区別しています。

  • パケットの転送
    細かく分けたパケットを相手に送るためにヘッダーにはどんな情報があってどんな順序でやりとりするかが定義されています(たくさんの情報が入っている) そのパケットを届けるために自分と相手のコンピューターの間にあるネットワーク機器がヘッダーを見てバケツリレーの様に転送していきます。

  • 信頼性の確立
    パケットは世界中に張り巡らされたネットワークに乗って、ありとあらゆるところを駆け巡っているので、いつどこでどんなときにパケットが壊れたりなくなったりするかわかりません。それでも大丈夫なようにエラーを通知したり、データを再送したりする仕組みを提供しています。また、有限なネットワークリソースがパケットでいっぱいになって、溢れてしまわないための仕組みも提供しています。 通信が多くなる時間帯に通信が重くなったりするのは、限られたネットワーク帯域をみんなで共有できるように、ネットワークで制御されている為です。

  • セキュリティの確保
    プロトコルは、重要な情報を安心してやり取りできるように、正しい通信相手であるか認証し、通信を暗号化する仕組みを提供しています。ログインの時はwebブラウザが接続先のサーバーが正しい通信相手であるかをしっかり確認した後、ユーザー名とパスワードを暗号化して送信します。

ここまでまとめ

プロトコルとは … 約束事の名称

パケットという小分けにしたデータを安全に処理するための約束事

例えてみました

プロトコルはネットワーク上での、交通ルールで、これを守るから事故などを起こさずに送ったものを届けることができる。信号があったり、時には渋滞もするから、送信に時間がかかる事態にもなるが、それは安全に届ける為に交通ルールを守っている為です。ヘッダーは伝票に当たるものだと思います。伝票に相手の住所を記載してあるから、送信者も受信者もその届け先が合っていると判断できると思います。

参考

本書のみ

りあクト!輪読会 - その2

What is JavaScript

const hoge = "Hello"

値のこと。上記コードで言えば、Helloがリテラル(文字リテラル) 変数の中にある値を示す。

オブジェクトはプロパティの集合です。プロパティとは名前(キー)と値(バリュー)が対になったものです。 プロパティのキーには文字列またはSymbolが利用でき、値には任意のデータを指定できます。 また、1つのオブジェクトは複数のプロパティを持てるため、1つのオブジェクトで多種多様な値を表現できます。

第一級関数とは? 関数がその他の変数と同様に扱われる。 https://jsprimer.net/basic/function-declaration/#first-class-function

// 関数の定義の仕方①
function func1(str) {
  return str;
}
console.log(func1("これはfunc1です"));
// => "これはfunc1です"
==============================
// 関数の定義の仕方② 変数の中に関数を入れることもできる
const func2 = function(str) {
  return str;
}
console.log(func2("これはfuc2です"));
// => "これはfunc2です"
=============================
// 関数の定義の仕方③ アロー関数 
const func3 = (str) => {
  return str;
}
console.log(func3("これはfunc3です"))
// => "これはfunc3です"

// 関数の定義の仕方④ 省略したアロー関数
const func3 = (str) => str;
console.log(func3("これはfunc3です"))
// => "これはfunc3です"
  • JavaScriptの標準仕様のことをECMAScriptといい、2021年の最新版はES2020

  • ES5とES6(ES2015)で大きく仕様が変わった。(もはや別の言語レベル)

  • JavaScript後方互換性を重視した言語であり、バージョンが変わることによるエラーはほぼ無い

  • ECMAScriptはバージョンではなくて、「仕様」(エディション)の名称。

2-2 変数の宣言

varは今後二度と使ってはいけません!!!! 変数はconstとletで定義しましょう!!

できればletもあまり使わない様にしましょう!

変数を宣言する前に変数に代入して使用することを「巻き上げ」といい、他の言語ではあまり見られない仕様で、よくない。

# constを使った良い例
const a = 100;
console.log(a);

paiza.ioは話半分よりちょい上くらいの感覚で試してください 実際のコンソールでの挙動と若干違う時があります

以下はTypeScriptでの例
const
let
var
varを書かない

2-3 JavaScriptのデータ型

型とは

オブジェクトは全て型を持っていることを踏まえた上で考える。 静的型付けの場合は、データ型を先に宣言してから変数を定義して、値を代入する。そして、宣言した型以外の代入は認めない。(TypeScriptは静的型付け言語)   
- JavaScriptRubyらと同じ動的型付け言語なので型が変わる

  • TypeScriptは静的型付けなのでエラーが出る

JavaScriptのデータ型はプリミティブ型オブジェクト型に大別され、プリミティブ型はさらに7種類に分類される。 プリミティブ型はそれ自体はオブジェクトではないので、インスタンスメソッドが存在しない。

①Boolean型

trueもしくはfalse。真偽を表す値。

②Number型

整数も少数も含めた全ての数字。最大9千兆。

③BigInt型

Number型よりも大きな値。Numberとの互換性はない。 ※ 扱える環境が限定的だからあまり活用できる場面がない。

④String型

文字列

⑤Symbol型

固有の識別子を表現する際に用いる。プロパティキーとして仕様が可能。 ※ JSONでのパースができないのであまり活用できる場面がない。

⑥Null型

データが存在しない状態を明示している。

⑦Undefined型

宣言のみが行われた変数や、存在しないプロパティを参照した際の型

プリミティブ型とオブジェクト型

  • プリミティブ型
    • 一般的な文字の型(数字とか、文字とか)
    • 一度作成したらその値を変更することができない
  • オブジェクト型
    • 配列とか正規表現とか。。
    • 一度作成した後も、その値自体を変更することができる

ターミナルでTypeScriptを試す場合(REPLモード)

ts-node -v

で現環境にTypeScriptが入っているか確認します。 もし、入っていなかったら、

npm install -g typescript ts-node

で現環境にTypeScriptを導入しましょう。-vで確認が取れた段階で

ts-node  -- <--こいつレプル!

をターミナルで入力するとTypeScript用のコンソールを開くことができます。

truthyとfalsy

falsyとして定義された値 (つまり、false, 0, -0, 0n, "", null, undefined, NaN(Not a Number)) を除くすべての値はtruthyです。

parseInt() : 文字列を整数に変換する関数

参照

オブジェクト
- JavaScript Primer https://jsprimer.net/basic/object
プリミティブ型について
https://wa3.i-3-i.info/word15876.html
プリミティブ型とオブジェクト型の違い
https://qiita.com/ta1fukumoto/items/effaa42cd296a2648d41 truthyとfalsyについて https://developer.mozilla.org/ja/docs/Glossary/Truthy

【図解入門 TCP/IP】を読む - その1

1.1.1 ネットワークの歴史

ネットワークの原型は一台の大型コンピュータとみんなで使用するTSS(Time Sharing System)という方式でコンピュータを使用していました。遠隔地にいるユーザーはTSS端末から大型コンピュータに電話でアクセスし、あたかも自分専用のコンピュータであるかのように扱うことができました。 TSSにより、大型コンピュータとTSS端末の2台だけで構成される最もシンプルなネットワークができたのが、ネットワークの始まりです。

APANET 超重要なネットワークで、インターネットのルーツです。

  • パケット ... 小さく分けたデータの単位
    • パケットのやりとりをパケット交換方式という

1.1.2 回線交換方式とパケット交換方式

回線交換方式
固定電話をイメージ。相手が話しても話さなくても電話を切るまで繋ぎっぱなし。

パケット交換方式
必要な時だけ必要な分だけパケットを送る。パケットにはヘッダーをくっつける。

ヘッダー 送信側のコンピュータはデータにつける情報です。宛先となるコンピュータの情報だったり、データの何番目に当たるパケットなのかだったり、様々な情報が含まれます。

パケット交換ネットワーク ヘッダーの情報を見て、宛先のコンピュータにパケットを送り届けます。また、宛先コンピュータはヘッダーの情報を見て、元のデータに復元します。

例: ニトリで家具を買い、家で組み立てる組み立ての説明書がヘッダーです。

参考

本書のみ

【パーフェクト Ruby on Rails】を読む - その6

SQLite3ではnot null制約を付与しつつカラムのデフォルト値を指定しない場合、カラムの追加でエラーになってしまいます。 例)

# NG
class AddNicknameToUsers < ActiveRecord::Migration[6.0]
    def change
        add_column :users, :nickname, :string, null: false
    end
end

# OK
class AddNicknameToUsers < ActiveRecord::Migration[6.0]
    def change
        add_column :users, :nickname, :string, null: false, default: 'sample'
    end
end

# OK
class AddNicknameToUsers < ActiveRecord::Migration[6.0]
    def change
        add_column :users, :nickname, :string
        change_column :users, :nickname, :string, null: false
    end
end

パーフェクト Ruby on Rails p60の例での説明

2-2-2

# 2xxxxxxxxxxxxx_add_publisher_id_to_books.rb
略
def change
  add_reference :books, :publisher, foreign_key: true #rails g migration時にreferencesをつけた事により外部キーであるforeign_keyが生成されており、この行でpublisher_idが作られます。(注:publisher_idとこの行で書いてしまうとpublisher_id_idとなってしまいます。)

  change_column :books, :publisher_id, :integer, null: false #ここで1行目に生成されたpublisher_idにnull: falseをつけることでnot null制約を付与しています。

rails c 内でのreloadとは

reloadメソッドとは、データベースに保存されているもともとのレコードをリロードすることです。 以下、検証結果です。

irb(main):021:0> user = User.new(last_name: '田中',
 first_name: '花子',
 password: 'password',
 password_confirmation: 'password',
 email: 'sample@sample.com' )

=> #<User id: nil,
 email: "sample@sample.com",
 crypted_password: nil,
 salt: nil,
 created_at: nil,
 updated_at: nil,
 last_name: "田中",
 first_name: "花子",
 avatar_image: nil,
 reset_password_token: nil,
 reset_password_token_expires_at: nil,
 reset_password_email_sent_at: nil,
 access_count_to_reset_password_page: 0,
 role: "general">

irb(main):022:0> user.reload
Traceback (most recent call last):
        1: from (irb):22
ActiveRecord::RecordNotFound (Couldn't find User without an ID')

irb(main):023:0> user.save
   (0.1ms)  begin transaction
  User Exists (0.6ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "sample@sample.com"], ["LIMIT", 1]]
  User Create (1.0ms)  INSERT INTO "users" ("email", "crypted_password", "salt", "created_at", "updated_at", "last_name", "first_name") VALUES (?, ?, ?, ?, ?, ?, ?)  [["email", "sample@sample.com"], ["crypted_password", "$2a$10$FFT.hUH/PZI5ae5ajqeLUe1kM.H0AWS/mJV0.ZIKJo1ev7N1JcDMC"], ["salt", "FNGVQWhJLc3N-nHWvs9H"], ["created_at", "2021-09-03 16:04:23.608038"], ["updated_at", "2021-09-03 16:04:23.608038"], ["last_name", "田中"], ["first_name", "花子"]]
   (1.1ms)  commit transaction
=> true
irb(main):024:0> user.reload
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 15], ["LIMIT", 1]]
=> #<User id: 15, email: "sample@sample.com", crypted_password: "$2a$10$FFT.hUH/PZI5ae5ajqeLUe1kM.H0AWS/mJV0.ZIKJo1...", salt: "FNGVQWhJLc3N-nHWvs9H", created_at: "2021-09-03 07:04:23", updated_at: "2021-09-03 07:04:23", last_name: "田中", first_name: "花子", avatar_image: nil, reset_password_token: nil, reset_password_token_expires_at: nil, reset_password_email_sent_at: nil, access_count_to_reset_password_page: 0, role: "general">
irb(main):025:0> 

SQLiteの出力モード

  • line : 1行ずつ表示。
  • html : html形式で表示。
  • tabs : 各列をタブ区切りで表示。
  • csv : 各列をカンマ区切りで表示。

2-2-3

結論 「バリデーション + データベースでの制約」どっちも必要、安全!!」 Race Conditionが起きないように!!

Race Condition レースコンディションとは、複数の処理が同じデータに同時にアクセスした場合に、機能停止など予期しない処理結果が生じてしまうことです。 つまり 同時にアクセスなどしたときにバリデーションをすり抜ける可能性があるため二重ロックの意味でもバリデーションだけでなくnot null制約もつけるべきです。 バリデーションはDBに渡る前のActiveRecordの段階で制御し、not nullはDBに渡ってからDBで制御されます。

バリデーションヘルパー

ActiveRecord/ActiveModelの機能を使用したバリデーションです。(presence: trueなど)

validates :price, numericality: { greater_than_or_equal_to: 0 }の内訳

numerucality は数値であること、オプションでゼロ以上、偶数かどうかなどを検査します。greater_than_or_equal_to: 0 はゼロ以上であること >= と同義です。

バリデーションでpresence: trueが無く、DBでNot NULL制約がある場合DBには保存はされない。
エラーメッセージを表示したい場合はインスタンス.errors.messageまたはインスタンス.errors.full_messageを記述すると良い。(errorsはメソッド)


メソッドに!をつけるかつけないか

!をつけないとバリデーション失敗時に例外を起こさない !をつけると失敗時に例外を起こす

nullとnil

nilrubyの言語。nullの意味。 -nilでもメソッドのエラーを出したくない時は&.ぼっち演算子を使う

参考文献

reloadメソッド
https://k-koh.hatenablog.com/entry/2020/12/28/214557
Race Condition
https://enterprisezine.jp/securityonline/word/detail/%E3%83%AC%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%B3%E3%83%87%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3

【パーフェクト Ruby on Rails】を読む。その5

第2章 Ruby on RailsMVC

2-1 MVCアーキテクチャ

  • M(Model)
    • データベースとの接続
  • V(View)
    • 視覚表現を行う部分
  • C(Controller)
    • ModelとViewをつなぐ部分

2-1-2

モデルの役割
- データベースと接続し、データベースのレコードをActive Recordオブジェクトを結びつける役割(Active Record)
- バリデーションや、レコード保存時などに実行する様々なコールバックなどを実行する役割。(Active Modelが担っている)

Active Record RailsにはModelにActiveRecordが適用されているおかげで、Rubyを用いてDBからデータを探したり、持ってきたりすることができます。
- Active Recordがあるおかげでcreatenew,find,all…などのメソッドが使うことができます。 生のSQLを書かなくていい様にしてくれています。

Active Model
Active Recordの多くのモジュール含むライブラリです。

Actioin Pack
MVC のうち View と Controller を司るのが Action Packというライブラリです。

MVCのモデルとは

あるシステムを作るという事は、そのシステムが解決する問題に対して必要な概念を探して、名前を付けたり相互の関係性を整理したりする事であり、その行為をモデリングと呼び、これがMVCのモデルです。
モデリング : ER図みたいなものを作ること

CRUD

データベースのレコードに対する基本的な操作のことです。
Create、Read、Update、Delete
※ Readはデータに影響を与えないので参照系、それ以外はデータに影響を与えるので更新系です。


2-2 モデルを扱う

モデルを通じて検索を行う = Railsの書き方でSQL検索

2-2-1

  • データベースに登録されているレコードを一件だけ検索
    • findメソッド : idを使用してレコードを取得
      • 検索結果が見つからないとActiverecord::RecordNotFound例外が発生
    • find_byメソッド : id以外を使ってレコードを取得
      • 検索結果が見つからないとnilを返す
  • 複数件のレコードを検索する場合
    • whereメソッド

ActiveRecord::Relation

  • SQLのそれぞれの表現に対応したメソッドをチェーンみたいに繋げていくことが可能です。(例:Book.where("price>?", 3000).limit(3).order(:name)みたいな感じ)
  • 実行結果のデータを普通の配列と同じような感覚で扱えます。

メソッドチェインを使うメリット

必要以上にデータベースとの通信が発生してしまうのを防ぐ効果があります。また、処理の流れを明確にできることがあります。
非同期処理を挟まない限り、前から後ろに向けて順番に処理されます。処理がメソッドチェインの各スコープの中で完結するため、一時変数を定義したり命名する手間が省けます。

Query Interface

Active Recordのメソッド(select、where、limitなど)のこと。

クエリ

「問い合わせ」。データベースに対する命令文のことです。
SQLは「言語」でクエリ(SQL文)は「命令(文)」です。

scopeの定義

よく利用する検索条件に名前をつけてひとまとめにしたもの (例:名前を"物件の条件"とした場合、内容を"バス・トイレ別, 室内洗濯機置き場"などにし、その後"物件の条件"という名前で呼び出せば、毎回内容を記載する必要がないため使い回しに便利です。)

scope :高級物件, -> { where("家賃 > ?", 100,000円)}
scope :トイレ, -> (toilet){ find_by(toilet: toilet) }

トイレのscopeを例にするとトイレがない場合はActiveRecord::Relationが返る。。

scopeで定義した場合は結果がnilでもActiveRecord::Relationを返します。もしnilを返したい場合はクラスメソッドとして定義したほうが良いです。

クラスメソッド

モデルで定義するメソッドのことです。

User.hoge # クラス名.クラスの中にあるメソッド名 で呼び出す


class Home
    def self.toilet(toilet)
        Home.find_by("あるよ")
        puts "トイレつきだあ"
    end
end


Home.toilet("あるよ")
=> トイレつきだあ
Home.toilet("ないよ")
=> nil

↑イメージ!

参照

Active Record
https://qiita.com/ryokky59/items/a1d0b4e86bacbd7ef6e8

Action Pack
https://magazine.rubyist.net/articles/0008/0008-RubyOnRails.html

クエリとは
https://wa3.i-3-i.info/word11290.html

scopeとクエリについて
https://qiita.com/ozin/items/24d1b220a002004a6351

クラスメソッドとは
https://qiita.com/right1121/items/c74d350bab32113d4f3d