My database looks nothing like my entities, and so my setup is fairly more complex than normal, and I am ending up needing to customize seemingly every layer.
I am able to create data, but part of the default insert is doing a query to return the inserted data, and when that happens it is not making use of my configured mapper, and since the columns don’t match it blows up. I’ve created a basic example showing the problem.
# !/usr/bin/env ruby
# This is an example of a complete ROM setup in one file.
# Useful for reproducing problems and posting to StackOverflow, forums, bug trackers, etc.
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'rom'
gem 'rom-sql'
gem 'sqlite3'
end
require "rom/transformer"
module Marv
module Entities
Types = ROM::Types
class Base < ROM::Struct
transform_keys(&:to_sym)
end
class Watch < Base
attribute(:id, Types::Integer)
attribute(:name, Types::String)
attribute(:created, Types::JSON::DateTime)
attribute(:updated, Types::JSON::DateTime)
end
end
module SqlRelations
Types = ROM::SQL::Types
class Base < ROM::Relation[:sql]
struct_namespace ::Marv::Entities
auto_struct false
end
class Watches < Base
schema(:watches) do
attribute(:id, Types::Serial)
attribute(:watch_type, Types::String)
attribute(:created_at, Types::DateTime)
attribute(:updated_at, Types::DateTime)
end
end
end
# This mapper never gets used. How can I force it?
class WatchMapper < ROM::Transformer
relation :watches, as: :watch_mapper
map do
symbolize_keys
rename_keys({
watch_type: :name,
created_at: :created,
updated_at: :updated,
})
end
end
class NewWatchChangeset < ROM::Changeset::Create
map do
symbolize_keys
rename_keys({
name: :watch_type,
created: :created_at,
updated: :updated_at,
})
end
end
class WatchRepo < ROM::Repository[:watches]
# by default always map with watch mapper to return Watch objects
# Doesn't work yet with a Root repo as super. See:
# https://rom-rb.zulipchat.com/#narrow/stream/191800-general/topic/repository.20default.20mapper
# But also, doesn't appear to work at all, period.
def root
super.map_with(:watch_mapper)
end
struct_namespace Marv::Entities
commands :create, update: :by_pk, delete: :by_pk
end
CONT = ROM.container(:sql, 'sqlite::memory') do |conf|
conf.default.create_table(:watches) do
primary_key :id
column :watch_type, String
column :created_at, Time
column :updated_at, Time
end
conf.register_relation(Marv::SqlRelations::Watches)
conf.register_mapper(Marv::WatchMapper)
end
end
WATCH_REPO = Marv::WatchRepo.new(Marv::CONT)
model = WATCH_REPO.watches.mapper.model
queried = WATCH_REPO.watches.to_a
now = Time.now
watch_data = { name: "i-am-a-puppy", created: now, updated: now }
puts <<HEAD
model: #{model}
schema: #{model.schema}
watch_data: #{watch_data}
queried: #{queried}
HEAD
changeset = WATCH_REPO.watches.changeset(Marv::NewWatchChangeset, watch_data)
puts <<HEAD
changeset: #{changeset.to_h}
HEAD
# Explodes regardless of which type of commit/create is run:
# result = changeset.commit
result = WATCH_REPO.create(changeset)
# And `map_with` isn't available on either WATCH_REPO or changeset.
# Separately, there are no examples of how `map_to` is expected to work in the real world... do I need it?
queried = WATCH_REPO
.watches
.map_to(Marv::Entities::Watch)
.map_with(:watch_mapper)
.to_a
puts <<HEAD
result: #{result}
queried: #{queried}
HEAD
The output with error is:
$ ruby example.rb
model: Marv::Entities::Watch
schema: #<Dry::Types[Constructor<Schema<key_fn=.to_sym keys={id: Nominal<Integer meta={primary_key: true, alias: nil, source: :watches}> name: Nominal<String> created: Constructor<Nominal<DateTime> fn=Dry::Types::Coercions::JSON.to_date_time> updated: Constructor<Nominal<DateTime> fn=Dry::Types::Coercions::JSON.to_date_time> watch_type: Nominal<String meta={alias: nil, source: :watches}> created_at: Nominal<DateTime meta={alias: nil, source: :watches}> updated_at: Nominal<DateTime meta={alias: nil, source: :watches}>}> fn=Kernel.Hash>]>
watch_data: {:name=>"i-am-a-puppy", :created=>2023-07-06 10:28:33.600305 -0700, :updated=>2023-07-06 10:28:33.600305 -0700}
queried: []
changeset: {:watch_type=>"i-am-a-puppy", :created_at=>2023-07-06 10:28:33.600305 -0700, :updated_at=>2023-07-06 10:28:33.600305 -0700}
Traceback (most recent call last):
30: from example.rb:122:in `<main>'
29: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-repository-5.3.0/lib/rom/repository/class_interface.rb:128:in `block in define_command_method'
28: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-changeset-5.3.0/lib/rom/changeset/stateful.rb:204:in `commit'
27: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/commands/composite.rb:19:in `call'
26: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-sql-3.6.1/lib/rom/sql/commands/error_wrapper.rb:18:in `call'
25: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/command.rb:278:in `call'
24: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-sql-3.6.1/lib/rom/sql/commands/create.rb:33:in `execute'
23: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-sql-3.6.1/lib/rom/sql/commands/create.rb:49:in `insert'
22: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/relation.rb:361:in `to_a'
21: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/relation.rb:361:in `to_a'
20: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/relation.rb:361:in `each'
19: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/relation.rb:221:in `each'
18: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/mapper.rb:97:in `call'
17: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/mapper.rb:97:in `reduce'
16: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/mapper.rb:97:in `each'
15: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/mapper.rb:97:in `block in call'
14: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/transproc-1.1.1/lib/transproc/function.rb:49:in `call'
13: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/transproc-1.1.1/lib/transproc/function.rb:49:in `call'
12: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/transproc-1.1.1/lib/transproc/array.rb:44:in `map_array'
11: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/transproc-1.1.1/lib/transproc/array.rb:44:in `map'
10: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/transproc-1.1.1/lib/transproc/array.rb:44:in `block in map_array'
9: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/transproc-1.1.1/lib/transproc/function.rb:49:in `call'
8: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/transproc-1.1.1/lib/transproc/function.rb:49:in `call'
7: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/transproc-1.1.1/lib/transproc/class.rb:32:in `constructor_inject'
6: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/dry-struct-1.6.0/lib/dry/struct/class_interface.rb:264:in `new'
5: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/dry-types-1.7.1/lib/dry/types/constructor.rb:81:in `call_unsafe'
4: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/dry-types-1.7.1/lib/dry/types/schema.rb:60:in `call_unsafe'
3: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/dry-types-1.7.1/lib/dry/types/schema.rb:341:in `resolve_unsafe'
2: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/dry-types-1.7.1/lib/dry/types/schema.rb:377:in `resolve_missing_keys'
1: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/dry-types-1.7.1/lib/dry/types/schema.rb:377:in `each'
/Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/dry-types-1.7.1/lib/dry/types/schema.rb:386:in `block in resolve_missing_keys': :name is missing in Hash input (Dry::Types::MissingKeyError)
30: from example.rb:122:in `<main>'
29: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-repository-5.3.0/lib/rom/repository/class_interface.rb:128:in `block in define_command_method'
28: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-changeset-5.3.0/lib/rom/changeset/stateful.rb:204:in `commit'
27: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/commands/composite.rb:19:in `call'
26: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-sql-3.6.1/lib/rom/sql/commands/error_wrapper.rb:18:in `call'
25: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/command.rb:278:in `call'
24: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-sql-3.6.1/lib/rom/sql/commands/create.rb:33:in `execute'
23: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-sql-3.6.1/lib/rom/sql/commands/create.rb:49:in `insert'
22: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/relation.rb:361:in `to_a'
21: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/relation.rb:361:in `to_a'
20: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/relation.rb:361:in `each'
19: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/relation.rb:221:in `each'
18: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/mapper.rb:97:in `call'
17: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/mapper.rb:97:in `reduce'
16: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/mapper.rb:97:in `each'
15: from /Users/pboling/.asdf/installs/ruby/2.7.8/lib/ruby/gems/2.7.0/gems/rom-core-5.3.0/lib/rom/mapper.rb:97:in `block in call'
I am so lost.