exact-target 0.0.4

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.
@@ -0,0 +1,8 @@
1
+ class Net::HTTP
2
+ alias_method :old_initialize, :initialize
3
+ def initialize(*args)
4
+ old_initialize(*args)
5
+ @ssl_context = OpenSSL::SSL::SSLContext.new
6
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
7
+ end
8
+ end
@@ -0,0 +1,284 @@
1
+ require 'builder'
2
+
3
+ module ExactTarget
4
+ class RequestBuilder
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ end
9
+
10
+ def accountinfo_retrieve_attrbs
11
+ build(:accountinfo, :retrieve_attrbs)
12
+ end
13
+
14
+ def list_add(list_name, list_type=nil)
15
+ list_type = :public unless %w(public private salesforce).include?(list_type.to_s)
16
+ build(:list, :add) do |li|
17
+ li.list_type list_type.to_s
18
+ li.list_name list_name.to_s
19
+ end
20
+ end
21
+
22
+ def list_edit(list_id, new_list_name)
23
+ build(:list, :edit, :listid, list_id) do |li|
24
+ li.list_name new_list_name.to_s
25
+ end
26
+ end
27
+
28
+ def list_retrieve(id_or_name=nil)
29
+ if id_or_name.is_a?(Fixnum) or id_or_name =~ /^\d+$/
30
+ build(:list, :retrieve, :listid, id_or_name.to_i)
31
+ else
32
+ build(:list, :retrieve, :listname, id_or_name)
33
+ end
34
+ end
35
+
36
+ def list_import(list_id, file_name, file_mapping, options={})
37
+ options = list_import_default_options(options, file_name)
38
+ build(:list, :import, :listid, list_id) do |li|
39
+ li.tags_from_options! options, :file_name, :email_address, :file_type, :column_headings
40
+ li.file_mapping do |fm|
41
+ file_mapping.each { |m| fm.field(m) }
42
+ end
43
+ li.tags_from_options! options, :import_type, :returnid, :encrypted, :encrypt_format
44
+ end
45
+ end
46
+
47
+ def list_importstatus(import_id)
48
+ build(
49
+ :list, :import,
50
+ :sub_action => :importstatus,
51
+ :search_type => :omit,
52
+ :search_value => import_id
53
+ )
54
+ end
55
+
56
+ def list_retrieve_sub(list_id, status=nil)
57
+ unless status.nil? or %w(Active Unsubscribed Returned Undeliverable Deleted).include?(status)
58
+ raise "Invalid status: #{status}"
59
+ end
60
+ build(:list, :retrieve_sub, :listid, list_id) do |li|
61
+ li.search_status status unless status.nil?
62
+ end
63
+ end
64
+
65
+ def list_delete(id)
66
+ build(:list, :delete, :listid, id)
67
+ end
68
+
69
+ def list_retrievegroups
70
+ build(:list, :retrievegroups, :groups)
71
+ end
72
+
73
+ def list_refresh_group(group_id)
74
+ build(
75
+ :list, :refresh_group,
76
+ :sub_action => nil,
77
+ :search_type => :omit,
78
+ :search_value => group_id,
79
+ :search_value2 => nil
80
+ )
81
+ end
82
+
83
+ def batch_inquire(batch_id)
84
+ build(
85
+ :batch, :inquire, :batchid, batch_id,
86
+ :sub_action => nil,
87
+ :search_value2 => nil
88
+ )
89
+ end
90
+
91
+ ###################################################################
92
+
93
+ def subscriber_add(list_id, subscriber, options={})
94
+ subscriber_edit(list_id, nil, subscriber, options)
95
+ end
96
+
97
+ def subscriber_edit(list_id, orig_email, subscriber, options={})
98
+ options = subscriber_edit_default_options(options)
99
+ subscriber = subscriber.to_et_hash if subscriber.is_a?(Subscriber)
100
+ action = orig_email.nil? ? :add : :edit
101
+ build(:subscriber, action, :listid, list_id, :search_value2 => orig_email) do |sub|
102
+ sub.values do |vs|
103
+ subscriber.each do |k, v|
104
+ vs.tag!(k.to_s, v) unless k.to_s =~ /status/i
105
+ end
106
+ vs.status options[:status].to_s
107
+ vs.reason options[:reason] if options.has_key? :reason
108
+ vs.ChannelMemberID options[:ChannelMemberID] if options.has_key? :ChannelMemberID
109
+ end
110
+ sub.update options[:update] if orig_email.nil?
111
+ end
112
+ end
113
+
114
+ def subscriber_retrieve(id, email=nil)
115
+ type = email.blank? ? :subid : :listid
116
+ build(:subscriber, :retrieve, type, id, :search_value2 => email)
117
+ end
118
+
119
+ def subscriber_delete(id, email=nil)
120
+ type = email.blank? ? :subid : :listid
121
+ build(:subscriber, :delete, type, id, :search_value2 => email)
122
+ end
123
+
124
+ def subscriber_masterunsub(*email_addresses)
125
+ build(:subscriber, :masterunsub, :emailaddress, :search_value => :omit) do |sub|
126
+ sub.search_value do |sv|
127
+ email_addresses.flatten.each { |a| sv.emailaddress(a) }
128
+ end
129
+ end
130
+ end
131
+
132
+ ###################################################################
133
+
134
+ def email_retrieve(name=nil, options={})
135
+ name, options = nil, name if name.is_a?(Hash)
136
+ start_date, end_date = %w(start_date end_date).map do |n|
137
+ et_date options[n.to_sym]
138
+ end
139
+ type = unless start_date.nil? and end_date.nil?
140
+ name.nil? ? :daterange : :emailnameanddaterange
141
+ else
142
+ name.nil? ? nil : :emailname
143
+ end
144
+ build(:email, :retrieve, type, name, :sub_action => :all, :search_value2 => nil) do |em|
145
+ em.daterange do |r|
146
+ r.startdate start_date if start_date
147
+ r.enddate end_date if end_date
148
+ end
149
+ end
150
+ end
151
+
152
+ def email_add(name, subject, options)
153
+ build(:email, :add, :search_type => :omit, :search_value => :omit, :sub_action => 'HTMLPaste') do |em|
154
+ em.category
155
+ em.email_name name
156
+ em.email_subject subject
157
+ if options.has_key? :body
158
+ em.email_body { |eb| eb.cdata! options[:body] }
159
+ elsif options.has_key? :file
160
+ em.file_name options[:file]
161
+ end
162
+ end
163
+ end
164
+
165
+ def email_add_text(email_id, options)
166
+ build(:email, :add, :search_type => :emailid,
167
+ :search_value => email_id, :sub_action => :text) do |em|
168
+ if options.has_key? :body
169
+ em.email_body { |eb| eb.cdata! options[:body] }
170
+ elsif options.has_key? :file
171
+ em.file_name options[:file]
172
+ end
173
+ end
174
+ end
175
+
176
+ def email_retrieve_body(email_id)
177
+ build(:email, :retrieve, :emailid, email_id, :sub_action => :htmlemail) do |em|
178
+ em.search_value2
179
+ em.search_value3
180
+ end
181
+ end
182
+
183
+ ###################################################################
184
+
185
+ def job_send(email_id, list_ids, options={})
186
+ options = job_send_default_options(options)
187
+
188
+ build(:job, :send, :emailid, email_id) do |job|
189
+ job.tags_from_options! options, :from_name, :from_email, :additional,
190
+ :multipart_mime, :track_links,
191
+ :send_date, :send_time
192
+ job_send_id_list job, :lists, list_ids
193
+ job_send_id_list job, :suppress, options[:suppress_ids]
194
+ job.test_send options[:test_send]
195
+ end
196
+ end
197
+
198
+ ###################################################################
199
+ private
200
+ ###################################################################
201
+
202
+ def list_import_default_options(options, file_name)
203
+ options = options.dup
204
+ options[:file_name] = file_name
205
+ options[:file_type] ||= (file_name =~ /\.txt$/i ? 'tab' : 'csv')
206
+ options[:column_headings] ||= true
207
+ options[:import_type] ||= 0
208
+ options[:returnid] ||= true
209
+ options[:encrypted] ||= false
210
+ options
211
+ end
212
+
213
+ def subscriber_edit_default_options(options)
214
+ options = options.dup
215
+ options[:status] = :active unless options[:status].to_s == 'unsub'
216
+ options[:update] = true unless options[:update] == false
217
+ options
218
+ end
219
+
220
+ def job_send_default_options(options)
221
+ options = options.nil? ? {} : options.dup # Don't munge hash passed in
222
+ options[:multipart_mime] = false unless options.has_key? :multipart_mime
223
+ options[:track_links] = true unless options.has_key? :track_links
224
+ options[:test_send] = false unless options.has_key? :test_send
225
+ options[:send_date] ||= :immediate
226
+ options[:suppress_ids] ||= []
227
+ options
228
+ end
229
+
230
+ def job_send_id_list(job, tag, ids)
231
+ job.tag!(tag.to_s) do |li|
232
+ [ids].flatten.compact.each { |id| li.list id }
233
+ end
234
+ end
235
+
236
+ def et_date(d)
237
+ d = Date.parse(d) if d.is_a?(String)
238
+ [d.month, d.day, d.year] * '/' if d
239
+ end
240
+
241
+ def build(system_name, action, search_type=nil, search_value=nil, options=nil, &block)
242
+ options = parse_options(search_type, search_value, options)
243
+ xml = Builder::XmlMarkup.new
244
+ xml.instruct! :xml, :version => "1.0", :encoding => nil
245
+ xml = xml.exacttarget do |et|
246
+ et.authorization do |a|
247
+ a.username @config.username
248
+ a.password @config.password
249
+ end
250
+ et.system do |s|
251
+ build_system(s, system_name, action, options, &block)
252
+ end
253
+ end
254
+ xml.to_s
255
+ end
256
+
257
+ def parse_options(search_type, search_value, options)
258
+ options, search_type = search_type, nil if search_type.is_a?(Hash)
259
+ options, search_value = search_value, nil if search_value.is_a?(Hash)
260
+ options ||= {}
261
+ { :search_type => search_type, :search_value => search_value }.merge(options)
262
+ end
263
+
264
+ def build_system(s, system_name, action, options, &block)
265
+ s.system_name system_name.to_s
266
+ s.action action.to_s
267
+ [:sub_action, :search_type, :search_value, :search_value2].each do |name|
268
+ build_system_option(s, name, options[name]) if options.has_key? name
269
+ end
270
+ yield(s) if block_given?
271
+ end
272
+
273
+ def build_system_option(s, name, value)
274
+ if name == :search_value and value.is_a?(Array)
275
+ s.search_values do |ss|
276
+ value.each { |v| ss.search_value v }
277
+ end
278
+ elsif value != :omit
279
+ s.tag! name.to_s, value.to_s
280
+ end
281
+ end
282
+
283
+ end
284
+ end
@@ -0,0 +1,30 @@
1
+ class <%= name %>
2
+
3
+ <% attributes.each do |rb_a, et_a| %>
4
+ attr_accessor :<%= rb_a %>
5
+
6
+ <% if rb_a != et_a %>
7
+ alias :<%= et_a %> :<%= rb_a %>
8
+ alias :<%= et_a %>= :<%= rb_a %>=
9
+ <% end %>
10
+ <% end %>
11
+
12
+ def attributes
13
+ [<%= attributes.map { |rb_a, et_a| ":#{rb_a}" } * ', ' %>]
14
+ end
15
+
16
+ def et_attributes
17
+ [<%= attributes.map { |rb_a, et_a| ":#{et_a}" } * ', ' %>]
18
+ end
19
+
20
+ def to_hash
21
+ attributes.map { |a| [a, send(a)] }
22
+ end
23
+
24
+ def to_et_hash
25
+ et_attributes.map { |a| [a, send(a)] }
26
+ end
27
+
28
+ alias :to_s :<%= attributes.first.first %>
29
+
30
+ end
@@ -0,0 +1,61 @@
1
+ require 'erb'
2
+
3
+ module ExactTarget
4
+ #
5
+ # Response classes for ExactTarget. We'll use these rather
6
+ # than just stuff it all in a hash as it allows us to more
7
+ # easily map between ugly ET names (e.g. GroupID) to ruby-friendly
8
+ # names (e.g. group_id).
9
+ #
10
+ module ResponseClasses
11
+ class << self
12
+
13
+ def extended(base)
14
+ class_from_et_attributes base, :ListProfileAttribute,
15
+ :name, :description, :default_value, :data_type, :required,
16
+ :min_size, :max_size, :subscriber_editable, :display, :values
17
+
18
+ class_from_et_attributes base, :ListInformation,
19
+ :list_name, :list_type, :modified, :subscriber_count,
20
+ :active_total, :held_count, :bounce_count, :unsub_count
21
+
22
+ class_from_et_attributes base, :ListGroupInformation,
23
+ :groupName, :groupID, :parentlistID, :description
24
+
25
+ class_from_et_attributes base, :SubscriberInformation,
26
+ :subid, :listid, :list_name, :subscriber
27
+
28
+ class_from_et_attributes base, :EmailInformation,
29
+ :emailname, :emailid, :emailsubject, :emailcreateddate, :categoryid
30
+
31
+ def base.subscriber_class
32
+ @subscriber_class ||= ResponseClasses.class_from_et_attributes(
33
+ self, :Subscriber, accountinfo_retrieve_attrbs.map(&:name)
34
+ )
35
+ end
36
+
37
+ def base.const_missing(name)
38
+ if name.to_sym == :Subscriber
39
+ subscriber_class
40
+ else
41
+ super
42
+ end
43
+ end
44
+ end
45
+
46
+ def class_from_et_attributes(base, name, *attribute_names)
47
+ attributes = attribute_names.flatten.map do |a|
48
+ [a.to_s.underscore.gsub(' ', '_'), a.to_s.gsub(' ', '__')]
49
+ end
50
+ class_def = class_template.result(binding)
51
+ base.module_eval(class_def)
52
+ base.const_get(name)
53
+ end
54
+
55
+ def class_template
56
+ @class_template ||= ERB.new File.read(File.expand_path '../response_class.erb', __FILE__)
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,167 @@
1
+ require 'builder'
2
+
3
+ module ExactTarget
4
+ #
5
+ # This class is responsible for turning parsed ExactTarget response xml
6
+ # into data that applications can use
7
+ #
8
+ class ResponseHandler
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ end
13
+
14
+ def handle_import_id_result(resp)
15
+ handle_id_result resp, :import_info, :importid, /is being imported/i
16
+ end
17
+
18
+ %w(email job list subscriber).each do |t|
19
+ define_method "handle_#{t}_id_result", lambda { |resp|
20
+ handle_id_result resp, "#{t}_info", "#{t}_description", /success/i
21
+ }
22
+ end
23
+
24
+ def accountinfo_retrieve_attrbs(resp)
25
+ resp.xpath('//attribute').map do |a|
26
+ create_result(ListProfileAttribute, a) do |child|
27
+ child.children.map { |v| cast_value(v.text) } if child.name == 'values'
28
+ end
29
+ end
30
+ end
31
+
32
+ alias :list_add :handle_list_id_result
33
+
34
+ alias :list_edit :handle_list_id_result
35
+
36
+ def list_retrieve(resp)
37
+ if resp.xpath('//list/list_name').size == 1
38
+ resp.xpath('//list[1]').map do |list|
39
+ create_result(ListInformation, list)
40
+ end.first
41
+ else
42
+ resp.xpath('//listid').map do |id|
43
+ id.text.to_i
44
+ end
45
+ end
46
+ end
47
+
48
+ alias :list_import :handle_import_id_result
49
+
50
+ def list_importstatus(resp)
51
+ resp.xpath('//import_info[1]').first.text
52
+ end
53
+
54
+ def list_retrieve_sub(resp)
55
+ resp.xpath('//subscriber').map do |s|
56
+ return [] if s.text =~ /no subscribers found/i
57
+ create_result(Subscriber, s)
58
+ end
59
+ end
60
+
61
+ alias :list_delete :handle_list_id_result
62
+
63
+ def list_retrievegroups(resp)
64
+ resp.xpath('//group').map do |group|
65
+ create_result(ListGroupInformation, group)
66
+ end
67
+ end
68
+
69
+ def list_refresh_group(resp)
70
+ resp.xpath('//groupRefresh/groupAsyncID[1]').first.text.to_i
71
+ end
72
+
73
+ def batch_inquire(resp)
74
+ resp.xpath('//Batch/status[1]').first.text
75
+ end
76
+
77
+ ###################################################################
78
+
79
+ alias :subscriber_add :handle_subscriber_id_result
80
+
81
+ alias :subscriber_edit :handle_subscriber_id_result
82
+
83
+ def subscriber_retrieve(resp)
84
+ resp.xpath('//subscriber').map do |s|
85
+ return [] if s.text =~ /no subscribers found/i
86
+ sri = create_result(SubscriberInformation, s)
87
+ sri.subscriber = create_result(Subscriber, s)
88
+ sri
89
+ end
90
+ end
91
+
92
+ alias :subscriber_delete :handle_subscriber_id_result
93
+
94
+ def subscriber_masterunsub(resp)
95
+ resp = resp.xpath('//subscriberunsub').map do |su|
96
+ %w(emailaddress status).map { |p| su.xpath(".//#{p}[1]").first.text }
97
+ end
98
+ Hash[resp]
99
+ end
100
+
101
+ ###################################################################
102
+
103
+ def email_retrieve(resp)
104
+ resp.xpath('//emaillist').map do |el|
105
+ create_result(EmailInformation, el)
106
+ end
107
+ end
108
+
109
+ def email_add(resp)
110
+ resp.xpath('//emailID[1]').first.text.to_i
111
+ end
112
+
113
+ alias :email_add_text :handle_email_id_result
114
+
115
+ def email_retrieve_body(resp)
116
+ resp.xpath('//htmlbody[1]').first.text
117
+ end
118
+
119
+ ###################################################################
120
+
121
+ alias :job_send :handle_job_id_result
122
+
123
+ ###################################################################
124
+ private
125
+ ###################################################################
126
+
127
+ def handle_id_result(resp, info_tag, id_tag, success_regex)
128
+ info = resp.xpath("//#{info_tag}[1]").first
129
+ if !info.nil? and info.text =~ success_regex
130
+ id = resp.xpath("//#{id_tag}[1]").first
131
+ id.nil? ? true : cast_value(id.text)
132
+ else
133
+ raise Error.new(0, "Unsupported id result: #{resp.to_s}")
134
+ end
135
+ end
136
+
137
+ def create_result(clazz, node, &block)
138
+ ret = clazz.new
139
+ node.children.each do |child|
140
+ if ret.respond_to? "#{child.name}="
141
+ val = yield(child) if block_given?
142
+ val = cast_value(child.text) if val.nil?
143
+ ret.send "#{child.name}=", val
144
+ end
145
+ end
146
+ ret
147
+ end
148
+
149
+ def cast_value(v)
150
+ case v
151
+ when /^[+-]?\d+$/
152
+ v.to_i
153
+ when /^([+-]?)(?=\dâ\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/
154
+ v.to_f
155
+ when /^true$/i
156
+ true
157
+ when /^false$/i
158
+ false
159
+ when %r{^\d+/\d+/\d+ \d+:\d+:\d+ [AP]M$}i, %r{^\d+/\d+/\d+$}i
160
+ DateTime.parse(v)
161
+ else
162
+ v.strip
163
+ end
164
+ end
165
+
166
+ end
167
+ end