factory_bot_instrumentation 2.8.0 → 3.0.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 +4 -4
- data/.github/workflows/release.yml +3 -3
- data/.github/workflows/test.yml +3 -3
- data/.gitignore +1 -0
- data/.rubocop.yml +6 -2
- data/Appraisals +0 -4
- data/CHANGELOG.md +17 -0
- data/Dockerfile +3 -3
- data/Gemfile +1 -1
- data/Makefile +8 -2
- data/Rakefile +19 -0
- data/app/assets/javascripts/factory_bot_instrumentation/application.js +454 -12
- data/app/assets/stylesheets/factory_bot_instrumentation/application.css +31 -33
- data/factory_bot_instrumentation.gemspec +9 -2
- data/gemfiles/rails_8.1.gemfile +1 -1
- data/lib/factory_bot/instrumentation/asset_bundler.rb +194 -0
- data/lib/factory_bot/instrumentation/engine.rb +24 -0
- data/lib/factory_bot/instrumentation/version.rb +1 -1
- data/lib/factory_bot/instrumentation.rb +3 -0
- metadata +4 -9
- data/app/assets/config/factory_bot_instrumentation_manifest.js +0 -2
- data/app/assets/javascripts/factory_bot_instrumentation/create.js +0 -134
- data/app/assets/javascripts/factory_bot_instrumentation/hooks.js +0 -123
- data/app/assets/javascripts/factory_bot_instrumentation/lib/form.js +0 -66
- data/app/assets/javascripts/factory_bot_instrumentation/lib/utils.js +0 -116
- data/gemfiles/rails_8.0.gemfile +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a38fbf7fbe98fed861a4fc524a10e5798e8e1abc918c742272365b11c476e81
|
|
4
|
+
data.tar.gz: a9b886fd2f52d856c264b34d8b2d4297df8ea12d1bb6bf1f84b1bbb6480b54ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0bbbb1aaad25753d40cd97dfdd968c9935cfc9d6aded00ceb39aded62067707ebde83364a6b688700bf2cef4c3b43778a3c67c6f54940224a2d6be237aec48bb
|
|
7
|
+
data.tar.gz: 412673861a682f7506c9b56e2be255f56ac66eb604596348de122e29e6aacd6cc54dcbbc6f51ec3eee1dd4c35b30dc2494f96e6b995678245c0d8ac54b275ec1
|
|
@@ -26,12 +26,12 @@ jobs:
|
|
|
26
26
|
settings: '${{ github.repository }}'
|
|
27
27
|
target: ci/gem-test
|
|
28
28
|
|
|
29
|
-
- name: Install Ruby
|
|
29
|
+
- name: Install Ruby 4.0
|
|
30
30
|
uses: ruby/setup-ruby@v1
|
|
31
31
|
with:
|
|
32
|
-
ruby-version:
|
|
32
|
+
ruby-version: 4.0
|
|
33
33
|
bundler-cache: true
|
|
34
|
-
rubygems: '
|
|
34
|
+
rubygems: '4.0.11'
|
|
35
35
|
|
|
36
36
|
- name: Switch to SSH remotes for the Git repository
|
|
37
37
|
run: git-ssh-remotes
|
data/.github/workflows/test.yml
CHANGED
|
@@ -18,8 +18,8 @@ jobs:
|
|
|
18
18
|
strategy:
|
|
19
19
|
fail-fast: false
|
|
20
20
|
matrix:
|
|
21
|
-
ruby: ['
|
|
22
|
-
rails: ['8.
|
|
21
|
+
ruby: ['4.0']
|
|
22
|
+
rails: ['8.1']
|
|
23
23
|
env:
|
|
24
24
|
BUNDLE_GEMFILE: 'gemfiles/rails_${{ matrix.rails }}.gemfile'
|
|
25
25
|
steps:
|
|
@@ -36,7 +36,7 @@ jobs:
|
|
|
36
36
|
with:
|
|
37
37
|
ruby-version: ${{ matrix.ruby }}
|
|
38
38
|
bundler-cache: true
|
|
39
|
-
rubygems: '
|
|
39
|
+
rubygems: '4.0.11'
|
|
40
40
|
|
|
41
41
|
- name: Run the gem tests
|
|
42
42
|
run: make test
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -17,8 +17,8 @@ AllCops:
|
|
|
17
17
|
NewCops: enable
|
|
18
18
|
SuggestExtensions: false
|
|
19
19
|
DisplayCopNames: true
|
|
20
|
-
TargetRubyVersion:
|
|
21
|
-
TargetRailsVersion: 8.
|
|
20
|
+
TargetRubyVersion: 4.0
|
|
21
|
+
TargetRailsVersion: 8.1
|
|
22
22
|
Exclude:
|
|
23
23
|
- bin/**/*
|
|
24
24
|
- vendor/**/*
|
|
@@ -39,3 +39,7 @@ Rails/FilePath:
|
|
|
39
39
|
# Because we just implemented the ActiveRecord API.
|
|
40
40
|
Rails/SkipsModelValidations:
|
|
41
41
|
Enabled: false
|
|
42
|
+
|
|
43
|
+
# Because we don't have a Rails environment here.
|
|
44
|
+
Rails/RakeEnvironment:
|
|
45
|
+
Enabled: false
|
data/Appraisals
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
* TODO: Replace this bullet point with an actual description of a change.
|
|
4
4
|
|
|
5
|
+
### 3.0.0 (13 May 2026)
|
|
6
|
+
|
|
7
|
+
* Added automatic asset registering with Sprockets ([#46](https://github.com/hausgold/factory_bot_instrumentation/pull/46))
|
|
8
|
+
|
|
9
|
+
So you can remove the following lines from your `app/assets/config/manifest.js`:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
//= link factory_bot_instrumentation/application.css
|
|
13
|
+
//= link factory_bot_instrumentation/application.js
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
* Added automatic asset bundling to support Propshaft ([#46](https://github.com/hausgold/factory_bot_instrumentation/pull/46))
|
|
17
|
+
|
|
18
|
+
### 2.9.0 (4 May 2026)
|
|
19
|
+
|
|
20
|
+
* Dropped Ruby 3.x and Rails <8.1 support ([#45](https://github.com/hausgold/factory_bot_instrumentation/pull/45))
|
|
21
|
+
|
|
5
22
|
### 2.8.0 (18 February 2026)
|
|
6
23
|
|
|
7
24
|
* Added gemspec dependencies for all loaded gems ([#44](https://github.com/hausgold/factory_bot_instrumentation/pull/44))
|
data/Dockerfile
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
FROM hausgold/ruby:
|
|
1
|
+
FROM hausgold/ruby:4.0
|
|
2
2
|
LABEL org.opencontainers.image.authors="containers@hausgold.de"
|
|
3
3
|
|
|
4
4
|
# Update system gem
|
|
5
|
-
RUN gem update --system '
|
|
5
|
+
RUN gem update --system '4.0.11'
|
|
6
6
|
|
|
7
7
|
# Install system packages and the latest bundler
|
|
8
8
|
RUN apt-get update -yqqq && \
|
|
@@ -11,7 +11,7 @@ RUN apt-get update -yqqq && \
|
|
|
11
11
|
ca-certificates \
|
|
12
12
|
bash-completion inotify-tools && \
|
|
13
13
|
echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen && /usr/sbin/locale-gen && \
|
|
14
|
-
gem install bundler -v '~>
|
|
14
|
+
gem install bundler -v '~> 4.0.11' --no-document --no-prerelease
|
|
15
15
|
|
|
16
16
|
# Add new web user
|
|
17
17
|
RUN mkdir /app && \
|
data/Gemfile
CHANGED
data/Makefile
CHANGED
|
@@ -28,6 +28,7 @@ HEAD ?= head
|
|
|
28
28
|
ID ?= id
|
|
29
29
|
MKDIR ?= mkdir
|
|
30
30
|
RM ?= rm
|
|
31
|
+
SED ?= sed
|
|
31
32
|
SORT ?= sort
|
|
32
33
|
TEST ?= test
|
|
33
34
|
XARGS ?= xargs
|
|
@@ -121,7 +122,7 @@ test-style: \
|
|
|
121
122
|
test-style-ruby:
|
|
122
123
|
# Run the static code analyzer (rubocop)
|
|
123
124
|
@$(call run-shell,$(BUNDLE) exec $(RUBOCOP) -a \
|
|
124
|
-
|| ($(TEST) $$($(RUBY_VERSION)) != '
|
|
125
|
+
|| ($(TEST) $$($(RUBY_VERSION)) != '4.0' && true))
|
|
125
126
|
|
|
126
127
|
clean:
|
|
127
128
|
# Clean the dependencies
|
|
@@ -174,6 +175,11 @@ stats:
|
|
|
174
175
|
# Print all the notes from the code
|
|
175
176
|
@$(call run-shell,$(BUNDLE) exec $(RAKE) stats)
|
|
176
177
|
|
|
177
|
-
|
|
178
|
+
build:
|
|
179
|
+
# Build and prepare the gem for releasing
|
|
180
|
+
@$(call run-shell,$(BUNDLE) exec $(RAKE) bundle_assets)
|
|
181
|
+
@$(SED) -i '/spec.extensions/d' factory_bot_instrumentation.gemspec
|
|
182
|
+
|
|
183
|
+
release: build
|
|
178
184
|
# Release a new gem version
|
|
179
185
|
@$(BUNDLE) exec $(RAKE) release
|
data/Rakefile
CHANGED
|
@@ -11,6 +11,17 @@ require 'countless/rake_tasks'
|
|
|
11
11
|
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
|
12
12
|
load 'rails/tasks/engine.rake'
|
|
13
13
|
|
|
14
|
+
desc 'Bundle the engine JavaScript and CSS sources into application.{js,css}'
|
|
15
|
+
task :bundle_assets do
|
|
16
|
+
require 'factory_bot/instrumentation/asset_bundler'
|
|
17
|
+
FactoryBot::Instrumentation::AssetBundler.bundle_all!.each do |kind, files|
|
|
18
|
+
conf = FactoryBot::Instrumentation::AssetBundler.config(kind)
|
|
19
|
+
rel = files.map { |f| f.sub("#{conf[:source_dir]}/", '') }
|
|
20
|
+
puts "Bundled #{files.size} #{kind.upcase} source(s) into " \
|
|
21
|
+
"#{conf[:output_name]}: #{rel.join(', ')}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
14
25
|
desc 'Run all specs in spec directory (excluding plugin specs)'
|
|
15
26
|
RSpec::Core::RakeTask.new(spec: [
|
|
16
27
|
'db:drop', 'db:create', 'db:migrate', 'db:setup'
|
|
@@ -18,6 +29,14 @@ RSpec::Core::RakeTask.new(spec: [
|
|
|
18
29
|
|
|
19
30
|
task default: :spec
|
|
20
31
|
|
|
32
|
+
# Neuter Bundler's `release:guard_clean` check. The `make build` step prunes
|
|
33
|
+
# the unbundled JS/CSS sources from `app/assets/**/factory_bot_instrumentation/`
|
|
34
|
+
# after bundling them into `application.{js,css}`, which leaves the working
|
|
35
|
+
# tree dirty by design. The commit + tag are already in place before release,
|
|
36
|
+
# so the clean-tree guard would only block pushing the artefact we just built.
|
|
37
|
+
Rake::Task['release:guard_clean'].clear
|
|
38
|
+
task 'release:guard_clean'
|
|
39
|
+
|
|
21
40
|
# Configure all code statistics directories
|
|
22
41
|
Countless.configure do |config|
|
|
23
42
|
config.stats_base_directories = [
|
|
@@ -1,16 +1,458 @@
|
|
|
1
|
-
//
|
|
2
|
-
// include all the files listed below.
|
|
1
|
+
// !!! AUTO-GENERATED FILE - DO NOT EDIT !!!
|
|
3
2
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// This file bundles every JS source under
|
|
4
|
+
// +app/assets/javascripts/factory_bot_instrumentation/+ into a single asset. It is regenerated:
|
|
5
|
+
// * on `gem install` / `bundle install` (via a RubyGems extension
|
|
6
|
+
// hook), and
|
|
7
|
+
// * during gem release (via the `bundle_assets` Rake task).
|
|
7
8
|
//
|
|
8
|
-
//
|
|
9
|
-
// the bottom of the compiled file. JavaScript code in this file should be
|
|
10
|
-
// added after the last require_* statement.
|
|
9
|
+
// To rebuild it manually, run:
|
|
11
10
|
//
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
//
|
|
11
|
+
// $ bundle exec rake bundle_assets
|
|
12
|
+
|
|
13
|
+
// >>> create.js
|
|
14
|
+
window.CreateForm = CreateForm = function()
|
|
15
|
+
{
|
|
16
|
+
this.scope = '#generate';
|
|
17
|
+
this.form = new Form(this.scope);
|
|
18
|
+
this.scenarios = window.scenarios;
|
|
19
|
+
this.select = $(`${this.scope} .scenario`);
|
|
20
|
+
this.desc = $(`${this.scope} .description`);
|
|
21
|
+
|
|
22
|
+
let self = this;
|
|
23
|
+
|
|
24
|
+
this.form.errorContent = function(payload, output, cb)
|
|
25
|
+
{
|
|
26
|
+
window.utils.waterfallWithHooks({
|
|
27
|
+
data: {
|
|
28
|
+
alert: `An unexpected error occurred. Looks like something went wrong
|
|
29
|
+
while generating your new entity. This might be a bug, or an
|
|
30
|
+
unexpected feature. It could be a temporary issue. When this
|
|
31
|
+
is persistent contact your friendly API Instrumentation
|
|
32
|
+
administrator.`,
|
|
33
|
+
output: output,
|
|
34
|
+
payload: payload,
|
|
35
|
+
pre: '',
|
|
36
|
+
post: ''
|
|
37
|
+
},
|
|
38
|
+
pre: window.hooks.preCreateError,
|
|
39
|
+
post: window.hooks.postCreateError,
|
|
40
|
+
action: (payload, innerCb) => {
|
|
41
|
+
cb(null, `
|
|
42
|
+
${payload.pre}
|
|
43
|
+
<div class="alert alert-danger" role="alert">${payload.alert}</div>
|
|
44
|
+
<pre id="data">${payload.output}</pre>
|
|
45
|
+
${window.utils.clipboardButton()}
|
|
46
|
+
${payload.post}
|
|
47
|
+
`);
|
|
48
|
+
innerCb(null, payload);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
this.form.resultContent = function(payload, output, cb)
|
|
54
|
+
{
|
|
55
|
+
let card = window.utils.card({
|
|
56
|
+
body: `
|
|
57
|
+
<pre id="data">${output}</pre>
|
|
58
|
+
${window.utils.clipboardButton()}
|
|
59
|
+
`
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
window.utils.waterfallWithHooks({
|
|
63
|
+
data: {
|
|
64
|
+
alert: `A new ${self.scenario().name.toLowerCase()} was created.`,
|
|
65
|
+
output: output,
|
|
66
|
+
payload: payload,
|
|
67
|
+
cards: [card],
|
|
68
|
+
pre: '',
|
|
69
|
+
post: '',
|
|
70
|
+
openCard: '#details'
|
|
71
|
+
},
|
|
72
|
+
pre: window.hooks.preCreateResult,
|
|
73
|
+
post: window.hooks.postCreateResult,
|
|
74
|
+
action: (payload, innerCb) => {
|
|
75
|
+
cb(null, `
|
|
76
|
+
${payload.pre}
|
|
77
|
+
<div class="alert alert-success" role="alert">${payload.alert}</div>
|
|
78
|
+
<div class="accordion" id="response">
|
|
79
|
+
${payload.cards.join(' ')}
|
|
80
|
+
</div>
|
|
81
|
+
${payload.post}
|
|
82
|
+
`);
|
|
83
|
+
innerCb(null, payload);
|
|
84
|
+
if (payload.openCard) {
|
|
85
|
+
$(
|
|
86
|
+
`.accordion#response button[data-target="${payload.openCard}"]`
|
|
87
|
+
).click();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
CreateForm.prototype.updateDesc = function()
|
|
95
|
+
{
|
|
96
|
+
this.desc.html(this.scenario().desc);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
CreateForm.prototype.activeScenario = function()
|
|
100
|
+
{
|
|
101
|
+
raw = this.select.find(':selected').val().split('/');
|
|
102
|
+
return { group: raw[0], index: raw[1] };
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
CreateForm.prototype.scenario = function()
|
|
106
|
+
{
|
|
107
|
+
scenario = this.activeScenario();
|
|
108
|
+
return this.scenarios[scenario.group][scenario.index];
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
CreateForm.prototype.bind = function()
|
|
112
|
+
{
|
|
113
|
+
this.form.bind((event) => {
|
|
114
|
+
this.submit();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.select.on('change', this.updateDesc.bind(this));
|
|
118
|
+
this.updateDesc();
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
CreateForm.prototype.submit = function()
|
|
122
|
+
{
|
|
123
|
+
let form = this.form;
|
|
124
|
+
let conf = this.scenario();
|
|
125
|
+
|
|
126
|
+
window.utils.waterfallWithHooks({
|
|
127
|
+
data: {
|
|
128
|
+
factory: conf.factory,
|
|
129
|
+
traits: conf.traits,
|
|
130
|
+
overwrite: conf.overwrite
|
|
131
|
+
},
|
|
132
|
+
pre: window.hooks.preCreate,
|
|
133
|
+
post: window.hooks.postCreate,
|
|
134
|
+
action: (payload, cb) => {
|
|
135
|
+
window.utils.request({
|
|
136
|
+
url: window.createUrl,
|
|
137
|
+
data: JSON.stringify(payload)
|
|
138
|
+
}, (err, result) => {
|
|
139
|
+
if (err) { return cb && cb(err); }
|
|
140
|
+
cb(null, { request: payload, response: result });
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}, function(err, result) {
|
|
144
|
+
if (err) { return form.showError(err, err.responseText); }
|
|
145
|
+
form.showResult(result, result.response);
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// >>> hooks.js
|
|
150
|
+
// You can define some custom hooks to enhance the functionality. With the help
|
|
151
|
+
// of the following hooks you are able to customize the outputs, perform
|
|
152
|
+
// additional HTTP requests or anything you like.
|
|
15
153
|
//
|
|
16
|
-
|
|
154
|
+
// All the hooks are designed to passthrough a payload. They receive this
|
|
155
|
+
// payload as the first argument, and a callback function to signal the end of
|
|
156
|
+
// the hook. You MUST pass the payload as second parameter to the callback, or
|
|
157
|
+
// pass an error object as first argument. You can modify the payload as you
|
|
158
|
+
// wish, eg. adding some data from subsequent requests.
|
|
159
|
+
//
|
|
160
|
+
// Example hooks:
|
|
161
|
+
//
|
|
162
|
+
// // Error case
|
|
163
|
+
// window.hooks.postCreate.push((payload, cb) => {
|
|
164
|
+
// cb({ error: true});
|
|
165
|
+
// });
|
|
166
|
+
//
|
|
167
|
+
// // Happy case
|
|
168
|
+
// window.hooks.postCreate.push((payload, cb) => {
|
|
169
|
+
// cb(null, Object.assign(payload, { additional: { data: true } }));
|
|
170
|
+
// });
|
|
171
|
+
//
|
|
172
|
+
// Mind the fact that you can define multiple custom functions per hook type.
|
|
173
|
+
// They are executed after each other in a waterfall like flow. The order of
|
|
174
|
+
// the hooks array is therefore essential.
|
|
175
|
+
window.hooks = {
|
|
176
|
+
// With the help of the +perCreate+ hooks you can manipulate the create
|
|
177
|
+
// request parameters. Think of an additional handling which reads an
|
|
178
|
+
// overwrite form or a kind of trait checkboxes to customize the factory
|
|
179
|
+
// call. The +payload+ looks like this:
|
|
180
|
+
//
|
|
181
|
+
// {
|
|
182
|
+
// factory: 'user',
|
|
183
|
+
// traits: ['confirmed'],
|
|
184
|
+
// overwrite: { password: 'secret' }
|
|
185
|
+
// }
|
|
186
|
+
preCreate: [],
|
|
187
|
+
|
|
188
|
+
// The +postCreate+ hook allows you to perform subsequent requests to fetch
|
|
189
|
+
// additional data. Think of a user instrumentation where you want to request
|
|
190
|
+
// a one time token for this user. This token can be added to the payload and
|
|
191
|
+
// can be shown with the help of the +preCreateResult+ hook. The payload
|
|
192
|
+
// contains the request parameters and the response body from the
|
|
193
|
+
// instrumentation request. Here comes an example +payload+:
|
|
194
|
+
//
|
|
195
|
+
// {
|
|
196
|
+
// request: { factory: 'user', /* [..] */ },
|
|
197
|
+
// response: { /* [..] */ }
|
|
198
|
+
// }
|
|
199
|
+
postCreate: [],
|
|
200
|
+
|
|
201
|
+
// With the help of the +preCreateResult+ hook you can customize the output
|
|
202
|
+
// of the result. You could also perform some subsequent requests or some UI
|
|
203
|
+
// preparations. You can access the output options and the runtime payload
|
|
204
|
+
// with all its data and make modifications to them. This hook is triggered
|
|
205
|
+
// before the result is rendered. A sample payload comes here:
|
|
206
|
+
//
|
|
207
|
+
// {
|
|
208
|
+
// alert: 'Your alert text.',
|
|
209
|
+
// output: 'Formatted response',
|
|
210
|
+
// payload: { request: { /* [..] */ }, response: { /* [..] */ } },
|
|
211
|
+
// cards: [
|
|
212
|
+
// `The details accordion card,
|
|
213
|
+
// you can add more, remove the details card
|
|
214
|
+
// or reorder them`
|
|
215
|
+
// ],
|
|
216
|
+
// openCard: '#details', // Open a custom card, or none
|
|
217
|
+
// pre: 'Additinal HTML content before the alert.',
|
|
218
|
+
// post: 'Additinal HTML content after the formatted response output.'
|
|
219
|
+
// }
|
|
220
|
+
preCreateResult: [],
|
|
221
|
+
|
|
222
|
+
// In case you want to perform some logic after the result is rendered, you
|
|
223
|
+
// can use the +postCreateResult+ hook. You can access the output options and
|
|
224
|
+
// the runtime payload with all its data, but changes to them won't take
|
|
225
|
+
// effect. The +payload+ looks like this:
|
|
226
|
+
//
|
|
227
|
+
// {
|
|
228
|
+
// alert: 'Your alert text.',
|
|
229
|
+
// output: 'Formatted response',
|
|
230
|
+
// payload: { request: { /* [..] */ }, response: { /* [..] */ } },
|
|
231
|
+
// cards: [
|
|
232
|
+
// `The details accordion card,
|
|
233
|
+
// you can add more, remove the details card
|
|
234
|
+
// or reorder them`
|
|
235
|
+
// ],
|
|
236
|
+
// openCard: '#details', // Open a custom card, or none
|
|
237
|
+
// pre: 'Additinal HTML content before the alert.',
|
|
238
|
+
// post: 'Additinal HTML content after the formatted response output.'
|
|
239
|
+
// }
|
|
240
|
+
postCreateResult: [],
|
|
241
|
+
|
|
242
|
+
// With the help of the +preCreateError+ hook you can customize the output of
|
|
243
|
+
// the error. Furthermore you can perform some subsequent requests or
|
|
244
|
+
// whatever comes to your mind. You can access the output options and the
|
|
245
|
+
// runtime payload with all its data and make modifications to them. This
|
|
246
|
+
// hook is triggered before the error is rendered. A sample payload comes
|
|
247
|
+
// here:
|
|
248
|
+
//
|
|
249
|
+
// {
|
|
250
|
+
// alert: 'Your alert text.',
|
|
251
|
+
// output: 'Formatted response',
|
|
252
|
+
// payload: { request: { /* [..] */ }, response: { /* [..] */ } },
|
|
253
|
+
// pre: 'Additinal HTML content before the alert.',
|
|
254
|
+
// post: 'Additinal HTML content after the formatted response output.'
|
|
255
|
+
// }
|
|
256
|
+
preCreateError: [],
|
|
257
|
+
|
|
258
|
+
// In case you want to perform some magic after an error occurred, you can use
|
|
259
|
+
// the +postCreateError+ hook. You can access the output options and the
|
|
260
|
+
// runtime payload with all its data, but changes to them won't take effect
|
|
261
|
+
// because this hook is triggered after the error is rendered. The +payload+
|
|
262
|
+
// looks like this:
|
|
263
|
+
//
|
|
264
|
+
// {
|
|
265
|
+
// alert: 'Your alert text.',
|
|
266
|
+
// output: 'Formatted response',
|
|
267
|
+
// payload: { request: { /* [..] */ }, response: { /* [..] */ } },
|
|
268
|
+
// pre: 'Additinal HTML content before the alert.',
|
|
269
|
+
// post: 'Additinal HTML content after the formatted response output.'
|
|
270
|
+
// }
|
|
271
|
+
postCreateError: []
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// >>> lib/form.js
|
|
275
|
+
window.Form = Form = function(scope)
|
|
276
|
+
{
|
|
277
|
+
this.button = $(scope).find('button');
|
|
278
|
+
this.result = $('#result');
|
|
279
|
+
this.spinner = $('#spinner');
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
Form.prototype.bind = function(action)
|
|
283
|
+
{
|
|
284
|
+
this.button.on('click', (event) => {
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
this.hideResult();
|
|
287
|
+
action(event);
|
|
288
|
+
return false;
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
Form.prototype.hideResult = function()
|
|
293
|
+
{
|
|
294
|
+
this.result.hide();
|
|
295
|
+
this.button.prop('disabled', true);
|
|
296
|
+
this.spinner.show();
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
Form.prototype.showResultContainer = function(html)
|
|
300
|
+
{
|
|
301
|
+
this.result.html(html);
|
|
302
|
+
$('pre').each((i, block) => hljs.highlightBlock(block));
|
|
303
|
+
new ClipboardJS('.cb-copy');
|
|
304
|
+
this.result.show();
|
|
305
|
+
this.button.prop('disabled', false);
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
Form.prototype.showError = function(payload, output)
|
|
309
|
+
{
|
|
310
|
+
this.spinner.hide();
|
|
311
|
+
output = window.utils.prepareOutput(output);
|
|
312
|
+
this.errorContent(payload, output, (err, html) => {
|
|
313
|
+
this.showResultContainer(html);
|
|
314
|
+
});
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
Form.prototype.errorContent = function(payload, output, cb)
|
|
318
|
+
{
|
|
319
|
+
cb(null, `
|
|
320
|
+
<pre id="data">${output}</pre>
|
|
321
|
+
${window.utils.clipboardButton()}
|
|
322
|
+
`);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
Form.prototype.showResult = function(payload, output)
|
|
326
|
+
{
|
|
327
|
+
this.spinner.hide();
|
|
328
|
+
output = window.utils.prepareOutput(output);
|
|
329
|
+
this.resultContent(payload, output, (err, html) => {
|
|
330
|
+
this.showResultContainer(html);
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
Form.prototype.resultContent = function(payload, output, cb)
|
|
335
|
+
{
|
|
336
|
+
cb(null, `
|
|
337
|
+
<pre id="data">${output}</pre>
|
|
338
|
+
${window.utils.clipboardButton()}
|
|
339
|
+
`);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// >>> lib/utils.js
|
|
343
|
+
window.utils = Utils = {};
|
|
344
|
+
|
|
345
|
+
Utils.pushWaterfallPayload = function(data)
|
|
346
|
+
{
|
|
347
|
+
return (cb) => cb(null, data);
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
Utils.waterfallWithHooks = function(opts, cb)
|
|
351
|
+
{
|
|
352
|
+
cb = cb || function(){};
|
|
353
|
+
opts = Object.assign({
|
|
354
|
+
pre: [],
|
|
355
|
+
post: [],
|
|
356
|
+
data: {},
|
|
357
|
+
action: (payload, cb) => cb(null, payload)
|
|
358
|
+
}, opts);
|
|
359
|
+
|
|
360
|
+
async.waterfall([
|
|
361
|
+
// Yield the data to pre hooks
|
|
362
|
+
Utils.pushWaterfallPayload(opts.data),
|
|
363
|
+
// Perform pre hooks
|
|
364
|
+
...opts.pre,
|
|
365
|
+
// Perform the create request
|
|
366
|
+
opts.action,
|
|
367
|
+
// Perform post hooks
|
|
368
|
+
...opts.post
|
|
369
|
+
], cb);
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
Utils.request = function(opts, cb)
|
|
373
|
+
{
|
|
374
|
+
opts = Object.assign({
|
|
375
|
+
url: '/',
|
|
376
|
+
type: 'POST',
|
|
377
|
+
data: '{}',
|
|
378
|
+
dataType: 'json',
|
|
379
|
+
contentType: 'application/json; charset=utf-8',
|
|
380
|
+
ignoreErrors: false
|
|
381
|
+
}, opts || {}, {
|
|
382
|
+
success: (result) => cb(null, result)
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
errCb = (err) => cb(err);
|
|
386
|
+
if (opts.ignoreErrors) {
|
|
387
|
+
errCb = (err) => cb(null, err);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
$.ajax(opts).fail(errCb);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
Utils.escape = function(str)
|
|
394
|
+
{
|
|
395
|
+
return str.replace(/&/g, "&")
|
|
396
|
+
.replace(/</g, "<")
|
|
397
|
+
.replace(/>/g, ">");
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
Utils.prepareOutput = function(output)
|
|
401
|
+
{
|
|
402
|
+
try {
|
|
403
|
+
if (typeof output !== 'object') { output = JSON.parse(output); }
|
|
404
|
+
output = JSON.stringify(output, null, 2);
|
|
405
|
+
} catch { }
|
|
406
|
+
|
|
407
|
+
return window.utils.escape(output);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
Utils.clipboardButton = function(id)
|
|
411
|
+
{
|
|
412
|
+
id = id || 'data';
|
|
413
|
+
return `
|
|
414
|
+
<span class="btn btn-primary cb-copy"
|
|
415
|
+
data-clipboard-target="#${id}">
|
|
416
|
+
<i class="fas fa-paste"></i> Copy result to clipboard
|
|
417
|
+
</span>
|
|
418
|
+
`;
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
Utils.clipboardBadge = function(id)
|
|
422
|
+
{
|
|
423
|
+
id = id || 'data';
|
|
424
|
+
return `
|
|
425
|
+
<span class="badge badge-dark cb-copy" title="Copy result to clipboard"
|
|
426
|
+
data-clipboard-target="#${id}"><i class="fas fa-paste"></i>
|
|
427
|
+
</span>
|
|
428
|
+
`;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
Utils.card = function(opts)
|
|
432
|
+
{
|
|
433
|
+
opts = Object.assign({
|
|
434
|
+
id: 'details',
|
|
435
|
+
icon: 'fa-asterisk',
|
|
436
|
+
title: 'Details',
|
|
437
|
+
body: ''
|
|
438
|
+
}, opts || {});
|
|
439
|
+
|
|
440
|
+
return `
|
|
441
|
+
<div class="card">
|
|
442
|
+
<div class="card-header">
|
|
443
|
+
<h5 class="mb-0">
|
|
444
|
+
<button class="btn btn-link collapsed" type="button"
|
|
445
|
+
data-toggle="collapse" data-target="#${opts.id}"
|
|
446
|
+
aria-expanded="false">
|
|
447
|
+
<i class="fas ${opts.icon}"></i> ${opts.title}</h5>
|
|
448
|
+
</button>
|
|
449
|
+
</h5>
|
|
450
|
+
</div>
|
|
451
|
+
<div id="${opts.id}" class="collapse" data-parent="#response">
|
|
452
|
+
<div class="card-body">
|
|
453
|
+
${opts.body}
|
|
454
|
+
</div>
|
|
455
|
+
</div>
|
|
456
|
+
</div>
|
|
457
|
+
`;
|
|
458
|
+
};
|