net-imap 0.4.2 → 0.4.5

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.

Potentially problematic release.


This version of net-imap might be problematic. Click here for more details.

@@ -58,8 +58,8 @@ module Net::IMAP::SASL
58
58
  # {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
59
59
  # implemented by +authenticator_class+ (for instance, <tt>"PLAIN"</tt>).
60
60
  #
61
- # If +mechanism+ refers to an existing authenticator, a warning will be
62
- # printed and the old authenticator will be replaced.
61
+ # If +mechanism+ refers to an existing authenticator,
62
+ # the old authenticator will be replaced.
63
63
  #
64
64
  # When only a single argument is given, the authenticator class will be
65
65
  # lazily loaded from <tt>Net::IMAP::SASL::#{name}Authenticator</tt> (case is
@@ -14,7 +14,7 @@
14
14
  # Although this mechanism was never standardized and has been obsoleted by
15
15
  # "+OAUTHBEARER+", it is still very widely supported.
16
16
  #
17
- # See Net::IMAP::SASL:: OAuthBearerAuthenticator.
17
+ # See Net::IMAP::SASL::OAuthBearerAuthenticator.
18
18
  class Net::IMAP::SASL::XOAuth2Authenticator
19
19
 
20
20
  # It is unclear from {Google's original XOAUTH2
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+
6
+ ##
7
+ # An IMAP {sequence
8
+ # set}[https://www.rfc-editor.org/rfc/rfc9051.html#section-4.1.1],
9
+ # is a set of message sequence numbers or unique identifier numbers
10
+ # ("UIDs"). It contains numbers and ranges of numbers. The numbers are all
11
+ # non-zero unsigned 32-bit integers and one special value, <tt>*</tt>, that
12
+ # represents the largest value in the mailbox.
13
+ #
14
+ # *NOTE:* This SequenceSet class is currently a placeholder for unhandled
15
+ # extension data. All it does now is validate. It will be expanded to a
16
+ # full API in a future release.
17
+ class SequenceSet
18
+
19
+ def self.[](str) new(str).freeze end
20
+
21
+ def initialize(input)
22
+ @atom = -String.try_convert(input)
23
+ validate
24
+ end
25
+
26
+ # Returns the IMAP string representation. In the IMAP grammar,
27
+ # +sequence-set+ is a subset of +atom+ which is a subset of +astring+.
28
+ attr_accessor :atom
29
+
30
+ # Returns #atom. In the IMAP grammar, +atom+ is a subset of +astring+.
31
+ alias astring atom
32
+
33
+ # Returns the value of #atom
34
+ alias to_s atom
35
+
36
+ # Hash equality requires the same encoded #atom representation.
37
+ #
38
+ # Net::IMAP::SequenceSet["1:3"] .eql? Net::IMAP::SequenceSet["1:3"] # => true
39
+ # Net::IMAP::SequenceSet["1,2,3"].eql? Net::IMAP::SequenceSet["1:3"] # => false
40
+ # Net::IMAP::SequenceSet["1,3"] .eql? Net::IMAP::SequenceSet["3,1"] # => false
41
+ # Net::IMAP::SequenceSet["9,1:*"].eql? Net::IMAP::SequenceSet["1:*"] # => false
42
+ #
43
+ def eql?(other) self.class == other.class && atom == other.atom end
44
+ alias == eql?
45
+
46
+ # See #eql?
47
+ def hash; [self.class. atom].hash end
48
+
49
+ def inspect
50
+ (frozen? ? "%s[%p]" : "#<%s %p>") % [self.class, to_s]
51
+ end
52
+
53
+ # Unstable API, for internal use only (Net::IMAP#validate_data)
54
+ def validate # :nodoc:
55
+ ResponseParser::Patterns::SEQUENCE_SET_STR.match?(@atom) or
56
+ raise ArgumentError, "invalid sequence-set: %p" % [input]
57
+ true
58
+ end
59
+
60
+ # Unstable API, for internal use only (Net::IMAP#send_data)
61
+ def send_data(imap, tag) # :nodoc:
62
+ imap.__send__(:put_string, atom)
63
+ end
64
+
65
+ end
66
+ end
67
+ end
data/lib/net/imap.rb CHANGED
@@ -404,18 +404,18 @@ module Net
404
404
  #
