Ruby on Railsチュートリアル
自分でカスタマイズするために、以下の内容をチュートリアルから変えている
- デプロイ環境はHerokuではなくAWS
- RESTful APIとして構築
JSON Serializerの選定
複数記事を参考にして以下から選定する
- https://github.com/procore/blueprinter
- https://github.com/okuramasafumi/alba
- https://github.com/rails/jbuilder
利用実績が多いものなら jbuilder
パフォーマンスがよく、安定してメンテされているなら blueprinter
新しいがパフォーマンス、型付けの観点なら alba
今回はチュートリアルなので alba
を採用する
参考になった資料
最初のモデルの作成
以下のコマンドでUsersに必要なリソースを作成する
$ rails generate scaffold User name:string email:string
追加されるのは以下
- Controller
- View
- Helper
- Model
- Migration
- Test
Albaの導入
jbuilder
を削除する
Gemから bundle exec gem uninstall jbuilder
- Gemファイルから
jbuilder
を削除 bundle install
Albaをインストール
- Gemファイルに
alba
を追加 bundle install
Railsの自動インポート
概要
Gemやパッケージのインポートは Zeitwerk を利用している
config.autoload_paths設定によりファイル名と同様の変数名が定義されていれば自動読み込みされる
例:users_controller.rb
→ UsersController
インポートされているパスの確認
$ rails r 'puts ActiveSupport::Dependencies.autoload_paths'
自動テスト
rails new
コマンドで作成した環境にはSeleniumによるE2Eテスト環境が構築されている
インテグレーションテストを実施するためにRspecを採用する
Rspec環境の構築&最初のテスト追加
ドキュメント読みながら構築
- Gemインストール
Gemfileを編集
group :development, :test do
gem 'rspec-rails', '~> 6.0.0'
end
$ bundle install
- Rspec初期化
$ rspec generate rspec:install
- User Modelのテスト追加
$ rails generate rspec:model user
- テスト実行
$ bundle exec rspec
Rubyの学習
rails console
を使ってく
メソッドを補完すると情報が出てきてすごい
String
文字列代入
- 文字列代入は
"#{var_name} is sample"
で表す
出力
改行ありは puts
、改行なしは print
Object
メソッド
Rubyはオブジェクト指向なので基本的に全てのバリューはオブジェクト
返り値が boolean
のメソッドは ?
が末尾につく
name = "numata"
puts name.empty?
# → false
構文
条件分岐
条件分岐は if - elseif - else - end
にて記述
条件文はそのまま記述し、Pythonの :
のような末尾は不要
一文の条件式を記述できる
puts "Hey!" if !name.nil?
# name変数がNilでない場合に実行される
メソッド
def string_message(str = '')
if str.empty?
"It's an empty string!"
else
"The string is nonempty."
end
end
特徴は以下
-
def - end
で定義 - デフォルト引数を取れる
- 最後に評価された値が自動的に
return
される
配列
文字列から生成
split
関数を利用できる
join
すれば逆に文字列生成
要素探索
0始まりの探索可能
最初、2番目、最後は first
, second
, last
メソッドもある
順序入替
sort
, reverse
値があるか確認
include(x)
要素追加
末尾に要素追加は push
もしくは <<
<<
は連続使用可能 ["foo"] << "bar" << "baz"
範囲
0..9
は0~9までの範囲で配列に変換できる
p = (0..9).to_a
puts p
# → [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
範囲で要素探索もできる
p = (0..9).to_a
puts p[0..2]
# → [0, 1, 2]
ブロック
ブロックを使って配列の複雑な処理ができる
ブロック変数は |
で囲う
ブロックの宣言は do - end
を利用
(1..6).each do |number|
puts number * 2
puts "---"
end
# 出力↓
2
---
4
---
6
---
8
---
10
---
12
---
ブロック変数は &
で省略可能
# 以下2行は同じ
(1..3).map { |n| n.next }
(1..3).map(&:next)
ハッシュ
{}
で定義できる
順序は保証されていない
未定義の場合は nil
が返る
name = {"last_name"=>"Hartl", "first_name"=>"Michael"}
シンボル
文字列の代わりにシンボルをキーにできる
name = {:last_name=>"Hartl", :first_name=>"Michael"}
puts name[:last_name]
# → "Hartl"
ハッシュのキーにシンボルを定義するときは以下のようにも定義可能
name = {last_name:"Hartl", first_name:"Michael"}
puts name[:last_name]
# → "Hartl"
ハッシュのfor
値の一覧は each
で取得可能
name = {last_name:"Hartl", first_name:"Michael"}
name.each do |key, value|
puts "Key #{key.inspect} has value #{value.inspect}"
end
# → Key :last_name has value "Hartl"
Key :first_name has value "Michael"
merge
二つのハッシュの結合は merge
メソッドを利用する
クラス
全てはObjectクラスから継承されている
s = "foo"
puts s.class.superclass
# → Object
継承
継承は <
を使う
class Word < String
def palindrome?
self == self.reverse
end
end
s = Word.new("level")
puts s.palindrome?
# → true
インスタンス変数
インスタンス変数は self
メソッド内部でのインスタンス変数のメソッド呼び出しは self
を省略可能
class Word < String
def palindrome?
self == reverse # self.reverse→reverse
end
end
変数を追加する場合はアクセサーを利用する
アクセサーはクラス内部では @
をプリフィックスにする
コンストラクタ( initialize
)内部で初期化できる
class Word < String
attr_accessor :times
def initialize(attributes = {})
@times = attributes[:times]
end
def palindrome?
self == reverse # self.reverse→reverse
end
end
s = Word.new({value:"test",times:3}
puts s.times
# → 3
コンストラクタ
Rubyはコンストラクタに initialize
を利用する
RDB環境の構築
SQLiteからMySQL(Docker)へ移行する
ステップは以下
-
mysql2
Gemをインストール -
database.yaml
を編集 - Docker Compose導入
- データベースの初期化
mysql2
Gemをインストール
$ gem install mysql2 -v 0.5.4
database.yaml
を編集
- adapterを
mysql2
に変更 - host,, username, passwordを環境変数から指定
Docker Compose導入
- MySQLは公式イメージを使用
- volumesに
my.cnf
とデータベースを指定
データベース初期化
$ RAILS_ENV=development rails db:create
$ RAILS_ENV=development rails db:migrate
本番環境の構築
本番環境のAurora Serverlessに対してデータベースを作成、マイグレーションを実行する
今回は簡易的にECS Execを利用
- ECSのタスクロールにSSMポリシーを追加
- ECS Serviceの
enableExecuteCommand
フラグを有効化$ aws ecs update-service --region ap-northeast-1 --cluster [cluster_name] --service [service_name] --enable-execute-command
- タスクを再起動
- ECS Execコマンドを実行
$ aws ecs execute-command --region ap-northeast-1 --cluster [cluster_name] --task [task_id] --container [container_name] --interactive --command "/bin/sh"
モデルの構築
やったことは以下
- モデルのメソッドを使ってみる
- RSpecを使ってバリデーションのテストを追加
モデルのメソッドを使ってみる
作成系
new
はインスタンス初期化のみ
create
はINSERTも実行
参照
find
はIDを指定して検索: ex. find(1)
find_by
は属性を指定して検索: ex. find_by(email: "sample@example.com")
all
は一覧を取得
更新
update
は指定した属性全て更新
update_attribute
は指定した属性のみ更新: ex. user.update_attribute(:name, "Test")
RSpecを使ってバリデーションのテストを追加
ユーザー名の存在チェック
バリデーションはクラスの属性 validates
に追加
RSpecはユーザーを new
で初期化して検証
## Model
class User < ApplicationRecord
validates :name, presence: true
end
## Spec
RSpec.describe User, type: :model do
context 'user validation' do
it "name should be present" do
user = User.new(name: " ", email: "user@example.com")
expect(user.valid?).to be false
end
end
end
RailsのAPIモードの修正
- APIモードを追加してCookie利用のためにミドルウェアをONにするconfig/application.rb
module RailsStarter class Application < Rails::Application config.load_defaults 7.0 config.api_only = true config.middleware.use ActionDispatch::Cookies config.middleware.use ActionDispatch::Session::CookieStore config.middleware.use ActionDispatch::ContentSecurityPolicy::Middleware end end
- Controllerの継承を
ActionController::API
に変更 -
rack-cors
の導入config/initializer/cors.rbRails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '*', headers: :any, methods: [:get, :post, :patch, :put] end end
ログインの追加
- Sessionモデルを追加
- ログイン時にパスワードチェックをして見つけたユーザーIDをセッションに保存する
参考
RubyでGemを入れているのにエラーにいなるケース