FactoryBot namespaced models without class_name
Asked Answered
L

3

7

I have models which are namespaced such as this:

class Vehicle < ActiveRecord::Base; end

class Vehicle::Car < Vehicle; end
class Vehicle::Train < Vehicle; end
class Vehicle::Jet < Vehicle; end

When creating factories for these models, they were set up in the following way:

factory :vehicle_car, class: Vehicle::Car do; end
factory :vehicle_train, class: Vehicle::Train do; end
factory :vehicle_jet, class: Vehicle::Jet do; end

This produces the following deprecation warning:

DEPRECATION WARNING: Looking up factories by class is deprecated and will be removed in 5.0. Use symbols instead and set FactoryBot.allow_class_lookup = false.

Is there a format for writing a symbol to name these factories such that I do not need to use the class name to comply with the deprecation warning?

Largescale answered 20/5, 2018 at 18:11 Comment(3)
Which version of FactoryBot are you using? I had a go at reproducing this and was able to define your factories as above without any warnings displayed.Knit
@Knit Try 4.9, you might need to pull it off GitHub though. At least that's were I found the warning message.Bilski
I'm using factory_bot_rails which is only at 4.8.2Largescale
B
12

The documentation wasn't terribly useful as to how the :class option behaves or what it expects as its value but the source was more helpful. Backtracking from the error message we find FactoryBot::Decorator::ClassKeyHash#symbolize_keys:

def symbolized_key(key)
  if key.respond_to?(:to_sym)
    key.to_sym
  elsif FactoryBot.allow_class_lookup
    ActiveSupport::Deprecation.warn "Looking up factories by class is deprecated and will be removed in 5.0. Use symbols instead and set FactoryBot.allow_class_lookup = false", caller
    key.to_s.underscore.to_sym
  end
end

The key.to_sym in the first branch is the usual idiom for "I want a Symbol or String". The key.to_s.underscore.to_sym in the second branch tells us what format is expected.

If you run Vehicle::Car through to_s.underscore, you get 'vehicle/car' so these should work:

factory :vehicle_car,   class: 'vehicle/car'   do; end
factory :vehicle_train, class: 'vehicle/train' do; end
factory :vehicle_jet,   class: 'vehicle/jet'   do; end

or if you really want Symbols (or have a thing for punctuation):

factory :vehicle_car,   class: :'vehicle/car'   do; end
factory :vehicle_train, class: :'vehicle/train' do; end
factory :vehicle_jet,   class: :'vehicle/jet'   do; end
Bilski answered 20/5, 2018 at 19:21 Comment(5)
I may have tried to implement your suggestion incorrectly but I am still getting the deprecation warning. The warning itself seems to indicate that using the class: option is what is being deprecated, rather than passing the value of the class itself, to the best of my understanding.Largescale
I set up an equivalent situation to what you have and it works for me. Are you sure you got them all? The warning (and the code) say that class: SomeClass is deprecated and you should use class: :symbol or class: 'string' instead.Bilski
ahh okay cool. let me give it another try and i'll get back to you. thanks so much for helping!Largescale
still not working for me but it seems that the error is in an after(:build) block where a create() for another factory that uses class_name (which I've since refactored to the suggestion above) is being invoked. marking this as the best answer despite my own deprecation warnings :) thanks for the assistanceLargescale
You could convert that comment into an answer and accept your own answer (after AFAIK 24 hours). That's important information and comments have a way of not being read. Glad to have helped in any case.Bilski
K
5

Also you can:

    factory Vehicle::Car.to_s.underscore.to_sym, class: Vehicle::Car { }
Kellar answered 4/6, 2018 at 11:33 Comment(0)
S
1

You can just use the fully qualified class name in string form:

factory :vehicle_car,   class: 'Vehicle::Car'   do; end
factory :vehicle_train, class: 'Vehicle::Train' do; end
factory :vehicle_jet,   class: 'Vehicle::Jet'   do; end

If you're set on working with symbols, :'Vehicle::Car' (as suggested elsewhere) or 'Vehicle::Car'.to_sym should work, although I'm not sure how useful this is in the context of tests.

Scleritis answered 17/9, 2020 at 17:2 Comment(1)
I'd say this is a more correct answer. FactoryBot is trying to get you away from loading the class your defining. By using the string version as above you define it as a reference and the class never loads. Plus it reads a little clearer.Antidepressant

© 2022 - 2024 — McMap. All rights reserved.