fat_core 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/bin/console +14 -0
- data/fat_core.gemspec +1 -0
- data/lib/fat_core/all.rb +14 -0
- data/lib/fat_core/array.rb +29 -22
- data/lib/fat_core/big_decimal.rb +12 -0
- data/lib/fat_core/date.rb +951 -938
- data/lib/fat_core/enumerable.rb +19 -3
- data/lib/fat_core/hash.rb +48 -43
- data/lib/fat_core/kernel.rb +4 -2
- data/lib/fat_core/nil.rb +13 -13
- data/lib/fat_core/numeric.rb +82 -86
- data/lib/fat_core/range.rb +164 -160
- data/lib/fat_core/string.rb +247 -242
- data/lib/fat_core/symbol.rb +17 -11
- data/lib/fat_core/version.rb +2 -2
- data/lib/fat_core.rb +0 -21
- data/spec/lib/array_spec.rb +2 -0
- data/spec/lib/big_decimal_spec.rb +7 -0
- data/spec/lib/date_spec.rb +7 -26
- data/spec/lib/enumerable_spec.rb +26 -1
- data/spec/lib/hash_spec.rb +2 -0
- data/spec/lib/kernel_spec.rb +2 -0
- data/spec/lib/nil_spec.rb +6 -0
- data/spec/lib/numeric_spec.rb +1 -5
- data/spec/lib/range_spec.rb +1 -1
- data/spec/lib/string_spec.rb +1 -6
- data/spec/lib/symbol_spec.rb +1 -1
- data/spec/spec_helper.rb +0 -2
- metadata +9 -8
- data/lib/fat_core/boolean.rb +0 -25
- data/lib/fat_core/latex_eruby.rb +0 -11
- data/lib/fat_core/period.rb +0 -533
- data/spec/lib/period_spec.rb +0 -677
data/lib/fat_core/string.rb
CHANGED
@@ -1,284 +1,289 @@
|
|
1
1
|
require 'damerau-levenshtein'
|
2
|
+
require 'active_support/core_ext/regexp'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
module FatCore
|
5
|
+
module String
|
6
|
+
# Remove leading and trailing white space and compress internal runs of
|
7
|
+
# white space to a single space.
|
8
|
+
def clean
|
9
|
+
strip.squeeze(' ')
|
10
|
+
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
def distance(other, block_size: 1, max_distance: 10)
|
13
|
+
dl = DamerauLevenshtein
|
14
|
+
# NOTE: DL 'gives up after' max_distance, so the distance function
|
15
|
+
# will return max_distance+1 if the distance is bigger than that.
|
16
|
+
# Here we subtract 1 so the max_distance also becomes the max
|
17
|
+
# return value.
|
18
|
+
dl.distance(self, other, block_size, max_distance - 1)
|
19
|
+
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
21
|
+
# See if self contains colon- or space-separated words that include
|
22
|
+
# the colon- or space-separated words of other. Return the matched
|
23
|
+
# portion of self. Other cannot be a regex embedded in a string.
|
24
|
+
def fuzzy_match(other)
|
25
|
+
# Remove periods, commas, and apostrophes
|
26
|
+
other = other.gsub(/[\*.,']/, '')
|
27
|
+
target = gsub(/[\*.,']/, '')
|
28
|
+
matchers = other.split(/[: ]+/)
|
29
|
+
regexp_string = matchers.map { |m| ".*?#{Regexp.escape(m)}.*?" }.join('[: ]')
|
30
|
+
regexp_string.sub!(/^\.\*\?/, '')
|
31
|
+
regexp_string.sub!(/\.\*\?$/, '')
|
32
|
+
regexp = /#{regexp_string}/i
|
33
|
+
matched_text =
|
34
|
+
if (match = regexp.match(target))
|
35
|
+
match[0]
|
36
|
+
end
|
37
|
+
matched_text
|
38
|
+
end
|
39
|
+
|
40
|
+
# Here are instance methods for the class that includes Matchable
|
41
|
+
# This tries to convert the receiver object into a string, then
|
42
|
+
# matches against the given matcher, either via regex or a fuzzy
|
43
|
+
# string matcher.
|
44
|
+
def matches_with(str)
|
45
|
+
if str.nil?
|
46
|
+
nil
|
47
|
+
elsif str =~ %r{^\s*/}
|
48
|
+
re = str.to_regexp
|
49
|
+
$& if to_s =~ re
|
50
|
+
else
|
51
|
+
to_s.fuzzy_match(str)
|
34
52
|
end
|
35
|
-
|
36
|
-
end
|
53
|
+
end
|
37
54
|
|
38
|
-
|
39
|
-
|
40
|
-
# matches against the given matcher, either via regex or a fuzzy
|
41
|
-
# string matcher.
|
42
|
-
def matches_with(str)
|
43
|
-
if str.nil?
|
44
|
-
nil
|
45
|
-
elsif str =~ %r{^\s*/}
|
46
|
-
re = str.to_regexp
|
47
|
-
$& if to_s =~ re
|
48
|
-
else
|
49
|
-
to_s.fuzzy_match(str)
|
55
|
+
def blank?
|
56
|
+
!!self =~ /\A\s*\z/
|
50
57
|
end
|
51
|
-
end
|
52
58
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
59
|
+
# Convert a string of the form '/.../Iixm' to a regular expression. However,
|
60
|
+
# make the regular expression case-insensitive by default and extend the
|
61
|
+
# modifier syntax to allow '/I' to indicate case-sensitive.
|
62
|
+
def to_regexp
|
63
|
+
if self =~ %r{^\s*/([^/]*)/([Iixm]*)\s*$}
|
64
|
+
body = $1
|
65
|
+
opts = $2
|
66
|
+
flags = Regexp::IGNORECASE
|
67
|
+
unless opts.blank?
|
68
|
+
flags = 0 if opts.include?('I')
|
69
|
+
flags |= Regexp::IGNORECASE if opts.include?('i')
|
70
|
+
flags |= Regexp::EXTENDED if opts.include?('x')
|
71
|
+
flags |= Regexp::MULTILINE if opts.include?('m')
|
72
|
+
end
|
73
|
+
flags = nil if flags.zero?
|
74
|
+
Regexp.new(body, flags)
|
75
|
+
else
|
76
|
+
Regexp.new(self)
|
66
77
|
end
|
67
|
-
flags = nil if flags.zero?
|
68
|
-
Regexp.new(body, flags)
|
69
|
-
else
|
70
|
-
Regexp.new(self)
|
71
78
|
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Convert to symbol "Hello World" -> :hello_world
|
75
|
-
def as_sym
|
76
|
-
strip.squeeze(' ').gsub(/\s+/, '_')
|
77
|
-
.gsub(/[^_A-Za-z0-9]/, '').downcase.to_sym
|
78
|
-
end
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
# Convert to symbol "Hello World" -> :hello_world
|
81
|
+
def as_sym
|
82
|
+
strip.squeeze(' ').gsub(/\s+/, '_')
|
83
|
+
.gsub(/[^_A-Za-z0-9]/, '').downcase.to_sym
|
84
|
+
end
|
83
85
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
rescue ArgumentError
|
88
|
-
return false
|
89
|
-
end
|
86
|
+
def as_string
|
87
|
+
self
|
88
|
+
end
|
90
89
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
whole = $2
|
97
|
-
frac = $5
|
98
|
-
# Place the commas in the whole part only
|
99
|
-
whole = whole.reverse
|
100
|
-
whole.gsub!(/([0-9]{3})/, '\\1,')
|
101
|
-
whole.gsub!(/,$/, '')
|
102
|
-
whole.reverse!
|
103
|
-
# Reassemble
|
104
|
-
if frac.blank?
|
105
|
-
neg + whole
|
106
|
-
else
|
107
|
-
neg + whole + '.' + frac
|
90
|
+
def number?
|
91
|
+
Float(self)
|
92
|
+
true
|
93
|
+
rescue ArgumentError
|
94
|
+
return false
|
108
95
|
end
|
109
|
-
end
|
110
96
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
result << "\n"
|
129
|
-
line_width_so_far = 0
|
130
|
-
first_line = false
|
131
|
-
first_word_on_line = true
|
97
|
+
# If the string is a number, add grouping commas to the whole number part.
|
98
|
+
def commify
|
99
|
+
# Break the number into parts
|
100
|
+
return self unless clean =~ /\A(-)?(\d*)((\.)?(\d*))?\z/
|
101
|
+
neg = $1 || ''
|
102
|
+
whole = $2
|
103
|
+
frac = $5
|
104
|
+
# Place the commas in the whole part only
|
105
|
+
whole = whole.reverse
|
106
|
+
whole.gsub!(/([0-9]{3})/, '\\1,')
|
107
|
+
whole.gsub!(/,$/, '')
|
108
|
+
whole.reverse!
|
109
|
+
# Reassemble
|
110
|
+
if frac.blank?
|
111
|
+
neg + whole
|
112
|
+
else
|
113
|
+
neg + whole + '.' + frac
|
132
114
|
end
|
133
115
|
end
|
134
|
-
result.strip
|
135
|
-
end
|
136
116
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
117
|
+
def wrap(width = 70, hang = 0)
|
118
|
+
result = ''
|
119
|
+
first_line = true
|
120
|
+
first_word_on_line = true
|
121
|
+
line_width_so_far = 0
|
122
|
+
words = split(' ')
|
123
|
+
words.each do |w|
|
124
|
+
if !first_line && first_word_on_line
|
125
|
+
w = ' ' * hang + w
|
126
|
+
end
|
127
|
+
unless first_word_on_line
|
128
|
+
w = ' ' + w
|
129
|
+
end
|
130
|
+
result << w
|
131
|
+
first_word_on_line = false
|
132
|
+
line_width_so_far += 1 + w.length
|
133
|
+
if line_width_so_far >= width
|
134
|
+
result << "\n"
|
135
|
+
line_width_so_far = 0
|
136
|
+
first_line = false
|
137
|
+
first_word_on_line = true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
result.strip
|
141
|
+
end
|
151
142
|
|
152
|
-
|
153
|
-
|
154
|
-
|
143
|
+
def tex_quote
|
144
|
+
r = dup
|
145
|
+
r = r.gsub(/[{]/, 'XzXzXobXzXzX')
|
146
|
+
r = r.gsub(/[}]/, 'XzXzXcbXzXzX')
|
147
|
+
r = r.gsub(/\\/, '\textbackslash{}')
|
148
|
+
r = r.gsub(/\^/, '\textasciicircum{}')
|
149
|
+
r = r.gsub(/~/, '\textasciitilde{}')
|
150
|
+
r = r.gsub(/\|/, '\textbar{}')
|
151
|
+
r = r.gsub(/\</, '\textless{}')
|
152
|
+
r = r.gsub(/\>/, '\textgreater{}')
|
153
|
+
r = r.gsub(/([_$&%#])/) { |m| '\\' + m }
|
154
|
+
r = r.gsub('XzXzXobXzXzX', '\\{')
|
155
|
+
r.gsub('XzXzXcbXzXzX', '\\}')
|
156
|
+
end
|
155
157
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
158
|
+
# Convert a string with an all-digit date to an iso string
|
159
|
+
# E.g., "20090923" -> "2009-09-23"
|
160
|
+
def digdate2iso
|
161
|
+
sub(/(\d\d\d\d)(\d\d)(\d\d)/, '\1-\2-\3')
|
162
|
+
end
|
161
163
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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
|
-
|
164
|
+
def entitle!
|
165
|
+
little_words = %w(a an the and or in on under of from as by to)
|
166
|
+
newwords = []
|
167
|
+
words = split(/\s+/)
|
168
|
+
first_word = true
|
169
|
+
num_words = words.length
|
170
|
+
words.each_with_index do |w, k|
|
171
|
+
last_word = (k + 1 == num_words)
|
172
|
+
if w =~ %r{c/o}i
|
173
|
+
# Care of
|
174
|
+
newwords.push('c/o')
|
175
|
+
elsif w =~ /^p\.?o\.?$/i
|
176
|
+
# Post office
|
177
|
+
newwords.push('P.O.')
|
178
|
+
elsif w =~ /^[0-9]+(st|nd|rd|th)$/i
|
179
|
+
# Ordinals
|
180
|
+
newwords.push(w.downcase)
|
181
|
+
elsif w =~ /^(cr|dr|st|rd|ave|pk|cir)$/i
|
182
|
+
# Common abbrs to capitalize
|
183
|
+
newwords.push(w.capitalize)
|
184
|
+
elsif w =~ /^(us|ne|se|rr)$/i
|
185
|
+
# Common 2-letter abbrs to upcase
|
186
|
+
newwords.push(w.upcase)
|
187
|
+
elsif w =~ /^[0-9].*$/i
|
188
|
+
# Other runs starting with numbers,
|
189
|
+
# like 3-A
|
190
|
+
newwords.push(w.upcase)
|
191
|
+
elsif w =~ /^(N|S|E|W|NE|NW|SE|SW)$/i
|
192
|
+
# Compass directions all caps
|
193
|
+
newwords.push(w.upcase)
|
194
|
+
elsif w =~ /^[^aeiouy]*$/i && w.size > 2
|
195
|
+
# All consonants and at least 3 chars, probably abbr
|
196
|
+
newwords.push(w.upcase)
|
197
|
+
elsif w =~ /^(\w+)-(\w+)$/i
|
198
|
+
# Hyphenated double word
|
199
|
+
newwords.push($1.capitalize + '-' + $2.capitalize)
|
200
|
+
elsif little_words.include?(w.downcase)
|
201
|
+
# Only capitalize at beginning or end
|
202
|
+
newwords.push(first_word || last_word ? w.capitalize : w.downcase)
|
203
|
+
else
|
204
|
+
# All else
|
205
|
+
newwords.push(w.capitalize)
|
206
|
+
end
|
207
|
+
first_word = false
|
204
208
|
end
|
205
|
-
|
209
|
+
self[0..-1] = newwords.join(' ')
|
206
210
|
end
|
207
|
-
self[0..-1] = newwords.join(' ')
|
208
|
-
end
|
209
211
|
|
210
|
-
|
211
|
-
|
212
|
-
|
212
|
+
def entitle
|
213
|
+
dup.entitle!
|
214
|
+
end
|
213
215
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
216
|
+
# Thanks to Eugene at stackoverflow for the following.
|
217
|
+
# http://stackoverflow.com/questions/8806643/
|
218
|
+
# colorized-output-breaks-linewrapping-with-readline
|
219
|
+
# These color strings without confusing readline about the length of
|
220
|
+
# the prompt string in the shell. (Unlike the rainbow routines)
|
221
|
+
def console_red
|
222
|
+
colorize(self, "\001\e[1m\e[31m\002")
|
221
223
|
end
|
222
|
-
end
|
223
224
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
# These color strings without confusing readline about the length of
|
228
|
-
# the prompt string in the shell. (Unlike the rainbow routines)
|
229
|
-
def console_red
|
230
|
-
colorize(self, "\001\e[1m\e[31m\002")
|
231
|
-
end
|
225
|
+
def console_dark_red
|
226
|
+
colorize(self, "\001\e[31m\002")
|
227
|
+
end
|
232
228
|
|
233
|
-
|
234
|
-
|
235
|
-
|
229
|
+
def console_green
|
230
|
+
colorize(self, "\001\e[1m\e[32m\002")
|
231
|
+
end
|
236
232
|
|
237
|
-
|
238
|
-
|
239
|
-
|
233
|
+
def console_dark_green
|
234
|
+
colorize(self, "\001\e[32m\002")
|
235
|
+
end
|
240
236
|
|
241
|
-
|
242
|
-
|
243
|
-
|
237
|
+
def console_yellow
|
238
|
+
colorize(self, "\001\e[1m\e[33m\002")
|
239
|
+
end
|
244
240
|
|
245
|
-
|
246
|
-
|
247
|
-
|
241
|
+
def console_dark_yellow
|
242
|
+
colorize(self, "\001\e[33m\002")
|
243
|
+
end
|
248
244
|
|
249
|
-
|
250
|
-
|
251
|
-
|
245
|
+
def console_blue
|
246
|
+
colorize(self, "\001\e[1m\e[34m\002")
|
247
|
+
end
|
252
248
|
|
253
|
-
|
254
|
-
|
255
|
-
|
249
|
+
def console_dark_blue
|
250
|
+
colorize(self, "\001\e[34m\002")
|
251
|
+
end
|
256
252
|
|
257
|
-
|
258
|
-
|
259
|
-
|
253
|
+
def console_purple
|
254
|
+
colorize(self, "\001\e[1m\e[35m\002")
|
255
|
+
end
|
260
256
|
|
261
|
-
|
262
|
-
|
263
|
-
|
257
|
+
def console_cyan
|
258
|
+
colorize(self, "\001\e[1m\e[36m\002")
|
259
|
+
end
|
264
260
|
|
265
|
-
|
266
|
-
|
267
|
-
|
261
|
+
def console_def
|
262
|
+
colorize(self, "\001\e[1m\002")
|
263
|
+
end
|
268
264
|
|
269
|
-
|
270
|
-
|
271
|
-
|
265
|
+
def console_bold
|
266
|
+
colorize(self, "\001\e[1m\002")
|
267
|
+
end
|
272
268
|
|
273
|
-
|
274
|
-
|
275
|
-
|
269
|
+
def console_blink
|
270
|
+
colorize(self, "\001\e[5m\002")
|
271
|
+
end
|
276
272
|
|
277
|
-
|
278
|
-
|
279
|
-
|
273
|
+
def colorize(text, color_code)
|
274
|
+
"#{color_code}#{text}\001\e[0m\002"
|
275
|
+
end
|
276
|
+
|
277
|
+
module ClassMethods
|
278
|
+
def random(size = 8)
|
279
|
+
('a'..'z').cycle.take(size).shuffle.join
|
280
|
+
end
|
281
|
+
end
|
280
282
|
|
281
|
-
|
282
|
-
|
283
|
+
def self.included(base)
|
284
|
+
base.extend(ClassMethods)
|
285
|
+
end
|
283
286
|
end
|
284
287
|
end
|
288
|
+
|
289
|
+
String.include FatCore::String
|
data/lib/fat_core/symbol.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
|
-
|
2
|
-
# Convert to capitalized string: :hello_world -> "Hello World"
|
3
|
-
def entitle
|
4
|
-
to_s.tr('_', ' ').split(' ').join(' ').entitle
|
5
|
-
end
|
6
|
-
alias as_string entitle
|
1
|
+
require 'fat_core/string'
|
7
2
|
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
module FatCore
|
4
|
+
module Symbol
|
5
|
+
# Convert to capitalized string: :hello_world -> "Hello World"
|
6
|
+
def entitle
|
7
|
+
to_s.tr('_', ' ').split(' ').join(' ').entitle
|
8
|
+
end
|
9
|
+
alias as_string entitle
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
def as_sym
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def tex_quote
|
16
|
+
to_s.tex_quote
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
20
|
+
|
21
|
+
Symbol.include(FatCore::Symbol)
|
data/lib/fat_core/version.rb
CHANGED
data/lib/fat_core.rb
CHANGED
@@ -1,22 +1 @@
|
|
1
|
-
|
2
|
-
require 'date'
|
3
|
-
require 'active_support'
|
4
|
-
require 'active_support/core_ext'
|
5
|
-
require 'active_support/number_helper'
|
6
|
-
require 'csv'
|
7
|
-
|
8
1
|
require 'fat_core/version'
|
9
|
-
|
10
|
-
require 'fat_core/array'
|
11
|
-
require 'fat_core/date'
|
12
|
-
require 'fat_core/boolean'
|
13
|
-
require 'fat_core/enumerable'
|
14
|
-
require 'fat_core/hash'
|
15
|
-
require 'fat_core/kernel'
|
16
|
-
require 'fat_core/latex_eruby'
|
17
|
-
require 'fat_core/nil'
|
18
|
-
require 'fat_core/numeric'
|
19
|
-
require 'fat_core/period'
|
20
|
-
require 'fat_core/range'
|
21
|
-
require 'fat_core/string'
|
22
|
-
require 'fat_core/symbol'
|
data/spec/lib/array_spec.rb
CHANGED