tty 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/README.md +78 -12
  2. data/benchmarks/shell.rb +26 -0
  3. data/benchmarks/table.rb +35 -0
  4. data/lib/tty.rb +23 -1
  5. data/lib/tty/coercer.rb +13 -0
  6. data/lib/tty/coercer/boolean.rb +39 -0
  7. data/lib/tty/coercer/float.rb +23 -0
  8. data/lib/tty/coercer/integer.rb +23 -0
  9. data/lib/tty/coercer/range.rb +33 -0
  10. data/lib/tty/shell.rb +6 -2
  11. data/lib/tty/shell/question.rb +158 -138
  12. data/lib/tty/shell/reader.rb +92 -0
  13. data/lib/tty/shell/response.rb +219 -0
  14. data/lib/tty/shell/response_delegation.rb +53 -0
  15. data/lib/tty/table.rb +90 -16
  16. data/lib/tty/table/border.rb +34 -8
  17. data/lib/tty/table/border/ascii.rb +16 -25
  18. data/lib/tty/table/border/null.rb +0 -6
  19. data/lib/tty/table/border/unicode.rb +16 -25
  20. data/lib/tty/table/column_set.rb +1 -1
  21. data/lib/tty/table/error.rb +10 -0
  22. data/lib/tty/table/operation/wrapped.rb +0 -6
  23. data/lib/tty/table/orientation.rb +57 -0
  24. data/lib/tty/table/orientation/horizontal.rb +19 -0
  25. data/lib/tty/table/orientation/vertical.rb +19 -0
  26. data/lib/tty/table/renderer.rb +7 -0
  27. data/lib/tty/table/renderer/ascii.rb +1 -1
  28. data/lib/tty/table/renderer/basic.rb +2 -2
  29. data/lib/tty/table/renderer/unicode.rb +1 -1
  30. data/lib/tty/table/validatable.rb +20 -0
  31. data/lib/tty/terminal.rb +15 -14
  32. data/lib/tty/terminal/color.rb +1 -1
  33. data/lib/tty/terminal/echo.rb +41 -0
  34. data/lib/tty/terminal/home.rb +31 -0
  35. data/lib/tty/text.rb +85 -0
  36. data/lib/tty/text/truncation.rb +83 -0
  37. data/lib/tty/text/wrapping.rb +96 -0
  38. data/lib/tty/version.rb +1 -1
  39. data/spec/tty/coercer/boolean/coerce_spec.rb +113 -0
  40. data/spec/tty/coercer/float/coerce_spec.rb +32 -0
  41. data/spec/tty/coercer/integer/coerce_spec.rb +39 -0
  42. data/spec/tty/coercer/range/coerce_spec.rb +73 -0
  43. data/spec/tty/shell/ask_spec.rb +14 -1
  44. data/spec/tty/shell/question/argument_spec.rb +30 -0
  45. data/spec/tty/shell/question/character_spec.rb +16 -0
  46. data/spec/tty/shell/question/default_spec.rb +25 -0
  47. data/spec/tty/shell/question/in_spec.rb +23 -0
  48. data/spec/tty/shell/question/initialize_spec.rb +11 -211
  49. data/spec/tty/shell/question/modifier/whitespace_spec.rb +1 -1
  50. data/spec/tty/shell/question/modify_spec.rb +44 -0
  51. data/spec/tty/shell/question/valid_spec.rb +46 -0
  52. data/spec/tty/shell/question/validate_spec.rb +30 -0
  53. data/spec/tty/shell/reader/getc_spec.rb +40 -0
  54. data/spec/tty/shell/response/read_bool_spec.rb +41 -0
  55. data/spec/tty/shell/response/read_char_spec.rb +17 -0
  56. data/spec/tty/shell/response/read_date_spec.rb +20 -0
  57. data/spec/tty/shell/response/read_email_spec.rb +43 -0
  58. data/spec/tty/shell/response/read_multiple_spec.rb +24 -0
  59. data/spec/tty/shell/response/read_number_spec.rb +29 -0
  60. data/spec/tty/shell/response/read_range_spec.rb +29 -0
  61. data/spec/tty/shell/response/read_spec.rb +68 -0
  62. data/spec/tty/shell/response/read_string_spec.rb +19 -0
  63. data/spec/tty/table/access_spec.rb +6 -0
  64. data/spec/tty/table/border/new_spec.rb +3 -3
  65. data/spec/tty/table/initialize_spec.rb +17 -1
  66. data/spec/tty/table/options_spec.rb +7 -1
  67. data/spec/tty/table/orientation_spec.rb +98 -0
  68. data/spec/tty/table/renders_with_spec.rb +76 -0
  69. data/spec/tty/table/rotate_spec.rb +72 -0
  70. data/spec/tty/table/to_s_spec.rb +13 -1
  71. data/spec/tty/table/validatable/validate_options_spec.rb +34 -0
  72. data/spec/tty/terminal/color/remove_spec.rb +34 -1
  73. data/spec/tty/terminal/echo_spec.rb +22 -0
  74. data/spec/tty/text/truncate_spec.rb +13 -0
  75. data/spec/tty/text/truncation/initialize_spec.rb +29 -0
  76. data/spec/tty/text/truncation/truncate_spec.rb +73 -0
  77. data/spec/tty/text/wrap_spec.rb +14 -0
  78. data/spec/tty/text/wrapping/initialize_spec.rb +25 -0
  79. data/spec/tty/text/wrapping/wrap_spec.rb +80 -0
  80. data/tty.gemspec +1 -0
  81. metadata +101 -8
