termplot 0.2.1 → 0.3.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +124 -54
  4. data/Rakefile +27 -14
  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 +16 -3
  15. data/lib/termplot/colors.rb +7 -0
  16. data/lib/termplot/commands.rb +27 -0
  17. data/lib/termplot/consumers.rb +12 -0
  18. data/lib/termplot/consumers/base_consumer.rb +132 -0
  19. data/lib/termplot/consumers/command_consumer.rb +14 -0
  20. data/lib/termplot/consumers/multi_source_consumer.rb +33 -0
  21. data/lib/termplot/consumers/single_source_consumer.rb +36 -0
  22. data/lib/termplot/consumers/stdin_consumer.rb +11 -0
  23. data/lib/termplot/cursors/buffered_console_cursor.rb +1 -1
  24. data/lib/termplot/cursors/virtual_cursor.rb +4 -0
  25. data/lib/termplot/dsl/panels.rb +80 -0
  26. data/lib/termplot/dsl/widgets.rb +128 -0
  27. data/lib/termplot/file_config.rb +37 -0
  28. data/lib/termplot/message_broker.rb +108 -0
  29. data/lib/termplot/options.rb +100 -20
  30. data/lib/termplot/positioned_widget.rb +8 -0
  31. data/lib/termplot/producer_options.rb +3 -0
  32. data/lib/termplot/producers.rb +3 -3
  33. data/lib/termplot/producers/base_producer.rb +12 -15
  34. data/lib/termplot/producers/command_producer.rb +25 -9
  35. data/lib/termplot/producers/stdin_producer.rb +1 -4
  36. data/lib/termplot/renderable.rb +35 -0
  37. data/lib/termplot/renderer.rb +16 -257
  38. data/lib/termplot/renderers.rb +6 -0
  39. data/lib/termplot/renderers/border_renderer.rb +48 -0
  40. data/lib/termplot/renderers/text_renderer.rb +73 -0
  41. data/lib/termplot/shell.rb +13 -9
  42. data/lib/termplot/utils/ansi_safe_string.rb +68 -0
  43. data/lib/termplot/version.rb +1 -1
  44. data/lib/termplot/widget_dsl.rb +130 -0
  45. data/lib/termplot/widgets.rb +8 -0
  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/window.rb +25 -5
  54. data/termplot.gemspec +1 -6
  55. metadata +36 -24
  56. data/doc/MSFT.png +0 -0
  57. data/doc/cpu.png +0 -0
  58. data/doc/demo.cast +0 -638
  59. data/lib/termplot/consumer.rb +0 -75
  60. data/lib/termplot/cursors/console_cursor.rb +0 -57
  61. data/lib/termplot/series.rb +0 -37
@@ -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
@@ -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
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
@@ -50,7 +50,7 @@ module Termplot
50
50
  end
51
51
 
52
52
  def flush
53
- print buffer.join
53
+ buffer.join
54
54
  end
55
55
 
56
56
  def position=()
@@ -64,6 +64,10 @@ module Termplot
64
64
  @position = position - (position % window.cols)
65
65
  end
66
66
 
67
+ def beginning_of_line?
68
+ @position % window.cols == 0
69
+ end
70
+
67
71
  def position=(n)
68
72
  @position = n
69
73
  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
@@ -0,0 +1,128 @@
1
+ require "termplot/positioned_widget"
2
+ require "termplot/widgets"
3
+
4
+ module Termplot
5
+ module WidgetDSL
6
+ def timeseries(attrs)
7
+ attrs = merge_defaults(attrs)
8
+ children.push(TimeSeriesConfig.new(attrs))
9
+ end
10
+
11
+ def statistics(attrs)
12
+ attrs = merge_defaults(attrs)
13
+ children.push(StatisticsConfig.new(attrs))
14
+ end
15
+
16
+ def histogram(attrs)
17
+ attrs = merge_defaults(attrs)
18
+ children.push(HistogramConfig.new(attrs))
19
+ end
20
+
21
+ private
22
+
23
+ def merge_defaults(attrs)
24
+ Termplot::Options.default_options.merge(attrs)
25
+ end
26
+
27
+ class WidgetConfig
28
+ attr_reader(
29
+ :title,
30
+ :command,
31
+ :interval,
32
+ :col,
33
+ :row,
34
+ :cols,
35
+ :rows,
36
+ :debug
37
+ )
38
+
39
+ def initialize(opts)
40
+ @title = opts[:title]
41
+
42
+ @command = opts[:command]
43
+ @interval = opts[:interval]
44
+ @debug = opts[:debug]
45
+
46
+ post_initialize(opts)
47
+ end
48
+
49
+ def post_initialize(opts)
50
+ # Implemented in subclasses
51
+ end
52
+
53
+ def set_dimensions(rows, cols, start_row, start_col)
54
+ @rows = rows
55
+ @cols = cols
56
+ @row = start_row
57
+ @col = start_col
58
+ end
59
+
60
+ def flatten
61
+ self
62
+ end
63
+
64
+ def positioned_widget
65
+ @positioned_widget ||= PositionedWidget.new(
66
+ row: row,
67
+ col: col,
68
+ widget: widget
69
+ )
70
+ end
71
+
72
+ def widget
73
+ raise "Must be implemented"
74
+ end
75
+
76
+ def producer_options
77
+ ProducerOptions.new(command: command, interval: interval)
78
+ end
79
+ end
80
+
81
+ class TimeSeriesConfig < WidgetConfig
82
+ attr_reader :color, :line_style
83
+ def post_initialize(opts)
84
+ @color = opts[:color]
85
+ @line_style = opts[:line_style]
86
+ end
87
+
88
+ def widget
89
+ @widget ||= Termplot::Widgets::TimeSeriesWidget.new(
90
+ title: title,
91
+ line_style: line_style,
92
+ color: color,
93
+ cols: cols,
94
+ rows: rows,
95
+ debug: debug
96
+ )
97
+ end
98
+ end
99
+
100
+ class StatisticsConfig < WidgetConfig
101
+ def widget
102
+ @widget ||= Termplot::Widgets::StatisticsWidget.new(
103
+ title: title,
104
+ cols: cols,
105
+ rows: rows,
106
+ debug: debug
107
+ )
108
+ end
109
+ end
110
+
111
+ class HistogramConfig < WidgetConfig
112
+ attr_reader :color
113
+ def post_initialize(opts)
114
+ @color = opts[:color]
115
+ end
116
+
117
+ def widget
118
+ @widget ||= Termplot::Widgets::HistogramWidget.new(
119
+ title: title,
120
+ color: color,
121
+ cols: cols,
122
+ rows: rows,
123
+ debug: debug
124
+ )
125
+ end
126
+ end
127
+ end
128
+ end