LoginSignup
0
0

More than 5 years have passed since last update.

一からはじめるRoRoBoW(後編)

Last updated at Posted at 2018-11-14

Ruby on Railsとは

設計思想

  • 編集中

Ruby on Railsリファレンス

ガイド

モデル

Active Record の基礎

Active Record マイグレーション

マイグレーション確認
rails db:migrate:status

 up     20181001000000 Create Hogehoge Tables
 up     20181001000010 Add Columns to Hages
 down   20181001000020 Add Columns to Fugas

# up      コミットが完了したもの
# down        コミットしていないもの 
マイグレーションロールバック
# 直前のマイグレーションコミットをロールバックする
rails db:rollback

# %n%つ前のマイグレーションバージョンにロールバックする
rails db:rollback STEP=%n%
マイグレーションロールバック+コミット
# %n%つ前のマイグレーションバージョンにロールバックし、再度マイグレーションコミットする
rails db:migrate:redo STEP=%n%
# 内部処理についてはマイグレーションコミットの項を参照
マイグレーションコミット
# %yyyyMMddHHmmss%接頭辞を持つマイグレーションバージョンのスクリプトをコミットする
rails db:migrate VERSION=%yyyyMMddHHmmss%

# 実行していないマイグレーションバージョンのスクリプトをコミットする
rails db:migrate

db:migrate(マイグレーションコミット)が実行されると、内部処理でdb:schema:dumpタスクも実行される
db:schema:dumpタスクはdb/schema.rbスキーマファイルを更新し、スキーマがデータベースの構造に一致する

マイグレーションスクリプト
  • クラス・マイグレーションファイル名の命名規則
# カラム追加
Add%Column_Camel_Case%To%Table_Name_Camel_Case%

# 例
rails g migration AddMailAddressToUsers #=>メールアドレスをユーザー情報に追加

# カラム削除
Remove%Column_Camel_Case%From%Table_Name_Camel_Case%

# 例
rails g migration RemoveHogeNameFromUsers #=>hoge_nameというカラムをユーザー情報からドロップする

カラム名と種類を続けて記載する
上記の命名規則でマイグレーションスクリプト内に適切なadd_column文/remove_column文が自動生成される

# 例
rails g migration AddMailAddressToUsers mail_address:string

上記で自動生成されるソース

YYYYMMDDHHMMSS_add_mail_address_to_users.rb
class AddMailAddressToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :mail_address, :string
  end
end

マイグレーションスクリプトの生成

# index    インデックスを追加する
rails g[enerate] migration Add%Column_Camel_Case%To%Table_Name_Camel_Case% %column_snake_case%:%type%[:index]

上記で自動生成されるソース

YYYYMMDDHHMMSS_add_mail_address_to_users.rb
class AddMailAddressToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :mail_address, :string
    add_index :users, :mail_address
  end
end
自作マイグレーションの為のメソッド
# upメソッド
class AddDeliveryTypesToOrders < ActiveRecord::Migration[5.1]
  def up
    add_reference :orders, :user_delivery, foreign_key: true, optional: true
  end
end

# changeの代用メソッド

# downメソッド
class AddDeliveryTypesToOrders < ActiveRecord::Migration[5.1]
  def down
    remove_foreign_key :orders, column: :user_delivery_id
    remove_index :orders, column: :user_delivery_id
    remove_reference :orders, :user_delivery
    remove_column :orders, :delivery_type
  end
end

# changeの代用メソッド。downメソッドにはupメソッドによって行われた変換を逆転する方法を記述する
# upの後にdownを実行した場合、スキーマが変更されないようにする必要がある

# changeメソッド
class AddMailAddressToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :mail_address, :string
  end
end
自作マイグレーションのメソッド内で定義可能なメソッド
# add_column
# add_foreign_key
# add_index
# add_reference
# add_timestamps

# change_column_default (:fromと:toの指定は省略できない)
# change_column_null

# create_join_table
# create_table

# change_table
change_table :products do |t|
  t.remove :description, :name
  t.string :part_number
  t.index :part_number
  t.rename :upccode, :upc_code
