SFEley-acts_as_icontact 0.1.4 → 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 +5 -2
- data/lib/acts_as_icontact/config.rb +12 -10
- data/lib/acts_as_icontact/resource.rb +81 -23
- 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 +1 -0
- data/lib/acts_as_icontact/resources/list.rb +7 -0
- data/lib/acts_as_icontact/resources/message.rb +19 -0
- data/spec/config_spec.rb +9 -9
- data/spec/resource_spec.rb +26 -12
- data/spec/resources/list_spec.rb +4 -1
- data/spec/resources/message_spec.rb +28 -0
- data/spec/spec_fakeweb.rb +21 -11
- data/spec/spec_helper.rb +1 -1
- metadata +5 -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
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
|
|
34
34
|
"lib/acts_as_icontact/resources/client.rb",
|
35
35
|
"lib/acts_as_icontact/resources/contact.rb",
|
36
36
|
"lib/acts_as_icontact/resources/list.rb",
|
37
|
+
"lib/acts_as_icontact/resources/message.rb",
|
37
38
|
"spec/config_spec.rb",
|
38
39
|
"spec/connection_spec.rb",
|
39
40
|
"spec/resource_collection_spec.rb",
|
@@ -42,6 +43,7 @@ Gem::Specification.new do |s|
|
|
42
43
|
"spec/resources/client_spec.rb",
|
43
44
|
"spec/resources/contact_spec.rb",
|
44
45
|
"spec/resources/list_spec.rb",
|
46
|
+
"spec/resources/message_spec.rb",
|
45
47
|
"spec/spec.opts",
|
46
48
|
"spec/spec_fakeweb.rb",
|
47
49
|
"spec/spec_helper.rb"
|
@@ -61,6 +63,7 @@ Gem::Specification.new do |s|
|
|
61
63
|
"spec/resources/client_spec.rb",
|
62
64
|
"spec/resources/contact_spec.rb",
|
63
65
|
"spec/resources/list_spec.rb",
|
66
|
+
"spec/resources/message_spec.rb",
|
64
67
|
"spec/spec_fakeweb.rb",
|
65
68
|
"spec/spec_helper.rb"
|
66
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
|
|
@@ -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,12 +32,8 @@ module ActsAsIcontact
|
|
33
32
|
if property =~ /(.*)=$/ # It's a value assignment
|
34
33
|
@newvalues ||= []
|
35
34
|
@newvalues << $1
|
36
|
-
|
37
|
-
|
38
|
-
else
|
39
|
-
@properties[$1] = params[0]
|
40
|
-
end
|
41
|
-
else
|
35
|
+
@properties[$1] = clean_value(params[0])
|
36
|
+
else
|
42
37
|
if @properties.has_key?(property)
|
43
38
|
if self.class.boolean_fields.include?(property)
|
44
39
|
(@properties[property] == 1)
|
@@ -109,28 +104,46 @@ module ActsAsIcontact
|
|
109
104
|
|
110
105
|
# Returns an array of resources starting at the base.
|
111
106
|
def self.find(type, options={})
|
112
|
-
query_options = default_options.merge(options)
|
113
|
-
query_options.merge(:limit => 1) if type == :first # Minor optimization
|
114
|
-
validate_options(query_options)
|
115
|
-
uri_extension = uri_component + build_query(query_options)
|
116
|
-
response = base[uri_extension].get
|
117
|
-
parsed = JSON.parse(response)
|
118
107
|
case type
|
119
|
-
when :first
|
120
|
-
|
121
|
-
when :all
|
122
|
-
|
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}'"
|
123
116
|
end
|
124
117
|
end
|
125
118
|
|
126
119
|
# Returns an array of resources starting at the base.
|
127
120
|
def self.all(options={})
|
128
|
-
|
121
|
+
query_options = default_options.merge(options)
|
122
|
+
validate_options(query_options)
|
123
|
+
result = query_collection(query_options)
|
124
|
+
ResourceCollection.new(self, result)
|
129
125
|
end
|
130
126
|
|
131
127
|
# Returns the first account associated with this username.
|
132
128
|
def self.first(options={})
|
133
|
-
|
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
|
134
147
|
end
|
135
148
|
|
136
149
|
protected
|
@@ -153,10 +166,10 @@ module ActsAsIcontact
|
|
153
166
|
@properties
|
154
167
|
end
|
155
168
|
|
156
|
-
# The base RestClient resource that this particular class nests from.
|
157
|
-
# 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.
|
158
171
|
def self.base
|
159
|
-
ActsAsIcontact.
|
172
|
+
ActsAsIcontact.client
|
160
173
|
end
|
161
174
|
|
162
175
|
# The name of the singular resource type pulled from iContact. Defaults to the lowercase
|
@@ -227,11 +240,49 @@ module ActsAsIcontact
|
|
227
240
|
# Validation rules that ensure proper data is passed to iContact on resource creation.
|
228
241
|
def validate_on_create(fields)
|
229
242
|
check_required_fields(fields, self.class.required_on_create)
|
243
|
+
validate_on_save(fields)
|
230
244
|
end
|
231
245
|
|
232
246
|
# Validation rules that ensure proper data is passed to iContact on resource update.
|
233
247
|
def validate_on_update(fields)
|
234
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
|
235
286
|
end
|
236
287
|
|
237
288
|
private
|
@@ -241,6 +292,13 @@ module ActsAsIcontact
|
|
241
292
|
build = "?" + terms.join('&')
|
242
293
|
end
|
243
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
|
+
|
244
302
|
def check_required_fields(fields, required)
|
245
303
|
# Check that all required fields are filled in
|
246
304
|
missing = required.select{|f| fields[f].blank?}
|
@@ -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
|
@@ -13,5 +13,12 @@ module ActsAsIcontact
|
|
13
13
|
def self.boolean_fields
|
14
14
|
super << "emailOwnerOnChange" << "welcomeOnManualAdd" << "welcomeOnSignupAdd"
|
15
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
|
+
|
16
23
|
end
|
17
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
|
@@ -66,15 +66,20 @@ describe ActsAsIcontact::Resource do
|
|
66
66
|
end
|
67
67
|
|
68
68
|
it "maps the 'first' method to find(:first)" do
|
69
|
-
ActsAsIcontact::Resource.expects(:
|
70
|
-
ActsAsIcontact::Resource.
|
69
|
+
ActsAsIcontact::Resource.expects(:first).with({:foo=>:bar}).returns(nil)
|
70
|
+
ActsAsIcontact::Resource.find(:first, :foo=>:bar)
|
71
71
|
end
|
72
72
|
|
73
73
|
it "maps the 'all' method to find(:all)" do
|
74
|
-
ActsAsIcontact::Resource.expects(:
|
75
|
-
ActsAsIcontact::Resource.
|
74
|
+
ActsAsIcontact::Resource.expects(:all).with({:foo=>:bar}).returns(nil)
|
75
|
+
ActsAsIcontact::Resource.find(:all, :foo=>:bar)
|
76
76
|
end
|
77
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
|
+
|
78
83
|
end
|
79
84
|
|
80
85
|
it "knows its properties" do
|
@@ -94,7 +99,7 @@ describe ActsAsIcontact::Resource do
|
|
94
99
|
|
95
100
|
it "has its own connection if it's not new" do
|
96
101
|
r = ActsAsIcontact::Resource.first
|
97
|
-
r.connection.url.should == ActsAsIcontact.
|
102
|
+
r.connection.url.should == ActsAsIcontact.client['resources/1'].url
|
98
103
|
end
|
99
104
|
|
100
105
|
it "does not have a connection if it's new" do
|
@@ -103,7 +108,7 @@ describe ActsAsIcontact::Resource do
|
|
103
108
|
end
|
104
109
|
|
105
110
|
it "knows its REST base resource" do
|
106
|
-
ActsAsIcontact::Resource.base.should == ActsAsIcontact.
|
111
|
+
ActsAsIcontact::Resource.base.should == ActsAsIcontact.client
|
107
112
|
end
|
108
113
|
|
109
114
|
it "knows its primary key" do
|
@@ -118,7 +123,16 @@ describe ActsAsIcontact::Resource do
|
|
118
123
|
ActsAsIcontact::Resource.never_on_create.should == ["resourceId"]
|
119
124
|
end
|
120
125
|
|
121
|
-
|
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
|
+
|
122
136
|
context "updating records" do
|
123
137
|
before(:each) do
|
124
138
|
@res = ActsAsIcontact::Resource.first
|
@@ -139,7 +153,7 @@ describe ActsAsIcontact::Resource do
|
|
139
153
|
|
140
154
|
it "knows the minimum set of properties that changed or must be sent" do
|
141
155
|
@res.too = "tar"
|
142
|
-
@res.send(:update_fields).should == {"resourceId" =>
|
156
|
+
@res.send(:update_fields).should == {"resourceId" => 1, "too" => "tar"}
|
143
157
|
end
|
144
158
|
|
145
159
|
it "throws an exception if required fields aren't included" do
|
@@ -249,7 +263,7 @@ describe ActsAsIcontact::Resource do
|
|
249
263
|
|
250
264
|
context "with successful save" do
|
251
265
|
before(:each) do
|
252
|
-
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"}]}>)
|
253
267
|
@res.too = "sar"
|
254
268
|
end
|
255
269
|
|
@@ -279,7 +293,7 @@ describe ActsAsIcontact::Resource do
|
|
279
293
|
|
280
294
|
context "with failed save but status 200" do
|
281
295
|
before(:each) do
|
282
|
-
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"]}>)
|
283
297
|
@res = ActsAsIcontact::Resource.new
|
284
298
|
@res.foo = nil
|
285
299
|
@result = @res.save
|
@@ -304,7 +318,7 @@ describe ActsAsIcontact::Resource do
|
|
304
318
|
|
305
319
|
context "with failed save on HTTP failure exception" do
|
306
320
|
before(:each) do
|
307
|
-
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"]}>)
|
308
322
|
@res = ActsAsIcontact::Resource.new
|
309
323
|
@res.foo = nil
|
310
324
|
@result = @res.save
|
data/spec/resources/list_spec.rb
CHANGED
@@ -27,6 +27,9 @@ describe ActsAsIcontact::List do
|
|
27
27
|
@list = ActsAsIcontact::List.first(:name => "First Test")
|
28
28
|
end
|
29
29
|
it "knows its subscribers"
|
30
|
-
|
30
|
+
|
31
|
+
it "knows its welcome message" do
|
32
|
+
@list.welcomeMessage.should == ActsAsIcontact::Message.find(555555)
|
33
|
+
end
|
31
34
|
end
|
32
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,31 +2,41 @@ require 'rubygems'
|
|
2
2
|
require 'fakeweb'
|
3
3
|
|
4
4
|
FakeWeb.allow_net_connect = false
|
5
|
-
i = "https://app.
|
5
|
+
i = "https://app.sandbox.icontact.com/icp"
|
6
6
|
ic = "#{i}/a/111111/c/222222"
|
7
7
|
|
8
8
|
# Resources (this one's a fake stub for pure testing)
|
9
|
-
FakeWeb.register_uri(:get, "#{
|
10
|
-
FakeWeb.register_uri(:get, "#{
|
11
|
-
FakeWeb.register_uri(:get, "#{
|
12
|
-
FakeWeb.register_uri(:get, "#{
|
13
|
-
FakeWeb.register_uri(:get, "#{
|
14
|
-
FakeWeb.register_uri(:
|
15
|
-
FakeWeb.register_uri(:
|
16
|
-
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"]}>)
|
17
19
|
|
18
20
|
# Time
|
19
21
|
FakeWeb.register_uri(:get, "#{i}/time", :body => %q<{"time":"2009-07-13T01:28:18-04:00","timestamp":1247462898}>)
|
20
22
|
|
21
23
|
# Accounts
|
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}>)
|
22
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}>)
|
23
26
|
|
24
27
|
# Clients
|
28
|
+
FakeWeb.register_uri(:get, "#{i}/a/111111/c?limit=1", :body => %q<{"clientfolders":[{"clientFolderId":"222222","logoId":null,"emailRecipient":"bob@example.org"}],"total":1}>)
|
25
29
|
FakeWeb.register_uri(:get, "#{i}/a/111111/c?limit=500", :body => %q<{"clientfolders":[{"clientFolderId":"222222","logoId":null,"emailRecipient":"bob@example.org"}],"total":1}>)
|
26
30
|
|
27
31
|
# Contacts
|
28
|
-
FakeWeb.register_uri(:get, "#{ic}/contacts?limit=
|
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"}]}>)
|
29
33
|
|
30
34
|
# Lists
|
31
|
-
FakeWeb.register_uri(:get, "#{ic}/lists?limit=
|
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."}]}>)
|
32
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: SFEley-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 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -39,6 +39,7 @@ files:
|
|
39
39
|
- lib/acts_as_icontact/resources/client.rb
|
40
40
|
- lib/acts_as_icontact/resources/contact.rb
|
41
41
|
- lib/acts_as_icontact/resources/list.rb
|
42
|
+
- lib/acts_as_icontact/resources/message.rb
|
42
43
|
- spec/config_spec.rb
|
43
44
|
- spec/connection_spec.rb
|
44
45
|
- spec/resource_collection_spec.rb
|
@@ -47,6 +48,7 @@ files:
|
|
47
48
|
- spec/resources/client_spec.rb
|
48
49
|
- spec/resources/contact_spec.rb
|
49
50
|
- spec/resources/list_spec.rb
|
51
|
+
- spec/resources/message_spec.rb
|
50
52
|
- spec/spec.opts
|
51
53
|
- spec/spec_fakeweb.rb
|
52
54
|
- spec/spec_helper.rb
|
@@ -85,5 +87,6 @@ test_files:
|
|
85
87
|
- spec/resources/client_spec.rb
|
86
88
|
- spec/resources/contact_spec.rb
|
87
89
|
- spec/resources/list_spec.rb
|
90
|
+
- spec/resources/message_spec.rb
|
88
91
|
- spec/spec_fakeweb.rb
|
89
92
|
- spec/spec_helper.rb
|