thoughtbot / factory_bot_rails

Factory Bot ♥ Rails
https://thoughtbot.com/services/ruby-on-rails
MIT License
3.06k stars 364 forks source link

FactoryBotRails Generators incorrectly namespace inside Rails Engines #467

Open katelovescode opened 3 months ago

katelovescode commented 3 months ago

Description

When I run rails g model Car in a Rails Engine (which is configured to be namespaced inside a module), the Rails model correctly namespaces, but the FactoryBot factory does not.

Reproduction Steps

Run the following from the Engine root:

bundle exec rails g model Car name:string

Expected behavior

Ideally, inside the Engine I would expect not to have to manually edit the generated files to get them to work, or to use a workaround with the confusing --skip-namespace alongside an explicit namespace string. Not running the --skip-namespace flag has the added benefit of keeping the nested module namespace rather than the implicit ::Namespace syntax in the model as generated by the base Rails generators.

Actual behavior

The resulting files are not namespaced correctly, and they won't work without manual intervention in the generated files:

# app/models/discord_bot/car.rb
module DiscordBot
  class Car < ApplicationRecord
  end
end

# spec/factories/discord_bot/cars_factory.rb
FactoryBot.define do
  factory :car do
    name { "MyString" }
  end
end

# spec/models/discord_bot/car_spec.rb
require 'rails_helper'

module DiscordBot
  RSpec.describe Car, type: :model do
    it "works" do
      car = build(:car)
      expect(car).to be_present
    end
  end
end

and when I run rspec, I get the following error:

Failures:

  1) DiscordBot::Car works
     Failure/Error: car = build(:car)

     NameError:
       uninitialized constant Car
     # ./spec/models/discord_bot/car_spec.rb:6:in `block (2 levels) in <module:DiscordBot>'

I can fix this by manually editing the factory like so:

FactoryBot.define do
  factory :car, class: "DiscordBot::Car" do
    name { "MyString" }
  end
end

In order to get this to work in an Engine, I had to pass --skip-namespace with the generator, but namespace the model itself, as below:

bundle exec rails g model DiscordBot::Car name:string --skip-namespace

which results in the following:

# app/models/discord_bot/car.rb
class DiscordBot::Car < ApplicationRecord
end

# spec/factories/discord_bot/cars.rb
FactoryBot.define do
  factory :discord_bot_car, class: 'DiscordBot::Car' do
    name { "MyString" }
  end
end

# spec/models/discord_bot/car_spec.rb
require 'rails_helper'

RSpec.describe DiscordBot::Car, type: :model do
  # basic spec to test
  it "works" do
    car = build(:discord_bot_car)
    expect(car).to be_present
  end
end

And then it works without any manual changes (other than writing the tests).

System configuration

factory_bot_rails version: 6.4.3 factory_bot version: 6.4.6 rails version: 7.1.3.3 ruby version: 3.2.3