acts_as_icontact 0.1.1 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|