outboxer 0.1.11 → 1.0.0.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +81 -47
  3. data/db/migrate/create_outboxer_exceptions.rb +20 -0
  4. data/db/migrate/create_outboxer_frames.rb +16 -0
  5. data/db/migrate/create_outboxer_messages.rb +19 -0
  6. data/generators/message_publisher_generator.rb +11 -0
  7. data/generators/{outboxer/install_generator.rb → schema_generator.rb} +4 -9
  8. data/lib/outboxer/database.rb +44 -0
  9. data/lib/outboxer/logger.rb +17 -0
  10. data/lib/outboxer/message.rb +262 -13
  11. data/lib/outboxer/messages.rb +232 -0
  12. data/lib/outboxer/models/exception.rb +15 -0
  13. data/lib/outboxer/models/frame.rb +14 -0
  14. data/lib/outboxer/models/message.rb +46 -0
  15. data/lib/outboxer/models.rb +3 -0
  16. data/lib/outboxer/railtie.rb +2 -4
  17. data/lib/outboxer/version.rb +1 -1
  18. data/lib/outboxer/web/public/css/bootstrap-icons.css +2078 -0
  19. data/lib/outboxer/web/public/css/bootstrap-icons.min.css +5 -0
  20. data/lib/outboxer/web/public/css/bootstrap.css +12071 -0
  21. data/lib/outboxer/web/public/css/bootstrap.min.css +6 -0
  22. data/lib/outboxer/web/public/css/fonts/bootstrap-icons.woff +0 -0
  23. data/lib/outboxer/web/public/css/fonts/bootstrap-icons.woff2 +0 -0
  24. data/lib/outboxer/web/public/favicon.svg +3 -0
  25. data/lib/outboxer/web/public/js/bootstrap.bundle.js +6306 -0
  26. data/lib/outboxer/web/public/js/bootstrap.bundle.min.js +7 -0
  27. data/lib/outboxer/web/views/error.erb +63 -0
  28. data/lib/outboxer/web/views/home.erb +172 -0
  29. data/lib/outboxer/web/views/layout.erb +80 -0
  30. data/lib/outboxer/web/views/message.erb +81 -0
  31. data/lib/outboxer/web/views/messages/index.erb +60 -0
  32. data/lib/outboxer/web/views/messages/show.erb +31 -0
  33. data/lib/outboxer/web/views/messages.erb +262 -0
  34. data/lib/outboxer/web.rb +430 -0
  35. data/lib/outboxer.rb +9 -5
  36. metadata +279 -22
  37. data/.rspec +0 -3
  38. data/.rubocop.yml +0 -229
  39. data/CHANGELOG.md +0 -5
  40. data/CODE_OF_CONDUCT.md +0 -84
  41. data/LICENSE.txt +0 -21
  42. data/Rakefile +0 -12
  43. data/generators/outboxer/templates/bin/publisher.rb +0 -11
  44. data/generators/outboxer/templates/migrations/create_outboxer_exceptions.rb +0 -15
  45. data/generators/outboxer/templates/migrations/create_outboxer_messages.rb +0 -13
  46. data/lib/outboxer/exception.rb +0 -9
  47. data/lib/outboxer/outboxable.rb +0 -21
  48. data/lib/outboxer/publisher.rb +0 -149
  49. data/lib/tasks/gem.rake +0 -58
  50. data/outboxer.gemspec +0 -33
  51. data/sig/outboxer.rbs +0 -19
