rails-wayback 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 837e9fe96c57c95c97120d9763ce5f479f96de05ef809efd9ff28489a4747e33
4
- data.tar.gz: 1123f82ce9d8e4a16b71fa318c881ca2fc0ec3a8ca50556a4bc3da720e1cab67
3
+ metadata.gz: 9d994f992691b844562d34eb5e06afaf69be7502d98c2107313a7fabcfe99209
4
+ data.tar.gz: d8175d1cd12a74f6db23b6a58c4d26acd11a36825a98b6c49da50515c154d945
5
5
  SHA512:
6
- metadata.gz: 289a30da1575e6f95f57c15ea74172d2ae7b10a97a396d18ea449008300f2345583a39ae853e0bd1e3d28281de72465e10ec9e5d2b656974c697c547a467ac40
7
- data.tar.gz: 67f4833b49c5c36381f672b8b48ec2368db8ef66bc04a10e5f70feb11fbf9c4c2beba98a0a311a46506fce8f942f037e90940b2002d381653842dbf24383e4c5
6
+ metadata.gz: 3c90265c6459015b3c684fe0f3446f52bda36d1fae2e54cdbba0beb7748d912add4da80946b5d431c2e0f174975d9ceb1591ec24bb3163b5087bb72fc46f6994
7
+ data.tar.gz: ba0e7095c8ebc4e2ea1d8034b9b3c9f49ed28a4b26608977c9cc9fc2a83c4594ec9e59476abb73b7c6856fec107ff85e8ae45b0a4b1818e0b6f8388f80ca204d
data/README.md CHANGED
@@ -56,6 +56,10 @@ bundle exec rails-wayback clean # drop the tmp/rails_wayback ref cache
56
56
  Same commands as rake tasks: `rake wayback:on`, `wayback:off`,
57
57
  `wayback:status`, `wayback:clean`.
58
58
 
59
+ Activation lasts for the current Rails server session. Every new server start
60
+ begins with `rails-wayback` disabled, while `on` and `off` can still be used at
61
+ any time without restarting Rails.
62
+
59
63
  With the gem enabled, load any page of your app. A dark bar appears at the
60
64
  bottom with:
61
65
 
