reidab-campaign_monitor 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
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