MattHall-campaign_monitor 1.3.2.2
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.
- 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 +240 -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 +110 -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.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 'soap4r', ['>= 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,240 @@
|
|
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. OYou probably should just use Campaign#summary which caches results
|
197
|
+
# It will raise ApiError if an error occurs
|
198
|
+
# Campaign#result will have the result of the API call
|
199
|
+
#
|
200
|
+
# Example
|
201
|
+
# @camp=@client.campaigns.first
|
202
|
+
# @camp.GetSummary["Clicks"]
|
203
|
+
def GetSummary
|
204
|
+
handle_response(cm_client.Campaign_GetSummary('CampaignID' => self.id)) do |response|
|
205
|
+
@result=Result.new(response)
|
206
|
+
@summary=parse_summary(@result.raw)
|
207
|
+
end
|
208
|
+
@summary
|
209
|
+
end
|
210
|
+
|
211
|
+
# Convenience method for accessing summary details of a campaign
|
212
|
+
#
|
213
|
+
# Examples
|
214
|
+
# @camp.summary["Recipients"]
|
215
|
+
# @camp.summary['Recipients']
|
216
|
+
# @camp.summary['TotalOpened']
|
217
|
+
# @camp.summary['Clicks']
|
218
|
+
# @camp.summary['Unsubscribed']
|
219
|
+
# @camp.summary['Bounced']
|
220
|
+
def summary(refresh=false)
|
221
|
+
self.GetSummary if refresh or @summary.nil?
|
222
|
+
@summary
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
def parse_summary(summary)
|
227
|
+
@summary = {
|
228
|
+
:number_recipients => summary['Recipients'].to_i,
|
229
|
+
:number_opened => summary['TotalOpened'].to_i,
|
230
|
+
:number_clicks => summary['Clicks'].to_i,
|
231
|
+
:number_unsubscribed => summary['Unsubscribed'].to_i,
|
232
|
+
:number_bounced => summary['Bounced'].to_i
|
233
|
+
}
|
234
|
+
summary.each do |key, value|
|
235
|
+
@summary[key]=value.to_i
|
236
|
+
end
|
237
|
+
@summary
|
238
|
+
end
|
239
|
+
end
|
240
|
+
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
|