postal 0.2.3 → 0.3.0

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.
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