msgthr 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9a571597f50c7ec04abef8c956d811854d727bcc
4
- data.tar.gz: 07f8634e7f6bd758206ad6e8170acd5048af2b07
2
+ SHA256:
3
+ metadata.gz: 5482fc1e20bf45ed07ee4b3eacc93eccd28c3f28195550d1629cb5d21919f42b
4
+ data.tar.gz: eaca3d5d6ef9840154d6b5fd70033382bdbb9fe3863f726585b7ef6d0d7dfae1
5
5
  SHA512:
6
- metadata.gz: 1e13bda13b30d4548955d306570848c8bf5f0993575617b81822a4fc684085182eb7200f5e0cadfc56c38215c166d06d8fa07853cbbad784c21665b13e9355ee
7
- data.tar.gz: 7217b572b347c32ef5a01d0b95c805dfc37e28bd54ad078d4bd38f8b7c5396ba0806d052c2194b078fac78770c72cb449cb485f2ec8af1f8fdff68b528fd3db6
6
+ metadata.gz: e73d95955bd492e97bd2c129986da0658df78fe82e185218195a769e77643b858bee1c64de84c94bb8e7baf6b5a611cad08ac492072ad25812c70821563dfc66
7
+ data.tar.gz: f12ec74ea787a1d09ccd20c191429bfafe0f291dfffa3fded5e4966efa1b8b82aad0e8653c06c1728e0a6ba06ee4703f63ffe2cf5a40e58a1cdb6d63c56c84d8
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  cgit_url: https://80x24.org/msgthr.git
3
3
  git_url: git://80x24.org/msgthr.git
4
- rdoc_url: https://80x24.org/msgthr/rdoc/
4
+ rdoc_url: https://80x24.org/msgthr/
5
5
  ml_url: https://80x24.org/msgthr-public/
6
6
  public_email: msgthr-public@80x24.org
@@ -4,7 +4,7 @@ all::
4
4
  pkg = msgthr
5
5
  RUBY = ruby
6
6
  lib := lib
7
- VERSION := 1.0.1
7
+ VERSION := 1.1.0
8
8
  RSYNC_DEST := 80x24.org:/srv/80x24/msgthr/
9
9
 
10
10
  RSYNC = rsync
data/README CHANGED
@@ -76,7 +76,7 @@ because CSS/JS/images are all too complicated for us.
76
76
 
77
77
  RDoc API documentation in simple HTML is available at:
78
78
 
79
- https://80x24.org/msgthr/rdoc/
79
+ https://80x24.org/msgthr/rdoc/Msgthr.html
80
80
 
81
81
  Copyright
82
82
  ---------
@@ -7,8 +7,7 @@
7
7
  #
8
8
  # * use Msgthr.new to create a new object
9
9
  # * use Msgthr#add! for every message you have
10
- # * use Msgthr#thread! to perform threading operations
11
- # * optionally, use Msgthr#order! to sort messages
10
+ # * use Msgthr#thread! to perform threading and (optionally) sort
12
11
  # * use Msgthr#walk_thread to iterate through the threaded tree
13
12
  #
14
13
  # See https://80x24.org/msgthr/README for more info
@@ -20,43 +19,39 @@ class Msgthr
20
19
  # calling Msgthr#thread!
21
20
  attr_reader :rootset
22
21
 
22
+ # raised when methods are called in an unsupported order
23
+ StateError = Class.new(RuntimeError)
24
+
23
25
  # Initialize a Msgthr object
24
26
  def initialize
25
27
  @id_table = {}
26
28
  @rootset = []
29
+ @state = :init # :init => :threaded => :ordered
27
30
  end
28
31
 
29
32
  # Clear internal data structures to save memory and prepare for reuse
30
33
  def clear
31
34
  @rootset.clear
32
35
  @id_table.clear
36
+ @state = :init
33
37
  end
34
38
 
35
39
  # Performs threading on the messages and returns the rootset
36
40
  # (set of message containers without parents).
37
41
  #
38
- # Call this after all #add operations are complete.
39
- # This does not sort, use #order! if sorting is necessary.
40
- def thread!
41
- ret = @rootset
42
- @id_table.each_value { |cont| ret << cont if cont.parent.nil? }.clear
43
- ret
44
- end
45
-
46
- # Performs an in-place sort on messages after thread!
47
- # This is optional and intended to be called this only after #thread!
42
+ # Call this only after all #add operations are complete.
48
43
  #
49
- # This takes a block which yields an array of Msgthr::Container
50
- # objects for sorting.
44
+ # If given an optional block, it will perform an in-place sort
45
+ # using the block parameter.
51
46
  #