end
# change、change_default、removeメソッドが内部実行されなければロールバック可能

# disable_extension

# drop_join_table
# drop_table (ブロックを渡さなければならない)

# enable_extension

# remove_column(型を指定しなければならない)
# remove_foreign_key(2番目のテーブルを指定しなければならない)
# remove_index
# remove_reference
# remove_timestamps

# rename_column
# rename_index
# rename_table
フィールド
  • ActiveRecordインスタンスで使用されるカラム名
    • created_at
    • updated_at
    • lock_version
    • type
    • 関連付け名_type
    • テーブル名_count
# 文字列型
string
text

# 数値型
integer
decimal
boolean

# バイト型
binary

# 日付型
timestamps

# 関連付け
reference

# 制約・インデックス
index

# limit         string/text/binary/integerフィールドの最大サイズを指定
# precision     
# scale         decimalフィールド
# polymorphic   
# null          
# default       
# index         
# comment       
# * nullとdefaultはコマンドラインで指定不可
複雑な処理の為のメソッド
  • reversibleメソッド

    • up/downメソッド以外のメソッドを使用したり、マイグレーションが複雑化したことによってup/downメソッドで逆転させられない場合
    • up/downメソッドの直後に何かしらの作業をしたい場合
    • upブロック
    • 正方向のマイグレーションスクリプトがコミットされたタイミングで実行される(upメソッド)
    • downブロック
    • 逆方向のマイグレーションスクリプトがコミット(=ロールバック)されたタイミングで実行される(downメソッド)
    • DBのデータ遡及にも応用可能
YYYYMMDDHHMISS_add_site_url_to_users.rb
# テーブルの制約を付ける
class AddSiteUrlToUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :distributors do |t|
      t.string :zipcode
    end

    reversible do |dir|
      dir.up do
        # CHECK制約を追加
        execute <<-SQL
          ALTER TABLE distributors
            ADD CONSTRAINT zipchk
              CHECK (char_length(zipcode) = 5) NO INHERIT;
        SQL
      end
      dir.down do
        execute <<-SQL
          ALTER TABLE distributors
            DROP CONSTRAINT zipchk
        SQL
      end
    end

    add_column :users, :home_page_url, :string
    rename_column :users, :email, :email_address
  end
end
YYYYMMDDHHMISS_add_delivery_types_to_orders.rb
# 遡及
# frozen_string_literal: true
class AddDeliveryTypesToOrders < ActiveRecord::Migration[5.1]

  class AddDeliveryTypes < ActiveRecord::Base
    self.table_name = :orders
    belongs_to :user_delivery, class_name: 'UserDelivery'
  end

  def up
    # remove_reference :orders, :user_delivery
    add_reference :orders, :user_delivery, foreign_key: true, optional: true

    # データ遡及
    reversible do |dir|
      dir.up do
        AddDeliveryTypes.reset_column_information
        AddDeliveryTypes.find_each do |delivery_type|
          fail 'Illegal argument user_delivery' unless order.user_delivery.present?
          delivery_type.update!(delivery_type: order.user_delivery.delivery_type)
        end
      end
      # downでは列削除なので遡及不要
    end
  end

  def down
    remove_foreign_key :orders, column: :user_delivery_id
    remove_index :orders, column: :user_delivery_id
    remove_reference :orders, :user_delivery
    remove_column :orders, :delivery_type
  end
end
  • Migration毎に専用のModelを用意する
    • 「マイグレーションファイルは後で手を入れない」のでマイグレーションスクリプト内ではマイグレーション外のコードを利用しない
    • マイグレーションスクリプト内ではそのファイル内でMigrationを完結させる
    • 特にModelを使ってデータの移行を行う場合は注意が必要
  • Modelを使う前には必ずreset_column_informationメソッドを呼ぶ
    • ActiveRecordはテーブルのカラム情報をキャッシュしている
    • キャッシュはModel毎ではなく、テーブル毎のようであるので、Migration内で完結するModelを用意していてもキャッシュしている可能性が高い
  • レコード数の多いテーブルに対してはall.each do |hoge| %block statement% endではなく、find_each do |hoge| %block statement% endを使用する
    • all.eachは全レコードを一回でSELECTしてくる
    • find_eachはデフォルトでは1000件ずつSELECTする
  • 登録・更新・削除メソッドでは!を使用する
    • save / create / update / removeなどではエラーを握りつぶすので注意
  • Migrationのassertionでデータ妥当性を担保する
    • fail 'Illegal argument user_delivery' unless order.user_delivery.present?
  • up / downコマンドのテストは欠かさず実行する
  • Rails で信頼性の高い Migration を書くには
トラブルシューティング
db/migrate/20181001000001_add_user_id_to_deliveries.rb
class AddUserIdToDeliveries < ActiveRecord::Migration[5.1]

  class AddUserId < ActiveRecord::Base
    self.table_name = :deliveries
    belongs_to :order, class_name: 'Order'
  end

  def up
    # remove_reference :deliveries, :user
    add_reference :deliveries, :user, foreign_key: true, optional: true

    # データ遡及
    reversible do |dir|
      dir.up do
        AddUserId.reset_column_information
        AddUserId.find_each do |delivery|
          delivery.update(user_id: delivery.order.user_id)
        end
      end
    end
  end

  def down
    remove_foreign_key :deliveries, column: :user_id
    remove_index :deliveries, column: :user_id
    remove_reference :deliveries, :user
  end

  # 失敗した時用
  # def up
  #   remove_foreign_key :deliveries, column: :user_id
  #   remove_index :deliveries, column: :user_id
  #   remove_reference :deliveries, :user
  # end

  # def down
  # end
end

downしている場合

#失敗した時用のup/downをアンコメントしてupを実行する
rails db:migrate # ここから
rails db:migrate:down VERSION=20181001000001
# イグレーションスクリプトを修正する
# 失敗した時用のup/downをコメントアウト ここまで
# 成功するまでここからここまでを繰り返す

upしている場合

恐らく失敗しているのでupしている事はないかと思われる

Active Record バリデーション

ビルトイン関数
presence: true
length: { maximum: 255 }

Active Record コールバック

  • インスタンス化

    • after_initialize
    • Active Recordのinitializeメソッドを直接オーバーライドしないで済む
  • 検索

    • after_find
    • Active Recordがデータベースからレコードを1つ読み込むたび(findメソッド実行時ではない)
  • 新規作成

    • before_validation
    • after_validation
    • before_save
    • around_save
    • before_create
    • around_create
    • after_create
    • after_save
  • 更新

    • before_validation
    • after_validation
    • before_save
    • around_save
    • before_update
    • around_update
    • after_update
    • after_save
    • after_touch
    • touchメソッド(update_atを更新)実行後
    • ActiveRecord.touch (:column_symbol)
    • belongs_toとの併用可能(アソシエーション側のafter_touchコールバックも実行される)
active_record_after_touch.rb
class Employee < ApplicationRecord
  belongs_to :company, touch: true
  after_touch do
    puts 'Employeeモデルにタッチされました'
  end
end

class Company < ApplicationRecord
  has_many :employees
  after_touch :log_when_employees_or_company_touched

  private
  def log_when_employees_or_company_touched
    puts 'Employee/Companyにタッチされました'
  end
end

>> @employee = Employee.last
=> #<Employee id: 1, company_id: 1, created_at: "2013-11-25 17:04:22", updated_at: "2013-11-25 17:05:05">

# @employee.company.touchをトリガーする
>> @employee.touch
Employeeにタッチされました
Employee/Companyにタッチされました
=> true
  • 削除
    • before_destroy
    • dependent: :destroyよりも前に配置
    • dependent: :destroyによって削除されるよりも前にbefore_destroyコールバックが実行される必要がある
    • around_destroy
    • after_destroy
    • dependent: :destroyのオプションされたアソシーエションでコールバックが実行される
active_record_after_destroy.rb
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
end

class Post < ApplicationRecord
  after_destroy :log_destroy_action

  def log_destroy_action
    puts 'Post destroyed'
  end
end

>> user = User.first
=> #<User id: 1>
>> user.posts.create!
=> #<Post id: 1, user_id: 1>
>> user.destroy
Post destroyed
=> #<User id: 1>
  • コールバックしない更新

    • decrement
    • decrement_counter
    • increment
    • increment_counter
    • update_column
    • update_columns
    • update_all
    • update_counters
  • コールバックしない削除

    • delete
    • delete_all

toggle

Active Record の関連付け

Active Record クエリインターフェイス

Active Record と PostgreSQL

Active Model の基礎

ビュー

Action View の概要

レイアウトとレンダリング

Action View フォームヘルパー

コントローラ

Action Controller の概要

スコープ変数・定数

flash[:notice]のワナ?
Railsのflashをクリアする

flash
ビルトイン関数
redirect_to
render [%render_template_name:string%|%action_controller_method:symbol%] [template: %render_template_name:string%|%action_controller_method:symbol%]? [file: %file_path:string%]? [inline: %inline_text:string%]?

# render_template_name 同一コントローラのテンプレート名('index'や'update')、他コントローラのテンプレート名("products/show")など
# action_controller_method コントローラの返却するContent-type(text xml json nothingなど)
# template: 上記を明示的にするオプション(付けなくても良い)
# file: ファイルであることを明示的にするオプション
# inline: テンプレートを使用せず、HTMLパーツなどを出力する場合に使用する
# type: ビルダー(ERBビルダなど)を指定。指定しなければerb形式
# plain: "OK"

header
  • フィルタ
# 実行順序ごと
prepend_around_action
prepend_before_action
before_action
around_action
append_before_action
append_around_action
append_after_action
after_action
prepend_after_action
after_filter # ※Rails 5から非推奨
before_filter # ※Rails 5から非推奨

Rails ルーティング

ノウハウ

ActiveRecord

ポリモーフィック

ポリモフィックリレーションとは

  • ポリモフィックリレーションがどのようなものであるかを以下の図.textを用いて説明する
    • コメントはどのようなページでも同じような項目となるため、コメントテーブルとして切り出す
    • コメントは複数投稿することができる
    • コメントテーブルは参照先となるテーブルをArticleかCaseのどちらかをもつようにする
    • このような関連をポリモフィック関連という
図.text
[記事(Article)]
id
string title
string context
string image_url

            [コメント(Comment)]
                id
                string title
                string context
                integer good
                integer bad
                integer reply_id
                string comment_type

[事例(Case)]
    id
    string title
    string context
    string image_url

ActiveModelでの実装方法

  • Generator
    • rails g model case title:string context:text image_url:string
    • rails g model article title:string context:text image_url:string
    • rails g model comment title:string context:text good:integer bad:integer reply_id:integer commentable_id:integer commentable_type:string
  • Migration File
    ```yyyymmddhhMMss_create_comments.rb
    class CreateComments < ActiveRecord::Migration
    def change
    create_table :comments do |t|
    t.string :title
    t.text :context
    t.integer :good
    t.integer :bad
    t.integer :reply_id
    t.integer :commentable_id
    t.string :commentable_type

    t.timestamps
    end

    add_index :comments, [:commentable_id, :commentable_type]
    end
    end
    ```

  • マイグレーション

    • rake db:migrate
  • アソシエーション設定

    • has_manyとbelongs_toを追加
      ```app/models/comment.rb

class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end


```app/models/event.rb
class Event < ActiveRecord::Base
  has_many :comments, as: :commentable
end
app/models/article.rb
class Article < ActiveRecord::Base
  has_many :comments, as: :commentable
end

Features

RSpec

Factory_bot

Gemfile
group :development, :test do
  gem 'rspec-rails'
  gem "factory_bot_rails"
end
bundle exec rails generate rspec:install
  • リファレンス

データ定義

シグネチャ 仕組みなど サンプル
FactoryBot.define do %block% end Factory botのデータ定義を宣言
factory :%model_name% do %block% end データ定義を宣言した内部でどのモデルのデータを作成するか宣言する部分(実質ここで実装)