405
405
  # Although IMAP4rev2[https://tools.ietf.org/html/rfc9051] is not supported
406
406
  # yet, Net::IMAP supports several extensions that have been folded into it:
407
- # +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, and +UNSELECT+.
407
+ # +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, +UNSELECT+, and
408
+ # the fetch side of +BINARY+.
408
409
  # Commands for these extensions are listed with the {Core IMAP
409
410
  # commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
410
411
  #
411
412
  # >>>
412
413
  # <em>The following are folded into +IMAP4rev2+ but are currently
413
414
  # unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
414
- # extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+,
415
- # +LIST-STATUS+, +LITERAL-+, +BINARY+ fetch, and +SPECIAL-USE+. The
416
- # following extensions are implicitly supported, but will be updated with
417
- # more direct support: RFC5530 response codes, <tt>STATUS=SIZE</tt>, and
418
- # <tt>STATUS=DELETED</tt>.</em>
415
+ # extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
416
+ # +LITERAL-+, and +SPECIAL-USE+. The following extensions are implicitly
417
+ # supported, but will be updated with more direct support: RFC5530 response
418
+ # codes, <tt>STATUS=SIZE</tt>, and <tt>STATUS=DELETED</tt>.</em>
419
419
  #
420
420
  # ==== RFC2087: +QUOTA+
421
421
  # - #getquota: returns the resource usage and limits for a quota root
@@ -437,6 +437,15 @@ module Net
437
437
  # ==== RFC2971: +ID+
438
438
  # - #id: exchanges client and server implementation information.
439
439
  #
440
+ # ==== RFC3516: +BINARY+
441
+ # The fetch side of +BINARY+ has been folded into
442
+ # IMAP4rev2[https://tools.ietf.org/html/rfc9051].
443
+ # - Updates #fetch and #uid_fetch with the +BINARY+, +BINARY.PEEK+, and
444
+ # +BINARY.SIZE+ items. See FetchData#binary and FetchData#binary_size.
445
+ #
446
+ # >>>
447
+ # *NOTE:* The binary extension the #append command is _not_ supported yet.
448
+ #
440
449
  # ==== RFC3691: +UNSELECT+
441
450
  # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
442
451
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
@@ -474,9 +483,17 @@ module Net
474
483
  # which arranges the results into ordered groups or threads according to a
475
484
  # chosen algorithm.
476
485
  #
477
- # ==== +XLIST+ (non-standard, deprecated)
486
+ # ==== +X-GM-EXT-1+
487
+ # +X-GM-EXT-1+ is a non-standard Gmail extension. See {Google's
488
+ # documentation}[https://developers.google.com/gmail/imap/imap-extensions].
489
+ # - Updates #fetch and #uid_fetch with support for +X-GM-MSGID+ (unique
490
+ # message ID), +X-GM-THRID+ (thread ID), and +X-GM-LABELS+ (Gmail labels).
491
+ # - Updates #search with the +X-GM-RAW+ search attribute.
478
492
  # - #xlist: replaced by +SPECIAL-USE+ attributes in #list responses.
479
493
  #
494
+ # *NOTE:* The +OBJECTID+ extension should replace +X-GM-MSGID+ and
495
+ # +X-GM-THRID+, but Gmail does not support it (as of 2023-11-10).
496
+ #
480
497
  # ==== RFC6851: +MOVE+
481
498
  # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
482
499
  # above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
@@ -487,6 +504,13 @@ module Net
487
504
  #
488
505
  # - See #enable for information about support for UTF-8 string encoding.
489
506
  #
507
+ # ==== RFC8474: +OBJECTID+
508
+ # - Adds +MAILBOXID+ ResponseCode to #create tagged response.
509
+ # - Adds +MAILBOXID+ ResponseCode to #select and #examine untagged response.
510
+ # - Updates #fetch and #uid_fetch with the +EMAILID+ and +THREADID+ items.
511
+ # See FetchData#emailid and FetchData#emailid.
512
+ # - Updates #status with support for the +MAILBOXID+ status attribute.
513
+ #
490
514
  # == References
