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.
@@ -1,284 +1,289 @@
1
1
  require 'damerau-levenshtein'
2
+ require 'active_support/core_ext/regexp'
2
3
 
3
- class String
4
- # Remove leading and trailing white space and compress internal runs of
5
- # white space to a single space.
6
- def clean
7
- strip.squeeze(' ')
8
- end
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
- def distance(other, block_size: 1, max_distance: 10)
11
- dl = DamerauLevenshtein
12
- # NOTE: DL 'gives up after' max_distance, so the distance function
13
- # will return max_distance+1 if the distance is bigger than that.
14
- # Here we subtract 1 so the max_distance also becomes the max
15
- # return value.
16
- dl.distance(self, other, block_size, max_distance - 1)
17
- end
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
- # See if self contains colon- or space-separated words that include
20
- # the colon- or space-separated words of other. Return the matched
21
- # portion of self. Other cannot be a regex embedded in a string.
22
- def fuzzy_match(other)
23
- # Remove periods, commas, and apostrophes
24
- other = other.gsub(/[\*.,']/, '')
25
- target = gsub(/[\*.,']/, '')
26
- matchers = other.split(/[: ]+/)
27
- regexp_string = matchers.map { |m| ".*?#{Regexp.escape(m)}.*?" }.join('[: ]')
28
- regexp_string.sub!(/^\.\*\?/, '')
29
- regexp_string.sub!(/\.\*\?$/, '')
30
- regexp = /#{regexp_string}/i
31
- matched_text =
32
- if (match = regexp.match(target))
33
- match[0]
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
- matched_text
36
- end
53
+ end
37
54
 
38
- # Here are instance methods for the class that includes Matchable
39
- # This tries to convert the receiver object into a string, then
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
- # Convert a string of the form '/.../Iixm' to a regular expression. However,
54
- # make the regular expression case-insensitive by default and extend the
55
- # modifier syntax to allow '/I' to indicate case-sensitive.
56
- def to_regexp
57
- if self =~ %r{^\s*/([^/]*)/([Iixm]*)\s*$}
58
- body = $1
59
- opts = $2
60
- flags = Regexp::IGNORECASE
61
- unless opts.blank?
62
- flags = 0 if opts.include?('I')
63
- flags |= Regexp::IGNORECASE if opts.include?('i')
64
- flags |= Regexp::EXTENDED if opts.include?('x')
65
- flags |= Regexp::MULTILINE if opts.include?('m')
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
- def as_string
81
- self
82
- end
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
- def number?
85
- Float(self)
86
- true
87
- rescue ArgumentError
88
- return false
89
- end
86
+ def as_string
87
+ self
88
+ end
90
89
 
91
- # If the string is a number, add grouping commas to the whole number part.
92
- def commify
93
- # Break the number into parts
94
- return self unless clean =~ /\A(-)?(\d*)((\.)?(\d*))?\z/
95
- neg = $1 || ''
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
- def wrap(width = 70, hang = 0)
112
- result = ''
113
- first_line = true
114
- first_word_on_line = true
115
- line_width_so_far = 0
116
- words = split(' ')
117
- words.each do |w|
118
- if !first_line && first_word_on_line
119
- w = ' ' * hang + w
120
- end
121
- unless first_word_on_line
122
- w = ' ' + w
123
- end
124
- result << w
125
- first_word_on_line = false
126
- line_width_so_far += 1 + w.length
127
- if line_width_so_far >= width
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
- def tex_quote
138
- r = dup
139
- r = r.gsub(/[{]/, 'XzXzXobXzXzX')
140
- r = r.gsub(/[}]/, 'XzXzXcbXzXzX')
141
- r = r.gsub(/\\/, '\textbackslash{}')
142
- r = r.gsub(/\^/, '\textasciicircum{}')
143
- r = r.gsub(/~/, '\textasciitilde{}')
144
- r = r.gsub(/\|/, '\textbar{}')
145
- r = r.gsub(/\</, '\textless{}')
146
- r = r.gsub(/\>/, '\textgreater{}')
147
- r = r.gsub(/([_$&%#])/) { |m| '\\' + m }
148
- r = r.gsub('XzXzXobXzXzX', '\\{')
149
- r.gsub('XzXzXcbXzXzX', '\\}')
150
- end
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
- def self.random(size = 8)
153
- ('a'..'z').cycle.take(size).shuffle.join
154
- end
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
- # Convert a string with an all-digit date to an iso string
157
- # E.g., "20090923" -> "2009-09-23"
158
- def digdate2iso
159
- sub(/(\d\d\d\d)(\d\d)(\d\d)/, '\1-\2-\3')
160
- end
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
- def entitle!
163
- little_words = %w(a an the and or in on under of from as by to)
164
- newwords = []
165
- words = split(/\s+/)
166
- first_word = true
167
- num_words = words.length
168
- words.each_with_index do |w, k|
169
- last_word = (k + 1 == num_words)
170
- if w =~ %r{c/o}i
171
- # Care of
172
- newwords.push('c/o')
173
- elsif w =~ /^p\.?o\.?$/i
174
- # Post office
175
- newwords.push('P.O.')
176
- elsif w =~ /^[0-9]+(st|nd|rd|th)$/i
177
- # Ordinals
178
- newwords.push(w.downcase)
179
- elsif w =~ /^(cr|dr|st|rd|ave|pk|cir)$/i
180
- # Common abbrs to capitalize
181
- newwords.push(w.capitalize)
182
- elsif w =~ /^(us|ne|se|rr)$/i
183
- # Common 2-letter abbrs to upcase
184
- newwords.push(w.upcase)
185
- elsif w =~ /^[0-9].*$/i
186
- # Other runs starting with numbers,
187
- # like 3-A
188
- newwords.push(w.upcase)
189
- elsif w =~ /^(N|S|E|W|NE|NW|SE|SW)$/i
190
- # Compass directions all caps
191
- newwords.push(w.upcase)
192
- elsif w =~ /^[^aeiouy]*$/i && w.size > 2
193
- # All consonants and at least 3 chars, probably abbr
194
- newwords.push(w.upcase)
195
- elsif w =~ /^(\w+)-(\w+)$/i
196
- # Hyphenated double word
197
- newwords.push($1.capitalize + '-' + $2.capitalize)
198
- elsif little_words.include?(w.downcase)
199
- # Only capitalize at beginning or end
200
- newwords.push(first_word || last_word ? w.capitalize : w.downcase)
201
- else
202
- # All else
203
- newwords.push(w.capitalize)
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
- first_word = false
209
+ self[0..-1] = newwords.join(' ')
206
210
  end
207
- self[0..-1] = newwords.join(' ')
208
- end
209
211
 
210
- def entitle
211
- dup.entitle!
212
- end
212
+ def entitle
213
+ dup.entitle!
214
+ end
213
215
 
214
- # Format the string according to the given sprintf format.
215
- def format_by(fmt)
216
- return self unless fmt
217
- begin
218
- format fmt, self
219
- rescue ArgumentError
220
- return self
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
- # Thanks to Eugene at stackoverflow for the following.
225
- # http://stackoverflow.com/questions/8806643/
226
- # colorized-output-breaks-linewrapping-with-readline
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
- def console_dark_red
234
- colorize(self, "\001\e[31m\002")
235
- end
229
+ def console_green
230
+ colorize(self, "\001\e[1m\e[32m\002")
231
+ end
236
232
 
237
- def console_green
238
- colorize(self, "\001\e[1m\e[32m\002")
239
- end
233
+ def console_dark_green
234
+ colorize(self, "\001\e[32m\002")
235
+ end
240
236
 
241
- def console_dark_green
242
- colorize(self, "\001\e[32m\002")
243
- end
237
+ def console_yellow
238
+ colorize(self, "\001\e[1m\e[33m\002")
239
+ end
244
240
 
245
- def console_yellow
246
- colorize(self, "\001\e[1m\e[33m\002")
247
- end
241
+ def console_dark_yellow
242
+ colorize(self, "\001\e[33m\002")
243
+ end
248
244
 
249
- def console_dark_yellow
250
- colorize(self, "\001\e[33m\002")
251
- end
245
+ def console_blue
246
+ colorize(self, "\001\e[1m\e[34m\002")
247
+ end
252
248
 
253
- def console_blue
254
- colorize(self, "\001\e[1m\e[34m\002")
255
- end
249
+ def console_dark_blue
250
+ colorize(self, "\001\e[34m\002")
251
+ end
256
252
 
257
- def console_dark_blue
258
- colorize(self, "\001\e[34m\002")
259
- end
253
+ def console_purple
254
+ colorize(self, "\001\e[1m\e[35m\002")
255
+ end
260
256
 
261
- def console_purple
262
- colorize(self, "\001\e[1m\e[35m\002")
263
- end
257
+ def console_cyan
258
+ colorize(self, "\001\e[1m\e[36m\002")
259
+ end
264
260
 
265
- def console_cyan
266
- colorize(self, "\001\e[1m\e[36m\002")
267
- end
261
+ def console_def
262
+ colorize(self, "\001\e[1m\002")
263
+ end
268
264
 
269
- def console_def
270
- colorize(self, "\001\e[1m\002")
271
- end
265
+ def console_bold
266
+ colorize(self, "\001\e[1m\002")
267
+ end
272
268
 
273
- def console_bold
274
- colorize(self, "\001\e[1m\002")
275
- end
269
+ def console_blink
270
+ colorize(self, "\001\e[5m\002")
271
+ end
276
272
 
277
- def console_blink
278
- colorize(self, "\001\e[5m\002")
279
- end
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
- def colorize(text, color_code)
282
- "#{color_code}#{text}\001\e[0m\002"
283
+ def self.included(base)
284
+ base.extend(ClassMethods)
285
+ end
283
286
  end
284
287
  end
288
+
289
+ String.include FatCore::String
@@ -1,15 +1,21 @@
1
- class Symbol
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
- def as_sym
9
- self
10
- end
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
- def tex_quote
13
- to_s.tex_quote
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)
@@ -1,7 +1,7 @@
1
1
  module FatCore
2
- MAJOR = 2
2
+ MAJOR = 3
3
3
  MINOR = 0
4
- PATCH = 1
4
+ PATCH = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, PATCH].compact.join('.')
7
7
  end
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'
@@ -1,5 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
+ require 'fat_core/array'
4
+
3
5
  describe Array do
4
6
  it 'should be able to report its last index' do
5
7
  letters = ('a'..'z').to_a
@@ -0,0 +1,7 @@
1
+ require 'fat_core/big_decimal'
2
+
3
+ describe BigDecimal do
4
+ it 'should provide a human-readable inspect for BigDecimal' do
5
+ expect(BigDecimal.new('33.45').inspect).to eq '33.45'
6
+ end
7
+ end