@@ -0,0 +1,55 @@
1
+ #rails-wayback-bar {
2
+ position: fixed; left: 0; right: 0; bottom: 0; z-index: 2147483000;
3
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
4
+ font-size: 12px; color: #e6e9ef;
5
+ background: rgba(15, 17, 21, 0.96);
6
+ border-top: 1px solid #2a2f3a;
7
+ box-shadow: 0 -8px 20px rgba(0,0,0,.35);
8
+ }
9
+ #rails-wayback-bar * { box-sizing: border-box; }
10
+ #rails-wayback-bar .rw-bar-inner {
11
+ display: flex; align-items: end; gap: 12px; padding: 8px 12px;
12
+ }
13
+ #rails-wayback-bar .rw-field { display: flex; flex-direction: column; gap: 3px; min-width: 180px; }
14
+ #rails-wayback-bar .rw-flex { flex: 1; }
15
+ #rails-wayback-bar label {
16
+ color: #8a93a6; text-transform: uppercase; letter-spacing: .06em; font-size: 10px;
17
+ }
18
+ #rails-wayback-bar select {
19
+ background: #0b0d12; color: #e6e9ef;
20
+ border: 1px solid #2a2f3a; border-radius: 4px;
21
+ padding: 5px 8px; font-family: inherit; font-size: 12px; width: 100%;
22
+ }
23
+ #rails-wayback-bar .rw-state { color: #8a93a6; font-size: 11px; margin: 0 4px; }
24
+ #rails-wayback-bar .rw-state.rw-active { color: #ffd479; }
25
+ #rails-wayback-bar .rw-btn {
26
+ border: 0; border-radius: 4px; padding: 8px 14px; cursor: pointer;
27
+ font-family: inherit; font-size: 12px; font-weight: 600;
28
+ }
29
+ #rails-wayback-bar .rw-btn-primary { background: #6ea8fe; color: #0b0d12; }
30
+ #rails-wayback-bar .rw-btn-ghost { background: transparent; color: #8a93a6; border: 1px solid #2a2f3a; }
31
+ #rails-wayback-bar .rw-btn:disabled { opacity: .5; cursor: not-allowed; }
32
+ #rails-wayback-bar .rw-diff {
33
+ border-top: 1px solid #2a2f3a;
34
+ padding: 6px 12px; font-size: 11px; color: #8a93a6;
35
+ display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
36
+ }
37
+ #rails-wayback-bar .rw-diff-match .rw-diff-msg { color: #7ee787; }
38
+ #rails-wayback-bar .rw-diff-miss .rw-diff-msg { color: #ffd479; }
39
+ #rails-wayback-bar .rw-diff-neutral .rw-diff-msg { color: #8a93a6; }
40
+ #rails-wayback-bar .rw-diff-details { color: #8a93a6; }
41
+ #rails-wayback-bar .rw-diff-details summary {
42
+ cursor: pointer; user-select: none;
43
+ }
44
+ #rails-wayback-bar .rw-diff-details[open] {
45
+ flex-basis: 100%;
46
+ }
47
+ #rails-wayback-bar .rw-diff-details ul {
48
+ margin: 6px 0 0; padding: 6px 10px; list-style: none;
49
+ background: #0b0d12; border: 1px solid #2a2f3a; border-radius: 4px;
50
+ max-height: 200px; overflow: auto;
51
+ font-family: inherit; font-size: 11px;
52
+ }
53
+ #rails-wayback-bar .rw-diff-details li { padding: 2px 0; color: #c8cfdc; }
54
+ body { padding-bottom: 56px !important; }
55
+ body:has(#rails-wayback-bar .rw-diff) { padding-bottom: 88px !important; }
@@ -0,0 +1,22 @@
1
+ <style data-rails-wayback><%= styles %></style>
2
+ <div id="rails-wayback-bar" data-mount="<%= escape(@engine_mount) %>"
3
+ data-branch="<%= escape(@current_branch) %>"
4
+ data-commit="<%= escape(@current_commit) %>"
5
+ data-active-ref="<%= escape(@active_ref.to_s) %>"
6
+ data-active-branch="<%= escape(@active_branch.to_s) %>">
7
+ <div class="rw-bar-inner">
8
+ <div class="rw-field">
9
+ <label><%= escape(branch_label) %></label>
10
+ <select data-rw-branch></select>
11
+ </div>
12
+ <div class="rw-field rw-flex">
13
+ <label><%= escape(commit_label) %></label>
14
+ <select data-rw-commit></select>
15
+ </div>
16
+ <div class="rw-state" data-rw-state></div>
17
+ <button type="button" data-rw-travel class="rw-btn rw-btn-primary">Travel</button>
18
+ <button type="button" data-rw-reset class="rw-btn rw-btn-ghost" hidden>Return to HEAD</button>
19
+ </div>
20
+ <%= diff_summary_html %>
21
+ </div>
22
+ <script data-rails-wayback><%= script %></script>
@@ -0,0 +1,132 @@
1
+ (function () {
2
+ const bar = document.getElementById("rails-wayback-bar");
3
+ if (!bar) return;
4
+
5
+ const mount = bar.dataset.mount;
6
+ const headBranch = bar.dataset.branch;
7
+ const headCommit = bar.dataset.commit;
8
+ const activeRef = bar.dataset.activeRef || "";
9
+ const activeBranch = bar.dataset.activeBranch || "";
10
+
11
+ // While traveling, reflect the ref's branch/commit in the
12
+ // dropdowns instead of the developer's local HEAD, so the bar
13
+ // matches what is actually being rendered.
14
+ const selectedBranch = activeBranch || headBranch;
15
+ const selectedCommit = activeRef || headCommit;
16
+
17
+ const branchSel = bar.querySelector("[data-rw-branch]");
18
+ const commitSel = bar.querySelector("[data-rw-commit]");
19
+ const stateEl = bar.querySelector("[data-rw-state]");
20
+ const travelBtn = bar.querySelector("[data-rw-travel]");
21
+ const resetBtn = bar.querySelector("[data-rw-reset]");
22
+
23
+ function setState() {
24
+ if (activeRef) {
25
+ const label = activeBranch
26
+ ? activeBranch + "@" + activeRef.slice(0, 7)
27
+ : activeRef.slice(0, 7);
28
+ stateEl.textContent = "Traveling: " + label;
29
+ stateEl.classList.add("rw-active");
30
+ resetBtn.hidden = false;
31
+ } else {
32
+ stateEl.textContent = "Live";
33
+ stateEl.classList.remove("rw-active");
34
+ resetBtn.hidden = true;
35
+ }
36
+ }
37
+
38
+ function showError(msg) {
39
+ stateEl.textContent = msg;
40
+ stateEl.classList.add("rw-active");
41
+ }
42
+
43
+ function fetchBranches() {
44
+ fetch(mount + "/branches", { headers: { Accept: "application/json" } })
45
+ .then(function (r) {
46
+ if (!r.ok) throw new Error("HTTP " + r.status + " on /branches");
47
+ return r.json();
48
+ })
49
+ .then(function (data) {
50
+ branchSel.innerHTML = "";
51
+ (data.branches || []).forEach(function (b) {
52
+ const opt = document.createElement("option");
53
+ opt.value = b;
54
+ opt.textContent = b;
55
+ if (b === selectedBranch) opt.selected = true;
56
+ branchSel.appendChild(opt);
57
+ });
58
+ if ((data.errors || []).length > 0) showError(data.errors.join(" | "));
59
+ if (branchSel.value) fetchCommits(branchSel.value);
60
+ })
61
+ .catch(function (err) {
62
+ showError("branches: " + err.message);
63
+ });
64
+ }
65
+
66
+ function fetchCommits(branch) {
67
+ travelBtn.disabled = true;
68
+ fetch(mount + "/commits/" + encodeURI(branch), { headers: { Accept: "application/json" } })
69
+ .then(function (r) {
70
+ if (!r.ok) throw new Error("HTTP " + r.status + " on /commits/" + branch);
71
+ return r.json();
72
+ })
73
+ .then(function (data) {
74
+ commitSel.innerHTML = "";
75
+ (data.commits || []).forEach(function (c) {
76
+ const opt = document.createElement("option");
77
+ opt.value = c.sha;
78
+ opt.textContent = c.short_sha + " — " + (c.subject || "").slice(0, 80);
79
+ if (c.sha === selectedCommit) opt.selected = true;
80
+ commitSel.appendChild(opt);
81
+ });
82
+ if (data.error) {
83
+ showError("commits: " + data.error);
84
+ } else if (!activeRef) {
85
+ setState();
86
+ }
87
+ travelBtn.disabled = !commitSel.value;
88
+ })
89
+ .catch(function (err) {
90
+ showError("commits: " + err.message);
91
+ });
92
+ }
93
+
94
+ function setCookie(name, value) {
95
+ document.cookie = name + "=" + encodeURIComponent(value) + "; Path=/; SameSite=Lax";
96
+ }
97
+
98
+ function clearCookie(name) {
99
+ document.cookie = name + "=; Path=/; Max-Age=0; SameSite=Lax";
100
+ }
101
+
102
+ // Travel is a *session*, not a one-off request: the ref lives
103
+ // in a cookie so it survives every subsequent link click. The
104
+ // URL params are added just so a first-time landing (or a
105
+ // shared link) still works even before the cookie is set.
106
+ function travel() {
107
+ const sha = commitSel.value;
108
+ if (!sha) return;
109
+ setCookie("rails_wayback_ref", sha);
110
+ if (branchSel.value) setCookie("rails_wayback_branch", branchSel.value);
111
+ const url = new URL(window.location.href);
112
+ url.searchParams.set("_wayback_ref", sha);
113
+ if (branchSel.value) url.searchParams.set("_wayback_branch", branchSel.value);
114
+ window.location.assign(url.toString());
115
+ }
116
+
117
+ function reset() {
118
+ clearCookie("rails_wayback_ref");
119
+ clearCookie("rails_wayback_branch");
120
+ const url = new URL(window.location.href);
121
+ url.searchParams.delete("_wayback_ref");
122
+ url.searchParams.delete("_wayback_branch");
123
+ window.location.assign(url.toString());
124
+ }
125
+
126
+ branchSel.addEventListener("change", function (e) { fetchCommits(e.target.value); });
127
+ travelBtn.addEventListener("click", travel);
128
+ resetBtn.addEventListener("click", reset);
129
+
130
+ setState();
131
+ fetchBranches();
132
+ })();
@@ -2,12 +2,17 @@
2
2
 
3
3
  require "json"
4
4
  require "cgi"
5
+ require "erb"
5
6
 
6
7
  module RailsWayback
7
8
  # Builds the HTML/CSS/JS payload that the middleware injects into
8
9
  # every HTML response so the developer can travel from any page of
9
10
  # their app without visiting a dedicated route.
10
11
  class BarRenderer
12
+ TEMPLATE = ERB.new(File.read(File.expand_path("bar_renderer.html.erb", __dir__))).freeze
13
+ STYLES = File.read(File.expand_path("bar_renderer.css", __dir__)).freeze
14
+ SCRIPT = File.read(File.expand_path("bar_renderer.js", __dir__)).freeze
15
+
11
16
  def initialize(current_branch:, current_commit:, active_ref: nil, active_branch: nil,
12
17
  engine_mount: "/rails-wayback", diff_info: nil)
13
18
  @current_branch = current_branch
@@ -19,30 +24,7 @@ module RailsWayback
19
24
  end
20
25
 
21
26
  def render
22
- <<~HTML
23
- <style data-rails-wayback>#{styles}</style>
24
- <div id="rails-wayback-bar" data-mount="#{escape(@engine_mount)}"
25
- data-branch="#{escape(@current_branch)}"
26
- data-commit="#{escape(@current_commit)}"
27
- data-active-ref="#{escape(@active_ref.to_s)}"
28
- data-active-branch="#{escape(@active_branch.to_s)}">
29
- <div class="rw-bar-inner">
30
- <div class="rw-field">
31
- <label>#{escape(branch_label)}</label>
32
- <select data-rw-branch></select>
33
- </div>
34
- <div class="rw-field rw-flex">
35
- <label>#{escape(commit_label)}</label>
36
- <select data-rw-commit></select>
37
- </div>
38
- <div class="rw-state" data-rw-state></div>
39
- <button type="button" data-rw-travel class="rw-btn rw-btn-primary">Travel</button>
40
- <button type="button" data-rw-reset class="rw-btn rw-btn-ghost" hidden>Return to HEAD</button>
41
- </div>
42
- #{diff_summary_html}
43
- </div>
44
- <script data-rails-wayback>#{script}</script>
45
- HTML
27
+ TEMPLATE.result(binding)
46
28
  end
47
29
 
48
30
  private
@@ -104,198 +86,11 @@ module RailsWayback
104
86
  end
105
87
 
106
88
  def styles
107
- <<~CSS
108
- #rails-wayback-bar {
109
- position: fixed; left: 0; right: 0; bottom: 0; z-index: 2147483000;
110
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
111
- font-size: 12px; color: #e6e9ef;
112
- background: rgba(15, 17, 21, 0.96);
113
- border-top: 1px solid #2a2f3a;
114
- box-shadow: 0 -8px 20px rgba(0,0,0,.35);
115
- }
116
- #rails-wayback-bar * { box-sizing: border-box; }
117
- #rails-wayback-bar .rw-bar-inner {
118
- display: flex; align-items: end; gap: 12px; padding: 8px 12px;
119
- }
120
- #rails-wayback-bar .rw-field { display: flex; flex-direction: column; gap: 3px; min-width: 180px; }
121
- #rails-wayback-bar .rw-flex { flex: 1; }
122
- #rails-wayback-bar label {
123
- color: #8a93a6; text-transform: uppercase; letter-spacing: .06em; font-size: 10px;
124
- }
125
- #rails-wayback-bar select {
126
- background: #0b0d12; color: #e6e9ef;
127
- border: 1px solid #2a2f3a; border-radius: 4px;
128
- padding: 5px 8px; font-family: inherit; font-size: 12px; width: 100%;
129
- }
130
- #rails-wayback-bar .rw-state { color: #8a93a6; font-size: 11px; margin: 0 4px; }
131
- #rails-wayback-bar .rw-state.rw-active { color: #ffd479; }
132
- #rails-wayback-bar .rw-btn {
133
- border: 0; border-radius: 4px; padding: 8px 14px; cursor: pointer;
134
- font-family: inherit; font-size: 12px; font-weight: 600;
135
- }
136
- #rails-wayback-bar .rw-btn-primary { background: #6ea8fe; color: #0b0d12; }
137
- #rails-wayback-bar .rw-btn-ghost { background: transparent; color: #8a93a6; border: 1px solid #2a2f3a; }
138
- #rails-wayback-bar .rw-btn:disabled { opacity: .5; cursor: not-allowed; }
139
- #rails-wayback-bar .rw-diff {
140
- border-top: 1px solid #2a2f3a;
141
- padding: 6px 12px; font-size: 11px; color: #8a93a6;
142
- display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
143
- }
144
- #rails-wayback-bar .rw-diff-match .rw-diff-msg { color: #7ee787; }
145
- #rails-wayback-bar .rw-diff-miss .rw-diff-msg { color: #ffd479; }
146
- #rails-wayback-bar .rw-diff-neutral .rw-diff-msg { color: #8a93a6; }
147
- #rails-wayback-bar .rw-diff-details { color: #8a93a6; }
148
- #rails-wayback-bar .rw-diff-details summary {
149
- cursor: pointer; user-select: none;
150
- }
151
- #rails-wayback-bar .rw-diff-details[open] {
152
- flex-basis: 100%;
153
- }
154
- #rails-wayback-bar .rw-diff-details ul {
155
- margin: 6px 0 0; padding: 6px 10px; list-style: none;
156
- background: #0b0d12; border: 1px solid #2a2f3a; border-radius: 4px;
157
- max-height: 200px; overflow: auto;
158
- font-family: inherit; font-size: 11px;
159
- }
160
- #rails-wayback-bar .rw-diff-details li { padding: 2px 0; color: #c8cfdc; }
161
- body { padding-bottom: 56px !important; }
162
- body:has(#rails-wayback-bar .rw-diff) { padding-bottom: 88px !important; }
163
- CSS
89
+ STYLES
164
90
  end
165
91
 
166
92
  def script
167
- <<~JS
168
- (function () {
169
- var bar = document.getElementById("rails-wayback-bar");
170
- if (!bar) return;
171
-
172
- var mount = bar.dataset.mount;
173
- var headBranch = bar.dataset.branch;
174
- var headCommit = bar.dataset.commit;
175
- var activeRef = bar.dataset.activeRef || "";
176
- var activeBranch = bar.dataset.activeBranch || "";
177
- // While traveling, reflect the ref's branch/commit in the
178
- // dropdowns instead of the developer's local HEAD, so the bar
179
- // matches what is actually being rendered.
180
- var selectedBranch = activeBranch || headBranch;
181
- var selectedCommit = activeRef || headCommit;
182
-
183
- var branchSel = bar.querySelector("[data-rw-branch]");
184
- var commitSel = bar.querySelector("[data-rw-commit]");
185
- var stateEl = bar.querySelector("[data-rw-state]");
186
- var travelBtn = bar.querySelector("[data-rw-travel]");
187
- var resetBtn = bar.querySelector("[data-rw-reset]");
188
-
189
- function setState() {
190
- if (activeRef) {
191
- var label = activeBranch
192
- ? activeBranch + "@" + activeRef.slice(0, 7)
193
- : activeRef.slice(0, 7);
194
- stateEl.textContent = "Traveling: " + label;
195
- stateEl.classList.add("rw-active");
196
- resetBtn.hidden = false;
197
- } else {
198
- stateEl.textContent = "Live";
199
- stateEl.classList.remove("rw-active");
200
- resetBtn.hidden = true;
201
- }
202
- }
203
-
204
- function showError(msg) {
205
- stateEl.textContent = msg;
206
- stateEl.classList.add("rw-active");
207
- }
208
-
209
- function loadBranches() {
210
- fetch(mount + "/branches", { headers: { Accept: "application/json" } })
211
- .then(function (r) {
212
- if (!r.ok) throw new Error("HTTP " + r.status + " on /branches");
213
- return r.json();
214
- })
215
- .then(function (data) {
216
- branchSel.innerHTML = "";
217
- (data.branches || []).forEach(function (b) {
218
- var opt = document.createElement("option");
219
- opt.value = b; opt.textContent = b;
220
- if (b === selectedBranch) opt.selected = true;
221
- branchSel.appendChild(opt);
222
- });
223
- if ((data.errors || []).length > 0) showError(data.errors.join(" | "));
224
- if (branchSel.value) loadCommits(branchSel.value);
225
- })
226
- .catch(function (err) {
227
- showError("branches: " + err.message);
228
- });
229
- }
230
-
231
- function loadCommits(branch) {
232
- travelBtn.disabled = true;
233
- fetch(mount + "/commits/" + encodeURI(branch), { headers: { Accept: "application/json" } })
234
- .then(function (r) {
235
- if (!r.ok) throw new Error("HTTP " + r.status + " on /commits/" + branch);
236
- return r.json();
237
- })
238
- .then(function (data) {
239
- commitSel.innerHTML = "";
240
- (data.commits || []).forEach(function (c) {
241
- var opt = document.createElement("option");
242
- opt.value = c.sha;
243
- opt.textContent = c.short_sha + " — " + (c.subject || "").slice(0, 80);
244
- if (c.sha === selectedCommit) opt.selected = true;
245
- commitSel.appendChild(opt);
246
- });
247
- if (data.error) {
248
- showError("commits: " + data.error);
249
- } else if (!activeRef) {
250
- setState();
251
- }
252
- travelBtn.disabled = !commitSel.value;
253
- })
254
- .catch(function (err) {
255
- showError("commits: " + err.message);
256
- });
257
- }
258
-
259
- function setCookie(name, value) {
260
- document.cookie = name + "=" + encodeURIComponent(value) + "; Path=/; SameSite=Lax";
261
- }
262
-
263
- function clearCookie(name) {
264
- document.cookie = name + "=; Path=/; Max-Age=0; SameSite=Lax";
265
- }
266
-
267
- // Travel is a *session*, not a one-off request: the ref lives
268
- // in a cookie so it survives every subsequent link click. The
269
- // URL params are added just so a first-time landing (or a
270
- // shared link) still works even before the cookie is set.
271
- function travel() {
272
- var sha = commitSel.value;
273
- if (!sha) return;
274
- setCookie("rails_wayback_ref", sha);
275
- if (branchSel.value) setCookie("rails_wayback_branch", branchSel.value);
276
- var url = new URL(window.location.href);
277
- url.searchParams.set("_wayback_ref", sha);
278
- if (branchSel.value) url.searchParams.set("_wayback_branch", branchSel.value);
279
- window.location.assign(url.toString());
280
- }
281
-
282
- function reset() {
283
- clearCookie("rails_wayback_ref");
284
- clearCookie("rails_wayback_branch");
285
- var url = new URL(window.location.href);
286
- url.searchParams.delete("_wayback_ref");
287
- url.searchParams.delete("_wayback_branch");
288
- window.location.assign(url.toString());
289
- }
290
-
291
- branchSel.addEventListener("change", function (e) { loadCommits(e.target.value); });
292
- travelBtn.addEventListener("click", travel);
293
- resetBtn.addEventListener("click", reset);
294
-
295
- setState();
296
- loadBranches();
297
- })();
298
- JS
93
+ SCRIPT
299
94
  end
300
95
  end
301
96
  end
@@ -40,7 +40,7 @@ module RailsWayback
40
40
 
41
41
  def cmd_on
42
42
  RailsWayback.enable!
43
- @stdout.puts "rails-wayback: enabled (mounted at /rails-wayback on the next request)"
43
+ @stdout.puts "rails-wayback: enabled for this server session (mounted at /rails-wayback on the next request)"
44
44
  0
45
45
  end
46
46
 
@@ -9,6 +9,13 @@ module RailsWayback
9
9
  class Engine < ::Rails::Engine
10
10
  isolate_namespace RailsWayback
11
11
 
12
+ # Activation is scoped to a Rails server session. The marker still lets
13
+ # the standalone CLI toggle a running server, but a later server boot
14
+ # always starts with Wayback disabled.
15
+ server do
16
+ RailsWayback.toggle.reset!
17
+ end
18
+
12
19
  initializer "rails_wayback.controller_extensions" do
13
20
  ActiveSupport.on_load(:action_controller_base) do
14
21
  include RailsWayback::ControllerExtensions
@@ -9,6 +9,9 @@ module RailsWayback
9
9
  # We shell out to git instead of pulling in a dependency to keep the
10
10
  # gem light. All commands are scoped to the host app root and return
11
11
  # plain Ruby data structures.
12
+ #
13
+ # NOTE: Maybe we should need to use a git library instead of shelling out to git.
14
+ # Only if this functionality is not enough for the gem on the future.
12
15
  class Git
13
16
  Commit = Struct.new(:sha, :short_sha, :subject, :author, :date, keyword_init: true)
14
17
 
@@ -3,11 +3,12 @@
3
3
  require "fileutils"
4
4
 
5
5
  module RailsWayback
6
- # Persists whether the engine should be mounted in the host app.
6
+ # Shares the current server session's state between the CLI and Rails.
7
7
  #
8
8
  # The flag is a plain file inside `tmp/rails_wayback/enabled` (ignored by
9
9
  # git). We keep it as a file so the CLI, rake tasks and running Rails
10
- # process all agree on the same state without booting extra services.
10
+ # process all agree on the same state without booting extra services. The
11
+ # engine clears stale state whenever a new Rails server session starts.
11
12
  class Toggle
12
13
  def initialize(configuration = RailsWayback.configuration)
13
14
  @configuration = configuration
@@ -30,6 +31,13 @@ module RailsWayback
30
31
  true
31
32
  end
32
33
 
34
+ # Start a new server session disabled. Unlike `disable!`, this does not
35
+ # touch routes: Rails is already loading them as part of server boot.
36
+ def reset!
37
+ FileUtils.rm_f(configuration.toggle_file_path)
38
+ true
39
+ end
40
+
33
41
  def status
34
42
  enabled? ? :on : :off
35
43
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsWayback
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-wayback
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - MrCesar107
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-22 00:00:00.000000000 Z
11
+ date: 2026-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -78,6 +78,9 @@ files:
78
78
  - lib/rails-wayback.rb
79
79
  - lib/rails_wayback.rb
80
80
  - lib/rails_wayback/bar_middleware.rb
81
+ - lib/rails_wayback/bar_renderer.css
82
+ - lib/rails_wayback/bar_renderer.html.erb
83
+ - lib/rails_wayback/bar_renderer.js
81
84
  - lib/rails_wayback/bar_renderer.rb
82
85
  - lib/rails_wayback/cli.rb
83
86
  - lib/rails_wayback/configuration.rb