constant-contact-ruby 0.2.1
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/.document +5 -0
- data/.gitignore +23 -0
- data/LICENSE +20 -0
- data/README.rdoc +75 -0
- data/Rakefile +52 -0
- data/TODO +1 -0
- data/VERSION.yml +5 -0
- data/constant-contact-ruby.gemspec +63 -0
- data/constant-contact-ruby.rb +8 -0
- data/lib/constant_contact.rb +18 -0
- data/lib/constant_contact/activity.rb +196 -0
- data/lib/constant_contact/base_resource.rb +23 -0
- data/lib/constant_contact/contact.rb +323 -0
- data/lib/constant_contact/contact_list.rb +152 -0
- data/test/fixtures/get_contact.xml +72 -0
- data/test/fixtures/get_contacts.xml +54 -0
- data/test/fixtures/post_contacts.xml +18 -0
- data/test/helper.rb +21 -0
- data/test/test_constant-contact-ruby.rb +8 -0
- data/test/test_contacts.rb +49 -0
- metadata +78 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 BatchBlue Software, LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
= constant-contact-ruby
|
2
|
+
|
3
|
+
Ruby wrapper for the Constant Contact REST API
|
4
|
+
|
5
|
+
== Requirements
|
6
|
+
|
7
|
+
HTTParty gem - http://rubygems.org/gems/httparty
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
require 'constant-contact-ruby'
|
12
|
+
include ConstantContact
|
13
|
+
|
14
|
+
### Contacts ###
|
15
|
+
|
16
|
+
all_contacts = Contact.all
|
17
|
+
|
18
|
+
contact_7 = Contact.get( 7 )
|
19
|
+
contact_7.update_attributes!( :first_name => 'John', :last_name => 'Doe' )
|
20
|
+
contact_7.add_to_list!( 3 )
|
21
|
+
contact_7.remove_from_list!( 3 )
|
22
|
+
contact_7.replace_contact_lists!( 1, 3, 9 )
|
23
|
+
|
24
|
+
new_contact = Contact.add( :email_address => 'john@example.com', :first_name => 'John', :last_name => 'Doe' )
|
25
|
+
|
26
|
+
john_doe = Contact.search_by_email( 'john@example.com' )
|
27
|
+
|
28
|
+
|
29
|
+
### Contact Lists ###
|
30
|
+
|
31
|
+
all_lists = ContactList.all
|
32
|
+
|
33
|
+
list_1 = ContactList.get( 1 )
|
34
|
+
list_1_members = ContactList.members( list_1.uid )
|
35
|
+
list_1.clear_contacts! # remove all contacts from list
|
36
|
+
ContactList.delete( 1 ) # delete the list
|
37
|
+
|
38
|
+
new_list = ContactList.add( 'my new list' )
|
39
|
+
|
40
|
+
|
41
|
+
### Bulk Activities (involving multiple contacts) ###
|
42
|
+
|
43
|
+
activities = Activity.all # list all activities in the system
|
44
|
+
|
45
|
+
activity_details = Activity.get( activities.first.uid )
|
46
|
+
|
47
|
+
Activity.remove_all_contacts_from_lists( 1, 3, 4 ) # remove all contacts from lists 1, 3, and 4
|
48
|
+
|
49
|
+
new_contacts = [
|
50
|
+
{ :email_address = 'user1@example.com', :first_name => 'User1', :last_name => 'test' },
|
51
|
+
{ :email_address = 'user2@example.com', :first_name => 'User2', :last_name => 'test 2' },
|
52
|
+
{ :email_address = 'user3@example.com', :first_name => 'User1' },
|
53
|
+
{ :email_address = 'user4@example.com' }
|
54
|
+
]
|
55
|
+
Activity.add_contacts_to_lists( new_contacts, 3, 4 ) # all the users array to lists 3 and 4
|
56
|
+
Activity.remove_contacts_from_lists( new_contacts, 4 ) # remove the users from list 4
|
57
|
+
|
58
|
+
|
59
|
+
== Note on Patches/Pull Requests
|
60
|
+
|
61
|
+
* Fork the project.
|
62
|
+
* Make your feature addition or bug fix.
|
63
|
+
* Add tests for it. This is important so I don't break it in a
|
64
|
+
future version unintentionally.
|
65
|
+
* Commit, do not mess with rakefile, version, or history.
|
66
|
+
(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)
|
67
|
+
* Send me a pull request. Bonus points for topic branches.
|
68
|
+
|
69
|
+
== Authors
|
70
|
+
|
71
|
+
Craig P Jolicoeur - http://craigjolicoeur.com
|
72
|
+
|
73
|
+
== Copyright
|
74
|
+
|
75
|
+
Copyright (c) 2010 BatchBlue Software, LLC. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "constant-contact-ruby"
|
8
|
+
gem.summary = %Q{Constant Contact Ruby API Wrapper}
|
9
|
+
gem.description = %Q{Ruby wrapper around the Constant Contact REST API}
|
10
|
+
gem.email = "cpjolicoeur@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/cpjolicoeur/constant-contact-ruby"
|
12
|
+
gem.authors = ["Craig P Jolicoeur"]
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/test_*.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'rcov/rcovtask'
|
29
|
+
Rcov::RcovTask.new do |test|
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
task :rcov do
|
36
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
task :test => :check_dependencies
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "constant-contact-ruby #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION.yml
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{constant-contact-ruby}
|
8
|
+
s.version = "0.2.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Craig P Jolicoeur"]
|
12
|
+
s.date = %q{2010-03-03}
|
13
|
+
s.description = %q{Ruby wrapper around the Constant Contact REST API}
|
14
|
+
s.email = %q{cpjolicoeur@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
".gitignore",
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"TODO",
|
27
|
+
"VERSION.yml",
|
28
|
+
"constant-contact-ruby.gemspec",
|
29
|
+
"constant-contact-ruby.rb",
|
30
|
+
"lib/constant_contact.rb",
|
31
|
+
"lib/constant_contact/activity.rb",
|
32
|
+
"lib/constant_contact/base_resource.rb",
|
33
|
+
"lib/constant_contact/contact.rb",
|
34
|
+
"lib/constant_contact/contact_list.rb",
|
35
|
+
"test/fixtures/get_contact.xml",
|
36
|
+
"test/fixtures/get_contacts.xml",
|
37
|
+
"test/fixtures/post_contacts.xml",
|
38
|
+
"test/helper.rb",
|
39
|
+
"test/test_constant-contact-ruby.rb",
|
40
|
+
"test/test_contacts.rb"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/cpjolicoeur/constant-contact-ruby}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.5}
|
46
|
+
s.summary = %q{Constant Contact Ruby API Wrapper}
|
47
|
+
s.test_files = [
|
48
|
+
"test/helper.rb",
|
49
|
+
"test/test_constant-contact-ruby.rb",
|
50
|
+
"test/test_contacts.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
58
|
+
else
|
59
|
+
end
|
60
|
+
else
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ConstantContact
|
2
|
+
|
3
|
+
include HTTParty
|
4
|
+
format :xml
|
5
|
+
headers 'Accept' => 'application/atom+xml'
|
6
|
+
headers 'Content-Type' => 'application/atom+xml'
|
7
|
+
|
8
|
+
API_KEY = "59ca4bb4-51e9-4c08-a2b2-a34aac7bb78f"
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Create a connection to the Constant Contact API using your login credentials
|
12
|
+
def setup( user, pass )
|
13
|
+
basic_auth "#{API_KEY}%#{user}", pass
|
14
|
+
base_uri "https://api.constantcontact.com/ws/customers/#{user.downcase}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
module ConstantContact
|
2
|
+
class Activity < BaseResource
|
3
|
+
|
4
|
+
ADD_CONTACTS = 'ADD_CONTACTS'.freeze
|
5
|
+
REMOVE_CONTACTS = 'REMOVE_CONTACTS_FROM_LISTS'.freeze
|
6
|
+
CLEAR_CONTACTS = 'CLEAR_CONTACTS_FROM_LISTS'.freeze
|
7
|
+
EXPORT_CONTACTS = 'EXPORT_CONTACTS'.freeze
|
8
|
+
|
9
|
+
attr_reader :uid, :original_xml
|
10
|
+
|
11
|
+
def initialize( params={}, orig_xml='' ) #:nodoc:
|
12
|
+
return false if params.empty?
|
13
|
+
|
14
|
+
@uid = params['id'].split('/').last
|
15
|
+
@original_xml = orig_xml
|
16
|
+
|
17
|
+
fields = params['content']['Activity']
|
18
|
+
|
19
|
+
if errors = fields.delete( 'Errors' )
|
20
|
+
# FIXME: handle the <Errors> node properly
|
21
|
+
end
|
22
|
+
|
23
|
+
fields.each do |k,v|
|
24
|
+
underscore_key = underscore( k )
|
25
|
+
|
26
|
+
instance_eval %{
|
27
|
+
@#{underscore_key} = "#{v}"
|
28
|
+
|
29
|
+
def #{underscore_key}
|
30
|
+
@#{underscore_key}
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# List all activities
|
38
|
+
def self.all( options={} )
|
39
|
+
activities = []
|
40
|
+
|
41
|
+
data = ConstantContact.get( '/activities', options )
|
42
|
+
return activities if ( data.nil? or data.empty? or data['feed']['entry'].nil? )
|
43
|
+
|
44
|
+
data['feed']['entry'].each do |entry|
|
45
|
+
activities << new( entry )
|
46
|
+
end
|
47
|
+
|
48
|
+
activities
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get the details of a specific activity
|
52
|
+
def self.get( id, options={} )
|
53
|
+
activity = ConstantContact.get( "/activities/#{id.to_s}", options )
|
54
|
+
return nil if ( activity.nil? or activity.empty? )
|
55
|
+
new( activity['entry'], activity.body )
|
56
|
+
end
|
57
|
+
|
58
|
+
# Add multiple users to one or more contact lists
|
59
|
+
def self.add_contacts_to_lists( users=[], *lists )
|
60
|
+
data_param = build_data_param( users )
|
61
|
+
list_param = build_lists_param( *lists )
|
62
|
+
|
63
|
+
data = ConstantContact.post( '/activities',
|
64
|
+
:headers => { 'Content-Type' => 'application/x-www-form-urlencoded' },
|
65
|
+
:body => { 'activityType' => ADD_CONTACTS, :data => data_param, :lists => list_param } )
|
66
|
+
|
67
|
+
if data.code == 201
|
68
|
+
new( data['entry'] )
|
69
|
+
else
|
70
|
+
puts "HTTP Status Code: #{data.code}, message: #{data.message}"
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Remove multiple users from a contact list
|
76
|
+
def self.remove_contacts_from_lists( users=[], *lists )
|
77
|
+
data_param = build_data_param( users )
|
78
|
+
list_param = build_lists_param( *lists )
|
79
|
+
|
80
|
+
data = ConstantContact.post( '/activities',
|
81
|
+
:headers => { 'Content-Type' => 'application/x-www-form-urlencoded' },
|
82
|
+
:body => { 'activityType' => REMOVE_CONTACTS, :data => data_param, :lists => list_param } )
|
83
|
+
|
84
|
+
if data.code == 201
|
85
|
+
new( data['entry'] )
|
86
|
+
else
|
87
|
+
puts "HTTP Status Code: #{data.code}, message: #{data.message}"
|
88
|
+
return false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Remove all users from a specific contact list
|
93
|
+
def self.remove_all_contacts_from_lists( *lists )
|
94
|
+
list_param = build_lists_param( *lists )
|
95
|
+
|
96
|
+
data = ConstantContact.post( '/activities',
|
97
|
+
:headers => { 'Content-Type' => 'application/x-www-form-urlencoded' },
|
98
|
+
:body => { 'activityType' => CLEAR_CONTACTS, :lists => list_param } )
|
99
|
+
|
100
|
+
if data.code == 201
|
101
|
+
new( data['entry'] )
|
102
|
+
else
|
103
|
+
puts "HTTP Status Code: #{data.code}, message: #{data.message}"
|
104
|
+
return false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Export subscribers list to a file
|
109
|
+
#
|
110
|
+
# @param [Integer/String] list_id is the uid of the list to export
|
111
|
+
# @param [Array] fields is an array of fields to export for list contacts
|
112
|
+
#
|
113
|
+
def self.export( list_id, *fields )
|
114
|
+
export_columns = build_export_columns( *fields )
|
115
|
+
|
116
|
+
data = ConstantContact.post( '/activities',
|
117
|
+
:headers => { 'Content-Type' => 'application/x-www-form-urlencoded' },
|
118
|
+
:body => {
|
119
|
+
'activityType' => EXPORT_CONTACTS,
|
120
|
+
'fileType' => 'CSV',
|
121
|
+
'exportOptDate' => true,
|
122
|
+
'exportOptSource' => true,
|
123
|
+
'exportListName' => true,
|
124
|
+
'sortBy' => 'EMAIL_ADDRESS',
|
125
|
+
'listId' => ContactList.url_for( list_id ),
|
126
|
+
:columns => export_columns
|
127
|
+
} )
|
128
|
+
|
129
|
+
if data.code == 201
|
130
|
+
new( data['entry'] )
|
131
|
+
else
|
132
|
+
puts "HTTP Status Code: #{data.code}, message: #{data.message}"
|
133
|
+
return false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
# Build the data= query param for a POST request
|
140
|
+
#
|
141
|
+
# @param [Array] Users - an array of user hash objects
|
142
|
+
#
|
143
|
+
def self.build_data_param( users )
|
144
|
+
return '' if users.empty?
|
145
|
+
data_start, data_end = '', ''
|
146
|
+
keys, fields = [], []
|
147
|
+
|
148
|
+
# get a list of all the key fields and then create values
|
149
|
+
users.each do |u|
|
150
|
+
u.each_key do |k|
|
151
|
+
readable_key = underscore(k).split('_').map{|x| x.capitalize}.join(' ')
|
152
|
+
packet = { :original => k, :readable => readable_key }
|
153
|
+
keys << packet unless keys.include?( packet )
|
154
|
+
end
|
155
|
+
end
|
156
|
+
data_start = keys.map { |k| k[:readable] }.join(',') + "\n"
|
157
|
+
|
158
|
+
# now build the data fields
|
159
|
+
users.each do |u|
|
160
|
+
tmp = ''
|
161
|
+
keys.each { |k| tmp << "#{u[k[:original]]}," }
|
162
|
+
fields << tmp.chomp(',') + "\n"
|
163
|
+
end
|
164
|
+
data_end = fields.join
|
165
|
+
|
166
|
+
return data_start + data_end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Build the lists= param for a POST request
|
170
|
+
#
|
171
|
+
# @param [Array] lists - an array of list ids
|
172
|
+
# @return [String] a usable string for the list param in a POST
|
173
|
+
def self.build_lists_param( *lists )
|
174
|
+
# list_param = lists.map { |list| ContactList.url_for( list.to_s ) }
|
175
|
+
#
|
176
|
+
# FIXME: this is hack because passing in an array of lists wasnt working because of Hash.to_params
|
177
|
+
# Hash#to_params returns lists[]=foo&lists[]=bar instead of lists=foo&lists=bar
|
178
|
+
# I can't even find where Hash#to_params is defined!!!
|
179
|
+
list_param = ''
|
180
|
+
lists.each do |list|
|
181
|
+
list_param << "#{ContactList.url_for( list.to_s )}&lists="
|
182
|
+
end
|
183
|
+
list_param.chomp('&lists=')
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.build_export_columns( *fields )
|
187
|
+
columns_param = ''
|
188
|
+
fields.each do |field|
|
189
|
+
readable_col = underscore( field ).split('_').map{ |x| x.upcase }.join(' ')
|
190
|
+
columns_param << "#{readable_col}&columns="
|
191
|
+
end
|
192
|
+
columns_param.chomp('&columns=')
|
193
|
+
end
|
194
|
+
|
195
|
+
end # class Activity
|
196
|
+
end # module ConstantContact
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ConstantContact
|
2
|
+
class BaseResource #:nodoc:
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def self.camelize( string )
|
7
|
+
string.split( /[^a-z0-9]/i ).map{ |w| w.capitalize }.join
|
8
|
+
end
|
9
|
+
|
10
|
+
def camelize( string )
|
11
|
+
BaseResource.camelize( string )
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.underscore( string )
|
15
|
+
string.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
|
16
|
+
end
|
17
|
+
|
18
|
+
def underscore( string )
|
19
|
+
BaseResource.underscore( string )
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
module ConstantContact
|
2
|
+
class Contact < BaseResource
|
3
|
+
|
4
|
+
attr_reader :uid, :contact_lists, :original_xml
|
5
|
+
|
6
|
+
def initialize( params={}, orig_xml='', from_contact_list=false ) #:nodoc:
|
7
|
+
return false if params.empty?
|
8
|
+
|
9
|
+
@uid = params['id'].split('/').last
|
10
|
+
@original_xml = orig_xml
|
11
|
+
@contact_lists = []
|
12
|
+
|
13
|
+
if from_contact_list
|
14
|
+
fields = params['content']['ContactListMember']
|
15
|
+
else
|
16
|
+
fields = params['content']['Contact']
|
17
|
+
end
|
18
|
+
|
19
|
+
if lists = fields.delete( 'ContactLists' )
|
20
|
+
if lists['ContactList'].is_a?( Array )
|
21
|
+
@contact_lists = lists['ContactList'].collect { |list| list['id'].split('/').last }
|
22
|
+
else
|
23
|
+
@contact_lists << lists['ContactList']['id'].split('/').last
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
fields.each do |k,v|
|
28
|
+
underscore_key = underscore( k )
|
29
|
+
|
30
|
+
instance_eval %{
|
31
|
+
@#{underscore_key} = "#{v}"
|
32
|
+
|
33
|
+
def #{underscore_key}
|
34
|
+
@#{underscore_key}
|
35
|
+
end
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
end # def initialize
|
40
|
+
|
41
|
+
# Update a single contact record
|
42
|
+
#
|
43
|
+
# NOTE: you cannot update a Contact's ContactList subscriptions through
|
44
|
+
# this method. Use the apropriate ContactList methods instead
|
45
|
+
#
|
46
|
+
def update_attributes!( params={} )
|
47
|
+
return false unless full_record? # TODO: raise some kind of specific error here
|
48
|
+
|
49
|
+
params.each do |key,val|
|
50
|
+
self.instance_variable_set("@#{key.to_s}", val)
|
51
|
+
end
|
52
|
+
|
53
|
+
data = ConstantContact.put( "/contacts/#{self.uid}", :body => self.send(:to_xml) )
|
54
|
+
if data.code == 204 # success
|
55
|
+
return true
|
56
|
+
else
|
57
|
+
return false # probably should raise an error here instead
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Add user to a contact list
|
62
|
+
def add_to_list!( list_id, options={} )
|
63
|
+
list_id = list_id.to_s
|
64
|
+
xml = update_contact_lists( *(self.contact_lists + [list_id]) )
|
65
|
+
|
66
|
+
# FIXME: clean up the following code - it appears in 3 methods in this class!
|
67
|
+
options.merge!({ :body => xml })
|
68
|
+
data = ConstantContact.put( "/contacts/#{self.uid}", options )
|
69
|
+
|
70
|
+
if data.code == 204 # success
|
71
|
+
self.contact_lists << list_id unless self.contact_lists.include?( list_id )
|
72
|
+
return true
|
73
|
+
else
|
74
|
+
return false # probably should raise an error here instead
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Remove user from a contact list
|
79
|
+
def remove_from_list!( list_id, options={} )
|
80
|
+
list_id = list_id.to_s
|
81
|
+
xml = update_contact_lists( *(self.contact_lists - [list_id]) )
|
82
|
+
|
83
|
+
# FIXME: clean up the following code - it appears in 3 methods in this class!
|
84
|
+
options.merge!({ :body => xml })
|
85
|
+
data = ConstantContact.put( "/contacts/#{self.uid}", options )
|
86
|
+
|
87
|
+
if data.code == 204 # success
|
88
|
+
self.contact_lists.delete( list_id )
|
89
|
+
return true
|
90
|
+
else
|
91
|
+
return false # probably should raise an error here instead
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Set a users contact lists
|
96
|
+
def replace_contact_lists!( *lists )
|
97
|
+
xml = update_contact_lists( *lists )
|
98
|
+
|
99
|
+
# FIXME: clean up the following code - it appears in 3 methods in this class!
|
100
|
+
options = { :body => xml }
|
101
|
+
data = ConstantContact.put( "/contacts/#{self.uid}", options )
|
102
|
+
|
103
|
+
if data.code == 204 # success
|
104
|
+
@contact_lists = lists.map { |l| l.to_s }
|
105
|
+
return true
|
106
|
+
else
|
107
|
+
return false # probably should raise an error here instead
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Opt-out from all contact lists
|
112
|
+
#
|
113
|
+
# Contact will be removed from all lists and become a member of the
|
114
|
+
# Do-Not_Mail special list
|
115
|
+
def opt_out!( options={} )
|
116
|
+
data = ConstantContact.delete( "/contacts/#{self.uid}", options )
|
117
|
+
|
118
|
+
if data.code == 204
|
119
|
+
@contact_lists = []
|
120
|
+
return true
|
121
|
+
else
|
122
|
+
return false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Opt-in a user who has previously opted out
|
127
|
+
#--
|
128
|
+
# FIXME: this isn't currently working. Currently I keep getting a 403-Forbidden response
|
129
|
+
# should this really even be in the API wrapper at all?
|
130
|
+
def opt_in!( *lists )
|
131
|
+
# # NOTE: same as replace_contact_lists but must set to ACTION_BY_CONTACT
|
132
|
+
# xml = update_contact_lists( *lists ).gsub( /<\/ContactLists>/, %Q(<OptInSource>ACTION_BY_CONTACT</OptInSource>\n\t</ContactLists>) )
|
133
|
+
|
134
|
+
# # FIXME: clean up the following code - it appears in 3 methods in this class!
|
135
|
+
# options = { :body => xml }
|
136
|
+
# data = ConstantContact.put( "/contacts/#{self.uid}", options )
|
137
|
+
#
|
138
|
+
# if data.code == 204 # success
|
139
|
+
# @contact_lists = lists.map { |l| l.to_s }
|
140
|
+
# return true
|
141
|
+
# else
|
142
|
+
# return false # probably should raise an error here instead
|
143
|
+
# end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Get a summary list all contacts
|
147
|
+
def self.all( options={} )
|
148
|
+
contacts = []
|
149
|
+
|
150
|
+
data = ConstantContact.get( '/contacts', options )
|
151
|
+
return contacts if ( data.nil? or data.empty? )
|
152
|
+
|
153
|
+
data['feed']['entry'].each do |entry|
|
154
|
+
contacts << new( entry )
|
155
|
+
end
|
156
|
+
|
157
|
+
contacts
|
158
|
+
end
|
159
|
+
|
160
|
+
# Add a new contact
|
161
|
+
#
|
162
|
+
# Required data fields:
|
163
|
+
# * EmailAddress => String
|
164
|
+
# * ContactLists => Array of list IDs
|
165
|
+
#
|
166
|
+
# Options data fields:
|
167
|
+
# * EmailType
|
168
|
+
# * FirstName
|
169
|
+
# * MiddleName
|
170
|
+
# * LastName
|
171
|
+
# * JobTitle
|
172
|
+
# * CompanyName
|
173
|
+
# * HomePhone
|
174
|
+
# * WorkPhone
|
175
|
+
# * Addr1
|
176
|
+
# * Addr2
|
177
|
+
# * Addr3
|
178
|
+
# * City
|
179
|
+
# * StateCode => Must be valid US/Canada Code (http://ui.constantcontact.com/CCSubscriberAddFileFormat.jsp#states)
|
180
|
+
# * StateName
|
181
|
+
# * CountryCode = Must be valid code (http://constantcontact.custhelp.com/cgi-bin/constantcontact.cfg/php/enduser/std_adp.php?p_faqid=3614)
|
182
|
+
# * CountryName
|
183
|
+
# * PostalCode
|
184
|
+
# * SubPostalCode
|
185
|
+
# * Note
|
186
|
+
# * CustomField[1-15]
|
187
|
+
# * OptInSource
|
188
|
+
# * OptOutSource
|
189
|
+
#
|
190
|
+
def self.add( data={}, opt_in='ACTION_BY_CUSTOMER', options={} )
|
191
|
+
xml = build_contact_xml_packet( data, opt_in )
|
192
|
+
|
193
|
+
options.merge!({ :body => xml })
|
194
|
+
data = ConstantContact.post( "/contacts", options )
|
195
|
+
|
196
|
+
# check response.code
|
197
|
+
if data.code == 201 # Entity Created
|
198
|
+
return new( data['entry'] )
|
199
|
+
else
|
200
|
+
# data.code == 409 # Conflict ( probably a duplicate )
|
201
|
+
puts "HTTP Status Code: #{data.code}, message: #{data.message}"
|
202
|
+
return nil
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Get detailed record for a single contact by id
|
207
|
+
def self.get( id, options={} )
|
208
|
+
data = ConstantContact.get( "/contacts/#{id.to_s}", options )
|
209
|
+
return nil if ( data.nil? or data.empty? )
|
210
|
+
new( data['entry'], data.body )
|
211
|
+
end
|
212
|
+
|
213
|
+
# Search for a contact by last updated date
|
214
|
+
#
|
215
|
+
# Valid options:
|
216
|
+
# * :updated_since => Time object
|
217
|
+
# * :list_type => One of 'active'|'removed'|'do-not-mail'
|
218
|
+
#
|
219
|
+
# def self.search_by_date( options={} )
|
220
|
+
# end
|
221
|
+
|
222
|
+
# Search for a contact by email address
|
223
|
+
#
|
224
|
+
# @param [String] email => "user@example.com"
|
225
|
+
#
|
226
|
+
def self.search_by_email( email )
|
227
|
+
data = ConstantContact.get( '/contacts', :query => { :email => email.downcase } )
|
228
|
+
return [] if ( data.nil? )
|
229
|
+
|
230
|
+
if data.code == 500
|
231
|
+
puts "HTTP Status Code: #{data.code}, message: #{data.message}"
|
232
|
+
return false
|
233
|
+
else
|
234
|
+
new( data['feed']['entry'] )
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns the objects API URI
|
239
|
+
def self.url_for( id )
|
240
|
+
"#{ConstantContact.base_uri}/contacts/#{id}"
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
def update_contact_lists( *lists )
|
246
|
+
str = %Q(<ContactLists>\n)
|
247
|
+
lists.each do |list|
|
248
|
+
str << %Q( <ContactList id="#{ContactList.url_for( list )}" />\n)
|
249
|
+
end
|
250
|
+
str << %Q( </ContactLists>)
|
251
|
+
|
252
|
+
# self.original_xml.gsub(/<ContactLists>.*<\/ContactLists>/m, str)
|
253
|
+
if self.original_xml =~ /<ContactLists>.*<\/ContactLists>/m
|
254
|
+
self.original_xml.gsub( /#{$&}/, str)
|
255
|
+
else
|
256
|
+
self.original_xml.gsub( /<\/Contact>/m, "#{str}\n</Contact>" )
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def self.build_contact_xml_packet( data={}, opt_in='ACTION_BY_CUSTOMER' )
|
261
|
+
xml = <<EOF
|
262
|
+
<entry xmlns="http://www.w3.org/2005/Atom">
|
263
|
+
<title type="text"> </title>
|
264
|
+
<updated>#{Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z")}</updated>
|
265
|
+
<author> </author>
|
266
|
+
<id>data:,none</id>
|
267
|
+
<summary type="text">Contact</summary>
|
268
|
+
<content type="application/vnd.ctct+xml">
|
269
|
+
<Contact xmlns="http://ws.constantcontact.com/ns/1.0/">
|
270
|
+
EOF
|
271
|
+
|
272
|
+
data.each do |key, val|
|
273
|
+
node = camelize(key.to_s)
|
274
|
+
|
275
|
+
if key == :contact_lists
|
276
|
+
xml << %Q( <ContactLists>\n)
|
277
|
+
val.each do |list_id|
|
278
|
+
xml<< %Q( <ContactList id="#{ContactList.url_for( list_id )}" />\n)
|
279
|
+
end
|
280
|
+
xml << %Q( </ContactLists>\n)
|
281
|
+
else
|
282
|
+
xml << %Q( <#{node}>#{val}</#{node}>\n)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
xml += <<EOF
|
287
|
+
<OptInSource>#{opt_in}</OptInSource>
|
288
|
+
</Contact>
|
289
|
+
</content>
|
290
|
+
</entry>
|
291
|
+
EOF
|
292
|
+
xml
|
293
|
+
end # def build_contact_xml_packet
|
294
|
+
|
295
|
+
# Is this a full contact record?
|
296
|
+
def full_record?
|
297
|
+
!self.contact_lists.empty?
|
298
|
+
end
|
299
|
+
|
300
|
+
# convert a full Contact record into XML format
|
301
|
+
def to_xml
|
302
|
+
return nil unless full_record?
|
303
|
+
|
304
|
+
do_not_process = [ "@contact_lists", "@original_source", "@original_xml", "@uid", "@xmlns" ]
|
305
|
+
|
306
|
+
xml = self.original_xml
|
307
|
+
|
308
|
+
self.instance_variables.each do |ivar|
|
309
|
+
next if do_not_process.include?( ivar )
|
310
|
+
|
311
|
+
var = camelize( ivar.gsub(/@/,'') )
|
312
|
+
|
313
|
+
xml.gsub!( /<#{var}>(.*)<\/#{var}>/ , "<#{var}>#{self.instance_variable_get(ivar)}</#{var}>" )
|
314
|
+
end
|
315
|
+
|
316
|
+
# replace <updated> node with current time
|
317
|
+
xml.gsub( /<updated>.*<\/updated>/, Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z") )
|
318
|
+
|
319
|
+
xml
|
320
|
+
end
|
321
|
+
|
322
|
+
end # class Contact
|
323
|
+
end # module ConstantContact
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module ConstantContact
|
2
|
+
class ContactList < BaseResource
|
3
|
+
|
4
|
+
attr_reader :uid, :original_xml
|
5
|
+
|
6
|
+
def initialize( params={}, orig_xml='' ) #:nodoc:
|
7
|
+
return false if params.empty?
|
8
|
+
|
9
|
+
@uid = params['id'].split('/').last
|
10
|
+
@original_xml = orig_xml
|
11
|
+
|
12
|
+
params['content']['ContactList'].each do |k,v|
|
13
|
+
underscore_key = underscore( k )
|
14
|
+
|
15
|
+
instance_eval %{
|
16
|
+
@#{underscore_key} = "#{v}"
|
17
|
+
|
18
|
+
def #{underscore_key}
|
19
|
+
@#{underscore_key}
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Update a contacts attributes
|
26
|
+
def update_attributes!( params={} )
|
27
|
+
return false unless full_record?
|
28
|
+
|
29
|
+
params.each do |key,val|
|
30
|
+
self.instance_variable_set("@#{key.to_s}", val)
|
31
|
+
end
|
32
|
+
|
33
|
+
data = ConstantContact.put( "/lists/#{self.uid}", :body => self.send(:to_xml) )
|
34
|
+
|
35
|
+
if data.code == 204
|
36
|
+
return true
|
37
|
+
else
|
38
|
+
return data # probably should raise an error here instead
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Remove all contacts from the contact list
|
43
|
+
def clear_contacts!
|
44
|
+
Activity.remove_all_contacts_from_lists( self.uid )
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get all contact lists
|
48
|
+
def self.all( options={} )
|
49
|
+
lists = []
|
50
|
+
|
51
|
+
data = ConstantContact.get( '/lists', options )
|
52
|
+
return lists if ( data.nil? or data.empty? )
|
53
|
+
|
54
|
+
data['feed']['entry'].each do |entry|
|
55
|
+
lists << new( entry )
|
56
|
+
end
|
57
|
+
|
58
|
+
lists
|
59
|
+
end
|
60
|
+
|
61
|
+
# Add a new contact list
|
62
|
+
def self.add( name, opt_in=false, sort_order=99, options={} )
|
63
|
+
return nil if( name.nil? || name.empty? )
|
64
|
+
|
65
|
+
xml = build_contact_list_xml_packet( name, opt_in, sort_order )
|
66
|
+
|
67
|
+
options.merge!({ :body => xml })
|
68
|
+
data = ConstantContact.post( "/lists", options )
|
69
|
+
|
70
|
+
if data.code == 201
|
71
|
+
return new( data['entry'] )
|
72
|
+
else
|
73
|
+
puts "HTTP Status Code: #{data.code}, message: #{data.message}"
|
74
|
+
return nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Delete a contact list
|
79
|
+
def self.delete( id, options={} )
|
80
|
+
data = ConstantContact.delete( "/lists/#{id.to_s}", options )
|
81
|
+
return ( data.code == 204 ) ? true : false
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get a single contact list
|
85
|
+
def self.get( id, options={} )
|
86
|
+
list = ConstantContact.get( "/lists/#{id.to_s}", options )
|
87
|
+
return nil if ( list.nil? or list.empty? )
|
88
|
+
new( list['entry'], list.body )
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get a lists members
|
92
|
+
def self.members( id, options={} )
|
93
|
+
members = ConstantContact.get( "/lists/#{id.to_s}/members", options )
|
94
|
+
return nil if ( members.nil? or members.empty? )
|
95
|
+
members['feed']['entry'].collect { |entry| Contact.new( entry, '', true ) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the objects API URI
|
99
|
+
def self.url_for( id )
|
100
|
+
"#{ConstantContact.base_uri}/lists/#{id}"
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def self.build_contact_list_xml_packet( name, opt_in=false, sort=99 )
|
106
|
+
xml = <<EOF
|
107
|
+
<entry xmlns="http://www.w3.org/2005/Atom">
|
108
|
+
<updated>#{Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z")}</updated>
|
109
|
+
<title />
|
110
|
+
<author />
|
111
|
+
<id>data:,none</id>
|
112
|
+
<content type="application/vnd.ctct+xml">
|
113
|
+
<ContactList xmlns="http://ws.constantcontact.com/ns/1.0/">
|
114
|
+
<Name>#{name}</Name>
|
115
|
+
<SortOrder>#{sort}</SortOrder>
|
116
|
+
<OptInDefault>#{opt_in.to_s}</OptInDefault>
|
117
|
+
</ContactList>
|
118
|
+
</content>
|
119
|
+
</entry>
|
120
|
+
EOF
|
121
|
+
xml
|
122
|
+
end
|
123
|
+
|
124
|
+
# Is this a full record or a summary record?
|
125
|
+
def full_record?
|
126
|
+
!self.members.emtpy?
|
127
|
+
end
|
128
|
+
|
129
|
+
# Convert the object into the needed API XML format
|
130
|
+
def to_xml
|
131
|
+
return nil unless full_record?
|
132
|
+
|
133
|
+
do_not_process = [ "@original_xml", "@uid", "@members" ]
|
134
|
+
|
135
|
+
xml = self.original_xml
|
136
|
+
|
137
|
+
self.instance_variables.each do |ivar|
|
138
|
+
next if do_not_process.include?( ivar )
|
139
|
+
|
140
|
+
var = camelize( ivar.gsub(/@/,'') )
|
141
|
+
|
142
|
+
xml.gsub!( /<#{var}>(.*)<\/#{var}>/ , "<#{var}>#{self.instance_variable_get(ivar)}</#{var}>" )
|
143
|
+
end
|
144
|
+
|
145
|
+
# replace <updated> node with current time
|
146
|
+
xml.gsub( /<updated>.*<\/updated>/, Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z") )
|
147
|
+
|
148
|
+
xml
|
149
|
+
end
|
150
|
+
|
151
|
+
end # class ContactList
|
152
|
+
end # module ConstantContact
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
2
|
+
<entry xmlns="http://www.w3.org/2005/Atom">
|
3
|
+
<link href="/ws/customers/joesflowers/contacts/22199" rel="edit" />
|
4
|
+
<id>http://api.constantcontact.com/ws/customers/joesflowers/contacts/22199</id>
|
5
|
+
<title type="text">Contact: joe@example.com</title>
|
6
|
+
<updated>2009-11-20T20:41:19.243Z</updated>
|
7
|
+
<author>
|
8
|
+
<name>Constant Contact</name>
|
9
|
+
</author>
|
10
|
+
<content type="application/vnd.ctct+xml">
|
11
|
+
<Contact xmlns="http://ws.constantcontact.com/ns/1.0/" id="http://api.constantcontact.
|
12
|
+
com/ws/customers/joesflowers/contacts/22199">
|
13
|
+
<Status>Active</Status>
|
14
|
+
<EmailAddress>joe@example.com</EmailAddress>
|
15
|
+
<EmailType>HTML</EmailType>
|
16
|
+
<Name>Customer Joe</Name>
|
17
|
+
<FirstName>Joe</FirstName>
|
18
|
+
<MiddleName></MiddleName>
|
19
|
+
<LastName>Smith</LastName>
|
20
|
+
<JobTitle></JobTitle>
|
21
|
+
<CompanyName></CompanyName>
|
22
|
+
<HomePhone></HomePhone>
|
23
|
+
<WorkPhone></WorkPhone>
|
24
|
+
<Addr1></Addr1>
|
25
|
+
<Addr2></Addr2>
|
26
|
+
<Addr3></Addr3>
|
27
|
+
<City></City>
|
28
|
+
<StateCode>MA</StateCode>
|
29
|
+
<StateName>Massachusetts</StateName>
|
30
|
+
<CountryCode>us</CountryCode>
|
31
|
+
<CountryName>United States</CountryName>
|
32
|
+
<PostalCode>02154</PostalCode>
|
33
|
+
<SubPostalCode>1781</SubPostalCode>
|
34
|
+
<Note></Note>
|
35
|
+
<CustomField1></CustomField1>
|
36
|
+
<CustomField2></CustomField2>
|
37
|
+
<CustomField3></CustomField3>
|
38
|
+
<CustomField4></CustomField4>
|
39
|
+
<CustomField5></CustomField5>
|
40
|
+
<CustomField6></CustomField6>
|
41
|
+
<CustomField7></CustomField7>
|
42
|
+
<CustomField8></CustomField8>
|
43
|
+
<CustomField9></CustomField9>
|
44
|
+
<CustomField10></CustomField10>
|
45
|
+
<CustomField11></CustomField11>
|
46
|
+
<CustomField12></CustomField12>
|
47
|
+
<CustomField13></CustomField13>
|
48
|
+
<CustomField14></CustomField14>
|
49
|
+
<CustomField15></CustomField15>
|
50
|
+
<ContactLists>
|
51
|
+
<ContactList id="http://api.constantcontact.com/ws/customers/joesflowers/lists/3">
|
52
|
+
<link xmlns="http://www.w3.org/2005/Atom" href="/ws/customers/joesflowers/lists/3" rel="self" />
|
53
|
+
<OptInSource>ACTION_BY_CONTACT</OptInSource>
|
54
|
+
<OptInTime>2009-11-20T20:41:06.595Z</OptInTime>
|
55
|
+
</ContactList>
|
56
|
+
</ContactLists>
|
57
|
+
<Confirmed>false</Confirmed>
|
58
|
+
<InsertTime>2009-11-20T20:41:06.593Z</InsertTime>
|
59
|
+
<LastUpdateTime>2009-11-20T20:41:19.243Z</LastUpdateTime>
|
60
|
+
</Contact>
|
61
|
+
</content>
|
62
|
+
<source>
|
63
|
+
<id>http://api.constantcontact.com/ws/customers/joesflowers/contacts</id>
|
64
|
+
<title type="text">Contacts for Customer: joesflowers</title>
|
65
|
+
<link href="contacts" />
|
66
|
+
<link href="contacts" rel="self" />
|
67
|
+
<author>
|
68
|
+
<name>joesflowers</name>
|
69
|
+
</author>
|
70
|
+
<updated>2009-11-20T20:45:44.199Z</updated>
|
71
|
+
</source>
|
72
|
+
</entry>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
2
|
+
<feed xmlns="http://www.w3.org/2005/Atom">
|
3
|
+
<id>http://api.constantcontact.com/ws/customers/joesflowers/contacts</id>
|
4
|
+
<title type="text">Contacts for Customer: joesflowers</title>
|
5
|
+
<link href="contacts" />
|
6
|
+
<link href="contacts" rel="self" />
|
7
|
+
<author>
|
8
|
+
<name>joesflowers</name>
|
9
|
+
</author>
|
10
|
+
<updated>2009-11-19T14:49:21.928Z</updated>
|
11
|
+
<link href="/ws/customers/joesflowers/contacts?next=g24umerp-fymhqg" rel="next" />
|
12
|
+
<link href="/ws/customers/joesflowers/contacts" rel="first" />
|
13
|
+
<link href="/ws/customers/joesflowers/contacts" rel="current" />
|
14
|
+
<entry>
|
15
|
+
<link href="/ws/customers/joesflowers/contacts/21930" rel="edit" />
|
16
|
+
<id>http://api.constantcontact.com/ws/customers/joesflowers/contacts/21930</id>
|
17
|
+
<title type="text">Contact: 1258642119407@example.com</title>
|
18
|
+
<updated>2009-11-19T14:49:23.203Z</updated>
|
19
|
+
<author>
|
20
|
+
<name>Constant Contact</name>
|
21
|
+
</author>
|
22
|
+
<content type="application/vnd.ctct+xml">
|
23
|
+
<Contact xmlns="http://ws.constantcontact.com/ns/1.0/" id="http://api.constantcontact.
|
24
|
+
com/ws/customers/joesflowers/contacts/21930">
|
25
|
+
<Status>Active</Status>
|
26
|
+
<EmailAddress>1258642119407@example.com</EmailAddress>
|
27
|
+
<EmailType>HTML</EmailType>
|
28
|
+
<Name>Customer 1</Name>
|
29
|
+
<OptInTime>2009-11-19T14:48:41.761Z</OptInTime>
|
30
|
+
<OptInSource>ACTION_BY_CONTACT</OptInSource>
|
31
|
+
</Contact>
|
32
|
+
</content>
|
33
|
+
</entry>
|
34
|
+
<entry>
|
35
|
+
<link href="/ws/customers/joesflowers/contacts/21929" rel="edit" />
|
36
|
+
<id>http://api.constantcontact.com/ws/customers/joesflowers/contacts/21929</id>
|
37
|
+
<title type="text">Contact: 1258641848349@example.com</title>
|
38
|
+
<updated>2009-11-19T14:49:23.204Z</updated>
|
39
|
+
<author>
|
40
|
+
<name>Constant Contact</name>
|
41
|
+
</author>
|
42
|
+
<content type="application/vnd.ctct+xml">
|
43
|
+
<Contact xmlns="http://ws.constantcontact.com/ns/1.0/" id="http://api.constantcontact.
|
44
|
+
com/ws/customers/joesflowers/contacts/21929">
|
45
|
+
<Status>Active</Status>
|
46
|
+
<EmailAddress>1258641848349@example.com</EmailAddress>
|
47
|
+
<EmailType>HTML</EmailType>
|
48
|
+
<Name>Customer 2</Name>
|
49
|
+
<OptInTime>2009-11-19T14:44:10.873Z</OptInTime>
|
50
|
+
<OptInSource>ACTION_BY_CONTACT</OptInSource>
|
51
|
+
</Contact>
|
52
|
+
</content>
|
53
|
+
</entry>
|
54
|
+
</feed>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<entry xmlns="http://www.w3.org/2005/Atom">
|
2
|
+
<title type="text"> </title>
|
3
|
+
<updated>2008-07-23T14:21:06.407Z</updated>
|
4
|
+
<author></author>
|
5
|
+
<id>data:,none</id>
|
6
|
+
<summary type="text">Contact</summary>
|
7
|
+
<content type="application/vnd.ctct+xml">
|
8
|
+
<Contact xmlns="http://ws.constantcontact.com/ns/1.0/">
|
9
|
+
<EmailAddress>test_100@example.com</EmailAddress>
|
10
|
+
<FirstName>First</FirstName>
|
11
|
+
<LastName>Last</LastName>
|
12
|
+
<OptInSource>ACTION_BY_CONTACT</OptInSource>
|
13
|
+
<ContactLists>
|
14
|
+
<ContactList id="http://api.constantcontact.com/ws/customers/geeksquad/joesflowers/lists/1" />
|
15
|
+
</ContactLists>
|
16
|
+
</Contact>
|
17
|
+
</content>
|
18
|
+
</entry>
|
data/test/helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'fakeweb'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib', 'constant_contact'))
|
8
|
+
require 'constant-contact-ruby'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
|
12
|
+
fixtures_path = File.join( File.dirname( __FILE__ ), 'fixtures' )
|
13
|
+
|
14
|
+
# setup FakeWeb
|
15
|
+
FakeWeb.allow_net_connect = false
|
16
|
+
# GET /contacts
|
17
|
+
FakeWeb.register_uri( :get, %r{https://.+:.+@api\.constantcontact\.com/ws/customers/.+/contacts$}, :body => File.read( File.join( fixtures_path, 'get_contacts.xml' ) ) )
|
18
|
+
# GET /contact/:id
|
19
|
+
FakeWeb.register_uri( :get, %r{https://.+:.+@api\.constantcontact\.com/ws/customers/.+/contact/\d+$}, :body => File.read( File.join( fixtures_path, 'get_contact.xml' ) ) )
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'contact'
|
3
|
+
|
4
|
+
class TestContact < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
ConstantContact.setup( 'user', 'password' )
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_all_contacts
|
11
|
+
contacts = ConstantContact::Contact.all
|
12
|
+
assert !contacts.nil?
|
13
|
+
assert_equal 2, contacts.size
|
14
|
+
assert_equal ConstantContact::Contact, contacts.first.class
|
15
|
+
|
16
|
+
assert_equal 'Customer 1', contacts.first.name
|
17
|
+
assert_equal '21930', contacts.first.uid
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_all_contacts_with_bad_credentials
|
21
|
+
# FakeWeb.register_uri( :get, %r{https://.+:.+@api\.constantcontact\.com/ws/customers/.+/contacts}, :body => '', :status => ['403', 'Not Authorized'] )
|
22
|
+
# contacts = ConstantContact::Contact.all
|
23
|
+
# assert contacts.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_get_contact
|
27
|
+
contact = ConstantContact::Contact.get( 22199 )
|
28
|
+
assert_equal 'Customer Joe', contact.name
|
29
|
+
assert_equal '22199', contact.uid
|
30
|
+
assert_equal 'joe@example.com', contact.emailaddress
|
31
|
+
|
32
|
+
# TODO: contactLists
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_add_contact
|
36
|
+
# c = ConstantContact::Contact.add(
|
37
|
+
# :email_address => 'test@example.com',
|
38
|
+
# :first_name => 'First',
|
39
|
+
# :last_name => 'Name',
|
40
|
+
# :opt_in_source => 'ACTION_BY_CONTACT', # or ACTION_BY_CUSTOMER
|
41
|
+
# :contact_lists => [1]
|
42
|
+
# )
|
43
|
+
|
44
|
+
# assert_equal 'test@example.com', c.email_address
|
45
|
+
#
|
46
|
+
# contact = ConstantContact::Contact.get( c.uid )
|
47
|
+
# assert_equal 'test@example.com', contact.email_address
|
48
|
+
end
|
49
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: constant-contact-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Craig P Jolicoeur
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-03 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Ruby wrapper around the Constant Contact REST API
|
17
|
+
email: cpjolicoeur@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
- TODO
|
26
|
+
files:
|
27
|
+
- .document
|
28
|
+
- .gitignore
|
29
|
+
- LICENSE
|
30
|
+
- README.rdoc
|
31
|
+
- Rakefile
|
32
|
+
- TODO
|
33
|
+
- VERSION.yml
|
34
|
+
- constant-contact-ruby.gemspec
|
35
|
+
- constant-contact-ruby.rb
|
36
|
+
- lib/constant_contact.rb
|
37
|
+
- lib/constant_contact/activity.rb
|
38
|
+
- lib/constant_contact/base_resource.rb
|
39
|
+
- lib/constant_contact/contact.rb
|
40
|
+
- lib/constant_contact/contact_list.rb
|
41
|
+
- test/fixtures/get_contact.xml
|
42
|
+
- test/fixtures/get_contacts.xml
|
43
|
+
- test/fixtures/post_contacts.xml
|
44
|
+
- test/helper.rb
|
45
|
+
- test/test_constant-contact-ruby.rb
|
46
|
+
- test/test_contacts.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: http://github.com/cpjolicoeur/constant-contact-ruby
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options:
|
53
|
+
- --charset=UTF-8
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.3.5
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Constant Contact Ruby API Wrapper
|
75
|
+
test_files:
|
76
|
+
- test/helper.rb
|
77
|
+
- test/test_constant-contact-ruby.rb
|
78
|
+
- test/test_contacts.rb
|