lite-ruby 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,19 @@
2
2
 
3
3
  class Hash
4
4
 
5
+ class << self
6
+
7
+ def zip(keys, values)
8
+ keys.size.times.with_object({}) { |i, hash| hash[keys[i]] = values[i] }
9
+ end
10
+
11
+ end
12
+
13
+ def alias(new_key, old_key)
14
+ self[new_key] = self[old_key] if key?(old_key)
15
+ self
16
+ end
17
+
5
18
  def assert_valid_keys!(*valid_keys)
6
19
  each_key do |key|
7
20
  next if valid_keys.include?(key)
@@ -50,6 +63,31 @@ class Hash
50
63
  end
51
64
  # rubocop:enable Style/GuardClause
52
65
 
66
+ # rubocop:disable Metrics/MethodLength
67
+ def collate(*others)
68
+ hash = {}
69
+
70
+ each_key { |key| hash[key] = [] }
71
+
72
+ others.each do |other|
73
+ other.each_key { |key| hash[key] = [] }
74
+ end
75
+
76
+ each { |key, val| hash[key] << val }
77
+
78
+ others.each do |other|
79
+ other.each { |key, val| hash[key] << val }
80
+ end
81
+
82
+ hash.each_value(&:flatten!)
83
+ hash
84
+ end
85
+ # rubocop:enable Metrics/MethodLength
86
+
87
+ def collate!(other_hash)
88
+ replace(collate(other_hash))
89
+ end
90
+
53
91
  def collect_keys
54
92
  collect { |key, _| yield(key) }
55
93
  end
@@ -58,16 +96,41 @@ class Hash
58
96
  collect { |_, val| yield(val) }
59
97
  end
60
98
 
99
+ def dearray_values(idx = 0)
100
+ each_with_object({}) do |(key, val), hash|
101
+ hash[key] = case val
102
+ when Array then val[idx] || val[-1]
103
+ else val
104
+ end
105
+ end
106
+ end
107
+
108
+ def dearray_values!(idx = 0)
109
+ replace(dearray_values(idx))
110
+ end
111
+
112
+ def dearray_singular_values
113
+ each_with_object({}) do |(key, val), hash|
114
+ hash[key] = case val
115
+ when Array then val.size < 2 ? val[0] : val
116
+ else val
117
+ end
118
+ end
119
+ end
120
+
121
+ def dearray_singular_values!
122
+ replace(dearray_singular_values)
123
+ end
124
+
61
125
  def deep_merge(other_hash, &block)
62
126
  dup.deep_merge!(other_hash, &block)
63
127
  end
64
128
 
65
- # rubocop:disable Metrics/MethodLength
66
129
  def deep_merge!(other_hash, &block)
67
- other_hash.each_pair do |current_key, other_value|
68
- this_value = self[current_key]
130
+ other_hash.each_pair.with_object(self) do |(current_key, other_value), hash|
131
+ this_value = hash[current_key]
69
132
 
