lepus 0.2.2 → 0.3.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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/Gemfile.lock +3 -3
  4. data/README.md +104 -9
  5. data/bin/web-demo +592 -0
  6. data/docs/brand/BRAND.md +67 -0
  7. data/docs/brand/apple-touch-icon.png +0 -0
  8. data/docs/brand/favicon-16.png +0 -0
  9. data/docs/brand/favicon-32.png +0 -0
  10. data/docs/brand/favicon.ico +0 -0
  11. data/docs/brand/favicon.svg +1 -0
  12. data/docs/brand/icon-512.png +0 -0
  13. data/docs/brand/lockup-lepus-mono.svg +1 -0
  14. data/docs/brand/lockup-lepus-on-dark.svg +1 -0
  15. data/docs/brand/lockup-lepus-on-light.svg +1 -0
  16. data/docs/brand/lockup-lepus-saga-mono.svg +1 -0
  17. data/docs/brand/lockup-lepus-saga-on-dark.svg +1 -0
  18. data/docs/brand/lockup-lepus-saga-on-light.svg +1 -0
  19. data/docs/brand/lockup-lepus-saga.svg +1 -0
  20. data/docs/brand/lockup-lepus.svg +1 -0
  21. data/docs/brand/mark-mono.svg +1 -0
  22. data/docs/brand/mark-on-dark.svg +1 -0
  23. data/docs/brand/mark-on-light.svg +1 -0
  24. data/docs/brand/mark-small-mono.svg +1 -0
  25. data/docs/brand/mark-small-on-dark.svg +1 -0
  26. data/docs/brand/mark-small-on-light.svg +1 -0
  27. data/docs/brand/mark-small.svg +1 -0
  28. data/docs/brand/mark.svg +1 -0
  29. data/docs/configuration.md +1 -0
  30. data/docs/consumers.md +39 -5
  31. data/docs/getting-started.md +1 -1
  32. data/docs/images/lepus-web.png +0 -0
  33. data/docs/middleware.md +1 -1
  34. data/docs/producers.md +41 -0
  35. data/docs/testing.md +67 -48
  36. data/docs/web.md +39 -13
  37. data/gemfiles/Gemfile.rails-5.2.lock +2 -2
  38. data/gemfiles/Gemfile.rails-6.1.lock +4 -4
  39. data/gemfiles/Gemfile.rails-7.2.lock +1 -1
  40. data/gemfiles/Gemfile.rails-8.0.lock +1 -1
  41. data/lib/lepus/configuration.rb +6 -0
  42. data/lib/lepus/consumer.rb +34 -0
  43. data/lib/lepus/consumers/config.rb +8 -7
  44. data/lib/lepus/consumers/stats.rb +15 -2
  45. data/lib/lepus/delay.rb +180 -0
  46. data/lib/lepus/primitive/string.rb +2 -12
  47. data/lib/lepus/producer.rb +6 -1
  48. data/lib/lepus/producers/definition.rb +3 -1
  49. data/lib/lepus/publisher.rb +29 -3
  50. data/lib/lepus/testing/rspec_matchers.rb +20 -1
  51. data/lib/lepus/testing.rb +2 -0
  52. data/lib/lepus/version.rb +1 -1
  53. data/lib/lepus/web/api.rb +45 -0
  54. data/lib/lepus/web/app.rb +19 -16
  55. data/lib/lepus/web/management_api.rb +125 -2
  56. data/lib/lepus/web/topology.rb +341 -0
  57. data/lib/lepus/web.rb +17 -18
  58. data/lib/lepus.rb +1 -0
  59. data/web/assets/app.css +862 -0
  60. data/web/assets/apple-touch-icon.png +0 -0
  61. data/web/assets/favicon-32.png +0 -0
  62. data/web/assets/favicon.svg +1 -0
  63. data/web/assets/fonts/overpass/OFL.txt +93 -0
  64. data/web/assets/fonts/overpass/overpass-latin-wght.woff2 +0 -0
  65. data/web/assets/js/api.js +27 -0
  66. data/web/assets/js/dom.js +95 -0
  67. data/web/assets/js/format.js +63 -0
  68. data/web/assets/js/live.js +74 -0
  69. data/web/assets/js/main.js +156 -0
  70. data/web/assets/js/map.js +606 -0
  71. data/web/assets/js/model.js +262 -0
  72. data/web/assets/js/rates.js +161 -0
  73. data/web/assets/js/theme.js +8 -0
  74. data/web/assets/js/views/connections.js +61 -0
  75. data/web/assets/js/views/overview.js +229 -0
  76. data/web/assets/js/views/processes.js +101 -0
  77. data/web/assets/js/views/queues.js +456 -0
  78. data/web/index.html +58 -153
  79. data/web/sw.js +14 -143
  80. metadata +47 -12
  81. data/test_offline.html +0 -189
  82. data/web/assets/css/styles.css +0 -635
  83. data/web/assets/js/app.js +0 -6
  84. data/web/assets/js/bootstrap.js +0 -20
  85. data/web/assets/js/controllers/connection_controller.js +0 -44
  86. data/web/assets/js/controllers/dashboard_controller.js +0 -499
  87. data/web/assets/js/controllers/queue_controller.js +0 -17
  88. data/web/assets/js/controllers/theme_controller.js +0 -31
  89. data/web/assets/js/offline-manager.js +0 -233
  90. data/web/assets/js/service-worker-manager.js +0 -65
