jsonapi-consumer 0.1.1 → 1.0.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 (76) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +27 -0
  3. data/.gitignore +1 -0
  4. data/Gemfile +6 -4
  5. data/README.md +9 -38
  6. data/Rakefile +17 -6
  7. data/bin/console +14 -0
  8. data/bin/setup +8 -0
  9. data/jsonapi-consumer.gemspec +10 -11
  10. data/lib/jsonapi/consumer/associations/base_association.rb +26 -0
  11. data/lib/jsonapi/consumer/associations/belongs_to.rb +30 -0
  12. data/lib/jsonapi/consumer/associations/has_many.rb +26 -0
  13. data/lib/jsonapi/consumer/associations/has_one.rb +19 -0
  14. data/lib/jsonapi/consumer/connection.rb +36 -0
  15. data/lib/jsonapi/consumer/error_collector.rb +91 -0
  16. data/lib/jsonapi/consumer/errors.rb +34 -76
  17. data/lib/jsonapi/consumer/formatter.rb +145 -0
  18. data/lib/jsonapi/consumer/helpers/callbacks.rb +27 -0
  19. data/lib/jsonapi/consumer/helpers/dirty.rb +71 -0
  20. data/lib/jsonapi/consumer/helpers/dynamic_attributes.rb +83 -0
  21. data/lib/jsonapi/consumer/helpers/uri.rb +9 -0
  22. data/lib/jsonapi/consumer/implementation.rb +12 -0
  23. data/lib/jsonapi/consumer/included_data.rb +49 -0
  24. data/lib/jsonapi/consumer/linking/links.rb +22 -0
  25. data/lib/jsonapi/consumer/linking/top_level_links.rb +39 -0
  26. data/lib/jsonapi/consumer/meta_data.rb +19 -0
  27. data/lib/jsonapi/consumer/middleware/json_request.rb +26 -0
  28. data/lib/jsonapi/consumer/middleware/parse_json.rb +22 -23
  29. data/lib/jsonapi/consumer/middleware/status.rb +41 -0
  30. data/lib/jsonapi/consumer/paginating/paginator.rb +89 -0
  31. data/lib/jsonapi/consumer/parsers/parser.rb +113 -0
  32. data/lib/jsonapi/consumer/query/builder.rb +212 -0
  33. data/lib/jsonapi/consumer/query/requestor.rb +67 -0
  34. data/lib/jsonapi/consumer/relationships/relations.rb +56 -0
  35. data/lib/jsonapi/consumer/relationships/top_level_relations.rb +30 -0
  36. data/lib/jsonapi/consumer/resource.rb +514 -54
  37. data/lib/jsonapi/consumer/result_set.rb +25 -0
  38. data/lib/jsonapi/consumer/schema.rb +153 -0
  39. data/lib/jsonapi/consumer/utils.rb +28 -0
  40. data/lib/jsonapi/consumer/version.rb +1 -1
  41. data/lib/jsonapi/consumer.rb +59 -34
  42. metadata +51 -111
  43. data/.rspec +0 -2
  44. data/CHANGELOG.md +0 -36
  45. data/lib/jsonapi/consumer/middleware/raise_error.rb +0 -21
  46. data/lib/jsonapi/consumer/middleware/request_headers.rb +0 -20
  47. data/lib/jsonapi/consumer/middleware/request_timeout.rb +0 -9
  48. data/lib/jsonapi/consumer/middleware.rb +0 -5
  49. data/lib/jsonapi/consumer/parser.rb +0 -75
  50. data/lib/jsonapi/consumer/query/base.rb +0 -34
  51. data/lib/jsonapi/consumer/query/create.rb +0 -9
  52. data/lib/jsonapi/consumer/query/delete.rb +0 -10
  53. data/lib/jsonapi/consumer/query/find.rb +0 -16
  54. data/lib/jsonapi/consumer/query/new.rb +0 -15
  55. data/lib/jsonapi/consumer/query/update.rb +0 -11
  56. data/lib/jsonapi/consumer/query.rb +0 -5
  57. data/lib/jsonapi/consumer/resource/association_concern.rb +0 -203
  58. data/lib/jsonapi/consumer/resource/attributes_concern.rb +0 -70
  59. data/lib/jsonapi/consumer/resource/connection_concern.rb +0 -99
  60. data/lib/jsonapi/consumer/resource/finders_concern.rb +0 -28
  61. data/lib/jsonapi/consumer/resource/object_build_concern.rb +0 -28
  62. data/lib/jsonapi/consumer/resource/serializer_concern.rb +0 -63
  63. data/spec/fixtures/.gitkeep +0 -0
  64. data/spec/fixtures/resources.rb +0 -45
  65. data/spec/fixtures/responses.rb +0 -64
  66. data/spec/jsonapi/consumer/associations_spec.rb +0 -166
  67. data/spec/jsonapi/consumer/attributes_spec.rb +0 -27
  68. data/spec/jsonapi/consumer/connection_spec.rb +0 -147
  69. data/spec/jsonapi/consumer/error_handling_spec.rb +0 -37
  70. data/spec/jsonapi/consumer/object_build_spec.rb +0 -20
  71. data/spec/jsonapi/consumer/parser_spec.rb +0 -39
  72. data/spec/jsonapi/consumer/resource_spec.rb +0 -62
  73. data/spec/jsonapi/consumer/serializer_spec.rb +0 -41
  74. data/spec/spec_helper.rb +0 -97
  75. data/spec/support/.gitkeep +0 -0
  76. data/spec/support/load_fixtures.rb +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0279cf38de5603887f2d3f092240e8e687321ca9
