bbcloud 0.6.2 → 0.7
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.
- data/bbcloud.gemspec +1 -3
- data/lib/bbcloud/commands/servers-show.rb +1 -1
- data/lib/bbcloud/version.rb +1 -1
- metadata +26 -61
- data/lib/bbcloud/commands/servers-restart.rb +0 -30
- data/lib/bbcloud/vendor/hirb/.gemspec +0 -22
- data/lib/bbcloud/vendor/hirb/CHANGELOG.rdoc +0 -106
- data/lib/bbcloud/vendor/hirb/LICENSE.txt +0 -22
- data/lib/bbcloud/vendor/hirb/README.rdoc +0 -182
- data/lib/bbcloud/vendor/hirb/Rakefile +0 -35
- data/lib/bbcloud/vendor/hirb/lib/bond/completions/hirb.rb +0 -15
- data/lib/bbcloud/vendor/hirb/lib/hirb/console.rb +0 -43
- data/lib/bbcloud/vendor/hirb/lib/hirb/dynamic_view.rb +0 -113
- data/lib/bbcloud/vendor/hirb/lib/hirb/formatter.rb +0 -116
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers/auto_table.rb +0 -24
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers/object_table.rb +0 -14
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers/parent_child_tree.rb +0 -24
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers/table/filters.rb +0 -10
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers/table/resizer.rb +0 -82
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers/table.rb +0 -323
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers/tree.rb +0 -181
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers/vertical_table.rb +0 -37
- data/lib/bbcloud/vendor/hirb/lib/hirb/helpers.rb +0 -17
- data/lib/bbcloud/vendor/hirb/lib/hirb/import_object.rb +0 -10
- data/lib/bbcloud/vendor/hirb/lib/hirb/menu.rb +0 -221
- data/lib/bbcloud/vendor/hirb/lib/hirb/pager.rb +0 -95
- data/lib/bbcloud/vendor/hirb/lib/hirb/string.rb +0 -44
- data/lib/bbcloud/vendor/hirb/lib/hirb/util.rb +0 -96
- data/lib/bbcloud/vendor/hirb/lib/hirb/version.rb +0 -3
- data/lib/bbcloud/vendor/hirb/lib/hirb/view.rb +0 -284
- data/lib/bbcloud/vendor/hirb/lib/hirb/views/couch_db.rb +0 -11
- data/lib/bbcloud/vendor/hirb/lib/hirb/views/misc_db.rb +0 -15
- data/lib/bbcloud/vendor/hirb/lib/hirb/views/mongo_db.rb +0 -14
- data/lib/bbcloud/vendor/hirb/lib/hirb/views/orm.rb +0 -11
- data/lib/bbcloud/vendor/hirb/lib/hirb/views/rails.rb +0 -19
- data/lib/bbcloud/vendor/hirb/lib/hirb/views.rb +0 -8
- data/lib/bbcloud/vendor/hirb/lib/hirb.rb +0 -81
- data/lib/bbcloud/vendor/hirb/test/auto_table_test.rb +0 -30
- data/lib/bbcloud/vendor/hirb/test/console_test.rb +0 -27
- data/lib/bbcloud/vendor/hirb/test/deps.rip +0 -4
- data/lib/bbcloud/vendor/hirb/test/dynamic_view_test.rb +0 -94
- data/lib/bbcloud/vendor/hirb/test/formatter_test.rb +0 -171
- data/lib/bbcloud/vendor/hirb/test/hirb_test.rb +0 -39
- data/lib/bbcloud/vendor/hirb/test/import_test.rb +0 -9
- data/lib/bbcloud/vendor/hirb/test/menu_test.rb +0 -239
- data/lib/bbcloud/vendor/hirb/test/object_table_test.rb +0 -79
- data/lib/bbcloud/vendor/hirb/test/pager_test.rb +0 -162
- data/lib/bbcloud/vendor/hirb/test/resizer_test.rb +0 -62
- data/lib/bbcloud/vendor/hirb/test/table_test.rb +0 -550
- data/lib/bbcloud/vendor/hirb/test/test_helper.rb +0 -61
- data/lib/bbcloud/vendor/hirb/test/tree_test.rb +0 -184
- data/lib/bbcloud/vendor/hirb/test/util_test.rb +0 -59
- data/lib/bbcloud/vendor/hirb/test/view_test.rb +0 -172
- data/lib/bbcloud/vendor/hirb/test/views_test.rb +0 -13
@@ -1,323 +0,0 @@
|
|
1
|
-
require 'hirb/helpers/table/filters'
|
2
|
-
require 'hirb/helpers/table/resizer'
|
3
|
-
|
4
|
-
module Hirb
|
5
|
-
# Base Table class from which other table classes inherit.
|
6
|
-
# By default, a table is constrained to a default width but this can be adjusted
|
7
|
-
# via the max_width option or Hirb::View.width.
|
8
|
-
# Rows can be an array of arrays or an array of hashes.
|
9
|
-
#
|
10
|
-
# An array of arrays ie [[1,2], [2,3]], would render:
|
11
|
-
# +---+---+
|
12
|
-
# | 0 | 1 |
|
13
|
-
# +---+---+
|
14
|
-
# | 1 | 2 |
|
15
|
-
# | 2 | 3 |
|
16
|
-
# +---+---+
|
17
|
-
#
|
18
|
-
# By default, the fields/columns are the numerical indices of the array.
|
19
|
-
#
|
20
|
-
# An array of hashes ie [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], would render:
|
21
|
-
# +-----+--------+
|
22
|
-
# | age | weight |
|
23
|
-
# +-----+--------+
|
24
|
-
# | 10 | 100 |
|
25
|
-
# | 80 | 500 |
|
26
|
-
# +-----+--------+
|
27
|
-
#
|
28
|
-
# By default, the fields/columns are the keys of the first hash.
|
29
|
-
#
|
30
|
-
# === Custom Callbacks
|
31
|
-
# Callback methods can be defined to add your own options that modify rows right before they are rendered.
|
32
|
-
# Here's an example that allows for searching with a :query option:
|
33
|
-
# module Query
|
34
|
-
# # Searches fields given a query hash
|
35
|
-
# def query_callback(rows, options)
|
36
|
-
# return rows unless options[:query]
|
37
|
-
# options[:query].map {|field,query|
|
38
|
-
# rows.select {|e| e[field].to_s =~ /#{query}/i }
|
39
|
-
# }.flatten.uniq
|
40
|
-
# end
|
41
|
-
# end
|
42
|
-
# Hirb::Helpers::Table.send :include, Query
|
43
|
-
#
|
44
|
-
# >> puts Hirb::Helpers::Table.render [{:name=>'batman'}, {:name=>'robin'}], :query=>{:name=>'rob'}
|
45
|
-
# +-------+
|
46
|
-
# | name |
|
47
|
-
# +-------+
|
48
|
-
# | robin |
|
49
|
-
# +-------+
|
50
|
-
# 1 row in set
|
51
|
-
#
|
52
|
-
# Callback methods:
|
53
|
-
# * must be defined in Helpers::Table and end in '_callback'.
|
54
|
-
# * should expect rows and a hash of render options. Rows will be an array of hashes.
|
55
|
-
# * are expected to return an array of hashes.
|
56
|
-
# * are invoked in alphabetical order.
|
57
|
-
# For a thorough example, see {Boson::Pipe}[http://github.com/cldwalker/boson/blob/master/lib/boson/pipe.rb].
|
58
|
-
#--
|
59
|
-
# derived from http://gist.github.com/72234
|
60
|
-
class Helpers::Table
|
61
|
-
BORDER_LENGTH = 3 # " | " and "-+-" are the borders
|
62
|
-
MIN_FIELD_LENGTH = 3
|
63
|
-
class TooManyFieldsForWidthError < StandardError; end
|
64
|
-
|
65
|
-
class << self
|
66
|
-
|
67
|
-
# Main method which returns a formatted table.
|
68
|
-
# ==== Options:
|
69
|
-
# [*:fields*] An array which overrides the default fields and can be used to indicate field order.
|
70
|
-
# [*:headers*] A hash of fields and their header names. Fields that aren't specified here default to their name.
|
71
|
-
# When set to false, headers are hidden. Can also be an array but only for array rows.
|
72
|
-
# [*:max_fields*] A hash of fields and their maximum allowed lengths. Maximum length can also be a percentage of the total width
|
73
|
-
# (decimal less than one). When a field exceeds it's maximum then it's
|
74
|
-
# truncated and has a ... appended to it. Fields that aren't specified have no maximum.
|
75
|
-
# [*:max_width*] The maximum allowed width of all fields put together including field borders. Only valid when :resize is true.
|
76
|
-
# Default is Hirb::View.width.
|
77
|
-
# [*:resize*] Resizes table to display all columns in allowed :max_width. Default is true. Setting this false will display the full
|
78
|
-
# length of each field.
|
79
|
-
# [*:number*] When set to true, numbers rows by adding a :hirb_number column as the first column. Default is false.
|
80
|
-
# [*: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.
|
81
|
-
# This is useful when wanting to change auto-generated keys to more user-friendly names i.e. for array rows.
|
82
|
-
# [*:filters*] A hash of fields and their filters, applied to every row in a field. A filter can be a proc, an instance method
|
83
|
-
# applied to the field value or a Filters method. Also see the filter_classes attribute below.
|
84
|
-
# [*:header_filter*] A filter, like one in :filters, that is applied to all headers after the :headers option.
|
85
|
-
# [*:filter_any*] When set to true, any cell defaults to being filtered by its class in :filter_classes.
|
86
|
-
# Default Hirb::Helpers::Table.filter_any().
|
87
|
-
# [*:filter_classes*] Hash which maps classes to filters. Default is Hirb::Helpers::Table.filter_classes().
|
88
|
-
# [*:vertical*] When set to true, renders a vertical table using Hirb::Helpers::VerticalTable. Default is false.
|
89
|
-
# [*:all_fields*] When set to true, renders fields in all rows. Valid only in rows that are hashes. Default is false.
|
90
|
-
# [*:description*] When set to true, renders row count description at bottom. Default is true.
|
91
|
-
# [*:escape_special_chars*] When set to true, escapes special characters \n,\t,\r so they don't disrupt tables. Default is false for
|
92
|
-
# vertical tables and true for anything else.
|
93
|
-
# Examples:
|
94
|
-
# Hirb::Helpers::Table.render [[1,2], [2,3]]
|
95
|
-
# Hirb::Helpers::Table.render [[1,2], [2,3]], :max_fields=>{0=>10}, :header_filter=>:capitalize
|
96
|
-
# Hirb::Helpers::Table.render [['a',1], ['b',2]], :change_fields=>%w{letters numbers}, :max_fields=>{'numbers'=>0.4}
|
97
|
-
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}]
|
98
|
-
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :headers=>{:weight=>"Weight(lbs)"}
|
99
|
-
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :filters=>{:age=>[:to_f]}
|
100
|
-
def render(rows, options={})
|
101
|
-
options[:vertical] ? Helpers::VerticalTable.render(rows, options) :
|
102
|
-
new(rows, options).render
|
103
|
-
rescue TooManyFieldsForWidthError
|
104
|
-
$stderr.puts "", "** Error: Too many fields for the current width. Configure your width " +
|
105
|
-
"and/or fields to avoid this error. Defaulting to a vertical table. **"
|
106
|
-
Helpers::VerticalTable.render(rows, options)
|
107
|
-
end
|
108
|
-
|
109
|
-
# 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
|
110
|
-
# values are a class in this hash. By default, Array values are comma joined and Hashes are inspected.
|
111
|
-
# See the :filter_any option to apply this filter per value.
|
112
|
-
attr_accessor :filter_classes
|
113
|
-
# Boolean which sets the default for :filter_any option.
|
114
|
-
attr_accessor :filter_any
|
115
|
-
# Holds last table object created
|
116
|
-
attr_accessor :last_table
|
117
|
-
end
|
118
|
-
self.filter_classes = { Array=>:comma_join, Hash=>:inspect }
|
119
|
-
|
120
|
-
#:stopdoc:
|
121
|
-
attr_accessor :width, :max_fields, :field_lengths, :fields
|
122
|
-
def initialize(rows, options={})
|
123
|
-
raise ArgumentError, "Table must be an array of hashes or array of arrays" unless rows.is_a?(Array) &&
|
124
|
-
(rows[0].is_a?(Hash) or rows[0].is_a?(Array) or rows.empty?)
|
125
|
-
@options = {:description=>true, :filters=>{}, :change_fields=>{}, :escape_special_chars=>true,
|
126
|
-
:filter_any=>Helpers::Table.filter_any, :resize=>true}.merge(options)
|
127
|
-
@fields = set_fields(rows)
|
128
|
-
@rows = set_rows(rows)
|
129
|
-
@headers = set_headers
|
130
|
-
if @options[:number]
|
131
|
-
@headers[:hirb_number] = "number"
|
132
|
-
@fields.unshift :hirb_number
|
133
|
-
end
|
134
|
-
Helpers::Table.last_table = self
|
135
|
-
end
|
136
|
-
|
137
|
-
def set_fields(rows)
|
138
|
-
@options[:change_fields] = array_to_indices_hash(@options[:change_fields]) if @options[:change_fields].is_a?(Array)
|
139
|
-
return @options[:fields].dup if @options[:fields]
|
140
|
-
|
141
|
-
fields = if rows[0].is_a?(Hash)
|
142
|
-
keys = @options[:all_fields] ? rows.map {|e| e.keys}.flatten.uniq : rows[0].keys
|
143
|
-
keys.sort {|a,b| a.to_s <=> b.to_s}
|
144
|
-
else
|
145
|
-
rows[0].is_a?(Array) ? (0..rows[0].length - 1).to_a : []
|
146
|
-
end
|
147
|
-
|
148
|
-
@options[:change_fields].each do |oldf, newf|
|
149
|
-
(index = fields.index(oldf)) && fields[index] = newf
|
150
|
-
end
|
151
|
-
fields
|
152
|
-
end
|
153
|
-
|
154
|
-
def set_rows(rows)
|
155
|
-
rows = Array(rows)
|
156
|
-
if rows[0].is_a?(Array)
|
157
|
-
rows = rows.inject([]) {|new_rows, row|
|
158
|
-
new_rows << array_to_indices_hash(row)
|
159
|
-
}
|
160
|
-
end
|
161
|
-
@options[:change_fields].each do |oldf, newf|
|
162
|
-
rows.each {|e| e[newf] = e.delete(oldf) if e.key?(oldf) }
|
163
|
-
end
|
164
|
-
rows = filter_values(rows)
|
165
|
-
rows.each_with_index {|e,i| e[:hirb_number] = (i + 1).to_s} if @options[:number]
|
166
|
-
deleted_callbacks = Array(@options[:delete_callbacks]).map {|e| "#{e}_callback" }
|
167
|
-
(methods.grep(/_callback$/).map {|e| e.to_s} - deleted_callbacks).sort.each do |meth|
|
168
|
-
rows = send(meth, rows, @options.dup)
|
169
|
-
end
|
170
|
-
validate_values(rows)
|
171
|
-
rows
|
172
|
-
end
|
173
|
-
|
174
|
-
def set_headers
|
175
|
-
headers = @fields.inject({}) {|h,e| h[e] = e.to_s; h}
|
176
|
-
if @options.has_key?(:headers)
|
177
|
-
headers = @options[:headers].is_a?(Hash) ? headers.merge(@options[:headers]) :
|
178
|
-
(@options[:headers].is_a?(Array) ? array_to_indices_hash(@options[:headers]) : @options[:headers])
|
179
|
-
end
|
180
|
-
if @options[:header_filter]
|
181
|
-
headers.each {|k,v|
|
182
|
-
headers[k] = call_filter(@options[:header_filter], v)
|
183
|
-
}
|
184
|
-
end
|
185
|
-
headers
|
186
|
-
end
|
187
|
-
|
188
|
-
def render
|
189
|
-
body = []
|
190
|
-
unless @rows.length == 0
|
191
|
-
setup_field_lengths
|
192
|
-
body += render_header
|
193
|
-
body += render_rows
|
194
|
-
body += render_footer
|
195
|
-
end
|
196
|
-
body << render_table_description if @options[:description]
|
197
|
-
body.join("\n")
|
198
|
-
end
|
199
|
-
|
200
|
-
def render_header
|
201
|
-
@headers ? render_table_header : [render_border]
|
202
|
-
end
|
203
|
-
|
204
|
-
def render_footer
|
205
|
-
[render_border]
|
206
|
-
end
|
207
|
-
|
208
|
-
def render_table_header
|
209
|
-
title_row = '| ' + @fields.map {|f|
|
210
|
-
format_cell(@headers[f], @field_lengths[f])
|
211
|
-
}.join(' | ') + ' |'
|
212
|
-
[render_border, title_row, render_border]
|
213
|
-
end
|
214
|
-
|
215
|
-
def render_border
|
216
|
-
'+-' + @fields.map {|f| '-' * @field_lengths[f] }.join('-+-') + '-+'
|
217
|
-
end
|
218
|
-
|
219
|
-
def format_cell(value, cell_width)
|
220
|
-
text = String.size(value) > cell_width ?
|
221
|
-
(
|
222
|
-
(cell_width < 5) ? String.slice(value, 0, cell_width) : String.slice(value, 0, cell_width - 3) + '...'
|
223
|
-
) : value
|
224
|
-
String.ljust(text, cell_width)
|
225
|
-
end
|
226
|
-
|
227
|
-
def render_rows
|
228
|
-
@rows.map do |row|
|
229
|
-
row = '| ' + @fields.map {|f|
|
230
|
-
format_cell(row[f], @field_lengths[f])
|
231
|
-
}.join(' | ') + ' |'
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
def render_table_description
|
236
|
-
(@rows.length == 0) ? "0 rows in set" :
|
237
|
-
"#{@rows.length} #{@rows.length == 1 ? 'row' : 'rows'} in set"
|
238
|
-
end
|
239
|
-
|
240
|
-
def setup_field_lengths
|
241
|
-
@field_lengths = default_field_lengths
|
242
|
-
if @options[:resize]
|
243
|
-
raise TooManyFieldsForWidthError if @fields.size > self.actual_width.to_f / MIN_FIELD_LENGTH
|
244
|
-
Resizer.resize!(self)
|
245
|
-
else
|
246
|
-
enforce_field_constraints
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
def enforce_field_constraints
|
251
|
-
max_fields.each {|k,max| @field_lengths[k] = max if @field_lengths[k].to_i > max }
|
252
|
-
end
|
253
|
-
|
254
|
-
def max_fields
|
255
|
-
@max_fields ||= (@options[:max_fields] ||= {}).each {|k,v|
|
256
|
-
@options[:max_fields][k] = (actual_width * v.to_f.abs).floor if v.to_f.abs < 1
|
257
|
-
}
|
258
|
-
end
|
259
|
-
|
260
|
-
def actual_width
|
261
|
-
@actual_width ||= self.width - (@fields.size * BORDER_LENGTH + 1)
|
262
|
-
end
|
263
|
-
|
264
|
-
def width
|
265
|
-
@width ||= @options[:max_width] || View.width
|
266
|
-
end
|
267
|
-
|
268
|
-
# find max length for each field; start with the headers
|
269
|
-
def default_field_lengths
|
270
|
-
field_lengths = @headers ? @headers.inject({}) {|h,(k,v)| h[k] = String.size(v); h} :
|
271
|
-
@fields.inject({}) {|h,e| h[e] = 1; h }
|
272
|
-
@rows.each do |row|
|
273
|
-
@fields.each do |field|
|
274
|
-
len = String.size(row[field])
|
275
|
-
field_lengths[field] = len if len > field_lengths[field].to_i
|
276
|
-
end
|
277
|
-
end
|
278
|
-
field_lengths
|
279
|
-
end
|
280
|
-
|
281
|
-
def set_filter_defaults(rows)
|
282
|
-
@filter_classes.each do |klass, filter|
|
283
|
-
@fields.each {|field|
|
284
|
-
if rows.all? {|r| r[field].class == klass }
|
285
|
-
@options[:filters][field] ||= filter
|
286
|
-
end
|
287
|
-
}
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
def filter_values(rows)
|
292
|
-
@filter_classes = Helpers::Table.filter_classes.merge @options[:filter_classes] || {}
|
293
|
-
set_filter_defaults(rows) unless @options[:filter_any]
|
294
|
-
rows.map {|row|
|
295
|
-
@fields.inject({}) {|new_row,f|
|
296
|
-
(filter = @options[:filters][f]) || (@options[:filter_any] && (filter = @filter_classes[row[f].class]))
|
297
|
-
new_row[f] = filter ? call_filter(filter, row[f]) : row[f]
|
298
|
-
new_row
|
299
|
-
}
|
300
|
-
}
|
301
|
-
end
|
302
|
-
|
303
|
-
def call_filter(filter, val)
|
304
|
-
filter.is_a?(Proc) ? filter.call(val) :
|
305
|
-
val.respond_to?(Array(filter)[0]) ? val.send(*filter) : Filters.send(filter, val)
|
306
|
-
end
|
307
|
-
|
308
|
-
def validate_values(rows)
|
309
|
-
rows.each {|row|
|
310
|
-
@fields.each {|f|
|
311
|
-
row[f] = row[f].to_s || ''
|
312
|
-
row[f] = row[f].gsub(/(\t|\r|\n)/) {|e| e.dump.gsub('"','') } if @options[:escape_special_chars]
|
313
|
-
}
|
314
|
-
}
|
315
|
-
end
|
316
|
-
|
317
|
-
# Converts an array to a hash mapping a numerical index to its array value.
|
318
|
-
def array_to_indices_hash(array)
|
319
|
-
array.inject({}) {|hash,e| hash[hash.size] = e; hash }
|
320
|
-
end
|
321
|
-
#:startdoc:
|
322
|
-
end
|
323
|
-
end
|
@@ -1,181 +0,0 @@
|
|
1
|
-
# Base tree class which given an array of nodes produces different types of trees.
|
2
|
-
# The types of trees currently are:
|
3
|
-
# * basic:
|
4
|
-
# 0
|
5
|
-
# 1
|
6
|
-
# 2
|
7
|
-
# 3
|
8
|
-
# 4
|
9
|
-
#
|
10
|
-
# * directory:
|
11
|
-
# 0
|
12
|
-
# |-- 1
|
13
|
-
# | |-- 2
|
14
|
-
# | `-- 3
|
15
|
-
# `-- 4
|
16
|
-
#
|
17
|
-
# * number:
|
18
|
-
# 1. 0
|
19
|
-
# 1. 1
|
20
|
-
# 1. 2
|
21
|
-
# 2. 3
|
22
|
-
# 2. 4
|
23
|
-
#
|
24
|
-
# Tree nodes can be given as an array of arrays or an array of hashes.
|
25
|
-
# To render the above basic tree with an array of hashes:
|
26
|
-
# Hirb::Helpers::Tree.render([{:value=>0, :level=>0}, {:value=>1, :level=>1}, {:value=>2, :level=>2},
|
27
|
-
# {:value=>3, :level=>2}, {:value=>4, :level=>1}])
|
28
|
-
# Note from the hash keys that :level refers to the depth of the tree while :value refers to the text displayed
|
29
|
-
# for a node.
|
30
|
-
#
|
31
|
-
# To render the above basic tree with an array of arrays:
|
32
|
-
# Hirb::Helpers::Tree.render([[0,0], [1,1], [2,2], [2,3], [1,4]])
|
33
|
-
# Note that the each array pair consists of the level and the value for the node.
|
34
|
-
class Hirb::Helpers::Tree
|
35
|
-
class ParentlessNodeError < StandardError; end
|
36
|
-
|
37
|
-
class <<self
|
38
|
-
# Main method which renders a tree.
|
39
|
-
# ==== Options:
|
40
|
-
# [:type] Type of tree. Either :basic, :directory or :number. Default is :basic.
|
41
|
-
# [:validate] Boolean to validate tree. Checks to see if all nodes have parents. Raises ParentlessNodeError if
|
42
|
-
# an invalid node is found. Default is false.
|
43
|
-
# [:indent] Number of spaces to indent between levels for basic + number trees. Default is 4.
|
44
|
-
# [:limit] Limits the level or depth of a tree that is displayed. Root node is level 0.
|
45
|
-
# [:description] Displays brief description about tree ie how many nodes it has.
|
46
|
-
# [:multi_line_nodes] Handles multi-lined nodes by indenting their newlines. Default is false.
|
47
|
-
# Examples:
|
48
|
-
# Hirb::Helpers::Tree.render([[0, 'root'], [1, 'child']], :type=>:directory)
|
49
|
-
def render(nodes, options={})
|
50
|
-
new(nodes, options).render
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# :stopdoc:
|
55
|
-
attr_accessor :nodes
|
56
|
-
|
57
|
-
def initialize(input_nodes, options={})
|
58
|
-
@options = options
|
59
|
-
@type = options[:type] || :basic
|
60
|
-
if input_nodes[0].is_a?(Array)
|
61
|
-
@nodes = input_nodes.map {|e| Node.new(:level=>e[0], :value=>e[1]) }
|
62
|
-
else
|
63
|
-
@nodes = input_nodes.map {|e| Node.new(e)}
|
64
|
-
end
|
65
|
-
@nodes.each_with_index {|e,i| e.merge!(:tree=>self, :index=>i)}
|
66
|
-
@nodes.each {|e| e[:value] = e[:value].to_s }
|
67
|
-
validate_nodes if options[:validate]
|
68
|
-
self
|
69
|
-
end
|
70
|
-
|
71
|
-
def render
|
72
|
-
body = render_tree
|
73
|
-
body += render_description if @options[:description]
|
74
|
-
body
|
75
|
-
end
|
76
|
-
|
77
|
-
def render_description
|
78
|
-
"\n\n#{@nodes.length} #{@nodes.length == 1 ? 'node' : 'nodes'} in tree"
|
79
|
-
end
|
80
|
-
|
81
|
-
def render_tree
|
82
|
-
@indent = ' ' * (@options[:indent] || 4 )
|
83
|
-
@nodes = @nodes.select {|e| e[:level] <= @options[:limit] } if @options[:limit]
|
84
|
-
case @type.to_s
|
85
|
-
when 'directory' then render_directory
|
86
|
-
when 'number' then render_number
|
87
|
-
else render_basic
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def render_nodes
|
92
|
-
value_indent = @options[:multi_line_nodes] ? @indent : nil
|
93
|
-
@nodes.map {|e| yield(e) + e.value(value_indent) }.join("\n")
|
94
|
-
end
|
95
|
-
|
96
|
-
def render_directory
|
97
|
-
mark_last_nodes_per_level
|
98
|
-
render_nodes {|e|
|
99
|
-
value = ''
|
100
|
-
unless e.root?
|
101
|
-
value << e.render_parent_characters
|
102
|
-
value << (e[:last_node] ? "`-- " : "|-- ")
|
103
|
-
end
|
104
|
-
value
|
105
|
-
}
|
106
|
-
end
|
107
|
-
|
108
|
-
def render_number
|
109
|
-
counter = {}
|
110
|
-
@nodes.each {|e|
|
111
|
-
parent_level_key = "#{(e.parent ||{})[:index]}.#{e[:level]}"
|
112
|
-
counter[parent_level_key] ||= 0
|
113
|
-
counter[parent_level_key] += 1
|
114
|
-
e[:pre_value] = "#{counter[parent_level_key]}. "
|
115
|
-
}
|
116
|
-
render_nodes {|e| @indent * e[:level] + e[:pre_value] }
|
117
|
-
end
|
118
|
-
|
119
|
-
def render_basic
|
120
|
-
render_nodes {|e| @indent * e[:level] }
|
121
|
-
end
|
122
|
-
|
123
|
-
def validate_nodes
|
124
|
-
@nodes.each do |e|
|
125
|
-
raise ParentlessNodeError if (e[:level] > e.previous[:level]) && (e[:level] - e.previous[:level]) > 1
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
# walks tree accumulating last nodes per unique parent+level
|
130
|
-
def mark_last_nodes_per_level
|
131
|
-
@nodes.each {|e| e.delete(:last_node)}
|
132
|
-
last_node_hash = @nodes.inject({}) {|h,e|
|
133
|
-
h["#{(e.parent ||{})[:index]}.#{e[:level]}"] = e; h
|
134
|
-
}
|
135
|
-
last_node_hash.values.uniq.each {|e| e[:last_node] = true}
|
136
|
-
end
|
137
|
-
#:startdoc:
|
138
|
-
class Node < ::Hash #:nodoc:
|
139
|
-
class MissingLevelError < StandardError; end
|
140
|
-
class MissingValueError < StandardError; end
|
141
|
-
|
142
|
-
def initialize(hash)
|
143
|
-
super
|
144
|
-
raise MissingLevelError unless hash.has_key?(:level)
|
145
|
-
raise MissingValueError unless hash.has_key?(:value)
|
146
|
-
replace(hash)
|
147
|
-
end
|
148
|
-
|
149
|
-
def value(indent=nil)
|
150
|
-
indent ? self[:value].gsub("\n", "\n#{indent * self[:level]}") : self[:value]
|
151
|
-
end
|
152
|
-
|
153
|
-
def parent
|
154
|
-
self[:tree].nodes.slice(0 .. self[:index]).reverse.detect {|e| e[:level] < self[:level]}
|
155
|
-
end
|
156
|
-
|
157
|
-
def next
|
158
|
-
self[:tree].nodes[self[:index] + 1]
|
159
|
-
end
|
160
|
-
|
161
|
-
def previous
|
162
|
-
self[:tree].nodes[self[:index] - 1]
|
163
|
-
end
|
164
|
-
|
165
|
-
def root?; self[:level] == 0; end
|
166
|
-
|
167
|
-
# refers to characters which connect parent nodes
|
168
|
-
def render_parent_characters
|
169
|
-
parent_chars = []
|
170
|
-
get_parents_character(parent_chars)
|
171
|
-
parent_chars.reverse.map {|level| level + ' ' * 3 }.join('')
|
172
|
-
end
|
173
|
-
|
174
|
-
def get_parents_character(parent_chars)
|
175
|
-
if self.parent
|
176
|
-
parent_chars << (self.parent[:last_node] ? ' ' : '|') unless self.parent.root?
|
177
|
-
self.parent.get_parents_character(parent_chars)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
class Hirb::Helpers::VerticalTable < Hirb::Helpers::Table
|
2
|
-
|
3
|
-
# Renders a vertical table using the same options as Hirb::Helpers::Table.render except for the ones below
|
4
|
-
# and :max_fields, :vertical and :max_width which aren't used.
|
5
|
-
# ==== Options:
|
6
|
-
# [:hide_empty] Boolean which hides empty values (nil or '') from being displayed. Default is false.
|
7
|
-
def self.render(rows, options={})
|
8
|
-
new(rows, {:escape_special_chars=>false, :resize=>false}.merge(options)).render
|
9
|
-
end
|
10
|
-
|
11
|
-
#:stopdoc:
|
12
|
-
def setup_field_lengths
|
13
|
-
@field_lengths = default_field_lengths
|
14
|
-
end
|
15
|
-
|
16
|
-
def render_header; []; end
|
17
|
-
def render_footer; []; end
|
18
|
-
|
19
|
-
def render_rows
|
20
|
-
i = 0
|
21
|
-
longest_header = Hirb::String.size @headers.values.sort_by {|e| Hirb::String.size(e) }.last
|
22
|
-
stars = "*" * [(longest_header + (longest_header / 2)), 3].max
|
23
|
-
@rows.map do |row|
|
24
|
-
row = "#{stars} #{i+1}. row #{stars}\n" +
|
25
|
-
@fields.map {|f|
|
26
|
-
if !@options[:hide_empty] || (@options[:hide_empty] && !row[f].empty?)
|
27
|
-
"#{Hirb::String.rjust(@headers[f], longest_header)}: #{row[f]}"
|
28
|
-
else
|
29
|
-
nil
|
30
|
-
end
|
31
|
-
}.compact.join("\n")
|
32
|
-
i+= 1
|
33
|
-
row
|
34
|
-
end
|
35
|
-
end
|
36
|
-
#:startdoc:
|
37
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Hirb
|
2
|
-
module Helpers #:nodoc:
|
3
|
-
@helper_classes ||= {}
|
4
|
-
def self.helper_class(klass)
|
5
|
-
@helper_classes[klass.to_s] ||= begin
|
6
|
-
if (helper_class = constants.find {|e| e.to_s == Util.camelize(klass.to_s)})
|
7
|
-
klass = "Hirb::Helpers::#{helper_class}"
|
8
|
-
end
|
9
|
-
Util.any_const_get(klass)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
%w{table object_table auto_table tree parent_child_tree vertical_table}.each do |e|
|
16
|
-
require "hirb/helpers/#{e}"
|
17
|
-
end
|