SFEley-acts_as_icontact 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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://beta.icontact.com> and sign up for an iContact Beta account. This will let you test your app without risk of blowing away your production mailing lists.
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 beta and production environments:
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
- * **BETA:** Go to <http://app.beta.icontact.com/icp/core/externallogin> and enter `Ml5SnuFhnoOsuZeTOuZQnLUHTbzeUyhx` for the Application Id. Choose a password for ActsAsIcontact that's different from your account password.
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 _(beta, 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:
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 = :beta
40
- ActsAsIcontact::Config.username = my_beta_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 beta environment.
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 beta _and_ production credentials:
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 :beta
128
- Config.username = my_beta_username
129
- Config.password = my_beta_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 `:beta` 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.)
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.4
1
+ 0.1.5
@@ -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.4"
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-24}
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 :beta. This changes the AppId and URL.
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 beta or production AppId and URL.
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 :beta
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 : :beta)
22
+ (ENV["RAILS_ENV"] == 'production' ? :production : :sandbox)
23
23
  when Object.const_defined?(:Rack)
24
- (ENV["RACK_ENV"] == 'production' ? :production : :beta)
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 takes their API out of beta and
49
- # changes the URI before the gem gets updated), but for the most part you can leave it alone.
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
- if self.class.boolean_fields.include?($1)
37
- @properties[$1] = params[0] ? 1 : 0
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 then
120
- self.new(parsed[collection_name].first) if parsed[collection_name]
121
- when :all then
122
- ResourceCollection.new(self, parsed)
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
- find(: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)
129
125
  end
130
126
 
131
127
  # Returns the first account associated with this username.
132
128
  def self.first(options={})
133
- find(: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
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. Starts with
157
- # the resource connection at 'https://api.icontact.com/icp/' and works its way up.
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.connection
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.accountId.to_i
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
@@ -15,5 +15,6 @@ module ActsAsIcontact
15
15
  def self.default_options
16
16
  super.merge(:status=>:total)
17
17
  end
18
+
18
19
  end
19
20
  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 beta if RAILS_ENV is not production" do
44
+ it "is sandbox if RAILS_ENV is not production" do
45
45
  ENV["RAILS_ENV"] = 'staging'
46
- ActsAsIcontact::Config.mode.should == :beta
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 beta if RACK_ENV is not production" do
68
+ it "is sandbox if RACK_ENV is not production" do
69
69
  ENV["RACK_ENV"] = 'staging'
70
- ActsAsIcontact::Config.mode.should == :beta
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 ":beta" do
84
+ context ":sandbox" do
85
85
  before(:each) do
86
- ActsAsIcontact::Config.mode = :beta
86
+ ActsAsIcontact::Config.mode = :sandbox
87
87
  end
88
88
 
89
- it "returns the beta AppId" do
89
+ it "returns the sandbox AppId" do
90
90
  ActsAsIcontact::Config.app_id.should == "Ml5SnuFhnoOsuZeTOuZQnLUHTbzeUyhx"
91
91
  end
92
92
 
93
- it "returns the beta URL" do
94
- ActsAsIcontact::Config.url.should == "https://app.beta.icontact.com/icp/"
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
@@ -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.connection['resources'].url
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(:find).with(:first,{:foo=>:bar}).returns(nil)
70
- ActsAsIcontact::Resource.first(:foo=>:bar)
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(:find).with(:all,{:foo=>:bar}).returns(nil)
75
- ActsAsIcontact::Resource.all(:foo=>:bar)
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.connection['resources/1'].url
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.connection
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" => "1", "too" => "tar"}
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.beta.icontact.com/icp/resources", :body => %q<{"resources":[{"resourceId":"100","foo":"flar","kroo":"krar","too":"sar"}]}>)
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.beta.icontact.com/icp/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"]}>)
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.beta.icontact.com/icp/resources", :status => ["400","Bad Request"], :body => %q<{"errors":["You did not provide a clue. Clue is a required field. Please provide a clue"]}>)
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
@@ -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
- it "knows its welcome message"
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.beta.icontact.com/icp"
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, "#{i}/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, "#{i}/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}>)
11
- FakeWeb.register_uri(:get, "#{i}/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}>)
12
- FakeWeb.register_uri(:get, "#{i}/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}>)
13
- FakeWeb.register_uri(:get, "#{i}/resources?offset=10&limit=5", :body => %q<{"resources":[{"foo":"kar","resourceId":"11"},{"foo":"yar","resourceId":"12"}],"total":12,"limit":5,"offset":10}>)
14
- FakeWeb.register_uri(:post, "#{i}/resources/1", :body => %q<{"resource":{"foo":"bar","resourceId":"1","too":"sar"}}>)
15
- FakeWeb.register_uri(:post, "#{i}/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"]}>)
16
- FakeWeb.register_uri(:post, "#{i}/resources/3", :status => ["400","Bad Request"], :body => %q<{"errors":["You did not provide a clue. Clue is a required field. Please provide a clue"]}>)
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=500&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"}]}>)
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=500&name=First%20Test", :body => %q<{"lists":[{"listId":"444444","name":"First Test","emailOwnerOnChange":"0","welcomeOnManualAdd":"0","welcomeOnSignupAdd":"0","welcomeMessageId":"555555","description":"Just a test list."}]}>)
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
@@ -14,5 +14,5 @@ Spec::Runner.configure do |config|
14
14
  config.mock_with :mocha
15
15
 
16
16
  # Set up some reasonable testing variables
17
- ActsAsIcontact::Config.mode = :beta
17
+ ActsAsIcontact::Config.mode = :sandbox
18
18
  end
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
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-24 00:00:00 -07:00
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