fat_core 6.0.0 → 7.1.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,338 @@ 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. In the subject string replace periods and commas with a space (so
805
+ they still act as word separators) but remove apostrophes, and
806
+ asterisks so the user need not remember whether they were used when
807
+ forming the matcher.
808
+ 3. In the matcher, make any period, comma, asterisk, or apostrophe
809
+ optional for the same reason.
810
+ 4. Treat internal ':stuff' or ' :stuff' in the matcher as the equivalent
811
+ of /\bstuff.*/ in a regular expression, that is, match any word
812
+ starting with stuff in self,
813
+ 5. Treat internal 'stuff: ' in the matcher as the equivalent of /.*stuff\b/ in
814
+ a regular expression, that is, match any word ending with stuff in self,
815
+ 6. A colon with no spaces around it is treated as belonging to the
816
+ following word, requiring it to start with it, so 'some:stuff'
817
+ requires 'some' anywhere followed by a word beginning with 'stuff',
818
+ i.e., /some.*\bstuff/i,
819
+ 7. Treat leading ':' in the matcher as anchoring the match to the
820
+ beginning of the target string,
821
+ 8. Treat ending ':' in the matcher as anchoring the match to the
822
+ end of the target string,
823
+ 9. Require each component to match some part of self, and
824
+
825
+ #+begin_src ruby
826
+ require_relative './lib/fat_core/string'
827
+
828
+ result = []
829
+ result << ['Self', 'Matcher', 'Match']
830
+ result << nil
831
+ subj = "St. Luke's Hospital"
832
+ matchers = ['st lukes', 'st. luke\'s', 'luk:hosp', 'st:spital', 'uk spital', 'st:laks', ':lukes', 's lukes', 'lukes:hospital']
833
+ matchers.each do |m|
834
+ result << [subj, m, subj.fuzzy_match(m)]
835
+ end
836
+ result
837
+ #+end_src
838
+
839
+ #+begin_example
840
+ | Self | Matcher | Match |
841
+ |---------------------+----------------+----------------|
842
+ | St. Luke's Hospital | st lukes | St Lukes |
843
+ | St. Luke's Hospital | st. luke's | St Lukes |
844
+ | St. Luke's Hospital | luk:hosp | Lukes Hosp |
845
+ | St. Luke's Hospital | st:spital | nil |
846
+ | St. Luke's Hospital | uk spital | ukes Hospital |
847
+ | St. Luke's Hospital | st:laks | nil |
848
+ | St. Luke's Hospital | :lukes | nil |
849
+ | St. Luke's Hospital | s lukes | St Lukes |
850
+ | St. Luke's Hospital | lukes:hospital | Lukes Hospital |
851
+ #+end_example
852
+
853
+ **** Method ~#matches_with~
854
+ The ~#matches_with(matcher)~ method allows the use of either a regular
855
+ expression or fuzzy matching as described above depending on whether the
856
+ matcher is enclosed in '/' characters. It also returns the matched portion of
857
+ ~self~ or nil if there is no match. Even when a regex is given, the match is
858
+ case insensitive by default and commas, apostrophes, and periods are removed
859
+ from the subject string before matching.
860
+
861
+ #+begin_src ruby
862
+ require_relative './lib/fat_core/string'
863
+
864
+ result = []
865
+ result << ['Self', 'Matcher', 'Match']
866
+ result << nil
867
+ subj = "St. Luke's Hospital"
868
+ matchers = ['st lukes', '/luk.*hosp/', 'st:spital', '/u.*s\b/', 'st:laks', ':lukes', 's lukes', '/lukes hospital\z/']
869
+ matchers.each do |m|
870
+ result << [subj, m, subj.matches_with(m)]
871
+ end
872
+ result
873
+ #+end_src
874
+
875
+ #+begin_example
876
+ | Self | Matcher | Match |
877
+ |---------------------+--------------------+----------------|
878
+ | St. Luke's Hospital | st lukes | St Lukes |
879
+ | St. Luke's Hospital | /luk.*hosp/ | Lukes Hosp |
880
+ | St. Luke's Hospital | st:spital | nil |
881
+ | St. Luke's Hospital | /u.*s\b/ | ukes |
882
+ | St. Luke's Hospital | st:laks | nil |
883
+ | St. Luke's Hospital | :lukes | nil |
884
+ | St. Luke's Hospital | s lukes | St Lukes |
885
+ | St. Luke's Hospital | /lukes hospital\z/ | Lukes Hospital |
886
+ #+end_example
887
+
888
+ **** Method ~#entitle~
889
+ For a string meant to serve as the title of a book, song, or other item, there
890
+ are certain rules in English as to which words should be capitalized and which
891
+ should be put in lower case. "PROFILES IN courage" should be rendered
892
+ "Profiles in Courage" for example. The preposition "in" is typically not
893
+ capitalized unless it starts the title: "in the HEAT OF THE NIght" should be
894
+ something like "In the Heat of the Night".
895
+
896
+ #+begin_src ruby
897
+ require_relative './lib/fat_core/string'
898
+
899
+ result = []
900
+ result << ['Self', 'Entitled']
901
+ result << nil
902
+ titles = ['PROFILES IN courage', 'in the HEAT OF THE NIght', 'a day in the life', 'FROM HERE TO ETERNITY',
903
+ 'lucy in the sky with diamonds']
904
+ titles.each do |t|
905
+ result << [t, t.entitle]
906
+ end
907
+ result
908
+ #+end_src
909
+
910
+ #+begin_example
911
+ | Self | Entitled |
912
+ |-------------------------------+-------------------------------|
913
+ | PROFILES IN courage | Profiles in Courage |
914
+ | in the HEAT OF THE NIght | In the Heat of the Night |
915
+ | a day in the life | A Day in the Life |
916
+ | FROM HERE TO ETERNITY | From Here to Eternity |
917
+ | lucy in the sky with diamonds | Lucy in the Sky With Diamonds |
918
+ #+end_example
919
+
920
+
921
+ **** Method ~#distance~
922
+ ~FatCore~ provides ~distance~ as a simple wrapper around the
923
+ Damerau-Levenshtein distance function in ~damerau-levenshtein~ gem, using a
924
+ block size of 1 and a max distance of 10.
925
+
926
+ #+begin_src ruby
927
+ require_relative './lib/fat_core/string'
928
+
929
+ result = []
930
+ result << ['Word1', 'Word2', 'Distance']
931
+ result << nil
932
+ pairs = [['Shelf', 'Shell'], ['Shelf', 'Shall'], ['Doherty', 'Daughtery'], ['Doherty', 'Dorrit'], ['Smith', 'Jones']]
933
+ pairs.each do |w1, w2|
934
+ result << [w1, w2, w1.distance(w2)]
935
+ end
936
+ result
937
+ #+end_src
938
+
939
+ #+begin_example
940
+ | Word1 | Word2 | Distance |
941
+ |---------+-----------+----------|
942
+ | Shelf | Shell | 1 |
943
+ | Shelf | Shall | 2 |
944
+ | Doherty | Daughtery | 5 |
945
+ | Doherty | Dorrit | 4 |
946
+ | Smith | Jones | 5 |
947
+ #+end_example
948
+
949
+ **** Method ~#commas(places)~
950
+ When presenting numbers, it is common to want to add grouping digits to make
951
+ the numbers more readable. The ~commas(places)~ method does this be
952
+ converting the number into a Float, rounding to places digits, then converting
953
+ back to a ~String~ with grouping commas inserted.
954
+
955
+ #+begin_src ruby
956
+ require_relative './lib/fat_core/string'
957
+
958
+ result = []
959
+ result << ['N', 'Places', 'With Commas']
960
+ result << nil
961
+ nums_places = [["798964655.66541325", 3], ["798964655.66541325", 0], ["798964655.66541325", 5], ["3.14159", 3],
962
+ ["3.14159e6", 3], ["-3.14159e4", 2], ["+3.14159e3", 2]]
963
+ nums_places.each do |n, p|
964
+ result << [n, p, n.commas(p)]
965
+ end
966
+ result
967
+ #+end_src
968
+
969
+ #+begin_example
970
+ | N | Places | With Commas |
971
+ |--------------------+--------+-------------------|
972
+ | 798964655.66541325 | 3 | 798,964,655.665 |
973
+ | 798964655.66541325 | 0 | 798,964,656 |
974
+ | 798964655.66541325 | 5 | 798,964,655.66541 |
975
+ | 3.14159 | 3 | 3.142 |
976
+ | 3.14159e6 | 3 | 3,141,590.000 |
977
+ | -3.14159e4 | 2 | -31,415.90 |
978
+ | +3.14159e3 | 2 | 3,141.59 |
979
+ #+end_example
980
+
981
+
982
+ **** Method =#wrap(width, hang)=
983
+ This method wraps the string to a given width with an optional hanging indent
984
+ for lines after the first.
985
+ #+begin_src ruby
986
+ require_relative './lib/fat_core/string'
987
+
988
+ getty = <<~EOS
989
+ Four score and seven years ago our fathers brought forth on this continent,
990
+ a new nation, conceived in Liberty, and dedicated to the proposition that
991
+ all men are created equal.
992
+
993
+ Now we are engaged in a great civil war, testing whether that nation, or any
994
+ nation so conceived and so dedicated, can long endure. We are met on a
995
+ great battle-field of that war. We have come to dedicate a portion of that
996
+ field, as a final resting place for those who here gave their lives that
997
+ that nation might live. It is altogether fitting and proper that we should
998
+ do this.
999
+
1000
+ But, in a larger sense, we can not dedicate---we can not consecrate---we can
1001
+ not hallow---this ground. The brave men, living and dead, who struggled
1002
+ here, have consecrated it, far above our poor power to add or detract. The
1003
+ world will little note, nor long remember what we say here, but it can never
1004
+ forget what they did here. It is for us the living, rather, to be dedicated
1005
+ here to the unfinished work which they who fought here have thus far so
1006
+ nobly advanced. It is rather for us to be here dedicated to the great task
1007
+ remaining before us---that from these honored dead we take increased
1008
+ devotion to that cause for which they gave the last full measure of
1009
+ devotion---that we here highly resolve that these dead shall not have died
1010
+ in vain---that this nation, under God, shall have a new birth of
1011
+ freedom---and that government of the people, by the people, for the people,
1012
+ shall not perish from the earth.
1013
+ EOS
1014
+ getty.wrap(110, 3)
1015
+ #+end_src
1016
+
1017
+ #+begin_example
1018
+ Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived
1019
+ in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged
1020
+ in a great civil war, testing whether that nation, or any nation so conceived and so dedicated,
1021
+ can long endure. We are met on a great battle-field of that war. We have come to dedicate
1022
+ a portion of that field, as a final resting place for those who here gave their lives that
1023
+ that nation might live. It is altogether fitting and proper that we should do this. But, in
1024
+ a larger sense, we can not dedicate---we can not consecrate---we can not hallow---this ground.
1025
+ The brave men, living and dead, who struggled here, have consecrated it, far above our poor
1026
+ power to add or detract. The world will little note, nor long remember what we say here, but
1027
+ it can never forget what they did here. It is for us the living, rather, to be dedicated here
1028
+ to the unfinished work which they who fought here have thus far so nobly advanced. It is rather
1029
+ for us to be here dedicated to the great task remaining before us---that from these honored
1030
+ dead we take increased devotion to that cause for which they gave the last full measure of
1031
+ devotion---that we here highly resolve that these dead shall not have died in vain---that this
1032
+ nation, under God, shall have a new birth of freedom---and that government of the people, by
1033
+ the people, for the people, shall not perish from the earth.
1034
+ #+end_example
1035
+
1036
+ **** Method =#as_sym=
1037
+ Convert a ~String~ to a ~Symbol~ by converting all letters to lower-case,
1038
+ replacing hyphens and white space with a single underscore, and removing all
1039
+ non-alphanumeric characters:
1040
+
1041
+ #+begin_src ruby
1042
+ require_relative './lib/fat_core/string'
1043
+
1044
+ " Hello-to-the World!!!".as_sym
1045
+ #+end_src
1046
+
1047
+ #+begin_example
1048
+ :hello_to_the_world
1049
+ #+end_example
1050
+
1051
+ *** Symbol
1052
+ **** Method =#as_str=
1053
+ A quasi-inverse of ~String#as_sym~, convert a ~Symbol~ into a ~String~ by
1054
+ converting '_' to a hyphen, white-space into '_', and eliminate any
1055
+ non-alphanumeric characters.
1056
+
1057
+ #+begin_src ruby
1058
+ require_relative 'lib/fat_core/symbol'
1059
+
1060
+ :hello_to_the_world.as_str
1061
+ #+end_src
1062
+
1063
+ #+begin_example
1064
+ hello-to-the-world
1065
+ #+end_example
1066
+
141
1067
  *** TeX Quoting
