lotus-mailer 0.0.0 → 0.1.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
  SHA1:
3
- metadata.gz: 376a615362a8b09e1ed02f98bc625e788dcc781f
4
- data.tar.gz: 756ab718d25f778a6c30a2eb922f19ec32cc1df9
3
+ metadata.gz: 010f5e6d0f8485fe6d90af54b84181fceb6d639e
4
+ data.tar.gz: be1bbd47898677b7f6a6179377d86c0457944899
5
5
  SHA512:
6
- metadata.gz: d217f39b95869118b6d339b124bd301f320a06e6425ae02951947eb319226d977aa6d428e7e19736c8e9e556426183678fdddda8d75a74f77a0d8ec806458a45
7
- data.tar.gz: 5ecb5dfad868949e6ee9bfbbeb509d4fe4d5dd04487bb0bc2309f2fae4d7e6e63a1f122ca086c49a96116bbbaf7a9ffb4556ac964fed422551ea9e7c99d38d81
6
+ metadata.gz: 71838f83857156392f4cd227676ac025a3dca6ba8021b0c05aa5c03b4e00ffa0f87eb5df58c7d1333d9a21520237f9e9dcb2c9294035a3992966e2381285eba3
7
+ data.tar.gz: 9eefa471b50fa187498e31a58eb484dcded69b0f43f540655f42a2966c055cd5d165a4e895aa43b6e529e2ffad66d5fab5eea4f0bd38493277d81a9056988339
@@ -1,2 +1,10 @@
1
1
  # Lotus::Router
2
2
  Mail for Ruby applications
3
+
4
+ ## v0.1.0 - 2015-09-30
5
+ ### Added
6
+ - [Ines Coelho & Rosa Faria & Luca Guidi] Email delivery
7
+ - [Ines Coelho & Rosa Faria & Luca Guidi] Attachments
8
+ - [Ines Coelho & Rosa Faria & Luca Guidi] Multipart rendering
9
+ - [Ines Coelho & Rosa Faria & Luca Guidi] Configuration
10
+ - [Ines Coelho & Rosa Faria & Luca Guidi] Official support for Ruby 2.0
data/README.md CHANGED
@@ -42,24 +42,375 @@ Or install it yourself as:
42
42
 
43
43
  ## Usage
44
44
 
45
- TODO: Write usage instructions here
45
+ ### Conventions
46
46
 
47
- ## Versioning
47
+ * Templates are searched under `Lotus::Mailer.configuration.root`, set this value according to your app structure (eg. `"app/templates"`).
48
+ * A mailer will look for a template with a file name that is composed by its full class name (eg. `"articles/index"`).
49
+ * A template must have two concatenated extensions: one for the format and one for the engine (eg. `".html.erb"`).
50
+ * The framework must be loaded before rendering the first time: `Lotus::Mailer.load!`.
48
51
 
