griddler 0.6.4 → 1.0.0.pre.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![thoughtbot](http://thoughtbot.com/images/tm/logo.png)
|
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
|