RSpec.describe 'Using changesets' do include_context 'database' include_context 'relations' before do module Test class User < Dry::Struct attribute :id, Dry::Types['strict.int'] attribute :name, Dry::Types['strict.string'] end end configuration.mappers do define(:users) do model Test::User register_as :user end end end describe 'Create' do subject(:repo) do Class.new(ROM::Repository[:users]) { relations :books, :posts commands :create, update: :by_pk }.new(rom) end let(:create_changeset) do Class.new(ROM::Changeset::Create) end let(:add_book_changeset) do Class.new(ROM::Changeset::Create[:books]) end let(:update_changeset) do Class.new(ROM::Changeset::Update) end it 'can be passed to a command' do changeset = repo.changeset(name: "Jane Doe") command = repo.command(:create, repo.users) result = command.(changeset) expect(result.id).to_not be(nil) expect(result.name).to eql("Jane Doe") end it 'can be passed to a command graph' do changeset = repo.changeset( name: "Jane Doe", posts: [{ title: "Just Do It", alien: "or sutin" }] ) command = repo.command(:create, repo.aggregate(:posts)) result = command.(changeset) expect(result.id).to_not be(nil) expect(result.name).to eql("Jane Doe") expect(result.posts.size).to be(1) expect(result.posts[0].title).to eql("Just Do It") end it 'preprocesses data using changeset pipes' do changeset = repo.changeset(:books, title: "rom-rb is awesome").map(:add_timestamps) command = repo.command(:create, repo.books) result = command.(changeset) expect(result.id).to_not be(nil) expect(result.title).to eql("rom-rb is awesome") expect(result.created_at).to be_instance_of(Time) expect(result.updated_at).to be_instance_of(Time) end it 'preprocesses data using custom block' do changeset = repo. changeset(:books, title: "rom-rb is awesome"). map { |tuple| tuple.merge(created_at: Time.now) } command = repo.command(:create, repo.books) result = command.(changeset) expect(result.id).to_not be(nil) expect(result.title).to eql("rom-rb is awesome") expect(result.created_at).to be_instance_of(Time) end it 'preprocesses data using built-in steps and custom block' do changeset = repo. changeset(:books, title: "rom-rb is awesome"). extend(:touch) { |tuple| tuple.merge(created_at: Time.now) } command = repo.command(:create, repo.books) result = command.(changeset) expect(result.id).to_not be(nil) expect(result.title).to eql("rom-rb is awesome") expect(result.created_at).to be_instance_of(Time) expect(result.updated_at).to be_instance_of(Time) end it 'preserves relation mappers with create' do changeset = repo. changeset(create_changeset). new(repo.users.relation.as(:user)). data(name: 'Joe Dane') expect(changeset.commit).to eql(Test::User.new(id: 1, name: 'Joe Dane')) end it 'creates changesets for non-root relations' do repo.create(name: 'John Doe') changeset = repo.changeset(add_book_changeset).data(title: 'The War of the Worlds') expect(changeset.commit). to include( id: 1, title: 'The War of the Worlds', created_at: nil, updated_at: nil ) end end describe 'Update' do subject(:repo) do Class.new(ROM::Repository[:books]) { commands :create, update: :by_pk }.new(rom) end it 'can be passed to a command' do book = repo.create(title: 'rom-rb is awesome') changeset = repo .changeset(book.id, title: 'rom-rb is awesome for real') .extend(:touch) expect(changeset.diff).to eql(title: 'rom-rb is awesome for real') result = repo.update(book.id, changeset) expect(result.id).to be(book.id) expect(result.title).to eql('rom-rb is awesome for real') expect(result.updated_at).to be_instance_of(Time) end it 'skips update execution with no diff' do book = repo.create(title: 'rom-rb is awesome') changeset = repo .changeset(book.id, title: 'rom-rb is awesome') .extend(:touch) expect(changeset).to_not be_diff result = repo.update(book.id, changeset) expect(result.id).to be(book.id) expect(result.title).to eql('rom-rb is awesome') expect(result.updated_at).to be(nil) end it 'works with mixed several class-level pipes' do book = repo.create(title: 'rom-rb is awesome') changeset_class = Class.new(ROM::Changeset::Update[:books]) do map { |title: | { title: title.upcase } } extend { |title: | { title: title.reverse } } end changeset = repo .changeset(changeset_class) .by_pk(book.id) .data(title: 'rom-rb is really awesome') expect(changeset.diff).to eql(title: 'ROM-RB IS REALLY AWESOME') expect(changeset.to_h).to eql(title: 'EMOSEWA YLLAER SI BR-MOR') end it 'works with mixed several instance-level pipes' do book = repo.create(title: 'rom-rb is awesome') changeset = repo. changeset(book.id, title: 'rom-rb is really awesome'). map { |title: | { title: title.upcase } }. extend { |title: | { title: title.reverse } } expect(changeset.diff).to eql(title: 'ROM-RB IS REALLY AWESOME') expect(changeset.to_h).to eql(title: 'EMOSEWA YLLAER SI BR-MOR') end end end