hirber 0.8.4 → 0.8.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gemspec +3 -2
- data/CHANGELOG.rdoc +6 -0
- data/lib/hirb/helpers/table.rb +349 -325
- data/lib/hirb/helpers/unicode_table.rb +29 -6
- data/lib/hirb/version.rb +1 -1
- data/lib/hirb/view.rb +4 -2
- data/spec/spec_helper.rb +2 -0
- data/spec/view_spec.rb +13 -1
- metadata +22 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1ee5c1edfe858985de14a9f502d2676a4cc7fdd6ee3dd3665b1c2640fc669ef
|
4
|
+
data.tar.gz: 151dc6d1a877c5660dce189f215260d074fed5a6f8a11160967180a18d0f51b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 894680dbe92e74acaf64dba6c9c9d65160e782840c5bd3645dad991c014a659f462960194cbccabb73b72d32bba2dd4fd90cf2b314b47b34bb4f1d68620c1fb3
|
7
|
+
data.tar.gz: 31179b6027cf78c5e8debf04e199aa797154bbac401e5df9dbef4cd557abea0de9a62c40044dde3718dea9a94f287f4edee8e098734763282ab0bf96f1c12f76
|
data/.gemspec
CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "hirber"
|
7
7
|
s.version = Hirb::VERSION
|
8
8
|
|
9
|
-
s.authors = ["Gabriel Horner", "Aleksander Długopolski"]
|
10
|
-
s.email = "
|
9
|
+
s.authors = ["Gabriel Horner", "Aleksander Długopolski", "Marz Drel"]
|
10
|
+
s.email = "marzdrel@dotpro.org"
|
11
11
|
s.homepage = "http://tagaholic.me/hirb/"
|
12
12
|
|
13
13
|
s.summary = <<~TXT.gsub(/[[:space:]]+/, " ").strip
|
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
|
|
32
32
|
s.required_rubygems_version = ">= 1.3.5"
|
33
33
|
|
34
34
|
s.add_development_dependency "rake"
|
35
|
+
s.add_development_dependency "pry"
|
35
36
|
s.add_development_dependency "rspec", ">= 3.9"
|
36
37
|
s.add_development_dependency "bacon", "~> 1.1"
|
37
38
|
s.add_development_dependency "mocha", "~> 0.12.1"
|
data/CHANGELOG.rdoc
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
+
== 0.8.5
|
2
|
+
* Fix issues with loading Hirber when using Pry
|
3
|
+
|
1
4
|
== 0.8.4
|
2
5
|
* Fix version number issues
|
3
6
|
|
4
7
|
== 0.8.3
|
5
8
|
* Add autoloading support for pry-rails
|
6
9
|
|
10
|
+
== 0.8.2
|
11
|
+
* Rename entry to hirber.rb to make loading easier.
|
12
|
+
|
7
13
|
== 0.8.1
|
8
14
|
* Add compatiblity with Ruby 3.0.0
|
9
15
|
|
data/lib/hirb/helpers/table.rb
CHANGED
@@ -3,374 +3,398 @@ require 'hirb/helpers/table/filters'
|
|
3
3
|
require 'hirb/helpers/table/resizer'
|
4
4
|
|
5
5
|
module Hirb
|
6
|
-
# Base Table class from which other table classes inherit.
|
7
|
-
# By default, a table is constrained to a default width but this can be adjusted
|
8
|
-
# via the max_width option or Hirb::View.width.
|
9
|
-
# Rows can be an array of arrays or an array of hashes.
|
10
|
-
#
|
11
|
-
# An array of arrays ie [[1,2], [2,3]], would render:
|
12
|
-
# +---+---+
|
13
|
-
# | 0 | 1 |
|
14
|
-
# +---+---+
|
15
|
-
# | 1 | 2 |
|
16
|
-
# | 2 | 3 |
|
17
|
-
# +---+---+
|
18
|
-
#
|
19
|
-
# By default, the fields/columns are the numerical indices of the array.
|
20
|
-
#
|
21
|
-
# An array of hashes ie [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], would render:
|
22
|
-
# +-----+--------+
|
23
|
-
# | age | weight |
|
24
|
-
# +-----+--------+
|
25
|
-
# | 10 | 100 |
|
26
|
-
# | 80 | 500 |
|
27
|
-
# +-----+--------+
|
28
|
-
#
|
29
|
-
# By default, the fields/columns are the keys of the first hash.
|
30
|
-
#
|
31
|
-
# === Custom Callbacks
|
32
|
-
# Callback methods can be defined to add your own options that modify rows right before they are rendered.
|
33
|
-
# Here's an example that allows for searching with a :query option:
|
34
|
-
# module Query
|
35
|
-
# # Searches fields given a query hash
|
36
|
-
# def query_callback(rows, options)
|
37
|
-
# return rows unless options[:query]
|
38
|
-
# options[:query].map {|field,query|
|
39
|
-
# rows.select {|e| e[field].to_s =~ /#{query}/i }
|
40
|
-
# }.flatten.uniq
|
41
|
-
# end
|
42
|
-
# end
|
43
|
-
# Hirb::Helpers::Table.send :include, Query
|
44
|
-
#
|
45
|
-
# >> puts Hirb::Helpers::Table.render [{:name=>'batman'}, {:name=>'robin'}], :query=>{:name=>'rob'}
|
46
|
-
# +-------+
|
47
|
-
# | name |
|
48
|
-
# +-------+
|
49
|
-
# | robin |
|
50
|
-
# +-------+
|
51
|
-
# 1 row in set
|
52
|
-
#
|
53
|
-
# Callback methods:
|
54
|
-
# * must be defined in Helpers::Table and end in '_callback'.
|
55
|
-
# * should expect rows and a hash of render options. Rows will be an array of hashes.
|
56
|
-
# * are expected to return an array of hashes.
|
57
|
-
# * are invoked in alphabetical order.
|
58
|
-
# For a thorough example, see {Boson::Pipe}[http://github.com/cldwalker/boson/blob/master/lib/boson/pipe.rb].
|
59
|
-
#--
|
60
|
-
# derived from http://gist.github.com/72234
|
61
|
-
class Helpers::Table
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
:
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
6
|
+
# Base Table class from which other table classes inherit.
|
7
|
+
# By default, a table is constrained to a default width but this can be adjusted
|
8
|
+
# via the max_width option or Hirb::View.width.
|
9
|
+
# Rows can be an array of arrays or an array of hashes.
|
10
|
+
#
|
11
|
+
# An array of arrays ie [[1,2], [2,3]], would render:
|
12
|
+
# +---+---+
|
13
|
+
# | 0 | 1 |
|
14
|
+
# +---+---+
|
15
|
+
# | 1 | 2 |
|
16
|
+
# | 2 | 3 |
|
17
|
+
# +---+---+
|
18
|
+
#
|
19
|
+
# By default, the fields/columns are the numerical indices of the array.
|
20
|
+
#
|
21
|
+
# An array of hashes ie [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], would render:
|
22
|
+
# +-----+--------+
|
23
|
+
# | age | weight |
|
24
|
+
# +-----+--------+
|
25
|
+
# | 10 | 100 |
|
26
|
+
# | 80 | 500 |
|
27
|
+
# +-----+--------+
|
28
|
+
#
|
29
|
+
# By default, the fields/columns are the keys of the first hash.
|
30
|
+
#
|
31
|
+
# === Custom Callbacks
|
32
|
+
# Callback methods can be defined to add your own options that modify rows right before they are rendered.
|
33
|
+
# Here's an example that allows for searching with a :query option:
|
34
|
+
# module Query
|
35
|
+
# # Searches fields given a query hash
|
36
|
+
# def query_callback(rows, options)
|
37
|
+
# return rows unless options[:query]
|
38
|
+
# options[:query].map {|field,query|
|
39
|
+
# rows.select {|e| e[field].to_s =~ /#{query}/i }
|
40
|
+
# }.flatten.uniq
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# Hirb::Helpers::Table.send :include, Query
|
44
|
+
#
|
45
|
+
# >> puts Hirb::Helpers::Table.render [{:name=>'batman'}, {:name=>'robin'}], :query=>{:name=>'rob'}
|
46
|
+
# +-------+
|
47
|
+
# | name |
|
48
|
+
# +-------+
|
49
|
+
# | robin |
|
50
|
+
# +-------+
|
51
|
+
# 1 row in set
|
52
|
+
#
|
53
|
+
# Callback methods:
|
54
|
+
# * must be defined in Helpers::Table and end in '_callback'.
|
55
|
+
# * should expect rows and a hash of render options. Rows will be an array of hashes.
|
56
|
+
# * are expected to return an array of hashes.
|
57
|
+
# * are invoked in alphabetical order.
|
58
|
+
# For a thorough example, see {Boson::Pipe}[http://github.com/cldwalker/boson/blob/master/lib/boson/pipe.rb].
|
59
|
+
#--
|
60
|
+
# derived from http://gist.github.com/72234
|
61
|
+
class Helpers::Table
|
62
|
+
BORDER_LENGTH = 3 # " | " and "-+-" are the borders
|
63
|
+
MIN_FIELD_LENGTH = 3
|
64
|
+
|
65
|
+
class TooManyFieldsForWidthError < StandardError; end
|
66
|
+
|
67
|
+
CHARS = {
|
68
|
+
:top => {
|
69
|
+
:left => '+',
|
70
|
+
:center => '+',
|
71
|
+
:right => '+',
|
72
|
+
:horizontal => '-',
|
73
|
+
:vertical => {
|
74
|
+
:outside => '|',
|
75
|
+
:inside => '|',
|
76
|
+
},
|
77
|
+
},
|
78
|
+
|
79
|
+
:middle => {
|
80
|
+
:left => '+',
|
81
|
+
:center => '+',
|
82
|
+
:right => '+',
|
83
|
+
:horizontal => '-',
|
84
|
+
},
|
85
|
+
|
86
|
+
:bottom => {
|
87
|
+
:left => '+',
|
88
|
+
:center => '+',
|
89
|
+
:right => '+',
|
90
|
+
:horizontal => '-',
|
91
|
+
:vertical => {
|
92
|
+
:outside => '|',
|
93
|
+
:inside => '|',
|
94
|
+
},
|
95
|
+
},
|
96
|
+
}.freeze
|
97
|
+
|
98
|
+
class << self
|
99
|
+
# Main method which returns a formatted table.
|
100
|
+
# ==== Options:
|
101
|
+
# [*:fields*] An array which overrides the default fields and can be used to indicate field order.
|
102
|
+
# [*:headers*] A hash of fields and their header names. Fields that aren't specified here default to their name.
|
103
|
+
# When set to false, headers are hidden. Can also be an array but only for array rows.
|
104
|
+
# [*:max_fields*] A hash of fields and their maximum allowed lengths. Maximum length can also be a percentage of the total width
|
105
|
+
# (decimal less than one). When a field exceeds it's maximum then it's
|
106
|
+
# truncated and has a ... appended to it. Fields that aren't specified have no maximum.
|
107
|
+
# [*:max_width*] The maximum allowed width of all fields put together including field borders. Only valid when :resize is true.
|
108
|
+
# Default is Hirb::View.width.
|
109
|
+
# [*:resize*] Resizes table to display all columns in allowed :max_width. Default is true. Setting this false will display the full
|
110
|
+
# length of each field.
|
111
|
+
# [*:number*] When set to true, numbers rows by adding a :hirb_number column as the first column. Default is false.
|
112
|
+
# [*:change_fields*] A hash to change old field names to new field names. This can also be an array of new names but only for array rows.
|
113
|
+
# This is useful when wanting to change auto-generated keys to more user-friendly names i.e. for array rows.
|
114
|
+
# [*:grep_fields*] A regexp that selects which fields to display. By default this is not set and applied.
|
115
|
+
# [*:filters*] A hash of fields and their filters, applied to every row in a field. A filter can be a proc, an instance method
|
116
|
+
# applied to the field value or a Filters method. Also see the filter_classes attribute below.
|
117
|
+
# [*:header_filter*] A filter, like one in :filters, that is applied to all headers after the :headers option.
|
118
|
+
# [*:filter_any*] When set to true, any cell defaults to being filtered by its class in :filter_classes.
|
119
|
+
# Default Hirb::Helpers::Table.filter_any().
|
120
|
+
# [*:filter_classes*] Hash which maps classes to filters. Default is Hirb::Helpers::Table.filter_classes().
|
121
|
+
# [*:all_fields*] When set to true, renders fields in all rows. Valid only in rows that are hashes. Default is false.
|
122
|
+
# [*:description*] When set to true, renders row count description at bottom. Default is true.
|
123
|
+
# [*:escape_special_chars*] When set to true, escapes special characters \n,\t,\r so they don't disrupt tables. Default is false for
|
124
|
+
# vertical tables and true for anything else.
|
125
|
+
# [*:vertical*] When set to true, renders a vertical table using Hirb::Helpers::VerticalTable. Default is false.
|
126
|
+
# [*:unicode*] When set to true, renders a unicode table using Hirb::Helpers::UnicodeTable. Default is false.
|
127
|
+
# [*:tab*] When set to true, renders a tab-delimited table using Hirb::Helpers::TabTable. Default is false.
|
128
|
+
# [*:style*] Choose style of table: :simple, :vertical, :unicode, :tab or :markdown. :simple
|
129
|
+
# just uses the default render. Other values map to a capitalized namespace in format
|
130
|
+
# Hirb::Helpers::OptionValTable.
|
131
|
+
#
|
132
|
+
# Examples:
|
133
|
+
# Hirb::Helpers::Table.render [[1,2], [2,3]]
|
134
|
+
# Hirb::Helpers::Table.render [[1,2], [2,3]], :max_fields=>{0=>10}, :header_filter=>:capitalize
|
135
|
+
# Hirb::Helpers::Table.render [['a',1], ['b',2]], :change_fields=>%w{letters numbers}, :max_fields=>{'numbers'=>0.4}
|
136
|
+
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}]
|
137
|
+
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :headers=>{:weight=>"Weight(lbs)"}
|
138
|
+
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :filters=>{:age=>[:to_f]}
|
139
|
+
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :style=> :simple}
|
140
|
+
def render(rows, options={})
|
141
|
+
choose_style(rows, options)
|
142
|
+
rescue TooManyFieldsForWidthError
|
143
|
+
$stderr.puts "", "** Hirb Warning: Too many fields for the current width. Configure your width " +
|
144
|
+
"and/or fields to avoid this error. Defaulting to a vertical table. **"
|
128
145
|
Helpers::VerticalTable.render(rows, options)
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
146
|
+
end
|
147
|
+
|
148
|
+
def choose_style(rows, options)
|
149
|
+
case options[:style]
|
150
|
+
when :vertical
|
151
|
+
Helpers::VerticalTable.render(rows, options)
|
152
|
+
when :unicode
|
153
|
+
Helpers::UnicodeTable.render(rows, options)
|
154
|
+
when :tab
|
155
|
+
Helpers::TabTable.render(rows, options)
|
156
|
+
when :markdown
|
157
|
+
Helpers::MarkdownTable.render(rows, options)
|
158
|
+
when :simple
|
142
159
|
new(rows, options).render
|
160
|
+
else
|
161
|
+
options[:vertical] ? Helpers::VerticalTable.render(rows, options) :
|
162
|
+
options[:unicode] ? Helpers::UnicodeTable.render(rows, options) :
|
163
|
+
options[:tab] ? Helpers::TabTable.render(rows, options) :
|
164
|
+
options[:markdown] ? Helpers::MarkdownTable.render(rows, options) :
|
165
|
+
new(rows, options).render
|
166
|
+
end
|
143
167
|
end
|
168
|
+
private :choose_style
|
169
|
+
|
170
|
+
# A hash which maps a cell value's class to a filter. This serves to set a default filter per field if all of its
|
171
|
+
# values are a class in this hash. By default, Array values are comma joined and Hashes are inspected.
|
172
|
+
# See the :filter_any option to apply this filter per value.
|
173
|
+
attr_accessor :filter_classes
|
174
|
+
# Boolean which sets the default for :filter_any option.
|
175
|
+
attr_accessor :filter_any
|
176
|
+
# Holds last table object created
|
177
|
+
attr_accessor :last_table
|
144
178
|
end
|
145
|
-
private :choose_style
|
146
|
-
|
147
|
-
# A hash which maps a cell value's class to a filter. This serves to set a default filter per field if all of its
|
148
|
-
# values are a class in this hash. By default, Array values are comma joined and Hashes are inspected.
|
149
|
-
# See the :filter_any option to apply this filter per value.
|
150
|
-
attr_accessor :filter_classes
|
151
|
-
# Boolean which sets the default for :filter_any option.
|
152
|
-
attr_accessor :filter_any
|
153
|
-
# Holds last table object created
|
154
|
-
attr_accessor :last_table
|
155
|
-
end
|
156
|
-
self.filter_classes = { Array=>:comma_join, Hash=>:inspect }
|
157
179
|
|
180
|
+
self.filter_classes = { Array=>:comma_join, Hash=>:inspect }
|
158
181
|
|
159
|
-
|
160
|
-
|
161
|
-
|
182
|
+
def chars
|
183
|
+
self.class.const_get(:CHARS)
|
184
|
+
end
|
162
185
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
:
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
@
|
176
|
-
|
186
|
+
#:stopdoc:
|
187
|
+
attr_accessor :width, :max_fields, :field_lengths, :fields
|
188
|
+
|
189
|
+
def initialize(rows, options={})
|
190
|
+
raise ArgumentError, "Table must be an array of hashes or array of arrays" unless rows.is_a?(Array) &&
|
191
|
+
(rows[0].is_a?(Hash) or rows[0].is_a?(Array) or rows.empty?)
|
192
|
+
@options = {:description=>true, :filters=>{}, :change_fields=>{}, :escape_special_chars=>true,
|
193
|
+
:filter_any=>Helpers::Table.filter_any, :resize=>true}.merge(options)
|
194
|
+
@fields = set_fields(rows)
|
195
|
+
@fields = @fields.select {|e| e.to_s[@options[:grep_fields]] } if @options[:grep_fields]
|
196
|
+
@rows = set_rows(rows)
|
197
|
+
@headers = set_headers
|
198
|
+
if @options[:number]
|
199
|
+
@headers[:hirb_number] ||= "number"
|
200
|
+
@fields.unshift :hirb_number
|
201
|
+
end
|
202
|
+
Helpers::Table.last_table = self
|
177
203
|
end
|
178
|
-
Helpers::Table.last_table = self
|
179
|
-
end
|
180
204
|
|
181
|
-
|
182
|
-
|
183
|
-
|
205
|
+
def set_fields(rows)
|
206
|
+
@options[:change_fields] = array_to_indices_hash(@options[:change_fields]) if @options[:change_fields].is_a?(Array)
|
207
|
+
return @options[:fields].dup if @options[:fields]
|
184
208
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
209
|
+
fields = if rows[0].is_a?(Hash)
|
210
|
+
keys = @options[:all_fields] ? rows.map {|e| e.keys}.flatten.uniq : rows[0].keys
|
211
|
+
keys.sort {|a,b| a.to_s <=> b.to_s}
|
212
|
+
else
|
213
|
+
rows[0].is_a?(Array) ? (0..rows[0].length - 1).to_a : []
|
214
|
+
end
|
191
215
|
|
192
|
-
|
193
|
-
|
216
|
+
@options[:change_fields].each do |oldf, newf|
|
217
|
+
(index = fields.index(oldf)) && fields[index] = newf
|
218
|
+
end
|
219
|
+
fields
|
194
220
|
end
|
195
|
-
fields
|
196
|
-
end
|
197
221
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
222
|
+
def set_rows(rows)
|
223
|
+
rows = Array(rows)
|
224
|
+
if rows[0].is_a?(Array)
|
225
|
+
rows = rows.inject([]) {|new_rows, row|
|
226
|
+
new_rows << array_to_indices_hash(row)
|
227
|
+
}
|
228
|
+
end
|
229
|
+
@options[:change_fields].each do |oldf, newf|
|
230
|
+
rows.each {|e| e[newf] = e.delete(oldf) if e.key?(oldf) }
|
231
|
+
end
|
232
|
+
rows = filter_values(rows)
|
233
|
+
rows.each_with_index {|e,i| e[:hirb_number] = (i + 1).to_s} if @options[:number]
|
234
|
+
deleted_callbacks = Array(@options[:delete_callbacks]).map {|e| "#{e}_callback" }
|
235
|
+
(methods.grep(/_callback$/).map {|e| e.to_s} - deleted_callbacks).sort.each do |meth|
|
236
|
+
rows = send(meth, rows, @options.dup)
|
237
|
+
end
|
238
|
+
validate_values(rows)
|
239
|
+
rows
|
213
240
|
end
|
214
|
-
validate_values(rows)
|
215
|
-
rows
|
216
|
-
end
|
217
241
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
242
|
+
def set_headers
|
243
|
+
headers = @fields.inject({}) {|h,e| h[e] = e.to_s; h}
|
244
|
+
if @options.has_key?(:headers)
|
245
|
+
headers = @options[:headers].is_a?(Hash) ? headers.merge(@options[:headers]) :
|
246
|
+
(@options[:headers].is_a?(Array) ? array_to_indices_hash(@options[:headers]) : @options[:headers])
|
247
|
+
end
|
248
|
+
if @options[:header_filter]
|
249
|
+
headers.each {|k,v|
|
250
|
+
headers[k] = call_filter(@options[:header_filter], v)
|
251
|
+
}
|
252
|
+
end
|
253
|
+
headers
|
228
254
|
end
|
229
|
-
headers
|
230
|
-
end
|
231
255
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
256
|
+
def render
|
257
|
+
body = []
|
258
|
+
unless @rows.length == 0
|
259
|
+
setup_field_lengths
|
260
|
+
body += render_header
|
261
|
+
body += render_rows
|
262
|
+
body += render_footer
|
263
|
+
end
|
264
|
+
body << render_table_description if @options[:description]
|
265
|
+
body.join("\n")
|
239
266
|
end
|
240
|
-
body << render_table_description if @options[:description]
|
241
|
-
body.join("\n")
|
242
|
-
end
|
243
267
|
|
244
|
-
|
245
|
-
|
246
|
-
|
268
|
+
def render_header
|
269
|
+
@headers ? render_table_header : [render_border(:top)]
|
270
|
+
end
|
247
271
|
|
248
|
-
|
249
|
-
|
250
|
-
|
272
|
+
def render_footer
|
273
|
+
[render_border(:bottom)]
|
274
|
+
end
|
251
275
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
276
|
+
def render_table_header
|
277
|
+
title_row = chars[:top][:vertical][:outside] + ' ' +
|
278
|
+
format_values(@headers).join(' ' + chars[:top][:vertical][:inside] +' ') +
|
279
|
+
' ' + chars[:top][:vertical][:outside]
|
280
|
+
[render_border(:top), title_row, render_border(:middle)]
|
281
|
+
end
|
258
282
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
283
|
+
def render_border(which)
|
284
|
+
chars[which][:left] + chars[which][:horizontal] +
|
285
|
+
@fields.map {|f| chars[which][:horizontal] * @field_lengths[f] }.
|
286
|
+
join(chars[which][:horizontal] + chars[which][:center] + chars[which][:horizontal]) +
|
287
|
+
chars[which][:horizontal] + chars[which][:right]
|
288
|
+
end
|
265
289
|
|
266
|
-
|
267
|
-
|
268
|
-
|
290
|
+
def format_values(values)
|
291
|
+
@fields.map {|field| format_cell(values[field], @field_lengths[field]) }
|
292
|
+
end
|
269
293
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
294
|
+
def format_cell(value, cell_width)
|
295
|
+
text = String.size(value) > cell_width ?
|
296
|
+
(
|
297
|
+
(cell_width < 5) ? String.slice(value, 0, cell_width) : String.slice(value, 0, cell_width - 3) + '...'
|
298
|
+
) : value
|
299
|
+
String.ljust(text, cell_width)
|
300
|
+
end
|
277
301
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
302
|
+
def render_rows
|
303
|
+
@rows.map do |row|
|
304
|
+
chars[:bottom][:vertical][:outside] + ' ' +
|
305
|
+
format_values(row).join(' ' + chars[:bottom][:vertical][:inside] + ' ') +
|
306
|
+
' ' + chars[:bottom][:vertical][:outside]
|
307
|
+
end
|
283
308
|
end
|
284
|
-
end
|
285
309
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
310
|
+
def render_table_description
|
311
|
+
(@rows.length == 0) ? "0 rows in set" :
|
312
|
+
"#{@rows.length} #{@rows.length == 1 ? 'row' : 'rows'} in set"
|
313
|
+
end
|
290
314
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
315
|
+
def setup_field_lengths
|
316
|
+
@field_lengths = default_field_lengths
|
317
|
+
if @options[:resize]
|
318
|
+
raise TooManyFieldsForWidthError if @fields.size > self.actual_width.to_f / MIN_FIELD_LENGTH
|
319
|
+
Resizer.resize!(self)
|
320
|
+
else
|
321
|
+
enforce_field_constraints
|
322
|
+
end
|
298
323
|
end
|
299
|
-
end
|
300
324
|
|
301
|
-
|
302
|
-
|
303
|
-
|
325
|
+
def enforce_field_constraints
|
326
|
+
max_fields.each {|k,max| @field_lengths[k] = max if @field_lengths[k].to_i > max }
|
327
|
+
end
|
304
328
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
329
|
+
undef :max_fields
|
330
|
+
def max_fields
|
331
|
+
@max_fields ||= (@options[:max_fields] ||= {}).each {|k,v|
|
332
|
+
@options[:max_fields][k] = (actual_width * v.to_f.abs).floor if v.to_f.abs < 1
|
333
|
+
}
|
334
|
+
end
|
311
335
|
|
312
|
-
|
313
|
-
|
314
|
-
|
336
|
+
def actual_width
|
337
|
+
@actual_width ||= self.width - (@fields.size * BORDER_LENGTH + 1)
|
338
|
+
end
|
315
339
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
340
|
+
undef :width
|
341
|
+
def width
|
342
|
+
@width ||= @options[:max_width] || View.width
|
343
|
+
end
|
320
344
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
345
|
+
# find max length for each field; start with the headers
|
346
|
+
def default_field_lengths
|
347
|
+
field_lengths = @headers ? @headers.inject({}) {|h,(k,v)| h[k] = String.size(v); h} :
|
348
|
+
@fields.inject({}) {|h,e| h[e] = 1; h }
|
349
|
+
@rows.each do |row|
|
350
|
+
@fields.each do |field|
|
351
|
+
len = String.size(row[field])
|
352
|
+
field_lengths[field] = len if len > field_lengths[field].to_i
|
353
|
+
end
|
329
354
|
end
|
355
|
+
field_lengths
|
330
356
|
end
|
331
|
-
field_lengths
|
332
|
-
end
|
333
357
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
358
|
+
def set_filter_defaults(rows)
|
359
|
+
@filter_classes.each do |klass, filter|
|
360
|
+
@fields.each {|field|
|
361
|
+
if rows.all? {|r| r[field].class == klass }
|
362
|
+
@options[:filters][field] ||= filter
|
363
|
+
end
|
364
|
+
}
|
365
|
+
end
|
341
366
|
end
|
342
|
-
end
|
343
367
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
368
|
+
def filter_values(rows)
|
369
|
+
@filter_classes = Helpers::Table.filter_classes.merge @options[:filter_classes] || {}
|
370
|
+
set_filter_defaults(rows) unless @options[:filter_any]
|
371
|
+
rows.map {|row|
|
372
|
+
@fields.inject({}) {|new_row,f|
|
373
|
+
(filter = @options[:filters][f]) || (@options[:filter_any] && (filter = @filter_classes[row[f].class]))
|
374
|
+
new_row[f] = filter ? call_filter(filter, row[f]) : row[f]
|
375
|
+
new_row
|
376
|
+
}
|
352
377
|
}
|
353
|
-
|
354
|
-
end
|
378
|
+
end
|
355
379
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
380
|
+
def call_filter(filter, val)
|
381
|
+
filter.is_a?(Proc) ? filter.call(val) :
|
382
|
+
val.respond_to?(Array(filter)[0]) ? val.send(*filter) : Filters.send(filter, val)
|
383
|
+
end
|
360
384
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
385
|
+
def validate_values(rows)
|
386
|
+
rows.each {|row|
|
387
|
+
@fields.each {|f|
|
388
|
+
row[f] = row[f].to_s || ''
|
389
|
+
row[f] = row[f].gsub(/(\t|\r|\n)/) {|e| e.dump.gsub('"','') } if @options[:escape_special_chars]
|
390
|
+
}
|
366
391
|
}
|
367
|
-
|
368
|
-
end
|
392
|
+
end
|
369
393
|
|
370
|
-
|
371
|
-
|
372
|
-
|
394
|
+
# Converts an array to a hash mapping a numerical index to its array value.
|
395
|
+
def array_to_indices_hash(array)
|
396
|
+
array.inject({}) {|hash,e| hash[hash.size] = e; hash }
|
397
|
+
end
|
398
|
+
#:startdoc:
|
373
399
|
end
|
374
|
-
#:startdoc:
|
375
|
-
end
|
376
400
|
end
|
@@ -1,12 +1,35 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
class Hirb::Helpers::UnicodeTable < Hirb::Helpers::Table
|
3
3
|
CHARS = {
|
4
|
-
:top => {
|
5
|
-
:
|
6
|
-
|
7
|
-
|
8
|
-
:
|
9
|
-
|
4
|
+
:top => {
|
5
|
+
:left => "┌",
|
6
|
+
:center => "┬",
|
7
|
+
:right => "┐",
|
8
|
+
:horizontal => "─",
|
9
|
+
:vertical => {
|
10
|
+
:outside => "│",
|
11
|
+
:inside => "│",
|
12
|
+
},
|
13
|
+
},
|
14
|
+
|
15
|
+
:middle => {
|
16
|
+
:left => "├",
|
17
|
+
:center => "┼",
|
18
|
+
:right => "┤",
|
19
|
+
:horizontal => "─",
|
20
|
+
},
|
21
|
+
|
22
|
+
:bottom => {
|
23
|
+
:left => "└",
|
24
|
+
:center => "┴",
|
25
|
+
:right => "┘",
|
26
|
+
:horizontal => "─",
|
27
|
+
:vertical => {
|
28
|
+
:outside => "│",
|
29
|
+
:inside => "╎",
|
30
|
+
},
|
31
|
+
},
|
32
|
+
}.freeze
|
10
33
|
|
11
34
|
# Renders a unicode table
|
12
35
|
def self.render(rows, options={})
|
data/lib/hirb/version.rb
CHANGED
data/lib/hirb/view.rb
CHANGED
@@ -187,16 +187,18 @@ module Hirb
|
|
187
187
|
if defined?(Ripl) && Ripl.respond_to?(:started?) && Ripl.started?
|
188
188
|
@output_method = true
|
189
189
|
require 'ripl/hirb' unless defined? Ripl::Hirb
|
190
|
+
end
|
190
191
|
|
191
|
-
|
192
|
+
if defined? Pry
|
192
193
|
original_print = Pry.config.print
|
193
194
|
|
194
195
|
Pry.config.print = proc do |output, result, pry_instance|
|
195
196
|
Hirb::View.view_or_page_output(result) ||
|
196
197
|
original_print.call(output, result, pry_instance)
|
197
198
|
end
|
199
|
+
end
|
198
200
|
|
199
|
-
|
201
|
+
if defined?(IRB::Irb)
|
200
202
|
@output_method = true
|
201
203
|
|
202
204
|
::IRB::Irb.class_eval do
|
data/spec/spec_helper.rb
CHANGED
data/spec/view_spec.rb
CHANGED
@@ -134,6 +134,16 @@ RSpec.describe "Hirb::View" do
|
|
134
134
|
expect(Hirb::View.formatter.config.size).to be > 1
|
135
135
|
end
|
136
136
|
|
137
|
+
it "sets up configuration for IRB and Pry if they're both defined" do
|
138
|
+
Hirb.disable
|
139
|
+
expect(Pry.config).to receive(:print=)
|
140
|
+
expect {
|
141
|
+
Hirb.enable
|
142
|
+
}.to change {
|
143
|
+
::IRB::Irb.instance_method(:output_value) == ::IRB::Irb.instance_method(:non_hirb_view_output)
|
144
|
+
}.from(true).to(false)
|
145
|
+
end
|
146
|
+
|
137
147
|
it "with config_file option adds to config_file" do
|
138
148
|
Hirb.enable :config_file => "test_file"
|
139
149
|
|
@@ -148,7 +158,9 @@ RSpec.describe "Hirb::View" do
|
|
148
158
|
.and_raise(Exception, "Ex mesg")
|
149
159
|
.twice
|
150
160
|
|
151
|
-
|
161
|
+
capture_stderr do
|
162
|
+
expect(Hirb::View.view_output("")).to eq(false)
|
163
|
+
end
|
152
164
|
|
153
165
|
expect(capture_stderr { Hirb::View.view_output("") })
|
154
166
|
.to match(/Error: Ex mesg/)
|
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hirber
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Horner
|
8
8
|
- Aleksander Długopolski
|
9
|
-
|
9
|
+
- Marz Drel
|
10
|
+
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2021-
|
13
|
+
date: 2021-11-11 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: rake
|
@@ -25,6 +26,20 @@ dependencies:
|
|
25
26
|
- - ">="
|
26
27
|
- !ruby/object:Gem::Version
|
27
28
|
version: '0'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: pry
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
28
43
|
- !ruby/object:Gem::Dependency
|
29
44
|
name: rspec
|
30
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,7 +120,7 @@ description: Hirb provides a mini view framework for console applications and us
|
|
105
120
|
offers a smart pager and a console menu. The smart pager only pages when the output
|
106
121
|
exceeds the current screen size. The menu is used in conjunction with tables to
|
107
122
|
offer two dimensional menus.
|
108
|
-
email:
|
123
|
+
email: marzdrel@dotpro.org
|
109
124
|
executables: []
|
110
125
|
extensions: []
|
111
126
|
extra_rdoc_files:
|
@@ -174,7 +189,7 @@ homepage: http://tagaholic.me/hirb/
|
|
174
189
|
licenses:
|
175
190
|
- MIT
|
176
191
|
metadata: {}
|
177
|
-
post_install_message:
|
192
|
+
post_install_message:
|
178
193
|
rdoc_options: []
|
179
194
|
require_paths:
|
180
195
|
- lib
|
@@ -189,8 +204,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
204
|
- !ruby/object:Gem::Version
|
190
205
|
version: 1.3.5
|
191
206
|
requirements: []
|
192
|
-
rubygems_version: 3.
|
193
|
-
signing_key:
|
207
|
+
rubygems_version: 3.2.15
|
208
|
+
signing_key:
|
194
209
|
specification_version: 4
|
195
210
|
summary: A mini view framework for console/irb that's easy to use, even while under
|
196
211
|
its influence.
|