fat_core 6.0.0 → 7.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.
data/README.org CHANGED
@@ -3,9 +3,9 @@
3
3
  #+PROPERTY: header-args:ruby :colnames no :hlines yes :exports both :wrap example :ruby ruby
4
4
  #+PROPERTY: header-args:sh :exports code
5
5
 
6
- [[https://travis-ci.org/ddoherty03/fat_core.svg?branch=master]]
6
+ [[https://github.com/ddoherty03/fat_core/actions/workflows/ruby.yml][https://github.com/ddoherty03/fat_core/actions/workflows/ruby.yml/badge.svg?branch=master]]
7
7
 
8
- * README Setup Do First for Code Blocks
8
+ * README Setup Do First for Code Blocks :noexport:
9
9
  Run this block before all others to ensure that we are reading the libraries
10
10
  from the source directory.
11
11
 
@@ -15,7 +15,7 @@ from the source directory.
15
15
  $:.unshift("./lib") unless $:[0] == './lib'
16
16
  $:[0..10].each { |d| puts d }
17
17
  puts "..."
18
- require 'fat_core/all' # => true
18
+ require_relative 'lib/fat_core/all' # => true
19
19
  #+end_src
20
20
 
21
21
  #+RESULTS:
@@ -24,29 +24,85 @@ Current directory: /home/ded/src/fat_core
24
24
  Ruby LOADPATH:
25
25
  ./lib
26
26
  /home/ded/.rbenv/rbenv.d/exec/gem-rehash
27
- /home/ded/.rbenv/versions/3.4.1/lib/ruby/site_ruby/3.4.0
28
- /home/ded/.rbenv/versions/3.4.1/lib/ruby/site_ruby/3.4.0/x86_64-linux
29
- /home/ded/.rbenv/versions/3.4.1/lib/ruby/site_ruby
30
- /home/ded/.rbenv/versions/3.4.1/lib/ruby/vendor_ruby/3.4.0
31
- /home/ded/.rbenv/versions/3.4.1/lib/ruby/vendor_ruby/3.4.0/x86_64-linux
32
- /home/ded/.rbenv/versions/3.4.1/lib/ruby/vendor_ruby
33
- /home/ded/.rbenv/versions/3.4.1/lib/ruby/3.4.0
34
- /home/ded/.rbenv/versions/3.4.1/lib/ruby/3.4.0/x86_64-linux
27
+ /home/ded/.rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
28
+ /home/ded/.rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-linux
29
+ /home/ded/.rbenv/versions/3.2.2/lib/ruby/site_ruby
30
+ /home/ded/.rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
31
+ /home/ded/.rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-linux
32
+ /home/ded/.rbenv/versions/3.2.2/lib/ruby/vendor_ruby
33
+ /home/ded/.rbenv/versions/3.2.2/lib/ruby/3.2.0
34
+ /home/ded/.rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-linux
35
35
  ...
36
36
  #+end_example
37
37
 
38
38
 
39
+ * Table of Contents :toc:noexport:
40
+ - [[#version][Version]]
41
+ - [[#fatcore][FatCore]]
42
+ - [[#installation][Installation]]
43
+ - [[#usage][Usage]]
44
+ - [[#array][Array]]
45
+ - [[#method-comma_joinsep-nil-last_sep-nil-two_sep-nil][Method ~#comma_join(sep: nil, last_sep: nil, two_sep: nil)~]]
46
+ - [[#method-last_i][Method ~#last_i~]]
47
+ - [[#method-intersect_with_dups][Method ~#intersect_with_dups~]]
48
+ - [[#method-diff_with_dups][Method ~diff_with_dups~]]
49
+ - [[#bigdecimal-inspect][BigDecimal ~#inspect~]]
50
+ - [[#enumerable][Enumerable]]
51
+ - [[#method-each_with_flags][Method ~#each_with_flags~]]
52
+ - [[#hash][Hash]]
53
+ - [[#method-each_pair_with_flags][Method ~#each_pair_with_flags~]]
54
+ - [[#method-delete_with_value-and-delete_with_value][Method ~#delete_with_value~ and ~#delete_with_value!~]]
55
+ - [[#method-keys_with_value][Method ~#keys_with_value~]]
56
+ - [[#method-remap_keys][Method ~#remap_keys~]]
57
+ - [[#method-replace_keys][Method ~#replace_keys~]]
58
+ - [[#alias-merge-to-][Alias ~#merge~ to ~<<~]]
59
+ - [[#numeric][Numeric]]
60
+ - [[#method-signum][Method ~#signum~]]
61
+ - [[#method-commasplaces--nil][Method ~#commas(places = nil)~]]
62
+ - [[#methods-whole-and-int_if_whole][Methods ~#whole?~ and ~#int_if_whole~]]
63
+ - [[#method-secs_to_hms][Method ~#secs_to_hms~]]
64
+ - [[#range][Range]]
65
+ - [[#methods-contiguous-left_contiguous-right_contiguous][Methods ~#contiguous~, ~#left_contiguous~, ~#right_contiguous~]]
66
+ - [[#method-joinother][Method ~#join(other)~]]
67
+ - [[#method-spanned_byothers][Method ~#spanned_by?(others)~]]
68
+ - [[#methods-gapsothers-overlapsothers][Methods ~#gaps(others)~, ~#overlaps(others)~]]
69
+ - [[#string][String]]
70
+ - [[#method-fuzzy_match][Method ~#fuzzy_match~]]
71
+ - [[#method-matches_with][Method ~#matches_with~]]
72
+ - [[#method-entitle][Method ~#entitle~]]
73
+ - [[#method-distance][Method ~#distance~]]
74
+ - [[#method-commasplaces][Method ~#commas(places)~]]
75
+ - [[#method-wrapwidth-hang][Method =#wrap(width, hang)=]]
76
+ - [[#method-as_sym][Method =#as_sym=]]
77
+ - [[#symbol][Symbol]]
78
+ - [[#method-as_str][Method =#as_str=]]
79
+ - [[#tex-quoting][TeX Quoting]]
80
+ - [[#contributing][Contributing]]
81
+
82
+ * Version
83
+ #+begin_src ruby
84
+ require_relative './lib/fat_core/version'
85
+ "Current version is: #{FatCore::VERSION}"
86
+ #+end_src
87
+
88
+ #+begin_example
89
+ Current version is: 7.0.0
90
+ #+end_example
91
+
39
92
  * FatCore
40
93
 
41
- ~fat-core~ is a simple gem to collect core extensions and a few new classes
42
- that I find useful in multiple projects.
94
+ ~fat-core~ is somewhat of a grab bag of core class extensions that I have
95
+ found useful across several projects. It's higgeldy-piggeldy nature reflects
96
+ the fact that none of them are important enough to deserve a gem of their own,
97
+ but nonetheless need to be collected in one place to reduce redundancy across
98
+ projects and provide a focused place to develop and test them.
43
99
 
44
- ** Installation
100
+ * Installation
45
101
 
46
102
  Add this line to your application's Gemfile:
47
103
 
48
- #+begin_SRC ruby
49
- gem 'fat_core', :git => 'https://github.com/ddoherty03/fat_core.git'
104
+ #+begin_src ruby
105
+ gem 'fat_core'
50
106
  #+end_SRC
51
107
 
52
108
  And then execute:
@@ -61,7 +117,7 @@ Or install it yourself as:
61
117
  $ gem install fat_core
62
118
  #+end_src
63
119
 
64
- ** Usage
120
+ * Usage
65
121
 
66
122
  You can extend classes individually by requiring the corresponding file:
67
123
 
@@ -86,50 +142,642 @@ Or, you can require them all:
86
142
  Many of these have little that is of general interest, but there are a few
87
143
  goodies.
88
144
 
89
- *** Range
145
+ *** Array
146
+ **** Method ~#comma_join(sep: nil, last_sep: nil, two_sep: nil)~
147
+ Convert this array into a single string by (1) applying ~#to_s~ to each
148
+ element and (2) joining the elements with the string given by the ~sep:~
149
+ parameter. By default the sep parameter is ', '.
90
150
 
91
- You can also extend the Range class with several useful methods that emphasize
92
- coverage of one range by one or more others (~#spanned_by?~ and ~#gaps~),
93
- contiguity of Ranges to one another (~#contiguous?~, ~#left_contiguous?~, and
94
- ~#right_contiguous?~, ~#join~), and the testing of overlaps between ranges
95
- (~#overlaps?~, ~#overlaps_among?~). These are put to good use in the
96
- 'fat_period' ([[https://github.com/ddoherty03/fat_period]]) gem, which combines
97
- fat_core's extended Range class with its extended Date class to make a useful
98
- Period class for date ranges, and you may find fat_core's extended Range class
99
- likewise useful.
151
+ You may use a different separation string in the case when there are only two
152
+ items in the list by supplying a ~two_sep~ parameter.
100
153
 
101
- For example, you can use the ~#gaps~ method to find the gaps left in the
102
- coverage on one Range by an Array of other Ranges:
154
+ You may also supply a difference separation string to separate the second-last
155
+ and last items in the array by supplying a ~last_sep:~ parameter.
103
156
 
104
- #+begin_SRC ruby
105
- require 'fat_core/range'
106
- (0..12).gaps([(0..2), (5..7), (10..12)]) => [(3..4), (8..9)]
107
- #+end_SRC
157
+ By default, the sep parameter is the string ', ', the ~two_sep~ is ' and ',
158
+ and the ~last_sep~ is ', and ', all of which makes for a well-punctuated
159
+ English clause.
160
+
161
+ If ~sep~ is given, the other two parameters are set to its
162
+ value by default. If ~last_sep~ is given, ~two_sep~ takes its value by
163
+ default.
164
+
165
+ If the input array is empty, ~#comma_join~ returns an empty string.
166
+
167
+ #+begin_src ruby
168
+ require_relative 'lib/fat_core/array'
169
+
170
+ %w{hammers nails glue bolts}.comma_join
171
+ #+end_src
172
+
173
+ #+begin_example
174
+ hammers, nails, glue, and bolts
175
+ #+end_example
176
+
177
+ #+begin_src ruby
178
+ require_relative 'lib/fat_core/array'
179
+
180
+ %w{hammers nails}.comma_join
181
+ #+end_src
182
+
183
+ #+begin_example
184
+ hammers and nails
185
+ #+end_example
186
+
187
+ And, if you are ideologically opposed to the Oxford comma:
188
+
189
+ #+begin_src ruby
190
+ require_relative 'lib/fat_core/array'
191
+
192
+ %w{hammers nails glue bolts}.comma_join(last_sep: ' and ')
193
+ #+end_src
194
+
195
+ #+begin_example
196
+ hammers, nails, glue and bolts
197
+ #+end_example
198
+
199
+ **** Method ~#last_i~
200
+ Return the index of the last element of the Array.
201
+
202
+ #+begin_src ruby
203
+ require_relative 'lib/fat_core/array'
204
+
205
+ %w{hammers nails glue bolts}.last_i
206
+ #+end_src
207
+
208
+ #+begin_example
209
+ 3
210
+ #+end_example
211
+
212
+ **** Method ~#intersect_with_dups~
213
+ Return a new Array that is the intersection of this Array with all ~others~,
214
+ but without removing duplicates as the ~Array#&~ method does. All items of
215
+ this Array are included in the result but only if they also appear in all of
216
+ the other Arrays.
217
+
218
+ #+begin_src ruby
219
+ require_relative 'lib/fat_core/array'
220
+
221
+ a = %w{hammers nails glue bolts nails}
222
+ b = %w{nails fingers knuckles nails}
223
+ a.intersect_with_dups(b)
224
+ #+end_src
225
+
226
+ #+begin_example
227
+ | nails | nails |
228
+ #+end_example
229
+
230
+ **** Method ~diff_with_dups~
231
+ Return an Array that is the difference between this Array and =other=, but
232
+ without removing duplicates as the Array#- method does. All items of this
233
+ Array are included in the result /unless/ they also appear in any of the
234
+ =other= Arrays.
235
+
236
+ #+begin_src ruby
237
+ require_relative 'lib/fat_core/array'
238
+
239
+ a = %w{hammers nails glue bolts hammers nails}
240
+ b = %w{nails fingers knuckles nails}
241
+ a.diff_with_dups(b)
242
+ #+end_src
243
+
244
+ #+begin_example
245
+ | hammers | glue | bolts | hammers |
246
+ #+end_example
247
+
248
+ *** BigDecimal ~#inspect~
249
+ ~FatCore~ provides nothing but a better ~#inspect~ method for the ~BigDecimal~
250
+ class since the default inspect method is not very readable.
251
+
252
+ #+begin_src ruby
253
+ require_relative 'lib/fat_core/bigdecimal'
254
+
255
+ BigDecimal('2.1718281828').inspect
256
+ #+end_src
257
+
258
+ #+begin_example
259
+ 2.1718281828
260
+ #+end_example
261
+
262
+ Without ~FatCore~, the result is "0.2718281828e1", forcing you to interpret
263
+ the exponent to understand where the decimal place is.
108
264
 
109
265
  *** Enumerable
110
- FatCore::Enumerable extends Enumerable with the ~#each_with_flags~ method that
111
- yields the elements of the Enumerable but also yields two booleans, ~first~ and
112
- ~last~ that are set to true on respectively, the first and last element of the
113
- Enumerable. This makes it easy to treat these two cases specially without
114
- testing the index as in ~#each_with_index~.
266
+ **** Method ~#each_with_flags~
267
+ ~FatCore::Enumerable~ extends ~Enumerable~ with the ~#each_with_flags~ method
268
+ that yields the elements of the ~Enumerable~ but also yields two booleans,
269
+ ~first~ and ~last~ that are set to ~true~ on respectively, the first and last
270
+ element of the Enumerable and ~false~ otherwise. This makes it easy to treat
271
+ these two cases specially without testing the index as in ~#each_with_index~.
272
+
273
+ #+begin_src ruby
274
+ require_relative 'lib/fat_core/enumerable'
275
+
276
+ result = []
277
+ fibs = %w{1, 1, 2, 3, 5, 8, 13, 21}
278
+ fibs.each_with_flags do |f, first, last|
279
+ result <<
280
+ if first
281
+ ["Start", f]
282
+ elsif last
283
+ ["Last", f]
284
+ else
285
+ ["Continue", f]
286
+ end
287
+ end
288
+ result
289
+ #+end_src
290
+
291
+ #+begin_example
292
+ | Start | 1, |
293
+ | Continue | 1, |
294
+ | Continue | 2, |
295
+ | Continue | 3, |
296
+ | Continue | 5, |
297
+ | Continue | 8, |
298
+ | Continue | 13, |
299
+ | Last | 21 |
300
+ #+end_example
115
301
 
116
302
  *** Hash
303
+ FatCore::Hash extends the Hash class with some useful methods.
117
304
 
118
- FatCore::Hash extends the Hash class with some useful methods for element
119
- deletion (~#delete_with_value~) and for manipulating the keys
120
- (~#keys_with_value~, ~#remap_keys~ and ~#replace_keys~) of a Hash. It also
121
- provides ~#each_pair_with_flags~ as an analog to Enumerable's
122
- ~#each_with_flags~.
305
+ **** Method ~#each_pair_with_flags~
306
+ As with the extension for ~Enumerables~, ~FatCore~ provides a method for
307
+ enumerating the key-value pair of the ~Hash~ with flags that are set ~true~
308
+ for the first and last elements but ~false~ otherwise:
123
309
 
124
- It also provides the shovel operator as a convenient alias for ~Hash#merge~,
125
- so that
310
+ #+begin_src ruby
311
+ require_relative './lib/fat_core/hash'
126
312
 
127
- #+begin_src ruby :tangle no
128
- {a: 'A', b: 'B', c: 'C'} << {c: 'CC', d: 'DD'} << {e: 'EEE'} => {a: 'A', b: 'B', c: 'CC', d: 'DD', e: 'EEE'}
313
+ h = {'Chaucer' => 'Cantebury Tales', 'Shakespeare' => 'The Merchant of Venice',
314
+ 'Austen' => 'Pride and Prejudice', 'C. Brontë' => 'Jane Eyre',
315
+ 'E. Brontë' => 'Wuthering Heights' }
316
+ result = []
317
+ result << ['Position', 'Author', 'Novel']
318
+ result << nil
319
+ h.each_pair_with_flags do |k, v, first, last|
320
+ pos=
321
+ if first
322
+ 'Begin'
323
+ elsif last
324
+ 'End'
325
+ else
326
+ 'Middle'
327
+ end
328
+ result << [pos, k, v]
329
+ end
330
+ result
129
331
  #+end_src
130
332
 
131
- *** String
333
+ #+begin_example
334
+ | Position | Author | Novel |
335
+ |----------+-------------+------------------------|
336
+ | Begin | Chaucer | Cantebury Tales |
337
+ | Middle | Shakespeare | The Merchant of Venice |
338
+ | Middle | Austen | Pride and Prejudice |
339
+ | Middle | C. Brontë | Jane Eyre |
340
+ | End | E. Brontë | Wuthering Heights |
341
+ #+end_example
342
+
343
+ **** Method ~#delete_with_value~ and ~#delete_with_value!~
344
+ This method modifies a ~Hash~ by deleting the key-value pairs when the value
345
+ equals the given value or values:
346
+
347
+ #+begin_src ruby :results output
348
+ require_relative './lib/fat_core/hash'
349
+
350
+ h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
351
+ h.delete_with_value!(2)
352
+ puts h
353
+ #+end_src
354
+
355
+ #+begin_example
356
+ {:a=>1, :c=>3, :e=>1}
357
+ #+end_example
358
+
359
+ You can supply multiple values for deletion:
360
+
361
+ #+begin_src ruby :results output
362
+ require_relative './lib/fat_core/hash'
363
+
364
+ h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
365
+ h.delete_with_value!(1, 3)
366
+ puts h
367
+ #+end_src
368
+
369
+ #+begin_example
370
+ {:b=>2, :d=>2}
371
+ #+end_example
372
+
373
+ The non-bang method returns a clone of the Hash with the given deletions made:
374
+
375
+ #+begin_src ruby :results output
376
+ require_relative './lib/fat_core/hash'
132
377
 
378
+ h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
379
+ h2 = h.delete_with_value(1, 3)
380
+ puts h
381
+ puts h2
382
+ #+end_src
383
+
384
+ #+begin_example
385
+ {:a=>1, :b=>2, :c=>3, :d=>2, :e=>1}
386
+ {:b=>2, :d=>2}
387
+ #+end_example
388
+
389
+ **** Method ~#keys_with_value~
390
+ Return an ~Array~ of keys of the ~Hash~ with a value ~==~ to the given value
391
+ or values.
392
+
393
+ #+begin_src ruby :results output
394
+ require_relative './lib/fat_core/hash'
395
+
396
+ h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
397
+ puts h.keys_with_value(1).inspect
398
+ puts h.keys_with_value(2, 3).inspect
399
+ #+end_src
400
+
401
+ #+begin_example
402
+ [:a, :e]
403
+ [:b, :d, :c]
404
+ #+end_example
405
+
406
+ **** Method ~#remap_keys~
407
+ This method pre-dates the new ~#transform_keys~ method now available for
408
+ ~Hash~, but it is kept as an alternative. It takes a ~Hash~ as an argument
409
+ that maps existing keys to their replacement in the resulting ~Hash~. The
410
+ original ~Hash~ is not effected.
411
+
412
+ #+begin_src ruby :results output
413
+ require_relative './lib/fat_core/hash'
414
+
415
+ h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
416
+ puts h.remap_keys({:a => :A, :b => :B}).inspect
417
+ #+end_src
418
+
419
+ #+begin_example
420
+ {:A=>1, :B=>2, :c=>3, :d=>2, :e=>1}
421
+ #+end_example
422
+
423
+ These days, a more systematic job could be done with ~#transform_keys~:
424
+ #+begin_src ruby :results output
425
+ h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
426
+ puts h.transform_keys { |k| k.to_s.upcase.to_sym }.inspect
427
+ #+end_src
428
+
429
+ #+begin_example
430
+ {:A=>1, :B=>2, :C=>3, :D=>2, :E=>1}
431
+ #+end_example
432
+
433
+ **** Method ~#replace_keys~
434
+ A wholesale replacement of the existing keys can be done with this method:
435
+
436
+ #+begin_src ruby :results output
437
+ require_relative './lib/fat_core/hash'
438
+
439
+ h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
440
+ puts h
441
+ puts h.replace_keys([:z, :y, :x, :w, :v]).inspect
442
+ #+end_src
443
+
444
+ #+begin_example
445
+ {:a=>1, :b=>2, :c=>3, :d=>2, :e=>1}
446
+ {:z=>1, :y=>2, :x=>3, :w=>2, :v=>1}
447
+ #+end_example
448
+
449
+ **** Alias ~#merge~ to ~<<~
450
+ Finally, ~FatCore~ adds the "shovel" operator as an alias for ~#merge~ to
451
+ provide a pretty way to represent the merger of the right ~Hash~ into the left
452
+ ~Hash~:
453
+
454
+ #+begin_src ruby :results output
455
+ require_relative './lib/fat_core/hash'
456
+
457
+ h = {a: 'A', b: 'B', c: 'C'} << {c: 'CC', d: 'DD'} << {d: 'DDD', e: 'EEE'}
458
+ puts h
459
+ #+end_src
460
+
461
+ #+begin_example
462
+ {:a=>"A", :b=>"B", :c=>"CC", :d=>"DDD", :e=>"EEE"}
463
+ #+end_example
464
+
465
+ It groups values into pairs and applies the ~#to_h~ method to the right-hand
466
+ argument if it is an ~Enumerable~, so it also works if the right-hand argument
467
+ is an ~Array~ or ~Enumerable~:
468
+
469
+ #+begin_src ruby :results output
470
+ require 'fileutils'
471
+ require_relative './lib/fat_core/hash'
472
+
473
+ FileUtils.mkdir_p('./tmp')
474
+ ff = File.open('./tmp/junk', 'w')
475
+ ff.write("f\n", "FFFF\n", "g\n", "GGGG\n")
476
+ ff.close
477
+ ff = File.open('./tmp/junk', 'r')
478
+ h = {a: 'A', b: 'B', c: 'C'} <<
479
+ [:c, 'CC', :d, 'DD'] <<
480
+ {d: 'DDD', e: 'EEE'} <<
481
+ ff.readlines.map(&:chomp) <<
482
+ [[:h, 'HHHHH'], [:j, 'JJJJJ']]
483
+ # h.transform_keys!(&:to_sym)
484
+ ff.close
485
+ FileUtils.rm_rf('./tmp/junk')
486
+ puts h
487
+ #+end_src
488
+
489
+ #+begin_example
490
+ {:a=>"A", :b=>"B", :c=>"CC", :d=>"DDD", :e=>"EEE", "f"=>"FFFF", "g"=>"GGGG", :h=>"HHHHH", :j=>"JJJJJ"}
491
+ #+end_example
492
+
493
+ *** Numeric
494
+ **** Method ~#signum~
495
+ Return ~-1~ for negative numbers, ~0~ for zero, and ~+1~ for positive numbers.
496
+ This is sometimes handy.
497
+
498
+ **** Method ~#commas(places = nil)~
499
+ To get s ~String~ representation of a ~Numeric~ with grouping commas inserted,
500
+ ~FatCore~ provides the ~#commas~ method:
501
+
502
+ #+begin_src ruby
503
+ require_relative 'lib/fat_core/numeric'
504
+ result = []
505
+ result << ['N', 'Places', 'N.commas(places)']
506
+ result << nil
507
+ nums = [3.14159, 2.718281828, 100000, 0.0059, 16236565468798.66877]
508
+ places = [0, 3, 5]
509
+ nums.each do |n|
510
+ places.each do |pl|
511
+ result << [n, pl, n.commas(pl)]
512
+ end
513
+ end
514
+ result
515
+ #+end_src
516
+
517
+ #+begin_example
518
+ | N | Places | N.commas(places) |
519
+ |--------------------+--------+--------------------------|
520
+ | 3.14159 | 0 | 3 |
521
+ | 3.14159 | 3 | 3.142 |
522
+ | 3.14159 | 5 | 3.14159 |
523
+ | 2.718281828 | 0 | 3 |
524
+ | 2.718281828 | 3 | 2.718 |
525
+ | 2.718281828 | 5 | 2.71828 |
526
+ | 100000 | 0 | 100,000 |
527
+ | 100000 | 3 | 100,000.000 |
528
+ | 100000 | 5 | 100,000.00000 |
529
+ | 0.0059 | 0 | 0 |
530
+ | 0.0059 | 3 | 0.006 |
531
+ | 0.0059 | 5 | 0.00590 |
532
+ | 16236565468798.668 | 0 | 16,236,565,468,799 |
533
+ | 16236565468798.668 | 3 | 16,236,565,468,798.668 |
534
+ | 16236565468798.668 | 5 | 16,236,565,468,798.66800 |
535
+ #+end_example
536
+
537
+ FatCore::Numeric has methods for inserting grouping commas into a number
538
+ (~#commas~ and ~#group~), for converting seconds to HH:MM:SS.dd format
539
+ (~#secs_to_hms~), for testing for integrality (~#whole?~ and ~#int_if_whole~), and
540
+ testing for sign (~#signum~).
541
+
542
+ **** Methods ~#whole?~ and ~#int_if_whole~
543
+ At times it is useful to know if a Float or BigDecimal can be converted to an
544
+ ~Integer~ without losing precision.
545
+
546
+ #+begin_src ruby
547
+ require_relative 'lib/fat_core/numeric'
548
+ result = []
549
+ result << ['N', '#whole?', '#int_if_whole', 'Classes']
550
+ result << nil
551
+ nums = [3.14159, 3.000000, 100000, 0.0059, 16236565468798.66877]
552
+ nums.each do |n|
553
+ result << [n, n.whole?, n.int_if_whole, "#{n.class} -> #{n.int_if_whole.class}"]
554
+ end
555
+ result
556
+ #+end_src
557
+
558
+ #+begin_example
559
+ | N | #whole? | #int_if_whole | Classes |
560
+ |--------------------+---------+--------------------+--------------------|
561
+ | 3.14159 | false | 3.14159 | Float -> Float |
562
+ | 3.0 | true | 3 | Float -> Integer |
563
+ | 100000 | true | 100000 | Integer -> Integer |
564
+ | 0.0059 | false | 0.0059 | Float -> Float |
565
+ | 16236565468798.668 | false | 16236565468798.668 | Float -> Float |
566
+ #+end_example
567
+
568
+ **** Method ~#secs_to_hms~
569
+ This method converts a numeric representing a number of seconds or an angle in
570
+ degrees to a ~String~ of the form "HH:MM:SS" representing the same quantity in
571
+ hours, minutes, and seconds.
572
+
573
+ #+begin_src ruby
574
+ require_relative 'lib/fat_core/numeric'
575
+ result = []
576
+ result << ['N', 'HH:MM:SS']
577
+ result << nil
578
+ nums = [85777.66, 959.66, -1198.33, 0, 3.14159 * 180]
579
+ nums.each do |n|
580
+ result << [n, n.secs_to_hms]
581
+ end
582
+ result
583
+ #+end_src
584
+
585
+ #+begin_example
586
+ | N | HH:MM:SS |
587
+ |-------------------+-------------|
588
+ | 85777.66 | 23:49:37.66 |
589
+ | 959.66 | 00:15:59.66 |
590
+ | -1198.33 | -1:40:01.67 |
591
+ | 0 | 00:00:00 |
592
+ | 565.4861999999999 | 00:09:25.48 |
593
+ #+end_example
594
+
595
+
596
+ *** Range
597
+ ~FatCore~ can also extend the Range class with several useful methods that
598
+ emphasize coverage of one range by one or more others (~#spanned_by?~ and
599
+ ~#gaps~), contiguity of Ranges to one another (~#contiguous?~,
600
+ ~#left_contiguous?~, and ~#right_contiguous?~, ~#join~), and the testing of
601
+ overlaps between ranges (~#overlaps?~, ~#overlaps_among?~). These are put to
602
+ good use in the 'fat_period' ([[https://github.com/ddoherty03/fat_period]]) gem,
603
+ which combines fat_core's extended Range class with its extended Date class to
604
+ make a useful Period class for date ranges, and you may find fat_core's
605
+ extended Range class likewise useful.
606
+
607
+ **** Methods ~#contiguous~, ~#left_contiguous~, ~#right_contiguous~
608
+ These methods determine whether the subject ~Range~ are "contiguous" with
609
+ another ~Range~ on the left, right, or either side. The notion of contiguity
610
+ is different for ~Ranges~ whose min and max values respond to the ~#succ~
611
+ method: if they do, "contiguity" only requires that the ~#succ~ of the max
612
+ value of the left range equal the min value of the right ~Range~; otherwise
613
+ the max value of the left ~Range~ must equal the min value of the right
614
+ ~Range~.
615
+
616
+ #+begin_src ruby
617
+ require_relative 'lib/fat_core/range'
618
+ require 'date'
619
+
620
+ result = []
621
+ result << ["Self", "Other", "Contiguous?", "Right?", "Left?"]
622
+ result << nil
623
+ pairs = [
624
+ [(0..10), (11..12)],
625
+ [(11..20), (0..10)],
626
+ [(0..10), (15..20)],
627
+ [(3.145..12.3), (0.5..3.145)],
628
+ [(3.146..12.3), (0.5..3.145)],
629
+ [('a'..'q'), ('r'..'z')],
630
+ [('a'..'q'), ('s'..'z')],
631
+ [(Date.parse('1963-11-22')..Date.parse('1964-11-03')), (Date.parse('1964-11-04')..Date.today)],
632
+ [(Date.parse('1963-11-22')..Date.parse('1964-11-03')), (Date.parse('1964-11-28')..Date.today)]
633
+ ]
634
+ pairs.each do |r1, r2|
635
+ result << [r1.to_s, r2.to_s, r1.contiguous?(r2), r1.right_contiguous?(r2), r1.left_contiguous?(r2)]
636
+ end
637
+ result
638
+ #+end_src
639
+
640
+ #+begin_example
641
+ | Self | Other | Contiguous? | Right? | Left? |
642
+ |------------------------+------------------------+-------------+--------+-------|
643
+ | 0..10 | 11..12 | true | true | false |
644
+ | 11..20 | 0..10 | true | false | true |
645
+ | 0..10 | 15..20 | false | false | false |
646
+ | 3.145..12.3 | 0.5..3.145 | true | false | true |
647
+ | 3.146..12.3 | 0.5..3.145 | false | false | false |
648
+ | a..q | r..z | true | true | false |
649
+ | a..q | s..z | false | false | false |
650
+ | 1963-11-22..1964-11-03 | 1964-11-04..2025-11-22 | true | true | false |
651
+ | 1963-11-22..1964-11-03 | 1964-11-28..2025-11-22 | false | false | false |
652
+ #+end_example
653
+
654
+ **** Method ~#join(other)~
655
+ If ~self~ is contiguous with ~other~, return a new ~Range~ that splices the
656
+ two ~Range~s into one ~Range~.
657
+
658
+ #+begin_src ruby
659
+ require_relative 'lib/fat_core/range'
660
+ require 'date'
661
+
662
+ result = []
663
+ result << ["Self", "Other", "Contiguous?", "Joined"]
664
+ result << nil
665
+ pairs = [
666
+ [(0..10), (11..12)],
667
+ [(11..20), (0..10)],
668
+ [(0..10), (15..20)],
669
+ [(3.145..12.3), (0.5..3.145)],
670
+ [(3.146..12.3), (0.5..3.145)],
671
+ [('a'..'q'), ('r'..'z')],
672
+ [('a'..'q'), ('s'..'z')],
673
+ [(Date.parse('1963-11-22')..Date.parse('1964-11-03')), (Date.parse('1964-11-04')..Date.today)],
674
+ [(Date.parse('1963-11-22')..Date.parse('1964-11-03')), (Date.parse('1964-11-28')..Date.today)]
675
+ ]
676
+ pairs.each do |r1, r2|
677
+ result << [r1.to_s, r2.to_s, r1.contiguous?(r2), "#{r1.join(r2)}"]
678
+ end
679
+ result
680
+ #+end_src
681
+
682
+ #+begin_example
683
+ | Self | Other | Contiguous? | Joined |
684
+ |------------------------+------------------------+-------------+------------------------|
685
+ | 0..10 | 11..12 | true | 0..12 |
686
+ | 11..20 | 0..10 | true | 0..20 |
687
+ | 0..10 | 15..20 | false | |
688
+ | 3.145..12.3 | 0.5..3.145 | true | 0.5..12.3 |
689
+ | 3.146..12.3 | 0.5..3.145 | false | |
690
+ | a..q | r..z | true | a..z |
691
+ | a..q | s..z | false | |
692
+ | 1963-11-22..1964-11-03 | 1964-11-04..2025-11-22 | true | 1963-11-22..2025-11-22 |
693
+ | 1963-11-22..1964-11-03 | 1964-11-28..2025-11-22 | false | |
694
+ #+end_example
695
+
696
+
697
+ **** Method ~#spanned_by?(others)~
698
+ A set of ~Ranges~ "spans" a given ~Range~ if the set is contiguous and fully
699
+ covers the given ~Range~ with no overlaps and no gaps. A set that over-covers
700
+ the given ~Range~ is still considered to span it, even though it is wider than
701
+ the given ~Range~. In other words, a set spans the given ~Range~ if the set
702
+ can be joined and the given ~Range~ is within the joined ~Range~.
703
+
704
+ #+begin_src ruby
705
+ require_relative 'lib/fat_core/range'
706
+ require 'date'
707
+
708
+ result = []
709
+ result << ["Self", "Others", "Spanned By?"]
710
+ result << nil
711
+ pairs = [
712
+ [(0..10), [(-1..5), (6..10)]],
713
+ [(1..20), [(0..10), (11..20)]],
714
+ [(1..20), [(0..10), (10..20)]],
715
+ [(3.145..12.3), [(0.5..3.45), (3.45..10.5), (10.5..13.5)]],
716
+ [(3.145..12.3), [(0.5..3.45), (3.45..10.5), (10.6..13.5)]],
717
+ [('a'..'z'), [('a'..'g'), ('h'..'s'), ('t'..'z')]],
718
+ [('a'..'z'), [('a'..'g'), ('j'..'s'), ('t'..'z')]],
719
+ ]
720
+ pairs.each do |r, others|
721
+ result << ["#{r}", "#{others}", r.spanned_by?(others)]
722
+ end
723
+ result
724
+ #+end_src
725
+
726
+ #+begin_example
727
+ | Self | Others | Spanned By? |
728
+ |-------------+-------------------------------------+-------------|
729
+ | 0..10 | [-1..5, 6..10] | true |
730
+ | 1..20 | [0..10, 11..20] | true |
731
+ | 1..20 | [0..10, 10..20] | false |
732
+ | 3.145..12.3 | [0.5..3.45, 3.45..10.5, 10.5..13.5] | true |
733
+ | 3.145..12.3 | [0.5..3.45, 3.45..10.5, 10.6..13.5] | false |
734
+ | a..z | ["a".."g", "h".."s", "t".."z"] | true |
735
+ | a..z | ["a".."g", "j".."s", "t".."z"] | false |
736
+ #+end_example
737
+
738
+ **** Methods ~#gaps(others)~, ~#overlaps(others)~
739
+ When the set of other ~Ranges~ does not span the given ~Range~, these methods
740
+ return an set of ~Ranges~ that represent the portions of the given ~Range~ no
741
+ covered by the ~others~, the "gaps", or the points within the given ~Range~
742
+ where the ~others~ overlap one another and thus are not contiguous.
743
+
744
+ #+begin_src ruby
745
+ require_relative 'lib/fat_core/range'
746
+ require 'date'
747
+
748
+ result = []
749
+ result << ["Self", "Others", "Spanned By?", "Gaps", "Overlaps"]
750
+ result << nil
751
+ pairs = [
752
+ [(0..10), [(-1..5), (6..10)]],
753
+ [(1..20), [(0..10), (11..20)]],
754
+ [(1..20), [(0..15), (11..20)]],
755
+ [(1..20), [(0..10), (10..20)]],
756
+ [(3.145..12.3), [(0.5..3.45), (3.45..10.5), (10.5..13.5)]],
757
+ [(3.145..12.3), [(0.5..3.45), (3.45..10.5), (10.6..13.5)]],
758
+ [('a'..'z'), [('a'..'g'), ('h'..'s'), ('t'..'z')]],
759
+ [('a'..'z'), [('a'..'g'), ('j'..'s'), ('t'..'z')]],
760
+ ]
761
+ pairs.each do |r, others|
762
+ result << ["#{r}", "#{others}", r.spanned_by?(others), "#{r.gaps(others)}", "#{r.overlaps(others)}"]
763
+ end
764
+ result
765
+ #+end_src
766
+
767
+ #+begin_example
768
+ | Self | Others | Spanned By? | Gaps | Overlaps |
769
+ |-------------+-------------------------------------+-------------+--------------+----------|
770
+ | 0..10 | [-1..5, 6..10] | true | [] | [] |
771
+ | 1..20 | [0..10, 11..20] | true | [] | [] |
772
+ | 1..20 | [0..15, 11..20] | false | [] | [11..15] |
773
+ | 1..20 | [0..10, 10..20] | false | [] | [] |
774
+ | 3.145..12.3 | [0.5..3.45, 3.45..10.5, 10.5..13.5] | true | [] | [] |
775
+ | 3.145..12.3 | [0.5..3.45, 3.45..10.5, 10.6..13.5] | false | [10.5..10.6] | [] |
776
+ | a..z | ["a".."g", "h".."s", "t".."z"] | true | [] | [] |
777
+ | a..z | ["a".."g", "j".."s", "t".."z"] | false | ["h".."i"] | [] |
778
+ #+end_example
779
+
780
+ *** String
133
781
  FatCore::String has methods for performing matching of one string with another
134
782
  (~#matches_with~, ~#fuzzy_match~), for converting a string to title-case as
135
783
  might by used in the title of a book (~#entitle~), for converting a String
@@ -138,21 +786,333 @@ into a useable Symbol (~#as_sym~) and vice-versa (~#as_str~ also
138
786
  cleaning up errant spaces (~#clean~), and computing the Damerau-Levenshtein
139
787
  distance between strings (~#distance~). And several others.
140
788
 
789
+ **** Method ~#fuzzy_match~
790
+ The ~#fuzzy_match~ method determines whether the subject string matches the
791
+ given "matcher" string, which provides a simple syntax that allows a limited
792
+ kind of pattern matching. If there is a match, it returns the matched portion
793
+ of self, minus punctuation characters, if self matches the string, and returns
794
+ nil otherwise.
795
+
796
+ What makes this handy is that a user trying to match by memory can be loose
797
+ about case, punctuation, and spaces, and still find desired matches. In the
798
+ matcher both the space and colon ':' have special meaning as shown below.
799
+
800
+ ~#fuzzy_match(matcher)~ uses the following rules for matching:
801
+
802
+ 1. Remove leading and trailing whitespace in the subject and the matcher
803
+ and collapse its internal whitespace to a single space,
804
+ 2. Remove all periods, commas, apostrophes, and asterisks (the
805
+ punctuation characters) from both self and `matcher`,
806
+ 3. Treat internal ':stuff' or ' :stuff' in the matcher as the equivalent
807
+ of /\bstuff.*/ in a regular expression, that is, match any word
808
+ starting with stuff in self,
809
+ 4. Treat internal 'stuff: ' in the matcher as the equivalent of /.*stuff\b/ in
810
+ a regular expression, that is, match any word ending with stuff in self,
811
+ 5. A colon with no spaces around it is treated as belonging to the
812
+ following word, requiring it to start with it, so 'some:stuff'
813
+ requires 'some' anywhere followed by a word beginning with 'stuff',
814
+ i.e., /some.*\bstuff/i,
815
+ 6. Treat leading ':' in the matcher as anchoring the match to the
816
+ beginning of the target string,
817
+ 7. Treat ending ':' in the matcher as anchoring the match to the
818
+ end of the target string,
819
+ 8. Require each component to match some part of self, and
820
+
821
+ #+begin_src ruby
822
+ require_relative './lib/fat_core/string'
823
+
824
+ result = []
825
+ result << ['Self', 'Matcher', 'Match']
826
+ result << nil
827
+ subj = "St. Luke's Hospital"
828
+ matchers = ['st lukes', 'luk:hosp', 'st:spital', 'uk spital', 'st:laks', ':lukes', 's lukes', 'lukes:hospital']
829
+ matchers.each do |m|
830
+ result << [subj, m, subj.fuzzy_match(m)]
831
+ end
832
+ result
833
+ #+end_src
834
+
835
+ #+begin_example
836
+ | Self | Matcher | Match |
837
+ |---------------------+----------------+----------------|
838
+ | St. Luke's Hospital | st lukes | St Lukes |
839
+ | St. Luke's Hospital | luk:hosp | Lukes Hosp |
840
+ | St. Luke's Hospital | st:spital | nil |
841
+ | St. Luke's Hospital | uk spital | ukes Hospital |
842
+ | St. Luke's Hospital | st:laks | nil |
843
+ | St. Luke's Hospital | :lukes | nil |
844
+ | St. Luke's Hospital | s lukes | St Lukes |
845
+ | St. Luke's Hospital | lukes:hospital | Lukes Hospital |
846
+ #+end_example
847
+
848
+ **** Method ~#matches_with~
849
+ The ~#matches_with(matcher)~ method allows the use of either a regular
850
+ expression or fuzzy matching as described above depending on whether the
851
+ matcher is enclosed in '/' characters. It also returns the matched portion of
852
+ ~self~ or nil if there is no match. Even when a regex is given, the match is
853
+ case insensitive by default and commas, apostrophes, and periods are removed
854
+ from the subject string before matching.
855
+
856
+ #+begin_src ruby
857
+ require_relative './lib/fat_core/string'
858
+
859
+ result = []
860
+ result << ['Self', 'Matcher', 'Match']
861
+ result << nil
862
+ subj = "St. Luke's Hospital"
863
+ matchers = ['st lukes', '/luk.*hosp/', 'st:spital', '/u.*s\b/', 'st:laks', ':lukes', 's lukes', '/lukes hospital\z/']
864
+ matchers.each do |m|
865
+ result << [subj, m, subj.matches_with(m)]
866
+ end
867
+ result
868
+ #+end_src
869
+
870
+ #+begin_example
871
+ | Self | Matcher | Match |
872
+ |---------------------+--------------------+----------------|
873
+ | St. Luke's Hospital | st lukes | St Lukes |
874
+ | St. Luke's Hospital | /luk.*hosp/ | Lukes Hosp |
875
+ | St. Luke's Hospital | st:spital | nil |
876
+ | St. Luke's Hospital | /u.*s\b/ | ukes |
877
+ | St. Luke's Hospital | st:laks | nil |
878
+ | St. Luke's Hospital | :lukes | nil |
879
+ | St. Luke's Hospital | s lukes | St Lukes |
880
+ | St. Luke's Hospital | /lukes hospital\z/ | Lukes Hospital |
881
+ #+end_example
882
+
883
+ **** Method ~#entitle~
884
+ For a string meant to serve as the title of a book, song, or other item, there
885
+ are certain rules in English as to which words should be capitalized and which
886
+ should be put in lower case. "PROFILES IN courage" should be rendered
887
+ "Profiles in Courage" for example. The preposition "in" is typically not
888
+ capitalized unless it starts the title: "in the HEAT OF THE NIght" should be
889
+ something like "In the Heat of the Night".
890
+
891
+ #+begin_src ruby
892
+ require_relative './lib/fat_core/string'
893
+
894
+ result = []
895
+ result << ['Self', 'Entitled']
896
+ result << nil
897
+ titles = ['PROFILES IN courage', 'in the HEAT OF THE NIght', 'a day in the life', 'FROM HERE TO ETERNITY',
898
+ 'lucy in the sky with diamonds']
899
+ titles.each do |t|
900
+ result << [t, t.entitle]
901
+ end
902
+ result
903
+ #+end_src
904
+
905
+ #+begin_example
906
+ | Self | Entitled |
907
+ |-------------------------------+-------------------------------|
908
+ | PROFILES IN courage | Profiles in Courage |
909
+ | in the HEAT OF THE NIght | In the Heat of the Night |
910
+ | a day in the life | A Day in the Life |
911
+ | FROM HERE TO ETERNITY | From Here to Eternity |
912
+ | lucy in the sky with diamonds | Lucy in the Sky With Diamonds |
913
+ #+end_example
914
+
915
+
916
+ **** Method ~#distance~
917
+ ~FatCore~ provides ~distance~ as a simple wrapper around the
918
+ Damerau-Levenshtein distance function in ~damerau-levenshtein~ gem, using a
919
+ block size of 1 and a max distance of 10.
920
+
921
+ #+begin_src ruby
922
+ require_relative './lib/fat_core/string'
923
+
924
+ result = []
925
+ result << ['Word1', 'Word2', 'Distance']
926
+ result << nil
927
+ pairs = [['Shelf', 'Shell'], ['Shelf', 'Shall'], ['Doherty', 'Daughtery'], ['Doherty', 'Dorrit'], ['Smith', 'Jones']]
928
+ pairs.each do |w1, w2|
929
+ result << [w1, w2, w1.distance(w2)]
930
+ end
931
+ result
932
+ #+end_src
933
+
934
+ #+begin_example
935
+ | Word1 | Word2 | Distance |
936
+ |---------+-----------+----------|
937
+ | Shelf | Shell | 1 |
938
+ | Shelf | Shall | 2 |
939
+ | Doherty | Daughtery | 5 |
940
+ | Doherty | Dorrit | 4 |
941
+ | Smith | Jones | 5 |
942
+ #+end_example
943
+
944
+ **** Method ~#commas(places)~
945
+ When presenting numbers, it is common to want to add grouping digits to make
946
+ the numbers more readable. The ~commas(places)~ method does this be
947
+ converting the number into a Float, rounding to places digits, then converting
948
+ back to a ~String~ with grouping commas inserted.
949
+
950
+ #+begin_src ruby
951
+ require_relative './lib/fat_core/string'
952
+
953
+ result = []
954
+ result << ['N', 'Places', 'With Commas']
955
+ result << nil
956
+ nums_places = [["798964655.66541325", 3], ["798964655.66541325", 0], ["798964655.66541325", 5], ["3.14159", 3],
957
+ ["3.14159e6", 3], ["-3.14159e4", 2], ["+3.14159e3", 2]]
958
+ nums_places.each do |n, p|
959
+ result << [n, p, n.commas(p)]
960
+ end
961
+ result
962
+ #+end_src
963
+
964
+ #+begin_example
965
+ | N | Places | With Commas |
966
+ |--------------------+--------+-------------------|
967
+ | 798964655.66541325 | 3 | 798,964,655.665 |
968
+ | 798964655.66541325 | 0 | 798,964,656 |
969
+ | 798964655.66541325 | 5 | 798,964,655.66541 |
970
+ | 3.14159 | 3 | 3.142 |
971
+ | 3.14159e6 | 3 | 3,141,590.000 |
972
+ | -3.14159e4 | 2 | -31,415.90 |
973
+ | +3.14159e3 | 2 | 3,141.59 |
974
+ #+end_example
975
+
976
+
977
+ **** Method =#wrap(width, hang)=
978
+ This method wraps the string to a given width with an optional hanging indent
979
+ for lines after the first.
980
+ #+begin_src ruby
981
+ require_relative './lib/fat_core/string'
982
+
983
+ getty = <<~EOS
984
+ Four score and seven years ago our fathers brought forth on this continent,
985
+ a new nation, conceived in Liberty, and dedicated to the proposition that
986
+ all men are created equal.
987
+
988
+ Now we are engaged in a great civil war, testing whether that nation, or any
989
+ nation so conceived and so dedicated, can long endure. We are met on a
990
+ great battle-field of that war. We have come to dedicate a portion of that
991
+ field, as a final resting place for those who here gave their lives that
992
+ that nation might live. It is altogether fitting and proper that we should
993
+ do this.
994
+
995
+ But, in a larger sense, we can not dedicate---we can not consecrate---we can
996
+ not hallow---this ground. The brave men, living and dead, who struggled
997
+ here, have consecrated it, far above our poor power to add or detract. The
998
+ world will little note, nor long remember what we say here, but it can never
999
+ forget what they did here. It is for us the living, rather, to be dedicated
1000
+ here to the unfinished work which they who fought here have thus far so
1001
+ nobly advanced. It is rather for us to be here dedicated to the great task
1002
+ remaining before us---that from these honored dead we take increased
1003
+ devotion to that cause for which they gave the last full measure of
1004
+ devotion---that we here highly resolve that these dead shall not have died
1005
+ in vain---that this nation, under God, shall have a new birth of
1006
+ freedom---and that government of the people, by the people, for the people,
1007
+ shall not perish from the earth.
1008
+ EOS
1009
+ getty.wrap(110, 3)
1010
+ #+end_src
1011
+
1012
+ #+begin_example
1013
+ Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived
1014
+ in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged
1015
+ in a great civil war, testing whether that nation, or any nation so conceived and so dedicated,
1016
+ can long endure. We are met on a great battle-field of that war. We have come to dedicate
1017
+ a portion of that field, as a final resting place for those who here gave their lives that
1018
+ that nation might live. It is altogether fitting and proper that we should do this. But, in
1019
+ a larger sense, we can not dedicate---we can not consecrate---we can not hallow---this ground.
1020
+ The brave men, living and dead, who struggled here, have consecrated it, far above our poor
1021
+ power to add or detract. The world will little note, nor long remember what we say here, but
1022
+ it can never forget what they did here. It is for us the living, rather, to be dedicated here
1023
+ to the unfinished work which they who fought here have thus far so nobly advanced. It is rather
1024
+ for us to be here dedicated to the great task remaining before us---that from these honored
1025
+ dead we take increased devotion to that cause for which they gave the last full measure of
1026
+ devotion---that we here highly resolve that these dead shall not have died in vain---that this
1027
+ nation, under God, shall have a new birth of freedom---and that government of the people, by
1028
+ the people, for the people, shall not perish from the earth.
1029
+ #+end_example
1030
+
1031
+ **** Method =#as_sym=
1032
+ Convert a ~String~ to a ~Symbol~ by converting all letters to lower-case,
1033
+ replacing hyphens and white space with a single underscore, and removing all
1034
+ non-alphanumeric characters:
1035
+
1036
+ #+begin_src ruby
1037
+ require_relative './lib/fat_core/string'
1038
+
1039
+ " Hello-to-the World!!!".as_sym
1040
+ #+end_src
1041
+
1042
+ #+begin_example
1043
+ :hello_to_the_world
1044
+ #+end_example
1045
+
1046
+ *** Symbol
1047
+ **** Method =#as_str=
1048
+ A quasi-inverse of ~String#as_sym~, convert a ~Symbol~ into a ~String~ by
1049
+ converting '_' to a hyphen, white-space into '_', and eliminate any
1050
+ non-alphanumeric characters.
1051
+
1052
+ #+begin_src ruby
1053
+ require_relative 'lib/fat_core/symbol'
1054
+
1055
+ :hello_to_the_world.as_str
1056
+ #+end_src
1057
+
1058
+ #+begin_example
1059
+ hello-to-the-world
1060
+ #+end_example
1061
+
141
1062
  *** TeX Quoting
1063
+ The extensions for ~String~, ~Numeric~, ~Range~, ~Symbol~, and ~NilClass~
1064
+ provide a ~#tex_quote~ method for quoting the string version of an object so
1065
+ as to allow its inclusion in a TeX document while quoting characters such as
1066
+ '_', $' or '%' that have a special meaning for TeX. At the same time it
1067
+ deploys TeX notation when special notation is available, for example, a
1068
+ ~Rational~ is rendered as a fraction.
142
1069
 
143
- Several of the extension, most notably 'fat_core/string', provides a
144
- ~#tex_quote~ method for quoting the string version of an object so as to allow
145
- its inclusion in a TeX document and quote characters such as '$' or '%' that
146
- have a special meaning for TeX.
1070
+ #+begin_src ruby
1071
+ require_relative 'lib/fat_core/all'
1072
+ require 'date'
147
1073
 
148
- *** Numbers
1074
+ result = []
1075
+ result << ['Class', 'Example', '#tex_quote']
1076
+ result << nil
1077
+ examples = [
1078
+ "Save $100 or 14% on this Friday_Black",
1079
+ 58743.44,
1080
+ Float::INFINITY,
1081
+ Math::PI,
1082
+ Complex(5, 3),
1083
+ Complex(5.0, 3.0),
1084
+ Rational(5, 3),
1085
+ Rational(8.0, 17.0),
1086
+ (Date.parse('2020-09-22')..Date.today),
1087
+ (Math::E..Math::PI),
1088
+ :four_score_and_7_years,
1089
+ nil
1090
+ ]
1091
+ examples.each do |ex|
1092
+ result << [ex.class, ex.to_s, ex.tex_quote.inspect]
1093
+ end
1094
+ result
1095
+ #+end_src
1096
+
1097
+ #+begin_example
1098
+ | Class | Example | #tex_quote |
1099
+ |----------+---------------------------------------+-----------------------------------------------|
1100
+ | String | Save $100 or 14% on this Friday_Black | "Save \\$100 or 14\\% on this Friday\\_Black" |
1101
+ | Float | 58743.44 | "58743.44" |
1102
+ | Float | Infinity | "$\\infty$" |
1103
+ | Float | 3.141592653589793 | "$\\pi$" |
1104
+ | Complex | 5+3i | "$5+3i$" |
1105
+ | Complex | 5.0+3.0i | "$5+3i$" |
1106
+ | Rational | 5/3 | "$\\frac{5}{3}$" |
1107
+ | Rational | 8/17 | "$\\frac{8}{17}$" |
1108
+ | Range | 2020-09-22..2025-11-24 | "(2020-09-22..2025-11-24)" |
1109
+ | Range | 2.718281828459045..3.141592653589793 | "($e$..$\\pi$)" |
1110
+ | Symbol | four_score_and_7_years | "four\\_score\\_and\\_7\\_years" |
1111
+ | NilClass | | "" |
1112
+ #+end_example
149
1113
 
150
- FatCore::Numeric has methods for inserting grouping commas into a number
151
- (~#commas~ and ~#group~), for converting seconds to HH:MM:SS.dd format
152
- (~#secs_to_hms~), for testing for integrality (~#whole?~ and ~#int_if_whole~), and
153
- testing for sign (~#signum~).
154
1114
 
155
- ** Contributing
1115
+ * Contributing
156
1116
 
157
1117
  1. Fork it ([[http://github.com/ddoherty03/fat_core/fork]] )
158
1118
  2. Create your feature branch (~git checkout -b my-new-feature~)