postal 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -5,4 +5,6 @@ rdoc
5
5
  pkg
6
6
  test/run*.rb
7
7
  run*.rb
8
- test/lyris.yml
8
+ test/lyris.yml
9
+ .rvmrc
10
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ postal (0.3.0)
5
+ savon (= 0.9.7)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ akami (1.0.0)
11
+ gyoku (>= 0.4.0)
12
+ builder (3.0.0)
13
+ gyoku (0.4.4)
14
+ builder (>= 2.1.2)
15
+ httpi (0.9.5)
16
+ rack
17
+ nokogiri (1.5.0)
18
+ nori (1.0.2)
19
+ rack (1.4.1)
20
+ savon (0.9.7)
21
+ akami (~> 1.0)
22
+ builder (>= 2.1.2)
23
+ gyoku (>= 0.4.0)
24
+ httpi (~> 0.9)
25
+ nokogiri (>= 1.4.0)
26
+ nori (~> 1.0)
27
+ wasabi (~> 2.0)
28
+ wasabi (2.0.0)
29
+ nokogiri (>= 1.4.0)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ postal!
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Rob Cameron
1
+ Copyright (c) 2009 The Active Network
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -0,0 +1,108 @@
1
+ # Introduction
2
+
3
+ Postal is a gem for working with the Lyris API. "But the Lyris API is just a SOAP service, why
4
+ don't I use something like Soap4r?" Well you could, but it wouldn't be very pretty. When the
5
+ interface is created all properties passed to a method are required and always in a certain order.
6
+
7
+ Postal does use the Soap4r interface behind the scenes but gives you a simple ActiveRecord-like
8
+ interface for search and adding records.
9
+
10
+ ## Installation
11
+
12
+ Get the gem:
13
+
14
+ gem sources -a http://gems.github.com
15
+ sudo gem install cannikin-postal
16
+
17
+ Then to use just add the require to your script:
18
+
19
+ require 'rubygems'
20
+ require 'postal'
21
+
22
+ ## Usage
23
+
24
+ Postal feels a lot like using ActiveRecord. You create/save new Members, find existing ones, etc.
25
+ All of the examples below assume you've already set up Postal with your username, password and WSDL
26
+ endpoint, and optionally the name of the list that all operations should use:
27
+
28
+ Postal.options[:wsdl] = 'http://mymailserver.com:82/?wsdl'
29
+ Postal.options[:username] = 'username'
30
+ Postal.options[:password] = 'password'
31
+ Postal.options[:list_name] = 'my-test-list'
32
+
33
+ If you don't set your list name in the options you can pass it in with each method call.
34
+
35
+ See the /test directory for examples of just about everything you can do with Postal.
36
+
37
+ ### Lists
38
+
39
+ # find a list based on its name
40
+ Postal::List.find('my-list-name')
41
+
42
+ ### Members
43
+
44
+ # add a member to a list (the list saved to Postal.options)
45
+ new_member = Postal::Member.new(:email => "john.doe@anonymous.com", :name => "John Doe")
46
+ id = new_member.save
47
+ # id => 1234567 (the new member_id in Lyris)
48
+
49
+ # add a member to a specific list and save in one call (both lines are equivalent)
50
+ Postal::Member.new(:email => "john.doe@anonymous.com", :name => "John Doe", :list_name => "new-list").save
51
+ Postal::Member.create(:email => "john.doe@anonymous.com", :name => "John Doe", :list_name => "new-list")
52
+
53
+ Postal also has a `save!` method that throws an error if the person can't be saved, rather than just
54
+ returning false.
55
+
56
+ # update a member's demographics
57
+ new_member = Postal::Member.new(:email => "john.doe@anonymous.com", :name => "John Doe")
58
+ new_member.save
59
+ new_member.update_attributes(:field_0 => 'Male')
60
+
61
+ # find a member's id based on an email address
62
+ Postal::Member.find('john.doe@anonymous.com')
63
+
64
+ # find a member based on member_id
65
+ Postal::Member.find(1234567)
66
+
67
+ # find a member based on Lyris "filters" (see Lyris API docs for syntax)
68
+ Postal::Member.find_by_filter('EmailAddress like john.doe%')
69
+
70
+ # delete member(s) based on filters
71
+ Postal::Member.destroy("EmailAddress like john.doe-delete%")
72
+
73
+ ### Mailings
74
+
75
+ # directly send an email to an array of email addresses
76
+ mail = Postal::Mailing.new( :to => ['john.doe@anonymous.com','jane.doe@anonymous.com'],
77
+ :html_message => "<p>Test from Postal at #{Time.now.to_s}</p>",
78
+ :text_message => 'Test test from Postal at #{Time.now.to_s}',
79
+ :from => 'Postmaster <postmaster@company.com>',
80
+ :subject => 'Test from Postal')
81
+ mail.valid?
82
+ mail.send
83
+
84
+ Mailings require a couple things to be valid, mainly someone to send to, a subject, and
85
+ the name of a list to send to. `mail.valid?` will return false if any of these do not exist.
86
+
87
+ ## To Do
88
+
89
+ * Implement wrappers for remaining Lyris methods
90
+
91
+ ## Thanks
92
+
93
+ Special thanks to this article: http://markthomas.org/2007/09/12/getting-started-with-soap4r/
94
+ Mark details how to use Soap4r (for which there is very little documentation in the world) and
95
+ his example includes talking to Lyris! Postal probably couldn't have happened without his article.
96
+
97
+ ## Note on Patches/Pull Requests
98
+
99
+ * Fork the project.
100
+ * Make your feature addition or bug fix.
101
+ * Add tests for it. This is important so I don't break it in a
102
+ future version unintentionally.
103
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
104
+ * Send me a pull request. Bonus points for topic branches.
105
+
106
+ ## Copyright
107
+
108
+ Copyright (c) 2009 The Active Network. See LICENSE for details.
@@ -4,9 +4,7 @@ $:.unshift File.dirname(__FILE__) # for use/testing when no gem is installed
4
4
  require 'logger'
