SFEley-acts_as_icontact 0.2.1 → 0.2.3

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.2.3
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{acts_as_icontact}
5
- s.version = "0.2.1"
5
+ s.version = "0.2.3"
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"]
@@ -44,6 +44,7 @@ Gem::Specification.new do |s|
44
44
  "lib/acts_as_icontact/resources/custom_field.rb",
45
45
  "lib/acts_as_icontact/resources/list.rb",
46
46
  "lib/acts_as_icontact/resources/message.rb",
47
+ "lib/acts_as_icontact/resources/subscription.rb",
47
48
  "rails/init.rb",
48
49
  "spec/config_spec.rb",
49
50
  "spec/connection_spec.rb",
@@ -59,6 +60,7 @@ Gem::Specification.new do |s|
59
60
  "spec/resources/custom_field_spec.rb",
60
61
  "spec/resources/list_spec.rb",
61
62
  "spec/resources/message_spec.rb",
63
+ "spec/resources/subscription_spec.rb",
62
64
  "spec/spec.opts",
63
65
  "spec/spec_helper.rb",
64
66
  "spec/support/active_record/connection_adapters/nulldb_adapter.rb",
@@ -86,6 +88,7 @@ Gem::Specification.new do |s|
86
88
  "spec/resources/custom_field_spec.rb",
87
89
  "spec/resources/list_spec.rb",
88
90
  "spec/resources/message_spec.rb",
91
+ "spec/resources/subscription_spec.rb",
89
92
  "spec/spec_helper.rb",
90
93
  "spec/support/active_record/connection_adapters/nulldb_adapter.rb",
91
94
  "spec/support/nulldb_rspec.rb",
@@ -266,7 +266,7 @@ module ActsAsIcontact
266
266
  # Finesses the properties hash passed in to make iContact and Ruby idioms compatible.
267
267
  # Turns symbol keys into strings and runs the clean_value method on values.
268
268
  # Subclasses may add additional conversions.
269
- def clean_properties(properties)
269
+ def clean_properties(properties={})
270
270
  newhash = {}
271
271
  properties.each_pair do |key, value|
272
272
  newhash[key.to_s] = clean_value(value)
@@ -1,17 +1,20 @@
1
1
  module ActsAsIcontact
2
2
  class ResourceCollection < Enumerator
3
- attr_reader :total
3
+ attr_reader :total, :retrieved
4
4
 
5
- def initialize(klass, collection)
5
+ def initialize(klass, collection, forwardTo=nil)
6
6
  @klass = klass
7
+ @forwardTo = forwardTo
8
+
7
9
  @collection = collection[klass.collection_name]
8
10
  # Get number of elements
9
- @total = @collection.size
11
+ @retrieved = @collection.size
12
+ @total = collection["total"]
10
13
 
11
14
  enumcode = Proc.new do |yielder|
12
15
  counter = 0
13
- while counter < @total
14
- yielder.yield klass.new(@collection[counter])
16
+ while counter < @retrieved
17
+ yielder.yield resource(@collection[counter])
15
18
  counter += 1
16
19
  end
17
20
  end
@@ -20,8 +23,23 @@ module ActsAsIcontact
20
23
  end
21
24
 
22
25
  def [](index)
23
- @klass.new(@collection[index]) if @collection[index]
26
+ resource(@collection[index]) if @collection[index]
27
+ end
28
+
29
+ # Calls "next" to kick off the enumerator. This is more in line with what users would expect.
30
+ def first
31
+ self.rewind
32
+ self.next
24
33
  end
25
34
 
35
+ private
36
+ def resource(properties)
37
+ if @forwardTo
38
+ id = @forwardTo.primary_key
39
+ @forwardTo.find(properties[id])
40
+ else
41
+ @klass.new(properties)
42
+ end
43
+ end
26
44
  end
27
45
  end
@@ -15,6 +15,11 @@ module ActsAsIcontact
15
15
  def self.default_options
