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
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
+ };