52
- # To sort by unique +mid+ identifiers for each container:
47
+ # To thread and sort by unique +mid+ identifiers for each container:
53
48
  #
54
- # msgthr.order! { |ary| ary.sort_by!(&:mid) }
49
+ # msgthr.thread! { |ary| ary.sort_by!(&:mid) }
55
50
  #
56
51
  # If your opaque message pointer contains a +time+ accessor which gives
57
52
  # a Time object:
58
53
  #
59
- # msgthr.order! do |ary|
54
+ # msgthr.thread! do |ary|
60
55
  # ary.sort_by! do |cont| # Msgthr::Container
61
56
  # cur = cont.topmost
62
57
  # cur ? cur.msg.time : Time.at(0)
@@ -67,16 +62,40 @@ class Msgthr
67
62
  # Msgthr::Container#mid, as any known missing messages (ghosts)
68
63
  # will still have a +mid+. However, Msgthr::Container#topmost is
69
64
  # necessary if accessing Msgthr::Container#msg.
65
+ def thread!
66
+ raise StateError, "already #@state" if @state != :init
67
+ ret = @rootset
68
+ @id_table.each_value { |cont| ret << cont if cont.parent.nil? }.clear
69
+ @state = :threaded
70
+ order! { |ary| yield(ary) } if block_given?
71
+ ret
72
+ end
73
+
74
+ # Calling this method is unnecessary since msgthr 1.1.0.
75
+ # In previous releases, the #thread! did not support a block
76
+ # parameter for ordering. This method remains for compatibility.
70
77
  def order!
78
+ case @state
79
+ when :init then raise StateError, "#thread! not called"
80
+ when :ordered then raise StateError, "already #@state"
81
+ # else @state == :threaded
82
+ end
83
+
71
84
  yield @rootset
72
85
  @rootset.each do |cont|
73
86
  # this calls Msgthr::Container#order!, which is non-recursive
74
87
  cont.order! { |children| yield(children) }
75
88
  end
89
+ @state = :ordered
90
+ @rootset
76
91
  end
77
92
 
78
93
  # non-recursively walk a set of messages after #thread!
79
- # (and optionally, #order!)
94
+ # (and optionally, #order!).
95
+ #
96
+ # If you do not care about ordering, you may call this
97
+ # immediately after all #add operations are complete starting
98
+ # with msgthr 1.1.0
80
99
  #
81
100
  # This takes a block and yields 3 elements to it: +|level, container, index|+
82
101
  # for each message container.
@@ -95,6 +114,8 @@ class Msgthr
95
114
  # printf("#{indent} % 3d. %s\n", index, subject)
96
115
  # end
97
116
  def walk_thread
117
+ thread! if @state == :init
118
+ order! { |_| } if @state == :threaded
98
119
  i = -1
99
120
  q = @rootset.map { |cont| [ 0, cont, i += 1 ] }
100
121
  while tmp = q.shift
@@ -128,6 +149,8 @@ class Msgthr
128
149
  # calling this method to avoid wasting memory on hash keys. Likewise
129
150
  # is true for any String objects in +refs+.
130
151
  def add(mid, refs, msg)
152
+ @state == :init or raise StateError, "cannot add when already #@state"
153
+
131
154
  cur = @id_table[mid] ||= Msgthr::Container.new(mid)
132
155
  cur.msg = msg
133
156
  refs or return
@@ -1,9 +1,9 @@
1
1
  # Copyright (C) 2016 all contributors <msgthr-public@80x24.org>
2
2
  # License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
3
3
 
4
- # An internal container class, this is exposed for Msgthr#order!
5
- # and Msgthr#walk_thread APIs. They should should not be initialized
6
- # in your own code.
4
+ # An internal container class, this is exposed for Msgthr#thread!
5
+ # Msgthr#order! and Msgthr#walk_thread APIs through block parameters.
6
+ # They should should not be initialized in your own code.
7
7
  #
8
8
  # One container object will exist for every message you call Msgthr#add! on,
9
9
  # so there can potentially be many of these objects for large sets of
@@ -4,7 +4,7 @@
4
4
  Gem::Specification.new do |s|
5
5
  manifest = File.read('.manifest').split(/\n/)
6
6
  s.name = %q{msgthr}
7
- s.version = ENV['VERSION'] || '1.0.1'
7
+ s.version = ENV['VERSION'] || '1.1.0'
8
8
  s.authors = ['msgthr hackers']
9
9
  s.summary = 'container-agnostic, non-recursive message threading'
10
10
  s.description = File.read('README').split(/\n\n/)[1].strip
@@ -14,4 +14,12 @@ Gem::Specification.new do |s|
14
14
  s.files = manifest
15
15
  s.licenses = 'GPL-2.0+'
