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 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