Associations and combines with Memory Adapter

Hi folks, TL;DR I’m working on using the Memory adapter to create an in-memory store for testing purposes (mocking an external service) where I’d prefer not to include an external dep like sqlite. I’m having trouble figuring out exactly how I utilize combine and/or how I might compose a set of relations:

Right now I’m modeling a folder and files:

config ||= ROM.container(:memory) do |config|
  config.relation(:folders) do
    schema(infer: true) do
      attribute :id, ROM::Types::String
      attribute :parent_id, ROM::Types::String

      primary_key :id

      associations do
        has_many :files, foreign_key: :folder_id
      end
    end

    def by_id(id)
      dataset = folders.select { |f| f[:id] == id }
      new(dataset)
    end

    def where(conditions = {})
      ds = conditions.reduce(folders) { |acc, (k, v)| acc.select { |a| a[k] == v } }
      new(ds)
    end

    def files_for(id)
      folders.combine(files).by_id(id)
    end
  end

  config.relation(:files) do |config|
    schema(infer: true) do
      attribute :id, ROM::Types::String
      attribute :folder_id, ROM::Types::String

      primary_key :id

      associations do
        belongs_to :folders, as: :folder, foreign_key: :folder_id
      end
    end

    def where(conditions = {})
      ds = conditions.reduce(files) { |acc, (k, v)| acc.select { |a| a[k] == v } }
      new(ds)
    end
  end
end

class FoldersRepo < ROM::Repository[:folders]
  commands :create
end

class FilesRepo < ROM::Repository[:files]
  commands :create
end

folders_repo = FoldersRepo.new(config)
files_repo = FilesRepo.new(config)

folders_repo.create(id: "1234", parent_id: "0")
files_repo.create(id: "5678", folder_id: "1234")
files_repo.create(id: "7890", folder_id: "0")

# this works but not sure its how it is designed:
[3] pry(main)> folders_repo.folders.by_id("1234").files.where(folder_id: "1234")
=> #<ROM::Relation[Files] name=ROM::Relation::Name(files) dataset=[{:id=>"5678", :folder_id=>"1234"}]>

# focusing in on the folders relation and the "files_for" method that uses combine
# it seems to pull the right folder
[1] pry(main)> folders_repo.folders.files_for("1234")
=> #<ROM::Relation::Combined root=#<ROM::Relation[Folders] name=ROM::Relation::Name(folders) dataset=[{:id=>"1234", :parent_id=>"0"}]> nodes=[]>

# but I don't understand this, should this not be scoped to only files with the right foreign key?:
[2] pry(main)> folders_repo.folders.files_for("1234").files
=> #<ROM::Relation[Files] name=ROM::Relation::Name(files) dataset=#<ROM::Memory::Dataset data=[{:id=>"5678", :folder_id=>"1234"}, {:id=>"7890", :folder_id=>"0"}]>>

additionally, following some of the documentation around combine, I found that if I redefine the files_for method as so:

    def files_for(id)
      folders.combine(:files).by_id(id)
    end

When invoked I get: NotImplementedError: NotImplementedError

I admit this might be an edge case for ROM.

I looked into the tutorial to build an adapter from scratch to see if there was something I need to implement. I also looked at memory specific specs in the rom repo. I’m sure I’m not understanding something but not sure where my misunderstanding is happening. Thanks for any feedback!

I don’t recommend doing this. Memory adapter is not meant to be used as as fake store. If you want to stub db access, use repositories and stub their methods instead.

Thanks @solnic appreciate the advice! Since I wrote this I moved towards using sqlite in-memory to solve the storage and get associations working and I agree its a better direction, I was just hopeful not to have to ensure sqlite was installed in our CI env. Probably is though…

1 Like