montoc 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +15 -0
- data/README.md +35 -0
- data/Rakefile +2 -0
- data/lib/montoc.rb +336 -0
- data/lib/montoc/version.rb +3 -0
- data/montoc.gemspec +27 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 22d841de97a5b5b8e39b3e5827693fe6fb81a3c5
|
4
|
+
data.tar.gz: f478d0705f8ddc55f14f1621caa08071f7bc057b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 91687e0f2ea29d6e5933047eba244f5a862bec15ef9d982d6bd29eb3f4aefaf73d7168d60e7c37b8eb892264b3bc51d11a678942ba56afa9be1b02e6106e025e
|
7
|
+
data.tar.gz: 3337e1f3fbde5af5675274d94dc1a3624962cba774bb65ce1244c6930adb59659fac250953a88bddc8ed00e1a194b4b5378b92783aafe74bf0b9cf6ae2e18e2e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Montoc (Monospace Text Document), a library to modify/format plain text.
|
2
|
+
Copyright (C) 2014 Adrian Setyadi
|
3
|
+
|
4
|
+
This program is free software: you can redistribute it and/or modify it
|
5
|
+
under the terms of the GNU General Public License as published by the
|
6
|
+
Free Software Foundation, either version 3 of the License, or (at your
|
7
|
+
option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful, but
|
10
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
12
|
+
Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License along
|
15
|
+
with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Montoc
|
2
|
+
|
3
|
+
Monospace Text Document: convert any plain text to rectangularize text
|
4
|
+
(same number of characters per line) or text matrix (two dimensional
|
5
|
+
Array of chars) by adding spaces in between the text. Set alignment to
|
6
|
+
left, right, center, and justify. You can also reflow the text to the
|
7
|
+
specified columns, and many other features.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'montoc'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install montoc
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
require 'montoc'
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it ( https://github.com/styd/montoc/fork )
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/montoc.rb
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
require "montoc/version"
|
2
|
+
|
3
|
+
module Montoc
|
4
|
+
class TextMatrix
|
5
|
+
attr_reader :text, :format
|
6
|
+
|
7
|
+
def initialize text = ""
|
8
|
+
@text = text
|
9
|
+
@rows = rows
|
10
|
+
@columns = columns
|
11
|
+
@format = :virgin
|
12
|
+
yield self if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
return @text
|
17
|
+
end
|
18
|
+
|
19
|
+
def + string
|
20
|
+
@text = squeeze_space
|
21
|
+
@text << string.to_s
|
22
|
+
@format = :left if @format == :virgin
|
23
|
+
method(@format.id2name + "!").call
|
24
|
+
return self
|
25
|
+
end
|
26
|
+
|
27
|
+
def text= string
|
28
|
+
@original = string
|
29
|
+
@text = string
|
30
|
+
end
|
31
|
+
|
32
|
+
# Find the longest lines in text and return the first match.
|
33
|
+
# to_s is called from the longest_line to avoid chomp being called
|
34
|
+
# from nil, in the case of text = ""
|
35
|
+
def longest_line remove_new_line_char = true
|
36
|
+
longest_line = squeeze_space.lines.max_by(&:length)
|
37
|
+
longest_line = longest_line.to_s.chomp if remove_new_line_char
|
38
|
+
return longest_line
|
39
|
+
end
|
40
|
+
|
41
|
+
def rows
|
42
|
+
return @text.lines.length
|
43
|
+
end
|
44
|
+
|
45
|
+
def row row_no = 1
|
46
|
+
raise ArgumentError, "Row number cannot be lower than 1." \
|
47
|
+
if row_no < 1
|
48
|
+
matricize
|
49
|
+
row_text = text.lines[row_no-1]
|
50
|
+
return row_text
|
51
|
+
end
|
52
|
+
|
53
|
+
def columns
|
54
|
+
return longest_line.length
|
55
|
+
end
|
56
|
+
|
57
|
+
def column col_no = 1
|
58
|
+
raise ArgumentError, "Column number cannot be lower than 1." \
|
59
|
+
if col_no < 1
|
60
|
+
matricize
|
61
|
+
if col_no > columns
|
62
|
+
return nil
|
63
|
+
end
|
64
|
+
column_text = ""
|
65
|
+
@text.each_line do |line|
|
66
|
+
column_text << line.to_s.chomp[col_no-1]
|
67
|
+
end
|
68
|
+
return column_text
|
69
|
+
end
|
70
|
+
|
71
|
+
def matricize align = :left, columns = 80
|
72
|
+
if !matrix?
|
73
|
+
reflow! columns
|
74
|
+
method(align.id2name + "!").call
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def matricize! align = :left, columns = 80
|
79
|
+
squeeze_space!
|
80
|
+
matricize align, columns
|
81
|
+
end
|
82
|
+
|
83
|
+
def matrix?
|
84
|
+
@text.each_line do |line|
|
85
|
+
return false if line.to_s.chomp.length != columns
|
86
|
+
end
|
87
|
+
return true
|
88
|
+
end
|
89
|
+
|
90
|
+
def virginize
|
91
|
+
@format = :virgin
|
92
|
+
@text = @original
|
93
|
+
return self
|
94
|
+
end
|
95
|
+
|
96
|
+
def virgin?
|
97
|
+
return true if @format == :virgin
|
98
|
+
return false
|
99
|
+
end
|
100
|
+
|
101
|
+
def left
|
102
|
+
new_text = squeeze_space.split("\n").map do |line|
|
103
|
+
line = line.to_s.ljust columns
|
104
|
+
end .join "\n"
|
105
|
+
return self.class.new(new_text)
|
106
|
+
end
|
107
|
+
|
108
|
+
def left!
|
109
|
+
@text = left.text
|
110
|
+
@format = :left
|
111
|
+
return self
|
112
|
+
end
|
113
|
+
|
114
|
+
def right
|
115
|
+
new_text = squeeze_space.split("\n").map do |line|
|
116
|
+
line = line.chomp.strip.rjust columns
|
117
|
+
end .join "\n"
|
118
|
+
return self.class.new(new_text)
|
119
|
+
end
|
120
|
+
|
121
|
+
def right!
|
122
|
+
@text = right.text
|
123
|
+
@format = :right
|
124
|
+
return self
|
125
|
+
end
|
126
|
+
|
127
|
+
def center
|
128
|
+
new_text = squeeze_space.split("\n").map do |line|
|
129
|
+
line = line.chomp.strip.rjust columns-(columns-line.length)/2
|
130
|
+
line = line.ljust columns
|
131
|
+
end .join "\n"
|
132
|
+
return self.class.new(new_text)
|
133
|
+
end
|
134
|
+
|
135
|
+
def center!
|
136
|
+
@text = center.text
|
137
|
+
@format = :center
|
138
|
+
return self
|
139
|
+
end
|
140
|
+
|
141
|
+
def squeeze_space
|
142
|
+
inter_text = @text
|
143
|
+
new_text = "".to_s
|
144
|
+
# strip extra space between non-space characters
|
145
|
+
inter_text.each_line do |line|
|
146
|
+
new_text << line.strip.squeeze(" ") + "\n"
|
147
|
+
end
|
148
|
+
|
149
|
+
return new_text
|
150
|
+
end
|
151
|
+
|
152
|
+
def squeeze_space!
|
153
|
+
@text = squeeze_space
|
154
|
+
end
|
155
|
+
|
156
|
+
def space_weight
|
157
|
+
# Initialize space position at line 0 as 0
|
158
|
+
space_weight = {0=>0}
|
159
|
+
|
160
|
+
i = 0
|
161
|
+
squeeze_space.each_line do |line|
|
162
|
+
# Initialize weight carried by a space at space position 0 as 0
|
163
|
+
weight = {0=>0}
|
164
|
+
|
165
|
+
# Space position index in each line starting from 0
|
166
|
+
j = 0
|
167
|
+
|
168
|
+
# Capture weight carried by each space in each line
|
169
|
+
# Weight means the total number of characters to the left and right
|
170
|
+
# of a space.
|
171
|
+
line.tr("\n","").split.each_cons(2) do |left_word, right_word|
|
172
|
+
weight[j] = left_word.length + right_word.length if left_word
|
173
|
+
|
174
|
+
# Move index to next space position
|
175
|
+
j += 1
|
176
|
+
end
|
177
|
+
|
178
|
+
# Copy space weight per line to overall space weight hash
|
179
|
+
space_weight[i] = weight
|
180
|
+
i +=1
|
181
|
+
end
|
182
|
+
|
183
|
+
return space_weight
|
184
|
+
# Return format:
|
185
|
+
# {line number => {space number => weight, next space number => weight, ...},
|
186
|
+
# next line number => {space number => weight, ...},
|
187
|
+
# ...}
|
188
|
+
# Return example:
|
189
|
+
# {0=>{0=>6, 1=>3, 2=>8}, 1=>{0=>4, 1=>8}, 2=>{0=>0}, 3=>{0=>8, 1=>8, 2=>9}}
|
190
|
+
# Meaning:
|
191
|
+
# Line 0 has 3 spaces with the first and the last space has weight of 6 and
|
192
|
+
# 8, respectively. Line 2 is an empty line.
|
193
|
+
end
|
194
|
+
|
195
|
+
def justify left_align_EOP = true
|
196
|
+
spacing_priority = {}
|
197
|
+
space_weight.each do |line, weights|
|
198
|
+
spacing_priority[line] = weights.sort_by do |position, weight|
|
199
|
+
weight
|
200
|
+
end .reverse.to_h.map do |position, weight|
|
201
|
+
position
|
202
|
+
end
|
203
|
+
end
|
204
|
+
# Result example:
|
205
|
+
# {0=>[2, 0, 1], 1=>[1, 0], 2=>[0], 3=>[2, 1, 0], 4=>[0]}
|
206
|
+
|
207
|
+
squeezed_text = squeeze_space
|
208
|
+
end_of_para = []
|
209
|
+
# Get lines that end paragraphs
|
210
|
+
squeezed_text.each_line.with_index do |line, line_index|
|
211
|
+
if line =~ /^\s*$/ && line_index != 0
|
212
|
+
end_of_para.push(line_index - 1)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
inter_text = ""
|
217
|
+
squeezed_text.each_line.with_index do |line, line_index|
|
218
|
+
words_arr = line.split
|
219
|
+
words_arr_length = words_arr.length < 2 ? 1 : words_arr.length-1
|
220
|
+
spaces_arr = (" ,"*words_arr_length).split(",")
|
221
|
+
space_needed = columns-line.tr("\n", "").length
|
222
|
+
(0...space_needed).each do |i|
|
223
|
+
spaces_arr.length == 0 ? index = i : index = i % spaces_arr.length
|
224
|
+
spaces_arr[spacing_priority[line_index][index]] += " "
|
225
|
+
end
|
226
|
+
|
227
|
+
(0...words_arr.length).each do |j|
|
228
|
+
inter_text << words_arr[j]
|
229
|
+
inter_text << spaces_arr[j] unless j == words_arr.length - 1
|
230
|
+
end
|
231
|
+
|
232
|
+
inter_text << "\n"
|
233
|
+
end
|
234
|
+
|
235
|
+
if left_align_EOP == true
|
236
|
+
text_arr = inter_text.split("\n")
|
237
|
+
inter_text.each_line.with_index do |line, line_index|
|
238
|
+
if end_of_para.include?(line_index) || line == inter_text.lines[-1]
|
239
|
+
text_arr[line_index] = line.strip.squeeze(" ").ljust(columns)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
inter_text = text_arr.join("\n")
|
243
|
+
end
|
244
|
+
|
245
|
+
new_text = ""
|
246
|
+
inter_text.each_line do |line|
|
247
|
+
line = line.tr("\n","")
|
248
|
+
line = " ".ljust(columns) if line =~ /^\s*$/
|
249
|
+
new_text << line + "\n"
|
250
|
+
end
|
251
|
+
|
252
|
+
# In case line contains only one word, this will add trailing spaces
|
253
|
+
newer_text = ""
|
254
|
+
new_text.each_line do |line|
|
255
|
+
newer_text << line.strip.ljust(columns) + "\n"
|
256
|
+
end
|
257
|
+
newer_text.chomp!
|
258
|
+
|
259
|
+
# return new object with new text
|
260
|
+
return self.class.new(newer_text)
|
261
|
+
end
|
262
|
+
|
263
|
+
def justify! left_align_EOP = true
|
264
|
+
@text = justify(left_align_EOP).text
|
265
|
+
@format = :justify
|
266
|
+
return self
|
267
|
+
end
|
268
|
+
|
269
|
+
def longest_word
|
270
|
+
longest_word = squeeze_space.split.max_by(&:length)
|
271
|
+
return longest_word
|
272
|
+
end
|
273
|
+
|
274
|
+
def reflow column_width = `echo $COLUMNS`.to_i
|
275
|
+
raise ArgumentError, "Columns cannot be shorter than the longest word." \
|
276
|
+
if columns < longest_word.length
|
277
|
+
new_text = ""
|
278
|
+
text_arr = @text.split(/\n\s*\n/)
|
279
|
+
new_paragraph = []
|
280
|
+
text_arr.each do |paragraph|
|
281
|
+
para = []
|
282
|
+
para_arr = paragraph.split(" ")
|
283
|
+
while para_arr.length > 0
|
284
|
+
line_arr = []
|
285
|
+
para_arr.take_while do |word|
|
286
|
+
line_arr.push(word)
|
287
|
+
line_arr.join(" ").length <= column_width
|
288
|
+
end
|
289
|
+
line_arr.pop if line_arr.join(" ").length > column_width
|
290
|
+
len = line_arr.length
|
291
|
+
para_arr.shift(len)
|
292
|
+
line = line_arr.join(" ")
|
293
|
+
para << line + " \n"
|
294
|
+
end
|
295
|
+
new_paragraph << para.join
|
296
|
+
end
|
297
|
+
new_text = new_paragraph.join("\n")
|
298
|
+
|
299
|
+
unless @format == :virgin
|
300
|
+
new_text = self.class.new(new_text).method(@format).call.text
|
301
|
+
else
|
302
|
+
return self.class.new(new_text).left # default alignment
|
303
|
+
end
|
304
|
+
|
305
|
+
return self.class.new(new_text)
|
306
|
+
end
|
307
|
+
|
308
|
+
def reflow! column_width = `echo $COLUMNS`.to_i
|
309
|
+
obj = reflow(column_width)
|
310
|
+
@text = obj.text
|
311
|
+
@format = obj.format
|
312
|
+
return self
|
313
|
+
end
|
314
|
+
|
315
|
+
# Define alias methods
|
316
|
+
method_aliases = %w{col column line row cols columns unmatricize
|
317
|
+
squeeze_space! unmat squeeze_space! restore
|
318
|
+
virginize}
|
319
|
+
method_aliases.each_slice(2) do |new_method, ori_method|
|
320
|
+
alias_method new_method, ori_method
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
class String
|
327
|
+
def +(other)
|
328
|
+
if other.kind_of? Montoc::TextMatrix
|
329
|
+
new_text = self + other.text
|
330
|
+
return new_text
|
331
|
+
end
|
332
|
+
result = self.dup
|
333
|
+
result << other.to_s
|
334
|
+
return result
|
335
|
+
end
|
336
|
+
end
|
data/montoc.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'montoc/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "montoc"
|
8
|
+
spec.version = Montoc::VERSION
|
9
|
+
spec.authors = ["Adrian Setyadi"]
|
10
|
+
spec.email = ["a.styd@yahoo.com"]
|
11
|
+
spec.summary = %q{Monospace Text Document}
|
12
|
+
spec.description = %q{Monospace Text Document: convert any plain text to rectangularized text
|
13
|
+
(same number of characters per line) or text matrix (two dimensional
|
14
|
+
Array of chars of the text) by adding spaces in between the text.
|
15
|
+
Set alignment to left, right, center, and justify. You can also reflow
|
16
|
+
the text to the specified columns, and many other features.}
|
17
|
+
spec.homepage = "https://github.com/styd/montoc/"
|
18
|
+
spec.license = "GPLv3"
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: montoc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adrian Setyadi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-11 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.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
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
|
+
description: "Monospace Text Document: convert any plain text to rectangularized text
|
42
|
+
\n(same number of characters per line) or text matrix (two dimensional\nArray of
|
43
|
+
chars of the text) by adding spaces in between the text.\nSet alignment to left,
|
44
|
+
right, center, and justify. You can also reflow\nthe text to the specified columns,
|
45
|
+
and many other features."
|
46
|
+
email:
|
47
|
+
- a.styd@yahoo.com
|
48
|
+
executables: []
|
49
|
+
extensions: []
|
50
|
+
extra_rdoc_files: []
|
51
|
+
files:
|
52
|
+
- ".gitignore"
|
53
|
+
- Gemfile
|
54
|
+
- LICENSE.txt
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
- lib/montoc.rb
|
58
|
+
- lib/montoc/version.rb
|
59
|
+
- montoc.gemspec
|
60
|
+
homepage: https://github.com/styd/montoc/
|
61
|
+
licenses:
|
62
|
+
- GPLv3
|
63
|
+
metadata: {}
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.4.1
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Monospace Text Document
|
84
|
+
test_files: []
|