49
- __Lotus::Mailer__ uses [Semantic Versioning 2.0.0](http://semver.org)
52
+ ### Mailers
50
53
 
51
- ## Development
54
+ A simple mailer looks like this:
52
55
 
53
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake false` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
56
+ ```ruby
57
+ require lotus/mailer
54
58
 
55
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
59
+ class InvoiceMailer
60
+ include Lotus::Mailer
61
+ end
62
+ ```
56
63
 
57
- ## Contributing
64
+ A mailer with `.to` and `.from` addresses and mailer delivery:
58
65
 
59
- Bug reports and pull requests are welcome on GitHub at https://github.com/lotus/mailer.
66
+ ```ruby
67
+ require lotus/mailer
60
68
 
61
- ## Copyright
69
+ Lotus::Mailer.configuration do
70
+ delivery_method :smtp,
71
+ address: "smtp.gmail.com",
72
+ port: 587,
73
+ domain: "example.com",
74
+ user_name: ENV['SMTP_USERNAME'],
75
+ password: ENV['SMTP_PASSWORD'],
76
+ authentication: "plain",
77
+ enable_starttls_auto: true
78
+ end.load!
62
79
 
63
- Copyright © 2015 Luca Guidi – Released under MIT License
80
+ class WelcomeMailer
81
+ include Lotus::Mailer
82
+
83
+ from 'noreply@sender.com'
84
+ to 'noreply@recipient.com'
85
+ subject 'Welcome'
86
+ end
87
+
88
+ WelcomeMailer.deliver
89
+ ```
90
+
91
+ ### Locals
92
+
93
+ The set of objects passed in the `deliver` call are called `locals` and are avaliable inside the mailer and the template.
94
+
95
+ ```ruby
96
+ require lotus/mailer
97
+
98
+ User = Struct.new(:name, :username)
99
+ user = User.new('Luca', 'jodosha')
100
+
101
+ Lotus::Mailer.load!
102
+
103
+ class WelcomeMailer
104
+ include Lotus::Mailer
105
+
106
+ from 'noreply@sender.com'
107
+ subject 'Welcome'
108
+ to :recipient
109
+
110
+ private
111
+
112
+ def recipient
113
+ user.email
114
+ end
115
+ end
116
+
117
+ InvoiceMailer.deliver(user: luca)
118
+ ```
119
+
120
+ The corresponding `erb` file:
121
+
122
+ ```erb
123
+ Hello <%= user.name %>!
124
+ ```
125
+
126
+ ### Scope
127
+
128
+ All public methods defined in the mailer are accessible from the template:
129
+
130
+ ```ruby
131
+ require lotus/mailer
132
+
133
+ class WelcomeMailer
134
+ include Lotus::Mailer
135
+
136
+ from 'noreply@sender.com'
137
+ to 'noreply@recipient.com'
138
+ subject 'Welcome'
139
+
140
+ def greeting
141
+ 'Ahoy'
142
+ end
143
+ end
144
+ ```
145
+
146
+ ```erb
147
+ <h2><%= greeting %></h2>
148
+ ```
149
+
150
+ ### Template
151
+
152
+ The template file must be located under the relevant `root` and must match the inflected snake case of the mailer class name.
153
+
154
+ ```ruby
155
+ # Given this root
156
+ Lotus::Mailer.configuration.root # => #<Pathname:app/templates>
157
+
158
+ # For InvoiceMailer, it looks for:
159
+ # * app/templates/invoice_mailer.html.erb
160
+ # * app/templates/invoice_mailer.txt.erb
161
+ ```
162
+
163
+ If we want to specify a different template, we can do:
164
+
165
+ ```ruby
166
+ class InvoiceMailer
167
+ include Lotus::Mailer
168
+
169
+ template 'invoice'
170
+ end
171
+
172
+ # It will look for:
173
+ # * app/templates/invoice.html.erb
174
+ # * app/templates/invoice.txt.erb
175
+ ```
176
+
177
+ ### Engines
178
+
179
+ The builtin rendering engine is [ERb](http://en.wikipedia.org/wiki/ERuby).
64
180
 
181
+ This is the list of the supported engines.
182
+ They are listed in order of **higher precedence**, for a given extension.
183
+ For instance, if [ERubis](http://www.kuwata-lab.com/erubis/) is loaded, it will be preferred over ERb to render `.erb` templates.
65
184
 
185
+ <table>
186
+ <tr>
187
+ <th>Engine</th>
188
+ <th>Extensions</th>
189
+ </tr>
190
+ <tr>
191
+ <td>Erubis</td>
192
+ <td>erb, rhtml, erubis</td>
193
+ </tr>
194
+ <tr>
195
+ <td>ERb</td>
196
+ <td>erb, rhtml</td>
197
+ </tr>
198
+ <tr>
199
+ <td>Redcarpet</td>
200
+ <td>markdown, mkd, md</td>
201
+ </tr>
202
+ <tr>
203
+ <td>RDiscount</td>
204
+ <td>markdown, mkd, md</td>
205
+ </tr>
206
+ <tr>
207
+ <td>Kramdown</td>
208
+ <td>markdown, mkd, md</td>
209
+ </tr>
210
+ <tr>
211
+ <td>Maruku</td>
212
+ <td>markdown, mkd, md</td>
213
+ </tr>
214
+ <tr>
215
+ <td>BlueCloth</td>
216
+ <td>markdown, mkd, md</td>
217
+ </tr>
218
+ <tr>
219
+ <td>Asciidoctor</td>
220
+ <td>ad, adoc, asciidoc</td>
221
+ </tr>
222
+ <tr>
223
+ <td>Builder</td>
224
+ <td>builder</td>
225
+ </tr>
226
+ <tr>
227
+ <td>CSV</td>
228
+ <td>rcsv</td>
229
+ </tr>
230
+ <tr>
231
+ <td>CoffeeScript</td>
232
+ <td>coffee</td>
233
+ </tr>
234
+ <tr>
235
+ <td>WikiCloth</td>
236
+ <td>wiki, mediawiki, mw</td>
237
+ </tr>
238
+ <tr>
239
+ <td>Creole</td>
240
+ <td>wiki, creole</td>
241
+ </tr>
242
+ <tr>
243
+ <td>Etanni</td>
244
+ <td>etn, etanni</td>
245
+ </tr>
246
+ <tr>
247
+ <td>Haml</td>
248
+ <td>haml</td>
249
+ </tr>
250
+ <tr>
251
+ <td>Less</td>
252
+ <td>less</td>
253
+ </tr>
254
+ <tr>
255
+ <td>Liquid</td>
256
+ <td>liquid</td>
257
+ </tr>
258
+ <tr>
259
+ <td>Markaby</td>
260
+ <td>mab</td>
261
+ </tr>
262
+ <tr>
263
+ <td>Nokogiri</td>
264
+ <td>nokogiri</td>
265
+ </tr>
266
+ <tr>
267
+ <td>Plain</td>
268
+ <td>html</td>
269
+ </tr>
270
+ <tr>
271
+ <td>RDoc</td>
272
+ <td>rdoc</td>
273
+ </tr>
274
+ <tr>
275
+ <td>Radius</td>
276
+ <td>radius</td>
277
+ </tr>
278
+ <tr>
279
+ <td>RedCloth</td>
280
+ <td>textile</td>
281
+ </tr>
282
+ <tr>
283
+ <td>Sass</td>
284
+ <td>sass</td>
285
+ </tr>
286
+ <tr>
287
+ <td>Scss</td>
288
+ <td>scss</td>
289
+ </tr>
290
+ <tr>
291
+ <td>Slim</td>
292
+ <td>slim</td>
293
+ </tr>
294
+ <tr>
295
+ <td>String</td>
296
+ <td>str</td>
297
+ </tr>
298
+ <tr>
299
+ <td>Yajl</td>
300
+ <td>yajl</td>
301
+ </tr>
302
+ </table>
303
+
304
+
305
+ ### Configuration
306
+
307
+ __Lotus::Mailer__ can be configured with a DSL that determines its behavior.
308
+ It supports a few options:
309
+
310
+ ```ruby
311
+ require 'lotus/mailer'
312
+
313
+ Lotus::Maler.configure do
314
+ # Set the root path where to search for templates
315
+ # Argument: String, Pathname, #to_pathname, defaults to the current directory
316
+ #
317
+ root '/path/to/root'
318
+
319
+ # Set the default charset for emails
320
+ # Argument: String, defaults to "UTF-8"
321
+ #
322
+ default_charset 'iso-8859'
323
+
324
+ # Set the delivery method
325
+ # Argument: Symbol
326
+ # Argument: Hash, optional configurations
327
+ delivery_method :stmp
328
+ ```
329
+
330
+ ### Attachments
331
+
332
+ Attachments can be added with the following API:
333
+
334
+ ```ruby
335
+ class InvoiceMailer
336
+ include Lotus::Mailer
337
+ # ...
338
+
339
+ def prepare
340
+ mail.attachments['invoice.pdf'] = '/path/to/invoice.pdf'
341
+ # or
342
+ mail.attachments['invoice.pdf'] = File.read('/path/to/invoice.pdf')
343
+ end
344
+ end
345
+ ```
346
+
347
+ ### Delivery Method
348
+
349
+ The global delivery method is defined through the __Lotus::Mailer__ configuration, as:
350
+
351
+ ```ruby
352
+ Lotus::Mailer.configuration do
353
+ delivery_method :smtp
354
+ end
355
+ ```
356
+
357
+ ```ruby
358
+ Lotus::Mailer.configuration do
359
+ delivery_method :smtp, address: "localhost", port: 1025
360
+ end
361
+ ```
362
+
363
+ Builtin options are:
364
+
365
+ * Exim (`:exim`)
366
+ * Sendmail (`:sendmail`)
367
+ * SMTP (`:smtp`, for local installations)
368
+ * SMTP Connection (`:smtp_connection`, via `Net::SMTP` - for remote installations)
369
+ * Test (`:test`, for testing purposes)
370
+
371
+ ### Custom Delivery Method
372
+
373
+ Developers can specify their own custom delivery policy:
374
+
375
+ ```ruby
376
+ require 'lotus/mailer'
377
+
378
+ class MandrillDeliveryMethod
379
+ def initialize(options)
380
+ @options = options
381
+ end
382
+
383
+ def deliver!(mail)
384
+ # ...
385
+ end
386
+ end
387
+
388
+ Lotus::Mailer.configure do
389
+ delivery_method MandrillDeliveryMethod,
390
+ username: ENV['MANDRILL_USERNAME'],
391
+ password: ENV['MANDRILL_API_KEY']
392
+ end.load!
393
+ ```
394
+
395
+ The class passed to `.delivery_method` must accept an optional set of options
396
+ with the constructor (`#initialize`) and respond to `#deliver!`.
397
+
398
+ ### Multipart Delivery
399
+
400
+ All the email are sent as multipart messages by default.
401
+ For a given mailer, the framework looks up for associated text (`.txt`) and `HTML` (`.html`) templates and render them.
402
+
403
+ ```ruby
404
+ InvoiceMailer.deliver # delivers both text and html templates
405
+ InvoiceMailer.deliver(format: :txt) # delivers only text template
406
+ ```
407
+
408
+ Please note that **they aren't both mandatory, but at least one of them MUST** be present.
409
+
410
+ ## Versioning
411
+
412
+ __Lotus::Mailer__ uses [Semantic Versioning 2.0.0](http://semver.org)
413
+
414
+ ## Copyright
415
+
416
+ Copyright © 2015 Luca Guidi – Released under MIT License
@@ -0,0 +1 @@
1
+ require 'lotus/mailer'
@@ -1,7 +1,316 @@
1
- require "lotus/mailer/version"
1
+ require 'lotus/utils/class_attribute'
2
+ require 'lotus/mailer/version'
3
+ require 'lotus/mailer/configuration'
4
+ require 'lotus/mailer/dsl'
5
+ require 'mail'
2
6
 
3
7
  module Lotus
8
+ # Lotus::Mailer
9
+ #
10
+ # @since 0.1.0
4
11
  module Mailer
5
- # Your code goes here...
12
+ # Base error for Lotus::Mailer
13
+ #
14
+ # @since 0.1.0
15
+ class Error < ::StandardError
16
+ end
17
+
18
+ # Missing delivery data error
19
+ #
20
+ # It's raised when a mailer doesn't specify <tt>from</tt> or <tt>to</tt>.
21
+ #
22
+ # @since 0.1.0
23
+ class MissingDeliveryDataError < Error
24
+ def initialize
25
+ super("Missing delivery data, please check 'from', or 'to'")
26
+ end
27
+ end
28
+
29
+ # Content types mapping
30
+ #
31
+ # @since 0.1.0
32
+ # @api private
33
+ CONTENT_TYPES = {
34
+ html: 'text/html',
35
+ txt: 'text/plain'
36
+ }.freeze
37
+
38
+ include Utils::ClassAttribute
39
+
40
+ # @since 0.1.0
41
+ # @api private
42
+ class_attribute :configuration
43
+ self.configuration = Configuration.new
44
+
45
+ # Configure the framework.
46
+ # It yields the given block in the context of the configuration
47
+ #
48
+ # @param blk [Proc] the configuration block
49
+ #
50
+ # @since 0.1.0
51
+ #
52
+ # @see Lotus::Mailer::Configuration
53
+ #
54
+ # @example
55
+ # require 'lotus/mailer'
56
+ #
57
+ # Lotus::Mailer.configure do
58
+ # root '/path/to/root'
59
+ # end
60
+ def self.configure(&blk)
61
+ configuration.instance_eval(&blk)
62
+ self
63
+ end
64
+
65
+ # Override Ruby's hook for modules.
66
+ # It includes basic Lotus::Mailer modules to the given Class.
67
+ # It sets a copy of the framework configuration
68
+ #
69
+ # @param base [Class] the target mailer
70
+ #
71
+ # @since 0.1.0
72
+ # @api private
73
+ #
74
+ # @see http://www.ruby-doc.org/core/Module.html#method-i-included
75
+ def self.included(base)
76
+ conf = self.configuration
77
+ conf.add_mailer(base)
78
+
79
+ base.class_eval do
80
+ extend Dsl
81
+ extend ClassMethods
82
+
83
+ include Utils::ClassAttribute
84
+ class_attribute :configuration
85
+
86
+ self.configuration = conf.duplicate
87
+ end
88
+
89
+ conf.copy!(base)
90
+ end
91
+
92
+ # Test deliveries
93
+ #
94
+ # This is a collection of delivered messages, used when <tt>delivery_method</tt>
95
+ # is set on <tt>:test</tt>
96
+ #
97
+ # @return [Array] a collection of delivered messages
98
+ #
99
+ # @since 0.1.0
100
+ #
101
+ # @see Lotus::Mailer::Configuration#delivery_mode
102
+ #
103
+ # @example
104
+ # require 'lotus/mailer'
105
+ #
106
+ # Lotus::Mailer.configure do
107
+ # delivery_method :test
108
+ # end.load!
109
+ #
110
+ # # In testing code
111
+ # Signup::Welcome.deliver
112
+ # Lotus::Mailer.deliveries.count # => 1
113
+ def self.deliveries
114
+ Mail::TestMailer.deliveries
115
+ end
116
+
117
+ # Load the framework
118
+ #
119
+ # @since 0.1.0
120
+ # @api private
121
+ def self.load!
122
+ Mail.eager_autoload!
123
+ configuration.load!
124
+ end
125
+
126
+ # @since 0.1.0
127
+ module ClassMethods
128
+ # Delivers a multipart email message.
129
+ #
130
+ # When a mailer defines a <tt>html</tt> and <tt>txt</tt> template, they are
131
+ # both delivered.
132
+ #
133
+ # In order to selectively deliver only one of the two templates, use
134
+ # <tt>Signup::Welcome.deliver(format: :txt)</tt>
135
+ #
136
+ # All the given locals, excepted the reserved ones (<tt>:format</tt> and
137
+ # <tt>charset</tt>), are avaliable as rendering context for the templates.
138
+ #
139
+ # @param locals [Hash] a set of objects that acts as context for the rendering
140
+ # @option :format [Symbol] specify format to deliver
141
+ # @option :charset [String] charset
142
+ #
143
+ # @since 0.1.0
144
+ #
145
+ # @see Lotus::Mailer::Configuration#default_charset
146
+ #
147
+ # @example
148
+ # require 'lotus/mailer'
149
+ #
150
+ # Lotus::Mailer.configure do
151
+ # delivery_method :smtp
152
+ # end.load!
153
+ #
154
+ # module Billing
155
+ # class Invoice
156
+ # include Lotus::Mailer
157
+ #
158
+ # from 'noreply@example.com'
159
+ # to :recipient
160
+ # subject :subject_line
161
+ #
162
+ # def prepare
163
+ # mail.attachments['invoice.pdf'] = File.read('/path/to/invoice.pdf')
164
+ # end
165
+ #
166
+ # private
167
+ #
168
+ # def recipient
169
+ # user.email
170
+ # end
171
+ #
172
+ # def subject_line
173
+ # "Invoice - #{ invoice.number }"
174
+ # end
175
+ # end
176
+ # end
177
+ #
178
+ # invoice = Invoice.new
179
+ # user = User.new(name: 'L', email: 'user@example.com')
180
+ #
181
+ # Billing::Invoice.deliver(invoice: invoice, user: user) # Deliver both text, HTML parts and the attachment
182
+ # Billing::Invoice.deliver(invoice: invoice, user: user, format: :txt) # Deliver only the text part and the attachment
183
+ # Billing::Invoice.deliver(invoice: invoice, user: user, format: :html) # Deliver only the text part and the attachment
184
+ # Billing::Invoice.deliver(invoice: invoice, user: user, charset: 'iso-8859') # Deliver both the parts with "iso-8859"
185
+ def deliver(locals = {})
186
+ new(locals).deliver
187
+ end
188
+ end
189
+
190
+ # Initialize a mailer
191
+ #
192
+ # @param locals [Hash] a set of objects that acts as context for the rendering
193
+ # @option :format [Symbol] specify format to deliver
194
+ # @option :charset [String] charset
195
+ #
196
+ # @since 0.1.0
197
+ def initialize(locals = {})
198
+ @locals = locals
199
+ @format = locals.fetch(:format, nil)
200
+ @charset = charset = locals.fetch(:charset, self.class.configuration.default_charset)
201
+ @mail = Mail.new.tap do |m|
202
+ m.from = __dsl(:from)
203
+ m.to = __dsl(:to)
204
+ m.subject = __dsl(:subject)
205
+
206
+ m.charset = charset
207
+ m.html_part = __part(:html)
208
+ m.text_part = __part(:txt)
209
+
210
+ m.delivery_method(*Lotus::Mailer.configuration.delivery_method)
211
+ end
212
+
213
+ prepare
214
+ end
215
+
216
+ # Render a single template with the specified format.
217
+ #
218
+ # @param format [Symbol] format
219
+ #
220
+ # @return [String] the output of the rendering process.
221
+ #
222
+ # @since 0.1.0
223
+ # @api private
224
+ def render(format)
225
+ self.class.templates(format).render(self, @locals)
226
+ end
227
+
228
+ # Delivers a multipart email, by looking at all the associated templates and render them.
229
+ #
230
+ # @since 0.1.0
231
+ # @api private
232
+ def deliver
233
+ mail.deliver
234
+ rescue ArgumentError
235
+ raise MissingDeliveryDataError
236
+ end
237
+
238
+ protected
239
+
240
+ # Prepare the email message when a new mailer is initialized.
241
+ #
242
+ # This is a hook that can be overwritten by mailers.
243
+ #
244
+ # @since 0.1.0
245
+ #
246
+ # @example
247
+ # require 'lotus/mailer'
248
+ #
249
+ # module Billing
250
+ # class Invoice
251
+ # include Lotus::Mailer
252
+ #
253
+ # subject 'Invoice'
254
+ # from 'noreply@example.com'
255
+ # to ''
256
+ #
257
+ # def prepare
258
+ # mail.attachments['invoice.pdf'] = File.read('/path/to/invoice.pdf')
259
+ # end
260
+ #
261
+ # private
262
+ #
263
+ # def recipient
264
+ # user.email
265
+ # end
266
+ # end
267
+ # end
268
+ #
269
+ # invoice = Invoice.new
270
+ # user = User.new(name: 'L', email: 'user@example.com')
271
+ def prepare
272
+ end
273
+
274
+ # @private
275
+ # @since 0.1.0
276
+ def method_missing(m)
277
+ @locals.fetch(m) { super }
278
+ end
279
+
280
+ # @since 0.1.0
281
+ attr_reader :mail
282
+
283
+ # @private
284
+ # @since 0.1.0
285
+ attr_reader :charset
286
+
287
+ private
288
+
289
+ # @private
290
+ # @since 0.1.0
291
+ def __dsl(method_name)
292
+ case result = self.class.__send__(method_name)
293
+ when Symbol
294
+ __send__(result)
295
+ else
296
+ result
297
+ end
298
+ end
299
+
300
+ # @private
301
+ # @since 0.1.0
302
+ def __part(format)
303
+ Mail::Part.new.tap do |part|
304
+ part.content_type = "#{ CONTENT_TYPES.fetch(format) }; charset=#{ charset }"
305
+ part.body = render(format)
306
+ end if __part?(format)
307
+ end
308
+
309
+ # @private
310
+ # @since 0.1.0
311
+ def __part?(format)
312
+ @format == format ||
313
+ (!@format && !self.class.templates(format).nil?)
314
+ end
6
315
  end
7
316
  end