exact-target 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/LICENSE +481 -0
- data/Manifest +22 -0
- data/README.rdoc +170 -0
- data/Rakefile +40 -0
- data/exact-target.gemspec +32 -0
- data/lib/exact_target.rb +109 -0
- data/lib/exact_target/builder_ext.rb +11 -0
- data/lib/exact_target/configuration.rb +42 -0
- data/lib/exact_target/error.rb +16 -0
- data/lib/exact_target/net_https_hack.rb +8 -0
- data/lib/exact_target/request_builder.rb +284 -0
- data/lib/exact_target/response_class.erb +30 -0
- data/lib/exact_target/response_classes.rb +61 -0
- data/lib/exact_target/response_handler.rb +167 -0
- data/lib/exact_target/string_ext.rb +22 -0
- data/spec/exact_target/net_https_hack_spec.rb +8 -0
- data/spec/exact_target/response_handler_spec.rb +15 -0
- data/spec/exact_target/string_ext_spec.rb +13 -0
- data/spec/exact_target_data.yml +133 -0
- data/spec/exact_target_spec.rb +382 -0
- data/spec/spec.opts +4 -0
- data/spec/subscriber_list_spec.rb +290 -0
- metadata +95 -0
data/Manifest
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
LICENSE
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
lib/exact_target.rb
|
6
|
+
lib/exact_target/builder_ext.rb
|
7
|
+
lib/exact_target/configuration.rb
|
8
|
+
lib/exact_target/error.rb
|
9
|
+
lib/exact_target/net_https_hack.rb
|
10
|
+
lib/exact_target/request_builder.rb
|
11
|
+
lib/exact_target/response_class.erb
|
12
|
+
lib/exact_target/response_classes.rb
|
13
|
+
lib/exact_target/response_handler.rb
|
14
|
+
lib/exact_target/string_ext.rb
|
15
|
+
spec/exact_target/net_https_hack_spec.rb
|
16
|
+
spec/exact_target/response_handler_spec.rb
|
17
|
+
spec/exact_target/string_ext_spec.rb
|
18
|
+
spec/exact_target_data.yml
|
19
|
+
spec/exact_target_spec.rb
|
20
|
+
spec/spec.opts
|
21
|
+
spec/subscriber_list_spec.rb
|
22
|
+
Manifest
|
data/README.rdoc
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
= ExactTarget - A Ruby interface to the ExactTarget API.
|
2
|
+
|
3
|
+
== Introduction
|
4
|
+
|
5
|
+
ExactTarget is an email marketing solution provider with an
|
6
|
+
http-based api for performing various tasks such as managing
|
7
|
+
mailing lists, managing subscribers, and queueing up email
|
8
|
+
jobs. This gem is a pure ruby library for accessing that api.
|
9
|
+
|
10
|
+
== Resources
|
11
|
+
|
12
|
+
=== Installation
|
13
|
+
|
14
|
+
gem install exact-target
|
15
|
+
|
16
|
+
=== Git Repository
|
17
|
+
|
18
|
+
http://github.com/ePublishing/exact_target
|
19
|
+
|
20
|
+
== Prerequisites
|
21
|
+
|
22
|
+
The ExactTarget gem depends on both Nokogiri and Builder. Both
|
23
|
+
should be installed automatically when them gem is installed
|
24
|
+
(if not already installed).
|
25
|
+
|
26
|
+
== Notes
|
27
|
+
|
28
|
+
Currently this is a partial implementation. None of the
|
29
|
+
tracking functionality is yet implemented.
|
30
|
+
|
31
|
+
== Usage
|
32
|
+
|
33
|
+
=== Setup/Configuration
|
34
|
+
|
35
|
+
require 'exact_target'
|
36
|
+
|
37
|
+
ExactTarget.configure do |config|
|
38
|
+
config.username = 'my_username'
|
39
|
+
config.password = 'my_password'
|
40
|
+
end
|
41
|
+
|
42
|
+
=== Account Information
|
43
|
+
|
44
|
+
# Retrieve subscriber attributes
|
45
|
+
ExactTarget.accountinfo_retrieve_attrbs
|
46
|
+
|
47
|
+
=== List Management
|
48
|
+
|
49
|
+
# Retrieve all list ids
|
50
|
+
ExactTarget.list_retrieve
|
51
|
+
|
52
|
+
# Retrieve list id for given name
|
53
|
+
ExactTarget.list_retrieve('Some List')
|
54
|
+
|
55
|
+
# Retrieve list information by list id
|
56
|
+
ExactTarget.list_retrieve(15123512)
|
57
|
+
|
58
|
+
# Create new list
|
59
|
+
ExactTarget.list_add("My Test List", :private)
|
60
|
+
|
61
|
+
# Rename a list
|
62
|
+
ExactTarget.list_edit(15123512, "My RENAMED Test List")
|
63
|
+
|
64
|
+
# Import new or updated subscribers en mass from a comma-delimited
|
65
|
+
# or tab-delimited file into an existing subscriber list.
|
66
|
+
ExactTarget.list_import(15123512, 'sometestfile.txt', ['Email Address', 'Field 2', ...])
|
67
|
+
ExactTarget.list_import([id_1, id2, ...], 'sometestfile.txt', ['Email Address', 'Field 2', ...])
|
68
|
+
|
69
|
+
# Request an update on the status of an import.
|
70
|
+
ExactTarget.list_importstatus(some_import_id)
|
71
|
+
|
72
|
+
# Retrieve the profile and preference attributes for all subscribers
|
73
|
+
# (or all subscribers with a certain status) on a specified list.
|
74
|
+
ExactTarget.list_retrieve_sub(42, 'Active')
|
75
|
+
|
76
|
+
# Delete a list and all subscribers who belong to the list.
|
77
|
+
ExactTarget.list_delete(15123512)
|
78
|
+
|
79
|
+
# Retrieve all groups in your account.
|
80
|
+
ExactTarget.list_retrievegroups
|
81
|
+
|
82
|
+
# Refresh the membership of an attribute-based (rule-based) group
|
83
|
+
# to reflect any changes to your subscribers' attribute values.
|
84
|
+
ExactTarget.list_refresh_group(3514)
|
85
|
+
|
86
|
+
# Request an update on the status of a group refresh.
|
87
|
+
ExactTarget.batch_inquire(8912)
|
88
|
+
|
89
|
+
=== Subscriber Management
|
90
|
+
|
91
|
+
# Add a subscriber to a list.
|
92
|
+
subscriber = ExactTarget::Subscriber.new.tap do |s|
|
93
|
+
s.email_address = 'test.person@company.com'
|
94
|
+
s.full_name = 'George Wills'
|
95
|
+
end
|
96
|
+
ExactTarget.subscriber_add(1234, subscriber,
|
97
|
+
:status => :active,
|
98
|
+
:update => true,
|
99
|
+
:ChannelMemberID => 5678)
|
100
|
+
|
101
|
+
# Update the attributes (including email address) and/or status of
|
102
|
+
# an existing subscriber. Can be used to reactivate a subscriber
|
103
|
+
# who was placed on the master unsubscribe list.
|
104
|
+
subscriber = ExactTarget::Subscriber.new.tap do |s|
|
105
|
+
s.email_address = 'new.email@company.com'
|
106
|
+
s.full_name = 'Frank Jones'
|
107
|
+
end
|
108
|
+
ExactTarget.subscriber_edit(63718, 'previous@email.com', subscriber,
|
109
|
+
:status => :unsub,
|
110
|
+
:reason => 'Some reason ...',
|
111
|
+
:ChannelMemberID => 5678)
|
112
|
+
|
113
|
+
# Retrieve all profile and preference attribute data entered for a
|
114
|
+
# subscriber on a specific list, or retrieve all attribute data for
|
115
|
+
# a subscriber and all lists to which the subscriber belongs.
|
116
|
+
ExactTarget.subscriber_retrieve(123456, 'someone@example.com')
|
117
|
+
ExactTarget.subscriber_retrieve(123456789)
|
118
|
+
|
119
|
+
# Delete a subscriber from your database, or remove a subscriber
|
120
|
+
# from a specified list.
|
121
|
+
ExactTarget.subscriber_delete(112233445566, 'bob@hotmail.com')
|
122
|
+
ExactTarget.subscriber_delete(112233445566)
|
123
|
+
|
124
|
+
# Place a subscriber's (or multiple subscribers') email address on
|
125
|
+
# your Master Unsubscribe list so that the email address is never
|
126
|
+
# added to any of your subscriber lists.
|
127
|
+
ExactTarget.subscriber_masterunsub 'someone@email.com', 'someone.else@email.com', ...
|
128
|
+
|
129
|
+
=== Mailing Management
|
130
|
+
|
131
|
+
# Retrieve all email IDs or filter by email name and/or date range.
|
132
|
+
ExactTarget.email_retrieve
|
133
|
+
ExactTarget.email_retrieve('Welcome to Fortune One!')
|
134
|
+
ExactTarget.email_retrieve(:start_date => Date.parse('2008-09-15'),
|
135
|
+
:end_date => Date.parse('2008-10-15'))
|
136
|
+
ExactTarget.email_retrieve('Welcome to Fortune One!',
|
137
|
+
:start_date => Date.parse('2008-09-15'),
|
138
|
+
:end_date => Date.parse('2008-10-15'))
|
139
|
+
|
140
|
+
# Create an email from HTML that you send to the ExactTarget application via an API call.
|
141
|
+
ExactTarget.email_add('Your email name', 'Your subject line', :body => 'Your HTML email body')
|
142
|
+
ExactTarget.email_add('Your email name', 'Your subject line', :file => 'Filename')
|
143
|
+
|
144
|
+
# Create the text version of an email, which will be displayed
|
145
|
+
# to any subscriber whose email client does not support HTML email.
|
146
|
+
ExactTarget.email_add_text(email_id, :body => 'Your text email body')
|
147
|
+
ExactTarget.email_add_text(email_id, :file => 'Filename')
|
148
|
+
|
149
|
+
# Retrieve the body of the HTML version of any email in your account.
|
150
|
+
ExactTarget.email_retrieve_body(email_id)
|
151
|
+
|
152
|
+
=== Job Initiation
|
153
|
+
|
154
|
+
# Kick off a mailing (test or live)
|
155
|
+
ExactTarget.job_send(email_id,
|
156
|
+
[list_id_1, list_id_2, ...],
|
157
|
+
:suppress_ids => [list_id_3, ...],
|
158
|
+
:from_name => 'John Doe',
|
159
|
+
:from_email => 'jd@nowhere.com',
|
160
|
+
:additional => 'additional information for job',
|
161
|
+
:multipart_mime => true,
|
162
|
+
:track_links => false,
|
163
|
+
:send_date => '5/3/2011',
|
164
|
+
:send_time => '17:35',
|
165
|
+
:test_send => true)
|
166
|
+
|
167
|
+
---
|
168
|
+
Author:: David McCullars <mailto:dmccullars@ePublishing.com>
|
169
|
+
Copyright:: (C) 2011 ePublishing
|
170
|
+
Licence:: GPL[http://www.gnu.org/copyleft/gpl.html]
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'echoe'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
|
6
|
+
task :default => :gem
|
7
|
+
|
8
|
+
Echoe.new("exact-target") do |s|
|
9
|
+
s.author = "David McCullars"
|
10
|
+
s.project = "exact-target"
|
11
|
+
s.email = "dmccullars@ePublishing.com"
|
12
|
+
s.url = "http://github.com/ePublishing/exact_target"
|
13
|
+
s.docs_host = "http://rdoc.info/github/ePublishing/exact_target/master/frames"
|
14
|
+
s.rdoc_pattern = /README|TODO|LICENSE|CHANGELOG|BENCH|COMPAT|exceptions|behaviors|exact-target.rb/
|
15
|
+
s.clean_pattern += ["ext/lib", "ext/include", "ext/share", "ext/libexact-target-?.??", "ext/bin", "ext/conftest.dSYM"]
|
16
|
+
s.summary = <<DONE
|
17
|
+
This is a pure-ruby implementation of the ExactTarget api.
|
18
|
+
For more information consule http://www.exacttarget.com/.
|
19
|
+
DONE
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run all specs"
|
23
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
24
|
+
t.spec_opts = ['--options', %q("spec/spec.opts")]
|
25
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
t.rcov = true
|
27
|
+
t.rcov_opts = ['--exclude', 'spec']
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'generate API documentation to doc/rdocs/index.html'
|
31
|
+
Rake::RDocTask.new do |rd|
|
32
|
+
rd.rdoc_dir = 'doc/rdocs'
|
33
|
+
rd.main = 'README.rdoc'
|
34
|
+
rd.rdoc_files.include 'README.rdoc', 'CHANGELOG', 'lib/**/*.rb'
|
35
|
+
rd.rdoc_files.exclude '**/string_ext.rb', '**/net_https_hack.rb'
|
36
|
+
rd.options << '--inline-source'
|
37
|
+
rd.options << '--line-numbers'
|
38
|
+
rd.options << '--all'
|
39
|
+
rd.options << '--fileboxes'
|
40
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{exact-target}
|
5
|
+
s.version = "0.0.4"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["David McCullars"]
|
9
|
+
s.date = %q{2011-04-11}
|
10
|
+
s.description = %q{This is a pure-ruby implementation of the ExactTarget api.
|
11
|
+
For more information consule http://www.exacttarget.com/.
|
12
|
+
}
|
13
|
+
s.email = %q{dmccullars@ePublishing.com}
|
14
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.rdoc"]
|
15
|
+
s.files = ["CHANGELOG", "LICENSE", "README.rdoc", "Rakefile", "lib/exact_target.rb", "lib/exact_target/builder_ext.rb", "lib/exact_target/configuration.rb", "lib/exact_target/error.rb", "lib/exact_target/net_https_hack.rb", "lib/exact_target/request_builder.rb", "lib/exact_target/response_class.erb", "lib/exact_target/response_classes.rb", "lib/exact_target/response_handler.rb", "lib/exact_target/string_ext.rb", "spec/exact_target/net_https_hack_spec.rb", "spec/exact_target/response_handler_spec.rb", "spec/exact_target/string_ext_spec.rb", "spec/exact_target_data.yml", "spec/exact_target_spec.rb", "spec/spec.opts", "spec/subscriber_list_spec.rb", "Manifest", "exact-target.gemspec"]
|
16
|
+
s.homepage = %q{http://github.com/ePublishing/exact_target}
|
17
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Exact-target", "--main", "README.rdoc"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubyforge_project = %q{exact-target}
|
20
|
+
s.rubygems_version = %q{1.3.6}
|
21
|
+
s.summary = %q{This is a pure-ruby implementation of the ExactTarget api. For more information consule http://www.exacttarget.com/.}
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 3
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
28
|
+
else
|
29
|
+
end
|
30
|
+
else
|
31
|
+
end
|
32
|
+
end
|
data/lib/exact_target.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
require 'exact_target/net_https_hack'
|
8
|
+
require 'exact_target/builder_ext'
|
9
|
+
require 'exact_target/string_ext'
|
10
|
+
|
11
|
+
require 'exact_target/configuration'
|
12
|
+
require 'exact_target/error'
|
13
|
+
require 'exact_target/request_builder'
|
14
|
+
require 'exact_target/response_classes'
|
15
|
+
require 'exact_target/response_handler'
|
16
|
+
|
17
|
+
# The ExactTarget library is a ruby implementation of the ExactTarget
|
18
|
+
# email marketing api. It allows for list/subscriber management,
|
19
|
+
# email creation, and job initiation.
|
20
|
+
module ExactTarget
|
21
|
+
|
22
|
+
VERSION = File.read(File.expand_path '../../CHANGELOG', __FILE__)[/v([\d\.]+)\./, 1]
|
23
|
+
LOG_PREFIX = "** [ExactTarget] "
|
24
|
+
|
25
|
+
extend ExactTarget::ResponseClasses
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# The builder object is responsible for building the xml for any given request
|
29
|
+
attr_accessor :builder
|
30
|
+
|
31
|
+
# The handler object is responsible for handling the xml response and returning
|
32
|
+
# response data
|
33
|
+
attr_accessor :handler
|
34
|
+
|
35
|
+
# A ExactTarget configuration object. Must act like a hash and return sensible
|
36
|
+
# values for all ExactTarget configuration options. See ExactTarget::Configuration.
|
37
|
+
attr_accessor :configuration
|
38
|
+
|
39
|
+
def configure(username=nil, password=nil)
|
40
|
+
self.configuration ||= Configuration.new
|
41
|
+
configuration.username = username if username
|
42
|
+
configuration.password = password if password
|
43
|
+
yield(configuration) if block_given?
|
44
|
+
self.builder = RequestBuilder.new(configuration)
|
45
|
+
self.handler = ResponseHandler.new(configuration)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def verify_configure
|
50
|
+
raise "ExactTarget must be configured before using" if configuration.nil? or !configuration.valid?
|
51
|
+
end
|
52
|
+
|
53
|
+
def log(level, message)
|
54
|
+
verify_configure
|
55
|
+
configuration.logger.send(level, message) unless configuration.logger.nil?
|
56
|
+
end
|
57
|
+
|
58
|
+
def call(method, *args, &block)
|
59
|
+
verify_configure
|
60
|
+
|
61
|
+
request = builder.send(method, *args, &block)
|
62
|
+
log :debug, "#{LOG_PREFIX}REQUEST: #{request}"
|
63
|
+
|
64
|
+
response = send_to_exact_target(request)
|
65
|
+
log :debug, "#{LOG_PREFIX}RESPONSE: #{response}"
|
66
|
+
|
67
|
+
response = parse_response_xml(response)
|
68
|
+
|
69
|
+
handler.send(method, response)
|
70
|
+
end
|
71
|
+
|
72
|
+
def send_to_exact_target(request)
|
73
|
+
verify_configure
|
74
|
+
uri = URI.parse "#{configuration.base_url}?qf=xml&xml=#{URI.escape request}"
|
75
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
76
|
+
http.use_ssl = configuration.secure?
|
77
|
+
http.open_timeout = configuration.http_open_timeout
|
78
|
+
http.read_timeout = configuration.http_read_timeout
|
79
|
+
resp = http.get(uri.request_uri)
|
80
|
+
if resp.is_a?(Net::HTTPSuccess)
|
81
|
+
resp.body
|
82
|
+
else
|
83
|
+
resp.error!
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Define ExactTarget methods
|
88
|
+
(RequestBuilder.instance_methods(false) & ResponseHandler.instance_methods(false)).each do |m|
|
89
|
+
define_method(m) do |*args, &block|
|
90
|
+
call(m, *args, &block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def parse_response_xml(xml)
|
97
|
+
verify_configure
|
98
|
+
resp = Nokogiri.parse(xml)
|
99
|
+
error = resp.xpath('//error[1]').first
|
100
|
+
error_description = resp.xpath('//error_description[1]').first
|
101
|
+
if error and error_description
|
102
|
+
raise Error.new(error.text.to_i, error_description.text)
|
103
|
+
else
|
104
|
+
resp
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ExactTarget
|
2
|
+
# Used to set up and modify settings for ExactTarget
|
3
|
+
class Configuration
|
4
|
+
|
5
|
+
OPTIONS = [:base_url, :username, :password,
|
6
|
+
:http_open_timeout, :http_read_timeout].freeze
|
7
|
+
|
8
|
+
# The (optional) base URL for accessing ExactTarget (can be http or https).
|
9
|
+
# Defaults to 'https://api.dc1.exacttarget.com/integrate.aspx'
|
10
|
+
attr_accessor :base_url
|
11
|
+
|
12
|
+
# The (required) ExactTarget username for making requests
|
13
|
+
attr_accessor :username
|
14
|
+
|
15
|
+
# The (required) ExactTarget password for making requests
|
16
|
+
attr_accessor :password
|
17
|
+
|
18
|
+
# The (optional) logger for outputting request/resposne xml
|
19
|
+
attr_accessor :logger
|
20
|
+
|
21
|
+
# The (optional) HTTP open timeout in seconds (defaults to 2).
|
22
|
+
attr_accessor :http_open_timeout
|
23
|
+
|
24
|
+
# The (optional) HTTP read timeout in seconds (defaults to 5).
|
25
|
+
attr_accessor :http_read_timeout
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@base_url = 'https://api.dc1.exacttarget.com/integrate.aspx'
|
29
|
+
@http_open_timeout = 2
|
30
|
+
@http_read_timeout = 5
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid?
|
34
|
+
[:base_url, :username, :password].none? { |f| send(f).nil? }
|
35
|
+
end
|
36
|
+
|
37
|
+
def secure?
|
38
|
+
!!(base_url =~ /^https:/i)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|