format_output 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9c8c65f32fd95cbd19905fc587273dbea1ce18da
4
+ data.tar.gz: baf65ce1f265d28f1174e5ac4151852c432ced21
5
+ SHA512:
6
+ metadata.gz: d42cd2b4fa43e1beaeaac9779bb389716543e89b80be942cf0d20eff34575f8e90677ffd5c55c749c9f19f77c2850aecfc4e0408bff5e9f7f1b173bce8d70078
7
+ data.tar.gz: 53ac409dcef76919bf95553f642e6ac3ac2ca7ede43d6d730b1200f0615b328d7bb4f7992d24de79e145633e2e02ee2ad963c40051283a87bab4bd9bc1c6fe7d
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at peter.c.camilleri@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in format_output.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 PeterCamilleri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,352 @@
1
+ # FormatOutput
2
+
3
+ The format_output gem is used to facilitate the creation of CLI programs in
4
+ Ruby by formatting data as bullet points, columns, or word wrap to the console,
5
+ strings, or arrays of strings.
6
+
7
+ This gem started out life buried deep within the mysh gem where it served as
8
+ the data formatting facility for that program. Such was the usefulness of these
9
+ methods that the long task of splitting them away from mysh was started.
10
+
11
+ Along the way, additional capabilities were added to flesh out range of
12
+ available formatting transformations. The following are taken from the
13
+ format_output unit test suite and include:
14
+
15
+ ### Columns
16
+
17
+ Neat, efficient columns of data are nice:
18
+
19
+ 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95
20
+ 1 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 86 91 96
21
+ 2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 82 87 92 97
22
+ 3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 83 88 93 98
23
+ 4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 84 89 94 99
24
+
25
+ ### Bullet Points
26
+
27
+ How about making some (bullet) points:
28
+
29
+ Name Wiley Coyote
30
+ Education Super Genius
31
+ Incident Run over by a large truck
32
+ Status Flattened and accordion like
33
+
34
+ ### Word Wrap
35
+
36
+ Well long text can be hard to (word) wrap your head around:
37
+
38
+ There are many many stars in the
39
+ heavens. Lots really. Like billions and
40
+ billions
41
+
42
+ There are many many fishies in the sea.
43
+ Lots really. Like billions and billions
44
+
45
+ There are many many birds in the sky.
46
+ Lots really. Like billions and billions
47
+
48
+
49
+ ## Installation
50
+
51
+ Add this line to your application's Gemfile:
52
+
53
+ ```ruby
54
+ gem 'format_output'
55
+ ```
56
+
57
+ And then execute:
58
+
59
+ $ bundle
60
+
61
+ Or install it yourself as:
62
+
63
+ $ gem install format_output
64
+
65
+ ## Usage
66
+
67
+ ### Formatting Control Parameters
68
+
69
+ The formatting of output is controlled by four parameters. These are:
70
+
71
+ Parameter| Description | Default
72
+ ---------|-------------------------------------------------------------------|-----------
73
+ width |The overall width of the output "stage". | 80
74
+ left |The size of the left margin. White space to the left of the data. | 0
75
+ body |The center of the "stage" where the formatted data resides. | 80
76
+ right |The size of the right margin. White space to the right of the data.| 0
77
+
78
+ These relationships are illustrated below:
79
+
80
+ |<-------------------------- width --------------------------->|
81
+ |<left margin>|<------------ body ------------>|<right margin>|
82
+ 1 4 7 10 13 16 19 22 25
83
+ 2 5 8 11 14 17 20 23
84
+ 3 6 9 12 15 18 21 24
85
+
86
+ width = 60
87
+ left = 14
88
+ right = 14
89
+ body = 32
90
+
91
+ In many cases however, the programmer will not need to modify these parameters.
92
+ That is the defaults values provided are suitable for most application.
93
+
94
+ If the defaults are not suitable, there are two approaches that are available:
95
+
96
+ 1. The global parameters can be changed. For example:
97
+
98
+ FormatOutput.width = 132
99
+
100
+ 2. The parameters can be set for one call only. For example:
101
+
102
+ my_array.format_output_columns(left: 5, right: 5)
103
+
104
+
105
+ Note: The format_output parameters are connected in that they define the same
106
+ formatting work area. As a result, the following interactions occur:
107
+
108
+ FormatOutput.left # Gets the left margin
109
+ FormatOutput.left = value # Sets the left margin
110
+ FormatOutput.right # Gets the left margin
111
+ FormatOutput.right = value # Sets the left margin
112
+ FormatOutput.width # Gets the full width
113
+ FormatOutput.width = value # Sets the full width
114
+ FormatOutput.body # Gets width - (left + right)
115
+ FormatOutput.width = value # Sets width = value + left + right
116
+
117
+ As can be seen, setting the body and setting the other parameters does interact
118
+ quite a bit. So these rules should be followed:
119
+
120
+ * Always set left and right margins before setting the body. Otherwise, the
121
+ body setting will be modified to a value other than the set value.
122
+ * Don't set both the body and the width or unexpected results may be observed.
123
+
124
+ When parameters are passed in for individual calls, they take effect at the
125
+ same time so the ordering is not significant. Still, setting body and width
126
+ will have unpredictable results.
127
+
128
+ ### API Levels
129
+
130
+ Each of the three types of formatting has three levels of support. These are
131
+ summarized as follows:
132
+
133
+ Columns | Word Wrap | Bullet Points | Returns
134
+ --------------------------|----------------------------|--------------------------|----------------
135
+ puts_format_output_columns|puts_format_output_word_wrap|puts_format_output_bullets| nil
136
+ format_output_columns |format_output_word_wrap |format_output_bullets | a string
137
+ format_output_raw_columns |format_output_raw_word_wrap |format_output_raw_bullets | [strings]
138
+
139
+ The puts_etc methods return nil because they directly print the results of the
140
+ formatting using the puts method. The intermediate methods return a string,
141
+ replete with any needed new line characters. Finally the "raw" methods return
142
+ an array of strings in place of said new line sequences.
143
+
144
+ Regardless of how output of the results is achieved, the formatting done is the
145
+ same for all three levels. The next sections focus on that formatting.
146
+
147
+ ### Columns
148
+
149
+ The column format is used to display data in neat columns. Yes sure, you can
150
+ just do a puts on an array of data and it will blast it to the console, one
151
+ item per line, but that can be a lot off lines scrolling meaninglessly off the
152
+ screen.
153
+
154
+ Column formatting tries to use as few lines of output as it can. For example:
155
+
156
+ ```ruby
157
+ # Some simple number, squared, and cubed columns
158
+ column_data = Array.new(25) { |i| "#{i}, #{i*i}, #{i*i*i} " }
159
+ puts "Numbers, squares, and cubes.", ""
160
+ column_data.puts_format_output_columns(width: 72)
161
+ ```
162
+
163
+ The output is:
164
+
165
+ Numbers, squares, and cubes.
166
+
167
+ 0, 0, 0 7, 49, 343 14, 196, 2744 21, 441, 9261
168
+ 1, 1, 1 8, 64, 512 15, 225, 3375 22, 484, 10648
169
+ 2, 4, 8 9, 81, 729 16, 256, 4096 23, 529, 12167
170
+ 3, 9, 27 10, 100, 1000 17, 289, 4913 24, 576, 13824
171
+ 4, 16, 64 11, 121, 1331 18, 324, 5832
172
+ 5, 25, 125 12, 144, 1728 19, 361, 6859
173
+ 6, 36, 216 13, 169, 2197 20, 400, 8000
174
+
175
+ Now this is not meant to be optimal, but to instead show how neat columns are
176
+ created even with items of varying length.
177
+
178
+ ### Word Wrap
179
+
180
+ The word wrap format is used to take very long lines of text and convert it
181
+ into a number of shorter lines. The trick is to avoid splitting words and
182
+ making the text hard to read.
183
+
184
+ ```ruby
185
+ # Maybe (word) wrap your head around a little Shakespear?
186
+ long_text = "Wherefore rejoice? What conquest brings he home? What tributaries follow him to Rome, to grace in captive bonds his chariot-wheels? You blocks, you stones, you worse than senseless things!"
187
+ puts "", "", "Word wrapping the Great Bard!", ""
188
+ long_text.puts_format_output_word_wrap(width: 72)
189
+ ```
190
+
191
+ The output is:
192
+
193
+ Word wrapping the Great Bard!
194
+
195
+ Wherefore rejoice? What conquest brings he home? What tributaries
196
+ follow him to Rome, to grace in captive bonds his chariot-wheels? You
197
+ blocks, you stones, you worse than senseless things!
198
+
199
+ Now, if the input is an array of really long strings, the word wrapper will
200
+ convert each of those lines into a neatly word wrapped paragraph with a blank
201
+ line between them.
202
+
203
+ ### Bullet Points
204
+
205
+ The bullet point formatter is used to create an output consisting of one or
206
+ more bullet points. So how is a bullet point defined? There are two essential
207
+ components, illustrated here:
208
+
209
+ tag1 detail1
210
+ tag2 detail2
211
+ tag3 etc etc etc
212
+
213
+ So what is the input? An array of bullet point data of course! That is an array
214
+ of arrays. The following array describes the sample bullet point above:
215
+
216
+ ```ruby
217
+ datum = [["tag1", "detail1"],["tag2", "detail2"],["tag3", "etc etc etc"]]
218
+ datum.puts_format_output_bullets
219
+ ```
220
+
221
+ Now, this array contained in the outer array describes the actual bullet points
222
+ that are created. This inner array may contain zero or more elelemts.
223
+
224
+ #### Zero Elements and Empty Bullets
225
+
226
+ When an empty array is used, the bullet formatter skips that entry. This can be
227
+ useful when gathering data for a report and for one item no data is found.
228
+ There are three ways to create an empty bullet point:
229
+
230
+ ```ruby
231
+ []
232
+ ["zero", []]
233
+ ["zero", nil]
234
+ ```
235
+
236
+ Note that the following, is not fully empty
237
+
238
+ ```ruby
239
+ ["zero", ""]
240
+ ```
241
+
242
+ Yields a bullet point with a bullet of "zero" and no detail text.
243
+
244
+ #### One Element
245
+
246
+ When an array with one element is used, that element becomes the detail and the
247
+ default bullet ("*") is used:
248
+
249
+ ```ruby
250
+ ["one with a (default) bullet."]
251
+ ```
252
+
253
+ produces:
254
+
255
+ * one with a (default) bullet.
256
+
257
+ #### Two Elements
258
+
259
+ When an array with two elements is used, the first is the bullet and the second
260
+ is the detail. This is by far the most common:
261
+
262
+ ```ruby
263
+ ["two", 2],
264
+ ```
265
+
266
+ produces:
267
+
268
+ two 2
269
+
270
+
271
+ #### Three or more Elements
272
+
273
+ When an array with three or more elements is used, the first is the bullet and
274
+ the second is the detail. The rest of the elements are additional details for
275
+ this bullet point. Like this:
276
+
277
+ ```ruby
278
+ ["three", 3, "more than two", "more than one", "more than zero"],
279
+ ```
280
+
281
+ produces:
282
+
283
+ three 3
284
+ more than two
285
+ more than one
286
+ more than zero
287
+
288
+ #### The Details
289
+
290
+ Now we can turn our attention to the actual details and to a lesser extent, the
291
+ tags. These can be:
292
+
293
+ 1. A String
294
+ 2. An object that responds favorably to the to_s method.
295
+
296
+ In addition, the details may be an Array.
297
+
298
+ So when the details are a string that string is used as the details. And yes,
299
+ if the string is too long to fit, it gets word wrapped! Like this:
300
+
301
+ ```ruby
302
+ ['five', "I can see for miles and miles and more miles and yet more miles and a lot of miles and damn more miles"]
303
+ ```
304
+
305
+ produces:
306
+
307
+ five I can see for miles and miles and more miles and yet more miles
308
+ and a lot of miles and damn more miles
309
+
310
+ Note, this courtesy does not extend to the bullet tags. They must exhibit a
311
+ sensible level of brevity!
312
+
313
+ Further, when the details are contained in there own little array, they are
314
+ displayed in columns if they don't fit in one line. Look here:
315
+
316
+ ```ruby
317
+ ["four", (1..100).to_a]
318
+ ```
319
+
320
+ produces:
321
+
322
+ four 1 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 86 91 96
323
+ 2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 82 87 92 97
324
+ 3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 83 88 93 98
325
+ 4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 84 89 94 99
326
+ 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100
327
+
328
+
329
+ ## Contributing
330
+
331
+ 1. Fork it
332
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
333
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
334
+ 4. Push to the branch (`git push origin my-new-feature`)
335
+ 5. Create new Pull Request
336
+
337
+ OR...
338
+
339
+ * Make a suggestion by raising an
340
+ [issue](https://github.com/PeterCamilleri/format_output/issues)
341
+ . All ideas and comments are welcome.
342
+
343
+ ## License
344
+
345
+ The gem is available as open source under the terms of the
346
+ [MIT License](./LICENSE.txt).
347
+
348
+ ## Code of Conduct
349
+
350
+ Everyone interacting in the format_output project’s codebases, issue trackers,
351
+ chat rooms and mailing lists is expected to follow the
352
+ [code of conduct](./CODE_OF_CONDUCT.md).
data/exe/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # format_output_demo
2
+
3
+ The format_output gem comes with the format_output_demo program. The source
4
+ code for that program is in this folder. This file shows the output of that
5
+ program:
6
+
7
+ A simple demo of the format_output gem in action.
8
+
9
+
10
+ Numbers, squares, and cubes.
11
+
12
+ 0, 0, 0 7, 49, 343 14, 196, 2744 21, 441, 9261
13
+ 1, 1, 1 8, 64, 512 15, 225, 3375 22, 484, 10648
14
+ 2, 4, 8 9, 81, 729 16, 256, 4096 23, 529, 12167
15
+ 3, 9, 27 10, 100, 1000 17, 289, 4913 24, 576, 13824
16
+ 4, 16, 64 11, 121, 1331 18, 324, 5832
17
+ 5, 25, 125 12, 144, 1728 19, 361, 6859
18
+ 6, 36, 216 13, 169, 2197 20, 400, 8000
19
+
20
+
21
+ Word wrapping the Great Bard!
22
+
23
+ Wherefore rejoice? What conquest brings he home? What tributaries
24
+ follow him to Rome, to grace in captive bonds his chariot-wheels? You
25
+ blocks, you stones, you worse than senseless things!
26
+
27
+
28
+ And now some Bullet Points!
29
+
30
+ zero
31
+ * one with a (default) bullet.
32
+ two 2
33
+ three 3
34
+ more than two
35
+ more than one
36
+ more than zero
37
+ four 1 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 86 91 96
38
+ 2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 82 87 92 97
39
+ 3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 83 88 93 98
40
+ 4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 84 89 94 99
41
+ 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100
42
+ five I can see for miles and miles and more miles and yet more miles
43
+ and a lot of miles and damn more miles
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # A demo of the format_output gem.
4
+ #
5
+
6
+ require_relative '..\lib\format_output'
7
+
8
+ puts "A simple demo of the format_output gem in action."
9
+
10
+ # Some simple number, squared, and cubed columns
11
+ column_data = Array.new(25) { |i| "#{i}, #{i*i}, #{i*i*i} " }
12
+ puts "", "", "Numbers, squares, and cubes.", ""
13
+ column_data.puts_format_output_columns(width: 72)
14
+
15
+ # Maybe (word) wrap your head around a little Shakespear?
16
+ long_text = "Wherefore rejoice? What conquest brings he home? What tributaries follow him to Rome, to grace in captive bonds his chariot-wheels? You blocks, you stones, you worse than senseless things!"
17
+ puts "", "", "Word wrapping the Great Bard!", ""
18
+ long_text.puts_format_output_word_wrap(width: 72)
19
+
20
+ # And now to dodge a bullet (point) or twelve!
21
+
22
+ demo = [
23
+ [],
24
+ ["zero", []],
25
+ ["zero", nil],
26
+ ["zero", ""],
27
+ ["one with a (default) bullet."],
28
+ ["two", 2],
29
+ ["three", 3, "more than two", "more than one", "more than zero"],
30
+ ["four", (1..100).to_a],
31
+ ['five', "I can see for miles and miles and more miles and yet more miles and a lot of miles and damn more miles"],
32
+ ]
33
+
34
+ puts "", "", "And now some Bullet Points!", ""
35
+
36
+ demo.puts_format_output_bullets(width: 72)
@@ -0,0 +1,32 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "format_output/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "format_output"
7
+ spec.version = FormatOutput::VERSION
8
+ spec.authors = ["PeterCamilleri"]
9
+ spec.email = ["peter.c.camilleri@gmail.com"]
10
+
11
+ spec.summary = %q{Formatted output to the console or strings.}
12
+ spec.description = %q{Formatted bullet points, columns, or word wrap to the console or strings.}
13
+ spec.homepage = "https://github.com/PeterCamilleri/format_output"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|docs)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec
21
+ .files
22
+ .reject { |f| f.downcase == 'exe/readme.md'}
23
+ .grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.17"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.0"
29
+ spec.add_development_dependency 'minitest_visible', "~> 0.1"
30
+ spec.add_development_dependency 'reek', "~> 5.0.2"
31
+
32
+ end
@@ -0,0 +1,61 @@
1
+ # coding: utf-8
2
+
3
+ # Print out bullet points.
4
+ module FormatOutput
5
+
6
+ # A class to build bullet points for display.
7
+ class BulletPointBuilder
8
+
9
+ # Prepare a blank slate.
10
+ def initialize(options)
11
+ @body = ::FormatOutput.width(options)
12
+ @pad = ::FormatOutput.pad(options)
13
+ @bullet_data = []
14
+ @key_length = nil
15
+ end
16
+
17
+ # Add items to these bullet points.
18
+ def add(bullet, *items)
19
+ items.each do |item|
20
+ @bullet_data << [bullet.to_s, item]
21
+ bullet = ""
22
+ end
23
+ end
24
+
25
+ # Render the bullet points as an array of strings.
26
+ def render
27
+ @key_length, results = get_key_length, []
28
+
29
+ @bullet_data.each do |key, item|
30
+ results.concat(render_bullet(key, item))
31
+ end
32
+
33
+ @bullet_data = []
34
+ results
35
+ end
36
+
37
+ private
38
+
39
+ # Allowing for a trailing space, how large is the largest bullet?
40
+ def get_key_length
41
+ (@bullet_data.max_by {|line| line[0].length})[0].length + 1
42
+ end
43
+
44
+ # Render one bullet point.
45
+ # Returns: An array of strings, formatted as: bullet details
46
+ # more details
47
+ # more etc
48
+ def render_bullet(key, item)
49
+ result = []
50
+
51
+ item.format_output_bullet_detail(width: @body - @key_length - 1, left: 0).each do |desc_line|
52
+ result << @pad + key.ljust(@key_length) + desc_line
53
+ key = ""
54
+ end
55
+
56
+ result
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,116 @@
1
+ # coding: utf-8
2
+
3
+ # Print out data in neat columns.
4
+ module FormatOutput
5
+
6
+ # A class to build columns for display.
7
+ class ColumnBuilder
8
+
9
+ # Prepare a blank page.
10
+ def initialize(options)
11
+ @body = ::FormatOutput.body(options)
12
+ @pad = ::FormatOutput.pad(options)
13
+ @page_data = []
14
+ end
15
+
16
+ # Add an item to this page.
17
+ # Returns: The number of items that did not fit in the page.
18
+ def add(raw_item)
19
+ item = raw_item.to_s
20
+ fail "Item too large to fit." unless item.length < @body
21
+
22
+ if (column = find_next_column)
23
+ @page_data[column] << item
24
+ else
25
+ @page_data << [item]
26
+ end
27
+
28
+ adjust_configuration
29
+ end
30
+
31
+ # Render the page as an array of strings.
32
+ def render
33
+ results, column_widths = [], get_column_widths
34
+
35
+ rows.times { |row_index| results << render_row(row_index, column_widths)}
36
+
37
+ @page_data.clear
38
+ results
39
+ end
40
+
41
+ private
42
+
43
+ # Get the widths of all columns
44
+ def get_column_widths
45
+ @page_data.map {|column| column.format_output_greatest_width}
46
+ end
47
+
48
+ # Render a single row of data.
49
+ # Returns: A string.
50
+ def render_row(row_index, widths)
51
+ @pad + @page_data.each_with_index.map do |column, index|
52
+ column[row_index].to_s.ljust(widths[index])
53
+ end.join(" ")
54
+ end
55
+
56
+ # Make sure the page fits within its boundaries.
57
+ def adjust_configuration
58
+ while total_width >= @body
59
+ add_a_row
60
+ end
61
+ end
62
+
63
+ # Add a row to the page, moving items as needed.
64
+ def add_a_row
65
+ new_rows = rows + 1
66
+ pool, @page_data = @page_data.flatten, []
67
+
68
+ until pool.empty?
69
+ @page_data << pool.shift(new_rows)
70
+ end
71
+ end
72
+
73
+ # Compute the total width of all of the columns.
74
+ # Returns: The sum of the widths of the widest items of each column plus
75
+ # a space between each of those columns.
76
+ def total_width
77
+ if empty?
78
+ 0
79
+ else
80
+ #The starting point, @page_data.length-1, represents the spaces needed
81
+ #between the columns. So N columns means N-1 spaces.
82
+ @page_data.inject(@page_data.length-1) do |sum, column|
83
+ sum + column.format_output_greatest_width
84
+ end
85
+ end
86
+ end
87
+
88
+ # Does the data fit on the page?
89
+ def fits?
90
+ total_width < @body
91
+ end
92
+
93
+ # How many rows are currently in this page?
94
+ def rows
95
+ empty? ? 0 : @page_data[0].length
96
+ end
97
+
98
+ # Is this page empty?
99
+ def empty?
100
+ @page_data.empty?
101
+ end
102
+
103
+ # Which column will receive the next item?
104
+ # Returns: The index of the first non-filled column or nil if none found.
105
+ def find_next_column
106
+ (1...(@page_data.length)).each do |index|
107
+ if @page_data[index].length < @page_data[index-1].length
108
+ return index
109
+ end
110
+ end
111
+
112
+ nil
113
+ end
114
+ end
115
+
116
+ end
@@ -0,0 +1,134 @@
1
+ # coding: utf-8
2
+
3
+ # Support for displaying an array formatted neatly.
4
+ # Output as bullet points starts with an array as input.
5
+ class Array
6
+
7
+ # Print out the array as bullet points.
8
+ def puts_format_output_bullets(options = {})
9
+ puts format_output_bullets(options)
10
+ end
11
+
12
+ # Convert the array to a string with bullet points.
13
+ # Returns: A string.
14
+ def format_output_bullets(options = {})
15
+ format_output_raw_bullets(options).join("\n")
16
+ end
17
+
18
+ # Convert the array to strings with bullet points.
19
+ # Returns: An array of strings.
20
+ def format_output_raw_bullets(options = {})
21
+ return [""] if empty?
22
+
23
+ builder = FormatOutput::BulletPointBuilder.new(options)
24
+
25
+ each {|pair| builder.add(*pair.format_output_prepare_bullet_detail)}
26
+
27
+ builder.render
28
+ end
29
+
30
+ # Helper methods for the BulletPointBuilder
31
+
32
+ # This method is a duplicate of a column method with a new name.
33
+ alias :format_output_bullet_detail :format_output_raw_columns
34
+
35
+ # Get data ready for being in a bullet point.
36
+ def format_output_prepare_bullet_detail
37
+ if length < 2
38
+ ["*", self[0]]
39
+ else
40
+ self
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ # Support for displaying data formatted neatly.
47
+ class Object
48
+
49
+ # Create a bullet point description from this object.
50
+ # Returns: An array of strings.
51
+ def format_output_bullet_detail(options = {})
52
+ self.to_s.format_output_bullet_detail(options)
53
+ end
54
+
55
+ # Get data ready for being in a bullet point.
56
+ def format_output_prepare_bullet_detail
57
+ ["*", self]
58
+ end
59
+
60
+ end
61
+
62
+ # Support for displaying nothing formatted neatly.
63
+ class NilClass
64
+
65
+ # Create a bullet point description from nothing.
66
+ # Returns: An array of nothing.
67
+ def format_output_bullet_detail(_options = nil)
68
+ []
69
+ end
70
+
71
+ end
72
+
73
+ # Support for displaying string data formatted in bullet points.
74
+ class String
75
+
76
+ # Create a bullet point description from this string.
77
+ # Returns: An array of strings.
78
+ def format_output_bullet_detail(options = {})
79
+ body = ::FormatOutput.width(options)
80
+ pad = ::FormatOutput.pad(options)
81
+
82
+ do_format_output_bullet_detail(split(' ').each, body, pad)
83
+ end
84
+
85
+ # Do the formatting legwork.
86
+ # Returns: An array of strings.
87
+ def do_format_output_bullet_detail(input, body, pad)
88
+ buffer, build, empty = [], "", false
89
+
90
+ # Grab "words" of input, splitting off lines as needed.
91
+ # Note: This loop exits when input.next runs out of data.
92
+ loop do
93
+ build = build.format_output_split_if_over(buffer, body, pad, input.next)
94
+ .format_output_split_if_huge(buffer, body, pad)
95
+
96
+ empty = build.empty?
97
+ end
98
+
99
+ unless empty
100
+ buffer << pad + build
101
+ end
102
+
103
+ buffer
104
+ end
105
+
106
+ # Split if adding a word goes over a little.
107
+ # Note: self is the build string from do_format_output_bullet_detail.
108
+ # Returns: A string.
109
+ def format_output_split_if_over(buffer, body, pad, word)
110
+ word.prepend(" ") unless empty? #Add a space except for the first word.
111
+
112
+ word_len = word.length
113
+
114
+ if (length + word_len) >= body && word_len < body
115
+ buffer << pad + self # Line done, add to buffer.
116
+ word.lstrip # Start of a new line, remove the leading space.
117
+ else
118
+ self + word
119
+ end
120
+ end
121
+
122
+ # Split up a overlong blob of text.
123
+ # Note: self is the result string from format_output_split_if_over.
124
+ # Returns: A string.
125
+ def format_output_split_if_huge(buffer, body, pad)
126
+
127
+ # Slice away any excess text into lines in the buffer.
128
+ while length >= body
129
+ buffer << pad + slice!(0, body)
130
+ end
131
+
132
+ self
133
+ end
134
+ end
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+
3
+ # Support for displaying an array formatted neatly.
4
+ # Output as columns starts with an array as input.
5
+ class Array
6
+
7
+ # Print out the array with efficient columns.
8
+ def puts_format_output_columns(options = {})
9
+ puts format_output_columns(options)
10
+ end
11
+
12
+ # Convert the array to a string with efficient columns.
13
+ # Returns: A string.
14
+ # Endemic Code Smells reek:FeatureEnvy -- false positive.
15
+ def format_output_columns(options = {})
16
+ format_output_raw_columns(options).join("\n")
17
+ end
18
+
19
+ # Convert the array to strings with efficient columns.
20
+ # Returns: An array of strings.
21
+ def format_output_raw_columns(options = {})
22
+ builder = FormatOutput::ColumnBuilder.new(options)
23
+
24
+ each {|item| builder.add(item)}
25
+
26
+ builder.render
27
+ end
28
+
29
+ # Helper methods for the ColumnBuilder
30
+
31
+ # Get the widest element of an array.
32
+ # Returns: The width of the widest string in the array.
33
+ def format_output_greatest_width
34
+ max_by {|item| item.length}.length
35
+ end
36
+
37
+ end
@@ -0,0 +1,7 @@
1
+ # coding: utf-8
2
+
3
+ module FormatOutput
4
+ VERSION = "0.1.0".freeze
5
+
6
+ DESCRIPTION = "Formatted output to the console or strings.".freeze
7
+ end
@@ -0,0 +1,51 @@
1
+ # coding: utf-8
2
+
3
+ # Support for displaying an array of strings formatted with word wrap.
4
+ class Array
5
+
6
+ # Print out the array with word wrap.
7
+ def puts_format_output_word_wrap(options = {})
8
+ puts format_output_word_wrap(options).join("\n")
9
+ end
10
+
11
+ # Convert the array to a string with bullet points.
12
+ # Returns: A string.
13
+ def format_output_word_wrap(options = {})
14
+ format_output_raw_word_wrap(options).join("\n")
15
+ end
16
+
17
+ # Returns: An array of strings.
18
+ # This method is a duplicate of a bullet point method with a new name.
19
+ def format_output_raw_word_wrap(options = {})
20
+ result = []
21
+
22
+ each do |item|
23
+ result.concat(item.to_s.format_output_raw_word_wrap(options))
24
+ result << ""
25
+ end
26
+
27
+ result
28
+ end
29
+
30
+ end
31
+
32
+ # Support for displaying string data formatted with word wrap.
33
+ class String
34
+
35
+ # Print out the string with word wrap.
36
+ def puts_format_output_word_wrap(options = {})
37
+ puts format_output_word_wrap(options)
38
+ end
39
+
40
+ # Convert the string to a string with bullet points.
41
+ # Returns: A string.
42
+ def format_output_word_wrap(options = {})
43
+ format_output_raw_word_wrap(options).join("\n")
44
+ end
45
+
46
+ # Convert the string to an array of strings with word wrap.
47
+ # Returns: An array of strings.
48
+ # This method is a duplicate of a bullet point method with a new name.
49
+ alias :format_output_raw_word_wrap :format_output_bullet_detail
50
+
51
+ end
@@ -0,0 +1,70 @@
1
+ # coding: utf-8
2
+
3
+ require_relative "format_output/columns"
4
+ require_relative "format_output/bullets"
5
+ require_relative "format_output/word_wrap"
6
+
7
+ require_relative "format_output/builders/bullet_builder"
8
+ require_relative "format_output/builders/column_builder"
9
+
10
+ require_relative "format_output/version"
11
+
12
+ # The format output facility. Neat columns and bullet points for all!
13
+ module FormatOutput
14
+
15
+ # Some property variables.
16
+ @width = 80
17
+ @left = 0
18
+ @right = 0
19
+
20
+ class << self
21
+
22
+ # Get the full page width.
23
+ def width(options = {})
24
+ (options[:width] || @width).to_i
25
+ end
26
+
27
+ # Set the full page width.
28
+ def width=(value)
29
+ @width = value.to_i
30
+ end
31
+
32
+ # Get the working page width.
33
+ def body(options = {})
34
+ width(options) - (left(options) + right(options))
35
+ end
36
+
37
+ # Set the working page width.
38
+ # Note always set after left and right are set.
39
+ def body=(value)
40
+ @width = value.to_i + @left + @right
41
+ end
42
+
43
+ # Get the left margin
44
+ def left(options = {})
45
+ (options[:left] || @left).to_i
46
+ end
47
+
48
+ # Set the left margin
49
+ def left=(value)
50
+ @left = value.to_i
51
+ end
52
+
53
+ # Get the left margin
54
+ def right(options = {})
55
+ (options[:right] || @right).to_i
56
+ end
57
+
58
+ # Set the right margin
59
+ def right=(value)
60
+ @right = value.to_i
61
+ end
62
+
63
+ # The left margin pad string.
64
+ def pad(options = {})
65
+ " " * left(options)
66
+ end
67
+
68
+ end
69
+
70
+ end
data/rakefile.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
11
+
12
+ desc "What version of format_output is this?"
13
+ task :vers do |t|
14
+ puts
15
+ puts "format_output version = #{::FormatOutput::VERSION}"
16
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: format_output
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - PeterCamilleri
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest_visible
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: reek
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 5.0.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 5.0.2
83
+ description: Formatted bullet points, columns, or word wrap to the console or strings.
84
+ email:
85
+ - peter.c.camilleri@gmail.com
86
+ executables:
87
+ - format_output_demo
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - CODE_OF_CONDUCT.md
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - exe/README.md
97
+ - exe/format_output_demo
98
+ - format_output.gemspec
99
+ - lib/format_output.rb
100
+ - lib/format_output/builders/bullet_builder.rb
101
+ - lib/format_output/builders/column_builder.rb
102
+ - lib/format_output/bullets.rb
103
+ - lib/format_output/columns.rb
104
+ - lib/format_output/version.rb
105
+ - lib/format_output/word_wrap.rb
106
+ - rakefile.rb
107
+ homepage: https://github.com/PeterCamilleri/format_output
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.5.2
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Formatted output to the console or strings.
131
+ test_files: []