gibbon 0.4.6 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of gibbon might be problematic. Click here for more details.

@@ -0,0 +1,83 @@
1
+ require 'gibbon/api'
2
+
3
+ module Gibbon
4
+ class APICategory
5
+ include HTTParty
6
+ format :plain
7
+ default_timeout 30
8
+
9
+ attr_accessor :category_name, :api_key, :api_endpoint, :timeout, :throws_exceptions, :default_params
10
+
11
+ def initialize(category_name, api_key, timeout, throws_exceptions, api_endpoint, default_params)
12
+ @category_name = category_name
13
+ @api_key = api_key
14
+ @api_endpoint = api_endpoint
15
+ @default_params = default_params
16
+ @throws_exceptions = throws_exceptions
17
+ @timeout = timeout
18
+
19
+ set_instance_defaults
20
+ end
21
+
22
+ def call(method, params = {})
23
+ api_url = base_api_url + method
24
+ params = @default_params.merge(params).merge({apikey: @api_key})
25
+ response = self.class.post(api_url, body: MultiJson.dump(params), timeout: @timeout)
26
+
27
+ parsed_response = nil
28
+
29
+ if (response.body)
30
+ parsed_response = MultiJson.load(response.body)
31
+
32
+ if should_raise_for_response?(parsed_response)
33
+ error = MailChimpError.new("MailChimp API Error: #{parsed_response["error"]} (code #{parsed_response["code"]})")
34
+ error.code = parsed_response["code"]
35
+ raise error
36
+ end
37
+ end
38
+
39
+ parsed_response
40
+ end
41
+
42
+ def method_missing(method, *args)
43
+ # To support underscores, we replace them with hyphens when calling the API
44
+ method = method.to_s.gsub("_", "-").downcase
45
+ call("#{@category_name}/#{method}", *args)
46
+ end
47
+
48
+ def set_instance_defaults
49
+ @timeout = (API.timeout || 30) if @timeout.nil?
50
+ # Two lines because the class variable could be false and (false || true) is always true
51
+ @throws_exceptions = API.throws_exceptions if @throws_exceptions.nil?
52
+ @throws_exceptions = true if @throws_exceptions.nil?
53
+ end
54
+
55
+ def api_key=(value)
56
+ @api_key = value.strip if value
57
+ end
58
+
59
+ def should_raise_for_response?(response)
60
+ @throws_exceptions && response.is_a?(Hash) && response["error"]
61
+ end
62
+
63
+ def base_api_url
64
+ "#{@api_endpoint || get_api_endpoint}/2.0/"
65
+ end
66
+
67
+ def get_api_endpoint
68
+ "https://#{get_data_center_from_api_key}api.mailchimp.com"
69
+ end
70
+
71
+ def get_data_center_from_api_key
72
+ # Return an empty string for invalid API keys so Gibbon hits the main endpoint
73
+ data_center = ""
74
+
75
+ if (@api_key && @api_key["-"])
76
+ # Add a period since the data_center is a subdomain and it keeps things dry
77
+ data_center = "#{@api_key.split('-').last}."
78
+ end
79
+
80
+ data_center
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,64 @@
1
+ module Gibbon
2
+ class Export < APICategory
3
+
4
+ def initialize(api_key = nil, default_params = {})
5
+ @api_key = api_key
6
+ @default_params = default_params
7
+
8
+ set_instance_defaults
9
+ end
10
+
11
+ protected
12
+
13
+ def export_api_url
14
+ "http://#{get_data_center_from_api_key}api.mailchimp.com/export/1.0/"
15
+ end
16
+
17
+ def call(method, params = {})
18
+ api_url = export_api_url + method + "/"
19
+ params = @default_params.merge(params).merge({apikey: @api_key})
20
+ response = self.class.post(api_url, body: MultiJson.dump(params), timeout: @timeout)
21
+
22
+ lines = response.body.lines
23
+ if @throws_exceptions
24
+ first_line = MultiJson.load(lines.first) if lines.first
25
+
26
+ if should_raise_for_response?(first_line)
27
+ error = MailChimpError.new("MailChimp Export API Error: #{first_line["error"]} (code #{first_line["code"]})")
28
+ error.code = first_line["code"]
29
+ raise error
30
+ end
31
+ end
32
+
33
+ lines
34
+ end
35
+
36
+ def set_instance_defaults
37
+ super
38
+ @api_key = self.class.api_key if @api_key.nil?
39
+ @timeout = self.class.timeout if @timeout.nil?
40
+ end
41
+
42
+ def method_missing(method, *args)
43
+ # To support underscores, we camelize the method name
44
+
45
+ # Thanks for the camelize gsub, Rails
46
+ method = method.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
47
+
48
+ # We need to downcase the first letter of every API method
49
+ # and MailChimp has a few of API methods that end in "AIM," which
50
+ # must be upcased (See "Campaign Report Data Methods" in their API docs).
51
+ method = method[0].chr.downcase + method[1..-1].gsub(/aim$/i, 'AIM')
52
+
53
+ call(method, *args)
54
+ end
55
+
56
+ class << self
57
+ attr_accessor :api_key, :timeout, :throws_exceptions
58
+
59
+ def method_missing(sym, *args, &block)
60
+ new(self.api_key, {timeout: self.timeout, throws_exceptions: self.throws_exceptions}).send(sym, *args, &block)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,5 @@
1
+ module Gibbon
2
+ class MailChimpError < StandardError
3
+ attr_accessor :code
4
+ end
5
+ end
@@ -0,0 +1,247 @@
1
+ require 'spec_helper'
2
+ require 'cgi'
3
+
4
+ describe Gibbon do
5
+
6
+ describe "attributes" do
7
+
8
+ before do
9
+ @api_key = "123-us1"
10
+ end
11
+
12
+ it "have no API by default" do
13
+ @gibbon = Gibbon::API.new
14
+ expect(@gibbon.api_key).to be_nil
15
+ end
16
+
17
+ it "set an API key in constructor" do
18
+ @gibbon = Gibbon::API.new(@api_key)
19
+ expect(@gibbon.api_key).to eq(@api_key)
20
+ end
21
+
22
+ it "set an API key from the 'MAILCHIMP_API_KEY' ENV variable" do
23
+ ENV['MAILCHIMP_API_KEY'] = @api_key
24
+ @gibbon = Gibbon::API.new
25
+ expect(@gibbon.api_key).to eq(@api_key)
26
+ ENV.delete('MAILCHIMP_API_KEY')
27
+ end
28
+
29
+ it "set an API key via setter" do
30
+ @gibbon = Gibbon::API.new
31
+ @gibbon.api_key = @api_key
32
+ expect(@gibbon.api_key).to eq(@api_key)
33
+ end
34
+
35
+ it "set timeout and get" do
36
+ @gibbon = Gibbon::API.new
37
+ timeout = 30
38
+ @gibbon.timeout = timeout
39
+ expect(timeout).to eq(@gibbon.timeout)
40
+ end
41
+
42
+ it "detect api endpoint from initializer parameters" do
43
+ api_endpoint = 'https://us6.api.mailchimp.com'
44
+ @gibbon = Gibbon::API.new(@api_key, :api_endpoint => api_endpoint)
45
+ expect(api_endpoint).to eq(@gibbon.api_endpoint)
46
+ end
47
+ end
48
+
49
+ describe "build api url" do
50
+ before do
51
+ @gibbon = Gibbon::API.new
52
+ @url = "https://api.mailchimp.com/2.0/say/hello"
53
+ end
54
+
55
+ it "handle empty api key" do
56
+ expect_post(@url, {"apikey" => nil})
57
+ @gibbon.say.hello
58
+ end
59
+
60
+ it "handle malformed api key" do
61
+ @api_key = "123"
62
+ @gibbon.api_key = @api_key
63
+ expect_post(@url, {"apikey" => @api_key})
64
+ @gibbon.say.hello
65
+ end
66
+
67
+ it "handle timeout" do
68
+ expect_post(@url, {"apikey" => nil}, 120)
69
+ @gibbon.timeout=120
70
+ @gibbon.say.hello
71
+ end
72
+
73
+ it "handle api key with dc" do
74
+ @api_key = "TESTKEY-us1"
75
+ @gibbon.api_key = @api_key
76
+ expect_post("https://us1.api.mailchimp.com/2.0/say/hello", {"apikey" => @api_key})
77
+ @gibbon.say.hello
78
+ end
79
+
80
+ # when the end user has signed in via oauth, api_key and endpoint it be supplied separately
81
+ it "not require datacenter in api key" do
82
+ @api_key = "TESTKEY"
83
+ @gibbon.api_key = @api_key
84
+ @gibbon.api_endpoint = "https://us6.api.mailchimp.com"
85
+ expect_post("https://us6.api.mailchimp.com/2.0/say/hello", {"apikey" => @api_key})
86
+ @gibbon.say.hello
87
+ end
88
+ end
89
+
90
+ describe "Gibbon class variables" do
91
+ before do
92
+ Gibbon::API.api_key = "123-us1"
93
+ Gibbon::API.timeout = 15
94
+ Gibbon::API.throws_exceptions = false
95
+ Gibbon::API.api_endpoint = 'https://us6.api.mailchimp.com'
96
+ end
97
+
98
+ after do
99
+ Gibbon::API.api_key = nil
100
+ Gibbon::API.timeout = nil
101
+ Gibbon::API.throws_exceptions = nil
102
+ Gibbon::API.api_endpoint = nil
103
+ end
104
+
105
+ it "set api key on new instances" do
106
+ expect(Gibbon::API.new.api_key).to eq(Gibbon::API.api_key)
107
+ end
108
+
109
+ it "set timeout on new instances" do
110
+ expect(Gibbon::API.new.timeout).to eq(Gibbon::API.timeout)
111
+ end
112
+
113
+ it "set throws_exceptions on new instances" do
114
+ expect(Gibbon::API.new.throws_exceptions).to eq(Gibbon::API.throws_exceptions)
115
+ end
116
+
117
+ it "set api_endpoint on new instances" do
118
+ expect(Gibbon::API.api_endpoint).not_to be_nil
119
+ expect(Gibbon::API.new.api_endpoint).to eq(Gibbon::API.api_endpoint)
120
+ end
121
+ end
122
+
123
+ describe "build api body" do
124
+ before do
125
+ @key = "TESTKEY-us1"
126
+ @gibbon = Gibbon::API.new(@key)
127
+ @url = "https://us1.api.mailchimp.com/2.0/say/hello"
128
+ @body = {"apikey" => @key}
129
+ end
130
+
131
+ it "works for string parameters" do
132
+ @message = "simon says"
133
+ expect_post(@url, @body.merge("message" => @message))
134
+ @gibbon.say.hello(:message => @message)
135
+ end
136
+
137
+ it "works for string parameters in an array" do
138
+ expect_post(@url, @body.merge("messages" => ["simon says", "do this"]))
139
+ @gibbon.say.hello(:messages => ["simon says", "do this"])
140
+ end
141
+
142
+ it "works for string parameters in a hash" do
143
+ expect_post(@url, @body.merge("messages" => {"simon says" => "do this"}))
144
+ @gibbon.say.hello(:messages => {"simon says" => "do this"})
145
+ end
146
+
147
+ it "works for nested string parameters" do
148
+ expect_post(@url, @body.merge("messages" => {"simon says" => ["do this", "and this"]}))
149
+ @gibbon.say.hello(:messages => {"simon says" => ["do this", "and this"]})
150
+ end
151
+
152
+ it "pass through non string parameters" do
153
+ expect_post(@url, @body.merge("fee" => 99))
154
+ @gibbon.say.hello(:fee => 99)
155
+ end
156
+ end
157
+
158
+ describe "Gibbon instances" do
159
+ before do
160
+ @key = "TESTKEY-us1"
161
+ @gibbon = Gibbon::API.new(@key)
162
+ @url = "https://us1.api.mailchimp.com/2.0/say/hello"
163
+ @body = {"apikey" => @key}
164
+ @returns = Struct.new(:body).new(MultiJson.dump(["array", "entries"]))
165
+ end
166
+
167
+ it "produce a good exporter" do
168
+ @exporter = @gibbon.get_exporter
169
+ expect(@exporter.api_key).to eq(@gibbon.api_key)
170
+ end
171
+
172
+ it "not throw exception if configured to and the API replies with a JSON hash containing a key called 'error'" do
173
+ @gibbon.throws_exceptions = false
174
+ Gibbon::APICategory.stub(:post).and_return(Struct.new(:body).new(MultiJson.dump({'error' => 'bad things'})))
175
+
176
+ @gibbon.say.hello
177
+ end
178
+
179
+ it "throw exception if configured to and the API replies with a JSON hash containing a key called 'error'" do
180
+ @gibbon.throws_exceptions = true
181
+ Gibbon::APICategory.stub(:post).and_return(Struct.new(:body).new(MultiJson.dump({'error' => 'bad things'})))
182
+ expect(->{
183
+ @gibbon.say.hello
184
+ }).to raise_error(Gibbon::MailChimpError)
185
+ end
186
+
187
+ it "not raise exception if the api returns no response body" do
188
+ Gibbon::APICategory.stub(:post).and_return(Struct.new(:body).new(nil))
189
+ expect(@gibbon.say.hello).to be_nil
190
+ end
191
+ end
192
+
193
+ describe "export API" do
194
+ before do
195
+ @key = "TESTKEY-us1"
196
+ @gibbon = Gibbon::Export.new(@key)
197
+ @url = "http://us1.api.mailchimp.com/export/1.0/"
198
+ @body = {:apikey => @key, :id => "listid"}
199
+ @returns = Struct.new(:body).new(MultiJson.dump(["array", "entries"]))
200
+ end
201
+
202
+ it "handle api key with dc" do
203
+ @api_key = "TESTKEY-us2"
204
+ @gibbon = Gibbon::Export.new(@api_key)
205
+
206
+ @body[:apikey] = @api_key
207
+ params = {:body => MultiJson.dump(@body), :timeout => 30}
208
+
209
+ url = @url.gsub('us1', 'us2') + "sayHello/"
210
+ Gibbon::Export.should_receive(:post).with(url, params).and_return(@returns)
211
+ @gibbon.say_hello(@body)
212
+ end
213
+
214
+ it "not throw exception if the Export API replies with a JSON hash containing a key called 'error'" do
215
+ @gibbon.throws_exceptions = false
216
+ Gibbon::Export.stub(:post).and_return(Struct.new(:body).new(MultiJson.dump({'error' => 'bad things'})))
217
+
218
+ @gibbon.say_hello(@body)
219
+ end
220
+
221
+ it "throw exception if configured to and the Export API replies with a JSON hash containing a key called 'error'" do
222
+ @gibbon.throws_exceptions = true
223
+ params = {:body => @body, :timeout => 30}
224
+ reply = Struct.new(:body).new MultiJson.dump({'error' => 'bad things', 'code' => '123'})
225
+ Gibbon::Export.stub(:post).and_return reply
226
+
227
+ expect(->{
228
+ @gibbon.say_hello(@body)
229
+ }).to raise_error(Gibbon::MailChimpError)
230
+ end
231
+
232
+ end
233
+
234
+ private
235
+
236
+ def expect_post(expected_url, expected_body, expected_timeout=30)
237
+ Gibbon::APICategory.should_receive(:post).with do |url, opts|
238
+ expect(url).to eq expected_url
239
+ expect(expected_body).to eq MultiJson.load(URI::decode(opts[:body]))
240
+ expect(opts[:timeout]).to eq expected_timeout
241
+ end.and_return(Struct.new(:body).new("[]"))
242
+ end
243
+
244
+ # def expect_post(expected_url, expected_body, expected_timeout=30)
245
+ # Gibbon::APICategory.should_receive(:post).and_return(Struct.new(:body).new(""))
246
+ # end
247
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.setup(:default, :development)
5
+
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ require 'gibbon'
9
+
10
+ RSpec.configure do |config|
11
+ config.color_enabled = true
12
+ end
metadata CHANGED
@@ -1,113 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gibbon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
5
- prerelease:
4
+ version: 0.5.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Amro Mousa
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-03-14 00:00:00.000000000 Z
11
+ date: 2013-07-23 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: httparty
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: multi_json
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: 1.3.4
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: 1.3.4
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rake
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
- name: debugger
56
+ name: rspec
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - ~>
68
60
  - !ruby/object:Gem::Version
