format_output 0.1.0

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.
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: []