Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 264 lines (230 sloc) 7.66 kB
320641d Moved directories
jordanbrock authored
1 # CampaignMonitor
cc63f93 @Empact Split out classes for better organization
Empact authored
2 # A wrapper class to access the Campaign Monitor API. Written using the wonderful
320641d Moved directories
jordanbrock authored
3 # Flickr interface by Scott Raymond as a guide on how to access remote web services
4 #
5 # For more information on the Campaign Monitor API, visit http://campaignmonitor.com/api
cc63f93 @Empact Split out classes for better organization
Empact authored
6 #
320641d Moved directories
jordanbrock authored
7 # Author:: Jordan Brock <jordan@spintech.com.au>
8 # Copyright:: Copyright (c) 2006 Jordan Brock <jordan@spintech.com.au>
9 # License:: MIT <http://www.opensource.org/licenses/mit-license.php>
10 #
11 # USAGE:
12 # require 'campaign_monitor'
13 # cm = CampaignMonitor.new(API_KEY) # creates a CampaignMonitor object
14 # # Can set CAMPAIGN_MONITOR_API_KEY in environment.rb
cc63f93 @Empact Split out classes for better organization
Empact authored
15 # cm.clients # Returns an array of clients associated with
320641d Moved directories
jordanbrock authored
16 # # the user account
17 # cm.campaigns(client_id)
18 # cm.lists(client_id)
19 # cm.add_subscriber(list_id, email, name)
20 #
21 # CLIENT
22 # client = Client.new(client_id)
23 # client.lists
24 # client.campaigns
cc63f93 @Empact Split out classes for better organization
Empact authored
25 #
320641d Moved directories
jordanbrock authored
26 # LIST
27 # list = List.new(list_id)
28 # list.add_subscriber(email, name)
29 # list.remove_subscriber(email)
30 # list.active_subscribers(date)
31 # list.unsubscribed(date)
32 # list.bounced(date)
33 #
34 # CAMPAIGN
35 # campaign = Campaign.new(campaign_id)
36 # campaign.clicks
37 # campaign.opens
38 # campaign.bounces
39 # campaign.unsubscribes
40 # campaign.number_recipients
41 # campaign.number_clicks
42 # campaign.number_opens
43 # campaign.number_bounces
44 # campaign.number_unsubscribes
cc63f93 @Empact Split out classes for better organization
Empact authored
45 #
320641d Moved directories
jordanbrock authored
46 #
47 # SUBSCRIBER
48 # subscriber = Subscriber.new(email)
49 # subscriber.add(list_id)
50 # subscriber.unsubscribe(list_id)
51 #
52 # Data Types
53 # SubscriberBounce
54 # SubscriberClick
55 # SubscriberOpen
56 # SubscriberUnsubscribe
57 # Result
58 #
59
cc63f93 @Empact Split out classes for better organization
Empact authored
60 require 'rubygems'
320641d Moved directories
jordanbrock authored
61 require 'cgi'
62 require 'net/http'
cc63f93 @Empact Split out classes for better organization
Empact authored
63 require 'xmlsimple'
64 require 'date'
65
6a0ad5a fix to prevent error message on "require 'campaign_monitor'"
Christoph Schiessl authored
66 require File.join(File.dirname(__FILE__), 'campaign_monitor/helpers.rb')
67 require File.join(File.dirname(__FILE__), 'campaign_monitor/client.rb')
68 require File.join(File.dirname(__FILE__), 'campaign_monitor/list.rb')
69 require File.join(File.dirname(__FILE__), 'campaign_monitor/subscriber.rb')
70 require File.join(File.dirname(__FILE__), 'campaign_monitor/result.rb')
71 require File.join(File.dirname(__FILE__), 'campaign_monitor/campaign.rb')
320641d Moved directories
jordanbrock authored
72
73 class CampaignMonitor
60d2173 @jeremyw a bit of cleanup
jeremyw authored
74 include CampaignMonitor::Helpers
75
e6f653c Refactoring. Added support for SOAP calls in List. DRYed up result ha…
patientslikeme authored
76 attr_reader :api_key, :api_url
77
320641d Moved directories
jordanbrock authored
78 # Replace this API key with your own (http://www.campaignmonitor.com/api/)
79 def initialize(api_key=CAMPAIGN_MONITOR_API_KEY)
80 @api_key = api_key
e6f653c Refactoring. Added support for SOAP calls in List. DRYed up result ha…
patientslikeme authored
81 @api_url = 'http://api.createsend.com/api/api.asmx'
320641d Moved directories
jordanbrock authored
82 end
e6f653c Refactoring. Added support for SOAP calls in List. DRYed up result ha…
patientslikeme authored
83
320641d Moved directories
jordanbrock authored
84
cc63f93 @Empact Split out classes for better organization
Empact authored
85 # Takes a CampaignMonitor API method name and set of parameters;
320641d Moved directories
jordanbrock authored
86 # returns an XmlSimple object with the response
21606e5 @Empact Remove unnecessary arg collections
Empact authored
87 def request(method, params)
867c977 use libxml-ruby if possible
Andy Shen authored
88 response = PARSER.xml_in(http_get(request_url(method, params)), { 'keeproot' => false,
cc63f93 @Empact Split out classes for better organization
Empact authored
89 'forcearray' => %w[List Campaign Subscriber Client SubscriberOpen SubscriberUnsubscribe SubscriberClick SubscriberBounce],
c6e39b5 need to remove d1p1:type since campaign monitor will not be processin…
Andy Shen authored
90 'noattr' => true })
91 response.delete('d1p1:type')
320641d Moved directories
jordanbrock authored
92 response
93 end
cc63f93 @Empact Split out classes for better organization
Empact authored
94
320641d Moved directories
jordanbrock authored
95 # Takes a CampaignMonitor API method name and set of parameters; returns the correct URL for the REST API.
21606e5 @Empact Remove unnecessary arg collections
Empact authored
96 def request_url(method, params={})
e6f653c Refactoring. Added support for SOAP calls in List. DRYed up result ha…
patientslikeme authored
97 params.merge!('ApiKey' => api_key)
551349f @jeremyw removed dependency on ActiveSupport, added a few basic unit tests
jeremyw authored
98
99 query = params.collect do |key, value|
100 "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
101 end.sort * '&'
102
103 "#{api_url}/#{method}?#{query}"
320641d Moved directories
jordanbrock authored
104 end
cc63f93 @Empact Split out classes for better organization
Empact authored
105
320641d Moved directories
jordanbrock authored
106 # Does an HTTP GET on a given URL and returns the response body
107 def http_get(url)
108 Net::HTTP.get_response(URI.parse(url)).body.to_s
109 end
cc63f93 @Empact Split out classes for better organization
Empact authored
110
320641d Moved directories
jordanbrock authored
111 # By overriding the method_missing method, it is possible to easily support all of the methods
112 # available in the API
f70bf90 fix bug introduced in 21606e565b9a15f44eaa3e04f51576b55e3bbe46
Andy Shen authored
113 def method_missing(method_id, params = {})
21606e5 @Empact Remove unnecessary arg collections
Empact authored
114 request(method_id.id2name.gsub(/_/, '.'), params)
320641d Moved directories
jordanbrock authored
115 end
cc63f93 @Empact Split out classes for better organization
Empact authored
116
320641d Moved directories
jordanbrock authored
117 # Returns an array of Client objects associated with the API Key
118 #
119 # Example
120 # @cm = CampaignMonitor.new()
121 # @clients = @cm.clients
cc63f93 @Empact Split out classes for better organization
Empact authored
122 #
320641d Moved directories
jordanbrock authored
123 # for client in @clients
124 # puts client.name
125 # end
126 def clients
3245628 @jeremyw Some refactoring to DRY up Client class. Added some content to readme.
jeremyw authored
127 handle_response(User_GetClients()) do |response|
4b51e74 @deploy-patientslikeme Remove #to_i call on ID. They're now hex strings.
deploy-patientslikeme authored
128 response["Client"].collect{|c| Client.new(c["ClientID"], c["Name"])}
e6f653c Refactoring. Added support for SOAP calls in List. DRYed up result ha…
patientslikeme authored
129 end
130 end
131
132 def system_date
3245628 @jeremyw Some refactoring to DRY up Client class. Added some content to readme.
jeremyw authored
133 User_GetSystemDate()
320641d Moved directories
jordanbrock authored
134 end
135
e6f653c Refactoring. Added support for SOAP calls in List. DRYed up result ha…
patientslikeme authored
136 def parsed_system_date
137 DateTime.strptime(system_date, timestamp_format)
138 end
139
320641d Moved directories
jordanbrock authored
140 # Returns an array of Campaign objects associated with the specified Client ID
cc63f93 @Empact Split out classes for better organization
Empact authored
141 #
320641d Moved directories
jordanbrock authored
142 # Example
143 # @cm = CampaignMonitor.new()
144 # @campaigns = @cm.campaigns(12345)
cc63f93 @Empact Split out classes for better organization
Empact authored
145 #
320641d Moved directories
jordanbrock authored
146 # for campaign in @campaigns
147 # puts campaign.subject
148 # end
149 def campaigns(client_id)
3245628 @jeremyw Some refactoring to DRY up Client class. Added some content to readme.
jeremyw authored
150 handle_response(Client_GetCampaigns("ClientID" => client_id)) do |response|
4b51e74 @deploy-patientslikeme Remove #to_i call on ID. They're now hex strings.
deploy-patientslikeme authored
151 response["Campaign"].collect{|c| Campaign.new(c["CampaignID"], c["Subject"], c["SentDate"], c["TotalRecipients"].to_i)}
320641d Moved directories
jordanbrock authored
152 end
153 end
cc63f93 @Empact Split out classes for better organization
Empact authored
154
320641d Moved directories
jordanbrock authored
155 # Returns an array of Subscriber Lists for the specified Client ID
156 #
157 # Example
158 # @cm = CampaignMonitor.new()
159 # @lists = @cm.lists(12345)
cc63f93 @Empact Split out classes for better organization
Empact authored
160 #
320641d Moved directories
jordanbrock authored
161 # for list in @lists
162 # puts list.name
163 # end
164 def lists(client_id)
3245628 @jeremyw Some refactoring to DRY up Client class. Added some content to readme.
jeremyw authored
165 handle_response(Client_GetLists("ClientID" => client_id)) do |response|
4b51e74 @deploy-patientslikeme Remove #to_i call on ID. They're now hex strings.
deploy-patientslikeme authored
166 response["List"].collect{|l| List.new(l["ListID"], l["Name"])}
320641d Moved directories
jordanbrock authored
167 end
168 end
cc63f93 @Empact Split out classes for better organization
Empact authored
169
320641d Moved directories
jordanbrock authored
170 # A quick method of adding a subscriber to a list. Returns a Result object
171 #
172 # Example
173 # @cm = CampaignMonitor.new()
174 # result = @cm.add_subscriber(12345, "ralph.wiggum@simpsons.net", "Ralph Wiggum")
cc63f93 @Empact Split out classes for better organization
Empact authored
175 #
2030c98 @Empact Create Result directly from the xml and add #succeeded? & #failed? he…
Empact authored
176 # if result.succeeded?
320641d Moved directories
jordanbrock authored
177 # puts "Subscriber Added to List"
178 # end
179 def add_subscriber(list_id, email, name)
2030c98 @Empact Create Result directly from the xml and add #succeeded? & #failed? he…
Empact authored
180 Result.new(Subscriber_Add("ListID" => list_id, "Email" => email, "Name" => name))
320641d Moved directories
jordanbrock authored
181 end
551349f @jeremyw removed dependency on ActiveSupport, added a few basic unit tests
jeremyw authored
182
d64b4fe @jeremyw Fixed adding subscribers with custom fields
jeremyw authored
183 def using_soap
184 driver = wsdl_driver_factory.create_rpc_driver
185 response = yield(driver)
186 driver.reset_stream
187
188 response
189 end
cc63f93 @Empact Split out classes for better organization
Empact authored
190
191 # Encapsulates
320641d Moved directories
jordanbrock authored
192 class SubscriberBounce
193 attr_reader :email_address, :bounce_type, :list_id
cc63f93 @Empact Split out classes for better organization
Empact authored
194
320641d Moved directories
jordanbrock authored
195 def initialize(email_address, list_id, bounce_type)
196 @email_address = email_address
197 @bounce_type = bounce_type
198 @list_id = list_id
199 end
200 end
cc63f93 @Empact Split out classes for better organization
Empact authored
201
202 # Encapsulates
320641d Moved directories
jordanbrock authored
203 class SubscriberOpen
204 attr_reader :email_address, :list_id, :opens
cc63f93 @Empact Split out classes for better organization
Empact authored
205
320641d Moved directories
jordanbrock authored
206 def initialize(email_address, list_id, opens)
207 @email_address = email_address
208 @list_id = list_id
209 @opens = opens
210 end
211 end
cc63f93 @Empact Split out classes for better organization
Empact authored
212
213 # Encapsulates
320641d Moved directories
jordanbrock authored
214 class SubscriberClick
215 attr_reader :email_address, :list_id, :clicked_links
cc63f93 @Empact Split out classes for better organization
Empact authored
216
320641d Moved directories
jordanbrock authored
217 def initialize(email_address, list_id, clicked_links)
218 @email_address = email_address
219 @list_id = list_id
220 @clicked_links = clicked_links
221 end
222 end
cc63f93 @Empact Split out classes for better organization
Empact authored
223
224 # Encapsulates
320641d Moved directories
jordanbrock authored
225 class SubscriberUnsubscribe
226 attr_reader :email_address, :list_id
cc63f93 @Empact Split out classes for better organization
Empact authored
227
320641d Moved directories
jordanbrock authored
228 def initialize(email_address, list_id)
229 @email_address = email_address
230 @list_id = list_id
231 end
232 end
e6f653c Refactoring. Added support for SOAP calls in List. DRYed up result ha…
patientslikeme authored
233
d64b4fe @jeremyw Fixed adding subscribers with custom fields
jeremyw authored
234 protected
235
236 def wsdl_driver_factory
237 SOAP::WSDLDriverFactory.new("#{api_url}?WSDL")
238 end
239
867c977 use libxml-ruby if possible
Andy Shen authored
240 end
241
242 # If libxml is installed, we use the FasterXmlSimple library, that provides most of the functionality of XmlSimple
243 # except it uses the xml/libxml library for xml parsing (rather than REXML).
244 # If libxml isn't installed, we just fall back on XmlSimple.
245
246 PARSER =
247 begin
248 require 'xml/libxml'
249 # Older version of libxml aren't stable (bus error when requesting attributes that don't exist) so we
250 # have to use a version greater than '0.3.8.2'.
251 raise LoadError unless XML::Parser::VERSION > '0.3.8.2'
252 $:.push(File.join(File.dirname(__FILE__), '..', 'support', 'faster-xml-simple', 'lib'))
253 require 'faster_xml_simple'
254 p 'Using libxml-ruby'
255 FasterXmlSimple
256 rescue LoadError
76fae72 load rexml security patch gem if not using libxml-ruby
Andy Shen authored
257 begin
258 require 'rexml-expansion-fix'
259 rescue LoadError => e
260 p 'Cannot load rexml security patch'
261 end
867c977 use libxml-ruby if possible
Andy Shen authored
262 XmlSimple
263 end
Something went wrong with that request. Please try again.