IFTTT/polo

has_and_belongs_to_many relationships will not store join table

Opened this issue · 3 comments

Title perhaps doesn't explain the problem well, code does:

class Account
  has_and_belongs_to_many :roles
end

class Role
  has_and_belongs_to_many :accounts
end

account = Account.create name: "Matt"
role = Role.create name: "admin"

account.roles << role

Polo.explore(Account, [1,2,3], [:roles])

This will result in the INSERT SQL for account and role being created, but not for the join table. The "solution" appears to be to use has_many through: instead, and always have an AR model with an ID column as the join table.

I don't have a solution, so I feel the README should at least clarify that HABTM doesn't work right now, and to use has_many through: instead.

Encountered on Rails 3.2

Some prying about revealed why. I threw a pry in here:

From: /Users/matt/work/polo/lib/polo/collector.rb @ line 39 Polo::Collector#collector:

    37: def collector
    38:   lambda do |name, start, finish, id, payload|
 => 39:     binding.pry
    40:     return unless payload[:name] =~ /^(.*) Load$/
    41:     begin
    42:       class_name = $1.constantize
    43:       sql = payload[:sql]
    44:       collect_sql(class_name, sql)
    45:     rescue ActiveRecord::StatementInvalid, NameError
    46:       # invalid table name (common when prefetching schemas)
    47:     end
    48:   end
    49: end

And, ignoring the irrelevant good payloads for Account, heres the interesting iterations:

# [1] directory(#<Polo::Collector>) »  payload
=> {
            :sql => "SHOW TABLES LIKE 'accounts_roles'",
           :name => "SCHEMA",
  :connection_id => 70096124074740,
          :binds => [],
           :line => 7,
       :filename => "/Users/matt/work/dir.caring.com/bin/rake",
         :method => "<top (required)>"
}

# [1] directory(#<Polo::Collector>) »  payload
=> {
            :sql => "SHOW FULL FIELDS FROM `accounts_roles`",
           :name => "SCHEMA",
  :connection_id => 70096124074740,
          :binds => [],
           :line => 7,
       :filename => "/Users/matt/work/dir.caring.com/bin/rake",
         :method => "<top (required)>"
}

# [1] directory(#<Polo::Collector>) »  payload
=> {
            :sql => "SELECT `roles`.*, `t0`.`account_id` AS ar_association_key_name FROM `roles` INNER JOIN `accounts_roles` `t0` ON `roles`.`id` = `t0`.`role_id` WHERE `t0`.`account_id` IN (2, 5, 6, ... this is a very long list ... ) AND (`roles`.`deleted_at` IS NULL)",
           :name => "SQL",
  :connection_id => 70096124074740,
          :binds => [],
           :line => 7,
       :filename => "/Users/matt/work/dir.caring.com/bin/rake",
         :method => "<top (required)>"
}

# [1] directory(#<Polo::Collector>) »  payload
=> {
            :sql => "SELECT `roles`.* FROM `roles`  WHERE `roles`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25) AND (`roles`.`deleted_at` IS NULL)",
           :name => "Role Load",
  :connection_id => 70096124074740,
          :binds => [],
           :line => 7,
       :filename => "/Users/matt/work/dir.caring.com/bin/rake",
         :method => "<top (required)>"
}

The Role Load statement has already worked out which Roles to load from the SQL statement proceeding it.

Well, I don't see a solution to this problem. 😅

Lastly I'll add the way we have "solved" this problem is to redefine the associations when we scrub to be has_many through:, and make a dummy join model. Its far from ideal but it works.

# lib/tasks/db.rake

namespace :db do
  task :scrub_setup do
    # Redefine the HABTM for roles so we can use polo.
    class AccountsRole < ActiveRecord::Base
      belongs_to :account
      belongs_to :role
    end

    class Account < ActiveRecord::Base
      has_many :accounts_roles
      has_many :roles, through: :accounts_roles
    end

    class Role < ActiveRecord::Base
      has_many :accounts_roles
      has_many :accounts, through: :accounts_roles
    end
  end
end

Rake::Task["db:scrub"].enhance ["db:scrub_setup"]

hey, @bessey. Great investigative work!
I'll see if I can dig into this a little bit too.. Maybe we can come up with some creative solution for this.