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

Bug with namespaced ActiveRecord models on 4.1.x (PostgreSQL) #15811

Open
thelucid opened this issue Jun 19, 2014 · 25 comments
Open

Bug with namespaced ActiveRecord models on 4.1.x (PostgreSQL) #15811

thelucid opened this issue Jun 19, 2014 · 25 comments

Comments

@thelucid
Copy link
Contributor

@thelucid thelucid commented Jun 19, 2014

I've come up against a bug with ActiveRecord when using namespaces models and polymorphic associations, it's a common use-case so should probably be fixed before a 4.2 release.

Error:

NoMethodError: undefined method `relation_delegate_class' for Comment:Module

Example:

class Post::Base < ActiveRecord::Base
  self.table_name = 'posts'
end

class Post::Special < Post::Base
  has_many :comments, as: :commentable
end

class Comment::Base < ActiveRecord::Base
  self.table_name = 'comments'
  belongs_to :commentable, polymorphic: true
end

class Comment::Special < Comment::Base
end

Post::Special.first.comments

# => NoMethodError: undefined method `relation_delegate_class' for Comment:Module
@rafaelfranca
Copy link
Member

@rafaelfranca rafaelfranca commented Jun 19, 2014

Which version are you using? Have you tried the latest 4.1.2.rc2? Could you create an reproduction script with this template https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_master.rb?

@taish
Copy link

@taish taish commented Jun 23, 2014

I think it is problem and should be fixed

I create an reproduction script here
This test should be passed but actually failed
I'll try to solve

@taish
Copy link

@taish taish commented Jun 23, 2014

I find

When Post::Special.first.comments is called, comments method is called and Post::Special make a relationship. To make a relationship, Rails use class_name method in this url.
in this method, Returns the class name for the macro.
that is has_many :comments returns 'Comment'

so We couldn't cope with using namespaces models at this moment because using has_many method.

But you could use namespaces model as follow

class Post::Special < Post::Base
  has_many :comments, as: :outline, class_name: 'Comment::Base'
end
@rafaelfranca rafaelfranca added this to the 4.1.3 milestone Jun 23, 2014
@thelucid
Copy link
Contributor Author

@thelucid thelucid commented Jun 24, 2014

@rafaelfranca Yes, this is with 4.1.2.rc2. I guess I should be specifying the class_name option so not sure this is actually a bug.

@rafaelfranca
Copy link
Member

@rafaelfranca rafaelfranca commented Jun 24, 2014

Even on 4.1.2.rc3?

@rafaelfranca
Copy link
Member

@rafaelfranca rafaelfranca commented Jun 26, 2014

So I tried to debug and fix this issue but I think it is not possible. There is no way of the automatic detection of the class name work correctly on this case.

When trying to find possible candidates for the class of your association called comments Rails tries to find inside the current class, inside the parent module and inside the global scope. So the candidates for this case would be: ["Post::Special::Comment", "Post::Comment", "Comment"]

Only the module Comment is a valid constant so it pick this choice. Of course it is not a valid Active Record instance so it will fail in some place of the code. I could reproduce this behaviour even on Rails 3.2.

I think we could improve the error checking if the candidate is a Active Record model and if not we raise an ArgumentError with the proper information. Something like: "Rails could not find a valid model for comments association. Please provide the :class_name option on the association declaration".

@thelucid if we agree with this solution @mdepolli will work on it.

@thelucid
Copy link
Contributor Author

@thelucid thelucid commented Jul 26, 2014

@rafaelfranca Sounds fine to be, using class_name is fine it's just that the error message was a bit cryptic.

@bhgames
Copy link

@bhgames bhgames commented Sep 10, 2014

+1 Still happening to me on 4.1.5

@frenkel
Copy link
Contributor

@frenkel frenkel commented Sep 17, 2014

I'm using class_name: '::Comment' as a workaround for now.

@rafaelfranca rafaelfranca modified the milestones: 4.1.9, 4.0.13 Nov 19, 2014
@rafaelfranca rafaelfranca modified the milestones: 4.0.13, 4.1.9 Jan 2, 2015
@rails-bot
Copy link

@rails-bot rails-bot commented Jun 25, 2015

This issue has been automatically marked as stale because it has not been commented on for at least
three months.

The resources of the Rails team are limited, and so we are asking for your help.

If you can still reproduce this error on the 4-2-stable, 4-1-stable branches or on master,
please reply with all of the information you have about it in order to keep the issue open.

Thank you for all your contributions.

@rails-bot rails-bot added the stale label Jun 25, 2015
@rafaelfranca rafaelfranca added pinned and removed stale labels Aug 25, 2015
@adiakritos
Copy link

@adiakritos adiakritos commented Jul 10, 2017

To be more precise for others who try @frenkel 's method... I found that the class name has to specifically match the model that contains polymorphic: true. I was thrown off for about an hour with the '::classname' format.

So in this case you'd write it like this:

