はじめに
Rails 6 に追加された新機能を試す第99段。 今回は、 association extension
編です。
Rails 6 では、 define_extensions
で定義される module が model 内の module として定義されるようになっています。
実際の例を見た方がわかりやすいです。
Ruby 2.6.5, Rails 6.0.0 で確認しました。
$ rails --version
Rails 6.0.0
今回は、 Author モデル と Book モデルを定義して、rails console で確認します。
Author は Book を複数持ちます( has_many
)。
Rails プロジェクトを作る
$ rails new rails_sandbox
cd rails_sandbox
Author モデルを作る
name
を持つ Author
モデルを作ります。
$ bin/rails g model Author name
Book モデルを作る
title
, published
, author_id
を持つ Book
モデルを作ります。
$ bin/rails g model Book title published:boolean author:references
Author モデルを修正する
Author モデルを修正します。
2つの has_many
アソシエーションを定義します。
1つ目の has_many
では、 ブロックで ordered
メソッドを定義します。
2つ目の scope published_books
は extending
を使って ordered
メソッドを利用できるようにします。
extending の引数が BooksAssociationExtension
になっていることに注意してください。
class Author < ApplicationRecord
has_many :books do
def ordered
order(:title)
end
end
has_many :published_books, -> { where(published: true).extending(BooksAssociationExtension) }, class_name: 'Book'
end
seed データを作成する
Author と Book の seed データを作成します。
author = Author.create(
{
name: 'Dave Thomas'
}
)
Book.create(
[
{ title: 'Programming Ruby', author: author, published: true },
{ title: 'Pragmatic Programmer', author: author, published: true },
{ title: 'Agile Web Development with Rails 6', author: author, published: false }
]
)
データベースを作成し、 seed データを登録する
$ bin/rails db:create db:migrate db:seed
rails console で確認する
rails console で確認します。
まずは、1つ目の has_many
を確認します。
irb(main):001:0> Author.first.books.ordered
Author Load (0.3ms) SELECT "authors".* FROM "authors" ORDER BY "authors"."id" ASC LIMIT $1 [["LIMIT", 1]]
Book Load (0.3ms) SELECT "books".* FROM "books" WHERE "books"."author_id" = $1 ORDER BY "books"."title" ASC LIMIT $2 [["author_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation [#<Book id: 3, title: "Agile Web Development with Rails 6", published: false, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">, #<Book id: 2, title: "Pragmatic Programmer", published: true, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">, #<Book id: 1, title: "Programming Ruby", published: true, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">]>
2つ目の has_many
を確認します。
irb(main):002:0> Author.first.published_books.ordered
Author Load (0.8ms) SELECT "authors".* FROM "authors" ORDER BY "authors"."id" ASC LIMIT $1 [["LIMIT", 1]]
Book Load (0.8ms) SELECT "books".* FROM "books" WHERE "books"."author_id" = $1 AND "books"."published" = $2 ORDER BY "books"."title" ASC LIMIT $3 [["author_id", 1], ["published", true], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation [#<Book id: 2, title: "Pragmatic Programmer", published: true, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">, #<Book id: 1, title: "Programming Ruby", published: true, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">]>
Author::BooksAssociationExtension
が定義されていることを確認します。
irb(main):003:0> Author::BooksAssociationExtension
=> Author::BooksAssociationExtension
Rails 5 では
Rails 5.2.3 では、 Author::BooksAssociationExtension
ではなく、 AuthorBooksAssociationExtension
が定義されます。
以下のように extending
の引数を AuthorBooksAssociationExtension
と書く必要があります。
class Author < ApplicationRecord
...
has_many :published_books, -> { where(published: true).extending(AuthorBooksAssociationExtension) }, class_name: 'Book'
end
Rails 5.2.3 ではグローバルなモジュールとして定義されるのに対し、Rails 6 では、 Author モデルの中のモジュールとして定義されるので、 Author モデルで利用するときは、Author をつけずに、 BooksAssociationExtension
と簡潔に書くことができるようになっています。
試したソース
試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try099_association_extension