Hi everyone! Exploring ROM and facing my first couple of issues. I’m creating a logger library which for now will just log to a database (in future, will do other things), so my basic first step is to just write something in the table.
Here is my table composition (the migration):
create_table?(table) do
column :id,
:uuid,
primary_key: true,
null: false,
default: Sequel.function(:uuid_generate_v4)
column :created_at,
"timestamp with time zone",
null: false,
default: Sequel::SQL::PlaceholderLiteralString.new(
"(statement_timestamp() at time zone 'utc')",
[]
)
column :severity, :text, null: false, default: "debug"
column :message, :text, null: false, default: ""
column :program_name, :text, null: false, default: ""
column :environment, :text, null: false, default: ""
column :metadata, "jsonb",
null: false,
default: "{}"
column :tags, "text[]",
null: false,
default: Sequel::SQL::PlaceholderLiteralString.new(
"array[]::text[]",
[]
)
column :compressed_message, :tsvector,
null: false,
default: Sequel.function(:to_tsvector, "")
index :created_at
index :severity
index :message
index :program_name
index :environment
index :metadata, type: "gin"
index :tags, type: "gin"
index :compressed_message, type: "gin"
end
class Log < Dry::Struct
types = ::CarbideLogger::Types
coercible = types::Coercible
strict = types::Strict
constructor_type :schema
attribute :id, types::UUID.optional
attribute :severity, types::Severity.default(:debug)
attribute :message, coercible::String.default("")
attribute :program_name, coercible::String.default("")
attribute :environment, coercible::String.default("")
attribute :metadata, coercible::Hash.default({})
attribute :tags, types::ArrayOfIndifferentSymbols.default([])
attribute :compressed_message, coercible::String.default("")
attribute :created_at, strict::Time.default { Time.now.utc }
end
class Logs < ROM::Relation[:sql]
register_as :logs
gateway :default
dataset :logs
schema :infer
def empty?
dataset.empty?
end
def latest_first
order(Sequel.desc(:created_at))
end
end
class LogRepo < ROM::Repository[:logs]
commands :create
end
Now, beside the specific types, I create an entity Log and try to supply it to create command, but I keep getting weird errors related to SQL. Pretty sure I’m doing something wrong, but I’m not sure what.
The hash I supply looks like this:
{:id=>nil,
:severity=>:debug,
:message=>"",
:program_name=>"",
:environment=>"",
:metadata=>{},
:tags=>[],
:compressed_message=>"",
:created_at=>2016-11-17 22:33:44 UTC}
But for some reason, unless I remove the metadata
key, I get Sequel::Error: The AND operator requires at least 1 argument
, if I remove the metadata one, i get:
ROM::SQL::DatabaseError: PG::UndefinedColumn: ERROR: column "debug" does not exist
LINE 1: ..."compressed_message", "created_at") VALUES (NULL, "debug", '...
Unless I convert severity from symbol to string. If I fix that too, the next problem is:
ROM::SQL::NotNullConstraintError: PG::NotNullViolation: ERROR: null value in column "id" violates not-null constraint
DETAIL: Failing row contains (null, 2016-11-17 23:33:44.722287+01, debug, , , , {}, null, )
So, yeah the id is nil, but I just want it not to be supplied, because the database will generate one for me if I don’t supply it. I’m not sure how to deal with it though, if I put optional, I get nil
, the key is still there, while I want it not to exist unless supplied.
If I remove the id
, I get ROM::SQL::NotNullConstraintError: PG::NotNullViolation: ERROR: null value in column "tags" violates not-null constraint
, which is related to the tag array, so I fill some values like [:foo, :bar]
and I get
ROM::SQL::DatabaseError: PG::UndefinedColumn: ERROR: column "foo" does not exist
LINE 1: ...age", "created_at") VALUES ('debug', '', '', '', ("foo", "ba...
If I use an array of strings instead, I get
ROM::SQL::DatabaseError: PG::DatatypeMismatch: ERROR: column "tags" is of type text[] but expression is of type record
LINE 1: ...sage", "created_at") VALUES ('debug', '', '', '', ('foo', 'b...
And this is the last one, if I remove the tags, I can finally create my object. So from my understanding, I have a clear problem with type conversion. Anyone able to provide some guidance? I have the feeling I need some kind of mapper that converts from entity to “what the db wants”