@@ -7,8 +7,6 @@ module TTY
7
7
  class Border
8
8
  include Unicode
9
9
 
10
- NEWLINE = "\n"
11
-
12
10
  # The row cell widths
13
11
  #
14
12
  # @api private
@@ -21,26 +19,53 @@ module TTY
21
19
  attr_reader :row
22
20
  private :row
23
21
 
22
+ class << self
23
+ # Store characters for border
24
+ #
25
+ # @api private
26
+ attr_accessor :characters
27
+ end
28
+
24
29
  # Instantiate a new object
25
30
  #
26
31
  # @return [Object]
27
32
  #
28
33
  # @api private
29
- def initialize(*)
34
+ def initialize(row=nil)
30
35
  if self.class == Border
31
36
  raise NotImplementedError, "#{self} is an abstract class"
32
37
  else
33
- super
38
+ @row = row
39
+ @widths = row.map { |cell| cell.chars.to_a.size }
34
40
  end
35
41
  end
36
42
 
43
+ # Define border characters
44
+ #
45
+ # @api public
46
+ def self.def_border(&block)
47
+ @characters = block
48
+ end
49
+
50
+ # Retrive individula character by type
51
+ #
52
+ # @param [String] type
53
+ # the character type
54
+ #
55
+ # @return [String]
56
+ #
57
+ # @api private
58
+ def [](type)
59
+ self.class.characters.call[type] || ''
60
+ end
61
+
37
62
  # A line spanning all columns marking top of a table.
38
63
  #
39
64
  # @return [String]
40
65
  #
41
66
  # @api private
42
67
  def top_line
43
- render :top
68
+ (result = render(:top)).empty? ? nil : result
44
69
  end
45
70
 
46
71
  # A line spanning all columns delemeting rows in a table.
@@ -49,7 +74,7 @@ module TTY
49
74
  #
50
75
  # @api private
51
76
  def separator
52
- render :mid
77
+ (result = render(:mid)).empty? ? nil : result
53
78
  end
54
79
 
55
80
  # A line spanning all columns delemeting cells in a row.
@@ -58,7 +83,8 @@ module TTY
58
83
  #
59
84
  # @api private
60
85
  def row_line
61
- self['left'] + row.join(self['right']) + self['right']
86
+ result = self['left'] + row.join(self['right']) + self['right']
87
+ result.empty? ? nil : result
62
88
  end
63
89
 
64
90
  # A line spannig all columns marking bottom of a table.
@@ -67,7 +93,7 @@ module TTY
67
93
  #
68
94
  # @api private
69
95
  def bottom_line
70
- render :bottom
96
+ (result = render(:bottom)).empty? ? nil : result
71
97
  end
72
98
 
73
99
  protected
@@ -7,32 +7,23 @@ module TTY
7
7
  # A class that represents an ascii border.
8
8
  class ASCII < Border
9
9
 