70
- self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
133
+ hash[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
71
134
  this_value.deep_merge(other_value, yield(block))
72
135
  elsif block_given? && key?(current_key)
73
136
  yield(current_key, this_value, other_value)
@@ -75,10 +138,20 @@ class Hash
75
138
  other_value
76
139
  end
77
140
  end
141
+ end
78
142
 
79
- self
143
+ def delete_unless
144
+ delete_if { |key, val| !yield(key, val) }
145
+ end
146
+
147
+ def delete_values(*values)
148
+ each_key.with_object([]) do |key, array|
149
+ next unless values.include?(self[key])
150
+
151
+ array << key
152
+ delete(key)
153
+ end
80
154
  end
81
- # rubocop:enable Metrics/MethodLength
82
155
 
83
156
  def demote(key)
84
157
  dup.demote!(key)
@@ -99,6 +172,12 @@ class Hash
99
172
  each { |key, val| self[key] = val.nil? ? value : val }
100
173
  end
101
174
 
175
+ def diff(hash)
176
+ h1 = dup.delete_if { |k, v| hash[k] == v }
177
+ h2 = hash.dup.delete_if { |k, _| key?(k) }
178
+ h1.merge(h2)
179
+ end
180
+
102
181
  def except(*keys)
103
182
  dup.except!(*keys)
104
183
  end
@@ -119,6 +198,30 @@ class Hash
119
198
  inject(self) { |hash, (key, val)| hash.merge(yield(key, val)) }
120
199
  end
121
200
 
201
+ def insert(name, value)
202
+ return false if key?(name)
203
+
204
+ store(name, value)
205
+ true
206
+ end
207
+
208
+ def invert
209
+ each_pair.with_object({}) do |(key, val), hash|
210
+ if val.is_a?(Array)
211
+ val.each { |x| hash[x] = (hash.key?(x) ? [key, hash[x]].flatten : key) }
212
+ else
213
+ hash[val] = (hash.key?(val) ? [key, hash[val]].flatten : key)
214
+ end
215
+ end
216
+ end
217
+
218
+ def keys?(*check_keys)
219
+ unknown_keys = check_keys - keys
220
+ unknown_keys.empty?
221
+ end
222
+
223
+ alias has_keys? keys?
224
+
122
225
  def nillify
123
226
  dup.nillify!
124
227
  end
@@ -137,6 +240,13 @@ class Hash
137
240
  replace(only_fill(*keys, placeholder: placeholder))
138
241
  end
139
242
 
243
+ def only_keys?(*check_keys)
244
+ unknown_keys = keys - check_keys
245
+ unknown_keys.empty?
246
+ end
247
+
248
+ alias has_only_keys? only_keys?
249
+
140
250
  def pair?(key, value)
141
251
  self[key] == value
142
252
  end
@@ -277,6 +387,22 @@ class Hash
277
387
 
278
388
  alias to_o to_object
279
389
 
390
+ def update_each
391
+ replace(each_with_object({}) { |(key, val), hash| hash.update(yield(key, val)) })
392
+ end
393
+
394
+ def update_keys
395
+ return to_enum(:update_keys) unless block_given?
396
+
397
+ replace(each_with_object({}) { |(key, val), hash| hash[yield(key)] = val })
398
+ end
399
+
400
+ def update_values
401
+ return to_enum(:update_values) unless block_given?
402
+
403
+ replace(each_with_object({}) { |(key, val), hash| hash[key] = yield(val) })
404
+ end
405
+
280
406
  def vacant?(key)
281
407
  self[key].blank?
282
408
  end
@@ -7,6 +7,38 @@ class Integer
7
7
  I: 1
8
8
  }.freeze
9
9
 
10
+ def bit(bit)
11
+ if bit.negative?
12
+ mask = (1 << ~bit)
13
+ self & ~mask
14
+ else
15
+ mask = (1 << bit)
16
+ self | mask
17
+ end
18
+ end
19
+
20
+ def bit?(bit)
21
+ mask = (1 << bit)
22
+ (self & mask) != 0
23
+ end
24
+
25
+ def bit_clear(bit)
26
+ mask = (1 << bit)
27
+ self & ~mask
28
+ end
29
+
30
+ def bitmask(mask)
31
+ if mask.negative?
32
+ self & mask
33
+ else
34
+ self | mask
35
+ end
36
+ end
37
+
38
+ def bitmask?(mask)
39
+ (self & mask) != 0
40
+ end
41
+
10
42
  def factorial
11
43
  return 1 if zero?
12
44
 
@@ -6,6 +6,14 @@ class Numeric
6
6
  self + num
7
7
  end
8
8
 
9
+ def at_least(lower)
10
+ self >= lower ? self : lower
11
+ end
12
+
13
+ def at_most(upper)
14
+ self <= upper ? self : upper
15
+ end
16
+
9
17
  # rubocop:disable Metrics/MethodLength
10
18
  def clamp(minimum, maximum = nil)
11
19
  if minimum.is_a?(Range)
@@ -23,14 +31,43 @@ class Numeric
23
31
  end
24
32
  # rubocop:enable Metrics/MethodLength
25
33
 
