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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- M2EzOWYzMzZiMTc2YzVmODA2ZTBiYmEzNmJhODFmMDRlNzMyMWY3OQ==
5
- data.tar.gz: !binary |-
6
- YjAzMmNlZTdlZDY0NDJkYjA2Njk4YjBkYWQ5MDY0OWM0MGYyZDg0Zg==
7
- !binary "U0hBNTEy":
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
@@ -1,4 +1,3 @@
1
1
  *.gem
2
2
  .bundle
3
- Gemfile.lock
4
3
  pkg/*
@@ -0,0 +1 @@
1
+ 2.0.0-p247
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+
6
+ script: bundle exec rspec
@@ -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 [![Build Status](https://travis-ci.org/econsultancy/dotmailer.png?branch=master)](https://travis-ci.org/econsultancy/dotmailer) [![Gem Version](https://badge.fury.io/rb/dotmailer.png)](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
- import = account.import_contacts [
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
@@ -1 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ desc "Load the gem into an irb console"
4
+ task :console do
5
+ require 'irb'
6
+ ARGV.clear
7
+ ARGV.push("-Ilib")
8
+ ARGV.push("-rdotmailer")
9
+ IRB.start
10
+ end
@@ -22,4 +22,5 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_development_dependency 'rspec'
24
24
  s.add_development_dependency 'webmock'
25
+ s.add_development_dependency 'pry'
25
26
  end
@@ -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 = 'Subscribed'
14
+ SUBSCRIBED_STATUS = 'Subscribed'
15
+ GLOBALLY_SUPPRESSED_STATUS = 'Globally Suppressed'
11
16
  end
@@ -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
+