factory_bot_instrumentation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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