34
+ def close?(number, epsilon = 0.01)
35
+ return self == number if epsilon.zero?
36
+
37
+ a = to_f
38
+ b = number.to_f
39
+
40
+ if a.zero? || b.zero?
41
+ (a - b).abs < epsilon
42
+ else
43
+ (a / b - 1).abs < epsilon
44
+ end
45
+ end
46
+
26
47
  def decrement(amount = 1.0)
27
48
  self - amount
28
49
  end
29
50
 
30
- def distance(num)
51
+ def delimit(options = {})
52
+ delimiter = options[:delimiter] || ','
53
+ separator = options[:separator] || '.'
54
+
55
+ digits, decimals = to_s.split('.')
56
+ digits = digits.reverse.chars.each_slice(3).map(&:join).join(delimiter).reverse
57
+
58
+ return digits unless decimals
59
+
60
+ [digits, decimals].join(separator)
61
+ end
62
+
63
+ def delta(num)
31
64
  (self - num).abs
32
65
  end
33
66
 
67
+ def distance(num)
68
+ self - num
69
+ end
70
+
34
71
  def divide(num)
35
72
  return num if num.zero?
36
73
 
@@ -158,6 +195,12 @@ class Numeric
158
195
  self**num
159
196
  end
160
197
 
198
+ def range(value)
199
+ (self - value)..(self + value)
200
+ end
201
+
202
+ alias plus_minus range
203
+
161
204
  def root(num)
162
205
  self**(1.0 / num)
163
206
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'ostruct'
4
+
5
+ class OpenStruct
6
+
7
+ def initialize(hash = nil, &block)
8
+ @table = if block && block.arity == 2
9
+ Hash.new(&block)
10
+ else
11
+ {}
12
+ end
13
+
14
+ if hash
15
+ hash.each do |key, val|
16
+ @table[key.to_sym] = val
17
+ new_ostruct_member(key)
18
+ end
19
+ end
20
+
21
+ yield self if block && block.arity == 1
22
+ end
23
+
24
+ def [](key)
25
+ key = key.to_sym unless key.is_a?(Symbol)
26
+ @table[key]
27
+ end
28
+
29
+ def []=(key, val)
30
+ raise TypeError, "can't modify frozen #{self.class}", caller(1) if frozen?
31
+
32
+ key = key.to_sym unless key.is_a?(Symbol)
33
+ @table[key] = val
34
+ end
35
+
36
+ def attributes
37
+ each_pair.with_object({}) { |(key, val), hash| hash[key] = val }
38
+ end
39
+
40
+ def replace(args)
41
+ args.each_pair { |key, val| send("#{key}=", val) }
42
+ end
43
+
44
+ end
@@ -33,28 +33,34 @@ class String
33
33
  }.freeze
34
34
 
35
35
  def acronym
36
- gsub(/(([a-zA-Z0-9])([a-zA-Z0-9])*)./, '\\2')
36
+ dup.acronym!
37
37
  end
38
38
 
39
39
  def acronym!
40
- replace(acronym)
40
+ gsub!(/(([a-zA-Z0-9])([a-zA-Z0-9])*)./, '\\2') || self
41
41
  end
42
42
 
43
43
  def any?(*keys)
44
44
  keys.any? { |key| include?(key) }
45
45
  end
46
46
 
47
+ def ascii_only(alt = '')
48
+ dup.ascii_only!(alt)
49
+ end
50
+
51
+ def ascii_only!(alt = '')
52
+ encode_only!('ASCII', alt)
53
+ end
54
+
47
55
  def at(position)
48
56
  self[position]
49
57
  end
50
58
 
51
59
  def camelize(first_letter = :upper)
52
- if first_letter.to_sym != :lower
53
- regex_last = Regexp.last_match(1).upcase
54
- to_s.gsub(%r{\/(.?)}) { "::#{regex_last}" }.gsub(%r{^/(?:^|_)(.)}) { regex_last }
55
- else
56
- "#{to_s.first.chr.downcase}#{camelize(self)[1..-1]}"
57
- end
60
+ case first_letter
61
+ when :upper, true then gsub(/(\A|\s)([a-z])/) { $1 + $2.upcase }
62
+ when :lower, false then gsub(/(\A|\s)([A-Z])/) { $1 + $2.downcase }
63
+ end || self
58
64
  end
