reidab-campaign_monitor 1.3.3

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 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,63 @@
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
+ patientslikeme's 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
+ This fork integrates the following additional changes:
16
+
17
+ * updates Subscriber#is_subscribed? to work with the latest API. (from amiel)
18
+ * adds support for array values in custom_field_hash (from oferlin)
19
+ * API key cleanup (from yyyc514)
20
+ * added ability to get fetch lists by id using List#GetDetails or List#[] (from yyyc514)
21
+ * added support for adding and deleting clients (from yyyc514)
22
+ * added support for querying CM for countries and timezones (from yyyc514)
23
+ * added support for creating campaigns (from yyyc514)
24
+ * general test and api cleanup (from yyyc514)
25
+
26
+
27
+ == Pre-requisites
28
+
29
+ An account with Campaign Monitor and the API Key. Accounts are free and can be created at
30
+ http://www.campaignmonitor.com.
31
+
32
+ == Resources
33
+
34
+ === Install
35
+ gem install patientslikeme-campaign_monitor
36
+
37
+ === Git Repository
38
+ http://github.com/patientslikeme/campaign_monitor
39
+
40
+
41
+ == Usage
42
+
43
+ cm = CampaignMonitor.new # assumes you've set CAMPAIGN_MONITOR_API_KEY in your project
44
+
45
+ for client in cm.clients
46
+ for list in client.lists
47
+ client.name # => returns the name
48
+
49
+ # modify a subscriber list
50
+ list.add_subscriber(email, name, custom_fields_hash)
51
+ list.remove_subscriber(email)
52
+ list.add_and_resubscribe(email, name, custom_fields_hash)
53
+
54
+ # get subscriber list details
55
+ subscribers = list.active_subscribers(since_time)
56
+ unsubscribed = list.unsubscribed(since_time)
57
+ bounced = list.bounced(since_time)
58
+ end
59
+
60
+ for campaign in client.campaigns
61
+
62
+ end
63
+ 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.3"
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/reidab/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,270 @@
1
+ require 'rubygems'
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'xmlsimple'
5
+ require 'date'
6
+ gem 'soap4r'
7
+
8
+ require File.join(File.dirname(__FILE__), '../support/class_enhancements.rb')
9
+ require File.join(File.dirname(__FILE__), 'campaign_monitor/helpers.rb')
10
+ require File.join(File.dirname(__FILE__), 'campaign_monitor/misc.rb')
11
+ require File.join(File.dirname(__FILE__), 'campaign_monitor/base.rb')
12
+ require File.join(File.dirname(__FILE__), 'campaign_monitor/client.rb')
13
+ require File.join(File.dirname(__FILE__), 'campaign_monitor/list.rb')
14
+ require File.join(File.dirname(__FILE__), 'campaign_monitor/subscriber.rb')
15
+ require File.join(File.dirname(__FILE__), 'campaign_monitor/result.rb')
16
+ require File.join(File.dirname(__FILE__), 'campaign_monitor/campaign.rb')
17
+
18
+ # A wrapper class to access the Campaign Monitor API. Written using the wonderful
19
+ # Flickr interface by Scott Raymond as a guide on how to access remote web services
20
+ #
21
+ # For more information on the Campaign Monitor API, visit http://campaignmonitor.com/api
22
+ #
23
+ # Author:: Jordan Brock <jordan@spintech.com.au>
24
+ # Copyright:: Copyright (c) 2006 Jordan Brock <jordan@spintech.com.au>
25
+ # License:: MIT <http://www.opensource.org/licenses/mit-license.php>
26
+ #
27
+ # USAGE:
28
+ # require 'campaign_monitor'
29
+ # cm = CampaignMonitor.new(API_KEY) # creates a CampaignMonitor object
30
+ # # Can set CAMPAIGN_MONITOR_API_KEY in environment.rb
31
+ # cm.clients # Returns an array of clients associated with
32
+ # # the user account
33
+ # cm.campaigns(client_id)
34
+ # cm.lists(client_id)
35
+ # cm.add_subscriber(list_id, email, name)
36
+ #
37
+ # == CLIENT
38
+ # client = Client[client_id] # find an existing client
39
+ # client = Client.new(attributes)
40
+ # client.Create
41
+ # client.Delete
42
+ # client.GetDetail
43
+ # client.UpdateAccessAndBilling
44
+ # client.UpdateBasics
45
+ # client.update # update basics, access, and billing
46
+ # client.lists # OR
47
+ # client.GetLists
48
+ # client.lists.build # to create a new unsaved list for a client
49
+ # client.campaigns # OR
50
+ # client.GetCampaigns
51
+ #
52
+ # == LIST
53
+ # list = List[list_id] # find an existing list
54
+ # list = List.new(attributes)
55
+ # list.Create
56
+ # list.Delete
57
+ # list.Update
58
+ # list.add_subscriber(email, name)
59
+ # list.remove_subscriber(email)
60
+ # list.active_subscribers(date)
61
+ # list.unsubscribed(date)
62
+ # list.bounced(date)
63
+ #
64
+ # == CAMPAIGN
65
+ # campaign = Campaign.new(campaign_id)
66
+ # campaign.clicks
67
+ # campaign.opens
68
+ # campaign.bounces
69
+ # campaign.unsubscribes
70
+ # campaign.number_recipients
71
+ # campaign.number_clicks
72
+ # campaign.number_opens
73
+ # campaign.number_bounces
74
+ # campaign.number_unsubscribes
75
+ #
76
+ #
77
+ # == SUBSCRIBER
78
+ # subscriber = Subscriber.new(email)
79
+ # subscriber.add(list_id)
80
+ # subscriber.unsubscribe(list_id)
81
+ #
82
+ # == Data Types
83
+ # SubscriberBounce
84
+ # SubscriberClick
85
+ # SubscriberOpen
86
+ # SubscriberUnsubscribe
87
+ # Result
88
+ #
89
+ class CampaignMonitor
90
+ include CampaignMonitor::Helpers
91
+
92
+ class InvalidAPIKey < StandardError
93
+ end
94
+
95
+ class ApiError < StandardError
96
+ end
97
+
98
+ attr_reader :api_key, :api_url
99
+
100
+ # Replace this API key with your own (http://www.campaignmonitor.com/api/)
101
+ def initialize(api_key=CAMPAIGN_MONITOR_API_KEY)
102
+ @api_key = api_key
103
+ @api_url = 'http://api.createsend.com/api/api.asmx'
104
+ CampaignMonitor::Base.client=self
105
+ end
106
+
107
+ # Takes a CampaignMonitor API method name and set of parameters;
108
+ # returns an XmlSimple object with the response
109
+ def request(method, params)
110
+ request_xml=http_get(request_url(method, params))
111
+ begin
112
+ response = PARSER.xml_in(request_xml, { 'keeproot' => false,
113
+ 'forcearray' => %w[List Campaign Subscriber Client SubscriberOpen SubscriberUnsubscribe SubscriberClick SubscriberBounce],
114
+ 'noattr' => true })
115
+ response.delete('d1p1:type')
116
+ response.delete("d1p1:http://www.w3.org/2001/XMLSchema-instance:type")
117
+ response
118
+ # rescue XML::Parser::ParseError
119
+ rescue XML::Error
120
+ { "Code" => 500, "Message" => request_xml.split(/\r?\n/).first, "FullError" => request_xml }
121
+ end
122
+ end
123
+
124
+ # Takes a CampaignMonitor API method name and set of parameters; returns the correct URL for the REST API.
125
+ def request_url(method, params={})
126
+ params.merge!('ApiKey' => api_key)
127
+
128
+ query = params.collect do |key, value|
129
+ "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
130
+ end.sort * '&'
131
+
132
+ "#{api_url}/#{method}?#{query}"
133
+ end
134
+
135
+ # Does an HTTP GET on a given URL and returns the response body
136
+ def http_get(url)
137
+ response=Net::HTTP.get_response(URI.parse(url))
138
+ response.body.to_s
139
+ end
140
+
141
+ # By overriding the method_missing method, it is possible to easily support all of the methods
142
+ # available in the API
143
+ def method_missing(method_id, params = {})
144
+ puts " CM: #{method_id} (#{params.inspect})" if $debug
145
+ res=request(method_id.id2name.gsub(/_/, '.'), params)
146
+ puts " returning: #{res.inspect}" if $debug
147
+ res
148
+ end
149
+
150
+ # Returns an array of Client objects associated with the API Key
151
+ #
152
+ # Example
153
+ # @cm = CampaignMonitor.new()
154
+ # @clients = @cm.clients
155
+ #
156
+ # for client in @clients
157
+ # puts client.name
158
+ # end
159
+ def clients
160
+ handle_response(User_GetClients()) do |response|
161
+ response["Client"].collect{|c| Client.new({"ClientID" => c["ClientID"], "CompanyName" => c["Name"]})}
162
+ end
163
+ end
164
+
165
+ def new_client
166
+ Client.new(nil)
167
+ end
168
+
169
+ def system_date
170
+ User_GetSystemDate()
171
+ end
172
+
173
+ def parsed_system_date
174
+ DateTime.strptime(system_date, timestamp_format)
175
+ end
176
+
177
+ def countries
178
+ handle_response(User_GetCountries()) do | response |
179
+ response["string"]
180
+ end
181
+ end
182
+
183
+ def timezones
184
+ handle_response(User_GetTimezones()) do | response |
185
+ response["string"]
186
+ end
187
+ end
188
+
189
+ # Returns an array of Campaign objects associated with the specified Client ID
190
+ #
191
+ # Example
192
+ # @cm = CampaignMonitor.new()
193
+ # @campaigns = @cm.campaigns(12345)
194
+ #
195
+ # for campaign in @campaigns
196
+ # puts campaign.subject
197
+ # end
198
+ def campaigns(client_id)
199
+ handle_response(Client_GetCampaigns("ClientID" => client_id)) do |response|
200
+ response["Campaign"].collect{|c| Campaign.new(c) }
201
+ end
202
+ end
203
+
204
+ # Returns an array of Subscriber Lists for the specified Client ID
205
+ #
206
+ # Example
207
+ # @cm = CampaignMonitor.new()
208
+ # @lists = @cm.lists(12345)
209
+ #
210
+ # for list in @lists
211
+ # puts list.name
212
+ # end
213
+ def lists(client_id)
214
+ handle_response(Client_GetLists("ClientID" => client_id)) do |response|
215
+ response["List"].collect{|l| List.new({"ListID" => l["ListID"], "Title" => l["Name"]})}
216
+ end
217
+ end
218
+
219
+ # A quick method of adding a subscriber to a list. Returns a Result object
220
+ #
221
+ # Example
222
+ # @cm = CampaignMonitor.new()
223
+ # result = @cm.add_subscriber(12345, "ralph.wiggum@simpsons.net", "Ralph Wiggum")
224
+ #
225
+ # if result.succeeded?
226
+ # puts "Subscriber Added to List"
227
+ # end
228
+ def add_subscriber(list_id, email, name)
229
+ Result.new(Subscriber_Add("ListID" => list_id, "Email" => email, "Name" => name))
230
+ end
231
+
232
+ def using_soap
233
+ driver = wsdl_driver_factory.create_rpc_driver
234
+ driver.wiredump_dev = STDERR if $debug
235
+ response = yield(driver)
236
+ driver.reset_stream
237
+
238
+ response
239
+ end
240
+
241
+ protected
242
+
243
+ def wsdl_driver_factory
244
+ SOAP::WSDLDriverFactory.new("#{api_url}?WSDL")
245
+ end
246
+
247
+ end
248
+
249
+ # If libxml is installed, we use the FasterXmlSimple library, that provides most of the functionality of XmlSimple
250
+ # except it uses the xml/libxml library for xml parsing (rather than REXML).
251
+ # If libxml isn't installed, we just fall back on XmlSimple.
252
+
253
+ PARSER =
254
+ begin
255
+ require 'xml/libxml'
256
+ # Older version of libxml aren't stable (bus error when requesting attributes that don't exist) so we
257
+ # have to use a version greater than '0.3.8.2'.
258
+ raise LoadError unless XML::Parser::VERSION > '0.3.8.2'
259
+ $:.push(File.join(File.dirname(__FILE__), '..', 'support', 'faster-xml-simple', 'lib'))
260
+ require 'faster_xml_simple'
261
+ p 'Using libxml-ruby'
262
+ FasterXmlSimple
263
+ rescue LoadError
264
+ begin
265
+ require 'rexml-expansion-fix'
266
+ rescue LoadError => e
267
+ p 'Cannot load rexml security patch'
268
+ end
269
+ XmlSimple
270
+ end