5
5
 
6
6
  # internal
7
- require 'postal/lmapi/lmapi.rb'
8
- require 'postal/lmapi/lmapi_driver.rb'
9
- require 'postal/lmapi/lmapi_mapping_registry.rb'
7
+ require 'postal/driver'
10
8
  require 'postal/base'
11
9
  require 'postal/content'
12
10
  require 'postal/list'
@@ -37,9 +35,7 @@ module Postal
37
35
  # Make a driver instance available at Postal.driver
38
36
  def driver
39
37
  unless @driver
40
- @driver = Postal::Lmapi::Soap.new(@options[:wsdl])
41
- @driver.options['protocol.http.basic_auth'] << [@options[:wsdl], @options[:username], @options[:password]]
42
- @driver.options['protocol.http.proxy'] = @options[:proxy]
38
+ @driver = Postal::Driver.new @options[:wsdl], @options[:proxy], @options[:username], @options[:password]
43
39
  end
44
40
  return @driver
45
41
  end
@@ -7,22 +7,21 @@ module Postal
7
7
  unless args.find { |arg| arg.match(/ListName/) }
8
8
  args << "ListName=#{Postal.options[:list_name]}"
9
9
  end
10
- if soap_contents = Postal.driver.selectContent(args)
10
+ if soap_contents = Postal.driver.selectContent(args)[:item]
11
11
  contents = soap_contents.collect do |content|
12
- puts content.inspect
13
- Content.new(:content_id => content.contentID,
14
- :date_created => content.dateCreated,
15
- :description => content.description,
16
- :doc_parts => content.docParts,
17
- :doc_type => content.docType,
18
- :header_from => content.headerFrom,
19
- :header_to => content.headerTo,
20
- :is_read_only => content.isReadOnly,
21
- :is_template => content.isTemplate,
22
- :list_name => content.listName,
23
- :native_title => content.nativeTitle,
24
- :site_name => content.siteName,
25
- :title => content.title)
12
+ Content.new(:content_id => content[:content_id],
13
+ :date_created => content[:date_created],
14
+ :description => content[:description],
15
+ :doc_parts => content[:doc_parts],
16
+ :doc_type => content[:doc_yype],
17
+ :header_from => content[:header_from],
18
+ :header_to => content[:header_to],
19
+ :is_read_only => content[:is_read_only],
20
+ :is_template => content[:is_template],
21
+ :list_name => content[:list_name],
22
+ :native_title => content[:native_title],
23
+ :site_name => content[:site_name],
24
+ :title => content[:title])
26
25
  end
