如何基于VALID_EVENTS动态生成FactoryBot关联类事件工厂?
Problem Context
You're trying to dynamically generate FactoryBot factories for your Event class, using the VALID_EVENTS hash from EventListConformanceValidator as the single source of truth. The goal is to create factories like foo_alert_event or bar_state_event that produce Event instances with valid type values matching the rules for each attribute (e.g., alert_events can only have onAlertLoad or onAlertUnload).
The issue with your current code is that the type block inside the factory definition can't access the correct klass (like foo/bar) and attribute (like alert_events) values from the outer loop—by the time FactoryBot executes these blocks, the loop variables have already been updated to their final iteration values.
Solution
The fix relies on Ruby's closure behavior to capture the correct values for each iteration. Instead of relying on the loop variables directly, we'll create local variables inside each loop iteration to hold the current klass, attribute, and its associated event types. This ensures each factory's type block references the values specific to that factory.
Here's the revised code:
FactoryBot.define do factory :event do type { nil } end EventListConformanceValidator::VALID_EVENTS.each do |klass, attribute_hash| # Iterate over each attribute and its valid event types directly attribute_hash.each do |attribute, event_types| # Capture current iteration values in local variables to preserve closure context current_klass = klass current_event_types = event_types # Generate the factory name as requested (e.g., foo_alert_event) factory_name = "#{current_klass}_#{attribute.to_s.singularize}".to_sym factory factory_name, class: Event, parent: :event do # Use the captured event types to pick a valid type type { current_event_types.keys.sample.to_s } end end end end
Key Explanations:
- Direct Iteration Over Attribute Hash: Instead of extracting keys and uniq-ing them, we iterate directly over
attribute_hash's key-value pairs. This lets us access the valid event types (event_types) for each attribute instantly, avoiding extra steps. - Local Variable Capture: By assigning
current_klassandcurrent_event_typesinside each iteration, we create a new variable binding that the factory'stypeblock will capture. This prevents all factories from using the final loop values (a common pitfall with delayed-execution blocks in Ruby). - Valid Type Selection: Using
current_event_types.keys.sample.to_spicks a random valid event type from the allowed list for that attribute. If you need a fixed default instead of random, replacesamplewithfirstor specify a specific key.
Example Output:
- Calling
create(:foo_alert_event)will generate anEventwithtypeset to either"onAlertLoad"or"onAlertUnload". - Calling
create(:bar_user_event)will generate anEventwithtypeset to"skipClicked".
内容的提问来源于stack exchange,提问作者slhck