10
- BORDER_TYPE = {
11
- 'top' => '-',
12
- 'top_mid' => '+',
13
- 'top_left' => '+',
14
- 'top_right' => '+',
15
- 'bottom' => '-',
16
- 'bottom_mid' => '+',
17
- 'bottom_left' => '+',
18
- 'bottom_right' => '+',
19
- 'mid' => '-',
20
- 'mid_mid' => '+',
21
- 'mid_left' => '+',
22
- 'mid_right' => '+',
23
- 'left' => '|',
24
- 'right' => '|'
10
+ def_border do
11
+ {
12
+ 'top' => '-',
13
+ 'top_mid' => '+',
14
+ 'top_left' => '+',
15
+ 'top_right' => '+',
16
+ 'bottom' => '-',
17
+ 'bottom_mid' => '+',
18
+ 'bottom_left' => '+',
19
+ 'bottom_right' => '+',
20
+ 'mid' => '-',
21
+ 'mid_mid' => '+',
22
+ 'mid_left' => '+',
23
+ 'mid_right' => '+',
24
+ 'left' => '|',
25
+ 'right' => '|'
25
26
  }
26
-
27
- # @api private
28
- def [](type)
29
- BORDER_TYPE[type]
30
- end
31
-
32
- # @api private
33
- def initialize(row)
34
- @row = row
35
- @widths = row.map { |cell| cell.chars.to_a.size }
36
27
  end
37
28
 
38
29
  end # ASCII
@@ -7,12 +7,6 @@ module TTY
7
7
  # A class that represents no border.
8
8
  class Null < Border
9
9
 
10
- # @api private
11
- def initialize(row)
12
- @row = row
13
- @widths = row.map { |cell| cell.chars.to_a.size }
14
- end
15
-
16
10
  # A stub top line
17
11
  #
18
12
  # @api private
@@ -7,32 +7,23 @@ module TTY
7
7
  # A class that represents a unicode border.
8
8
  class Unicode < Border
9
9
 
10
- BORDER_TYPE = {
11
- 'top' => '─',
12
- 'top_mid' => '',
13
- 'top_left' => '',
14
- 'top_right' => '',
15
- 'bottom' => '',
16
- 'bottom_mid' => '',
17
- 'bottom_left' => '',
18
- 'bottom_right' => '',
19
- 'mid' => '',
20
- 'mid_mid' => '',
21
- 'mid_left' => '',
22
- 'mid_right' => '',
23
- 'left' => '',
24
- 'right' => '│'
10
+ def_border do
11
+ {
12
+ 'top' => '',
13
+ 'top_mid' => '',
14
+ 'top_left' => '',
15
+ 'top_right' => '',
16
+ 'bottom' => '',
17
+ 'bottom_mid' => '',
18
+ 'bottom_left' => '',
19
+ 'bottom_right' => '',
20
+ 'mid' => '',
21
+ 'mid_mid' => '',
22
+ 'mid_left' => '',
23
+ 'mid_right' => '',
24
+ 'left' => '│',
25
+ 'right' => '│'
25
26
  }
26
-
27
- # @api private
28
- def [](type)
29
- BORDER_TYPE[type]
30
- end
31
-
32
- # @api private
33
- def initialize(row)
34
- @row = row
35
- @widths = row.map { |cell| cell.chars.to_a.size }
36
27
  end
37
28
 
38
29
  end # Unicode
@@ -31,7 +31,7 @@ module TTY
31
31
  #
32
32
  # @api private
33
33
  def extract_widths!
34
- return column_widths unless column_widths.empty?
34
+ return column_widths if (column_widths && !column_widths.empty?)
35
35
 
36
36
  rows = table.to_a
37
37
  data = table.header ? rows + [table.header] : rows
@@ -6,5 +6,15 @@ module TTY
6
6
  # Raised when inserting into table with a mismatching row(s)
7
7
  class DimensionMismatchError < ArgumentError; end
8
8
 
9
+ # Raised when reading non-existent element from a table
10
+ class TupleMissing < IndexError
11
+ attr_reader :i, :j
12
+
13
+ def initialize(i, j)
14
+ @i, @j = i, j
15
+ super("element at(#{i},#{j}) not found")
16
+ end
17
+ end
18
+
9
19
  end # Table
10
20
  end # TTY
@@ -27,12 +27,6 @@ module TTY
27
27
  end
28
28
  end
29
29
 
30
- private
31
-
32
- def actual_length(string)
33
- string.to_s.gsub(/\e\[\d{1,2}m/, '').length
34
- end
35
-
36
30
  end # Wrapped
37
31
  end # Operation
38
32
  end # Table
@@ -0,0 +1,57 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # A class representing table orientation
7
+ class Orientation
8
+
9
+ # The name for the orientation
10
+ #
11
+ # @api public
12
+ attr_reader :name
13
+
14
+ # Initialize an Orientation
15
+ #
16
+ # @api public
17
+ def initialize(name)
18
+ @name = name
19
+ end
20
+
21
+ # Coerce the name argument into an orientation
22
+ #
23
+ # @param [Symbol] name
24
+ #
25
+ # @api public
26
+ def self.coerce(name)
27
+ case name.to_s
28
+ when /h|horiz(ontal)?/i
29
+ Horizontal.new :horizontal
30
+ when /v|ert(ical)?/i
31
+ Vertical.new :vertical
32
+ else
33
+ raise InvalidOrientationError, "orientation must be one of :horizontal, :vertical"
34
+ end
35
+ end
36
+
37
+ # Check if orientation is vertical
38
+ #
39
+ # @return [Boolean]
40
+ #
41
+ # @api public
42
+ def vertical?
43
+ name == :vertical
44
+ end
45
+
46
+ # Check if orientation is horizontal
47
+ #
48
+ # @return [Boolean]
49
+ #
50
+ # @api public
51
+ def horizontal?
52
+ name == :horizontal
53
+ end
54
+
55
+ end # Orientation
56
+ end # Table
57
+ end # TTY
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # A class representing table orientation
7
+ class Orientation
8
+
9
+ class Horizontal < Orientation
10
+
11
+ def transform(table)
12
+ table.rotate_horizontal
13
+ end
14
+
15
+ end # Horizontal
16
+
17
+ end # Orientation
18
+ end # Table
19
+ end # TTY
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # A class representing table orientation
7
+ class Orientation
8
+
9
+ class Vertical < Orientation
10
+
11
+ def transform(table)
12
+ table.rotate_vertical
13
+ end
14
+
15
+ end # Vertical
16
+
17
+ end # Orientation
18
+ end # Table
19
+ end # TTY
@@ -80,6 +80,13 @@ module TTY
80
80
  @renderer = renderer
81
81
  end
82
82
 
83
+ # Add custom border for the renderer
84
+ #
85
+ # @api public
86
+ def renders_with(klass)
87
+ @border_class = klass
88
+ end
89
+
83
90
  delegatable_method :renderer, *RENDERER_DELEGATED_METHODS
84
91
 
85
92
  end # Renderer
@@ -5,7 +5,7 @@ module TTY
5
5
  module Renderer
6
6
  class ASCII < Basic
7
7
 
8
- def render(table, border=nil)
8
+ def render(table)
9
9
  super table, TTY::Table::Border::ASCII
10
10
  end
11
11
 
@@ -87,7 +87,7 @@ module TTY
87
87
  # @api public
88
88
  def render(table, border_class=Border::Null)
89
89
  @table = table
90
- @border_class = border_class
90
+ @border_class = table.border_class || border_class
91
91
 
92
92
  return if table.to_a.empty?
93
93
  # setup(options)
@@ -113,7 +113,7 @@ module TTY
113
113
  # @api private
114
114
  def render_header
115
115
  header = table.header
116
- if header
116
+ if header && !header.empty?
117
117
  aligned = alignments.align_header header,
118
118
  :column_widths => column_widths
119
119
  border = border_class.new(aligned)
@@ -5,7 +5,7 @@ module TTY
5
5
  module Renderer
6
6
  class Unicode < Basic
7
7
 
8
- def render(table, border=nil)
8
+ def render(table)
9
9
  super table, TTY::Table::Border::Unicode
10
10
  end
11
11
 
@@ -29,6 +29,26 @@ module TTY
29
29
  def assert_string_values(rows)
30
30
  end
31
31
 
32
+ # Check if options are of required type
33
+ #
34
+ # @api private
35
+ def validate_options!(options)
36
+ if (header = options[:header]) &&
37
+ (!header.kind_of?(Array) || header.empty?)
38
+ raise InvalidArgument, ":header must be a non-empty array"
39
+ end
40
+
41
+ if (rows = options[:rows]) &&
42
+ !(rows.kind_of?(Array) || rows.kind_of?(Hash))
43
+ raise InvalidArgument, ":rows must be a non-empty array or hash"
44
+ end
45
+
46
+ if (column_widths = options[:column_widths]) &&
47
+ (!column_widths.kind_of?(Array) || column_widths.empty?)
48
+ raise InvalidArgument, ":column_widths must be a non-empty array"
49
+ end
50
+ end
51
+
32
52
  end # Validatable
33
53
  end # Table
34
54
  end # TTY
@@ -28,6 +28,7 @@ module TTY
28
28
 
29
29
  def initialize
30
30
  @color = TTY::Terminal::Color.new(self.color?)
31
+ @echo = TTY::Terminal::Echo.new
31
32
  @default_width = 80
32
33
  @default_height = 24
33
34
  end
@@ -148,26 +149,26 @@ module TTY
148
149
  %x{tput colors 2>/dev/null}.to_i > 2
149
150
  end
150
151
 
152
+ def echo_on
153
+ @echo.on
154
+ end
155
+
156
+ def echo_off
157
+ @echo.off
158
+ end
159
+
160
+ # @api public
161
+ def echo(is_on=true, &block)
162
+ @echo.echo(is_on, &block)
163
+ end
164
+
151
165
  # Find user home directory
152
166
  #
153
167
  # @return [String]
154
168
  #
155
169
  # @api public
156
170
  def home
157
- @home ||= if (env_home = ENV['HOME'])
158
- env_home
159
- else
160
- begin
161
- require 'etc'
162
- File.expand_path("~#{Etc.getlogin}")
163
- rescue
164
- if TTY::System.windows?
165
- "C:/"
166
- else
167
- "/"
168
- end
169
- end
170
- end
171
+ @home ||= Home.new.home
171
172
  end
172
173
 
173
174
  end # Terminal