createsend 2.5.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
- - 1.9.2
3
+ - 2.0.0
5
4
  - 1.9.3
5
+ - 1.9.2
6
+ - 1.8.7
6
7
  - ree
data/Gemfile.lock CHANGED
@@ -1,37 +1,40 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- createsend (2.5.1)
5
- hashie (~> 1.0)
4
+ createsend (3.0.0)
5
+ hashie (>= 1.2, < 3)
6
6
  httparty (~> 0.10)
7
7
  json
8
8
 
9
9
  GEM
10
10
  remote: http://rubygems.org/
11
11
  specs:
12
- activesupport (3.2.8)
12
+ activesupport (3.2.11)
13
13
  i18n (~> 0.6)
14
14
  multi_json (~> 1.0)
15
+ bourne (1.1.2)
16
+ mocha (= 0.10.5)
15
17
  fakeweb (1.3.0)
16
- hashie (1.2.0)
18
+ hashie (2.0.3)
17
19
  httparty (0.10.2)
18
20
  multi_json (~> 1.0)
19
21
  multi_xml (>= 0.5.2)
20
22
  i18n (0.6.1)
21
23
  jnunemaker-matchy (0.4.0)
22
- json (1.7.6)
24
+ json (1.7.7)
23
25
  metaclass (0.0.1)
24
- mocha (0.13.2)
26
+ mocha (0.10.5)
25
27
  metaclass (~> 0.0.1)
26
- multi_json (1.0.3)
27
- multi_xml (0.5.2)
28
+ multi_json (1.5.0)
29
+ multi_xml (0.5.3)
28
30
  rake (10.0.3)
29
- shoulda (3.3.0)
30
- shoulda-context (~> 1.0)
31
- shoulda-matchers (~> 1.4)
32
- shoulda-context (1.0.0)
33
- shoulda-matchers (1.4.0)
31
+ shoulda (3.3.2)
32
+ shoulda-context (~> 1.0.1)
33
+ shoulda-matchers (~> 1.4.1)
34
+ shoulda-context (1.0.2)
35
+ shoulda-matchers (1.4.2)
34
36
  activesupport (>= 3.0.0)
37
+ bourne (~> 1.1.2)
35
38
 
36
39
  PLATFORMS
37
40
  ruby
@@ -40,6 +43,5 @@ DEPENDENCIES
40
43
  createsend!
41
44
  fakeweb (~> 1.3)
42
45
  jnunemaker-matchy (~> 0.4)
43
- mocha (~> 0.13)
44
46
  rake (~> 10.0)
45
47
  shoulda (~> 3.3)
data/HISTORY.md CHANGED
@@ -1,13 +1,66 @@
1
1
  # createsend-ruby history
2
2
 
