badbill 0.0.1 → 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.
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.