16
16
  s.required_ruby_version = '>= 1.9.3'
17
+
18
+ if s.respond_to?(:metadata=)
19
+ s.metadata = {
20
+ 'source_code_uri' => 'https://80x24.org/msgthr.git',
21
+ 'mailing_list_uri' => 'https://80x24.org/msgthr-public/',
22
+ 'bug_tracker_uri' => 'https://80x24.org/msgthr-public/',
23
+ }
24
+ end
17
25
  end
@@ -11,8 +11,9 @@ class TestMsgthr < Test::Unit::TestCase
11
11
  thr.add('c', nil, 'c')
12
12
  thr.add('D', nil, 'D')
13
13
  thr.add('d', %w(missing), 'd')
14
- thr.thread!
14
+ rset = thr.thread!
15
15
  rootset = thr.order! { |c| c.sort_by!(&:mid) }
16
+ assert_same rset, rootset
16
17
  assert_equal %w(D c missing), rootset.map(&:mid)
17
18
  assert_equal 'D', rootset[0].msg
18
19
  assert_equal %w(b), rootset[1].children.map(&:mid)
@@ -30,6 +31,67 @@ class TestMsgthr < Test::Unit::TestCase
30
31
  0. abc
31
32
  2. [missing: <missing>]
32
33
  0. d
34
+ EOF
35
+ assert_equal exp, out
36
+ end
37
+
38
+ def test_order_in_thread
39
+ thr = Msgthr.new
40
+ thr.add(1, nil, 'a')
41
+ thr.add(2, [1], 'b')
42
+ thr.thread! do |ary|
43
+ ary.sort_by! do |cont|
44
+ cur = cont.topmost
45
+ cur ? cur : 0
46
+ end
47
+ end
48
+ out = ''
49
+ thr.walk_thread do |level, container, index|
50
+ msg = container.msg
51
+ out << "#{level} [#{index}] #{msg}\n"
52
+ end
53
+ exp = <<EOF.b
54
+ 0 [0] a
55
+ 1 [0] b
56
+ EOF
57
+ assert_equal exp, out
58
+ end
59
+
60
+ def test_out_of_order
61
+ thr = Msgthr.new
62
+ thr.thread!
63
+ assert_raise(Msgthr::StateError) { thr.add(1, nil, 'a') }
64
+ thr.clear # make things good again, following should not raise:
65
+ thr.add(1, nil, 'a')
66
+ thr.thread!
67
+ assert_raise(Msgthr::StateError) { thr.thread! }
68
+
69
+ out = []
70
+ thr.walk_thread do |level, container, index|
71
+ msg = container.msg
72
+ out << "#{level} [#{index}] #{msg}"
73
+ end
74
+ assert_equal [ '0 [0] a' ], out
75
+ assert_raise(Msgthr::StateError) { thr.thread! { raise "DO NOT CALL" } }
76
+ assert_raise(Msgthr::StateError) { thr.order! { |_| raise "DO NOT CALL" } }
77
+
78
+ # this is legal, even if non-sensical
79
+ thr.clear
80
+ thr.walk_thread { |level, container, index| raise "DO NOT CALL" }
81
+ end
82
+
83
+ def test_short_add_to_walk
84
+ thr = Msgthr.new
85
+ thr.add(1, nil, 'a')
86
+ thr.add(2, [1], 'b')
87
+ out = ''
88
+ thr.walk_thread do |level, container, index|
89
+ msg = container.msg
90
+ out << "#{level} [#{index}] #{msg}\n"
91
+ end
92
+ exp = <<EOF.b
93
+ 0 [0] a
94
+ 1 [0] b
33
95
  EOF
34
96
  assert_equal exp, out
35
97
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: msgthr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - msgthr hackers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-08 00:00:00.000000000 Z
11
+ date: 2017-12-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
14
  Pure Ruby message threading based on the algorithm described by
@@ -33,7 +33,10 @@ files:
33
33
  homepage: https://80x24.org/msgthr/
34
34
  licenses:
35
35
  - GPL-2.0+
36
- metadata: {}
36
+ metadata:
37
+ source_code_uri: https://80x24.org/msgthr.git
38
+ mailing_list_uri: https://80x24.org/msgthr-public/
39
+ bug_tracker_uri: https://80x24.org/msgthr-public/
37
40
  post_install_message:
38
41
  rdoc_options: []
39
42
  require_paths:
@@ -50,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
53
  version: '0'
51
54
  requirements: []
52
55
  rubyforge_project:
53
- rubygems_version: 2.6.12
56
+ rubygems_version: 2.7.3
54
57
  signing_key:
55
58
  specification_version: 4
56
59
  summary: container-agnostic, non-recursive message threading