2019年9月からプログラミング学習をスタートしてきましたが、ここにきて復習って大事だな!っと思ったので、
Ruby on Railsの基礎知識をここにまとめておきたいと思います。
*復習のたびに更新するスタイルで行きます!
#Railsとは
Rubyプログラミング言語で書かれたWebアプリケーションフレームワーク。
他の多くの言語によるWebアプリケーションフレームワークと比較して、アプリケーションを作成する際のコード量がより少なくて済むにもかかわらず、より多くの機能を実現できます。
<Railsの特徴>
・習得したスキルを広く活用できる
言語によっては、使われているフレームワークが多数あります。例えばPHPを学んでも、それとは別に様々なフレームワークについても学ぶ必要があります。RubyについてはRailsがほぼ1択であるため、一度習得してしまえば幅広く活用することができます。
・開発を効率化する仕組みが多数用意されているため生産性が高い
Railsにはrails newをするだけでアプリの雛形を作成できたりActiveRecordによってSQL操作ができるなど、開発効率を高めてくれる仕組みが標準で組み込まれています。
1.同じことを繰り返すな (Don't Repeat Yourself: DRY):
DRYはソフトウェア開発上の原則であり、「システムを構成する知識のあらゆる部品は、常に単一であり、明確であり、信頼できる形で表現されていなければならない」というものです。
同じコードを繰り返し書くことを徹底的に避けることで、コードが保守しやすくなり、容易に拡張できるようになり、そして何よりバグを減らすことができます。
2.設定より規約が優先される (Convention Over Configuration):
Railsでは、Webアプリケーションで行われるさまざまなことを実現するための最善の方法を明確に思い描いており、Webアプリケーションの各種設定についても従来の経験や慣習を元に、それらのデフォルト値を定めています。このようにある種独断でデフォルト値が決まっているおかげで、開発者の意見をすべて取り入れようとした自由過ぎるWebアプリケーションのように、開発者が延々と設定ファイルを設定して回らずに済みます。
#Active Record
Active Recordとはデータベースからデータを読み出すためのアプローチで、ORM (オブジェクト/リレーショナルマッピング) システムに記述されている「Active Recordパターン」を実装したものであり、このパターンと同じ名前が付けられています。
- モデルおよびモデル内のデータを表現する
- モデル同士の関連付け(アソシエーション)を表現する
- 関連付けられているモデル間の継承階層を表現する
- データをデータベースで永続化する前にバリデーション(検証)を行なう
- データベースをオブジェクト指向スタイルで操作する
##オブジェクト/リレーショナルマッピング(O/Rマッピング,ORM)
アプリケーションが持つリッチなオブジェクトをリレーショナルデータベース(RDBMS)のテーブルに接続することです。ORMを用いると、SQL文を直接書く代りにわずかなアクセスコードを書くだけで、アプリケーションにおけるオブジェクトの属性やリレーションシップをデータベースに保存することもデータベースから読み出すこともできるようになります。
- モデルおよびモデル内のデータを表現する
- モデル同士の関連付け(アソシエーション)を表現する
- 関連付けられているモデル間の継承階層を表現する
- データをデータベースで永続化する前にバリデーション(検証)を行なう
- データベースをオブジェクト指向スタイルで操作する
##命名ルール
Active Recordには、モデルとデータベースのテーブルとのマッピング作成時に従うべきルールがいくつかあります。Railsでは、データベースのテーブル名を探索するときに、モデルのクラス名を複数形にした名前で探索します。
つまり、Bookというモデルクラスがある場合、これに対応するデータベースのテーブルは複数形の「books」になります。Railsの複数形化メカニズムは非常に強力で、不規則な語でも複数形/単数形に変換できます(person <-> peopleなど)。
モデルのクラス名が2語以上の複合語である場合、Rubyの慣習であるキャメルケース(CamelCaseのように語頭を大文字にしてスペースなしでつなぐ)に従ってください。
一方、テーブル名は(camel_caseなどのように)小文字かつアンダースコアで区切られなければなりません。
- モデルのクラス:単数形、語頭を大文字にする (例: BookClub)
- データベースのテーブル:複数形、語はアンダースコアで区切られる (例: book_clubs)
モデル / クラス | テーブル / スキーマ |
Post | posts |
LineItem | line_items |
Deer | deers |
Mouse | mice |
Person | people |
##スキーマのルール
Active Recordでは、データベースのテーブルで使うカラム名についても利用目的に応じたルールがあります。
- 外部キー:このカラムはテーブル名の単数形_idにする必要があります(例: item_id、order_id)。
これらのカラムは、Active Recordがモデル間の関連付けを作成するときに参照されます。
-主キー:デフォルトでは id という名前のintegerカラムがテーブルの主キーに使われます(PostgreSQLやMySQLではbigint、SQLiteではinteger)。
Active Recordマイグレーションでテーブルを作成すると、これらのカラムが自動的に作成されます。
- created_at: レコード作成時に現在の日付時刻が自動的に設定されます
- updated_at: レコード作成時や更新時に現在の日付時刻が自動的に設定されます
- lock_version: モデルにoptimistic lockingを追加します
- type: モデルでSingle Table Inheritanceを使う場合に指定します
- 関連付け名_type: ポリモーフィック関連付けの種類を保存します
-テーブル名_count: 関連付けにおいて、所属しているオブジェクトの数をキャッシュするのに使われます。たとえば、Articleクラスにcomments_countというカラムがあり、そこにCommentのインスタンスが多数あると、ポストごとのコメント数がここにキャッシュされます。
#DBについて
Railsのアプリケーションで使用するデータベースの種類はMySQLやPostgreSQLなどいくつかありますが、デフォルトではSQLiteを使用するようになっています。
そのため、Railsアプリケーションを作成する時に特に指定しなければまずSQLiteを使う前提でデータベース設定ファイルが作成されます。
##database.yml
DBを作成時に上記のdatabase.ymlファイルも生成されます。
このファイルに、Railsで用意されているアプリケーションを実行する時の3種のモード(開発、テスト、本番)ごとにDBの種類などが記載されています。
そして、同じアプリケーションであっても実行モード毎に使用するデータベースを変えることができます。
例えば開発時に使用していたテストデータが格納されるデータベースと、本番時に使用する実際のデータが格納されるデータベースを分けることができますし、設定項目も実行モード毎に別々に設定できます。
development:
実行モードが開発の場合の設定を記載
test:
実行モードがテストの場合の設定を記載
production:
実行モードが本番の場合の設定を記載
実行モードではデフォルトでは「開発」になっています。モードは環境変数を設定することで変更することができます。
設定ファイルで記載されている項目は次のとおりです。
adapter: 使用するデータベース
database: データベースファイル名
pool: コネクションプーリングで使用するコネクションの上限
timeout: タイムアウトまでの時間(ms)
#オブジェクトとは
Rubyの世界では、全てのデータは「オブジェクト」と呼ばれます。
- 文字列オブジェクト:文字を表現する
- 数値オブジェクト:数を表現する
- 配列オブジェクトやハッシュオブジェクト:オブジェクトを保管するオブジェクト
上記のようなオブジェクトを管理・生成するためにクラスとインスタンスが存在してます。
#クラス・インスタンス
- クラス:とある種類のオブジェクトの共通の属性とメソッドをまとめて定義しておく型のようなもの
- インスタンス:クラスに基いて生み出されたオブジェクトのこと
この辺りの説明に関しては、いろいろな例えがありますが、個人的にはたい焼きのイメージが結構しっくりきます。
例) - たい焼きの型:クラス
- たい焼き:インスタンス
たい焼きはクラスに基づいて作成されており、どのたい焼きも同じ設計図を使って作られます。
しかしながら、たい焼きにはあんこだったり、チョコだったり、色々と味が違うものがあります。
これがインスタンスであり、どれも同じ設計図からできていていますが、別々のオブジェクトを生成してます。
- インスタンスA:あんこたい焼き
- インスタンスB:チョコたい焼き
- インスタンスC:カスタードたい焼き
以下URLにて、より詳細について記載されておりますので、詳細は下記URLをご参照ください!
https://qiita.com/jnchito/items/f07e58824f92395c353b
##クラスの定義方法について
###通常のクラス定義法
基本的には以下のようにクラスを定義します。
class User
# ブロックの内部で各種メソッドを定義
end
###動的なクラス定義法
クラスの定義は以下の方法でも定義できます。
User = Class.new do
# ブロックの内部で各種メソッドを定義
end
基本的にはこの定義方法は使用しませんが、
動的にクラスを定義するメリットは以下2点ございます。
1. ブロックの外の変数が呼び出せる
var = 'hogehoge'
class Tweet
# classブロックの内部からは、ブロックの外で定義されたvarは呼び出せない
puts var
end
Tweet = Class.new do
# Class.newブロックの内部からは、varを呼び出すことができる
puts var
end
2. クラスを動的に定義できる
class Product
def self.create_new_class(class_name)
# 引数の値に応じて、クラスを作成する
self.class.const_set :"#{class_name}", Class.new
end
end
#クラスメソッドとインスタンスメソッド
##クラスメソッド
クラスが使用できるメソッド。
クラスメソッドを定義したクラス自身が使用できます。クラスで共通の情報を使った処理に使用します。
-定義の仕方:
クラスメソッドの定義の仕方はいくつかありますが、すべてクラスの定義内で行います。
クラスメソッドはメソッド名の前にself.を付けます。
class クラス名
def self.メソッド名
# 処理
end
end
-呼び出し
呼び出しは、クラス名に続けてメソッド名を書きます。
引数は()の中にいれます。ここは普通のメソッドと同様です。
クラス名.メソッド名(引数)
#例
user = User.new
##インスタンスメソッド
インスタンスが使用できるメソッド。
インスタンスメソッドを定義したクラスのインスタンスに使用できます。インスタンスごとの個別の情報(属性値)を使った処理に使用します。
-定義の仕方:
インスタンスメソッドの定義はそのインスタンスのクラスの定義内で行います。
普通のメソッドと書き方は同じです。
class クラス名
def メソッド名
# 処理
end
end
-呼び出し
呼び出しは、インスタンスに続けてメソッド名を書きます。
引数は()の中にいれます。ここは普通のメソッドと同様です。
インスタンス.メソッド名(引数)
#例
user = User.new
#ActiveRecord::Baseとは
RailsにデフォルトでインストールされているActiveRecordを実際に利用する時に、継承するクラス。
例えばrails g model tweetのコマンドを実行するとTweetクラスが作成されるが、そのTweetクラスはActiveRecord::Baseを継承しており、Tweetクラスのインスタンスに対して、ActiveRecord::Baseで定義されているメソッドを使用することができる。
##ActiveRecord::Baseメソッド(一例)
Tweet.all
#Tweetモデルからすべてのインスタンス取得
Tweet.new(name: "John", text: "Hello!")
#Tweetモデルに新規インスタンスを作成
tweet.save
#作成したインスタンスを保存
Tweet.create(name: "John", text: "Hello!")
#newメソッドとsaveメソッドを一気に行うことと同じ
Tweet.find(3) #=> <Tweet id:3, name: "John", text: "Hello!" >
#Tweetモデルからidが3のデータをインスタンスとして取得 ※該当するものがない場合はエラーになる
Tweet.find_by(name: "John")
#条件に合うデータをidの若い順に1つだけ取得 ※該当するものがない場合はnilを返す
Tweet.where(name: "John")
#条件に合うデータを複数取得
Tweet.limit(5)
#Tweetモデルからidの若い順に5つのデータを取得
Tweet.order("created_at DESC")
#データの作成日時が新しい順にソート。DESCは降順(Descending)、ASCは昇順(Ascending)
tweet.destroy
#削除
tweet.update
#更新
#application.html.erbとは
Railsのファイルに、application.html.erbというファイルがデフォルトで存在してます。
こちらの機能について、以下に記載します。
機能/役割
-共通情報の記述
デフォルトでは、全てのビューが読み込まれるときapplication.html.erbが読み込まれます。
そのため、ヘッダーやフッターなど共通で表示させたい内容をここに記述する。
bodyタグの下がコンテンツになりますが、ここでは基本的にyieldというメソッドを使用することで、各ページに記述した内容がこのyieldに埋め込まれてブラウザの画面に表示します
-cssファイルの読み込み
application.html.erbにstylesheet_link_tag 'application'という記述することで、app/assets/stylesheets/application.cssが読み込まれる。
application.cssにあるrequire tree . という記述により、同じフォルダにあるcssファイル全てが読み込まれる。
そのため、各htmlファイルに個別でhtmlファイルにlinkタグを記述してcssファイルを読み込む必要がなくなります。
#ストロングパラメータとは
ストロングパラメータとは「Web上から入力されてきた値を制限することで、不正なパラメータを防ぐ仕組み」です。MassAssingmentの脆弱性というセキュリティ上の問題に対処するための仕組みとしてRails4系から導入されました。
*MassAssingmentとは:
フォームが送信するparamsの内容を改ざんするなどして、開発者が意図していないデータをDBに保存させる攻撃。
攻撃される例)
chromeのデベロッパーツールを使用することで簡単に攻撃できてしまいます。
・情報を投稿する画面でデベロッパーツールを開く。
・テキストフィールドのフォームを選択する。
・表示されたHTMLタグの属性の内容を変更する。
例えば、あるテーブルにadminカラムがあり、そこに1というフラグが立っていたら管理者であるという設計になっていたとします。
上記の手順でparamsのキーは変更できてしまいますので、本来は別のカラムに保存すべき内容をadminカラムに保存させることができ、テキストフィールドに1と入力すれば管理者になりすますことができてしまいます。
このように、悪意をもってキーを書き換えたとしても、想定しているキー以外は弾く仕組みがストロングパラメータです。
#アセットパイプライン
アセットパイプライン:
JavaScriptやCSSのアセットを最小化(スペースや改行を詰めるなど)または圧縮して連結するためのフレームワーク。この機能はSprocketsというGemが担っている。
各ビューが表示される際、必ず"application.html.erb"が読み込まれるが、その中にstylesheet_link_tagの記述があり、それによってapp/assets/stylesheets/application.cssが呼び出される。
"application.css"はマニフェストファイルといわれ、このファイルから各CSSファイルを呼び出す仕組みになっている。
JavaScriptも同様です。
*アセットパイプラインはsprockets-rails gemによって実装され、デフォルトで有効になっています。
アプリケーションの新規作成中にアセットパイプラインを無効にするには、--skip-sprocketsオプションを渡します。
Railsではsass-rails、coffee-rails、uglifier gemが自動的にGemfileに追加されます。
Sprocketsはアセット圧縮の際にこれらのgemを使用します。
gem 'sass-rails':sassファイルをコンパイル
gem 'uglifier':javascriptのコード軽量化
gem 'coffee-rails':coffeeファイルをコンパイル
##主要な機能
###アセットの連結
アセットパイプラインの第一の機能はアセットを連結することです。
これにより、ブラウザがWebページをレンダリングするためのリクエスト数を減らすことができます。
Webブラウザが同時に処理できるリクエスト数には限りがあるため、同時リクエスト数を減らすことができればその分読み込みが高速になります。
SprocketsはすべてのJavaScriptファイルを1つのマスター.jsファイルに連結し、すべてのCSSファイルを1つのマスター.cssファイルに連結します。
###アセットの最小化
アセットパイプラインのもうひとつの機能はアセットの最小化 (一種の圧縮) です。
CSSファイルの最小化は、ホワイトスペースとコメントを削除することによって行われます。
JavaScriptの最小化プロセスはもう少し複雑です。最小化方法はビルトインのオプションから選んだり、独自に指定したりすることができます。
#routingにおけるscope/namespace/module の違い
コントローラの中にフォルダを作成して、その中のコントローラを動かすときに上記を使用します。
しかしながら、各々特徴がございますので、以下にまとめます。
URL(path) | ファイル構成(コントローラ) | |
---|---|---|
scope | 指定パス 例)admin/user~path | 変更なし 例)users |
namespace | 指定パス 例)admin/users〜path | 指定パス 例)admin/users |
module | 変更なし 例)users~path | 指定パス 例)admin/users |
#layoutメソッド
コントローラによってヘッダー・フッターのデザインを変更したり、読み込むCSSを切り替えるメソッド。
使い方:コントローラのファイル内に以下のように記載します。
items_controller.rb
class ItemsController < ApplicationController
layout 'hogehoge'
これでitemsコントローラのアクションが呼び出された際、
レイアウトファイルとしてapp/views/layouts/hogehoge.htmlが使われるようになる。
何も指定しない場合はapp/views/layouts/application.htmlが読み込まれる。
#dependentオプション(Active record)
dependentオプション:モデルが削除されるとき、関係づけされたモデルに対する挙動を定義するもの。
オプションにはいくつか種類があるので、以下に記載していきます。
##dependent: destroy
削除時に指定したモデルに対してdestroyが実行されるようになります。
以下の例の場合、Articleモデルが削除されるとそれに付随しているuserモデルが削除されます。
孫クラスにもdependent :destroyがあれば、孫クラスも合わせて削除されます。
一見便利そうですが、実行する処理が多いというデメリットもあるので、複数のデータを持っている場合(has_many)、処理時間がかかるため、考慮必要です。
class Article < ActiveRecord::Base
belongs_to :user, dependent: :destroy
end
##dependent: delete / delete_all
belongs_to, has_oneの場合=>:delete
has_manyの場合=>:delete_all
delete / delete_allを指定すると、オブジェクトが削除されるときに、関連付けられたオブジェクトが直接データベースから削除されます。destroyメソッドは実行されません。
destroyとの違いは、直接レコードを消しにいくため、孫クラスにdependent :destroyがあっても、孫クラスには影響を及ぼさないという点があります。
そのため、このオプションは、他のクラスのhas_many関連付けとつながりのあるbelongs_to関連付けに対して使ってはいけません。孤立したレコードがデータベースに残ってしまう可能性があります。
##dependent: nullify
:nullifyを指定すると、外部キーがNULLに設定されます。
削除されることはないため、残ったレコードは永遠に残り続けることになります。
##dependent:restrict_with_exception/:restrict_with_error
restrict_with_exception:
関連付けられたレコードがある場合にActiveRecord::DeleteRestrictionError例外が発生します。
restrict_with_error:関連付けられたオブジェクトがある場合にエラーがオーナーに追加されます。
つまり、子要素があるときに削除されては困る場合に使うことになるでしょう。
#アソシエーションにおけるclass_nameの定義(自己結合関連付け)
データモデルを設計時、アソシエーションのメソッド名とクラス名が一致している場合は問題ありませんが、メソッド名を変えたいこともあります。
たとえば、1つのデータベースモデルに全従業員を格納しておきたいが、マネージャーと部下(subordinate)の関係も追えるようにしておきたい場合が考えられます。
この状況は、自己結合関連付けを用いてモデル化できます。
class Employee < ApplicationRecord
has_many :subordinates, class_name: "Employee",
foreign_key: "manager_id"
belongs_to :manager, class_name: "Employee", optional: true
end
上のように宣言しておくと、@employee.subordinatesと@employee.managerが使えるようになります。
#参照
Railsガイド
https://railsguides.jp/
【初心者向け】RailsのActive Recordの解説&メソッドまとめ
https://qiita.com/ryokky59/items/a1d0b4e86bacbd7ef6e8
SQLite用のデータベース設定ファイル(database.yml)
https://www.javadrive.jp/rails/model/index1.html
Rails: ActiveRecord::Baseメソッドのまとめ
https://qiita.com/penguin_note/items/adb0b9bf7c13c1b1d44d
Railsのroutingにおけるscope / namespace / module の違い
https://qiita.com/ryosuketter/items/9240d8c2561b5989f049
Active Record の関連付け
https://railsguides.jp/association_basics.html
【Rails】ActiveRecordの:dependent使い分けまとめ【: destroy, :delete, :nullify】
https://qiita.com/drafts/cfb96a60db984a36e2dd/edit
以上となります。最後までご覧いただき、ありがとうございました!
今後も学習した事項に関してQiitaに投稿していきますので、よろしくお願いします!
記述に何か誤りなどございましたら、お手数ですが、ご連絡いただけますと幸いです。