1068
+ The extensions for ~String~, ~Numeric~, ~Range~, ~Symbol~, and ~NilClass~
1069
+ provide a ~#tex_quote~ method for quoting the string version of an object so
1070
+ as to allow its inclusion in a TeX document while quoting characters such as
1071
+ '_', $' or '%' that have a special meaning for TeX. At the same time it
1072
+ deploys TeX notation when special notation is available, for example, a
1073
+ ~Rational~ is rendered as a fraction.
142
1074
 
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.
1075
+ #+begin_src ruby
1076
+ require_relative 'lib/fat_core/all'
1077
+ require 'date'
147
1078
 
148
- *** Numbers
1079
+ result = []
1080
+ result << ['Class', 'Example', '#tex_quote']
1081
+ result << nil
1082
+ examples = [
1083
+ "Save $100 or 14% on this Friday_Black",
1084
+ 58743.44,
1085
+ Float::INFINITY,
1086
+ Math::PI,
1087
+ Complex(5, 3),
1088
+ Complex(5.0, 3.0),
1089
+ Rational(5, 3),
1090
+ Rational(8.0, 17.0),
1091
+ (Date.parse('2020-09-22')..Date.today),
1092
+ (Math::E..Math::PI),
1093
+ :four_score_and_7_years,
1094
+ nil
1095
+ ]
1096
+ examples.each do |ex|
1097
+ result << [ex.class, ex.to_s, ex.tex_quote.inspect]
1098
+ end
1099
+ result
1100
+ #+end_src
1101
+
1102
+ #+begin_example
1103
+ | Class | Example | #tex_quote |
1104
+ |----------+---------------------------------------+-----------------------------------------------|
1105
+ | String | Save $100 or 14% on this Friday_Black | "Save \\$100 or 14\\% on this Friday\\_Black" |
1106
+ | Float | 58743.44 | "58743.44" |
1107
+ | Float | Infinity | "$\\infty$" |
1108
+ | Float | 3.141592653589793 | "$\\pi$" |
1109
+ | Complex | 5+3i | "$5+3i$" |
1110
+ | Complex | 5.0+3.0i | "$5+3i$" |
1111
+ | Rational | 5/3 | "$\\frac{5}{3}$" |
1112
+ | Rational | 8/17 | "$\\frac{8}{17}$" |
1113
+ | Range | 2020-09-22..2025-11-24 | "(2020-09-22..2025-11-24)" |
1114
+ | Range | 2.718281828459045..3.141592653589793 | "($e$..$\\pi$)" |
1115
+ | Symbol | four_score_and_7_years | "four\\_score\\_and\\_7\\_years" |
1116
+ | NilClass | | "" |
1117
+ #+end_example
149
1118
 
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
1119
 
155
- ** Contributing
1120
+ * Contributing
156
1121
 
157
1122
  1. Fork it ([[http://github.com/ddoherty03/fat_core/fork]] )
158
1123
  2. Create your feature branch (~git checkout -b my-new-feature~)