badbill 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CONTRIBUTING.md +5 -2
  2. data/Gemfile +14 -3
  3. data/Gemfile.lock +51 -15
  4. data/Guardfile +6 -0
  5. data/LICENSE +1 -1
  6. data/README.md +8 -3
  7. data/Rakefile +5 -1
  8. data/badbill.gemspec +86 -0
  9. data/lib/badbill.rb +32 -2
  10. data/lib/badbill/base_resource.rb +51 -9
  11. data/lib/badbill/client.rb +1 -1
  12. data/lib/badbill/faraday_gzip.rb +18 -0
  13. data/lib/badbill/forward_methods.rb +6 -0
  14. data/lib/badbill/invoice.rb +31 -1
  15. data/lib/badbill/invoice_comment.rb +24 -0
  16. data/lib/badbill/invoice_item.rb +17 -0
  17. data/lib/badbill/invoice_payment.rb +28 -0
  18. data/lib/badbill/recurring.rb +11 -0
  19. data/spec/badbill/base_resource_spec.rb +1 -1
  20. data/spec/badbill/client_spec.rb +72 -68
  21. data/spec/badbill/invoice_comment_spec.rb +33 -0
  22. data/spec/badbill/invoice_item_spec.rb +17 -0
  23. data/spec/badbill/invoice_payment_spec.rb +17 -0
  24. data/spec/badbill/invoice_spec.rb +77 -176
  25. data/spec/badbill/recurring_spec.rb +18 -0
  26. data/spec/badbill_spec.rb +19 -17
  27. data/spec/fixtures/vcr_cassettes/aggregated_invoices.yml +64 -0
  28. data/spec/fixtures/vcr_cassettes/all_clients.yml +72 -0
  29. data/spec/fixtures/vcr_cassettes/all_invoices.yml +134 -0
  30. data/spec/fixtures/vcr_cassettes/all_recurring_invoices.yml +131 -0
  31. data/spec/fixtures/vcr_cassettes/client_on_change.yml +54 -0
  32. data/spec/fixtures/vcr_cassettes/draft_item.yml +71 -0
  33. data/spec/fixtures/vcr_cassettes/existent_client.yml +71 -0
  34. data/spec/fixtures/vcr_cassettes/fetched_invoice_pdf.yml +2111 -0
  35. data/spec/fixtures/vcr_cassettes/fetches_invoice-item_by_id.yml +123 -0
  36. data/spec/fixtures/vcr_cassettes/invoice-comments_by_invoice_id.yml +68 -0
  37. data/spec/fixtures/vcr_cassettes/invoice-comments_by_invoice_id_and_actionkey_PAYMENT.yml +131 -0
  38. data/spec/fixtures/vcr_cassettes/invoice-comments_by_invoice_id_and_actionkeys.yml +67 -0
  39. data/spec/fixtures/vcr_cassettes/invoice_canceled.yml +50 -0
  40. data/spec/fixtures/vcr_cassettes/invoice_deleted.yml +46 -0
  41. data/spec/fixtures/vcr_cassettes/invoice_info.yml +131 -0
  42. data/spec/fixtures/vcr_cassettes/invoice_marked_as_complete.yml +95 -0
  43. data/spec/fixtures/vcr_cassettes/invoice_payments.yml +123 -0
  44. data/spec/fixtures/vcr_cassettes/invoice_send_email_with_basic_info.yml +48 -0
  45. data/spec/fixtures/vcr_cassettes/invoice_send_email_with_basic_info_and_from.yml +48 -0
  46. data/spec/fixtures/vcr_cassettes/invoice_send_email_with_basic_info_from_and_subject.yml +48 -0
  47. data/spec/fixtures/vcr_cassettes/invoice_send_mail.yml +48 -0
  48. data/spec/fixtures/vcr_cassettes/invoice_send_mail_invalid_address.yml +48 -0
  49. data/spec/fixtures/vcr_cassettes/invoice_uploaded_signature.yml +48 -0
  50. data/spec/fixtures/vcr_cassettes/myself_client.yml +70 -0
  51. data/spec/fixtures/vcr_cassettes/new_client.yml +50 -0
  52. data/spec/fixtures/vcr_cassettes/non_existent_client.yml +303 -0
  53. data/spec/fixtures/vcr_cassettes/save_for_non-existent_client.yml +48 -0
  54. data/spec/fixtures/vcr_cassettes/wrong_data_for_client.yml +50 -0
  55. data/spec/spec_helper.rb +50 -0
  56. metadata +43 -4
  57. data/spec/helper.rb +0 -24