4
- data.tar.gz: 7b34f561d00437e45054114cc6f1b81177e30129
2
+ SHA256:
3
+ metadata.gz: 1dada35491a8ef6e7c3128cb4636bb126a903f5d2081998719033486b6252246
4
+ data.tar.gz: 16ea1a557a08f41f8581edae35e5fd83820ea0b3915a719121ddf0e14b45a5a7
5
5
  SHA512:
6
- metadata.gz: 598a4b6321e2e7d742f8d781b095fb9f9eb2ade74eadf1035b110c8cbbef244d7a34c256a6a50e15850e3d3177038b5bb0d6549ce5eb81a4239a2f2d17754833
7
- data.tar.gz: e1068ea2f333ca27a0f536628e2c3165f2af473703a71331553d579b62994045d7d6533bc30b9f841d59c987e290a0224d6ed168b1d88fa1ced9dffbd81ae4b7
6
+ metadata.gz: 39d0ca8e2933a7e73f005bcd95d45fb43ffa0d193f41017cc768cb5721c15a3ce36eed77c734a9e0c9fe800f4a31038d65aa97dfc566daf4ae46531eb73e02f8
7
+ data.tar.gz: 2f4985cc2d24da2721699a752938b7c2a78238e6cf786c16c708a71e8af9091bb5f50b2f043480c9558c8ad68cb7289aae9440eafc53d5d6ac61605e67e92aa5
@@ -0,0 +1,27 @@
1
+ # Ruby CircleCI 2.0 configuration file
2
+ #
3
+ # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
+ #
5
+ version: 2
6
+ jobs:
7
+ build:
8
+ docker:
9
+ # specify the version you desire here
10
+ - image: circleci/ruby:2.5
11
+ working_directory: ~/jsonapi_consumer
12
+
13
+ steps:
14
+ - checkout
15
+
16
+ - run:
17
+ name: install dependencies
18
+ command: |
19
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
20
+
21
+ - run:
22
+ command: env CI=true bundle exec rake test
23
+ when: always
24
+
25
+ # collect reports
26
+ - store_test_results:
27
+ path: /tmp/reports
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /.bundle/
2
+ /.ruby-version
2
3
  /.yardoc
3
4
  /Gemfile.lock
4
5
  /_yardoc/
data/Gemfile CHANGED
@@ -1,7 +1,9 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
2
4
 
3
- # Specify your gem's dependencies in jsonapi-consumer.gemspec
4
5
  gemspec
5
6
 