class Post::Special < Post::Base
  has_many :comments, as: :commentable, class_name: "Comment::Base"
end

Not sure if he's trying to accomplish something different from me, but this makes namespaced models work with polymorphism work for me in rails 3.1.

@shrirambalakrishnan
Copy link

@shrirambalakrishnan shrirambalakrishnan commented Aug 5, 2017

@matthewd I will work on this.

@tiagonbotelho
Copy link

@tiagonbotelho tiagonbotelho commented Sep 25, 2017

@shrirambalakrishnan are you working on this? If not can I start working on it? Thanks 😄

@shrirambalakrishnan
Copy link

@shrirambalakrishnan shrirambalakrishnan commented Sep 26, 2017

@tiagonbotelho I am not working on this. You can start with this.

@tiagonbotelho
Copy link

@tiagonbotelho tiagonbotelho commented Sep 26, 2017

@rafaelfranca could you point me to the code snippet where:

When trying to find possible candidates for the class of your association called comments Rails tries to find inside the current class, inside the parent module and inside the global scope. So the candidates for this case would be: ["Post::Special::Comment", "Post::Comment", "Comment"]

happens?

I also assume that checking if the candidate is an Active Record model is just candidate.is_a?(ActiveRecord::Base) or do we already have a method in place for that?

It is my first time contributing so sorry in advance if they are bad questions 😄

Thanks!

@tiagonbotelho
Copy link

@tiagonbotelho tiagonbotelho commented Oct 2, 2017

Ok so I was able to track down where we pick the candidates (https://github.com/rails/rails/blob/master/activerecord/lib/active_record/inheritance.rb#L143) however I am not being able to find where we actually check if a candidate is a Active Record model

@al2o3cr
Copy link
Contributor

@al2o3cr al2o3cr commented Oct 9, 2017

@tiagonbotelho I don't believe there's currently a check - that's the cause of the original error report; the search finds Comment (a module) but assumes it is a model.

The check is going to be tricky; some libraries (ActiveHash, for one) rely on the duck-typing behavior that's currently in place, making classes that respond to the required methods but which aren't subclasses of ActiveRecord::Base.

@tiagonbotelho
Copy link

@tiagonbotelho tiagonbotelho commented Oct 9, 2017

Thanks @al2o3cr I created a comment https://github.com/rails/rails/pull/30778/files#r143513045 where I propose a possible solution to the "Checking if the object is a active record model"

Also could you provide me the example you mentioned from ActiveHash if possible?

@saveriomiroddi
Copy link
Contributor

@saveriomiroddi saveriomiroddi commented Jan 30, 2018

@rafaelfranca I've provided my proposed fix in #31829.

@vedant1811
Copy link

@vedant1811 vedant1811 commented May 5, 2018

Is this still open? Can I pick it up?

@saveriomiroddi
Copy link
Contributor

@saveriomiroddi saveriomiroddi commented May 8, 2018

Is this still open? Can I pick it up?

@vedant1811 hello! there are two PRs for this issue, one incomplete and another possibly/likely complete (mine). since the feedback cycle died on mine, I've closed it, but feel free to rip it off and improve it as you please; it's for the community's sake, anyway.

@schneems
Copy link
Member

@schneems schneems commented Jul 17, 2018

Is this still an issue with master? We wouldn’t accept a backport for Rails 4.1 at this time.

@sharang-d
Copy link
Contributor

@sharang-d sharang-d commented Sep 1, 2018

I updated @taish's reproduction script and looks like it still fails on the latest rails.

# frozen_string_literal: true

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails", github: "rails/rails"
  gem "sqlite3"
end

require "active_record"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :posts do |t|
  end

  create_table :comments do |t|
    t.integer :outline_id
    t.string :outline_type
  end
end

module Post
end

module Comment
end

class Post::Base < ActiveRecord::Base
  self.table_name = 'posts'
end

class Post::Special < Post::Base
  has_many :comments, as: :outline
end

class Comment::Base < ActiveRecord::Base
  self.table_name = 'comments'
  belongs_to :outline, polymorphic: true
end

class Comment::Special < Comment::Base
end

class BugTest < Minitest::Test
  def test_association_stuff
    post_special = Post::Special.create
    post_special.comments << Comment::Base.create

    assert_equal 1, post_special.comments.count
    assert_equal 1, Comment::Base.count
    assert_equal post_special.id, Comment::Base.first.outline_id
  end
end

Output:

E

Error:
BugTest#test_association_stuff:
NoMethodError: undefined method `relation_delegate_class' for Comment:Module
@ngetahun
Copy link

@ngetahun ngetahun commented Sep 26, 2018

Is this issue up for grabs? I would like to make my first PR to the project

@Dean-Coakley
Copy link

@Dean-Coakley Dean-Coakley commented Oct 4, 2018

@ngetahun Looks like #30778 should fix it but is awaiting review. It's quite old/inactive though, not sure if new PR should be opened. 😕

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.