sendgrid_toolkit 0.1.0

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/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg/
2
+ doc/
data/README.rdoc ADDED
@@ -0,0 +1,101 @@
1
+ = SendgridToolkit - a Ruby wrapper for the Sendgrid Web API
2
+
3
+ The Statistics and Unsubscribe APIs are fully supported.
4
+
5
+ This allows you to:
6
+ * manage your unsubscribed users
7
+ * view API-supported email statistics.
8
+
9
+ Support for Sendgrid's other Web APIs is coming soon.
10
+
11
+ SendgridToolkit provides one class for each Web API module.
12
+
13
+ === A note about authentication
14
+ Each class is initialized with +api_user+ and +api_key+ parameters. +api_user+ is your sendgrid account email address, and +api_key+ is your sendgrid password.
15
+
16
+ If you don't supply +api_user+ or +api_key+, SendgridToolkit will look for the SMTP_USERNAME or SMTP_PASSWORD environment variables. If they are not found, SendgridToolkit will throw +NoAPIKeySpecified+ or +NoAPIUserSpecified+, depending on what you omitted.
17
+
18
+ == Unsubscribes Class
19
+
20
+ Instantiate the Unsubscribes object:
21
+ unsubscribes = SendgridToolkit::Unsubscribes.new(api_user, api_key)
22
+
23
+ === Listing Unsubscribes
24
+ You can see everybody who has unsubscribed from your emails with:
25
+ listing = unsubscribes.retrieve
26
+ +listing+ will be an array of hashes, each of which has an +email+ key.
27
+
28
+ If you want the timestamp for when each user unsubscribed, use:
29
+ listing = unsubscribes.retrieve_with_timestamps
30
+ Each hash in +listing+ will now contain a +created+ key, which holds a Ruby Time object.
31
+
32
+ === Adding Unsubscribes
33
+ To manually unsubscribe a user from your sendgrid emails, use:
34
+ unsubscribes.add :email => "email@to.unsubscribe"
35
+
36
+ SendgridToolkit will throw +UnsubscribeEmailAlreadyExists+ if the email you specified is already on the list
37
+
38
+ === Deleting Unsubscribes
39
+ To remove a user from your unsubscribe list, use:
40
+ unsubscribes.delete :email => "email@that_is.unsubscribed"
41
+
42
+ SendgridToolkit will throw +UnsubscribeEmailDoesNotExist+ if the email you specified is not on the list
43
+
44
+ == Statistics Class
45
+
46
+ Instantiate the Statistics object:
47
+ statistics = SendgridToolkit::Statistics.new(api_user, api_key)
48
+
49
+ === Retrieve statistics
50
+ To retrieve statistics, use:
51
+ stats = statistics.retrieve
52
+ +stats+ will be an array of hashes, each of which contains the following keys:
53
+ * +date+: The date to which the statistics in this hash refer to
54
+ * +requests+: The number of emails you sent
55
+ * +bounces+: The number of users who opened your email but did not click on your links
56
+ * +clicks+: The number of users who clicked on a link in your email
57
+ * +opens+: The number of users who opened your email
58
+ * +spamreports+: The number of users who have reported your emails as spam
59
+ +stats+ may also contain some keys that Sendgrid does not officially document, such as: +delivered+, +invalid_email+, +repeat_bounces+, +repeat_spamreports+, +repeat_unsubscribes+ and +unsubscribes+
60
+
61
+ To narrow your retrieval to the past 5 days, you would use:
62
+ stats = statistics.retrieve :days => 5
63
+
64
+ To narrow your retrieval to emails within the last month but before one week ago, you would use:
65
+ stats = statistics.retrieve :start_date => 1.month.ago, :end_date => 1.week.ago
66
+
67
+ To narrow your search to a particular category (applicable only if you use this Sendgrid feature), you would use:
68
+ stats = statistics.retrieve :category => "NameOfYourCategory"
69
+ Note: You may combine a category query with other options, i.e.:
70
+ stats = statistics.retrieve :category => "NameOfYourCategory", :days => 5
71
+
72
+ === Retrieve aggregate (all-time) statistics
73
+ To receive your all-time statistics, use:
74
+ stats = statistics.retrieve_aggregate
75
+ +stats+ will be a single hash containing all of the aforementioned keys except +date+.
76
+
77
+ === List your categories
78
+ If you use Sendgrid's category feature, you can get a listing of your categories with:
79
+ cats = statistics.list_categories
80
+ +cats+ is an array of hashes, each of which has a +category+ key that holds the name of a category.
81
+
82
+ = Behind the Scenes
83
+ Things you may want to know:
84
+
85
+ 1. API requests are made and responses are received in JSON.
86
+
87
+ 2. All requests are made as POSTs unless noted otherwise (Sendgrid's examples are via GET, but they support POST)
88
+
89
+ 3. Each class takes a final options parameter in the form of a hash. You may use this parameter to pass additional options to the Sendgrid API. For example, let's say you are using the unsubscribes function:
90
+ unsubscribes = SendgridToolkit::Unsubscribes.new(api_user, api_key)
91
+ listing = unsubscribes.retrieve
92
+ If Sendgrid were to add a +only_verified+ option to this API call, you could call:
93
+ listing = unsubscribes.retrieve :only_verified => true
94
+ to make use of it.
95
+
96
+ = Testing
97
+ In addition to unit tests, SendgridToolkit comes with a suite of "webconnect" tests that will actually hit Sendgrid's servers and perform various actions for purposes of real-world testing. In order to use these tests, you must:
98
+ 1. Create a test account with sendgrid and store the credentials in TEST_SMTP_USERNAME and TEST_SMTP_PASSWORD environment variables. This is so that actions are performed on a test account and not your real sendgrid account. If you forget, don't worry -- the tests will fail but they will not fall back on the account that uses SMTP_USERNAME and SMTP_PASSWORD.
99
+ 2. Change "xit" it "it" on the tests you wish to run.
100
+
101
+ Running "spec spec" out of the box will run the standard suite of tests (all network access is stubbed out).
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gemspec|
7
+ gemspec.name = "sendgrid_toolkit"
8
+ gemspec.summary = "A Ruby wrapper and utility library for communicating with the Sendgrid API"
9
+ gemspec.description = "A Ruby wrapper and utility library for communicating with the Sendgrid API"
10
+ gemspec.email = "robby@freerobby.com"
11
+ gemspec.homepage = "http://github.com/freerobby/sendgrid_toolkit"
12
+ gemspec.authors = ["Robby Grossman"]
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler must be installed. Use 'sudo gem install jeweler'."
17
+ end
18
+
19
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each {|ext| load ext}
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,22 @@
1
+ module SendgridToolkit
2
+ class AbstractSendgridClient
3
+
4
+ def initialize(api_user = nil, api_key = nil)
5
+ @api_user = (api_user.nil?) ? ENV['SMTP_USERNAME'] : api_user
6
+ @api_key = (api_key.nil?) ? ENV['SMTP_PASSWORD'] : api_key
7
+
8
+ raise SendgridToolkit::NoAPIUserSpecified if @api_user.blank?
9
+ raise SendgridToolkit::NoAPIKeySpecified if @api_key.blank?
10
+ end
11
+
12
+ protected
13
+
14
+ def api_post(module_name, action_name, opts = {})
15
+ HTTParty.post("https://#{BASE_URI}/#{module_name}.#{action_name}.json?", :query => get_credentials.merge(opts), :format => :json)
16
+ end
17
+
18
+ def get_credentials
19
+ {:api_user => @api_user, :api_key => @api_key}
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ module SendgridToolkit
2
+ class NoAPIKeySpecified < StandardError; end
3
+ class NoAPIUserSpecified < StandardError; end
4
+ class UnsubscribeEmailAlreadyExists < StandardError; end
5
+ class UnsubscribeEmailDoesNotExist < StandardError; end
6
+ end
@@ -0,0 +1,23 @@
1
+ module SendgridToolkit
2
+ class Statistics < AbstractSendgridClient
3
+ def retrieve(options = {})
4
+ response = api_post('stats', 'get', options)
5
+ response
6
+ end
7
+
8
+ def retrieve_aggregate(options = {})
9
+ options.merge! :aggregate => 1
10
+ response = retrieve options
11
+ %w(bounces clicks delivered invalid_email opens repeat_bounces repeat_spamreports repeat_unsubscribes requests spamreports unsubscribes).each do |int_field|
12
+ response[int_field] = response[int_field].to_i if response.has_key?(int_field)
13
+ end
14
+ response
15
+ end
16
+
17
+ def list_categories(options = {})
18
+ options.merge! :list => true
19
+ response = retrieve options
20
+ response
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ module SendgridToolkit
2
+ class Unsubscribes < AbstractSendgridClient
3
+ def add(options = {})
4
+ response = api_post('unsubscribes', 'add', options)
5
+ raise UnsubscribeEmailAlreadyExists if response['message'].include?('already exists')
6
+ response
7
+ end
8
+
9
+ def delete(options = {})
10
+ response = api_post('unsubscribes', 'delete', options)
11
+ raise UnsubscribeEmailDoesNotExist if response['message'].include?('does not exist')
12
+ response
13
+ end
14
+
15
+ def retrieve(options = {})
16
+ response = api_post('unsubscribes', 'get', options)
17
+ response
18
+ end
19
+
20
+ def retrieve_with_timestamps(options = {})
21
+ options.merge! :date => 1
22
+ response = retrieve options
23
+ response.each do |unsubscribe|
24
+ unsubscribe['created'] = Time.parse(unsubscribe['created']) if unsubscribe.has_key?('created')
25
+ end
26
+ response
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ require 'httparty'
2
+
3
+ require 'sendgrid_toolkit/abstract_sendgrid_client'
4
+ require 'sendgrid_toolkit/sendgrid_error'
5
+ require 'sendgrid_toolkit/statistics'
6
+ require 'sendgrid_toolkit/unsubscribes'
7
+
8
+ module SendgridToolkit
9
+ BASE_URI = "sendgrid.com/api"
10
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'fakeweb'
2
+ require 'sendgrid_toolkit'
3
+ require 'spec'
4
+
5
+ FakeWeb.allow_net_connect = false
6
+
7
+ def backup_env
8
+ @env_backup = Hash.new
9
+ ENV.keys.each {|key| @env_backup[key] = ENV[key]}
10
+ end
11
+ def restore_env
12
+ @env_backup.keys.each {|key| ENV[key] = @env_backup[key]}
13
+ end
@@ -0,0 +1,45 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../helper")
2
+
3
+ describe SendgridToolkit::AbstractSendgridClient do
4
+ before do
5
+ backup_env
6
+ end
7
+ after do
8
+ restore_env
9
+ end
10
+
11
+ describe "#initialize" do
12
+ it "stores api credentials when passed in" do
13
+ ENV['SMTP_USERNAME'] = "env_username"
14
+ ENV['SMTP_PASSWORD'] = "env_apikey"
15
+
16
+ @obj = SendgridToolkit::AbstractSendgridClient.new("username", "apikey")
17
+ @obj.instance_variable_get("@api_user").should == "username"
18
+ @obj.instance_variable_get("@api_key").should == "apikey"
19
+ end
20
+ it "resorts to environment variables when no credentials specified" do
21
+ ENV['SMTP_USERNAME'] = "env_username"
22
+ ENV['SMTP_PASSWORD'] = "env_apikey"
23
+
24
+ @obj = SendgridToolkit::AbstractSendgridClient.new()
25
+ @obj.instance_variable_get("@api_user").should == "env_username"
26
+ @obj.instance_variable_get("@api_key").should == "env_apikey"
27
+ end
28
+ it "throws error when no credentials are found" do
29
+ ENV['SMTP_USERNAME'] = nil
30
+ ENV['SMTP_PASSWORD'] = nil
31
+
32
+ lambda {
33
+ @obj = SendgridToolkit::AbstractSendgridClient.new()
34
+ }.should raise_error SendgridToolkit::NoAPIUserSpecified
35
+
36
+ lambda {
37
+ @obj = SendgridToolkit::AbstractSendgridClient.new(nil, "password")
38
+ }.should raise_error SendgridToolkit::NoAPIUserSpecified
39
+
40
+ lambda {
41
+ @obj = SendgridToolkit::AbstractSendgridClient.new("user", nil)
42
+ }.should raise_error SendgridToolkit::NoAPIKeySpecified
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../helper")
2
+
3
+ describe SendgridToolkit::Statistics do
4
+ before do
5
+ FakeWeb.clean_registry
6
+ @obj = SendgridToolkit::Statistics.new("fakeuser", "fakepass")
7
+ end
8
+
9
+ describe "#retrieve" do
10
+ it "parses daily totals" do
11
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/stats\.get\.json\?|, :body => '[{"date":"2009-06-20","requests":12342,"bounces":12,"clicks":10223,"opens":9992,"spamreports":5},{"date":"2009-06-21","requests":32342,"bounces":10,"clicks":14323,"opens":10995,"spamreports":7},{"date":"2009-06-22","requests":52342,"bounces":11,"clicks":19223,"opens":12992,"spamreports":2}]')
12
+ stats = @obj.retrieve
13
+ stats.each do |stat|
14
+ %w(bounces clicks delivered invalid_email opens repeat_bounces repeat_spamreports repeat_unsubscribes requests spamreports unsubscribes).each do |int|
15
+ stat[int].kind_of?(Integer).should == true if stat.has_key?(int) # We support all fields presently returned, but we are only testing what sendgrid officially documents
16
+ end
17
+ stat['date'].kind_of?(Date).should == true
18
+ end
19
+ end
20
+ end
21
+
22
+ describe "#retrieve_aggregate" do
23
+ it "parses aggregate statistics" do
24
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/stats\.get\.json\?.*aggregate=1|, :body => '{"requests":12342,"bounces":12,"clicks":10223,"opens":9992,"spamreports":5}')
25
+ stats = @obj.retrieve_aggregate
26
+ %w(bounces clicks delivered invalid_email opens repeat_bounces repeat_spamreports repeat_unsubscribes requests spamreports unsubscribes).each do |int|
27
+ stats[int].kind_of?(Integer).should == true if stats.has_key?(int) # We support all fields presently returned, but we are only testing what sendgrid officially documents
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "#list_categories" do
33
+ it "parses categories list" do
34
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/stats\.get\.json\?.*list=true|, :body => '[{"category":"categoryA"},{"category":"categoryB"},{"category":"categoryC"}]')
35
+ cats = @obj.list_categories
36
+ cats[0]['category'].should == 'categoryA'
37
+ cats[1]['category'].should == 'categoryB'
38
+ cats[2]['category'].should == 'categoryC'
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../helper")
2
+
3
+ describe SendgridToolkit::Unsubscribes do
4
+ before do
5
+ FakeWeb.clean_registry
6
+ @obj = SendgridToolkit::Unsubscribes.new("fakeuser", "fakepass")
7
+ end
8
+
9
+ describe "#retrieve" do
10
+ it "returns array of unsubscribed email addresses" do
11
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/unsubscribes\.get\.json\?|, :body => '[{"email":"user@domain.com"},{"email":"user2@domain2.com"},{"email":"user3@domain2.com"}]')
12
+ emails = @obj.retrieve
13
+ emails[0]['email'].should == "user@domain.com"
14
+ emails[1]['email'].should == "user2@domain2.com"
15
+ emails[2]['email'].should == "user3@domain2.com"
16
+ end
17
+ end
18
+ describe "#retrieve_with_timestamps" do
19
+ it "parses timestamps" do
20
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/unsubscribes\.get\.json\?.*date=1|, :body => '[{"email":"user@domain.com","created":"2010-03-03 11:00:00"},{"email":"user2@domain2.com","created":"2010-03-04 21:00:00"},{"email":"user3@domain2.com","created":"2010-03-05 23:00:00"}]')
21
+ emails = @obj.retrieve_with_timestamps
22
+ 0.upto(2) do |index|
23
+ emails[index]['created'].kind_of?(Time).should == true
24
+ end
25
+ emails[0]['created'].asctime.should == "Wed Mar 3 11:00:00 2010"
26
+ emails[1]['created'].asctime.should == "Thu Mar 4 21:00:00 2010"
27
+ emails[2]['created'].asctime.should == "Fri Mar 5 23:00:00 2010"
28
+ end
29
+ end
30
+
31
+ describe "#add" do
32
+ it "raises no errors on success" do
33
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/unsubscribes\.add\.json\?.*email=.+|, :body => '{"message":"success"}')
34
+ lambda {
35
+ @obj.add :email => "user@domain.com"
36
+ }.should_not raise_error
37
+ end
38
+ it "raises error when email already exists" do
39
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/unsubscribes\.add\.json\?.*email=.+|, :body => '{"message":"Unsubscribe email address already exists"}')
40
+ lambda {
41
+ @obj.add :email => "user@domain.com"
42
+ }.should raise_error SendgridToolkit::UnsubscribeEmailAlreadyExists
43
+ end
44
+ end
45
+
46
+ describe "#delete" do
47
+ it "raises no errors on success" do
48
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/unsubscribes\.delete\.json\?.*email=.+|, :body => '{"message":"success"}')
49
+ lambda {
50
+ @obj.delete :email => "user@domain.com"
51
+ }.should_not raise_error
52
+ end
53
+ it "raises error when email address does not exist" do
54
+ FakeWeb.register_uri(:post, %r|https://sendgrid\.com/api/unsubscribes\.delete\.json\?.*email=.+|, :body => '{"message":"Email does not exist"}')
55
+ lambda {
56
+ @obj.delete :email => "user@domain.com"
57
+ }.should raise_error SendgridToolkit::UnsubscribeEmailDoesNotExist
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ describe SendgridToolkit do
4
+ end
@@ -0,0 +1,65 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ FakeWeb.allow_net_connect = true
4
+
5
+ describe SendgridToolkit do
6
+ before :all do
7
+ FakeWeb.clean_registry
8
+ FakeWeb.allow_net_connect = true
9
+ end
10
+ after :all do
11
+ FakeWeb.allow_net_connect = false
12
+ end
13
+
14
+ before do
15
+ backup_env
16
+
17
+ # Don't let SendgridToolkit fall back to SMTP_USERNAME and SMTP_PASSWORD
18
+ ENV['SMTP_USERNAME'] = ENV['TEST_SMTP_USERNAME'].blank? ? "fakeuser" : ENV['TEST_SMTP_USERNAME']
19
+ ENV['SMTP_PASSWORD'] = ENV['TEST_SMTP_PASSWORD'].blank? ? "fakepass" : ENV['TEST_SMTP_PASSWORD']
20
+ end
21
+ after do
22
+ restore_env
23
+ end
24
+
25
+ describe "statistics i/o" do
26
+ before do
27
+ @obj = SendgridToolkit::Statistics.new
28
+ end
29
+ xit "retrieves statistics by day" do
30
+ stats = @obj.retrieve
31
+ stats.count.should > 0
32
+ day_stats = stats.first
33
+ %w(date requests bounces clicks opens spamreports).each do |k|
34
+ day_stats.has_key?(k).should == true
35
+ end
36
+ end
37
+ xit "retrieves aggregate statistics" do
38
+ stats = @obj.retrieve_aggregate
39
+ %w(requests bounces clicks opens spamreports).each do |k|
40
+ stats.has_key?(k).should == true
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "unsubscribes i/o" do
46
+ before do
47
+ @obj = SendgridToolkit::Unsubscribes.new
48
+ unsubscribes = @obj.retrieve
49
+ unsubscribes.each do |u|
50
+ @obj.delete u['email']
51
+ end
52
+ end
53
+ xit "adds, retrieves and deletes email addresses properly" do
54
+ @obj.add "user@domain.com"
55
+ @obj.add "user2@domain.com"
56
+ unsubs = @obj.retrieve_with_timestamps
57
+ emails = unsubs.map {|h| h['email']}
58
+ emails.include?('user@domain.com').should == true
59
+ emails.include?('user2@domain.com').should == true
60
+ @obj.delete 'user@domain.com'
61
+ @obj.delete 'user2@domain.com'
62
+ @obj.retrieve.count.should == 0
63
+ end
64
+ end
65
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sendgrid_toolkit
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Robby Grossman
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-07 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: A Ruby wrapper and utility library for communicating with the Sendgrid API
22
+ email: robby@freerobby.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - .gitignore
31
+ - README.rdoc
32
+ - Rakefile
33
+ - VERSION
34
+ - lib/sendgrid_toolkit.rb
35
+ - lib/sendgrid_toolkit/abstract_sendgrid_client.rb
36
+ - lib/sendgrid_toolkit/sendgrid_error.rb
37
+ - lib/sendgrid_toolkit/statistics.rb
38
+ - lib/sendgrid_toolkit/unsubscribes.rb
39
+ - spec/helper.rb
40
+ - spec/lib/sendgrid_toolkit/abstract_sendgrid_client_spec.rb
41
+ - spec/lib/sendgrid_toolkit/statistics_spec.rb
42
+ - spec/lib/sendgrid_toolkit/unsubscribes_spec.rb
43
+ - spec/lib/sendgrid_toolkit_spec.rb
44
+ - spec/webconnect/sendgrid_toolkit_spec.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/freerobby/sendgrid_toolkit
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.3.6
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: A Ruby wrapper and utility library for communicating with the Sendgrid API
75
+ test_files:
76
+ - spec/helper.rb
77
+ - spec/lib/sendgrid_toolkit/abstract_sendgrid_client_spec.rb
78
+ - spec/lib/sendgrid_toolkit/statistics_spec.rb
79
+ - spec/lib/sendgrid_toolkit/unsubscribes_spec.rb
80
+ - spec/lib/sendgrid_toolkit_spec.rb
81
+ - spec/webconnect/sendgrid_toolkit_spec.rb