griddler 0.6.4 → 1.0.0.pre.alpha.1
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 +121 -186
- data/Rakefile +1 -0
- data/lib/griddler.rb +7 -4
- data/lib/griddler/adapter_registry.rb +28 -0
- data/lib/griddler/adapters/mailgun_adapter.rb +21 -13
- data/lib/griddler/configuration.rb +14 -19
- data/lib/griddler/testing.rb +57 -0
- data/lib/griddler/version.rb +1 -1
- metadata +6 -8
- data/config/routes.rb +0 -3
- data/lib/griddler/adapters/mandrill_adapter.rb +0 -71
- data/lib/griddler/adapters/sendgrid_adapter.rb +0 -39
- data/lib/tasks/griddler_tasks.rake +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e799f0b0004bfe2b6628c40f370f2f16eab17223
|
4
|
+
data.tar.gz: b82b19547ba1ed0280e2e16460c54d4c22ed6cd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 240c7e9eda058081f8ee93e55f564f4c2a2683506c40d929cfc8fba43fbcbd7787fd91eef94871c272062ba9ae683a144e50cbcc442ddb2f3d22555c6762d35d
|
7
|
+
data.tar.gz: b5c5a4c2ad91fdaf9816178409cb12daca799ba9dd6be5e7311adf758bc879dfd14cf62e8fa0c7742a79e6c18a00c76b6581ae8ad929daa33af8fa46bc3839e7
|
data/README.md
CHANGED
@@ -6,133 +6,58 @@ Griddler
|
|
6
6
|
|
7
7
|
### Receive emails in your Rails app
|
8
8
|
|
9
|
-
Griddler is a Rails engine
|
10
|
-
[SendGrid
|
11
|
-
[Cloudmailin
|
12
|
-
[Postmark
|
13
|
-
[Mandrill
|
14
|
-
[Mailgun
|
15
|
-
that hands off a built email object to a class implemented by you.
|
9
|
+
Griddler is a Rails engine that provides an endpoint for the
|
10
|
+
[SendGrid](http://sendgrid.com/docs/API%20Reference/Webhooks/parse.html),
|
11
|
+
[Cloudmailin](http://cloudmailin.com),
|
12
|
+
[Postmark](http://developer.postmarkapp.com/developer-inbound-parse.html),
|
13
|
+
[Mandrill](http://help.mandrill.com/entries/21699367-Inbound-Email-Processing-Overview), or
|
14
|
+
[Mailgun](http://documentation.mailgun.com/user\_manual.html#receiving-messages-via-http-through-a-forward-action)
|
15
|
+
parse APIs that hands off a built email object to a class implemented by you.
|
16
16
|
|
17
17
|
Tutorials
|
18
18
|
---------
|
19
19
|
|
20
|
-
* SendGrid
|
20
|
+
* SendGrid wrote a
|
21
21
|
[great tutorial](http://blog.sendgrid.com/receiving-email-in-your-rails-app-with-griddler/)
|
22
22
|
on integrating Griddler with your application.
|
23
|
-
*
|
24
|
-
[Giant Robots](http://robots.thoughtbot.com/
|
23
|
+
* We have our own blog post on the subject over at
|
24
|
+
[Giant Robots](http://robots.thoughtbot.com/handle-incoming-email-with-griddler).
|
25
25
|
|
26
26
|
Installation
|
27
27
|
------------
|
28
28
|
|
29
|
-
Add griddler to your application's Gemfile
|
29
|
+
1. Add `griddler` and an [adapter] gem to your application's Gemfile
|
30
|
+
and run `bundle install`.
|
30
31
|
|
31
|
-
|
32
|
-
gem 'griddler'
|
33
|
-
```
|
34
|
-
A route is needed for the endpoint which receives `POST` messages. Currently,
|
35
|
-
the route is automatically appended to the route table like so:
|
36
|
-
|
37
|
-
```ruby
|
38
|
-
email_processor POST /email_processor(.:format) griddler/emails#create
|
39
|
-
```
|
32
|
+
[adapter]: #adapters
|
40
33
|
|
41
|
-
|
42
|
-
|
34
|
+
2. A route is needed for the endpoint which receives `POST` messages. To add the
|
35
|
+
route, in `config/routes.rb` you may either use the provided routing method
|
36
|
+
`mount_griddler` or set the route explicitly. Examples:
|
43
37
|
|
44
|
-
|
45
|
-
|
38
|
+
```ruby
|
39
|
+
# config/routes.rb
|
46
40
|
|
47
|
-
|
48
|
-
|
49
|
-
mount_griddler
|
41
|
+
# mount using default path: /email_processor
|
42
|
+
mount_griddler
|
50
43
|
|
51
|
-
# mount using a custom path
|
52
|
-
mount_griddler('/email/incoming')
|
44
|
+
# mount using a custom path
|
45
|
+
mount_griddler('/email/incoming')
|
53
46
|
|
54
|
-
# the
|
55
|
-
post '/email_processor' => 'griddler/emails#create'
|
56
|
-
```
|
47
|
+
# the DIY approach:
|
48
|
+
post '/email_processor' => 'griddler/emails#create'
|
49
|
+
```
|
57
50
|
|
58
|
-
|
59
|
-
--------
|
51
|
+
### Configuration Options
|
60
52
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
```ruby
|
66
|
-
class EmailProcessor
|
67
|
-
def self.process(email)
|
68
|
-
# all of your application-specific code here - creating models,
|
69
|
-
# processing reports, etc
|
70
|
-
end
|
71
|
-
end
|
72
|
-
```
|
73
|
-
|
74
|
-
The contents of the `email` object passed into your process method is an object
|
75
|
-
that responds to:
|
76
|
-
|
77
|
-
* `.to`
|
78
|
-
* `.from`
|
79
|
-
* `.cc`
|
80
|
-
* `.subject`
|
81
|
-
* `.body`
|
82
|
-
* `.raw_text`
|
83
|
-
* `.raw_html`
|
84
|
-
* `.raw_body`
|
85
|
-
* `.attachments`
|
86
|
-
* `.headers`
|
87
|
-
* `.raw_headers`
|
88
|
-
|
89
|
-
Each of those has some sensible defaults.
|
90
|
-
|
91
|
-
`.raw_body`, `.raw_headers`, and `.subject` will contain the obvious
|
92
|
-
values found in the email, the raw values from those fields.
|
93
|
-
|
94
|
-
`.body` will contain the full contents of the email body **unless** there is a
|
95
|
-
line in the email containing the string `-- Reply ABOVE THIS LINE --`. In that
|
96
|
-
case `.body` will contain everything before that line.
|
97
|
-
|
98
|
-
`.to` will contain an array of hashes. Each hash will have the following
|
99
|
-
information of each recipient:
|
100
|
-
|
101
|
-
* `token`: All the text before the email's "@". We've found that this is the
|
102
|
-
most often used portion of the email address and consider it to be the token
|
103
|
-
we'll key off of for interaction with our application.
|
104
|
-
|
105
|
-
* `host`: All the text after the email's "@". This is important to filter
|
106
|
-
the recipients sent to the application vs emails to other domains. More
|
107
|
-
info below on the Upgrading to 0.5.0 section.
|
108
|
-
|
109
|
-
* `email`: The email address of the recipient.
|
110
|
-
|
111
|
-
* `full`: The whole recipient field. E.g, `Some User <hello@example.com>`
|
112
|
-
* `name`: The name of the recipient. E.g, `Some User`
|
113
|
-
|
114
|
-
`.from` will default to the `email` value of a hash like `.to`, and can be
|
115
|
-
configured to return the full hash.
|
116
|
-
|
117
|
-
`.cc` behaves and can be configured like `.to`. If the adapter does not
|
118
|
-
pass along a `cc` key then the headers will be parsed.
|
119
|
-
|
120
|
-
`.attachments` will contain an array of attachments as multipart/form-data files
|
121
|
-
which can be passed off to attachment libraries like Carrierwave or Paperclip.
|
122
|
-
|
123
|
-
`.headers` will contain a hash of header names and values as parsed by the Mail
|
124
|
-
gem. Headers will only be parsed if the adapter supports a headers option.
|
125
|
-
|
126
|
-
Configuration Options
|
127
|
-
---------------------
|
128
|
-
|
129
|
-
An initializer can be created to control some of the options in Griddler. Defaults
|
130
|
-
are shown below with sample overrides following. In `config/initializers/griddler.rb`:
|
53
|
+
An initializer can be created to control some of the options in Griddler.
|
54
|
+
Defaults are shown below with sample overrides following. In
|
55
|
+
`config/initializers/griddler.rb`:
|
131
56
|
|
132
57
|
```ruby
|
133
58
|
Griddler.configure do |config|
|
134
|
-
config.processor_class = EmailProcessor #
|
135
|
-
config.processor_method = :process # :
|
59
|
+
config.processor_class = EmailProcessor # CommentViaEmail
|
60
|
+
config.processor_method = :process # :create_comment (A method on CommentViaEmail)
|
136
61
|
config.to = :hash # :full, :email, :token
|
137
62
|
config.cc = :email # :full, :hash, :token
|
138
63
|
config.from = :email # :full, :token, :hash
|
@@ -145,21 +70,63 @@ Griddler.configure do |config|
|
|
145
70
|
end
|
146
71
|
```
|
147
72
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
73
|
+
| Option | Meaning
|
74
|
+
| ------ | -------
|
75
|
+
| `processor_class` | The class Griddler will use to handle your incoming emails.
|
76
|
+
| `processor_method` | The method Griddler will call on the processor class when handling your incoming emails.
|
77
|
+
| `reply_delimiter` | The string searched for that will split your body.
|
78
|
+
| `email_service` | Tells Griddler which email service you are using. The supported email service options are `:sendgrid` (the default), `:cloudmailin` (expects multipart format), `:postmark` and `:mandrill`. You will also need to have an appropriate [adapter] gem included in your Gemfile.
|
79
|
+
| `to`, `config.cc` and `config.from` | The format of the returned value for that address in the email object. `:hash` will return all options within a -- (surprise!) -- hash.
|
80
|
+
|
81
|
+
By default Griddler will look for a class named `EmailProcessor` with a class
|
82
|
+
method named `process`, taking in one argument, a `Griddler::Email` instance
|
83
|
+
representing the incoming email. For example, in `./lib/email_processor.rb`:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class EmailProcessor
|
87
|
+
def self.process(email)
|
88
|
+
# all of your application-specific code here - creating models,
|
89
|
+
# processing reports, etc
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
Griddler::Email attributes
|
95
|
+
--------------------------
|
96
|
+
|
97
|
+
| Attribute | Description
|
98
|
+
| -------------- | -----------
|
99
|
+
| `#to` | An array of hashes containing recipient address information. See [Email Addresses](#email-addresses) for more information.
|
100
|
+
| `#from` | A hash containing the sender address information.
|
101
|
+
| `#cc` | An array of hashes containing cc email address information.
|
102
|
+
| `#subject` | The subject of the email message.
|
103
|
+
| `#body` | The full contents of the email body **unless** there is a line in the email containing the string `-- Reply ABOVE THIS LINE --`. In that case `.body` will contain everything before that line.
|
104
|
+
| `#raw_text` | The raw text part of the body.
|
105
|
+
| `#raw_html` | The raw html part of the body.
|
106
|
+
| `#raw_body` | The raw body information provided by the email service.
|
107
|
+
| `#attachments` | An array of `File` objects containing any attachments.
|
108
|
+
| `#headers` | A hash of headers parsed by `Mail::Header`.
|
109
|
+
| `#raw_headers` | The raw headers included in the message.
|
110
|
+
|
111
|
+
### Email Addresses
|
112
|
+
|
113
|
+
Gridder::Email provides email addresses as hashes. Each hash will have the following
|
114
|
+
information of each recipient:
|
115
|
+
|
116
|
+
| Key | Value
|
117
|
+
| --- | -----
|
118
|
+
| `:token` | All the text before the email's "@". We've found that this is the most often used portion of the email address and consider it to be the token we'll key off of for interaction with our application.
|
119
|
+
| `:host` | All the text after the email's "@". This is important to filter the recipients sent to the application vs emails to other domains. More info below on the Upgrading to 0.5.0 section.
|
120
|
+
| `:email` | The email address of the recipient.
|
121
|
+
| `:full` | The whole recipient field (e.g., `Some User <hello@example.com>`).
|
122
|
+
| `:name` | The name of the recipient (e.g., `Some User`).
|
156
123
|
|
157
124
|
Testing In Your App
|
158
125
|
-------------------
|
159
126
|
|
160
|
-
You may want to create a factory for when testing the integration of Griddler
|
161
|
-
your application. If you're using factory\_girl this can be accomplished
|
162
|
-
following sample factory
|
127
|
+
You may want to create a factory for when testing the integration of Griddler
|
128
|
+
into your application. If you're using factory\_girl this can be accomplished
|
129
|
+
with the following sample factory:
|
163
130
|
|
164
131
|
```ruby
|
165
132
|
factory :email, class: OpenStruct do
|
@@ -175,7 +142,7 @@ factory :email, class: OpenStruct do
|
|
175
142
|
ActionDispatch::Http::UploadedFile.new({
|
176
143
|
filename: 'img.png',
|
177
144
|
type: 'image/png',
|
178
|
-
tempfile: File.new("#{File.expand_path
|
145
|
+
tempfile: File.new("#{File.expand_path(File.dirname(__FILE__))}/fixtures/img.png")
|
179
146
|
})
|
180
147
|
]}
|
181
148
|
end
|
@@ -186,76 +153,49 @@ Bear in mind, if you plan on using the `:with_attachment` trait, that this
|
|
186
153
|
example assumes your factories are in `spec/factories.rb` and you have
|
187
154
|
an image file in `spec/fixtures/`.
|
188
155
|
|
189
|
-
To use it in your
|
156
|
+
To use it in your tests, build with `email = build(:email)`
|
190
157
|
or `email = build(:email, :with_attachment)`.
|
191
158
|
|
192
159
|
Adapters
|
193
160
|
--------
|
194
161
|
|
195
|
-
|
196
|
-
|
197
|
-
of your adapter returns a hash with these keys:
|
198
|
-
|
199
|
-
* `:to` The recipient field
|
200
|
-
* `:from` The sender field
|
201
|
-
* `:subject` Email subject
|
202
|
-
* `:text` The text body of the email
|
203
|
-
* `:html` The html body of the email, nil or empty string if not present
|
204
|
-
* `:attachments` (can be an empty array) Array of attachments to the email
|
205
|
-
* `:headers` (optional) The raw headers of the email
|
206
|
-
* `:charsets` (optional) A JSON string containing the character sets of the
|
207
|
-
fields extracted from the message
|
208
|
-
|
209
|
-
Upgrading to Griddler 0.5.0
|
210
|
-
---------------------------
|
211
|
-
|
212
|
-
Because of an issue with the way Griddler handled recipients in the `To` header,
|
213
|
-
a breaking change was introduced in Griddler 0.5.0 that requires a minor change
|
214
|
-
to `EmailProcessor` or `processor_class`.
|
215
|
-
|
216
|
-
Previously, a single address was returned from `Griddler::Email#to`. Moving
|
217
|
-
forward, this field will always be an array. Generally speaking, you will want
|
218
|
-
to do something like this to handle the change:
|
162
|
+
Depending on the service you want to use Griddler with, you'll need to add an
|
163
|
+
adapter gem in addition to `griddler`.
|
219
164
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
@from = email.from
|
225
|
-
@body = email.body
|
226
|
-
end
|
227
|
-
|
228
|
-
# after
|
229
|
-
def initialize(email)
|
230
|
-
@to = pick_meaningful_recipient(email.to)
|
231
|
-
@from = email.from
|
232
|
-
@body = email.body
|
233
|
-
end
|
165
|
+
| Service | Adapter
|
166
|
+
| ------- | -------
|
167
|
+
| sendgrid | [griddler-sendgrid]
|
168
|
+
| mandrill | [griddler-mandrill]
|
234
169
|
|
235
|
-
|
170
|
+
[griddler-sendgrid]: https://github.com/thoughtbot/griddler-sendgrid
|
171
|
+
[griddler-mandrill]: https://github.com/wingrunr21/griddler-mandrill
|
236
172
|
|
237
|
-
|
238
|
-
|
239
|
-
end
|
240
|
-
```
|
173
|
+
Writing an Adapter
|
174
|
+
------------------
|
241
175
|
|
242
|
-
|
243
|
-
|
176
|
+
Griddler can theoretically work with any email => POST service. In order to work
|
177
|
+
correctly, adapters need to have their POST parameters restructured.
|
244
178
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
179
|
+
`Griddler::Email` expects certain parameters to be in place for proper parsing
|
180
|
+
to occur. When writing an adapter, ensure that the `normalized_params` method of
|
181
|
+
your adapter returns a hash with these keys:
|
182
|
+
|
183
|
+
| Parameter | Contents
|
184
|
+
| --------- | --------
|
185
|
+
| `:to` | The recipient field
|
186
|
+
| `:from` | The sender field
|
187
|
+
| `:subject` | Email subject
|
188
|
+
| `:text` | The text body of the email
|
189
|
+
| `:html` | The html body of the email, nil or empty string if not present
|
190
|
+
| `:attachments` | Array of attachments to the email. Can be an empty array.
|
191
|
+
| `:headers` | The raw headers of the email. **Optional**.
|
192
|
+
| `:charsets` | A JSON string containing the character sets of the fields extracted from the message. **Optional**.
|
193
|
+
|
194
|
+
All keys are required unless otherwise stated.
|
195
|
+
|
196
|
+
Adapters should be provided as gems. If you write an adapter, let us know and we
|
197
|
+
will add it to this README. See [griddler-sendgrid] for an example
|
198
|
+
implementation.
|
259
199
|
|
260
200
|
More Information
|
261
201
|
----------------
|
@@ -269,7 +209,7 @@ More Information
|
|
269
209
|
* [Mandrill](http://mandrill.com)
|
270
210
|
* [Mandrill Docs](http://help.mandrill.com/forums/21092258-Inbound-Email-Processing)
|
271
211
|
* [Mailgun](http://mailgun.com)
|
272
|
-
* [Mailgun Docs](http://documentation.mailgun.com/
|
212
|
+
* [Mailgun Docs](http://documentation.mailgun.com/user\_manual.html#receiving-forwarding-and-storing-messages)
|
273
213
|
|
274
214
|
Credits
|
275
215
|
-------
|
@@ -279,13 +219,8 @@ Griddler was written by Caleb Thompson and Joel Oliveira.
|
|
279
219
|
Large portions of the codebase were extracted from thoughtbot's
|
280
220
|
[Trajectory](http://www.apptrajectory.com).
|
281
221
|
|
222
|
+
Thanks to our [contributors](https://github.com/thoughtbot/griddler/contributors)!
|
223
|
+
|
282
224
|

|
283
225
|
|
284
226
|
The names and logos for thoughtbot are trademarks of thoughtbot, inc.
|
285
|
-
|
286
|
-
License
|
287
|
-
-------
|
288
|
-
|
289
|
-
Griddler is Copyright © 2014 Caleb Thompson, Joel Oliveira and thoughtbot. It is
|
290
|
-
free software, and may be redistributed under the terms specified in the LICENSE
|
291
|
-
file.
|
data/Rakefile
CHANGED
data/lib/griddler.rb
CHANGED
@@ -6,11 +6,14 @@ require 'griddler/email'
|
|
6
6
|
require 'griddler/email_parser'
|
7
7
|
require 'griddler/configuration'
|
8
8
|
require 'griddler/route_extensions'
|
9
|
-
require 'griddler/
|
9
|
+
require 'griddler/adapter_registry'
|
10
10
|
require 'griddler/adapters/cloudmailin_adapter'
|
11
11
|
require 'griddler/adapters/postmark_adapter'
|
12
|
-
require 'griddler/adapters/mandrill_adapter'
|
13
12
|
require 'griddler/adapters/mailgun_adapter'
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
require 'griddler/sendgrid'
|
15
|
+
require 'griddler/mandrill'
|
16
|
+
|
17
|
+
Griddler.adapter_registry.register(:cloudmailin, Griddler::Adapters::CloudmailinAdapter)
|
18
|
+
Griddler.adapter_registry.register(:postmark, Griddler::Adapters::PostmarkAdapter)
|
19
|
+
Griddler.adapter_registry.register(:mailgun, Griddler::Adapters::MailgunAdapter)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Griddler
|
2
|
+
class AdapterRegistry
|
3
|
+
DEFAULT_ADAPTER = :sendgrid
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@registry = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def register(adapter_name, adapter_class)
|
10
|
+
if adapter_name == DEFAULT_ADAPTER
|
11
|
+
@registry[:default] = adapter_class
|
12
|
+
end
|
13
|
+
@registry[adapter_name] = adapter_class
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](adapter_name)
|
17
|
+
@registry[adapter_name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch(key, &block)
|
21
|
+
@registry.fetch(key, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.adapter_registry
|
26
|
+
@adapter_registry ||= AdapterRegistry.new
|
27
|
+
end
|
28
|
+
end
|
@@ -12,7 +12,7 @@ module Griddler
|
|
12
12
|
|
13
13
|
def normalize_params
|
14
14
|
params.merge(
|
15
|
-
to:
|
15
|
+
to: tos,
|
16
16
|
cc: ccs,
|
17
17
|
text: params['body-plain'],
|
18
18
|
html: params['body-html'],
|
@@ -25,24 +25,32 @@ module Griddler
|
|
25
25
|
|
26
26
|
attr_reader :params
|
27
27
|
|
28
|
-
def
|
29
|
-
|
28
|
+
def tos
|
29
|
+
to = param_or_header(:To)
|
30
|
+
to = params[:recipient] unless to
|
31
|
+
to.split(',').map(&:strip)
|
30
32
|
end
|
31
33
|
|
32
34
|
def ccs
|
33
|
-
cc =
|
34
|
-
params[:Cc]
|
35
|
-
else
|
36
|
-
extract_header_cc
|
37
|
-
end
|
35
|
+
cc = param_or_header(:Cc)
|
38
36
|
cc.split(',').map(&:strip)
|
39
37
|
end
|
40
38
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
def extract_header(key)
|
40
|
+
return nil unless params['message-headers'].present?
|
41
|
+
|
42
|
+
headers = params['message-headers'].select do |h|
|
43
|
+
h.first.to_s == key.to_s
|
44
|
+
end
|
45
|
+
headers.flatten.last
|
46
|
+
end
|
47
|
+
|
48
|
+
def param_or_header(key)
|
49
|
+
if params[key].present?
|
50
|
+
params[key]
|
51
|
+
else
|
52
|
+
extract_header(key)
|
53
|
+
end
|
46
54
|
end
|
47
55
|
|
48
56
|
def attachment_files
|
@@ -42,7 +42,18 @@ module Griddler
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def processor_class
|
45
|
-
@processor_class ||=
|
45
|
+
@processor_class ||=
|
46
|
+
begin
|
47
|
+
if Kernel.const_defined?(:EmailProcessor)
|
48
|
+
EmailProcessor
|
49
|
+
else
|
50
|
+
raise NameError.new(<<-ERROR.strip_heredoc, 'EmailProcessor')
|
51
|
+
To use Griddler, you must either define `EmailProcessor` or configure a
|
52
|
+
different processor. See https://github.com/thoughtbot/griddler#defaults for
|
53
|
+
more information.
|
54
|
+
ERROR
|
55
|
+
end
|
56
|
+
end
|
46
57
|
end
|
47
58
|
|
48
59
|
def processor_method
|
@@ -54,27 +65,11 @@ module Griddler
|
|
54
65
|
end
|
55
66
|
|
56
67
|
def email_service
|
57
|
-
@email_service_adapter ||=
|
68
|
+
@email_service_adapter ||= Griddler.adapter_registry[:sendgrid]
|
58
69
|
end
|
59
70
|
|
60
71
|
def email_service=(new_email_service)
|
61
|
-
|
62
|
-
new_email_service = :sendgrid
|
63
|
-
end
|
64
|
-
|
65
|
-
@email_service_adapter = adapter_class.fetch(new_email_service) { raise Griddler::Errors::EmailServiceAdapterNotFound }
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def adapter_class
|
71
|
-
{
|
72
|
-
sendgrid: Griddler::Adapters::SendgridAdapter,
|
73
|
-
cloudmailin: Griddler::Adapters::CloudmailinAdapter,
|
74
|
-
postmark: Griddler::Adapters::PostmarkAdapter,
|
75
|
-
mandrill: Griddler::Adapters::MandrillAdapter,
|
76
|
-
mailgun: Griddler::Adapters::MailgunAdapter
|
77
|
-
}
|
72
|
+
@email_service_adapter = Griddler.adapter_registry.fetch(new_email_service) { raise Griddler::Errors::EmailServiceAdapterNotFound }
|
78
73
|
end
|
79
74
|
end
|
80
75
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'action_dispatch'
|
2
|
+
|
3
|
+
module Griddler::Testing
|
4
|
+
def upload_1
|
5
|
+
@upload_1 ||= UploadedImage.new('photo1.jpg').file
|
6
|
+
end
|
7
|
+
|
8
|
+
def upload_2
|
9
|
+
@upload_2 ||= UploadedImage.new('photo2.jpg').file
|
10
|
+
end
|
11
|
+
|
12
|
+
def normalize_params(params)
|
13
|
+
Griddler::Sendgrid::Adapter.normalize_params(params)
|
14
|
+
end
|
15
|
+
|
16
|
+
class UploadedImage
|
17
|
+
def initialize(name)
|
18
|
+
@name = name
|
19
|
+
end
|
20
|
+
|
21
|
+
def file
|
22
|
+
ActionDispatch::Http::UploadedFile.new({
|
23
|
+
filename: @name,
|
24
|
+
type: 'image/jpeg',
|
25
|
+
tempfile: fixture_file
|
26
|
+
})
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def fixture_file
|
32
|
+
cwd = File.expand_path File.dirname(__FILE__)
|
33
|
+
File.new(File.join(cwd, '..', '..', 'spec', 'fixtures', @name))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
shared_examples_for 'Griddler adapter' do |adapter, service_params|
|
39
|
+
it 'adapts params to expected values' do
|
40
|
+
Griddler.configuration.email_service = adapter
|
41
|
+
|
42
|
+
normalized_params = Griddler.configuration.email_service.normalize_params(service_params)
|
43
|
+
|
44
|
+
Array.wrap(normalized_params).each do |params|
|
45
|
+
email = Griddler::Email.new(params)
|
46
|
+
|
47
|
+
email.to.should eq([{
|
48
|
+
token: 'hi',
|
49
|
+
host: 'example.com',
|
50
|
+
full: 'Hello World <hi@example.com>',
|
51
|
+
email: 'hi@example.com',
|
52
|
+
name: 'Hello World',
|
53
|
+
}])
|
54
|
+
email.cc.should eq ['emily@example.com']
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/griddler/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: griddler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.pre.alpha.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Caleb Thompson
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-
|
14
|
+
date: 2014-06-17 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rails
|
@@ -97,21 +97,19 @@ files:
|
|
97
97
|
- Rakefile
|
98
98
|
- app/controllers/griddler/emails_controller.rb
|
99
99
|
- config/initializers/griddler.rb
|
100
|
-
- config/routes.rb
|
101
100
|
- lib/griddler.rb
|
101
|
+
- lib/griddler/adapter_registry.rb
|
102
102
|
- lib/griddler/adapters/cloudmailin_adapter.rb
|
103
103
|
- lib/griddler/adapters/mailgun_adapter.rb
|
104
|
-
- lib/griddler/adapters/mandrill_adapter.rb
|
105
104
|
- lib/griddler/adapters/postmark_adapter.rb
|
106
|
-
- lib/griddler/adapters/sendgrid_adapter.rb
|
107
105
|
- lib/griddler/configuration.rb
|
108
106
|
- lib/griddler/email.rb
|
109
107
|
- lib/griddler/email_parser.rb
|
110
108
|
- lib/griddler/engine.rb
|
111
109
|
- lib/griddler/errors.rb
|
112
110
|
- lib/griddler/route_extensions.rb
|
111
|
+
- lib/griddler/testing.rb
|
113
112
|
- lib/griddler/version.rb
|
114
|
-
- lib/tasks/griddler_tasks.rake
|
115
113
|
homepage: http://thoughtbot.com
|
116
114
|
licenses: []
|
117
115
|
metadata: {}
|
@@ -130,9 +128,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
128
|
version: 1.9.2
|
131
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
130
|
requirements:
|
133
|
-
- - "
|
131
|
+
- - ">"
|
134
132
|
- !ruby/object:Gem::Version
|
135
|
-
version:
|
133
|
+
version: 1.3.1
|
136
134
|
requirements: []
|
137
135
|
rubyforge_project:
|
138
136
|
rubygems_version: 2.2.2
|
data/config/routes.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
module Griddler
|
2
|
-
module Adapters
|
3
|
-
class MandrillAdapter
|
4
|
-
def initialize(params)
|
5
|
-
@params = params
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.normalize_params(params)
|
9
|
-
adapter = new(params)
|
10
|
-
adapter.normalize_params
|
11
|
-
end
|
12
|
-
|
13
|
-
def normalize_params
|
14
|
-
events.map do |event|
|
15
|
-
{
|
16
|
-
to: recipients(:to, event),
|
17
|
-
cc: recipients(:cc, event),
|
18
|
-
from: full_email([ event[:from_email], event[:from_name] ]),
|
19
|
-
subject: event[:subject],
|
20
|
-
text: event.fetch(:text, ''),
|
21
|
-
html: event.fetch(:html, ''),
|
22
|
-
raw_body: event[:raw_msg],
|
23
|
-
attachments: attachment_files(event)
|
24
|
-
}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
attr_reader :params
|
31
|
-
|
32
|
-
def events
|
33
|
-
@events ||= ActiveSupport::JSON.decode(params[:mandrill_events]).map do |event|
|
34
|
-
event['msg'].with_indifferent_access
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def recipients(field, event)
|
39
|
-
Array.wrap(event[field]).map { |recipient| full_email(recipient) }
|
40
|
-
end
|
41
|
-
|
42
|
-
def full_email(contact_info)
|
43
|
-
email = contact_info[0]
|
44
|
-
if contact_info[1]
|
45
|
-
"#{contact_info[1]} <#{email}>"
|
46
|
-
else
|
47
|
-
email
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def attachment_files(event)
|
52
|
-
attachments = event[:attachments] || Array.new
|
53
|
-
attachments.map do |key, attachment|
|
54
|
-
ActionDispatch::Http::UploadedFile.new({
|
55
|
-
filename: attachment[:name],
|
56
|
-
type: attachment[:type],
|
57
|
-
tempfile: create_tempfile(attachment)
|
58
|
-
})
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def create_tempfile(attachment)
|
63
|
-
filename = attachment[:name]
|
64
|
-
tempfile = Tempfile.new(filename, Dir::tmpdir, encoding: 'ascii-8bit')
|
65
|
-
tempfile.write(Base64.decode64(attachment[:content]))
|
66
|
-
tempfile.rewind
|
67
|
-
tempfile
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module Griddler
|
2
|
-
module Adapters
|
3
|
-
class SendgridAdapter
|
4
|
-
def initialize(params)
|
5
|
-
@params = params
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.normalize_params(params)
|
9
|
-
adapter = new(params)
|
10
|
-
adapter.normalize_params
|
11
|
-
end
|
12
|
-
|
13
|
-
def normalize_params
|
14
|
-
params.merge(
|
15
|
-
to: recipients(:to),
|
16
|
-
cc: recipients(:cc),
|
17
|
-
attachments: attachment_files,
|
18
|
-
)
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
attr_reader :params
|
24
|
-
|
25
|
-
def recipients(key)
|
26
|
-
( params[key] || '' ).split(',')
|
27
|
-
end
|
28
|
-
|
29
|
-
def attachment_files
|
30
|
-
params.delete('attachment-info')
|
31
|
-
attachment_count = params[:attachments].to_i
|
32
|
-
|
33
|
-
attachment_count.times.map do |index|
|
34
|
-
params.delete("attachment#{index + 1}".to_sym)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|