constant-contact-ruby 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|