termplot 0.1.0 → 0.3.1

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/README.md +134 -58
  4. data/Rakefile +28 -13
  5. data/doc/dash.png +0 -0
  6. data/doc/demo.png +0 -0
  7. data/doc/file.png +0 -0
  8. data/doc/memory.png +0 -0
  9. data/doc/ping.png +0 -0
  10. data/doc/sin.png +0 -0
  11. data/doc/tcp.png +0 -0
  12. data/examples/sample.rb +17 -0
  13. data/lib/termplot/character_map.rb +15 -4
  14. data/lib/termplot/cli.rb +14 -48
  15. data/lib/termplot/colors.rb +36 -29
  16. data/lib/termplot/commands.rb +27 -0
  17. data/lib/termplot/consumers/base_consumer.rb +132 -0
  18. data/lib/termplot/consumers/command_consumer.rb +14 -0
  19. data/lib/termplot/consumers/multi_source_consumer.rb +33 -0
  20. data/lib/termplot/consumers/single_source_consumer.rb +36 -0
  21. data/lib/termplot/consumers/stdin_consumer.rb +11 -0
  22. data/lib/termplot/consumers.rb +12 -0
  23. data/lib/termplot/{cursors/control_chars.rb → control_chars.rb} +0 -0
  24. data/lib/termplot/cursors/buffered_console_cursor.rb +51 -52
  25. data/lib/termplot/cursors/virtual_cursor.rb +64 -58
  26. data/lib/termplot/cursors.rb +7 -0
  27. data/lib/termplot/dsl/panels.rb +80 -0
  28. data/lib/termplot/dsl/widgets.rb +128 -0
  29. data/lib/termplot/file_config.rb +37 -0
  30. data/lib/termplot/message_broker.rb +111 -0
  31. data/lib/termplot/options.rb +211 -0
  32. data/lib/termplot/positioned_widget.rb +8 -0
  33. data/lib/termplot/producer_options.rb +3 -0
  34. data/lib/termplot/producers/base_producer.rb +32 -0
  35. data/lib/termplot/producers/command_producer.rb +42 -0
  36. data/lib/termplot/producers/stdin_producer.rb +11 -0
  37. data/lib/termplot/producers.rb +7 -0
  38. data/lib/termplot/renderable.rb +35 -0
  39. data/lib/termplot/renderer.rb +16 -257
  40. data/lib/termplot/renderers/border_renderer.rb +48 -0
  41. data/lib/termplot/renderers/text_renderer.rb +73 -0
  42. data/lib/termplot/renderers.rb +6 -0
  43. data/lib/termplot/shell.rb +13 -9
  44. data/lib/termplot/utils/ansi_safe_string.rb +68 -0
  45. data/lib/termplot/version.rb +1 -1
  46. data/lib/termplot/widgets/base_widget.rb +79 -0
  47. data/lib/termplot/widgets/border.rb +6 -0
  48. data/lib/termplot/widgets/dataset.rb +50 -0
  49. data/lib/termplot/widgets/histogram_widget.rb +196 -0
  50. data/lib/termplot/widgets/statistics.rb +21 -0
  51. data/lib/termplot/widgets/statistics_widget.rb +104 -0
  52. data/lib/termplot/widgets/time_series_widget.rb +248 -0
  53. data/lib/termplot/widgets.rb +8 -0
  54. data/lib/termplot/window.rb +29 -9
  55. data/termplot.gemspec +1 -6
  56. metadata +46 -30
  57. data/doc/cpu.png +0 -0
  58. data/doc/demo.cast +0 -638
  59. data/doc/demo.gif +0 -0
  60. data/lib/termplot/consumer.rb +0 -71
  61. data/lib/termplot/cursors/console_cursor.rb +0 -56
  62. data/lib/termplot/series.rb +0 -37
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "termplot/renderer"
4
+ require "termplot/message_broker"
5
+ require "termplot/shell"
6
+
7
+ module Termplot
8
+ module Consumers
9
+ class BaseConsumer
10
+ def initialize(options)
11
+ @options = options
12
+ @broker_pool = MessageBrokerPool.new
13
+ @producer_pool = ProducerPool.new
14
+ @renderer = Renderer.new(
15
+ cols: options.cols,
16
+ rows: options.rows,
17
+ widgets: positioned_widgets,
18
+ debug: options.debug
19
+ )
20
+
21
+ @renderer_thread = RendererThread.new(
22
+ renderer: renderer,
23
+ broker_pool: broker_pool
24
+ )
25
+ end
26
+
27
+ def run
28
+ register_producers_and_brokers
29
+ broker_pool.on_message { renderer_thread.continue }
30
+
31
+ Shell.init(clear: options.full_screen)
32
+ renderer_thread.start
33
+
34
+ # Blocks main thread to produce values from the producer pool
35
+ producer_pool.start_and_block
36
+
37
+ # At this point producer threads have all exited, tell renderer to
38
+ # consume all messages left on the queue
39
+ renderer_thread.continue while !broker_pool.empty?
40
+
41
+ # Close queues
42
+ broker_pool.shutdown
43
+
44
+ # Shutdown renderer
45
+ renderer_thread.join
46
+ end
47
+
48
+ private
49
+ attr_reader :options,
50
+ :broker_pool,
51
+ :producer_pool,
52
+ :renderer,
53
+ :renderer_thread
54
+
55
+ class RendererThread
56
+ def initialize(renderer:, broker_pool:, &block)
57
+ @renderer = renderer
58
+ @broker_pool = broker_pool
59
+ @block = block
60
+ @thread = nil
61
+ end
62
+
63
+ def start
64
+ @thread = Thread.new do
65
+ # Pause and wait to be woken for rendering
66
+ pause
67
+ while !broker_pool.closed?
68
+ num_samples = broker_pool.pending_message_count
69
+
70
+ if num_samples.zero?
71
+ pause
72
+ else
73
+ broker_pool.flush_messages
74
+
75
+ if num_samples > 0
76
+ renderer.render
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def continue
84
+ thread.run
85
+ end
86
+
87
+ def pause
88
+ Thread.stop
89
+ end
90
+
91
+ def join
92
+ thread.join unless thread.stop?
93
+ end
94
+
95
+ private
96
+ attr_reader :renderer, :broker_pool, :block, :thread
97
+ end
98
+
99
+ class ProducerPool
100
+ def initialize
101
+ @producers = []
102
+ @threads = []
103
+ end
104
+
105
+ def start
106
+ # Run Producers with producers
107
+ @threads = producers.map do |producer|
108
+ Thread.new do
109
+ producer.run
110
+ end
111
+ end
112
+ end
113
+
114
+ def wait
115
+ threads.each(&:join)
116
+ end
117
+
118
+ def start_and_block
119
+ start
120
+ wait
121
+ end
122
+
123
+ def add_producer(producer)
124
+ producers.push(producer)
125
+ end
126
+
127
+ private
128
+ attr_reader :producers, :threads
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,14 @@
1
+
2
+ require "termplot/positioned_widget"
3
+ require "termplot/widgets"
4
+ require "termplot/producers"
5
+
6
+ module Termplot
7
+ module Consumers
8
+ class CommandConsumer < SingleSourceConsumer
9
+ def producer_class
10
+ Termplot::Producers::CommandProducer
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "termplot/file_config"
4
+ require "termplot/producers"
5
+
6
+ module Termplot
7
+ module Consumers
8
+ class MultiSourceConsumer < BaseConsumer
9
+ def positioned_widgets
10
+ @positioned_widgets ||= config.positioned_widgets
11
+ end
12
+
13
+ def register_producers_and_brokers
14
+ config.widget_configs.each do |widget_config|
15
+ producer = build_producer(widget_config)
16
+ broker_pool.broker(sender: producer, receiver: widget_config.widget)
17
+ producer_pool.add_producer(producer)
18
+ end
19
+ end
20
+
21
+ private
22
+ def config
23
+ @config ||= FileConfig.new(options).parse_config
24
+ end
25
+
26
+ def build_producer(widget_config)
27
+ Termplot::Producers::CommandProducer.new(
28
+ widget_config.producer_options
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+
2
+ require "termplot/positioned_widget"
3
+ require "termplot/widgets"
4
+ require "termplot/producers"
5
+
6
+ module Termplot
7
+ module Consumers
8
+ class SingleSourceConsumer < BaseConsumer
9
+ def positioned_widgets
10
+ @positioned_widgets ||= [PositionedWidget.new(row: 0, col: 0, widget: widget)]
11
+ end
12
+
13
+ def register_producers_and_brokers
14
+ producer = build_producer
15
+ broker_pool.broker(sender: producer, receiver: widget)
16
+ producer_pool.add_producer(producer)
17
+ end
18
+
19
+ private
20
+ def widget
21
+ return @widget if defined? @widget
22
+ wigdet_classes = {
23
+ "timeseries" => "Termplot::Widgets::TimeSeriesWidget",
24
+ "stats" => "Termplot::Widgets::StatisticsWidget",
25
+ "hist" => "Termplot::Widgets::HistogramWidget",
26
+ }
27
+ klass = Object.const_get(wigdet_classes[options.type])
28
+ @widget = klass.new(**options.to_h)
29
+ end
30
+
31
+ def build_producer
32
+ producer_class.new(options.producer_options)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ require "termplot/producers"
2
+
3
+ module Termplot
4
+ module Consumers
5
+ class StdinConsumer < SingleSourceConsumer
6
+ def producer_class
7
+ Termplot::Producers::StdinProducer
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Termplot
4
+ module Consumers
5
+ autoload :BaseConsumer, "termplot/consumers/base_consumer"
6
+ autoload :MultiSourceConsumer, "termplot/consumers/multi_source_consumer"
7
+ autoload :SingleSourceConsumer, "termplot/consumers/single_source_consumer"
8
+
9
+ autoload :StdinConsumer, "termplot/consumers/stdin_consumer"
10
+ autoload :CommandConsumer, "termplot/consumers/command_consumer"
11
+ end
12
+ end
@@ -1,70 +1,69 @@
1
- require "termplot/cursors/virtual_cursor"
2
- require "termplot/cursors/control_chars"
3
-
4
1
  module Termplot
5
- class BufferedConsoleCursor < VirtualCursor
6
- include Termplot::ControlChars
7
- attr_reader :buffer
2
+ module Cursors
3
+ class BufferedConsoleCursor < VirtualCursor
4
+ include Termplot::ControlChars
5
+ attr_reader :buffer
8
6
 
9
- def initialize(window, buffer)
10
- super(window)
11
- @buffer = buffer
12
- end
7
+ def initialize(window, buffer)
8
+ super(window)
9
+ @buffer = buffer
10
+ end
13
11
 
14
- def write(char)
15
- if writeable?
16
- buffer << char
17
- super(char)
12
+ def write(char)
13
+ if writeable?
14
+ buffer << char
15
+ super(char)
16
+ end
18
17
  end
19
- end
20
18
 
21
- def forward(n = 1)
22
- moved = super(n)
23
- moved.times { buffer << FORWARD }
24
- end
19
+ def forward(n = 1)
20
+ moved = super(n)
21
+ moved.times { buffer << FORWARD }
22
+ end
25
23
 
26
- def back(n = 1)
27
- moved = super(n)
28
- moved.times { buffer << BACK }
29
- end
24
+ def back(n = 1)
25
+ moved = super(n)
26
+ moved.times { buffer << BACK }
27
+ end
30
28
 
31
- def up(n=1)
32
- moved = super(n)
33
- moved.times { buffer << UP }
34
- end
29
+ def up(n=1)
30
+ moved = super(n)
31
+ moved.times { buffer << UP }
32
+ end
35
33
 
36
- def down(n=1)
37
- moved = super(n)
38
- moved.times { buffer << DOWN }
39
- end
34
+ def down(n=1)
35
+ moved = super(n)
36
+ moved.times { buffer << DOWN }
37
+ end
40
38
 
41
- def beginning_of_line
42
- super
43
- buffer << CR
44
- end
39
+ def beginning_of_line
40
+ super
41
+ buffer << CR
42
+ end
45
43
 
46
- def new_line
47
- buffer << NEWLINE
48
- end
44
+ def new_line
45
+ buffer << NEWLINE
46
+ end
49
47
 
50
- def clear_buffer
51
- buffer.clear
52
- end
48
+ def clear_buffer
49
+ buffer.clear
50
+ end
53
51
 
54
- def flush
55
- print buffer.join
56
- end
52
+ def flush
53
+ buffer.join
54
+ end
57
55
 
58
- def position=()
59
- raise "Cannot set cursor position directly"
60
- end
56
+ def position=()
57
+ raise "Cannot set cursor position directly"
58
+ end
61
59
 
62
- def row=()
63
- raise "Cannot set cursor position directly"
64
- end
60
+ def row=()
61
+ raise "Cannot set cursor position directly"
62
+ end
65
63
 
66
- def col=()
67
- raise "Cannot set cursor position directly"
64
+ def col=()
65
+ raise "Cannot set cursor position directly"
66
+ end
68
67
  end
69
68
  end
70
69
  end
@@ -1,76 +1,82 @@
1
1
  module Termplot
2
- class VirtualCursor
3
- attr_reader :position, :window
2
+ module Cursors
3
+ class VirtualCursor
4
+ attr_reader :position, :window
4
5
 
5
- def initialize(window)
6
- @window = window
7
- @position = 0
8
- end
6
+ def initialize(window)
7
+ @window = window
8
+ @position = 0
9
+ end
9
10
 
10
- def write(char)
11
- @position += 1 if writeable?
12
- end
11
+ def write(char)
12
+ @position += 1 if writeable?
13
+ end
13
14
 
14
- def writeable?
15
- position < window.buffer.size
16
- end
15
+ def writeable?
16
+ position < window.buffer.size
17
+ end
17
18
 
18
- def forward(n = 1)
19
- movable_chars = window.buffer.size - position
20
- chars_to_move = [movable_chars, n].min
21
- @position += chars_to_move
22
- chars_to_move
23
- end
19
+ def forward(n = 1)
20
+ movable_chars = window.buffer.size - position
21
+ chars_to_move = [movable_chars, n].min
22
+ @position += chars_to_move
23
+ chars_to_move
24
+ end
24
25
 
25
- def back(n = 1)
26
- chars_to_move = [position, n].min
27
- @position -= chars_to_move
28
- chars_to_move
29
- end
26
+ def back(n = 1)
27
+ chars_to_move = [position, n].min
28
+ @position -= chars_to_move
29
+ chars_to_move
30
+ end
30
31
 
31
- def up(n=1)
32
- return unless row > 0
33
- rows_to_move = [n, row].min
34
- @position -= rows_to_move * window.cols
35
- rows_to_move
36
- end
32
+ def up(n=1)
33
+ return unless row > 0
34
+ rows_to_move = [n, row].min
35
+ @position -= rows_to_move * window.cols
36
+ rows_to_move
37
+ end
37
38
 
38
- def row
39
- (position / window.cols).floor
40
- end
39
+ def row
40
+ (position / window.cols).floor
41
+ end
41
42
 
42
- def col
43
- position % window.cols
44
- end
43
+ def col
44
+ position % window.cols
45
+ end
45
46
 
46
- def row=(y)
47
- @position = y * window.cols + col
48
- end
47
+ def row=(y)
48
+ @position = y * window.cols + col
49
+ end
49
50
 
50
- def col=(x)
51
- beginning_of_line
52
- forward(x)
53
- end
51
+ def col=(x)
52
+ beginning_of_line
53
+ forward(x)
54
+ end
54
55
 
55
- def down(n=1)
56
- return 0 unless row < (window.rows - 1)
57
- rows_to_move = [n, window.rows - 1 - row].min
58
- @position += window.cols * rows_to_move
59
- rows_to_move
60
- end
56
+ def down(n=1)
57
+ return 0 unless row < (window.rows - 1)
58
+ rows_to_move = [n, window.rows - 1 - row].min
59
+ @position += window.cols * rows_to_move
60
+ rows_to_move
61
+ end
61
62
 
62
- def beginning_of_line
63
- @position = position - (position % window.cols)
64
- end
63
+ def beginning_of_line
64
+ @position = position - (position % window.cols)
65
+ end
65
66
 
66
- def position=(n)
67
- @position = n
68
- end
67
+ def beginning_of_line?
68
+ @position % window.cols == 0
69
+ end
70
+
71
+ def position=(n)
72
+ @position = n
73
+ end
69
74
 
70
- def reset_position
71
- return if position == 0
72
- up(row) # Go up by row num times
73
- beginning_of_line
75
+ def reset_position
76
+ return if position == 0
77
+ up(row) # Go up by row num times
78
+ beginning_of_line
79
+ end
74
80
  end
75
81
  end
76
82
  end
@@ -0,0 +1,7 @@
1
+ module Termplot
2
+ module Cursors
3
+ autoload :VirtualCursor, "termplot/cursors/virtual_cursor"
4
+ autoload :ConsoleCursor, "termplot/cursors/console_cursor"
5
+ autoload :BufferedConsoleCursor, "termplot/cursors/buffered_console_cursor"
6
+ end
7
+ end
@@ -0,0 +1,80 @@
1
+ require "termplot/commands"
2
+ require "termplot/dsl/widgets"
3
+
4
+ module Termplot
5
+ module DSL
6
+ class Panel
7
+ include Termplot::Commands
8
+ include Termplot::WidgetDSL
9
+
10
+ attr_accessor(
11
+ :rows,
12
+ :cols,
13
+ :start_row,
14
+ :start_col
15
+ )
16
+
17
+ def initialize(options, children = [])
18
+ @options = options
19
+ @children = children
20
+ end
21
+
22
+ def set_dimensions(rows, cols, start_row, start_col)
23
+ raise "Must be implemented"
24
+ end
25
+
26
+ def flatten
27
+ children.map(&:flatten).flatten
28
+ end
29
+
30
+ private
31
+ attr_reader :options, :children
32
+
33
+ def row(&block)
34
+ new_row = Row.new(options)
35
+ new_row.instance_eval(&block)
36
+ children.push(new_row)
37
+ end
38
+
39
+ def col(&block)
40
+ new_col = Col.new(options)
41
+ new_col.instance_eval(&block)
42
+ children.push(new_col)
43
+ end
44
+ end
45
+
46
+ class Row < Panel
47
+ def set_dimensions(rows, cols, start_row, start_col)
48
+ @rows = rows
49
+ @cols = cols
50
+ child_cols = cols / children.count
51
+
52
+ children.each_with_index do |child, index|
53
+ child.set_dimensions(
54
+ rows,
55
+ child_cols,
56
+ start_row,
57
+ child_cols * index
58
+ )
59
+ end
60
+ end
61
+ end
62
+
63
+ class Col < Panel
64
+ def set_dimensions(rows, cols, start_row, start_col)
65
+ @rows = rows
66
+ @cols = cols
67
+ child_rows = rows / children.count
68
+
69
+ children.each_with_index do |child, index|
70
+ child.set_dimensions(
71
+ child_rows,
72
+ cols,
73
+ child_rows * index,
74
+ start_col
75
+ )
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end