restful_resource 2.3.0 → 2.4.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +20 -0
- data/.codeclimate.yml +3 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +203 -0
- data/CHANGELOG.md +5 -0
- data/README.md +1 -1
- data/Rakefile +5 -5
- data/lib/restful_resource/associations.rb +10 -8
- data/lib/restful_resource/base.rb +36 -33
- data/lib/restful_resource/http_client.rb +50 -48
- data/lib/restful_resource/instrumentation.rb +17 -20
- data/lib/restful_resource/null_logger.rb +1 -2
- data/lib/restful_resource/open_object.rb +2 -2
- data/lib/restful_resource/paginated_array.rb +3 -1
- data/lib/restful_resource/rails_validations.rb +12 -12
- data/lib/restful_resource/redirections.rb +5 -6
- data/lib/restful_resource/request.rb +0 -1
- data/lib/restful_resource/resource_id_missing_error.rb +1 -1
- data/lib/restful_resource/response.rb +4 -2
- data/lib/restful_resource/version.rb +1 -1
- data/restful_resource.gemspec +26 -25
- data/spec/fixtures.rb +7 -7
- data/spec/restful_resource/associations_spec.rb +23 -21
- data/spec/restful_resource/base_authorization_spec.rb +6 -7
- data/spec/restful_resource/base_spec.rb +134 -117
- data/spec/restful_resource/http_client_configuration_spec.rb +20 -19
- data/spec/restful_resource/http_client_spec.rb +38 -38
- data/spec/restful_resource/open_object_spec.rb +8 -8
- data/spec/restful_resource/rails_validations_spec.rb +68 -68
- data/spec/restful_resource/redirections_spec.rb +26 -26
- data/spec/spec_helper.rb +3 -4
- metadata +44 -13
- data/circle.yml +0 -3
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            # Use the Faraday-Typhoeus adapter provided by Typhoeus, not Faraday
         | 
| 2 | 
            -
            require  | 
| 2 | 
            +
            require 'typhoeus/adapters/faraday'
         | 
| 3 3 |  | 
| 4 4 | 
             
            module RestfulResource
         | 
| 5 5 | 
             
              class HttpClient
         | 
| @@ -7,15 +7,16 @@ module RestfulResource | |
| 7 7 | 
             
                  attr_reader :request, :response
         | 
| 8 8 |  | 
| 9 9 | 
             
                  def initialize(request, response = nil)
         | 
| 10 | 
            -
                    @request | 
| 10 | 
            +
                    @request = request
         | 
| 11 | 
            +
                    @response = assign_response(response)
         | 
| 11 12 | 
             
                  end
         | 
| 12 13 |  | 
| 13 14 | 
             
                  def assign_response(response = nil)
         | 
| 14 | 
            -
                    if response
         | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 15 | 
            +
                    @response = if response
         | 
| 16 | 
            +
                                  Response.new(body: response[:body], headers: response[:headers], status: response[:status])
         | 
| 17 | 
            +
                                else
         | 
| 18 | 
            +
                                  Response.new
         | 
| 19 | 
            +
                                end
         | 
| 19 20 | 
             
                  end
         | 
| 20 21 | 
             
                end
         | 
| 21 22 |  | 
| @@ -24,7 +25,7 @@ module RestfulResource | |
| 24 25 |  | 
| 25 26 | 
             
                class ResourceNotFound < HttpError
         | 
| 26 27 | 
             
                  def message
         | 
| 27 | 
            -
                     | 
| 28 | 
            +
                    'HTTP 404: Resource Not Found'
         | 
| 28 29 | 
             
                  end
         | 
| 29 30 | 
             
                end
         | 
| 30 31 |  | 
| @@ -40,44 +41,45 @@ module RestfulResource | |
| 40 41 |  | 
| 41 42 | 
             
                class BadGateway < RetryableError
         | 
| 42 43 | 
             
                  def message
         | 
| 43 | 
            -
                     | 
| 44 | 
            +
                    'HTTP 502: Bad gateway'
         | 
| 44 45 | 
             
                  end
         | 
| 45 46 | 
             
                end
         | 
| 46 47 |  | 
| 47 48 | 
             
                class ServiceUnavailable < RetryableError
         | 
| 48 49 | 
             
                  def message
         | 
| 49 | 
            -
                     | 
| 50 | 
            +
                    'HTTP 503: Service unavailable'
         | 
| 50 51 | 
             
                  end
         | 
| 51 52 | 
             
                end
         | 
| 52 53 |  | 
| 53 54 | 
             
                class Timeout < RetryableError
         | 
| 54 55 | 
             
                  def message
         | 
| 55 | 
            -
                     | 
| 56 | 
            +
                    'Timeout: Service not responding'
         | 
| 56 57 | 
             
                  end
         | 