491
515
  #
492
516
  # [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
@@ -612,6 +636,10 @@ module Net
612
636
  # [ID[https://tools.ietf.org/html/rfc2971]]::
613
637
  # Showalter, T., "IMAP4 ID extension", RFC 2971, DOI 10.17487/RFC2971,
614
638
  # October 2000, <https://www.rfc-editor.org/info/rfc2971>.
639
+ # [BINARY[https://tools.ietf.org/html/rfc3516]]::
640
+ # Nerenberg, L., "IMAP4 Binary Content Extension", RFC 3516,
641
+ # DOI 10.17487/RFC3516, April 2003,
642
+ # <https://www.rfc-editor.org/info/rfc3516>.
615
643
  # [ACL[https://tools.ietf.org/html/rfc4314]]::
616
644
  # Melnikov, A., "IMAP4 Access Control List (ACL) Extension", RFC 4314,
617
645
  # DOI 10.17487/RFC4314, December 2005,
@@ -662,7 +690,7 @@ module Net
662
690
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
663
691
  #
664
692
  class IMAP < Protocol
665
- VERSION = "0.4.2"
693
+ VERSION = "0.4.5"
666
694
 
667
695
  # Aliases for supported capabilities, to be used with the #enable command.
668
696
  ENABLE_ALIASES = {
@@ -1045,7 +1073,7 @@ module Net
1045
1073
  # ===== Capabilities
1046
1074
  #
1047
1075
  # The server's capabilities must include +ID+
1048
- # [RFC2971[https://tools.ietf.org/html/rfc2971]]
1076
+ # [RFC2971[https://tools.ietf.org/html/rfc2971]].
1049
1077
  def id(client_id=nil)
1050
1078
  synchronize do
1051
1079
  send_command("ID", ClientID.new(client_id))
@@ -1058,7 +1086,7 @@ module Net
1058
1086
  #
1059
1087
  # This allows the server to send unsolicited untagged EXPUNGE #responses,
1060
1088
  # but does not execute any client request. \IMAP servers are permitted to
1061
- # send unsolicited untagged responses at any time, except for `EXPUNGE`.
1089
+ # send unsolicited untagged responses at any time, except for +EXPUNGE+:
1062
1090
  #
1063
1091
  # * +EXPUNGE+ can only be sent while a command is in progress.
1064
1092
  # * +EXPUNGE+ must _not_ be sent during #fetch, #store, or #search.
@@ -1143,10 +1171,7 @@ module Net
1143
1171
  end
1144
1172
 
1145
1173
  # :call-seq:
1146
- # authenticate(mechanism, *,
1147
- # sasl_ir: true,
1148
- # registry: Net::IMAP::SASL.authenticators,
1149
- # **, &) -> ok_resp
1174
+ # authenticate(mechanism, *, sasl_ir: true, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
1150
1175
  #
1151
1176
  # Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
1152
1177
  # to authenticate the client. If successful, the connection enters the
@@ -1330,7 +1355,7 @@ module Net
1330
1355
  # the server may return an untagged "NO" response with a "UIDNOTSTICKY"
1331
1356
  # response code indicating that the mailstore does not support persistent
1332
1357
  # UIDs:
1333
- # @responses["NO"].last.code.name == "UIDNOTSTICKY"
1358
+ # imap.responses("NO", &:last)&.code&.name == "UIDNOTSTICKY"
1334
1359
  def select(mailbox)
1335
1360
  synchronize do
1336
1361
  @responses.clear
@@ -1420,10 +1445,10 @@ module Net
1420
1445
  # to the client. +refname+ provides a context (for instance, a base
1421
1446
  # directory in a directory-based mailbox hierarchy). +mailbox+ specifies a
1422
1447
  # mailbox or (via wildcards) mailboxes under that context. Two wildcards
1423
- # may be used in +mailbox+: '*', which matches all characters *including*
1424
- # the hierarchy delimiter (for instance, '/' on a UNIX-hosted
1425
- # directory-based mailbox hierarchy); and '%', which matches all characters
1426
- # *except* the hierarchy delimiter.
1448
+ # may be used in +mailbox+: <tt>"*"</tt>, which matches all characters
1449
+ # *including* the hierarchy delimiter (for instance, "/" on a UNIX-hosted
1450
+ # directory-based mailbox hierarchy); and <tt>"%"</tt>, which matches all
1451
+ # characters *except* the hierarchy delimiter.
1427
1452
  #
1428
1453
  # If +refname+ is empty, +mailbox+ is used directly to determine
1429
1454
  # which mailboxes to match. If +mailbox+ is empty, the root
@@ -1471,16 +1496,16 @@ module Net
1471
1496
  # servers, then folder creation (and listing, moving, etc) can lead to
1472
1497
  # errors.
1473
1498
  #
1474
- # From RFC2342:
1475
- #
1476
- # Although typically a server will support only a single Personal
1499
+ # From RFC2342[https://tools.ietf.org/html/rfc2342]:
1500
+ # >>>
1501
+ # <em>Although typically a server will support only a single Personal
1477
1502
  # Namespace, and a single Other User's Namespace, circumstances exist
1478
1503
  # where there MAY be multiples of these, and a client MUST be prepared
1479
1504
  # for them. If a client is configured such that it is required to create
1480
1505
  # a certain mailbox, there can be circumstances where it is unclear which
1481
1506
  # Personal Namespaces it should create the mailbox in. In these
1482
1507
  # situations a client SHOULD let the user select which namespaces to
1483
- # create the mailbox in.
1508
+ # create the mailbox in.</em>
1484
1509
  #
1485
1510
  # Related: #list, Namespaces, Namespace
1486
1511
  #
@@ -1665,21 +1690,52 @@ module Net
1665
1690
 
1666
1691
  # Sends a {STATUS commands [IMAP4rev1 §6.3.10]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.10]
1667
1692
  # and returns the status of the indicated +mailbox+. +attr+ is a list of one
1668
- # or more attributes whose statuses are to be requested. Supported
1669
- # attributes include:
1693
+ # or more attributes whose statuses are to be requested.
1694
+ #
1695
+ # The return value is a hash of attributes.
1696
+ #
1697
+ # A Net::IMAP::NoResponseError is raised if status values
1698
+ # for +mailbox+ cannot be returned; for instance, because it
1699
+ # does not exist.
1700
+ #
1701
+ # ===== Supported attributes:
1702
+ #
1703
+ # +MESSAGES+:: The number of messages in the mailbox.
1670
1704
  #
1671
- # MESSAGES:: the number of messages in the mailbox.
1672
- # RECENT:: the number of recent messages in the mailbox.
1673
- # UNSEEN:: the number of unseen messages in the mailbox.
1705
+ # +UIDNEXT+:: The next unique identifier value of the mailbox.
1674
1706
  #
1675
- # The return value is a hash of attributes. For example:
1707
+ # +UIDVALIDITY+:: The unique identifier validity value of the mailbox.
1708
+ #
1709
+ # +UNSEEN+:: The number of messages without the <tt>\Seen</tt> flag.
1710
+ #
1711
+ # +DELETED+:: The number of messages with the <tt>\Deleted</tt> flag.
1712
+ #
1713
+ # +SIZE+::
1714
+ # The approximate size of the mailbox---must be greater than or equal to
1715
+ # the sum of all messages' +RFC822.SIZE+ fetch item values.
1716
+ #
1717
+ # +MAILBOXID+::
1718
+ # A server-allocated unique identifier for the mailbox.
1719
+ # See +OBJECTID+
1720
+ # {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-4].
1721
+ #
1722
+ # +RECENT+::
1723
+ # The number of messages with the <tt>\Recent</tt> flag.
1724
+ # _NOTE:_ +RECENT+ was removed from IMAP4rev2.
1725
+ #
1726
+ # ===== For example:
1676
1727
  #
1677
1728
  # p imap.status("inbox", ["MESSAGES", "RECENT"])
1678
1729
  # #=> {"RECENT"=>0, "MESSAGES"=>44}
1679
1730
  #
1680
- # A Net::IMAP::NoResponseError is raised if status values
1681
- # for +mailbox+ cannot be returned; for instance, because it
1682
- # does not exist.
1731
+ # ===== Capabilities
1732
+ #
1733
+ # +SIZE+ requires the server's capabilities to include either +IMAP4rev2+ or
1734
+ # <tt>STATUS=SIZE</tt>
1735
+ # {[RFC8483]}[https://www.rfc-editor.org/rfc/rfc8483.html].
1736
+ #
1737
+ # +MAILBOXID+ requires the server's capabilities to include +OBJECTID+
1738
+ # {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html].
1683
1739
  def status(mailbox, attr)
1684
1740
  synchronize do
1685
1741
  send_command("STATUS", mailbox, attr)
@@ -1935,11 +1991,11 @@ module Net
1935
1991
  # to alter data associated with messages in the mailbox, in particular their
1936
1992
  # flags. The +set+ parameter is a number, an array of numbers, or a Range
1937
1993
  # object. Each number is a message sequence number. +attr+ is the name of a
1938
- # data item to store: 'FLAGS' will replace the message's flag list with the
1939
- # provided one, '+FLAGS' will add the provided flags, and '-FLAGS' will
1940
- # remove them. +flags+ is a list of flags.
1994
+ # data item to store: <tt>"FLAGS"</tt> will replace the message's flag list
1995
+ # with the provided one, <tt>"+FLAGS"</tt> will add the provided flags, and
1996
+ # <tt>"-FLAGS"</tt> will remove them. +flags+ is a list of flags.
1941
1997
  #
1942
- # The return value is an array of FetchData
1998
+ # The return value is an array of FetchData.
1943
1999
  #
1944
2000
  # Related: #uid_store
1945
2001
  #
@@ -2191,7 +2247,7 @@ module Net
2191
2247
  .join(' ')
2192
2248
  synchronize do
2193
2249
  send_command("ENABLE #{capabilities}")
2194
- result = clear_responses("ENABLED").last
2250
+ result = clear_responses("ENABLED").last || []
2195
2251
  @utf8_strings ||= result.include? "UTF8=ACCEPT"
2196
2252
  @utf8_strings ||= result.include? "IMAP4REV2"
2197
2253
  result
@@ -2522,7 +2578,8 @@ module Net
2522
2578
  when /\A(?:BAD)\z/ni
2523
2579
  raise BadResponseError, resp
2524
2580
  else
2525
- raise UnknownResponseError, resp
2581
+ disconnect
2582
+ raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw.chomp]
2526
2583
  end
2527
2584
  end
2528
2585
 
@@ -2644,7 +2701,7 @@ module Net
2644
2701
  else
2645
2702
  send_command(cmd, *keys)
2646
2703
  end
2647
- clear_responses("SEARCH").last
2704
+ clear_responses("SEARCH").last || []
2648
2705
  end
2649
2706
  end
2650
2707
 
@@ -2693,7 +2750,7 @@ module Net
2693
2750
  normalize_searching_criteria(search_keys)
2694
2751
  synchronize do
2695
2752
  send_command(cmd, sort_keys, charset, *search_keys)
2696
- clear_responses("SORT").last
2753
+ clear_responses("SORT").last || []
2697
2754
  end
2698
2755
  end
2699
2756
 
@@ -2706,7 +2763,7 @@ module Net
2706
2763
  normalize_searching_criteria(search_keys)
2707
2764
  synchronize do
2708
2765
  send_command(cmd, algorithm, charset, *search_keys)
2709
- clear_responses("THREAD").last
2766
+ clear_responses("THREAD").last || []
2710
2767
  end
2711
2768
  end
2712
2769
 
data/net-imap.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  name = File.basename(__FILE__, ".gemspec")
4
4
  version = ["lib", Array.new(name.count("-"), "..").join("/")].find do |dir|
