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 +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: SFEley-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
|
@@ -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
|