functional-ruby 0.7.4 → 0.7.5
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/README.md +46 -12
- data/lib/functional.rb +19 -0
- data/lib/functional/catalog.rb +487 -0
- data/lib/functional/collection.rb +403 -0
- data/lib/functional/inflect.rb +127 -0
- data/lib/functional/search.rb +132 -0
- data/lib/functional/sort.rb +41 -0
- data/lib/functional/utilities.rb +0 -3
- data/lib/functional/version.rb +1 -1
- data/md/catalog.md +32 -0
- data/md/collection.md +32 -0
- data/md/inflect.md +32 -0
- data/md/pattern_matching.md +2 -2
- data/md/platform.md +32 -0
- data/md/search.md +32 -0
- data/md/sort.md +32 -0
- data/md/utilities.md +2 -2
- data/spec/functional/catalog_spec.rb +1206 -0
- data/spec/functional/collection_spec.rb +752 -0
- data/spec/functional/inflect_spec.rb +85 -0
- data/spec/functional/pattern_matching_spec.rb +0 -2
- data/spec/functional/search_spec.rb +187 -0
- data/spec/functional/sort_spec.rb +61 -0
- data/spec/spec_helper.rb +9 -0
- metadata +23 -2
@@ -0,0 +1,403 @@
|
|
1
|
+
module Functional
|
2
|
+
|
3
|
+
module Collection
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Returns a random sample of #size integers between 0 and 100 or
|
7
|
+
# the provided :min and/or :max options.
|
8
|
+
#
|
9
|
+
# @param [Integer] size the size of the sample to create
|
10
|
+
# @param [Hash] opts processing options
|
11
|
+
#
|
12
|
+
# @option opts [Integer] :min the minimum value in the sample
|
13
|
+
# @option opts [Integer] :max the maximun value in the sample
|
14
|
+
#
|
15
|
+
# @return [Array] an array of integers
|
16
|
+
def random_sample(size, opts={})
|
17
|
+
min = opts[:min].to_i
|
18
|
+
max = opts[:max] || 100
|
19
|
+
sample = []
|
20
|
+
size.times do
|
21
|
+
sample << rand(max-min) + min
|
22
|
+
end
|
23
|
+
return sample
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return the index where to insert item x in list a, assuming a is sorted.
|
27
|
+
#
|
28
|
+
# The return value i is such that all e in a[:i] have e < x, and all e in
|
29
|
+
# a[i:] have e >= x. So if x already appears in the list, a.insert(x) will
|
30
|
+
# insert just before the leftmost x already there.
|
31
|
+
#
|
32
|
+
# Optional args lo (default 0) and hi (default len(a)) bound the
|
33
|
+
# slice of a to be searched.
|
34
|
+
#
|
35
|
+
# @see http://docs.python.org/3/library/bisect.html
|
36
|
+
# @see http://hg.python.org/cpython/file/3.3/Lib/bisect.py
|
37
|
+
# @see http://effbot.org/librarybook/bisect.htm
|
38
|
+
def bisect_left(a, x, opts={})
|
39
|
+
return nil if a.nil?
|
40
|
+
return 0 if a.empty?
|
41
|
+
|
42
|
+
lo = (opts[:lo] || opts[:low]).to_i
|
43
|
+
hi = opts[:hi] || opts[:high] || a.length
|
44
|
+
|
45
|
+
while lo < hi
|
46
|
+
mid = (lo + hi) / 2
|
47
|
+
v = (block_given? ? yield(a[mid]) : a[mid])
|
48
|
+
if v < x
|
49
|
+
lo = mid + 1
|
50
|
+
else
|
51
|
+
hi = mid
|
52
|
+
end
|
53
|
+
end
|
54
|
+
return lo
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the index where to insert item x in list a, assuming a is sorted.
|
58
|
+
#
|
59
|
+
# The return value i is such that all e in a[:i] have e <= x, and all e in
|
60
|
+
# a[i:] have e > x. So if x already appears in the list, a.insert(x) will
|
61
|
+
# insert just after the rightmost x already there.
|
62
|
+
#
|
63
|
+
# Optional args lo (default 0) and hi (default len(a)) bound the
|
64
|
+
# slice of a to be searched.
|
65
|
+
#
|
66
|
+
# @see http://docs.python.org/3/library/bisect.html
|
67
|
+
# @see http://hg.python.org/cpython/file/3.3/Lib/bisect.py
|
68
|
+
# @see http://effbot.org/librarybook/bisect.htm
|
69
|
+
def bisect_right(a, x, opts={})
|
70
|
+
return nil if a.nil?
|
71
|
+
return 0 if a.empty?
|
72
|
+
|
73
|
+
lo = (opts[:lo] || opts[:low]).to_i
|
74
|
+
hi = opts[:hi] || opts[:high] || a.length
|
75
|
+
|
76
|
+
while lo < hi
|
77
|
+
mid = (lo + hi) / 2
|
78
|
+
v = (block_given? ? yield(a[mid]) : a[mid])
|
79
|
+
if x < v
|
80
|
+
hi = mid
|
81
|
+
else
|
82
|
+
lo = mid + 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
return lo
|
86
|
+
end
|
87
|
+
|
88
|
+
alias_method :bisect, :bisect_right
|
89
|
+
|
90
|
+
# Insert item x in list a, and keep it sorted assuming a is sorted.
|
91
|
+
#
|
92
|
+
# If x is already in a, insert it to the left of the leftmost x.
|
93
|
+
#
|
94
|
+
# Optional args lo (default 0) and hi (default len(a)) bound the
|
95
|
+
# slice of a to be searched.
|
96
|
+
#
|
97
|
+
# @see http://docs.python.org/3/library/bisect.html
|
98
|
+
# @see http://hg.python.org/cpython/file/3.3/Lib/bisect.py
|
99
|
+
# @see http://effbot.org/librarybook/bisect.htm
|
100
|
+
def insort_left(a, x, opts={}, &block)
|
101
|
+
return [x] if a.nil?
|
102
|
+
if a.respond_to?(:dup)
|
103
|
+
a = a.dup
|
104
|
+
else
|
105
|
+
a = collect(a)
|
106
|
+
end
|
107
|
+
return insort_left!(a, x, opts, &block)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Insert item x in list a, and keep it sorted assuming a is sorted.
|
111
|
+
# Returns a duplicate of the original list, leaving it intact.
|
112
|
+
#
|
113
|
+
# If x is already in a, insert it to the left of the leftmost x.
|
114
|
+
#
|
115
|
+
# Optional args lo (default 0) and hi (default len(a)) bound the
|
116
|
+
# slice of a to be searched.
|
117
|
+
#
|
118
|
+
# @see http://docs.python.org/3/library/bisect.html
|
119
|
+
# @see http://hg.python.org/cpython/file/3.3/Lib/bisect.py
|
120
|
+
# @see http://effbot.org/librarybook/bisect.htm
|
121
|
+
def insort_left!(a, x, opts={}, &block)
|
122
|
+
return [x] if a.nil?
|
123
|
+
return a << x if a.empty?
|
124
|
+
|
125
|
+
v = (block_given? ? yield(x) : x)
|
126
|
+
index = bisect_left(a, v, opts, &block)
|
127
|
+
return a.insert(index, x)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Insert item x in list a, and keep it sorted assuming a is sorted.
|
131
|
+
# Returns a duplicate of the original list, leaving it intact.
|
132
|
+
#
|
133
|
+
# If x is already in a, insert it to the right of the rightmost x.
|
134
|
+
#
|
135
|
+
# Optional args lo (default 0) and hi (default len(a)) bound the
|
136
|
+
# slice of a to be searched.
|
137
|
+
#
|
138
|
+
# @see http://docs.python.org/3/library/bisect.html
|
139
|
+
# @see http://hg.python.org/cpython/file/3.3/Lib/bisect.py
|
140
|
+
# @see http://effbot.org/librarybook/bisect.htm
|
141
|
+
def insort_right(a, x, opts={}, &block)
|
142
|
+
return [x] if a.nil?
|
143
|
+
if a.respond_to?(:dup)
|
144
|
+
a = a.dup
|
145
|
+
else
|
146
|
+
a = collect(a)
|
147
|
+
end
|
148
|
+
return insort_right!(a, x, opts, &block)
|
149
|
+
end
|
150
|
+
|
151
|
+
alias_method :insort, :insort_right
|
152
|
+
|
153
|
+
# Insert item x in list a, and keep it sorted assuming a is sorted.
|
154
|
+
#
|
155
|
+
# If x is already in a, insert it to the right of the rightmost x.
|
156
|
+
#
|
157
|
+
# Optional args lo (default 0) and hi (default len(a)) bound the
|
158
|
+
# slice of a to be searched.
|
159
|
+
#
|
160
|
+
# @see http://docs.python.org/3/library/bisect.html
|
161
|
+
# @see http://hg.python.org/cpython/file/3.3/Lib/bisect.py
|
162
|
+
# @see http://effbot.org/librarybook/bisect.htm
|
163
|
+
def insort_right!(a, x, opts={}, &block)
|
164
|
+
return [x] if a.nil?
|
165
|
+
return a << x if a.empty?
|
166
|
+
|
167
|
+
v = (block_given? ? yield(x) : x)
|
168
|
+
index = bisect_right(a, v, opts, &block)
|
169
|
+
return a.insert(index, x)
|
170
|
+
end
|
171
|
+
|
172
|
+
alias_method :insort!, :insort_right!
|
173
|
+
|
174
|
+
# Collect sample data from a generic collection, processing each item
|
175
|
+
# with a block when given. Returns an array of the items from +data+
|
176
|
+
# in order.
|
177
|
+
#
|
178
|
+
# When a block is given the block will be applied to both arguments.
|
179
|
+
# Using a block in this way allows computation against a specific field
|
180
|
+
# in a data set of hashes or objects.
|
181
|
+
#
|
182
|
+
# @yield iterates over each element in the data set
|
183
|
+
# @yieldparam item each element in the data set
|
184
|
+
#
|
185
|
+
# @param [Enumerable] data the data set to be collected
|
186
|
+
#
|
187
|
+
# @return [Array] an array of zero or more items
|
188
|
+
def collect(data, opts={})
|
189
|
+
return [] if data.nil?
|
190
|
+
sample = []
|
191
|
+
data.each do |datum|
|
192
|
+
datum = yield(datum) if block_given?
|
193
|
+
sample << datum
|
194
|
+
end
|
195
|
+
return sample
|
196
|
+
end
|
197
|
+
|
198
|
+
# Collect sample data from a generic collection, processing each item
|
199
|
+
# with a block when given. Returns an array of arrays. Each element
|
200
|
+
# is a two-element array where the first element is the index within
|
201
|
+
# the outer array and the second element is the corresponding item
|
202
|
+
# from within +data+. The elements in the returned array are in the
|
203
|
+
# same order as the original +data+ collection.
|
204
|
+
#
|
205
|
+
# @example
|
206
|
+
# sample = [5, 1, 9, 3, 14, 9, 7]
|
207
|
+
# Collection.catalog(sample) #=> [[0, 5], [1, 1], [2, 9], [3, 3], [4, 14], [5, 9], [6, 7]]
|
208
|
+
#
|
209
|
+
# When a block is given the block will be applied to both arguments.
|
210
|
+
# Using a block in this way allows computation against a specific field
|
211
|
+
# in a data set of hashes or objects.
|
212
|
+
#
|
213
|
+
# @yield iterates over each element in the data set
|
214
|
+
# @yieldparam item each element in the data set
|
215
|
+
#
|
216
|
+
# @param [Enumerable] data the data set to be collected
|
217
|
+
#
|
218
|
+
# @return [Array] an array of zero or more items
|
219
|
+
def index_and_catalog(data, opts={})
|
220
|
+
return [] if data.nil?
|
221
|
+
sample = []
|
222
|
+
index = 0
|
223
|
+
data.each do |datum|
|
224
|
+
datum = yield(datum) if block_given?
|
225
|
+
sample << [index, datum]
|
226
|
+
index += 1
|
227
|
+
end
|
228
|
+
return sample
|
229
|
+
end
|
230
|
+
|
231
|
+
alias_method :index_and_catalogue, :index_and_catalog
|
232
|
+
|
233
|
+
# Convert a hash to catalog.
|
234
|
+
#
|
235
|
+
# When a block is given the block will be applied to both arguments.
|
236
|
+
# Using a block in this way allows computation against a specific field
|
237
|
+
# in a data set of hashes or objects.
|
238
|
+
#
|
239
|
+
# @yield iterates over each element in the data set
|
240
|
+
# @yieldparam item each element in the data set
|
241
|
+
#
|
242
|
+
# @param [Enumerable] data the data to convert
|
243
|
+
# @param [Hash] opts search options
|
244
|
+
#
|
245
|
+
# @return [Array] if the data set is in ascending order
|
246
|
+
def catalog_hash(data, opts={})
|
247
|
+
return [] if data.nil? || data.empty?
|
248
|
+
catalog = []
|
249
|
+
data.each do |key, value|
|
250
|
+
value = yield(value) if block_given?
|
251
|
+
catalog << [key, value]
|
252
|
+
end
|
253
|
+
return catalog
|
254
|
+
end
|
255
|
+
|
256
|
+
alias_method :catalogue_hash, :catalog_hash
|
257
|
+
|
258
|
+
# Convert a catalog to a hash. Keeps the last value when keys are
|
259
|
+
# duplicated.
|
260
|
+
#
|
261
|
+
# When a block is given the block will be applied to both arguments.
|
262
|
+
# Using a block in this way allows computation against a specific field
|
263
|
+
# in a data set of hashes or objects.
|
264
|
+
#
|
265
|
+
# @yield iterates over each element in the data set
|
266
|
+
# @yieldparam item each element in the data set
|
267
|
+
#
|
268
|
+
# @param [Enumerable] data the data to convert
|
269
|
+
# @param [Hash] opts search options
|
270
|
+
#
|
271
|
+
# @return [Hash] if the data set is in ascending order
|
272
|
+
def hash_catalog(data, opts={})
|
273
|
+
return {} if data.nil? || data.empty?
|
274
|
+
hash = {}
|
275
|
+
data.each do |item|
|
276
|
+
value = (block_given? ? yield(item.last) : item.last)
|
277
|
+
hash[item.first] = value
|
278
|
+
end
|
279
|
+
return hash
|
280
|
+
end
|
281
|
+
|
282
|
+
alias_method :hash_catalogue, :hash_catalog
|
283
|
+
|
284
|
+
# Scan a collection and determine if the elements are all in
|
285
|
+
# ascending order. Returns true for an empty set and false for
|
286
|
+
# a nil sample.
|
287
|
+
#
|
288
|
+
# When a block is given the block will be applied to both arguments.
|
289
|
+
# Using a block in this way allows computation against a specific field
|
290
|
+
# in a data set of hashes or objects.
|
291
|
+
#
|
292
|
+
# @yield iterates over each element in the data set
|
293
|
+
# @yieldparam item each element in the data set
|
294
|
+
#
|
295
|
+
# @param [Enumerable] data the data set to search
|
296
|
+
# @param [Hash] opts search options
|
297
|
+
#
|
298
|
+
# @return [true, false] if the data set is in ascending order
|
299
|
+
def ascending?(data, opts={})
|
300
|
+
return false if data.nil?
|
301
|
+
(data.size-1).times do |i|
|
302
|
+
if block_given?
|
303
|
+
return false if yield(data[i]) > yield(data[i+1])
|
304
|
+
else
|
305
|
+
return false if data[i] > data[i+1]
|
306
|
+
end
|
307
|
+
end
|
308
|
+
return true
|
309
|
+
end
|
310
|
+
|
311
|
+
# Scan a collection and determine if the elements are all in
|
312
|
+
# descending order. Returns true for an empty set and false for
|
313
|
+
# a nil sample.
|
314
|
+
#
|
315
|
+
# When a block is given the block will be applied to both arguments.
|
316
|
+
# Using a block in this way allows computation against a specific field
|
317
|
+
# in a data set of hashes or objects.
|
318
|
+
#
|
319
|
+
# @yield iterates over each element in the data set
|
320
|
+
# @yieldparam item each element in the data set
|
321
|
+
#
|
322
|
+
# @param [Enumerable] data the data set to search
|
323
|
+
# @param [Hash] opts search options
|
324
|
+
#
|
325
|
+
# @return [true, false] if the data set is in descending order
|
326
|
+
def descending?(data, opts={})
|
327
|
+
return false if data.nil?
|
328
|
+
(data.size-1).times do |i|
|
329
|
+
if block_given?
|
330
|
+
return false if yield(data[i]) < yield(data[i+1])
|
331
|
+
else
|
332
|
+
return false if data[i] < data[i+1]
|
333
|
+
end
|
334
|
+
end
|
335
|
+
return true
|
336
|
+
end
|
337
|
+
|
338
|
+
# Override of #slice from Ruby Array. Provides a consistent interface
|
339
|
+
# to slice data structures that do not have a native #slice method.
|
340
|
+
#
|
341
|
+
# Returns the element at index, or returns a subarray starting at
|
342
|
+
# start and continuing for length elements, or returns a subarray
|
343
|
+
# specified by range. Negative indices count backward from the end
|
344
|
+
# of the array (-1 is the last element). Returns nil if the index
|
345
|
+
# (or starting index) is out of range.
|
346
|
+
#
|
347
|
+
# @overload slice(data, index)
|
348
|
+
# @param [Enumerable] data the collection to slice
|
349
|
+
# @param [Integer] index the index to slice
|
350
|
+
#
|
351
|
+
# @overload slice(data, start, length)
|
352
|
+
# @param [Enumerable] data the collection to slice
|
353
|
+
# @param [Integer] start the start index for the slice
|
354
|
+
# @param [Integer] length the length of the slice
|
355
|
+
#
|
356
|
+
# @overload slice(data, range)
|
357
|
+
# @param [Enumerable] data the collection to slice
|
358
|
+
# @param [Range] range range of indices to include in the slice
|
359
|
+
#
|
360
|
+
# @return [Array] the slice
|
361
|
+
def slice(data, *args)
|
362
|
+
index = args[0]
|
363
|
+
length = args[1]
|
364
|
+
if args.size == 1
|
365
|
+
if index.is_a? Range
|
366
|
+
slice_with_range(data, index)
|
367
|
+
else
|
368
|
+
slice_with_index(data, index)
|
369
|
+
end
|
370
|
+
elsif args.size == 2
|
371
|
+
slice_with_length(data, index, length)
|
372
|
+
else
|
373
|
+
raise ArgumentError.new("wrong number of arguments (#{args.size} for 2..3)")
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
# :nodoc:
|
378
|
+
# @private
|
379
|
+
def slice_with_index(data, index)
|
380
|
+
return data[index]
|
381
|
+
end
|
382
|
+
|
383
|
+
# :nodoc:
|
384
|
+
# @private
|
385
|
+
def slice_with_length(data, start, length)
|
386
|
+
range = Range.new(start, start+length-1)
|
387
|
+
slice_with_range(data, range)
|
388
|
+
end
|
389
|
+
|
390
|
+
# :nodoc:
|
391
|
+
# @private
|
392
|
+
def slice_with_range(data, range)
|
393
|
+
return nil if range.first < 0 || range.first >= data.size
|
394
|
+
last = [range.last, data.size-1].min
|
395
|
+
range = Range.new(range.first, last)
|
396
|
+
slice = []
|
397
|
+
range.each do |index|
|
398
|
+
slice << data[index]
|
399
|
+
end
|
400
|
+
return slice
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Functional
|
2
|
+
|
3
|
+
module Inflect
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# By default, +camelize+ converts strings to UpperCamelCase. If the argument
|
7
|
+
# to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
|
8
|
+
# lowerCamelCase.
|
9
|
+
#
|
10
|
+
# +camelize+ will also convert '/' to '::' which is useful for converting
|
11
|
+
# paths to namespaces.
|
12
|
+
#
|
13
|
+
# 'active_model'.camelize # => "ActiveModel"
|
14
|
+
# 'active_model'.camelize(:lower) # => "activeModel"
|
15
|
+
# 'active_model/errors'.camelize # => "ActiveModel::Errors"
|
16
|
+
# 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
|
17
|
+
#
|
18
|
+
# As a rule of thumb you can think of +camelize+ as the inverse of
|
19
|
+
# +underscore+, though there are cases where that does not hold:
|
20
|
+
#
|
21
|
+
# 'SSLError'.underscore.camelize # => "SslError"
|
22
|
+
#
|
23
|
+
# @see https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
|
24
|
+
def camelize(term, uppercase_first_letter = true)
|
25
|
+
string = term.to_s
|
26
|
+
if uppercase_first_letter == true
|
27
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
28
|
+
else
|
29
|
+
string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
|
30
|
+
end
|
31
|
+
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
|
32
|
+
end
|
33
|
+
|
34
|
+
# Makes an underscored, lowercase form from the expression in the string.
|
35
|
+
#
|
36
|
+
# Changes '::' to '/' to convert namespaces to paths.
|
37
|
+
#
|
38
|
+
# 'ActiveModel'.underscore # => "active_model"
|
39
|
+
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
|
40
|
+
#
|
41
|
+
# As a rule of thumb you can think of +underscore+ as the inverse of
|
42
|
+
# +camelize+, though there are cases where that does not hold:
|
43
|
+
#
|
44
|
+
# 'SSLError'.underscore.camelize # => "SslError"
|
45
|
+
#
|
46
|
+
# @see https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
|
47
|
+
def underscore(camel_cased_word)
|
48
|
+
word = camel_cased_word.to_s.dup
|
49
|
+
word.gsub!('::', '/')
|
50
|
+
word.gsub!(/\s+/, '_')
|
51
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
52
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
53
|
+
word.tr!("-", "_")
|
54
|
+
word.downcase!
|
55
|
+
word
|
56
|
+
end
|
57
|
+
|
58
|
+
# Capitalizes the first word and turns underscores into spaces and strips a
|
59
|
+
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty
|
60
|
+
# output.
|
61
|
+
#
|
62
|
+
# 'employee_salary'.humanize # => "Employee salary"
|
63
|
+
# 'author_id'.humanize # => "Author"
|
64
|
+
#
|
65
|
+
# @see https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
|
66
|
+
def humanize(lower_case_and_underscored_word)
|
67
|
+
result = lower_case_and_underscored_word.to_s.dup
|
68
|
+
result.gsub!(/_id$/, "")
|
69
|
+
result.tr!('_', ' ')
|
70
|
+
result.gsub(/([a-z\d]*)/i) { |match| "#{match.downcase}" }.gsub(/^\w/) { $&.upcase }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Capitalizes all the words and replaces some characters in the string to
|
74
|
+
# create a nicer looking title. +titleize+ is meant for creating pretty
|
75
|
+
# output. It is not used in the Rails internals.
|
76
|
+
#
|
77
|
+
# +titleize+ is also aliased as +titlecase+.
|
78
|
+
#
|
79
|
+
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
|
80
|
+
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
|
81
|
+
# 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
|
82
|
+
# 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
|
83
|
+
#
|
84
|
+
# @see https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
|
85
|
+
def titleize(word)
|
86
|
+
#humanize(underscore(word)).gsub(/\b(?<!['`])[a-z]/) { $&.capitalize }
|
87
|
+
humanize(underscore(word)).gsub(/\b["'`]?[a-z]/) { $&.capitalize }
|
88
|
+
end
|
89
|
+
|
90
|
+
# Replaces underscores with dashes in the string.
|
91
|
+
#
|
92
|
+
# 'puni_puni'.dasherize # => "puni-puni"
|
93
|
+
#
|
94
|
+
# @see https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
|
95
|
+
def dasherize(underscored_word)
|
96
|
+
underscored_word.to_s.dup.tr('_', '-')
|
97
|
+
end
|
98
|
+
|
99
|
+
# Add the given extension to the given file name. Removes the current
|
100
|
+
# extension if one exists. Strips trailing characters from the file name
|
101
|
+
# if present. Does nothing if the file name already has the given
|
102
|
+
# extension.
|
103
|
+
#
|
104
|
+
# @example
|
105
|
+
# extensionize('my_file.png', :png) #=> 'my_file.png'
|
106
|
+
# extensionize('my_file', :png) #=> 'my_file.png'
|
107
|
+
# extensionize('my_file.png', :jpg) #=> 'my_file.png.jpg'
|
108
|
+
# extensionize('My File', :png) #=> 'My File.png'
|
109
|
+
# extensionize('My File ', :png) #=> 'My File.png'
|
110
|
+
# extensionize('my_file.png', :jpg, :chomp => true) #=> 'my_file.jpg'
|
111
|
+
#
|
112
|
+
# @param [String] fname the name of the file to add an extension to
|
113
|
+
# @param [String, Symbol] ext the extension to append, with or without a leading dot
|
114
|
+
# @param [Hash] opts processing options
|
115
|
+
#
|
116
|
+
# @option opts [Symbol] :chomp when true removes the existing entension
|
117
|
+
# from the file name (default: false)
|
118
|
+
#
|
119
|
+
# @return [String] the *fname* with *ext* appended
|
120
|
+
def extensionize(fname, ext, opts={})
|
121
|
+
extname = File.extname(fname)
|
122
|
+
return fname if (extname =~ /\.?#{ext}$/i) == 0
|
123
|
+
fname = fname.gsub(/#{extname}$/, '') if opts[:chomp] == true
|
124
|
+
return fname.strip + '.' + ext.to_s.gsub(/^\./, '')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|