acts_as_icontact 0.2.1 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/acts_as_icontact.gemspec +4 -1
- data/lib/acts_as_icontact/resource.rb +1 -1
- data/lib/acts_as_icontact/resource_collection.rb +24 -6
- data/lib/acts_as_icontact/resources/contact.rb +5 -0
- data/lib/acts_as_icontact/resources/list.rb +5 -0
- data/lib/acts_as_icontact/resources/subscription.rb +75 -0
- data/spec/resource_collection_spec.rb +4 -0
- data/spec/resources/contact_spec.rb +4 -1
- data/spec/resources/list_spec.rb +5 -1
- data/spec/resources/subscription_spec.rb +57 -0
- data/spec/support/spec_fakeweb.rb +17 -2
- metadata +4 -1
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.3
|
data/acts_as_icontact.gemspec
CHANGED
@@ -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.
|
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
|
-
@
|
11
|
+
@retrieved = @collection.size
|
12
|
+
@total = collection["total"]
|
10
13
|
|
11
14
|
enumcode = Proc.new do |yielder|
|
12
15
|
counter = 0
|
13
|
-
while counter < @
|
14
|
-
yielder.yield
|
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
|
-
|
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
|
@@ -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
|
|
data/spec/resources/list_spec.rb
CHANGED
@@ -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
|
-
|
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: acts_as_icontact
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Eley
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- lib/acts_as_icontact/resources/custom_field.rb
|
53
53
|
- lib/acts_as_icontact/resources/list.rb
|
54
54
|
- lib/acts_as_icontact/resources/message.rb
|
55
|
+
- lib/acts_as_icontact/resources/subscription.rb
|
55
56
|
- rails/init.rb
|
56
57
|
- spec/config_spec.rb
|
57
58
|
- spec/connection_spec.rb
|
@@ -67,6 +68,7 @@ files:
|
|
67
68
|
- spec/resources/custom_field_spec.rb
|
68
69
|
- spec/resources/list_spec.rb
|
69
70
|
- spec/resources/message_spec.rb
|
71
|
+
- spec/resources/subscription_spec.rb
|
70
72
|
- spec/spec.opts
|
71
73
|
- spec/spec_helper.rb
|
72
74
|
- spec/support/active_record/connection_adapters/nulldb_adapter.rb
|
@@ -115,6 +117,7 @@ test_files:
|
|
115
117
|
- spec/resources/custom_field_spec.rb
|
116
118
|
- spec/resources/list_spec.rb
|
117
119
|
- spec/resources/message_spec.rb
|
120
|
+
- spec/resources/subscription_spec.rb
|
118
121
|
- spec/spec_helper.rb
|
119
122
|
- spec/support/active_record/connection_adapters/nulldb_adapter.rb
|
120
123
|
- spec/support/nulldb_rspec.rb
|