@@ -1,233 +0,0 @@
1
- // Offline Manager - Handles offline detection and external dependency loading
2
- class OfflineManager {
3
- constructor() {
4
- this.isOnline = navigator.onLine;
5
- this.stimulusLoaded = false;
6
- this.chartLoaded = false;
7
- this.initialized = false;
8
-
9
- this.setupEventListeners();
10
- }
11
-
12
- setupEventListeners() {
13
- // Set up online/offline event listeners
14
- window.addEventListener('online', () => {
15
- this.isOnline = true;
16
- this.hideOfflineBanner();
17
- this.hideOfflineContent();
18
- });
19
-
20
- window.addEventListener('offline', () => {
21
- this.isOnline = false;
22
- this.showOfflineBanner();
23
- this.showOfflineContent();
24
- });
25
- }
26
-
27
- // Function to show offline banner
28
- showOfflineBanner() {
29
- const banner = document.getElementById('offline-banner');
30
- if (banner) {
31
- banner.style.display = 'block';
32
- }
33
- }
34
-
35
- // Function to hide offline banner
36
- hideOfflineBanner() {
37
- const banner = document.getElementById('offline-banner');
38
- if (banner) {
39
- banner.style.display = 'none';
40
- }
41
- }
42
-
43
- // Function to show offline content
44
- showOfflineContent() {
45
- const content = document.getElementById('offline-content');
46
- const mainContent = document.querySelector('.stats-grid');
47
- if (content && mainContent) {
48
- content.style.display = 'flex';
49
- mainContent.style.display = 'none';
50
- // Hide other main sections
51
- document.querySelectorAll('.chart-grid, .processes-grid, .queues-grid').forEach(section => {
52
- section.style.display = 'none';
53
- });
54
- }
55
- }
56
-
57
- // Function to hide offline content
58
- hideOfflineContent() {
59
- const content = document.getElementById('offline-content');
60
- const mainContent = document.querySelector('.stats-grid');
61
- if (content && mainContent) {
62
- content.style.display = 'none';
63
- mainContent.style.display = 'grid';
64
- // Show other main sections
65
- document.querySelectorAll('.chart-grid, .processes-grid, .queues-grid').forEach(section => {
66
- section.style.display = 'grid';
67
- });
68
- }
69
- }
70
-
71
- // Function to load external scripts with fallback
72
- loadExternalScript(src, fallbackCode, onLoad) {
73
- const script = document.createElement('script');
74
- script.src = src;
75
- script.onload = function() {
76
- onLoad(true);
77
- };
78
- script.onerror = function() {
79
- console.warn('Failed to load external script:', src);
80
- if (fallbackCode) {
81
- // Execute fallback code
82
- eval(fallbackCode);
83
- onLoad(false);
84
- } else {
85
- onLoad(false);
86
- }
87
- };
88
- document.head.appendChild(script);
89
- }
90
-
91
- // Load Stimulus with fallback
92
- loadStimulus() {
93
- return new Promise((resolve) => {
94
- this.loadExternalScript(
95
- 'https://unpkg.com/@hotwired/stimulus@3.2.2/dist/stimulus.umd.js',
96
- `
97
- // Minimal Stimulus fallback
98
- window.Stimulus = {
99
- Application: class {
100
- constructor() { this.controllers = new Map(); }
101
- register(name, controller) { this.controllers.set(name, controller); }
102
- start() {
103
- console.warn('Using offline Stimulus fallback');
104
- // Basic controller initialization
105
- document.querySelectorAll('[data-controller]').forEach(element => {
106
- const controllers = element.dataset.controller.split(' ');
107
- controllers.forEach(controllerName => {
108
- const ControllerClass = this.controllers.get(controllerName);
109
- if (ControllerClass) {
110
- new ControllerClass(element);
111
- }
112
- });
113
- });
114
- }
115
- },
116
- Controller: class {
117
- constructor(element) { this.element = element; }
118
- get targets() {
119
- return {
120
- find: (name) => this.element.querySelector('[data-' + this.constructor.name.toLowerCase().replace('controller', '') + '-target="' + name + '"]'),
121
- findAll: (name) => this.element.querySelectorAll('[data-' + this.constructor.name.toLowerCase().replace('controller', '') + '-target="' + name + '"]')
122
- };
123
- }
124
- }
125
- };
126
- `,
127
- (loaded) => {
128
- this.stimulusLoaded = loaded;
129
- if (!loaded) this.showOfflineBanner();
130
- resolve(loaded);
131
- }
132
- );
133
- });
134
- }
135
-
136
- // Load Chart.js with fallback
137
- loadChartJs() {
138
- return new Promise((resolve) => {
139
- this.loadExternalScript(
140
- 'https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js',
141
- `
142
- // Chart.js fallback - simple canvas drawing
143
- window.Chart = class {
144
- constructor(ctx, config) {
145
- this.ctx = ctx;
146
- this.config = config;
147
- this.drawFallbackChart();
148
- }
149
-
150
- drawFallbackChart() {
151
- const canvas = this.ctx.canvas;
152
- const width = canvas.width;
153
- const height = canvas.height;
154
-
155
- // Clear canvas
156
- this.ctx.clearRect(0, 0, width, height);
157
-
158
- // Draw fallback message
159
- this.ctx.fillStyle = '#666';
160
- this.ctx.font = '14px system-ui';
161
- this.ctx.textAlign = 'center';
162
- this.ctx.fillText('Charts unavailable offline', width/2, height/2);
163
- }
164
-
165
- update() { this.drawFallbackChart(); }
166
- destroy() {}
167
- };
168
- `,
169
- (loaded) => {
170
- this.chartLoaded = loaded;
171
- if (!loaded) this.showOfflineBanner();
172
- resolve(loaded);
173
- }
174
- );
175
- });
176
- }
177
-
178
- // Load local scripts serially. Controllers reference `StimulusApp`
179
- // defined by app.js, so they must not execute before app.js.
180
- loadLocalScripts() {
181
- const scripts = [
182
- 'assets/js/app.js',
183
- 'assets/js/controllers/theme_controller.js',
184
- 'assets/js/controllers/connection_controller.js',
185
- 'assets/js/controllers/dashboard_controller.js',
186
- 'assets/js/controllers/queue_controller.js'
187
- ];
188
-
189
- const loadOne = (src) => new Promise((resolve) => {
190
- const script = document.createElement('script');
191
- script.src = src;
192
- script.onload = () => resolve();
193
- script.onerror = () => {
194
- console.warn('Failed to load local script:', src);
195
- resolve();
196
- };
197
- document.head.appendChild(script);
198
- });
199
-
200
- return scripts.reduce((chain, src) => chain.then(() => loadOne(src)), Promise.resolve());
201
- }
202
-
203
- // Initialize the application
204
- async initialize() {
205
- if (this.initialized) return;
206
- this.initialized = true;
207
-
208
- // Set up dismiss button
209
- const dismissBtn = document.getElementById('dismiss-offline');
210
- if (dismissBtn) {
211
- dismissBtn.addEventListener('click', () => this.hideOfflineBanner());
212
- }
213
-
214
- // Check initial online status
215
- if (!this.isOnline) {
216
- this.showOfflineBanner();
217
- // Show offline content if we're offline and external dependencies failed to load
218
- setTimeout(() => {
219
- if (!this.stimulusLoaded || !this.chartLoaded) {
220
- this.showOfflineContent();
221
- }
222
- }, 2000); // Wait 2 seconds to see if dependencies load
223
- }
224
-
225
- // Load dependencies in sequence
226
- await this.loadStimulus();
227
- await this.loadChartJs();
228
- await this.loadLocalScripts();
229
- }
230
- }
231
-
232
- // Export for use in other modules
233
- window.OfflineManager = OfflineManager;
@@ -1,65 +0,0 @@
1
- // Service Worker Manager - Handles service worker registration and updates
2
- class ServiceWorkerManager {
3
- constructor() {
4
- this.registration = null;
5
- }
6
-
7
- // Register service worker. The SW is purely an offline-cache enhancement,
8
- // so we never await `.ready` — if the SW install fails (e.g. one cached
9
- // asset 401s behind auth) we would otherwise hang the whole dashboard
10
- // bootstrap waiting for a worker that will never activate.
11
- async register() {
12
- if (!('serviceWorker' in navigator)) {
13
- console.warn('Service Worker not supported');
14
- return false;
15
- }
16
-
17
- try {
18
- this.registration = await navigator.serviceWorker.register('sw.js');
19
- console.log('Service Worker registered:', this.registration.scope);
20
-
21
- this.registration.addEventListener('updatefound', () => {
22
- const newWorker = this.registration.installing;
23
- newWorker.addEventListener('statechange', () => {
24
- if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
25
- console.log('New service worker available. Reload to update.');
26
- this.showUpdateNotification();
27
- }
28
- });
29
- });
30
-
31
- return true;
32
- } catch (error) {
33
- console.warn('Service Worker registration failed:', error);
34
- return false;
35
- }
36
- }
37
-
38
- // Show update notification (optional enhancement)
39
- showUpdateNotification() {
40
- // You could implement a notification here to prompt users to reload
41
- // For now, we'll just log it
42
- console.log('Update available - consider implementing user notification');
43
- }
44
-
45
- // Unregister service worker (for development/testing)
46
- async unregister() {
47
- if (this.registration) {
48
- await this.registration.unregister();
49
- console.log('Service Worker unregistered');
50
- }
51
- }
52
-
53
- // Check if service worker is active
54
- isActive() {
55
- return navigator.serviceWorker.controller !== null;
56
- }
57
-
58
- // Get service worker registration
59
- getRegistration() {
60
- return this.registration;
61
- }
62
- }
63
-
64
- // Export for use in other modules
65
- window.ServiceWorkerManager = ServiceWorkerManager;