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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +30 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +43 -0
  6. data/.simplecov +3 -0
  7. data/.travis.yml +28 -0
  8. data/Appraisals +15 -0
  9. data/CHANGELOG.md +3 -0
  10. data/Gemfile +8 -0
  11. data/Gemfile.lock +161 -0
  12. data/LICENSE +21 -0
  13. data/Makefile +96 -0
  14. data/README.md +713 -0
  15. data/Rakefile +8 -0
  16. data/app/assets/config/factory_bot_instrumentation_manifest.js +2 -0
  17. data/app/assets/javascripts/factory_bot_instrumentation/application.js +16 -0
  18. data/app/assets/javascripts/factory_bot_instrumentation/create.js +134 -0
  19. data/app/assets/javascripts/factory_bot_instrumentation/hooks.js +123 -0
  20. data/app/assets/javascripts/factory_bot_instrumentation/lib/form.js +66 -0
  21. data/app/assets/javascripts/factory_bot_instrumentation/lib/utils.js +116 -0
  22. data/app/assets/stylesheets/factory_bot_instrumentation/application.css +91 -0
  23. data/app/controllers/factory_bot/instrumentation/application_controller.rb +7 -0
  24. data/app/controllers/factory_bot/instrumentation/root_controller.rb +113 -0
  25. data/app/views/factory_bot/instrumentation/application/_config.html.erb +5 -0
  26. data/app/views/factory_bot/instrumentation/application/_scripts.html.erb +18 -0
  27. data/app/views/factory_bot/instrumentation/application/_spinner.html.erb +7 -0
  28. data/app/views/factory_bot/instrumentation/application/_styles.html.erb +20 -0
  29. data/app/views/factory_bot/instrumentation/root/index.html.erb +31 -0
  30. data/app/views/factory_bot_instrumentation/_blocks.html.erb +6 -0
  31. data/app/views/factory_bot_instrumentation/_navigation.html.erb +13 -0
  32. data/app/views/factory_bot_instrumentation/_scripts.html.erb +5 -0
  33. data/app/views/factory_bot_instrumentation/_styles.html.erb +5 -0
  34. data/app/views/layouts/factory_bot/instrumentation/application.html.erb +29 -0
  35. data/bin/rails +14 -0
  36. data/config/instrumentation.yml +55 -0
  37. data/config/routes.rb +8 -0
  38. data/doc/assets/blocks.png +0 -0
  39. data/doc/assets/customized.png +0 -0
  40. data/doc/assets/navigation.png +0 -0
  41. data/doc/assets/project.svg +68 -0
  42. data/doc/assets/regular.png +0 -0
  43. data/docker-compose.yml +8 -0
  44. data/factory_bot_instrumentation.gemspec +33 -0
  45. data/gemfiles/rails_4.gemfile +7 -0
  46. data/gemfiles/rails_4.gemfile.lock +147 -0
  47. data/gemfiles/rails_5.0.gemfile +7 -0
  48. data/gemfiles/rails_5.0.gemfile.lock +154 -0
  49. data/gemfiles/rails_5.1.gemfile +7 -0
  50. data/gemfiles/rails_5.1.gemfile.lock +154 -0
  51. data/gemfiles/rails_5.2.gemfile +7 -0
  52. data/gemfiles/rails_5.2.gemfile.lock +162 -0
  53. data/lib/factory_bot/instrumentation/configuration.rb +20 -0
  54. data/lib/factory_bot/instrumentation/engine.rb +19 -0
  55. data/lib/factory_bot/instrumentation/version.rb +7 -0
  56. data/lib/factory_bot_instrumentation.rb +43 -0
  57. 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FactoryBot::Instrumentation
4
+ # A base engine application controller.
5
+ class ApplicationController < ActionController::API
6
+ end
7
+ end
@@ -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,5 @@
1
+ <script>
2
+ window.config = <%== @config.config.to_json %>;
3
+ window.scenarios = <%== @scenarios.to_json %>;
4
+ window.createUrl = '<%= root_path %>';
5
+ </script>
@@ -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,7 @@
1
+ <div id="spinner" class="spinner" style="display: none">
2
+ <div class="rect1"></div>
3
+ <div class="rect2"></div>
4
+ <div class="rect3"></div>
5
+ <div class="rect4"></div>
6
+ <div class="rect5"></div>
7
+ </div>
@@ -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,6 @@
1
+ <%#
2
+ You can replace this file at you application to add custom blocks.
3
+ Create a new file at
4
+ +app/views/factory_bot_instrumentation/_blocks.html.erb+
5
+ to make use of this feature.
6
+ %>
@@ -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,5 @@
1
+ <%#
2
+ You can replace this file at you application to add custom scripts.
3
+ Create a new file at +app/views/factory_bot_instrumentation/_scripts.html.erb+
4
+ to make use of this feature.
5
+ %>
@@ -0,0 +1,5 @@
1
+ <%#
2
+ You can replace this file at you application to add custom styles.
3
+ Create a new file at +app/views/factory_bot_instrumentation/_styles.html.erb+
4
+ to make use of this feature.
5
+ %>
@@ -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