contactology 0.0.1

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.
Files changed (86) hide show
  1. data/.gitignore +5 -0
  2. data/.infinity_test +16 -0
  3. data/.rspec +3 -0
  4. data/.rvmrc +41 -0
  5. data/.watchr +36 -0
  6. data/Gemfile +6 -0
  7. data/Rakefile +1 -0
  8. data/contactology.gemspec +28 -0
  9. data/lib/contactology.rb +125 -0
  10. data/lib/contactology/api.rb +80 -0
  11. data/lib/contactology/basic_object.rb +21 -0
  12. data/lib/contactology/campaign.rb +127 -0
  13. data/lib/contactology/campaign/preview.rb +11 -0
  14. data/lib/contactology/campaigns.rb +2 -0
  15. data/lib/contactology/campaigns/standard.rb +80 -0
  16. data/lib/contactology/campaigns/transactional.rb +58 -0
  17. data/lib/contactology/configuration.rb +42 -0
  18. data/lib/contactology/contact.rb +193 -0
  19. data/lib/contactology/errors.rb +4 -0
  20. data/lib/contactology/issue.rb +24 -0
  21. data/lib/contactology/issues.rb +18 -0
  22. data/lib/contactology/list.rb +192 -0
  23. data/lib/contactology/list_proxy.rb +25 -0
  24. data/lib/contactology/parser.rb +5 -0
  25. data/lib/contactology/send_result.rb +35 -0
  26. data/lib/contactology/stash.rb +29 -0
  27. data/lib/contactology/transactional_message.rb +38 -0
  28. data/lib/contactology/version.rb +3 -0
  29. data/spec/factories/campaigns.rb +18 -0
  30. data/spec/factories/contacts.rb +3 -0
  31. data/spec/factories/issues.rb +9 -0
  32. data/spec/factories/lists.rb +3 -0
  33. data/spec/factories/transactional_messages.rb +5 -0
  34. data/spec/fixtures/net/campaign/destroy.yml +246 -0
  35. data/spec/fixtures/net/campaign/find/failure.yml +36 -0
  36. data/spec/fixtures/net/campaign/find/success.yml +176 -0
  37. data/spec/fixtures/net/campaign/find_by_name/failure.yml +36 -0
  38. data/spec/fixtures/net/campaign/find_by_name/success.yml +211 -0
  39. data/spec/fixtures/net/campaign/preview.yml +106 -0
  40. data/spec/fixtures/net/campaigns/standard/create/failure.yml +106 -0
  41. data/spec/fixtures/net/campaigns/standard/create/invalid.yml +141 -0
  42. data/spec/fixtures/net/campaigns/standard/create/success.yml +176 -0
  43. data/spec/fixtures/net/campaigns/standard/send_campaign/failure.yml +316 -0
  44. data/spec/fixtures/net/campaigns/standard/send_campaign/success.yml +316 -0
  45. data/spec/fixtures/net/campaigns/transactional/create/failure.yml +36 -0
  46. data/spec/fixtures/net/campaigns/transactional/create/success.yml +71 -0
  47. data/spec/fixtures/net/contact/active.yml +106 -0
  48. data/spec/fixtures/net/contact/change_email/success.yml +176 -0
  49. data/spec/fixtures/net/contact/change_email/unknown.yml +36 -0
  50. data/spec/fixtures/net/contact/create.yml +106 -0
  51. data/spec/fixtures/net/contact/destroy.yml +141 -0
  52. data/spec/fixtures/net/contact/find/active.yml +106 -0
  53. data/spec/fixtures/net/contact/find/suppressed.yml +141 -0
  54. data/spec/fixtures/net/contact/find/unknown.yml +36 -0
  55. data/spec/fixtures/net/contact/lists/empty.yml +106 -0
  56. data/spec/fixtures/net/contact/lists/full.yml +246 -0
  57. data/spec/fixtures/net/contact/lists/unknown.yml +71 -0
  58. data/spec/fixtures/net/contact/suppress.yml +141 -0
  59. data/spec/fixtures/net/list/all.yml +141 -0
  60. data/spec/fixtures/net/list/create.yml +106 -0
  61. data/spec/fixtures/net/list/destroy.yml +176 -0
  62. data/spec/fixtures/net/list/find/success.yml +141 -0
  63. data/spec/fixtures/net/list/find/unknown.yml +36 -0
  64. data/spec/fixtures/net/list/import/success.yml +351 -0
  65. data/spec/fixtures/net/list/subscribe/success.yml +211 -0
  66. data/spec/fixtures/net/list/unsubscribe/success.yml +246 -0
  67. data/spec/fixtures/net/transactional_message/send_message/failure.yml +176 -0
  68. data/spec/fixtures/net/transactional_message/send_message/success.yml +176 -0
  69. data/spec/models/contactology/api_spec.rb +51 -0
  70. data/spec/models/contactology/campaign_spec.rb +75 -0
  71. data/spec/models/contactology/campaigns/standard_spec.rb +84 -0
  72. data/spec/models/contactology/campaigns/transactional_spec.rb +28 -0
  73. data/spec/models/contactology/configuration_spec.rb +29 -0
  74. data/spec/models/contactology/contact_spec.rb +175 -0
  75. data/spec/models/contactology/issues_spec.rb +34 -0
  76. data/spec/models/contactology/list_spec.rb +125 -0
  77. data/spec/models/contactology/send_result_spec.rb +65 -0
  78. data/spec/models/contactology/stash_spec.rb +65 -0
  79. data/spec/models/contactology/transactional_message_spec.rb +65 -0
  80. data/spec/models/contactology_spec.rb +67 -0
  81. data/spec/requests/contacts_spec.rb +4 -0
  82. data/spec/spec_helper.rb +15 -0
  83. data/spec/support/contactology.rb +34 -0
  84. data/spec/support/factory_girl.rb +19 -0
  85. data/spec/support/vcr.rb +11 -0
  86. metadata +282 -0
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Contactology::Campaigns::Standard do
4
+ context '.create' do
5
+ context 'when successful' do
6
+ use_vcr_cassette 'campaigns/standard/create/success'
7
+ let(:list) { Factory :list, :name => 'campaign-standard-create-success' }
8
+ let(:campaign) { create_campaign :recipients => list }
9
+ after(:each) { list.destroy; campaign.destroy }
10
+
11
+ subject { campaign }
12
+
13
+ it { should be_instance_of Contactology::Campaigns::Standard }
14
+ its(:id) { should_not be_nil }
15
+ end
16
+
17
+ context 'with invalid data' do
18
+ use_vcr_cassette 'campaigns/standard/create/invalid'
19
+ let(:list) { Factory :list, :name => 'campaign-standard-create-invalid' }
20
+ let(:campaign) { create_campaign :sender_email => 'bad@example', :recipients => list }
21
+ after(:each) { list.destroy }
22
+
23
+ subject { campaign }
24
+
25
+ it { should be_false }
26
+ end
27
+
28
+ context 'with required attributes missing' do
29
+ use_vcr_cassette 'campaigns/standard/create/failure'
30
+ let(:list) { Factory :list, :name => 'campaign-standard-create-failure' }
31
+ let(:campaign) { create_campaign :name => nil, :recipients => list }
32
+ after(:each) { list.destroy }
33
+
34
+ subject { campaign }
35
+
36
+ it 'raises an ArgumentError' do
37
+ expect { subject }.to raise_error(ArgumentError, /The property 'name' is required/)
38
+ end
39
+ end
40
+ end
41
+
42
+ context '#send_campaign' do
43
+ context 'when successful' do
44
+ use_vcr_cassette 'campaigns/standard/send_campaign/success'
45
+ let(:list) { Factory :list, :name => 'send-standard-campaign-success' }
46
+ let(:contact) { Factory :contact }
47
+ let(:campaign) { Factory :standard_campaign, :recipients => list }
48
+
49
+ before(:each) { list.subscribe contact }
50
+ after(:each) { list.destroy; contact.destroy; campaign.destroy }
51
+
52
+ subject { campaign.send_campaign }
53
+
54
+ it { should be_instance_of Contactology::SendResult }
55
+ it { should be_successful }
56
+ its(:issues) { should be_empty }
57
+ end
58
+
59
+ context 'when unsuccessful (high spam score)' do
60
+ use_vcr_cassette 'campaigns/standard/send_campaign/failure'
61
+ let(:list) { Factory :list, :name => 'send-standard-campaign-failure' }
62
+ let(:contact) { Factory :contact }
63
+ let(:campaign) { Factory :standard_campaign, :recipients => list, :content => {'text' => 'OMG BUY VIAGRA'} }
64
+
65
+ before(:each) { list.subscribe(contact) }
66
+ after(:each) { list.destroy; contact.destroy; campaign.destroy }
67
+
68
+ subject { campaign.send_campaign }
69
+
70
+ it { should be_instance_of Contactology::SendResult }
71
+ it { should_not be_successful }
72
+ its(:issues) { should_not be_empty }
73
+ its(:score) { should be < 100 }
74
+ end
75
+ end
76
+
77
+
78
+ private
79
+
80
+
81
+ def create_campaign(attributes = {})
82
+ Contactology::Campaigns::Standard.create Factory.attributes_for(:standard_campaign).merge(attributes)
83
+ end
84
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Contactology::Campaigns::Transactional do
4
+ context '.create' do
5
+ context 'when successful' do
6
+ use_vcr_cassette 'campaigns/transactional/create/success'
7
+ let(:campaign) { Contactology::Campaigns::Transactional.create Factory.attributes_for(:transactional_campaign) }
8
+ after(:each) { campaign.destroy }
9
+
10
+ subject { campaign }
11
+
12
+ it { should be_instance_of Contactology::Campaigns::Transactional }
13
+ its(:id) { should_not be_nil }
14
+ its(:object_id) { should == campaign.object_id }
15
+ end
16
+
17
+ context 'when unsuccessful' do
18
+ use_vcr_cassette 'campaigns/transactional/create/failure'
19
+ let(:campaign) { Contactology::Campaigns::Transactional.create Factory.attributes_for(:transactional_campaign).merge(:content => {:text => 'bad'}) }
20
+
21
+ subject { campaign }
22
+
23
+ it { should be_kind_of Contactology::SendResult }
24
+ it { should_not be_successful }
25
+ its(:issues) { should_not be_empty }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Contactology::Configuration do
4
+ let(:configuration) { Contactology::Configuration.new }
5
+
6
+ context '#endpoint' do
7
+ it 'defaults to http://api.emailcampaigns.net/2/REST/' do
8
+ configuration.endpoint.should == 'https://api.emailcampaigns.net/2/REST/'
9
+ end
10
+
11
+ it 'may be overridden' do
12
+ expect {
13
+ configuration.endpoint = 'https://example.local/'
14
+ }.to change(configuration, :endpoint).to('https://example.local/')
15
+ end
16
+ end
17
+
18
+ context '#key' do
19
+ it 'is unset by default' do
20
+ configuration.key.should be_nil
21
+ end
22
+
23
+ it 'may be set' do
24
+ expect {
25
+ configuration.key = 'abcdefg'
26
+ }.to change(configuration, :key).to('abcdefg')
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,175 @@
1
+ require 'spec_helper'
2
+
3
+ describe Contactology::Contact do
4
+ context '.create' do
5
+ use_vcr_cassette 'contact/create'
6
+
7
+ it 'raises an ArgumentError without an :email given' do
8
+ expect {
9
+ Contactology::Contact.create
10
+ }.to raise_error(ArgumentError)
11
+ end
12
+
13
+ context 'when successful' do
14
+ let(:contact) { Contactology::Contact.create :email => 'created@example.com' }
15
+ subject { contact }
16
+ after(:each) { contact.destroy }
17
+
18
+ it 'creates a new contact' do
19
+ subject
20
+ Contactology::Contact.find('created@example.com').should_not be_nil
21
+ end
22
+
23
+ it { should be_kind_of Contactology::Contact }
24
+ its(:email) { should eql 'created@example.com' }
25
+ its(:active?) { should be_true }
26
+ end
27
+ end
28
+
29
+ context '.find' do
30
+ context 'for a matching, active contact' do
31
+ use_vcr_cassette 'contact/find/active'
32
+ let(:contact) { Contactology::Contact.create(:email => 'found@example.com') }
33
+ after(:each) { contact.destroy }
34
+ subject { Contactology::Contact.find(contact.email) }
35
+
36
+ it { should be_kind_of Contactology::Contact }
37
+ its(:email) { should eql 'found@example.com' }
38
+ it { should be_active }
39
+ end
40
+
41
+ context 'for a matching, suppressed contact' do
42
+ use_vcr_cassette 'contact/find/suppressed'
43
+ let(:contact) { Contactology::Contact.create(:email => 'found-suppressed@example.com') }
44
+ before(:each) { contact.suppress }
45
+ after(:each) { contact.destroy }
46
+ subject { Contactology::Contact.find('found-suppressed@example.com') }
47
+
48
+ it { should be_kind_of Contactology::Contact }
49
+ its(:email) { should eql 'found-suppressed@example.com' }
50
+ it { should be_suppressed }
51
+ end
52
+
53
+ context 'for an unknown email' do
54
+ use_vcr_cassette 'contact/find/unknown'
55
+ subject { Contactology::Contact.find('unknown@example.com') }
56
+ it { should be_nil }
57
+ end
58
+ end
59
+
60
+ context '#active?' do
61
+ use_vcr_cassette 'contact/active'
62
+ let(:contact) do
63
+ Contactology::Contact.create :email => 'active@example.com'
64
+ Contactology::Contact.find 'active@example.com'
65
+ end
66
+ subject { contact.active? }
67
+ after(:each) { contact.destroy }
68
+
69
+ it { should be_true }
70
+ end
71
+
72
+ context '#change_email' do
73
+ context 'for a known contact' do
74
+ use_vcr_cassette 'contact/change_email/success'
75
+ let(:contact) { Contactology::Contact.create :email => 'change-email-3@example.com' }
76
+ let(:new_email) { 'changed-3@example.com' }
77
+ subject { contact.change_email new_email }
78
+ after(:each) { contact.destroy }
79
+
80
+ it 'updates the Contactology email' do
81
+ expect { subject }.to change { Contactology::Contact.find new_email }.from(nil)
82
+ end
83
+
84
+ it { should be_true }
85
+
86
+ it 'updates the local contact email' do
87
+ expect { subject }.to change(contact, :email).to(new_email)
88
+ end
89
+ end
90
+
91
+ context 'for an unknown contact' do
92
+ use_vcr_cassette 'contact/change_email/unknown'
93
+ let(:contact) { Contactology::Contact.new(:email => 'unknown@example.com') }
94
+ subject { contact.change_email 'changefail@example.com' }
95
+
96
+ it { should be_false }
97
+
98
+ it 'does not update the local contact email' do
99
+ expect { subject }.to_not change(contact, :email)
100
+ end
101
+ end
102
+ end
103
+
104
+ context '#destroy' do
105
+ use_vcr_cassette 'contact/destroy'
106
+
107
+ context 'when successful' do
108
+ let(:contact) { Contactology::Contact.create :email => 'destroy@example.com' }
109
+ subject { contact.destroy }
110
+
111
+ it 'deletes the contact on Contactology' do
112
+ expect { subject }.to change(contact, :deleted?).to(true)
113
+ end
114
+
115
+ it { should be_true }
116
+ end
117
+ end
118
+
119
+ context '#lists' do
120
+ context 'for a known contact with subscription lists' do
121
+ use_vcr_cassette 'contact/lists/full'
122
+ let(:contact) { Contactology::Contact.create :email => 'lists@example.com' }
123
+ let(:list) { Contactology::List.create :name => 'contact-list-test' }
124
+ before(:each) { list.subscribe contact.email }
125
+ after(:each) { contact.destroy; list.destroy }
126
+
127
+ subject { contact.lists }
128
+
129
+ it { should be_kind_of Enumerable }
130
+ it { should_not be_empty }
131
+
132
+ it 'contains the subscribed list' do
133
+ subject.any? { |remote_list| remote_list.id == list.id }.should be_true
134
+ end
135
+ end
136
+
137
+ context 'for a known contact without subscription lists' do
138
+ use_vcr_cassette 'contact/lists/empty'
139
+ let(:contact) { Contactology::Contact.create :email => 'emptylists@example.com' }
140
+ subject { contact.lists }
141
+ after(:each) { contact.destroy }
142
+
143
+ it { should be_kind_of Enumerable }
144
+ it { should be_empty }
145
+ end
146
+
147
+ context 'for an unknown contact' do
148
+ use_vcr_cassette 'contact/lists/unknown'
149
+ let(:contact) { Contactology::Contact.new :email => 'unknown@example.com' }
150
+ subject { contact.lists }
151
+ after(:each) { contact.destroy }
152
+
153
+ it { should be_kind_of Enumerable }
154
+ it { should be_empty }
155
+ end
156
+ end
157
+
158
+ context '#suppress' do
159
+ use_vcr_cassette 'contact/suppress'
160
+ let(:contact) do
161
+ Contactology::Contact.create :email => 'suppressed@example.com'
162
+ Contactology::Contact.find 'suppressed@example.com'
163
+ end
164
+ subject { contact.suppress }
165
+ after(:each) { contact.destroy }
166
+
167
+ it { should be_true }
168
+
169
+ context 'the contact' do
170
+ subject { contact.suppress; contact }
171
+
172
+ its(:suppressed?) { should be_true }
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Contactology::Issues do
4
+ let(:issues) { Contactology::Issues.new }
5
+ subject { issues }
6
+
7
+ it 'is empty by default' do
8
+ issues.should be_empty
9
+ end
10
+
11
+ it 'holds Contactology::Issue objects' do
12
+ issues = Contactology::Issues.new('issues' => [Factory.attributes_for(:issue)])
13
+ issues.should_not be_empty
14
+ issues.all? { |i| i.kind_of?(Contactology::Issue) }.should be_true
15
+ end
16
+
17
+ it 'converts pushed objects to Issue instances' do
18
+ expect {
19
+ issues << Factory.attributes_for(:issue)
20
+ }.to change(issues, :size).by(1)
21
+
22
+ issues.all? { |i| i.kind_of? Contactology::Issue }.should be_true
23
+ end
24
+
25
+ context '#score' do
26
+ it 'defaults to 0' do
27
+ Contactology::Issues.new.score.should == 0
28
+ end
29
+
30
+ it 'may be set during initialization' do
31
+ Contactology::Issues.new('score' => 95).score.should == 95
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ describe Contactology::List do
4
+ context '.all' do
5
+ use_vcr_cassette 'list/all'
6
+ let!(:list) { Contactology::List.create :name => 'all-test-list' }
7
+ after(:each) { list.destroy }
8
+
9
+ subject { Contactology::List.all }
10
+
11
+ it { should be_kind_of Enumerable }
12
+ it { should_not be_empty }
13
+
14
+ it 'contains List instances' do
15
+ subject.each do |o|
16
+ o.should be_kind_of Contactology::List
17
+ end
18
+ end
19
+
20
+ it 'contains the created list' do
21
+ subject.any? { |remote_list|
22
+ remote_list.id == list.id &&
23
+ remote_list.name == list.name
24
+ }.should be_true
25
+ end
26
+ end
27
+
28
+ context '.create' do
29
+ it 'raises an ArgumentError without a name given' do
30
+ expect {
31
+ Contactology::List.create
32
+ }.to raise_error(ArgumentError)
33
+ end
34
+
35
+ context 'when successful' do
36
+ use_vcr_cassette 'list/create'
37
+ let(:list) { Contactology::List.create :name => 'creationtest' }
38
+ subject { list }
39
+ after(:each) { list.destroy }
40
+
41
+ it { should be_instance_of Contactology::List }
42
+ it { should be_public }
43
+
44
+ its(:name) { should eql 'creationtest' }
45
+ its(:id) { should_not be_empty }
46
+ its(:type) { should eql 'public' }
47
+ end
48
+ end
49
+
50
+ context '.find' do
51
+ context 'when successful' do
52
+ use_vcr_cassette 'list/find/success'
53
+ let(:list) { Contactology::List.create :name => 'find-success' }
54
+ subject { Contactology::List.find list.id }
55
+ after(:each) { list.destroy }
56
+
57
+ it { should be_kind_of Contactology::List }
58
+ it { should be_public }
59
+ it { should_not be_opt_in }
60
+ its(:id) { should == list.id }
61
+ its(:name) { should == list.name }
62
+ end
63
+
64
+ context 'for an unknown list' do
65
+ use_vcr_cassette 'list/find/unknown'
66
+ subject { Contactology::List.find '123456789' }
67
+
68
+ it { should be_nil }
69
+ end
70
+ end
71
+
72
+ context '#destroy' do
73
+ use_vcr_cassette 'list/destroy'
74
+ let(:list) { Contactology::List.create :name => 'destroy-list' }
75
+ subject { list.destroy }
76
+
77
+ it 'removes the list from Contactology' do
78
+ expect { list.destroy }.to change { Contactology::List.find list.id }.to(nil)
79
+ end
80
+
81
+ it { should be_true }
82
+ end
83
+
84
+ context '#import' do
85
+ context 'when successful' do
86
+ use_vcr_cassette 'list/import/success'
87
+ let(:list) { Contactology::List.create :name => 'import-test' }
88
+ let(:email) { 'import@example.com' }
89
+ let(:contact) { Contactology::Contact.find email }
90
+ after(:each) { list.destroy; contact.destroy if contact }
91
+
92
+ subject { list.import [{'email' => email}] }
93
+
94
+ it { should be_true }
95
+ end
96
+
97
+ context 'with an failure' do
98
+ end
99
+ end
100
+
101
+ context '#subscribe' do
102
+ context 'when successful' do
103
+ use_vcr_cassette 'list/subscribe/success'
104
+ let(:contact) { Contactology::Contact.create :email => 'successful-subscribe@example.com' }
105
+ let(:list) { Contactology::List.create :name => 'subscribe-test' }
106
+ after(:each) { contact.destroy; list.destroy }
107
+ subject { list.subscribe contact.email }
108
+
109
+ it { should be_true }
110
+ end
111
+ end
112
+
113
+ context '#unsubscribe' do
114
+ context 'when successful' do
115
+ use_vcr_cassette 'list/unsubscribe/success'
116
+ let(:contact) { Contactology::Contact.create :email => 'successful-unsubscribe@example.com' }
117
+ let(:list) { Contactology::List.create :name => 'unsubscribe-test' }
118
+ before(:each) { list.subscribe contact.email }
119
+ after(:each) { contact.destroy; list.destroy }
120
+ subject { list.unsubscribe contact.email }
121
+
122
+ it { should be_true }
123
+ end
124
+ end
125
+ end