6
- gem 'fsevent'
7
- gem 'guard-rspec', require: false
7
+ gem 'rake'
8
+ gem 'minitest-ci'
9
+ gem 'pry'
data/README.md CHANGED
@@ -2,33 +2,30 @@
2
2
 
3
3
  An ActiveModel-compliant consumer framework for communicating with JSONAPI-based APIs.
4
4
 
5
- [![Build Status](https://travis-ci.org/jsmestad/jsonapi-consumer.svg?branch=master)](https://travis-ci.org/jsmestad/jsonapi-consumer)
5
+ [![CircleCI](https://circleci.com/gh/jsmestad/jsonapi-consumer.svg?style=svg)](https://circleci.com/gh/jsmestad/jsonapi-consumer)
6
6
 
7
7
  ## Installation
8
8
 
9
9
  Add this line to your application's Gemfile:
10
10
 
11
11
  ```ruby
12
- gem 'jsonapi-consumer'
12
+ gem 'jsonapi-consumer', '~> 1.0'
13
13
  ```
14
14
 
15
15
  And then execute:
16
16
 
17
17
  $ bundle
18
18
 
19
- Or install it yourself as:
20
-
21
- $ gem install jsonapi-consumer
22
-
23
19
  ## Usage
24
20
 
25
21
  It's suggested to create a base resource for the whole API that you can re-use.
26
22
 
27
23
  ```ruby
28
- class Base
29
- include JSONAPI::Consumer::Resource
30
-
31
- self.host = 'http://localhost:3000/api/'
24
+ class Base < JSONAPI::Consumer::Resource
25
+ # self.connection_options = {} # Faraday connection options
26
+ # self.json_key_format = :dasherized_key # (default: underscored_key)
27
+ # self.route_format = :dasherized_route # (default: underscored_route)
28
+ self.site = 'http://localhost:3000/api/'
32
29
  end
33
30
  ```
34
31
 
@@ -36,7 +33,6 @@ Then inherit from that Base class for each resource defined in your API.
36
33
 
37
34
  ```ruby
38
35
  module Blog
39
-
40
36
  class Author < Base
41
37
  has_many :posts, class_name: 'Blog::Post'
42
38
  end
@@ -53,36 +49,9 @@ module Blog
53
49
  class Comment < Base
54
50
 
55
51
  end
56
-
57
52
  end
58
53
  ```
59
54
 
60
- #### Additional Features
61
-
62
- ##### Dynamic Objects
63
-
64
- By default calling `.new` or `.build` on a resource will give you an empty
65
- object with no attributes defined. This is less than ideal when building forms
66
- with something like Rails' FormBuilder.
67
-
68
- We suggest setting up your model to do a `GET /{resource_name}/new` if your
69
- server supports it and using `.build` instead of `.new`. This will populate the
70
- object with defaults set by the server response.
71
-
72
- ```ruby
73
- class User
74
- include JSONAPI::Consumer::Resource
75
-
76
- self.request_new_object_on_build = true
77
-
78
- # .build will now call GET /users/new
79
- end
80
- ```
81
-
82
- #### Testing
83
-
84
- We suggest [Webmock](https://github.com/bblimke/webmock) at this stage in
85
- development. We plan to add test helpers before the first major release.
86
55
 
87
56
  ## Contributing
88
57
 
@@ -95,3 +64,5 @@ development. We plan to add test helpers before the first major release.
95
64
  ## Copyright & License
96
65
 
97
66
  JSONAPI::Consumer is distributed under the Apache 2.0 License. See LICENSE.txt file for more information.
67
+
68
+ Version v1 is a rewrite is based on the excellent work by [json_api_client](https://github.com/chingor13/json_api_client) [v1.5.3](https://github.com/chingor13/json_api_client/tree/v1.5.3).
data/Rakefile CHANGED
@@ -1,12 +1,23 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
- begin
4
- require 'rspec/core/rake_task'
3
+ require 'rdoc/task'
5
4
 
6
- RSpec::Core::RakeTask.new(:spec)
5
+ RDoc::Task.new(:rdoc) do |rdoc|
6
+ rdoc.rdoc_dir = 'rdoc'
7
+ rdoc.title = 'JsonApiClient'
8
+ rdoc.options << '--line-numbers'
9
+ rdoc.rdoc_files.include('README.rdoc')
10
+ rdoc.rdoc_files.include('lib/**/*.rb')
11
+ end
12
+
13
+
14
+ require 'rake/testtask'
7
15
 
8
- task default: :spec
9
- rescue LoadError
10
- # no rspec available
16
+ Rake::TestTask.new(:test) do |t|
17
+ t.libs << 'lib'
18
+ t.libs << 'test'
19
+ t.pattern = 'test/**/*_test.rb'
20
+ t.verbose = false
11
21
  end
12
22
 
23
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mikasa_client"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start(__FILE__)
12
+
13
+ # require "irb"
14
+ # IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -13,20 +13,19 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://github.com/jsmestad/jsonapi-consumer"
14
14
  spec.license = "Apache-2.0"
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0") - ['Guardfile', '.travis.yml']
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
21
  spec.require_paths = ["lib"]
20
22
 
21
- spec.add_runtime_dependency "activemodel"
22
- spec.add_runtime_dependency "activesupport"
23
- spec.add_runtime_dependency "faraday", "~> 0.9.0"
23
+ spec.add_runtime_dependency "activesupport", '>= 3.2'
24
+ spec.add_runtime_dependency "faraday", '>= 0.9'
24
25
  spec.add_runtime_dependency "faraday_middleware"
26
+ spec.add_runtime_dependency "addressable", '~> 2.5.2'
27
+ spec.add_runtime_dependency "activemodel", '>= 3.2'
25
28
 
26
- spec.add_development_dependency "bundler", "~> 1.6"
27
- spec.add_development_dependency "factory_girl"
28
- spec.add_development_dependency "rake", "~> 10.0"
29
- spec.add_development_dependency "rspec"
30
- spec.add_development_dependency "rspec-its"
31
29
  spec.add_development_dependency "webmock"
30
+ spec.add_development_dependency "mocha"
32
31
  end
@@ -0,0 +1,26 @@
1
+ module JSONAPI::Consumer
2
+ module Associations
3
+ class BaseAssociation
4
+ attr_reader :attr_name, :klass, :options
5
+ def initialize(attr_name, klass, options = {})
6
+ @attr_name = attr_name
7
+ @klass = klass
8
+ @options = options
9
+ end
10
+
11
+ def association_class
12
+ @association_class ||= Utils.compute_type(klass, options.fetch(:class_name) do
13
+ attr_name.to_s.classify
14
+ end)
15
+ end
16
+
17
+ def data(url)
18
+ from_result_set(association_class.requestor.linked(url))
19
+ end
20
+
21
+ def from_result_set(result_set)
22
+ result_set.to_a
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ module JSONAPI::Consumer
2
+ module Associations
3
+ module BelongsTo
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def belongs_to(attr_name, options = {})
8
+ # self.associations = self.associations + [HasOne::Association.new(attr_name, self, options)]
9
+ self.associations += [BelongsTo::Association.new(attr_name, self, options)]
10
+ end
11
+ end
12
+
13
+ class Association < BaseAssociation
14
+ include Helpers::URI
15
+ def param
16
+ :"#{attr_name}_id"
17
+ end
18
+
19
+ def to_prefix_path(formatter)
20
+ "#{formatter.format(attr_name.to_s.pluralize)}/%{#{param}}"
21
+ end
22
+
23
+ def set_prefix_path(attrs, formatter)
24
+ attrs[param] = encode_part(attrs[param]) if attrs.key?(param)
25
+ to_prefix_path(formatter) % attrs
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module JSONAPI::Consumer
2
+ module Associations
3
+ module HasMany
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def has_many(attr_name, options = {})
8
+ self.associations = self.associations + [HasMany::Association.new(attr_name, self, options)]
9
+ end
10
+ end
11
+
12
+ class Association < BaseAssociation
13
+ def query_builder(url)
14
+ association_class.query_builder.new(
15
+ association_class,
16
+ association_class.requestor_class.new(association_class, url)
17
+ )
18
+ end
19
+
20
+ def data(url)
21
+ query_builder(url)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module JSONAPI::Consumer
2
+ module Associations
3
+ module HasOne
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def has_one(attr_name, options = {})
8
+ self.associations += [HasOne::Association.new(attr_name, self, options)]
9
+ end
10
+ end
11
+
12
+ class Association < BaseAssociation
13
+ def from_result_set(result_set)
14
+ result_set.first
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ module JSONAPI::Consumer
2
+ class Connection
3
+
4
+ attr_reader :faraday
5
+
6
+ def initialize(options = {})
7
+ site = options.fetch(:site)
8
+ connection_options = options.slice(:proxy, :ssl, :request, :headers, :params)
9
+ adapter_options = Array(options.fetch(:adapter, Faraday.default_adapter))
10
+ @faraday = Faraday.new(site, connection_options) do |builder|
11
+ builder.request :json
12
+ builder.use Middleware::JsonRequest
13
+ builder.use Middleware::Status
14
+ builder.use Middleware::ParseJson
15
+ builder.adapter(*adapter_options)
16
+ end
17
+ yield(self) if block_given?
18
+ end
19
+
20
+ # insert middleware before ParseJson - middleware executed in reverse order -
21
+ # inserted middleware will run after json parsed
22
+ def use(middleware, *args, &block)
23
+ return if faraday.builder.locked?
24
+ faraday.builder.insert_before(Middleware::ParseJson, middleware, *args, &block)
25
+ end
26
+
27
+ def delete(middleware)
28
+ faraday.builder.delete(middleware)
29
+ end
30
+
31
+ def run(request_method, path, params = {}, headers = {})
32
+ faraday.send(request_method, path, params, headers)
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,91 @@
1
+ module JSONAPI::Consumer
2
+ class ErrorCollector < Array
3
+ class Error
4
+ delegate :[], to: :attrs
5
+
6
+ def initialize(attrs = {})
7
+ @attrs = (attrs || {}).with_indifferent_access
8
+ end
9
+
10
+ def id
11
+ attrs[:id]
12
+ end
13
+
14
+ def about
15
+ res = attrs.fetch(:links, {})
16
+ res ? res[:about] : {}
17
+ end
18
+
19
+ def status
20
+ attrs[:status]
21
+ end
22
+
23
+ def code
24
+ attrs[:code]
25
+ end
26
+
27
+ def title
28
+ attrs[:title]
29
+ end
30
+
31
+ def detail
32
+ attrs[:detail]
33
+ end
34
+
35
+ def source_parameter
36
+ source[:parameter]
37
+ end
38
+
39
+ def source_pointer
40
+ source[:pointer]
41
+ end
42
+
43
+ def error_key
44
+ if source_pointer && source_pointer != "/data"
45
+ source_pointer.split("/").last
46
+ else
47
+ "base"
48
+ end
49
+ end
50
+
51
+ def error_msg
52
+ msg = title || detail || "invalid"
53
+ if source_parameter
54
+ "#{source_parameter} #{msg}"
55
+ else
56
+ msg
57
+ end
58
+ end
59
+
60
+ def source
61
+ res = attrs.fetch(:source, {})
62
+ res ? res : {}
63
+ end
64
+
65
+ def meta
66
+ MetaData.new(attrs.fetch(:meta, {}))
67
+ end
68
+
69
+ protected
70
+
71
+ attr_reader :attrs
72
+ end
73
+
74
+ def initialize(error_data)
75
+ super(error_data.map do |data|
76
+ Error.new(data)
77
+ end)
78
+ end
79
+
80
+ def full_messages
81
+ map(&:title)
82
+ end
83
+
84
+ def [](source)
85
+ map do |error|
86
+ error.error_key == source
87
+ end
88
+ end
89
+
90
+ end
91
+ end
@@ -1,98 +1,56 @@
1
- # JSONAPI::Consumer::Errors is responsible for wrapping API errors into a consistent
2
- # form. Handling or Rescuing from these errors is left up to the client, but
3
- # a possible way to do this is detailed below.
4
- #
5
- # # lib/errors/rescue_error.rb
6
- # module Errors
7
- # module RescueError
8
- #
9
- # def self.included(base)
10
- # base.rescue_from Errors::ResponseError do |e|
11
- # render "public/#{e.code}", :status => e.code
12
- # end
13
- # end
14
- #
15
- # end
16
- # end
17
- #
18
1
  module JSONAPI::Consumer
19
2
  module Errors
20
- class ConnectionNotConfigured < StandardError; end
21
- class RecordNotSaved < StandardError; end
22
-
23
- class ServerNotResponding < StandardError
24
- def message
25
- 'Server did not respond in a timely fashion. Is it running?'
3
+ class ApiError < StandardError
4
+ attr_reader :env
5
+ def initialize(env)
6
+ @env = env
26
7
  end
27
8
  end
28
9
 
29
- # Error constants
30
- BAD_REQUEST = 400
31
- NOT_AUTHORIZED = 401
32
- FORBIDDEN = 403
33
- NOT_FOUND = 404
34
- METHOD_NOT_ALLOWED = 405
35
- BAD_FORMAT = 406
36
- REQUESTED_RANGE_NOT_SATISFIABLE = 416
37
- UNPROCESSABLE_ENTITY = 422
38
- INTERNAL_SERVER_ERROR = 500
10
+ class ClientError < ApiError
11
+ end
39
12
 
40
- # Base subclass for all response errors
41
- class ResponseError < StandardError
42
- attr_accessor :response
13
+ class AccessDenied < ClientError
14
+ end
43
15
 
44
- def initialize(message=nil, response=nil)
45
- super(message)
46
- self.response = response
47
- end
16
+ class NotAuthorized < ClientError
17
+ end
48
18
 
49
- def errors
50
- response[:body].fetch('errors', [])
51
- end
19
+ class ConnectionError < ApiError
20
+ end
52
21
 
53
- def response_body
54
- response[:body]
22
+ class ServerError < ApiError
23
+ def message
24
+ "Internal server error"
55
25
  end
56
26
  end
57
27
 
58
- class << self
59
-
60
- # Returns a hash of error names to response codes.
61
- def error_constants
62
- constants.each_with_object({}) do |name, hash|
63
- # Ignore any class constants
64
- next if (code = Errors.const_get(name)).is_a?(Class)
65
- hash[name] = code
66
- end
28
+ class Conflict < ServerError
29
+ def message
30
+ "Resource already exists"
67
31
  end
32
+ end
68
33
 
69
- # Returns a class name from a constant name.
70
- def class_name_for_error_name(name)
71
- name.to_s.titleize.gsub(' ', '')
34
+ class NotFound < ServerError
35
+ attr_reader :uri
36
+ def initialize(uri)
37
+ @uri = uri
72
38
  end
73
-
74
- # Returns the error class for a given constant name.
75
- # Default to InternalServerError.
76
- def class_for_error_name(name)
77
- class_name = class_name_for_error_name(name)
78
- const_defined?(class_name) ? Errors.const_get(class_name) : Errors::InternalServerError
39
+ def message
40
+ "Couldn't find resource at: #{uri.to_s}"
79
41
  end
42
+ end
80
43
 
81
- # Returns the error class for a given error code.
82
- # Default to InternalServerError.
83
- def class_for_error_code(code)
84
- name = error_constants.select { |k, v| v == code }.keys.first
85
- name.present? ? class_for_error_name(name) : Errors::InternalServerError
44
+ class UnexpectedStatus < ServerError
45
+ attr_reader :code, :uri
46
+ def initialize(code, uri)
47
+ @code = code
48
+ @uri = uri
49
+ end
50
+ def message
51
+ "Unexpected response status: #{code} from: #{uri.to_s}"
86
52
  end
87
53
  end
88
- end
89
54
 
90
- # Dynamically creates a subclass of ResponseError for each error constant.
91
- # Adds a code method to return the correct response code.
92
- # Adds the new class to the constants in the Errors module.
93
- Errors.error_constants.each do |name, code|
94
- klass = Class.new(Errors::ResponseError)
95
- klass.send(:define_method, :code) { code }
96
- Errors.const_set(Errors.class_name_for_error_name(name), klass)
97
55
  end
98
56
  end