active_rest_client 1.0.9 → 1.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.
- checksums.yaml +4 -4
- data/README.md +22 -0
- data/active_rest_client.gemspec +1 -0
- data/lib/active_rest_client/base.rb +5 -1
- data/lib/active_rest_client/caching.rb +6 -5
- data/lib/active_rest_client/configuration.rb +6 -0
- data/lib/active_rest_client/connection_manager.rb +15 -0
- data/lib/active_rest_client/proxy_base.rb +36 -2
- data/lib/active_rest_client/request.rb +54 -45
- data/lib/active_rest_client/request_delegator.rb +44 -0
- data/lib/active_rest_client/result_iterator.rb +4 -3
- data/lib/active_rest_client/validation.rb +1 -1
- data/lib/active_rest_client/version.rb +1 -1
- data/lib/active_rest_client.rb +1 -0
- data/spec/lib/base_spec.rb +34 -18
- data/spec/lib/caching_spec.rb +5 -5
- data/spec/lib/configuration_spec.rb +24 -0
- data/spec/lib/connection_manager_spec.rb +7 -0
- data/spec/lib/instrumentation_spec.rb +1 -1
- data/spec/lib/proxy_spec.rb +7 -7
- data/spec/lib/request_spec.rb +117 -60
- data/spec/lib/result_iterator_spec.rb +8 -1
- data/spec/lib/validation_spec.rb +14 -14
- data/spec/spec_helper.rb +31 -0
- metadata +17 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 93a842b5f48d12dcd6e53fe6ffef4ff8bed6ba35
         | 
| 4 | 
            +
              data.tar.gz: 2fe79284a9107ababa2758575b61906b2814b9d1
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f45afa7dde42bc05d0b22cb913ce121b99dd77d20ce2f67e2b7990f3ea0b686cb8c533c4e91a03eb0eb043a00c0bb0953f5e8390e77b945bff319f321136a941
         | 
| 7 | 
            +
              data.tar.gz: 12307bd1cb569a0dba41172e052a60667ef27b7c81aacf7f1b2db9df30806d26ab75b717fc9aca10ea4eb740c01d96081dd852ec98d3b539edf29c1ceda75609
         | 
    
        data/README.md
    CHANGED
    
    | @@ -495,6 +495,28 @@ class Person < ActiveRestClient::Base | |
| 495 495 | 
             
            end
         | 
| 496 496 | 
             
            ```
         | 
| 497 497 |  | 
| 498 | 
            +
            ### Parallel Requests
         | 
| 499 | 
            +
             | 
| 500 | 
            +
            Sometimes you know you will need to make a bunch of requests and you don't want to wait for one to finish to start the next. When using parallel requests there is the potential to finish many requests all at the same time taking only as long as the single longest request. To use parallel requests you will need to set Active-Rest-Client to use a Faraday adapter that supports parallel requests [(such as Typhoeus)](https://github.com/lostisland/faraday/wiki/Parallel-requests).
         | 
| 501 | 
            +
            ```ruby
         | 
| 502 | 
            +
            # Set adapter to Typhoeus to use parallel requests
         | 
| 503 | 
            +
            ActiveRestClient::Base.adapter = :typhoeus
         | 
| 504 | 
            +
            ```
         | 
| 505 | 
            +
             | 
| 506 | 
            +
            Now you just need to get ahold of the connection that is going to make the requests by specifying the same host that the models will be using. When inside the `in_parallel` block call request methods as usual and access the results after the `in_parallel` block ends.
         | 
| 507 | 
            +
            ```ruby
         | 
| 508 | 
            +
            ActiveRestClient::ConnectionManager.in_parallel('https://www.example.com') do
         | 
| 509 | 
            +
                @person = Person.find(1234)
         | 
| 510 | 
            +
                @employers = Employer.all
         | 
| 511 | 
            +
             | 
| 512 | 
            +
                puts @person #=> nil
         | 
| 513 | 
            +
                puts @employers #=> nil
         | 
| 514 | 
            +
            end # The requests are all fired in parallel during this end statement
         | 
| 515 | 
            +
             | 
| 516 | 
            +
            puts @person.name #=> "John"
         | 
| 517 | 
            +
            puts @employers.size #=> 7
         | 
| 518 | 
            +
            ```
         | 
| 519 | 
            +
             | 
| 498 520 | 
             
            ### Faking Calls
         | 
| 499 521 |  | 
| 500 522 | 
             
            There are times when an API hasn't been developed yet, so you want to fake the API call response.  To do this, you can simply pass a `fake` option when mapping the call containing the response.
         | 
    
        data/active_rest_client.gemspec
    CHANGED
    
    | @@ -30,6 +30,7 @@ Gem::Specification.new do |spec| | |
| 30 30 | 
             
              spec.add_development_dependency 'terminal-notifier-guard'
         | 
| 31 31 | 
             
              spec.add_development_dependency 'coveralls'
         | 
| 32 32 | 
             
              spec.add_development_dependency "api-auth", ">= 1.3.1"
         | 
| 33 | 
            +
              spec.add_development_dependency 'typhoeus'
         | 
| 33 34 |  | 
| 34 35 | 
             
              spec.add_runtime_dependency "multi_json"
         | 
| 35 36 | 
             
              spec.add_runtime_dependency "activesupport"
         | 
| @@ -12,7 +12,7 @@ module ActiveRestClient | |
| 12 12 | 
             
                attr_accessor :_headers
         | 
| 13 13 |  | 
| 14 14 | 
             
                instance_methods.each do |m|
         | 
| 15 | 
            -
                  next unless %w{display  | 
| 15 | 
            +
                  next unless %w{display presence load require hash untrust trust freeze method enable_warnings with_warnings suppress capture silence quietly debugger breakpoint}.map(&:to_sym).include? m
         | 
| 16 16 | 
             
                  undef_method m
         | 
| 17 17 | 
             
                end
         | 
| 18 18 |  | 
| @@ -52,6 +52,10 @@ module ActiveRestClient | |
| 52 52 | 
             
                  @dirty_attributes.size > 0
         | 
| 53 53 | 
             
                end
         | 
| 54 54 |  | 
| 55 | 
            +
                def errors
         | 
| 56 | 
            +
                  @attributes[:errors] || (_errors != {} ? _errors : nil)
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 55 59 | 
             
                def self._request(request, method = :get, params = nil)
         | 
| 56 60 | 
             
                  prepare_direct_request(request, method).call(params)
         | 
| 57 61 | 
             
                end
         | 
| @@ -56,17 +56,18 @@ module ActiveRestClient | |
| 56 56 | 
             
                    return if result.is_a? Symbol
         | 
| 57 57 | 
             
                    return unless perform_caching
         | 
| 58 58 | 
             
                    return unless !result.respond_to?(:_status) || [200, 304].include?(result._status)
         | 
| 59 | 
            +
                    headers = response.response_headers
         | 
| 59 60 |  | 
| 60 | 
            -
                     | 
| 61 | 
            -
                       | 
| 61 | 
            +
                    headers.keys.select{|h| h.is_a? String}.each do |key|
         | 
| 62 | 
            +
                      headers[key.downcase.to_sym] = headers[key]
         | 
| 62 63 | 
             
                    end
         | 
| 63 64 |  | 
| 64 | 
            -
                    if cache_store && ( | 
| 65 | 
            +
                    if cache_store && (headers[:etag] || headers[:expires])
         | 
| 65 66 | 
             
                      key = "#{request.class_name}:#{request.original_url}"
         | 
| 66 67 | 
             
                      ActiveRestClient::Logger.debug "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{key} - Writing to cache"
         | 
| 67 68 | 
             
                      cached_response = CachedResponse.new(status:response.status, result:result)
         | 
| 68 | 
            -
                      cached_response.etag =  | 
| 69 | 
            -
                      cached_response.expires = Time.parse( | 
| 69 | 
            +
                      cached_response.etag = headers[:etag] if headers[:etag]
         | 
| 70 | 
            +
                      cached_response.expires = Time.parse(headers[:expires]) rescue nil if headers[:expires]
         | 
| 70 71 | 
             
                      cache_store.write(key, Marshal.dump(cached_response), {}) if cached_response.etag.present? || cached_response.expires
         | 
| 71 72 | 
             
                    end
         | 
| 72 73 | 
             
                  end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require 'cgi'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ActiveRestClient
         | 
| 2 4 | 
             
              module Configuration
         | 
| 3 5 | 
             
                module ClassMethods
         | 
| @@ -36,12 +38,14 @@ module ActiveRestClient | |
| 36 38 | 
             
                        @username
         | 
| 37 39 | 
             
                      end
         | 
| 38 40 | 
             
                    else
         | 
| 41 | 
            +
                      value = CGI::escape(value) if value.present? && !value.include?("%")
         | 
| 39 42 | 
             
                      @username = value
         | 
| 40 43 | 
             
                    end
         | 
| 41 44 | 
             
                  end
         | 
| 42 45 |  | 
| 43 46 | 
             
                  def username=(value)
         | 
| 44 47 | 
             
                    ActiveRestClient::Logger.info "\033[1;4;32m#{name}\033[0m Username set to be #{value}"
         | 
| 48 | 
            +
                    value = CGI::escape(value) if value.present? && !value.include?("%")
         | 
| 45 49 | 
             
                    @@username = value
         | 
| 46 50 | 
             
                  end
         | 
| 47 51 |  | 
| @@ -53,12 +57,14 @@ module ActiveRestClient | |
| 53 57 | 
             
                        @password
         | 
| 54 58 | 
             
                      end
         | 
| 55 59 | 
             
                    else
         | 
| 60 | 
            +
                      value = CGI::escape(value) if value.present? && !value.include?("%")
         | 
| 56 61 | 
             
                      @password = value
         | 
| 57 62 | 
             
                    end
         | 
| 58 63 | 
             
                  end
         | 
| 59 64 |  | 
| 60 65 | 
             
                  def password=(value)
         | 
| 61 66 | 
             
                    ActiveRestClient::Logger.info "\033[1;4;32m#{name}\033[0m Password set..."
         | 
| 67 | 
            +
                    value = CGI::escape(value) if value.present? && !value.include?("%")
         | 
| 62 68 | 
             
                    @@password = value
         | 
| 63 69 | 
             
                  end
         | 
| 64 70 |  | 
| @@ -17,5 +17,20 @@ module ActiveRestClient | |
| 17 17 | 
             
                  Thread.current[:_connections][found] if found
         | 
| 18 18 | 
             
                end
         | 
| 19 19 |  | 
| 20 | 
            +
                def self.in_parallel(base_url)
         | 
| 21 | 
            +
                  begin
         | 
| 22 | 
            +
                    require 'typhoeus'
         | 
| 23 | 
            +
                    require 'typhoeus/adapters/faraday'
         | 
| 24 | 
            +
                  rescue LoadError
         | 
| 25 | 
            +
                    raise MissingOptionalLibraryError.new("To call '::ActiveRestClient::ConnectionManager.in_parallel' you must include the gem 'Typhoeus' in your Gemfile.")
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                  session = ConnectionManager.get_connection(base_url).session
         | 
| 28 | 
            +
                  session.in_parallel do
         | 
| 29 | 
            +
                    yield
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 20 33 | 
             
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              class MissingOptionalLibraryError < StandardError ; end
         | 
| 21 36 | 
             
            end
         | 
| @@ -68,7 +68,7 @@ module ActiveRestClient | |
| 68 68 |  | 
| 69 69 | 
             
                  def translate(result, options = {})
         | 
| 70 70 | 
             
                    result.headers["content-type"] = "application/hal+json"
         | 
| 71 | 
            -
                    result = OpenStruct.new(status:result.status,  | 
| 71 | 
            +
                    result = FaradayResponseProxy.new(OpenStruct.new(status:result.status, response_headers:result.headers, body:result.body))
         | 
| 72 72 | 
             
                    if result.body.present?
         | 
| 73 73 | 
             
                      result.body = yield MultiJson.load(result.body)
         | 
| 74 74 | 
             
                    end
         | 
| @@ -135,7 +135,7 @@ module ActiveRestClient | |
| 135 135 |  | 
| 136 136 | 
             
                  def render(body, status=200, content_type="application/javascript", headers={})
         | 
| 137 137 | 
             
                    headers["Content-type"] = content_type
         | 
| 138 | 
            -
                    OpenStruct.new(body:body, status:status,  | 
| 138 | 
            +
                    FaradayResponseProxy.new(OpenStruct.new(body:body, status:status, response_headers:headers, proxied:true))
         | 
| 139 139 | 
             
                  end
         | 
| 140 140 | 
             
                end
         | 
| 141 141 |  | 
| @@ -143,4 +143,38 @@ module ActiveRestClient | |
| 143 143 | 
             
                  base.extend(ClassMethods)
         | 
| 144 144 | 
             
                end
         | 
| 145 145 | 
             
              end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
              # FaradayResponseProxy acts just like a Faraday Response object,
         | 
| 148 | 
            +
              # however it always resolves the request immediately regardless of
         | 
| 149 | 
            +
              # whether it is inside an in_parallel block or not
         | 
| 150 | 
            +
              class FaradayResponseProxy
         | 
| 151 | 
            +
                def initialize(response)
         | 
| 152 | 
            +
                  @response = response
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                def headers
         | 
| 156 | 
            +
                  @response.response_headers
         | 
| 157 | 
            +
                end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                def status
         | 
| 160 | 
            +
                  @response.status
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                def body
         | 
| 164 | 
            +
                  @response.body
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                def body=(value)
         | 
| 168 | 
            +
                  @response.body = value
         | 
| 169 | 
            +
                  value
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                def on_complete
         | 
| 173 | 
            +
                  yield(@response)
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                def finished?
         | 
| 177 | 
            +
                  true
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
              end
         | 
| 146 180 | 
             
            end
         | 
| @@ -13,6 +13,7 @@ module ActiveRestClient | |
| 13 13 | 
             
                  @method[:options][:has_one] ||= {}
         | 
| 14 14 | 
             
                  @overriden_name             = @method[:options][:overriden_name]
         | 
| 15 15 | 
             
                  @object                     = object
         | 
| 16 | 
            +
                  @response_delegate          = ActiveRestClient::RequestDelegator.new(nil)
         | 
| 16 17 | 
             
                  @params                     = params
         | 
| 17 18 | 
             
                  @headers                    = HeadersList.new
         | 
| 18 19 | 
             
                end
         | 
| @@ -45,14 +46,6 @@ module ActiveRestClient | |
| 45 46 | 
             
                  end
         | 
| 46 47 | 
             
                end
         | 
| 47 48 |  | 
| 48 | 
            -
                def base_url
         | 
| 49 | 
            -
                  if object_is_class?
         | 
| 50 | 
            -
                    @object.base_url
         | 
| 51 | 
            -
                  else
         | 
| 52 | 
            -
                    @object.class.base_url
         | 
| 53 | 
            -
                  end
         | 
| 54 | 
            -
                end
         | 
| 55 | 
            -
             | 
| 56 49 | 
             
                def username
         | 
| 57 50 | 
             
                  if object_is_class?
         | 
| 58 51 | 
             
                    @object.username
         | 
| @@ -123,7 +116,7 @@ module ActiveRestClient | |
| 123 116 | 
             
                        fake = fake.call(self)
         | 
| 124 117 | 
             
                      end
         | 
| 125 118 | 
             
                      ActiveRestClient::Logger.debug "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{@instrumentation_name} - Faked response found"
         | 
| 126 | 
            -
                      return handle_response(OpenStruct.new(status:200, body:fake,  | 
| 119 | 
            +
                      return handle_response(OpenStruct.new(status:200, body:fake, response_headers:{"X-ARC-Faked-Response" => "true"}))
         | 
| 127 120 | 
             
                    end
         | 
| 128 121 | 
             
                    if object_is_class?
         | 
| 129 122 | 
             
                      @object.send(:_filter_request, :before, @method[:name], self)
         | 
| @@ -143,24 +136,47 @@ module ActiveRestClient | |
| 143 136 | 
             
                        etag = cached.etag
         | 
| 144 137 | 
             
                      end
         | 
| 145 138 | 
             
                    end
         | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 139 | 
            +
             | 
| 140 | 
            +
                    response = (
         | 
| 141 | 
            +
                      if proxy
         | 
| 142 | 
            +
                        proxy.handle(self) do |request|
         | 
| 143 | 
            +
                          request.do_request(etag)
         | 
| 144 | 
            +
                        end
         | 
| 145 | 
            +
                      else
         | 
| 146 | 
            +
                        do_request(etag)
         | 
| 149 147 | 
             
                      end
         | 
| 150 | 
            -
                     | 
| 151 | 
            -
             | 
| 152 | 
            -
                     | 
| 153 | 
            -
                     | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 148 | 
            +
                    )
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    # This block is called immediately when this request is not inside a parallel request block.
         | 
| 151 | 
            +
                    # Otherwise this callback is called after the parallel request block ends.
         | 
| 152 | 
            +
                    response.on_complete do |response_env|
         | 
| 153 | 
            +
                      if verbose?
         | 
| 154 | 
            +
                        ActiveRestClient::Logger.debug "  Response"
         | 
| 155 | 
            +
                        ActiveRestClient::Logger.debug "  << Status : #{response_env.status}"
         | 
| 156 | 
            +
                        response_env.response_headers.each do |k,v|
         | 
| 157 | 
            +
                          ActiveRestClient::Logger.debug "  << #{k} : #{v}"
         | 
| 158 | 
            +
                        end
         | 
| 159 | 
            +
                        ActiveRestClient::Logger.debug "  << Body:\n#{response_env.body}"
         | 
| 160 | 
            +
                      end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                      if object_is_class? && @object.record_response?
         | 
| 163 | 
            +
                        @object.record_response(self.url, response_env)
         | 
| 164 | 
            +
                      end
         | 
| 165 | 
            +
                      if object_is_class?
         | 
| 166 | 
            +
                        @object.send(:_filter_request, :after, @method[:name], response_env)
         | 
| 167 | 
            +
                      else
         | 
| 168 | 
            +
                        @object.class.send(:_filter_request, :after, @method[:name], response_env)
         | 
| 169 | 
            +
                      end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                      result = handle_response(response_env, cached)
         | 
| 172 | 
            +
                      @response_delegate.__setobj__(result)
         | 
| 173 | 
            +
                      original_object_class.write_cached_response(self, response_env, result)
         | 
| 160 174 | 
             
                    end
         | 
| 161 | 
            -
             | 
| 162 | 
            -
                     | 
| 163 | 
            -
                    result
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                    # If this was not a parallel request just return the original result
         | 
| 177 | 
            +
                    return result if response.finished?
         | 
| 178 | 
            +
                    # Otherwise return the delegate which will get set later once the call back is completed
         | 
| 179 | 
            +
                    return @response_delegate
         | 
| 164 180 | 
             
                  end
         | 
| 165 181 | 
             
                end
         | 
| 166 182 |  | 
| @@ -277,15 +293,6 @@ module ActiveRestClient | |
| 277 293 | 
             
                    raise InvalidRequestException.new("Invalid method #{http_method}")
         | 
| 278 294 | 
             
                  end
         | 
| 279 295 |  | 
| 280 | 
            -
                  if verbose?
         | 
| 281 | 
            -
                    ActiveRestClient::Logger.debug "  Response"
         | 
| 282 | 
            -
                    ActiveRestClient::Logger.debug "  << Status : #{response.status}"
         | 
| 283 | 
            -
                    response.headers.each do |k,v|
         | 
| 284 | 
            -
                      ActiveRestClient::Logger.debug "  << #{k} : #{v}"
         | 
| 285 | 
            -
                    end
         | 
| 286 | 
            -
                    ActiveRestClient::Logger.debug "  << Body:\n#{response.body}"
         | 
| 287 | 
            -
                  end
         | 
| 288 | 
            -
             | 
| 289 296 | 
             
                  response
         | 
| 290 297 | 
             
                end
         | 
| 291 298 |  | 
| @@ -343,6 +350,8 @@ module ActiveRestClient | |
| 343 350 | 
             
                      raise HTTPClientException.new(status:status, result:error_response, url:@url)
         | 
| 344 351 | 
             
                    elsif (500..599).include? status
         | 
| 345 352 | 
             
                      raise HTTPServerException.new(status:status, result:error_response, url:@url)
         | 
| 353 | 
            +
                    elsif status == 0
         | 
| 354 | 
            +
                      raise TimeoutException.new("Timed out getting #{response.url}")
         | 
| 346 355 | 
             
                    end
         | 
| 347 356 | 
             
                  end
         | 
| 348 357 |  | 
| @@ -359,11 +368,7 @@ module ActiveRestClient | |
| 359 368 | 
             
                    overriden_name = name
         | 
| 360 369 | 
             
                    object = @method[:options][:has_one][name].new
         | 
| 361 370 | 
             
                  else
         | 
| 362 | 
            -
                     | 
| 363 | 
            -
                      object = @object.new
         | 
| 364 | 
            -
                    else
         | 
| 365 | 
            -
                      object = @object.class.new
         | 
| 366 | 
            -
                    end
         | 
| 371 | 
            +
                    object = create_object_instance
         | 
| 367 372 | 
             
                  end
         | 
| 368 373 |  | 
| 369 374 | 
             
                  if hal_response? && name.nil?
         | 
| @@ -400,8 +405,8 @@ module ActiveRestClient | |
| 400 405 | 
             
                end
         | 
| 401 406 |  | 
| 402 407 | 
             
                def hal_response?
         | 
| 403 | 
            -
                  _, content_type = @response. | 
| 404 | 
            -
                  faked_response = @response. | 
| 408 | 
            +
                  _, content_type = @response.response_headers.detect{|k,v| k.downcase == "content-type"}
         | 
| 409 | 
            +
                  faked_response = @response.response_headers.detect{|k,v| k.downcase == "x-arc-faked-response"}
         | 
| 405 410 | 
             
                  if content_type && content_type.respond_to?(:each)
         | 
| 406 411 | 
             
                    content_type.each do |ct|
         | 
| 407 412 | 
             
                      return true if ct[%r{application\/hal\+json}i]
         | 
| @@ -448,6 +453,10 @@ module ActiveRestClient | |
| 448 453 |  | 
| 449 454 | 
             
                private
         | 
| 450 455 |  | 
| 456 | 
            +
                def create_object_instance
         | 
| 457 | 
            +
                  return object_is_class? ? @object.new : @object.class.new
         | 
| 458 | 
            +
                end
         | 
| 459 | 
            +
             | 
| 451 460 | 
             
                def select_name(name, parent_name)
         | 
| 452 461 | 
             
                  if @method[:options][:has_many][name] || @method[:options][:has_one][name]
         | 
| 453 462 | 
             
                    return name
         | 
| @@ -457,7 +466,7 @@ module ActiveRestClient | |
| 457 466 | 
             
                end
         | 
| 458 467 |  | 
| 459 468 | 
             
                def is_json_response?
         | 
| 460 | 
            -
                  @response. | 
| 469 | 
            +
                  @response.response_headers['Content-Type'].nil? || @response.response_headers['Content-Type'].include?('json')
         | 
| 461 470 | 
             
                end
         | 
| 462 471 |  | 
| 463 472 | 
             
                def generate_new_object(options={})
         | 
| @@ -472,15 +481,15 @@ module ActiveRestClient | |
| 472 481 | 
             
                    body
         | 
| 473 482 | 
             
                  end
         | 
| 474 483 | 
             
                  if body.is_a? Array
         | 
| 475 | 
            -
                    result = ActiveRestClient::ResultIterator.new(@response | 
| 484 | 
            +
                    result = ActiveRestClient::ResultIterator.new(@response)
         | 
| 476 485 | 
             
                    body.each do |json_object|
         | 
| 477 486 | 
             
                      result << new_object(json_object, @overriden_name)
         | 
| 478 487 | 
             
                    end
         | 
| 479 488 | 
             
                  else
         | 
| 480 489 | 
             
                    result = new_object(body, @overriden_name)
         | 
| 481 490 | 
             
                    result._status = @response.status
         | 
| 482 | 
            -
                    result._headers = @response. | 
| 483 | 
            -
                    result._etag = @response. | 
| 491 | 
            +
                    result._headers = @response.response_headers
         | 
| 492 | 
            +
                    result._etag = @response.response_headers['ETag']
         | 
| 484 493 | 
             
                    if !object_is_class? && options[:mutable] != false
         | 
| 485 494 | 
             
                      @object._copy_from(result)
         | 
| 486 495 | 
             
                      @object._clean!
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            module ActiveRestClient
         | 
| 2 | 
            +
              class RequestDelegator < Delegator
         | 
| 3 | 
            +
                def initialize(obj)
         | 
| 4 | 
            +
                  super
         | 
| 5 | 
            +
                  @delegate_obj = obj
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def __getobj__
         | 
| 9 | 
            +
                  @delegate_obj
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def __setobj__(obj)
         | 
| 13 | 
            +
                  @delegate_obj = obj
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def class
         | 
| 17 | 
            +
                  @delegate_obj.class
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def method_missing(name, *args, &block)
         | 
| 21 | 
            +
                  # Handles issue with private method 'test' on base Ruby Object
         | 
| 22 | 
            +
                  return @delegate_obj.test if name.to_sym == :test
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  # Forward request to delegate
         | 
| 25 | 
            +
                  @delegate_obj.send(name, *args, &block)
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def kind_of?(obj)
         | 
| 29 | 
            +
                  @delegate_obj.kind_of?(obj)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def is_a?(obj)
         | 
| 33 | 
            +
                  @delegate_obj.is_a?(obj)
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def instance_of?(obj)
         | 
| 37 | 
            +
                  @delegate_obj.instance_of?(obj)
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def _delegate?
         | 
| 41 | 
            +
                  return true
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| @@ -3,10 +3,11 @@ module ActiveRestClient | |
| 3 3 | 
             
                include Enumerable
         | 
| 4 4 |  | 
| 5 5 | 
             
                attr_accessor :_status
         | 
| 6 | 
            -
                attr_reader :items
         | 
| 6 | 
            +
                attr_reader :items, :_headers
         | 
| 7 7 |  | 
| 8 | 
            -
                def initialize( | 
| 9 | 
            -
                  @_status | 
| 8 | 
            +
                def initialize(response = nil)
         | 
| 9 | 
            +
                  @_status  = response.try :status
         | 
| 10 | 
            +
                  @_headers = response.try :response_headers
         | 
| 10 11 | 
             
                  @items = []
         | 
| 11 12 | 
             
                end
         | 
| 12 13 |  | 
    
        data/lib/active_rest_client.rb
    CHANGED
    
    | @@ -12,6 +12,7 @@ require "active_rest_client/headers_list" | |
| 12 12 | 
             
            require "active_rest_client/lazy_loader"
         | 
| 13 13 | 
             
            require "active_rest_client/lazy_association_loader"
         | 
| 14 14 | 
             
            require "active_rest_client/request"
         | 
| 15 | 
            +
            require "active_rest_client/request_delegator"
         | 
| 15 16 | 
             
            require "active_rest_client/validation"
         | 
| 16 17 | 
             
            require "active_rest_client/request_filtering"
         | 
| 17 18 | 
             
            require "active_rest_client/proxy_base"
         | 
    
        data/spec/lib/base_spec.rb
    CHANGED
    
    | @@ -156,14 +156,14 @@ describe ActiveRestClient::Base do | |
| 156 156 | 
             
              end
         | 
| 157 157 |  | 
| 158 158 | 
             
              it "should be able to lazy instantiate an object from a prefixed lazy_ method call" do
         | 
| 159 | 
            -
                expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with('/find/1', anything).and_return(OpenStruct.new(status:200,  | 
| 159 | 
            +
                expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 160 160 | 
             
                example = AlteringClientExample.lazy_find(1)
         | 
| 161 161 | 
             
                expect(example).to be_an_instance_of(ActiveRestClient::LazyLoader)
         | 
| 162 162 | 
             
                expect(example.first_name).to eq("Billy")
         | 
| 163 163 | 
             
              end
         | 
| 164 164 |  | 
| 165 165 | 
             
              it "should be able to lazy instantiate an object from a prefixed lazy_ method call from an instance" do
         | 
| 166 | 
            -
                expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with('/find/1', anything).and_return(OpenStruct.new(status:200,  | 
| 166 | 
            +
                expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 167 167 | 
             
                example = AlteringClientExample.new.lazy_find(1)
         | 
| 168 168 | 
             
                expect(example).to be_an_instance_of(ActiveRestClient::LazyLoader)
         | 
| 169 169 | 
             
                expect(example.first_name).to eq("Billy")
         | 
| @@ -246,49 +246,65 @@ describe ActiveRestClient::Base do | |
| 246 246 |  | 
| 247 247 | 
             
              context "directly call a URL, rather than via a mapped method" do
         | 
| 248 248 | 
             
                it "should be able to directly call a URL" do
         | 
| 249 | 
            -
                  expect_any_instance_of(ActiveRestClient::Request).to receive(:do_request).with(any_args).and_return(OpenStruct.new(status:200,  | 
| 249 | 
            +
                  expect_any_instance_of(ActiveRestClient::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 250 250 | 
             
                  EmptyExample._request("http://api.example.com/")
         | 
| 251 251 | 
             
                end
         | 
| 252 252 |  | 
| 253 253 | 
             
                it "runs filters as usual" do
         | 
| 254 | 
            -
                  expect_any_instance_of(ActiveRestClient::Request).to receive(:do_request).with(any_args).and_return(OpenStruct.new(status:200,  | 
| 254 | 
            +
                  expect_any_instance_of(ActiveRestClient::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 255 255 | 
             
                  expect(EmptyExample).to receive(:_filter_request).with(any_args).exactly(2).times
         | 
| 256 256 | 
             
                  EmptyExample._request("http://api.example.com/")
         | 
| 257 257 | 
             
                end
         | 
| 258 258 |  | 
| 259 259 | 
             
                it "should make an HTTP request" do
         | 
| 260 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with(any_args).and_return(OpenStruct.new(status:200,  | 
| 260 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 261 261 | 
             
                  EmptyExample._request("http://api.example.com/")
         | 
| 262 262 | 
             
                end
         | 
| 263 263 |  | 
| 264 264 | 
             
                it "should make an HTTP request including the path in the base_url" do
         | 
| 265 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with('/v1/all', anything).and_return(OpenStruct.new(status:200,  | 
| 265 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with('/v1/all', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 266 266 | 
             
                  NonHostnameBaseUrlExample.all
         | 
| 267 267 | 
             
                end
         | 
| 268 268 |  | 
| 269 269 | 
             
                it "should map the response from the directly called URL in the normal way" do
         | 
| 270 | 
            -
                  expect_any_instance_of(ActiveRestClient::Request).to receive(:do_request).with(any_args).and_return(OpenStruct.new(status:200,  | 
| 270 | 
            +
                  expect_any_instance_of(ActiveRestClient::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 271 271 | 
             
                  example = EmptyExample._request("http://api.example.com/")
         | 
| 272 272 | 
             
                  expect(example.first_name).to eq("Billy")
         | 
| 273 273 | 
             
                end
         | 
| 274 274 |  | 
| 275 275 | 
             
                it "should be able to pass the plain response from the directly called URL bypassing JSON loading" do
         | 
| 276 | 
            -
                   | 
| 277 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:post).with(any_args).and_return(OpenStruct.new(status:200,  | 
| 278 | 
            -
                  expect(EmptyExample._plain_request("http://api.example.com/", :post, {id:1234})).to eq( | 
| 276 | 
            +
                  response_body = "This is another non-JSON string"
         | 
| 277 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:post).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:response_body)))
         | 
| 278 | 
            +
                  expect(EmptyExample._plain_request("http://api.example.com/", :post, {id:1234})).to eq(response_body)
         | 
| 279 | 
            +
                end
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                context "Simulating Faraday connection in_parallel" do
         | 
| 282 | 
            +
                  it "should be able to pass the plain response from the directly called URL bypassing JSON loading" do
         | 
| 283 | 
            +
                    response_body = "This is another non-JSON string"
         | 
| 284 | 
            +
                    response = ::FaradayResponseMock.new(
         | 
| 285 | 
            +
                      OpenStruct.new(status:200, response_headers:{}, body:response_body),
         | 
| 286 | 
            +
                      false)
         | 
| 287 | 
            +
                    expect_any_instance_of(ActiveRestClient::Connection).to receive(:post).with(any_args).and_return(response)
         | 
| 288 | 
            +
                    result = EmptyExample._plain_request("http://api.example.com/", :post, {id:1234})
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                    expect(result).to eq(nil)
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                    response.finish
         | 
| 293 | 
            +
                    expect(result).to eq(response_body)
         | 
| 294 | 
            +
                  end
         | 
| 279 295 | 
             
                end
         | 
| 280 296 |  | 
| 281 297 | 
             
                it "should cache plain requests separately" do
         | 
| 282 298 | 
             
                  perform_caching = EmptyExample.perform_caching
         | 
| 283 299 | 
             
                  cache_store = EmptyExample.cache_store
         | 
| 284 300 | 
             
                  begin
         | 
| 285 | 
            -
                    response =  | 
| 286 | 
            -
                    other_response =  | 
| 301 | 
            +
                    response = "This is a non-JSON string"
         | 
| 302 | 
            +
                    other_response = "This is another non-JSON string"
         | 
| 287 303 | 
             
                    allow_any_instance_of(ActiveRestClient::Connection).to receive(:get) do |instance, url, others|
         | 
| 288 304 | 
             
                      if url == "/?test=1"
         | 
| 289 | 
            -
                        OpenStruct.new(status:200,  | 
| 305 | 
            +
                        ::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:response))
         | 
| 290 306 | 
             
                      else
         | 
| 291 | 
            -
                        OpenStruct.new(status:200,  | 
| 307 | 
            +
                        ::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:other_response))
         | 
| 292 308 | 
             
                      end
         | 
| 293 309 | 
             
                    end
         | 
| 294 310 | 
             
                    EmptyExample.perform_caching = true
         | 
| @@ -302,19 +318,19 @@ describe ActiveRestClient::Base do | |
| 302 318 | 
             
                end
         | 
| 303 319 |  | 
| 304 320 | 
             
                it "should be able to lazy load a direct URL request" do
         | 
| 305 | 
            -
                  expect_any_instance_of(ActiveRestClient::Request).to receive(:do_request).with(any_args).and_return(OpenStruct.new(status:200,  | 
| 321 | 
            +
                  expect_any_instance_of(ActiveRestClient::Request).to receive(:do_request).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 306 322 | 
             
                  example = EmptyExample._lazy_request("http://api.example.com/")
         | 
| 307 323 | 
             
                  expect(example).to be_an_instance_of(ActiveRestClient::LazyLoader)
         | 
| 308 324 | 
             
                  expect(example.first_name).to eq("Billy")
         | 
| 309 325 | 
             
                end
         | 
| 310 326 |  | 
| 311 327 | 
             
                it "should be able to specify a method and parameters for the call" do
         | 
| 312 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:post).with(any_args).and_return(OpenStruct.new(status:200,  | 
| 328 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:post).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 313 329 | 
             
                  EmptyExample._request("http://api.example.com/", :post, {id:1234})
         | 
| 314 330 | 
             
                end
         | 
| 315 331 |  | 
| 316 332 | 
             
                it "should be able to use mapped methods to create a request to pass in to _lazy_request" do
         | 
| 317 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with('/find/1', anything).and_return(OpenStruct.new(status:200,  | 
| 333 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with('/find/1', anything).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"{\"first_name\":\"Billy\"}")))
         | 
| 318 334 | 
             
                  request = AlteringClientExample._request_for(:find, :id => 1)
         | 
| 319 335 | 
             
                  example = AlteringClientExample._lazy_request(request)
         | 
| 320 336 | 
             
                  expect(example.first_name).to eq("Billy")
         | 
| @@ -323,7 +339,7 @@ describe ActiveRestClient::Base do | |
| 323 339 |  | 
| 324 340 | 
             
              context "Recording a response" do
         | 
| 325 341 | 
             
                it "calls back to the record_response callback with the url and response body" do
         | 
| 326 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with(any_args).and_return(OpenStruct.new(status:200,  | 
| 342 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with(any_args).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, response_headers:{}, body:"Hello world")))
         | 
| 327 343 | 
             
                  expect{RecordResponseExample.all}.to raise_error(Exception, "/all|Hello world")
         | 
| 328 344 | 
             
                end
         | 
| 329 345 | 
             
              end
         | 
    
        data/spec/lib/caching_spec.rb
    CHANGED
    
    | @@ -111,7 +111,7 @@ describe ActiveRestClient::Caching do | |
| 111 111 | 
             
                    result:@cached_object,
         | 
| 112 112 | 
             
                    etag:@etag)
         | 
| 113 113 | 
             
                  expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(Marshal.dump(cached_response))
         | 
| 114 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with("/", hash_including("If-None-Match" => @etag)).and_return(OpenStruct.new(status:304,  | 
| 114 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with("/", hash_including("If-None-Match" => @etag)).and_return(::FaradayResponseMock.new(OpenStruct.new(status:304, response_headers:{})))
         | 
| 115 115 | 
             
                  ret = Person.all
         | 
| 116 116 | 
             
                  expect(ret.first_name).to eq("Johnny")
         | 
| 117 117 | 
             
                end
         | 
| @@ -125,7 +125,7 @@ describe ActiveRestClient::Caching do | |
| 125 125 | 
             
                  allow_any_instance_of(CachingExampleCacheStore5).to receive(:read).and_return(Marshal.dump(cached_response))
         | 
| 126 126 | 
             
                  new_name = 'Pete'
         | 
| 127 127 | 
             
                  response_body = Person.new(first_name: new_name).to_json
         | 
| 128 | 
            -
                  response = double(status: 200,  | 
| 128 | 
            +
                  response = ::FaradayResponseMock.new(double(status: 200, response_headers: {}, body: response_body))
         | 
| 129 129 | 
             
                  allow_any_instance_of(ActiveRestClient::Connection).to(
         | 
| 130 130 | 
             
                    receive(:get).with('/', hash_including('If-None-Match' => @etag)).and_return(response))
         | 
| 131 131 |  | 
| @@ -159,7 +159,7 @@ describe ActiveRestClient::Caching do | |
| 159 159 |  | 
| 160 160 | 
             
                it "should restore a result iterator from the cache store, if there's a hard expiry" do
         | 
| 161 161 | 
             
                  class CachingExample3 < ActiveRestClient::Base ; end
         | 
| 162 | 
            -
                  object = ActiveRestClient::ResultIterator.new(200)
         | 
| 162 | 
            +
                  object = ActiveRestClient::ResultIterator.new(double(status: 200))
         | 
| 163 163 | 
             
                  object << CachingExample3.new(first_name:"Johnny")
         | 
| 164 164 | 
             
                  object << CachingExample3.new(first_name:"Billy")
         | 
| 165 165 | 
             
                  etag = "6527914a91e0c5769f6de281f25bd891"
         | 
| @@ -185,7 +185,7 @@ describe ActiveRestClient::Caching do | |
| 185 185 | 
             
                it "should write the response to the cache if there's an etag" do
         | 
| 186 186 | 
             
                  expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(nil)
         | 
| 187 187 | 
             
                  expect_any_instance_of(CachingExampleCacheStore5).to receive(:write).once.with("Person:/", an_instance_of(String), {})
         | 
| 188 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(status:200, body:"{\"result\":true}",  | 
| 188 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, body:"{\"result\":true}", response_headers:{etag:"1234567890"})))
         | 
| 189 189 | 
             
                  Person.perform_caching true
         | 
| 190 190 | 
             
                  ret = Person.all
         | 
| 191 191 | 
             
                end
         | 
| @@ -193,7 +193,7 @@ describe ActiveRestClient::Caching do | |
| 193 193 | 
             
                it "should write the response to the cache if there's a hard expiry" do
         | 
| 194 194 | 
             
                  expect_any_instance_of(CachingExampleCacheStore5).to receive(:read).once.with("Person:/").and_return(nil)
         | 
| 195 195 | 
             
                  expect_any_instance_of(CachingExampleCacheStore5).to receive(:write).once.with("Person:/", an_instance_of(String), an_instance_of(Hash))
         | 
| 196 | 
            -
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(status:200, body:"{\"result\":true}",  | 
| 196 | 
            +
                  expect_any_instance_of(ActiveRestClient::Connection).to receive(:get).with("/", an_instance_of(Hash)).and_return(::FaradayResponseMock.new(OpenStruct.new(status:200, body:"{\"result\":true}", response_headers:{expires:(Time.now + 30).rfc822})))
         | 
| 197 197 | 
             
                  Person.perform_caching = true
         | 
| 198 198 | 
             
                  ret = Person.all
         | 
| 199 199 | 
             
                end
         |