@@ -0,0 +1,60 @@
1
+ <!-- Table -->
2
+ <div class="container table-container">
3
+ <table class="table table-hover">
4
+ <thead class="table-light">
5
+ <tr>
6
+ <%
7
+ headers = {
8
+ 'Id' => 'id',
9
+ 'Messageable Type' => 'messageable_type',
10
+ 'Messageable Id' => 'messageable_id',
11
+ 'Status' => 'status',
12
+ 'Created At' => 'created_at',
13
+ 'Updated At' => 'updated_at'
14
+ }
15
+ headers.each do |display, col|
16
+ %>
17
+ <th>
18
+ <a href="?page=<%= page %>&limit=<%= limit %>&sort=<%= col %>&order=<%= sort.to_s == col && order == :asc ? 'desc' : 'asc' %>">
19
+ <%= display %>
20
+ <% if sort.to_s == col %>
21
+ <%= order == :asc ? '▲' : '▼' %>
22
+ <% end %>
23
+ </a>
24
+ </th>
25
+ <% end %>
26
+ </tr>
27
+ </thead>
28
+ <tbody>
29
+ <% messages.each do |message| %>
30
+ <tr class="clickable-row" data-href="/messages/<%= message.id %>">
31
+ <td><%= message.id %></td>
32
+ <td><%= message.messageable_type %></td>
33
+ <td><%= message.messageable_id %></td>
34
+ <td><%= message.status %></td>
35
+ <td><%= message.created_at %></td>
36
+ <td><%= message.updated_at %></td>
37
+ </tr>
38
+ <% end %>
39
+ </tbody>
40
+ </table>
41
+ </div>
42
+
43
+ <!-- Pagination Controls -->
44
+ <div class="container">
45
+ <nav aria-label="Page navigation">
46
+ <ul class="pagination justify-content-center">
47
+ <li class="page-item <%= 'disabled' unless messages.current_page > 1 %>">
48
+ <a class="page-link" href="?page=<%= messages.prev_page %>&limit=<%= limit %>&order=<%= order %>&sort=<%= sort %>" tabindex="-1" aria-disabled="true">&lt;&lt;</a>
49
+ </li>
50
+ <% messages.total_pages.times do |i| %>
51
+ <li class="page-item <%= 'active' if messages.current_page == i + 1 %>">
52
+ <a class="page-link" href="?page=<%= i + 1 %>&limit=<%= limit %>&order=<%= order %>&sort=<%= sort %>"><%= i + 1 %></a>
53
+ </li>
54
+ <% end %>
55
+ <li class="page-item <%= 'disabled' unless messages.current_page < messages.total_pages %>">
56
+ <a class="page-link" href="?page=<%= messages.next_page %>&limit=<%= limit %>&order=<%= order %>&sort=<%= sort %>" >&gt;&gt;</a>
57
+ </li>
58
+ </ul>
59
+ </nav>
60
+ </div>
@@ -0,0 +1,31 @@
1
+ <div <div class="container mt-4">
2
+ <h1 class="mb-3">Message Details</h1>
3
+
4
+ <div class="card mb-3">
5
+ <div class="card-body">
6
+ <h5 class="card-title">Message Info</h5>
7
+ <p class="card-text"><strong>ID:</strong> <%= message.id %></p>
8
+ <p class="card-text"><strong>Status:</strong> <%= message.status %></p>
9
+ <p class="card-text"><strong>Messageable Type:</strong> <%= message.messageable_type %></p>
10
+ <p class="card-text"><strong>Messageable ID:</strong> <%= message.messageable_id %></p>
11
+ <p class="card-text"><strong>Created At:</strong> <%= message.created_at %></p>
12
+ <p class="card-text"><strong>Updated At:</strong> <%= message.updated_at %></p>
13
+ </div>
14
+ </div>
15
+
16
+ <% if message.exceptions.any? %>
17
+ <div class="card">
18
+ <div class="card-body">
19
+ <h5 class="card-title">Exceptions</h5>
20
+ <ul class="list-group list-group-flush">
21
+ <% message.exceptions.each do |exception| %>
22
+ <li class="list-group-item">
23
+ <p><strong>Class Name:</strong> <%= exception.class_name %></p>
24
+ <p><strong>Message:</strong> <%= exception.message_text %></p>
25
+ </li>
26
+ <% end %>
27
+ </ul>
28
+ </div>
29
+ </div>
30
+ <% end %>
31
+ </div>
@@ -0,0 +1,262 @@
1
+ <div class="container mt-4">
2
+ <!-- Card to wrap the table and its controls -->
3
+ <div class="card">
4
+ <div class="card-header">
5
+ <!-- Controls Row -->
6
+ <div class="container-fluid">
7
+ <div class="row justify-content-between align-items-center">
8
+ <!-- Title aligned to left -->
9
+ <div class="col">
10
+ <h3 class="mb-0">Messages</h3>
11
+ </div>
12
+ <% if messages.present? %>
13
+ <!-- Buttons aligned to right on larger screens, hidden on smallest screens -->
14
+ <div class="col d-none d-sm-flex justify-content-end">
15
+ <% if Outboxer::Messages.can_republish_all?(status: denormalised_params[:status]) %>
16
+ <form id="republishAllForm" action="<%= outboxer_path("/messages/republish_all") %>" method="post" class="me-2">
17
+ <input type="hidden" name="action" value="republish_all">
18
+ <input type="hidden" name="status" value="<%= denormalised_params[:status] %>">
19
+ <button type="submit" class="btn btn-sm btn-outline-secondary">
20
+ <i class="bi bi-arrow-clockwise"></i> Republish All
21
+ </button>
22
+ </form>
23
+ <% end %>
24
+ <form id="deleteAllForm" action="<%= outboxer_path("/messages/delete_all") %>" method="post">
25
+ <input type="hidden" name="action" value="delete_all">
26
+ <% if denormalised_params[:status] %>
27
+ <input type="hidden" name="status" value="<%= denormalised_params[:status] %>">
28
+ <% end %>
29
+ <button type="submit" class="btn btn-sm btn-outline-danger">
30
+ <i class="bi bi-trash"></i> Delete All
31
+ </button>
32
+ </form>
33
+ </div>
34
+ <!-- Buttons aligned underneath the header on the smallest screens -->
35
+ <div class="col-12 d-sm-none mt-2">
36
+ <% if Outboxer::Messages.can_republish_all?(status: denormalised_params[:status]) %>
37
+ <form id="republishAllForm" action="<%= outboxer_path("/messages/republish_all") %>" method="post" class="mb-2">
38
+ <input type="hidden" name="action" value="republish_all">
39
+ <input type="hidden" name="status" value="publishing">
40
+ <button type="submit" class="btn btn-sm btn-outline-secondary w-100">
41
+ <i class="bi bi-arrow-clockwise"></i> Republish All
42
+ </button>
43
+ </form>
44
+ <% end %>
45
+ <form id="deleteAllForm" action="<%= outboxer_path("/messages/delete_all") %>" method="post">
46
+ <input type="hidden" name="action" value="delete_all">
47
+ <% if denormalised_params[:status] %>
48
+ <input type="hidden" name="status" value="<%= denormalised_params[:status] %>">
49
+ <% end %>
50
+ <button type="submit" class="btn btn-sm btn-outline-danger w-100">
51
+ <i class="bi bi-trash"></i> Delete All
52
+ </button>
53
+ </form>
54
+ </div>
55
+ <% end %>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ <!-- Bulk Actions Form -->
60
+ <form id="bulkActionForm" action="<%= outboxer_path("/messages/update") %>" method="post">
61
+ <div class="card-toolbar py-2 d-none" id="bulkActionsToolbar" style="border-top: 1px solid #eee; border-bottom: 1px solid #eee; background-color: #E9F5FB;">
62
+ <div class="container">
63
+ <div class="row justify-content-start">
64
+ <div class="col-auto">
65
+ <% [:status, :sort, :order, :page, :per_page].each do |param| %>
66
+ <% if denormalised_params[param] %>
67
+ <input type="hidden" name="<%= param %>"
68
+ value="<%= denormalised_params[param] %>">
69
+ <% end %>
70
+ <% end %>
71
+
72
+ <!-- Republish Selected Button -->
73
+ <button class="btn btn-sm btn-outline-secondary me-2" type="submit" name="action" value="republish_selected" data-bs-toggle="tooltip" data-bs-placement="top" title="Republish selected">
74
+ <i class="bi bi-arrow-clockwise"></i> Republish Selected
75
+ </button>
76
+ <!-- Delete Selected Button -->
77
+ <button class="btn btn-sm btn-outline-danger" type="submit" name="action" value="delete_selected" data-bs-toggle="tooltip" data-bs-placement="top" title="Delete selected">
78
+ <i class="bi bi-trash"></i> Delete Selected
79
+ </button>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ </form>
85
+ <div class="card-body">
86
+ <% if messages.empty? %>
87
+ <div class="text-center mt-4 mb-4">
88
+ <p class="fw-bold">There are no messages to display.</p>
89
+ </div>
90
+ <% else %>
91
+ <div class="table-responsive">
92
+ <!-- Table -->
93
+ <table class="table table-hover">
94
+ <thead>
95
+ <tr>
96
+ <th scope="col">
97
+ <input type="checkbox" id="checkAll">
98
+ </th>
99
+ <% headers.each do |header| %>
100
+ <th scope="col">
101
+ <a href="<%= header[:href] %>">
102
+ <%= header[:text] %> <i class="<%= header[:icon_class] %>"></i>
103
+ </a>
104
+ </th>
105
+ <% end %>
106
+ <th scope="col">Actions</th>
107
+ </tr>
108
+ </thead>
109
+ <tbody>
110
+ <% messages.each do |message| %>
111
+ <tr>
112
+ <td><input type="checkbox" class="individual-check" form="bulkActionForm" name="selected_ids[]" value="<%= message[:id] %>"></td>
113
+ <td scope="row">
114
+ <a href="<%= outboxer_path("/message/#{message[:id]}") %>" class="custom-link">
115
+ <%= message[:id] %>
116
+ </a>
117
+ </td>
118
+ <td><%= message[:status] %></td>
119
+ <td><%= message[:messageable_type] %>::<%= message[:messageable_id] %></td>
120
+ <td>
121
+ <%= message[:updated_at] %>
122
+ </td>
123
+ <td>
124
+ <%= message[:created_at] %>
125
+ </td>
126
+ <td>
127
+ <!-- Action buttons -->
128
+ <div class="d-flex">
129
+ <!-- Republish Button Form -->
130
+ <form action="<%= outboxer_path("/message/#{message[:id]}/republish") %>" method="post">
131
+
132
+ <button type="submit" class="btn btn-sm btn-outline-secondary me-1 d-flex align-items-center justify-content-center">
133
+ <i class="bi bi-arrow-clockwise me-1"></i>
134
+ <span>Republish</span>
135
+ </button>
136
+ </form>
137
+
138
+ <!-- Delete Button Form -->
139
+ <form action="<%= outboxer_path("/message/#{message[:id]}/delete") %>" method="post">
140
+ <button type="submit" class="btn btn-sm btn-outline-danger ms-1 d-flex align-items-center justify-content-center">
141
+ <i class="bi bi-trash me-1"></i>
142
+ <span>Delete</span>
143
+ </button>
144
+ </form>
145
+ </div>
146
+ </td>
147
+ </tr>
148
+ <% end %>
149
+ </tbody>
150
+ </table>
151
+ </div>
152
+ <% end %>
153
+ </div>
154
+ <div class="card-footer">
155
+ <div class="container-fluid">
156
+ <div class="row align-items-center justify-content-center">
157
+ <div class="col-auto mt-1 mb-1">
158
+ <!-- Pagination -->
159
+ <nav aria-label="Page navigation example" class="d-flex align-items-center">
160
+ <div class="btn-group btn-group-sm" role="group">
161
+ <% if pagination[:previous_page] %>
162
+ <a class="btn btn-secondary" href="<%= pagination[:previous_page][:href] %>"
163
+ aria-label="<%= pagination[:previous_page][:text] %>">
164
+ <%= pagination[:previous_page][:text] %>
165
+ </a>
166
+ <% end %>
167
+ <% pagination[:pages].each do |page| %>
168
+ <a class="btn btn-secondary <%= 'active' if page[:is_active] %>" href="<%= page[:href] %>">
169
+ <%= page[:text] %>
170
+ </a>
171
+ <% end %>
172
+ <% if pagination[:next_page] %>
173
+ <a class="btn btn-secondary" href="<%= pagination[:next_page][:href] %>"
174
+ aria-label="Next: <%= pagination[:next_page][:text] %>">
175
+ <%= pagination[:next_page][:text] %>
176
+ </a>
177
+ <% end %>
178
+ </div>
179
+ </nav>
180
+ </div>
181
+ <div class="col-auto mb-1 mt-1">
182
+ <!-- "Per page" selection -->
183
+ <form action="<%= outboxer_path("/messages/update_per_page") %>" method="post" id="perPageForm"
184
+ class="d-flex align-items-center">
185
+ <% [:status, :sort, :order, :page].each do |param| %>
186
+ <% if denormalised_params[param] %>
187
+ <input type="hidden" name="<%= param %>" value="<%= denormalised_params[param] %>">
188
+ <% end %>
189
+ <% end %>
190
+ <label for="perPageSelect" class="me-2">Per&nbsp;page:</label>
191
+ <select class="form-select form-select-sm" aria-label="Per page" id="perPageSelect" name="per_page">
192
+ <% [10, 100, 200, 500, 1000].each do |number| %>
193
+ <option value="<%= number %>"
194
+ <%= 'selected' if denormalised_params[:per_page]&.to_i == number %>>
195
+ <%= number %>
196
+ </option>
197
+ <% end %>
198
+ </select>
199
+ <button type="submit" class="btn btn-sm btn-secondary ms-2" id="submitBtn">Update</button>
200
+ </form>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ <script>
208
+ document.addEventListener('DOMContentLoaded', function () {
209
+ const checkboxes = document.querySelectorAll('.individual-check');
210
+ const checkAll = document.getElementById('checkAll');
211
+ const bulkActionsToolbar = document.getElementById('bulkActionsToolbar');
212
+
213
+ function toggleBulkActionsToolbar() {
214
+ const anyChecked = Array.from(checkboxes).some(checkbox => checkbox.checked);
215
+ bulkActionsToolbar.classList.toggle('d-none', !anyChecked);
216
+ }
217
+
218
+ checkboxes.forEach(checkbox => {
219
+ checkbox.addEventListener('change', toggleBulkActionsToolbar);
220
+ });
221
+
222
+
223
+ if (checkAll) {
224
+ checkAllElement.addEventListener('change', function (e) {
225
+ var checkboxes = document.querySelectorAll('.individual-check');
226
+ for (var i = 0; i < checkboxes.length; i++) {
227
+ checkboxes[i].checked = e.target.checked;
228
+ }
229
+ });
230
+
231
+ checkAll.addEventListener('change', function () {
232
+ toggleBulkActionsToolbar();
233
+ });
234
+ }
235
+
236
+ const deleteAllBtn = document.getElementById('deleteAllBtn');
237
+
238
+ if (deleteAllBtn) {
239
+ deleteAllBtn.addEventListener('click', function(e) {
240
+ const confirmation = confirm('Are you sure you want to delete all?');
241
+ if (!confirmation) {
242
+ e.preventDefault();
243
+ return false;
244
+ }
245
+ });
246
+ }
247
+
248
+ const retryAllBtn = document.getElementById('retryAllBtn');
249
+
250
+ if (retryAllBtn) {
251
+ retryAllBtn.addEventListener('click', function(e) {
252
+ const confirmation = confirm('Are you sure you want to retry all?');
253
+ if (!confirmation) {
254
+ e.preventDefault();
255
+ return false;
256
+ }
257
+ });
258
+ }
259
+ });
260
+
261
+ var checkAllElement = document.getElementById('checkAll');
262
+ </script>