| 57 58 | 
             
                end
         | 
| 58 59 |  | 
| 59 60 | 
             
                class TooManyRequests < RetryableError
         | 
| 60 61 | 
             
                  def message
         | 
| 61 | 
            -
                     | 
| 62 | 
            +
                    'HTTP 429: Too Many Requests'
         | 
| 62 63 | 
             
                  end
         | 
| 63 64 | 
             
                end
         | 
| 64 65 |  | 
| 65 66 | 
             
                class ClientError < RetryableError
         | 
| 66 67 | 
             
                  def message
         | 
| 67 | 
            -
                     | 
| 68 | 
            +
                    'There was some client error'
         | 
| 68 69 | 
             
                  end
         | 
| 69 70 | 
             
                end
         | 
| 70 71 |  | 
| 71 72 | 
             
                def initialize(username: nil,
         | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 73 | 
            +
                  password: nil,
         | 
| 74 | 
            +
                  auth_token: nil,
         | 
| 75 | 
            +
                  logger: nil,
         | 
| 76 | 
            +
                  cache_store: nil,
         | 
| 77 | 
            +
                  connection: nil,
         | 
| 78 | 
            +
                  instrumentation: {},
         | 
| 79 | 
            +
                  open_timeout: 2,
         | 
| 80 | 
            +
                  timeout: 10,
         | 
| 81 | 
            +
                  faraday_config: nil,
         | 
| 82 | 
            +
                  faraday_options: nil)
         | 
| 81 83 | 
             
                  api_name = instrumentation[:api_name]            ||= 'api'
         | 
| 82 84 | 
             
                  instrumentation[:request_instrument_name]        ||= "http.#{api_name}"
         | 
| 83 85 | 
             
                  instrumentation[:cache_instrument_name]          ||= "http_cache.#{api_name}"
         | 
| @@ -85,11 +87,13 @@ module RestfulResource | |
| 85 87 |  | 
| 86 88 | 
             
                  if instrumentation[:metric_class]
         | 
| 87 89 | 
             
                    @metrics = Instrumentation.new(instrumentation.slice(:app_name,
         | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 90 | 
            +
                      :api_name,
         | 
| 91 | 
            +
                      :request_instrument_name,
         | 
| 92 | 
            +
                      :cache_instrument_name,
         | 
| 93 | 
            +
                      :server_cache_instrument_name,
         | 
| 94 | 
            +
                      :metric_class
         | 
| 95 | 
            +
                    )
         | 
| 96 | 
            +
                                                  )
         | 
| 93 97 | 
             
                    @metrics.subscribe_to_notifications
         | 
| 94 98 | 
             
                  end
         | 
| 95 99 |  | 
| @@ -100,7 +104,9 @@ module RestfulResource | |
| 100 104 | 
             
                                                                    request_instrument_name: instrumentation.fetch(:request_instrument_name, nil),
         | 
| 101 105 | 
             
                                                                    cache_instrument_name: instrumentation.fetch(:cache_instrument_name, nil),
         | 
| 102 106 | 
             
                                                                    server_cache_instrument_name: instrumentation.fetch(:server_cache_instrument_name, nil),
         | 
| 103 | 
            -
                                                                    faraday_config: faraday_config | 
| 107 | 
            +
                                                                    faraday_config: faraday_config,
         | 
| 108 | 
            +
                                                                    faraday_options: faraday_options
         | 
| 109 | 
            +
                                                                   )
         | 
| 104 110 |  | 
| 105 111 | 
             
                  if auth_token
         | 
| 106 112 | 
             
                    @connection.headers[:authorization] = "Bearer #{auth_token}"
         | 
| @@ -168,20 +174,19 @@ module RestfulResource | |
| 168 174 | 
             
                attr_reader :default_open_timeout, :default_timeout
         | 
| 169 175 |  | 
| 170 176 | 
             
                def initialize_connection(logger: nil,
         | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 177 | 
            +
                  cache_store: nil,
         | 
| 178 | 
            +
                  instrumenter: nil,
         | 
| 179 | 
            +
                  request_instrument_name: nil,
         | 
| 180 | 
            +
                  cache_instrument_name: nil,
         | 
| 181 | 
            +
                  server_cache_instrument_name: nil,
         | 
| 182 | 
            +
                  faraday_config: nil,
         | 
| 183 | 
            +
                  faraday_options: nil)
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                  @connection = Faraday.new(nil, faraday_options) do |b|
         | 
| 179 186 | 
             
                    b.request :json
         | 
| 180 187 | 
             
                    b.response :raise_error
         | 
| 181 188 |  | 
| 182 | 
            -
                    if logger
         | 
| 183 | 
            -
                      b.response :logger, logger
         | 
| 184 | 
            -
                    end
         | 
| 189 | 
            +
                    b.response :logger, logger if logger
         | 
