SFEley-acts_as_icontact 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +6 -0
- data/LICENSE +20 -0
- data/README.markdown +196 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/acts_as_icontact.gemspec +74 -0
- data/lib/acts_as_icontact/config.rb +106 -0
- data/lib/acts_as_icontact/connection.rb +32 -0
- data/lib/acts_as_icontact/exceptions.rb +19 -0
- data/lib/acts_as_icontact/resource.rb +204 -0
- data/lib/acts_as_icontact/resource_collection.rb +27 -0
- data/lib/acts_as_icontact/resources/account.rb +38 -0
- data/lib/acts_as_icontact/resources/client.rb +45 -0
- data/lib/acts_as_icontact/resources/contact.rb +7 -0
- data/lib/acts_as_icontact.rb +19 -0
- data/spec/config_spec.rb +181 -0
- data/spec/connection_spec.rb +36 -0
- data/spec/resource_collection_spec.rb +23 -0
- data/spec/resource_spec.rb +301 -0
- data/spec/resources/account_spec.rb +52 -0
- data/spec/resources/client_spec.rb +52 -0
- data/spec/resources/contact_spec.rb +8 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_fakeweb.rb +23 -0
- data/spec/spec_helper.rb +18 -0
- metadata +86 -0
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'activesupport'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module ActsAsIcontact
|
5
|
+
# Base class for shared functionality between iContact resources. Supports getting, finding, saving,
|
6
|
+
# all that fun stuff.
|
7
|
+
class Resource
|
8
|
+
|
9
|
+
# Creates a new resource object from a values hash. (Which is passed to us via the magic of JSON.)
|
10
|
+
def initialize(properties={})
|
11
|
+
@properties = properties
|
12
|
+
@new_record = !@properties.has_key?(self.class.primary_key)
|
13
|
+
# Initialize other useful attributes
|
14
|
+
@errors = []
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the primary key ID for an existing resource. Returns nil if the resource is a new record.
|
19
|
+
def id
|
20
|
+
@properties[self.class.primary_key].to_i unless new_record?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the specific RestClient connection for an existing resource. (E.g., the connection
|
24
|
+
# to "http://api.icontact.com/icp/a/12345" for account 12345.) Returns nil if the resource
|
25
|
+
# is a new record.
|
26
|
+
def connection
|
27
|
+
self.class.connection[id] unless new_record?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Enables keys from the iContact resource to act as attribute methods.
|
31
|
+
def method_missing(method, *params)
|
32
|
+
property = method.to_s
|
33
|
+
if property =~ /(.*)=$/ # It's a value assignment
|
34
|
+
@newvalues ||= []
|
35
|
+
@newvalues << $1
|
36
|
+
@properties[$1] = params[0]
|
37
|
+
else
|
38
|
+
if @properties.has_key?(property)
|
39
|
+
@properties[property]
|
40
|
+
else
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns true if the resource object did not originate from iContact. We determine this
|
47
|
+
# by the rather naive method of checking upon creation whether one of the properties passed
|
48
|
+
# was the primary key.
|
49
|
+
def new_record?
|
50
|
+
@new_record
|
51
|
+
end
|
52
|
+
|
53
|
+
# Sends changes to iContact. Returns true if the save was successful (i.e. we receive
|
54
|
+
# an updated object back from them); if it was not, returns false and populates the
|
55
|
+
# +errors+ array with the warnings iContact sends to us. If iContact returns an HTTP
|
56
|
+
# error, raises an exception with it.
|
57
|
+
def save
|
58
|
+
if new_record?
|
59
|
+
result_type = self.class.collection_name
|
60
|
+
payload = {self.class.collection_name => [create_fields]}
|
61
|
+
response = self.class.connection.post(payload.to_json)
|
62
|
+
else
|
63
|
+
result_type = self.class.resource_name
|
64
|
+
response = connection.post(update_fields.to_json)
|
65
|
+
end
|
66
|
+
parsed = JSON.parse(response)
|
67
|
+
if parsed[result_type].empty?
|
68
|
+
@errors = parsed["warnings"]
|
69
|
+
false
|
70
|
+
else
|
71
|
+
@properties = (new_record? ? parsed[result_type].first : parsed[result_type])
|
72
|
+
@new_record = false
|
73
|
+
@errors = []
|
74
|
+
true
|
75
|
+
end
|
76
|
+
rescue RestClient::RequestFailed => e
|
77
|
+
response = e.response.body
|
78
|
+
parsed = JSON.parse(response)
|
79
|
+
@errors = parsed["errors"] || [e.message]
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
# Like +save+, but raises an ActsAsIcontact::RecordNotSaved exception if the save
|
84
|
+
# failed. The exception message contains the first error from iContact.
|
85
|
+
def save!
|
86
|
+
save or raise ActsAsIcontact::RecordNotSaved.new(errors)
|
87
|
+
end
|
88
|
+
|
89
|
+
# The first message from the +errors+ array.
|
90
|
+
def error
|
91
|
+
errors.first
|
92
|
+
end
|
93
|
+
|
94
|
+
# The warning messages sent back by iContact on a failed request.
|
95
|
+
def errors
|
96
|
+
@errors
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns an array of resources starting at the base.
|
100
|
+
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
|
+
case type
|
105
|
+
when :first then
|
106
|
+
self.new(parsed[collection_name].first) if parsed[collection_name]
|
107
|
+
when :all then
|
108
|
+
ResourceCollection.new(self, parsed)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns an array of resources starting at the base.
|
113
|
+
def self.all
|
114
|
+
find(:all)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the first account associated with this username.
|
118
|
+
def self.first
|
119
|
+
find(:first)
|
120
|
+
end
|
121
|
+
|
122
|
+
protected
|
123
|
+
# The minimum set of fields that must be sent back to iContact on an update.
|
124
|
+
# Includes any fields that changed or were added, the primary key, and anything
|
125
|
+
# else from the "required_on_update" set from the class definition. It excludes
|
126
|
+
# anything from the "never_on_update" set.
|
127
|
+
def update_fields
|
128
|
+
fieldlist = self.class.required_on_update + @newvalues.to_a - self.class.never_on_update
|
129
|
+
@properties.select{|key, value| fieldlist.include?(key)}
|
130
|
+
end
|
131
|
+
|
132
|
+
# The minimum set of fields that must be sent back to iContact on a create.
|
133
|
+
# Includes any fields that were added and anything
|
134
|
+
# else from the "required_on_create" set from the class definition. It excludes
|
135
|
+
# anything from the "never_on_create" set.
|
136
|
+
def create_fields
|
137
|
+
self.class.required_on_create.each{|key| @properties[key] ||= ""} # Add required fields
|
138
|
+
self.class.never_on_create.each{|key| @properties.delete(key)} # Remove prohibited fields
|
139
|
+
@properties
|
140
|
+
end
|
141
|
+
|
142
|
+
# The base RestClient resource that this particular class nests from. Starts with
|
143
|
+
# the resource connection at 'https://api.icontact.com/icp/' and works its way up.
|
144
|
+
def self.base
|
145
|
+
ActsAsIcontact.connection
|
146
|
+
end
|
147
|
+
|
148
|
+
# The name of the singular resource type pulled from iContact. Defaults to the lowercase
|
149
|
+
# version of the class name.
|
150
|
+
def self.resource_name
|
151
|
+
name.demodulize.downcase
|
152
|
+
end
|
153
|
+
|
154
|
+
# The name of the resource collection pulled from iContact. Defaults to the lowercase
|
155
|
+
# pluralized version of the class name.
|
156
|
+
def self.collection_name
|
157
|
+
resource_name.pluralize
|
158
|
+
end
|
159
|
+
|
160
|
+
# The URI component name corresponding to this resource type. In many cases it's the same as the
|
161
|
+
# collection name; exceptions include accounts ('a') and clientFolders ('c').
|
162
|
+
def self.uri_component
|
163
|
+
collection_name
|
164
|
+
end
|
165
|
+
|
166
|
+
# The RestClient resource object for this resource class. Its own find/update methods
|
167
|
+
# will call on this, and singular objects will derive from it.
|
168
|
+
def self.connection
|
169
|
+
base[uri_component]
|
170
|
+
end
|
171
|
+
|
172
|
+
# The primary key field for this resource. Used on updates.
|
173
|
+
def self.primary_key
|
174
|
+
resource_name + "Id"
|
175
|
+
end
|
176
|
+
|
177
|
+
# Fields that _must_ be included for this resource upon creation.
|
178
|
+
def self.required_on_create
|
179
|
+
[]
|
180
|
+
end
|
181
|
+
|
182
|
+
# Fields that _must_ be included for this resource upon updating.
|
183
|
+
def self.required_on_update
|
184
|
+
[primary_key]
|
185
|
+
end
|
186
|
+
|
187
|
+
# Fields that _cannot_ be included for this resource upon creation.
|
188
|
+
def self.never_on_create
|
189
|
+
[primary_key]
|
190
|
+
end
|
191
|
+
|
192
|
+
# Fields that _cannot_ be included for this resource upon updating.
|
193
|
+
def self.never_on_update
|
194
|
+
[]
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
def self.build_query(options={})
|
199
|
+
return "" if options.empty?
|
200
|
+
terms = options.collect{|k,v| "#{k}=#{URI.escape(v.to_s)}"}
|
201
|
+
build = "?" + terms.join('&')
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActsAsIcontact
|
2
|
+
class ResourceCollection < Enumerator
|
3
|
+
attr_reader :total
|
4
|
+
|
5
|
+
def initialize(klass, collection)
|
6
|
+
@klass = klass
|
7
|
+
@collection = collection[klass.collection_name]
|
8
|
+
# Get number of elements
|
9
|
+
@total = @collection.size
|
10
|
+
|
11
|
+
enumcode = Proc.new do |yielder|
|
12
|
+
counter = 0
|
13
|
+
while counter < @total
|
14
|
+
yielder.yield klass.new(@collection[counter])
|
15
|
+
counter += 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
super(&enumcode)
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](index)
|
23
|
+
@klass.new(@collection[index]) if @collection[index]
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ActsAsIcontact
|
2
|
+
# The top-level Accounts resource from iContact. Currently only supports retrieval -- and is
|
3
|
+
# highly targeted toward the _first_ account, since that seems to be the dominant use case.
|
4
|
+
class Account < Resource
|
5
|
+
def self.uri_component
|
6
|
+
'a'
|
7
|
+
end
|
8
|
+
|
9
|
+
# Accounts can't pass back a userName or password on updating
|
10
|
+
def self.never_on_update
|
11
|
+
['userName','password']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# The accountId retrieved from iContact. Can also be set manually for performance optimization,
|
16
|
+
# but remembers it so that it won't be pulled more than once anyway.
|
17
|
+
def self.account_id
|
18
|
+
@account_id ||= Account.first.accountId.to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
# Manually sets the accountId used in subsequent calls. Setting this in your initializer will save
|
22
|
+
# at least one unnecessary request to the iContact server.
|
23
|
+
def self.account_id=(val)
|
24
|
+
@account_id = val
|
25
|
+
end
|
26
|
+
|
27
|
+
# RestClient subresource scoped to the specific account ID. Most other iContact calls will derive
|
28
|
+
# from this one.
|
29
|
+
def self.account
|
30
|
+
@account ||= connection["a/#{account_id}"]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Clears the account resource from memory. Called by reset_connection! since the only likely reason
|
34
|
+
# to do this is connecting as a different user.
|
35
|
+
def self.reset_account!
|
36
|
+
@account = nil
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ActsAsIcontact
|
2
|
+
# The nested Client Folder resource from iContact. Currently only supports retrieval -- and is
|
3
|
+
# highly targeted toward the _first_ client folder, since that seems to be the dominant use case.
|
4
|
+
class Client < Resource
|
5
|
+
def self.resource_name
|
6
|
+
'clientfolder'
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.collection_name
|
10
|
+
'clientfolders'
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.uri_component
|
14
|
+
'c'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.base
|
18
|
+
ActsAsIcontact.account
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# The clientFolderId retrieved from iContact. Can also be set manually for performance
|
23
|
+
# optimization, but remembers it so that it won't be pulled more than once anyway.
|
24
|
+
def self.client_id
|
25
|
+
@client_id ||= Client.first.clientFolderId.to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
# Manually sets the clientFolderId used in subsequent calls. Setting this in your
|
29
|
+
# initializer will save at least one unnecessary request to the iContact server.
|
30
|
+
def self.client_id=(val)
|
31
|
+
@client_id = val
|
32
|
+
end
|
33
|
+
|
34
|
+
# RestClient subresource scoped to the specific account ID. Most other iContact calls will derive
|
35
|
+
# from this one.
|
36
|
+
def self.client
|
37
|
+
@client ||= account["c/#{client_id}"]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Clears the account resource from memory. Called by reset_connection! since the only likely reason
|
41
|
+
# to do this is connecting as a different user.
|
42
|
+
def self.reset_client!
|
43
|
+
@client = nil
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
require 'acts_as_icontact/exceptions'
|
7
|
+
require 'acts_as_icontact/config'
|
8
|
+
require 'acts_as_icontact/connection'
|
9
|
+
require 'acts_as_icontact/resource'
|
10
|
+
require 'acts_as_icontact/resource_collection'
|
11
|
+
|
12
|
+
# Load all of our resource files
|
13
|
+
Dir[File.join(File.dirname(__FILE__), 'acts_as_icontact', 'resources', '*.rb')].sort.each do |path|
|
14
|
+
filename = File.basename(path, '.rb')
|
15
|
+
require "acts_as_icontact/resources/#{filename}"
|
16
|
+
end
|
17
|
+
|
18
|
+
module ActsAsIcontact
|
19
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Configuration" do
|
4
|
+
Rails = "dummy"
|
5
|
+
Rack = "dummy"
|
6
|
+
before(:all) do
|
7
|
+
# Copy our configuration to a safe place, then wipe it
|
8
|
+
@old_config = {}
|
9
|
+
ActsAsIcontact::Config.instance_variables.each do |v|
|
10
|
+
@old_config[v] = ActsAsIcontact::Config.instance_variable_get(v)
|
11
|
+
ActsAsIcontact::Config.instance_variable_set(v,nil)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "mode" do
|
16
|
+
before(:all) do
|
17
|
+
@old_mode = ENV["ICONTACT_MODE"]
|
18
|
+
ENV["ICONTACT_MODE"] = nil
|
19
|
+
end
|
20
|
+
it "defaults to production if nothing else is set" do
|
21
|
+
ActsAsIcontact::Config.mode.should == :production
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can set the mode attribute manually" do
|
25
|
+
ActsAsIcontact::Config.mode = :foo
|
26
|
+
ActsAsIcontact::Config.mode.should == :foo
|
27
|
+
end
|
28
|
+
|
29
|
+
it "reads the ICONTACT_MODE environment variable" do
|
30
|
+
ENV["ICONTACT_MODE"] = 'bar'
|
31
|
+
ActsAsIcontact::Config.mode.should == :bar
|
32
|
+
ENV["ICONTACT_MODE"] = @old_mode
|
33
|
+
end
|
34
|
+
|
35
|
+
context "within a Rails application" do
|
36
|
+
before(:all) do
|
37
|
+
@old_rails_env = ENV["RAILS_ENV"]
|
38
|
+
end
|
39
|
+
|
40
|
+
before(:each) do
|
41
|
+
Object.expects(:const_defined?).with(:Rails).returns(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "is beta if RAILS_ENV is not production" do
|
45
|
+
ENV["RAILS_ENV"] = 'staging'
|
46
|
+
ActsAsIcontact::Config.mode.should == :beta
|
47
|
+
end
|
48
|
+
|
49
|
+
it "is production if RAILS_ENV is production" do
|
50
|
+
ENV["RAILS_ENV"] = 'production'
|
51
|
+
ActsAsIcontact::Config.mode.should == :production
|
52
|
+
end
|
53
|
+
|
54
|
+
after(:all) do
|
55
|
+
ENV["RAILS_ENV"] = @old_rails_env
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "within a Rack environment" do
|
60
|
+
before(:all) do
|
61
|
+
@old_rack_env = ENV["RACK_ENV"]
|
62
|
+
end
|
63
|
+
before(:each) do
|
64
|
+
Object.expects(:const_defined?).with(:Rails).returns(false)
|
65
|
+
Object.expects(:const_defined?).with(:Rack).returns(true)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "is beta if RACK_ENV is not production" do
|
69
|
+
ENV["RACK_ENV"] = 'staging'
|
70
|
+
ActsAsIcontact::Config.mode.should == :beta
|
71
|
+
end
|
72
|
+
|
73
|
+
it "is production if RACK_ENV is production" do
|
74
|
+
ENV["RACK_ENV"] = 'production'
|
75
|
+
ActsAsIcontact::Config.mode.should == :production
|
76
|
+
end
|
77
|
+
|
78
|
+
after(:all) do
|
79
|
+
ENV["RACK_ENV"] = @old_rack_env
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
context ":beta" do
|
85
|
+
before(:each) do
|
86
|
+
ActsAsIcontact::Config.mode = :beta
|
87
|
+
end
|
88
|
+
|
89
|
+
it "returns the beta AppId" do
|
90
|
+
ActsAsIcontact::Config.app_id.should == "Ml5SnuFhnoOsuZeTOuZQnLUHTbzeUyhx"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "returns the beta URL" do
|
94
|
+
ActsAsIcontact::Config.url.should == "https://app.beta.icontact.com/icp/"
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
context ":production" do
|
100
|
+
before(:each) do
|
101
|
+
ActsAsIcontact::Config.mode = :production
|
102
|
+
end
|
103
|
+
|
104
|
+
it "returns the production AppId" do
|
105
|
+
ActsAsIcontact::Config.app_id.should == "IYDOhgaZGUKNjih3hl1ItLln7zpAtWN2"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "returns the production URL" do
|
109
|
+
ActsAsIcontact::Config.url.should == "https://app.icontact.com/icp/"
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
after(:each) do
|
115
|
+
ActsAsIcontact::Config.mode = nil
|
116
|
+
ENV["ICONTACT_MODE"] = nil
|
117
|
+
end
|
118
|
+
|
119
|
+
after(:all) do
|
120
|
+
ENV["ICONTACT_MODE"] = @old_mode
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
it "knows it's version 2.0" do
|
126
|
+
ActsAsIcontact::Config.api_version.should == 2.0
|
127
|
+
end
|
128
|
+
|
129
|
+
it "can set its URL base" do
|
130
|
+
ActsAsIcontact::Config.url = "https://blah.example.com/foo/bar/"
|
131
|
+
ActsAsIcontact::Config.url.should == "https://blah.example.com/foo/bar/"
|
132
|
+
end
|
133
|
+
|
134
|
+
it "knows its content type" do
|
135
|
+
ActsAsIcontact::Config.content_type.should == "application/json"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "can set its content type to XML" do
|
139
|
+
ActsAsIcontact::Config.content_type = "text/xml"
|
140
|
+
ActsAsIcontact::Config.content_type.should == "text/xml"
|
141
|
+
end
|
142
|
+
|
143
|
+
it "throws an error if the content type is anything other than JSON or XML" do
|
144
|
+
lambda{ActsAsIcontact::Config.content_type = "text/plain"}.should raise_error(ActsAsIcontact::ConfigError, "Content Type must be application/json or text/xml")
|
145
|
+
end
|
146
|
+
|
147
|
+
it "knows the username you give it" do
|
148
|
+
ActsAsIcontact::Config.username = "johndoe"
|
149
|
+
ActsAsIcontact::Config.username.should == "johndoe"
|
150
|
+
end
|
151
|
+
|
152
|
+
it "gets the username from an environment variable if not supplied" do
|
153
|
+
old_env = ENV['ICONTACT_USERNAME']
|
154
|
+
ENV['ICONTACT_USERNAME'] = "bobdoe"
|
155
|
+
ActsAsIcontact::Config.username.should == "bobdoe"
|
156
|
+
# Set our environment back to the way we like it
|
157
|
+
ENV['ICONTACT_USERNAME'] = old_env if old_env
|
158
|
+
end
|
159
|
+
|
160
|
+
it "knows the password you give it" do
|
161
|
+
ActsAsIcontact::Config.password = "foobar"
|
162
|
+
ActsAsIcontact::Config.password.should == "foobar"
|
163
|
+
end
|
164
|
+
|
165
|
+
it "gets the username from an environment variable if not supplied" do
|
166
|
+
old_env = ENV['ICONTACT_PASSWORD']
|
167
|
+
ENV['ICONTACT_PASSWORD'] = "hoohar"
|
168
|
+
ActsAsIcontact::Config.password.should == "hoohar"
|
169
|
+
ENV['ICONTACT_PASSWORD'] = old_env if old_env
|
170
|
+
end
|
171
|
+
|
172
|
+
after(:each) do
|
173
|
+
# Clear any variables we might have set
|
174
|
+
ActsAsIcontact::Config.instance_variables.each{|v| ActsAsIcontact::Config.instance_variable_set(v,nil)}
|
175
|
+
end
|
176
|
+
|
177
|
+
after(:all) do
|
178
|
+
# Restore our saved configuration
|
179
|
+
@old_config.each_pair {|k, v| ActsAsIcontact::Config.instance_variable_set(k,v)}
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe ActsAsIcontact, "connection method" do
|
4
|
+
it "returns a RestClient resource" do
|
5
|
+
ActsAsIcontact.connection.should be_a_kind_of(RestClient::Resource)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "throws an error if no username is given" do
|
9
|
+
ActsAsIcontact::Config.expects(:username).returns(nil)
|
10
|
+
lambda{ActsAsIcontact.connection}.should raise_error(ActsAsIcontact::ConfigError, "Username is required")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "throws an error if no password is given" do
|
14
|
+
ActsAsIcontact::Config.expects(:password).returns(nil)
|
15
|
+
lambda{ActsAsIcontact.connection}.should raise_error(ActsAsIcontact::ConfigError, "Password is required")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can be cleared with the reset_client! method" do
|
19
|
+
RestClient::Resource.expects(:new).returns(true)
|
20
|
+
ActsAsIcontact.reset_connection!
|
21
|
+
ActsAsIcontact.connection.should_not be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "resets the account when reset_client! is called" do
|
25
|
+
ActsAsIcontact.expects(:reset_account!).at_least_once.returns(nil)
|
26
|
+
ActsAsIcontact.reset_connection!
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can be used to make calls to the iContact server" do
|
30
|
+
ActsAsIcontact.connection['time'].get.should =~ /"timestamp":\d+/
|
31
|
+
end
|
32
|
+
|
33
|
+
after(:each) do
|
34
|
+
ActsAsIcontact.reset_connection!
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe ActsAsIcontact::ResourceCollection do
|
4
|
+
before(:each) do
|
5
|
+
@dummy = {"total"=>2, "resources"=>[{"foo"=>"bar"}, {"yoo"=>"yar"}], "limit"=>20, "offset"=>0}
|
6
|
+
@this = ActsAsIcontact::ResourceCollection.new(ActsAsIcontact::Resource, @dummy)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "takes a resource class and a parsed JSON collection" do
|
10
|
+
@this.should be_a_kind_of(ActsAsIcontact::ResourceCollection)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns an object of the resource class for each element" do
|
14
|
+
@this.each do |element|
|
15
|
+
element.should be_a_kind_of(ActsAsIcontact::Resource)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can return an element at a specified index" do
|
20
|
+
@this[1].yoo.should == "yar"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|