定义知道自己 class 的工厂并查找值

Defining factories that know their own class and look up the values

我有一个验证器 class,它为 FooBar 类型的对象定义有效事件。每个 Foo 对象都有属性 alert_eventswindow_eventsuser_events,每个 Bar 对象都有 state_eventsuser_events

class EventListConformanceValidator
  VALID_EVENTS = {
    foo: {
      alert_events: {
        onAlertLoad: String,
        onAlertUnload: String,
      },
      window_events: {
        onWindowLoaded: Array,
      },
      user_events: {
        onLoginStatusChanged: String,
      },
    },
    bar: {
      state_events: {
        onAborted: Float,
      },
      user_events: {
        skipClicked: nil
      }
    }
  }
end

我想使用 FactoryBot 动态生成工厂,它将为每个属性填充正确的事件类型。例如,对于 alert_events,只应创建类型为 onAlertLoadonAlertUnload 的事件。

天真地,我可以写出以下内容:

class Event
  attr_accessor :type
end

FactoryBot.define do
  factory :event do
    type { nil }
  end

  factory :alert_event do
    type { ["onAlertLoad", "onAlertUnload"].sample }
  end

  factory :window_events do
    # and so on ...
  end
end

但显然这会重复很多次。相反,我想要一个单一的事实来源,并从哈希 VALID_EVENTS 中获取定义。此外,还有一个问题是 user_events 可以同时存在于 FooBar,所以我不能单独使用 user_events,因为它会产生歧义。

所以我尝试动态生成工厂foo_alert_eventfoo_window_event等:

EventListConformanceValidator::VALID_EVENTS.each do |klass, attribute_hash|
    attribute_hash.keys.map(&:to_s).uniq.each do |attribute|
      factory_name = (klass.to_s + "_" + attribute.to_s.singularize).to_sym

      factory factory_name, class: Event, parent: :event do
        type { "how do I get the event types to sample?" }
      end

    end
end

但是在type的动态求值中,我不知道如何引用实际的工厂class名称(foo/bar)或者属性名称(alert_events 等)以便我可以在 VALID_EVENTS.

中查找键

我怎样才能做到这一点?

听起来你想要这样的东西(但我不明白你想要达到什么目的)

VALID_EVENTS = { 
  foo: { 
    alert_events: { 
      onAlertLoad: String,
      onAlertUnload: String,
    },
    window_events: { 
      onWindowLoaded: Array,
    },
    user_events: { 
      onLoginStatusChanged: String,
    },
  },
  bar: { 
    state_events: { 
      onAborted: Float,
    },
    user_events: { 
      skipClicked: nil,
    },
  },
}.freeze

class Event
  attr_accessor :type
end

FactoryBot.define do
  VALID_EVENTS.each do |klass, attribute_hash|
    attribute_hash.each do |name, events|
      factory_name = "#{klass}_#{name.to_s.singularize}".to_sym
      types = events.keys.map(&:to_s)

      factory factory_name, class: Event do
        type { types } 
      end
    end
  end
end

describe "SO" do
  it "works" do
    klass = build(:foo_alert_event)
    expect(klass.type).to eq(["onAlertLoad", "onAlertUnload"])
  end
end
rspec
SO
  works

Finished in 0.94792 seconds (files took 3.25 seconds to load)
1 example, 0 failures