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

Question: Possible to get reference to the build_class from initialize_with? #1575

Open
jasonkarns opened this issue May 22, 2023 · 2 comments

Comments

@jasonkarns
Copy link

I'm working with STI and am customizing our initialize_with block. However, there are some conditions we'd like to put in place that require introspection on the class in question.

It seems there are a number of layers of indirection around the class (decorator, @component, @build_class, etc). Is there a way to get a reference to the factory's class (essentially new.class, but without invoking new) from within the initialize_with block?

@AndrewSwerlick
Copy link

I'd like to bump this questions because I spent a long time digging into this problem before concluding it's just not possible. I'm trying to build a factorybot plugin/integration for an framework we have for managing data from third party api responses. We're using factorybot to build up these non active record data objects which are serialized from the response data. We don't use new when serializing these objects, so I'd like to be able to have a custom factorybot method that users can add to any factory to indicate this is a "API object factory", and have that method setup the correct initialize_with block for them. As is the only way I can do that is write a method like this

  def api_object_factory(build_class)
    initialize_with { build_class.from_hash(attributes) }  
  end

So users are forced to specific the class twice when defining their factory

  factory :api_user, class: "Api::User" do 
    api_object_factory("Api::User")
  end

Would love to make this a little less repetitive

@AndrewSwerlick
Copy link

I figured out a way around my particular problem, by doing this

  module FactoryHelpers
    def api_object_factory(name, options = {}, &block)
      raise ArgumentError, "You must specify a class" unless options[:class]
      raise ArgumentError, "You must specify a block" unless block

      factory(name, options) do
        skip_create

        initialize_with do
          options[:class].constantize.from_hash(attributes)
        end

        instance_eval(&block)
      end
    end
  end
  
  class FactoryBot::Syntax::Default::DSL
    include FactoryHelpers
  end

So now consumers don't use the normal factory method and instead write code like

FactoryBot.define do 
  api_object_factory(:api_user, class: 'Api::User') {}

This is a fairly reasonable experience in my opinion. But is extending the default DSL a supported extension from the FactoryBot perspective?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants