demo_mode 2.3.0 → 3.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 +4 -4
- data/app/controllers/demo_mode/sessions_controller.rb +5 -2
- data/app/jobs/demo_mode/account_generation_job.rb +4 -2
- data/app/models/demo_mode/session.rb +20 -0
- data/app/views/demo_mode/sessions/show.html.erb +5 -1
- data/db/migrate/20250210222933_add_demo_mode_sessions_status.rb +13 -0
- data/lib/demo_mode/persona.rb +8 -0
- data/lib/demo_mode/version.rb +1 -1
- data/public/demo_mode/assets/demo_mode.css +4 -0
- data/public/demo_mode/assets/demo_mode.js +50 -37
- metadata +4 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10de8b9508baa5777b2781794f82c8babd7f8c24bd70dcdfe3b8a7a875e583d8
|
4
|
+
data.tar.gz: 76b7e2216e689a68c81fed89e90b553642936a7eff850a0b65b0f75e9b809f1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '087250bc248228cd873012cf28653b98919864f46146ed278f0138a0e4000e75d5f151695e36735e795115cdcf2f042fff1dd1d3f14e4ae15bca37a318b05fee'
|
7
|
+
data.tar.gz: 9300fc6fa3fd1a2f84dc904ea8c7822e3b8b07e05d225ca403d423cbf9ae484b9250c5de2463dafe8a73453a0c3c25585aff35ca1a01884acb98568529672206
|
@@ -26,6 +26,7 @@ module DemoMode
|
|
26
26
|
def create
|
27
27
|
@session = Session.new(create_params)
|
28
28
|
@session.save_and_generate_account_later!(**options_params.to_unsafe_h.deep_symbolize_keys)
|
29
|
+
@session.reload
|
29
30
|
session[:demo_session] = { 'id' => @session.id, 'last_request_at' => Time.zone.now }
|
30
31
|
respond_to do |f|
|
31
32
|
f.html { redirect_to @session, status: :see_other }
|
@@ -50,13 +51,15 @@ module DemoMode
|
|
50
51
|
|
51
52
|
def render_signinable_json
|
52
53
|
if @session.signinable.blank?
|
53
|
-
render json: { id: @session.id, processing:
|
54
|
+
render json: { id: @session.id, processing: @session.processing?, status: @session.status }
|
54
55
|
else
|
55
56
|
render json: {
|
56
57
|
id: @session.id,
|
57
|
-
processing:
|
58
|
+
processing: @session.processing?,
|
59
|
+
status: @session.status,
|
58
60
|
username: @session.signinable_username,
|
59
61
|
password: @session.signinable_password,
|
62
|
+
metadata: @session.signinable_metadata,
|
60
63
|
}
|
61
64
|
end
|
62
65
|
end
|
@@ -8,9 +8,11 @@ module DemoMode
|
|
8
8
|
raise "Unknown persona: #{session.persona_name}" if persona.blank?
|
9
9
|
|
10
10
|
signinable = persona.generate!(variant: session.variant, password: session.signinable_password, options: options)
|
11
|
-
session.update!(signinable: signinable)
|
11
|
+
session.update!(signinable: signinable, status: 'successful')
|
12
12
|
end
|
13
|
-
|
13
|
+
rescue StandardError => e
|
14
|
+
session.update!(status: 'failed')
|
15
|
+
raise e
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -4,8 +4,17 @@ module DemoMode
|
|
4
4
|
class Session < ActiveRecord::Base
|
5
5
|
attribute :variant, default: :default
|
6
6
|
|
7
|
+
if ActiveRecord.gem_version >= Gem::Version.new('7.2')
|
8
|
+
enum :status, { processing: 'processing', successful: 'successful', failed: 'failed' }, default: 'processing'
|
9
|
+
else
|
10
|
+
attribute :status, default: :processing
|
11
|
+
enum status: { processing: 'processing', successful: 'successful', failed: 'failed' }
|
12
|
+
end
|
13
|
+
|
7
14
|
validates :persona_name, :variant, presence: true
|
8
15
|
validates :persona, presence: { message: :required }, on: :create, if: :persona_name?
|
16
|
+
validate :successful_status_requires_signinable
|
17
|
+
|
9
18
|
belongs_to :signinable, polymorphic: true, optional: true
|
10
19
|
|
11
20
|
before_create :set_password!
|
@@ -13,6 +22,7 @@ module DemoMode
|
|
13
22
|
delegate :begin_demo,
|
14
23
|
:custom_sign_in?,
|
15
24
|
:display_credentials?,
|
25
|
+
:metadata,
|
16
26
|
to: :persona,
|
17
27
|
allow_nil: true
|
18
28
|
|
@@ -20,6 +30,10 @@ module DemoMode
|
|
20
30
|
signinable.public_send(DemoMode.signinable_username_method)
|
21
31
|
end
|
22
32
|
|
33
|
+
def signinable_metadata
|
34
|
+
successful? ? metadata.call(self) : {}
|
35
|
+
end
|
36
|
+
|
23
37
|
# Heads up: finding a persona is not guaranteed (e.g. past sessions)
|
24
38
|
def persona
|
25
39
|
DemoMode.personas.find { |p| p.name.to_s == persona_name.to_s }
|
@@ -44,5 +58,11 @@ module DemoMode
|
|
44
58
|
def set_password!
|
45
59
|
self.signinable_password ||= DemoMode.current_password
|
46
60
|
end
|
61
|
+
|
62
|
+
def successful_status_requires_signinable
|
63
|
+
if status == 'successful' && signinable.blank?
|
64
|
+
errors.add(:status, 'cannot be successful if signinable is not present')
|
65
|
+
end
|
66
|
+
end
|
47
67
|
end
|
48
68
|
end
|
@@ -17,10 +17,14 @@
|
|
17
17
|
</article>
|
18
18
|
<% else %>
|
19
19
|
<article class="middle center" data-polling-refresh-url='<%= session_path(@session, format: :json) %>'>
|
20
|
-
<figure>
|
20
|
+
<figure id="LoadingMessage" class="<%= 'hidden' if @session.failed? %>">
|
21
21
|
<%= instance_eval(&DemoMode.loader) %>
|
22
22
|
<h4><span id="DemoTypedText"></span></h4>
|
23
23
|
</figure>
|
24
|
+
<div id="ErrorMessage" class="<%= 'hidden' unless @session.failed? %>">
|
25
|
+
<div>Unable to generate persona.</div>
|
26
|
+
<%= link_to "Go back to persona selection", new_session_path, aria: { label: 'Homepage' } %>
|
27
|
+
</div>
|
24
28
|
|
25
29
|
<script type="text/javascript">
|
26
30
|
new Typed('#DemoTypedText', {
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AddDemoModeSessionsStatus < ActiveRecord::Migration[5.1]
|
4
|
+
def change
|
5
|
+
add_column :demo_mode_sessions, :status, :string, null: false, default: 'processing'
|
6
|
+
|
7
|
+
reversible do |dir|
|
8
|
+
dir.up do
|
9
|
+
execute "UPDATE demo_mode_sessions SET status = 'successful'"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/demo_mode/persona.rb
CHANGED
data/lib/demo_mode/version.rb
CHANGED
@@ -1,15 +1,24 @@
|
|
1
|
-
(function(document, performance, location) {
|
1
|
+
(function (document, performance, location) {
|
2
2
|
function* getJSON(url) {
|
3
|
-
while(true) {
|
3
|
+
while (true) {
|
4
|
+
yield fetch(url).then((resp) => resp.json());
|
5
|
+
}
|
4
6
|
}
|
5
7
|
|
6
8
|
function pollURL(url, minInterval, success, func) {
|
7
9
|
const then = performance.now();
|
8
|
-
if(!minInterval) {
|
9
|
-
|
10
|
+
if (!minInterval) {
|
11
|
+
minInterval = 1000;
|
12
|
+
}
|
13
|
+
if (!func) {
|
14
|
+
func = getJSON(url);
|
15
|
+
}
|
10
16
|
|
11
17
|
func.next().value.then((resp) => {
|
12
|
-
if(resp.
|
18
|
+
if (resp.status === 'failed') {
|
19
|
+
document.querySelector("#LoadingMessage").classList.add("hidden");
|
20
|
+
document.querySelector("#ErrorMessage").classList.remove("hidden");
|
21
|
+
} else if (resp.status === 'processing') {
|
13
22
|
setTimeout(
|
14
23
|
() => pollURL(url, minInterval, success, func),
|
15
24
|
Math.max(0, minInterval - (performance.now() - then))
|
@@ -20,50 +29,54 @@
|
|
20
29
|
});
|
21
30
|
}
|
22
31
|
|
23
|
-
document.addEventListener(
|
24
|
-
[...document.querySelectorAll(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
document.addEventListener("DOMContentLoaded", (event) => {
|
33
|
+
[...document.querySelectorAll("[data-polling-refresh-url]")].forEach(
|
34
|
+
(elem) => {
|
35
|
+
pollURL(
|
36
|
+
elem.getAttribute("data-polling-refresh-url"),
|
37
|
+
elem.getAttribute("data-polling-refresh-interval") * 1000,
|
38
|
+
() => location.reload()
|
39
|
+
);
|
40
|
+
}
|
41
|
+
);
|
31
42
|
});
|
32
|
-
}(document, performance, location)
|
43
|
+
})(document, performance, location);
|
33
44
|
|
34
|
-
(function() {
|
35
|
-
var TableFilter = (function() {
|
45
|
+
(function () {
|
46
|
+
var TableFilter = (function () {
|
36
47
|
var input;
|
37
48
|
var inputValue;
|
38
49
|
|
39
50
|
function onInputEvent(e) {
|
40
51
|
input = e.target;
|
41
|
-
inputValue = input.value.toLowerCase().replace(/[^0-9a-zA-Z ]/g,
|
42
|
-
updateTable()
|
43
|
-
updateHistory()
|
52
|
+
inputValue = input.value.toLowerCase().replace(/[^0-9a-zA-Z ]/g, "");
|
53
|
+
updateTable();
|
54
|
+
updateHistory();
|
44
55
|
}
|
45
56
|
|
46
57
|
function updateTable() {
|
47
58
|
var table = document.querySelector(`.${input.dataset.table}`);
|
48
59
|
if (table) {
|
49
|
-
Array.prototype.forEach.call(table.tBodies, function(tbody) {
|
60
|
+
Array.prototype.forEach.call(table.tBodies, function (tbody) {
|
50
61
|
Array.prototype.forEach.call(tbody.rows, filter);
|
51
62
|
});
|
52
63
|
} else {
|
53
|
-
alert(
|
64
|
+
alert("TableFilter cannot find its table");
|
54
65
|
}
|
55
66
|
}
|
56
67
|
|
57
68
|
function updateHistory() {
|
58
|
-
var searchParams = new URLSearchParams(window.location.search)
|
69
|
+
var searchParams = new URLSearchParams(window.location.search);
|
59
70
|
searchParams.set("search", inputValue);
|
60
|
-
var newRelativePathQuery =
|
61
|
-
|
71
|
+
var newRelativePathQuery =
|
72
|
+
window.location.pathname + "?" + searchParams.toString();
|
73
|
+
history.pushState(null, "", newRelativePathQuery);
|
62
74
|
}
|
63
75
|
|
64
76
|
function filter(row) {
|
65
|
-
var text = row.textContent.toLowerCase().replace(/[^0-9a-zA-Z ]/g,
|
66
|
-
row.style.display =
|
77
|
+
var text = row.textContent.toLowerCase().replace(/[^0-9a-zA-Z ]/g, "");
|
78
|
+
row.style.display =
|
79
|
+
text.indexOf(inputValue) === -1 ? "none" : "table-row";
|
67
80
|
}
|
68
81
|
|
69
82
|
function debounce(func, threshold) {
|
@@ -76,7 +89,7 @@
|
|
76
89
|
function delayed() {
|
77
90
|
func.apply(obj, args);
|
78
91
|
timeout = null;
|
79
|
-
}
|
92
|
+
}
|
80
93
|
|
81
94
|
if (timeout) {
|
82
95
|
clearTimeout(timeout);
|
@@ -86,32 +99,32 @@
|
|
86
99
|
}
|
87
100
|
|
88
101
|
return {
|
89
|
-
init: function() {
|
90
|
-
var input = document.querySelector(
|
102
|
+
init: function () {
|
103
|
+
var input = document.querySelector("input[data-behavior=table-filter]");
|
91
104
|
if (!input) return;
|
92
105
|
|
93
106
|
input.oninput = debounce(onInputEvent, 250);
|
94
107
|
var urlParams = new URLSearchParams(window.location.search);
|
95
|
-
var search = urlParams.get(
|
108
|
+
var search = urlParams.get("search");
|
96
109
|
if (search) {
|
97
|
-
input.value = search
|
98
|
-
input.dispatchEvent(new InputEvent(
|
110
|
+
input.value = search;
|
111
|
+
input.dispatchEvent(new InputEvent("input", { data: search }));
|
99
112
|
}
|
100
113
|
|
101
|
-
window.addEventListener(
|
114
|
+
window.addEventListener("popstate", (event) => {
|
102
115
|
var urlParams = new URLSearchParams(window.location.search);
|
103
|
-
var search = urlParams.get(
|
116
|
+
var search = urlParams.get("search");
|
104
117
|
if (search) {
|
105
118
|
input.value = search;
|
106
119
|
inputValue = search;
|
107
|
-
updateTable()
|
120
|
+
updateTable();
|
108
121
|
}
|
109
122
|
});
|
110
|
-
}
|
123
|
+
},
|
111
124
|
};
|
112
125
|
})();
|
113
126
|
|
114
|
-
document.addEventListener(
|
127
|
+
document.addEventListener("DOMContentLoaded", (event) => {
|
115
128
|
TableFilter.init();
|
116
129
|
});
|
117
130
|
})();
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: demo_mode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Griffith
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-
|
10
|
+
date: 2025-03-19 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: actionpack
|
@@ -322,6 +321,7 @@ files:
|
|
322
321
|
- db/migrate/20190503143021_add_demo_mode_sessions.rb
|
323
322
|
- db/migrate/20201111000000_add_demo_mode_sessions_variant.rb
|
324
323
|
- db/migrate/20210505000000_add_demo_mode_sessions_password.rb
|
324
|
+
- db/migrate/20250210222933_add_demo_mode_sessions_status.rb
|
325
325
|
- lib/demo_mode.rb
|
326
326
|
- lib/demo_mode/clever_sequence.rb
|
327
327
|
- lib/demo_mode/cli.rb
|
@@ -350,7 +350,6 @@ licenses:
|
|
350
350
|
metadata:
|
351
351
|
rubygems_mfa_required: 'true'
|
352
352
|
changelog_uri: https://github.com/Betterment/demo_mode/blob/main/CHANGELOG.md
|
353
|
-
post_install_message:
|
354
353
|
rdoc_options: []
|
355
354
|
require_paths:
|
356
355
|
- lib
|
@@ -365,8 +364,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
365
364
|
- !ruby/object:Gem::Version
|
366
365
|
version: '0'
|
367
366
|
requirements: []
|
368
|
-
rubygems_version: 3.5
|
369
|
-
signing_key:
|
367
|
+
rubygems_version: 3.6.5
|
370
368
|
specification_version: 4
|
371
369
|
summary: A configurable demo mode for your Rails app.
|
372
370
|
test_files: []
|