lite-ruby 1.0.1 → 1.0.2

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.
@@ -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