@@ -54,25 +54,112 @@ class IMAP
54
54
# plain_client.config.inherited?(:debug) # => true
55
55
# plain_client.config.debug? # => false
56
56
#
57
+ # == Versioned defaults
58
+ #
59
+ # The effective default configuration for a specific +x.y+ version of
60
+ # +net-imap+ can be loaded with the +config+ keyword argument to
61
+ # Net::IMAP.new. Requesting default configurations for previous versions
62
+ # enables extra backward compatibility with those versions:
63
+ #
64
+ # client = Net::IMAP.new(hostname, config: 0.3)
65
+ # client.config.sasl_ir # => false
66
+ # client.config.responses_without_block # => :silence_deprecation_warning
67
+ #
68
+ # client = Net::IMAP.new(hostname, config: 0.4)
69
+ # client.config.sasl_ir # => true
70
+ # client.config.responses_without_block # => :silence_deprecation_warning
71
+ #
72
+ # client = Net::IMAP.new(hostname, config: 0.5)
73
+ # client.config.sasl_ir # => true
74
+ # client.config.responses_without_block # => :warn
75
+ #
76
+ # client = Net::IMAP.new(hostname, config: :future)
77
+ # client.config.sasl_ir # => true
78
+ # client.config.responses_without_block # => :raise
79
+ #
80
+ # The versioned default configs inherit certain specific config options from
81
+ # Config.global, for example #debug:
82
+ #
83
+ # client = Net::IMAP.new(hostname, config: 0.4)
84
+ # Net::IMAP.debug = false
85
+ # client.config.debug? # => false
86
+ #
87
+ # Net::IMAP.debug = true
88
+ # client.config.debug? # => true
89
+ #
90
+ # === Named defaults
91
+ # In addition to +x.y+ version numbers, the following aliases are supported:
92
+ #
93
+ # [+:default+]
94
+ # An alias for +:current+.
95
+ #
96
+ # >>>
97
+ # *NOTE*: This is _not_ the same as Config.default. It inherits some
98
+ # attributes from Config.global, for example: #debug.
99
+ # [+:current+]
100
+ # An alias for the current +x.y+ version's defaults.
101
+ # [+:next+]
102
+ # The _planned_ config for the next +x.y+ version.
103
+ # [+:future+]
104
+ # The _planned_ eventual config for some future +x.y+ version.
105
+ #
106
+ # For example, to raise exceptions for all current deprecations:
107
+ # client = Net::IMAP.new(hostname, config: :future)
108
+ # client.responses # raises an ArgumentError
57
109
#
58
110
# == Thread Safety
59
111
#
60
112
# *NOTE:* Updates to config objects are not synchronized for thread-safety.
61
113
#
62
114
class Config
115
+ # Array of attribute names that are _not_ loaded by #load_defaults.
116
+ DEFAULT_TO_INHERIT = %i[ debug ] . freeze
117
+ private_constant :DEFAULT_TO_INHERIT
118
+
63
119
# The default config, which is hardcoded and frozen.
64
120
def self . default ; @default end
65
121
66
122
# The global config object. Also available from Net::IMAP.config.
67
123
def self . global ; @global end
68
124
69
- def self . []( config ) # :nodoc: unfinished API
125
+ # A hash of hard-coded configurations, indexed by version number.
126
+ def self . version_defaults ; @version_defaults end
127
+ @version_defaults = { }
128
+
129
+ # :call-seq:
130
+ # Net::IMAP::Config[number] -> versioned config
131
+ # Net::IMAP::Config[symbol] -> named config
132
+ # Net::IMAP::Config[hash] -> new frozen config
133
+ # Net::IMAP::Config[config] -> same config
134
+ #
135
+ # Given a version number, returns the default configuration for the target
136
+ # version. See Config@Versioned+defaults.
137
+ #
138
+ # Given a version name, returns the default configuration for the target
139
+ # version. See Config@Named+defaults.
140
+ #
141
+ # Given a Hash, creates a new _frozen_ config which inherits from
142
+ # Config.global. Use Config.new for an unfrozen config.
143
+ #
144
+ # Given a config, returns that same config.
145
+ def self . []( config )
70
146
if config . is_a? ( Config ) || config . nil? && global . nil?
71
147
config
148
+ elsif config . respond_to? ( :to_hash )
149
+ new ( global , **config ) . freeze
72
150
else
73
- raise TypeError , "no implicit conversion of %s to %s" % [
74
- config . class , Config
75
- ]
151
+ version_defaults . fetch ( config ) {
152
+ case config
153
+ when Numeric
154
+ raise RangeError , "unknown config version: %p" % [ config ]
155
+ when Symbol
156
+ raise KeyError , "unknown config name: %p" % [ config ]
157
+ else
158
+ raise TypeError , "no implicit conversion of %s to %s" % [
159
+ config . class , Config
160
+ ]
161
+ end
162
+ }
76
163
end
77
164
end
78
165
@@ -198,6 +285,31 @@ def to_h; data.members.to_h { [_1, send(_1)] } end
198
285
199
286
@global = default . new
200
287
288
+ version_defaults [ 0.4 ] = Config [
289
+ default . to_h . reject { |k , v | DEFAULT_TO_INHERIT . include? ( k ) }
290
+ ]
291
+
292
+ version_defaults [ 0 ] = Config [ 0.4 ] . dup . update (
293
+ sasl_ir : false ,
294
+ ) . freeze
295
+ version_defaults [ 0.0 ] = Config [ 0 ]
296
+ version_defaults [ 0.1 ] = Config [ 0 ]
297
+ version_defaults [ 0.2 ] = Config [ 0 ]
298
+ version_defaults [ 0.3 ] = Config [ 0 ]
299
+
300
+ version_defaults [ 0.5 ] = Config [ 0.4 ] . dup . update (
301
+ responses_without_block : :warn ,
302
+ ) . freeze
303
+
304
+ version_defaults [ :default ] = Config [ 0.4 ]
305
+ version_defaults [ :current ] = Config [ 0.4 ]
306
+ version_defaults [ :next ] = Config [ 0.5 ]
307
+
308
+ version_defaults [ :future ] = Config [ 0.5 ] . dup . update (
309
+ responses_without_block : :raise ,
310
+ ) . freeze
311
+
312
+ version_defaults . freeze
201
313
end
202
314
end
203
315
end
0 commit comments