5
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
5
+ break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb"), :encoding=> 'utf-8') do |line|
6
6
  /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
7
7
  end rescue nil
8
8
  end
@@ -25,7 +25,8 @@ Gem::Specification.new do |spec|
25
25
  # Specify which files should be added to the gem when it is released.
26
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
27
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
- `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(bin|test|spec|features|rfcs)/}) }
28
+ `git ls-files -z 2>/dev/null`.split("\x0")
29
+ .reject {|f| f.match(%r{^(bin|test|spec|benchmarks|features|rfcs)/}) }
29
30
  end
30
31
  spec.bindir = "exe"
31
32
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ PARSER_TEST_FIXTURES = FileList.new "test/net/imap/fixtures/response_parser/*.yml"
4
+ CLOBBER.include "benchmarks/parser.yml"
5
+ CLEAN.include "benchmarks/Gemfile-*"
6
+
7
+ BENCHMARK_INIT = <<RUBY
8
+ require "yaml"
9
+ require "net/imap"
10
+
11
+ def load_response(file, name)
12
+ YAML.unsafe_load_file(file).dig(:tests, name, :response)
13
+ .force_encoding "ASCII-8BIT" \\
14
+ or abort "ERRORO: missing %p fixture data in %p" % [name, file]
15
+ end
16
+
17
+ parser = Net::IMAP::ResponseParser.new
18
+ RUBY
19
+
20
+ file "benchmarks/parser.yml" => PARSER_TEST_FIXTURES do |t|
21
+ require "yaml"
22
+ require "pathname"
23
+ require "net/imap"
24
+
25
+ path = Pathname.new(__dir__) / "../test/net/imap/fixtures/response_parser"
26
+ files = path.glob("*.yml")
27
+ tests = files.flat_map {|file|
28
+ file.read
29
+ .gsub(%r{([-:]) !ruby/(object|struct):\S+}) { $1 }
30
+ .then {
31
+ YAML.safe_load(_1, filename: file,
32
+ permitted_classes: [Symbol, Regexp], aliases: true)
33
+ }
34
+ .fetch(:tests)
35
+ .select {|test_name, test|
36
+ :parser_assert_equal == test.fetch(:test_type) {
37
+ test.key?(:expected) ? :parser_assert_equal : :parser_pending
38
+ }
39
+ }
40
+ .map {|test_name, test| [test_name.to_s, test.fetch(:response)] }
41
+ }
42
+
43
+ benchmarks = tests.map {|fixture_name, response|
44
+ {"name" => fixture_name.delete_prefix("test_"),
45
+ "prelude" => "response = %s" % [response.dump],
46
+ "script" => "parser.parse(response)"}
47
+ }
48
+ .sort_by { _1["name"] }
49
+
50
+ YAML.dump({"prelude" => BENCHMARK_INIT, "benchmark" => benchmarks})
51
+ .then { File.write t.name, _1 }
52
+ end
53
+
54
+ namespace :benchmarks do
55
+ desc "Generate benchmarks from fixture data"
56
+ task :generate => "benchmarks/parser.yml"
57
+
58
+ desc "run the parser benchmarks comparing multiple gem versions"
59
+ task :compare => :generate do |task, args|
60
+ cd Pathname.new(__dir__) + ".."
61
+ current = `git describe --tags --dirty`.chomp
62
+ current = "dev" if current.empty?
63
+ versions = args.to_a
64
+ if versions.empty?
65
+ latest = %x{git describe --tags --abbrev=0 --match 'v*.*.*'}.chomp
66
+ versions = latest.empty? ? [] : [latest.delete_prefix("v")]
67
+ end
68
+ versions = versions.to_h { [_1, "Gemfile-v#{_1}"] }
69
+ cd "benchmarks" do
70
+ versions.each do |version, gemfile|
71
+ File.write gemfile, <<~RUBY
72
+ # frozen_string_literal: true
73
+ source "https://rubygems.org"
74
+ gem "net-imap", #{version.dump}
75
+ RUBY
76
+ end
77
+ versions = {current => "../Gemfile" , **versions}.map {
78
+ "%s::/usr/bin/env BUNDLE_GEMFILE=%s ruby" % _1
79
+ }.join(";")
80
+
81
+ extra = ENV.fetch("BENCHMARK_ARGS", "").shellsplit
82
+
83
+ sh("benchmark-driver",
84
+ "--bundler",
85
+ "-e", versions,
86
+ "parser.yml",
87
+ *extra)
88
+ end
89
+ end
90
+
91
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-10-20 00:00:00.000000000 Z
12
+ date: 2023-11-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-protocol
@@ -83,10 +83,6 @@ files:
83
83
  - LICENSE.txt
84
84
  - README.md
85
85
  - Rakefile
86
- - benchmarks/generate_parser_benchmarks
87
- - benchmarks/parser.yml
88
- - benchmarks/stringprep.yml
89
- - benchmarks/table-regexps.yml
90
86
  - docs/styles.css
91
87
  - lib/net/imap.rb
92
88
  - lib/net/imap/authenticators.rb
@@ -94,6 +90,7 @@ files:
94
90
  - lib/net/imap/data_encoding.rb
95
91
  - lib/net/imap/deprecated_client_options.rb
96
92
  - lib/net/imap/errors.rb
93
+ - lib/net/imap/fetch_data.rb
97
94
  - lib/net/imap/flags.rb
98
95
  - lib/net/imap/response_data.rb
99
96
  - lib/net/imap/response_parser.rb
@@ -116,6 +113,7 @@ files:
116
113
  - lib/net/imap/sasl/stringprep.rb
117
114
  - lib/net/imap/sasl/xoauth2_authenticator.rb
118
115
  - lib/net/imap/sasl_adapter.rb
116
+ - lib/net/imap/sequence_set.rb
119
117
  - lib/net/imap/stringprep.rb
120
118
  - lib/net/imap/stringprep/nameprep.rb
121
119
  - lib/net/imap/stringprep/saslprep.rb
@@ -123,6 +121,7 @@ files:
123
121
  - lib/net/imap/stringprep/tables.rb
124
122
  - lib/net/imap/stringprep/trace.rb
125
123
  - net-imap.gemspec
124
+ - rakelib/benchmarks.rake
126
125
  - rakelib/rdoc.rake
127
126
  - rakelib/rfcs.rake
128
127
  - rakelib/saslprep.rake
@@ -1,52 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "yaml"
4
- require "pathname"
5
- require "net/imap"
6
-
7
- path = Pathname.new(__dir__) / "../test/net/imap/fixtures/response_parser"
8
- files = path.glob("*.yml")
9
- tests = files.flat_map {|file|
10
- file.to_s
11
- .then { YAML.unsafe_load_file _1 }
12
- .fetch(:tests)
13
- .select {|test_name, test|
14
- :parser_assert_equal == test.fetch(:test_type) {
15
- test.key?(:expected) ? :parser_assert_equal : :parser_pending
16
- }
17
- }
18
- .map {|test_name, _|
19
- [
20
- file.relative_path_from(__dir__).to_s,
21
- test_name.to_s,
22
- ]
23
- }
24
- }
25
-
26
- init = <<RUBY
27
- require "yaml"
28
- require "net/imap"
29
-
30
- def load_response(file, name)
31
- YAML.unsafe_load_file(file).dig(:tests, name, :response)
32
- .force_encoding "ASCII-8BIT" \\
33
- or abort "ERRORO: missing %p fixture data in %p" % [name, file]
34
- end
35
-
36
- parser = Net::IMAP::ResponseParser.new
37
- RUBY
38
-
39
- prelude = <<RUBY
40
- response = load_response(%p,
41
- %p)
42
- RUBY
43
- script = "parser.parse(response)"
44
-
45
- benchmarks = tests.map {|file, fixture_name|
46
- name = fixture_name.delete_prefix("test_")
47
- {name:, prelude: prelude % [file, fixture_name], script:}
48
- .transform_keys(&:to_s)
49
- }
50
- .sort_by { _1["name"] }
51
-
52
- puts YAML.dump({"prelude" => init, "benchmark" => benchmarks})