Skip to content

Convert ProxyOptions, RequestOptions, SSLOptions to BaseOptions #1651

@iMacTia

Description

@iMacTia

Convert ProxyOptions, RequestOptions, SSLOptions to BaseOptions

Phase: 2 - Small Options
Release Target: v2.x.1
Tracking Issue: #1647
RFC: docs/options-detach-plan.md

Overview

Convert three smaller Options subclasses to inherit from BaseOptions instead of Options:

  1. ProxyOptions (38 lines, minimal coupling)
  2. RequestOptions (23 lines, references ProxyOptions)
  3. SSLOptions (76 lines, adapter interactions)

These are grouped together because they're relatively simple and can be released together in v2.x.1.

Implementation Strategy

Convert each class one at a time, testing thoroughly after each:

  1. ProxyOptions (smallest, no dependencies)
  2. RequestOptions (depends on ProxyOptions)
  3. SSLOptions (larger but self-contained)

Part 1: Convert ProxyOptions

File: lib/faraday/options/proxy_options.rb

Current Structure

  • Inherits from Options
  • 6 members: :uri, :user, :password, :scheme, :host, :port, :path
  • Has URI coercion logic
  • Has delegators for scheme, host, port, path
  • Handles empty string => nil

New Structure

# frozen_string_literal: true

module Faraday
  class ProxyOptions < BaseOptions
    MEMBERS = %i[uri user password].freeze
    COERCIONS = { uri: Faraday::Utils::URI }.freeze

    attr_accessor :uri, :user, :password

    def initialize(options = {})
      # Handle empty string => nil
      options = nil if options.is_a?(String) && options.empty?
      super(options)
      
      # Extract user/password from URI if present
      return unless uri && uri.user

      @user ||= Utils.unescape(uri.user)
      @password ||= Utils.unescape(uri.password)
    end

    # Delegators for URI properties
    def scheme
      uri&.scheme
    end

    def host
      uri&.host
    end

    def port
      uri&.port
    end

    def path
      uri&.path
    end
  end
end

Tasks for ProxyOptions

  • Update class to inherit from BaseOptions
  • Define MEMBERS and COERCIONS constants
  • Add explicit attr_accessor for members
  • Preserve URI coercion and user/password extraction
  • Preserve delegator methods (scheme, host, port, path)
  • Handle empty string => nil case
  • Update tests in spec/faraday/options/proxy_options_spec.rb
  • Run integration tests

Part 2: Convert RequestOptions

File: lib/faraday/options/request_options.rb

Current Structure

  • Inherits from Options
  • 6 members: :params_encoder, :proxy, :bind, :timeout, :open_timeout, :read_timeout, :write_timeout, :boundary, :oauth, :context, :on_data
  • Has nested ProxyOptions
  • Has stream_response? method

New Structure

# frozen_string_literal: true

module Faraday
  class RequestOptions < BaseOptions
    MEMBERS = %i[
      params_encoder proxy bind timeout open_timeout read_timeout 
      write_timeout boundary oauth context on_data
    ].freeze
    
    COERCIONS = { proxy: ProxyOptions }.freeze

    attr_accessor :params_encoder, :proxy, :bind, :timeout, :open_timeout,
                  :read_timeout, :write_timeout, :boundary, :oauth, :context, :on_data

    def stream_response?
      !!on_data
    end
  end
end

Tasks for RequestOptions

  • Update class to inherit from BaseOptions
  • Define MEMBERS and COERCIONS constants
  • Add explicit attr_accessor for all members
  • Ensure proxy coercion works (depends on ProxyOptions conversion)
  • Preserve stream_response? method
  • Update tests in spec/faraday/options/request_options_spec.rb
  • Run integration tests

Part 3: Convert SSLOptions

File: lib/faraday/options/ssl_options.rb

Current Structure

  • Inherits from Options
  • 15 members including ca_file, ca_path, cert_store, client_cert, client_key, etc.
  • Has lazy cert_store initialization
  • Has helper methods: verify?, disable?, verify_hostname?

New Structure

# frozen_string_literal: true

module Faraday
  class SSLOptions < BaseOptions
    MEMBERS = %i[
      ca_file ca_path cert_store client_cert client_key certificate_authority
      client_cert_passwd verify verify_hostname verify_mode version
      min_version max_version ciphers
    ].freeze
    
    COERCIONS = {}.freeze

    attr_reader :cert_store
    attr_accessor :ca_file, :ca_path, :client_cert, :client_key, 
                  :certificate_authority, :client_cert_passwd, :verify,
                  :verify_hostname, :verify_mode, :version, :min_version,
                  :max_version, :ciphers

    def cert_store=(value)
      @cert_store = value
    end

    def cert_store
      @cert_store ||= begin
        # Lazy initialization logic
        require 'openssl'
        OpenSSL::X509::Store.new.tap do |store|
          store.set_default_paths
          store.add_file(ca_file) if ca_file
          store.add_path(ca_path) if ca_path
        end
      end
    end

    def verify?
      verify != false
    end

    def disable?
      !verify?
    end

    def verify_hostname?
      verify_hostname != false
    end
  end
end

Tasks for SSLOptions

  • Update class to inherit from BaseOptions
  • Define MEMBERS and COERCIONS constants
  • Add explicit attr_accessor for all members
  • Preserve lazy cert_store initialization
  • Preserve helper methods: verify?, disable?, verify_hostname?
  • Update tests in spec/faraday/options/ssl_options_spec.rb
  • Test adapter interactions (particularly net_http, excon)
  • Run integration tests

Acceptance Criteria

  • All three classes inherit from BaseOptions
  • All existing public APIs preserved
  • All tests pass (unit + integration)
  • No breaking changes detected
  • Backward compatibility validated

Dependencies

Files to Modify

  • lib/faraday/options/proxy_options.rb
  • lib/faraday/options/request_options.rb
  • lib/faraday/options/ssl_options.rb
  • spec/faraday/options/proxy_options_spec.rb
  • spec/faraday/options/request_options_spec.rb
  • spec/faraday/options/ssl_options_spec.rb

Testing Checklist

For each class, verify:

  • .from works with hash, instance, and nil
  • Initialization from hash works
  • Nested coercion works (ProxyOptions in RequestOptions)
  • update and merge! work correctly
  • deep_dup creates independent copies
  • to_hash returns correct structure
  • All helper methods preserved
  • Integration tests pass

Backward Compatibility

All existing APIs preserved:

  • ProxyOptions: URI delegators, user/password extraction
  • RequestOptions: stream_response? method, proxy coercion
  • SSLOptions: lazy cert_store, verify helpers

No breaking changes expected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions