-
Notifications
You must be signed in to change notification settings - Fork 999
Open
Description
Foundation: Create OptionsLike and BaseOptions
Phase: 1 - Foundation
Release Target: v2.x.0
Tracking Issue: #1647
RFC: docs/options-detach-plan.md
Overview
Create the foundational components for the new Options architecture:
Faraday::OptionsLike- Marker module for duck-typed interopFaraday::BaseOptions- Abstract superclass with shared behavior
This establishes the base architecture that all Options subclasses will use.
Implementation Details
1. Create OptionsLike Module
File: lib/faraday/options_like.rb
# frozen_string_literal: true
module Faraday
# Marker module for Options-like objects.
# Enables duck-typed interop and integration with Utils.deep_merge!
module OptionsLike
end
endPurpose: Allows both legacy Options and new BaseOptions classes to be treated uniformly in duck-typed contexts.
2. Create BaseOptions Class
File: lib/faraday/base_options.rb
# frozen_string_literal: true
module Faraday
# Abstract base class for Options-like classes.
# Provides common functionality for nested coercion, deep merging, and duplication.
class BaseOptions
include OptionsLike
# Subclasses must define:
# - MEMBERS: Array of attribute names (symbols)
# - COERCIONS: Hash mapping attribute names to coercion classes
class << self
# Create new instance from hash or existing instance
def from(value)
return value if value.is_a?(self)
return new if value.nil?
new(value)
end
end
def initialize(options = {})
options = options.to_hash if options.respond_to?(:to_hash)
self.class::MEMBERS.each do |key|
value = options[key] || options[key.to_s]
value = coerce(key, value)
instance_variable_set(:"@#{key}", value)
end
end
# Update this instance with values from another hash/instance
def update(obj)
obj = obj.to_hash if obj.respond_to?(:to_hash)
obj.each do |key, value|
key = key.to_sym
if self.class::MEMBERS.include?(key)
value = coerce(key, value)
instance_variable_set(:"@#{key}", value)
end
end
self
end
# Non-destructive merge
def merge(obj)
deep_dup.merge!(obj)
end
# Destructive merge (uses Utils.deep_merge!)
def merge!(obj)
obj = obj.to_hash if obj.respond_to?(:to_hash)
Utils.deep_merge!(to_hash, obj)
update(to_hash)
end
# Deep duplication
def deep_dup
self.class.new(
self.class::MEMBERS.each_with_object({}) do |key, hash|
value = instance_variable_get(:"@#{key}")
hash[key] = Utils.deep_dup(value)
end
)
end
# Convert to hash
def to_hash
self.class::MEMBERS.each_with_object({}) do |key, hash|
hash[key] = instance_variable_get(:"@#{key}")
end
end
# Inspect
def inspect
"#<#{self.class} #{to_hash.inspect}>"
end
private
def coerce(key, value)
coercion = self.class::COERCIONS[key]
return value unless coercion
return value if value.is_a?(coercion)
coercion.from(value)
end
end
end3. Update Autoload
File: lib/faraday.rb
Add to autoloads section:
autoload :OptionsLike, 'faraday/options_like'
autoload :BaseOptions, 'faraday/base_options'Tasks
- Create
lib/faraday/options_like.rb - Create
lib/faraday/base_options.rbwith full implementation - Add autoloads to
lib/faraday.rb - Create
spec/faraday/base_options_spec.rbwith comprehensive tests:- Test
.fromclass method - Test initialization from hash
- Test initialization from instance
- Test
updatemethod - Test
merge(non-destructive) - Test
merge!(destructive) - Test
deep_dup - Test
to_hash - Test
inspect - Test nested coercion behavior
- Test
- Run full test suite to ensure no regressions
- Integration tests pass (Add comprehensive integration tests using faraday-live approach #1648)
Acceptance Criteria
-
OptionsLikemodule exists and can be included -
BaseOptionsprovides all shared functionality - All tests pass
- No breaking changes to existing Options classes
- Documentation includes YARD comments
Dependencies
- Depends on: Add comprehensive integration tests using faraday-live approach #1648 (integration tests must exist first)
- Blocks: All subsequent issues
Files to Create
lib/faraday/options_like.rblib/faraday/base_options.rbspec/faraday/base_options_spec.rb
Files to Modify
lib/faraday.rb(add autoloads)
Backward Compatibility
No breaking changes - this only adds new classes, doesn't modify existing ones.
Copilot
Metadata
Metadata
Assignees
Labels
No labels