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
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/factory_bot_instrumentation .js
2
+ //= link_directory ../stylesheets/factory_bot_instrumentation .css
@@ -0,0 +1,16 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will
2
+ // include all the files listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts,
5
+ // vendor/assets/javascripts, or any plugin's vendor/assets/javascripts
6
+ // directory can be referenced here using a relative path.
7
+ //
8
+ // It's not advisable to add code directly here, but if you do, it'll appear at
9
+ // the bottom of the compiled file. JavaScript code in this file should be
10
+ // added after the last require_* statement.
11
+ //
12
+ // Read Sprockets README
13
+ // (https://github.com/rails/sprockets#sprockets-directives) for details about
14
+ // supported directives.
15
+ //
16
+ //= require_tree .
@@ -0,0 +1,134 @@
1
+ window.CreateForm = CreateForm = function()
2
+ {
3
+ this.scope = '#generate';
4
+ this.form = new Form(this.scope);
5
+ this.scenarios = window.scenarios;
6
+ this.select = $(`${this.scope} .scenario`);
7
+ this.desc = $(`${this.scope} .description`);
8
+
9
+ let self = this;
10
+
11
+ this.form.errorContent = function(payload, output, cb)
12
+ {
13
+ window.utils.waterfallWithHooks({
14
+ data: {
15
+ alert: `An unexpected error occured. Looks like something went wrong
16
+ while generating your new entity. This migth be a bug, or an
17
+ unexpected feature. It could be a temporary issue. When is is
18
+ persistent contact your friendly API Instrumentation
19
+ administrator.`,
20
+ output: output,
21
+ payload: payload,
22
+ pre: '',
23
+ post: ''
24
+ },
25
+ pre: window.hooks.preCreateError,
26
+ post: window.hooks.postCreateError,
27
+ action: (payload, innerCb) => {
28
+ cb(null, `
29
+ ${payload.pre}
30
+ <div class="alert alert-danger" role="alert">${payload.alert}</div>
31
+ <pre id="data">${payload.output}</pre>
32
+ ${window.utils.clipboardButton()}
33
+ ${payload.post}
34
+ `);
35
+ innerCb(null, payload);
36
+ }
37
+ });
38
+ };
39
+
40
+ this.form.resultContent = function(payload, output, cb)
41
+ {
42
+ let card = window.utils.card({
43
+ body: `
44
+ <pre id="data">${output}</pre>
45
+ ${window.utils.clipboardButton()}
46
+ `
47
+ });
48
+
49
+ window.utils.waterfallWithHooks({
50
+ data: {
51
+ alert: `A new ${self.scenario().name.toLowerCase()} was created.`,
52
+ output: output,
53
+ payload: payload,
54
+ cards: [card],
55
+ pre: '',
56
+ post: '',
57
+ openCard: '#details'
58
+ },
59
+ pre: window.hooks.preCreateResult,
60
+ post: window.hooks.postCreateResult,
61
+ action: (payload, innerCb) => {
62
+ cb(null, `
63
+ ${payload.pre}
64
+ <div class="alert alert-success" role="alert">${payload.alert}</div>
65
+ <div class="accordion" id="response">
66
+ ${payload.cards.join(' ')}
67
+ </div>
68
+ ${payload.post}
69
+ `);
70
+ innerCb(null, payload);
71
+ if (payload.openCard) {
72
+ $(
73
+ `.accordion#response button[data-target="${payload.openCard}"]`
74
+ ).click();
75
+ }
76
+ }
77
+ });
78
+ };
79
+ };
80
+
81
+ CreateForm.prototype.updateDesc = function()
82
+ {
83
+ this.desc.html(this.scenario().desc);
84
+ };
85
+
86
+ CreateForm.prototype.activeScenario = function()
87
+ {
88
+ raw = this.select.find(':selected').val().split('/');
89
+ return { group: raw[0], index: raw[1] };
90
+ };
91
+
92
+ CreateForm.prototype.scenario = function()
93
+ {
94
+ scenario = this.activeScenario();
95
+ return this.scenarios[scenario.group][scenario.index];
96
+ };
97
+
98
+ CreateForm.prototype.bind = function()
99
+ {
100
+ this.form.bind((event) => {
101
+ this.submit();
102
+ });
103
+
104
+ this.select.on('change', this.updateDesc);
105
+ this.updateDesc();
106
+ };
107
+
108
+ CreateForm.prototype.submit = function()
109
+ {
110
+ let form = this.form;
111
+ let conf = this.scenario();
112
+
113
+ window.utils.waterfallWithHooks({
114
+ data: {
115
+ factory: conf.factory,
116
+ traits: conf.traits,
117
+ overwrite: conf.overwrite
118
+ },
119
+ pre: window.hooks.preCreate,
120
+ post: window.hooks.postCreate,
121
+ action: (payload, cb) => {
122
+ window.utils.request({
123
+ url: window.createUrl,
124
+ data: JSON.stringify(payload)
125
+ }, (err, result) => {
126
+ if (err) { return cb && cb(err); }
127
+ cb(null, { request: payload, response: result });
128
+ });
129
+ }
130
+ }, function(err, result) {
131
+ if (err) { return form.showError(err, err.responseText); }
132
+ form.showResult(result, result.response);
133
+ });
134
+ };
@@ -0,0 +1,123 @@
1
+ // You can define some custom hooks to enhance the functionality. With the help
2
+ // of the following hooks you are able to customize the outputs, perform
3
+ // additional HTTP requests or anything you like.
4
+ //
5
+ // All the hooks are designed to passthrough a payload. They receive this
6
+ // payload as the first argument, and a callback function to signal the end of
7
+ // the hook. You MUST pass the payload as second parameter to the callback, or
8
+ // pass an error object as first argument. You can modify the payload as you
9
+ // wish, eg. adding some data from subsequent requests.
10
+ //
11
+ // Example hooks:
12
+ //
13
+ // // Error case
14
+ // window.hooks.postCreate.push((payload, cb) => {
15
+ // cb({ error: true});
16
+ // });
17
+ //
18
+ // // Happy case
19
+ // window.hooks.postCreate.push((payload, cb) => {
20
+ // cb(null, Object.assign(payload, { additional: { data: true } }));
21
+ // });
22
+ //
23
+ // Mind the fact that you can define multiple custom functions per hook type.
24
+ // They are executed after each other in a waterfall like flow. The order of
25
+ // the hooks array is therefore essential.
26
+ window.hooks = {
27
+ // With the help of the +perCreate+ hooks you can manipulate the create
28
+ // request parameters. Think of an additional handling which reads an
29
+ // overwrite form or a kind of trait checkboxes to customize the factory
30
+ // call. The +payload+ looks like this:
31
+ //
32
+ // {
33
+ // factory: 'user',
34
+ // traits: ['confirmed'],
35
+ // overwrite: { password: 'secret' }
36
+ // }
37
+ preCreate: [],
38
+
39
+ // The +postCreate+ hook allows you to perform subsequent requests to fetch
40
+ // additional data. Think of a user instrumentation where you want to request
41
+ // a one time token for this user. This token can be added to the payload and
42
+ // can be shown with the help of the +preCreateResult+ hook. The payload
43
+ // contains the request parameters and the response body from the
44
+ // instrumentation request. Here comes an example +payload+:
45
+ //
46
+ // {
47
+ // request: { factory: 'user', /* [..] */ },
48
+ // response: { /* [..] */ }
49
+ // }
50
+ postCreate: [],
51
+
52
+ // With the help of the +preCreateResult+ hook you can customize the output
53
+ // of the result. You could also perform some subsequent requests or some UI
54
+ // preparations. You can access the output options and the runtime payload
55
+ // with all its data and make modifications to them. This hook is triggered
56
+ // before the result is rendered. A sample payload comes here:
57
+ //
58
+ // {
59
+ // alert: 'Your alert text.',
60
+ // output: 'Formatted response',
61
+ // payload: { request: { /* [..] */ }, response: { /* [..] */ } },
62
+ // cards: [
63
+ // `The details accordion card,
64
+ // you can add more, remove the details card
65
+ // or reorder them`
66
+ // ],
67
+ // openCard: '#details', // Open a custom card, or none
68
+ // pre: 'Additinal HTML content before the alert.',
69
+ // post: 'Additinal HTML content after the formatted response output.'
70
+ // }
71
+ preCreateResult: [],
72
+
73
+ // In case you want to perform some logic after the result is rendered, you
74
+ // can use the +postCreateResult+ hook. You can access the output options and
75
+ // the runtime payload with all its data, but changes to them won't take
76
+ // effect. The +payload+ looks like this:
77
+ //
78
+ // {
79
+ // alert: 'Your alert text.',
80
+ // output: 'Formatted response',
81
+ // payload: { request: { /* [..] */ }, response: { /* [..] */ } },
82
+ // cards: [
83
+ // `The details accordion card,
84
+ // you can add more, remove the details card
85
+ // or reorder them`
86
+ // ],
87
+ // openCard: '#details', // Open a custom card, or none
88
+ // pre: 'Additinal HTML content before the alert.',
89
+ // post: 'Additinal HTML content after the formatted response output.'
90
+ // }
91
+ postCreateResult: [],
92
+
93
+ // With the help of the +preCreateError+ hook you can customize the output of
94
+ // the error. Furthermore you can perform some subsequent requests or
95
+ // whatever comes to your mind. You can access the output options and the
96
+ // runtime payload with all its data and make modifications to them. This
97
+ // hook is triggered before the error is rendered. A sample payload comes
98
+ // here:
99
+ //
100
+ // {
101
+ // alert: 'Your alert text.',
102
+ // output: 'Formatted response',
103
+ // payload: { request: { /* [..] */ }, response: { /* [..] */ } },
104
+ // pre: 'Additinal HTML content before the alert.',
105
+ // post: 'Additinal HTML content after the formatted response output.'
106
+ // }
107
+ preCreateError: [],
108
+
109
+ // In case you want to perform some magic after an error occured, you can use
110
+ // the +postCreateError+ hook. You can access the output options and the
111
+ // runtime payload with all its data, but changes to them won't take effect
112
+ // because this hook is triggered after the error is rendered. The +payload+
113
+ // looks like this:
114
+ //
115
+ // {
116
+ // alert: 'Your alert text.',
117
+ // output: 'Formatted response',
118
+ // payload: { request: { /* [..] */ }, response: { /* [..] */ } },
119
+ // pre: 'Additinal HTML content before the alert.',
120
+ // post: 'Additinal HTML content after the formatted response output.'
121
+ // }
122
+ postCreateError: []
123
+ };
@@ -0,0 +1,66 @@
1
+ window.Form = Form = function(scope)
2
+ {
3
+ this.button = $(scope).find('button');
4
+ this.result = $('#result');
5
+ this.spinner = $('#spinner');
6
+ };
7
+
8
+ Form.prototype.bind = function(action)
9
+ {
10
+ this.button.on('click', (event) => {
11
+ event.preventDefault();
12
+ this.hideResult();
13
+ action(event);
14
+ return false;
15
+ });
16
+ };
17
+
18
+ Form.prototype.hideResult = function()
19
+ {
20
+ this.result.hide();
21
+ this.button.prop('disabled', true);
22
+ this.spinner.show();
23
+ };
24
+
25
+ Form.prototype.showResultContainer = function(html)
26
+ {
27
+ this.result.html(html);
28
+ $('pre').each((i, block) => hljs.highlightBlock(block));
29
+ new ClipboardJS('.cb-copy');
30
+ this.result.show();
31
+ this.button.prop('disabled', false);
32
+ };
33
+
34
+ Form.prototype.showError = function(payload, output)
35
+ {
36
+ this.spinner.hide();
37
+ output = window.utils.prepareOutput(output);
38
+ this.errorContent(payload, output, (err, html) => {
39
+ this.showResultContainer(html);
40
+ });
41
+ };
42
+
43
+ Form.prototype.errorContent = function(payload, output, cb)
44
+ {
45
+ cb(null, `
46
+ <pre id="data">${output}</pre>
47
+ ${window.utils.clipboardButton()}
48
+ `);
49
+ };
50
+
51
+ Form.prototype.showResult = function(payload, output)
52
+ {
53
+ this.spinner.hide();
54
+ output = window.utils.prepareOutput(output);
55
+ this.resultContent(payload, output, (err, html) => {
56
+ this.showResultContainer(html);
57
+ });
58
+ };
59
+
60
+ Form.prototype.resultContent = function(payload, output, cb)
61
+ {
62
+ cb(null, `
63
+ <pre id="data">${output}</pre>
64
+ ${window.utils.clipboardButton()}
65
+ `);
66
+ };
@@ -0,0 +1,116 @@
1
+ window.utils = Utils = {};
2
+
3
+ Utils.pushWaterfallPayload = function(data)
4
+ {
5
+ return (cb) => cb(null, data);
6
+ };
7
+
8
+ Utils.waterfallWithHooks = function(opts, cb)
9
+ {
10
+ cb = cb || function(){};
11
+ opts = Object.assign({
12
+ pre: [],
13
+ post: [],
14
+ data: {},
15
+ action: (payload, cb) => cb(null, payload)
16
+ }, opts);
17
+
18
+ async.waterfall([
19
+ // Yield the data to pre hooks
20
+ Utils.pushWaterfallPayload(opts.data),
21
+ // Perform pre hooks
22
+ ...opts.pre,
23
+ // Perform the create request
24
+ opts.action,
25
+ // Perform post hooks
26
+ ...opts.post
27
+ ], cb);
28
+ };
29
+
30
+ Utils.request = function(opts, cb)
31
+ {
32
+ opts = Object.assign({
33
+ url: '/',
34
+ type: 'POST',
35
+ data: '{}',
36
+ dataType: 'json',
37
+ contentType: 'application/json; charset=utf-8',
38
+ ignoreErrors: false
39
+ }, opts || {}, {
40
+ success: (result) => cb(null, result)
41
+ });
42
+
43
+ errCb = (err) => cb(err);
44
+ if (opts.ignoreErrors) {
45
+ errCb = (err) => cb(null, err);
46
+ }
47
+
48
+ $.ajax(opts).fail(errCb);
49
+ };
50
+
51
+ Utils.escape = function(str)
52
+ {
53
+ return str.replace(/&/g, "&amp;")
54
+ .replace(/</g, "&lt;")
55
+ .replace(/>/g, "&gt;");
56
+ };
57
+
58
+ Utils.prepareOutput = function(output)
59
+ {
60
+ try {
61
+ if (typeof output !== 'object') { output = JSON.parse(output); }
62
+ output = JSON.stringify(output, null, 2);
63
+ } catch { }
64
+
65
+ return window.utils.escape(output);
66
+ };
67
+
68
+ Utils.clipboardButton = function(id)
69
+ {
70
+ id = id || 'data';
71
+ return `
72
+ <span class="btn btn-primary cb-copy"
73
+ data-clipboard-target="#${id}">
74
+ <i class="fas fa-paste"></i> Copy result to clipboard
75
+ </span>
76
+ `;
77
+ };
78
+
79
+ Utils.clipboardBadge = function(id)
80
+ {
81
+ id = id || 'data';
82
+ return `
83
+ <span class="badge badge-dark cb-copy" title="Copy result to clipboard"
84
+ data-clipboard-target="#${id}"><i class="fas fa-paste"></i>
85
+ </span>
86
+ `;
87
+ };
88
+
89
+ Utils.card = function(opts)
90
+ {
91
+ opts = Object.assign({
92
+ id: 'details',
93
+ icon: 'fa-asterisk',
94
+ title: 'Details',
95
+ body: ''
96
+ }, opts || {});
97
+
98
+ return `
99
+ <div class="card">
100
+ <div class="card-header">
101
+ <h5 class="mb-0">
102
+ <button class="btn btn-link collapsed" type="button"
103
+ data-toggle="collapse" data-target="#${opts.id}"
104
+ aria-expanded="false">
105
+ <i class="fas ${opts.icon}"></i> ${opts.title}</h5>
106
+ </button>
107
+ </h5>
108
+ </div>
109
+ <div id="${opts.id}" class="collapse" data-parent="#response">
110
+ <div class="card-body">
111
+ ${opts.body}
112
+ </div>
113
+ </div>
114
+ </div>
115
+ `;
116
+ };