fat_core 2.0.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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