solid_errors 0.4.2 → 0.5.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/README.md +50 -2
- data/app/controllers/solid_errors/errors_controller.rb +20 -4
- data/app/mailers/solid_errors/error_mailer.rb +1 -1
- data/app/models/solid_errors/error.rb +26 -2
- data/app/views/layouts/solid_errors/_style.html +84 -5
- data/app/views/solid_errors/error_mailer/error_occurred.html.erb +1 -1
- data/app/views/solid_errors/errors/_actions.html.erb +5 -8
- data/app/views/solid_errors/errors/_delete_button.html.erb +7 -0
- data/app/views/solid_errors/errors/_error.html.erb +24 -21
- data/app/views/solid_errors/errors/_resolve_button.html.erb +8 -0
- data/app/views/solid_errors/errors/_row.html.erb +6 -7
- data/app/views/solid_errors/errors/index.html.erb +22 -0
- data/app/views/solid_errors/occurrences/_collection.html.erb +2 -1
- data/app/views/solid_errors/occurrences/_occurrence.html.erb +3 -3
- data/config/routes.rb +1 -1
- data/lib/solid_errors/version.rb +1 -1
- data/lib/solid_errors.rb +1 -1
- metadata +17 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef3e4cca2bd412b918d7b7c36b3e754d79fefbc06e754a54fd58d5b04ef02a79
|
4
|
+
data.tar.gz: 1ca69a12aa28386778c66d7cd9114c06d20fbce1f846bf78391cfcad6796ff63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d5313d9fd88ce1d75bbd1faf09bd47bca8caaafd61ea7930f982e02b117957b11b077406c5c8109531d7d718a37ee344b136f9fbf483fe371271951ee137b55
|
7
|
+
data.tar.gz: cd5a876a317307109b4660e39b11be2e80b6fa2b7a5a33db00c3edac84bcf27d0a3dc777a2a3f49837411e4a3168483f2c57656bc18e6d24903d5106400a697c
|
data/README.md
CHANGED
@@ -27,7 +27,7 @@
|
|
27
27
|
|
28
28
|
Solid Errors is a DB-based, app-internal exception tracker for Rails applications, designed with simplicity and performance in mind. It uses the new [Rails error reporting API](https://guides.rubyonrails.org/error_reporting.html) to store uncaught exceptions in the database, and provides a simple UI for viewing and managing exceptions.
|
29
29
|
|
30
|
-
> [!WARNING]
|
30
|
+
> [!WARNING]
|
31
31
|
> The current point release of Rails (7.1.3.2) has a bug which severely limits the utility of Solid Errors. Exceptions raised during a web request *are not* reported to Rails' error reporter. There is a fix in the `main` branch, but it has not been released in a new point release. As such, Solid Errors is **not** production-ready unless you are running Rails from the `main` branch or until a new point version is released and you upgrade.
|
32
32
|
> The original bug report can be found [here](https://github.com/rails/rails/issues/51002) and the pull request making the fix is [here](https://github.com/rails/rails/pull/51050). I will try to backport the fix into the gem directly, but I haven't quite figured it out yet.
|
33
33
|
|
@@ -69,6 +69,54 @@ Please consult the [official guides](https://guides.rubyonrails.org/error_report
|
|
69
69
|
|
70
70
|
There are intentionally few features; you can view and resolve errors. That’s it. The goal is to provide a simple, lightweight, and performant solution for tracking exceptions in your Rails application. If you need more features, you should probably use a 3rd party service like [Honeybadger](https://www.honeybadger.io/), whose MIT-licensed [Ruby agent gem](https://github.com/honeybadger-io/honeybadger-ruby) provided a couple of critical pieces of code for this project.
|
71
71
|
|
72
|
+
### Manually reporting an Error
|
73
|
+
|
74
|
+
Errors can be added to Solid Errors via the [Rails error reporter](https://guides.rubyonrails.org/error_reporting.html).
|
75
|
+
|
76
|
+
There are [three ways](https://guides.rubyonrails.org/error_reporting.html#using-the-error-reporter) you can use the error reporter:
|
77
|
+
|
78
|
+
`Rails.error.handle` will report any error raised within the block. It will then swallow the error, and the rest of your code outside the block will continue as normal.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
result = Rails.error.handle do
|
82
|
+
1 + '1' # raises TypeError
|
83
|
+
end
|
84
|
+
result # => nil
|
85
|
+
1 + 1 # This will be executed
|
86
|
+
```
|
87
|
+
|
88
|
+
`Rails.error.record` will report errors to all registered subscribers and then re-raise the error, meaning that the rest of your code won't execute.
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
Rails.error.record do
|
92
|
+
1 + '1' # raises TypeError
|
93
|
+
end
|
94
|
+
1 + 1 # This won't be executed
|
95
|
+
```
|
96
|
+
|
97
|
+
You can also manually report errors by calling `Rails.error.report`:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
begin
|
101
|
+
# code
|
102
|
+
rescue StandardError => e
|
103
|
+
Rails.error.report(e)
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
All 3 reporting APIs (`#handle`, `#record`, and `#report`) support the following options, which are then passed along to all registered subscribers:
|
108
|
+
|
109
|
+
* `handled`: a Boolean to indicate if the error was handled. This is set to `true` by default. `#record` sets this to `false`.
|
110
|
+
* `severity`: a Symbol describing the severity of the error. Expected values are: `:error`, `:warning`, and `:info`. `#handle` sets this to `:warning`, while `#record` sets it to `:error`.
|
111
|
+
* `context`: a Hash to provide more context about the error, like request or user details
|
112
|
+
* `source`: a String about the source of the error. The default source is `"application"`. Errors reported by internal libraries may set other sources; the Redis cache library may use "redis_cache_store.active_support", for instance. Your subscriber can use the source to ignore errors you aren't interested in.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
Rails.error.handle(context: { user_id: user.id }, severity: :info) do
|
116
|
+
# ...
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
72
120
|
### Configuration
|
73
121
|
|
74
122
|
You can configure Solid Errors via the Rails configuration object, under the `solid_errors` key. Currently, 6 configuration options are available:
|
@@ -132,7 +180,7 @@ Solid Errors _can_ send email notifications whenever an error occurs, if your ap
|
|
132
180
|
There are two ways to configure email notifications. First, you can use environment variables:
|
133
181
|
|
134
182
|
```ruby
|
135
|
-
ENV["SOLIDERRORS_SEND_EMAILS"] = true # defaults to
|
183
|
+
ENV["SOLIDERRORS_SEND_EMAILS"] = true # defaults to false
|
136
184
|
ENV["SOLIDERRORS_EMAIL_FROM"] = "errors@myapp.com" # defaults to "solid_errors@noreply.com"
|
137
185
|
ENV["SOLIDERRORS_EMAIL_TO"] = "devs@myapp.com" # no default, must be set
|
138
186
|
```
|
@@ -2,14 +2,16 @@ module SolidErrors
|
|
2
2
|
class ErrorsController < ApplicationController
|
3
3
|
around_action :force_english_locale!
|
4
4
|
|
5
|
-
before_action :set_error, only: %i[show update]
|
5
|
+
before_action :set_error, only: %i[show update destroy]
|
6
|
+
|
7
|
+
helper_method :error_scope
|
6
8
|
|
7
9
|
# GET /errors
|
8
10
|
def index
|
9
11
|
errors_table = Error.arel_table
|
10
12
|
occurrences_table = Occurrence.arel_table
|
11
|
-
|
12
|
-
@errors =
|
13
|
+
query_scope = error_scope.resolved? ? Error.resolved : Error.unresolved
|
14
|
+
@errors = query_scope
|
13
15
|
.joins(:occurrences)
|
14
16
|
.select(errors_table[Arel.star],
|
15
17
|
occurrences_table[:created_at].maximum.as("recent_occurrence"),
|
@@ -21,7 +23,7 @@ module SolidErrors
|
|
21
23
|
# GET /errors/1
|
22
24
|
def show
|
23
25
|
@page = Page.new(@error.occurrences, params)
|
24
|
-
@occurrences = @error.occurrences.offset(@page.offset).limit(@page.items)
|
26
|
+
@occurrences = @error.occurrences.offset(@page.offset).limit(@page.items).order(created_at: :desc)
|
25
27
|
end
|
26
28
|
|
27
29
|
# PATCH/PUT /errors/1
|
@@ -30,6 +32,16 @@ module SolidErrors
|
|
30
32
|
redirect_to errors_path, notice: "Error marked as resolved."
|
31
33
|
end
|
32
34
|
|
35
|
+
# DELETE /errors/1
|
36
|
+
def destroy
|
37
|
+
if @error.resolved?
|
38
|
+
@error.destroy
|
39
|
+
redirect_to errors_path(scope: :resolved), notice: "Error deleted."
|
40
|
+
else
|
41
|
+
redirect_to error_path(@error), alert: "You must resolve the error before deleting it."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
33
45
|
private
|
34
46
|
|
35
47
|
# Only allow a list of trusted parameters through.
|
@@ -44,5 +56,9 @@ module SolidErrors
|
|
44
56
|
def force_english_locale!(&action)
|
45
57
|
I18n.with_locale(:en, &action)
|
46
58
|
end
|
59
|
+
|
60
|
+
def error_scope
|
61
|
+
ActiveSupport::StringInquirer.new(params[:scope] || "unresolved")
|
62
|
+
end
|
47
63
|
end
|
48
64
|
end
|
@@ -12,6 +12,14 @@ module SolidErrors
|
|
12
12
|
warning: "bg-yellow-100 text-yellow-800",
|
13
13
|
info: "bg-blue-100 text-blue-800"
|
14
14
|
}
|
15
|
+
STATUS_TO_EMOJI = {
|
16
|
+
resolved: "✅",
|
17
|
+
unresolved: "⏳"
|
18
|
+
}
|
19
|
+
STATUS_TO_BADGE_CLASSES = {
|
20
|
+
resolved: "bg-green-100 text-green-800",
|
21
|
+
unresolved: "bg-violet-100 text-violet-800"
|
22
|
+
}
|
15
23
|
|
16
24
|
has_many :occurrences, class_name: "SolidErrors::Occurrence", dependent: :destroy
|
17
25
|
|
@@ -22,12 +30,28 @@ module SolidErrors
|
|
22
30
|
scope :resolved, -> { where.not(resolved_at: nil) }
|
23
31
|
scope :unresolved, -> { where(resolved_at: nil) }
|
24
32
|
|
25
|
-
def
|
33
|
+
def severity_emoji
|
26
34
|
SEVERITY_TO_EMOJI[severity.to_sym]
|
27
35
|
end
|
28
36
|
|
29
|
-
def
|
37
|
+
def severity_badge_classes
|
30
38
|
"px-2 inline-flex text-sm font-semibold rounded-md #{SEVERITY_TO_BADGE_CLASSES[severity.to_sym]}"
|
31
39
|
end
|
40
|
+
|
41
|
+
def status
|
42
|
+
resolved? ? :resolved : :unresolved
|
43
|
+
end
|
44
|
+
|
45
|
+
def status_emoji
|
46
|
+
STATUS_TO_EMOJI[status]
|
47
|
+
end
|
48
|
+
|
49
|
+
def status_badge_classes
|
50
|
+
"px-2 inline-flex text-sm font-semibold rounded-md #{STATUS_TO_BADGE_CLASSES[status]}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def resolved?
|
54
|
+
resolved_at.present?
|
55
|
+
end
|
32
56
|
end
|
33
57
|
end
|
@@ -617,6 +617,10 @@
|
|
617
617
|
margin-bottom: 0.75rem
|
618
618
|
}
|
619
619
|
|
620
|
+
.mb-6{
|
621
|
+
margin-bottom: 1.5rem
|
622
|
+
}
|
623
|
+
|
620
624
|
.ml-6{
|
621
625
|
margin-left: 1.5rem
|
622
626
|
}
|
@@ -629,6 +633,10 @@
|
|
629
633
|
margin-top: 1rem
|
630
634
|
}
|
631
635
|
|
636
|
+
.-mb-px {
|
637
|
+
margin-bottom: -1px;
|
638
|
+
}
|
639
|
+
|
632
640
|
.inline-block{
|
633
641
|
display: inline-block
|
634
642
|
}
|
@@ -743,6 +751,12 @@
|
|
743
751
|
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse))
|
744
752
|
}
|
745
753
|
|
754
|
+
.space-x-8 > :not([hidden]) ~ :not([hidden]) {
|
755
|
+
--tw-space-x-reverse: 0;
|
756
|
+
margin-right: calc(2rem * var(--tw-space-x-reverse));
|
757
|
+
margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse)));
|
758
|
+
}
|
759
|
+
|
746
760
|
.divide-y > :not([hidden]) ~ :not([hidden]){
|
747
761
|
--tw-divide-y-reverse: 0;
|
748
762
|
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
@@ -770,6 +784,10 @@
|
|
770
784
|
border-radius: 0.25rem
|
771
785
|
}
|
772
786
|
|
787
|
+
.rounded-md{
|
788
|
+
border-radius: 0.375rem
|
789
|
+
}
|
790
|
+
|
773
791
|
.rounded-lg{
|
774
792
|
border-radius: 0.5rem
|
775
793
|
}
|
@@ -787,6 +805,10 @@
|
|
787
805
|
border-bottom-width: 1px
|
788
806
|
}
|
789
807
|
|
808
|
+
.border-b-2 {
|
809
|
+
border-bottom-width: 2px;
|
810
|
+
}
|
811
|
+
|
790
812
|
.border-black{
|
791
813
|
--tw-border-opacity: 1;
|
792
814
|
border-color: rgb(0 0 0 / var(--tw-border-opacity))
|
@@ -802,11 +824,24 @@
|
|
802
824
|
border-color: rgb(209 213 219 / var(--tw-border-opacity))
|
803
825
|
}
|
804
826
|
|
827
|
+
.border-red-500{
|
828
|
+
--tw-border-opacity: 1;
|
829
|
+
border-color: rgb(239 68 68 / var(--tw-border-opacity))
|
830
|
+
}
|
831
|
+
|
832
|
+
.border-transparent {
|
833
|
+
border-color: transparent;
|
834
|
+
}
|
835
|
+
|
805
836
|
.bg-gray-100{
|
806
837
|
--tw-bg-opacity: 1;
|
807
838
|
background-color: rgb(243 244 246 / var(--tw-bg-opacity))
|
808
839
|
}
|
809
840
|
|
841
|
+
.bg-gray-600\/50{
|
842
|
+
background-color: rgb(75 85 99 / 0.5);
|
843
|
+
}
|
844
|
+
|
810
845
|
.bg-green-50{
|
811
846
|
--tw-bg-opacity: 1;
|
812
847
|
background-color: rgb(240 253 244 / var(--tw-bg-opacity))
|
@@ -844,6 +879,11 @@
|
|
844
879
|
padding-right: 0px
|
845
880
|
}
|
846
881
|
|
882
|
+
.px-1 {
|
883
|
+
padding-left: 0.25rem;
|
884
|
+
padding-right: 0.25rem;
|
885
|
+
}
|
886
|
+
|
847
887
|
.px-2{
|
848
888
|
padding-left: 0.5rem;
|
849
889
|
padding-right: 0.5rem
|
@@ -912,10 +952,6 @@
|
|
912
952
|
padding-top: 1.75rem
|
913
953
|
}
|
914
954
|
|
915
|
-
.rounded-md{
|
916
|
-
border-radius: 0.375rem
|
917
|
-
}
|
918
|
-
|
919
955
|
.text-left{
|
920
956
|
text-align: left
|
921
957
|
}
|
@@ -927,7 +963,7 @@
|
|
927
963
|
.text-right{
|
928
964
|
text-align: right
|
929
965
|
}
|
930
|
-
|
966
|
+
|
931
967
|
.text-black{
|
932
968
|
--tw-text-opacity: 1;
|
933
969
|
color: rgb(0 0 0 / var(--tw-text-opacity))
|
@@ -983,6 +1019,16 @@
|
|
983
1019
|
background-color: rgb(254 249 195 / var(--tw-bg-opacity))
|
984
1020
|
}
|
985
1021
|
|
1022
|
+
.bg-green-100{
|
1023
|
+
--tw-bg-opacity: 1;
|
1024
|
+
background-color: rgb(220 252 231 / var(--tw-bg-opacity))
|
1025
|
+
}
|
1026
|
+
|
1027
|
+
.bg-violet-100{
|
1028
|
+
--tw-bg-opacity: 1;
|
1029
|
+
background-color: rgb(237 233 254 / var(--tw-bg-opacity))
|
1030
|
+
}
|
1031
|
+
|
986
1032
|
.text-blue-400{
|
987
1033
|
--tw-text-opacity: 1;
|
988
1034
|
color: rgb(96 165 250 / var(--tw-text-opacity))
|
@@ -1033,6 +1079,16 @@
|
|
1033
1079
|
color: rgb(133 77 14 / var(--tw-text-opacity))
|
1034
1080
|
}
|
1035
1081
|
|
1082
|
+
.text-green-800{
|
1083
|
+
--tw-text-opacity: 1;
|
1084
|
+
color: rgb(22 101 52 / var(--tw-text-opacity))
|
1085
|
+
}
|
1086
|
+
|
1087
|
+
.text-violet-800{
|
1088
|
+
--tw-text-opacity: 1;
|
1089
|
+
color: rgb(91 33 182 / var(--tw-text-opacity))
|
1090
|
+
}
|
1091
|
+
|
1036
1092
|
.text-white{
|
1037
1093
|
--tw-text-opacity: 1;
|
1038
1094
|
color: rgb(255 255 255 / var(--tw-text-opacity))
|
@@ -1042,6 +1098,14 @@
|
|
1042
1098
|
text-decoration-line: underline
|
1043
1099
|
}
|
1044
1100
|
|
1101
|
+
.uppercase{
|
1102
|
+
text-transform: uppercase
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
.whitespace-pre-wrap{
|
1106
|
+
white-space: pre-wrap
|
1107
|
+
}
|
1108
|
+
|
1045
1109
|
.transition-opacity{
|
1046
1110
|
transition-property: opacity;
|
1047
1111
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
@@ -1078,6 +1142,11 @@
|
|
1078
1142
|
--tw-ring-color: rgb(229 231 235 / var(--tw-ring-opacity))
|
1079
1143
|
}
|
1080
1144
|
|
1145
|
+
.hover\:ring-red-200:hover{
|
1146
|
+
--tw-ring-opacity: 1;
|
1147
|
+
--tw-ring-color: rgb(254 202 202 / var(--tw-ring-opacity))
|
1148
|
+
}
|
1149
|
+
|
1081
1150
|
.hover\:bg-black:hover{
|
1082
1151
|
--tw-bg-opacity: 1;
|
1083
1152
|
background-color: rgb(0 0 0 / var(--tw-bg-opacity))
|
@@ -1088,6 +1157,16 @@
|
|
1088
1157
|
color: rgb(255 255 255 / var(--tw-text-opacity))
|
1089
1158
|
}
|
1090
1159
|
|
1160
|
+
.hover\:text-gray-700:hover {
|
1161
|
+
--tw-text-opacity: 1;
|
1162
|
+
color: rgb(55 65 81 / var(--tw-text-opacity));
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
.hover\:border-gray-300:hover {
|
1166
|
+
--tw-border-opacity: 1;
|
1167
|
+
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
1168
|
+
}
|
1169
|
+
|
1091
1170
|
@media (min-width: 640px){
|
1092
1171
|
.sm\:mx-0{
|
1093
1172
|
margin-left: 0px;
|
@@ -1,16 +1,13 @@
|
|
1
1
|
<div class="inline-flex items-center justify-start flex-wrap gap-3 whitespace-nowrap">
|
2
|
-
<%= link_to errors_path, class: "inline-flex items-center justify-center gap-2 font-medium cursor-pointer border rounded-lg py-3 px-5 bg-gray-100 text-initial border-gray-300 hover:ring-gray-200 hover:ring-8" do %>
|
2
|
+
<%= link_to errors_path(scope: error.resolved? ? :resolved : :unresolved), class: "inline-flex items-center justify-center gap-2 font-medium cursor-pointer border rounded-lg py-3 px-5 bg-gray-100 text-initial border-gray-300 hover:ring-gray-200 hover:ring-8" do %>
|
3
3
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
4
4
|
<path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8"/>
|
5
5
|
</svg>
|
6
6
|
<span>Back to errors</span>
|
7
7
|
<% end %>
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
<path d="M15.354 3.354a.5.5 0 0 0-.708-.708L8 9.293 5.354 6.646a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0z"/>
|
13
|
-
</svg>
|
14
|
-
<span>Resolve Error<span class="sr-only"> #<%= error.id %></span></span>
|
8
|
+
<% if error.resolved? %>
|
9
|
+
<%= render 'solid_errors/errors/delete_button', error: error %>
|
10
|
+
<% else %>
|
11
|
+
<%= render 'solid_errors/errors/resolve_button', error: error %>
|
15
12
|
<% end %>
|
16
13
|
</div>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<%# locals: (error:) -%>
|
2
|
+
<%= button_to error_path(error), method: :delete, class: "inline-flex items-center justify-center gap-2 font-medium cursor-pointer border rounded-lg py-3 px-5 bg-transparent text-red-500 border-red-500 hover:ring-red-200 hover:ring-8" do %>
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-trash3" fill="currentColor" viewBox="0 0 16 16">
|
4
|
+
<path d="M6.5 1h3a.5.5 0 0 1 .5.5v1H6v-1a.5.5 0 0 1 .5-.5M11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3A1.5 1.5 0 0 0 5 1.5v1H1.5a.5.5 0 0 0 0 1h.538l.853 10.66A2 2 0 0 0 4.885 16h6.23a2 2 0 0 0 1.994-1.84l.853-10.66h.538a.5.5 0 0 0 0-1zm1.958 1-.846 10.58a1 1 0 0 1-.997.92h-6.23a1 1 0 0 1-.997-.92L3.042 3.5zm-7.487 1a.5.5 0 0 1 .528.47l.5 8.5a.5.5 0 0 1-.998.06L5 5.03a.5.5 0 0 1 .47-.53Zm5.058 0a.5.5 0 0 1 .47.53l-.5 8.5a.5.5 0 1 1-.998-.06l.5-8.5a.5.5 0 0 1 .528-.47M8 4.5a.5.5 0 0 1 .5.5v8.5a.5.5 0 0 1-1 0V5a.5.5 0 0 1 .5-.5"/>
|
5
|
+
</svg>
|
6
|
+
<span>Delete <span class="sr-only">, Error #<%= error.id %></span></span>
|
7
|
+
<% end %>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<%= tag.section id: dom_id(error), class: "space-y-6" do %>
|
4
4
|
<div class="flex justify-between items-center">
|
5
5
|
<%= tag.h1 class: "font-bold flex items-center text-2xl gap-2" do %>
|
6
|
-
<%= error.
|
6
|
+
<%= error.severity_emoji %>
|
7
7
|
<code><%= error.exception_class %></code>
|
8
8
|
from
|
9
9
|
<em><code><%= error.source %></code></em>
|
@@ -14,9 +14,31 @@
|
|
14
14
|
</small>
|
15
15
|
</div>
|
16
16
|
|
17
|
-
<pre class=""><%= error.message %></pre>
|
17
|
+
<pre class="whitespace-pre-wrap"><%= error.message %></pre>
|
18
18
|
|
19
19
|
<dl class="flex-1 grid grid-cols-2 gap-x-4">
|
20
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
21
|
+
<dt class="font-bold">
|
22
|
+
<%= SolidErrors::Error.human_attribute_name(:severity) %>
|
23
|
+
</dt>
|
24
|
+
<dd class="inline-flex items-center gap-1">
|
25
|
+
<%= error.severity_emoji %>
|
26
|
+
<span class="<%= error.severity_badge_classes %>">
|
27
|
+
<%= error.severity %>
|
28
|
+
</span>
|
29
|
+
</dd>
|
30
|
+
</div>
|
31
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
32
|
+
<dt class="font-bold">
|
33
|
+
<%= SolidErrors::Error.human_attribute_name(:status) %>
|
34
|
+
</dt>
|
35
|
+
<dd class="inline-flex items-center gap-1">
|
36
|
+
<%= error.status_emoji %>
|
37
|
+
<span class="<%= error.status_badge_classes %>">
|
38
|
+
<%= error.status %>
|
39
|
+
</span>
|
40
|
+
</dd>
|
41
|
+
</div>
|
20
42
|
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
21
43
|
<dt class="font-bold">
|
22
44
|
<%= SolidErrors::Error.human_attribute_name(:first_seen) %>
|
@@ -41,14 +63,6 @@
|
|
41
63
|
</abbr>
|
42
64
|
</dd>
|
43
65
|
</div>
|
44
|
-
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
45
|
-
<dt class="font-bold">
|
46
|
-
<%= SolidErrors::Error.human_attribute_name(:occurrences) %>
|
47
|
-
</dt>
|
48
|
-
<dd class="inline-flex items-center gap-1">
|
49
|
-
<%= error.occurrences.size %>
|
50
|
-
</dd>
|
51
|
-
</div>
|
52
66
|
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
53
67
|
<dt class="font-bold">
|
54
68
|
<%= SolidErrors::Error.human_attribute_name(:exception_class) %>
|
@@ -57,17 +71,6 @@
|
|
57
71
|
<code><%= error.exception_class %></code>
|
58
72
|
</dd>
|
59
73
|
</div>
|
60
|
-
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
61
|
-
<dt class="font-bold">
|
62
|
-
<%= SolidErrors::Error.human_attribute_name(:severity) %>
|
63
|
-
</dt>
|
64
|
-
<dd class="inline-flex items-center gap-1">
|
65
|
-
<%= error.emoji %>
|
66
|
-
<span class="<%= error.badge_classes %>">
|
67
|
-
<%= error.severity %>
|
68
|
-
</span>
|
69
|
-
</dd>
|
70
|
-
</div>
|
71
74
|
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
72
75
|
<dt class="font-bold">
|
73
76
|
<%= SolidErrors::Error.human_attribute_name(:source) %>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%# locals: (error:) -%>
|
2
|
+
<%= button_to error_path(error), method: :patch, class: "inline-flex items-center justify-center gap-2 font-medium cursor-pointer border rounded-lg py-3 px-5 bg-transparent text-blue-500 border-blue-500 hover:ring-blue-200 hover:ring-8", params: { error: { resolved_at: Time.now } } do %>
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check2-circle" viewBox="0 0 16 16">
|
4
|
+
<path d="M2.5 8a5.5 5.5 0 0 1 8.25-4.764.5.5 0 0 0 .5-.866A6.5 6.5 0 1 0 14.5 8a.5.5 0 0 0-1 0 5.5 5.5 0 1 1-11 0"/>
|
5
|
+
<path d="M15.354 3.354a.5.5 0 0 0-.708-.708L8 9.293 5.354 6.646a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0z"/>
|
6
|
+
</svg>
|
7
|
+
<span>Resolve <span class="sr-only">, Error #<%= error.id %></span></span>
|
8
|
+
<% end %>
|
@@ -1,7 +1,8 @@
|
|
1
|
+
<%# locals: (error:) -%>
|
1
2
|
<tr class="even:bg-gray-50 align-top">
|
2
3
|
<td scope="col" class="whitespace-wrap py-4 pl-4 pr-3 font-medium text-gray-900 sm:pl-3">
|
3
4
|
<div>
|
4
|
-
<%= error.
|
5
|
+
<%= error.severity_emoji %>
|
5
6
|
<%= link_to error_path(error), class: "text-blue-400 underline inline-flex items-baseline gap-1" do %>
|
6
7
|
<strong><code><%= error.exception_class %></code></strong>
|
7
8
|
<% end %>
|
@@ -20,12 +21,10 @@
|
|
20
21
|
</abbr>
|
21
22
|
</td>
|
22
23
|
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-3">
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
</svg>
|
28
|
-
<span>Resolve<span class="sr-only">, Error #<%= error.id %></span></span>
|
24
|
+
<% if error.resolved? %>
|
25
|
+
<%= render 'solid_errors/errors/delete_button', error: error %>
|
26
|
+
<% else %>
|
27
|
+
<%= render 'solid_errors/errors/resolve_button', error: error %>
|
29
28
|
<% end %>
|
30
29
|
</td>
|
31
30
|
</tr>
|
@@ -1,3 +1,25 @@
|
|
1
|
+
<div class="mb-6 border-b border-gray-300 flex justify-between items-baseline">
|
2
|
+
<h1 class="font-bold flex items-center text-2xl gap-2">
|
3
|
+
Errors
|
4
|
+
</h1>
|
5
|
+
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
|
6
|
+
<%= link_to errors_path(scope: :unresolved), class: class_names(
|
7
|
+
"inline-flex items-center border-b-2 px-1 py-4 text-sm font-medium",
|
8
|
+
"border-blue-500 text-blue-500" => !error_scope.resolved?,
|
9
|
+
"border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700" => error_scope.resolved?) do %>
|
10
|
+
<span class="mr-2"><%= SolidErrors::Error::STATUS_TO_EMOJI[:unresolved] %></span>
|
11
|
+
<span>Unresolved</span>
|
12
|
+
<% end %>
|
13
|
+
<%= link_to errors_path(scope: :resolved), class: class_names(
|
14
|
+
"inline-flex items-center border-b-2 px-1 py-4 text-sm font-medium",
|
15
|
+
"border-blue-500 text-blue-500" => error_scope.resolved?,
|
16
|
+
"border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700" => !error_scope.resolved?) do %>
|
17
|
+
<span class="mr-2"><%= SolidErrors::Error::STATUS_TO_EMOJI[:resolved] %></span>
|
18
|
+
<span>Resolved</span>
|
19
|
+
<% end %>
|
20
|
+
</nav>
|
21
|
+
</div>
|
22
|
+
|
1
23
|
<table class="min-w-full divide-y divide-gray-300">
|
2
24
|
<thead>
|
3
25
|
<tr>
|
@@ -49,7 +49,8 @@
|
|
49
49
|
|
50
50
|
<div class="min-w-full">
|
51
51
|
<% if occurrences.any? %>
|
52
|
-
<%= render occurrences
|
52
|
+
<%= render partial: "solid_errors/occurrences/occurrence",
|
53
|
+
collection: occurrences, as: :occurrence %>
|
53
54
|
<% else %>
|
54
55
|
<div class="flex items-center justify-center gap-2">
|
55
56
|
<%= bootstrap_svg "list-ul" %>
|
@@ -33,11 +33,11 @@
|
|
33
33
|
<span class="text-gray-500">in</span>
|
34
34
|
<code class="text-green-500 font-medium"><%= line.filtered_method %></code>
|
35
35
|
<% else %>
|
36
|
-
<span class="text-gray-500"><%= line.unparsed_line
|
36
|
+
<span class="text-gray-500"><%= line.unparsed_line %></span>
|
37
37
|
<% end %>
|
38
38
|
</summary>
|
39
|
-
<div><pre class="flex overflow-auto rounded-b-lg bg-slate-800 p-4 text-sm leading-normal text-white sm:rounded-t-lg"><code class="flex flex-col min-h-full min-w-content px-0"><% line.source.each do |n, code| %>
|
40
|
-
|
39
|
+
<div><pre class="flex overflow-auto rounded-b-lg bg-slate-800 p-4 text-sm leading-normal text-white sm:rounded-t-lg"><code class="flex flex-1 flex-col min-h-full min-w-content px-0"><% line.source.each do |n, code| %>
|
40
|
+
<div class="line <%= n == line.filtered_number.to_i ? 'bg-gray-600/50' : '' %>"><span class="mr-2 text-right select-none text-gray-600"><%= n %></span><span><%= code %></span></div>
|
41
41
|
<% end %></code></pre></div>
|
42
42
|
<% end %>
|
43
43
|
<% end %>
|
data/config/routes.rb
CHANGED
data/lib/solid_errors/version.rb
CHANGED
data/lib/solid_errors.rb
CHANGED
metadata
CHANGED
@@ -1,97 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_errors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Margheim
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionmailer
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '7.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '7.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: actionpack
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '7.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '7.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: actionview
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '7.0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '7.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: activerecord
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '7.0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '7.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: activesupport
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '7.0'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '7.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: railties
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '7.0'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '7.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
@@ -130,7 +130,9 @@ files:
|
|
130
130
|
- app/views/solid_errors/error_mailer/error_occurred.html.erb
|
131
131
|
- app/views/solid_errors/error_mailer/error_occurred.text.erb
|
132
132
|
- app/views/solid_errors/errors/_actions.html.erb
|
133
|
+
- app/views/solid_errors/errors/_delete_button.html.erb
|
133
134
|
- app/views/solid_errors/errors/_error.html.erb
|
135
|
+
- app/views/solid_errors/errors/_resolve_button.html.erb
|
134
136
|
- app/views/solid_errors/errors/_row.html.erb
|
135
137
|
- app/views/solid_errors/errors/index.html.erb
|
136
138
|
- app/views/solid_errors/errors/show.html.erb
|
@@ -167,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
169
|
- !ruby/object:Gem::Version
|
168
170
|
version: '0'
|
169
171
|
requirements: []
|
170
|
-
rubygems_version: 3.5.
|
172
|
+
rubygems_version: 3.5.17
|
171
173
|
signing_key:
|
172
174
|
specification_version: 4
|
173
175
|
summary: Database-backed Rails error subscriber
|