rails_mail 0.10.1 → 0.10.2
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 +83 -0
- data/app/helpers/rails_mail/emails_helper.rb +10 -0
- data/app/models/rails_mail/email.rb +11 -11
- data/app/views/rails_mail/emails/_email_tabs.html.erb +25 -25
- data/app/views/rails_mail/emails/_exception.html.erb +54 -0
- data/app/views/rails_mail/emails/_html_content.html.erb +5 -0
- data/app/views/rails_mail/emails/_text_content.html.erb +3 -0
- data/lib/rails_mail/configuration.rb +11 -0
- data/lib/rails_mail/exception_parser.rb +90 -0
- data/lib/rails_mail/renderer/base.rb +25 -0
- data/lib/rails_mail/renderer/exception_notifier_renderer.rb +25 -0
- data/lib/rails_mail/renderer/html_renderer.rb +22 -0
- data/lib/rails_mail/renderer/text_renderer.rb +22 -0
- data/lib/rails_mail/renderer.rb +10 -0
- data/lib/rails_mail/renderer_registry.rb +19 -0
- data/lib/rails_mail/version.rb +1 -1
- data/lib/rails_mail.rb +2 -0
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 536a20c5a826b0b73dfa72b4fc50e2b3ec3821bf7af3407973c2c6aa02151072
|
4
|
+
data.tar.gz: ef136ee4aae7150889c7383850fd18ca37fa1f72decc3683d933e38b1d555cb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d48f19287f7bc951ce9b293bfbbedec5850eb22cfd39f7339328122bea226d80cfeddfd1559e121e20419379553982c0bea22dd7a6de198340ca98a366c8e63
|
7
|
+
data.tar.gz: 9972c2e2f77d9202ea1b5b737a4168cf819371f44590eca9ea3dbd5e455dbcf00b2d0e164a601de860749f30b7d971842a89f6dfd2a84eed688359a157950593
|
data/README.md
CHANGED
@@ -19,6 +19,7 @@ RailsMail saves all outgoing emails to your database instead of actually sending
|
|
19
19
|
* Dynamic time ago in words using date-fns
|
20
20
|
* Ability to customize how the job that trims emails is enqueued
|
21
21
|
* Ability to customize the title in the top left of the page via a standard Rails view that overrides the engine's default view.
|
22
|
+
* Toggle between HTML & text-only views for emails.
|
22
23
|
|
23
24
|
## Installation
|
24
25
|
|
@@ -120,6 +121,86 @@ end
|
|
120
121
|
- `trim_emails_max_count`: Keeps only the N most recent emails, deleting older ones.
|
121
122
|
- `sync_via`: Controls whether the trimming job runs synchronously (:now) or asynchronously (:later)
|
122
123
|
|
124
|
+
### Custom Renderers
|
125
|
+
|
126
|
+
RailsMail supports custom renderers that allow you to add new ways to display emails. For example, you could add a renderer for markdown emails or a special format your application uses.
|
127
|
+
|
128
|
+
By default, RailsMail includes these renderers:
|
129
|
+
- HTML renderer: Displays HTML email content
|
130
|
+
- Text renderer: Displays plain text email content
|
131
|
+
- Exception Notifier renderer: Provides formatted display of exception emails from the ExceptionNotifier gem
|
132
|
+
|
133
|
+
#### Creating a Custom Renderer
|
134
|
+
|
135
|
+
1. Create a new renderer class that inherits from `RailsMail::Renderer::Base`:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
# app/renderers/markdown_renderer.rb
|
139
|
+
module RailsMail
|
140
|
+
module Renderer
|
141
|
+
class MarkdownRenderer < Base
|
142
|
+
def self.handles?(email)
|
143
|
+
email.content_type&.include?("text/markdown")
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.partial_name
|
147
|
+
"rails_mail/emails/markdown_content"
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.title
|
151
|
+
"Markdown"
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.priority
|
155
|
+
5 # Between Text and Exception renderers
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.data(email)
|
159
|
+
{ markdown_content: process_markdown(email.text_body) }
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def self.process_markdown(text)
|
165
|
+
# Your markdown processing logic here
|
166
|
+
text
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
2. Create a partial for your renderer:
|
174
|
+
|
175
|
+
```erb
|
176
|
+
# app/views/rails_mail/emails/_markdown_content.html.erb
|
177
|
+
<div class="mt-3 markdown-content">
|
178
|
+
<%= markdown_content %>
|
179
|
+
</div>
|
180
|
+
```
|
181
|
+
|
182
|
+
3. Register your renderer in an initializer:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# config/initializers/rails_mail.rb
|
186
|
+
RailsMail.configure do |config|
|
187
|
+
# ... other configuration ...
|
188
|
+
end
|
189
|
+
|
190
|
+
# Register custom renderers after configuration
|
191
|
+
RailsMail::RendererRegistry.register(RailsMail::Renderer::MarkdownRenderer)
|
192
|
+
```
|
193
|
+
|
194
|
+
#### Renderer API
|
195
|
+
|
196
|
+
Custom renderers must implement these class methods:
|
197
|
+
|
198
|
+
- `handles?(email)`: Returns true if this renderer should handle the email
|
199
|
+
- `partial_name`: Returns the path to the partial that renders the content
|
200
|
+
- `title`: (optional) The tab title. Defaults to the class name without "Renderer"
|
201
|
+
- `priority`: (optional) Order in which renderers appear. Lower numbers appear first
|
202
|
+
- `data(email)`: (optional) Additional data to pass to the partial. Returns a hash
|
203
|
+
|
123
204
|
### Customize the title
|
124
205
|
Since this is a Rails engine, you can customize the title by creating a file at `app/views/layouts/rails_mail/_title.html.erb`.
|
125
206
|
|
@@ -165,6 +246,7 @@ RailsMail uses Turbo, TurboStreams, and ActionCable to provide real-time updates
|
|
165
246
|
|
166
247
|
- In development environment, the typical default for ActionCable (cable.yml) is to use the async adapter which is an in-memory adapter. If you try to send an email from the rails console, it will not auto-update the ui. You can change the adapter to the development adapter by running `cable.yml` to use something like the redis, postgresql adapter, or solidcable.
|
167
248
|
- In staging environments, the same idea typically applies that you need to use a multi-process adapter like redis, postgresql, or solidcable.
|
249
|
+
- Inline `<style>` tags will be sanitized in email bodies.
|
168
250
|
|
169
251
|
## Future work / ideas
|
170
252
|
|
@@ -176,6 +258,7 @@ RailsMail uses Turbo, TurboStreams, and ActionCable to provide real-time updates
|
|
176
258
|
- Implement read/unread functionality
|
177
259
|
- Implement individual email delete
|
178
260
|
- Implement multi-part (text/html) email support
|
261
|
+
- Allow clients to add additional acceptable HTML tags to render
|
179
262
|
|
180
263
|
## Contributing
|
181
264
|
Contribution directions go here.
|
@@ -1,4 +1,14 @@
|
|
1
1
|
module RailsMail
|
2
2
|
module EmailsHelper
|
3
|
+
def prepare_email_html(raw_html)
|
4
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(raw_html)
|
5
|
+
doc.css("a[href]").each do |a|
|
6
|
+
a.set_attribute("target", "_blank")
|
7
|
+
a.set_attribute("data-turbo", "false")
|
8
|
+
end
|
9
|
+
sanitize(doc.to_html,
|
10
|
+
tags: ActionView::Base.sanitized_allowed_tags + [ "table", "tbody", "tr", "td" ],
|
11
|
+
attributes: ActionView::Base.sanitized_allowed_attributes + [ "style", "target", "data-turbo" ])
|
12
|
+
end
|
3
13
|
end
|
4
14
|
end
|
@@ -13,12 +13,8 @@ module RailsMail
|
|
13
13
|
where("CAST(data AS CHAR) LIKE :q", q: "%#{query}%")
|
14
14
|
}
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
def html?
|
21
|
-
content_type&.include?("text/html") || content_type&.include?("multipart/alternative")
|
16
|
+
def exception_parser
|
17
|
+
@exception_parser ||= ExceptionParser.new(text_body)
|
22
18
|
end
|
23
19
|
|
24
20
|
def next_email
|
@@ -26,15 +22,19 @@ module RailsMail
|
|
26
22
|
end
|
27
23
|
|
28
24
|
def html_body
|
29
|
-
|
30
|
-
|
31
|
-
html_part["raw_source"]
|
25
|
+
html_part&.dig("raw_source")
|
32
26
|
end
|
33
27
|
|
34
28
|
def text_body
|
35
|
-
|
29
|
+
text_part&.dig("raw_source")
|
30
|
+
end
|
31
|
+
|
32
|
+
def renderers
|
33
|
+
RailsMail::RendererRegistry.matching_renderers(self)
|
34
|
+
end
|
36
35
|
|
37
|
-
|
36
|
+
def render_partials
|
37
|
+
renderers.map(&:partial_name)
|
38
38
|
end
|
39
39
|
|
40
40
|
private
|
@@ -1,28 +1,28 @@
|
|
1
1
|
<div class="prose max-w-none break-words">
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
2
|
+
<% @email.renderers.each_with_index do |renderer, index| %>
|
3
|
+
<input
|
4
|
+
type="radio"
|
5
|
+
name="email_tab"
|
6
|
+
id="<%= renderer.partial_name.parameterize %>_tab"
|
7
|
+
class="hidden peer/<%= renderer.partial_name.parameterize %>"
|
8
|
+
<%= index == 0 ? 'checked' : '' %>
|
9
|
+
/>
|
10
|
+
<label
|
11
|
+
for="<%= renderer.partial_name.parameterize %>_tab"
|
12
|
+
class="px-4 py-2 text-sm font-medium cursor-pointer
|
13
|
+
peer-checked/<%= renderer.partial_name.parameterize %>:text-blue-600
|
14
|
+
peer-checked/<%= renderer.partial_name.parameterize %>:border-blue-600
|
15
|
+
peer-checked/<%= renderer.partial_name.parameterize %>:border-b-2
|
16
|
+
peer-disabled/<%= renderer.partial_name.parameterize %>:text-gray-400
|
17
|
+
peer-disabled/<%= renderer.partial_name.parameterize %>:cursor-not-allowed">
|
18
|
+
<%= renderer.title %>
|
19
|
+
</label>
|
20
|
+
<% end %>
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
<%=
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
</div>
|
23
|
-
<div class="hidden peer-checked/text:block mt-3 bg-black text-white font-mono p-4 rounded">
|
24
|
-
<% if email.text? %>
|
25
|
-
<%= simple_format email.text_body %>
|
26
|
-
<% end %>
|
27
|
-
</div>
|
22
|
+
<% @email.renderers.each do |renderer| %>
|
23
|
+
<div class="hidden peer-checked/<%= renderer.partial_name.parameterize %>:block">
|
24
|
+
<%= render partial: renderer.partial_name,
|
25
|
+
locals: { email: @email }.merge(renderer.data(@email)) %>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
28
|
</div>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<div class="max-w-5xl mx-auto p-6 space-y-6">
|
2
|
+
<div class="bg-red-50 p-4 rounded-xl shadow">
|
3
|
+
<h2 class="text-xl font-semibold text-red-800">⚠️ <%= exception.dig(:error, :type) %> in <%= exception.dig(:error, :location) %></h2>
|
4
|
+
<p class="text-red-700 mt-2"><%= exception.dig(:error, :message) %></p>
|
5
|
+
<% if exception.dig(:error, :backtrace_line).present? %>
|
6
|
+
<p class="text-gray-700 mt-2 font-mono text-sm"><%= exception.dig(:error, :backtrace_line) %></p>
|
7
|
+
<% end %>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<% # Reorder the sections for better logical flow %>
|
11
|
+
<% [:request, :session, :data].each do |section| %>
|
12
|
+
<% if exception[section].present? %>
|
13
|
+
<div class="bg-white border border-gray-200 rounded-xl shadow p-4">
|
14
|
+
<h3 class="text-lg font-bold text-gray-800 capitalize"><%= section %></h3>
|
15
|
+
<dl class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3 text-sm">
|
16
|
+
<% exception[section].each do |k, v| %>
|
17
|
+
<div>
|
18
|
+
<dt class="text-gray-500"><%= k %></dt>
|
19
|
+
<dd class="text-gray-800 font-mono break-words"><%= v %></dd>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
</dl>
|
23
|
+
</div>
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% if exception[:backtrace].present? %>
|
28
|
+
<div class="bg-gray-100 border border-gray-200 rounded-xl shadow p-4">
|
29
|
+
<h3 class="text-lg font-bold text-gray-800">Backtrace</h3>
|
30
|
+
<ul class="mt-2 list-disc list-inside text-sm text-gray-700 font-mono space-y-1">
|
31
|
+
<% exception[:backtrace].each do |line| %>
|
32
|
+
<li><%= line %></li>
|
33
|
+
<% end %>
|
34
|
+
</ul>
|
35
|
+
</div>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<% if exception[:environment].present? %>
|
39
|
+
<div class="bg-white border border-gray-200 rounded-xl shadow p-4">
|
40
|
+
<h3 class="text-lg font-bold text-gray-800">Environment</h3>
|
41
|
+
<details>
|
42
|
+
<summary class="cursor-pointer text-blue-600 hover:text-blue-800 mb-2">Show Environment Variables</summary>
|
43
|
+
<dl class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3 text-sm">
|
44
|
+
<% exception[:environment].each do |k, v| %>
|
45
|
+
<div>
|
46
|
+
<dt class="text-gray-500"><%= k %></dt>
|
47
|
+
<dd class="text-gray-800 font-mono break-words"><%= v %></dd>
|
48
|
+
</div>
|
49
|
+
<% end %>
|
50
|
+
</dl>
|
51
|
+
</details>
|
52
|
+
</div>
|
53
|
+
<% end %>
|
54
|
+
</div>
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "rails_mail/renderer"
|
2
|
+
|
1
3
|
module RailsMail
|
2
4
|
class Configuration
|
3
5
|
attr_accessor :authentication_callback, :show_clear_all_button_callback,
|
@@ -10,6 +12,7 @@ module RailsMail
|
|
10
12
|
@trim_emails_older_than = nil
|
11
13
|
@trim_emails_max_count = nil
|
12
14
|
@enqueue_trim_job = ->(email) { RailsMail::TrimEmailsJob.perform_later }
|
15
|
+
register_default_renderers
|
13
16
|
end
|
14
17
|
|
15
18
|
def authenticate(&callback)
|
@@ -25,5 +28,13 @@ module RailsMail
|
|
25
28
|
end
|
26
29
|
@show_clear_all_button_callback = callback
|
27
30
|
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def register_default_renderers
|
35
|
+
RailsMail::RendererRegistry.register(RailsMail::Renderer::HtmlRenderer)
|
36
|
+
RailsMail::RendererRegistry.register(RailsMail::Renderer::TextRenderer)
|
37
|
+
RailsMail::RendererRegistry.register(RailsMail::Renderer::ExceptionNotifierRenderer)
|
38
|
+
end
|
28
39
|
end
|
29
40
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMail
|
4
|
+
class ExceptionParser
|
5
|
+
attr_reader :raw
|
6
|
+
|
7
|
+
def initialize(raw)
|
8
|
+
@raw = raw
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid_format?
|
12
|
+
return false unless @raw.present?
|
13
|
+
@raw.include?("occurred in")
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse
|
17
|
+
return {} unless @raw
|
18
|
+
|
19
|
+
# Extract the error information from the first few lines
|
20
|
+
error_info = extract_error
|
21
|
+
|
22
|
+
# Extract sections using a more direct approach
|
23
|
+
sections = {
|
24
|
+
"Data" => extract_section("Data"),
|
25
|
+
"Request" => extract_section("Request"),
|
26
|
+
"Session" => extract_section("Session"),
|
27
|
+
"Backtrace" => extract_section("Backtrace"),
|
28
|
+
"Environment" => extract_section("Environment")
|
29
|
+
}
|
30
|
+
|
31
|
+
{
|
32
|
+
error: error_info,
|
33
|
+
data: parse_section_content(sections["Data"]),
|
34
|
+
request: parse_section_content(sections["Request"]),
|
35
|
+
session: parse_section_content(sections["Session"]),
|
36
|
+
backtrace: parse_backtrace(sections["Backtrace"]),
|
37
|
+
environment: parse_section_content(sections["Environment"])
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_error
|
42
|
+
# Look for the error line pattern - supporting both "A" and "An"
|
43
|
+
error_match = @raw.match(/^A(?:n)? ([\w:]+) occurred in ([^:]+):\r\n\r\n\s+(.*?)\r\n\s+(.+?)\r\n/m)
|
44
|
+
return {} unless error_match
|
45
|
+
|
46
|
+
{
|
47
|
+
type: error_match[1],
|
48
|
+
location: error_match[2],
|
49
|
+
message: error_match[3],
|
50
|
+
backtrace_line: error_match[4]
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def extract_section(section_name)
|
55
|
+
# Match the section header and everything until the next section header or end of string
|
56
|
+
section_regex = /^-+\r\n#{Regexp.escape(section_name)}:\r\n-+\r\n(.*?)(?=\r\n-+\r\n|\z)/m
|
57
|
+
match = @raw.match(section_regex)
|
58
|
+
match ? match[1] : ""
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_section_content(content)
|
62
|
+
return {} unless content && !content.empty?
|
63
|
+
|
64
|
+
result = {}
|
65
|
+
content.split("\r\n").each do |line|
|
66
|
+
line = line.strip
|
67
|
+
# Handle lines that start with * and have indentation
|
68
|
+
if line.start_with?("*")
|
69
|
+
# Remove the asterisk and any leading whitespace
|
70
|
+
key_value = line.sub(/^\*\s*/, "").split(":", 2)
|
71
|
+
if key_value.length == 2
|
72
|
+
key = key_value[0].strip
|
73
|
+
value = key_value[1].strip
|
74
|
+
result[key] = value if key && !key.empty?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_backtrace(content)
|
82
|
+
return [] unless content && !content.empty?
|
83
|
+
|
84
|
+
# Split by Windows-style line endings and extract non-empty lines
|
85
|
+
lines = content.split("\r\n").map(&:strip).reject(&:empty?)
|
86
|
+
# Filter out lines that start with * (which would be key-value pairs)
|
87
|
+
lines.reject { |line| line.start_with?("*") }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RailsMail
|
2
|
+
module Renderer
|
3
|
+
class Base
|
4
|
+
def self.handles?(email)
|
5
|
+
raise NotImplementedError, "#{self.name} must implement .handles?"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.partial_name
|
9
|
+
raise NotImplementedError, "#{self.name} must implement .partial_name"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.title
|
13
|
+
self.name.demodulize.sub("Renderer", "")
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.priority
|
17
|
+
0 # Lower numbers render first
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.data(email)
|
21
|
+
{} # Override to provide additional data to the partial
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RailsMail
|
2
|
+
module Renderer
|
3
|
+
class ExceptionNotifierRenderer < Base
|
4
|
+
def self.handles?(email)
|
5
|
+
email.exception_parser.valid_format?
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.partial_name
|
9
|
+
"rails_mail/emails/exception"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.title
|
13
|
+
"Exception"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.priority
|
17
|
+
1 # After standard renderers
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.data(email)
|
21
|
+
{ exception: email.exception_parser.parse }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RailsMail
|
2
|
+
module Renderer
|
3
|
+
class HtmlRenderer < Base
|
4
|
+
def self.handles?(email)
|
5
|
+
email.content_type&.include?("text/html") ||
|
6
|
+
email.content_type&.include?("multipart/alternative")
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.partial_name
|
10
|
+
"rails_mail/emails/html_content"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.title
|
14
|
+
"HTML"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.priority
|
18
|
+
10 # Base priority for standard content
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RailsMail
|
2
|
+
module Renderer
|
3
|
+
class TextRenderer < Base
|
4
|
+
def self.handles?(email)
|
5
|
+
email.content_type&.include?("text/plain") ||
|
6
|
+
email.content_type&.include?("multipart/alternative")
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.partial_name
|
10
|
+
"rails_mail/emails/text_content"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.title
|
14
|
+
"Text"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.priority
|
18
|
+
20 # Just after HTML
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "rails_mail/renderer/base"
|
2
|
+
require "rails_mail/renderer/html_renderer"
|
3
|
+
require "rails_mail/renderer/text_renderer"
|
4
|
+
require "rails_mail/renderer/exception_notifier_renderer"
|
5
|
+
require "rails_mail/renderer_registry"
|
6
|
+
|
7
|
+
module RailsMail
|
8
|
+
module Renderer
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RailsMail
|
2
|
+
class RendererRegistry
|
3
|
+
class << self
|
4
|
+
def register(renderer_class)
|
5
|
+
renderers << renderer_class
|
6
|
+
end
|
7
|
+
|
8
|
+
def renderers
|
9
|
+
@renderers ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
def matching_renderers(email)
|
13
|
+
renderers
|
14
|
+
.select { |renderer| renderer.handles?(email) }
|
15
|
+
.sort_by(&:priority)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/rails_mail/version.rb
CHANGED
data/lib/rails_mail.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_mail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Philips
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -87,7 +87,10 @@ files:
|
|
87
87
|
- app/views/rails_mail/emails/_email.html.erb
|
88
88
|
- app/views/rails_mail/emails/_email_tabs.html.erb
|
89
89
|
- app/views/rails_mail/emails/_empty_state.html.erb
|
90
|
+
- app/views/rails_mail/emails/_exception.html.erb
|
90
91
|
- app/views/rails_mail/emails/_form.html.erb
|
92
|
+
- app/views/rails_mail/emails/_html_content.html.erb
|
93
|
+
- app/views/rails_mail/emails/_text_content.html.erb
|
91
94
|
- app/views/rails_mail/emails/destroy.turbo_stream.erb
|
92
95
|
- app/views/rails_mail/emails/destroy_all.turbo_stream.erb
|
93
96
|
- app/views/rails_mail/emails/edit.html.erb
|
@@ -105,6 +108,13 @@ files:
|
|
105
108
|
- lib/rails_mail/configuration.rb
|
106
109
|
- lib/rails_mail/delivery_method.rb
|
107
110
|
- lib/rails_mail/engine.rb
|
111
|
+
- lib/rails_mail/exception_parser.rb
|
112
|
+
- lib/rails_mail/renderer.rb
|
113
|
+
- lib/rails_mail/renderer/base.rb
|
114
|
+
- lib/rails_mail/renderer/exception_notifier_renderer.rb
|
115
|
+
- lib/rails_mail/renderer/html_renderer.rb
|
116
|
+
- lib/rails_mail/renderer/text_renderer.rb
|
117
|
+
- lib/rails_mail/renderer_registry.rb
|
108
118
|
- lib/rails_mail/version.rb
|
109
119
|
- lib/tasks/rails_mail_tasks.rake
|
110
120
|
homepage: https://github.com/synth/rails_mail
|