Skip to content

Instantly share code, notes, and snippets.

@IzumiSy
Created December 15, 2017 04:01
Show Gist options
  • Save IzumiSy/7c345b5f545bc0b9bbf3861399246535 to your computer and use it in GitHub Desktop.
Save IzumiSy/7c345b5f545bc0b9bbf3861399246535 to your computer and use it in GitHub Desktop.
ROM.rb repository example
require "rom"
require "rom/sql"
#
# ## Relationを定義
#
# ROMのRelationは`ROM::Relation[:アダプタ名]`を継承したクラスとして定義する
# associationsブロックの中で`belongs_to`や`has_many`などの関連も定義をする
# ここで定義したRelationは下で出てくるROMの初期化の際にROMへの登録を行う必要がある。
#
module Relations
class Users < ROM::Relation[:sql]
schema(:users) do
# Relationの関連定義はActiveRecordと似ている。
# has_many, belongs_toやhas_many-throughなどがあるのでドキュメントを参照のこと
# http://rom-rb.org/learn/sql/associations/
associations do
has_many :books
end
# schemaブロックの中では関連の定義に加えて以下のように明示的に
# Relationのカラムを定義できる。この場合、プライマリ・キーは
# `Types::Serial`である必要がある
attribute :id, Types::Serial
attribute :name, Types::String
attribute :age, Types::Int
end
# Repositoryの中で呼び出せるクラスメソッドを以下のように定義できる
# ROM.rbのSQLアダプタ(rom-sql)は内部でSequalizeを使っているので
# where句などもその記法に従う
def by_pk(id)
where(id: id)
end
def adult
where{age > 20}
end
end
class Books < ROM::Relation[:sql]
schema(:books) do
associations do
belongs_to :user
end
# booksの場合にはuserに対してbelongs_toで関連を持っているので
# その関連に対して`Types::ForeignKey(:users)`のように明示的に
# 利用する外部キーを指定できる。ない場合には自動で推論される。
attribute :id, Types::Serial
attribute :title, Types::Int
attribute :user_id, Types::ForeignKey(:users)
end
end
end
#
# ## Repositoryを定義
#
# Repositoryからは定義されているRelationを触ることができる
#
module Repositories
class User < ROM::Repository[:users]
# ROM.rbではRepositoryがRelationに対してどのようなアクセスをできるのか
# という点を明確に制限できる。ここでは:createをコマンドとして有効化しているが
# :create以外にも:updateや:deleteがある。
commands :create
# 以下のようにクラスメソッドを定義できる。
# これらのデータはROM::Structによってラップされて返される
# ここではusersのみを読んでいるが、booksも同様にメソッドのなかでアクセスできる
def adults
users.adult
end
# `aggregate`メソッドを使うことによって、Relationで定義されている
# 関連Relationのデータを子要素としてフェッチできる
# `aggregates`を呼ぶ場合には明示的にRelationを指定しなくてもよさそう。
def all
aggregate(:books)
end
def by_id(id)
aggregate(:books).by_pk(id).one
end
end
end
#
# ## ROMにRelationを登録
#
# 上で定義した2つのRelationをROMに登録
#
config = ROM::Configuration.new(:sql, "sqlite::memory")
config.register_relation(Relations::Users)
config.register_relation(Relations::Books)
rom = ROM.container(config)
# SQLiteでテーブルを作るマイグレーションを適用
migration = ROM::SQL.migration(rom) do
change do
create_table(:users) do
primary_key :id
string :name
integer :age
end
create_table(:books) do
primary_key :id
foreign_key :user_id, :users
string :title
end
end
end
gateway = rom.gateways[:default]
migration.apply(gateway.connection, :up)
#
# ## Repositoryを使ってみる
#
# RepositoryはRelationとは異なり、ROMへ登録するのではなく、ROMのインスタンスを
# コンストラクタDIすることでRepositoryのインスタンスを作るという形になる。
#
userRepository = Repositories::User.new(rom)
userRepository.create(name: "Justine", age: 10)
userRepository.create(name: "Jessy", age: 18)
userRepository.create(name: "Michael", age: 23)
# テストのためにBooksテーブルにRelation経由でデータを入れてみる
# (外部キー成約があるので、userレコードを作ったあとじゃないとダメ)
books = rom.relations[:books]
books.insert(title: "Good Book", user_id: 1)
books.insert(title: "Nice Book", user_id: 1)
#
# ### Repositoryからレコードを取得
#
# ここでは`by_id`や`adults`などの、Repositories::Userの中で定義した
# クラスメソッドを呼び出すことができる。Relationの結果が常にハッシュなのに対して
# Repositoryによる取得の返り値はROM::Structという構造体の形になる。
# これはデフォルトでtrueになっている`auto_struct`をfalseに指定することで
# 止めることができる
#
users = userRepository.all
users.each { |user| p user }
# => #<ROM::Struct::User id=1 name="Justine" age=10 books=[
# #<ROM::Struct::Book id=1 title="Good Book" user_id=1>,
# #<ROM::Struct::Book id=2 title="Nice Book" user_id=1>
# ]>
# #<ROM::Struct::User id=2 name="Jessy" age=18 books=[]>
# #<ROM::Struct::User id=3 name="Michael" age=23 books=[]>
single_user = userRepository.by_id(1)
p single_user
# => #<ROM::Struct::User id=1 name="Justine" age=10 books=[
# #<ROM::Struct::Book id=1 title="Good Book" user_id=1>,
# #<ROM::Struct::Book id=2 title="Nice Book" user_id=1>
# ]>
users = userRepository.adults
users.each { |user| p user }
# => #<ROM::Struct::User id=3 name="Michael" age=23>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment