fat_core 5.6.1 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +54 -0
- data/.rubocop.yml +4 -6
- data/.simplecov +2 -0
- data/CHANGELOG.org +49 -0
- data/Gemfile +5 -4
- data/README.org +1016 -427
- data/Rakefile +15 -2
- data/bin/console +2 -3
- data/fat_core.gemspec +1 -0
- data/lib/fat_core/all.rb +9 -11
- data/lib/fat_core/array.rb +5 -5
- data/lib/fat_core/enumerable.rb +0 -15
- data/lib/fat_core/hash.rb +48 -11
- data/lib/fat_core/numeric.rb +48 -1
- data/lib/fat_core/range.rb +108 -55
- data/lib/fat_core/string.rb +70 -57
- data/lib/fat_core/symbol.rb +1 -1
- data/lib/fat_core/version.rb +2 -2
- data/lib/fat_core.rb +2 -2
- data/spec/lib/array_spec.rb +2 -0
- data/spec/lib/big_decimal_spec.rb +2 -0
- data/spec/lib/enumerable_spec.rb +29 -39
- data/spec/lib/hash_spec.rb +19 -2
- data/spec/lib/nil_class_spec.rb +2 -0
- data/spec/lib/numeric_spec.rb +14 -0
- data/spec/lib/range_spec.rb +101 -90
- data/spec/lib/string_spec.rb +78 -20
- data/spec/lib/symbol_spec.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- metadata +9 -10
- data/bin/easter +0 -45
- data/lib/fat_core/date.rb +0 -1897
- data/lib/fat_core/kernel.rb +0 -26
- data/spec/lib/date_spec.rb +0 -1390
- data/spec/lib/kernel_spec.rb +0 -13
data/README.org
CHANGED
|
@@ -1,17 +1,108 @@
|
|
|
1
|
-
|
|
1
|
+
#+TITLE: FatCore Guide
|
|
2
|
+
#+OPTIONS: toc:5
|
|
3
|
+
#+PROPERTY: header-args:ruby :colnames no :hlines yes :exports both :wrap example :ruby ruby
|
|
4
|
+
#+PROPERTY: header-args:sh :exports code
|
|
5
|
+
|
|
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
|
+
|
|
8
|
+
* README Setup Do First for Code Blocks :noexport:
|
|
9
|
+
Run this block before all others to ensure that we are reading the libraries
|
|
10
|
+
from the source directory.
|
|
11
|
+
|
|
12
|
+
#+begin_src ruby :results output
|
|
13
|
+
puts "Current directory: #{Dir.pwd}"
|
|
14
|
+
puts "Ruby LOADPATH:"
|
|
15
|
+
$:.unshift("./lib") unless $:[0] == './lib'
|
|
16
|
+
$:[0..10].each { |d| puts d }
|
|
17
|
+
puts "..."
|
|
18
|
+
require_relative 'lib/fat_core/all' # => true
|
|
19
|
+
#+end_src
|
|
20
|
+
|
|
21
|
+
#+RESULTS:
|
|
22
|
+
#+begin_example
|
|
23
|
+
Current directory: /home/ded/src/fat_core
|
|
24
|
+
Ruby LOADPATH:
|
|
25
|
+
./lib
|
|
26
|
+
/home/ded/.rbenv/rbenv.d/exec/gem-rehash
|
|
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
|
+
...
|
|
36
|
+
#+end_example
|
|
37
|
+
|
|
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
|
|
2
91
|
|
|
3
92
|
* FatCore
|
|
4
93
|
|
|
5
|
-
~fat-core~ is a
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
8
99
|
|
|
9
|
-
|
|
100
|
+
* Installation
|
|
10
101
|
|
|
11
102
|
Add this line to your application's Gemfile:
|
|
12
103
|
|
|
13
|
-
#+
|
|
14
|
-
gem 'fat_core'
|
|
104
|
+
#+begin_src ruby
|
|
105
|
+
gem 'fat_core'
|
|
15
106
|
#+end_SRC
|
|
16
107
|
|
|
17
108
|
And then execute:
|
|
@@ -26,14 +117,13 @@ Or install it yourself as:
|
|
|
26
117
|
$ gem install fat_core
|
|
27
118
|
#+end_src
|
|
28
119
|
|
|
29
|
-
|
|
120
|
+
* Usage
|
|
30
121
|
|
|
31
122
|
You can extend classes individually by requiring the corresponding file:
|
|
32
123
|
|
|
33
124
|
#+begin_SRC ruby
|
|
34
125
|
require 'fat_core/array'
|
|
35
126
|
require 'fat_core/bigdecimal'
|
|
36
|
-
require 'fat_core/date'
|
|
37
127
|
require 'fat_core/enumerable'
|
|
38
128
|
require 'fat_core/hash'
|
|
39
129
|
require 'fat_core/kernel'
|
|
@@ -43,7 +133,6 @@ You can extend classes individually by requiring the corresponding file:
|
|
|
43
133
|
require 'fat_core/symbol'
|
|
44
134
|
#+end_SRC
|
|
45
135
|
|
|
46
|
-
|
|
47
136
|
Or, you can require them all:
|
|
48
137
|
|
|
49
138
|
#+begin_SRC ruby
|
|
@@ -53,454 +142,642 @@ Or, you can require them all:
|
|
|
53
142
|
Many of these have little that is of general interest, but there are a few
|
|
54
143
|
goodies.
|
|
55
144
|
|
|
56
|
-
***
|
|
57
|
-
****
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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 ', '.
|
|
150
|
+
|
|
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.
|
|
153
|
+
|
|
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.
|
|
156
|
+
|
|
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.
|
|
76
166
|
|
|
77
167
|
#+begin_src ruby
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
from = Date.ensure(arg) # => ArgumentError: cannot convert class 'Array' to a Date or DateTime
|
|
83
|
-
from + 2.days # => Mon, 03 Jun 2024, Wed, 16 Oct 2024 05:47:30 -0500, Sun, 03 Mar 2024
|
|
84
|
-
end # => :tomorow_tomorrow
|
|
85
|
-
|
|
86
|
-
tomorow_tomorrow('June 1') # => Mon, 03 Jun 2024
|
|
87
|
-
tomorow_tomorrow(Time.now) # => Wed, 16 Oct 2024 05:47:30 -0500
|
|
88
|
-
# But it's only as good as Date.parse!
|
|
89
|
-
tomorow_tomorrow('Ides of March') # => Sun, 03 Mar 2024
|
|
90
|
-
|
|
91
|
-
tomorow_tomorrow([])
|
|
92
|
-
# =>
|
|
93
|
-
|
|
94
|
-
# ~> ArgumentError
|
|
95
|
-
# ~> cannot convert class 'Array' to a Date or DateTime
|
|
96
|
-
# ~>
|
|
97
|
-
# ~> /home/ded/src/fat_core/lib/fat_core/date.rb:1849:in `ensure_date'
|
|
98
|
-
# ~> /home/ded/src/fat_core/lib/fat_core/date.rb:1863:in `ensure'
|
|
99
|
-
# ~> /tmp/seeing_is_believing_temp_dir20241014-1457038-xj4k5x/program.rb:5:in `tomorow_tomorrow'
|
|
100
|
-
# ~> /tmp/seeing_is_believing_temp_dir20241014-1457038-xj4k5x/program.rb:14:in `<main>'
|
|
101
|
-
#+end_src
|
|
102
|
-
|
|
103
|
-
**** Formatting
|
|
104
|
-
|
|
105
|
-
~FatCore~ provides some concise methods for printing string versions of dates
|
|
106
|
-
that are often useful:
|
|
107
|
-
|
|
108
|
-
#+begin_SRC ruby :results output :wrap example :exports both
|
|
109
|
-
require 'fat_core/date'
|
|
110
|
-
d = Date.parse('1957-09-22')
|
|
111
|
-
puts "ISO: #{d.iso}"
|
|
112
|
-
puts "All Numbers: #{d.num}"
|
|
113
|
-
puts "Emacs Org Mode Inactive: #{d.org}"
|
|
114
|
-
puts "Emacs Org Mode Active: #{d.org(true)}"
|
|
115
|
-
puts "LaTeX: #{d.tex_quote}"
|
|
116
|
-
puts "English: #{d.eng}"
|
|
117
|
-
puts "American: #{d.american}"
|
|
118
|
-
#+end_SRC
|
|
168
|
+
require_relative 'lib/fat_core/array'
|
|
169
|
+
|
|
170
|
+
%w{hammers nails glue bolts}.comma_join
|
|
171
|
+
#+end_src
|
|
119
172
|
|
|
120
173
|
#+begin_example
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
#+
|
|
129
|
-
|
|
130
|
-
Most of these are self-explanatory, but a couple are not. The ~#org~ method
|
|
131
|
-
formats a date as an Emacs org-mode timestamp, by default an inactive
|
|
132
|
-
timestamp that does not show up in the org agenda, but can be made active with
|
|
133
|
-
the optional parameter set to a truthy value. See
|
|
134
|
-
[[https://orgmode.org/manual/Timestamps.html#Timestamps]].
|
|
135
|
-
|
|
136
|
-
The ~#tex_quote~ method formats the date in iso form but using TeX's
|
|
137
|
-
convention of using en-dashes to separate the components.
|
|
138
|
-
|
|
139
|
-
**** Chunks
|
|
140
|
-
|
|
141
|
-
Many of the methods provided by ~FatCore~ deal with various calendar periods
|
|
142
|
-
that are less common than those provided by the Ruby Standard Library or gems
|
|
143
|
-
such as ~active_support~. This documentation refers to these calendar periods
|
|
144
|
-
as "chunks", and they are the following:
|
|
145
|
-
|
|
146
|
-
- year,
|
|
147
|
-
- half,
|
|
148
|
-
- quarter,
|
|
149
|
-
- bimonth,
|
|
150
|
-
- month,
|
|
151
|
-
- semimonth,
|
|
152
|
-
- biweek,
|
|
153
|
-
- week, and
|
|
154
|
-
- day
|
|
155
|
-
|
|
156
|
-
~FatCore~ provides methods that query whether the date falls on the beginning
|
|
157
|
-
or end of each of these chunks:
|
|
158
|
-
|
|
159
|
-
#+begin_SRC ruby :wrap example :exports both
|
|
160
|
-
require 'fat_core/date'
|
|
161
|
-
|
|
162
|
-
tab = []
|
|
163
|
-
d = Date.parse('2017-06-30')
|
|
164
|
-
%i[beginning end].each do |side|
|
|
165
|
-
%i(year half quarter bimonth month semimonth biweek week).each do |chunk|
|
|
166
|
-
meth = "#{side}_of_#{chunk}?".to_sym
|
|
167
|
-
tab << [d.iso, meth.to_s, "#{d.send(meth)}"]
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
tab
|
|
171
|
-
#+end_SRC
|
|
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
|
|
172
182
|
|
|
173
|
-
#+RESULTS:
|
|
174
183
|
#+begin_example
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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.
|
|
264
|
+
|
|
265
|
+
*** Enumerable
|
|
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
|
|
206
287
|
end
|
|
207
|
-
|
|
208
|
-
#+
|
|
288
|
+
result
|
|
289
|
+
#+end_src
|
|
209
290
|
|
|
210
|
-
#+RESULTS:
|
|
211
291
|
#+begin_example
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
-
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
301
|
+
|
|
302
|
+
*** Hash
|
|
303
|
+
FatCore::Hash extends the Hash class with some useful methods.
|
|
304
|
+
|
|
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:
|
|
309
|
+
|
|
310
|
+
#+begin_src ruby
|
|
311
|
+
require_relative './lib/fat_core/hash'
|
|
312
|
+
|
|
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]
|
|
240
329
|
end
|
|
241
|
-
|
|
242
|
-
#+
|
|
330
|
+
result
|
|
331
|
+
#+end_src
|
|
332
|
+
|
|
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'
|
|
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
|
|
243
418
|
|
|
244
|
-
#+RESULTS:
|
|
245
419
|
#+begin_example
|
|
246
|
-
|
|
247
|
-
| 2017-06-03 | d.half | in half number 1 |
|
|
248
|
-
| 2017-05-30 | d.quarter | in quarter number 2 |
|
|
249
|
-
| 2017-07-08 | d.bimonth | in bimonth number 4 |
|
|
250
|
-
| 2017-06-28 | d.month | in month number 6 |
|
|
251
|
-
| 2017-05-14 | d.semimonth | in semimonth number 9 |
|
|
252
|
-
| 2017-07-25 | d.biweek | in biweek number 15 |
|
|
253
|
-
| 2017-06-19 | d.week | in week number 25 |
|
|
420
|
+
{:A=>1, :B=>2, :c=>3, :d=>2, :e=>1}
|
|
254
421
|
#+end_example
|
|
255
422
|
|
|
256
|
-
|
|
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
|
|
257
432
|
|
|
258
|
-
|
|
259
|
-
|
|
433
|
+
**** Method ~#replace_keys~
|
|
434
|
+
A wholesale replacement of the existing keys can be done with this method:
|
|
260
435
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
will parse such a string as d/M/Y, often resulting in invalid date errors.
|
|
264
|
-
~FatCore~ adds the specialty parsing method, ~Date.parse_american~ to handle
|
|
265
|
-
such strings.
|
|
436
|
+
#+begin_src ruby :results output
|
|
437
|
+
require_relative './lib/fat_core/hash'
|
|
266
438
|
|
|
267
|
-
|
|
268
|
-
|
|
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
|
|
269
460
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
|
276
513
|
end
|
|
277
|
-
|
|
514
|
+
result
|
|
515
|
+
#+end_src
|
|
278
516
|
|
|
279
|
-
#+RESULTS:
|
|
280
517
|
#+begin_example
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
- MM-DD :: returns the specified day of the specified month in the current
|
|
300
|
-
year, beginning or end,
|
|
301
|
-
- YYYY-Wnn or YYYY-nnW :: returns the nn'th commercial week of the given year
|
|
302
|
-
according to the ISO 8601 standard, in which the week containing the first
|
|
303
|
-
Thursday of the year counts as the first commercial week, even if that week
|
|
304
|
-
started in the prior calendar year,
|
|
305
|
-
- Wnn or nnW :: returns the nn'th commercial week of the current year,
|
|
306
|
-
- YYYY-1H or YYYY-2H :: returns the specified half year for the given year,
|
|
307
|
-
- 1H or 2H :: returns the specified half year for the current year,
|
|
308
|
-
- YYYY-1Q, YYYY-2Q, etc :: returns the calendar quarter for the given year,
|
|
309
|
-
- 1Q, 2Q, etc :: returns the calendar quarter for the current year,
|
|
310
|
-
- YYYY-MM-I or YYYY-MM-II :: returns the semi-month for the given month and
|
|
311
|
-
year, where the first semi-month always runs from the 1st to the 15th and
|
|
312
|
-
the second semi-month always runs from the 16th to the last day of the given
|
|
313
|
-
month, regardless of the number of days in the month,
|
|
314
|
-
- YYYY-MM-i or YYYY-MM-ii up to YYYY-MM-vi :: returns the given week within
|
|
315
|
-
the month, including any partial weeks,
|
|
316
|
-
- MM-i or MM-ii up to MM-vi :: returns the given week within the month of the
|
|
317
|
-
current year, including any partial weeks,
|
|
318
|
-
- i or ii up to vi :: returns the given week within the current month of the current
|
|
319
|
-
year, including any partial weeks,
|
|
320
|
-
- YYYY-MM-nSu up to YYYY-MM-nSa :: returns the single date that is the n'th
|
|
321
|
-
Sunday, Monday, etc., in the given month using the first two letters of the
|
|
322
|
-
English names for the days of the week,
|
|
323
|
-
- MM-nSu up to MM-nSa :: returns the single date that is the n'th Sunday,
|
|
324
|
-
Monday, etc., in the given month of the current year using the first two
|
|
325
|
-
letters of the English names for the days of the week,
|
|
326
|
-
- nSu up to nSa :: returns the single date that is the n'th Sunday, Monday,
|
|
327
|
-
etc., in the current month of the current year using the first two letters
|
|
328
|
-
of the English names for the days of the week,
|
|
329
|
-
- YYYY-nnn :: is the nnn'th day of the given year, exactly three digits needed,
|
|
330
|
-
- nnn :: is the nnn'th day of the current year, exactly three digits needed,
|
|
331
|
-
- YYYY-E :: returns the single date of Easter in the Western church for the
|
|
332
|
-
given year,
|
|
333
|
-
- E :: returns the single date of Easter in the Western church for the current
|
|
334
|
-
year,
|
|
335
|
-
- YYYY-E-n or YYYY-E+n :: returns the single date that falls n days before (-)
|
|
336
|
-
or after (+) Easter in the Western church for the given year,
|
|
337
|
-
- E-n or E+n :: returns the single date that falls n days before (-) or after
|
|
338
|
-
(+) Easter in the Western church for the current year,
|
|
339
|
-
- yesterday or yesteryear or lastday or last_year, etc :: the relative
|
|
340
|
-
prefixes, 'last' or 'yester' prepended to any chunk name returns the period
|
|
341
|
-
named by the chunk that precedes today's date.
|
|
342
|
-
- today or toyear or this-year or thissemimonth, etc :: the relative prefixes,
|
|
343
|
-
'to' or 'this' prepended to any chunk name returns the period named by
|
|
344
|
-
the chunk that contains today's date.
|
|
345
|
-
- nextday or nextyear or next-year or nextsemimonth, etc :: the relative
|
|
346
|
-
prefixes, 'next' prepended to any chunk name returns the period named by the
|
|
347
|
-
chunk that follows today's date. As a special case, 'tomorrow' is treated as
|
|
348
|
-
equivalent to 'nextday'.
|
|
349
|
-
- forever :: returns the period Date::BOT to Date::EOT, which, for financial
|
|
350
|
-
applications is meant to stand in for eternity.
|
|
351
|
-
- never :: returns nil, representing no date.
|
|
352
|
-
|
|
353
|
-
Some things to note with respect to ~Date.parse_spec~:
|
|
354
|
-
|
|
355
|
-
1. The second argument should be either ~:from~ or ~:to~, but it defaults to
|
|
356
|
-
~:from~. If it is ~:from~, ~parse_spec~ returns the first date of the
|
|
357
|
-
specified period; if it is ~:to~, it returns the last date of the specified
|
|
358
|
-
period. When the "period" resolves to a single day, both arguments return
|
|
359
|
-
the same date, so ~parse_spec('2024-E', :from)~ and ~parse_spec('2024-E',
|
|
360
|
-
:to)~ both result in March 31, 2024.
|
|
361
|
-
2. Where relevant, ~parse_spec~ accepts letters of either upper or lower case:
|
|
362
|
-
so 2024-1Q can be written 2024-1q and 'yesteryear' can be written
|
|
363
|
-
'YeSterYeaR', and likewise for all components of the spec using letters.
|
|
364
|
-
3. Date components can be separated with either a hyphen, as in the examples
|
|
365
|
-
above, or with a '/' as is common. Thus, 2024-11-09 can also be
|
|
366
|
-
2024/11/09, or indeed, 2024/11-09 or 2024-11/09.
|
|
367
|
-
4. The prefixes for relative periods can be separated from the period name by
|
|
368
|
-
a hyphen, and underscore, or by nothing at all. Thus, yester-day,
|
|
369
|
-
yester_day, and yesterday are all acceptable. Clearly neologisms such as
|
|
370
|
-
'yestermonth' are quaint, but not harmful.
|
|
371
|
-
5. On the other hand, to get a day-of-year spec right, you must use exactly 3
|
|
372
|
-
digits: 2024-011 is the 11th day of 2024, but 2024-11 is November of 2024.
|
|
373
|
-
|
|
374
|
-
**** Holidays and Workdays
|
|
375
|
-
One of the original motivations for this library was to provide an easy way to
|
|
376
|
-
determine whether a given date is a federal holiday in the United States or,
|
|
377
|
-
nearly but not quite the same, a non-trading day on the New York Stock
|
|
378
|
-
Exchange. To that end, ~FatCore~ provides the following methods:
|
|
379
|
-
|
|
380
|
-
- Date#weekend? -- is this date on a weekend?
|
|
381
|
-
- Date#weekday? -- is this date on a week day?
|
|
382
|
-
- Date#easter_this_year -- the date of Easter in the Date's year
|
|
383
|
-
|
|
384
|
-
Methods concerning Federal holidays:
|
|
385
|
-
|
|
386
|
-
- Date#fed_holiday? -- is this date a Federal holiday? It knows about
|
|
387
|
-
obscurities such as holidays decreed by past Presidents, dates of
|
|
388
|
-
Presidential funerals, and the Federal rule for when holidays fall on a
|
|
389
|
-
weekend, whether it is moved to the prior Friday or the following Monday.
|
|
390
|
-
- Date#fed_workday? -- is it a date when the Federal government is open?,
|
|
391
|
-
inverse of Date#fed_holiday?
|
|
392
|
-
- Date#add_fed_workdays(n) -- n Federal workdays following (or preceding if n
|
|
393
|
-
negative) this date,
|
|
394
|
-
- Date#next_fed_workday -- the next Federal workday following this date,
|
|
395
|
-
- Date#prior_fed_workday -- the previous Federal workday before this date,
|
|
396
|
-
- Date#next_until_fed_workday -- starting with this date, move forward until
|
|
397
|
-
we hit a Federal workday
|
|
398
|
-
- Date#prior_until_fed_workday -- starting with this date, move back until
|
|
399
|
-
we hit a Federal workday
|
|
400
|
-
|
|
401
|
-
And we have similar methods for "holidays" or non-trading days on the NYSE:
|
|
402
|
-
|
|
403
|
-
- Date#nyse_holiday? -- is this date a NYSE holiday?
|
|
404
|
-
- Date#nyse_workday? -- is it a date when the NYSE is open for trading?,
|
|
405
|
-
inverse of Date#nyse_holiday?
|
|
406
|
-
- Date#add_nyse_workdays(n) -- n NYSE workdays following (or preceding if n
|
|
407
|
-
negative) this date,
|
|
408
|
-
- Date#next_nyse_workday -- the next NYSE workday following this date,
|
|
409
|
-
- Date#prior_nyse_workday -- the previous NYSE workday before this date,
|
|
410
|
-
- Date#next_until_nyse_~~workday -- starting with this date, move forward until
|
|
411
|
-
we hit a NYSE workday
|
|
412
|
-
- Date#prior_until_nyse_workday -- starting with this date, move back until
|
|
413
|
-
we hit a Federal workday
|
|
414
|
-
|
|
415
|
-
**** Ordinal Weekdays in Month
|
|
416
|
-
It is often useful to find the 1st, 2nd, etc, Sunday, Monday, etc. in a given
|
|
417
|
-
month. ~FatCore~ provides the class method ~Date.nth_wday_in_year_month(nth,
|
|
418
|
-
wday, year, month)~ to return such dates. The first parameter can be
|
|
419
|
-
negative, which will count from the end of the month.
|
|
420
|
-
|
|
421
|
-
**** Easter
|
|
422
|
-
The ~Date~ class extension adds two methods for determining whether a given
|
|
423
|
-
date is a US federal holiday as defined by federal law, including such things
|
|
424
|
-
as federal holidays established by executive decree:
|
|
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
|
|
425
536
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
#+end_SRC
|
|
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~).
|
|
431
541
|
|
|
432
|
-
|
|
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.
|
|
433
545
|
|
|
434
|
-
#+
|
|
435
|
-
|
|
436
|
-
|
|
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
|
|
437
567
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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.
|
|
441
572
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
|
449
584
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
|
453
594
|
|
|
454
|
-
Finally, it provides a ~#parse_spec~ method for parsing a string, typically
|
|
455
|
-
provided by a user, allowing all the period chunks to be conveniently and
|
|
456
|
-
tersely specified by a user. For example, the string '2Q' will be parsed as the
|
|
457
|
-
second calendar quarter of the current year, while '2014-3Q' will be parsed as
|
|
458
|
-
the third quarter of the year 2014.
|
|
459
595
|
|
|
460
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~.
|
|
461
615
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
|
471
639
|
|
|
472
|
-
|
|
473
|
-
|
|
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
|
|
474
653
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
#+end_SRC
|
|
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~.
|
|
479
657
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
|
486
681
|
|
|
487
|
-
|
|
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~.
|
|
488
703
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
|
494
725
|
|
|
495
|
-
|
|
496
|
-
|
|
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
|
|
497
737
|
|
|
498
|
-
|
|
499
|
-
|
|
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
|
|
500
765
|
#+end_src
|
|
501
766
|
|
|
502
|
-
|
|
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
|
|
503
779
|
|
|
780
|
+
*** String
|
|
504
781
|
FatCore::String has methods for performing matching of one string with another
|
|
505
782
|
(~#matches_with~, ~#fuzzy_match~), for converting a string to title-case as
|
|
506
783
|
might by used in the title of a book (~#entitle~), for converting a String
|
|
@@ -509,21 +786,333 @@ into a useable Symbol (~#as_sym~) and vice-versa (~#as_str~ also
|
|
|
509
786
|
cleaning up errant spaces (~#clean~), and computing the Damerau-Levenshtein
|
|
510
787
|
distance between strings (~#distance~). And several others.
|
|
511
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
|
+
|
|
512
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.
|
|
513
1069
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
1070
|
+
#+begin_src ruby
|
|
1071
|
+
require_relative 'lib/fat_core/all'
|
|
1072
|
+
require 'date'
|
|
1073
|
+
|
|
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
|
|
518
1096
|
|
|
519
|
-
|
|
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
|
|
520
1113
|
|
|
521
|
-
FatCore::Numeric has methods for inserting grouping commas into a number
|
|
522
|
-
(~#commas~ and ~#group~), for converting seconds to HH:MM:SS.dd format
|
|
523
|
-
(~#secs_to_hms~), for testing for integrality (~#whole?~ and ~#int_if_whole~), and
|
|
524
|
-
testing for sign (~#signum~).
|
|
525
1114
|
|
|
526
|
-
|
|
1115
|
+
* Contributing
|
|
527
1116
|
|
|
528
1117
|
1. Fork it ([[http://github.com/ddoherty03/fat_core/fork]] )
|
|
529
1118
|
2. Create your feature branch (~git checkout -b my-new-feature~)
|