hanami-mailer 0.0.0 → 0.2.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/CHANGELOG.md +14 -0
- data/LICENSE.md +22 -0
- data/README.md +390 -8
- data/hanami-mailer.gemspec +18 -12
- data/lib/hanami-mailer.rb +1 -0
- data/lib/hanami/mailer.rb +311 -2
- data/lib/hanami/mailer/configuration.rb +307 -0
- data/lib/hanami/mailer/dsl.rb +288 -0
- data/lib/hanami/mailer/rendering/template_name.rb +52 -0
- data/lib/hanami/mailer/rendering/templates_finder.rb +133 -0
- data/lib/hanami/mailer/template.rb +40 -0
- data/lib/hanami/mailer/version.rb +2 -1
- metadata +84 -13
- data/.gitignore +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -0,0 +1,288 @@
|
|
1
|
+
require 'hanami/mailer/rendering/template_name'
|
2
|
+
require 'hanami/mailer/rendering/templates_finder'
|
3
|
+
|
4
|
+
module Hanami
|
5
|
+
module Mailer
|
6
|
+
# Class level DSL
|
7
|
+
#
|
8
|
+
# @since 0.1.0
|
9
|
+
module Dsl
|
10
|
+
# Set the template name IF it differs from the convention.
|
11
|
+
#
|
12
|
+
# For a given mailer named <tt>Signup::Welcome</tt> it will look for
|
13
|
+
# <tt>signup/welcome.*.*</tt> templates under the root directory.
|
14
|
+
#
|
15
|
+
# If for some reason, we need to specify a different template name, we can
|
16
|
+
# use this method.
|
17
|
+
#
|
18
|
+
# This is part of a DSL, for this reason when this method is called with
|
19
|
+
# an argument, it will set the corresponding class variable. When
|
20
|
+
# called without, it will return the already set value, or the default.
|
21
|
+
#
|
22
|
+
# @overload template(value)
|
23
|
+
# Sets the given value
|
24
|
+
# @param value [String, #to_s] relative template path, under root
|
25
|
+
# @return [NilClass]
|
26
|
+
#
|
27
|
+
# @overload template
|
28
|
+
# Gets the template name
|
29
|
+
# @return [String]
|
30
|
+
#
|
31
|
+
# @since 0.1.0
|
32
|
+
#
|
33
|
+
# @see Hanami::Mailers::Configuration.root
|
34
|
+
#
|
35
|
+
# @example Custom template name
|
36
|
+
# require 'hanami/mailer'
|
37
|
+
#
|
38
|
+
# class MyMailer
|
39
|
+
# include Hanami::Mailer
|
40
|
+
# template 'mailer'
|
41
|
+
# end
|
42
|
+
def template(value = nil)
|
43
|
+
if value.nil?
|
44
|
+
@template ||= ::Hanami::Mailer::Rendering::TemplateName.new(name, configuration.namespace).to_s
|
45
|
+
else
|
46
|
+
@template = value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a set of associated templates or only one for the given format
|
51
|
+
#
|
52
|
+
# This is part of a DSL, for this reason when this method is called with
|
53
|
+
# an argument, it will set the corresponding class variable. When
|
54
|
+
# called without, it will return the already set value, or the default.
|
55
|
+
#
|
56
|
+
# @overload templates(format)
|
57
|
+
# Returns the template associated with the given format
|
58
|
+
# @param value [Symbol] the format
|
59
|
+
# @return [Hash]
|
60
|
+
#
|
61
|
+
# @overload templates
|
62
|
+
# Returns all the associated templates
|
63
|
+
# Gets the template name
|
64
|
+
# @return [Hash] a set of templates
|
65
|
+
#
|
66
|
+
# @since 0.1.0
|
67
|
+
# @api private
|
68
|
+
def templates(format = nil)
|
69
|
+
if format.nil?
|
70
|
+
@templates = ::Hanami::Mailer::Rendering::TemplatesFinder.new(self).find
|
71
|
+
else
|
72
|
+
@templates.fetch(format, nil)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sets the sender for mail messages
|
77
|
+
#
|
78
|
+
# It accepts a hardcoded value as a string, or a symbol that represents
|
79
|
+
# an instance method for more complex logic.
|
80
|
+
#
|
81
|
+
# This value MUST be set, otherwise an exception is raised at the delivery
|
82
|
+
# time.
|
83
|
+
#
|
84
|
+
# When a value is given, specify the sender of the email
|
85
|
+
# Otherwise, it returns the sender of the email
|
86
|
+
#
|
87
|
+
# This is part of a DSL, for this reason when this method is called with
|
88
|
+
# an argument, it will set the corresponding class variable. When
|
89
|
+
# called without, it will return the already set value, or the default.
|
90
|
+
#
|
91
|
+
# @overload from(value)
|
92
|
+
# Sets the sender
|
93
|
+
# @param value [String, Symbol] the hardcoded value or method name
|
94
|
+
# @return [NilClass]
|
95
|
+
#
|
96
|
+
# @overload from
|
97
|
+
# Returns the sender
|
98
|
+
# @return [String, Symbol] the sender
|
99
|
+
#
|
100
|
+
# @since 0.1.0
|
101
|
+
#
|
102
|
+
# @example Hardcoded value (String)
|
103
|
+
# require 'hanami/mailer'
|
104
|
+
#
|
105
|
+
# class WelcomeMailer
|
106
|
+
# include Hanami::Mailer
|
107
|
+
#
|
108
|
+
# from "noreply@example.com"
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# @example Method (Symbol)
|
112
|
+
# require 'hanami/mailer'
|
113
|
+
#
|
114
|
+
# class WelcomeMailer
|
115
|
+
# include Hanami::Mailer
|
116
|
+
# from :sender
|
117
|
+
#
|
118
|
+
# private
|
119
|
+
#
|
120
|
+
# def sender
|
121
|
+
# "noreply@example.com"
|
122
|
+
# end
|
123
|
+
# end
|
124
|
+
def from(value = nil)
|
125
|
+
if value.nil?
|
126
|
+
@from
|
127
|
+
else
|
128
|
+
@from = value
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Sets the recipient for mail messages
|
133
|
+
#
|
134
|
+
# It accepts a hardcoded value as a string or array of strings.
|
135
|
+
# For dynamic values, you can specify a symbol that represents an instance
|
136
|
+
# method.
|
137
|
+
#
|
138
|
+
# This value MUST be set, otherwise an exception is raised at the delivery
|
139
|
+
# time.
|
140
|
+
#
|
141
|
+
# When a value is given, specify the recipient of the email
|
142
|
+
# Otherwise, it returns the recipient of the email
|
143
|
+
#
|
144
|
+
# This is part of a DSL, for this reason when this method is called with
|
145
|
+
# an argument, it will set the corresponding class variable. When
|
146
|
+
# called without, it will return the already set value, or the default.
|
147
|
+
#
|
148
|
+
# @overload to(value)
|
149
|
+
# Sets the recipient
|
150
|
+
# @param value [String, Array, Symbol] the hardcoded value or method name
|
151
|
+
# @return [NilClass]
|
152
|
+
#
|
153
|
+
# @overload to
|
154
|
+
# Returns the recipient
|
155
|
+
# @return [String, Array, Symbol] the recipient
|
156
|
+
#
|
157
|
+
# @since 0.1.0
|
158
|
+
#
|
159
|
+
# @example Hardcoded value (String)
|
160
|
+
# require 'hanami/mailer'
|
161
|
+
#
|
162
|
+
# class WelcomeMailer
|
163
|
+
# include Hanami::Mailer
|
164
|
+
#
|
165
|
+
# to "user@example.com"
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# @example Hardcoded value (Array)
|
169
|
+
# require 'hanami/mailer'
|
170
|
+
#
|
171
|
+
# class WelcomeMailer
|
172
|
+
# include Hanami::Mailer
|
173
|
+
#
|
174
|
+
# to ["user-1@example.com", "user-2@example.com"]
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
# @example Method (Symbol)
|
178
|
+
# require 'hanami/mailer'
|
179
|
+
#
|
180
|
+
# class WelcomeMailer
|
181
|
+
# include Hanami::Mailer
|
182
|
+
# to :email_address
|
183
|
+
#
|
184
|
+
# private
|
185
|
+
#
|
186
|
+
# def email_address
|
187
|
+
# user.email
|
188
|
+
# end
|
189
|
+
# end
|
190
|
+
#
|
191
|
+
# user = User.new(name: 'L')
|
192
|
+
# WelcomeMailer.deliver(user: user)
|
193
|
+
#
|
194
|
+
# @example Method that returns a collection of recipients
|
195
|
+
# require 'hanami/mailer'
|
196
|
+
#
|
197
|
+
# class WelcomeMailer
|
198
|
+
# include Hanami::Mailer
|
199
|
+
# to :recipients
|
200
|
+
#
|
201
|
+
# private
|
202
|
+
#
|
203
|
+
# def recipients
|
204
|
+
# users.map(&:email)
|
205
|
+
# end
|
206
|
+
# end
|
207
|
+
#
|
208
|
+
# users = [User.new(name: 'L'), User.new(name: 'MG')]
|
209
|
+
# WelcomeMailer.deliver(users: users)
|
210
|
+
def to(value = nil)
|
211
|
+
if value.nil?
|
212
|
+
@to
|
213
|
+
else
|
214
|
+
@to = value
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Sets the subject for mail messages
|
219
|
+
#
|
220
|
+
# It accepts a hardcoded value as a string, or a symbol that represents
|
221
|
+
# an instance method for more complex logic.
|
222
|
+
#
|
223
|
+
# This value MUST be set, otherwise an exception is raised at the delivery
|
224
|
+
# time.
|
225
|
+
#
|
226
|
+
# This is part of a DSL, for this reason when this method is called with
|
227
|
+
# an argument, it will set the corresponding class variable. When
|
228
|
+
# called without, it will return the already set value, or the default.
|
229
|
+
#
|
230
|
+
# @overload subject(value)
|
231
|
+
# Sets the subject
|
232
|
+
# @param value [String, Symbol] the hardcoded value or method name
|
233
|
+
# @return [NilClass]
|
234
|
+
#
|
235
|
+
# @overload subject
|
236
|
+
# Returns the subject
|
237
|
+
# @return [String, Symbol] the subject
|
238
|
+
#
|
239
|
+
# @since 0.1.0
|
240
|
+
#
|
241
|
+
# @example Hardcoded value (String)
|
242
|
+
# require 'hanami/mailer'
|
243
|
+
#
|
244
|
+
# class WelcomeMailer
|
245
|
+
# include Hanami::Mailer
|
246
|
+
#
|
247
|
+
# subject "Welcome"
|
248
|
+
# end
|
249
|
+
#
|
250
|
+
# @example Method (Symbol)
|
251
|
+
# require 'hanami/mailer'
|
252
|
+
#
|
253
|
+
# class WelcomeMailer
|
254
|
+
# include Hanami::Mailer
|
255
|
+
# subject :greeting
|
256
|
+
#
|
257
|
+
# private
|
258
|
+
#
|
259
|
+
# def greeting
|
260
|
+
# "Hello, #{ user.name }"
|
261
|
+
# end
|
262
|
+
# end
|
263
|
+
#
|
264
|
+
# user = User.new(name: 'L')
|
265
|
+
# WelcomeMailer.deliver(user: user)
|
266
|
+
def subject(value = nil)
|
267
|
+
if value.nil?
|
268
|
+
@subject
|
269
|
+
else
|
270
|
+
@subject = value
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
protected
|
275
|
+
|
276
|
+
# Loading mechanism hook.
|
277
|
+
#
|
278
|
+
# @api private
|
279
|
+
# @since 0.1.0
|
280
|
+
#
|
281
|
+
# @see Hanami::Mailer.load!
|
282
|
+
def load!
|
283
|
+
templates.freeze
|
284
|
+
configuration.freeze
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'hanami/utils/string'
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module Mailer
|
5
|
+
module Rendering
|
6
|
+
# @since 0.1.0
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
# TODO this is identical to Hanami::View, consider to move into Hanami::Utils
|
10
|
+
class TemplateName
|
11
|
+
# @since 0.1.0
|
12
|
+
# @api private
|
13
|
+
NAMESPACE_SEPARATOR = '::'.freeze
|
14
|
+
|
15
|
+
# @since 0.1.0
|
16
|
+
# @api private
|
17
|
+
def initialize(name, namespace)
|
18
|
+
@name = name
|
19
|
+
compile!(namespace)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @since 0.1.0
|
23
|
+
# @api private
|
24
|
+
def to_s
|
25
|
+
@name
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
# @since 0.1.0
|
30
|
+
# @api private
|
31
|
+
def compile!(namespace)
|
32
|
+
tokens(namespace) { |token| replace!(token) }
|
33
|
+
@name = Utils::String.new(@name).underscore
|
34
|
+
end
|
35
|
+
|
36
|
+
# @since 0.1.0
|
37
|
+
# @api private
|
38
|
+
def tokens(namespace)
|
39
|
+
namespace.to_s.split(NAMESPACE_SEPARATOR).each do |token|
|
40
|
+
yield token
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @since 0.1.0
|
45
|
+
# @api private
|
46
|
+
def replace!(token)
|
47
|
+
@name.gsub!(%r{\A#{ token }#{ NAMESPACE_SEPARATOR }}, '')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'hanami/mailer/template'
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module Mailer
|
5
|
+
module Rendering
|
6
|
+
# Find templates for a mailer
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.1.0
|
10
|
+
#
|
11
|
+
# @see Mailer::Template
|
12
|
+
class TemplatesFinder
|
13
|
+
# Default format
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
# @since 0.1.0
|
17
|
+
FORMAT = '*'.freeze
|
18
|
+
|
19
|
+
# Default template engines
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
# @since 0.1.0
|
23
|
+
ENGINES = '*'.freeze
|
24
|
+
|
25
|
+
# Recursive pattern
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @since 0.1.0
|
29
|
+
RECURSIVE = '**'.freeze
|
30
|
+
|
31
|
+
# Initialize a finder
|
32
|
+
#
|
33
|
+
# @param mailer [Class] the mailer class
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
# @since 0.1.0
|
37
|
+
def initialize(mailer)
|
38
|
+
@mailer = mailer
|
39
|
+
end
|
40
|
+
|
41
|
+
# Find all the associated templates to the mailer.
|
42
|
+
# It recursively looks for templates under the root path of the mailer,
|
43
|
+
# that are matching the template name
|
44
|
+
#
|
45
|
+
# @return [Hash] the templates
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.1.0
|
49
|
+
#
|
50
|
+
# @see Hanami::Mailer::Dsl#root
|
51
|
+
# @see Hanami::Mailer::Dsl#templates
|
52
|
+
#
|
53
|
+
# @example
|
54
|
+
# require 'hanami/mailer'
|
55
|
+
#
|
56
|
+
# module Mailers
|
57
|
+
# class Welcome
|
58
|
+
# include Hanami::Mailer
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# Mailers::Welcome.root # => "/path/to/templates"
|
63
|
+
# Mailers::Welcome.templates # => {[:html] => "welcome"}
|
64
|
+
#
|
65
|
+
# # This mailer has a template:
|
66
|
+
# #
|
67
|
+
# # "/path/to/templates/welcome.html.erb"
|
68
|
+
#
|
69
|
+
# Hanami::Mailer::Rendering::TemplatesFinder.new(Mailers::Welcome).find
|
70
|
+
# # => [#<Hanami::Mailer::Template:0x007f8a0a86a970 ... @file="/path/to/templates/welcome.html.erb">]
|
71
|
+
def find
|
72
|
+
templates = Hash.new
|
73
|
+
_find.map do |template|
|
74
|
+
name = File.basename(template)
|
75
|
+
format = (( name.split(".") )[-2]).to_sym
|
76
|
+
templates[ format ] = Mailer::Template.new(template)
|
77
|
+
end
|
78
|
+
templates
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
# @api private
|
84
|
+
# @since 0.1.0
|
85
|
+
def _find(lookup = search_path)
|
86
|
+
Dir.glob( "#{ [root, lookup, template_name].join(separator) }.#{ format }.#{ engines }" )
|
87
|
+
end
|
88
|
+
|
89
|
+
# @api private
|
90
|
+
# @since 0.1.0
|
91
|
+
def template_name
|
92
|
+
Rendering::TemplateName.new(@mailer.template, @mailer.configuration.namespace).to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
# @api private
|
96
|
+
# @since 0.1.0
|
97
|
+
def root
|
98
|
+
@mailer.configuration.root
|
99
|
+
end
|
100
|
+
|
101
|
+
# @api private
|
102
|
+
# @since 0.1.0
|
103
|
+
def search_path
|
104
|
+
recursive
|
105
|
+
end
|
106
|
+
|
107
|
+
# @api private
|
108
|
+
# @since 0.1.0
|
109
|
+
def recursive
|
110
|
+
RECURSIVE
|
111
|
+
end
|
112
|
+
|
113
|
+
# @api private
|
114
|
+
# @since 0.1.0
|
115
|
+
def separator
|
116
|
+
::File::SEPARATOR
|
117
|
+
end
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
# @since 0.1.0
|
121
|
+
def format
|
122
|
+
FORMAT
|
123
|
+
end
|
124
|
+
|
125
|
+
# @api private
|
126
|
+
# @since 0.1.0
|
127
|
+
def engines
|
128
|
+
ENGINES
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|