outboxer 0.1.11 → 1.0.0.pre.beta
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/README.md +81 -47
- data/db/migrate/create_outboxer_exceptions.rb +20 -0
- data/db/migrate/create_outboxer_frames.rb +16 -0
- data/db/migrate/create_outboxer_messages.rb +19 -0
- data/generators/message_publisher_generator.rb +11 -0
- data/generators/{outboxer/install_generator.rb → schema_generator.rb} +4 -9
- data/lib/outboxer/database.rb +44 -0
- data/lib/outboxer/logger.rb +17 -0
- data/lib/outboxer/message.rb +262 -13
- data/lib/outboxer/messages.rb +232 -0
- data/lib/outboxer/models/exception.rb +15 -0
- data/lib/outboxer/models/frame.rb +14 -0
- data/lib/outboxer/models/message.rb +46 -0
- data/lib/outboxer/models.rb +3 -0
- data/lib/outboxer/railtie.rb +2 -4
- data/lib/outboxer/version.rb +1 -1
- data/lib/outboxer/web/public/css/bootstrap-icons.css +2078 -0
- data/lib/outboxer/web/public/css/bootstrap-icons.min.css +5 -0
- data/lib/outboxer/web/public/css/bootstrap.css +12071 -0
- data/lib/outboxer/web/public/css/bootstrap.min.css +6 -0
- data/lib/outboxer/web/public/css/fonts/bootstrap-icons.woff +0 -0
- data/lib/outboxer/web/public/css/fonts/bootstrap-icons.woff2 +0 -0
- data/lib/outboxer/web/public/favicon.svg +3 -0
- data/lib/outboxer/web/public/js/bootstrap.bundle.js +6306 -0
- data/lib/outboxer/web/public/js/bootstrap.bundle.min.js +7 -0
- data/lib/outboxer/web/views/error.erb +63 -0
- data/lib/outboxer/web/views/home.erb +172 -0
- data/lib/outboxer/web/views/layout.erb +80 -0
- data/lib/outboxer/web/views/message.erb +81 -0
- data/lib/outboxer/web/views/messages/index.erb +60 -0
- data/lib/outboxer/web/views/messages/show.erb +31 -0
- data/lib/outboxer/web/views/messages.erb +262 -0
- data/lib/outboxer/web.rb +430 -0
- data/lib/outboxer.rb +9 -5
- metadata +279 -22
- data/.rspec +0 -3
- data/.rubocop.yml +0 -229
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -84
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -12
- data/generators/outboxer/templates/bin/publisher.rb +0 -11
- data/generators/outboxer/templates/migrations/create_outboxer_exceptions.rb +0 -15
- data/generators/outboxer/templates/migrations/create_outboxer_messages.rb +0 -13
- data/lib/outboxer/exception.rb +0 -9
- data/lib/outboxer/outboxable.rb +0 -21
- data/lib/outboxer/publisher.rb +0 -149
- data/lib/tasks/gem.rake +0 -58
- data/outboxer.gemspec +0 -33
- 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"><<</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 %>" >>></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 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>
|