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
data/Rakefile
ADDED
@@ -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, "&")
|
54
|
+
.replace(/</g, "<")
|
55
|
+
.replace(/>/g, ">");
|
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
|
+
};
|