dotmailer 0.0.3 → 0.0.4
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 +6 -14
- data/.gitignore +0 -1
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile.lock +58 -0
- data/README.md +75 -2
- data/Rakefile +9 -0
- data/dotmailer.gemspec +1 -0
- data/lib/dot_mailer.rb +6 -1
- data/lib/dot_mailer/account.rb +22 -2
- data/lib/dot_mailer/campaign.rb +95 -0
- data/lib/dot_mailer/campaign_summary.rb +77 -0
- data/lib/dot_mailer/client.rb +7 -0
- data/lib/dot_mailer/contact.rb +4 -0
- data/lib/dot_mailer/contact_import.rb +21 -3
- data/lib/dot_mailer/exceptions.rb +6 -0
- data/lib/dot_mailer/from_address.rb +36 -0
- data/lib/dot_mailer/segment.rb +36 -0
- data/lib/dot_mailer/version.rb +1 -1
- data/spec/dot_mailer/account_spec.rb +57 -8
- data/spec/dot_mailer/campaign_spec.rb +206 -0
- data/spec/dot_mailer/campaign_summary_spec.rb +20 -0
- data/spec/dot_mailer/client_spec.rb +15 -1
- data/spec/dot_mailer/contact_import_spec.rb +47 -6
- data/spec/dot_mailer/contact_spec.rb +9 -1
- data/spec/dot_mailer/from_address_spec.rb +25 -0
- data/spec/dot_mailer/segment_spec.rb +80 -0
- data/spec/dot_mailer/suppression_spec.rb +2 -2
- data/spec/spec_helper.rb +2 -0
- metadata +38 -13
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
ODAxZjliNTc4MmFjZTE4ZDg0ZmZlYTM0NmIxYjQ4ZTI4ZWVhOWI0YjNiNGE5
|
10
|
-
MzZiNGU3OTY0ZjkwY2M0NDQzN2RlODA1NGU1NDdhNWNjOTBiYzVhMmQ2MDZi
|
11
|
-
MmQxZmI5YzFjMDlkNGU4NTM2NjFlMzU1MzkyZDdhZGFmZjNlNTE=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
NWZiOTcxNmQ4ZDgyNjJhOGExMzFjMTFjNmEwMTExOTZjZTc4NDUxZmVlODYz
|
14
|
-
ZTA5ZDk2Y2FmMzBlZjk3MDY5ZDQ5MGZiYmYzZGNmNmQ2ODg5ZjllYmIzOGZm
|
15
|
-
NjRmZTZiNzc1NmI5Mjc5NjE2YzM1Nzg2YjNmNzJkM2ViYTMwMjg=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9c297ddc3dfc6b1e75f6cf9620cda8d2215699b3
|
4
|
+
data.tar.gz: 822f8f022616b4e52bfda533e268d61ab677d7fe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 499bedb96622c4b193300b9045f34cae23d22c6bbd3d461ed1b0e27aff1cd715315c76581ccc08aa3a798545ca43c8120b78256839811ffd16527fe89bba43f9
|
7
|
+
data.tar.gz: 14294fe5fa456b21e3fc6aba390d43088cf89588b7c089a0ee813f3c684091abd56e4ef949e49bf7d47e194f5ebb2c7ea2489030d7141654fc71f61b0c776328
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p247
|
data/.travis.yml
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
dotmailer (0.0.4)
|
5
|
+
activesupport
|
6
|
+
rest-client
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (4.0.1)
|
12
|
+
i18n (~> 0.6, >= 0.6.4)
|
13
|
+
minitest (~> 4.2)
|
14
|
+
multi_json (~> 1.3)
|
15
|
+
thread_safe (~> 0.1)
|
16
|
+
tzinfo (~> 0.3.37)
|
17
|
+
addressable (2.3.5)
|
18
|
+
atomic (1.1.14)
|
19
|
+
coderay (1.0.9)
|
20
|
+
crack (0.4.1)
|
21
|
+
safe_yaml (~> 0.9.0)
|
22
|
+
diff-lcs (1.2.4)
|
23
|
+
i18n (0.6.5)
|
24
|
+
method_source (0.8.2)
|
25
|
+
mime-types (2.0)
|
26
|
+
minitest (4.7.5)
|
27
|
+
multi_json (1.8.2)
|
28
|
+
pry (0.9.12.2)
|
29
|
+
coderay (~> 1.0.5)
|
30
|
+
method_source (~> 0.8)
|
31
|
+
slop (~> 3.4)
|
32
|
+
rest-client (1.6.7)
|
33
|
+
mime-types (>= 1.16)
|
34
|
+
rspec (2.14.1)
|
35
|
+
rspec-core (~> 2.14.0)
|
36
|
+
rspec-expectations (~> 2.14.0)
|
37
|
+
rspec-mocks (~> 2.14.0)
|
38
|
+
rspec-core (2.14.7)
|
39
|
+
rspec-expectations (2.14.3)
|
40
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
41
|
+
rspec-mocks (2.14.4)
|
42
|
+
safe_yaml (0.9.7)
|
43
|
+
slop (3.4.6)
|
44
|
+
thread_safe (0.1.3)
|
45
|
+
atomic
|
46
|
+
tzinfo (0.3.38)
|
47
|
+
webmock (1.15.2)
|
48
|
+
addressable (>= 2.2.7)
|
49
|
+
crack (>= 0.3.2)
|
50
|
+
|
51
|
+
PLATFORMS
|
52
|
+
ruby
|
53
|
+
|
54
|
+
DEPENDENCIES
|
55
|
+
dotmailer!
|
56
|
+
pry
|
57
|
+
rspec
|
58
|
+
webmock
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
dotMailer
|
1
|
+
dotMailer [](https://travis-ci.org/econsultancy/dotmailer) [](http://badge.fury.io/rb/dotmailer)
|
2
2
|
=========
|
3
3
|
|
4
4
|
[dotMailer](http://www.dotmailer.co.uk/) provide both a REST and SOAP API for interacting with their system. The REST API supports both XML and JSON payloads.
|
@@ -138,18 +138,31 @@ Then, once the contact has gone through the resubscribe process and been redirec
|
|
138
138
|
contact.subscribed?
|
139
139
|
=> true
|
140
140
|
|
141
|
+
### Deleting a contact
|
142
|
+
|
143
|
+
Contacts can be deleted by calling `DotMailer::Contact#delete`:
|
144
|
+
|
145
|
+
account = DotMailer::Account.new('your-api-username', 'your-api-password')
|
146
|
+
|
147
|
+
contact = account.find_contact_by_email 'john@example.com'
|
148
|
+
=> DotMailer::Contact id: 12345, email: john@example.com, email_type: Html
|
149
|
+
|
150
|
+
contact.delete
|
151
|
+
|
141
152
|
### Bulk Import
|
142
153
|
|
143
154
|
`DotMailer::Account#import_contacts` will start a batch import of contacts into the global address book, and return a `DotMailer::ContactImport` object which has a `status`:
|
144
155
|
|
145
156
|
account = DotMailer::Account.new('your-api-username', 'your-api-password')
|
146
157
|
|
147
|
-
|
158
|
+
contacts = [
|
148
159
|
{ 'Email' => 'joe@example.com' },
|
149
160
|
{ 'Email' => 'sue@example.com' },
|
150
161
|
{ 'Email' => 'bob@example.com' },
|
151
162
|
{ 'Email' => 'invalid@email' }
|
152
163
|
]
|
164
|
+
|
165
|
+
import = account.import_contacts contacts
|
153
166
|
=> DotMailer::ContactImport contacts: [{"Email"=>"joe@example.com" }, {"Email"=>"sue@example.com" }, {"Email"=>"bob@example.com"}]
|
154
167
|
|
155
168
|
import.finished?
|
@@ -157,6 +170,18 @@ Then, once the contact has gone through the resubscribe process and been redirec
|
|
157
170
|
import.status
|
158
171
|
=> "NotFinished"
|
159
172
|
|
173
|
+
|
174
|
+
The call can also block for you and will wait until the import is finished :
|
175
|
+
|
176
|
+
import = account.import_contacts contacts, wait_for_finish: true
|
177
|
+
|
178
|
+
# it will block here until finished is true
|
179
|
+
# if the import takes longer than 385 seconds then it will raise `DotMailer::ImportNotFinished`
|
180
|
+
|
181
|
+
import.finished?
|
182
|
+
=> true
|
183
|
+
|
184
|
+
|
160
185
|
Then, once the import has finished, you can query the status and get any errors (as a CSV::Table object):
|
161
186
|
|
162
187
|
import.finished?
|
@@ -200,3 +225,51 @@ To fetch these suppressions, pass a Time object to `DotMailer::Account#find_supp
|
|
200
225
|
|
201
226
|
suppressions.first.contact
|
202
227
|
=> DotMailer::Contact id: 12345, email: john@example.com
|
228
|
+
|
229
|
+
Campaigns
|
230
|
+
---------
|
231
|
+
|
232
|
+
### From addresses
|
233
|
+
|
234
|
+
Campaigns can only be sent with a from address which has been set up in dotMailer (see [here](https://support.dotmailer.com/entries/20653397-How-do-I-create-a-custom-from-address-or-additional-alias-)).
|
235
|
+
|
236
|
+
To access this list of from addresses, call `DotMailer::Account#from_addresses`:
|
237
|
+
|
238
|
+
account = DotMailer::Account.new('your-api-username', 'your-api-password')
|
239
|
+
|
240
|
+
account.from_addresses
|
241
|
+
=> [
|
242
|
+
DotMailer::FromAddress id: 123 email: info@example.com,
|
243
|
+
DotMailer::FromAddress id: 456 email: no-reply@example.com
|
244
|
+
]
|
245
|
+
|
246
|
+
### Creating Campaigns
|
247
|
+
|
248
|
+
To create a dotMailer campaign, call `DotMailer::Account#create_campaign` with a hash containing the following required keys:
|
249
|
+
|
250
|
+
* `:name` - The name of the campaign as it will appear in the web interface
|
251
|
+
* `:subject` - The subject to use when sending the campaign
|
252
|
+
* `:from_name` - The name which will appear in the "From" header of the sent campaign
|
253
|
+
* `:from_email` - The email which will appear in the "From" header of the sent campaign (this must be a valid from address in the dotMailer system, see "From addresses" above)
|
254
|
+
* `:html_content` - The content which will be included in the HTML part of the sent campaign
|
255
|
+
* `:plain_text_content` - The content which will be included in the Plain Text part of the sent campaign
|
256
|
+
|
257
|
+
For example:
|
258
|
+
|
259
|
+
account = DotMailer::Account.new('your-api-username', 'your-api-password')
|
260
|
+
|
261
|
+
campaign = account.create_campaign(
|
262
|
+
:name => 'my_campaign',
|
263
|
+
:subject => 'My Campaign',
|
264
|
+
:from_name => 'Me',
|
265
|
+
:from_email => 'me@example.com',
|
266
|
+
:html_content => '<h1>Hello!</h1><a href="http://$UNSUB$">Unsubscribe</a>',
|
267
|
+
:plain_text_content => "Hello!\n======\nhttp://$UNSUB$"
|
268
|
+
)
|
269
|
+
=> DotMailer::Campaign id: 123, name: my_campaign
|
270
|
+
|
271
|
+
campaign.subject
|
272
|
+
=> "My Campaign"
|
273
|
+
|
274
|
+
campaign.from_address
|
275
|
+
=> DotMailer::FromAddress id: 345, email: 'me@example.com'
|
data/Rakefile
CHANGED
data/dotmailer.gemspec
CHANGED
data/lib/dot_mailer.rb
CHANGED
@@ -3,9 +3,14 @@ require 'dot_mailer/data_field'
|
|
3
3
|
require 'dot_mailer/contact_import'
|
4
4
|
require 'dot_mailer/contact'
|
5
5
|
require 'dot_mailer/suppression'
|
6
|
+
require 'dot_mailer/from_address'
|
7
|
+
require 'dot_mailer/campaign'
|
6
8
|
require 'dot_mailer/account'
|
7
9
|
require 'dot_mailer/client'
|
10
|
+
require 'dot_mailer/segment'
|
11
|
+
require 'dot_mailer/campaign_summary'
|
8
12
|
|
9
13
|
module DotMailer
|
10
|
-
SUBSCRIBED_STATUS
|
14
|
+
SUBSCRIBED_STATUS = 'Subscribed'
|
15
|
+
GLOBALLY_SUPPRESSED_STATUS = 'Globally Suppressed'
|
11
16
|
end
|
data/lib/dot_mailer/account.rb
CHANGED
@@ -23,8 +23,8 @@ module DotMailer
|
|
23
23
|
cache.delete 'data_fields'
|
24
24
|
end
|
25
25
|
|
26
|
-
def import_contacts(contacts)
|
27
|
-
ContactImport.import self, contacts
|
26
|
+
def import_contacts(contacts, options = {})
|
27
|
+
ContactImport.import self, contacts, options[:wait_for_finish]
|
28
28
|
end
|
29
29
|
|
30
30
|
def find_contact_by_email(email)
|
@@ -47,6 +47,26 @@ module DotMailer
|
|
47
47
|
client.post_json '/contacts/unsubscribe', 'Email' => email
|
48
48
|
end
|
49
49
|
|
50
|
+
def from_addresses
|
51
|
+
response = cache.fetch 'from_addresses' do
|
52
|
+
client.get('/custom-from-addresses')
|
53
|
+
end
|
54
|
+
|
55
|
+
response.map { |a| FromAddress.new(a) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_campaign(attributes)
|
59
|
+
Campaign.create(self, attributes)
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_campaign_by_id(id)
|
63
|
+
Campaign.find_by_id(self, id)
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_segment_by_id(id)
|
67
|
+
DotMailer::Segment.find_by_id(self, id)
|
68
|
+
end
|
69
|
+
|
50
70
|
def to_s
|
51
71
|
"#{self.class.name} client: #{client}"
|
52
72
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module DotMailer
|
2
|
+
class Campaign
|
3
|
+
def self.create(account, attributes)
|
4
|
+
params = {}
|
5
|
+
|
6
|
+
params['Name'] = attributes[:name] || raise('missing :name')
|
7
|
+
params['Subject'] = attributes[:subject] || raise('missing :subject')
|
8
|
+
params['FromName'] = attributes[:from_name] || raise('missing :from_name')
|
9
|
+
params['HtmlContent'] = attributes[:html_content] || raise('missing :html_content')
|
10
|
+
params['PlainTextContent'] = attributes[:plain_text_content] || raise('missing :plain_text_content')
|
11
|
+
|
12
|
+
raise 'missing :from_email' unless attributes[:from_email]
|
13
|
+
|
14
|
+
from_address = account.from_addresses.detect do |from_address|
|
15
|
+
from_address.email == attributes[:from_email]
|
16
|
+
end
|
17
|
+
|
18
|
+
raise InvalidFromAddress, attributes[:from_email] unless from_address.present?
|
19
|
+
|
20
|
+
params['FromAddress'] = from_address.to_hash
|
21
|
+
|
22
|
+
response = account.client.post_json('/campaigns', params)
|
23
|
+
|
24
|
+
new(account, response)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.find_by_id(account, id)
|
28
|
+
response = account.client.get "/campaigns/#{id}"
|
29
|
+
|
30
|
+
new(account, response)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(account, attributes)
|
34
|
+
self.account = account
|
35
|
+
self.attributes = attributes
|
36
|
+
end
|
37
|
+
|
38
|
+
def id
|
39
|
+
attributes['id']
|
40
|
+
end
|
41
|
+
|
42
|
+
def name
|
43
|
+
attributes['name']
|
44
|
+
end
|
45
|
+
|
46
|
+
def from_name
|
47
|
+
attributes['fromName']
|
48
|
+
end
|
49
|
+
|
50
|
+
def from_address
|
51
|
+
FromAddress.new attributes['fromAddress']
|
52
|
+
end
|
53
|
+
|
54
|
+
def html_content
|
55
|
+
attributes['htmlContent']
|
56
|
+
end
|
57
|
+
|
58
|
+
def plain_text_content
|
59
|
+
attributes['plainTextContent']
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
%{#{self.class.name} id: #{id}, name: #{name}}
|
64
|
+
end
|
65
|
+
|
66
|
+
def inspect
|
67
|
+
to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def send_to_contact_ids(contact_ids)
|
71
|
+
client.post_json '/campaigns/send', {
|
72
|
+
'campaignId' => id,
|
73
|
+
'contactIds' => contact_ids
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def send_to_segment(segment)
|
78
|
+
client.post_json '/campaigns/send', {
|
79
|
+
'campaignId' => id,
|
80
|
+
'addressBookIds' => [segment.id]
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def summary
|
85
|
+
DotMailer::CampaignSummary.new(account, id)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
attr_accessor :attributes, :account
|
90
|
+
|
91
|
+
def client
|
92
|
+
account.client
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module DotMailer
|
2
|
+
class CampaignSummary
|
3
|
+
%w(
|
4
|
+
numUniqueOpens
|
5
|
+
numUniqueTextOpens
|
6
|
+
numTotalUniqueOpens
|
7
|
+
numOpens
|
8
|
+
numTextOpens
|
9
|
+
numTotalOpens
|
10
|
+
numClicks
|
11
|
+
numTextClicks
|
12
|
+
numTotalClicks
|
13
|
+
numPageViews
|
14
|
+
numTotalPageViews
|
15
|
+
numTextPageViews
|
16
|
+
numForwards
|
17
|
+
numTextForwardsge
|
18
|
+
numEstimatedForwards
|
19
|
+
numTextEstimatedForwards
|
20
|
+
numTotalEstimatedForwards
|
21
|
+
numReplies
|
22
|
+
numTextReplies
|
23
|
+
numTotalReplies
|
24
|
+
numHardBounces
|
25
|
+
numTextHardBounces
|
26
|
+
numTotalHardBounces
|
27
|
+
numSoftBounces
|
28
|
+
numTextSoftBounces
|
29
|
+
numTotalSoftBounces
|
30
|
+
numUnsubscribes
|
31
|
+
numTextUnsubscribes
|
32
|
+
numTotalUnsubscribes
|
33
|
+
numIspComplaints
|
34
|
+
numTextIspComplaints
|
35
|
+
numTotalIspComplaints
|
36
|
+
numMailBlocks
|
37
|
+
numTextMailBlocks
|
38
|
+
numTotalMailBlocks
|
39
|
+
numSent
|
40
|
+
numTextSent
|
41
|
+
numTotalSent
|
42
|
+
numRecipientsClicked
|
43
|
+
numDelivered
|
44
|
+
numTextDelivered
|
45
|
+
numTotalDelivered
|
46
|
+
).each do |meth|
|
47
|
+
define_method(meth.underscore) do
|
48
|
+
@params[meth].to_i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
%w(
|
53
|
+
percentageDelivered
|
54
|
+
percentageUniqueOpens
|
55
|
+
percentageOpens
|
56
|
+
percentageUnsubscribes
|
57
|
+
percentageReplies
|
58
|
+
percentageHardBounces
|
59
|
+
percentageSoftBounces
|
60
|
+
percentageUsersClicked
|
61
|
+
percentageClicksToOpens
|
62
|
+
).each do |meth|
|
63
|
+
define_method(meth.underscore) do
|
64
|
+
@params[meth].to_f
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def date_sent
|
69
|
+
Time.parse(@params["dateSent"])
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(account, id)
|
73
|
+
@params = account.client.get "/campaigns/#{id.to_s}/summary"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|