27
26
  if contents.size == 1
28
27
  return contents.first
@@ -0,0 +1,121 @@
1
+ # Main class used by client programs
2
+
3
+ require 'savon'
4
+
5
+ module Postal
6
+ class Driver
7
+
8
+ attr_accessor :client
9
+
10
+ DefaultEndpointUrl = "http://www.mymailserver.com/"
11
+
12
+ def initialize(wsdl_url = nil, proxy = nil, username = nil, password = nil)
13
+ wsdl_url ||= DefaultEndpointUrl
14
+ @client = Savon::Client.new do |wsdl, http|
15
+ wsdl.document = wsdl_url
16
+ if proxy
17
+ http.proxy = proxy
18
+ end
19
+ if username && password
20
+ http.auth.basic username, password
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+ def soap_actions
27
+ @client.wsdl.soap_actions
28
+ end
29
+
30
+
31
+ def selectMembers(args)
32
+ response = @client.request :wsdl, :select_members do |soap|
33
+ soap.body = { "FilterCriteriaArray" => {'item' => args} }
34
+ end
35
+ response.to_hash[:select_members_response][:return]
36
+ end
37
+
38
+
39
+ def createSingleMember(email, name, list_name)
40
+ response = @client.request :wsdl, :create_single_member do |soap|
41
+ soap.body = { "EmailAddress" => email,
42
+ "FullName" => name || "",
43
+ "ListName" => list_name}
44
+ end
45
+ response.to_hash[:create_single_member_response][:return].to_i
46
+ end
47
+
48
+
49
+ def deleteMembers(args)
50
+ response = @client.request :wsdl, :delete_members do |soap|
51
+ soap.body = { "FilterCriteriaArray" => {'item' => args} }
52
+ end
53
+ response.to_hash[:delete_members_response][:return].to_i
54
+ end
55
+
56
+
57
+ def unsubscribe(list_name, id, email)
58
+ response = @client.request :wsdl, :unsubscribe do |soap|
59
+ soap.body = { 'SimpleMemberStructArrayIn' => {
60
+ 'item' => {
61
+ 'ListName' => list_name,
62
+ 'MemberID' => id,
63
+ 'EmailAddress' => email }}}
64
+ end
65
+ response.to_hash[:unsubscribe_response][:return]
66
+ end
67
+
68
+
69
+ def updateMemberDemographics(list_name, id, email, demographics)
70
+ response = @client.request :wsdl, :update_member_demographics do |soap|
71
+ soap.body = { "SimpleMemberStructIn" => {
72
+ 'ListName' => list_name,
73
+ 'MemberID' => id,
74
+ 'EmailAddress' => email},
75
+ "DemographicsArray" => {'item' => demographics} }
76
+ end
77
+ response.to_hash[:update_member_demographics_response][:return]
78
+ end
79
+
80
+
81
+ def selectContent(args)
82
+ response = @client.request :wsdl, :select_content do |soap|
83
+ soap.body = { "FilterCriteriaArray" => {'item' => args} }
84
+ end
85
+ response.to_hash[:select_content_response][:return]
86
+ end
87
+
88
+
89
+ def selectLists(list_name, site_name)
90
+ response = @client.request :wsdl, :select_lists do |soap|
91
+ soap.body = { "ListName" => list_name,
92
+ "SiteName" => site_name}
93
+ end
94
+ if response.to_hash[:select_lists_response]
95
+ return response.to_hash[:select_lists_response][:return]
96
+ else
97
+ return nil
98
+ end
99
+ end
100
+
101
+
102
+ def importContent(content_id)
103
+ response = @client.request :wsdl, :import_content do |soap|
104
+ soap.body = { "ContentID" => content_id }
105
+ end
106
+ response.to_hash[:import_content_response][:return]
107
+ end
108
+
109
+
110
+ def sendMailingDirect(email_addresses=[], member_ids=[], mail)
111
+ response = @client.request :wsdl, :send_mailing_direct do |soap|
112
+ soap.body = { "EmailAddressArray" => {:item => email_addresses},
113
+ "MemberIDArray" => {:item => member_ids},
114
+ "MailingStructIn" => mail }
115
+ end
116
+ response.to_hash[:send_mailing_direct_response][:return]
117
+ end
118
+
119
+ end
120
+ end
121
+
@@ -4,7 +4,7 @@ module Postal
4
4
  class << self
