Skip to content

Commit

Permalink
🔧🏷 Add type coercion for config attributes
Browse files Browse the repository at this point in the history
This is implemented as another module included under the other two,
still based on overriding `attr_accessor`.  Types are only enforced by
the attr_writer methods.  Fortunately, rdoc isn't confused by keyword
arguments to `attr_accessor`, so the type can be added to that.

Currently, only `:boolean` and `Integer` are supported, but it should be
easy to add more.
  • Loading branch information
nevans committed Jun 12, 2024
1 parent fd518a5 commit 716804f
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
14 changes: 10 additions & 4 deletions lib/net/imap/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

require_relative "config/attr_accessors"
require_relative "config/attr_inheritance"
require_relative "config/attr_type_coercion"

module Net
class IMAP
Expand Down Expand Up @@ -48,14 +49,19 @@ def self.[](config) # :nodoc: unfinished API

include AttrAccessors
include AttrInheritance
include AttrTypeCoercion

# The debug mode (boolean)
#
# | Starting with version | The default value is |
# |-----------------------|----------------------|
# | _original_ | +false+ |
attr_accessor :debug
alias debug? debug
attr_accessor :debug, type: :boolean

# method: debug?
# :call-seq: debug? -> boolean
#
# Alias for #debug

# Seconds to wait until a connection is opened.
#
Expand All @@ -65,15 +71,15 @@ def self.[](config) # :nodoc: unfinished API
# | Starting with version | The default value is |
# |-----------------------|----------------------|
# | _original_ | +30+ seconds |
attr_accessor :open_timeout
attr_accessor :open_timeout, type: Integer

# Seconds to wait until an IDLE response is received, after
# the client asks to leave the IDLE state. See Net::IMAP#idle_done.
#
# | Starting with version | The default value is |
# |-----------------------|----------------------|
# | _original_ | +5+ seconds |
attr_accessor :idle_response_timeout
attr_accessor :idle_response_timeout, type: Integer

# Creates a new config object and initialize its attribute with +attrs+.
#
Expand Down
45 changes: 45 additions & 0 deletions lib/net/imap/config/attr_type_coercion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

module Net
class IMAP
class Config
# Adds a +type+ keyword parameter to +attr_accessor+, which enforces
# config attributes have valid types, for example: boolean, numeric,
# enumeration, non-nullable, etc.
module AttrTypeCoercion
# :stopdoc: internal APIs only

module Macros # :nodoc: internal API
def attr_accessor(attr, type: nil)
super(attr)
AttrTypeCoercion.attr_accessor(attr, type: type)
end
end
private_constant :Macros

def self.included(mod)
mod.extend Macros
end
private_class_method :included

def self.attr_accessor(attr, type: nil)
return unless type
if :boolean == type then boolean attr
elsif Integer == type then integer attr
else raise ArgumentError, "unknown type coercion %p" % [type]
end
end

def self.boolean(attr)
define_method :"#{attr}=" do |val| super !!val end
define_method :"#{attr}?" do send attr end
end

def self.integer(attr)
define_method :"#{attr}=" do |val| super Integer val end
end

end
end
end
end
22 changes: 22 additions & 0 deletions test/net/imap/test_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,28 @@ class ConfigTest < Test::Unit::TestCase
refute config.debug?
end

test "boolean type constraints and conversion" do
config = Config.new
config.debug = 111
assert_equal true, config.debug
config.debug = nil
assert_equal false, config.debug
end

test "integer type constraints and conversion" do
config = Config.new
config.open_timeout = "111"
assert_equal 111, config.open_timeout
config.open_timeout = 222.0
assert_equal 222, config.open_timeout
config.open_timeout = 333.3
assert_equal 333, config.open_timeout
assert_raise(ArgumentError) do
config.open_timeout = "444 NaN"
end
assert_equal 333, config.open_timeout
end

test ".default" do
default = Config.default
assert default.equal?(Config.default)
Expand Down

0 comments on commit 716804f

Please sign in to comment.