Rails: ファイルを役割別に整理する
目的
自己整理のために、railsで使用するファイルを役割別にまとめています。
なお、ログイン機能を実装した実際のコードを実例として載せています。
現時点では理解が及んでいない部分も多々ございます。
学習を進めながら、気づきが起こり次第、都度修正していきます。
MVCモデル
設計モデルの1つで、Model, View, Controllerの頭文字をとったもの。
それぞれ役割が異なり、以下のように設計されています。
Model(モデル):データベースに問い合わせ、データの新規作成、更新、削除などを担当する
View(ビュー):クライアントに表示するページを生成する
Controller(コントローラー):rooterからの指示をもとにサーバー側で必要な処理を行う
【その他】
※Rooter(ルーター):クライアントからのリクエストに応じて、コントローラーに指示を出す。
※Helper(ヘルパー):コントローラーで使用するメソッドを定義しておける場所。
Rooter
ルーターは、各コントローラに対してアクションを指定し、命令を出します。
設定は、routes.rb
というファイル1つに集約してまとめられています。
例えば、冒頭の一文(get '/home', to: 'static_pages#home')
は、次の意味を表します。
■code意味:
/homeのURLで、GETリクエストをクライアントから受けた場合、static_pagesコントローラーのhomeアクションを実行しなさい。
また、resource: に対してコントローラー名を渡すと、railsがよく使う7つのアクション(index, show, new, create, edit, update, destory)を自動的に作成してくれます。
なお、rootはクライアントがはじめに飛ぶページを設定しています。
Rails.application.routes.draw do
get '/home', to: 'static_pages#home'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
root 'static_pages#home'
end
Controller
コントローラーは、ルーターからの指示を受けて、具体的な処理を行います。
生成したコントローラーの数だけファイルが存在し、<コントローラー名>.controller.rb
というファイルに処理内容をまとめます。
実例では、はじめのnewアクション
は
処理を未定義であるため、railsのデフォルト動作をします。
デフォルトでは、対応するビュー(<コントローラー名>/<アクション名>.html.erb)に飛びます。
2つ目のcreateアクション
では
- クライアントから送信されたパラメーターに入っているセッション内のemailをもとにユーザー情報を検索し、user変数に格納します
- user変数がtrueでかつ、送信されてきたセッション内のパスワードを鍵にユーザー認証がされれば、
- ユーザをログインさせて
- もしクライアントがremember meのチェックボックスにチェックが入っていれば、PCにユーザ情報を覚えさせ、なければ忘れさせます。
- 一方で、ユーザー認証が失敗すれば
- 1度だけ’emailとpasswordのバリデーション通りませんでした’というエラーメッセージを表示させて
- ログインページへ移動させる
- user変数がtrueでかつ、送信されてきたセッション内のパスワードを鍵にユーザー認証がされれば、
といった処理を記述しています。
3つ目のdestroyアクション
では
- もしログインしていた場合は、ログアウトさせ、ルートで設定されているURLに移動させる
といった処理を記述しています。
class SessionsController < ApplicationController
def new; end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user&.authenticate(params[:session][:password])
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end
Helper
ヘルパーでは、コントローラーで使用するメソッドを定義できます。
ヘルパーも生成されたコントローラーの数だけ、ファイルが存在します。
<コントローラー名>_helper.rb
という名称で作成されます。
ヘルパーで、分かりやすいメソッド名でより詳細の処理を定義して、コントローラー側では、可読性が高いコードを記述します。
要するに、コントローラーでは、デフォルトメソッド+ヘルパーメソッドを用いてコードを書き、最小限に文字数を抑えて、見やすいコードにします。
例えば、current_userでは、ケースに分けて2つの動作が行われる複雑な処理がされます。
1、セッションの中にユーザIDがあるなら:
@current_userがnilの場合、DBからユーザIDを鍵にユーザー情報を取得する
(@current_userがあれば、DBへの問合せをしない = クエリ数を減らす)
2、もしくは、cookieの中にユーザIDがあるなら:
DBからユーザIDを鍵にユーザー情報を取得して、userに格納して、
そのuserのnilチェックをした上で、DBに保存しているremember_tokenで認証されれば
ログインさせて(セッションにユーザIDを入れる)
インスタンス変数に格納しておく(2回目以降はDB問い合わせ不要になる)
これをコントローラー側に定義すると、読み解くことが難しくなるので
current_user(現在のユーザー情報という意味)といったメソッドを
ヘルパー側で定義しています。
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user&.authenticated?(cookies[:remember_token])
log_in user
@current_user = user
end
end
end
def logged_in?
!current_user.nil?
end
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end
end
Model
モデルは、DBへの問い合わせを行います。
生成したモデルの数だけ、<モデル名>.rb
というファイルが存在します。
まず、上部コードについて簡単に解説ですが、
・attr_accessor : ゲッター・セッターの作成(ここではremember_tokenを指す)
・before_save : 保存直前の動作指示
・validates : 各種制限をかける(文字数や必須入力させる等)
・has_secure_password : authenticateなど特殊なメソッドを使用できるようにする
さらに、下部コードでは、DBに関わる(作成、更新、削除)といけない動作は
各モデルのファイルにメソッドを定義します。
例えば、rememberのように
User.new_tokenで定義されたランダムな文字列で生成されたものをremember_tokenに代入し、
User.digestで定義されたハッシュ化されたremember_tokenを
DBのremember_digestカラムに紐付けて、値を更新させる
といったメソッドは、更新する挙動を設定したいので、モデルのファイルで定義します。
また、forgetも同様で、
DBの:remember_digestに、nilを代入することで忘却させますので
更新する挙動を設定したいので、同ファイルに定義されています。
class User < ApplicationRecord
attr_accessor :remember_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
validates :email, presence: true, length: { maximum: 255 },
format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i },
uniqueness: true
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
def self.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def self.new_token
SecureRandom.urlsafe_base64
end
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
def forget
update_attribute(:remember_digest, nil)
end
end
View
ビューは、クライアントに表示するページ生成を担当します。
コントローラーに定義しているアクションによって、表示先を指定することができます。
<コントローラー名>/<アクション名>.html.erb
という名称でファイルを作成します。
拡張子に注目いただきたいのですが、erbという埋め込みrubyのファイル形式
を使用します。
これでHTML形式の中に<%= %>などを用いて、rubyコードが記述可能になります。
また、railsには自動的にHTMLを生成してくれる便利なメソッドがあります。
その一つがform_withメソッドで、下記コードの場合は
クライアントから/loginのURLで入力された情報を
設定を行ってくれます。
POSTリクエストとして送信する
<% provide(:title, "Log in") %>
<h1>Log in</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(url: login_path, scope: :session, local: true) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :remember_me, class: "checkbox inline" do %>
<%= f.check_box :remember_me %>
<span>PCにログイン情報を記憶させる</span>
<% end %>
<%= f.submit "ログイン", class: "btn btn-primary" %>
<% end %>
<p>新規アカウント作成の場合は <%= link_to "こちらをクリック", signup_path %></p>
</div>
</div>
動作について
1つのモデルケースの検証ですが、
クライアントからのWEBサイトにアクセスがあり、
ケースを想定すると
remember meをチェックされて
loginボタンを押された
-
View:new.html.erb
URLを「/login」とPOSTリクエストを設定されたページで ユーザーが入力してリクエストを送る
↓
-
Rooter:routes.rb
ユーザーから送信された /loginのURLとPOSTリクエストから 指令を出すべきコントローラーを判断して指示を出す
↓
-
Controller:sessions.controller.rb
Rooterからの指示を受けて、createアクションを実行する。
(DBからユーザー検索をして、認証手続きをしてユーザーページに飛ばします。)
・・・Helper:<コントローラー名>_helper.rb
便利メソッドを定めて、Controllerの動作を補助する。
↓
-
Model:user.rb
クライアントがremember meにチェックしたことにより、digestに記録する トークンを生成して、DBに保存させる。
(ログアウトしない限り、2回目以降のログイン時にパスワードなど入力不要になる)
↓
-
View:show.html.erb
ユーザー検索してヒットした該当ユーザーのページを表示させる
といった動作となります。
おわりに
自分なりにrailsの挙動を整理したくて、まとめてみました。
まだ、理解が浅いので、随時で修正すると思います…
最後までお読みいただき、ありがとうございました!
Author And Source
この問題について(Rails: ファイルを役割別に整理する), 我々は、より多くの情報をここで見つけました https://qiita.com/Shuhei_Nakada/items/0d7fcae4c422e538976d著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .