From f54f14398def30aff6ad70eeef4015dbb6a9c050 Mon Sep 17 00:00:00 2001 From: Andy Brody Date: Tue, 30 May 2017 01:34:42 -0400 Subject: [PATCH] Add a lot of YARD docs to various classes. Also switch to Markdown as the markup language, rather than rdoc. --- .yardopts | 1 + Rakefile | 5 + lib/restclient.rb | 125 +++++++++++++++------ lib/restclient/abstract_response.rb | 68 ++++++++++-- lib/restclient/exceptions.rb | 53 ++++++--- lib/restclient/payload.rb | 4 + lib/restclient/platform.rb | 21 ++++ lib/restclient/raw_response.rb | 44 ++++++-- lib/restclient/request.rb | 162 +++++++++++++++++++--------- lib/restclient/resource.rb | 18 ++-- lib/restclient/response.rb | 25 ++++- lib/restclient/version.rb | 5 + 12 files changed, 403 insertions(+), 128 deletions(-) diff --git a/.yardopts b/.yardopts index f0b9d91..8b4e63d 100644 --- a/.yardopts +++ b/.yardopts @@ -1,3 +1,4 @@ +--markup markdown - history.md LICENSE diff --git a/Rakefile b/Rakefile index 58a8bfa..f4637c8 100644 --- a/Rakefile +++ b/Rakefile @@ -123,3 +123,8 @@ end require 'yard' YARD::Rake::YardocTask.new do |t| end + +desc 'Run YARD documentation server, reload with changes' +task :yardserver do + sh 'yard server --reload' +end diff --git a/lib/restclient.rb b/lib/restclient.rb index d4c455e..9cc2a68 100644 --- a/lib/restclient.rb +++ b/lib/restclient.rb @@ -18,47 +18,88 @@ require File.dirname(__FILE__) + '/restclient/windows' # This module's static methods are the entry point for using the REST client. # -# # GET -# xml = RestClient.get 'http://example.com/resource' -# jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg' +# These helpers provide a concise way to issue simple requests with headers. If +# you need to set other options on a request (e.g. timeout, SSL options, etc.) +# then use {RestClient::Request.execute}, which supports all of these options. # -# # authentication and SSL -# RestClient.get 'https://user:password@example.com/private/resource' +# The {.get}, {.head}, {.delete}, and {.options} methods take a URL String and +# optional HTTP headers Hash. # -# # POST or PUT with a hash sends parameters as a urlencoded form body -# RestClient.post 'http://example.com/resource', :param1 => 'one' +# The {.post}, {.put}, and {.patch} methods take a URL String, a payload, and +# an optional HTTP headers Hash. # -# # nest hash parameters -# RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' } +# All of these helpers are just thin wrappers around +# {RestClient::Request.execute RestClient::Request.execute}. # -# # POST and PUT with raw payloads -# RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain' -# RestClient.post 'http://example.com/resource.xml', xml_doc -# RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf' # -# # DELETE -# RestClient.delete 'http://example.com/resource' +# ```ruby +# # Simple GET request, potentially with headers. +# RestClient.get('http://example.com/') +# # => # -# # retreive the response http code and headers -# res = RestClient.get 'http://example.com/some.jpg' -# res.code # => 200 -# res.headers[:content_type] # => 'image/jpg' +# RestClient.get('http://example.com/resource', accept: 'image/jpg') # -# # HEAD -# RestClient.head('http://example.com').headers +# RestClient.get('http://example.com', 'If-Modified-Since': 'Sat, 10 Aug 2013 10:23:00 GMT') +# # raises RestClient::NotModified: 304 Not Modified +# +# # Basic authentication and SSL +# RestClient.get 'https://user:password@example.com/private/resource' +# +# # POST or PUT with a hash sends parameters as a urlencoded form body +# RestClient.post 'http://example.com/resource', :param1 => 'one' +# +# # nest hash parameters +# RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' } +# +# # POST and PUT with raw payloads +# RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain' +# RestClient.post 'http://example.com/resource.xml', xml_doc +# RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf' +# +# # DELETE +# RestClient.delete 'http://example.com/resource' +# +# # Retrieve the response http code and headers +# res = RestClient.get 'http://example.com/some.jpg' +# res.code # => 200 +# res.headers[:content_type] # => 'image/jpg' +# +# # HEAD +# RestClient.head('http://example.com').headers +# ``` # # To use with a proxy, just set RestClient.proxy to the proper http proxy: # -# RestClient.proxy = "http://proxy.example.com/" +# RestClient.proxy = "http://proxy.example.com/" # -# Or inherit the proxy from the environment: +# Proxies can also be set via the `http_proxy`/`https_proxy` environment +# variables, or with the `:proxy` option on an individual {RestClient::Request}. # -# RestClient.proxy = ENV['http_proxy'] +# For live tests of RestClient, try using https://httpbin.org/. This service +# echoes back information about the HTTP request. # -# For live tests of RestClient, try using http://rest-test.heroku.com, which echoes back information about the rest call: -# -# >> RestClient.put 'http://rest-test.heroku.com/resource', :foo => 'baz' -# => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}" +# r = RestClient.post('https://httpbin.org/post', foo: 'bar') +# # => +# puts r.body +# { +# "args": {}, +# "data": "", +# "files": {}, +# "form": { +# "foo": "bar" +# }, +# "headers": { +# "Accept": "*/*", +# "Accept-Encoding": "gzip;q=1.0,deflate;q=0.6,identity;q=0.3", +# "Connection": "close", +# "Content-Length": "7", +# "Content-Type": "application/x-www-form-urlencoded", +# "Host": "httpbin.org", +# "User-Agent": "rest-client/2.1.0 (linux-gnu x86_64) ruby/2.3.3p222" +# }, +# "json": null, +# "url": "https://httpbin.org/post" +# } # module RestClient @@ -96,6 +137,10 @@ module RestClient @proxy ||= nil end + # Set a proxy URL to use for all requests. + # + # @param value [String] The proxy URL. + # def self.proxy=(value) @proxy = value @proxy_set = true @@ -113,12 +158,21 @@ module RestClient # Setup the log for RestClient calls. # Value should be a logger but can can be stdout, stderr, or a filename. # You can also configure logging by the environment variable RESTCLIENT_LOG. + # + # @param log [Logger, #<<, String] The log to write to. See {.create_log} + # def self.log= log @@log = create_log log end - # Create a log that respond to << like a logger - # param can be 'stdout', 'stderr', a string (then we will log to that file) or a logger (then we return it) + # Create a log that responds to `<<` like a Logger. + # + # @param param [Logger, #<<, String, nil] The log to write to. Should be a + # Logger, IO, or other object with a `<<` method. If param is a String, it + # will be treated as a filename and that file will be opened as a log file. + # The special Strings `"stdout"` and `"stderr"` will log to `STDOUT` and + # `STDERR`. + # def self.create_log param if param if param.is_a? String @@ -158,10 +212,16 @@ module RestClient @@log = nil - def self.log # :nodoc: + # The RestClient global logger. This will be used for all requests unless a + # `:log` option is set on the individual request. + # + # @see log= + # + def self.log @@env_log || @@log end + # Array of procs executed prior to each request. @@before_execution_procs = [] # Add a Proc to be called before each request in executed. @@ -176,7 +236,8 @@ module RestClient @@before_execution_procs = [] end - def self.before_execution_procs # :nodoc: + # Array of procs executed prior to each request. + def self.before_execution_procs @@before_execution_procs end diff --git a/lib/restclient/abstract_response.rb b/lib/restclient/abstract_response.rb index 153776a..a04e8d6 100644 --- a/lib/restclient/abstract_response.rb +++ b/lib/restclient/abstract_response.rb @@ -3,6 +3,17 @@ require 'http-cookie' module RestClient + # The AbstractResponse module provides common functionality for RestClient + # responses. It is included by {RestClient::Response} and + # {RestClient::RawResponse}. + # + # Classes that include AbstractResponse should call {response_set_vars} as + # part of their `#initialize` method. + # + # Ideally this would have been a parent class, not a module. But the original + # RestClient API made {Response} be a subclass of `String`, so we are + # stuck with that for backwards compatibility. + # module AbstractResponse attr_reader :net_http_res, :request, :start_time, :end_time, :duration @@ -12,10 +23,17 @@ module RestClient end # Logger from the request, potentially nil. + # + # @see Request#log + # def log request.log end + # Write log information about the response. + # + # @return [void] + # def log_response return unless log @@ -27,21 +45,39 @@ module RestClient end # HTTP status code + # + # @return [Integer] + # def code @code ||= @net_http_res.code.to_i end + # An array of prior responses in the redirection chain, if any. If + # RestClient followed any redirects, this provides a way to see each + # individual response in the chain. + # + # @return [Array] + # + # @see Request#redirection_history + # def history @history ||= request.redirection_history || [] end # A hash of the headers, beautified with symbols and underscores. - # e.g. "Content-type" will become :content_type. + # e.g. `"Content-type"` will become `:content_type`. + # + # @see beautify_headers + # + # @return [Hash] + # def headers @headers ||= AbstractResponse.beautify_headers(@net_http_res.to_hash) end # The raw headers. + # + # @return [Hash] def raw_headers @raw_headers ||= @net_http_res.to_hash end @@ -49,6 +85,9 @@ module RestClient # @param [Net::HTTPResponse] net_http_res # @param [RestClient::Request] request # @param [Time] start_time + # + # @return [void] + # def response_set_vars(net_http_res, request, start_time) @net_http_res = net_http_res @request = request @@ -67,9 +106,9 @@ module RestClient # Hash of cookies extracted from response headers. # - # NB: This will return only cookies whose domain matches this request, and - # may not even return all of those cookies if there are duplicate names. - # Use the full cookie_jar for more nuanced access. + # **Note:** This will return only cookies whose domain matches this + # request, and may not even return all of those cookies if there are + # duplicate names. Use the full cookie_jar for more nuanced access. # # @see #cookie_jar # @@ -104,11 +143,20 @@ module RestClient # # For 20x status codes: return the response itself # - # For 30x status codes: - # 301, 302, 307: redirect GET / HEAD if there is a Location header - # 303: redirect, changing method to GET, if there is a Location header + # For 30x status codes, if there is a `Location` header and we have not yet + # reached {Request#max_redirects}: # - # For all other responses, raise a response exception + # - 301, 302, 307: redirect GET / HEAD requests + # - 303: redirect, changing method to GET + # + # For all other responses, raise a response exception, a subclass of + # {ExceptionWithResponse} corresponding to the HTTP status code. + # + # For example, HTTP 404 => {RestClient::NotFound RestClient::NotFound} + # + # @raise [ExceptionWithResponse] for non-20x status codes, the exception + # from {RestClient::Exceptions::EXCEPTIONS_MAP} based on + # {RestClient::STATUSES} will be thrown. # def return!(&block) case code @@ -130,11 +178,13 @@ module RestClient end end + # @deprecated Use {code} instead. def to_i - warn('warning: calling Response#to_i is not recommended') + warn('warning: calling Response#to_i is deprecated. Use .code instead.') super end + # @return [String] def description "#{code} #{STATUSES[code]} | #{(headers[:content_type] || '').gsub(/;.*$/, '')} #{size} bytes\n" end diff --git a/lib/restclient/exceptions.rb b/lib/restclient/exceptions.rb index 5703bff..fb0117a 100644 --- a/lib/restclient/exceptions.rb +++ b/lib/restclient/exceptions.rb @@ -2,18 +2,26 @@ module RestClient # Hash of HTTP status code => message. # - # 1xx: Informational - Request received, continuing process - # 2xx: Success - The action was successfully received, understood, and - # accepted - # 3xx: Redirection - Further action must be taken in order to complete the - # request - # 4xx: Client Error - The request contains bad syntax or cannot be fulfilled - # 5xx: Server Error - The server failed to fulfill an apparently valid - # request + # ### HTTP status code families: + # + # - 1xx: Informational --- Request received, continuing process + # - 2xx: Success --- The action was successfully received, understood, and + # accepted + # - 3xx: Redirection --- Further action must be taken in order to complete + # the request + # - 4xx: Client Error --- The request contains bad syntax or cannot be + # fulfilled + # - 5xx: Server Error --- The server failed to fulfill an apparently valid + # request + # + # This hash is used to populate all of the individual response exception + # classes, which are collected in {RestClient::Exceptions::EXCEPTIONS_MAP}. # # @see # http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml # + # **TODO:** Move these out of the top-level namespace and into {Exceptions}. + # STATUSES = {100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', #WebDAV @@ -84,6 +92,11 @@ module RestClient 511 => 'Network Authentication Required', # RFC6585 } + # This Hash contains a mapping of aliases for the canonical HTTP status code + # classes that are preserved for backwards compatibility. For example, + # `RestClient::ResourceNotFound` is an alias for `RestClient::NotFound`. + # + # **TODO:** Move this out of the top-level namespace and into {Exceptions}. STATUSES_COMPATIBILITY = { # The RFCs all specify "Not Found", but "Resource Not Found" was used in # earlier RestClient releases. @@ -101,16 +114,22 @@ module RestClient # This is the base RestClient exception class. Rescue it if you want to - # catch any exception that your request might raise - # You can get the status code by e.http_code, or see anything about the - # response via e.response. - # For example, the entire result body (which is - # probably an HTML error page) is e.response. + # catch any exception that your request might raise. Note that the underlying + # network and socket libraries may raise other exceptions. + # + # You can get the status code with {#http_code}, or see anything about the + # response via {#response}. + # + # For more detailed information from the response, just call the + # corresponding methods on the response object (e.g. `e.response.history`). + # class Exception < RuntimeError attr_accessor :response attr_accessor :original_exception attr_writer :message + # @param response [RestClient::Response, RestClient::RawResponse] + # def initialize response = nil, initial_response_code = nil @response = response @message = nil @@ -126,10 +145,14 @@ module RestClient end end + # @see Response#headers + # @return [Hash, nil] def http_headers @response.headers if @response end + # @see Response#body + # @return [String, nil] def http_body @response.body if @response end @@ -169,7 +192,9 @@ module RestClient # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html # module Exceptions - # Map http status codes to the corresponding exception class + # Map http status codes to the corresponding exception class. + # This Hash is populated when the library is loaded, so values won't appear + # in the YARD documentation. EXCEPTIONS_MAP = {} end diff --git a/lib/restclient/payload.rb b/lib/restclient/payload.rb index 5112f33..cf265c7 100644 --- a/lib/restclient/payload.rb +++ b/lib/restclient/payload.rb @@ -10,6 +10,9 @@ rescue LoadError end module RestClient + + # The Payload module contains several different classes used to process and + # represent different types of HTTP bodies / payloads. module Payload extend self @@ -56,6 +59,7 @@ module RestClient end end + # The base class for all the Payload classes. class Base def initialize(params) build_stream(params) diff --git a/lib/restclient/platform.rb b/lib/restclient/platform.rb index 87df973..a642172 100644 --- a/lib/restclient/platform.rb +++ b/lib/restclient/platform.rb @@ -29,10 +29,20 @@ module RestClient RUBY_ENGINE == 'jruby' end + # Return the host architecture and CPU from `RbConfig::CONFIG`. + # + # @return [String] def self.architecture "#{RbConfig::CONFIG['host_os']} #{RbConfig::CONFIG['host_cpu']}" end + # Return information about the ruby version from `RUBY_ENGINE`, + # `RUBY_VERSION`, and `RUBY_PATCHLEVEL`. + # + # When running in jruby, also return the jruby version. + # + # @return [String] + # def self.ruby_agent_version case RUBY_ENGINE when 'jruby' @@ -42,6 +52,17 @@ module RestClient end end + # Return a reasonable string for the `User-Agent` HTTP header. + # + # @example + # "rest-client/2.1.0 (linux-gnu x86_64) ruby/2.3.3p222" + # + # @return [String] + # + # @see VERSION RestClient::VERSION + # @see .architecture + # @see .ruby_agent_version + # def self.default_user_agent "rest-client/#{VERSION} (#{architecture}) #{ruby_agent_version}" end diff --git a/lib/restclient/raw_response.rb b/lib/restclient/raw_response.rb index 508eb4f..87524a4 100644 --- a/lib/restclient/raw_response.rb +++ b/lib/restclient/raw_response.rb @@ -1,14 +1,35 @@ module RestClient - # The response from RestClient on a raw request looks like a string, but is - # actually one of these. 99% of the time you're making a rest call all you - # care about is the body, but on the occassion you want to fetch the - # headers you can: + + # {RawResponse} is used to represent RestClient responses when + # `:raw_response => true` is passed to the {Request}. # - # RestClient.get('http://example.com').headers[:content_type] + # Instead of processing the response data in various ways, the `RawResponse` + # downloads the response body to a `Tempfile` and does little processing of + # the underlying `Net::HTTPResponse` object. This is especially useful for + # large downloads when you don't want to load the entire response into + # memory. + # + # Use {#file} to access the `Tempfile` containing the raw response body. The + # file path is accessible at `.file.path`. + # + # **Note that like all `Tempfile` objects, the {#file} will be deleted when + # the object is dereferenced.** + # + # This class brings in all the common functionality from {AbstractResponse}, + # such as {AbstractResponse.headers}, etc. + # + # @example + # + # r = RestClient::Request.execute(method: :get, url: 'http://example.com', raw_response: true) + # r.code + # # => 200 + # puts r.file.inspect + # # => # + # r.file.path + # # => "/tmp/rest-client.20170102-15213-b8kgcj" + # r.size + # # => 1270 # - # In addition, if you do not use the response as a string, you can access - # a Tempfile object at res.file, which contains the path to the raw - # downloaded request body. class RawResponse include AbstractResponse @@ -36,11 +57,18 @@ module RestClient body end + # Read the response body from {#file} into memory and return as a String. + # + # @return [String] + # def body @file.rewind @file.read end + # Return the response body file size. + # + # @return [Integer] def size file.size end diff --git a/lib/restclient/request.rb b/lib/restclient/request.rb index be75628..cfb7f3f 100644 --- a/lib/restclient/request.rb +++ b/lib/restclient/request.rb @@ -15,24 +15,27 @@ module RestClient # call it directly if you'd like to use a method not supported by the # main API. # - # @example using {.execute} class method: + # @example Using {.execute} class method: # RestClient::Request.execute(method: :head, url: 'http://example.com') # - # @example initializing {#initialize} and then calling {#execute}: + # @example Initializing {#initialize} and then calling {#execute}: # req = RestClient::Request.new(method: :get, url: 'http://example.com', timeout: 5) # req.execute # # The `:method` and `:url` parameters are required. All others are optional. # - # @deprecated *Note:* - # The RestClient API has a very ugly misfeature that dates to the original - # design, where certain options are destructively pulled out of the - # `:headers` hash that normally contains HTTP request headers. This is - # because in the top-level helper shortcuts like {RestClient.get}, the only - # hash argument permitted is the headers hash, so there is no place to put - # options. For example, while it is currently allowed to pass options like - # `:params` or `:cookies` as keys inside the `:headers` hash, this is - # strongly discouraged. + # **See {#initialize} for the full list of available options.** + # + # **Deprecation note:** + # Certain options are accepted as keys in the headers hash, but doing so is + # deprecated. This misfeature in the RestClient API dates to the original + # design, where certain options are destructively pulled out of the + # `:headers` hash that normally contains HTTP request headers. This is + # because in the top-level helper shortcuts like {RestClient.get}, the only + # hash argument permitted is the headers hash, so there is no place to put + # options. For example, while it is currently allowed to pass options like + # `:params` or `:cookies` as keys inside the `:headers` hash, this is + # strongly discouraged. # class Request @@ -46,12 +49,18 @@ module RestClient # Shorthand for initializing a Request and executing it. # - # RestClient::Request.execute is a great way to pass complex options. + # `RestClient::Request.execute` is the recommended way to pass complex + # options. It is shorthand for `RestClient::Request.new(args).execute`. # - # For example: - # RestClient::Request.execute(method: get, url: 'http://example.com', timeout: 5) + # @example + # RestClient::Request.execute(method: :get, url: 'http://example.com', timeout: 5) # - # @see RestClient::Request + # @example + # RestClient::Request.execute(method: :get, url: 'http://httpbin.org/redirect/2', max_redirects: 1) + # + # @see RestClient::Request#initialize + # + # @return [RestClient::Response, RestClient::RawResponse] # def self.execute(args, & block) new(args).execute(& block) @@ -71,15 +80,15 @@ module RestClient # # @param [Hash] args # - # @option args [String] :url *Required.* The HTTP URL to request. - # @option args [String, Symbol] :method *Required.* The HTTP request method - # or verb, such as "GET", "HEAD", or "POST". + # @option args [String] :url **Required.** The HTTP URL to request. + # @option args [String, Symbol] :method **Required.** The HTTP request method + # or verb, such as `"GET"`, `"HEAD"`, or `"POST"`. # # @option args [Hash] :headers The HTTP request headers. Keys may be # Symbol or String. Symbol keys will be converted to String header names # by {#stringify_headers}. For backwards compatibility, this Hash # recognizes certain keys that will be pulled out as options, but relying - # on this is deprecated and strongly discouraged. + # on this behavior is deprecated and strongly discouraged. # @option args :cookies [HTTP::CookieJar, Hash{String, Symbol => String}, # Array] The cookies to be sent with the request. This can # be passed as a Hash, an array of HTTP::Cookie objects, or as a full @@ -121,10 +130,10 @@ module RestClient # timeout on an individual network read, but does not limit the overall # duration of the request so long as the server continues sending data at # a trickle. Pass nil to disable the timeout. See - # {Net::HTTP#read_timeout}. + # `Net::HTTP#read_timeout`. # @option args [Numeric, nil] :open_timeout Number of seconds to wait for # the connection to be established. Pass nil to disable the timeout. See - # {Net::HTTP#open_timeout}. + # `Net::HTTP#open_timeout`. # @option args [Numeric, nil] :timeout Set both `:read_timeout` and # `:open_timeout` # @option args :ssl_client_cert @@ -258,7 +267,7 @@ module RestClient # Extract the query parameters and append them to the url # - # Look through the headers hash for a :params option (case-insensitive, + # Look through the headers hash for a `:params` option (case-insensitive, # may be string or symbol). If present and the value is a Hash or # RestClient::ParamsArray, *delete* the key/value pair from the headers # hash and encode the value into a query string. Append this query string @@ -266,10 +275,12 @@ module RestClient # # @param [String] url # @param [Hash] headers An options/headers hash to process. Mutation - # warning: the params key may be removed if present! + # warning: the `params` key may be removed if present! # # @return [String] resulting url with query string # + # @api private + # def process_url_params(url, headers) url_params = nil @@ -342,19 +353,21 @@ module RestClient end # Process cookies passed as hash or as HTTP::CookieJar. For backwards - # compatibility, these may be passed as a :cookies option masquerading - # inside the headers hash. To avoid confusion, if :cookies is passed in - # both headers and Request#initialize, raise an error. + # compatibility, these may be passed as a `:cookies` option masquerading + # inside the headers hash. To avoid confusion, if `:cookies` is passed in + # both headers and {#initialize}, raise an error. # - # :cookies may be a: - # - Hash{String/Symbol => String} - # - Array - # - HTTP::CookieJar + # `:cookies` may be a: + # + # - `Hash{String/Symbol => String}` + # - `Array` + # - `HTTP::CookieJar` + # + # **Passing as a hash:** # - # Passing as a hash: # Keys may be symbols or strings. Values must be strings. # Infer the domain name from the request URI and allow subdomains (as - # though '.example.com' had been set in a Set-Cookie header). Assume a + # though '.example.com' had been set in a `Set-Cookie` header). Assume a # path of '/'. # # RestClient::Request.new(url: 'http://example.com', method: :get, @@ -362,28 +375,30 @@ module RestClient # ) # # results in cookies as though set from the server by: + # # Set-Cookie: foo=Value; Domain=.example.com; Path=/ # Set-Cookie: bar=123; Domain=.example.com; Path=/ # # which yields a client cookie header of: + # # Cookie: foo=Value; bar=123 # - # Passing as HTTP::CookieJar, which will be passed through directly: + # **Passing as HTTP::CookieJar, which will be passed through directly:** # # jar = HTTP::CookieJar.new # jar.add(HTTP::Cookie.new('foo', 'Value', domain: 'example.com', # path: '/', for_domain: false)) # - # RestClient::Request.new(..., :cookies => jar) + # RestClient::Request.new('...', :cookies => jar) # # @param [URI::HTTP] uri The URI for the request. This will be used to - # infer the domain name for cookies passed as strings in a hash. To avoid - # this implicit behavior, pass a full cookie jar or use HTTP::Cookie hash - # values. - # @param [Hash] headers The headers hash from which to pull the :cookies - # option. MUTATION NOTE: This key will be deleted from the hash if - # present. - # @param [Hash] args The options passed to Request#initialize. This hash + # infer the domain name for cookies passed as strings in a hash. To avoid + # this implicit behavior, pass a full cookie jar or use `HTTP::Cookie` + # hash values. + # @param [Hash] headers The headers hash from which to pull the `:cookies` + # option. **MUTATION NOTE:** This key will be deleted from the hash if + # present. Passing cookies in this way is deprecated. + # @param [Hash] args The options passed to {Request#initialize}. This hash # will be used as another potential source for the :cookies key. # These args will not be mutated. # @@ -439,19 +454,20 @@ module RestClient end # Generate headers for use by a request. Header keys will be stringified - # using `#stringify_headers` to normalize them as capitalized strings. + # using {#stringify_headers} to normalize them as capitalized strings. # # The final headers consist of: - # - default headers from #default_headers - # - user_headers provided here - # - headers from the payload object (e.g. Content-Type, Content-Lenth) - # - cookie headers from #make_cookie_header # - # BUG: stringify_headers does not alter the capitalization of headers that - # are passed as strings, it only normalizes those passed as symbols. This - # behavior will probably remain for a while for compatibility, but it means - # that the warnings that attempt to detect accidental header overrides may - # not always work. + # - default headers from {#default_headers} + # - `user_headers` provided here + # - headers from the payload object (e.g. Content-Type, Content-Lenth) + # - cookie headers from {#make_cookie_header} + # + # **BUG:** stringify_headers does not alter the capitalization of headers + # that are passed as strings, it only normalizes those passed as symbols. + # This behavior will probably remain for a while for compatibility, but it + # means that the warnings that attempt to detect accidental header + # overrides may not always work. # https://github.com/rest-client/rest-client/issues/599 # # @param [Hash] user_headers User-provided headers to include @@ -494,7 +510,7 @@ module RestClient end # The proxy URI for this request. If `:proxy` was provided on this request, - # use it over `RestClient.proxy`. + # use it over {RestClient.proxy}. # # Return false if a proxy was explicitly set and is falsy. # @@ -518,6 +534,17 @@ module RestClient end end + # Create a new Net::HTTP object representing a connection to a server + # without actually opening the connection. This method will set up an HTTP + # proxy according to {#proxy_uri}. + # + # @param hostname [String] + # @param port [Integer] + # + # @return [Net::HTTP] + # + # @api private + # def net_http_object(hostname, port) p_uri = proxy_uri @@ -534,10 +561,28 @@ module RestClient end end + # Find the Net::HTTPRequest subclass for a given HTTP method/verb. + # + # @param method [Symbol, String] + # + # @return [Class] A subclass of Net::HTTPRequest. + # + # @api private + # def net_http_request_class(method) Net::HTTP.const_get(method.capitalize, false) end + # Actually execute the request with Net::HTTP. + # + # @param http [Net::HTTP] + # @param req [Net::HTTPRequest] + # @param body [String, IO, nil] + # + # @see https://ruby-doc.org/stdlib-2.4.0/libdoc/net/http/rdoc/Net/HTTP.html#method-i-request + # Net::HTTP#request + # + # @api private def net_http_do_request(http, req, body=nil, &block) if body && body.respond_to?(:read) req.body_stream = body @@ -557,6 +602,8 @@ module RestClient # # @return [String] # + # @api private + # def normalize_url(url) url = 'http://' + url unless url.match(%r{\A[a-z][a-z0-9+.-]*://}i) url @@ -605,11 +652,20 @@ module RestClient redacted_uri.to_s end - # Default to the global logger if there's not a request-specific one + # The log used for this request. + # + # Defaults to the global logger {RestClient.log} if there was no `:log` + # option set on this request. + # def log @log || RestClient.log end + # Write log information about the request. Called just prior to sending the + # request to the server. + # + # @return [void] + # def log_request return unless log diff --git a/lib/restclient/resource.rb b/lib/restclient/resource.rb index a5c41ac..5a7714f 100644 --- a/lib/restclient/resource.rb +++ b/lib/restclient/resource.rb @@ -4,34 +4,34 @@ module RestClient # # Example: # - # resource = RestClient::Resource.new('http://some/resource') - # jpg = resource.get(:accept => 'image/jpg') + # resource = RestClient::Resource.new('http://some/resource') + # jpg = resource.get(:accept => 'image/jpg') # # With HTTP basic authentication: # - # resource = RestClient::Resource.new('http://protected/resource', :user => 'user', :password => 'password') - # resource.delete + # resource = RestClient::Resource.new('http://protected/resource', :user => 'user', :password => 'password') + # resource.delete # # With a timeout (seconds): # - # RestClient::Resource.new('http://slow', :read_timeout => 10) + # RestClient::Resource.new('http://slow', :read_timeout => 10) # # With an open timeout (seconds): # - # RestClient::Resource.new('http://behindfirewall', :open_timeout => 10) + # RestClient::Resource.new('http://behindfirewall', :open_timeout => 10) # # You can also use resources to share common headers. For headers keys, # symbols are converted to strings. Example: # - # resource = RestClient::Resource.new('http://some/resource', :headers => { :client_version => 1 }) + # resource = RestClient::Resource.new('http://some/resource', :headers => { :client_version => 1 }) # # This header will be transported as X-Client-Version (notice the X prefix, # capitalization and hyphens) # # Use the [] syntax to allocate subresources: # - # site = RestClient::Resource.new('http://example.com', :user => 'adam', :password => 'mypasswd') - # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain' + # site = RestClient::Resource.new('http://example.com', :user => 'adam', :password => 'mypasswd') + # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain' # class Resource attr_reader :url, :options, :block diff --git a/lib/restclient/response.rb b/lib/restclient/response.rb index 8743031..0073c58 100644 --- a/lib/restclient/response.rb +++ b/lib/restclient/response.rb @@ -2,6 +2,17 @@ module RestClient # A Response from RestClient, you can access the response body, the code or the headers. # + # Response is a subclass of `String` for backwards compatibility. This may + # change in a future major release of RestClient, so it is recommended to + # explicitly call {#body} or {#to_s} before invoking `String` methods. + # + # The request that resulted in this response is accessible at {#request}. + # + # Much of the functionality is implemented on the {AbstractResponse} module. + # + # @see AbstractResponse + # @see RawResponse + # class Response < String include AbstractResponse @@ -38,14 +49,17 @@ module RestClient "" end - # Initialize a Response object. Because RestClient::Response is + # Initialize a Response object. Because `RestClient::Response` is # (unfortunately) a subclass of String for historical reasons, - # Response.create is the preferred initializer. + # `Response.create` is the preferred initializer. # # @param [String, nil] body The response body from the Net::HTTPResponse # @param [Net::HTTPResponse] net_http_res # @param [RestClient::Request] request # @param [Time] start_time + # + # @return [Response] + # def self.create(body, net_http_res, request, start_time=nil) result = self.new(body || '') @@ -55,8 +69,13 @@ module RestClient result end - # Set the String encoding according to the 'Content-Type: charset' header, + # Set the String encoding according to the `Content-Type: charset` header, # if possible. + # + # @param response [Response] + # + # @return [Response] + # def self.fix_encoding(response) charset = RestClient::Utils.get_encoding_from_headers(response.headers) encoding = nil diff --git a/lib/restclient/version.rb b/lib/restclient/version.rb index d2bbf28..6ba6901 100644 --- a/lib/restclient/version.rb +++ b/lib/restclient/version.rb @@ -1,7 +1,12 @@ module RestClient + + # The current RestClient version array VERSION_INFO = [2, 1, 0, 'rc1'] unless defined?(self::VERSION_INFO) + + # The current RestClient version string VERSION = VERSION_INFO.map(&:to_s).join('.') unless defined?(self::VERSION) + # @return [String] The current RestClient version string def self.version VERSION end