3
+ ## v3.0.0 - 25 Mar, 2013 (129ad0e)
4
+
5
+ * Added support for authenticating using OAuth. See the [README](README.md#authenticating) for full usage instructions.
6
+ * Refactored authentication so that it is _always_ done at the instance level. This introduces some breaking changes, which are clearly explained below.
7
+ * Authentication is now _always_ done at the instance level.
8
+
9
+ So when you _previously_ would have authenticated using an API key as follows:
10
+
11
+ ```ruby
12
+ CreateSend.api_key "your api key"
13
+ cs = CreateSend::CreateSend.new
14
+ cs.clients
15
+ ```
16
+
17
+ If you want to authenticate using an API key, you should _now_ do this:
18
+
19
+ ```ruby
20
+ auth = {:api_key => "your api key"}
21
+ cs = CreateSend::CreateSend.new auth
22
+ cs.clients
23
+ ```
24
+
25
+ * Instances of `CreateSend::CreateSend` (and all subclasses) are now _always_ created by passing an `auth` hash as the first argument.
26
+
27
+ So for example, when you _previously_ would have called `CreateSend::Client.new` like so:
28
+
29
+ ```ruby
30
+ CreateSend.api_key "your api key"
31
+ cl = CreateSend::Client.new "your client id"
32
+ ```
33
+
34
+ You _now_ call `CreateSend::Client.new` like so:
35
+
36
+ ```ruby
37
+ auth = {:api_key => "your api key"}
38
+ cl = CreateSend::Client.new auth, "your client id"
39
+ ```
40
+
41
+ * Any of the class methods on `CreateSend::CreateSend` (and all subclasses) are now _always_ called by passing an `auth` hash as the first argument.
42
+
43
+ So for example, when you _previously_ would have called `CreateSend::Subscriber.add` like so:
44
+
45
+ ```ruby
46
+ CreateSend.api_key "your api key"
47
+ sub = CreateSend::Subscriber.add "list id", "dave@example.com", "Dave", [], true
48
+ ```
49
+
50
+ You _now_ call `CreateSend::Subscriber.add` like so:
51
+
52
+ ```ruby
53
+ auth = {:api_key => "your api key"}
54
+ sub = CreateSend::Subscriber.add auth, "list id", "dave@example.com", "Dave", [], true
55
+ ```
56
+
3
57
  ## v2.5.1 - 4 Feb, 2013 (f0a35ae)
4
58
 
5
59
  * Updated dependencies in gemspec. Removed cane as a development dependency.
6
60
 
7
61
  ## v2.5.0 - 11 Dec, 2012 (3054885)
8
62
 
9
- * Added support for including from name, from email, and reply to email in
10
- drafts, scheduled, and sent campaigns.
63
+ * Added support for including from name, from email, and reply to email in drafts, scheduled, and sent campaigns.
11
64
  * Added support for campaign text version urls.
12
65
  * Added support for transferring credits to/from a client.
13
66
  * Added support for getting account billing details as well as client credits.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010-2012 James Dennes
1
+ Copyright (c) 2010-2013 James Dennes
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # createsend
2
2
  [![Build Status](https://secure.travis-ci.org/campaignmonitor/createsend-ruby.png)][travis] [![Dependency Status](https://gemnasium.com/campaignmonitor/createsend-ruby.png)][gemnasium] [![Gem Version](https://badge.fury.io/rb/createsend.png)][gembadge]
3
3
 
4
- A ruby library which implements the complete functionality of v3 of the [Campaign Monitor API](http://www.campaignmonitor.com/api/).
4
+ A ruby library which implements the complete functionality of the [Campaign Monitor API](http://www.campaignmonitor.com/api/).
5
5
 
6
6
  [travis]: http://travis-ci.org/campaignmonitor/createsend-ruby
7
7
  [gemnasium]: https://gemnasium.com/campaignmonitor/createsend-ruby
@@ -9,73 +9,191 @@ A ruby library which implements the complete functionality of v3 of the [Campaig
9
9
 
10
10
  ## Installation
11
11
 
12
- gem install createsend
12
+ To install:
13
13
 
14
- ## Documentation
14
+ ```
15
+ gem install createsend
16
+ ```
15
17
 
16
- Full documentation is hosted by [RubyDoc.info](http://rubydoc.info/gems/createsend/frames).
18
+ Or, add a dependency to your `Gemfile` then `bundle install`.
17
19
 
18
- ## Examples
20
+ ```ruby
21
+ gem 'createsend'
22
+ ```
19
23
 
20
- ### Basic usage
21
- This example of listing all your clients demonstrates basic usage of the library:
24
+ ## Authenticating
22
25
 
23
- require 'createsend'
26
+ The Campaign Monitor API supports authentication using either OAuth or an API key.
24
27
 
25
- CreateSend.api_key 'your_api_key'
28
+ ### Using OAuth
26
29
 
27
- cs = CreateSend::CreateSend.new
28
- clients = cs.clients
29
-
30
- clients.each do |c|
31
- puts "#{c.ClientID}: #{c.Name}"
32
- end
30
+ If you're developing a Rails or Rack based application, we recommend using [omniauth-createsend](https://github.com/jdennes/omniauth-createsend) to authenticate with the Campaign Monitor API. You might find this [example application](https://github.com/jdennes/createsendoauthtest) helpful.
33
31
 
34
- Running this example will result in something like:
32
+ If you don't use [omniauth-createsend](https://github.com/jdennes/omniauth-createsend), you'll need to get access tokens for your users by following the instructions included in the Campaign Monitor API [documentation](http://www.campaignmonitor.com/api/getting-started/#authenticating_with_oauth). This gem provides functionality to help you do this, as described below. There's also another [example application](https://gist.github.com/jdennes/4945412) you may wish to reference, which doesn't depend on any OAuth libraries.
33
+
34
+ The first thing your application should do is redirect your user to the Campaign Monitor authorization URL where they will have the opportunity to approve your application to access their Campaign Monitor account. You can get this authorization URL by using `CreateSend::CreateSend.authorize_url`, like so:
35
+
36
+ ```ruby
37
+ require 'createsend'
38
+
39
+ authorize_url = CreateSend::CreateSend.authorize_url(
40
+ 'Client ID for your application',
41
+ 'Redirect URI for your application',
42
+ 'The permission level your application requires',
43
+ 'Optional state data to be included'
44
+ )
45
+ # Redirect your users to authorize_url.
46
+ ```
47
+
48
+ If your user approves your application, they will then be redirected to the `redirect_uri` you specified, which will include a `code` parameter, and optionally a `state` parameter in the query string. Your application should implement a handler which can exchange the code passed to it for an access token, using `CreateSend::CreateSend.exchange_token` like so:
49
+
50
+ ```ruby
51
+ require 'createsend'
52
+
53
+ access_token, expires_in, refresh_token = CreateSend::CreateSend.exchange_token(
54
+ 'Client ID for your application',
55
+ 'Client Secret for your application',
56
+ 'Redirect URI for your application',
57
+ 'A unique code for your user' # Get the code parameter from the query string
58
+ )
59
+ # Save access_token, expires_in, and refresh_token.
60
+ ```
61
+
62
+ At this point you have an access token and refresh token for your user which you should store somewhere convenient so that your application can look up these values when your user wants to make future Campaign Monitor API calls.
63
+
64
+ Once you have an access token and refresh token for your user, you can use the `createsend` gem to authenticate and make further API calls like so:
65
+
66
+ ```ruby
67
+ require 'createsend'
35
68
 
36
- 4a397ccaaa55eb4e6aa1221e1e2d7122: Client One
37
- a206def0582eec7dae47d937a4109cb2: Client Two
69
+ auth = {
70
+ :access_token => 'your access token',
71
+ :refresh_token => 'your refresh token'
72
+ }
73
+ cs = CreateSend::CreateSend.new auth
74
+ clients = cs.clients
75
+ ```
38
76
 
39
- ### Handling errors
40
- If the Campaign Monitor API returns an error, an exception will be thrown. For example, if you attempt to create a campaign and enter empty values for subject etc:
77
+ All OAuth tokens have an expiry time, and can be renewed with a corresponding refresh token. If your access token expires when attempting to make an API call, the `CreateSend::ExpiredOAuthToken` exception will be raised, so your code should handle this. Here's an example of how you could do this:
41
78
 
42
- require 'createsend'
79
+ ```ruby
80
+ require 'createsend'
43
81
 
44
- CreateSend.api_key 'your_api_key'
82
+ auth = {
83
+ :access_token => 'your access token',
84
+ :refresh_token => 'your refresh token'
85
+ }
86
+ cs = CreateSend::CreateSend.new auth
45
87
 
46
- begin
47
- cl = CreateSend::Client.new "4a397ccaaa55eb4e6aa1221e1e2d7122"
48
- id = CreateSend::Campaign.create cl.client_id, "", "", "", "", "", "", "", [], []
49
- puts "New campaign ID: #{id}"
50
- rescue CreateSend::BadRequest => br
51
- puts "Bad request error: #{br}"
52
- puts "Error Code: #{br.data.Code}"
53
- puts "Error Message: #{br.data.Message}"
54
- rescue Exception => e
55
- puts "Error: #{e}"
56
- end
88
+ begin
89
+ tries ||= 2
90
+ clients = cs.clients
91
+ rescue CreateSend::ExpiredOAuthToken => eot
92
+ access_token, expires_in, refresh_token = cs.refresh_token
93
+ # Save your updated access token, expire in value, and refresh token
94
+ retry unless (tries -= 1).zero?
95
+ p "Error: #{eot}"
96
+ rescue Exception => e
97
+ p "Error: #{e}"
98
+ end
99
+ ```
57
100
 
58
- Results in:
101
+ ### Using an API key
59
102
 
60
- Bad request error: The CreateSend API responded with the following error - 304: Campaign Subject Required
61
- Error Code: 304
62
- Error Message: Campaign Subject Required
103
+ ```ruby
104
+ require 'createsend'
105
+
106
+ cs = CreateSend::CreateSend.new {:api_key => 'your api key'}
107
+ clients = cs.clients
108
+ ```
109
+
110
+ ## Basic usage
111
+ This example of listing all your clients and their campaigns demonstrates basic usage of the library and the data returned from the API:
112
+
113
+ ```ruby
114
+ require 'createsend'
115
+
116
+ auth = {
117
+ :access_token => 'your access token',
118
+ :refresh_token => 'your refresh token'
119
+ }
120
+ cs = CreateSend::CreateSend.new auth
121
+
122
+ clients = cs.clients
123
+ clients.each do |cl|
124
+ p "Client: #{cl.Name}"
125
+ client = CreateSend::Client.new auth, cl.ClientID
126
+ p "- Campaigns:"
127
+ client.campaigns.each do |cm|
128
+ p " - #{cm.Subject}"
129
+ end
130
+ end
131
+ ```
132
+
133
+ Running this example will result in something like:
134
+
135
+ ```
136
+ Client: First Client
137
+ - Campaigns:
138
+ - Newsletter Number One
139
+ - Newsletter Number Two
140
+ Client: Second Client
141
+ - Campaigns:
142
+ - News for January 2013
143
+ ```
144
+
145
+ ## Handling errors
146
+ If the Campaign Monitor API returns an error, an exception will be raised. For example, if you attempt to create a campaign and enter empty values for subject and other required fields:
147
+
148
+ ```ruby
149
+ require 'createsend'
150
+
151
+ auth = {
152
+ :access_token => 'your access token',
153
+ :refresh_token => 'your refresh token'
154
+ }
155
+
156
+ begin
157
+ id = CreateSend::Campaign.create auth, "4a397ccaaa55eb4e6aa1221e1e2d7122", "", "", "", "", "", "", "", [], []
158
+ p "New campaign ID: #{id}"
159
+ rescue CreateSend::BadRequest => br
160
+ p "Bad request error: #{br}"
161
+ p "Error Code: #{br.data.Code}"
162
+ p "Error Message: #{br.data.Message}"
163
+ rescue Exception => e
164
+ p "Error: #{e}"
165
+ end
166
+ ```
167
+
168
+ Running this example will result in:
169
+
170
+ ```
171
+ Bad request error: The CreateSend API responded with the following error - 304: Campaign Subject Required
172
+ Error Code: 304
173
+ Error Message: Campaign Subject Required
174
+ ```
63
175
 
64
176
  ### Expected input and output
65
177
  The best way of finding out the expected input and output of a particular method in a particular class is to use the unit tests as a reference.
66
178
 
67
179
  For example, if you wanted to find out how to call the CreateSend::Subscriber.add method, you would look at the file test/subscriber_test.rb
68
180
 
69
- should "add a subscriber with custom fields" do
70
- stub_post(@api_key, "subscribers/#{@list_id}.json", "add_subscriber.json")
71
- custom_fields = [ { :Key => 'website', :Value => 'http://example.com/' } ]
72
- email_address = CreateSend::Subscriber.add @list_id, "subscriber@example.com", "Subscriber", custom_fields, true
73
- email_address.should == "subscriber@example.com"
74
- end
181
+ ```ruby
182
+ should "add a subscriber with custom fields" do
183
+ stub_post(@auth, "subscribers/#{@list_id}.json", "add_subscriber.json")
184
+ custom_fields = [ { :Key => 'website', :Value => 'http://example.com/' } ]
185
+ email_address = CreateSend::Subscriber.add @list_id, "subscriber@example.com", "Subscriber", custom_fields, true
186
+ email_address.should == "subscriber@example.com"
187
+ end
188
+ ```
189
+
190
+ ## Documentation
191
+
192
+ Full documentation is hosted by [RubyDoc.info](http://rubydoc.info/gems/createsend/frames).
75
193
 
76
194
  ## Contributing
77
195
  1. Fork the repository
78
196
  2. Make your changes, including tests for your changes.
79
- 3. Ensure that the build passes, by running `bundle exec rake` (CI runs on: `1.8.7`, `1.9.2`, `1.9.3`, and `ree`)
197
+ 3. Ensure that the build passes, by running `bundle exec rake` (CI runs on: `2.0.0`, `1.9.3`, `1.9.2`, `1.8.7`, and `ree`)
80
198
  4. It should go without saying, but do not increment the version number in your commits.
81
199
  5. Submit a pull request.
data/createsend.gemspec CHANGED
@@ -4,14 +4,13 @@ require 'bundler/version'
4
4
  require File.expand_path('lib/createsend/version')
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.add_development_dependency('rake', '~> 10.0')
8
- s.add_development_dependency('fakeweb', '~> 1.3')
9
- s.add_development_dependency('jnunemaker-matchy', '~> 0.4')
10
- s.add_development_dependency('mocha', '~> 0.13')
11
- s.add_development_dependency('shoulda', '~> 3.3')
12
- s.add_runtime_dependency('json', '>= 0')
13
- s.add_runtime_dependency('hashie', '~> 1.0')
14
- s.add_runtime_dependency('httparty', '~> 0.10')
7
+ s.add_development_dependency 'rake', '~> 10.0'
8
+ s.add_development_dependency 'fakeweb', '~> 1.3'
9
+ s.add_development_dependency 'jnunemaker-matchy', '~> 0.4'
10
+ s.add_development_dependency 'shoulda', '~> 3.3'
11
+ s.add_runtime_dependency 'json', '>= 0'
12
+ s.add_runtime_dependency 'hashie', ['>= 1.2', '< 3']
13
+ s.add_runtime_dependency 'httparty', '~> 0.10'
15
14
  s.name = "createsend"
16
15
  s.author = "James Dennes"
17
16
  s.description = %q{Implements the complete functionality of the Campaign Monitor API.}
data/lib/createsend.rb CHANGED
@@ -1,12 +1,8 @@
1
- require 'cgi'
2
- require 'uri'
3
- require 'httparty'
4
- require 'hashie'
5
-
6
1
  libdir = File.dirname(__FILE__)
7
2
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
8
3
 
9
4
  require 'createsend/version'
5
+ require 'createsend/createsend'
10
6
  require 'createsend/client'
11
7
  require 'createsend/campaign'
12
8
  require 'createsend/list'
@@ -15,154 +11,3 @@ require 'createsend/subscriber'
15
11
  require 'createsend/template'
16
12
  require 'createsend/person'
17
13
  require 'createsend/administrator'
18
-
19
- module CreateSend
20
-
21
- # Just allows callers to do CreateSend.api_key "..." rather than
22
- # CreateSend::CreateSend.api_key "..." etc
23
- class << self
24
- def api_key(api_key=nil)
25
- r = CreateSend.api_key api_key
26
- end
27
-
28
- def base_uri(uri)
29
- r = CreateSend.base_uri uri
30
- end
31
- end
32
-
33
- # Represents a CreateSend API error. Contains specific data about the error.
34
- class CreateSendError < StandardError
35
- attr_reader :data
36
- def initialize(data)
37
- @data = data
38
- # @data should contain Code, Message and optionally ResultData
39
- extra = @data.ResultData ? "\nExtra result data: #{@data.ResultData}" : ""
40
- super "The CreateSend API responded with the following error"\
41
- " - #{@data.Code}: #{@data.Message}#{extra}"
42
- end
43
- end
44
-
45
- # Raised for HTTP response codes of 400...500
46
- class ClientError < StandardError; end
47
- # Raised for HTTP response codes of 500...600
48
- class ServerError < StandardError; end
49
- # Raised for HTTP response code of 400
50
- class BadRequest < CreateSendError; end
51
- # Raised for HTTP response code of 401
52
- class Unauthorized < CreateSendError; end
53
- # Raised for HTTP response code of 404
54
- class NotFound < ClientError; end
55
-
56
- # Provides high level CreateSend functionality/data you'll probably need.
57
- class CreateSend
58
- include HTTParty
59
-
60
- # Deals with an unfortunate situation where responses aren't valid json.
61
- class Parser::DealWithCreateSendInvalidJson < HTTParty::Parser
62
- # The createsend API returns an ID as a string when a 201 Created
63
- # response is returned. Unfortunately this is invalid json.
64
- def parse
65
- begin
66
- super
67
- rescue MultiJson::DecodeError => e
68
- body[1..-2] # Strip surrounding quotes and return as is.
69
- end
70
- end
71
- end
72
- parser Parser::DealWithCreateSendInvalidJson
73
- @@base_uri = "https://api.createsend.com/api/v3"
74
- @@api_key = ""
75
- headers({
76
- 'User-Agent' => "createsend-ruby-#{VERSION}",
77
- 'Content-Type' => 'application/json; charset=utf-8',
78
- 'Accept-Encoding' => 'gzip, deflate' })
79
- base_uri @@base_uri
80
- basic_auth @@api_key, 'x'
81
-
82
- # Sets the API key which will be used to make calls to the CreateSend API.
83
- def self.api_key(api_key=nil)
84
- return @@api_key unless api_key
85
- @@api_key = api_key
86
- basic_auth @@api_key, 'x'
87
- end
88
-
89
- # Gets your CreateSend API key, given your site url, username and password.
90
- def apikey(site_url, username, password)
91
- site_url = CGI.escape(site_url)
92
- self.class.basic_auth username, password
93
- response = CreateSend.get("/apikey.json?SiteUrl=#{site_url}")
94
- # Revert basic_auth to use @@api_key, 'x'
95
- self.class.basic_auth @@api_key, 'x'
96
- Hashie::Mash.new(response)
97
- end
98
-
99
- # Gets your clients.
100
- def clients
101
- response = CreateSend.get('/clients.json')
102
- response.map{|item| Hashie::Mash.new(item)}
103
- end
104
-
105
- # Get your billing details.
106
- def billing_details
107
- response = CreateSend.get('/billingdetails.json')
108
- Hashie::Mash.new(response)
109
- end
110
-
111
- # Gets valid countries.
112
- def countries
113
- response = CreateSend.get('/countries.json')
114
- response.parsed_response
115
- end
116
-
117
- # Gets the current date in your account's timezone.
118
- def systemdate
119
- response = CreateSend.get('/systemdate.json')
120
- Hashie::Mash.new(response)
121
- end
122
-
123
- # Gets valid timezones.
124
- def timezones
125
- response = CreateSend.get('/timezones.json')
126
- response.parsed_response
127
- end
128
-
129
- # Gets the administrators
130
- def administrators
131
- response = CreateSend.get('/admins.json')
132
- response.map{|item| Hashie::Mash.new(item)}
133
- end
134
-
135
- def get_primary_contact
136
- response = CreateSend.get('/primarycontact.json')
137
- Hashie::Mash.new(response)
138
- end
139
-
140
- def set_primary_contact(email)
141
- options = { :query => { :email => email } }
142
- response = CreateSend.put("/primarycontact.json", options)
143
- Hashie::Mash.new(response)
144
- end
145
-
146
- def self.get(*args); handle_response super end
147
- def self.post(*args); handle_response super end
148
- def self.put(*args); handle_response super end
149
- def self.delete(*args); handle_response super end
150
-
151
- def self.handle_response(response) # :nodoc:
152
- case response.code
153
- when 400
154
- raise BadRequest.new(Hashie::Mash.new response)
155
- when 401
156
- raise Unauthorized.new(Hashie::Mash.new response)
157
- when 404
158
- raise NotFound.new
159
- when 400...500
160
- raise ClientError.new
161
- when 500...600
162
- raise ServerError.new
163
- else
164
- response
165
- end
166
- end
167
- end
168
- end