yyyc514-campaign_monitor 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +52 -0
- data/Rakefile +31 -0
- data/TODO +6 -0
- data/campaign_monitor.gemspec +53 -0
- data/init.rb +1 -0
- data/install.rb +0 -0
- data/lib/campaign_monitor/base.rb +55 -0
- data/lib/campaign_monitor/campaign.rb +238 -0
- data/lib/campaign_monitor/client.rb +228 -0
- data/lib/campaign_monitor/helpers.rb +27 -0
- data/lib/campaign_monitor/list.rb +211 -0
- data/lib/campaign_monitor/misc.rb +46 -0
- data/lib/campaign_monitor/result.rb +31 -0
- data/lib/campaign_monitor/subscriber.rb +43 -0
- data/lib/campaign_monitor.rb +270 -0
- data/support/class_enhancements.rb +35 -0
- data/support/faster-xml-simple/lib/faster_xml_simple.rb +187 -0
- data/support/faster-xml-simple/test/regression_test.rb +47 -0
- data/support/faster-xml-simple/test/test_helper.rb +17 -0
- data/support/faster-xml-simple/test/xml_simple_comparison_test.rb +46 -0
- data/test/campaign_monitor_test.rb +90 -0
- data/test/campaign_test.rb +115 -0
- data/test/client_test.rb +129 -0
- data/test/list_test.rb +115 -0
- data/test/test_helper.rb +27 -0
- metadata +97 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006 Jordan Brock
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
= campaign_monitor
|
2
|
+
|
3
|
+
This RubyGem provides access to the Campaign Monitor API (http://www.campaignmonitor.com/api).
|
4
|
+
|
5
|
+
Campaign Monitor recently made some changes to their API.
|
6
|
+
|
7
|
+
This fork makes the following changes:
|
8
|
+
|
9
|
+
* host changed from http://app.campaignmonitor.com to http://api.createsend.com
|
10
|
+
* ID values are no longer sent #to_i because they are hex strings
|
11
|
+
* added support for subscribers with custom fields using SOAP API
|
12
|
+
* refactored gemspec to build on github
|
13
|
+
* misc. cleanup and refactoring
|
14
|
+
|
15
|
+
|
16
|
+
== Pre-requisites
|
17
|
+
|
18
|
+
An account with Campaign Monitor and the API Key. Accounts are free and can be created at
|
19
|
+
http://www.campaignmonitor.com.
|
20
|
+
|
21
|
+
== Resources
|
22
|
+
|
23
|
+
=== Install
|
24
|
+
gem install patientslikeme-campaign_monitor
|
25
|
+
|
26
|
+
=== Git Repository
|
27
|
+
http://github.com/patientslikeme/campaign_monitor
|
28
|
+
|
29
|
+
|
30
|
+
== Usage
|
31
|
+
|
32
|
+
cm = CampaignMonitor.new # assumes you've set CAMPAIGN_MONITOR_API_KEY in your project
|
33
|
+
|
34
|
+
for client in cm.clients
|
35
|
+
for list in client.lists
|
36
|
+
client.name # => returns the name
|
37
|
+
|
38
|
+
# modify a subscriber list
|
39
|
+
list.add_subscriber(email, name, custom_fields_hash)
|
40
|
+
list.remove_subscriber(email)
|
41
|
+
list.add_and_resubscribe(email, name, custom_fields_hash)
|
42
|
+
|
43
|
+
# get subscriber list details
|
44
|
+
subscribers = list.active_subscribers(since_time)
|
45
|
+
unsubscribed = list.unsubscribed(since_time)
|
46
|
+
bounced = list.bounced(since_time)
|
47
|
+
end
|
48
|
+
|
49
|
+
for campaign in client.campaigns
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
# read the contents of the gemspec, eval it, and assign it to 'spec'
|
7
|
+
# this lets us maintain all gemspec info in one place. Nice and DRY.
|
8
|
+
spec = eval(IO.read("campaign_monitor.gemspec"))
|
9
|
+
|
10
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
11
|
+
pkg.gem_spec = spec
|
12
|
+
end
|
13
|
+
|
14
|
+
task :install => [:package] do
|
15
|
+
sh %{sudo gem install pkg/#{spec.name}-#{spec.version}}
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => [:test]
|
19
|
+
|
20
|
+
Rake::TestTask.new do |t|
|
21
|
+
t.libs << "test"
|
22
|
+
t.test_files = FileList['test/*_test.rb']
|
23
|
+
t.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
Rake::RDocTask.new do |rd|
|
27
|
+
rd.main = "README.rdoc"
|
28
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
29
|
+
rd.rdoc_dir = 'doc'
|
30
|
+
rd.options = spec.rdoc_options
|
31
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
* Decide on convents about when to raise and when to return true/false
|
2
|
+
|
3
|
+
So far I've kind of taken the stance if we're retrieving a list and there
|
4
|
+
are only two possible failures (APIKey or primary key) to raise if there
|
5
|
+
is an error but for update/save/create operations to return true/false
|
6
|
+
and then let the user probe @object.result for more details
|
@@ -0,0 +1,53 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.platform = Gem::Platform::RUBY
|
3
|
+
s.name = 'campaign_monitor'
|
4
|
+
s.version = "1.3.2"
|
5
|
+
s.summary = 'Provides access to the Campaign Monitor API.'
|
6
|
+
s.description = <<-EOF
|
7
|
+
A simple wrapper class that provides basic access to the Campaign Monitor API.
|
8
|
+
EOF
|
9
|
+
s.author = 'Jeremy Weiskotten'
|
10
|
+
s.email = 'jweiskotten@patientslikeme.com'
|
11
|
+
s.homepage = 'http://github.com/patientslikeme/campaign_monitor/'
|
12
|
+
s.has_rdoc = true
|
13
|
+
|
14
|
+
s.requirements << 'none'
|
15
|
+
s.require_path = 'lib'
|
16
|
+
|
17
|
+
s.add_dependency 'xml-simple', ['>= 1.0.11']
|
18
|
+
s.add_dependency 'soapr4', ['>= 1.5.8']
|
19
|
+
|
20
|
+
s.files = [
|
21
|
+
'campaign_monitor.gemspec',
|
22
|
+
'init.rb',
|
23
|
+
'install.rb',
|
24
|
+
'MIT-LICENSE',
|
25
|
+
'Rakefile',
|
26
|
+
'README.rdoc',
|
27
|
+
'TODO',
|
28
|
+
|
29
|
+
'lib/campaign_monitor.rb',
|
30
|
+
'lib/campaign_monitor/base.rb',
|
31
|
+
'lib/campaign_monitor/misc.rb',
|
32
|
+
'lib/campaign_monitor/campaign.rb',
|
33
|
+
'lib/campaign_monitor/client.rb',
|
34
|
+
'lib/campaign_monitor/helpers.rb',
|
35
|
+
'lib/campaign_monitor/list.rb',
|
36
|
+
'lib/campaign_monitor/result.rb',
|
37
|
+
'lib/campaign_monitor/subscriber.rb',
|
38
|
+
|
39
|
+
'support/class_enhancements.rb',
|
40
|
+
'support/faster-xml-simple/lib/faster_xml_simple.rb',
|
41
|
+
'support/faster-xml-simple/test/regression_test.rb',
|
42
|
+
'support/faster-xml-simple/test/test_helper.rb',
|
43
|
+
'support/faster-xml-simple/test/xml_simple_comparison_test.rb',
|
44
|
+
|
45
|
+
'test/campaign_monitor_test.rb',
|
46
|
+
'test/campaign_test.rb',
|
47
|
+
'test/client_test.rb',
|
48
|
+
'test/list_test.rb',
|
49
|
+
'test/test_helper.rb'
|
50
|
+
]
|
51
|
+
|
52
|
+
s.test_file = 'test/campaign_monitor_test.rb'
|
53
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'campaign_monitor'
|
data/install.rb
ADDED
File without changes
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class CampaignMonitor
|
2
|
+
# Provides access to the lists and campaigns associated with a client
|
3
|
+
class Base
|
4
|
+
|
5
|
+
attr_reader :result, :attributes, :cm_client
|
6
|
+
|
7
|
+
@@client=nil
|
8
|
+
|
9
|
+
def self.client
|
10
|
+
@@client
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.client=(a)
|
14
|
+
@@client=a
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](k)
|
18
|
+
if m=self.class.get_data_types[k]
|
19
|
+
@attributes[k].send(m)
|
20
|
+
else
|
21
|
+
@attributes[k]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(k,v)
|
26
|
+
@attributes[k]=v
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(*args)
|
30
|
+
@attributes={}
|
31
|
+
@cm_client=@@client
|
32
|
+
end
|
33
|
+
|
34
|
+
# id and name field stuff
|
35
|
+
|
36
|
+
inherited_property "id_field", "id"
|
37
|
+
inherited_property "name_field", "name"
|
38
|
+
inherited_property "data_types", {}
|
39
|
+
|
40
|
+
def id
|
41
|
+
@attributes[self.class.get_id_field]
|
42
|
+
end
|
43
|
+
|
44
|
+
def id=(v)
|
45
|
+
@attributes[self.class.get_id_field]=v
|
46
|
+
end
|
47
|
+
|
48
|
+
def name
|
49
|
+
@attributes[self.class.get_name_field]
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,238 @@
|
|
1
|
+
class CampaignMonitor
|
2
|
+
|
3
|
+
# The Client class aims to impliment the full functionality of the CampaignMonitor
|
4
|
+
# Clients API as detailed at: http://www.campaignmonitor.com/api/
|
5
|
+
# === Attributes
|
6
|
+
#
|
7
|
+
# Attriutes can be read and set as if Campaign were a Hash
|
8
|
+
#
|
9
|
+
# @client["CompanyName"]="Road Running, Inc."
|
10
|
+
# @client["ContactName"] => "Wiley Coyote"
|
11
|
+
#
|
12
|
+
# Convenience attribute readers are provided for name and id
|
13
|
+
#
|
14
|
+
# @campaign.id == @client["CampaignID"]
|
15
|
+
# @campaign.name == @client["CampaignName"]
|
16
|
+
#
|
17
|
+
# === API calls supported
|
18
|
+
#
|
19
|
+
# * Campaign.Create
|
20
|
+
# * Campaign.Send
|
21
|
+
# * Campaign.GetBounces
|
22
|
+
# * Campaign.GetLists
|
23
|
+
# * Campaign.GetOpens
|
24
|
+
# * Campaign.GetSubscriberClicks
|
25
|
+
# * Campaign.GetUnsubscribes
|
26
|
+
# * Campaign.GetSummary
|
27
|
+
#
|
28
|
+
class Campaign < Base
|
29
|
+
include CampaignMonitor::Helpers
|
30
|
+
id_field "CampaignID"
|
31
|
+
name_field "Subject"
|
32
|
+
|
33
|
+
class MissingParameter < StandardError
|
34
|
+
end
|
35
|
+
|
36
|
+
data_types "TotalRecipients" => "to_i"
|
37
|
+
|
38
|
+
def initialize(attrs={})
|
39
|
+
super
|
40
|
+
@attributes=attrs
|
41
|
+
end
|
42
|
+
|
43
|
+
# Calls Campaign.Create
|
44
|
+
# It will return true if successful and false if not.
|
45
|
+
# Campaign#result will have the result of the API call
|
46
|
+
#
|
47
|
+
# Example
|
48
|
+
# @camp=@client.new_campaign
|
49
|
+
# @camp["CampaignName"]="Yummy Gummy Bears"
|
50
|
+
# @camp["CampaignSubject"]="Yummy Gummy Bears"
|
51
|
+
# @camp["FromName"]="Mr Yummy"
|
52
|
+
# @camp["FromEmail"]="yummy@gummybears.com"
|
53
|
+
# @camp["ReplyTo"]="support@gummybears.com"
|
54
|
+
# @camp["HtmlUrl"]="http://www.gummybears.com/newsletter2009.html"
|
55
|
+
# @camp["TextUrl"]="http://www.gummybears.com/newsletter2009.txt"
|
56
|
+
# @camp.Create
|
57
|
+
def Create
|
58
|
+
required_params=%w{CampaignName CampaignSubject FromName FromEmail ReplyTo HtmlUrl TextUrl}
|
59
|
+
required_params.each do |f|
|
60
|
+
raise MissingParameter, "'#{f}' is required to call Create" unless self[f]
|
61
|
+
end
|
62
|
+
response = cm_client.using_soap do |driver|
|
63
|
+
opts=attributes.merge(:ApiKey => cm_client.api_key, :SubscriberListIDs => @lists.map {|x| x.id})
|
64
|
+
driver.createCampaign opts
|
65
|
+
end
|
66
|
+
@result=Result.new(response["Campaign.CreateResult"])
|
67
|
+
self.id=@result.content if @result.success?
|
68
|
+
@result.success?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Calls Campaign.Send
|
72
|
+
# It will return true if successful and false if not.
|
73
|
+
# Campaign#result will have the result of the API call
|
74
|
+
#
|
75
|
+
# Example
|
76
|
+
# @camp=@client.new_campaign(attributes)
|
77
|
+
# @camp.Create
|
78
|
+
# @camp.Send("ConfirmationEmail" => "bob@aol.com", "SendDate" => "Immediately")
|
79
|
+
def Send(options={})
|
80
|
+
required_params=%w{ConfirmationEmail SendDate}
|
81
|
+
required_params.each do |f|
|
82
|
+
raise MissingParameter, "'#{f}' is required to call Send" unless options[f]
|
83
|
+
end
|
84
|
+
options.merge!("CampaignID" => self.id)
|
85
|
+
@result=Result.new(@cm_client.Campaign_Send(options))
|
86
|
+
@result.success?
|
87
|
+
end
|
88
|
+
|
89
|
+
# Calls Campaign.GetLists. Often you probably should just use Campaign#lists
|
90
|
+
# It will raise an ApiError if an error occurs
|
91
|
+
# Campaign#result will have the result of the API call
|
92
|
+
#
|
93
|
+
# Example
|
94
|
+
# @camp=@client.campaigns.first
|
95
|
+
# @camp.GetLists
|
96
|
+
def GetLists
|
97
|
+
handle_response(@cm_client.Campaign_GetLists(:CampaignID => id)) do |response|
|
98
|
+
@result=Result.new(response)
|
99
|
+
@lists=response["List"].collect{|l| List.new({"ListID" => l["ListID"], "Title" => l["Name"]})}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Creates a new list object with the given id.
|
104
|
+
# You'll still need to call another method to load data or actually do anything useful
|
105
|
+
# as this method just generators a new object and doesn't hit the API at all. This was
|
106
|
+
# added as a quick way to setup an object to request data from it
|
107
|
+
#
|
108
|
+
# Example
|
109
|
+
# @campaign=Campaign[1234]
|
110
|
+
# @campaign.lists.each do ...
|
111
|
+
def self.[](id)
|
112
|
+
Campaign.new("CampaignID" => id)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Convenience method for accessing or adding lists to a new (uncreated) campaign
|
116
|
+
# Calls GetLists behind the scenes if needed
|
117
|
+
#
|
118
|
+
# Example
|
119
|
+
# @camp=@client.campaigns.first
|
120
|
+
# @camp.lists.each do
|
121
|
+
#
|
122
|
+
# @camp=@client.new_campaign(attributes)
|
123
|
+
# @camp.lists << @client.lists.first
|
124
|
+
# @camp.Create
|
125
|
+
def lists
|
126
|
+
# pull down the list of lists if we have an id
|
127
|
+
self.GetLists if @lists.nil? and id
|
128
|
+
@lists||=[]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Example
|
132
|
+
# @campaign = Campaign[12345]
|
133
|
+
# @subscriber_opens = @campaign.opens
|
134
|
+
#
|
135
|
+
# for subscriber in @subscriber_opens
|
136
|
+
# puts subscriber.email
|
137
|
+
# end
|
138
|
+
def GetOpens
|
139
|
+
handle_response(cm_client.Campaign_GetOpens("CampaignID" => self.id)) do |response|
|
140
|
+
response["SubscriberOpen"].collect{|s| SubscriberOpen.new(s["EmailAddress"], s["ListID"], s["NumberOfOpens"])}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
alias opens GetOpens
|
144
|
+
|
145
|
+
# Example
|
146
|
+
# @campaign = Campaign[12345]
|
147
|
+
# @subscriber_bounces = @campaign.bounces
|
148
|
+
#
|
149
|
+
# for subscriber in @subscriber_bounces
|
150
|
+
# puts subscriber.email
|
151
|
+
# end
|
152
|
+
def GetBounces
|
153
|
+
handle_response(cm_client.Campaign_GetBounces("CampaignID"=> self.id)) do |response|
|
154
|
+
response["SubscriberBounce"].collect{|s| SubscriberBounce.new(s["EmailAddress"], s["ListID"], s["BounceType"])}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
alias bounces GetBounces
|
158
|
+
|
159
|
+
# Example
|
160
|
+
# @campaign = Campaign[12345]
|
161
|
+
# @subscriber_clicks = @campaign.clicks
|
162
|
+
#
|
163
|
+
# for subscriber in @subscriber_clicks
|
164
|
+
# puts subscriber.email
|
165
|
+
# end
|
166
|
+
def GetSubscriberClicks
|
167
|
+
handle_response(cm_client.Campaign_GetSubscriberClicks("CampaignID" => self.id)) do |response|
|
168
|
+
response["SubscriberClick"].collect{|s| SubscriberClick.new(s["EmailAddress"], s["ListID"], s["ClickedLinks"])}
|
169
|
+
end
|
170
|
+
end
|
171
|
+
alias clicks GetSubscriberClicks
|
172
|
+
|
173
|
+
# Example
|
174
|
+
# @campaign = Campaign[12345]
|
175
|
+
# @subscriber_unsubscribes = @campaign.unsubscribes
|
176
|
+
#
|
177
|
+
# for subscriber in @subscriber_unsubscribes
|
178
|
+
# puts subscriber.email
|
179
|
+
# end
|
180
|
+
def GetUnsubscribes
|
181
|
+
handle_response(cm_client.Campaign_GetUnsubscribes("CampaignID" => self.id)) do |response|
|
182
|
+
response["SubscriberUnsubscribe"].collect{|s| SubscriberUnsubscribe.new(s["EmailAddress"], s["ListID"])}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
alias unsubscribes GetUnsubscribes
|
186
|
+
|
187
|
+
# hook up the old API calls
|
188
|
+
def method_missing(m, *args)
|
189
|
+
if %w{number_bounced number_unsubscribed number_clicks number_opened number_recipients}.include?(m.to_s)
|
190
|
+
summary[m]
|
191
|
+
else
|
192
|
+
super
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Calls Campaign.GetSummary. Often you probably should just use Campaign#summary
|
197
|
+
# It will return true if successful and false if not.
|
198
|
+
# Campaign#result will have the result of the API call
|
199
|
+
#
|
200
|
+
# Example
|
201
|
+
# @camp=@client.campaigns.first
|
202
|
+
# @camp.GetSummary
|
203
|
+
def GetSummary
|
204
|
+
@result=Result.new(cm_client.Campaign_GetSummary('CampaignID' => self.id))
|
205
|
+
@summary=parse_summary(@result.raw) if @result.success?
|
206
|
+
@result.success?
|
207
|
+
end
|
208
|
+
|
209
|
+
# Convenience method for accessing summary details of a campaign
|
210
|
+
#
|
211
|
+
# Examples
|
212
|
+
# @camp.summary["Recipients"]
|
213
|
+
# @camp.summary['Recipients']
|
214
|
+
# @camp.summary['TotalOpened']
|
215
|
+
# @camp.summary['Clicks']
|
216
|
+
# @camp.summary['Unsubscribed']
|
217
|
+
# @camp.summary['Bounced']
|
218
|
+
def summary(refresh=false)
|
219
|
+
self.GetSummary if refresh or @summary.nil?
|
220
|
+
@summary
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
def parse_summary(summary)
|
225
|
+
@summary = {
|
226
|
+
:number_recipients => summary['Recipients'].to_i,
|
227
|
+
:number_opened => summary['TotalOpened'].to_i,
|
228
|
+
:number_clicks => summary['Clicks'].to_i,
|
229
|
+
:number_unsubscribed => summary['Unsubscribed'].to_i,
|
230
|
+
:number_bounced => summary['Bounced'].to_i
|
231
|
+
}
|
232
|
+
summary.each do |key, value|
|
233
|
+
@summary[key]=value.to_i
|
234
|
+
end
|
235
|
+
@summary
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
class CampaignMonitor
|
2
|
+
# Provides access to the lists and campaigns associated with a client
|
3
|
+
class ClientLists < Array
|
4
|
+
def initialize(v,parent)
|
5
|
+
@parent=parent
|
6
|
+
super(v)
|
7
|
+
end
|
8
|
+
def build(attrs={})
|
9
|
+
List.new(attrs.merge(:ClientID => @parent.id))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# The Client class aims to impliment the full functionality of the CampaignMonitor
|
14
|
+
# Clients API as detailed at: http://www.campaignmonitor.com/api/
|
15
|
+
# === Attributes
|
16
|
+
#
|
17
|
+
# Attriutes can be read and set as if Client were a Hash
|
18
|
+
#
|
19
|
+
# @client["CompanyName"]="Road Running, Inc."
|
20
|
+
# @client["ContactName"] => "Wiley Coyote"
|
21
|
+
#
|
22
|
+
# Convenience attribute readers are provided for name and id
|
23
|
+
#
|
24
|
+
# @client.id == @client["ClientID"]
|
25
|
+
# @client.name == @client["CompanyName"]
|
26
|
+
#
|
27
|
+
# === API calls supported
|
28
|
+
#
|
29
|
+
# * Client.Create
|
30
|
+
# * Client.Delete
|
31
|
+
# * Client.GetCampaigns
|
32
|
+
# * Client.GetDetail
|
33
|
+
# * Client.GetLists
|
34
|
+
# * Client.UpdateAccessAndBilling
|
35
|
+
# * Client.UpdateBasics
|
36
|
+
#
|
37
|
+
# === Not yet supported
|
38
|
+
#
|
39
|
+
# * Client.GetSegments - TODO
|
40
|
+
# * Client.GetSuppressionList - TODO
|
41
|
+
class Client < Base
|
42
|
+
include CampaignMonitor::Helpers
|
43
|
+
id_field "ClientID"
|
44
|
+
name_field "CompanyName"
|
45
|
+
|
46
|
+
data_types "AccessLevel" => "to_i"
|
47
|
+
|
48
|
+
# we will assume if something isn't a basic attribute that it's a AccessAndBilling attribute
|
49
|
+
BASIC_ATTRIBUTES=%w{CompanyName ContactName EmailAddress Country Timezone}
|
50
|
+
|
51
|
+
# Creates a new client that you can later create (or load)
|
52
|
+
# The prefered way to load a client is using Client#[] however
|
53
|
+
#
|
54
|
+
# Example
|
55
|
+
#
|
56
|
+
# @client = Client.new(attributes)
|
57
|
+
# @client.Create
|
58
|
+
#
|
59
|
+
# @client = Client.new("ClientID" => 12345)
|
60
|
+
# @client.GetDetails
|
61
|
+
def initialize(attrs={})
|
62
|
+
super
|
63
|
+
@attributes=attrs
|
64
|
+
end
|
65
|
+
|
66
|
+
# Calls Client.GetLists and returns a collection of CM::Campaign objects
|
67
|
+
#
|
68
|
+
# Example
|
69
|
+
# @client = @cm.clients.first
|
70
|
+
# @new_list = @client.lists.build
|
71
|
+
# @lists = @client.lists
|
72
|
+
#
|
73
|
+
# for list in @lists
|
74
|
+
# puts list.name # a shortcut for list["Title"]
|
75
|
+
# end
|
76
|
+
def GetLists
|
77
|
+
ClientLists.new(cm_client.lists(self.id), self)
|
78
|
+
end
|
79
|
+
|
80
|
+
alias lists GetLists
|
81
|
+
|
82
|
+
# Calls Client.GetCampaigns and returns a collection of CM::List objects
|
83
|
+
#
|
84
|
+
# Example
|
85
|
+
# @client = @cm.clients.first
|
86
|
+
# @campaigns = @client.campaigns
|
87
|
+
#
|
88
|
+
# for campaign in @campaigns
|
89
|
+
# puts campaign.subject
|
90
|
+
# end
|
91
|
+
def GetCampaigns
|
92
|
+
cm_client.campaigns(self.id)
|
93
|
+
end
|
94
|
+
|
95
|
+
alias campaigns GetCampaigns
|
96
|
+
|
97
|
+
def new_campaign(attrs={})
|
98
|
+
Campaign.new(attrs.merge("ClientID" => self.id))
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# Calls Client.GetDetails to load a specific client
|
103
|
+
# Client#result will have the result of the API call
|
104
|
+
#
|
105
|
+
# Example
|
106
|
+
#
|
107
|
+
# @client=Client[12345]
|
108
|
+
# puts @client.name if @client.result.success?
|
109
|
+
def self.[](id)
|
110
|
+
client=self.new("ClientID" => id)
|
111
|
+
client.GetDetail(true)
|
112
|
+
client.result.code == 102 ? nil : client
|
113
|
+
end
|
114
|
+
|
115
|
+
# Calls Client.GetDetails
|
116
|
+
# This is needed because often if you're working with a list of clients you really only
|
117
|
+
# have their company name when what you want is the full record.
|
118
|
+
# It will return true if successful and false if not.
|
119
|
+
# Client#result will have the result of the API call
|
120
|
+
#
|
121
|
+
# Example
|
122
|
+
#
|
123
|
+
# @client=@cm.clients.first
|
124
|
+
# @client["CompanyName"]="Ben's Widgets"
|
125
|
+
# @client["ContactName"] => nil
|
126
|
+
# @client.GetDetail
|
127
|
+
# @client["ContactName"] => "Ben Wilder"
|
128
|
+
def GetDetail(overwrite=false)
|
129
|
+
@result=Result.new(cm_client.Client_GetDetail("ClientID" => id))
|
130
|
+
return false if @result.failed?
|
131
|
+
@flatten={}
|
132
|
+
@flatten.merge!(@result.raw["BasicDetails"])
|
133
|
+
@flatten.merge!(@result.raw["AccessAndBilling"])
|
134
|
+
# TODO - look into
|
135
|
+
# map {} to nil - some weird XML converstion issue?
|
136
|
+
@flatten=@flatten.inject({}) { |sum,a| sum[a[0]]=a[1]=={} ? nil : a[1]; sum }
|
137
|
+
@attributes=@flatten.merge(@attributes)
|
138
|
+
@attributes.merge!(@flatten) if overwrite
|
139
|
+
@fully_baked=true if @result.success?
|
140
|
+
@result.success?
|
141
|
+
end
|
142
|
+
|
143
|
+
# This is just a convenience method that calls both Client.UpdateBasics and Client.UpdateAccessAndBilling.
|
144
|
+
# It will return true if successful and false if not.
|
145
|
+
# Client#result will have the result of the API call
|
146
|
+
#
|
147
|
+
# Example
|
148
|
+
# @client=@cm.clients.first
|
149
|
+
# @client["CompanyName"]="Ben's Widgets"
|
150
|
+
# @client.update
|
151
|
+
def update
|
152
|
+
self.UpdateBasics
|
153
|
+
self.UpdateAccessAndBilling if result.success?
|
154
|
+
@result.success?
|
155
|
+
end
|
156
|
+
|
157
|
+
# Calls Client.UpdateAccessAndBilling
|
158
|
+
# This will also call GetDetails first to prepoluate any empty fields the API call needs
|
159
|
+
# It will return true if successful and false if not.
|
160
|
+
# Client#result will have the result of the API call
|
161
|
+
#
|
162
|
+
# Example
|
163
|
+
# @client=@cm.clients.first
|
164
|
+
# @client["Currency"]="USD"
|
165
|
+
# @client.UpdateAccessAndBilling
|
166
|
+
def UpdateAccessAndBilling
|
167
|
+
fully_bake
|
168
|
+
@result=Result.new(cm_client.Client_UpdateAccessAndBilling(@attributes))
|
169
|
+
@result.success?
|
170
|
+
end
|
171
|
+
|
172
|
+
# Calls Client.UpdateBasics
|
173
|
+
# This will also call GetDetails first to prepoluate any empty fields the API call needs
|
174
|
+
# It will return true if successful and false if not.
|
175
|
+
# Client#result will have the result of the API call
|
176
|
+
#
|
177
|
+
# Example
|
178
|
+
# @client=@cm.clients.first
|
179
|
+
# @client["CompanyName"]="Ben's Widgets"
|
180
|
+
# @client.UpdateBasics
|
181
|
+
def UpdateBasics
|
182
|
+
fully_bake
|
183
|
+
@result=Result.new(cm_client.Client_UpdateBasics(@attributes))
|
184
|
+
@result.success?
|
185
|
+
end
|
186
|
+
|
187
|
+
# Calls Client.Create
|
188
|
+
# It will return true if successful and false if not.
|
189
|
+
# Client#result will have the result of the API call
|
190
|
+
#
|
191
|
+
# Example
|
192
|
+
# @client=CampaignMonitor::Client.new
|
193
|
+
# @client["CompanyName"]="Ben's Widgets"
|
194
|
+
# @client["ContactName"]="Ben Winters"
|
195
|
+
# @client["Country"]=@cm.countries.first
|
196
|
+
# @client["Timezone"]=@cm.timezones.first
|
197
|
+
# ...
|
198
|
+
# @client.Create
|
199
|
+
def Create
|
200
|
+
@result=Result.new(cm_client.Client_Create(@attributes))
|
201
|
+
self.id = @result.content if @result.success?
|
202
|
+
@result.success?
|
203
|
+
end
|
204
|
+
|
205
|
+
# Calls Client.Delete.
|
206
|
+
# It will return true if successful and false if not.
|
207
|
+
# Client#result will have the result of the API call
|
208
|
+
#
|
209
|
+
# Example
|
210
|
+
# @client=@cm.clients.first
|
211
|
+
# @client.Delete
|
212
|
+
def Delete
|
213
|
+
@result=Result.new(cm_client.Client_Delete("ClientID" => id))
|
214
|
+
@result.success?
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
#:nodoc:
|
220
|
+
def fully_bake
|
221
|
+
unless @fully_baked
|
222
|
+
self.GetDetail
|
223
|
+
@fully_baked=true
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
end
|