16
16
  super.merge(:status=>:total)
17
17
  end
18
+
19
+ # Returns the lists to which this contact is subscribed (via the Subscription class).
20
+ def lists
21
+ @lists ||= ActsAsIcontact::Subscription.lists(:contactId => id)
22
+ end
18
23
 
19
24
  end
20
25
  end
@@ -25,5 +25,10 @@ module ActsAsIcontact
25
25
  ActsAsIcontact::Message.find(welcomeMessageId)
26
26
  end
27
27
 
28
+ # Returns the contacts subscribed to this list (via the Subscription class).
29
+ def subscribers
30
+ @subscribers ||= ActsAsIcontact::Subscription.contacts(:listId => id)
31
+ end
32
+
28
33
  end
29
34
  end
@@ -0,0 +1,75 @@
1
+ module ActsAsIcontact
2
+
3
+ # Subscriptions are effectively a "has_many :through" mapping between Contacts and Lists. They are implemented
4
+ # that way, with a .lists property on Contact and a .subscribers property on List that both work through the
5
+ # Subscription class.
6
+ class Subscription < Resource
7
+
8
+ STATUSES = %w(normal pending unsubscribed)
9
+
10
+ # Defaults status to "normal"
11
+ def initialize(properties = {})
12
+ super({:status => "normal"}.merge(properties))
13
+ end
14
+
15
+ # Searches on the compound primary key ("listid_contactid").
16
+ def self.find_by_string(value)
17
+ find_by_id(value)
18
+ end
19
+
20
+ # Requires listId, contactId, and status when creating a record
21
+ def self.required_on_create
22
+ super + %w(listId contactId status)
23
+ end
24
+
25
+ # Never send listId or contactId on update
26
+ def self.never_on_update
27
+ super + %w(listId contactId)
28
+ end
29
+
30
+
31
+ # Overrides "all" to pass an optional forwardTo parameter to ResourceCollection. This instructs ResourceCollection
32
+ # to look up and create a new object of the given type, rather than the base resource. (This is how the #lists and
33
+ # #contacts methods are implemented.)
34
+ def self.all(options={}, forwardTo = nil)
35
+ query_options = default_options.merge(options)
36
+ validate_options(query_options)
37
+ result = query_collection(query_options)
38
+ ResourceCollection.new(self, result, forwardTo)
39
+ end
40
+
41
+ # Returns a collection of all contacts matching the query. Unfortunately, this has to be performed by looking up
42
+ # each one individually. We forward the Contact class to ResourceCollection to do that work.
43
+ def self.contacts(options={})
44
+ all(options, ActsAsIcontact::Contact)
45
+ end
46
+
47
+ # Returns a collection of all lists matching the query. Unfortunately, this has to be performed by looking up
48
+ # each one individually. We forward the List class to ResourceCollection to do that work.
49
+ def self.lists(options={})
50
+ all(options, ActsAsIcontact::List)
51
+ end
52
+
53
+ # status must be one of: normal, pending, unsubscribed
54
+ def validate_on_save(fields)
55
+ raise ActsAsIcontact::ValidationError, "Status must be one of: #{STATUSES.join(', ')}" unless STATUSES.include?(fields["status"])
56
+ end
57
+
58
+ # The list that this subscription is associated with.
59
+ def list
60
+ ActsAsIcontact::List.find(listId) if properties["listId"]
61
+ end
62
+
63
+ # The contact that this subscription is associated with.
64
+ def contact
65
+ ActsAsIcontact::Contact.find(contactId) if properties["contactId"]
66
+ end
67
+
68
+ # The confirmation message for this subscription, if any.
69
+ def confirmationMessage
70
+ ActsAsIcontact::Message.find(confirmationMessageId) if properties["confirmationMessageId"]
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -20,4 +20,8 @@ describe ActsAsIcontact::ResourceCollection do
20
20
  @this[1].yoo.should == "yar"
