diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index 853e23c4..a58a4ca8 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -3,6 +3,7 @@ require_relative "config/attr_accessors" require_relative "config/attr_inheritance" +require_relative "config/attr_type_coercion" module Net class IMAP @@ -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. # @@ -65,7 +71,7 @@ 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. @@ -73,7 +79,7 @@ def self.[](config) # :nodoc: unfinished API # | 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+. # diff --git a/lib/net/imap/config/attr_type_coercion.rb b/lib/net/imap/config/attr_type_coercion.rb new file mode 100644 index 00000000..7511193d --- /dev/null +++ b/lib/net/imap/config/attr_type_coercion.rb @@ -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 diff --git a/test/net/imap/test_config.rb b/test/net/imap/test_config.rb index 208c09df..e14d7b74 100644 --- a/test/net/imap/test_config.rb +++ b/test/net/imap/test_config.rb @@ -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)