59
65
 
60
66
  alias camelcase camelize
@@ -65,12 +71,17 @@ class String
65
71
 
66
72
  alias camelcase! camelize!
67
73
 
74
+ def capitalized?
75
+ capitalize == self
76
+ end
77
+
68
78
  def classify
69
- to_s.sub(/.*\./, '').camelize
79
+ dup.classify!
70
80
  end
71
81
 
72
82
  def classify!
73
- replace(classify)
83
+ sub!(/.*\./, '')
84
+ camelize!
74
85
  end
75
86
 
76
87
  def constantize
@@ -78,15 +89,15 @@ class String
78
89
  end
79
90
 
80
91
  def dasherize
81
- tr(/_/, '-')
92
+ dup.dasherize!
82
93
  end
83
94
 
84
95
  def dasherize!
85
- replace(dasherize)
96
+ tr!('_', '-')
86
97
  end
87
98
 
88
99
  def deconstantize
89
- to_s[0, rindex('::') || 0]
100
+ [0, rindex('::') || 0]
90
101
  end
91
102
 
92
103
  def deconstantize!
@@ -94,7 +105,7 @@ class String
94
105
  end
95
106
 
96
107
  def demodulize
97
- to_s.gsub(/^.*::/, '')
108
+ gsub(/^.*::/, '')
98
109
  end
99
110
 
100
111
  def demodulize!
@@ -111,6 +122,25 @@ class String
111
122
  downcase == self
112
123
  end
113
124
 
125
+ def each_word(&block)
126
+ words.each(&block)
127
+ end
128
+
129
+ def encode_only(encoding, alt = '')
130
+ dup.encode_only!(encoding, alt)
131
+ end
132
+
133
+ def encode_only!(encoding, alt = '')
134
+ encoding_options = {
135
+ invalid: :replace,
136
+ undef: :replace,
137
+ replace: alt,
138
+ UNIVERSAL_NEWLINE_DECORATOR: true
139
+ }
140
+
141
+ encode!(Encoding.find(encoding), encoding_options)
142
+ end
143
+
114
144
  def ellipsize(ellipsize_at, options = {})
115
145
  return self if length <= ellipsize_at
116
146
 
@@ -139,7 +169,7 @@ class String
139
169
  end
140
170
 
141
171
  def headerize
142
- squish.split(' ').map(&:capitalize).join(' ')
172
+ squish.each_word(&:capitalize!).join(' ')
143
173
  end
144
174
 
145
175
  def headerize!
@@ -147,28 +177,28 @@ class String
147
177
  end
148
178
 
149
179
  def humanize(options = {})
150
- capitalize = options[:capitalize] || true
151
-
152
- underscore.gsub(/_id\z/, '')
153
- .tr('_', ' ')
154
- .squish
155
- .gsub(/([a-z\d]*)/i, &:downcase)
156
- .gsub(/\A\w/) { |str| capitalize ? str.upcase : str }
180
+ dup.humanize!(options)
157
181
  end
158
182
 
159
- def humanize!(options = {})
160
- replace(humanize(options))
183
+ def humanize!(capitalize: true)
184
+ underscore!
185
+ gsub!(/_id\z/, '')
186
+ tr!('_', ' ')
187
+ squish!
188
+ gsub!(/([a-z\d]*)/i, &:downcase)
189
+ gsub!(/\A\w/) { |str| capitalize ? str.upcase : str } || self
161
190
  end
162
191
 
163
- def indent(amount, indent_string = nil, indent_empty_lines = false)
164
- indent_string = indent_string || self[/^[ \t]/] || ' '
165
- substitutes = indent_empty_lines ? /^/ : /^(?!$)/
166
-
167
- gsub(substitutes, indent_string * amount)
192
+ def indent(amount, seperator = ' ')
193
+ dup.indent!(amount, seperator)
168
194
  end
