solid_errors 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 502b309733d074ca7a0d47c9207964de7316ec9b0fdb39530d987e4d8698aea7
4
- data.tar.gz: 1a81591573e559628e99360fffe6d2f727f519a68c6254339b089cf9f6b9da12
3
+ metadata.gz: ef3e4cca2bd412b918d7b7c36b3e754d79fefbc06e754a54fd58d5b04ef02a79
4
+ data.tar.gz: 1ca69a12aa28386778c66d7cd9114c06d20fbce1f846bf78391cfcad6796ff63
5
5
  SHA512:
6
- metadata.gz: 12642107e9c31caa1819026ae43d992ceae2449d07655ef3fff6569a258b18272c2c94098a1b0dc6028e8d71bb652a86a6a2030ce1b38afea90cc55e7d87735b
7
- data.tar.gz: ddd1e53133b7c2a7e04fd8a1fa53072d00838bfe901e58236b23870cc520551b431933eed72a1d07b4dcf6745addb481df1637486540d8b5201e784d2b41412a
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 true
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 = Error.unresolved
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
@@ -6,7 +6,7 @@ module SolidErrors
6
6
  @error = occurrence.error
7
7
 
8
8
  mail(
9
- subject: "#{@error.emoji} #{@error.exception_class}",
9
+ subject: "#{@error.severity_emoji} #{@error.exception_class}",
10
10
  from: SolidErrors.email_from,
11
11
  to: SolidErrors.email_to
12
12
  )
@@ -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 emoji
33
+ def severity_emoji
26
34
  SEVERITY_TO_EMOJI[severity.to_sym]
27
35
  end
28
36
 
29
- def badge_classes
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,6 +1,6 @@
1
1
  <html>
2
2
  <head>
3
- <title>Solid Errors | <%= @error.emoji %> <%= @error.exception_class %></title>
3
+ <title>Solid Errors | <%= @error.severity_emoji %> <%= @error.exception_class %></title>
4
4
  <%= render "layouts/solid_errors/style" %>
5
5
  </head>
6
6
  <body>
@@ -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
- <%= 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 %>
10
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check2-circle" viewBox="0 0 16 16">
11
- <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"/>
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.emoji %>
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.emoji %>
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
- <%= 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 %>
24
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check2-circle" viewBox="0 0 16 16">
25
- <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"/>
26
- <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"/>
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
- <div class="line"><span class="mr-2 text-right select-none text-gray-600"><%= n %></span><span><%= code %></span></div>
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
@@ -1,5 +1,5 @@
1
1
  SolidErrors::Engine.routes.draw do
2
2
  get "/" => "errors#index", :as => :root
3
3
 
4
- resources :errors, only: [:index, :show, :update], path: ""
4
+ resources :errors, only: [:index, :show, :update, :destroy], path: ""
5
5
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidErrors
4
- VERSION = "0.4.2"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/solid_errors.rb CHANGED
@@ -27,7 +27,7 @@ module SolidErrors
27
27
  end
28
28
 
29
29
  def send_emails?
30
- @send_emails ||= ENV["SOLIDERRORS_SEND_EMAILS"] || @@send_emails || true
30
+ @send_emails ||= ENV["SOLIDERRORS_SEND_EMAILS"] || @@send_emails || false
31
31
  end
32
32
 
33
33
  def email_from
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.2
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-04-10 00:00:00.000000000 Z
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.1
172
+ rubygems_version: 3.5.17
171
173
  signing_key:
172
174
  specification_version: 4
173
175
  summary: Database-backed Rails error subscriber