acts_as_icontact 0.1.1 → 0.1.5
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/README.markdown +12 -12
- data/VERSION +1 -1
- data/acts_as_icontact.gemspec +8 -2
- data/lib/acts_as_icontact/config.rb +12 -10
- data/lib/acts_as_icontact/exceptions.rb +6 -0
- data/lib/acts_as_icontact/resource.rb +129 -22
- data/lib/acts_as_icontact/resources/account.rb +6 -1
- data/lib/acts_as_icontact/resources/client.rb +5 -0
- data/lib/acts_as_icontact/resources/contact.rb +14 -1
- data/lib/acts_as_icontact/resources/list.rb +24 -0
- data/lib/acts_as_icontact/resources/message.rb +19 -0
- data/spec/config_spec.rb +9 -9
- data/spec/resource_spec.rb +51 -7
- data/spec/resources/contact_spec.rb +20 -3
- data/spec/resources/list_spec.rb +35 -0
- data/spec/resources/message_spec.rb +28 -0
- data/spec/spec_fakeweb.rb +30 -11
- data/spec/spec_helper.rb +1 -1
- metadata +8 -2
data/README.markdown
CHANGED
@@ -23,26 +23,26 @@ Using ActsAsIcontact is easy, but going through iContact's authorization process
|
|
23
23
|
|
24
24
|
$ sudo gem install acts_as_icontact
|
25
25
|
|
26
|
-
2. _Optional but recommended:_ Go to <http://
|
26
|
+
2. _Optional but recommended:_ Go to <http://sandbox.icontact.com> and sign up for an iContact Sandbox account. This will let you test your app without risk of blowing away your production mailing lists.
|
27
27
|
|
28
|
-
3. Enable the ActsAsIcontact gem for use with your iContact account. The URL and credentials you'll use are different between the
|
28
|
+
3. Enable the ActsAsIcontact gem for use with your iContact account. The URL and credentials you'll use are different between the sandbox and production environments:
|
29
29
|
|
30
|
-
* **
|
30
|
+
* **Sandbox:** Go to <http://app.sandbox.icontact.com/icp/core/externallogin> and enter `Ml5SnuFhnoOsuZeTOuZQnLUHTbzeUyhx` for the Application Id. Choose a password for ActsAsIcontact that's different from your account password.
|
31
31
|
|
32
32
|
* **PRODUCTION:** Go to <http://app.icontact.com/icp/core/externallogin> and enter `IYDOhgaZGUKNjih3hl1ItLln7zpAtWN2` for the Application Id. Choose a password for ActsAsIcontact that's different from your account password.
|
33
33
|
|
34
|
-
4. Set your _(
|
34
|
+
4. Set your _(sandbox, if applicable)_ account username and the password you just chose for API access. You can either set the environment variables `ICONTACT_MODE`, `ICONTACT_USERNAME`, and `ICONTACT_PASSWORD`, or you can explicitly do it with calls to the Config module:
|
35
35
|
|
36
36
|
require 'rubygems'
|
37
37
|
require 'acts_as_icontact'
|
38
38
|
|
39
|
-
ActsAsIcontact::Config.mode = :
|
40
|
-
ActsAsIcontact::Config.username =
|
39
|
+
ActsAsIcontact::Config.mode = :sandbox
|
40
|
+
ActsAsIcontact::Config.username = my_sandbox_username
|
41
41
|
ActsAsIcontact::Config.password = my_api_password
|
42
42
|
|
43
43
|
If you're using Rails, the recommended approach is to require the gem with `config.gem 'acts_as_icontact'` in your **config/environment.rb** file, and then set up an initializer (i.e. **config/initializers/acts\_as\_icontact.rb**) with the above code. See more about Rails below.
|
44
44
|
|
45
|
-
5. Rinse and repeat with production credentials when you're ready to move out of the
|
45
|
+
5. Rinse and repeat with production credentials when you're ready to move out of the sandbox environment.
|
46
46
|
|
47
47
|
API Access
|
48
48
|
----------
|
@@ -120,20 +120,20 @@ Rails Integration
|
|
120
120
|
The _real_ power of ActsAsIcontact is its automatic syncing with ActiveRecord. At this time this feature is focused entirely on Contacts.
|
121
121
|
|
122
122
|
### Activation
|
123
|
-
First add the line `config.gem 'acts_as_icontact'` to your **config/environment.rb** file. Then create an initializer (e.g. **config/initializers/acts\_as\_icontact.rb**) and set it up with your username and password. If applicable, you can give it both the
|
123
|
+
First add the line `config.gem 'acts_as_icontact'` to your **config/environment.rb** file. Then create an initializer (e.g. **config/initializers/acts\_as\_icontact.rb**) and set it up with your username and password. If applicable, you can give it both the sandbox _and_ production credentials:
|
124
124
|
|
125
125
|
module ActsAsIcontact
|
126
126
|
case Config.mode
|
127
|
-
when :
|
128
|
-
Config.username =
|
129
|
-
Config.password =
|
127
|
+
when :sandbox
|
128
|
+
Config.username = my_sandbox_username
|
129
|
+
Config.password = my_sandbox_password
|
130
130
|
when :production
|
131
131
|
Config.username = my_production_username
|
132
132
|
Config.password = my_production_password
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
If ActsAsIcontact detects that it's running in a Rails app, the default behavior is to set the mode to `:production` if RAILS\_ENV is equal to "production" and `:
|
136
|
+
If ActsAsIcontact detects that it's running in a Rails app, the default behavior is to set the mode to `:production` if RAILS\_ENV is equal to "production" and `:sandbox` if RAILS\_ENV is set to anything else. (Incidentally, if you're _not_ in a Rails app but running Rack, the same logic applies for the RACK\_ENV environment variable.)
|
137
137
|
|
138
138
|
Finally, enable one of your models to synchronize with iContact with a simple declaration:
|
139
139
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.5
|
data/acts_as_icontact.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{acts_as_icontact}
|
5
|
-
s.version = "0.1.
|
5
|
+
s.version = "0.1.5"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Stephen Eley"]
|
9
|
-
s.date = %q{2009-07-
|
9
|
+
s.date = %q{2009-07-25}
|
10
10
|
s.description = %q{ActsAsIcontact connects Ruby applications with the iContact e-mail marketing service using the iContact API v2.0. Building on the RestClient gem, it offers two significant feature sets:
|
11
11
|
|
12
12
|
* Simple, consistent access to all resources in the iContact API; and
|
@@ -33,6 +33,8 @@ Gem::Specification.new do |s|
|
|
33
33
|
"lib/acts_as_icontact/resources/account.rb",
|
34
34
|
"lib/acts_as_icontact/resources/client.rb",
|
35
35
|
"lib/acts_as_icontact/resources/contact.rb",
|
36
|
+
"lib/acts_as_icontact/resources/list.rb",
|
37
|
+
"lib/acts_as_icontact/resources/message.rb",
|
36
38
|
"spec/config_spec.rb",
|
37
39
|
"spec/connection_spec.rb",
|
38
40
|
"spec/resource_collection_spec.rb",
|
@@ -40,6 +42,8 @@ Gem::Specification.new do |s|
|
|
40
42
|
"spec/resources/account_spec.rb",
|
41
43
|
"spec/resources/client_spec.rb",
|
42
44
|
"spec/resources/contact_spec.rb",
|
45
|
+
"spec/resources/list_spec.rb",
|
46
|
+
"spec/resources/message_spec.rb",
|
43
47
|
"spec/spec.opts",
|
44
48
|
"spec/spec_fakeweb.rb",
|
45
49
|
"spec/spec_helper.rb"
|
@@ -58,6 +62,8 @@ Gem::Specification.new do |s|
|
|
58
62
|
"spec/resources/account_spec.rb",
|
59
63
|
"spec/resources/client_spec.rb",
|
60
64
|
"spec/resources/contact_spec.rb",
|
65
|
+
"spec/resources/list_spec.rb",
|
66
|
+
"spec/resources/message_spec.rb",
|
61
67
|
"spec/spec_fakeweb.rb",
|
62
68
|
"spec/spec_helper.rb"
|
63
69
|
]
|
@@ -3,15 +3,15 @@ module ActsAsIcontact
|
|
3
3
|
# required by the iContact API for authentication.
|
4
4
|
module Config
|
5
5
|
|
6
|
-
# Sets :production or :
|
6
|
+
# Sets :production or :sandbox. This changes the AppId and URL.
|
7
7
|
def self.mode=(val)
|
8
8
|
@mode = val
|
9
9
|
end
|
10
10
|
|
11
|
-
# Determines whether to return the
|
11
|
+
# Determines whether to return the sandbox or production AppId and URL.
|
12
12
|
# If not explicitly set, it will first look for an ICONTACT_MODE environment variable.
|
13
13
|
# If it doesn't find one, it will attempt to detect a Rails or Rack environment; in either
|
14
|
-
# case it will default to :production if RAILS_ENV or RACK_ENV is 'production', and :
|
14
|
+
# case it will default to :production if RAILS_ENV or RACK_ENV is 'production', and :sandbox
|
15
15
|
# otherwise. If none of these conditions apply, it assumes :production. (Because that
|
16
16
|
# probably means someone's doing ad hoc queries.)
|
17
17
|
def self.mode
|
@@ -19,9 +19,9 @@ module ActsAsIcontact
|
|
19
19
|
when ENV["ICONTACT_MODE"]
|
20
20
|
ENV["ICONTACT_MODE"].to_sym
|
21
21
|
when Object.const_defined?(:Rails)
|
22
|
-
(ENV["RAILS_ENV"] == 'production' ? :production : :
|
22
|
+
(ENV["RAILS_ENV"] == 'production' ? :production : :sandbox)
|
23
23
|
when Object.const_defined?(:Rack)
|
24
|
-
(ENV["RACK_ENV"] == 'production' ? :production : :
|
24
|
+
(ENV["RACK_ENV"] == 'production' ? :production : :sandbox)
|
25
25
|
else
|
26
26
|
:production
|
27
27
|
end
|
@@ -32,7 +32,7 @@ module ActsAsIcontact
|
|
32
32
|
# to change this. Ever.
|
33
33
|
def self.app_id
|
34
34
|
case mode
|
35
|
-
when :beta
|
35
|
+
when :beta, :sandbox
|
36
36
|
"Ml5SnuFhnoOsuZeTOuZQnLUHTbzeUyhx"
|
37
37
|
when :production
|
38
38
|
"IYDOhgaZGUKNjih3hl1ItLln7zpAtWN2"
|
@@ -45,14 +45,16 @@ module ActsAsIcontact
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Prefixed to the beginning of every API request. You can override this if you have some special
|
48
|
-
# need (e.g. working against a testing server, or if iContact
|
49
|
-
#
|
48
|
+
# need (e.g. working against a testing server, or if iContact changes their URL and you have to
|
49
|
+
# fix it before the gem gets updated), but for the most part you can leave it alone.
|
50
50
|
def self.url
|
51
51
|
@url ||= case mode
|
52
|
-
when :beta
|
53
|
-
"https://app.beta.icontact.com/icp/"
|
54
52
|
when :production
|
55
53
|
"https://app.icontact.com/icp/"
|
54
|
+
when :sandbox
|
55
|
+
"https://app.sandbox.icontact.com/icp/"
|
56
|
+
when :beta # The 'beta' environment still works as of 7/25/2009
|
57
|
+
"https://app.beta.icontact.com/icp/"
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
@@ -2,6 +2,12 @@ module ActsAsIcontact
|
|
2
2
|
# Thrown when a configuration value isn't provided or is invalid.
|
3
3
|
class ConfigError < StandardError; end
|
4
4
|
|
5
|
+
# Thrown when a bad parameter is passed to a resource find.
|
6
|
+
class QueryError < StandardError; end
|
7
|
+
|
8
|
+
# Thrown before saving if iContact validation rules are not met.
|
9
|
+
class ValidationError < StandardError; end
|
10
|
+
|
5
11
|
# Thrown when a resource calls save! and fails. Contains the +.errors+ array from
|
6
12
|
# the resource.
|
7
13
|
class RecordNotSaved < StandardError
|
@@ -8,11 +8,10 @@ module ActsAsIcontact
|
|
8
8
|
|
9
9
|
# Creates a new resource object from a values hash. (Which is passed to us via the magic of JSON.)
|
10
10
|
def initialize(properties={})
|
11
|
-
@properties = properties
|
11
|
+
@properties = clean_properties(properties)
|
12
12
|
@new_record = !@properties.has_key?(self.class.primary_key)
|
13
13
|
# Initialize other useful attributes
|
14
14
|
@errors = []
|
15
|
-
|
16
15
|
end
|
17
16
|
|
18
17
|
# Returns the primary key ID for an existing resource. Returns nil if the resource is a new record.
|
@@ -33,10 +32,14 @@ module ActsAsIcontact
|
|
33
32
|
if property =~ /(.*)=$/ # It's a value assignment
|
34
33
|
@newvalues ||= []
|
35
34
|
@newvalues << $1
|
36
|
-
@properties[$1] = params[0]
|
37
|
-
|
35
|
+
@properties[$1] = clean_value(params[0])
|
36
|
+
else
|
38
37
|
if @properties.has_key?(property)
|
39
|
-
|
38
|
+
if self.class.boolean_fields.include?(property)
|
39
|
+
(@properties[property] == 1)
|
40
|
+
else
|
41
|
+
@properties[property]
|
42
|
+
end
|
40
43
|
else
|
41
44
|
super
|
42
45
|
end
|
@@ -56,12 +59,15 @@ module ActsAsIcontact
|
|
56
59
|
# error, raises an exception with it.
|
57
60
|
def save
|
58
61
|
if new_record?
|
62
|
+
fields = create_fields
|
63
|
+
validate_on_create(fields)
|
59
64
|
result_type = self.class.collection_name
|
60
|
-
|
61
|
-
response = self.class.connection.post(payload.to_json)
|
65
|
+
response = self.class.connection.post([fields].to_json)
|
62
66
|
else
|
67
|
+
fields = update_fields
|
68
|
+
validate_on_update(fields)
|
63
69
|
result_type = self.class.resource_name
|
64
|
-
response = connection.post(
|
70
|
+
response = connection.post(fields.to_json)
|
65
71
|
end
|
66
72
|
parsed = JSON.parse(response)
|
67
73
|
if parsed[result_type].empty?
|
@@ -98,25 +104,46 @@ module ActsAsIcontact
|
|
98
104
|
|
99
105
|
# Returns an array of resources starting at the base.
|
100
106
|
def self.find(type, options={})
|
101
|
-
uri_extension = uri_component + build_query(options)
|
102
|
-
response = base[uri_extension].get
|
103
|
-
parsed = JSON.parse(response)
|
104
107
|
case type
|
105
|
-
when :first
|
106
|
-
|
107
|
-
when :all
|
108
|
-
|
108
|
+
when :first
|
109
|
+
first(options)
|
110
|
+
when :all
|
111
|
+
all(options)
|
112
|
+
when Integer
|
113
|
+
find_by_id(type)
|
114
|
+
else
|
115
|
+
raise ActsAsIcontact::QueryError, "Don't know how to find '#{type.to_s}'"
|
109
116
|
end
|
110
117
|
end
|
111
118
|
|
112
119
|
# Returns an array of resources starting at the base.
|
113
|
-
def self.all
|
114
|
-
|
120
|
+
def self.all(options={})
|
121
|
+
query_options = default_options.merge(options)
|
122
|
+
validate_options(query_options)
|
123
|
+
result = query_collection(query_options)
|
124
|
+
ResourceCollection.new(self, result)
|
115
125
|
end
|
116
126
|
|
117
127
|
# Returns the first account associated with this username.
|
118
|
-
def self.first
|
119
|
-
|
128
|
+
def self.first(options={})
|
129
|
+
query_options = default_options.merge(options).merge(:limit => 1) # Minor optimization
|
130
|
+
validate_options(query_options)
|
131
|
+
result = query_collection(query_options)
|
132
|
+
self.new(result[collection_name].first)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the single resource at the URL identified by the passed integer. Takes no options; this is
|
136
|
+
# not a search, this is a single record retrieval. Raises an exception if the record can't be found.
|
137
|
+
def self.find_by_id(id)
|
138
|
+
response = self.connection[id].get
|
139
|
+
parsed = JSON.parse(response)
|
140
|
+
raise ActsAsiContact::QueryError, "iContact's response did not contain a #{resource_name}!" unless parsed[resource_name]
|
141
|
+
self.new(parsed[resource_name])
|
142
|
+
end
|
143
|
+
|
144
|
+
# Two resources are identical if they have exactly the same property array.
|
145
|
+
def ==(obj)
|
146
|
+
properties == obj.properties
|
120
147
|
end
|
121
148
|
|
122
149
|
protected
|
@@ -139,10 +166,10 @@ module ActsAsIcontact
|
|
139
166
|
@properties
|
140
167
|
end
|
141
168
|
|
142
|
-
# The base RestClient resource that this particular class nests from.
|
143
|
-
# the
|
169
|
+
# The base RestClient resource that this particular class nests from. Defaults to
|
170
|
+
# the clientFolders path since that's the most common case.
|
144
171
|
def self.base
|
145
|
-
ActsAsIcontact.
|
172
|
+
ActsAsIcontact.client
|
146
173
|
end
|
147
174
|
|
148
175
|
# The name of the singular resource type pulled from iContact. Defaults to the lowercase
|
@@ -174,6 +201,11 @@ module ActsAsIcontact
|
|
174
201
|
resource_name + "Id"
|
175
202
|
end
|
176
203
|
|
204
|
+
# Options that are always passed on 'find' requests unless overridden.
|
205
|
+
def self.default_options
|
206
|
+
{:limit => 500}
|
207
|
+
end
|
208
|
+
|
177
209
|
# Fields that _must_ be included for this resource upon creation.
|
178
210
|
def self.required_on_create
|
179
211
|
[]
|
@@ -194,11 +226,86 @@ module ActsAsIcontact
|
|
194
226
|
[]
|
195
227
|
end
|
196
228
|
|
229
|
+
# Fields that operate as 0/1 boolean toggles. Can be assigned to with true and false.
|
230
|
+
def self.boolean_fields
|
231
|
+
[]
|
232
|
+
end
|
233
|
+
|
234
|
+
# Validation rules that ensure proper parameters are passed to iContact on querying.
|
235
|
+
def self.validate_options(options)
|
236
|
+
# See: http://developer.icontact.com/forums/api-beta-moderated-support/there-upper-limit-result-sets#comment-136
|
237
|
+
raise ActsAsIcontact::QueryError, "Limit must be between 1 and 500" if options[:limit].to_i < 1 or options[:limit].to_i > 500
|
238
|
+
end
|
239
|
+
|
240
|
+
# Validation rules that ensure proper data is passed to iContact on resource creation.
|
241
|
+
def validate_on_create(fields)
|
242
|
+
check_required_fields(fields, self.class.required_on_create)
|
243
|
+
validate_on_save(fields)
|
244
|
+
end
|
245
|
+
|
246
|
+
# Validation rules that ensure proper data is passed to iContact on resource update.
|
247
|
+
def validate_on_update(fields)
|
248
|
+
check_required_fields(fields, self.class.required_on_update)
|
249
|
+
validate_on_save(fields)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Validation rules that apply to both creates and updates. The method on the abstract Resource class is just a placeholder;
|
253
|
+
# this is intended to be used by resource subclasses.
|
254
|
+
def validate_on_save(fields)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Finesses the properties hash passed in to make iContact and Ruby idioms compatible.
|
258
|
+
# Turns symbol keys into strings and runs the clean_value method on values.
|
259
|
+
# Subclasses may add additional conversions.
|
260
|
+
def clean_properties(properties)
|
261
|
+
newhash = {}
|
262
|
+
properties.each_pair do |key, value|
|
263
|
+
newhash[key.to_s] = clean_value(value)
|
264
|
+
end
|
265
|
+
newhash
|
266
|
+
end
|
267
|
+
|
268
|
+
# Finesses values passed in to properties to make iContact and Ruby idioms compatible.
|
269
|
+
# Turns symbols into strings, numbers into integers or floats, true/false into 1 and 0,
|
270
|
+
# and empty strings into nil. Subclasses may add additional conversions.
|
271
|
+
def clean_value(value)
|
272
|
+
case value
|
273
|
+
when Symbol then value.to_s
|
274
|
+
when TrueClass then 1
|
275
|
+
when FalseClass then 0
|
276
|
+
when /^\d+$/ then value.to_i # Integer
|
277
|
+
when /^\d+(\.\d+)?([eE]\d+)?$/ then value.to_f # Float
|
278
|
+
when blank? then nil
|
279
|
+
else value
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# The properties array, for comparison against other resources or debugging.
|
284
|
+
def properties
|
285
|
+
@properties
|
286
|
+
end
|
287
|
+
|
197
288
|
private
|
198
289
|
def self.build_query(options={})
|
199
290
|
return "" if options.empty?
|
200
291
|
terms = options.collect{|k,v| "#{k}=#{URI.escape(v.to_s)}"}
|
201
292
|
build = "?" + terms.join('&')
|
202
293
|
end
|
294
|
+
|
295
|
+
def self.query_collection(options={})
|
296
|
+
uri_extension = uri_component + build_query(options)
|
297
|
+
response = base[uri_extension].get
|
298
|
+
parsed = JSON.parse(response)
|
299
|
+
parsed
|
300
|
+
end
|
301
|
+
|
302
|
+
def check_required_fields(fields, required)
|
303
|
+
# Check that all required fields are filled in
|
304
|
+
missing = required.select{|f| fields[f].blank?}
|
305
|
+
unless missing.empty?
|
306
|
+
missing_fields = missing.join(', ')
|
307
|
+
raise ActsAsIcontact::ValidationError, "Missing required fields: #{missing_fields}"
|
308
|
+
end
|
309
|
+
end
|
203
310
|
end
|
204
311
|
end
|
@@ -2,6 +2,11 @@ module ActsAsIcontact
|
|
2
2
|
# The top-level Accounts resource from iContact. Currently only supports retrieval -- and is
|
3
3
|
# highly targeted toward the _first_ account, since that seems to be the dominant use case.
|
4
4
|
class Account < Resource
|
5
|
+
# This is the one major resource that comes directly from the main path.
|
6
|
+
def self.base
|
7
|
+
ActsAsIcontact.connection
|
8
|
+
end
|
9
|
+
|
5
10
|
def self.uri_component
|
6
11
|
'a'
|
7
12
|
end
|
@@ -15,7 +20,7 @@ module ActsAsIcontact
|
|
15
20
|
# The accountId retrieved from iContact. Can also be set manually for performance optimization,
|
16
21
|
# but remembers it so that it won't be pulled more than once anyway.
|
17
22
|
def self.account_id
|
18
|
-
@account_id ||= Account.first.
|
23
|
+
@account_id ||= Account.first.id.to_i
|
19
24
|
end
|
20
25
|
|
21
26
|
# Manually sets the accountId used in subsequent calls. Setting this in your initializer will save
|
@@ -2,6 +2,11 @@ module ActsAsIcontact
|
|
2
2
|
# The nested Client Folder resource from iContact. Currently only supports retrieval -- and is
|
3
3
|
# highly targeted toward the _first_ client folder, since that seems to be the dominant use case.
|
4
4
|
class Client < Resource
|
5
|
+
# Derives from the Account resource.
|
6
|
+
def self.base
|
7
|
+
ActsAsIcontact.account
|
8
|
+
end
|
9
|
+
|
5
10
|
def self.resource_name
|
6
11
|
'clientfolder'
|
7
12
|
end
|
@@ -1,7 +1,20 @@
|
|
1
1
|
module ActsAsIcontact
|
2
2
|
class Contact < Resource
|
3
|
+
|
4
|
+
# Email is required
|
3
5
|
def self.required_on_create
|
4
|
-
['email']
|
6
|
+
super << ['email']
|
5
7
|
end
|
8
|
+
|
9
|
+
# Derived from clientFolder
|
10
|
+
def self.base
|
11
|
+
ActsAsIcontact.client
|
12
|
+
end
|
13
|
+
|
14
|
+
# Defaults to status=total to return contacts on or off lists
|
15
|
+
def self.default_options
|
16
|
+
super.merge(:status=>:total)
|
17
|
+
end
|
18
|
+
|
6
19
|
end
|
7
20
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActsAsIcontact
|
2
|
+
class List < Resource
|
3
|
+
# Derives from clientFolder.
|
4
|
+
def self.base
|
5
|
+
ActsAsIcontact.client
|
6
|
+
end
|
7
|
+
|
8
|
+
# Requires name, emailOwnerOnChange, welcomeOnManualAdd, welcomeOnSignupAdd, and welcomeMessageId.
|
9
|
+
def self.required_on_create
|
10
|
+
super << "name" << "emailOwnerOnChange" << "welcomeOnManualAdd" << "welcomeOnSignupAdd" << "welcomeMessageId"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.boolean_fields
|
14
|
+
super << "emailOwnerOnChange" << "welcomeOnManualAdd" << "welcomeOnSignupAdd"
|
15
|
+
end
|
16
|
+
|
17
|
+
# The welcome message pointed to by the welcomeMessageId.
|
18
|
+
def welcomeMessage
|
19
|
+
return nil unless welcomeMessageId
|
20
|
+
ActsAsIcontact::Message.find(welcomeMessageId)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActsAsIcontact
|
2
|
+
class Message < Resource
|
3
|
+
# Has a default messageType of "normal" if another isn't passed as an option.
|
4
|
+
def initialize(properties={})
|
5
|
+
super({:messageType => "normal"}.merge(properties))
|
6
|
+
end
|
7
|
+
|
8
|
+
# Requires messageType and subject
|
9
|
+
def self.required_on_create
|
10
|
+
super << "messageType" << "subject"
|
11
|
+
end
|
12
|
+
|
13
|
+
# messageType must be one of four values: normal, autoresponder, welcome, or confirmation
|
14
|
+
def validate_on_save(fields)
|
15
|
+
messageType = %w(normal autoresponder welcome confirmation)
|
16
|
+
raise ActsAsIcontact::ValidationError, "messageType must be one of: " + messageType.join(', ') unless messageType.include?(fields["messageType"])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/config_spec.rb
CHANGED
@@ -41,9 +41,9 @@ describe "Configuration" do
|
|
41
41
|
Object.expects(:const_defined?).with(:Rails).returns(true)
|
42
42
|
end
|
43
43
|
|
44
|
-
it "is
|
44
|
+
it "is sandbox if RAILS_ENV is not production" do
|
45
45
|
ENV["RAILS_ENV"] = 'staging'
|
46
|
-
ActsAsIcontact::Config.mode.should == :
|
46
|
+
ActsAsIcontact::Config.mode.should == :sandbox
|
47
47
|
end
|
48
48
|
|
49
49
|
it "is production if RAILS_ENV is production" do
|
@@ -65,9 +65,9 @@ describe "Configuration" do
|
|
65
65
|
Object.expects(:const_defined?).with(:Rack).returns(true)
|
66
66
|
end
|
67
67
|
|
68
|
-
it "is
|
68
|
+
it "is sandbox if RACK_ENV is not production" do
|
69
69
|
ENV["RACK_ENV"] = 'staging'
|
70
|
-
ActsAsIcontact::Config.mode.should == :
|
70
|
+
ActsAsIcontact::Config.mode.should == :sandbox
|
71
71
|
end
|
72
72
|
|
73
73
|
it "is production if RACK_ENV is production" do
|
@@ -81,17 +81,17 @@ describe "Configuration" do
|
|
81
81
|
end
|
82
82
|
|
83
83
|
|
84
|
-
context ":
|
84
|
+
context ":sandbox" do
|
85
85
|
before(:each) do
|
86
|
-
ActsAsIcontact::Config.mode = :
|
86
|
+
ActsAsIcontact::Config.mode = :sandbox
|
87
87
|
end
|
88
88
|
|
89
|
-
it "returns the
|
89
|
+
it "returns the sandbox AppId" do
|
90
90
|
ActsAsIcontact::Config.app_id.should == "Ml5SnuFhnoOsuZeTOuZQnLUHTbzeUyhx"
|
91
91
|
end
|
92
92
|
|
93
|
-
it "returns the
|
94
|
-
ActsAsIcontact::Config.url.should == "https://app.
|
93
|
+
it "returns the sandbox URL" do
|
94
|
+
ActsAsIcontact::Config.url.should == "https://app.sandbox.icontact.com/icp/"
|
95
95
|
end
|
96
96
|
|
97
97
|
end
|
data/spec/resource_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
2
|
|
3
3
|
describe ActsAsIcontact::Resource do
|
4
4
|
it "has a RestClient connection" do
|
5
|
-
ActsAsIcontact::Resource.connection.url.should == ActsAsIcontact.
|
5
|
+
ActsAsIcontact::Resource.connection.url.should == ActsAsIcontact.client['resources'].url
|
6
6
|
end
|
7
7
|
|
8
8
|
it "can return all resources for the given URL" do
|
@@ -51,6 +51,35 @@ describe ActsAsIcontact::Resource do
|
|
51
51
|
r.first.foo.should == "kar"
|
52
52
|
r[1].foo.should == "yar"
|
53
53
|
end
|
54
|
+
|
55
|
+
it "defaults to a limit of 500" do
|
56
|
+
ActsAsIcontact::Resource.base.expects(:[]).with(regexp_matches(/limit=500/)).returns(stub(:get => '{"resources":[]}'))
|
57
|
+
r = ActsAsIcontact::Resource.find(:all)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "throws an exception if a limit higher than 500 is attempted" do
|
61
|
+
lambda{r = ActsAsIcontact::Resource.find(:all, :limit => 501)}.should raise_error(ActsAsIcontact::QueryError, "Limit must be between 1 and 500")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "throws an exception if a limit lower than 500 is attempted" do
|
65
|
+
lambda{r = ActsAsIcontact::Resource.find(:all, :limit => 501)}.should raise_error(ActsAsIcontact::QueryError, "Limit must be between 1 and 500")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "maps the 'first' method to find(:first)" do
|
69
|
+
ActsAsIcontact::Resource.expects(:first).with({:foo=>:bar}).returns(nil)
|
70
|
+
ActsAsIcontact::Resource.find(:first, :foo=>:bar)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "maps the 'all' method to find(:all)" do
|
74
|
+
ActsAsIcontact::Resource.expects(:all).with({:foo=>:bar}).returns(nil)
|
75
|
+
ActsAsIcontact::Resource.find(:all, :foo=>:bar)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "can find a single resource by ID" do
|
79
|
+
a = ActsAsIcontact::Resource.find(1)
|
80
|
+
a.too.should == "sar"
|
81
|
+
end
|
82
|
+
|
54
83
|
end
|
55
84
|
|
56
85
|
it "knows its properties" do
|
@@ -70,7 +99,7 @@ describe ActsAsIcontact::Resource do
|
|
70
99
|
|
71
100
|
it "has its own connection if it's not new" do
|
72
101
|
r = ActsAsIcontact::Resource.first
|
73
|
-
r.connection.url.should == ActsAsIcontact.
|
102
|
+
r.connection.url.should == ActsAsIcontact.client['resources/1'].url
|
74
103
|
end
|
75
104
|
|
76
105
|
it "does not have a connection if it's new" do
|
@@ -79,7 +108,7 @@ describe ActsAsIcontact::Resource do
|
|
79
108
|
end
|
80
109
|
|
81
110
|
it "knows its REST base resource" do
|
82
|
-
ActsAsIcontact::Resource.base.should == ActsAsIcontact.
|
111
|
+
ActsAsIcontact::Resource.base.should == ActsAsIcontact.client
|
83
112
|
end
|
84
113
|
|
85
114
|
it "knows its primary key" do
|
@@ -94,6 +123,16 @@ describe ActsAsIcontact::Resource do
|
|
94
123
|
ActsAsIcontact::Resource.never_on_create.should == ["resourceId"]
|
95
124
|
end
|
96
125
|
|
126
|
+
it "accepts symbols for properties on creation" do
|
127
|
+
a = ActsAsIcontact::Resource.new(:foofoo => "bunny")
|
128
|
+
a.foofoo.should == "bunny"
|
129
|
+
end
|
130
|
+
|
131
|
+
it "typecasts all integer property values if it can" do
|
132
|
+
a = ActsAsIcontact::Resource.new("indianaPi" => "3")
|
133
|
+
a.indianaPi.should == 3
|
134
|
+
end
|
135
|
+
|
97
136
|
context "updating records" do
|
98
137
|
before(:each) do
|
99
138
|
@res = ActsAsIcontact::Resource.first
|
@@ -114,7 +153,12 @@ describe ActsAsIcontact::Resource do
|
|
114
153
|
|
115
154
|
it "knows the minimum set of properties that changed or must be sent" do
|
116
155
|
@res.too = "tar"
|
117
|
-
@res.send(:update_fields).should == {"resourceId" =>
|
156
|
+
@res.send(:update_fields).should == {"resourceId" => 1, "too" => "tar"}
|
157
|
+
end
|
158
|
+
|
159
|
+
it "throws an exception if required fields aren't included" do
|
160
|
+
@res.resourceId = nil
|
161
|
+
lambda{@res.save}.should raise_error(ActsAsIcontact::ValidationError, "Missing required fields: resourceId")
|
118
162
|
end
|
119
163
|
|
120
164
|
context "with successful save" do
|
@@ -219,7 +263,7 @@ describe ActsAsIcontact::Resource do
|
|
219
263
|
|
220
264
|
context "with successful save" do
|
221
265
|
before(:each) do
|
222
|
-
FakeWeb.register_uri(:post, "https://app.
|
266
|
+
FakeWeb.register_uri(:post, "https://app.sandbox.icontact.com/icp/a/111111/c/222222/resources", :body => %q<{"resources":[{"resourceId":"100","foo":"flar","kroo":"krar","too":"sar"}]}>)
|
223
267
|
@res.too = "sar"
|
224
268
|
end
|
225
269
|
|
@@ -249,7 +293,7 @@ describe ActsAsIcontact::Resource do
|
|
249
293
|
|
250
294
|
context "with failed save but status 200" do
|
251
295
|
before(:each) do
|
252
|
-
FakeWeb.register_uri(:post, "https://app.
|
296
|
+
FakeWeb.register_uri(:post, "https://app.sandbox.icontact.com/icp/a/111111/c/222222/resources", :body => %q<{"resources":[],"warnings":["You did not provide a foo. foo is a required field. Please provide a foo","This was not a good record"]}>)
|
253
297
|
@res = ActsAsIcontact::Resource.new
|
254
298
|
@res.foo = nil
|
255
299
|
@result = @res.save
|
@@ -274,7 +318,7 @@ describe ActsAsIcontact::Resource do
|
|
274
318
|
|
275
319
|
context "with failed save on HTTP failure exception" do
|
276
320
|
before(:each) do
|
277
|
-
FakeWeb.register_uri(:post, "https://app.
|
321
|
+
FakeWeb.register_uri(:post, "https://app.sandbox.icontact.com/icp/a/111111/c/222222/resources", :status => ["400","Bad Request"], :body => %q<{"errors":["You did not provide a clue. Clue is a required field. Please provide a clue"]}>)
|
278
322
|
@res = ActsAsIcontact::Resource.new
|
279
323
|
@res.foo = nil
|
280
324
|
@result = @res.save
|
@@ -1,8 +1,25 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
3
|
describe ActsAsIcontact::Contact do
|
4
|
-
it "defaults to a limit of 500"
|
5
|
-
it "defaults to searching on all contacts regardless of list status"
|
6
|
-
it "throws an exception if a limit higher than 500 is attempted"
|
7
4
|
|
5
|
+
it "defaults to searching on all contacts regardless of list status" do
|
6
|
+
ActsAsIcontact::Contact.base.expects(:[]).with(regexp_matches(/status=total/)).returns(stub(:get => '{"contacts":[]}'))
|
7
|
+
r = ActsAsIcontact::Contact.find(:all)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "requires email address" do
|
11
|
+
c = ActsAsIcontact::Contact.new
|
12
|
+
lambda{c.save}.should raise_error(ActsAsIcontact::ValidationError, "Missing required fields: email")
|
13
|
+
end
|
14
|
+
|
15
|
+
context "associations" do
|
16
|
+
# We have _one_ really good contact set up here
|
17
|
+
before(:each) do
|
18
|
+
@john = ActsAsIcontact::Contact.first(:firstName => "John", :lastName => "Test")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "knows which lists it's subscribed to"
|
22
|
+
it "knows its history"
|
23
|
+
end
|
24
|
+
|
8
25
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ActsAsIcontact::List do
|
4
|
+
it "requires name, emailOwnerOnChange, welcomeOnManualAdd, welcomeOnSignupAdd, welcomeMessageId" do
|
5
|
+
l = ActsAsIcontact::List.new
|
6
|
+
lambda{l.save}.should raise_error(ActsAsIcontact::ValidationError, "Missing required fields: name, emailOwnerOnChange, welcomeOnManualAdd, welcomeOnSignupAdd, welcomeMessageId")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "uses true and false to assign boolean fields" do
|
10
|
+
l = ActsAsIcontact::List.new
|
11
|
+
l.emailOwnerOnChange = true
|
12
|
+
l.welcomeOnSignupAdd = false
|
13
|
+
l.instance_variable_get(:@properties)["emailOwnerOnChange"].should == 1
|
14
|
+
l.instance_variable_get(:@properties)["welcomeOnSignupAdd"].should == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
it "uses true and false to retrieve boolean fields" do
|
18
|
+
l = ActsAsIcontact::List.new
|
19
|
+
l.instance_variable_set(:@properties,{"welcomeOnManualAdd" => 1, "emailOwnerOnChange" => 0})
|
20
|
+
l.emailOwnerOnChange.should be_false
|
21
|
+
l.welcomeOnManualAdd.should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
context "associations" do
|
25
|
+
# Create one good list
|
26
|
+
before(:each) do
|
27
|
+
@list = ActsAsIcontact::List.first(:name => "First Test")
|
28
|
+
end
|
29
|
+
it "knows its subscribers"
|
30
|
+
|
31
|
+
it "knows its welcome message" do
|
32
|
+
@list.welcomeMessage.should == ActsAsIcontact::Message.find(555555)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ActsAsIcontact::Message do
|
4
|
+
|
5
|
+
it "defaults messageType to normal" do
|
6
|
+
m = ActsAsIcontact::Message.new
|
7
|
+
m.messageType.should == "normal"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "requires messageType and subject" do
|
11
|
+
m = ActsAsIcontact::Message.new(:messageType => nil)
|
12
|
+
lambda{m.save}.should raise_error(ActsAsIcontact::ValidationError, "Missing required fields: messageType, subject")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "must have an acceptable messageType" do
|
16
|
+
m = ActsAsIcontact::Message.new(:messageType => "dummy", :subject => "test")
|
17
|
+
lambda{m.save}.should raise_error(ActsAsIcontact::ValidationError, "messageType must be one of: normal, autoresponder, welcome, confirmation")
|
18
|
+
end
|
19
|
+
|
20
|
+
context "associations" do
|
21
|
+
before(:each) do
|
22
|
+
@message = ActsAsIcontact::Message.first(:subject => "Test Message")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "knows which campaign it has (if any)"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/spec/spec_fakeweb.rb
CHANGED
@@ -2,22 +2,41 @@ require 'rubygems'
|
|
2
2
|
require 'fakeweb'
|
3
3
|
|
4
4
|
FakeWeb.allow_net_connect = false
|
5
|
+
i = "https://app.sandbox.icontact.com/icp"
|
6
|
+
ic = "#{i}/a/111111/c/222222"
|
5
7
|
|
6
8
|
# Resources (this one's a fake stub for pure testing)
|
7
|
-
FakeWeb.register_uri(:get, "
|
8
|
-
FakeWeb.register_uri(:get, "
|
9
|
-
FakeWeb.register_uri(:get, "
|
10
|
-
FakeWeb.register_uri(:get, "
|
11
|
-
FakeWeb.register_uri(:get, "
|
12
|
-
FakeWeb.register_uri(:
|
13
|
-
FakeWeb.register_uri(:
|
14
|
-
FakeWeb.register_uri(:post, "
|
9
|
+
FakeWeb.register_uri(:get, "#{ic}/resources?limit=500", :body => %q<{"resources":[{"foo":"bar","resourceId":"1","too":"bar"},{"foo":"aar","resourceId":"2"},{"foo":"far","resourceId":"3"},{"foo":"car","resourceId":"4"},{"foo":"dar","resourceId":"5"},{"foo":"ear","resourceId":"6"},{"foo":"gar","resourceId":"7"},{"foo":"har","resourceId":"8"},{"foo":"iar","resourceId":"9"},{"foo":"jar","resourceId":"10"},{"foo":"kar","resourceId":"11"},{"foo":"yar","resourceId":"12"}],"total":12,"limit":20,"offset":0}>)
|
10
|
+
FakeWeb.register_uri(:get, "#{ic}/resources?limit=1", :body => %q<{"resources":[{"foo":"bar","resourceId":"1","too":"bar"},{"foo":"aar","resourceId":"2"},{"foo":"far","resourceId":"3"},{"foo":"car","resourceId":"4"},{"foo":"dar","resourceId":"5"},{"foo":"ear","resourceId":"6"},{"foo":"gar","resourceId":"7"},{"foo":"har","resourceId":"8"},{"foo":"iar","resourceId":"9"},{"foo":"jar","resourceId":"10"},{"foo":"kar","resourceId":"11"},{"foo":"yar","resourceId":"12"}],"total":12,"limit":20,"offset":0}>)
|
11
|
+
FakeWeb.register_uri(:get, "#{ic}/resources?limit=5", :body => %q<{"resources":[{"foo":"bar","resourceId":"1"},{"foo":"aar","resourceId":"2"},{"foo":"far","resourceId":"3"},{"foo":"car","resourceId":"4"},{"foo":"dar","resourceId":"5"}],"total":12,"limit":5,"offset":0}>)
|
12
|
+
FakeWeb.register_uri(:get, "#{ic}/resources?limit=500&offset=5", :body => %q<{"resources":[{"foo":"ear","resourceId":"6"},{"foo":"gar","resourceId":"7"},{"foo":"har","resourceId":"8"},{"foo":"iar","resourceId":"9"},{"foo":"jar","resourceId":"10"},{"foo":"kar","resourceId":"11"},{"foo":"yar","resourceId":"12"}],"total":12,"limit":20,"offset":5}>)
|
13
|
+
FakeWeb.register_uri(:get, "#{ic}/resources?offset=5&limit=5", :body => %q<{"resources":[{"foo":"ear","resourceId":"6"},{"foo":"gar","resourceId":"7"},{"foo":"har","resourceId":"8"},{"foo":"iar","resourceId":"9"},{"foo":"jar","resourceId":"10"}],"total":12,"limit":5,"offset":5}>)
|
14
|
+
FakeWeb.register_uri(:get, "#{ic}/resources?offset=10&limit=5", :body => %q<{"resources":[{"foo":"kar","resourceId":"11"},{"foo":"yar","resourceId":"12"}],"total":12,"limit":5,"offset":10}>)
|
15
|
+
FakeWeb.register_uri(:get, "#{ic}/resources/1", :body => %q<{"resource":{"foo":"bar","resourceId":"1","too":"sar"}}>)
|
16
|
+
FakeWeb.register_uri(:post, "#{ic}/resources/1", :body => %q<{"resource":{"foo":"bar","resourceId":"1","too":"sar"}}>)
|
17
|
+
FakeWeb.register_uri(:post, "#{ic}/resources/2", :body => %q<{"resource":{},"warnings":["You did not provide a foo. foo is a required field. Please provide a foo","This was not a good record"]}>)
|
18
|
+
FakeWeb.register_uri(:post, "#{ic}/resources/3", :status => ["400","Bad Request"], :body => %q<{"errors":["You did not provide a clue. Clue is a required field. Please provide a clue"]}>)
|
15
19
|
|
16
20
|
# Time
|
17
|
-
FakeWeb.register_uri(:get, "
|
21
|
+
FakeWeb.register_uri(:get, "#{i}/time", :body => %q<{"time":"2009-07-13T01:28:18-04:00","timestamp":1247462898}>)
|
18
22
|
|
19
23
|
# Accounts
|
20
|
-
FakeWeb.register_uri(:get, "
|
24
|
+
FakeWeb.register_uri(:get, "#{i}/a?limit=1", :body => %q<{"accounts":[{"billingStreet":"","billingCity":"","billingState":"","billingPostalCode":"","billingCountry":"","city":"Testville","accountId":"111111","companyName":"","country":"United States","email":"bob@example.org","enabled":1,"fax":"","firstName":"Bob","lastName":"Tester","multiClientFolder":"0","multiUser":"0","phone":"","postalCode":"12345","state":"TN","street":"123 Test Street","title":"","accountType":"0","subscriberLimit":"250000"}],"total":1,"limit":20,"offset":0}>)
|
25
|
+
FakeWeb.register_uri(:get, "#{i}/a?limit=500", :body => %q<{"accounts":[{"billingStreet":"","billingCity":"","billingState":"","billingPostalCode":"","billingCountry":"","city":"Testville","accountId":"111111","companyName":"","country":"United States","email":"bob@example.org","enabled":1,"fax":"","firstName":"Bob","lastName":"Tester","multiClientFolder":"0","multiUser":"0","phone":"","postalCode":"12345","state":"TN","street":"123 Test Street","title":"","accountType":"0","subscriberLimit":"250000"}],"total":1,"limit":20,"offset":0}>)
|
21
26
|
|
22
27
|
# Clients
|
23
|
-
FakeWeb.register_uri(:get, "
|
28
|
+
FakeWeb.register_uri(:get, "#{i}/a/111111/c?limit=1", :body => %q<{"clientfolders":[{"clientFolderId":"222222","logoId":null,"emailRecipient":"bob@example.org"}],"total":1}>)
|
29
|
+
FakeWeb.register_uri(:get, "#{i}/a/111111/c?limit=500", :body => %q<{"clientfolders":[{"clientFolderId":"222222","logoId":null,"emailRecipient":"bob@example.org"}],"total":1}>)
|
30
|
+
|
31
|
+
# Contacts
|
32
|
+
FakeWeb.register_uri(:get, "#{ic}/contacts?limit=1&status=total&firstName=John&lastName=Test", :body => %q<{"contacts":[{"email":"john@example.org","firstName":"John","lastName":"Test","status":"normal","contactId":"333333","createDate":"2009-07-24 01:00:00"}]}>)
|
33
|
+
|
34
|
+
# Lists
|
35
|
+
FakeWeb.register_uri(:get, "#{ic}/lists?limit=1&name=First%20Test", :body => %q<{"lists":[{"listId":"444444","name":"First Test","emailOwnerOnChange":"0","welcomeOnManualAdd":"0","welcomeOnSignupAdd":"0","welcomeMessageId":"555555","description":"Just a test list."}]}>)
|
36
|
+
|
37
|
+
# Message
|
38
|
+
#### Test message for List association
|
39
|
+
FakeWeb.register_uri(:get, "#{ic}/messages/555555", :body => %q<{"message":{"messageId":"555555","subject":"Welcome!","messageType":"welcome","textBody":"Welcome to the Test List!","htmlBody":"<p>Welcome to the <b>Test List</b>!</p>","createDate":"20090725 14:55:12"}}>)
|
40
|
+
|
41
|
+
#### Test message for associations originating from Message spec
|
42
|
+
FakeWeb.register_uri(:get, "#{ic}/messages?limit=1&subject=Test%20Message", :body => %q<{"messages":[{"messageId":"666666","subject":"Test Message","messageType":"normal","textBody":"Hi there!\nThis is just a test.","htmlBody":"<p><b>Hi there!</b></p><p>This is just a <i>test.</i></p>","createDate":"20090725 14:53:33"}]}>)
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_icontact
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Eley
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-07-
|
12
|
+
date: 2009-07-25 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -43,6 +43,8 @@ files:
|
|
43
43
|
- lib/acts_as_icontact/resources/account.rb
|
44
44
|
- lib/acts_as_icontact/resources/client.rb
|
45
45
|
- lib/acts_as_icontact/resources/contact.rb
|
46
|
+
- lib/acts_as_icontact/resources/list.rb
|
47
|
+
- lib/acts_as_icontact/resources/message.rb
|
46
48
|
- spec/config_spec.rb
|
47
49
|
- spec/connection_spec.rb
|
48
50
|
- spec/resource_collection_spec.rb
|
@@ -50,6 +52,8 @@ files:
|
|
50
52
|
- spec/resources/account_spec.rb
|
51
53
|
- spec/resources/client_spec.rb
|
52
54
|
- spec/resources/contact_spec.rb
|
55
|
+
- spec/resources/list_spec.rb
|
56
|
+
- spec/resources/message_spec.rb
|
53
57
|
- spec/spec.opts
|
54
58
|
- spec/spec_fakeweb.rb
|
55
59
|
- spec/spec_helper.rb
|
@@ -89,5 +93,7 @@ test_files:
|
|
89
93
|
- spec/resources/account_spec.rb
|
90
94
|
- spec/resources/client_spec.rb
|
91
95
|
- spec/resources/contact_spec.rb
|
96
|
+
- spec/resources/list_spec.rb
|
97
|
+
- spec/resources/message_spec.rb
|
92
98
|
- spec/spec_fakeweb.rb
|
93
99
|
- spec/spec_helper.rb
|