tty 0.0.1 → 0.0.2

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.
@@ -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