net-imap 0.5.14 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33bccbb75eba778cb42fc5340afc2f10a899ca671123e8b538a39acbdf16bd1b
4
- data.tar.gz: c4252164f38a0f36b827fb32247500e95293880e2f8026f4b7ed04926614df41
3
+ metadata.gz: 561e6151a9540c8eaa6c12fd16d625061843a053fb0b0c8a2f215eb1a518ef70
4
+ data.tar.gz: 5be61c15097d2e0007822fc6c4825f59795d6078d6cf9b5cbe6ed4dd2bd1d22e
5
5
  SHA512:
6
- metadata.gz: e682041f5c1f0e071578c0910f3eace9438064741cce16b148870c73137fd38926154269907f4948dec365e83b9115facff5ec21ac23072270ef39bab64cea10
7
- data.tar.gz: 8a04cac2ad54cd0bc4b2e2f5855bac16f571c3ab6aa0183caa1427ad7c420f8c3920e74b85871c22454dbd827c7772b24cfe96bcaf262222664c05f7483f5dbd
6
+ metadata.gz: f84a8adf89f177e953fe57e390b027232bc4c7325ecb0049221a88db87fccc76a09281aea87c4f48de05bc547db1248b628b9c271134a466aedba953305001cc
7
+ data.tar.gz: 0b82898fb8581bab9a30c5b3edb96c89d81c4bddf197ade29a25ef166db827983a6c2c25d1eda9989a9f985cfcbec52f4bf484dbc1b57cfde32f432dcd0dfbc3
data/Gemfile CHANGED
@@ -7,6 +7,7 @@ gemspec
7
7
  # gem "digest" # not included as a workaround for #576
8
8
  gem "strscan"
9
9
  gem "base64"
10
+ gem "psych", ">= 5.3.0" # 5.2.5 for Data serialization, 5.3.0 for TruffleRuby
10
11
 
11
12
  gem "irb"
12
13
  gem "rake"
@@ -3,9 +3,6 @@
3
3
  require "date"
4
4
 
5
5
  require_relative "errors"
6
- require_relative "data_lite"
7
-
8
- # :enddoc:
9
6
 
10
7
  module Net
11
8
  class IMAP < Protocol
@@ -28,7 +25,6 @@ module Net
28
25
  end
29
26
  when Time, Date, DateTime
30
27
  when Symbol
31
- Flag.validate(data)
32
28
  else
33
29
  data.validate
34
30
  end
@@ -49,7 +45,7 @@ module Net
49
45
  when Date
50
46
  send_date_data(data)
51
47
  when Symbol
52
- Flag[data].send_data(self, tag)
48
+ send_symbol_data(data)
53
49
  else
54
50
  data.send_data(self, tag)
55
51
  end
@@ -81,23 +77,9 @@ module Net
81
77
  put_string('"' + str.gsub(/["\\]/, "\\\\\\&") + '"')
82
78
  end
83
79
 
84
- def send_binary_literal(*a, **kw); send_literal(*a, **kw, binary: true) end
85
-
86
- # `non_sync` is an optional tri-state flag:
87
- # * `true` -> Force non-synchronizing `LITERAL+`/`LITERAL-` behavior.
88
- # TODO: raise or warn when capabilities don't allow non_sync.
89
- # * `false` -> Force normal synchronizing literal behavior.
90
- # * `nil` -> (default) Currently behaves like `false` (will be dynamic).
91
- # TODO: Dynamic, based on capabilities and bytesize.
92
- def send_literal(str, tag = nil, binary: false, non_sync: nil)
80
+ def send_literal(str, tag = nil)
93
81
  synchronize do
94
- prefix = "~" if binary
95
- plus = "+" if non_sync
96
- put_string("#{prefix}{#{str.bytesize}#{plus}}\r\n")
97
- if non_sync
98
- put_string(str)
99
- return
100
- end
82
+ put_string("{" + str.bytesize.to_s + "}" + CRLF)
101
83
  @continued_command_tag = tag
102
84
  @continuation_request_exception = nil
103
85
  begin
@@ -133,13 +115,11 @@ module Net
133
115
  def send_date_data(date) put_string Net::IMAP.encode_date(date) end
134
116
  def send_time_data(time) put_string Net::IMAP.encode_time(time) end
135
117
 
136
- CommandData = Data.define(:data) do # :nodoc:
137
- def self.validate(...)
138
- data = new(...)
139
- data.validate
140
- data
141
- end
118
+ def send_symbol_data(symbol)
119
+ put_string("\\" + symbol.to_s)
120
+ end
142
121
 
122
+ CommandData = Data.define(:data) do # :nodoc:
143
123
  def send_data(imap, tag)
144
124
  raise NoMethodError, "#{self.class} must implement #{__method__}"
145
125
  end
@@ -148,109 +128,15 @@ module Net
148
128
  end
149
129
  end
150
130
 
151
- # Represents IMAP +text+ data, which may contain any 7-bit ASCII character,
152
- # except for +NULL+, +CR+, or +LF+. +text+ is extended to allow any
153
- # multibyte +UTF-8+ character when either +UTF8=ACCEPT+ or +IMAP4rev2+ have
154
- # been enabled, or when the server supports only +IMAP4rev2+ and not earlier
155
- # IMAP revisions, or when the server advertises +UTF8=ONLY+.
156
- #
157
- # NOTE: The current implementation does not validate whether the connection
158
- # currently supports UTF-8. Future versions may change.
159
- #
160
- # The string's bytes must be valid ASCII or valid UTF-8. The string's
161
- # reported encoding is ignored, but the string is _not_ transcoded.
162
- class RawText < CommandData # :nodoc:
163
- def initialize(data:)
164
- data = String(data.to_str)
165
- data = if data.encoding in Encoding::ASCII | Encoding::UTF_8
166
- -data
167
- elsif data.ascii_only?
168
- -(data.dup.force_encoding("ASCII"))
169
- else
170
- -(data.dup.force_encoding("UTF-8"))
171
- end
172
- super
173
- validate
174
- end
175
-
176
- def validate
177
- if data.include?("\0")
178
- raise DataFormatError, "NULL byte must be binary literal encoded"
179
- elsif !data.valid_encoding?
180
- raise DataFormatError, "invalid UTF-8 must be literal encoded"
181
- elsif /[\r\n]/.match?(data)
182
- raise DataFormatError, "CR and LF bytes must be literal encoded"
183
- end
184
- end
185
-
186
- def ascii_only? = data.ascii_only?
187
-
188
- def send_data(imap, tag) = imap.__send__(:put_string, data)
189
- end
190
-
191
131
  class RawData < CommandData # :nodoc:
192
- def initialize(data:)
193
- data = split_parts(data)
194
- super
195
- validate
196
- end
197
-
198
- def send_data(imap, tag) = data.each do _1.send_data(imap, tag) end
199
-
200
- def validate
201
- return unless data.last in RawText(data: text)
202
- if text.rindex(/~?\{[1-9]\d*\+?\}\z/n)
203
- raise DataFormatError, "RawData cannot end with literal continuation"
204
- end
205
- end
206
-
207
- private
208
-
209
- def split_parts(data)
210
- data = data.b # dups and ensures BINARY encoding
211
- parts = []
212
- while data.match(/(~)?\{(0|[1-9]\d*)(\+)?\}\r\n/n)
213
- text, binary, bytesize, non_sync, data = $`, !!$1, $2, !!$3, $'
214
- bytesize = Integer bytesize, 10
215
- parts << RawText[text] unless text.empty?
216
- parts << extract_literal(data, binary:, bytesize:, non_sync:)
217
- data[0, bytesize] = ""
218
- end
219
- parts << RawText[data] unless data.empty?
220
- parts
221
- end
222
-
223
- def extract_literal(data, binary:, bytesize:, non_sync:)
224
- if data.bytesize < bytesize
225
- raise DataFormatError, "Too few bytes in string for literal, " \
226
- "expected: %s, remaining: %s" % [bytesize, data.bytesize]
227
- end
228
- literal = data.byteslice(0, bytesize)
229
- (binary ? Literal8 : Literal).new(data: literal, non_sync:)
230
- end
231
- end
232
-
233
- class Atom < CommandData # :nodoc:
234
- def initialize(**)
235
- super
236
- validate
237
- end
238
-
239
- def validate
240
- data.to_s.ascii_only? \
241
- or raise DataFormatError, "#{self.class} must be ASCII only"
242
- data.match?(ResponseParser::Patterns::ATOM_SPECIALS) \
243
- and raise DataFormatError, "#{self.class} must not contain atom-specials"
244
- end
245
-
246
132
  def send_data(imap, tag)
247
- imap.__send__(:put_string, data.to_s)
133
+ imap.__send__(:put_string, data)
248
134
  end
249
135
  end
250
136
 
251
- class Flag < Atom # :nodoc:
137
+ class Atom < CommandData # :nodoc:
252
138
  def send_data(imap, tag)
253
- imap.__send__(:put_string, "\\#{data}")
139
+ imap.__send__(:put_string, data)
254
140
  end
255
141
  end
256
142
 
@@ -260,39 +146,9 @@ module Net
260
146
  end
261
147
  end
262
148
 
263
- class Literal < Data.define(:data, :non_sync) # :nodoc:
264
- def self.validate(...)
265
- data = new(...)
266
- data.validate
267
- data
268
- end
269
-
270
- def initialize(data:, non_sync: nil)
271
- data = -String(data.to_str).b or
272
- raise DataFormatError, "#{self.class} expects string input"
273
- super
274
- validate
275
- end
276
-
277
- def bytesize; data.bytesize end
278
-
279
- def validate
280
- if data.include?("\0")
281
- raise DataFormatError, "NULL byte not allowed in #{self.class}. " \
282
- "Use #{Literal8} or a null-safe encoding."
283
- end
284
- end
285
-
286
- def send_data(imap, tag)
287
- imap.__send__(:send_literal, data, tag, non_sync:)
288
- end
289
- end
290
-
291
- class Literal8 < Literal # :nodoc:
292
- def validate; nil end # all bytes are okay
293
-
149
+ class Literal < CommandData # :nodoc:
294
150
  def send_data(imap, tag)
295
- imap.__send__(:send_binary_literal, data, tag, non_sync:)
151
+ imap.__send__(:send_literal, data, tag)
296
152
  end
297
153
  end
298
154
 
@@ -328,73 +184,6 @@ module Net
328
184
  end
329
185
  end
330
186
 
331
- # *DEPRECATED*. Replaced by SequenceSet.
332
- class MessageSet < CommandData # :nodoc:
333
- def send_data(imap, tag)
334
- imap.__send__(:put_string, format_internal(data))
335
- end
336
-
337
- def validate
338
- validate_internal(data)
339
- end
340
-
341
- private
342
-
343
- def initialize(data:)
344
- super
345
- warn("DEPRECATED: #{MessageSet} should be replaced with #{SequenceSet}.",
346
- uplevel: 1, category: :deprecated)
347
- begin
348
- # to ensure the input works with SequenceSet, too
349
- SequenceSet.new(data)
350
- rescue
351
- warn "MessageSet input is incompatible with SequenceSet: [%s] %s" % [
352
- $!.class, $!.message
353
- ]
354
- end
355
- end
356
-
357
- def format_internal(data)
358
- case data
359
- when "*"
360
- return data
361
- when Integer
362
- if data == -1
363
- return "*"
364
- else
365
- return data.to_s
366
- end
367
- when Range
368
- return format_internal(data.first) +
369
- ":" + format_internal(data.last)
370
- when Array
371
- return data.collect {|i| format_internal(i)}.join(",")
372
- when ThreadMember
373
- return data.seqno.to_s +
374
- ":" + data.children.collect {|i| format_internal(i).join(",")}
375
- end
376
- end
377
-
378
- def validate_internal(data)
379
- case data
380
- when "*"
381
- when Integer
382
- NumValidator.ensure_nz_number(data)
383
- when Range
384
- when Array
385
- data.each do |i|
386
- validate_internal(i)
387
- end
388
- when ThreadMember
389
- data.children.each do |i|
390
- validate_internal(i)
391
- end
392
- else
393
- raise DataFormatError, data.inspect
394
- end
395
- end
396
- end
397
-
398
187
  class ClientID < CommandData # :nodoc:
399
188
 
400
189
  def send_data(imap, tag)
@@ -54,9 +54,22 @@ module Net
54
54
  # Creates a new config, which inherits from +self+.
55
55
  def new(**attrs) self.class.new(self, **attrs) end
56
56
 
57
+ # :call-seq:
58
+ # inherited?(attr) -> true or false
59
+ # inherited?(*attrs) -> true or false
60
+ # inherited? -> true or false
61
+ #
57
62
  # Returns +true+ if +attr+ is inherited from #parent and not overridden
58
63
  # by this config.
59
- def inherited?(attr) data[attr] == INHERITED end
64
+ #
65
+ # When multiple +attrs+ are given, returns +true+ if *all* of them are
66
+ # inherited, or +false+ if any of them are overriden. When no +attrs+
67
+ # are given, returns +true+ if *all* attributes are inherited, or
68
+ # +false+ if any attribute is overriden.
69
+ def inherited?(*attrs)
70
+ attrs = data.members if attrs.empty?
71
+ attrs.all? { data[_1] == INHERITED }
72
+ end
60
73
 
61
74
  # :call-seq:
62
75
  # reset -> self
@@ -24,7 +24,7 @@ module Net
24
24
  VERSIONS = ((0.0r..FUTURE_VERSION) % 0.1r).to_a.freeze
25
25
 
26
26
  # See Config.version_defaults.
27
- singleton_class.attr_reader :version_defaults
27
+ singleton_class.attr_accessor :version_defaults
28
28
 
29
29
  @version_defaults = Hash.new {|h, k|
30
30
  # NOTE: String responds to both so the order is significant.
@@ -234,13 +234,26 @@ module Net
234
234
  # Do not use +SASL-IR+, even when it is supported by the server and the
235
235
  # mechanism.
236
236
  #
237
+ # [+:when_capabilities_cached+]
238
+ # Use +SASL-IR+ when Net::IMAP#capabilities_cached? is +true+ and it is
239
+ # supported by the server and the mechanism, but do not send a
240
+ # +CAPABILITY+ command to discover the server capabilities.
241
+ #
242
+ # <em>(+:when_capabilities_cached+ option was added by +v0.6.0+)</em>
243
+ #
237
244
  # [+true+ <em>(default since +v0.4+)</em>]
238
245
  # Use +SASL-IR+ when it is supported by the server and the mechanism.
239
- attr_accessor :sasl_ir, type: :boolean, defaults: {
246
+ attr_accessor :sasl_ir, type: Enum[
247
+ false, :when_capabilities_cached, true
248
+ ], defaults: {
240
249
  0.0r => false,
241
250
  0.4r => true,
242
251
  }
243
252
 
253
+ # :stopdoc:
254
+ alias sasl_ir? sasl_ir
255
+ # :startdoc:
256
+
244
257
  # Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
245
258
  # capability is present. When enforced, Net::IMAP will raise a
246
259
  # LoginDisabledError when that capability is present.
@@ -347,39 +360,32 @@ module Net
347
360
  #
348
361
  # Alias for responses_without_block
349
362
 
350
- # Whether ResponseParser should use the deprecated UIDPlusData or
351
- # CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
352
- # AppendUIDData for +APPENDUID+ response codes.
363
+ # **NOTE:** <em>+UIDPlusData+ has been removed since +v0.6.0+, and this
364
+ # config option only affects deprecation warnings.
365
+ # This config option will be **removed** in +v0.7.0+.</em>
353
366
  #
354
- # UIDPlusData stores its data in arrays of numbers, which is vulnerable to
355
- # a memory exhaustion denial of service attack from an untrusted or
356
- # compromised server. Set this option to +false+ to completely block this
357
- # vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
358
- # mitigates this vulnerability.
367
+ # ResponseParser always returns CopyUIDData for +COPYUID+ response codes,
368
+ # and AppendUIDData for +APPENDUID+ response codes. Previously, this
369
+ # option determined when UIDPlusData would be returned instead.
359
370
  #
360
- # AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
361
- # UIDPlusData. Most applications should be able to upgrade with little
362
- # or no changes.
371
+ # Parser support for +UIDPLUS+ added in +v0.3.2+.
363
372
  #
364
- # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
373
+ # Config option added in +v0.4.19+ and +v0.5.6+.
365
374
  #
366
- # <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
375
+ # <em>UIDPlusData removed in +v0.6.0+.</em>
367
376
  #
368
- # <em>UIDPlusData will be removed in +v0.6+ and this config setting will
369
- # be ignored.</em>
370
- #
371
- # ==== Valid options
377
+ # ==== Options
372
378
  #
373
379
  # [+true+ <em>(original default)</em>]
374
- # ResponseParser only uses UIDPlusData.
380
+ # <em>Since v0.6.0:</em>
381
+ # Prints a deprecation warning when parsing +COPYUID+ or +APPENDUID+.
375
382
  #
376
383
  # [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
377
- # ResponseParser uses UIDPlusData when the +uid-set+ size is below
378
- # parser_max_deprecated_uidplus_data_size. Above that size,
379
- # ResponseParser uses AppendUIDData or CopyUIDData.
384
+ # <em>Since v0.6.0:</em>
385
+ # Prints a deprecation warning when parsing +COPYUID+ or +APPENDUID+.
380
386
  #
381
- # [+false+ <em>(planned default for +v0.6+)</em>]
382
- # ResponseParser _only_ uses AppendUIDData and CopyUIDData.
387
+ # [+false+ <em>(default since +v0.6.0+)</em>]
388
+ # This is the only supported option <em>(since v0.6.0)</em>.
383
389
  attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
384
390
  true, :up_to_max_size, false
385
391
  ], defaults: {
@@ -388,22 +394,22 @@ module Net
388
394
  0.6r => false,
389
395
  }
390
396
 
391
- # The maximum +uid-set+ size that ResponseParser will parse into
392
- # deprecated UIDPlusData. This limit only applies when
393
- # parser_use_deprecated_uidplus_data is not +false+.
397
+ # **NOTE:** <em>+UIDPlusData+ has been removed since +v0.6.0+, and this
398
+ # config option is ignored.
399
+ # This config option will be **removed** in +v0.7.0+.</em>
394
400
  #
395
- # <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
401
+ # ResponseParser always returns CopyUIDData for +COPYUID+ response codes,
402
+ # and AppendUIDData for +APPENDUID+ response codes. Previously, this
403
+ # option determined when UIDPlusData would be returned instead.
396
404
  #
397
- # <em>Support for limiting UIDPlusData to a maximum size was added in
398
- # +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
405
+ # Parser support for +UIDPLUS+ added in +v0.3.2+.
399
406
  #
400
- # <em>UIDPlusData will be removed in +v0.6+.</em>
407
+ # Support for limiting UIDPlusData to a maximum size was added in
408
+ # +v0.3.8+, +v0.4.19+, and +v0.5.6+.
401
409
  #
402
- # ==== Versioned Defaults
410
+ # <em>UIDPlusData was removed in +v0.6.0+.</em>
403
411
  #
404
- # Because this limit guards against a remote server causing catastrophic
405
- # memory exhaustion, the versioned default (used by #load_defaults) also
406
- # applies to versions without the feature.
412
+ # ==== Versioned Defaults
407
413
  #
408
414
  # * +0.3+ and prior: <tt>10,000</tt>
409
415
  # * +0.4+: <tt>1,000</tt>
@@ -485,8 +491,142 @@ module Net
485
491
  # Returns all config attributes in a hash.
486
492
  def to_h; data.members.to_h { [_1, send(_1)] } end
487
493
 
494
+ # Returns a string representation of overriden config attributes and the
495
+ # inheritance chain.
496
+ #
497
+ # Attributes overridden by ancestors are also inspected, recursively.
498
+ # Attributes that are inherited from default configs are not shown (see
499
+ # Config@Versioned+defaults and Config@Named+defaults).
500
+ #
501
+ # # (Line breaks have been added to the example output for legibility.)
502
+ #
503
+ # Net::IMAP::Config.new(0.4)
504
+ # .new(open_timeout: 10, enforce_logindisabled: true)
505
+ # .inspect
506
+ # #=> "#<Net::IMAP::Config:0x0000745871125410 open_timeout=10 enforce_logindisabled=true
507
+ # # inherits from Net::IMAP::Config[0.4]
508
+ # # inherits from Net::IMAP::Config.global
509
+ # # inherits from Net::IMAP::Config.default>"
510
+ #
511
+ # Non-default attributes are listed after the ancestor config from which
512
+ # they are inherited.
513
+ #
514
+ # # (Line breaks have been added to the example output for legibility.)
515
+ #
516
+ # config = Net::IMAP::Config.global
517
+ # .new(open_timeout: 10, idle_response_timeout: 2)
518
+ # .new(enforce_logindisabled: :when_capabilities_cached, sasl_ir: false)
519
+ # config.inspect
520
+ # #=> "#<Net::IMAP::Config:0x00007ce2a1e20e40 sasl_ir=false enforce_logindisabled=:when_capabilities_cached
521
+ # # inherits from Net::IMAP::Config:0x00007ce2a1e20f80 open_timeout=10 idle_response_timeout=2
522
+ # # inherits from Net::IMAP::Config.global
523
+ # # inherits from Net::IMAP::Config.default>"
524
+ #
525
+ # Net::IMAP.debug = true
526
+ # config.inspect
527
+ # #=> "#<Net::IMAP::Config:0x00007ce2a1e20e40 sasl_ir=false enforce_logindisabled=:when_capabilities_cached
528
+ # # inherits from Net::IMAP::Config:0x00007ce2a1e20f80 open_timeout=10 idle_response_timeout=2
529
+ # # inherits from Net::IMAP::Config.global debug=true
530
+ # # inherits from Net::IMAP::Config.default>"
531
+ #
532
+ # Use +pp+ (see #pretty_print) to inspect _all_ config attributes,
533
+ # including default values.
534
+ #
535
+ # Use #to_h to inspect all config attributes ignoring inheritance.
536
+ def inspect;
537
+ "#<#{inspect_recursive}>"
538
+ end
539
+ alias to_s inspect
540
+
541
+ # Used by PP[https://docs.ruby-lang.org/en/master/PP.html] to create a
542
+ # string representation of all config attributes and the inheritance
543
+ # chain. Inherited attributes are listed with the ancestor config from
544
+ # which they are inherited.
545
+ #
546
+ # pp Config.new[0.4].new(open_timeout: 10, idle_response_timeout: 10)
547
+ # # #<Net::IMAP::Config:0x0000745871125410
548
+ # # open_timeout=10
549
+ # # idle_response_timeout=10
550
+ # # inherits from Net::IMAP::Config[0.4]
551
+ # # responses_without_block=:silence_deprecation_warning
552
+ # # max_response_size=nil
553
+ # # sasl_ir=true
554
+ # # enforce_logindisabled=false
555
+ # # parser_use_deprecated_uidplus_data=true
556
+ # # parser_max_deprecated_uidplus_data_size=1000
557
+ # # inherits from Net::IMAP::Config.global
558
+ # # inherits from Net::IMAP::Config.default
559
+ # # debug=false>
560
+ #
561
+ # Related: #inspect, #to_h.
562
+ def pretty_print(pp)
563
+ pp.group(2, "#<", ">") do
564
+ pretty_print_recursive(pp)
565
+ end
566
+ end
567
+
568
+ # :stopdoc:
569
+
488
570
  protected
489
571
 
572
+ def named_default?
573
+ equal?(Config.default) ||
574
+ AttrVersionDefaults::VERSIONS.any? { equal? Config[_1] }
575
+ end
576
+
577
+ def name
578
+ if equal? Config.default then "#{Config}.default"
579
+ elsif equal? Config.global then "#{Config}.global"
580
+ elsif equal? Config[0.0r] then "#{Config}[:original]"
581
+ elsif equal? Config[:default] then "#{Config}[:default]"
582
+ elsif (v = AttrVersionDefaults::VERSIONS.find { equal? Config[_1] })
583
+ "%s[%0.1f]" % [Config, v]
584
+ else
585
+ Kernel.instance_method(:to_s).bind_call(self).delete("<#>")
586
+ end
587
+ end
588
+
589
+ def inspect_recursive(attrs = AttrAccessors.struct.members)
590
+ strings = [name]
591
+ assigned = assigned_attrs_hash(attrs)
592
+ strings.concat assigned.map { "%s=%p" % _1 }
593
+ if parent
594
+ if parent.equal?(Config.default)
595
+ inherited_overrides = []
596
+ elsif parent
597
+ inherited_overrides = attrs - assigned.keys
598
+ inherited_overrides &= DEFAULT_TO_INHERIT if parent.named_default?
599
+ end
600
+ strings << "inherits from #{parent.inspect_recursive(inherited_overrides)}"
601
+ end
602
+ strings.join " "
603
+ end
604
+
605
+ def pretty_print_recursive(pp, attrs = AttrAccessors.struct.members)
606
+ pp.text name
607
+ assigned = assigned_attrs_hash(attrs)
608
+ pp.breakable
609
+ pp.seplist(assigned, ->{pp.breakable}) do |key, val|
610
+ pp.text key.to_s
611
+ pp.text "="
612
+ pp.pp val
613
+ end
614
+ if parent
615
+ pp.breakable if assigned.any?
616
+ pp.nest(2) do
617
+ pp.text "inherits from "
618
+ parent.pretty_print_recursive(pp, attrs - assigned.keys)
619
+ end
620
+ elsif assigned.empty?
621
+ pp.text "(overridden)"
622
+ end
623
+ end
624
+
625
+ def assigned_attrs_hash(attrs)
626
+ own_attrs = attrs.reject { inherited?(_1) }
627
+ own_attrs.to_h { [_1, data[_1]] }
628
+ end
629
+
490
630
  def defaults_hash
491
631
  to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
492
632
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Net
4
4
  class IMAP
5
- class ConnectionState < Net::IMAP::Data # :nodoc:
5
+ class ConnectionState < Data # :nodoc:
6
6
  def self.define(symbol, *attrs)
7
7
  symbol => Symbol
8
8
  state = super(*attrs)