@@ -1,6 +1,6 @@
1
1
  # How to contribute
2
2
 
3
- This API client is implemented for my own use, so I will only work on the parts I need for myself.
3
+ This API client is implemented for use at [rrbone](http://www.rrbone.net), so I will only work on the parts we need for our application.
4
4
  Additional improvements, comments on code and implementation and bug fixes are very welcome.
5
5
 
6
6
  ## Report bugs, improvements or feature requests
@@ -8,6 +8,9 @@ Additional improvements, comments on code and implementation and bug fixes are v
8
8
  * Submit a ticket for your issue, assuming one does not already exist.
9
9
  * Clearly describe the issue including steps to reproduce when it is a bug.
10
10
  * Make sure you fill in the earliest version that you know has the issue.
11
+ * If you need a resource implemented in Ruby, but does not know how to do it
12
+ the right way, just contact me or open an issue. I will try to find time to
13
+ implement any needed things.
11
14
 
12
15
  ## Making Changes
13
16
 
@@ -30,5 +33,5 @@ Additional improvements, comments on code and implementation and bug fixes are v
30
33
 
31
34
  Feel free to contact me with any issues or questions.
32
35
 
33
- * Mail: badboy@archlinux.us
36
+ * Mail: jer@rrbone.net
34
37
  * Twitter: [@badboy_](https://twitter.com/badboy_)
data/Gemfile CHANGED
@@ -1,9 +1,20 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  group :development do
4
- gem 'yard'
5
- gem 'redcarpet'
6
- gem 'simplecov'
4
+ gem 'yard', '~> 0.8.2'
5
+ gem 'redcarpet', '~> 2.1.1'
6
+ gem 'simplecov', '~> 0.6.4'
7
+
8
+ gem 'guard-rspec', '~> 2.0.0'
9
+ gem 'fuubar', '~> 1.1.0' # Guard output mode
10
+ gem 'rb-inotify', '~> 0.8.0' # Notification of test status (used by guard)
11
+ end
12
+
13
+ group :test do
14
+ gem 'rake'
15
+ gem 'webmock', '~> 1.9.0'
16
+ gem 'vcr', '~> 2.4.0'
17
+ gem 'rspec', '~> 2.11.0'
7
18
  end
8
19
 
9
20
  gemspec
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- badbill (0.0.1)
4
+ badbill (0.0.3dev)
5
5
  faraday
6
6
  faraday_middleware
7
7
  hashie
@@ -10,16 +10,41 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- addressable (2.3.2)
14
- crack (0.3.1)
13
+ addressable (2.3.3)
14
+ coderay (1.0.9)
15
+ crack (0.3.2)
15
16
  diff-lcs (1.1.3)
16
- faraday (0.8.4)
17
+ faraday (0.8.6)
17
18
  multipart-post (~> 1.1)
18
- faraday_middleware (0.8.8)
19
+ faraday_middleware (0.9.0)
19
20
  faraday (>= 0.7.4, < 0.9)
20
- hashie (1.2.0)
21
- multi_json (1.3.6)
21
+ ffi (1.4.0)
22
+ fuubar (1.1.0)
23
+ rspec (~> 2.0)
24
+ rspec-instafail (~> 0.2.0)
25
+ ruby-progressbar (~> 1.0.0)
26
+ guard (1.6.2)
27
+ listen (>= 0.6.0)
28
+ lumberjack (>= 1.0.2)
29
+ pry (>= 0.9.10)
30
+ terminal-table (>= 1.4.3)
31
+ thor (>= 0.14.6)
32
+ guard-rspec (2.0.0)
33
+ guard (>= 1.1)
34
+ rspec (~> 2.11)
35
+ hashie (2.0.1)
36
+ listen (0.7.3)
37
+ lumberjack (1.0.2)
38
+ method_source (0.8.1)
39
+ multi_json (1.6.1)
22
40
  multipart-post (1.1.5)
41
+ pry (0.9.12)
42
+ coderay (~> 1.0.5)
43
+ method_source (~> 0.8)
44
+ slop (~> 3.4)
45
+ rake (10.0.3)
46
+ rb-inotify (0.8.8)
47
+ ffi (>= 0.5.0)
23
48
  redcarpet (2.1.1)
24
49
  rspec (2.11.0)
25
50
  rspec-core (~> 2.11.0)
@@ -28,24 +53,35 @@ GEM
28
53
  rspec-core (2.11.1)
29
54
  rspec-expectations (2.11.3)
30
55
  diff-lcs (~> 1.1.3)
56
+ rspec-instafail (0.2.4)
31
57
  rspec-mocks (2.11.3)
58
+ ruby-progressbar (1.0.2)
32
59
  simplecov (0.6.4)
33
60
  multi_json (~> 1.0)
34
61
  simplecov-html (~> 0.5.3)
35
62
  simplecov-html (0.5.3)
36
- webmock (1.8.10)
63
+ slop (3.4.3)
64
+ terminal-table (1.4.5)
65
+ thor (0.17.0)
66
+ vcr (2.4.0)
67
+ webmock (1.9.3)
37
68
  addressable (>= 2.2.7)
38
- crack (>= 0.1.7)
69
+ crack (>= 0.3.2)
39
70
  yajl-ruby (1.1.0)
40
- yard (0.8.2.1)
71
+ yard (0.8.5.2)
41
72
 
42
73
  PLATFORMS
43
74
  ruby
44
75
 
45
76
  DEPENDENCIES
46
77
  badbill!
47
- redcarpet
48
- rspec
49
- simplecov
50
- webmock
51
- yard
78
+ fuubar (~> 1.1.0)
79
+ guard-rspec (~> 2.0.0)
80
+ rake
81
+ rb-inotify (~> 0.8.0)
82
+ redcarpet (~> 2.1.1)
83
+ rspec (~> 2.11.0)
84
+ simplecov (~> 0.6.4)
85
+ vcr (~> 2.4.0)
86
+ webmock (~> 1.9.0)
87
+ yard (~> 0.8.2)
@@ -0,0 +1,6 @@
1
+ guard 'rspec', cli: '--drb --format Fuubar --color', version: 2 do
2
+ # run every updated spec file
3
+ watch(%r{^spec/.+_spec\.rb$})
4
+ # run the lib specs when a file in lib/ changes
5
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
6
+ end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Jan-Erik Rediger <http://fnordig.de/about/>
1
+ Copyright (c) 2012 Jan-Erik Rediger <jer@rrbone.net>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person ob-
4
4
  taining a copy of this software and associated documentation
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  BadBill - Billomat API Client
2
2
  ===================
3
3
 
4
+ (developed for internal use at [rrbone](http://www.rrbone.net))
5
+
4
6
  Simple but working API client for the [Billomat API][apidocu].
5
7
 
6
8
  See the [API documentation][apidocu] for full documentation of all resources.
@@ -19,10 +21,13 @@ The basic BadBill class allows access to all resources. It includes no syntactic
19
21
 
20
22
  The following resources are currently implemented as its own class:
21
23
 
22
- * [clients](http://www.billomat.com/en/api/invoices/) (`BadBill::Client`)
23
- * [invoices](http://www.billomat.com/en/api/invoices/) (`BadBill::Invoices`)
24
+ * [clients](http://www.billomat.com/en/api/clients/) (`BadBill::Client`)
25
+ * [invoices](http://www.billomat.com/en/api/invoices/) (`BadBill::Invoice`)
26
+ * [invoice-payments](http://www.billomat.com/en/api/invoices/payments/) (`BadBill::InvoicePayment`)
27
+ * [invoice-items](http://www.billomat.com/en/api/invoices/items/) (`BadBill::InvoiceItem`)
28
+ * [invoice-comments](http://www.billomat.com/en/api/invoices/comments) (`BadBill::InvoiceComment`)
29
+ * [recurring](http://www.billomat.com/en/api/recurrings/) (`BadBill::Recurring`)
24
30
 
25
- These are the two I need right now.
26
31
  Implementing new resources is easy. Pull Requests for others are welcome.
27
32
 
28
33
  ## Requirements
data/Rakefile CHANGED
@@ -12,6 +12,10 @@ end
12
12
  task :default => :spec
13
13
  task :doc => :yard
14
14
 
15
+ task :tags do
16
+ sh 'ctags -R --exclude=.git *'
17
+ end
18
+
15
19
  ## helper functions
16
20
 
17
21
  def name
@@ -73,7 +77,7 @@ task :gemspec do
73
77
  split("\n").
74
78
  sort.
75
79
  reject { |file| file =~ /^\./ }.
76
- reject { |file| file =~ /^(doc|coverage|pkg)/ }.
80
+ reject { |file| file =~ /^(doc|coverage|pkg|contrib)/ }.
77
81
  map { |file| " ./#{file}" }.
78
82
  join("\n")
79
83
 
@@ -0,0 +1,86 @@
1
+ Gem::Specification.new do |s|
2
+ s.specification_version = 2 if s.respond_to? :specification_version=
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if s.respond_to? :required_rubygems_version=
4
+
5
+ s.name = 'badbill'
6
+ s.version = '0.1.0'
7
+
8
+ s.summary = "Simple but working API client for the Billomat API."
9
+ s.description = <<-EOF
10
+ Fast and easy access to all resources the Billomat API provides (not all resources are Ruby classes, yet).
11
+ EOF
12
+
13
+ s.authors = ["Jan-Erik Rediger"]
14
+ s.email = 'badboy@archlinux.us'
15
+ s.homepage = 'https://github.com/badboy/badbill'
16
+
17
+ s.add_dependency 'yajl-ruby'
18
+ s.add_dependency 'faraday'
19
+ s.add_dependency 'faraday_middleware'
20
+ s.add_dependency 'hashie'
21
+ s.add_development_dependency 'rspec'
22
+ s.add_development_dependency 'webmock'
23
+
24
+ # = MANIFEST =
25
+ s.files = %w[
26
+ ./CONTRIBUTING.md
27
+ ./Gemfile
28
+ ./Gemfile.lock
29
+ ./Guardfile
30
+ ./LICENSE
31
+ ./README.md
32
+ ./Rakefile
33
+ ./badbill.gemspec
34
+ ./lib/badbill.rb
35
+ ./lib/badbill/base_resource.rb
36
+ ./lib/badbill/client.rb
37
+ ./lib/badbill/faraday_gzip.rb
38
+ ./lib/badbill/forward_methods.rb
39
+ ./lib/badbill/invoice.rb
40
+ ./lib/badbill/invoice_comment.rb
41
+ ./lib/badbill/invoice_item.rb
42
+ ./lib/badbill/invoice_payment.rb
43
+ ./lib/badbill/recurring.rb
44
+ ./lib/badbill/resource.rb
45
+ ./spec/badbill/base_resource_spec.rb
46
+ ./spec/badbill/client_spec.rb
47
+ ./spec/badbill/invoice_comment_spec.rb
48
+ ./spec/badbill/invoice_item_spec.rb
49
+ ./spec/badbill/invoice_payment_spec.rb
50
+ ./spec/badbill/invoice_spec.rb
51
+ ./spec/badbill/recurring_spec.rb
52
+ ./spec/badbill_spec.rb
53
+ ./spec/fixtures/vcr_cassettes/aggregated_invoices.yml
54
+ ./spec/fixtures/vcr_cassettes/all_clients.yml
55
+ ./spec/fixtures/vcr_cassettes/all_invoices.yml
56
+ ./spec/fixtures/vcr_cassettes/all_recurring_invoices.yml
57
+ ./spec/fixtures/vcr_cassettes/client_on_change.yml
58
+ ./spec/fixtures/vcr_cassettes/draft_item.yml
59
+ ./spec/fixtures/vcr_cassettes/existent_client.yml
60
+ ./spec/fixtures/vcr_cassettes/fetched_invoice_pdf.yml
61
+ ./spec/fixtures/vcr_cassettes/fetches_invoice-item_by_id.yml
62
+ ./spec/fixtures/vcr_cassettes/invoice-comments_by_invoice_id.yml
63
+ ./spec/fixtures/vcr_cassettes/invoice-comments_by_invoice_id_and_actionkey_PAYMENT.yml
64
+ ./spec/fixtures/vcr_cassettes/invoice-comments_by_invoice_id_and_actionkeys.yml
65
+ ./spec/fixtures/vcr_cassettes/invoice_canceled.yml
66
+ ./spec/fixtures/vcr_cassettes/invoice_deleted.yml
67
+ ./spec/fixtures/vcr_cassettes/invoice_info.yml
68
+ ./spec/fixtures/vcr_cassettes/invoice_marked_as_complete.yml
69
+ ./spec/fixtures/vcr_cassettes/invoice_payments.yml
70
+ ./spec/fixtures/vcr_cassettes/invoice_send_email_with_basic_info.yml
71
+ ./spec/fixtures/vcr_cassettes/invoice_send_email_with_basic_info_and_from.yml
72
+ ./spec/fixtures/vcr_cassettes/invoice_send_email_with_basic_info_from_and_subject.yml
73
+ ./spec/fixtures/vcr_cassettes/invoice_send_mail.yml
74
+ ./spec/fixtures/vcr_cassettes/invoice_send_mail_invalid_address.yml
75
+ ./spec/fixtures/vcr_cassettes/invoice_uploaded_signature.yml
76
+ ./spec/fixtures/vcr_cassettes/myself_client.yml
77
+ ./spec/fixtures/vcr_cassettes/new_client.yml
78
+ ./spec/fixtures/vcr_cassettes/non_existent_client.yml
79
+ ./spec/fixtures/vcr_cassettes/save_for_non-existent_client.yml
80
+ ./spec/fixtures/vcr_cassettes/wrong_data_for_client.yml
81
+ ./spec/spec_helper.rb
82
+ ]
83
+ # = MANIFEST =
84
+
85
+ s.test_files = s.files.select { |path| path =~ %r{^spec/*/.+\.rb} }
86
+ end
@@ -1,15 +1,25 @@
1
1
  # encoding: utf-8
2
+ #
3
+ # Copyright (c) 2012 Jan-Erik Rediger <jer@rrbone.net>
4
+ # See LICENSE for info.
5
+ # Developed for internal use at rrbone (http://www.rrbone.net)
2
6
 
3
7
  require 'yajl/json_gem'
4
8
  require 'faraday_middleware'
5
9
  require 'hashie/mash'
6
10
 
11
+ require_relative 'badbill/faraday_gzip'
12
+
7
13
  require_relative 'badbill/resource'
8
14
  require_relative 'badbill/forward_methods'
9
15
  require_relative 'badbill/base_resource'
10
16
 
11
17
  require_relative 'badbill/client'
12
18
  require_relative 'badbill/invoice'
19
+ require_relative 'badbill/invoice_payment'
20
+ require_relative 'badbill/invoice_item'
21
+ require_relative 'badbill/invoice_comment'
22
+ require_relative 'badbill/recurring'
13
23
 
14
24
  # Handles the connection and requests to the Billomat API.
15
25
  #
@@ -25,12 +35,14 @@ require_relative 'badbill/invoice'
25
35
  # billo.get 'clients'
26
36
  # # => {"clients"=>{"client"=>[...]}}
27
37
  class BadBill
28
- VERSION = '0.0.1'
38
+ VERSION = '0.1.0'
29
39
 
30
40
  # Reject any not allowed HTTP method.
31
41
  class NotAllowedException < Exception; end
32
42
  # Fail if no global connection is set.
33
43
  class NoConnection < Exception; end
44
+ # Fail without API key and Billomat ID
45
+ class MissingConfiguration < Exception; end
34
46
 
35
47
  # The API url used for all connections.
36
48
  API_URL = 'http%s://%s.billomat.net/'
@@ -45,6 +57,10 @@ class BadBill
45
57
  def initialize billomat_id, api_key, ssl=false
46
58
  @billomat_id = billomat_id
47
59
  @api_key = api_key
60
+
61
+ raise MissingConfiguration.new("Billomat ID missing") if @billomat_id.nil?
62
+ raise MissingConfiguration.new("API Key missing") if @api_key.nil?
63
+
48
64
  @ssl = ssl
49
65
  @http_adapter = connection
50
66
 
@@ -65,6 +81,11 @@ class BadBill
65
81
  @connection
66
82
  end
67
83
 
84
+ def self.clear_connection
85
+ @connection = nil
86
+ end
87
+
88
+
68
89
  # Call the specified resource.
69
90
  #
70
91
  # It sets the X-BillomatApiKey header, the Content-Type header and the Accept header.
@@ -98,7 +119,15 @@ class BadBill
98
119
  req.body = options if method != :get && options && !options.empty?
99
120
  }.body
100
121
  rescue Faraday::Error::ClientError => error
101
- Hashie::Mash.new :error => error
122
+ if error.response && error.response.has_key?(:body) && error.response[:body]
123
+ body = error.response[:body]
124
+ if !body.kind_of?(Hash) && ['{', '[', '"'].include?(body[0])
125
+ body = JSON.parse body
126
+ end
127
+ Hashie::Mash.new error: error, body: body
128
+ else
129
+ Hashie::Mash.new error: error
130
+ end
102
131
  end
103
132
 
104
133
  # Send a GET request.
@@ -143,6 +172,7 @@ class BadBill
143
172
 
144
173
  conn.response :mashify
145
174
  conn.response :json, :content_type => /\bjson$/
175
+ conn.response :gzip
146
176
  #conn.response :logger
147
177
  conn.response :raise_error
148
178
  conn.adapter :net_http
@@ -33,8 +33,19 @@ class BadBill
33
33
  # @return [Array<Resource>] All found resources.
34
34
  def self.all filter={}
35
35
  all = get resource_name, filter
36
- all.__send__(resource_name).__send__(resource_name_singular).map do |res|
37
- new res.id, res
36
+ return all if all.error
37
+
38
+ resources = all.__send__(resource_name)
39
+ case resources['@total'].to_i
40
+ when 0
41
+ []
42
+ when 1
43
+ data = resources.__send__(resource_name_singular)
44
+ [new(data.id.to_i, data)]
45
+ else
46
+ resources.__send__(resource_name_singular).map do |res|
47
+ new res.id.to_i, res
48
+ end
38
49
  end
39
50
  end
40
51
 
@@ -45,7 +56,17 @@ class BadBill
45
56
  # @return [Resource] New resource with id and data set.
46
57
  def self.find id
47
58
  c = get resource_name, id
48
- new id, c.__send__(resource_name_singular)
59
+ if c.error
60
+ if c.error.kind_of? Faraday::Error::ResourceNotFound
61
+ return nil
62
+ else
63
+ return c
64
+ end
65
+ end
66
+
67
+ data = c.__send__(resource_name_singular)
68
+ return nil if data.nil?
69
+ new id, data
49
70
  end
50
71
 
51
72
  # Create a new resource with the given parameters.
@@ -56,17 +77,30 @@ class BadBill
56
77
  # @return [Resource] New resource with id and data set.
57
78
  def self.create params
58
79
  res = post(resource_name, {resource_name_singular => params})
80
+ return res if res.error
81
+
59
82
  res_data = res.__send__(resource_name_singular)
60
- new res_data.id, res_data
83
+ new res_data.id.to_i, res_data
61
84
  end
62
85
 
63
86
  # Save any changed data.
64
87
  #
65
- # @return [Boolean] True if successfull, false otherwise.
88
+ # @return [Resource] The instance with changed values or the error object.
66
89
  def save
67
- @data.id = id
68
- resp = put resource_name, id, {resource_name_singular => @data}
69
- !resp
90
+ # Only save
91
+ if @__mutated__ && !@__mutated__.empty?
92
+ data = Hash[@__mutated__.map { |k| [k, @data[k]] }]
93
+
94
+ res = put resource_name, id, {resource_name_singular => data}
95
+
96
+ return res if res.error
97
+
98
+ res_data = res.__send__(resource_name_singular)
99
+ @data.merge!(res_data)
100
+ @__mutated__.clear
101
+ end
102
+
103
+ self
70
104
  end
71
105
 
72
106
  # Delete this resource.
@@ -85,6 +119,14 @@ class BadBill
85
119
  raise NoMethodError, "undefined method `id=' for '#{self.inspect.sub(/ .+/, '>')}`"
86
120
  end
87
121
 
122
+ def error
123
+ if @data.respond_to?(:error)
124
+ @data.error
125
+ else
126
+ nil
127
+ end
128
+ end
129
+
88
130
  private
89
131
 
90
132
  def resource_name_singular
@@ -102,7 +144,7 @@ class BadBill
102
144
  #
103
145
  # @return [String] The singular resource name.
104
146
  def self.resource_name_singular
105
- @resource_name_singular ||= self.name.split(/::/).last.downcase
147
+ @resource_name_singular ||= self.name.split(/::/).last.split(/^|([A-Z][a-z]+)/).join('-').downcase
106
148
  end
107
149
 
108
150
  # The resource name (plural) for use with the API.