69
- version: '0'
70
- type: :development
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: '0'
78
- - !ruby/object:Gem::Dependency
79
- name: shoulda
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
61
+ version: 2.13.0
86
62
  type: :development
87
63
  prerelease: false
88
64
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
65
  requirements:
91
- - - ! '>='
66
+ - - ~>
92
67
  - !ruby/object:Gem::Version
93
- version: '0'
94
- - !ruby/object:Gem::Dependency
95
- name: mocha
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
102
- type: :development
103
- prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
- description: A simple wrapper for MailChimp's primary and export APIs
68
+ version: 2.13.0
69
+ description: A wrapper for MailChimp API 2.0 and Export API 1.0
111
70
  email:
112
71
  - amromousa@gmail.com
113
72
  executables: []
@@ -123,42 +82,39 @@ files:
123
82
  - Rakefile
124
83
  - gibbon.gemspec
125
84
  - lib/gibbon.rb
126
- - test/helper.rb
127
- - test/test_gibbon.rb
85
+ - lib/gibbon/api.rb
86
+ - lib/gibbon/api_category.rb
87
+ - lib/gibbon/export.rb
88
+ - lib/gibbon/mailchimp_error.rb
89
+ - spec/gibbon/gibbon_spec.rb
90
+ - spec/spec_helper.rb
128
91
  homepage: http://github.com/amro/gibbon
