nationbuilder-rb 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 478eaf49468a14664586855b0105a5c0781b9e82
4
- data.tar.gz: be6334a496d0b620c74c357515fb026224935594
3
+ metadata.gz: 7f736fe98de0df24dd6a2e38b3d7013ec6d49245
4
+ data.tar.gz: fca4e68a8753f003ba0f6815abcf74e9d16c8e7d
5
5
  SHA512:
6
- metadata.gz: d3f42b168b4b0ce526e17ef7462c1666e27e57b05822c005e3f97fd5ca4832041de1884a3c6cbd11656bf89d1d02d874fd66e5dd57ceb533d9efbc057b7f6652
7
- data.tar.gz: a1b4b4fef2cad1592d178c50cc91f385e69b1b9828c7037bdbb963020afb5469129b4ed1f3e23c4f9f98d383ccab76b564d20ec1bf06c0d3daa85efc3fd03c69
6
+ metadata.gz: 25aec085a33bab04ce580aa96d6f650893a5d603f87caa46f4b910401559b3d66bc161d71bb56370b9bada6a20be5d11b0f4759a587b9edcc3d6c252eb6f2d8f
7
+ data.tar.gz: a958baba40eaefda72a00248ef5d3ada11d9b751763de3a97ad17ef068fe6a21220f7eafdf74007ffca41c105f9bad5fa4a463f15ca1a708fa1a67921708e080
data/CHANGELOG.md CHANGED
@@ -1,2 +1,6 @@
1
+ # 1.2.0
2
+ - Native exception types
3
+ - [#9] Exponential backoff when encountering rate limit
4
+
1
5
  # 1.1.0
2
6
  - [#6] Pagination class for iterating through paginated sets
data/Gemfile CHANGED
@@ -3,9 +3,9 @@ source 'http://rubygems.org'
3
3
  gem 'httpclient', '~> 2.4.0'
4
4
 
5
5
  group :development do
6
- gem 'jeweler', '~> 2.0.1'
7
- gem 'rspec', '~> 2.8.0'
8
- gem 'simplecov', '~> 0.8.2'
9
- gem 'vcr', '~> 2.9.2'
10
- gem 'webmock', '~> 1.18.0'
6
+ gem 'jeweler', '~> 2.0'
7
+ gem 'rspec', '~> 3.2'
8
+ gem 'simplecov', '~> 0.8'
9
+ gem 'vcr', '~> 2.9'
10
+ gem 'webmock', '~> 1.18'
11
11
  end
data/Gemfile.lock CHANGED
@@ -1,26 +1,26 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- addressable (2.3.6)
4
+ addressable (2.3.7)
5
5
  builder (3.2.2)
6
6
  crack (0.4.2)
7
7
  safe_yaml (~> 1.0.0)
8
8
  descendants_tracker (0.0.4)
9
9
  thread_safe (~> 0.3, >= 0.3.1)
10
- diff-lcs (1.1.3)
10
+ diff-lcs (1.2.5)
11
11
  docile (1.1.5)
12
- faraday (0.9.0)
12
+ faraday (0.9.1)
13
13
  multipart-post (>= 1.2, < 3)
14
- git (1.2.7)
15
- github_api (0.11.3)
14
+ git (1.2.9.1)
15
+ github_api (0.12.3)
16
16
  addressable (~> 2.3)
17
- descendants_tracker (~> 0.0.1)
17
+ descendants_tracker (~> 0.0.4)
18
18
  faraday (~> 0.8, < 0.10)
19
- hashie (>= 1.2)
19
+ hashie (>= 3.3)
20
20
  multi_json (>= 1.7.5, < 2.0)
21
- nokogiri (~> 1.6.0)
21
+ nokogiri (~> 1.6.3)
22
22
  oauth2
23
- hashie (3.1.0)
23
+ hashie (3.4.0)
24
24
  highline (1.6.21)
25
25
  httpclient (2.4.0)
26
26
  jeweler (2.0.1)
@@ -32,41 +32,46 @@ GEM
32
32
  nokogiri (>= 1.5.10)
33
33
  rake
34
34
  rdoc
35
- json (1.8.1)
36
- jwt (1.0.0)
37
- mini_portile (0.6.0)
35
+ json (1.8.2)
36
+ jwt (1.2.1)
37
+ mini_portile (0.6.2)
38
38
  multi_json (1.10.1)
39
39
  multi_xml (0.5.5)
40
40
  multipart-post (2.0.0)
41
- nokogiri (1.6.2.1)
42
- mini_portile (= 0.6.0)
43
- oauth2 (0.9.4)
41
+ nokogiri (1.6.6.2)
42
+ mini_portile (~> 0.6.0)
43
+ oauth2 (1.0.0)
44
44
  faraday (>= 0.8, < 0.10)
45
45
  jwt (~> 1.0)
46
46
  multi_json (~> 1.3)
47
47
  multi_xml (~> 0.5)
48
48
  rack (~> 1.2)
49
- rack (1.5.2)
50
- rake (10.3.2)
51
- rdoc (4.1.1)
49
+ rack (1.6.0)
50
+ rake (10.4.2)
51
+ rdoc (4.2.0)
52
52
  json (~> 1.4)
53
- rspec (2.8.0)
54
- rspec-core (~> 2.8.0)
55
- rspec-expectations (~> 2.8.0)
56
- rspec-mocks (~> 2.8.0)
57
- rspec-core (2.8.0)
58
- rspec-expectations (2.8.0)
59
- diff-lcs (~> 1.1.2)
60
- rspec-mocks (2.8.0)
61
- safe_yaml (1.0.3)
62
- simplecov (0.8.2)
53
+ rspec (3.2.0)
54
+ rspec-core (~> 3.2.0)
55
+ rspec-expectations (~> 3.2.0)
56
+ rspec-mocks (~> 3.2.0)
57
+ rspec-core (3.2.0)
58
+ rspec-support (~> 3.2.0)
59
+ rspec-expectations (3.2.0)
60
+ diff-lcs (>= 1.2.0, < 2.0)
61
+ rspec-support (~> 3.2.0)
62
+ rspec-mocks (3.2.0)
63
+ diff-lcs (>= 1.2.0, < 2.0)
64
+ rspec-support (~> 3.2.0)
65
+ rspec-support (3.2.1)
66
+ safe_yaml (1.0.4)
67
+ simplecov (0.9.1)
63
68
  docile (~> 1.1.0)
64
- multi_json
69
+ multi_json (~> 1.0)
65
70
  simplecov-html (~> 0.8.0)
66
71
  simplecov-html (0.8.0)
67
72
  thread_safe (0.3.4)
68
- vcr (2.9.2)
69
- webmock (1.18.0)
73
+ vcr (2.9.3)
74
+ webmock (1.20.4)
70
75
  addressable (>= 2.3.6)
71
76
  crack (>= 0.3.2)
72
77
 
@@ -75,8 +80,8 @@ PLATFORMS
75
80
 
76
81
  DEPENDENCIES
77
82
  httpclient (~> 2.4.0)
78
- jeweler (~> 2.0.1)
79
- rspec (~> 2.8.0)
80
- simplecov (~> 0.8.2)
81
- vcr (~> 2.9.2)
82
- webmock (~> 1.18.0)
83
+ jeweler (~> 2.0)
84
+ rspec (~> 3.2)
85
+ simplecov (~> 0.8)
86
+ vcr (~> 2.9)
87
+ webmock (~> 1.18)
data/README.md CHANGED
@@ -30,9 +30,13 @@ Then, create a client by specifying the name of your nation and
30
30
  your API token:
31
31
 
32
32
  ```ruby
33
- client = NationBuilder::Client.new('my_nation_name', 'my_api_token')
33
+ client = NationBuilder::Client.new('my_nation_name', 'my_api_token', retries: 8)
34
34
  ```
35
35
 
36
+ The `retries` parameter specifies the maximum number of retries to attempt
37
+ when the client is rate limited. This uses an exponential backoff strategy.
38
+ The default is `8`.
39
+
36
40
  ## Calling the API
37
41
 
38
42
  The primary method for calling the NationBuilder API in
@@ -81,7 +85,7 @@ page1 = paginated
81
85
  page2 = page1.next
82
86
  page3 = page2.next
83
87
  ```
84
- Methods `#next` and `#prev` return the results of the next or previous page of results, nil if none. `#next?` and `#prev?` return the path to the next or prev page, or nil if none. The results of a page can be accessed through `.body` - in the above example, `page1.body` returns the same hash as `response`.
88
+ Methods `#next` and `#prev` return the results of the next or previous page of results, nil if none. `#next?` and `#prev?` return the path to the next or prev page, or nil if none. The results of a page can be accessed through `.body` - in the above example, `page1.body` returns the same hash as `response`.
85
89
 
86
90
  ## Documentation
87
91
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.2.0
data/lib/nationbuilder.rb CHANGED
@@ -12,3 +12,4 @@ require 'nationbuilder/parameter'
12
12
  require 'nationbuilder/url'
13
13
  require 'nationbuilder/spec_parser'
14
14
  require 'nationbuilder/paginator'
15
+ require 'nationbuilder/errors'
@@ -1,10 +1,16 @@
1
1
  class NationBuilder::Client
2
2
 
3
- def initialize(nation_name, api_key, base_url = 'https://:nation_name.nationbuilder.com')
3
+ def initialize(nation_name, api_key, opts = {})
4
4
  @nation_name = nation_name
5
5
  @api_key = api_key
6
- @base_url = base_url
7
6
  @name_to_endpoint = {}
7
+ @base_url = opts[:base_url] || 'https://:nation_name.nationbuilder.com'
8
+ @retries = opts[:retries] || 8
9
+
10
+ if @retries < 1
11
+ raise 'A positive number of retries must be specified'
12
+ end
13
+
8
14
  parsed_endpoints.each do |endpoint|
9
15
  @name_to_endpoint[endpoint.name] = endpoint
10
16
  end
@@ -31,6 +37,8 @@ class NationBuilder::Client
31
37
  @base_url.gsub(':nation_name', @nation_name)
32
38
  end
33
39
 
40
+ RETRY_DELAY = 0.1 # seconds
41
+
34
42
  def raw_call(path, method, body = {}, args = {})
35
43
  url = NationBuilder::URL.new(base_url).generate_url(path, args)
36
44
 
@@ -51,8 +59,7 @@ class NationBuilder::Client
51
59
  request_args[:body] = JSON(body)
52
60
  end
53
61
 
54
- set_response(HTTPClient.send(method, url, request_args))
55
- return parse_response_body(response)
62
+ perform_request_with_retries(method, url, request_args)
56
63
  end
57
64
 
58
65
  def call(endpoint_name, method_name, args={})
@@ -64,22 +71,52 @@ class NationBuilder::Client
64
71
  return raw_call(method.uri, method.http_method, nonmethod_args, args)
65
72
  end
66
73
 
74
+ def perform_request_with_retries(method, url, request_args)
75
+ raw_response = HTTPClient.send(method, url, request_args)
76
+ parsed_response = nil
77
+
78
+ @retries.times do |i|
79
+ begin
80
+ parsed_response = parse_response_body(raw_response)
81
+ rescue NationBuilder::RateLimitedError
82
+ Kernel.sleep(RETRY_DELAY * 2**i)
83
+ rescue => e
84
+ raise e
85
+ else
86
+ break
87
+ end
88
+ end
89
+
90
+ set_response(raw_response)
91
+ parsed_response
92
+ end
93
+
67
94
  def set_response(value)
68
95
  Thread.current[:nationbuilder_rb_response] = value
69
96
  end
70
97
 
98
+ # This getter is used for fetching the raw response
71
99
  def response
72
100
  Thread.current[:nationbuilder_rb_response]
73
101
  end
74
102
 
75
- class ServerResponseError < StandardError; end
103
+ def classify_response_error(response)
104
+ case
105
+ when response.code == 429
106
+ return NationBuilder::RateLimitedError.new(response.body)
107
+ when response.code.to_s.start_with?('4')
108
+ return NationBuilder::ClientError.new(response.body)
109
+ when response.code.to_s.start_with?('5')
110
+ return NationBuilder::ServerError.new(response.body)
111
+ end
112
+ end
76
113
 
77
114
  def parse_response_body(response)
78
- success = response.code.to_s.start_with?('2')
115
+ error = classify_response_error(response)
116
+ raise error if error
79
117
 
80
118
  if response.header['Content-Type'].first != 'application/json'
81
- return {} if success
82
- raise ServerResponseError.new("Non-JSON content-type for server response: #{response.body}")
119
+ return nil
83
120
  end
84
121
 
85
122
  body = response.body.strip
@@ -0,0 +1,6 @@
1
+ module NationBuilder
2
+ class BaseError < StandardError; end
3
+ class ServerError < BaseError; end
4
+ class ClientError < BaseError; end
5
+ class RateLimitedError < ClientError; end
6
+ end
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: nationbuilder-rb 1.1.0 ruby lib
5
+ # stub: nationbuilder-rb 1.2.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "nationbuilder-rb"
9
- s.version = "1.1.0"
9
+ s.version = "1.2.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["David Huie"]
14
- s.date = "2015-02-10"
14
+ s.date = "2015-02-17"
15
15
  s.description = "A Ruby client to the NationBuilder API"
16
16
  s.email = "david@nationbuilder.com"
17
17
  s.executables = ["nbdoc"]
@@ -37,6 +37,7 @@ Gem::Specification.new do |s|
37
37
  "lib/nationbuilder/api_spec.json",
38
38
  "lib/nationbuilder/client.rb",
39
39
  "lib/nationbuilder/endpoint.rb",
40
+ "lib/nationbuilder/errors.rb",
40
41
  "lib/nationbuilder/method.rb",
41
42
  "lib/nationbuilder/paginator.rb",
42
43
  "lib/nationbuilder/parameter.rb",
@@ -62,26 +63,26 @@ Gem::Specification.new do |s|
62
63
 
63
64
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
65
  s.add_runtime_dependency(%q<httpclient>, ["~> 2.4.0"])
65
- s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
66
- s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
67
- s.add_development_dependency(%q<simplecov>, ["~> 0.8.2"])
68
- s.add_development_dependency(%q<vcr>, ["~> 2.9.2"])
69
- s.add_development_dependency(%q<webmock>, ["~> 1.18.0"])
66
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0"])
67
+ s.add_development_dependency(%q<rspec>, ["~> 3.2"])
68
+ s.add_development_dependency(%q<simplecov>, ["~> 0.8"])
69
+ s.add_development_dependency(%q<vcr>, ["~> 2.9"])
70
+ s.add_development_dependency(%q<webmock>, ["~> 1.18"])
70
71
  else
71
72
  s.add_dependency(%q<httpclient>, ["~> 2.4.0"])
72
- s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
73
- s.add_dependency(%q<rspec>, ["~> 2.8.0"])
74
- s.add_dependency(%q<simplecov>, ["~> 0.8.2"])
75
- s.add_dependency(%q<vcr>, ["~> 2.9.2"])
76
- s.add_dependency(%q<webmock>, ["~> 1.18.0"])
73
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
74
+ s.add_dependency(%q<rspec>, ["~> 3.2"])
75
+ s.add_dependency(%q<simplecov>, ["~> 0.8"])
76
+ s.add_dependency(%q<vcr>, ["~> 2.9"])
77
+ s.add_dependency(%q<webmock>, ["~> 1.18"])
77
78
  end
78
79
  else
79
80
  s.add_dependency(%q<httpclient>, ["~> 2.4.0"])
80
- s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
81
- s.add_dependency(%q<rspec>, ["~> 2.8.0"])
82
- s.add_dependency(%q<simplecov>, ["~> 0.8.2"])
83
- s.add_dependency(%q<vcr>, ["~> 2.9.2"])
84
- s.add_dependency(%q<webmock>, ["~> 1.18.0"])
81
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
82
+ s.add_dependency(%q<rspec>, ["~> 3.2"])
83
+ s.add_dependency(%q<simplecov>, ["~> 0.8"])
84
+ s.add_dependency(%q<vcr>, ["~> 2.9"])
85
+ s.add_dependency(%q<webmock>, ["~> 1.18"])
85
86
  end
86
87
  end
87
88
 
@@ -17,7 +17,7 @@ http_interactions:
17
17
  - application/json
18
18
  response:
19
19
  status:
20
- code: 409
20
+ code: 200
21
21
  message: Conflict
22
22
  headers:
23
23
  Server:
@@ -76,6 +76,6 @@ http_interactions:
76
76
  LOS ANGELES\",\"closed_invoices_amount_in_cents\":null,\"closed_invoices_count\":null,\"contact_status_id\":null,\"contact_status_name\":null,\"could_vote_status\":null,\"demo\":\"\",\"donations_amount_in_cents\":0,\"donations_amount_this_cycle_in_cents\":0,\"donations_count\":0,\"donations_count_this_cycle\":0,\"donations_pledged_amount_in_cents\":0,\"donations_raised_amount_in_cents\":0,\"donations_raised_amount_this_cycle_in_cents\":0,\"donations_raised_count\":0,\"donations_raised_count_this_cycle\":0,\"donations_to_raise_amount_in_cents\":0,\"email1\":\"bob@example.com\",\"email1_is_bad\":false,\"email2\":null,\"email2_is_bad\":false,\"email3\":null,\"email3_is_bad\":false,\"email4\":null,\"email4_is_bad\":false,\"ethnicity\":\"\",\"facebook_address\":null,\"facebook_profile_url\":null,\"facebook_updated_at\":null,\"facebook_username\":null,\"fax_number\":\"\",\"federal_donotcall\":false,\"first_donated_at\":null,\"first_fundraised_at\":null,\"first_invoice_at\":null,\"first_prospect_at\":null,\"first_recruited_at\":null,\"first_supporter_at\":\"2014-04-24T10:27:34-07:00\",\"first_volunteer_at\":\"2014-09-02T13:10:28-07:00\",\"full_name\":\"Bob
77
77
  Smith\",\"home_address\":null,\"import_id\":null,\"inferred_party\":\"\",\"inferred_support_level\":null,\"invoice_payments_amount_in_cents\":null,\"invoice_payments_referred_amount_in_cents\":null,\"invoices_amount_in_cents\":null,\"invoices_count\":null,\"is_deceased\":false,\"is_donor\":false,\"is_fundraiser\":false,\"is_ignore_donation_limits\":false,\"is_leaderboardable\":true,\"is_mobile_bad\":false,\"is_possible_duplicate\":false,\"is_profile_private\":false,\"is_profile_searchable\":true,\"is_prospect\":false,\"is_supporter\":true,\"is_survey_question_private\":false,\"language\":\"\",\"last_call_id\":null,\"last_contacted_at\":null,\"last_contacted_by\":null,\"last_donated_at\":null,\"last_fundraised_at\":null,\"last_invoice_at\":null,\"last_rule_violation_at\":null,\"legal_name\":null,\"locale\":\"\",\"mailing_address\":null,\"marital_status\":\"\",\"media_market_name\":null,\"meetup_address\":null,\"membership_expires_at\":null,\"membership_level_name\":null,\"membership_started_at\":null,\"middle_name\":null,\"mobile_normalized\":null,\"nbec_precinct_code\":null,\"note_updated_at\":null,\"outstanding_invoices_amount_in_cents\":null,\"outstanding_invoices_count\":null,\"overdue_invoices_count\":null,\"page_slug\":\"ticket_page\",\"parent\":null,\"parent_id\":null,\"party_member\":false,\"phone_normalized\":\"2135551234\",\"phone_time\":\"\",\"precinct_code\":null,\"precinct_name\":null,\"prefix\":null,\"previous_party\":\"\",\"primary_email_id\":1,\"priority_level\":null,\"priority_level_changed_at\":null,\"profile_content\":null,\"profile_content_html\":null,\"profile_headline\":null,\"received_capital_amount_in_cents\":17700,\"recruiter\":null,\"recruits_count\":0,\"registered_address\":null,\"registered_at\":null,\"religion\":\"\",\"rule_violations_count\":0,\"spent_capital_amount_in_cents\":1000,\"submitted_address\":\"\",\"subnations\":[],\"suffix\":null,\"support_level_changed_at\":null,\"support_probability_score\":null,\"turnout_probability_score\":null,\"twitter_address\":null,\"twitter_description\":null,\"twitter_followers_count\":null,\"twitter_friends_count\":null,\"twitter_location\":null,\"twitter_login\":null,\"twitter_updated_at\":null,\"twitter_website\":null,\"unsubscribed_at\":null,\"user_submitted_address\":{\"address1\":\"488
78
78
  S Hill St\",\"address2\":null,\"address3\":null,\"city\":\"Los Angeles\",\"state\":\"CA\",\"country_code\":\"US\",\"zip\":\"90013\",\"lat\":\"34.0490467\",\"lng\":\"-118.2515224\"},\"username\":\"randomeveguy\",\"warnings_count\":0,\"website\":null,\"work_address\":null,\"bag_preference\":null,\"do_you_drive_a_fork_lift\":null,\"have_you_ever_been_contacted_by_a_union_representative_before_\":null,\"have_you_been_involved_in_an_election_campaign_before_\":null,\"sub_branch\":null,\"sams_custom_field\":null,\"mrow\":null,\"received_an_email\":null,\"brett_petition_mc\":null,\"headshot_url\":null,\"university\":null,\"membership_number\":null},\"precinct\":null}"
79
- http_version:
79
+ http_version:
80
80
  recorded_at: Thu, 05 Feb 2015 05:54:33 GMT
81
81
  recorded_with: VCR 2.9.2
@@ -4,13 +4,14 @@ describe NationBuilder::Client do
4
4
 
5
5
  let(:client) do
6
6
  NationBuilder::Client.new('organizeralexandreschmitt',
7
- '53920a524356034a065515a37650df2bd295971975d5742b9daa50eb8c7404d5')
7
+ '53920a524356034a065515a37650df2bd295971975d5742b9daa50eb8c7404d5',
8
+ retries: 2)
8
9
  end
9
10
 
10
11
  describe '#endpoints' do
11
12
 
12
13
  it 'should contain all defined endpoints' do
13
- client.endpoints.sort.should eq([
14
+ expect(client.endpoints.sort).to eq([
14
15
  :basic_pages,
15
16
  :blog_posts,
16
17
  :blogs,
@@ -38,7 +39,7 @@ describe NationBuilder::Client do
38
39
  describe '#base_url' do
39
40
 
40
41
  it 'should contain the nation slug' do
41
- client.base_url.should eq('https://organizeralexandreschmitt.nationbuilder.com')
42
+ expect(client.base_url).to eq('https://organizeralexandreschmitt.nationbuilder.com')
42
43
  end
43
44
  end
44
45
 
@@ -48,7 +49,7 @@ describe NationBuilder::Client do
48
49
  VCR.use_cassette('parametered_get') do
49
50
  response = client.call(:basic_pages, :index, site_slug: 'organizeralexandreschmitt')
50
51
  response['results'].each do |result|
51
- result['site_slug'].should eq('organizeralexandreschmitt')
52
+ expect(result['site_slug']).to eq('organizeralexandreschmitt')
52
53
  end
53
54
  end
54
55
  end
@@ -66,7 +67,7 @@ describe NationBuilder::Client do
66
67
  client.call(:people, :create, params)
67
68
  end
68
69
 
69
- response['person']['first_name'].should eq('Bob')
70
+ expect(response['person']['first_name']).to eq('Bob')
70
71
  end
71
72
 
72
73
  it 'should handle a DELETE' do
@@ -78,7 +79,51 @@ describe NationBuilder::Client do
78
79
  client.call(:people, :destroy, params)
79
80
  end
80
81
 
81
- response.should eq({})
82
+ expect(response).to eq(nil)
83
+ end
84
+ end
85
+
86
+ describe '#classify_response_error' do
87
+ it 'should account for rate limits' do
88
+ response = double(code: 429, body: 'rate limiting')
89
+ expect(client.classify_response_error(response).class).
90
+ to eq(NationBuilder::RateLimitedError)
91
+ end
92
+ it 'should account for client errors' do
93
+ response = double(code: 404, body: '404ing')
94
+ expect(client.classify_response_error(response).class).
95
+ to eq(NationBuilder::ClientError)
96
+ end
97
+ it 'should account for client errors' do
98
+ response = double(code: 500, body: '500ing')
99
+ expect(client.classify_response_error(response).class).
100
+ to eq(NationBuilder::ServerError)
101
+ end
102
+ end
103
+
104
+ describe '#perform_request_with_retries' do
105
+ before do
106
+ expect(HTTPClient).to receive(:send)
107
+ end
108
+
109
+ it 'should raise non-rate limiting execeptions' do
110
+ expect(client).to receive(:parse_response_body) { raise StandardError.new('boom') }
111
+ expect do
112
+ client.perform_request_with_retries(nil, nil, nil)
113
+ end.to raise_error
114
+ end
115
+
116
+ it 'should return a response if the rate limit is eventually dropped' do
117
+ expect(Kernel).to receive(:sleep).twice
118
+ allow(client).to receive(:parse_response_body) do
119
+ @count ||= 0
120
+ if @count != 2
121
+ raise NationBuilder::RateLimitedError.new
122
+ end
123
+ end
124
+ expect do
125
+ client.perform_request_with_retries(nil, nil, nil)
126
+ end.to_not raise_error
82
127
  end
83
128
  end
84
129
  end
@@ -19,26 +19,26 @@ describe Paginator do
19
19
  end
20
20
 
21
21
  it 'should check for next and prev page link' do
22
- @page1.next?.should_not be_nil
23
- @page1.prev?.should be_nil
22
+ expect(@page1.next?).to_not be_nil
23
+ expect(@page1.prev?).to be_nil
24
24
  end
25
25
 
26
26
  it 'should return next page' do
27
27
  VCR.use_cassette('paginated_get_page2') do
28
28
  page2 = @page1.next
29
- page2.body.should_not eq(@page1.body)
29
+ expect(page2.body).to_not eq(@page1.body)
30
30
  end
31
31
  end
32
32
 
33
33
  it 'should return additional pages' do
34
34
  VCR.use_cassette('paginated_get_page3') do
35
35
  page3 = @page2.next
36
- page3.body.should_not eq(@page2.body)
36
+ expect(page3.body).to_not eq(@page2.body)
37
37
  end
38
38
  end
39
39
 
40
40
  it 'should return nil if no prev page' do
41
- @page1.prev.should be_nil
41
+ expect(@page1.prev).to be_nil
42
42
  end
43
43
  end
44
- end
44
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nationbuilder-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Huie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-10 00:00:00.000000000 Z
11
+ date: 2015-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httpclient
@@ -30,70 +30,70 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.0.1
33
+ version: '2.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.0.1
40
+ version: '2.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 2.8.0
47
+ version: '3.2'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 2.8.0
54
+ version: '3.2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.8.2
61
+ version: '0.8'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.8.2
68
+ version: '0.8'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: vcr
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 2.9.2
75
+ version: '2.9'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 2.9.2
82
+ version: '2.9'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: webmock
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 1.18.0
89
+ version: '1.18'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 1.18.0
96
+ version: '1.18'
97
97
  description: A Ruby client to the NationBuilder API
98
98
  email: david@nationbuilder.com
99
99
  executables:
@@ -120,6 +120,7 @@ files:
120
120
  - lib/nationbuilder/api_spec.json
121
121
  - lib/nationbuilder/client.rb
122
122
  - lib/nationbuilder/endpoint.rb
123
+ - lib/nationbuilder/errors.rb
123
124
  - lib/nationbuilder/method.rb
124
125
  - lib/nationbuilder/paginator.rb
125
126
  - lib/nationbuilder/parameter.rb