rom-rb / rom

Data mapping and persistence toolkit for Ruby
https://rom-rb.org
MIT License
2.08k stars 161 forks source link

Generic Component API #650

Closed solnic closed 3 years ago

solnic commented 3 years ago

This adds a generic component API which is the culmination of all the previous refactorings/improvements/cleanups. There's still a lot of additional complexity caused by backward-compatiblity but this will be cleaned up and moved to rom/compat eventually.

The important part is that IT WORKS šŸ˜†

Up until now, setting up rom could be a challenge, reasons were plenty and due to various limitations caused by the internals, it was hard to make it better. All the limitations are removed now and the entire system is way, way, WAY more flexible to the point that I can't even believe that it was possible.

Anyhow, the main idea behind rom-rb was always to be a toolkit that consists of a bunch of decoupled components that can be put together. It should be easy to use rom-rb for simple stuff, but it should be also easy to use rom-rb for advanced stuff.

Following this huge goal, this PR enables using rom-rb as if it was "Lego for persistence". You can start with a single class and define everything you need there and because most components can be inferred, this can be automated in 99%.

Examples

UPDATE: this particular API was extracted into a separate branch because this PR would become too big (it's already huge).

Here's an example of defining a custom class that is rom-enabled and it defines a relation with its schema explicitly. We auto_migrate so that the db schema is established automatically for us. Everything else is fully dynamic and automated:

require "rom"
require "rom/sql"

class Repo
  extend ROM(:sql, "sqlite::memory", as: :db)

  relation(:users) do
    schema do
      attribute :id, ROM::Types::Integer
      attribute :name, ROM::Types::String

      primary_key :id
    end
  end

  def create_user(params)
    relations[:users].changeset(:create, params).commit
  end
end

# this...just...works
repo = Repo.new

# and this is really cool
repo.gateways[:default].auto_migrate!(repo.db, inline: true)

# and this is some standard rom stuff, by default no struct mapping but this can be easily enabled
repo.create_user(name: "Jane").inspect
# {:id=>1, :name=>"Jane"}

Eventually, you'll be able to define any component type like that and either use your standalone rom-enabled objects, or provide a single rom setup like you used to.