5
5
 
6
6
  def import(content_id)
7
- mail = Postal.driver.importContent(content_id)
7
+ mail = Mailing.new(Postal.driver.importContent(content_id))
8
8
  return mail
9
9
  end
10
10
 
@@ -106,17 +106,17 @@ module Postal
106
106
  @mailing = args[:mailing]
107
107
  else
108
108
  @subject = args[:mailing].subject
109
- @is_html_section_encoded = args[:mailing].isHtmlSectionEncoded
110
- @html_section_encoding = args[:mailing].htmlSectionEncoding
111
- @html_message = args[:mailing].htmlMessage
112
- @char_set_id = args[:mailing].charSetID
113
- @is_text_section_encoded = args[:mailing].isTextSectionEncoded
114
- @text_section_encoding = args[:mailing].textSectionEncoding
109
+ @is_html_section_encoded = args[:mailing].is_html_section_encoded
110
+ @html_section_encoding = args[:mailing].html_section_encoding
111
+ @html_message = args[:mailing].html_message
112
+ @char_set_id = args[:mailing].char_set_id
113
+ @is_text_section_encoded = args[:mailing].is_text_section_encoded
114
+ @text_section_encoding = args[:mailing].text_section_encoding
115
115
  @title = args[:mailing].title
116
- @text_message = args[:mailing].textMessage
116
+ @text_message = args[:mailing].text_message
117
117
  @attachments = args[:mailing].attachments
118
118
  @from = args[:mailing].from
119
- @additional_headers = args[:mailing].additionalHeaders
119
+ @additional_headers = args[:mailing].additional_headers
120
120
  @mailing = args[:mailing]
121
121
  end
122
122
  end
@@ -127,48 +127,73 @@ module Postal
127
127
  if valid?
128
128
 
129
129
  # are we sending to a list of email addresses or member ids
130
- case @to.to_a.first
130
+ case [@to].flatten.first
131
131
  when ::String
132
- emails = @to.to_a
132
+ emails = [@to].flatten
133
133
  member_ids = []
134
134
  when ::Fixnum
135
135
  emails = []
136
- member_ids = @to.to_a
136
+ member_ids = [@to].flatten
137
137
  end
138
138
 
139
139
  if @mailing.nil?
