Search code examples
ruby-on-railsrubyrails-engines

Rails: Belongs_to and Has_many associations in Rails Engines


So I building an application using Rails 6. I have implemented some core features of the app using Rails Engines.

The name of the Engine Baseblog, and I have 2 models for it called Author and Post.

An author has many posts, while a post belongs to one author. Below are my model implementations:

Author Model:

module Baseblog
  class Author < ApplicationRecord
    has_many :posts, dependent: :destroy

    validates :name, presence: true
    validates :address, presence: true
  end
end

Post Model:

module Baseblog
  class Post < ApplicationRecord
    belongs_to :author

    validates :name, presence: true
  end
end

Here's my rspec test for the Author model:

Author Spec

require 'rails_helper'

module Baseblog
  RSpec.describe Author, type: :model do
    describe 'associations' do
      it { is_expected.to have_many(:posts) }
    end

    describe "validations" do
      it { is_expected.to validate_presence_of(:name) }
      it { is_expected.to validate_presence_of(:address) }
    end
  end
end

Author Factory Bot

FactoryBot.define do
  factory :school do
    name { "MyString" }
    address { "MyText" }
  end
end

Schema for Posts

create_table "baseblog_posts", force: :cascade do |t|
  t.string "name"
  t.text "description"
  t.bigint "baseblog_author_id", null: false
  t.datetime "created_at", precision: 6, null: false
  t.datetime "updated_at", precision: 6, null: false
  t.index ["baseblog_author_id"], name: "index_baseblog_posts_on_baseblog_author_id"
  end

However, when I run my test for the Author model, I get the error:

Failures:

  1) Baseblog::Author associations is expected to have many posts
     Failure/Error: it { is_expected.to have_many(:posts) }
       Expected Baseblog::Author to have a has_many association called posts (Baseblog::Post does not have a author_id foreign key.)
     # ./spec/models/baseblog/author_spec.rb:6:in `block (3 levels) in <module:Baseblog>'

Finished in 0.29222 seconds (files took 2.9 seconds to load)
6 examples, 1 failure

How do I define the has_many and belongs_to association between the Author model and the Post model? Any form of assistance will be highly appreciated.

Update:

The test spec for the Author model passes now after I modified it following way using dedypuji's answer:

Author Model

module Baseblog
  class Author < ApplicationRecord
    has_many :posts, class_name: 'Baseblog::Post', foreign_key: :baseblog_author_id, dependent: :destroy

    validates :name, presence: true
    validates :address, presence: true
  end
end

Post Model

module Baseblog
  class Post < ApplicationRecord
    belongs_to :author, class_name: 'Baseblog::Author', foreign_key: :baseblog_author_id

    validates :name, presence: true
  end
end

However, the spec test for Post model still fails:

Post Spec

require 'rails_helper'

module Baseblog RSpec.describe Post, type: :model do describe 'associations' do it { is_expected.to belong_to(:author) } end

describe 'validations' do
  it { is_expected.to validate_presence_of(:name) }
  it { is_expected.to validate_presence_of(:author_id) }
end

end end

Factory Bot for Post spec

FactoryBot.define do
  factory :post do
    name { "MyString" }

    association :author
  end
end

However, when I run the spec test for Post I get the error below:

Failures:

  1) Baseblog::Post associations is expected to belong to author required: true
     Failure/Error: it { is_expected.to belong_to(:author) }
     
     NoMethodError:
       undefined method `author_id' for #<Baseblog::Post:0x000056397e47dc50>
       Did you mean?  author
                      author=
     # ./spec/models/baseblog/post_spec.rb:6:in `block (3 levels) in <module:Baseblog>'

Baseblog::Post validations is expected to validate that :author_id cannot be empty/falsy
     Failure/Error: it { is_expected.to validate_presence_of(:author_id) }
     
     Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError:
       The matcher attempted to set :author_id on the Baseblog::Post to
       nil, but that attribute does not exist.
     # ./spec/models/baseblog/post_spec.rb:19:in `block (3 levels) in <module:Baseblog>'

Solution

  • can you try this?

    module Baseblog
      class Author < ApplicationRecord
        has_many :posts, class_name: 'Baseblog::Post', foreign_key: :baseblog_author_id, dependent: :destroy
    
        validates :name, presence: true
        validates :address, presence: true
      end
    end
    
    module Baseblog
      class Post < ApplicationRecord
        belongs_to :author, class_name: 'Baseblog::Author', foreign_key: :baseblog_author_id
    
        validates :name, presence: true
      end
    end