Ruby meetup ROM
-
Upload
nikita-shilnikov -
Category
Technology
-
view
125 -
download
1
Transcript of Ruby meetup ROM
MeNikita Shilnikov
• github.com/flash-gordon
• Whatever developer
• dry-rb and rom-rb core team member
ROM vs AR builder = SqlBuilder.new <<-SQL UPDATE topic_users tu SET notification_level = CASE WHEN should_track THEN :tracking WHEN should_watch THEN :watching ELSE notification_level END, notifications_reason_id = CASE WHEN should_track THEN null WHEN should_watch THEN :auto_watch_category ELSE notifications_reason_id END FROM ( SELECT tu1.topic_id, tu1.user_id, CASE WHEN cu.user_id IS NULL AND tu1.notification_level = :watching AND tu1.notifications_reason_id = :auto_watch_category THEN true ELSE false END should_track, CASE WHEN cu.user_id IS NOT NULL AND tu1.notification_level in (:regular, :tracking) THEN true ELSE false END should_watch
FROM topic_users tu1 JOIN topics t ON t.id = tu1.topic_id LEFT JOIN category_users cu ON cu.category_id = t.category_id AND cu.user_id = tu1.user_id AND cu.notification_level = :watching /*where2*/ ) as X
/*where*/SQL
ROM vs AR --builder = SqlBuilder.new <<-SQL UPDATE topic_users tu SET notification_level = CASE WHEN should_track THEN :tracking WHEN should_watch THEN :watching ELSE notification_level END, notifications_reason_id = CASE WHEN should_track THEN null WHEN should_watch THEN :auto_watch_category ELSE notifications_reason_id END FROM ( SELECT tu1.topic_id, tu1.user_id, CASE WHEN cu.user_id IS NULL AND tu1.notification_level = :watching AND tu1.notifications_reason_id = :auto_watch_category THEN true ELSE false END should_track, CASE WHEN cu.user_id IS NOT NULL AND tu1.notification_level in (:regular, :tracking) THEN true ELSE false END should_watch
FROM topic_users tu1 JOIN topics t ON t.id = tu1.topic_id LEFT JOIN category_users cu ON cu.category_id = t.category_id AND cu.user_id = tu1.user_id AND cu.notification_level = :watching /*where2*/ ) as X
/*where*/SQL
Gems
• rom – core facili-es
• rom-sql – RDBMS interac-on
• rom-repository – applica-on-level interface
Rela%on
class Users < ROM::Relation[:sql] schema do attribute :id, Types::Serial attribute :name, Types::String endend
Rela%on
class Users < ROM::Relation[:sql] schema(infer: true)
def by_name(name) where(name: name) endend
Command
class CreateUser < ROM::Commands::Create[:sql] relation :users register_as :create result :oneend
Repository
(ROM::Repository::Root.instance_methods - Object.new.methods).size # => 12(ROM::Repository::Root.methods - Class.new.methods).size # => 9
Repository
(ROM::Repository::Root.instance_methods - Object.new.methods).size # => 12(ROM::Repository::Root.methods - Class.new.methods).size # => 9
(ApplicationRecord.instance_methods - Object.new.methods).size(ApplicationRecord.methods - Class.new.methods).size
Repository
(ROM::Repository::Root.instance_methods - Object.new.methods).size # => 12(ROM::Repository::Root.methods - Class.new.methods).size # => 9
(ApplicationRecord.instance_methods - Object.new.methods).size # => 244(ApplicationRecord.methods - Class.new.methods).size # => 467
TOTAL # => 711
Changeset
changeset = repo.changeset(name: 'John')changeset.map(:add_timestamps).to_h# => {:name=>"John",# :created_at=>2017-03-18 16:33:19 +0300,# :updated_at=>2017-03-18 16:33:19 +0300}
repo.create(changeset.map(:add_timestamps))
Changeset
changeset = repo.changeset(user.id, name: 'John Doe')
changeset.diff? # => truechangeset.diff # => {:name=>'John Doe'}
Many containers
rom = ROM.container(:sql, DB_URL)rom_replica = ROM.container(:sql, DB_REPLICA_URL)
repo = UserRepo.new(rom)read_only_repo = UserRepo.new(rom_replica)
Many adapters
rom = ROM.container( default: [:sql, 'postgres://localhost/rom_repository'], cache: [:sql, 'sqlite::memory']) do |c| c.gateways[:cache].create_table :counters do primary_key :id column :user_id, Integer column :type, String column :value, Integer end
end
Many adapters
rom = ROM.container( default: [:sql, 'postgres://localhost/rom_repository'], cache: [:sql, 'sqlite::memory']) do |c| c.gateways[:cache].create_table :counters do primary_key :id column :user_id, Integer column :type, String column :value, Integer end
c.relation(:users) do schema(infer: true) end
end
Many adapters
rom = ROM.container( default: [:sql, 'postgres://localhost/rom_repository'], cache: [:sql, 'sqlite::memory']) do |c| c.gateways[:cache].create_table :counters do primary_key :id column :user_id, Integer column :type, String column :value, Integer end
c.relation(:users) do schema(infer: true) end
c.relation(:counters) do gateway :cache
schema(infer: true)
def for_users(users) where(user_id: users.pluck(:id)) end endend
Many adapters
class UserRepo < ROM::Repository[:users] relations :countersend
repo = UserRepo.new(rom)
Many adapters
repo. users. combine(many: { counters: [repo.counters.for_users, id: :user_id] }). where { id <= 2 }. to_a
=begin
[ #<ROM::Struct[User] id=1 name="Jane" counters=[ #<ROM::Struct[Counter] id=1 user_id=1 type="posts" value=3>, #<ROM::Struct[Counter] id=2 user_id=1 type="comments" value=512> ]>, #<ROM::Struct[User] id=2 name="Joe" counters=[]>]
=end
No N+1!
I, [2017-03-18T19:37:14 #6638] (0.000765s) SELECT "id", "name" FROM "users" WHERE ("id" <= 2) ORDER BY "users"."id"I, [2017-03-18T19:37:14 #6638] (0.000172s) SELECT `id`, `user_id`, `type`, `value` FROM `counters` WHERE (`user_id` IN (1, 2)) ORDER BY `counters`.`id`
PlansROM 4.0 roadmap
• More powerful mapping facili2es
• Cross-adapter associa2ons
• Move a bunch of features from rom-repository to rom-core
• Auto-migra2ons in rom-sql ✨