Closed12

Ruby on Railsチュートリアル

Keisuke NumataKeisuke Numata

https://railstutorial.jp/
チュートリアルをもとに試してみる
自分でカスタマイズするために、以下の内容をチュートリアルから変えている

  • デプロイ環境はHerokuではなくAWS
  • RESTful APIとして構築
Keisuke NumataKeisuke Numata

JSON Serializerの選定

複数記事を参考にして以下から選定する

利用実績が多いものなら jbuilder
パフォーマンスがよく、安定してメンテされているなら blueprinter
新しいがパフォーマンス、型付けの観点なら alba

今回はチュートリアルなので alba を採用する

参考になった資料

Keisuke NumataKeisuke Numata

最初のモデルの作成

以下のコマンドでUsersに必要なリソースを作成する

$ rails generate scaffold User name:string email:string

追加されるのは以下

  • Controller
  • View
  • Helper
  • Model
  • Migration
  • Test
Keisuke NumataKeisuke Numata

Albaの導入

Gemから jbuilder を削除する

  • bundle exec gem uninstall jbuilder
  • Gemファイルから jbuilder を削除
  • bundle install

Albaをインストール

  • Gemファイルに alba を追加
  • bundle install
Keisuke NumataKeisuke Numata

Railsの自動インポート

概要

Gemやパッケージのインポートは Zeitwerk を利用している
config.autoload_paths設定によりファイル名と同様の変数名が定義されていれば自動読み込みされる
例:users_controller.rbUsersController

インポートされているパスの確認

$ rails r 'puts ActiveSupport::Dependencies.autoload_paths' 
Keisuke NumataKeisuke Numata

自動テスト

rails new コマンドで作成した環境にはSeleniumによるE2Eテスト環境が構築されている
インテグレーションテストを実施するためにRspecを採用する

Rspec環境の構築&最初のテスト追加

https://github.com/rspec/rspec-rails
ドキュメント読みながら構築

  1. Gemインストール
    Gemfileを編集
group :development, :test do
  gem 'rspec-rails', '~> 6.0.0'
end
$ bundle install
  1. Rspec初期化
$ rspec generate rspec:install
  1. User Modelのテスト追加
$ rails generate rspec:model user
  1. テスト実行
$ bundle exec rspec
Keisuke NumataKeisuke Numata

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 を利用する

Keisuke NumataKeisuke Numata

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"
    
Keisuke NumataKeisuke Numata

モデルの構築

やったことは以下

  • モデルのメソッドを使ってみる
  • 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
Keisuke NumataKeisuke Numata

RailsのAPIモードの修正

https://railsguides.jp/api_app.html

  • 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.rb
    Rails.application.config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins '*'
        resource '*', headers: :any, methods: [:get, :post, :patch, :put]
      end
    end
    
Keisuke NumataKeisuke Numata

ログインの追加

  • Sessionモデルを追加
  • ログイン時にパスワードチェックをして見つけたユーザーIDをセッションに保存する
このスクラップは2022/08/24にクローズされました