129
92
  licenses:
130
93
  - MIT
131
- post_install_message: ! 'Warning: Gibbon versions 0.4.0 and newer include breaking
132
- changes like throwing exceptions by default!
133
-
134
- Please read more at http://github.com/amro/gibbon before upgrading from 0.3.x!'
94
+ metadata: {}
95
+ post_install_message: |-
96
+ IMPORTANT: Gibbon now targets MailChimp API 2.0, which is substantially different from API 1.3.
97
+ Please use Gibbon 0.4.6 if you need to use API 1.3.
98
+ If you're upgrading from <0.5.0 your code WILL break.
135
99
  rdoc_options: []
136
100
  require_paths:
137
101
  - lib
138
102
  required_ruby_version: !ruby/object:Gem::Requirement
139
- none: false
140
103
  requirements:
141
- - - ! '>='
104
+ - - '>='
142
105
  - !ruby/object:Gem::Version
143
106
  version: '0'
144
- segments:
145
- - 0
146
- hash: -2273673627394400762
147
107
  required_rubygems_version: !ruby/object:Gem::Requirement
148
- none: false
149
108
  requirements:
150
- - - ! '>='
109
+ - - '>='
151
110
  - !ruby/object:Gem::Version
152
111
  version: '0'
153
- segments:
154
- - 0
155
- hash: -2273673627394400762
156
112
  requirements: []
157
113
  rubyforge_project: gibbon
158
- rubygems_version: 1.8.24
114
+ rubygems_version: 2.0.3
159
115
  signing_key:
160
- specification_version: 3
161
- summary: A simple wrapper for MailChimp's primary and export APIs
116
+ specification_version: 4
117
+ summary: A wrapper for MailChimp API 2.0 and Export API 1.0
162
118
  test_files:
163
- - test/helper.rb
164
- - test/test_gibbon.rb
119
+ - spec/gibbon/gibbon_spec.rb
120
+ - spec/spec_helper.rb