tty 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # Raised when inserting into table with a mismatching row(s)
7
+ class DimensionMismatchError < ArgumentError; end
8
+
9
+ end # Table
10
+ end # TTY
@@ -5,7 +5,7 @@ module TTY
5
5
 
6
6
  # @api public
7
7
  def self.renderer
8
- @renderer ||= if TTY::Color.color?
8
+ @renderer ||= if TTY.terminal.color?
9
9
  TTY::Table::Renderer::Color
10
10
  else
11
11
  TTY::Table::Renderer::Basic
@@ -23,11 +23,14 @@ module TTY
23
23
  #
24
24
  # @api public
25
25
  module Renderer
26
+ extend TTY::Delegatable
26
27
 
27
28
  autoload :Basic, 'tty/table/renderer/basic'
28
29
  autoload :Color, 'tty/table/renderer/color'
29
30
  autoload :Unicode, 'tty/table/renderer/unicode'
30
31
 
32
+ RENDERER_DELEGATED_METHODS = [ :render, :extract_column_widths, :total_width]
33
+
31
34
  RENDERER_MAPPER = {
32
35
  :basic => TTY::Table::Renderer::Basic,
33
36
  :color => TTY::Table::Renderer::Color,
@@ -43,13 +46,18 @@ module TTY
43
46
  end
44
47
 
45
48
  def pick_renderer(renderer)
46
- if renderer
47
- RENDERER_MAPPER[renderer].new
48
- else
49
- self.renderer
50
- end
49
+ if renderer
50
+ RENDERER_MAPPER[renderer].new
51
+ else
52
+ self.renderer
53
+ end
51
54
  end
52
55
 
56
+ # Return the default renderer
57
+ #
58
+ # @return [TTY::Table::Renderer]
59
+ #
60
+ # @api public
53
61
  def renderer
54
62
  @renderer ||= TTY::Table.renderer.new
55
63
  end
@@ -58,6 +66,8 @@ module TTY
58
66
  @renderer = renderer
59
67
  end
60
68
 
69
+ delegatable_method :renderer, *RENDERER_DELEGATED_METHODS
70
+
61
71
  end # Renderer
62
72
 
63
73
  end # Table
@@ -9,6 +9,10 @@ module TTY
9
9
 
10
10
  attr_reader :indent
11
11
 
12
+ attr_reader :column_widths
13
+
14
+ attr_reader :rows
15
+
12
16
  # @param [Hash] options
13
17
  # :indent - Indent the first column by indent value
14
18
  # :padding - Pad out the row cell by padding value
@@ -16,9 +20,10 @@ module TTY
16
20
  #
17
21
  # @return [Table::Renderer::Basic]
18
22
  def initialize(options={})
19
- @padding = options.fetch :padding, 0
23
+ @padding = 0
20
24
  @indent = options.fetch :indent, 0
21
- @col_widths = options.fetch :col_widths, []
25
+ @column_widths = []
26
+ @column_aligns = options.fetch :column_aligns, []
22
27
  end
23
28
 
24
29
  # Sets the output padding,
@@ -36,12 +41,6 @@ module TTY
36
41
  new(options).render(rows)
37
42
  end
38
43
 
39
- # @api private
40
- def extract_column_widths
41
- # TODO Calculate column widths if none provided
42
- # throw an error if too many columns as compared to terminal width
43
- end
44
-
45
44
  # Renders table
46
45
  #
47
46
  # @param [Enumerable] rows
@@ -50,24 +49,65 @@ module TTY
50
49
  # @return [String] string representation of table
51
50
  #
52
51
  # @api public
53
- def render(rows)
52
+ def render(rows, options={})
54
53
  return if rows.empty?
55
-
54
+ # TODO: Decide about table orientation
55
+ @rows = rows
56
56
  body = []
57
57
  unless rows.length.zero?
58
- rows.each do |row|
59
- line = ""
60
- row.each_with_index do |column, index|
61
- if index == row.size - 1
62
- line << "#{column.to_s}"
63
- else
64
- line << "#{column.to_s} "
65
- end
58
+ extract_column_widths(rows)
59
+ body += render_rows
60
+ end
61
+ body.join("\n")
62
+ end
63
+
64
+ # Calcualte total table width
65
+ #
66
+ # @return [Integer]
67
+ #
68
+ # @api public
69
+ def total_width
70
+ column_widths.reduce(:+)
71
+ end
72
+
73
+ # Calcualte maximum column widths
74
+ #
75
+ # @return [Array] column widths
76
+ #
77
+ # @api private
78
+ def extract_column_widths(rows)
79
+ # TODO: throw an error if too many columns as compared to terminal width
80
+ colcount = rows.max{ |a,b| a.size <=> b.size }.size
81
+ maximas = []
82
+ start = 0
83
+
84
+ start.upto(colcount - 1) do |index|
85
+ maximum = rows.map { |row|
86
+ row[index] ? (row[index].to_s.size) : 0
87
+ }.max
88
+ maximas << maximum
89
+ end
90
+ @column_widths = maximas
91
+ end
92
+
93
+ # Adjust the rows to maximum widths
94
+ #
95
+ # @return [Arrays[String]]
96
+ #
97
+ # @api private
98
+ def render_rows
99
+ rows.map do |row|
100
+ line = ""
101
+ row.each_with_index do |column, index|
102
+ column_width = column_widths[index]
103
+ if index == row.size - 1
104
+ line << "%-#{column_width}s" % column.to_s
105
+ else
106
+ line << "%-#{column_width}s " % column.to_s
66
107
  end
67
- body << line
68
108
  end
109
+ line
69
110
  end
70
- body.join("\n")
71
111
  end
72
112
 
73
113
  end # Basic
@@ -3,7 +3,7 @@
3
3
  module TTY
4
4
  class Table
5
5
  module Renderer
6
- class Color
6
+ class Color < Basic
7
7
 
8
8
  def initialize
9
9
 
@@ -3,7 +3,7 @@
3
3
  module TTY
4
4
  class Table
5
5
  module Renderer
6
- class Unicode
6
+ class Unicode < Basic
7
7
 
8
8
  def initialize
9
9
 
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+ module Validatable
6
+
7
+ MIN_CELL_WIDTH = 3.freeze
8
+
9
+ # Check if table rows are the equal size
10
+ #
11
+ # @raise [DimensionMismatchError]
12
+ # if the rows are not equal length
13
+ #
14
+ # @return [nil]
15
+ #
16
+ # @api private
17
+ def assert_row_sizes(rows)
18
+ size = (rows[0] || []).size
19
+ rows.each do |row|
20
+ if not row.size == size
21
+ raise TTY::Table::DimensionMismatchError, "row size differs (#{row.size} should be #{size})"
22
+ end
23
+ end
24
+ end
25
+
26
+ def assert_matching_widths(rows)
27
+ end
28
+
29
+ def assert_string_values(rows)
30
+ end
31
+
32
+ end # Validatable
33
+ end # Table
34
+ end # TTY
@@ -0,0 +1,149 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Terminal
5
+
6
+ @@default_width = 80
7
+
8
+ @@default_height = 24
9
+
10
+ # Return default width of terminal
11
+ #
12
+ # @example
13
+ # default_width = TTY::Terminal.default_width
14
+ #
15
+ # @return [Integer]
16
+ #
17
+ # @api public
18
+ def default_width
19
+ @@default_width
20
+ end
21
+
22
+ # Set default width of terminal
23
+ #
24
+ # @param [Integer] width
25
+ #
26
+ # @return [Integer]
27
+ #
28
+ # @api public
29
+ def default_width=(width)
30
+ @@default_width = width
31
+ end
32
+
33
+ # Return default height of terminal
34
+ #
35
+ # @example
36
+ # default_height = TTY::Terminal.default_height
37
+ #
38
+ # @return [Integer]
39
+ #
40
+ # @api public
41
+ def default_height
42
+ @@default_height
43
+ end
44
+
45
+ # Set default height of terminal
46
+ #
47
+ # @example
48
+ # default_height = TTY::Terminal.default_height
49
+ #
50
+ # @return [Integer]
51
+ #
52
+ # @api public
53
+ def default_height=(height)
54
+ @@default_height = height
55
+ end
56
+
57
+ # Determine current width
58
+ #
59
+ # @return [Integer] width
60
+ #
61
+ # @api width
62
+ def width
63
+ if ENV['TTY_COLUMNS'] =~ /^\d+$/
64
+ result = ENV['TTY_COLUMNS'].to_i
65
+ else
66
+ result = TTY::System.unix? ? dynamic_width : default_width
67
+ end
68
+ rescue
69
+ default_width
70
+ end
71
+
72
+ # Determine current height
73
+ #
74
+ # @api public
75
+ def height
76
+ if ENV['TTY_LINES'] =~ /^\d+$/
77
+ result = ENV['TTY_LINES'].to_i
78
+ else
79
+ result = TTY::System.unix? ? dynamic_height : self.default_height
80
+ end
81
+ rescue
82
+ self.default_height
83
+ end
84
+
85
+ # Calculate dynamic width of the terminal
86
+ #
87
+ # @return [Integer] width
88
+ #
89
+ # @api public
90
+ def dynamic_width
91
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
92
+ end
93
+
94
+ # Calculate dynamic height of the terminal
95
+ #
96
+ # @return [Integer] height
97
+ #
98
+ # @api public
99
+ def dynamic_height
100
+ @dynamic_height ||= (dynamic_height_stty.nonzero? || dynamic_height_tput)
101
+ end
102
+
103
+ # Detect terminal width with stty utility
104
+ #
105
+ # @return [Integer] width
106
+ #
107
+ # @api public
108
+ def dynamic_width_stty
109
+ %x{stty size 2>/dev/null}.split[1].to_i
110
+ end
111
+
112
+ # Detect terminal height with stty utility
113
+ #
114
+ # @return [Integer] height
115
+ #
116
+ # @api public
117
+ def dynamic_height_stty
118
+ %x{stty size 2>/dev/null}.split[0].to_i
119
+ end
120
+
121
+ # Detect terminal width with tput utility
122
+ #
123
+ # @return [Integer] width
124
+ #
125
+ # @api public
126
+ def dynamic_width_tput
127
+ %x{tput cols 2>/dev/null}.to_i
128
+ end
129
+
130
+ # Detect terminal height with tput utility
131
+ #
132
+ # @return [Integer] height
133
+ #
134
+ # @api public
135
+ def dynamic_height_tput
136
+ %x{tput lines 2>/dev/null}.to_i
137
+ end
138
+
139
+ # Check if terminal supports color
140
+ #
141
+ # @return [Boolean]
142
+ #
143
+ # @api public
144
+ def color?
145
+ %x{tput colors 2>/dev/null}.to_i > 2
146
+ end
147
+
148
+ end # Terminal
149
+ end # TTY
data/lib/tty/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  module TTY
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+
5
+ describe TTY::Conversion do
6
+ let(:described_class) { Class.new { include TTY::Conversion } }
7
+ let(:object) { described_class.new }
8
+ let(:enumerable) { [] }
9
+
10
+ subject { object.convert_to_array(enumerable) }
11
+
12
+ context 'Array type' do
13
+ it { should == enumerable }
14
+ end
15
+
16
+ context 'Hash type' do
17
+ let(:enumerable) { {:a => 1, :b => 2} }
18
+
19
+ it { should include([:a, 1]) }
20
+
21
+ it { should include([:b, 2]) }
22
+ end
23
+
24
+ context 'responds to #to_ary' do
25
+ let(:converted) { [] }
26
+ let(:enumerable) { mock('Enumerable', :to_ary => converted) }
27
+
28
+ it { should == converted }
29
+ end
30
+
31
+ context 'does not respond to #to_ary' do
32
+ let(:enumerable) { mock('Enumerable') }
33
+
34
+ it 'raises error' do
35
+ expect { subject}.to raise_error(TTY::TypeError)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require File.expand_path('../fixtures/classes', __FILE__)
5
+
6
+ describe TTY::Delegatable do
7
+ let(:target) { :test }
8
+ let(:methods) { [:output] }
9
+ let(:object) { Class.new(DelegetableSpec::Object)}
10
+ let(:delegatable) { object.new }
11
+
12
+ subject { object.delegatable_method target, *methods }
13
+
14
+ it 'creates a method #output' do
15
+ expect { subject }.to change { delegatable.respond_to?(:output) }.
16
+ from(false).
17
+ to(true)
18
+ end
19
+
20
+ it 'delegates #output to #test' do
21
+ subject
22
+ value = mock('value')
23
+ delegatable.should_receive(:output).and_return(value)
24
+ delegatable.output.should == value
25
+ end
26
+ end