Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

1 year with ROM on production

808 views

Published on

ROM - Ruby Object Mapper

Published in: Software
  • Be the first to comment

1 year with ROM on production

  1. 1. @gotar | @oskarszrajer gotar.info Oskar Szrajer
  2. 2. One year with on production
  3. 3. What is ROM (Ruby Object Mapper) https://github.com/orgs/rom-rb PERSISTENCE & MAPPING TOOLKIT FOR RUBY http://rom-rb.org
  4. 4. Author of ROM Piotr Solnica | @_solnic_ | http://solnic.eu author of ROM, Virtus, Dry-*, Rodakase, ...
  5. 5. Community • dozen of devs • very active Gitter channel https://gitter.im/rom-rb/chat • stable API >1.0v • battle tested on production
  6. 6. History of ROM • successor of DataMapper - aka DataMapper v2 • first commit - Feb 1 2012 • first version - v0.1 - Jun 15 2013 • first public version - v0.6 - Mar 22, 2015 • v1.0 - stable API - Jan 6, 2016
  7. 7. Core ideas • reduced global state to minimum • immutability • mix OO with FP • blazing fast
  8. 8. Core concepts • Configuration • Relations • Commands • Mappers
  9. 9. Configuration require 'rom' config = ROM::Configuration.new( :sql, 'postgres://localhost/rom_repo' ) rom = ROM.container(config) rom.gateways[:default] # #<ROM::SQL::Gateway:0x007fde7a087158>
  10. 10. ROM::Configuration.new( default: [:sql, 'postgres://localhost/db'], legacy: [:sql, 'mysql://localhost/legacy_db'], marketing: [:yesql, 'postgres://localhost/db', path: './sql'], cache: [:redis, 'redis://localhost'], csv: [:csv, File.expand_path('./data.csv', __FILE__], events: [:event_store, '127.0.0.1:1113'] # ... )
  11. 11. Relations class Users < ROM::Relation[:sql] def by_id(id) where(id: id) end end rom.relation(:users).by_id(1).to_a # [{:id=>1, :name=>"Jane"}]
  12. 12. Everything is lazy (callable) rom.relation(:users).by_id(1) #<Db::Relations::Users dataset=#<Sequel::Postgres::Dataset: "SELECT "id", ... rom.relation(:users).by_id(1).call # [{:id=>1, :name=>"Jane"}] rom.relation(:users).by_id(1).only(:id).to_a # [{:id=>1}] rom.relation(:users).by_id(1).only(:id).one # {:id=>1}
  13. 13. Commands class Create < ROM::Commands::Create[:sql] relation :users register_as :create result :one end create_user = rom.command(:users).create create_user.call(name: 'Jack') # {id: 2, name: 'Jack'}
  14. 14. class Create < ROM::Commands::Create[:sql] relation :users register_as :create input NewUserParams validator UserValidator result :one end
  15. 15. class AuthorWithBooks < ROM::Commands::Create[:sql] #... end create_authors_with_books = rom.command(:authors) do |authors| authors.create(:books) end create_authors_with_books[authors: evaluated_authors] # {:id=>1, :code=>"jd001", :name=>"Jane Doe", :email=>"jane@doe.org"} # {:id=>1, :author_id=>1, :title=>"First", :published_on=>#<Date: 2010-10-10 ((2455480j, 0s,0n),+0s,2299161j)>} # {:id=>2, :author_id=>1, :title=>"Second", :published_on=>#<Date: 2011-11-11 ((2455877j, 0s,0n),+0s,2299161j)>} # {:id=>3, :author_id=>1, :title=>"Third", :published_on=>#<Date: 2012-12-12 ((2456274j, 0s,0n),+0s,2299161j)>}
  16. 16. Mappers class UsersMapper < ROM::Mapper relation :users register_as :entity attribute :id attribute :name attribute :age do rand(18..90) end end
  17. 17. users = rom.relation(:users).as(:entity) users.call [ { id: 1, name: "Jane", age: 22 }, { id: 2, name: "Jack", age: 64 } ]
  18. 18. Pipelines (everything that responds to #call can be a mapper) only_name = -> input { input.map{|u| u[:name]} } upcase = -> input { input.map{|x| x.upcase} } users = rom.relation(:users) result = users >> only_name >> upcase result.to_a > ["JANE", "JACK"]
  19. 19. Domain data types & Repositories class User < Dry::Types::Value attribute :name, Types::Strict::String end class UserRepo < ROM::Repository relation :users def all users.select(:name).as(User).to_a end end
  20. 20. user_repo = UserRepo.new(rom) user_repo.all > [#<User name="Jane">, #<User name="Jack">]
  21. 21. My story
  22. 22. My story
  23. 23. Both project use similar technologies: • Roda (as HTTP layer) • ROM • Yaks (JSON API)
  24. 24. Request cycle ParamsObject >> Validator >> ROM >> Mapper(s) >> JSON API Mapper Mapping & coercion layer Mapping & coercion layer combine (or change) data convert data to json api format CQRS
  25. 25. combining data class UserRepository < ROM::Repository::Base relations :users, :tasks def with_tasks(id) users.by_id(id).combine_children(many: tasks) end end user_repo.with_tasks.to_a # [#<ROM::Struct[User] id=1 name="Jane" tasks=[#<ROM::Struct[Task] id=2 user_id=1 title="Jane Task">]>, #<ROM::Struct[User] id=2 name="Jack" tasks=[#<ROM::Struct[Task] id=1 user_id=2 title="Jack Task">]>]
  26. 26. pros • separating persistence from application & encouraging that boundary • ability to choose appropriate storage (SQL, NoSQL, ...), but keeping the same API • possibility to pull from different data sources, and combine everything together
  27. 27. pros • easy way to map (manipulate, coerce) data • freedom • community
  28. 28. cons • commands (right now repository do not support them, force to use very low level API, should be done before v2) • hard to learn (many new concepts) • lack of documentation (community will help you) (currently community works to fix it, and there is always someone sitting on Gitter to help you)
  29. 29. Worth it?
  30. 30. Q&A @gotar | @oskarszrajer gotar.info
  31. 31. Thx @gotar | @oskarszrajer gotar.info

×