termistat 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ pkg/*
4
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in termistat.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,20 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ termistat (0.0.1)
5
+ ffi-ncurses
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ ffi (1.0.9)
11
+ ffi-ncurses (0.3.3)
12
+ ffi (>= 0.6.3)
13
+ rake (0.9.2)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ rake
20
+ termistat!
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2011 Solomon White
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # Termistat
2
+
3
+ Termistat displays an ncurses-based status bar atop your STDOUT.
4
+
5
+ ## Scenario:
6
+
7
+ You have a long-running process that writes detailed log information to
8
+ standard output. You would like to also be able to determine overall
9
+ progress at a glance. You could scatter status lines among the log
10
+ lines, but that's messy and can be hard to find. You could ditch the
11
+ detail output and display status only, but then you lose the sense of
12
+ what's going on at the detail level.
13
+
14
+ ## The Solution
15
+
16
+ Termistat gives you the best of both worlds. You still have your
17
+ detailed information, but you also have a status bar overlay with which
18
+ you can display summary information.
19
+
20
+ ## Screenshot
21
+
22
+ Here's what termistat looks like in action:
23
+
24
+ ![screenshot](https://github.com/rubysolo/termistat/raw/master/examples/file_copy.png)
25
+
26
+
27
+ ## Installation:
28
+
29
+ gem install termistat
30
+
31
+ ## Usage:
32
+
33
+ Include the module in your class, and you get a `status_bar` method.
34
+
35
+ class VerboseProcess
36
+ include Termistat
37
+
38
+ def perform
39
+ 1000.times do |index|
40
+ status_bar progress(index / 1000.0)
41
+ $stdout.puts "This is normal log entry number #{ index }"
42
+
43
+ # do some hard work
44
+ sleep(rand(10))
45
+ end
46
+ end
47
+
48
+ def progress(pct)
49
+ "status: %0.2f% complete" % (pct * 100)
50
+ end
51
+ end
52
+
53
+ Don't want to or can't include? No problem, you can use the module
54
+ method.
55
+
56
+ class VerboseProcess
57
+ def perform
58
+ 1000.times do |index|
59
+ Termistat.status_bar progress(index / 1000.0)
60
+ $stdout.puts "This is normal log entry number #{ index }"
61
+
62
+ # do some hard work
63
+ sleep(rand(10))
64
+ end
65
+ end
66
+
67
+ def progress(pct)
68
+ "status: %0.2f complete" % pct
69
+ end
70
+ end
71
+
72
+ ## Configuration:
73
+
74
+ Termistat provides a default configuration that will display your status
75
+ bar in the top right corner of your terminal. Using the configuration
76
+ DSL, you can customize the status bar to your liking.
77
+
78
+ Termistat.config do
79
+ position :top
80
+ align :center
81
+ background :black
82
+ foreground :magenta
83
+ end
84
+
85
+ ## Requirements
86
+
87
+ * ffi-ncurses gem
88
+ * ncurses (tested with homebrew-installed ncurses)
89
+
90
+ ## License
91
+
92
+ The MIT License (MIT)
93
+ Copyright (c) 2011 Solomon White
94
+
95
+ Permission is hereby granted, free of charge, to any person obtaining a copy
96
+ of this software and associated documentation files (the "Software"), to deal
97
+ in the Software without restriction, including without limitation the rights
98
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99
+ copies of the Software, and to permit persons to whom the Software is
100
+ furnished to do so, subject to the following conditions:
101
+
102
+ The above copyright notice and this permission notice shall be included in all
103
+ copies or substantial portions of the Software.
104
+
105
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
106
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
107
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
108
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
109
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
110
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
111
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new('test') do |t|
5
+ t.pattern = 'test/**/test_*.rb'
6
+ t.warning = true
7
+ end
8
+
9
+ task :default => :test
10
+
Binary file
@@ -0,0 +1,17 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'termistat'
3
+
4
+ class FileCopy
5
+ include Termistat
6
+
7
+ def simulate
8
+ ('a'..'z').to_a.each_with_index do |letter, index|
9
+ status_bar("%0.1f%% complete" % ((index / 26.to_f) * 100))
10
+ puts "copying /path/to/file/#{ letter }..."
11
+ sleep rand(0.5)
12
+ end
13
+ end
14
+ end
15
+
16
+ fc = FileCopy.new
17
+ fc.simulate
data/lib/termistat.rb ADDED
@@ -0,0 +1,146 @@
1
+ require 'termistat/config'
2
+ require 'termistat/tee_io'
3
+ require 'termistat/version'
4
+
5
+ begin
6
+ require 'ffi-ncurses'
7
+ rescue LoadError
8
+ require 'rubygems'
9
+ require 'ffi-ncurses'
10
+ end
11
+
12
+ #
13
+ # Termistat is a status bar for your terminal
14
+ #
15
+ # :title:Termistat
16
+
17
+ module Termistat
18
+ #
19
+ # The +status_bar+ instance method initializes the status bar if necessary
20
+ # and displays your message.
21
+ #
22
+ # === Parameters
23
+ # * message = the messge you want to display
24
+ #
25
+ # === Example
26
+ # include Termistat
27
+ # status_bar "37% complete"
28
+ #
29
+ def status_bar(message)
30
+ Termistat.status_bar message
31
+ end
32
+
33
+ class << self
34
+ #
35
+ # the +status_bar+ class method initializes the status bar if necessary
36
+ # and displays your message.
37
+ #
38
+ # === Parameters
39
+ # * message = the messge you want to display
40
+ #
41
+ # === Example
42
+ # Termistat.status_bar "37% complete"
43
+ #
44
+ def status_bar(message)
45
+ setup unless @stdscr
46
+ m = formatted_message(message, config.align, status_bar_width)
47
+
48
+ FFI::NCurses.wmove @status, 0, 0
49
+ FFI::NCurses.wattr_set @status, FFI::NCurses::A_NORMAL, 1, nil
50
+ FFI::NCurses.waddstr @status, m
51
+ FFI::NCurses.wrefresh @status
52
+ end
53
+
54
+ #
55
+ # +config+ either returns the active configuration or (when a block is
56
+ # passed), sets up the configuration DSL. See Termistat::Config for
57
+ # supported parameters and options.
58
+ #
59
+ # === Example
60
+ # Termistat.config do
61
+ # align :left
62
+ # end
63
+ #
64
+ def config(&block)
65
+ if block_given?
66
+ c = Config.new
67
+ c.instance_eval(&block)
68
+ @config = c
69
+ end
70
+
71
+ @config ||= Config.new
72
+ end
73
+
74
+ #:enddoc:
75
+ def config=(config)
76
+ @config = config
77
+ end
78
+
79
+ def setup
80
+ # set up ncurses standard screen
81
+ @stdscr = FFI::NCurses.initscr
82
+ FFI::NCurses.scrollok @stdscr, 1
83
+
84
+ # set up ncurses status bar
85
+ @height, @width = FFI::NCurses.getmaxyx(@stdscr)
86
+
87
+ @status = FFI::NCurses.newwin(*status_bar_params)
88
+ FFI::NCurses.scrollok @status, 0
89
+
90
+ # set up colors
91
+ FFI::NCurses.start_color
92
+ background = FFI::NCurses::Color.const_get(config.background.to_s.upcase)
93
+ foreground = FFI::NCurses::Color.const_get(config.foreground.to_s.upcase)
94
+ FFI::NCurses.init_pair(1, foreground, background)
95
+
96
+ # hide cursor
97
+ FFI::NCurses.curs_set 0
98
+
99
+ # redirect stdout
100
+ $stdout = TeeIO.new do |msg|
101
+ FFI::NCurses.addstr msg
102
+ FFI::NCurses.refresh
103
+
104
+ FFI::NCurses.touchwin @status
105
+ FFI::NCurses.wrefresh @status
106
+ end
107
+
108
+ at_exit do
109
+ FFI::NCurses.endwin
110
+
111
+ output = $stdout.string
112
+ $stdout = STDOUT
113
+ puts output
114
+ end
115
+ end
116
+
117
+ def status_bar_params
118
+ case config.position
119
+ when Array
120
+ config.position
121
+ when :top
122
+ [1, @width, 0, 0]
123
+
124
+ when :top_right
125
+ x = @width / 2
126
+ w = @width - x
127
+ [1, w, 0, x]
128
+
129
+ when :top_left
130
+ x = @width / 2
131
+ w = @width - x
132
+ [1, w, 0, 0]
133
+
134
+ end
135
+ end
136
+
137
+ def status_bar_width
138
+ status_bar_params[1]
139
+ end
140
+
141
+ def formatted_message(string, alignment, width)
142
+ return string.center(width) if :center === alignment
143
+ "%#{ :left === alignment ? '-' : '' }#{ width }s" % string
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,77 @@
1
+ module Termistat
2
+ #
3
+ # The configuration class and DSL for Termistat
4
+ #
5
+ class Config
6
+ def initialize(options={}) #:notnew:
7
+ @options = {
8
+ :position => :top_right,
9
+ :align => :left,
10
+ :foreground => :white,
11
+ :background => :blue,
12
+ }.merge(options)
13
+ end
14
+
15
+ #
16
+ # position of the status bar on the terminal
17
+ #
18
+ # === Supported Options
19
+ # * +:top_left+ : left half of top line
20
+ # * +:top+ : full top line
21
+ # * +:top_right+ : right half of top line
22
+ #
23
+ def position(position=nil)
24
+ @options[:position] = position unless position.nil?
25
+ @options[:position]
26
+ end
27
+
28
+ #
29
+ # alignment of text within the status bar
30
+ #
31
+ # === Supported Options
32
+ # * +:left+
33
+ # * +:center+
34
+ # * +:right+
35
+ #
36
+ def align(align=nil)
37
+ @options[:align] = align unless align.nil?
38
+ @options[:align]
39
+ end
40
+
41
+ #
42
+ # color of foreground (text)
43
+ #
44
+ # === Supported Options
45
+ # * +:black+
46
+ # * +:red+
47
+ # * +:green+
48
+ # * +:yellow+
49
+ # * +:blue+
50
+ # * +:magenta+
51
+ # * +:cyan+
52
+ # * +:white+
53
+ #
54
+ def foreground(color=nil)
55
+ @options[:foreground] = color unless color.nil?
56
+ @options[:foreground]
57
+ end
58
+
59
+ #
60
+ # color of background
61
+ #
62
+ # === Supported Options
63
+ # * +:black+
64
+ # * +:red+
65
+ # * +:green+
66
+ # * +:yellow+
67
+ # * +:blue+
68
+ # * +:magenta+
69
+ # * +:cyan+
70
+ # * +:white+
71
+ #
72
+ def background(color=nil)
73
+ @options[:background] = color unless color.nil?
74
+ @options[:background]
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,30 @@
1
+ require 'stringio'
2
+
3
+ module Termistat
4
+ #
5
+ # TeeIO wraps a StringIO object such that writes to the IO can trigger events
6
+ # immediately, but the entirety of the string is available later.
7
+ #
8
+ # initialize method takes a block that will be called on every write.
9
+ #
10
+ class TeeIO < StringIO
11
+ # create a new TeeIO object
12
+ #
13
+ # == Example
14
+ # $stdout = TeeIO.new {|str| STDOUT.puts str.reverse }
15
+ #
16
+ def initialize(&block)
17
+ @io = StringIO.new
18
+ @callback = block
19
+ end
20
+
21
+ def write(chars)
22
+ @io.write(chars)
23
+ @callback.call(chars)
24
+ end
25
+
26
+ def string
27
+ @io.string
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Termistat
2
+ VERSION = "0.0.1"
3
+ end
data/termistat.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "termistat/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "termistat"
7
+ s.version = Termistat::VERSION
8
+ s.authors = ["Solomon White"]
9
+ s.email = ["rubysolo@gmail.com"]
10
+ s.homepage = "https://github.com/rubysolo/termistat"
11
+ s.summary = %q{Terminal status bar}
12
+ s.description = %q{Display status bar overlay for summary information}
13
+
14
+ s.rubyforge_project = "termistat"
15
+
16
+ s.add_runtime_dependency "ffi-ncurses"
17
+ s.add_development_dependency "rake"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'minitest/autorun'
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
@@ -0,0 +1,16 @@
1
+ require_relative '../helper'
2
+ require 'termistat/tee_io'
3
+
4
+ class TestTeeIO < MiniTest::Unit::TestCase
5
+ def setup
6
+ @default = Termistat::Config.new
7
+ @custom = Termistat::Config.new(
8
+ :position => :top_right,
9
+ :align => :left,
10
+ )
11
+ end
12
+
13
+ def test_configuration
14
+ assert_equal :top_right, @default.position
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../helper'
2
+ require 'termistat/tee_io'
3
+
4
+ class TestTeeIO < MiniTest::Unit::TestCase
5
+ def test_callback
6
+ @capture = nil
7
+ @tee_io = Termistat::TeeIO.new {|msg| @capture = msg }
8
+ @tee_io.print "foo"
9
+ assert_equal "foo", @capture
10
+ end
11
+
12
+ def test_output_is_recorded
13
+ @tee_io = Termistat::TeeIO.new {|*args|}
14
+ @tee_io.puts "foo"
15
+ assert_equal "foo\n", @tee_io.string
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../helper'
2
+ require 'termistat'
3
+
4
+ class Included
5
+ include Termistat
6
+ end
7
+
8
+ class TestTermistat < MiniTest::Unit::TestCase
9
+ def setup
10
+ Termistat.config = nil
11
+ @included = Included.new
12
+ end
13
+
14
+ def test_status_bar_method
15
+ assert @included.respond_to?(:status_bar)
16
+ end
17
+
18
+ def test_ncurses_initialization
19
+ # @included.status_bar "hello"
20
+ end
21
+
22
+ def test_default_configuration
23
+ assert_equal :top_right, Termistat.config.position
24
+ end
25
+
26
+ def test_configuration
27
+ Termistat.config do
28
+ position :top_left
29
+ align :center
30
+ end
31
+
32
+ assert_equal :top_left, Termistat.config.position
33
+ end
34
+
35
+ def test_text_alignment
36
+ assert_equal "foo ", Termistat.formatted_message("foo", :left, 10)
37
+ assert_equal " foo", Termistat.formatted_message("foo", :right, 10)
38
+ assert_equal " foo ", Termistat.formatted_message("foo", :center, 10)
39
+ end
40
+
41
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: termistat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Solomon White
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-24 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi-ncurses
16
+ requirement: &70307433171700 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70307433171700
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70307433171180 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70307433171180
36
+ description: Display status bar overlay for summary information
37
+ email:
38
+ - rubysolo@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - Gemfile
45
+ - Gemfile.lock
46
+ - LICENSE
47
+ - README.md
48
+ - Rakefile
49
+ - examples/file_copy.png
50
+ - examples/file_copy.rb
51
+ - lib/termistat.rb
52
+ - lib/termistat/config.rb
53
+ - lib/termistat/tee_io.rb
54
+ - lib/termistat/version.rb
55
+ - termistat.gemspec
56
+ - test/helper.rb
57
+ - test/units/test_config.rb
58
+ - test/units/test_tee_io.rb
59
+ - test/units/test_termistat.rb
60
+ homepage: https://github.com/rubysolo/termistat
61
+ licenses: []
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project: termistat
80
+ rubygems_version: 1.8.6
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Terminal status bar
84
+ test_files:
85
+ - test/helper.rb
86
+ - test/units/test_config.rb
87
+ - test/units/test_tee_io.rb
88
+ - test/units/test_termistat.rb