jsonapi-consumer 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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