Skip to content
This repository has been archived by the owner on Aug 9, 2022. It is now read-only.

Commit

Permalink
Merge branch 'yyyc514/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
reidab committed Apr 19, 2009
2 parents 474688f + a26de1d commit a9eb2f5
Show file tree
Hide file tree
Showing 19 changed files with 1,150 additions and 189 deletions.
4 changes: 3 additions & 1 deletion Rakefile
Expand Up @@ -15,9 +15,11 @@ task :install => [:package] do
sh %{sudo gem install pkg/#{spec.name}-#{spec.version}}
end

task :default => [:test]

Rake::TestTask.new do |t|
t.libs << "test"
t.test_files = FileList['test/test*.rb']
t.test_files = FileList['test/*_test.rb']
t.verbose = true
end

Expand Down
6 changes: 6 additions & 0 deletions TODO
@@ -0,0 +1,6 @@
* Decide on convents about when to raise and when to return true/false

So far I've kind of taken the stance if we're retrieving a list and there
are only two possible failures (APIKey or primary key) to raise if there
is an error but for update/save/create operations to return true/false
and then let the user probe @object.result for more details
13 changes: 11 additions & 2 deletions campaign_monitor.gemspec
@@ -1,7 +1,7 @@
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'campaign_monitor'
s.version = "1.3.0"
s.version = "1.3.2.2"
s.summary = 'Provides access to the Campaign Monitor API.'
s.description = <<-EOF
A simple wrapper class that provides basic access to the Campaign Monitor API.
Expand All @@ -15,29 +15,38 @@ Gem::Specification.new do |s|
s.require_path = 'lib'

s.add_dependency 'xml-simple', ['>= 1.0.11']

s.add_dependency 'soap4r', ['>= 1.5.8']

s.files = [
'campaign_monitor.gemspec',
'init.rb',
'install.rb',
'MIT-LICENSE',
'Rakefile',
'README.rdoc',
'TODO',

'lib/campaign_monitor.rb',
'lib/campaign_monitor/base.rb',
'lib/campaign_monitor/misc.rb',
'lib/campaign_monitor/campaign.rb',
'lib/campaign_monitor/client.rb',
'lib/campaign_monitor/helpers.rb',
'lib/campaign_monitor/list.rb',
'lib/campaign_monitor/result.rb',
'lib/campaign_monitor/subscriber.rb',

'support/class_enhancements.rb',
'support/faster-xml-simple/lib/faster_xml_simple.rb',
'support/faster-xml-simple/test/regression_test.rb',
'support/faster-xml-simple/test/test_helper.rb',
'support/faster-xml-simple/test/xml_simple_comparison_test.rb',

'test/campaign_monitor_test.rb',
'test/campaign_test.rb',
'test/client_test.rb',
'test/list_test.rb',
'test/test_helper.rb'
]

s.test_file = 'test/campaign_monitor_test.rb'
Expand Down
163 changes: 85 additions & 78 deletions lib/campaign_monitor.rb
@@ -1,4 +1,20 @@
# CampaignMonitor
require 'rubygems'
require 'cgi'
require 'net/http'
require 'xmlsimple'
require 'date'
gem 'soap4r'

require File.join(File.dirname(__FILE__), '../support/class_enhancements.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/helpers.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/misc.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/base.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/client.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/list.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/subscriber.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/result.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/campaign.rb')

# A wrapper class to access the Campaign Monitor API. Written using the wonderful
# Flickr interface by Scott Raymond as a guide on how to access remote web services
#
Expand All @@ -18,20 +34,34 @@
# cm.lists(client_id)
# cm.add_subscriber(list_id, email, name)
#
# CLIENT
# client = Client.new(client_id)
# client.lists
# client.campaigns
# == CLIENT
# client = Client[client_id] # find an existing client
# client = Client.new(attributes)
# client.Create
# client.Delete
# client.GetDetail
# client.UpdateAccessAndBilling
# client.UpdateBasics
# client.update # update basics, access, and billing
# client.lists # OR
# client.GetLists
# client.lists.build # to create a new unsaved list for a client
# client.campaigns # OR
# client.GetCampaigns
#
# LIST
# list = List.new(list_id)
# == LIST
# list = List[list_id] # find an existing list
# list = List.new(attributes)
# list.Create
# list.Delete
# list.Update
# list.add_subscriber(email, name)
# list.remove_subscriber(email)
# list.active_subscribers(date)
# list.unsubscribed(date)
# list.bounced(date)
#
# CAMPAIGN
# == CAMPAIGN
# campaign = Campaign.new(campaign_id)
# campaign.clicks
# campaign.opens
Expand All @@ -44,52 +74,51 @@
# campaign.number_unsubscribes
#
#
# SUBSCRIBER
# == SUBSCRIBER
# subscriber = Subscriber.new(email)
# subscriber.add(list_id)
# subscriber.unsubscribe(list_id)
#
# Data Types
# == Data Types
# SubscriberBounce
# SubscriberClick
# SubscriberOpen
# SubscriberUnsubscribe
# Result
#

require 'rubygems'
require 'cgi'
require 'net/http'
require 'xmlsimple'
require 'date'

require File.join(File.dirname(__FILE__), 'campaign_monitor/helpers.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/client.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/list.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/subscriber.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/result.rb')
require File.join(File.dirname(__FILE__), 'campaign_monitor/campaign.rb')

class CampaignMonitor
include CampaignMonitor::Helpers

class InvalidAPIKey < StandardError
end

class ApiError < StandardError
end

attr_reader :api_key, :api_url

# Replace this API key with your own (http://www.campaignmonitor.com/api/)
def initialize(api_key=CAMPAIGN_MONITOR_API_KEY)
@api_key = api_key
@api_url = 'http://api.createsend.com/api/api.asmx'
CampaignMonitor::Base.client=self
end


# Takes a CampaignMonitor API method name and set of parameters;
# returns an XmlSimple object with the response
def request(method, params)
response = PARSER.xml_in(http_get(request_url(method, params)), { 'keeproot' => false,
'forcearray' => %w[List Campaign Subscriber Client SubscriberOpen SubscriberUnsubscribe SubscriberClick SubscriberBounce],
'noattr' => true })
response.delete('d1p1:type')
response
request_xml=http_get(request_url(method, params))
begin
response = PARSER.xml_in(request_xml, { 'keeproot' => false,
'forcearray' => %w[List Campaign Subscriber Client SubscriberOpen SubscriberUnsubscribe SubscriberClick SubscriberBounce],
'noattr' => true })
response.delete('d1p1:type')
response.delete("d1p1:http://www.w3.org/2001/XMLSchema-instance:type")
response
# rescue XML::Parser::ParseError
rescue XML::Error
{ "Code" => 500, "Message" => request_xml.split(/\r?\n/).first, "FullError" => request_xml }
end
end

# Takes a CampaignMonitor API method name and set of parameters; returns the correct URL for the REST API.
Expand All @@ -105,13 +134,17 @@ def request_url(method, params={})

# Does an HTTP GET on a given URL and returns the response body
def http_get(url)
Net::HTTP.get_response(URI.parse(url)).body.to_s
response=Net::HTTP.get_response(URI.parse(url))
response.body.to_s
end

# By overriding the method_missing method, it is possible to easily support all of the methods
# available in the API
def method_missing(method_id, params = {})
request(method_id.id2name.gsub(/_/, '.'), params)
puts " CM: #{method_id} (#{params.inspect})" if $debug
res=request(method_id.id2name.gsub(/_/, '.'), params)
puts " returning: #{res.inspect}" if $debug
res
end

# Returns an array of Client objects associated with the API Key
Expand All @@ -125,10 +158,14 @@ def method_missing(method_id, params = {})
# end
def clients
handle_response(User_GetClients()) do |response|
response["Client"].collect{|c| Client.new(c["ClientID"], c["Name"])}
response["Client"].collect{|c| Client.new({"ClientID" => c["ClientID"], "CompanyName" => c["Name"]})}
end
end

def new_client
Client.new(nil)
end

def system_date
User_GetSystemDate()
end
Expand All @@ -137,6 +174,18 @@ def parsed_system_date
DateTime.strptime(system_date, timestamp_format)
end

def countries
handle_response(User_GetCountries()) do | response |
response["string"]
end
end

def timezones
handle_response(User_GetTimezones()) do | response |
response["string"]
end
end

# Returns an array of Campaign objects associated with the specified Client ID
#
# Example
Expand All @@ -148,7 +197,7 @@ def parsed_system_date
# end
def campaigns(client_id)
handle_response(Client_GetCampaigns("ClientID" => client_id)) do |response|
response["Campaign"].collect{|c| Campaign.new(c["CampaignID"], c["Subject"], c["SentDate"], c["TotalRecipients"].to_i)}
response["Campaign"].collect{|c| Campaign.new(c) }
end
end

Expand All @@ -163,7 +212,7 @@ def campaigns(client_id)
# end
def lists(client_id)
handle_response(Client_GetLists("ClientID" => client_id)) do |response|
response["List"].collect{|l| List.new(l["ListID"], l["Name"])}
response["List"].collect{|l| List.new({"ListID" => l["ListID"], "Title" => l["Name"]})}
end
end

Expand All @@ -182,55 +231,13 @@ def add_subscriber(list_id, email, name)

def using_soap
driver = wsdl_driver_factory.create_rpc_driver
driver.wiredump_dev = STDERR if $debug
response = yield(driver)
driver.reset_stream

response
end

# Encapsulates
class SubscriberBounce
attr_reader :email_address, :bounce_type, :list_id

def initialize(email_address, list_id, bounce_type)
@email_address = email_address
@bounce_type = bounce_type
@list_id = list_id
end
end

# Encapsulates
class SubscriberOpen
attr_reader :email_address, :list_id, :opens

def initialize(email_address, list_id, opens)
@email_address = email_address
@list_id = list_id
@opens = opens
end
end

# Encapsulates
class SubscriberClick
attr_reader :email_address, :list_id, :clicked_links

def initialize(email_address, list_id, clicked_links)
@email_address = email_address
@list_id = list_id
@clicked_links = clicked_links
end
end

# Encapsulates
class SubscriberUnsubscribe
attr_reader :email_address, :list_id

def initialize(email_address, list_id)
@email_address = email_address
@list_id = list_id
end
end

protected

def wsdl_driver_factory
Expand Down
55 changes: 55 additions & 0 deletions lib/campaign_monitor/base.rb
@@ -0,0 +1,55 @@
class CampaignMonitor
# Provides access to the lists and campaigns associated with a client
class Base

attr_reader :result, :attributes, :cm_client

@@client=nil

def self.client
@@client
end

def self.client=(a)
@@client=a
end

def [](k)
if m=self.class.get_data_types[k]
@attributes[k].send(m)
else
@attributes[k]
end
end

def []=(k,v)
@attributes[k]=v
end

def initialize(*args)
@attributes={}
@cm_client=@@client
end

# id and name field stuff

inherited_property "id_field", "id"
inherited_property "name_field", "name"
inherited_property "data_types", {}

def id
@attributes[self.class.get_id_field]
end

def id=(v)
@attributes[self.class.get_id_field]=v
end

def name
@attributes[self.class.get_name_field]
end

end

end

0 comments on commit a9eb2f5

Please sign in to comment.