| 185 190 |  | 
| 186 191 | 
             
                    if server_cache_instrument_name
         | 
| 187 192 | 
             
                      b.use :cdn_metrics, instrumenter: instrumenter,
         | 
| @@ -195,13 +200,9 @@ module RestfulResource | |
| 195 200 | 
             
                                         instrument_name: cache_instrument_name
         | 
| 196 201 | 
             
                    end
         | 
| 197 202 |  | 
| 198 | 
            -
                    if instrumenter && request_instrument_name
         | 
| 199 | 
            -
                      b.use :instrumentation, name: request_instrument_name
         | 
| 200 | 
            -
                    end
         | 
| 203 | 
            +
                    b.use :instrumentation, name: request_instrument_name if instrumenter && request_instrument_name
         | 
| 201 204 |  | 
| 202 | 
            -
                     | 
| 203 | 
            -
                      faraday_config.call(b)
         | 
| 204 | 
            -
                    end
         | 
| 205 | 
            +
                    faraday_config&.call(b)
         | 
| 205 206 |  | 
| 206 207 | 
             
                    b.response :encoding
         | 
| 207 208 | 
             
                    b.use :gzip
         | 
| @@ -211,7 +212,7 @@ module RestfulResource | |
| 211 212 | 
             
                end
         | 
| 212 213 |  | 
| 213 214 | 
             
                def build_user_agent(app_name)
         | 
| 214 | 
            -
                  parts = [ | 
| 215 | 
            +
                  parts = ['carwow/internal']
         | 
| 215 216 | 
             
                  parts << "RestfulResource/#{VERSION}"
         | 
| 216 217 | 
             
                  parts << "(#{app_name})" if app_name
         | 
| 217 218 | 
             
                  parts << "Faraday/#{Faraday::VERSION}"
         | 
| @@ -232,10 +233,11 @@ module RestfulResource | |
| 232 233 | 
             
                rescue Faraday::ConnectionFailed
         | 
| 233 234 | 
             
                  raise
         | 
| 234 235 | 
             
                rescue Faraday::TimeoutError
         | 
| 235 | 
            -
                  raise HttpClient::Timeout | 
| 236 | 
            +
                  raise HttpClient::Timeout, request
         | 
| 236 237 | 
             
                rescue Faraday::ClientError => e
         | 
| 237 238 | 
             
                  response = e.response
         | 
| 238 | 
            -
                  raise ClientError | 
| 239 | 
            +
                  raise ClientError, request unless response
         | 
| 240 | 
            +
             | 
| 239 241 | 
             
                  case response[:status]
         | 
| 240 242 | 
             
                  when 404 then raise HttpClient::ResourceNotFound.new(request, response)
         | 
| 241 243 | 
             
                  when 409 then raise HttpClient::Conflict.new(request, response)
         | 
| @@ -2,7 +2,6 @@ require 'active_support/notifications' | |
| 2 2 |  | 
| 3 3 | 
             
            module RestfulResource
         | 
| 4 4 | 
             
              class Instrumentation
         | 
| 5 | 
            -
             | 
| 6 5 | 
             
                def initialize(app_name:, api_name:, request_instrument_name:, cache_instrument_name:, server_cache_instrument_name:, metric_class:)
         | 
| 7 6 | 
             
                  @app_name = app_name
         | 
| 8 7 | 
             
                  @api_name = api_name
         | 
| @@ -50,18 +49,18 @@ module RestfulResource | |
| 50 49 | 
             
                    # count#quotes_site.research_site_api.cache_hit=1
         | 
| 51 50 | 
             
                    # count#quotes_site.research_site_api.api_v2_cap_derivatives.cache_hit=1
         | 
| 52 51 | 
             
                    case cache_status
         | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 52 | 
            +
                    when :fresh, :valid
         | 
| 53 | 
            +
                      metric_class.count cache_notifier_namespace(metric: 'cache_hit'), 1
         | 
| 54 | 
            +
                      metric_class.count cache_notifier_namespace(metric: 'cache_hit', event: event), 1
         | 
| 55 | 
            +
                    when :invalid, :miss
         | 
| 56 | 
            +
                      metric_class.count cache_notifier_namespace(metric: 'cache_miss'), 1
         | 
| 57 | 
            +
                      metric_class.count cache_notifier_namespace(metric: 'cache_miss', event: event), 1
         | 
| 58 | 
            +
                    when :unacceptable
         | 
| 59 | 
            +
                      metric_class.count cache_notifier_namespace(metric: 'cache_not_cacheable'), 1
         | 
| 60 | 
            +
                      metric_class.count cache_notifier_namespace(metric: 'cache_not_cacheable', event: event), 1
         | 
| 61 | 
            +
                    when :bypass
         | 
| 62 | 
            +
                      metric_class.count cache_notifier_namespace(metric: 'cache_bypass'), 1
         | 
| 63 | 
            +
                      metric_class.count cache_notifier_namespace(metric: 'cache_bypass', event: event), 1
         | 
| 65 64 | 
             
                    end
         | 
| 66 65 | 
             
                  end
         | 
| 67 66 |  | 
| @@ -71,7 +70,7 @@ module RestfulResource | |
| 71 70 | 
             
                    client_cache_status = event.payload.fetch(:client_cache_status, nil)
         | 
| 72 71 | 
             
                    server_cache_status = event.payload.fetch(:server_cache_status, nil)
         | 
| 73 72 |  | 
| 74 | 
            -
                    if client_cache_status.nil? || !client_cache_status.in?([ | 
| 73 | 
            +
                    if client_cache_status.nil? || !client_cache_status.in?(%i[fresh valid])
         | 
| 75 74 | 
             
                      # Outputs log lines like:
         | 
| 76 75 | 
             
                      # count#quotes_site.research_site_api.server_cache_hit=1
         | 
| 77 76 | 
             
                      # count#quotes_site.research_site_api.api_v2_cap_derivatives.server_cache_hit=1
         | 
| @@ -100,19 +99,17 @@ module RestfulResource | |
| 100 99 | 
             
                end
         | 
| 101 100 |  | 
| 102 101 | 
             
                def validate_metric_class!
         | 
| 103 | 
            -
                  metric_methods = %i | 
| 104 | 
            -
                  if metric_methods.any? {|m| !metric_class.respond_to?(m) }
         | 
| 105 | 
            -
                    raise ArgumentError.new "Metric class '#{metric_class}' does not respond to #{metric_methods.join ','}"
         | 
| 106 | 
            -
                  end
         | 
| 102 | 
            +
                  metric_methods = %i[count sample measure]
         | 
| 103 | 
            +
                  raise ArgumentError, "Metric class '#{metric_class}' does not respond to #{metric_methods.join ','}" if metric_methods.any? { |m| !metric_class.respond_to?(m) }
         | 
| 107 104 | 
             
                end
         | 
| 108 105 |  | 
| 109 106 | 
             
                def cache_notifier_namespace(metric:, event: nil)
         | 
| 110 107 | 
             
                  [app_name, api_name, base_request_path(event), metric].compact.join('.')
         | 
| 111 108 | 
             
                end
         | 
| 112 109 |  | 
| 113 | 
            -
                # | 
| 110 | 
            +
                #  Converts a path like "/api/v2/cap_derivatives/75423/with_colours" to "api_v2_cap_derivatives_with_colours"
         | 
| 114 111 | 
             
                def base_request_path(event)
         | 
| 115 | 
            -
                  path_from_event(event).split('/').drop(1).select {|a| a.match(/\d+/).nil? }.join('_') if event
         | 
| 112 | 
            +
                  path_from_event(event).split('/').drop(1).select { |a| a.match(/\d+/).nil? }.join('_') if event
         | 
| 116 113 | 
             
                end
         | 
| 117 114 |  | 
| 118 115 | 
             
                def path_from_event(event)
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            module RestfulResource
         | 
| 2 2 | 
             
              class OpenObject
         | 
| 3 | 
            -
                def initialize(attributes = {},  | 
| 3 | 
            +
                def initialize(attributes = {}, _hack_for_activeresource = false)
         | 
| 4 4 | 
             
                  @inner_object = OpenStruct.new(attributes)
         | 
| 5 5 | 
             
                end
         | 
| 6 6 |  | 
| @@ -16,7 +16,7 @@ module RestfulResource | |
| 16 16 | 
             
                  super || @inner_object.respond_to?(method, include_private)
         | 
| 17 17 | 
             
                end
         | 
| 18 18 |  | 
| 19 | 
            -
                def as_json(options=nil)
         | 
| 19 | 
            +
                def as_json(options = nil)
         | 
| 20 20 | 
             
                  @inner_object.send(:table).as_json(options)
         | 
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            module RestfulResource
         | 
| 2 2 | 
             
              class PaginatedArray < Array
         | 
| 3 | 
            -
                def initialize(original_array, previous_page_url:, next_page_url:, total_count: | 
| 3 | 
            +
                def initialize(original_array, previous_page_url:, next_page_url:, total_count:)
         | 
| 4 4 | 
             
                  super(original_array)
         | 
| 5 5 |  | 
| 6 6 | 
             
                  @previous_page_url = previous_page_url
         | 
| @@ -21,8 +21,10 @@ module RestfulResource | |
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| 23 23 | 
             
                private
         | 
| 24 | 
            +
             | 
| 24 25 | 
             
                def get_page_from_url(url)
         | 
| 25 26 | 
             
                  return nil unless url
         | 
| 27 | 
            +
             | 
| 26 28 | 
             
                  params = Rack::Utils.parse_query URI(url).query
         | 
| 27 29 | 
             
                  params['page'].to_i
         | 
| 28 30 | 
             
                end
         | 
| @@ -6,13 +6,13 @@ module RestfulResource | |
| 6 6 | 
             
                  rescue HttpClient::UnprocessableEntity => e
         | 
| 7 7 | 
             
                    errors = parse_json(e.response.body)
         | 
| 8 8 | 
             
                    result = nil
         | 
| 9 | 
            -
                    if errors.is_a?(Hash) && errors. | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 9 | 
            +
                    result = if errors.is_a?(Hash) && errors.key?('errors')
         | 
| 10 | 
            +
                               data.merge(errors)
         | 
| 11 | 
            +
                             else
         | 
| 12 | 
            +
                               data.merge(errors: errors)
         | 
| 13 | 
            +
                             end
         | 
| 14 14 | 
             
                    result = result.merge(id: id)
         | 
| 15 | 
            -
                     | 
| 15 | 
            +
                    new(result)
         | 
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 18 | 
             
                  def post(data: {}, **)
         | 
| @@ -30,12 +30,12 @@ module RestfulResource | |
| 30 30 | 
             
                  rescue HttpClient::UnprocessableEntity => e
         | 
| 31 31 | 
             
                    errors = parse_json(e.response.body)
         | 
| 32 32 | 
             
                    result = nil
         | 
| 33 | 
            -
                    if errors.is_a?(Hash) && errors. | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
                     | 
| 33 | 
            +
                    result = if errors.is_a?(Hash) && errors.key?('errors')
         | 
| 34 | 
            +
                               data.merge(errors)
         | 
| 35 | 
            +
                             else
         | 
| 36 | 
            +
                               data.merge(errors: errors)
         | 
| 37 | 
            +
                             end
         | 
| 38 | 
            +
                    new(result)
         | 
| 39 39 | 
             
                  end
         | 
| 40 40 | 
             
                end
         | 
| 41 41 |  | 
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            module RestfulResource
         | 
| 2 2 | 
             
              class MaximumAttemptsReached < StandardError
         | 
| 3 3 | 
             
                def message
         | 
| 4 | 
            -
                   | 
| 4 | 
            +
                  'The maximum attempts limit was reached before the resource was ready'
         | 
| 5 5 | 
             
                end
         | 
| 6 6 | 
             
              end
         | 
| 7 7 |  | 
| @@ -11,12 +11,13 @@ module RestfulResource | |
| 11 11 | 
             
                    def post(data: {}, delay: 1.0, max_attempts: 10, headers: {}, open_timeout: nil, timeout: nil, **params)
         | 
| 12 12 | 
             
                      url = collection_url(params)
         | 
| 13 13 |  | 
| 14 | 
            -
                      response =  | 
| 14 | 
            +
                      response = accept_redirected_result(response: http.post(url, data: data, headers: headers, open_timeout: nil, timeout: nil), delay: delay, max_attempts: max_attempts)
         | 
| 15 15 |  | 
| 16 | 
            -
                       | 
| 16 | 
            +
                      new(parse_json(response.body))
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 19 | 
             
                    private
         | 
| 20 | 
            +
             | 
| 20 21 | 
             
                    def self.accept_redirected_result(response:, delay:, max_attempts:)
         | 
| 21 22 | 
             
                      new_response = response
         | 
| 22 23 | 
             
                      if response.status == 303
         | 
| @@ -32,9 +33,7 @@ module RestfulResource | |
| 32 33 | 
             
                          new_response = http.get(resource_location, headers: {}, open_timeout: nil, timeout: nil)
         | 
| 33 34 | 
             
                        end
         | 
| 34 35 |  | 
| 35 | 
            -
                        if attempts == max_attempts
         | 
| 36 | 
            -
                          raise RestfulResource::MaximumAttemptsReached
         | 
| 37 | 
            -
                        end
         | 
| 36 | 
            +
                        raise RestfulResource::MaximumAttemptsReached if attempts == max_attempts
         | 
| 38 37 | 
             
                      end
         | 
| 39 38 | 
             
                      response = new_response
         | 
| 40 39 | 
             
                    end
         | 
| @@ -2,8 +2,10 @@ module RestfulResource | |
| 2 2 | 
             
              class Response
         | 
| 3 3 | 
             
                attr_reader :body, :headers, :status
         | 
| 4 4 |  | 
| 5 | 
            -
                def initialize(body:  | 
| 6 | 
            -
                  @body | 
| 5 | 
            +
                def initialize(body: '{}', headers: {}, status: nil)
         | 
| 6 | 
            +
                  @body = body
         | 
| 7 | 
            +
                  @headers = headers
         | 
| 8 | 
            +
                  @status = status
         | 
| 7 9 | 
             
                end
         | 
| 8 10 | 
             
              end
         | 
| 9 11 | 
             
            end
         | 
    
        data/restful_resource.gemspec
    CHANGED
    
    | @@ -1,36 +1,37 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            lib = File.expand_path('../lib', __FILE__)
         | 
| 1 | 
            +
            lib = File.expand_path('lib', __dir__)
         | 
| 3 2 | 
             
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 3 | 
             
            require 'restful_resource/version'
         | 
| 5 4 |  | 
| 6 5 | 
             
            Gem::Specification.new do |spec|
         | 
| 7 | 
            -
              spec.name          =  | 
| 6 | 
            +
              spec.name          = 'restful_resource'
         | 
| 8 7 | 
             
              spec.version       = RestfulResource::VERSION
         | 
| 9 | 
            -
              spec.authors       = [ | 
| 10 | 
            -
              spec.email         = [ | 
| 11 | 
            -
              spec.description   =  | 
| 12 | 
            -
              spec.summary       =  | 
| 13 | 
            -
              spec.homepage      =  | 
| 14 | 
            -
              spec.license       =  | 
| 8 | 
            +
              spec.authors       = ['David Santoro', 'Federico Rebora']
         | 
| 9 | 
            +
              spec.email         = ['developers@carwow.co.uk']
         | 
| 10 | 
            +
              spec.description   = 'A simple activerecord inspired rest resource base class implemented using rest-client'
         | 
| 11 | 
            +
              spec.summary       = 'A simple activerecord inspired rest resource base class implemented using rest-client'
         | 
| 12 | 
            +
              spec.homepage      = 'http://www.github.com/carwow/restful_resource'
         | 
| 13 | 
            +
              spec.license       = 'MIT'
         | 
| 15 14 |  | 
| 16 | 
            -
              spec.files         = `git ls-files`.split( | 
| 15 | 
            +
              spec.files         = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
         | 
| 17 16 | 
             
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 18 17 | 
             
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 19 | 
            -
              spec.require_paths = [ | 
| 20 | 
            -
              spec.required_ruby_version = '>= 2. | 
| 18 | 
            +
              spec.require_paths = ['lib']
         | 
| 19 | 
            +
              spec.required_ruby_version = '>= 2.4'
         | 
| 21 20 |  | 
| 22 | 
            -
              spec.add_development_dependency  | 
| 23 | 
            -
              spec.add_development_dependency  | 
| 24 | 
            -
              spec.add_development_dependency  | 
| 25 | 
            -
              spec.add_development_dependency  | 
| 21 | 
            +
              spec.add_development_dependency 'bundler', '~> 1.3'
         | 
| 22 | 
            +
              spec.add_development_dependency 'carwow_rubocop', '~> 2.0'
         | 
| 23 | 
            +
              spec.add_development_dependency 'rake'
         | 
| 24 | 
            +
              spec.add_development_dependency 'rspec', '~> 3.1'
         | 
| 25 | 
            +
              spec.add_development_dependency 'rspec_junit_formatter'
         | 
| 26 | 
            +
              spec.add_development_dependency 'rspec-its'
         | 
| 26 27 |  | 
| 27 | 
            -
              spec.add_dependency  | 
| 28 | 
            -
              spec.add_dependency  | 
| 29 | 
            -
              spec.add_dependency  | 
| 30 | 
            -
              spec.add_dependency  | 
| 31 | 
            -
              spec.add_dependency  | 
| 32 | 
            -
              spec.add_dependency  | 
| 33 | 
            -
              spec.add_dependency  | 
| 34 | 
            -
              spec.add_dependency  | 
| 35 | 
            -
              spec.add_dependency  | 
| 28 | 
            +
              spec.add_dependency 'activesupport'
         | 
| 29 | 
            +
              spec.add_dependency 'faraday'
         | 
| 30 | 
            +
              spec.add_dependency 'faraday-cdn-metrics'
         | 
| 31 | 
            +
              spec.add_dependency 'faraday-encoding'
         | 
| 32 | 
            +
              spec.add_dependency 'faraday-http-cache'
         | 
| 33 | 
            +
              spec.add_dependency 'faraday_middleware'
         | 
| 34 | 
            +
              spec.add_dependency 'link_header'
         | 
| 35 | 
            +
              spec.add_dependency 'rack'
         | 
| 36 | 
            +
              spec.add_dependency 'typhoeus'
         | 
| 36 37 | 
             
            end
         | 
    
        data/spec/fixtures.rb
    CHANGED
    
    | @@ -1,17 +1,17 @@ | |
| 1 1 | 
             
            class Make < RestfulResource::Base
         | 
| 2 | 
            -
              resource_path  | 
| 2 | 
            +
              resource_path 'makes'
         | 
| 3 3 | 
             
              has_many :models
         | 
| 4 4 | 
             
            end
         | 
| 5 5 |  | 
| 6 6 | 
             
            class Model < RestfulResource::Base
         | 
| 7 7 | 
             
              has_one :make
         | 
| 8 | 
            -
              resource_path  | 
| 8 | 
            +
              resource_path 'groups/:group_id/makes/:make_slug/models'
         | 
| 9 9 | 
             
            end
         | 
| 10 10 |  | 
| 11 11 | 
             
            class Dealer < RestfulResource::Base
         | 
| 12 12 | 
             
              include RestfulResource::RailsValidations
         | 
| 13 13 |  | 
| 14 | 
            -
              resource_path  | 
| 14 | 
            +
              resource_path 'dealers'
         | 
| 15 15 | 
             
            end
         | 
| 16 16 |  | 
| 17 17 | 
             
            class BaseA < RestfulResource::Base
         | 
| @@ -21,11 +21,11 @@ class BaseB < RestfulResource::Base | |
| 21 21 | 
             
            end
         | 
| 22 22 |  | 
| 23 23 | 
             
            class TestA < BaseA
         | 
| 24 | 
            -
               | 
| 24 | 
            +
              resource_path 'testa'
         | 
| 25 25 | 
             
            end
         | 
| 26 26 |  | 
| 27 27 | 
             
            class TestB < BaseB
         | 
| 28 | 
            -
               | 
| 28 | 
            +
              resource_path 'testb'
         | 
| 29 29 | 
             
            end
         | 
| 30 30 |  | 
| 31 31 | 
             
            class ModelWithRedirections < RestfulResource::Base
         | 
| @@ -36,7 +36,7 @@ end | |
| 36 36 |  | 
| 37 37 | 
             
            module ComplicatedModule
         | 
| 38 38 | 
             
              class Parent < BaseA
         | 
| 39 | 
            -
                resource_path  | 
| 39 | 
            +
                resource_path 'parent'
         | 
| 40 40 | 
             
                has_many :children
         | 
| 41 41 | 
             
                has_many :other_things
         | 
| 42 42 | 
             
                has_many :missing
         | 
| @@ -51,7 +51,7 @@ module ComplicatedModule | |
| 51 51 | 
             
                has_one :parent
         | 
| 52 52 |  | 
| 53 53 | 
             
                def full_name
         | 
| 54 | 
            -
                  "#{ | 
| 54 | 
            +
                  "#{first_name} #{second_name}"
         | 
| 55 55 | 
             
                end
         | 
| 56 56 | 
             
              end
         | 
| 57 57 | 
             
            end
         | 
| @@ -1,53 +1,55 @@ | |
| 1 1 | 
             
            require_relative '../spec_helper'
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe RestfulResource::Associations do
         | 
| 4 | 
            -
              describe  | 
| 5 | 
            -
                before  | 
| 6 | 
            -
                  @parent = ComplicatedModule::Parent.new( | 
| 4 | 
            +
              describe '#has_many' do
         | 
| 5 | 
            +
                before do
         | 
| 6 | 
            +
                  @parent = ComplicatedModule::Parent.new(
         | 
| 7 7 | 
             
                    name: 'John Doe',
         | 
| 8 | 
            -
                    other_things: [{stuff: 'aaa'}, {stuff: 'bbb'}],
         | 
| 8 | 
            +
                    other_things: [{ stuff: 'aaa' }, { stuff: 'bbb' }],
         | 
| 9 9 | 
             
                    children:
         | 
| 10 10 | 
             
                      [
         | 
| 11 | 
            -
                        {first_name: 'David', second_name: 'Doe'},
         | 
| 12 | 
            -
                        {first_name: 'Mary', second_name: 'Doe'}
         | 
| 11 | 
            +
                        { first_name: 'David', second_name: 'Doe' },
         | 
| 12 | 
            +
                        { first_name: 'Mary', second_name: 'Doe' }
         | 
| 13 13 | 
             
                      ]
         | 
| 14 | 
            -
             | 
| 14 | 
            +
             | 
| 15 | 
            +
                  )
         | 
| 15 16 | 
             
                end
         | 
| 16 17 |  | 
| 17 | 
            -
                it  | 
| 18 | 
            +
                it 'adds a method to access nested resource' do
         | 
| 18 19 | 
             
                  expect(@parent.children.first.first_name).to eq 'David'
         | 
| 19 20 | 
             
                  expect(@parent.children.last.first_name).to eq 'Mary'
         | 
| 20 | 
            -
                  expect(@parent.children.first.to_json).to eq({first_name: 'David', second_name: 'Doe'}.to_json)
         | 
| 21 | 
            +
                  expect(@parent.children.first.to_json).to eq({ first_name: 'David', second_name: 'Doe' }.to_json)
         | 
| 21 22 | 
             
                end
         | 
| 22 23 |  | 
| 23 | 
            -
                it  | 
| 24 | 
            +
                it 'picks the right class for the instantiation of chilren' do
         | 
| 24 25 | 
             
                  expect(@parent.children.first.full_name).to eq 'David Doe'
         | 
| 25 26 | 
             
                end
         | 
| 26 27 |  | 
| 27 | 
            -
                it " | 
| 28 | 
            +
                it "uses open object when can't infer class name of association" do
         | 
| 28 29 | 
             
                  expect(@parent.other_things.first.stuff).to eq 'aaa'
         | 
| 29 30 | 
             
                end
         | 
| 30 31 |  | 
| 31 | 
            -
                it  | 
| 32 | 
            +
                it 'returns nil for missing associations' do
         | 
| 32 33 | 
             
                  expect(@parent.missing).to be_nil
         | 
| 33 34 | 
             
                end
         | 
| 34 35 | 
             
              end
         | 
| 35 36 |  | 
| 36 | 
            -
              describe  | 
| 37 | 
            -
                before  | 
| 38 | 
            -
                  @child = ComplicatedModule::Child.new( | 
| 37 | 
            +
              describe '#has_one' do
         | 
| 38 | 
            +
                before do
         | 
| 39 | 
            +
                  @child = ComplicatedModule::Child.new(
         | 
| 39 40 | 
             
                    first_name: 'David', second_name: 'Smith',
         | 
| 40 | 
            -
                    parent: {name: 'John Smith'}
         | 
| 41 | 
            -
             | 
| 41 | 
            +
                    parent: { name: 'John Smith' }
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  )
         | 
| 42 44 | 
             
                end
         | 
| 43 45 |  | 
| 44 | 
            -
                it  | 
| 46 | 
            +
                it 'adds a method to access nested resource' do
         | 
| 45 47 | 
             
                  expect(@child.parent.name).to eq 'John Smith'
         | 
| 46 | 
            -
                  expect(@child.parent.to_json).to eq({name: 'John Smith'}.to_json)
         | 
| 48 | 
            +
                  expect(@child.parent.to_json).to eq({ name: 'John Smith' }.to_json)
         | 
| 47 49 | 
             
                end
         | 
| 48 50 |  | 
| 49 | 
            -
                it  | 
| 50 | 
            -
                  expect(@child.parent | 
| 51 | 
            +
                it 'picks the right class for the instantiation of chilren' do
         | 
| 52 | 
            +
                  expect(@child.parent).to be_is_parent
         | 
| 51 53 | 
             
                end
         | 
| 52 54 | 
             
              end
         | 
| 53 55 | 
             
            end
         | 
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            require_relative '../spec_helper'
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe RestfulResource::Base, 'authorization' do
         | 
| 4 | 
            -
              before  | 
| 4 | 
            +
              before do
         | 
| 5 5 | 
             
                class FirstClient < RestfulResource::Base
         | 
| 6 6 | 
             
                end
         | 
| 7 7 |  | 
| @@ -21,20 +21,19 @@ describe RestfulResource::Base, 'authorization' do | |
| 21 21 | 
             
                FirstClient.configure(base_url: 'http://api.carwow.co.uk/api/first')
         | 
| 22 22 | 
             
                SecondClient.configure(base_url: 'http://api.carwow.co.uk/api/second',
         | 
| 23 23 | 
             
                                       username: 'test_user',
         | 
| 24 | 
            -
                                       password: 'test_pass' | 
| 24 | 
            +
                                       password: 'test_pass'
         | 
| 25 | 
            +
                                      )
         | 
| 25 26 | 
             
              end
         | 
| 26 27 |  | 
| 27 | 
            -
              it  | 
| 28 | 
            +
              it 'uses two different http instances' do
         | 
| 28 29 | 
             
                expect(FirstTest.send(:http)).not_to equal(SecondTest.send(:http))
         | 
| 29 30 | 
             
              end
         | 
| 30 31 |  | 
| 31 | 
            -
              it ' | 
| 32 | 
            +
              it 'has same http auth on superclass' do
         | 
| 32 33 | 
             
                expect(SecondTest.send(:http)).to equal(SecondClient.send(:http))
         | 
| 33 34 | 
             
              end
         | 
| 34 35 |  | 
| 35 | 
            -
              it ' | 
| 36 | 
            +
              it 'raises exception if base_url is not set' do
         | 
| 36 37 | 
             
                expect { NotConfiguredClient.send(:base_url) }.to raise_error 'Base url missing'
         | 
| 37 38 | 
             
              end
         | 
| 38 | 
            -
             | 
| 39 39 | 
             
            end
         | 
| 40 | 
            -
             |