成する | factory_bot_create_list.rb |
| create_list(:%model_name%, %create_count%) | 指定のcount分、レコードを作成する | factory_bot_create_list.rb |

レシーブ

シグネチャ 仕組みなど サンプル
create(:%model_name%) DBレコード作成 factory_bot_create.rb
build(:%model_name%) モデルのオブジェクト作成(DB未登録) factory_bot_build.rb
create_list(:%model_name%, %create_count%) 指定のcount分、レコードを作成する factory_bot_create_list.rb

spec内での呼び出し

before do 
    @user = build(:user)
end

当該スペックコードを実行すると自動でオブジェクトが生成される

terminal
rspec spec/acceptance/api/v1/alerts_spec.rb:22

リファレンス

テーブル

機能 仕組み・固有機能 名称 シグネチャ 仕組みなど サンプル
ActionController フィルタ skip_before_action - 特定のアクションでフィルタを止めることができる
before_actionフィルタに対する停止
action_controller_skip_before_action.rb
ActionController フィルタ skip_after_action - 特定のアクションでフィルタを止めることができる
after_actionフィルタに対する停止
action_controller_skip_after_action.rb
ActionController フィルタ prepend_around_action - フィルタ
フィルタ起動順:1
action_controller_prepend_around_action.rb
ActionController フィルタ prepend_before_action - フィルタ
フィルタ起動順:2
action_controller_prepend_before_action.rb
ActionController フィルタ before_action - アクション実行前のフィルタ
フィルタ起動順:3
action_controller_before_action.rb
ActionController フィルタ around_action - アクション実行時のフィルタ
フィルタ起動順:4
action_controller_around_action.rb
ActionController フィルタ append_before_action - フィルタ
フィルタ起動順:5
action_controller_append_before_action.rb
ActionController フィルタ append_around_action - フィルタ
フィルタ起動順:6
action_controller_append_around_action.rb
ActionController フィルタ append_after_action - フィルタ
フィルタ起動順:7
action_controller_append_after_action.rb
ActionController フィルタ after_action - アクション実行後のフィルタ
フィルタ起動順:8
action_controller_after_action.rb
ActionController フィルタ prepend_after_action - フィルタ
フィルタ起動順:9
action_controller_prepend_after_action.rb
ActiveModel 検索 none - 空のモデルオブジェクトを取得 active_model_none.rb
ActiveModel 変換 pluck ActiveRecord_Relation.pluck(symbol, [symbol]... 指定のカラムのみの配列を取得
*但し、戻り値がActiveRecord_Relationクラスを継承したオブジェクトかArrayクラスのオブジェクト
active_model_pluck.rb
ActiveModel 検索 without_soft_destroyed - kakurenbo-putiによる論理削除 active_model_without_soft_destroyed.rb
ActiveSupport 変換 classify - テーブル名からモデルクラス名へ変換 active_support_classify.rb

サンプルコード

action_controller_skip_before_action.rb
class LoginsController < ApplicationController
  skip_before_action :require_login, only: [:new, :create]
end
# LoginsControllerのnewアクションとcreateアクションをこれまでどおり認証不要にすることができる
action_controller_skip_after_action.rb
class LoginsController < ApplicationController
  skip_after_action :verify_authorized, if: proc { ("Api::V1::" + "#{controller_name}_loyalty".camelize).safe_constantize.present? }, only: [:new, :create]
end
# LoginsControllerのnewアクションとcreateアクションであるファイルが存在する場合のみ認証不要にすることができる
active_model_none.rb
User.none # => []
active_model_pluck.rb
User.all
[#<User:0x000000000785f5c8
  id: 3,
  prefecture_id: nil,
  name: "ホゲホゲ",
  address: nil,
  email: "hogehoge+1@hoge.com",
  password_digest: "$2a$10$ArMoILf5lSLh28C/aLJYtOLYtIbK6GGrbCUF9oxkGLeazBnakVDoe",
  firebase_cloud_messaging_token: "your_devise_token",
  gender: nil,
  birthday: nil,
  created_at: Tue, 09 Oct 2018 04:10:45 UTC +00:00,
  updated_at: Tue, 09 Oct 2018 04:11:09 UTC +00:00,
  agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
]
User.all.pluck(:email) #=> ["hogehoge+1@hoge.com"]

# allメソッドは対応
Shop.all.class
=> Shop::ActiveRecord_Relation
Shop.all.pluck(:id)
   (2.1ms)  SELECT `shops`.`id` FROM `shops`
=> [3]
# find_by_sqlは対応
Shop.find_by_sql("select * from shops where name= 'コマドリぃむ'").class
  Shop Load (1.3ms)  select * from shops where name= 'コマドリぃむ'
=> Array
Shop.find_by_sql("select * from shops where name= 'コマドリぃむ'").pluck(:name)
  Shop Load (3.1ms)  select * from shops where name= 'コマドリぃむ'
=> ["コマドリぃむ"]
# whereは対応
Shop.where(name: 'コマドリぃむ').class
=> Shop::ActiveRecord_Relation
Shop.where(name: 'コマドリぃむ').pluck(:name)
   (0.8ms)  SELECT `shops`.`name` FROM `shops` WHERE `shops`.`name` = 'コマドリぃむ'
=> ["コマドリぃむ"]
# find_by_columnは非対応
Shop.find_by_name('コマドリぃむ').class
  Shop Load (1.2ms)  SELECT  `shops`.* FROM `shops` WHERE `shops`.`name` = 'コマドリぃむ' LIMIT 1
=> Shop(id: integer, prefecture_id: integer, payday_type: string, payday_id: integer, started_at: datetime, remind_day: integer, shift_cutoff_date_type: string, shift_cutoff_date_id: integer, work_cutoff_date_type: string, work_cutoff_date_id: integer, name: string, address: string, cast_code: string, extra_wage_pay_rate: decimal, deadline_to_submit_shift: integer, minimum_shift_registered_days: integer, minimum_working_minutes: integer, dummy_flag: boolean, latitude: decimal, longitude: decimal, created_at: datetime, updated_at: datetime, status: integer, closed: boolean, pay_in_month: integer, calc_extra: boolean, extra_rate: decimal, calc_midnight: boolean, midnight_rate: decimal, auto_rest_flag: boolean, write_rest_permission: integer)
Shop.find_by_name('コマドリぃむ').pluck(:name)
  Shop Load (1.0ms)  SELECT  `shops`.* FROM `shops` WHERE `shops`.`name` = 'コマドリぃむ' LIMIT 1
NoMethodError: undefined method `pluck' for #<Shop:0x0000000008d94038>
from /usr/local/bundle/gems/activemodel-5.1.4/lib/active_model/attribute_methods.rb:432:in `method_missing'
# find_byは非対応
[37] pry(main)> res = Shop.find_by name: 'コマドリぃむ'; res.class
  Shop Load (0.9ms)  SELECT  `shops`.* FROM `shops` WHERE `shops`.`name` = 'コマドリぃむ' LIMIT 1
=> Shop(id: integer, prefecture_id: integer, payday_type: string, payday_id: integer, started_at: datetime, remind_day: integer, shift_cutoff_date_type: string, shift_cutoff_date_id: integer, work_cutoff_date_type: string, work_cutoff_date_id: integer, name: string, address: string, cast_code: string, extra_wage_pay_rate: decimal, deadline_to_submit_shift: integer, minimum_shift_registered_days: integer, minimum_working_minutes: integer, dummy_flag: boolean, latitude: decimal, longitude: decimal, created_at: datetime, updated_at: datetime, status: integer, closed: boolean, pay_in_month: integer, calc_extra: boolean, extra_rate: decimal, calc_midnight: boolean, midnight_rate: decimal, auto_rest_flag: boolean, write_rest_permission: integer)
res.pluck(:name)
NoMethodError: undefined method `pluck' for #<Shop:0x0000000008dfc7c8>
from /usr/local/bundle/gems/activemodel-5.1.4/lib/active_model/attribute_methods.rb:432:in `method_missing'

active_model_without_soft_destroyed.rb
class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable, :timeoutable
  soft_deletable

  def self.hoge(hoge_conditions)
    without_soft_destroyed.where(email: hoge_conditions[:email]).first
  end
end

マイグレーション時にrails g migration AddSoftDestroyedAtToUser soft_destroyed_at:datetime:indexのようにsoft_destroyed_atを指定
ActiveModelクラスにsoft_deletableメソッドを実装しておきます
論理削除対象を取得しないようにwithout_soft_destroyedメソッドをコールする

active_support_classify.rb

コードハック

ActiveRecordの絞り込み

  • Object.send + ActiveRecord scope
app/models/hogehoge.rb
Hogehoge < ApplicationRecord
    belongs_to :hage

  scope :since, -> since_at { where('started_at >= ?', since_at) }
  scope :till, -> till_at { where('ended_at <= ?', till_at) }
end
app/contlloers/baka/hogehoges_controller.rb
module Baka
  class HogehogesController < ApplicationController

  def resource_collection
    scope = Hogehoge
    scope = scope.send(:since, params[:since]) if params[:since].present? # Object.sendでレシーバーのメソッドを実行する(シンボル指定)
    scope = scope.send(:till, params[:till]) if params[:till].present?    # Object.sendでレシーバーのメソッドを実行する(シンボル指定)
    scope = scope.order(:user_id)
    scope = scope.group(:user_id)
    scope
  end

  def index
    render json: current_resources, each_serializer: HogehogeSerializer, include: include_resources, root: root_elements_name, status: :ok
  end

  private(*delegate(:model_class_name, :include_resources, :resource_collection, to: :class))
end
app/serializers/hogehoge_serializer.rb
class HogehogeSerializer < ApplicationSerializer
  attributes :user_id,
             :detail

  def detail
    return unless object.user_groups
    ActiveModel::Serializer::CollectionSerializer.new(object.user_groups, serializer: HogehogeUserGroupSerializer)
  end

  class HogehogeUserGroupSerializer < ApplicationSerializer
    attributes :post_address,
               :post_date,
               :tax,
               :send_amount,
               :amount
  end
end
app/models/hogehoge.rb
class Hogehoge < ApplicationRecord
  def user_groups
    Hogehoge.select(:post_address, :post_date, :tax, :send_amount :amount).where('user_id = ?', user_id)
    # Alert.where('employee_id = ?', employee_id)
    # Alert.find_by(employee_id: employee_id)
  end
end
spec/factories/hogehoge.rb
FactoryBot.define do
  factory :hogehoge do
    zip_code { %w[100-0011 100-0001 140-0001 ...略... 999-9999].sample }
    prefecture { %w[Hokkaido Aomori Iwate ...略... Okinawa].sample }
    city { %w[Mito Ichinohe Sinagawa].sample }
    address { %w[Hogehoge Hagehage Fugafuga].sample }
    delivery_at 6.days.ago.iso8601(3)

    after :build do |hogehoge|
      hogehoge.order ||= build(:order)
    end
  end
end
spec/acceptance/hogehoge_spec.rb
require 'rails_helper'
require 'rspec_api_documentation/dsl'

resource 'Hogehoges (何か)' do
  include_context 'Common factories'

  header 'Content-Type', 'application/json'

  before(:each) do
    FactoryBot.create(:user, email: 'test@example.com', name: 'テストユーザー', password: 'testtest')
    FactoryBot.create(:shop, name: '何か確認用店舗', closed_type: false) do |s|
      FactoryBot.create(:item, name: '何か商品', category: Category.hage, size: 'width:100,height:20,color:cyan', unit_price: 15_000)
    end
    FactoryBot.create_list(:hogehoge, 2, user: User.first)
  end
  get '/api/hogehoges' do
    header 'Authorization', :token

    let(:token) { jwt_token(user: employee) }

    parameter :since, '検索開始期間'
    parameter :till, '検索終了期間'

    let(:since) { 7.days.ago.iso8601(3) }
    let(:till) { 7.days.from_now.iso8601(3) }

    before(:each) do
      do_request
    end

    example '01. 何かの一覧' do
      puts response_body
      expect(status).to eq 200
    end
  end
end
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0