factory_seeder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +111 -0
- data/LICENSE.txt +21 -0
- data/README.md +445 -0
- data/app/assets/stylesheets/factory_seeder.css +637 -0
- data/app/controllers/factory_seeder/application_controller.rb +8 -0
- data/app/controllers/factory_seeder/custom_seeds_controller.rb +134 -0
- data/app/controllers/factory_seeder/dashboard_controller.rb +36 -0
- data/app/controllers/factory_seeder/factory_controller.rb +70 -0
- data/app/views/factory_seeder/custom_seeds/index.html.erb +51 -0
- data/app/views/factory_seeder/custom_seeds/show.html.erb +113 -0
- data/app/views/factory_seeder/dashboard/index.html.erb +99 -0
- data/app/views/factory_seeder/factory/index.html.erb +71 -0
- data/app/views/factory_seeder/factory/show.html.erb +108 -0
- data/app/views/factory_seeder/seeds/show.html.erb +2 -0
- data/app/views/layouts/factory_seeder/application.html.erb +25 -0
- data/bin/factory_seeder +27 -0
- data/config/factory_seeder.rb +24 -0
- data/config/routes.rb +20 -0
- data/lib/factory_seeder/asset_helper.rb +34 -0
- data/lib/factory_seeder/cli.rb +352 -0
- data/lib/factory_seeder/configuration.rb +32 -0
- data/lib/factory_seeder/custom_seed_loader.rb +39 -0
- data/lib/factory_seeder/engine.rb +16 -0
- data/lib/factory_seeder/execution_log_store.rb +48 -0
- data/lib/factory_seeder/factory_scanner.rb +149 -0
- data/lib/factory_seeder/loader.rb +26 -0
- data/lib/factory_seeder/rails_integration.rb +29 -0
- data/lib/factory_seeder/seed.rb +102 -0
- data/lib/factory_seeder/seed_builder.rb +67 -0
- data/lib/factory_seeder/seed_generator.rb +305 -0
- data/lib/factory_seeder/seed_manager.rb +128 -0
- data/lib/factory_seeder/seeder.rb +41 -0
- data/lib/factory_seeder/version.rb +5 -0
- data/lib/factory_seeder/web_interface.rb +119 -0
- data/lib/factory_seeder.rb +209 -0
- data/templates/seed_template.rb +84 -0
- metadata +276 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FactorySeeder
|
|
4
|
+
class CustomSeedsController < ApplicationController
|
|
5
|
+
def index
|
|
6
|
+
@seeds = FactorySeeder.list_custom_seeds
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def show
|
|
10
|
+
@seed = FactorySeeder.find_custom_seed(params[:name])
|
|
11
|
+
if @seed.nil?
|
|
12
|
+
flash[:error] = "Seed '#{params[:name]}' not found"
|
|
13
|
+
redirect_to custom_seeds_path
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
|
+
@seed_name = @seed.name
|
|
17
|
+
@execution_logs = []
|
|
18
|
+
|
|
19
|
+
# Retrieve logs from temporary storage if available (PRG pattern)
|
|
20
|
+
if params[:log_id].present?
|
|
21
|
+
stored = ExecutionLogStore.retrieve(params[:log_id])
|
|
22
|
+
if stored
|
|
23
|
+
@execution_logs = stored[:logs] || []
|
|
24
|
+
flash.now[stored[:flash_type]] = stored[:flash_message] if stored[:flash_type]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create
|
|
30
|
+
seed_name = params[:name]
|
|
31
|
+
seed = FactorySeeder.find_custom_seed(seed_name)
|
|
32
|
+
|
|
33
|
+
if seed.nil?
|
|
34
|
+
flash[:error] = "Seed '#{seed_name}' not found"
|
|
35
|
+
redirect_to custom_seeds_path
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
attributes = safe_attributes_params
|
|
40
|
+
result = FactorySeeder.run_custom_seed(seed_name, **attributes)
|
|
41
|
+
logs = result[:logs] || []
|
|
42
|
+
|
|
43
|
+
flash_type = result[:success] ? :success : :error
|
|
44
|
+
log_id = ExecutionLogStore.store(logs, flash_type: flash_type, flash_message: result[:message])
|
|
45
|
+
|
|
46
|
+
redirect_to custom_seed_path(seed_name, log_id: log_id)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def new
|
|
50
|
+
# For creating new seeds via web interface (future feature)
|
|
51
|
+
@seed = nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def edit
|
|
55
|
+
@seed = FactorySeeder.find_custom_seed(params[:name])
|
|
56
|
+
return unless @seed.nil?
|
|
57
|
+
|
|
58
|
+
flash[:error] = "Seed '#{params[:name]}' not found"
|
|
59
|
+
redirect_to custom_seeds_path
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def update
|
|
64
|
+
# For updating existing seeds (future feature)
|
|
65
|
+
seed_name = params[:name]
|
|
66
|
+
# Implementation would go here
|
|
67
|
+
redirect_to custom_seed_path(seed_name)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def destroy
|
|
71
|
+
# For deleting seeds (future feature)
|
|
72
|
+
params[:name]
|
|
73
|
+
# Implementation would go here
|
|
74
|
+
redirect_to custom_seeds_path
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def safe_attributes_params
|
|
80
|
+
if params.key?(:attributes)
|
|
81
|
+
# Convert string values to appropriate types based on seed parameter definitions
|
|
82
|
+
raw_attributes = params.require(:attributes).permit!
|
|
83
|
+
seed = FactorySeeder.find_custom_seed(params[:name])
|
|
84
|
+
|
|
85
|
+
if seed
|
|
86
|
+
convert_attributes_to_types(raw_attributes, seed)
|
|
87
|
+
else
|
|
88
|
+
raw_attributes.transform_keys(&:to_sym)
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
{}
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def convert_attributes_to_types(raw_attributes, seed)
|
|
96
|
+
converted = {}
|
|
97
|
+
|
|
98
|
+
raw_attributes.each do |key, value|
|
|
99
|
+
param_info = seed.parameter_info(key)
|
|
100
|
+
converted[key.to_sym] = convert_value_to_type(value, param_info)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
converted
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def convert_value_to_type(value, param_info)
|
|
107
|
+
return value if value.blank?
|
|
108
|
+
|
|
109
|
+
case param_info&.dig(:type)
|
|
110
|
+
when :integer
|
|
111
|
+
value.to_i
|
|
112
|
+
when :boolean
|
|
113
|
+
case value.to_s.downcase
|
|
114
|
+
when 'true', '1', 'yes', 'on'
|
|
115
|
+
true
|
|
116
|
+
when 'false', '0', 'no', 'off'
|
|
117
|
+
false
|
|
118
|
+
else
|
|
119
|
+
value
|
|
120
|
+
end
|
|
121
|
+
when :symbol
|
|
122
|
+
value.to_sym
|
|
123
|
+
when :array
|
|
124
|
+
if value.is_a?(String)
|
|
125
|
+
value.split(',').map(&:strip)
|
|
126
|
+
else
|
|
127
|
+
value
|
|
128
|
+
end
|
|
129
|
+
else
|
|
130
|
+
value
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FactorySeeder
|
|
4
|
+
class DashboardController < ApplicationController
|
|
5
|
+
def index
|
|
6
|
+
@factories = FactorySeeder.scan_loaded_factories
|
|
7
|
+
@seeds_info = FactorySeeder.list_seeds
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run_seed
|
|
11
|
+
params[:name]
|
|
12
|
+
|
|
13
|
+
begin
|
|
14
|
+
flash[:info] = 'Seed system coming soon! For now, use the factory generation interface.'
|
|
15
|
+
redirect_to root_path
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
flash[:error] = "Error running seed: #{e.message}"
|
|
18
|
+
redirect_to root_path
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run_all_seeds
|
|
23
|
+
flash[:info] = 'Seed system coming soon! For now, use the factory generation interface.'
|
|
24
|
+
redirect_to root_path
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
flash[:error] = "Error running seeds: #{e.message}"
|
|
27
|
+
redirect_to root_path
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def get_seeds_info
|
|
33
|
+
FactorySeeder.list_seeds.keys
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FactorySeeder
|
|
4
|
+
class FactoryController < ApplicationController
|
|
5
|
+
def index
|
|
6
|
+
@factories = FactorySeeder.scan_loaded_factories
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def show
|
|
10
|
+
@factory_name = params[:name]
|
|
11
|
+
@factories = FactorySeeder.scan_loaded_factories
|
|
12
|
+
@factory = @factories[@factory_name]
|
|
13
|
+
@execution_logs = []
|
|
14
|
+
|
|
15
|
+
# Retrieve logs from temporary storage if available (PRG pattern)
|
|
16
|
+
if params[:log_id].present?
|
|
17
|
+
stored = ExecutionLogStore.retrieve(params[:log_id])
|
|
18
|
+
if stored
|
|
19
|
+
@execution_logs = stored[:logs] || []
|
|
20
|
+
flash.now[stored[:flash_type]] = stored[:flash_message] if stored[:flash_type]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
return if @factory
|
|
25
|
+
|
|
26
|
+
redirect_to root_path, alert: "Factory '#{@factory_name}' not found"
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate
|
|
31
|
+
factory_name = params[:name]
|
|
32
|
+
count = (params[:count] || 1).to_i
|
|
33
|
+
traits = parse_traits(params[:selected_traits])
|
|
34
|
+
|
|
35
|
+
begin
|
|
36
|
+
generator = SeedGenerator.new
|
|
37
|
+
result = generator.generate(factory_name, count, traits, generate_params[:attributes].to_h.compact_blank)
|
|
38
|
+
logs = result[:logs] || []
|
|
39
|
+
|
|
40
|
+
if result[:errors].any?
|
|
41
|
+
log_id = ExecutionLogStore.store(logs, flash_type: :error,
|
|
42
|
+
flash_message: "Error generating seeds: #{result[:errors].join(', ')}")
|
|
43
|
+
else
|
|
44
|
+
log_id = ExecutionLogStore.store(logs, flash_type: :success,
|
|
45
|
+
flash_message: "Successfully generated #{result[:count]} #{factory_name} records")
|
|
46
|
+
end
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
log_id = ExecutionLogStore.store([], flash_type: :error, flash_message: "Error generating seeds: #{e.message}")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
redirect_to factory_path(factory_name, log_id: log_id)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def parse_traits(traits_param)
|
|
57
|
+
return [] if traits_param.blank?
|
|
58
|
+
|
|
59
|
+
if traits_param.is_a?(String)
|
|
60
|
+
traits_param.split(',').map(&:strip).reject(&:blank?)
|
|
61
|
+
else
|
|
62
|
+
traits_param
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def generate_params
|
|
67
|
+
params.permit(:name, :count, :selected_traits, attributes: {})
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<div class="page-shell">
|
|
2
|
+
<div class="scanlines"></div>
|
|
3
|
+
<div class="grid-background"></div>
|
|
4
|
+
<div class="page-content">
|
|
5
|
+
<header class="section-title">Custom Seeds (<%= @seeds.count %>)</header>
|
|
6
|
+
|
|
7
|
+
<% if flash[:success] %>
|
|
8
|
+
<div class="alert-message alert-message--success"><%= flash[:success] %></div>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<% if flash[:error] %>
|
|
12
|
+
<div class="alert-message alert-message--error"><%= flash[:error] %></div>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<% if @seeds.empty? %>
|
|
16
|
+
<div class="alert-message alert-message--info">
|
|
17
|
+
No custom seed files detected. Drop your Ruby seed scripts under <code>db/seeds</code>
|
|
18
|
+
or configure them via the FactorySeeder DSL.
|
|
19
|
+
</div>
|
|
20
|
+
<% else %>
|
|
21
|
+
<div class="seed-grid">
|
|
22
|
+
<% @seeds.each do |seed| %>
|
|
23
|
+
<% parameter_count = seed.parameter_names.count %>
|
|
24
|
+
<article class="seed-card">
|
|
25
|
+
<div class="seed-card__header">
|
|
26
|
+
<div>
|
|
27
|
+
<div class="seed-card__title"><%= seed.name.to_s.humanize %></div>
|
|
28
|
+
<div class="factory-card__meta"><%= seed.description %></div>
|
|
29
|
+
</div>
|
|
30
|
+
<span class="seed-card__tag"><%= pluralize(parameter_count, 'parameter') %></span>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="seed-card__description">
|
|
34
|
+
<% if seed.has_parameters? %>
|
|
35
|
+
<% seed.parameter_names.each do |param_name| %>
|
|
36
|
+
<span class="seed-card__tag"><%= param_name.to_s.humanize %></span>
|
|
37
|
+
<% end %>
|
|
38
|
+
<% else %>
|
|
39
|
+
<span class="seed-card__tag">No parameters</span>
|
|
40
|
+
<% end %>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="seed-card__actions">
|
|
44
|
+
<%= link_to "Inspect & Run", custom_seed_path(seed.name), class: "btn btn-accent" %>
|
|
45
|
+
</div>
|
|
46
|
+
</article>
|
|
47
|
+
<% end %>
|
|
48
|
+
</div>
|
|
49
|
+
<% end %>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<div class="page-shell">
|
|
2
|
+
<div class="scanlines"></div>
|
|
3
|
+
<div class="grid-background"></div>
|
|
4
|
+
<div class="page-content">
|
|
5
|
+
<header class="section-title">Custom Seed · <%= @seed.name.to_s.humanize %></header>
|
|
6
|
+
<div class="factory-card__label">
|
|
7
|
+
<%= link_to "← Back to seeds", custom_seeds_path, class: "btn btn-accent" %>
|
|
8
|
+
</div>
|
|
9
|
+
<p class="hero__subtitle"><%= @seed.description %></p>
|
|
10
|
+
|
|
11
|
+
<% if flash[:success] %>
|
|
12
|
+
<div class="alert-message alert-message--success"><%= flash[:success] %></div>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<% if flash[:error] %>
|
|
16
|
+
<div class="alert-message alert-message--error"><%= flash[:error] %></div>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<% if flash[:info] %>
|
|
20
|
+
<div class="alert-message alert-message--info"><%= flash[:info] %></div>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
<div class="factory-show-grid">
|
|
24
|
+
<section class="panel seed-detail">
|
|
25
|
+
<div class="seed-detail__hero">
|
|
26
|
+
<h2 class="seed-detail__title"><%= @seed.name.to_s.humanize %></h2>
|
|
27
|
+
<p class="seed-detail__subtitle"><%= @seed.description %></p>
|
|
28
|
+
<div class="seed-card__tags">
|
|
29
|
+
<span class="seed-card__tag"><%= pluralize(@seed.parameter_names.count, 'parameter') %></span>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="seed-detail-form panel__body">
|
|
34
|
+
<% if @seed.has_parameters? %>
|
|
35
|
+
<div class="panel__title">Parameters</div>
|
|
36
|
+
<%= form_tag create_custom_seede_path(@seed_name), method: :post do %>
|
|
37
|
+
<% @seed.parameter_names.each do |param_name| %>
|
|
38
|
+
<% param_info = @seed.parameter_info(param_name) %>
|
|
39
|
+
<div class="form-group">
|
|
40
|
+
<label for="<%= param_name %>">
|
|
41
|
+
<%= param_name.to_s.humanize %>
|
|
42
|
+
<% if param_info[:required] %>
|
|
43
|
+
<span class="required">*</span>
|
|
44
|
+
<% end %>
|
|
45
|
+
</label>
|
|
46
|
+
<% if param_info[:description].present? %>
|
|
47
|
+
<small class="parameter-description"><%= param_info[:description] %></small>
|
|
48
|
+
<% end %>
|
|
49
|
+
|
|
50
|
+
<% case param_info[:type] %>
|
|
51
|
+
<% when :boolean %>
|
|
52
|
+
<%= select_tag "attributes[#{param_name}]", options_for_select([['Select...', ''], ['Yes', 'true'], ['No', 'false']], param_info[:default]&.to_s), class: "form-control" %>
|
|
53
|
+
<% when :integer %>
|
|
54
|
+
<%= number_field_tag "attributes[#{param_name}]", param_info[:default], class: "form-control", min: param_info[:min], max: param_info[:max], placeholder: "Enter value" %>
|
|
55
|
+
<% else %>
|
|
56
|
+
<%= text_field_tag "attributes[#{param_name}]", param_info[:default], class: "form-control", placeholder: "Enter #{param_name.to_s.humanize.downcase}" %>
|
|
57
|
+
<% end %>
|
|
58
|
+
|
|
59
|
+
<% if param_info[:allowed_values].present? && param_info[:type] != :symbol %>
|
|
60
|
+
<small class="allowed-values">Allowed: <%= param_info[:allowed_values].join(', ') %></small>
|
|
61
|
+
<% end %>
|
|
62
|
+
</div>
|
|
63
|
+
<% end %>
|
|
64
|
+
|
|
65
|
+
<div class="form-actions">
|
|
66
|
+
<%= submit_tag "Run Seed", class: "btn btn-primary" %>
|
|
67
|
+
</div>
|
|
68
|
+
<% end %>
|
|
69
|
+
<% else %>
|
|
70
|
+
<div class="alert-message alert-message--info">
|
|
71
|
+
This seed does not expose any parameters. It will execute with its default settings.
|
|
72
|
+
</div>
|
|
73
|
+
<div class="form-actions">
|
|
74
|
+
<%= form_tag create_custom_seede_path(@seed_name), method: :post do %>
|
|
75
|
+
<%= submit_tag "Run Seed", class: "btn btn-primary" %>
|
|
76
|
+
<% end %>
|
|
77
|
+
</div>
|
|
78
|
+
<% end %>
|
|
79
|
+
</div>
|
|
80
|
+
</section>
|
|
81
|
+
|
|
82
|
+
<section class="panel console">
|
|
83
|
+
<div class="console__header">
|
|
84
|
+
<span>Execution Console</span>
|
|
85
|
+
<span class="factory-card__meta">Logs stream here when running.</span>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="console__body">
|
|
88
|
+
<% logs = @execution_logs || [] %>
|
|
89
|
+
<% if logs.any? %>
|
|
90
|
+
<div class="space-y-1">
|
|
91
|
+
<% logs.each do |log| %>
|
|
92
|
+
<% entry = log.is_a?(Hash) ? log : log.to_h %>
|
|
93
|
+
<% message = entry['message'] || entry[:message] || entry.to_s %>
|
|
94
|
+
<% level = (entry['level'] || entry[:level] || :info).to_s %>
|
|
95
|
+
<% timestamp_value = entry['timestamp'] || entry[:timestamp] || Time.now %>
|
|
96
|
+
<% timestamp =
|
|
97
|
+
timestamp_value.respond_to?(:strftime) ? timestamp_value.strftime('%H:%M:%S') : timestamp_value.to_s %>
|
|
98
|
+
<div class="console-log console-log--<%= level %>">
|
|
99
|
+
<span class="text-muted-foreground">[<%= timestamp %>]</span>
|
|
100
|
+
<span><%= message %></span>
|
|
101
|
+
</div>
|
|
102
|
+
<% end %>
|
|
103
|
+
</div>
|
|
104
|
+
<% else %>
|
|
105
|
+
<div class="console-log">
|
|
106
|
+
<span>[ --:--:-- ]</span>Waiting for execution...
|
|
107
|
+
</div>
|
|
108
|
+
<% end %>
|
|
109
|
+
</div>
|
|
110
|
+
</section>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<div class="page-shell">
|
|
2
|
+
<div class="scanlines"></div>
|
|
3
|
+
<div class="grid-background"></div>
|
|
4
|
+
<div class="page-content">
|
|
5
|
+
<section class="hero">
|
|
6
|
+
<div class="hero__icon">🛠️</div>
|
|
7
|
+
<h1 class="hero__title">Factory Seeder</h1>
|
|
8
|
+
<p class="hero__subtitle">
|
|
9
|
+
Industrial-grade seed generation that keeps your FactoryBot definitions
|
|
10
|
+
and custom scripts in sync with production-ready tooling.
|
|
11
|
+
</p>
|
|
12
|
+
<div class="hero__flair">
|
|
13
|
+
<span>FactoryBot</span>
|
|
14
|
+
<span>|</span>
|
|
15
|
+
<span>Custom Seeds</span>
|
|
16
|
+
<span>|</span>
|
|
17
|
+
<span>Live Logs</span>
|
|
18
|
+
</div>
|
|
19
|
+
</section>
|
|
20
|
+
|
|
21
|
+
<% if flash[:success] %>
|
|
22
|
+
<div class="alert-message alert-message--success">
|
|
23
|
+
<%= flash[:success] %>
|
|
24
|
+
</div>
|
|
25
|
+
<% end %>
|
|
26
|
+
|
|
27
|
+
<% if flash[:error] %>
|
|
28
|
+
<div class="alert-message alert-message--error">
|
|
29
|
+
<%= flash[:error] %>
|
|
30
|
+
</div>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<% if flash[:info] %>
|
|
34
|
+
<div class="alert-message alert-message--info">
|
|
35
|
+
<%= flash[:info] %>
|
|
36
|
+
</div>
|
|
37
|
+
<% end %>
|
|
38
|
+
|
|
39
|
+
<% factories_with_traits = @factories.count { |_, info| info[:traits].present? } %>
|
|
40
|
+
<div class="stat-row">
|
|
41
|
+
<div class="stat-card">
|
|
42
|
+
<div class="stat-card__value"><%= @factories.count %></div>
|
|
43
|
+
<div class="stat-card__label">Loaded Factories</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="stat-card">
|
|
46
|
+
<div class="stat-card__value"><%= @seeds_info.count %></div>
|
|
47
|
+
<div class="stat-card__label">Custom Seeds</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="stat-card">
|
|
50
|
+
<div class="stat-card__value"><%= factories_with_traits %></div>
|
|
51
|
+
<div class="stat-card__label">Factories w/ Traits</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="stat-card">
|
|
54
|
+
<div class="stat-card__value">Ready</div>
|
|
55
|
+
<div class="stat-card__label">System Status</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div class="mode-grid">
|
|
60
|
+
<article class="mode-card">
|
|
61
|
+
<div class="mode-card__header">
|
|
62
|
+
<span class="mode-card__icon">🏭</span>
|
|
63
|
+
<h2 class="mode-card__title">Factories</h2>
|
|
64
|
+
</div>
|
|
65
|
+
<p class="mode-card__description">
|
|
66
|
+
Generate Faker data directly from your FactoryBot definitions. Tweak traits,
|
|
67
|
+
associations, and batch counts before execution.
|
|
68
|
+
</p>
|
|
69
|
+
<div class="mode-card__tags">
|
|
70
|
+
<% @factories.each_key.take(3).each do |name| %>
|
|
71
|
+
<span class="mode-card__tag"><%= name %></span>
|
|
72
|
+
<% end %>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="mode-card__actions">
|
|
75
|
+
<%= link_to "Open factories", factory_index_path, class: "btn btn-primary" %>
|
|
76
|
+
</div>
|
|
77
|
+
</article>
|
|
78
|
+
|
|
79
|
+
<article class="mode-card">
|
|
80
|
+
<div class="mode-card__header">
|
|
81
|
+
<span class="mode-card__icon">💾</span>
|
|
82
|
+
<h2 class="mode-card__title">Custom Seeds</h2>
|
|
83
|
+
</div>
|
|
84
|
+
<p class="mode-card__description">
|
|
85
|
+
Run your bespoke Ruby seed files with the same industrial UI. Logs, progress,
|
|
86
|
+
and retry actions are available from the execution screen.
|
|
87
|
+
</p>
|
|
88
|
+
<div class="mode-card__tags">
|
|
89
|
+
<% @seeds_info.each_key.take(3).each do |seed| %>
|
|
90
|
+
<span class="mode-card__tag"><%= seed.to_s.humanize %></span>
|
|
91
|
+
<% end %>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="mode-card__actions">
|
|
94
|
+
<%= link_to "View seeds", custom_seeds_path, class: "btn btn-accent" %>
|
|
95
|
+
</div>
|
|
96
|
+
</article>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<div class="page-shell">
|
|
2
|
+
<div class="scanlines"></div>
|
|
3
|
+
<div class="grid-background"></div>
|
|
4
|
+
<div class="page-content">
|
|
5
|
+
<header class="section-title">Available Factories (<%= @factories.count %>)</header>
|
|
6
|
+
|
|
7
|
+
<% if flash[:success] %>
|
|
8
|
+
<div class="alert-message alert-message--success"><%= flash[:success] %></div>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<% if flash[:error] %>
|
|
12
|
+
<div class="alert-message alert-message--error"><%= flash[:error] %></div>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<% if flash[:info] %>
|
|
16
|
+
<div class="alert-message alert-message--info"><%= flash[:info] %></div>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<% if @factories.empty? %>
|
|
20
|
+
<div class="alert-message alert-message--info">
|
|
21
|
+
No FactoryBot definitions were detected yet. Confirm there are files under
|
|
22
|
+
<code>spec/factories/</code> or <code>test/factories/</code>, then refresh.
|
|
23
|
+
</div>
|
|
24
|
+
<% else %>
|
|
25
|
+
<div class="factories-grid">
|
|
26
|
+
<% @factories.each do |name, info| %>
|
|
27
|
+
<% traits = Array(info[:traits]) %>
|
|
28
|
+
<% associations = Array(info[:associations]) %>
|
|
29
|
+
<article class="factory-card">
|
|
30
|
+
<div class="factory-card__header">
|
|
31
|
+
<div>
|
|
32
|
+
<div class="factory-card__title"><%= name %></div>
|
|
33
|
+
<div class="factory-card__meta"><%= info[:class_name] %></div>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="factory-card__label">Live</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<% if traits.any? %>
|
|
39
|
+
<div class="factory-card__badges">
|
|
40
|
+
<% traits.first(4).each do |trait| %>
|
|
41
|
+
<span class="trait-pill"><%= trait %></span>
|
|
42
|
+
<% end %>
|
|
43
|
+
<% if traits.size > 4 %>
|
|
44
|
+
<span class="trait-pill">+<%= traits.size - 4 %> more</span>
|
|
45
|
+
<% end %>
|
|
46
|
+
</div>
|
|
47
|
+
<% end %>
|
|
48
|
+
|
|
49
|
+
<% if associations.any? %>
|
|
50
|
+
<div class="factory-card__badges">
|
|
51
|
+
<% associations.each do |assoc| %>
|
|
52
|
+
<span class="assoc-pill"><%= assoc[:name] %></span>
|
|
53
|
+
<% end %>
|
|
54
|
+
</div>
|
|
55
|
+
<% end %>
|
|
56
|
+
|
|
57
|
+
<div class="factory-card__description">
|
|
58
|
+
Rendered class: <%= info[:class_name] %><br>
|
|
59
|
+
Traits: <%= traits.present? ? traits.join(', ') : 'none' %><br>
|
|
60
|
+
Associations: <%= associations.present? ? associations.count : 0 %>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="factory-card__footer">
|
|
64
|
+
<%= link_to "Generate Seeds", factory_path(name), class: "btn btn-primary w-full" %>
|
|
65
|
+
</div>
|
|
66
|
+
</article>
|
|
67
|
+
<% end %>
|
|
68
|
+
</div>
|
|
69
|
+
<% end %>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|