Attributes coerced multiple times

Hi, I’ve been trying to work with coercing types in ROM schemas, and came across something which I think is a bit unintuitive. If you define custom types in order to coerce certain data types, then they seem to get coerced more than once before saving to the DB, and likewise when they are retrieved.

As an example, I tried creating some types that coerce a ruby hash into a JSON string on saving to a DB, then parsing the JSON when retrieving. My code looks something like the following:

class Users
  schema(:infer => true) do
    JSONString = Types::String.constructor do |h|
      JSON.dump(h)
    end

    attribute :preferences, JSONString, :read => (Types::Hash.constructor{ |s| JSON.parse(s) })
  end
end

Then if I insert some data and retrieve it, I get the following:

users.command(:create).call(:preferences => {"foo" => ["bar"]})

users.last
# returns:
{:id => 1, :preferences => "{\"foo\":[\"bar\"]}"}

Adding some instrumentation (puts!) to the block constructor implementations above, I noticed that the dumping happens twice before insertion and twice after retrieval. Despite the fact that the number of parses and dumps are equal, the above return value still does not give me back my ruby hash as I would expect.

As an experiment, I changed the implementations again to the following:

    JSONString = Types::String.constructor do |h|
      h.is_a?(String) ? h : JSON.dump(h)
    end

    attribute :preferences, JSONString, :read => (Types::Hash.constructor do |s|
      s.is_a?(Hash) ? s : JSON.parse(s)
    end)

This seems to produce the behaviour I want. My question then is, is this expected behaviour?

Additional Info

I believe the offending code might be in ROM::Plugins::Command::Schema::ClassInterface#build but I’m not really familiar enough with the code to be sure.

I also have another use case where I wish to encode a string using Base64 before inserting into the DB, and using the above solution in that case would be tricky since it’s hard to tell whether an arbitrary string is already in Base64!

Any help or guidance would be much appreciated!

This is a bug, I reported an issue and made a PR https://github.com/rom-rb/rom/pull/589 Thanks for bringing it, it was a result of some (relatively) recent changes.

1 Like

5.2.2 is out with a bugfix https://rubygems.org/gems/rom

1 Like

That’s great. Thanks for the prompt response!

1 Like