Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord::Base doesn't belong in a hierarchy descending from ActiveRecord

I'm trying to create a Rails plugin. For the most part, what I've written works. However, there's a problem with associations. When I try to call an association, I get this error:

ActiveRecord::Base doesn't belong in a hierarchy descending from ActiveRecord

At the moment, the plugin looks like this:

module ControlledVersioning
  module ActsAsVersionable
    extend ActiveSupport::Concern

    included do
      has_many :versions, as: :versionable

      after_create :create_initial_version
    end

    module ClassMethods
      def acts_as_versionable(options = {})

        cattr_accessor :versionable_attributes
        self.versionable_attributes = options[:versionable_attributes]
      end
    end

    private
    def create_initial_version
      version = versions.create
    end
  end
end

ActiveRecord::Base.send :include, ControlledVersioning::ActsAsVersionable

Again, the error message is triggered whenever I try to call the association. I used debugger in the after_create callback and tried running:

> versions.create
*** ActiveRecord::Base doesn't belong in a hierarchy descending from ActiveRecord

> versions
*** ActiveRecord::Base doesn't belong in a hierarchy descending from ActiveRecord

> Version.new
#<Version id: nil, versionable_id: nil, versionable_type: nil>
like image 479
nullnullnull Avatar asked Jan 26 '14 06:01

nullnullnull


1 Answers

There are a few things you need to change in your code in order for it to work.

First, versions is a reserved keyboard from rails -- you can't have a relationship with that name - (I used the name versionings in order to make it work)

Also, you want to make sure to just add has_many versionings for the models that want to acts_as_versionable - meaning, move has_many :versionings, as: :versionable, class_name: 'Version' and after_create :create_initial_version calls to inside the acts_as_versionable method.

Here's how all together will look like:

module ControlledVersioning
  module ActsAsVersionable
    extend ActiveSupport::Concern

    module ClassMethods
      def acts_as_versionable(options = {})
        has_many :versionings, as: :versionable, class_name: 'Version'
        after_create :create_initial_version

        cattr_accessor :versionable_attributes
        self.versionable_attributes = options[:versionable_attributes]
      end
    end

    private

    def create_initial_version
      version = versionings.create
    end
  end
end

ActiveRecord::Base.send :include, ControlledVersioning::ActsAsVersionable

Doing those changes made the plugin work for me:

irb(main):003:0> Post.create!
   (0.1ms)  begin transaction
  Post Create (0.7ms)  INSERT INTO "posts" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", "2019-07-16 08:55:13.768196"], ["updated_at", "2019-07-16 08:55:13.768196"]]
  Version Create (0.2ms)  INSERT INTO "versions" ("versionable_type", "versionable_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["versionable_type", "Post"], ["versionable_id", 3], ["created_at", "2019-07-16 08:55:13.772246"], ["updated_at", "2019-07-16 08:55:13.772246"]]
   (2.0ms)  commit transaction
=> #<Post id: 3, created_at: "2019-07-16 08:55:13", updated_at: "2019-07-16 08:55:13", name: nil>
irb(main):004:0> Post.last.versionings
  Post Load (0.2ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT ?  [["LIMIT", 1]]
  Version Load (0.2ms)  SELECT  "versions".* FROM "versions" WHERE "versions"."versionable_id" = ? AND "versions"."versionable_type" = ? LIMIT ?  [["versionable_id", 3], ["versionable_type", "Post"], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Version id: 2, versionable_type: "Post", versionable_id: 3, created_at: "2019-07-16 08:55:13", updated_at: "2019-07-16 08:55:13">]>
irb(main):005:0> 
like image 118
Jonathan Duarte Avatar answered Nov 15 '22 19:11

Jonathan Duarte