140
- mail = Postal::Lmapi::MailingStruct.new(:additionalHeaders => @additional_headers,
141
- :attachments => @attachments,
142
- :bypassModeration => @bypass_moderation,
143
- :campaign => @campaign,
144
- :charSetID => @char_set_id,
145
- :detectHtml => @detect_html,
146
- :dontAttemptAfterDate => @dont_attempt_after_date,
147
- :enableRecovery => @enable_recovery,
148
- :from => @from,
149
- :htmlMessage => @html_message,
150
- :htmlSectionEncoding => @html_section_encoding,
151
- :isHtmlSectionEncoded => @is_html_section_encoded,
152
- :isTextSectionEncoded => @is_text_section_encoded,
153
- :listName => @list_name,
154
- :recencyNumberOfMailings => @recency_number_of_mailings,
155
- :recencyWhich => @recency_which,
156
- :replyTo => @reply_to,
157
- :resendAfterDays => @resend_after_days,
158
- :sampleSize => @sample_size,
159
- :scheduledMailingDate => @scheduled_mailing_date,
160
- :subject => @subject,
161
- :textMessage => @text_message,
162
- :textSectionEncoding => @text_section_encoding,
163
- :title => @title,
164
- # :to => @to,
165
- :trackOpens => @track_opens,
166
- :rewriteDateWhenSent => @rewrite_date_when_sent )
140
+ mail = { 'AdditionalHeaders' => @additional_headers,
141
+ 'Attachments' => @attachments,
142
+ 'BypassModeration' => @bypass_moderation,
143
+ 'Campaign' => @campaign,
144
+ 'CharSetID' => @char_set_id,
145
+ 'DetectHtml' => @detect_html,
146
+ 'DontAttemptAfterDate' => @dont_attempt_after_date,
147
+ 'EnableRecovery' => @enable_recovery,
148
+ 'From' => @from,
149
+ 'HtmlMessage' => @html_message,
150
+ 'HtmlSectionEncoding' => @html_section_encoding,
151
+ 'IsHtmlSectionEncoded' => @is_html_section_encoded,
152
+ 'IsTextSectionEncoded' => @is_text_section_encoded,
153
+ 'ListName' => @list_name,
154
+ 'RecencyNumberOfMailings' => @recency_number_of_mailings,
155
+ 'RecencyWhich' => @recency_which,
156
+ 'ReplyTo' => @reply_to,
157
+ 'ResendAfterDays' => @resend_after_days,
158
+ 'SampleSize' => @sample_size,
159
+ 'ScheduledMailingDate' => @scheduled_mailing_date,
160
+ 'Subject' => @subject,
161
+ 'TextMessage' => @text_message,
162
+ 'TextSectionEncoding' => @text_section_encoding,
163
+ 'Title' => @title,
164
+ # :to => @to,
165
+ 'TrackOpens' => @track_opens,
166
+ 'RewriteDateWhenSent' => @rewrite_date_when_sent }
167
167
 
168
168
  return Postal.driver.sendMailingDirect(emails,member_ids,mail)
169
169
  else
170
- mail = Postal::Lmapi::MailingStruct.new(@mailing)
171
- mail.listName = @list_name || Postal.options[:list_name]
170
+ mail = { 'AdditionalHeaders' => @mailing.additional_headers,
171
+ 'Attachments' => @mailing.attachments,
172
+ 'BypassModeration' => @mailing.bypass_moderation,
173
+ 'Campaign' => @mailing.campaign,
174
+ 'CharSetID' => @mailing.char_set_id,
175
+ 'DetectHtml' => @mailing.detect_html,
176
+ 'DontAttemptAfterDate' => @mailing.dont_attempt_after_date,
177
+ 'EnableRecovery' => @mailing.enable_recovery,
178
+ 'From' => @mailing.from,
179
+ 'HtmlMessage' => @mailing.html_message,
180
+ 'HtmlSectionEncoding' => @mailing.html_section_encoding,
181
+ 'IsHtmlSectionEncoded' => @mailing.is_html_section_encoded,
182
+ 'IsTextSectionEncoded' => @mailing.is_text_section_encoded,
183
+ 'RecencyNumberOfMailings' => @mailing.recency_number_of_mailings,
184
+ 'RecencyWhich' => @mailing.recency_which,
185
+ 'ReplyTo' => @mailing.reply_to,
186
+ 'ResendAfterDays' => @mailing.resend_after_days,
187
+ 'SampleSize' => @mailing.sample_size,
188
+ 'ScheduledMailingDate' => @mailing.scheduled_mailing_date,
189
+ 'Subject' => @mailing.subject,
190
+ 'TextMessage' => @mailing.text_message,
191
+ 'TextSectionEncoding' => @mailing.text_section_encoding,
192
+ 'Title' => @mailing.title,
193
+ # :to => @to,
194
+ 'TrackOpens' => @mailing.track_opens,
195
+ 'RewriteDateWhenSent' => @mailing.rewrite_date_when_sent,
196
+ 'ListName' => @mailing.list_name || Postal.options[:list_name] }
172
197
  return Postal.driver.sendMailingDirect(emails,member_ids,mail)
173
198
  end
174
199