factory_bot_instrumentation 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/.editorconfig +30 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +43 -0
- data/.simplecov +3 -0
- data/.travis.yml +28 -0
- data/Appraisals +15 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +161 -0
- data/LICENSE +21 -0
- data/Makefile +96 -0
- data/README.md +713 -0
- data/Rakefile +8 -0
- data/app/assets/config/factory_bot_instrumentation_manifest.js +2 -0
- data/app/assets/javascripts/factory_bot_instrumentation/application.js +16 -0
- data/app/assets/javascripts/factory_bot_instrumentation/create.js +134 -0
- data/app/assets/javascripts/factory_bot_instrumentation/hooks.js +123 -0
- data/app/assets/javascripts/factory_bot_instrumentation/lib/form.js +66 -0
- data/app/assets/javascripts/factory_bot_instrumentation/lib/utils.js +116 -0
- data/app/assets/stylesheets/factory_bot_instrumentation/application.css +91 -0
- data/app/controllers/factory_bot/instrumentation/application_controller.rb +7 -0
- data/app/controllers/factory_bot/instrumentation/root_controller.rb +113 -0
- data/app/views/factory_bot/instrumentation/application/_config.html.erb +5 -0
- data/app/views/factory_bot/instrumentation/application/_scripts.html.erb +18 -0
- data/app/views/factory_bot/instrumentation/application/_spinner.html.erb +7 -0
- data/app/views/factory_bot/instrumentation/application/_styles.html.erb +20 -0
- data/app/views/factory_bot/instrumentation/root/index.html.erb +31 -0
- data/app/views/factory_bot_instrumentation/_blocks.html.erb +6 -0
- data/app/views/factory_bot_instrumentation/_navigation.html.erb +13 -0
- data/app/views/factory_bot_instrumentation/_scripts.html.erb +5 -0
- data/app/views/factory_bot_instrumentation/_styles.html.erb +5 -0
- data/app/views/layouts/factory_bot/instrumentation/application.html.erb +29 -0
- data/bin/rails +14 -0
- data/config/instrumentation.yml +55 -0
- data/config/routes.rb +8 -0
- data/doc/assets/blocks.png +0 -0
- data/doc/assets/customized.png +0 -0
- data/doc/assets/navigation.png +0 -0
- data/doc/assets/project.svg +68 -0
- data/doc/assets/regular.png +0 -0
- data/docker-compose.yml +8 -0
- data/factory_bot_instrumentation.gemspec +33 -0
- data/gemfiles/rails_4.gemfile +7 -0
- data/gemfiles/rails_4.gemfile.lock +147 -0
- data/gemfiles/rails_5.0.gemfile +7 -0
- data/gemfiles/rails_5.0.gemfile.lock +154 -0
- data/gemfiles/rails_5.1.gemfile +7 -0
- data/gemfiles/rails_5.1.gemfile.lock +154 -0
- data/gemfiles/rails_5.2.gemfile +7 -0
- data/gemfiles/rails_5.2.gemfile.lock +162 -0
- data/lib/factory_bot/instrumentation/configuration.rb +20 -0
- data/lib/factory_bot/instrumentation/engine.rb +19 -0
- data/lib/factory_bot/instrumentation/version.rb +7 -0
- data/lib/factory_bot_instrumentation.rb +43 -0
- metadata +209 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will
|
3
|
+
* include all the files listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets,
|
6
|
+
* vendor/assets/stylesheets, or any plugin's vendor/assets/stylesheets
|
7
|
+
* directory can be referenced here using a relative path.
|
8
|
+
*
|
9
|
+
* You're free to add application-wide styles to this file and they'll appear
|
10
|
+
* at the bottom of the compiled file so the styles you add here take
|
11
|
+
* precedence over styles defined in any other CSS/SCSS files in this
|
12
|
+
* directory. Styles in this file should be added after the last require_*
|
13
|
+
* statement. It is generally better to create a new file per style scope.
|
14
|
+
*
|
15
|
+
*= require_tree .
|
16
|
+
*= require_self
|
17
|
+
*/
|
18
|
+
|
19
|
+
.row { margin-top: 5em }
|
20
|
+
.result-container { background-color: #f8f8f8 }
|
21
|
+
.hljs { background-color: #fff; }
|
22
|
+
.jumbotron { padding: 1rem }
|
23
|
+
.jumbotron .row { margin-top: 0 }
|
24
|
+
.badge { cursor: pointer }
|
25
|
+
.btn-link:hover, .btn-link:active, .btn-link:focus { text-decoration: none }
|
26
|
+
|
27
|
+
pre {
|
28
|
+
margin-bottom: 0;
|
29
|
+
white-space: pre-wrap;
|
30
|
+
white-space: -moz-pre-wrap;
|
31
|
+
white-space: -pre-wrap;
|
32
|
+
white-space: -o-pre-wrap;
|
33
|
+
word-wrap: break-word;
|
34
|
+
}
|
35
|
+
|
36
|
+
pre + .btn {
|
37
|
+
margin-top: 1rem;
|
38
|
+
}
|
39
|
+
|
40
|
+
.spinner {
|
41
|
+
margin: 100px auto;
|
42
|
+
width: 50px;
|
43
|
+
height: 40px;
|
44
|
+
text-align: center;
|
45
|
+
font-size: 10px;
|
46
|
+
}
|
47
|
+
|
48
|
+
.spinner > div {
|
49
|
+
background-color: #333;
|
50
|
+
height: 100%;
|
51
|
+
width: 6px;
|
52
|
+
display: inline-block;
|
53
|
+
|
54
|
+
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
|
55
|
+
animation: sk-stretchdelay 1.2s infinite ease-in-out;
|
56
|
+
}
|
57
|
+
|
58
|
+
.spinner .rect2 {
|
59
|
+
-webkit-animation-delay: -1.1s;
|
60
|
+
animation-delay: -1.1s;
|
61
|
+
}
|
62
|
+
|
63
|
+
.spinner .rect3 {
|
64
|
+
-webkit-animation-delay: -1.0s;
|
65
|
+
animation-delay: -1.0s;
|
66
|
+
}
|
67
|
+
|
68
|
+
.spinner .rect4 {
|
69
|
+
-webkit-animation-delay: -0.9s;
|
70
|
+
animation-delay: -0.9s;
|
71
|
+
}
|
72
|
+
|
73
|
+
.spinner .rect5 {
|
74
|
+
-webkit-animation-delay: -0.8s;
|
75
|
+
animation-delay: -0.8s;
|
76
|
+
}
|
77
|
+
|
78
|
+
@-webkit-keyframes sk-stretchdelay {
|
79
|
+
0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
|
80
|
+
20% { -webkit-transform: scaleY(1.0) }
|
81
|
+
}
|
82
|
+
|
83
|
+
@keyframes sk-stretchdelay {
|
84
|
+
0%, 40%, 100% {
|
85
|
+
transform: scaleY(0.4);
|
86
|
+
-webkit-transform: scaleY(0.4);
|
87
|
+
} 20% {
|
88
|
+
transform: scaleY(1.0);
|
89
|
+
-webkit-transform: scaleY(1.0);
|
90
|
+
}
|
91
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FactoryBot::Instrumentation
|
4
|
+
# The Instrumentation engine controller with frontend and API actions.
|
5
|
+
class RootController < ApplicationController
|
6
|
+
# This is required to make API controllers template renderable
|
7
|
+
include ActionView::Layouts
|
8
|
+
|
9
|
+
# Configure the default application layout
|
10
|
+
layout 'factory_bot/instrumentation/application'
|
11
|
+
|
12
|
+
# Show the instrumentation frontend which features the output of configured
|
13
|
+
# dynamic seeds scenarios. The frontend allows humans to generate new seed
|
14
|
+
# data on the fly.
|
15
|
+
def index
|
16
|
+
@instrumentation = instrumentation
|
17
|
+
@scenarios = scenarios
|
18
|
+
@config = FactoryBot::Instrumentation.configuration
|
19
|
+
render :index, layout: true
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create a new entity with the given factory settings to create on demand
|
23
|
+
# dependencies for your testing needs. You can pass in requests without
|
24
|
+
# authentication in the following JSON format:
|
25
|
+
#
|
26
|
+
# {
|
27
|
+
# "factory": "user",
|
28
|
+
# "traits": ["confirmed"],
|
29
|
+
# "overwrite": {
|
30
|
+
# "first_name": "Bernd",
|
31
|
+
# "last_name": "Müller",
|
32
|
+
# "email": "bernd.mueller@example.com",
|
33
|
+
# "password": "secret"
|
34
|
+
# }
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
# The result is the API v1 representation of the created entity.
|
38
|
+
def create
|
39
|
+
# Reload the factories to improve the test development experience
|
40
|
+
FactoryBot.reload
|
41
|
+
# Call the factory construction with the user given parameters
|
42
|
+
entity = FactoryBot.create(*factory_params)
|
43
|
+
# Render the resulting entity as an API v1 representation
|
44
|
+
render plain: entity.to_json, content_type: 'application/json'
|
45
|
+
rescue StandardError => err
|
46
|
+
# Log for local factory debugging and re-raise for canary onwards
|
47
|
+
Rails.logger.error("#{err}\n#{err.backtrace.join("\n")}")
|
48
|
+
raise err
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Parse the given parameters from the request and build
|
54
|
+
# a valid FactoryBot options set.
|
55
|
+
#
|
56
|
+
# @return [Array<Mixed>] the FactoryBot options
|
57
|
+
def factory_params
|
58
|
+
data = params.permit(:factory, traits: [])
|
59
|
+
overwrite = params.to_unsafe_h.fetch(:overwrite, {}).deep_symbolize_keys
|
60
|
+
|
61
|
+
[
|
62
|
+
data.fetch(:factory).to_sym,
|
63
|
+
*data.fetch(:traits, []).map(&:to_sym),
|
64
|
+
**overwrite
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Unfortunately +Rails.configuration.instrumentation+ is only read once and
|
69
|
+
# do not hot-reload on changes. Thats why we read this file manually to get
|
70
|
+
# always a fresh state.
|
71
|
+
#
|
72
|
+
# @return [Hash{String => Mixed}] the instrumentation scenarios
|
73
|
+
def instrumentation
|
74
|
+
config_file = FactoryBot::Instrumentation.configuration.config_file.to_s
|
75
|
+
content = Pathname.new(config_file).read
|
76
|
+
template = ERB.new(content)
|
77
|
+
YAML.load(template.result(binding))[Rails.env]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Map all the instrumentation scenarios into groups and pass them back.
|
81
|
+
#
|
82
|
+
# @return [Hash{String => Array}] the grouped scenarios
|
83
|
+
def scenarios
|
84
|
+
instrumentation['scenarios'].each_with_object({}) do |scenario, memo|
|
85
|
+
group = scenario_group(scenario['name'])
|
86
|
+
scenario['group'] = group
|
87
|
+
memo[group] = [] unless memo.key? group
|
88
|
+
memo[group] << scenario
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Map all the configured scenario groups to a useable hash.
|
93
|
+
#
|
94
|
+
# @return [Hash{Regexp => String}] the group mapping
|
95
|
+
def groups
|
96
|
+
instrumentation['groups'].each_with_object({}) do |(key, val), memo|
|
97
|
+
memo[Regexp.new(Regexp.quote(key))] = val
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Fetch the group name for a given scenario name. This will utilize the
|
102
|
+
# +SCENARIO_GROUPS+ map.
|
103
|
+
#
|
104
|
+
# @param name [String] the scenario name
|
105
|
+
# @return [String] the group name
|
106
|
+
def scenario_group(name)
|
107
|
+
groups.map do |pattern, group_name|
|
108
|
+
next unless pattern.match? name
|
109
|
+
group_name
|
110
|
+
end.compact.first || 'Various'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/async/2.6.1/async.min.js"
|
2
|
+
integrity="sha256-QRRHCc3xM0GNZvTCvi0vm2f9zdOiOptAy6xGq7qN5hI="
|
3
|
+
crossorigin="anonymous"></script>
|
4
|
+
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
|
5
|
+
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
|
6
|
+
crossorigin="anonymous"></script>
|
7
|
+
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.bundle.min.js"
|
8
|
+
integrity="sha384-u/bQvRA/1bobcXlcEYpsEdFVK/vJs3+T+nXLsBYJthmdBuavHvAW6UsmqO2Gd/F9"
|
9
|
+
crossorigin="anonymous"></script>
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"
|
11
|
+
integrity="sha256-/BfiIkHlHoVihZdc6TFuj7MmJ0TWcWsMXkeDFwhi0zw="
|
12
|
+
crossorigin="anonymous"></script>
|
13
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/json.min.js"
|
14
|
+
integrity="sha256-KPdGtw3AdDen/v6+9ue/V3m+9C2lpNiuirroLsHrJZM="
|
15
|
+
crossorigin="anonymous"></script>
|
16
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.1/clipboard.js"
|
17
|
+
integrity="sha256-OBi1PBVEbUGcnFd3wGpTEGuz/VKAdU0Zvux5ovdamtI="
|
18
|
+
crossorigin="anonymous"></script>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css"
|
2
|
+
rel="stylesheet"
|
3
|
+
integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB"
|
4
|
+
crossorigin="anonymous">
|
5
|
+
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css"
|
6
|
+
rel="stylesheet"
|
7
|
+
integrity="sha256-3YM6A3pH4QFCl9WbSU8oXF5N6W/2ylvW0o2g+Z6TmLQ="
|
8
|
+
crossorigin="anonymous" />
|
9
|
+
<link href="https://use.fontawesome.com/releases/v5.1.0/css/solid.css"
|
10
|
+
rel="stylesheet"
|
11
|
+
integrity="sha384-TbilV5Lbhlwdyc4RuIV/JhD8NR+BfMrvz4BL5QFa2we1hQu6wvREr3v6XSRfCTRp"
|
12
|
+
crossorigin="anonymous">
|
13
|
+
<link href="https://use.fontawesome.com/releases/v5.1.0/css/regular.css"
|
14
|
+
rel="stylesheet"
|
15
|
+
integrity="sha384-avJt9MoJH2rB4PKRsJRHZv7yiFZn8LrnXuzvmZoD3fh1aL6aM6s0BBcnCvBe6XSD"
|
16
|
+
crossorigin="anonymous">
|
17
|
+
<link href="https://use.fontawesome.com/releases/v5.1.0/css/fontawesome.css"
|
18
|
+
rel="stylesheet"
|
19
|
+
integrity="sha384-ozJwkrqb90Oa3ZNb+yKFW2lToAWYdTiF1vt8JiH5ptTGHTGcN7qdoR1F95e0kYyG"
|
20
|
+
crossorigin="anonymous">
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<div class="row">
|
2
|
+
<div class="col-xl-4 col-lg-12">
|
3
|
+
<form id="generate" class="jumbotron">
|
4
|
+
<div class="form-group">
|
5
|
+
<select class="form-control scenario">
|
6
|
+
<% @scenarios.each do |group, scenarios| %>
|
7
|
+
<optgroup label="<%= group %>">
|
8
|
+
<% scenarios.each_with_index do |scenario, idx| %>
|
9
|
+
<option value="<%= group %>/<%= idx %>">
|
10
|
+
<%= scenario['name'] %>
|
11
|
+
</option>
|
12
|
+
<% end %>
|
13
|
+
</optgroup>
|
14
|
+
<% end %>
|
15
|
+
</select>
|
16
|
+
<small class="form-text text-muted description"></small>
|
17
|
+
</div>
|
18
|
+
<button type="submit" class="btn btn-primary btn-block">Generate</button>
|
19
|
+
</form>
|
20
|
+
<%= render partial: 'factory_bot_instrumentation/blocks' %>
|
21
|
+
</div>
|
22
|
+
<div class="col-xl-8 col-lg-12">
|
23
|
+
<%= render partial: 'spinner' %>
|
24
|
+
<div id="result"
|
25
|
+
class="jumbotron result-container"
|
26
|
+
style="display: none"></div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<script>
|
30
|
+
$(() => { (new CreateForm()).bind(); });
|
31
|
+
</script>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<%#
|
2
|
+
You can replace this file at you application to add navigation entries.
|
3
|
+
Create a new file at
|
4
|
+
+app/views/factory_bot_instrumentation/_navigation.html.erb+
|
5
|
+
to make use of this feature.
|
6
|
+
|
7
|
+
<li class="nav-item active">
|
8
|
+
<a class="nav-link" href="#">Home</span></a>
|
9
|
+
</li>
|
10
|
+
<li class="nav-item">
|
11
|
+
<a class="nav-link" href="#">Link</a>
|
12
|
+
</li>
|
13
|
+
%>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<title><%= @config.application_name %> Instrumentation</title>
|
7
|
+
<%= render partial: 'styles' %>
|
8
|
+
<%= stylesheet_link_tag 'factory_bot_instrumentation/application',
|
9
|
+
media: 'all' %>
|
10
|
+
<%= render 'factory_bot_instrumentation/styles' %>
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
14
|
+
<a class="navbar-brand" href="#">
|
15
|
+
<%= @config.application_name %> Instrumentation
|
16
|
+
</a>
|
17
|
+
<ul class="navbar-nav mr-auto">
|
18
|
+
<%= render 'factory_bot_instrumentation/navigation' %>
|
19
|
+
</ul>
|
20
|
+
</nav>
|
21
|
+
<%= render partial: 'scripts' %>
|
22
|
+
<%= render partial: 'config' %>
|
23
|
+
<%= javascript_include_tag 'factory_bot_instrumentation/application' %>
|
24
|
+
<%= render 'factory_bot_instrumentation/scripts' %>
|
25
|
+
<main role="main" class="container">
|
26
|
+
<%= yield %>
|
27
|
+
</main>
|
28
|
+
</body>
|
29
|
+
</html>
|
data/bin/rails
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
3
|
+
# installed from the root of your application.
|
4
|
+
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
6
|
+
ENGINE_PATH = File.expand_path('../lib/factory_bot_instrumentation/engine', __dir__)
|
7
|
+
APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
|
8
|
+
|
9
|
+
# Set up gems listed in the Gemfile.
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
11
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
12
|
+
|
13
|
+
require 'rails/all'
|
14
|
+
require 'rails/engine/commands'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Define new dynamic seed scenarios here which can be used on the API
|
2
|
+
# instrumentation frontend.
|
3
|
+
|
4
|
+
default: &default
|
5
|
+
# Each group consists of a key (the pattern to match) and the value (group
|
6
|
+
# name). The patterns are put inside a quoted regex and the first matching
|
7
|
+
# one will be used so the configuration order is important.
|
8
|
+
groups:
|
9
|
+
UX: UX Scenarios
|
10
|
+
user: Users
|
11
|
+
|
12
|
+
# All the scenarios which can be generated.
|
13
|
+
scenarios:
|
14
|
+
- name: Empty user
|
15
|
+
desc: Create a new user without any dependent data.
|
16
|
+
factory: :user
|
17
|
+
traits:
|
18
|
+
- :confirmed
|
19
|
+
overwrite: {}
|
20
|
+
|
21
|
+
- name: User with a single friend
|
22
|
+
desc: Create a new user with a single friend.
|
23
|
+
factory: :user
|
24
|
+
traits:
|
25
|
+
- :confirmed
|
26
|
+
- :with_friend
|
27
|
+
overwrite: {}
|
28
|
+
|
29
|
+
- name: User with a single friend named Bob
|
30
|
+
desc: Create a new user with a single friend whoes name is Bob.
|
31
|
+
factory: :user
|
32
|
+
traits:
|
33
|
+
- :confirmed
|
34
|
+
- :with_friend
|
35
|
+
overwrite:
|
36
|
+
friend_overwrites:
|
37
|
+
first_name: Bob
|
38
|
+
|
39
|
+
- name: User with multiple friends
|
40
|
+
desc: Create a new user with 5 friends.
|
41
|
+
factory: :user
|
42
|
+
traits:
|
43
|
+
- :confirmed
|
44
|
+
- :with_friends
|
45
|
+
overwrite:
|
46
|
+
friends_amount: 5
|
47
|
+
|
48
|
+
test:
|
49
|
+
<<: *default
|
50
|
+
|
51
|
+
development:
|
52
|
+
<<: *default
|
53
|
+
|
54
|
+
production:
|
55
|
+
<<: *default
|