Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom relations fails using the real tablename on queries #91

Closed
jmcervera opened this issue Sep 16, 2016 · 1 comment
Closed

Custom relations fails using the real tablename on queries #91

jmcervera opened this issue Sep 16, 2016 · 1 comment

Comments

@jmcervera
Copy link

jmcervera commented Sep 16, 2016

I have a table in database called 'users' and I have defined and registered two relations over it, 'users' and 'drivers'. I have done this because I have other tables, 'journeys' for instance that hold two relations (belongs_to) with the 'users' table.

The problem is that when I want to aggregate the entity driver to the query for journeys, the sql sentence that is generated is using 'driver' as the table name, which not exists, it should use the name of the table, not the name of the relation.
Maybe I'm forgetting something, isn't it?

This is some code that shows the error

#!/usr/bin/env ruby

require "bundler/setup"

require "rom-sql"
require "rom-repository"

module Relations
  class Users < ROM::Relation[:sql]
    schema(:users) do
      attribute :id, Types::Serial
      attribute :name, Types::String
      attribute :type, Types::String

      associations do
        has_many :journeys
      end
    end
  end

  class Drivers < ROM::Relation[:sql]
    register_as :drivers

    schema(:users) do
      attribute :id, Types::Serial
      attribute :name, Types::String
      attribute :type, Types::String

      associations do
        has_many :journeys
      end
    end
  end

  class Journeys < ROM::Relation[:sql]
    schema(:journeys) do
      attribute :id, Types::Serial
      attribute :name, Types::String
      attribute :user_id, Types::ForeignKey(:users)
      attribute :driver_id, Types::ForeignKey(:drivers)

      associations do
        belongs_to :user
        belongs_to :driver
      end
    end
  end
end

module Repositories

  class Users < ROM::Repository[:users]
    commands :create
  end

  class Drivers < ROM::Repository[:drivers]
    commands :create

    def by_id(id)
      drivers.fetch(id)
    end
  end

  class Journeys < ROM::Repository[:journeys]
    commands :create

    relations :journeys, :users, :drivers

    def get_all
      journeys.to_a
    end

    def get_all_with_users
      aggregate(:user).to_a
    end

    def get_all_with_drivers
      aggregate(:driver).to_a
    end
  end

end

config = ROM::Configuration.new(:sql, "sqlite::memory")
config.register_relation Relations::Users
config.register_relation Relations::Drivers
config.register_relation Relations::Journeys

container = ROM.container(config)

container.gateways[:default].tap do |gateway|
  migration = gateway.migration do
    change do
      create_table :users do
        primary_key :id
        string :name, null: false
        string :type, null: false
      end
      create_table :journeys do
        primary_key :id
        string :name, null: false
        integer :user_id
        integer :driver_id
      end
    end
  end
  migration.apply gateway.connection, :up
end

users_repo = Repositories::Users.new(container)
john = users_repo.create(name: 'John', type: 'user')

drivers_repo = Repositories::Drivers.new(container)
drivers_repo.create(name: 'Jerry', type: 'driver')
jerry = drivers_repo.by_id(2)
puts jerry.inspect

journeys_repo = Repositories::Journeys.new(container)
journeys_repo.create(name: 'Madrid-Barcelona', user_id: john.id, driver_id: jerry.id)

journeys = journeys_repo.get_all_with_users
puts journeys.inspect

journeys = journeys_repo.get_all_with_drivers
puts journeys.inspect
@solnic solnic added bug and removed bug labels Sep 30, 2016
@solnic
Copy link
Member

solnic commented Sep 30, 2016

I thought it's a bug but it's actually not :) Here's the tweaked scripted that works:

require 'rom-sql'
require 'rom-repository'

module Relations
  class Users < ROM::Relation[:sql]
    schema(:users) do
      attribute :id, Types::Serial
      attribute :name, Types::String
      attribute :type, Types::String

      associations do
        has_many :journeys
      end
    end
  end

  class Drivers < ROM::Relation[:sql]
    register_as :drivers

    schema(:users) do
      attribute :id, Types::Serial
      attribute :name, Types::String
      attribute :type, Types::String

      associations do
        has_many :journeys
      end
    end
  end

  class Journeys < ROM::Relation[:sql]
    schema(:journeys) do
      attribute :id, Types::Serial
      attribute :name, Types::String
      attribute :user_id, Types::ForeignKey(:users)
      attribute :driver_id, Types::ForeignKey(:drivers)

      associations do
        belongs_to :user
        belongs_to :user, as: :driver, relation: :drivers
      end
    end
  end
end

module Repositories
  class Users < ROM::Repository[:users]
    commands :create
  end

  class Drivers < ROM::Repository[:drivers]
    commands :create

    def by_id(id)
      drivers.by_pk(id).one
    end
  end

  class Journeys < ROM::Repository[:journeys]
    commands :create

    relations :journeys, :users, :drivers

    def get_all
      journeys.to_a
    end

    def get_all_with_users
      aggregate(:user).to_a
    end

    def get_all_with_drivers
      aggregate(:driver).to_a
    end
  end
end

config = ROM::Configuration.new(:sql, 'sqlite::memory')
config.register_relation Relations::Users
config.register_relation Relations::Drivers
config.register_relation Relations::Journeys

container = ROM.container(config)

container.gateways[:default].tap do |gateway|
  migration = gateway.migration do
    change do
      create_table :users do
        primary_key :id
        string :name, null: false
        string :type, null: false
      end

      create_table :journeys do
        primary_key :id
        string :name, null: false
        integer :user_id
        integer :driver_id
      end
    end
  end
  migration.apply gateway.connection, :up
end

users_repo = Repositories::Users.new(container)
john = users_repo.create(name: 'John', type: 'user')

drivers_repo = Repositories::Drivers.new(container)
drivers_repo.create(name: 'Jerry', type: 'driver')
jerry = drivers_repo.by_id(2)
puts jerry.inspect

journeys_repo = Repositories::Journeys.new(container)
journeys_repo.create(name: 'Madrid-Barcelona', user_id: john.id, driver_id: jerry.id)

journeys = journeys_repo.get_all_with_users
puts journeys.inspect

journeys = journeys_repo.get_all_with_drivers
puts journeys.inspect

the output:

#<ROM::Struct[User] id=2 name="Jerry" type="driver">
[#<ROM::Struct[Journey] id=1 name="Madrid-Barcelona" user_id=1 driver_id=2 user=#<ROM::Struct[User] id=1 name="John" type="user" journey_id=1>>]
[#<ROM::Struct[Journey] id=1 name="Madrid-Barcelona" user_id=1 driver_id=2 driver=#<ROM::Struct[User] id=2 name="Jerry" type="driver" journey_id=1>>]

In general, the first argument in association macros must refer to the dataset name, in case of belongs_to (which just calls many_to_one under the hood) it is a singular version of the dataset that you want to "connect to", then it supports specifying which relation should be used via :relation option and you can alias it too via :as option, which will be used as the attribute name in loaded structs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants