Attribute serialization

It doesn’t work because you’re using SQLite, something about that adapter is causing the type error and I’m not certain why. The example does work with PostgreSQL.

Here’s another way you might approach it:

#!/usr/bin/env ruby
# frozen_string_literal: true

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'sqlite3'
  gem 'rom-sql'
  gem 'dry-core'
  gem 'dry-auto_inject'
  gem 'pry'
end

require 'rom'
require 'rom/sql'

rom_config = ROM::Configuration.new(:sql, 'sqlite::memory')
gateway    = rom_config.default_gateway

migration = gateway.migration do
  change do
    create_table :items do
      primary_key :id
      column :tags, 'text', null: false
    end
  end
end

migration.apply(gateway.connection, :up)

module Structs
  class Item < ROM::Struct
    def tags
      @tags ||= JSON.parse(attributes[:tags])
    end
  end
end

class Items < ROM::Relation[:sql]
  Tags = Types.define(Types::String) do
    input { |tags| ::JSON.dump(Array(tags)) }
    output { |json| json }
  end

  schema :items, infer: true do
    attribute :tags, Tags
  end
end

rom_config.register_relation(Items)

container = Dry::Core::Container.new
container.register("persistence.rom", ROM.container(rom_config))

Deps = Dry::AutoInject(container)

class Repository < ROM::Repository::Root
  include Deps[container: "persistence.rom"]
  def find(id) = root.by_pk(id).one
end

class ItemRepo < Repository[:items]
  struct_namespace Structs
  commands :create, update: :by_pk, delete: :by_pk
end

repo = ItemRepo.new

Pry.start(self)
[1] pry(main)> item = repo.create(tags: %w[foo bar])
=> #<Structs::Item tags="[\"foo\",\"bar\"]" id=1>
[2] pry(main)> item.tags
=> ["foo", "bar"]
[3] pry(main)> item = repo.update(item.id, tags: item.tags + ['baz'])
=> #<Structs::Item tags="[\"foo\",\"bar\",\"baz\"]" id=1>
[4] pry(main)> item.tags
=> ["foo", "bar", "baz"]