21
21
  end
22
22
 
23
+ it "treats 'first' as an initial call of 'next'" do
24
+ @this.first.foo.should == "bar"
25
+ @this.next.yoo.should == "yar"
26
+ end
23
27
  end
@@ -18,7 +18,10 @@ describe ActsAsIcontact::Contact do
18
18
  @john = ActsAsIcontact::Contact.first(:firstName => "John", :lastName => "Test")
19
19
  end
20
20
 
21
- it "knows which lists it's subscribed to"
21
+ it "knows which lists it's subscribed to" do
22
+ @john.lists.first.should == ActsAsIcontact::List.find(444444)
23
+ end
24
+
22
25
  it "knows its history"
23
26
  end
24
27
 
@@ -31,7 +31,11 @@ describe ActsAsIcontact::List do
31
31
  before(:each) do
32
32
  @list = ActsAsIcontact::List.first(:name => "First Test")
33
33
  end
34
- it "knows its subscribers"
34
+
35
+ it "knows its subscribers" do
36
+ @list.subscribers.first.should == ActsAsIcontact::Contact.find(333444)
37
+ @list.subscribers.next.should == ActsAsIcontact::Contact.find(333333)
38
+ end
35
39
 
36
40
  it "knows its welcome message" do
37
41
  @list.welcomeMessage.should == ActsAsIcontact::Message.find(555555)
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe ActsAsIcontact::Subscription do
4
+
5
+ it "requires listId, contactId, status on create" do
6
+ s = ActsAsIcontact::Subscription.new(:status => nil)
7
+ lambda{s.save}.should raise_error(ActsAsIcontact::ValidationError, "Missing required fields: listId, contactId, status")
8
+ end
9
+
10
+ it "validates that status is in (pending, normal, unsubscribed)" do
11
+ s = ActsAsIcontact::Subscription.new(:status => "foo", :listId => 1, :contactId => 2)
12
+ lambda{s.save}.should raise_error(ActsAsIcontact::ValidationError, "Status must be one of: normal, pending, unsubscribed")
13
+ end
14
+
15
+ it "sets status to normal by default" do
16
+ s = ActsAsIcontact::Subscription.new
17
+ s.status.should == "normal"
18
+ end
19
+
20
+ it "can find by the compound primary key" do
21
+ s = ActsAsIcontact::Subscription.find("444444_333333")
22
+ s.listId.should == 444444
23
+ s.contactId.should == 333333
24
+ end
25
+
26
+ it "can return the contacts for a query as a collection" do
27
+ collection = ActsAsIcontact::Subscription.contacts(:listId => 444444)
28
+ collection.first.should == ActsAsIcontact::Contact.find(333444)
29
+ collection.next.should == ActsAsIcontact::Contact.find(333333)
30
+ end
31
+
32
+ it "can return the lists for a query as a collection" do
33
+ collection = ActsAsIcontact::Subscription.lists(:contactId => 333444)
34
+ collection.first.should == ActsAsIcontact::List.find(444444)
35
+ end
36
+
37
+ context "associations" do
38
+ # Create one good subscription
39
+ before(:each) do
40
+ @sub = ActsAsIcontact::Subscription.first(:contactId => 333444)
41
+ end
42
+
43
+ it "knows its contact" do
44
+ @sub.contact.should == ActsAsIcontact::Contact.find(333444)
45
+ end
46
+
47
+ it "knows its list" do
48
+ @sub.list.should == ActsAsIcontact::List.find(444444)
49
+ end
50
+
51
+ it "knows its confirmation message" do
52
+ @sub.confirmationMessage.should == ActsAsIcontact::Message.find(555666)
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -30,14 +30,19 @@ FakeWeb.register_uri(:get, "#{i}/a/111111/c?limit=500", :body => %q<{"clientfold
30
30
 
31
31
  # Contacts
32
32
  FakeWeb.register_uri(:get, "#{ic}/contacts?limit=1&status=total&firstName=John&lastName=Test", :body => %q<{"contacts":[{"email":"john@example.org","firstName":"John","lastName":"Test","status":"normal","contactId":"333333","createDate":"2009-07-24 01:00:00"}]}>)
33
- FakeWeb.register_uri(:post, "#{ic}/contacts", :body => %q<{"contacts":[{"email":"john@example.org","firstName":"John","lastName":"Smith","status":"normal","contactId":"333444","createDate":"2009-07-24 01:00:00","street":"","street2":"","prefix":"","suffix":"","fax":"","phone":"","city":"","state":"","postalCode":"","bounceCount":0,"custom_field":"","test_field":"","business":""}]}>)
33
+ FakeWeb.register_uri(:post, "#{ic}/contacts", :body => %q<{"contacts":[{"email":"john@example.org","firstName":"John","lastName":"Smith","status":"normal","contactId":"333444","createDate":"2009-07-24 01:00:00","street":"","street2":"","prefix":"","suffix":"","fax":"","phone":"","city":"","state":"","postalCode":"","bounceCount":0,"custom_field":"","test_field":"","business":""},{"email":"john@example.org","firstName":"John","lastName":"Test","status":"normal","contactId":"333333","createDate":"2009-07-24 01:00:00"}]}>)
34
+ FakeWeb.register_uri(:get, "#{ic}/contacts/333444", :body => %q<{"contact":{"email":"john@example.org","firstName":"John","lastName":"Smith","status":"normal","contactId":"333444","createDate":"2009-07-24 01:00:00","street":"","street2":"","prefix":"","suffix":"","fax":"","phone":"","city":"","state":"","postalCode":"","bounceCount":0,"custom_field":"","test_field":"","business":""}}>)
35
+ FakeWeb.register_uri(:get, "#{ic}/contacts/333333", :body => %q<{"contact":{"email":"john@example.org","firstName":"John","lastName":"Test","status":"normal","contactId":"333333","createDate":"2009-07-24 01:00:00"}}>)
34
36
 
35
37
  # Lists
36
38
  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."}]}>)
39
+ FakeWeb.register_uri(:get, "#{ic}/lists/444444", :body => %q<{"list":{"listId":"444444","name":"First Test","emailOwnerOnChange":"0","welcomeOnManualAdd":"0","welcomeOnSignupAdd":"0","welcomeMessageId":"555555","description":"Just a test list."}}>)
37
40
 
38
41
  # Message
39
- #### Test message for List association
42
+ #### Test welcome message for List association
40
43
  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"}}>)
44
+ #### Test confirmation message
45
+ FakeWeb.register_uri(:get, "#{ic}/messages/555666", :body => %q<{"message":{"messageId":"555666","subject":"Confirm!","messageType":"confirmation","textBody":"Please confirm your subscription.","htmlBody":"<p>Please confirm your subscription.</p>","createDate":"20090727 14:55:12"}}>)
41
46
 
42
47
  #### Test message for associations originating from Message spec
43
48
  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"}]}>)
@@ -45,3 +50,13 @@ FakeWeb.register_uri(:get, "#{ic}/messages?limit=1&subject=Test%20Message", :bod
45
50
  # CustomField
46
51
  FakeWeb.register_uri(:get, "#{ic}/customfields?limit=500", :body => %q<{"customfields":[{"privateName":"test_field","publicName":"Test Field","displayToUser":"0","fieldType":"text"},{"privateName":"custom_field","publicName":"This is for the Rails integration specs","displayToUser":1,"fieldType":"text"}],"total":2}>)
47
52
  FakeWeb.register_uri(:get, "#{ic}/customfields/test_field", :body => %q<{"customfield":{"privateName":"test_field","publicName":"Test Field","displayToUser":"0","fieldType":"text"}}>)
53
+
54
+ # Subscription
55
+ FakeWeb.register_uri(:get, "#{ic}/subscriptions/444444_333333", :body => %q<{"subscription":{"status":"normal","addDate":"2009-07-27T15:36:37-04:00","contactId":333333,"listId":444444, "subscriptionId":"444444_333333"}}>)
56
+ FakeWeb.register_uri(:get, "#{ic}/subscriptions/444444_333444", :body => %q<{"subscription":{"status":"normal","addDate":"2009-07-27T15:37:38-04:00","contactId":333444,"listId":444444, "subscriptionId":"444444_333444"}}>)
57
+ FakeWeb.register_uri(:get, "#{ic}/subscriptions?limit=500", :body => %q<{"subscriptions":[{"status":"normal","addDate":"2009-07-27T15:36:37-04:00","contactId":333333,"listId":444444, "subscriptionId":"444444_333333"},{"status":"normal","addDate":"2009-07-27T15:36:37-04:00","contactId":333333,"listId":444444, "subscriptionId":"444444_333333"}],"total":2}>)
58
+ FakeWeb.register_uri(:get, "#{ic}/subscriptions?limit=500&listId=444444", :body => %q<{"subscriptions":[{"status":"normal","addDate":"2009-07-27T15:36:37-04:00","contactId":333444,"listId":444444, "subscriptionId":"444444_333444","confirmationMessageId":555666},{"status":"normal","addDate":"2009-07-27T15:36:37-04:00","contactId":333333,"listId":444444, "subscriptionId":"444444_333333"}],"total":1}>)
59
+ FakeWeb.register_uri(:get, "#{ic}/subscriptions?limit=1&contactId=333444", :body => %q<{"subscriptions":[{"status":"normal","addDate":"2009-07-27T15:36:37-04:00","contactId":333444,"listId":444444, "subscriptionId":"444444_333333","confirmationMessageId":555666}]}>)
60
+ FakeWeb.register_uri(:get, "#{ic}/subscriptions?limit=500&contactId=333444", :body => %q<{"subscriptions":[{"status":"normal","addDate":"2009-07-27T15:36:37-04:00","contactId":333444,"listId":444444, "subscriptionId":"444444_333444","confirmationMessageId":555666}]}>)
61
+ FakeWeb.register_uri(:get, "#{ic}/subscriptions?limit=500&contactId=333333", :body => %q<{"subscriptions":[{"status":"normal","addDate":"2009-07-27T15:36:37-04:00","contactId":333333,"listId":444444, "subscriptionId":"444444_333333","confirmationMessageId":555666}]}>)
62
+
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.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Eley
@@ -47,6 +47,7 @@ files:
47
47
  - lib/acts_as_icontact/resources/custom_field.rb
48
48
  - lib/acts_as_icontact/resources/list.rb
49
49
  - lib/acts_as_icontact/resources/message.rb
50
+ - lib/acts_as_icontact/resources/subscription.rb
50
51
  - rails/init.rb
51
52
  - spec/config_spec.rb
52
53
  - spec/connection_spec.rb
@@ -62,6 +63,7 @@ files:
62
63
  - spec/resources/custom_field_spec.rb
63
64
  - spec/resources/list_spec.rb
64
65
  - spec/resources/message_spec.rb
66
+ - spec/resources/subscription_spec.rb
65
67
  - spec/spec.opts
66
68
  - spec/spec_helper.rb
67
69
  - spec/support/active_record/connection_adapters/nulldb_adapter.rb
@@ -109,6 +111,7 @@ test_files:
109
111
  - spec/resources/custom_field_spec.rb
110
112
  - spec/resources/list_spec.rb
111
113
  - spec/resources/message_spec.rb
114
+ - spec/resources/subscription_spec.rb
112
115
  - spec/spec_helper.rb
113
116
  - spec/support/active_record/connection_adapters/nulldb_adapter.rb
114
117
  - spec/support/nulldb_rspec.rb