net-imap 0.4.2 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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})