ここ最近複数の専門書を断片的に読む機会やら、(自分にとって)新たな技術にチャレンジする機会やらを頂いたため、最近数ヶ月で学んだ内容をせっかくなので体系的にまとめました。気づいたら大分ボリュームを増やしていましたので、必要なとき必要な箇所に行き当たってくれることを祈っています。
なお、以下の5記事を姉妹記事として同時に書いています。
文中でもリンクが出てきますのでよろしければ併せて目を通して頂けると幸いです。
- YAMLとは何か? - いつもRailsの設定ファイルで出てくる奴の正体
- redirect_to使った時にフラッシュメッセージを表示させる
- JavaScriptを卒業してCoffeeScriptで飛躍しよう - 基本などまとめ
- モモンガでも使えるAjax(えいじゃっくす) − 投稿したコメントをリロードせずに表示しよう
- Railsでメール自動配信機能をつくるまでの道程
■ Modelに関するあれこれ
activerecord関連
O/Rマッパー
- modelがデータベースとのやり取りをする際に欠かせないのがO/Rマッパー
- リレーショナルデータベースとオブジェクト指向言語の橋渡しをする
- 例えばオブジェクトのプロパティ名をテーブルのカラム名と一致させたりする
- Railsではデフォルトで
ActiveRcord
がO/Rマッパーとして使われる
yamlとは何か
-
database.yml
とか設定ファイルに時々でてくる奴 - まとめたら長すぎたので別記事にしました(下記)
「YAMLとは何か? - いつもRailsの設定ファイルで出てくる奴の正体」
SQL関連
whereメソッドのバリエーション
- プレイスホルダ(
?
)を使用して検索条件を後置できる - プレイスホルダ(
?
)の個数分引数として値を渡してあげる必要がある - 具体例:
Book.where('title = ? AND author = ?', <値1>, <値2>)
$ Book.where('title = ? AND author = ?', params[:title], params[:author])
Book Load (0.3ms) SELECT `books`.* FROM `books` WHERE (title = '白鯨' AND author = 'メルヴィル')
-
?
を使用した検索条件指定は名前なしパラメータと呼ばれる - 名前付きパラメータを使用すると上記の例は以下のように書き換えられる
$ Book.where('title = :title AND author = :author', title: params[:title], author: params[:author])
- 検索条件には
not
を付与し「●●以外のもの全て」という条件式をつくることができる
$ Book.where.not(title: "白鯨")
Book Load (0.3ms) SELECT `books`.* FROM `books` WHERE (`books`.`title` != '白鯨')
取得するプロパティの制限
- 多くの情報を扱うテーブルにおいて不要な列まで無条件に取り出すことは処理速度の遅延にもつながる
-
select
メソッドを併せて使用してあげると取得するプロパティを制限することができる
$ Book.all.select(:id, :title)
Book Load (0.3ms) SELECT `books`.`id`,`books`.`title` FROM `books`
#=> [#<Book:0x007fb5ea28a4b8 id: 2, title: "白鯨">,
#<Book:0x007fb5ea28a1c0 id: 3, title: "そして">]
指定したカラムの値で配列をつくる
- 取得してきたレコードに
pluckメソッド
を使用すると指定の値だけを取り出して配列にすることができる
$ Book.all.select(:id, :title).pluck(:title)
Book Load (0.3ms) SELECT `books`.`title` FROM `books`
#=> ["白鯨", "そして"]
config関連
アプリケーションやアクティブレコードで使用するタイムゾーンを変更する
-
application.rb
内に記述する - デフォルトでコメントアウトされている部分の記述を以下のように直す
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
#=>
config.time_zone = 'Tokyo'
console関連
コンソールで検証した内容を実際のデータベース等に反映させたくない時
- 通常コンソールは
rails cコマンド
を用いて起動しレコードの生成・保存等の挙動を検証できる
$ rails c
Loading development environment (Rails 4.2.1)
[1] pry(main)>
-
rails c --sandboxオプション
を使用するとコンソール終了時にデータベースに対する全ての変更をロールバックする
$ rails c --sandbox
Loading development environment in sandbox (Rails 4.2.1)
Any modifications you make will be rolled back on exit
[1] pry(main)>
コード中のコメントを集めてTODO化する
- ひっそりと存在しているコマンド
rake notes
- ルールに則りコメントを記述することでTODOを一覧化できる
- 書き方は
# [TODO/FIXME/OPTIMIZE]: hogehoge
class Users < ActiveRecord::Base
# TODO: set sign-in validations
# FIXME: fix class name as singular
end
- 出力形式は
[行番号] [TODO/FIXME/OPTIMIZE] コメント
- 特定の種類のコメントだけを出力することも可能
$ rake notes
#=>
app/models/user.rb:
* [2] [TODO] set sign-in validations
* [3] [FIXME] fix class name as singular
$ rake notes:fixme
#=>
app/models/user.rb:
* [3] [FIXME] fix class name as singular
migrationファイル関連
各カラムに追加設定できるオプション
- データベースに変更を加える際にmigrationファイルにはオプションを設定することができる
- 主なオプションとして以下の5つが挙げられる
フラグ名 | オプション内容 | 使用可能なデータ型 |
---|---|---|
limit | 列の桁数 | 全て |
default | デフォルト値 | 全て |
null | null値を許可するか | 全て |
precision | 少数以下含めた数値の全体桁 | decimal型 |
scale | 小数点以下の桁数 | decimal型 |
- 以下のように実行前のmigrationファイルに追記することでデータベースに反映させることができる
def change
create_table :books do |t|
t.string :title, limit: 9, null: false
t.decimal :price, precision: 5, scale: 0
t.string :publisher, default: 'HBS'
t.timestamps
end
end
- 上記の指定はテーブルに対し以下の制約を与える
- titleには9文字以下で何かしらの値が入っていなければ保存できない
- priceは小数点以下0桁かつ全体5桁までの数字のみ保存可能
- publisherには何も指定しなくともデフォルトで文字列'HBS'が代入される
has_and_belongs_to_many関連の中間テーブルを生成する
- 中間テーブルは外部キー以外の列を持ってはいけないという制約がある
- migrationファイルにおいて
create_join_tableメソッド
を使用すれば制約を気にせずにテーブルを生成できる -
rails g migration CreateJoinTableTable1AndTable2 <テーブル1名> <テーブル2名>
コマンドによってファイル中の記述も自動生成できる
$ rails g migration CreateJoinTableAuthorsAndBooks authors books
# invoke active_record
# create db/migrate/20150728175239_create_join_table_authors_and_books.rb
- migrationファイルの中身は以下のようになっている
class CreateJoinTableAuthorsAndBooks < ActiveRecord::Migration
def change
create_join_table :authors, :books do |t|
# t.index [:author_id, :book_id]
# t.index [:book_id, :author_id]
end
end
end
■ Viewに関するあれこれ
viewヘルパー関連
html.erbは何の略か
-
erb
= Embedded Ruby - html以外にもxmlやplain textにも使えるらしい
- 使ったことはない
Rubyタグ直後の改行を無くす
-
-%>
のように閉じることでデザインに影響を与えるようなタグ直後の空白・改行を打ち消せる
<%= ... -%> || <% ... -%>
コメントアウトの方法5種類
①
<%# 一般的なコメントアウトです %>
②
<%#= これもコメントアウトです
この行もコメントアウトです
複数行に渡りコメントアウトできます %>
③
<% これはコメントではありません
# この行がコメントになります %>
④
<% if false %>
<%= この行はコメントアウトになります %>
<% end %>
⑤
<%
=begin
%>
<%= この間の部分は全てコメントになります %>
<%
=end
%>
改行文字を<p>/<br>タグで置き換える
-
simple_formatメソッド
によって改行文字を含むテキストをブラウザ上でも正しく表示できる - 以下の4つの基準に従って文章を整形する
- 文字列全体を
<p>
タグで括る - 単一の改行文字には
<br />
タグを付与 - 連続した改行文字には
</p><p>
タグを付与 - 元の改行文字の前後に追加され元の改行文字は破棄されない
- 文字列全体を
# @text =
# 本日の天気
#
# 関東地区の天気をお知らせします。
# 明日は全体的に猛暑日となる見込みです。
# 以上です。
<%= simple_format(@text) %>
# => 出力結果
<p>本日の天気</p>
<p>
関東地区の天気をお知らせします。<br />
明日は全体的に猛暑日となる見込みです。<br />
以上です。
</p>
条件に応じてリンクを生成する
-
link_to_if/unlessメソッド
を使用すると条件式がtrueの場合のみリンクを生成しfalseの場合はテキストを表示する -
link_to_unless_currentメソッド
を使用するとリンク先が現在のページの場合はテキストのみを出力する
<%= link_to_if @user.present, "MY PAGE", user_path(@user) %>
<%= link_to_unless_current "HOME", root_path %>
form関連
リンク・ボタンクリック時に確認ダイアログを表示
-
link_to
にdata-confrimオプション
を指定する -
OK
を選択した場合のみ画面遷移・処理を行うようにできる - データの削除等、不可逆な処理に対する誤操作を極力無くすことができる
<%= link_to '削除', book, method: :delete, data: {confirm: '本当によろしいですか?'} %>
-
submitタグ
にdata-confirmオプション
を使用しても同様の処理を行える
form_for配下でのform_tagヘルパーの使用
- モデルに紐付いたオブジェクトを扱うform_for内部でもモデルに紐付かない
form_tagヘルパー
を使用可能 - 例えばレビューを投稿するフォームの中にタグの入力欄を並列で作る時など有効
form_forにおけるsubmitボタンの自動表示
- form_forメソッドは扱うモデルオブジェクトによってHTTPメソッドを使い分けてくれる
- 実はform内
submitボタン
の表示文字も使い分けてくれる - valueを設定しない場合初期値で「
Create (モデル名)
」または「Update (モデル名)
」が自動で入る
存在検証機能の二重化
- コメント投稿欄に文字列が入っていなければデータを保存できないという検証機能をつける場合を考える
- 対象の入力欄に
required属性
を付与すると指定の入力欄が空の状態では警告メッセージが表示されデータを送信できなくなる
<%= f.text_filed :comment, required %>
- 警告メッセージを非常に簡易に実装でき便利だがブラウザ側から開発者ツール等を使用すれば容易に
required
指定を外し空のデータを送れてしまう - そのためにクライアントサイドだけでなくきちんとサーバーサイドでもバリデーションをかけなければならない
class Comment < ActiveRecord::Base
validates :text, presence: true
end
layout関連
部分テンプレートを保存するべき場所
- 部分テンプレートの保存先には規則が存在する
- 規則に従うことでシンプルな記述で呼び出せるようになる
用途 | 保存先フォルダ |
---|---|
特定のコントローラでのみ共有 | /views/コントローラ名 |
アプリケーション全体で共有 | /views/application |
リソースに強く関連づいた部品 | /views/リソース名(複数形) |
-
views/application
ディレクトリに保存された部分テンプレートはパスを指定せずともrender 'review'
のように呼び出せる - 同名の部分テンプレートが複数存在する場合にはメインテンプレートと同ディレクトリ配下の部分テンプレートが優先される
■ Controllerに関するあれこれ
controllerの挙動関連
ルーティングに対応するアクションが存在しない場合
- アクションの定義が存在せずともエラーにはならない
- 対応するテンプレートつまりビューファイルを直接読み込みにかかる
便利メソッド関連
sleepメソッドによる処理中の演出
-
sleepメソッド
を使用すると指定秒数間処理を休止する - formの
submit
においてdata-disable_withオプション
と併用すると処理中の演出ができる - 下記の場合「投稿する」ボタンを押した後3秒間ボタンが押せなくなくと同時に「処理中」の文字が表示される
class ReviewsController < ApplicationController
def create
sleep 3
Review.create(review_params)
end
#- (中略) -#
end
<%= form_for @review do |f| %>
<!-- (中略) -->
<%= f.submit "投稿する", data: {disable_with: "処理中..."} %>
<% end %>
around_actionフィルタで記述をコンパクトにする
- フィルタ発火のタイミングはアクション実行直前直後の二種類
class BooksController < ApplicationController
before_action :start_log
after_action :end_log
def create
Book.create(book_params)
end
private
def start_log
logger.debug("[START: #{Time.now.to_s}]")
end
def end_log
logger.debug("[END: #{Time.now.to_s}]")
end
end
-
before_action
+after_action
はaround_action
+yield
で書き換えられる
class BooksController < ApplicationController
around_action :output_log
def create
Book.create(book_params)
end
private
def output_log
logger.debug("[START: #{Time.now.to_s}]")
yield #アクションを実行
logger.debug("[END: #{Time.now.to_s}]")
end
end
parameter関連
アクセスしたユーザーのIPアドレスを取得する
- ユーザーのログイン認証を必要としない投票ボタンを実装する時に活躍
- アカウント機能は作りたくないけれど極力ひとり一票までにしたいときはIPアドレスで識別しよう
-
request.remote_ip
をコントローラーで使用すれば簡単にIPアドレスを取得できる
class BooksController < ApplicationController
#--(中略)--#
private
def set_ip
@ip = request.remote_ip
end
end
status関連
条件に応じて意図的に404エラーを表示させる
- 条件分岐内にrenderメソッドでファイル名を指定してあげればok
- status
-
and return
を付けてあげないと痛い目を見る事が多い
class BooksController < ApplicationController
def show
@book = Book.find(params[:id])
if @book.status == '1'
render file: Rails.root.join('public/404.html'), status: :not_found and return
end
render 'top/show'
end
end
404以外のステータスコード
- logを見るときにもステータスコードを知っていると読解量が違ってくる
- SEOの観点からも302なのか301なのか正しいステータスコードを付与することは大切
シンボル | コード | 意味 |
---|---|---|
:ok | 200 | 成功 |
:created | 201 | リソースの生成に成功 |
:moved_permanently | 301 | リソースが恒久的に移動した |
:found | 302 | リソースが一時的に移動した |
:see_other | 303 | リソースが別の場所にある |
:unauthorized | 401 | 認証を要求 |
:forbidden | 403 | アクセスが禁止されている |
:not_found | 404 | リソースが存在しない |
:method_not_allowed | 405 | HTTPメソッドが許可されていない |
:internal_server_error | 500 | サーバーエラー |
helper関連
現在のコントローラーに対応したヘルパーファイルのみ読み込む
- デフォルトではどのヘルパーファイルにヘルパーメソッドを記述してもアプリケーション全体で使用できてしまう
- アプリケーションが肥大化した場合名前の競合が意図せず起きてしまうことも考えられる
- 現在のコントローラー名に対応したヘルパーファイルだけを読み込む設定は
config/application.rb
に記述する
class Application < Rails::Application
# Do not share helper methods
config.action_controller.include_all_helpers = false
end
- こうすることで、現在
UsersController
に関連した動作を行っている場合はusers_helper.rb
が読み込まれる
認証関連
ダイジェスト認証の設定
- コントローラーに記述するだけで簡単に実装できるセキュリティには二種類ある
- ベーシック認証とそれよりもセキュリティ強度の増した
ダイジェスト認証
の2つ -
before_action
を利用し簡単に実装可能かつ記述も簡潔なダイジェスト認証の実装を紹介 - サイトにアクセスする際に共通ユーザー名と共通パスワードの入力が必須となる
class ApplicationController < ActionController::Base
before_action :digest_auth
private
def digest_auth
members = { '<ユーザー名>' => '<パスワード>' }
authenticate_or_request_with_http_digest('<アプリケーション名>') do |name|
members[name]
end
end
end
flashメッセージ関連
redirect_toのオプションとしてフラッシュを設定する
- Bootstrapのalertのスタイルに当たるように任意のキーで送信できるようにする
- 長くなったので別記事にまとめました(下記)
「redirect_to使った時にフラッシュメッセージを表示させる」
■ Routingに関するあれこれ
ルーティング表示関連
検索機能付きルーティング一覧を表示する
- 最も使われるルーティング表示方法は
rake routesコマンド
- 大規模なアプリケーションではルーティングが肥大化し検索機能を使いたい時も
- そんな時は
http://localhost:[ポート番号]/rails/info/routes
にアクセスすると良い - リアルタイム検索機能付きのルーティング一覧を表示してくれる
(.:format)の記述をルーティングから外す
- resourcesを利用してルーティングを設定すると勝手に
(.:format)
のオプションが付与される - 大変親切なのだがjsonとかxmlとか今回使わねーしという場合外したくなる時も
-
format: false
オプションを指定し て外してあげよう
resources :users, format: false, only: [:index]
#=> users GET /users users#index
routes.rbに記述すべき順番
-
routes.rb
に記載した順番がそのまま優先順位になる - そのため汎用性の高いルーティングほど下に書くべきである
- 例えば
rootパス
の設定は非常に汎用性が高いと言えるので多くの場合最下部に記述する
アクション追加関連
resourcesの7つのアクション以外にアクションを定義する
- resourcesメソッドで生成される7つのアクションでは補いきれない場合がある
- その時は
collectionブロック
またはmemberブロック
を使用してアクションを追加できる -
collection
は複数のオブジェクトを扱うアクションに(ex:index) -
member
は単一のオブジェクトを扱うアクションに(ex:edit)
resources :books do
collection do
get :newest
end
member do
get :archive
end
end
#=> $ rake routes
# newest_books GET /books/newest books#newest
# archive_book GET /books/:id/archive books#archive
- ブロック配下のアクションがひとつである場合には
onパラメータ
を使用してシンプルに記述することも可能
resources :books do
get :newest, on: :collection
get :archive, on: :member
end
■ フロントエンドに関するあれこれ
読み込み関連
application.js/.cssの役割
- ファイル内に記述された
マニフェスト
と呼ばれるリストを元にアセットを読み込む - 読み込む動作自体は
Sprockets
というgemが担っている -
application.js
に記述してある標準ライブラリはturbolinks
というgemの中に配置されている
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
/*
*= require_tree .
*= require_self
*/
application.html.erbの役割
- 用意したJavaScriptやStyleSheetをviewで読み込む際には個々のファイル名は記述しない
-
application.html.erb
にマニフェストのファイル名を指定する
<!DOCTYPE html>
<html lang="ja">
<head>
<!-- 中略 -->
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
</head>
アセットパイプライン関連
アセットパイプラインのしくみ
-
Sprocketsによって提供されるJavaScriptやStyleSheetをまとめて管理する仕組み
-
大きく4つの段階が存在する
- コンパイル
オリジナルソースである複数の
.js.coffee
形式で存在するファイルたちを.js
形式に
.css.scss
形式で存在するファイルたちを.css
形式に
それぞれ変換する。
(※実行速度が遅延するためproduction環境ではスキップ)- 統合
コンパイル済みである複数の
.js
ファイルたちをapplication.js
に
.css
ファイルたちをapplication.css
に
それぞれ統合する。
(※デバッグしやすいためdevelopment環境ではここで出力)- 圧縮
統合された
application.js
とapplication.css
から
コメント・改行・空白等取り除き圧縮する。- ダイジェスト付与
圧縮された
application.css
・application.js
と個別の画像たちに
ハッシュ値
というそのファイル固有の文字列を付与する。
これによりアセットに編集作業を加えアップデートするごとに
全く別のファイルだと認識され予期せぬキャッシュの適用を防ぐ。 -
コンパイルの対象となるCoffeeScriptの正体に関しては以下のまとめを参照されたい
「JavaScriptを卒業してCoffeeScriptで飛躍しよう - 基本などまとめ」
アセットプリコンパイル
- アセットをアプリケーションで使う際には
コンパイル
して形式を統一する必要がある - しかしproduction環境では処理速度に対する影響を避けるためコンパイル処理を自動で行わない
- そのため事前に手動でコンパイルを実行しなければならない
- 事前に手動でコンパイルを行っておくことを
アセットプリコンパイル
と呼ぶ - ターミナルから以下のコマンドで実行できる
$ rake assets:precompile
※WEBrick環境では /config/environments/production.rb
中の config.serve_static_assets = false
をコメントアウトして静的アセットファイルの処理を放棄する
Turbolinks関連
- ページ遷移に際してページ全体ではなくタイトルと本文のみを置き換える仕組み
- このことによりページの読み込みを高速化させている
- 内部的にAjax通信(非同期通信)を発生させている
- デフォルトで有効にされているため無効にする場合はgemfileに記載の
gem 'turbolinks'
をコメントアウトし$ bundle
する
Ajax関連
- HTMLだけで完結する内容でもなかったので別記事にまとめたものを参照されたい
「モモンガでも使えるAjax(えいじゃっくす) − 投稿したコメントをリロードせずに表示しよう」
■ その他Railsに関するあれこれ
Mailer関連
- 例えば記事が更新された際に読者全員にお知らせメールを自動配信するような機能を簡単に実装できる。
- 導入からTipsまで若干体系的な話になり別記事にまとめたため下記を参照されたい。
Gemfile関連
数字の意味
- gem名の隣には様々数字が書いてある場合があるがそれには意味がある
- 各々の数字はgemのバージョンを指定している
- 記号と意味は下表のとおり
指定例 | 概要 |
---|---|
2.4.3 | 特定のバージョンで固定 |
>= 2.1.2 | 特定のバージョン以上のものが必要 |
~> 3.1 | 3.x は良いが 4.x は不可 |
バージョンの意味
- gemに限らず何かしらのバージョンは最大で数字4つを点で区切って表す事が多い
- 「3.1.4.2」というバージョンがあったとする
- 左から「
メジャーバージョン
.マイナーバージョン
.リビジョン
.ビルド
」と呼ばれる - 各バージョンの変更が持つべき意味は下表のとおり。上に行くほど変化の角度が高い。
バージョン名 | 概要 |
---|---|
メジャーバージョン | 抜本から変更を加える |
マイナーバージョン | 大幅な仕様変更や機能追加を行う |
リビジョン | 仕様変更や機能追加を行う |
ビルド | 修正パッチを追加する |