Reputation: 38691
I have a validator class that defines valid events for objects of type Foo
and Bar
. Each Foo
object has attributes alert_events
, window_events
and user_events
, and each Bar
object has state_events
and user_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
I want to dynamically generate factories using FactoryBot which will populate the correct event types for each attribute. For example, for alert_events
, only events with type onAlertLoad
or onAlertUnload
should be created.
Naively, I could write the following:
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
But obviously that'd be a lot of repetition. Instead I want a single source of truth, and source the definitions from the Hash VALID_EVENTS
. Also, there is the problem that user_events
can exist both for Foo
and Bar
, so I cannot use user_events
alone, as it'd be ambiguous.
So I tried to dynamically generate factories foo_alert_event
, foo_window_event
etc.:
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
But inside the dynamic evaluation of type
, I don't know how I could refer to the actual factory class name (foo
/bar
) or attribute name (alert_events
etc.) so I can look up the keys in VALID_EVENTS
.
How could I achieve that?
Upvotes: 0
Views: 359
Reputation: 299
It sounds like you want something like this (however I don't understand what you are trying to achieve)
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
Upvotes: 1