mailclerk 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +85 -14
- data/lib/client.rb +62 -0
- data/lib/errors.rb +15 -0
- data/lib/mailclerk.rb +14 -66
- data/lib/outbox.rb +93 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0411122cd662e0f7e37bfeb21853e818ecc8ec6e91fa54ad21ead1fad9ac998
|
4
|
+
data.tar.gz: 4b62af5e3f1b1e4e252c31f97d488cec0ee2e59d2023761c2b69fb83c7afa8d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47d25d797fd25b415c8c251e464539df9544dd9863716ad37cc2b6bae843fe61f49c39f6853ff74d994239e920895db290282b84f64016672e590129bf443d37
|
7
|
+
data.tar.gz: 4eeea9f9278a145646f3fad23e0672c191c7b6b53ce148cf561dd7a76f268bfa729feec32bf5a0871270bf10c4f39d7aaac5896d237e0c202fb89aed24499186
|
data/README.md
CHANGED
@@ -16,8 +16,9 @@ Mailclerk helps anyone on your team design great emails, improve their performan
|
|
16
16
|
- [Setup](#setup)
|
17
17
|
- [API Key & URL](#api-key--url)
|
18
18
|
- [Usage](#usage)
|
19
|
+
- [Usage in Test Environments](#usage-in-test-environments)
|
19
20
|
- [Varying API Keys](#varying-api-keys)
|
20
|
-
- [Tests](#tests)
|
21
|
+
- [Gem Tests](#gem-tests)
|
21
22
|
- [Versioning](#versioning)
|
22
23
|
- [Code of Conduct](#code-of-conduct)
|
23
24
|
- [Contributions](#contributions)
|
@@ -28,7 +29,7 @@ Mailclerk helps anyone on your team design great emails, improve their performan
|
|
28
29
|
|
29
30
|
## Requirements
|
30
31
|
|
31
|
-
1. [Ruby 2.
|
32
|
+
1. [Ruby 2.4.0](https://www.ruby-lang.org)
|
32
33
|
|
33
34
|
## Setup
|
34
35
|
|
@@ -50,9 +51,9 @@ To set the Mailclerk API Key (begins with `mc_`), you can provide it as an
|
|
50
51
|
environmental variable: `MAILCLERK_API_KEY`. Alternatively, you can
|
51
52
|
set it directly on the Mailclerk module:
|
52
53
|
|
53
|
-
```
|
54
|
+
```ruby
|
54
55
|
# config/initializers/mailclerk.rb
|
55
|
-
Mailclerk.api_key = "
|
56
|
+
Mailclerk.api_key = "mc_live_yourprivatekey"
|
56
57
|
```
|
57
58
|
|
58
59
|
_If you are using version control like git, we strongly recommend storing your
|
@@ -67,44 +68,114 @@ You'll need an active account and at least one template (in the example `welcome
|
|
67
68
|
|
68
69
|
To send an email to "alice@example.com":
|
69
70
|
|
70
|
-
```
|
71
|
+
```ruby
|
71
72
|
Mailclerk.deliver("welcome-email", "alice@example.com")
|
73
|
+
Mailclerk.deliver("welcome-email", "Alice Adams <alice@example.com>")
|
74
|
+
Mailclerk.deliver("welcome-email", { name: "Alice Adams", address: "<alice@example.com>" })
|
72
75
|
```
|
73
76
|
|
74
77
|
If the template has any dynamic data, you can include it in the third parameter
|
75
78
|
as a hash:
|
76
79
|
|
77
|
-
```
|
80
|
+
```ruby
|
78
81
|
Mailclerk.deliver("welcome-email", "alice@example.com", { name: "Alice" })
|
79
82
|
```
|
80
83
|
|
81
84
|
See [Mailclerk documentation](https://dashboard.mailclerk.app/docs) for more details.
|
82
85
|
|
86
|
+
## Usage in Test Environments
|
87
|
+
|
88
|
+
Your Mailclerk environment has two API keys: a production key (beginning with `mc_live`)
|
89
|
+
and a test key (beginning with `mc_test`). If you use the test key, emails will
|
90
|
+
not be delivered, but will show up in the logs on your Mailclerk account and can be
|
91
|
+
previewed there. This replaces tools like [Letter Opener](https://github.com/ryanb/letter_opener) for previewing emails in development.
|
92
|
+
|
93
|
+
To avoid cluttering up your Mailclerk test logs with sends triggered by your
|
94
|
+
automated test suite, call `Mailclerk.outbox.enable` in the file that
|
95
|
+
configures your tests. For example, in Rspec with Rails, add:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
# spec/rails_helper.rb
|
99
|
+
Mailclerk.outbox.enable
|
100
|
+
```
|
101
|
+
|
102
|
+
This will also enable utility methods which you can use to write tests that check
|
103
|
+
emails are sent with the correct data:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
# Number of emails "sent"
|
107
|
+
Mailclerk.outbox.length
|
108
|
+
|
109
|
+
# Returns all emails of matching a template or email recipient. See method
|
110
|
+
Mailclerk.outbox.filter(template: "welcome-email")
|
111
|
+
Mailclerk.outbox.filter(recipient_email: "felix@example.com")
|
112
|
+
|
113
|
+
# Returns the most recent email (instance of Mailclerk::TestEmail):
|
114
|
+
email = Mailclerk.outbox.last
|
115
|
+
email.template # "welcome-email"
|
116
|
+
email.recipient_email # "felix@example.com"
|
117
|
+
email.subject # "Welcome to Acme Felix"
|
118
|
+
email.html # "<html><body>..."
|
119
|
+
```
|
120
|
+
|
121
|
+
In between test cases, you should clear the stored emails by calling `Mailclerk.outbox.reset`.
|
122
|
+
|
123
|
+
For example, in Rspec with Rails:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
# spec/rails_helper.rb
|
127
|
+
RSpec.configure do |config|
|
128
|
+
config.before(:each) do
|
129
|
+
Mailclerk.outbox.reset
|
130
|
+
end
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
`Mailclerk::OutboxEmail` has the following attributes:
|
135
|
+
|
136
|
+
| Attribute | Description |
|
137
|
+
| ----------------- | -------------------------------------------------------------------------- |
|
138
|
+
| `template` | Slug of the template sent (1st argument to `Mailclerk.deliver`) |
|
139
|
+
| `recipient` | Hash representing the send recipient (2nd argument to `Mailclerk.deliver`) |
|
140
|
+
| `recipient_email` | Email of the send recipient |
|
141
|
+
| `recipient_name` | Name of the send recipient (nil if not specified) |
|
142
|
+
| `data` | Dynamic data for the send (3rd argument to `Mailclerk.deliver`) |
|
143
|
+
| `options` | Options specified for the send (4th argument to `Mailclerk.deliver`) |
|
144
|
+
| `from` | From Mailclerk: Hash with `name` and `address` of the sender |
|
145
|
+
| `subject` | From Mailclerk: Text of the send's subject line |
|
146
|
+
| `preheader` | From Mailclerk: Text of the send's preheader |
|
147
|
+
| `html` | From Mailclerk: Rendered body HTML for the send |
|
148
|
+
| `text` | From Mailclerk: Rendered plaintext version of the send |
|
149
|
+
| `headers` | From Mailclerk: Extra email headers (e.g. `reply-to`) |
|
150
|
+
|
151
|
+
See the [Mailclerk testing documentation](https://dashboard.mailclerk.app/docs#testing)
|
152
|
+
for more details.
|
153
|
+
|
83
154
|
## Varying API Keys
|
84
155
|
|
85
156
|
If you need to use multiple API keys, you can also initialize `Mailclerk::Client`
|
86
157
|
instances with different keys. This:
|
87
158
|
|
88
|
-
```
|
89
|
-
mc_client = Mailclerk.new("
|
159
|
+
```ruby
|
160
|
+
mc_client = Mailclerk.new("mc_live_yourprivatekey")
|
90
161
|
mc_client.deliver("welcome-email", "bob@example.com")
|
91
162
|
```
|
92
163
|
|
93
164
|
Is equivalent to this:
|
94
165
|
|
95
|
-
```
|
96
|
-
Mailclerk.api_key = "
|
166
|
+
```ruby
|
167
|
+
Mailclerk.api_key = "mc_live_yourprivatekey"
|
97
168
|
Mailclerk.deliver("welcome-email", "bob@example.com")
|
98
169
|
```
|
99
170
|
|
100
|
-
## Tests
|
101
|
-
|
102
|
-
Tests aren't currently implemented. When they are, to test, run:
|
171
|
+
## Gem Tests
|
103
172
|
|
104
173
|
```
|
105
|
-
bundle exec
|
174
|
+
bundle exec rspec
|
106
175
|
```
|
107
176
|
|
177
|
+
Requires values in .env file as well
|
178
|
+
|
108
179
|
## Versioning
|
109
180
|
|
110
181
|
Read [Semantic Versioning](https://semver.org) for details. Briefly, it means:
|
data/lib/client.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Mailclerk
|
2
|
+
class Client
|
3
|
+
def initialize(api_key, api_url=nil)
|
4
|
+
@api_key = api_key
|
5
|
+
@api_url = api_url || ENV['MAILCLERK_API_URL'] || DEFAULT_API_URL
|
6
|
+
|
7
|
+
if @api_key.nil?
|
8
|
+
raise MailclerkError.new(
|
9
|
+
"No Mailclerk API Key provided. Set `Mailclerk.api_key`"
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
if @api_url.nil? || @api_url.empty?
|
14
|
+
raise MailclerkError.new("Mailclerk API URL empty")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def deliver(template, recipient, data={}, options={})
|
19
|
+
conn = Faraday.new(url: @api_url)
|
20
|
+
conn.basic_auth(@api_key, '')
|
21
|
+
|
22
|
+
if Mailclerk.outbox_enabled?
|
23
|
+
options = options.merge(
|
24
|
+
"local_outbox" => true
|
25
|
+
)
|
26
|
+
options.delete(:local_outbox)
|
27
|
+
end
|
28
|
+
|
29
|
+
params = {
|
30
|
+
'template' => template,
|
31
|
+
'recipient' => recipient,
|
32
|
+
'data' => data,
|
33
|
+
'options' => options
|
34
|
+
}
|
35
|
+
|
36
|
+
response = conn.post('deliver', params.to_json, {
|
37
|
+
'Content-Type' => 'application/json',
|
38
|
+
'X-Client-Version' => Identity.version_label
|
39
|
+
})
|
40
|
+
|
41
|
+
if response.status >= 400
|
42
|
+
begin
|
43
|
+
message = JSON.parse(response.body)["message"] || "Unknown"
|
44
|
+
description = "Mailclerk API Error: #{ message }"
|
45
|
+
rescue JSON::ParserError
|
46
|
+
description = "Mailclerk API Unknown Error"
|
47
|
+
end
|
48
|
+
|
49
|
+
raise MailclerkAPIError.new(
|
50
|
+
description, response.status, response
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
if Mailclerk.outbox_enabled?
|
55
|
+
params["options"].delete("local_outbox")
|
56
|
+
Mailclerk.outbox.add_send(params, JSON.parse(response.body)["delivery"])
|
57
|
+
end
|
58
|
+
|
59
|
+
return response
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/errors.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Mailclerk
|
2
|
+
class MailclerkError < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class MailclerkAPIError < MailclerkError
|
6
|
+
attr_accessor :http_status
|
7
|
+
attr_accessor :http_response
|
8
|
+
|
9
|
+
def initialize(description, http_status=nil, http_response=nil)
|
10
|
+
super(description)
|
11
|
+
self.http_status = http_status
|
12
|
+
self.http_response = http_response
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/mailclerk.rb
CHANGED
@@ -3,6 +3,10 @@
|
|
3
3
|
require 'faraday'
|
4
4
|
require 'json'
|
5
5
|
|
6
|
+
require "client"
|
7
|
+
require "errors"
|
8
|
+
require "outbox"
|
9
|
+
|
6
10
|
module Mailclerk
|
7
11
|
DEFAULT_API_URL = "https://api.mailclerk.app"
|
8
12
|
|
@@ -22,7 +26,7 @@ module Mailclerk
|
|
22
26
|
end
|
23
27
|
|
24
28
|
def self.version
|
25
|
-
"1.0
|
29
|
+
"1.1.0"
|
26
30
|
end
|
27
31
|
|
28
32
|
def self.version_label
|
@@ -30,75 +34,19 @@ module Mailclerk
|
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
|
-
class MailclerkError < StandardError
|
34
|
-
end
|
35
|
-
|
36
|
-
class MailclerkAPIError < MailclerkError
|
37
|
-
attr_accessor :http_status
|
38
|
-
attr_accessor :http_response
|
39
|
-
|
40
|
-
def initialize(description, http_status=nil, http_response=nil)
|
41
|
-
super(description)
|
42
|
-
self.http_status = http_status
|
43
|
-
self.http_response = http_response
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
class Client
|
49
|
-
def initialize(api_key, api_url=nil)
|
50
|
-
@api_key = api_key
|
51
|
-
@api_url = api_url || ENV['MAILCLERK_API_URL'] || DEFAULT_API_URL
|
52
|
-
|
53
|
-
if @api_key.nil?
|
54
|
-
raise MailclerkError.new(
|
55
|
-
"No Mailclerk API Key provided. Set `Mailclerk.api_key`"
|
56
|
-
)
|
57
|
-
end
|
58
|
-
|
59
|
-
if @api_url.nil? || @api_url.empty?
|
60
|
-
raise MailclerkError.new("Mailclerk API URL empty")
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def deliver(template, recipient, data={}, options={})
|
65
|
-
conn = Faraday.new(url: @api_url)
|
66
|
-
conn.basic_auth(@api_key, '')
|
67
|
-
|
68
|
-
response = conn.post('deliver', {
|
69
|
-
'template' => template,
|
70
|
-
'recipient' => recipient,
|
71
|
-
'data' => data,
|
72
|
-
'options' => options
|
73
|
-
}.to_json, {
|
74
|
-
'Content-Type' => 'application/json',
|
75
|
-
'X-Client-Version' => Identity.version_label
|
76
|
-
})
|
77
|
-
|
78
|
-
if response.status >= 400
|
79
|
-
begin
|
80
|
-
message = JSON.parse(response.body)["message"] || "Unknown"
|
81
|
-
description = "Mailclerk API Error: #{ message }"
|
82
|
-
rescue JSON::ParserError
|
83
|
-
description = "Mailclerk API Unknown Error"
|
84
|
-
end
|
85
|
-
|
86
|
-
raise MailclerkAPIError.new(
|
87
|
-
description, response.status, response
|
88
|
-
)
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
return response
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
37
|
# Syntax intended to emulate ActionMailer
|
97
38
|
def self.deliver(*args)
|
98
39
|
api_key = self.api_key || ENV['MAILCLERK_API_KEY']
|
99
40
|
|
100
|
-
client = Client.new(api_key, self.api_url)
|
41
|
+
client = Mailclerk::Client.new(api_key, self.api_url)
|
101
42
|
return client.deliver(*args)
|
102
43
|
end
|
44
|
+
|
45
|
+
def self.outbox
|
46
|
+
@outbox ||= Mailclerk::Outbox.new
|
47
|
+
end
|
103
48
|
|
104
|
-
|
49
|
+
def self.outbox_enabled?
|
50
|
+
!!(@outbox && @outbox.enabled)
|
51
|
+
end
|
52
|
+
end
|
data/lib/outbox.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
module Mailclerk
|
2
|
+
class Outbox < Array
|
3
|
+
attr_accessor :enabled
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
self.enabled = false
|
7
|
+
end
|
8
|
+
|
9
|
+
def enable
|
10
|
+
self.enabled = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset
|
14
|
+
self.clear
|
15
|
+
end
|
16
|
+
|
17
|
+
# Not just an alias for 'select'
|
18
|
+
def filter(query)
|
19
|
+
puts query
|
20
|
+
self.select do |email|
|
21
|
+
query.all? do |key, value|
|
22
|
+
puts email[key]
|
23
|
+
email[key] == value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_send(request, response)
|
29
|
+
email = OutboxEmail.new(
|
30
|
+
OutboxEmail.recursive_init(
|
31
|
+
response.merge(request)
|
32
|
+
)
|
33
|
+
)
|
34
|
+
self << email
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class OutboxEmail < OpenStruct
|
39
|
+
|
40
|
+
def self.recursive_init(data)
|
41
|
+
|
42
|
+
data.each do |key, val|
|
43
|
+
if val.is_a?(Hash)
|
44
|
+
data[key] = self.recursive_init(val)
|
45
|
+
else
|
46
|
+
data[key] = val
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
return OpenStruct.new(data)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Custom getters
|
54
|
+
|
55
|
+
def recipient_email
|
56
|
+
return parse_recipient[:address]
|
57
|
+
end
|
58
|
+
|
59
|
+
def recipient_name
|
60
|
+
return parse_recipient[:name]
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def parse_recipient
|
66
|
+
return {} unless self.recipient
|
67
|
+
|
68
|
+
if self.recipient.is_a?(OpenStruct)
|
69
|
+
return self.recipient
|
70
|
+
end
|
71
|
+
|
72
|
+
text = self.recipient.strip
|
73
|
+
|
74
|
+
if text =~ /^[^<]+<[^<]+>$/
|
75
|
+
parts = text.split("<", 2);
|
76
|
+
|
77
|
+
name = parts[0].strip.gsub('"', "")
|
78
|
+
|
79
|
+
address = parts[1].strip.gsub(">", "")
|
80
|
+
|
81
|
+
return {
|
82
|
+
name: name,
|
83
|
+
address: address
|
84
|
+
}
|
85
|
+
else
|
86
|
+
return {
|
87
|
+
name: nil,
|
88
|
+
address: text
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mailclerk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Noah Litvin
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-03-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -109,6 +109,20 @@ dependencies:
|
|
109
109
|
- - "~>"
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '3.9'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: dotenv
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
112
126
|
description:
|
113
127
|
email:
|
114
128
|
- developers@mailclerk.app
|
@@ -120,7 +134,10 @@ extra_rdoc_files:
|
|
120
134
|
files:
|
121
135
|
- LICENSE.md
|
122
136
|
- README.md
|
137
|
+
- lib/client.rb
|
138
|
+
- lib/errors.rb
|
123
139
|
- lib/mailclerk.rb
|
140
|
+
- lib/outbox.rb
|
124
141
|
homepage: https://github.com/mailclerk/mailclerk-ruby
|
125
142
|
licenses:
|
126
143
|
- MIT
|