169
195
 
170
- def indent!(amount, indent_string = nil, indent_empty_lines = false)
171
- replace(indent(amount, indent_string, indent_empty_lines))
196
+ def indent!(amount, seperator = ' ')
197
+ if amount >= 0
198
+ gsub!(/^/, seperator * amount)
199
+ else
200
+ gsub!(/^#{Regexp.escape(seperator)}{0,#{-amount}}/, '')
201
+ end
172
202
  end
173
203
 
174
204
  def index_all(pattern)
@@ -186,19 +216,18 @@ class String
186
216
  end
187
217
 
188
218
  def labelize(options = {})
189
- capitalize = options[:capitalize] || true
190
-
191
- underscore.tr('_', ' ')
192
- .squish
193
- .gsub(/([a-z\d]*)/i, &:downcase)
194
- .gsub(/\A\w/) { |str| capitalize ? str.upcase : str }
195
- .gsub(/ id\z/, ' ID')
219
+ dup.labelize!(options)
196
220
  end
197
221
 
198
222
  alias labelcase labelize
199
223
 
200
- def labelize!(options = {})
201
- replace(labelize(options))
224
+ def labelize!(capitalize: true)
225
+ underscore!
226
+ tr!('_', ' ')
227
+ squish!
228
+ gsub!(/([a-z\d]*)/i, &:downcase)
229
+ gsub!(/\A\w/) { |str| capitalize ? str.upcase : str }
230
+ gsub!(/ id\z/, ' ID') || self
202
231
  end
203
232
 
204
233
  alias labelcase! labelize!
@@ -213,6 +242,40 @@ class String
213
242
  end
214
243
  end
215
244
 
245
+ def lchomp(match)
246
+ dup.lchomp!(match)
247
+ end
248
+
249
+ def lchomp!(match)
250
+ return self unless index(match)
251
+
252
+ self[0...match.size] = ''
253
+ self
254
+ end
255
+
256
+ def methodize
257
+ dup.methodize!
258
+ end
259
+
260
+ def methodize!
261
+ gsub!(/([A-Z]+)([A-Z])/, '\1_\2')
262
+ gsub!(/([a-z])([A-Z])/, '\1_\2')
263
+ gsub!('/', '__')
264
+ gsub!('::', '__')
265
+ downcase! || self
266
+ end
267
+
268
+ def modulize
269
+ dup.modulize!
270
+ end
271
+
272
+ def modulize!
273
+ gsub!(/__(.?)/) { "::#{$1.upcase}" }
274
+ gsub!(%r{/(.?)}) { "::#{$1.upcase}" }
275
+ gsub!(/(?:_+|-+)([a-z])/) { $1.upcase }
276
+ gsub!(/(\A|\s)([a-z])/) { $1 + $2.upcase } || self
277
+ end
278
+
216
279
  def mixedcase?
217
280
  !upcase? && !downcase?
218
281
  end
@@ -226,15 +289,31 @@ class String
226
289
  end
227
290
 
228
291
  def parameterize(separator: '-')
229
- underscore.gsub(/\s+/, separator).downcase
292
+ dup.parameterize!(separator: separator)
230
293
  end
231
294
 
232
295
  def parameterize!(separator: '-')
233
- replace(parameterize(separator: separator))
296
+ underscore!
297
+ gsub!(/\s+/, separator)
298
+ downcase! || self
299
+ end
300
+
301
+ def pathize
302
+ dup.pathize!
303
+ end
304
+
305
+ def pathize!
306
+ gsub!(/([A-Z]+)([A-Z])/, '\1_\2')
307
+ gsub!(/([a-z])([A-Z])/, '\1_\2')
308
+ gsub!('__', '/')
309
+ gsub!('::', '/')
310
+ gsub!(/\s+/, '')
311
+ gsub!(/[?%*:|"<>.]+/, '')
312
+ downcase! || self
234
313
  end
235
314
 
236
315
  def pollute(delimiter = '^--^--^')
237
- split('').map { |chr| "#{chr}#{delimiter}" }.join
316
+ chars.map { |chr| "#{chr}#{delimiter}" }.join
238
317
  end
239
318
 
240
319
  def pollute!(delimiter = '^--^--^')
@@ -249,22 +328,75 @@ class String
249
328
  replace(concat(string))
250
329
  end
251
330
 
252
- def remove(*patterns)
253
- patterns.each_with_object(dup) do |pat, str|
254
- pat.is_a?(Range) ? str.slice!(pat) : str.gsub!(pat, '')
331
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
332
+ def quote(type = :double, amount = nil)
333
+ if type.is_a?(Integer)
334
+ tmp = amount
335
+ amount = type
336
+ type = tmp || :mixed
337
+ else
338
+ amount ||= 1
339
+ end
340
+
341
+ case type.to_s
342
+ when "'", 'single', 's', '1'
343
+ f = "'" * amount
344
+ b = f
345
+ when '"', 'double', 'd', '2'
346
+ f = '"' * amount
347
+ b = f
348
+ when '`', 'back', 'backtick', 'b', '-1'
349
+ f = '`' * amount
350
+ b = f
351
+ when "`'", 'bracket', 'sb'
352
+ f = '`' * amount
353
+ b = "'" * amount
354
+ when "'\"", 'mixed', 'm', 'Integer'
355
+ c = (amount.to_f / 2).to_i
356
+ f = '"' * c
357
+ b = f
358
+
359
+ if amount.odd?
360
+ f = "'" + f
361
+ b += "'"
362
+ end
363
+ else
364
+ raise ArgumentError, "Invalid quote type: #{type.inspect}"
255
365
  end
366
+
367
+ "#{f}#{self}#{b}"
368
+ end
369
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
370
+
371
+ def quote!(type = :double, amount = nil)
372
+ replace(quote(type, amount))
373
+ end
374
+
375
+ def remove(*patterns)
376
+ dup.remove!(*patterns)
256
377
  end
257
378
 
258
379
  def remove!(*patterns)
259
- replace(remove(*patterns))
380
+ patterns.each_with_object(self) do |pat, str|
381
+ pat.is_a?(Range) ? str.slice!(pat) : str.gsub!(pat, '')
382
+ end
260
383
  end
261
384
 
262
385
  def remove_tags
263
- gsub(%r{<\/?[^>]*>}, '')
386
+ dup.remove_tags!
264
387
  end
265
388
 
266
389
  def remove_tags!
267
- replace(remove_tags)
390
+ gsub!(%r{<\/?[^>]*>}, '') || self
391
+ end
392
+
393
+ def rotate(amount = 1)
394
+ dup.rotate!(amount)
395
+ end
396
+
397
+ def rotate!(amount = 1)
398
+ amount += size if amount.negative?
399
+ slice!(amount, size - amount) + slice!(0, amount)
268
400
  end
269
401
 
270
402
  def sample(separator = ' ')
@@ -276,13 +408,13 @@ class String
276
408
  end
277
409
 
278
410
  def shift(*patterns)
279
- return self[0] if patterns.empty?
280
-
281
- patterns.each_with_object(dup) { |pat, str| str.sub!(pat, '') }
411
+ dup.shift!(*patterns)
282
412
  end
283
413
 
284
414
  def shift!(*patterns)
285
- replace(shift(*patterns))
415
+ return self[0] if patterns.empty?
416
+
417
+ patterns.each_with_object(self) { |pat, str| str.sub!(pat, '') }
286
418
  end
287
419
 
288
420
  def shuffle(separator = '')
@@ -298,7 +430,7 @@ class String
298
430
  when String then keep.chars
299
431
  when Array then keep.map(&:to_s)
300
432
  when Range then keep.to_a.map(&:to_s)
301
- else raise TypeError, "Invalid parameter #{keep.inspect}"
433
+ else raise TypeError, "Invalid parameter: #{keep.inspect}"
302
434
  end
303
435
 
304
436
  chars.keep_if { |chr| keep.include?(chr) }.join
@@ -309,23 +441,24 @@ class String
309
441
  end
310
442
 
311
443
  def slugify
312
- to_s.gsub(/[^\x00-\x7F]+/, '')
313
- .gsub(/[^\w_ \-]+/i, '')
314
- .gsub(/[ \-]+/i, '-')
315
- .gsub(/^\-|\-$/i, '')
316
- .downcase
444
+ dup.slugify!
317
445
  end
318
446
 
319
447
  def slugify!
320
- replace(slugify)
448
+ gsub!(/[^\x00-\x7F]+/, '')
449
+ gsub!(/[^\w_ \-]+/i, '')
450
+ gsub!(/[ \-]+/i, '-')
451
+ gsub!(/^\-|\-$/i, '')
452
+ downcase! || self
321
453
  end
322
454
 
323
455
  def squish
324
- strip.gsub(/\s+/, ' ')
456
+ dup.squish!
325
457
  end
326
458
 
327
459
  def squish!
328
- replace(squish)
460
+ strip!
461
+ gsub!(/\s+/, ' ') || self
329
462
  end
330
463
 
331
464
  def sort
@@ -337,13 +470,15 @@ class String
337
470
  end
338
471
 
339
472
  def titleize
340
- underscore.humanize.gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
473
+ dup.titleize!
341
474
  end
342
475
 
343
476
  alias titlecase titleize
344
477
 
345
478
  def titleize!
346
- replace(titleize)
479
+ underscore!
480
+ humanize!
481
+ gsub!(/\b(?<!['’`])[a-z]/) { $&.capitalize } || self
347
482
  end
348
483
 
349
484
  alias titlecase! titleize!
@@ -385,23 +520,27 @@ class String
385
520
  end
386
521
 
387
522
  def underscore
388
- to_s.gsub(/::/, '/')
389
- .gsub(/([A-Z\d]+)([A-Z][a-z])/, "\1_\2")
390
- .gsub(/([a-z\d])([A-Z])/, "\1_\2")
391
- .tr('-', '_')
392
- .downcase
523
+ dup.underscore!
393
524
  end
394
525
 
526
+ alias snakecase underscore
527
+
395
528
  def underscore!
396
- replace(underscore)
529
+ gsub!(/::/, '/')
530
+ gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
531
+ gsub!(/([a-z\d])([A-Z])/, '\1_\2')
532
+ tr!('-', '_')
533
+ downcase! || self
397
534
  end
398
535
 
536
+ alias snakecase! underscore!
537
+
399
538
  def unpollute(delimiter = '^--^--^')
400
- gsub(delimiter, '')
539
+ dup.unpollute!(delimiter)
401
540
  end
402
541
 
403
542
  def unpollute!(delimiter = '^--^--^')
404
- replace(unpollute(delimiter))
543
+ gsub!(delimiter, '') || self
405
544
  end
406
545
 
407
546
  def upcase?
@@ -417,4 +556,42 @@ class String
417
556
  replace(unshift(*patterns))
418
557
  end
419
558
 
559
+ def unquote
560
+ dup.unquote!
561
+ end
562
+
563
+ def unquote!
564
+ [0, -1].each do |i|
565
+ case self[i, 1]
566
+ when "'", '"', '`' then self[i] = ''
567
+ end
568
+ end
569
+
570
+ self
571
+ end
572
+
573
+ def words
574
+ split(/\s+/)
575
+ end
576
+
577
+ def words_without_punctuation
578
+ str = dup
579
+ str.gsub!(%r{[.?¿¡…!,::;—"。?!、‘“”„«»〈〉《》,/\[\]]}, ' ')
580
+ str.gsub!('- ', ' ')
581
+ str.squeeze!(' ')
582
+ str.strip!
583
+ str.words
584
+ end
585
+
586
+ def variablize
587
+ "@#{gsub(/\W/, '_')}"
588
+ end
589
+
590
+ def variablize!
591
+ replace(variablize)
592
+ end
593
+
594
+ alias ends_with? end_with?
595
+ alias starts_with? start_with?
596
+
420
597
  end