casual_support 2.0.0 → 3.0.0
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 +52 -36
- data/benchmarks/struct/assign_attributes.rb +69 -0
- data/lib/casual_support.rb +3 -0
- data/lib/casual_support/boolean.rb +1 -0
- data/lib/casual_support/boolean/to_z.rb +22 -0
- data/lib/casual_support/comparable.rb +0 -1
- data/lib/casual_support/comparable/at_least.rb +6 -2
- data/lib/casual_support/comparable/at_most.rb +6 -2
- data/lib/casual_support/date/to_ymd.rb +8 -5
- data/lib/casual_support/enumerable/duplicates.rb +5 -14
- data/lib/casual_support/enumerable/index_to.rb +8 -6
- data/lib/casual_support/hash.rb +1 -0
- data/lib/casual_support/hash/displace.rb +26 -0
- data/lib/casual_support/hash/putbang.rb +9 -4
- data/lib/casual_support/integer/to_hex.rb +9 -4
- data/lib/casual_support/numeric.rb +1 -0
- data/lib/casual_support/numeric/sign.rb +15 -0
- data/lib/casual_support/string.rb +0 -1
- data/lib/casual_support/string/after.rb +13 -8
- data/lib/casual_support/string/after_last.rb +13 -7
- data/lib/casual_support/string/before.rb +12 -7
- data/lib/casual_support/string/before_last.rb +12 -7
- data/lib/casual_support/string/between.rb +14 -7
- data/lib/casual_support/string/drop.rb +10 -4
- data/lib/casual_support/string/first.rb +25 -9
- data/lib/casual_support/string/from.rb +18 -2
- data/lib/casual_support/string/last.rb +24 -10
- data/lib/casual_support/string/prefix.rb +11 -7
- data/lib/casual_support/string/suffix.rb +11 -7
- data/lib/casual_support/string/to.rb +17 -1
- data/lib/casual_support/struct.rb +2 -0
- data/lib/casual_support/struct/assign_attributes.rb +26 -0
- data/lib/casual_support/struct/from_h.rb +22 -0
- data/lib/casual_support/time/to_hms.rb +6 -3
- data/lib/casual_support/time/to_ymd.rb +6 -3
- data/lib/casual_support/version.rb +1 -1
- metadata +12 -5
- data/lib/casual_support/comparable/clamp.rb +0 -16
- data/lib/casual_support/string/lchomp.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 312ae68b392788d7e86e0ca1c60cac0342f51b29
|
4
|
+
data.tar.gz: 0b734117b2ff24d89fd7a26d53256798ab946dba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e993b4d89958ae079329c3311ce4505e04ee58ef3986eb44c6f347193903efd9f04e060116c161da29871625d0b131c329dcd6c0bc0b6388728d0ff0f1709ba
|
7
|
+
data.tar.gz: e175c89da241db40ef6b88a376b23dcd2ceeb5a6847f40dc88a1de70b5abe39808a02155fb9b1266101e8c6815f9c7b1a228d5d268e277ab06beac3a7d55bb60
|
data/README.md
CHANGED
@@ -3,12 +3,12 @@
|
|
3
3
|
Utility methods as extensions to Ruby core objects, a la Active Support.
|
4
4
|
|
5
5
|
Also a nod to the epic [Facets] library, which has a vast quantity and
|
6
|
-
wide variety of utility functions. casual_support distinguishes
|
7
|
-
in a few ways:
|
6
|
+
wide variety of utility functions. *casual_support* distinguishes
|
7
|
+
itself in a few ways:
|
8
8
|
|
9
9
|
- Focus on fast implementations. See the benchmarks directory for
|
10
10
|
performance comparisons with typical and alternative implementations.
|
11
|
-
- Low memory footprint without
|
11
|
+
- Low memory footprint without required cherry-picking. Cherry-picking
|
12
12
|
is still possible though!
|
13
13
|
- Intended to be used alongside Active Support. Does not clash with
|
14
14
|
Active Support methods, and overrides only a few to provide more
|
@@ -19,44 +19,60 @@ in a few ways:
|
|
19
19
|
|
20
20
|
## Utility Methods
|
21
21
|
|
22
|
-
-
|
23
|
-
- [
|
24
|
-
|
25
|
-
- [
|
26
|
-
-
|
27
|
-
|
28
|
-
-
|
29
|
-
|
30
|
-
- [
|
31
|
-
-
|
32
|
-
|
33
|
-
-
|
34
|
-
- [
|
35
|
-
-
|
36
|
-
- [
|
37
|
-
|
38
|
-
- [
|
39
|
-
|
40
|
-
- [
|
41
|
-
- [
|
42
|
-
- [
|
43
|
-
- [
|
44
|
-
- [
|
45
|
-
- [
|
46
|
-
- [
|
47
|
-
- [
|
48
|
-
- [
|
49
|
-
-
|
50
|
-
- [
|
51
|
-
- [
|
22
|
+
- Boolean
|
23
|
+
- [#to_z](http://www.rubydoc.info/gems/casual_support/TrueClass:to_z)
|
24
|
+
- [Comparable](http://www.rubydoc.info/gems/casual_support/Comparable)
|
25
|
+
- [#at_least](http://www.rubydoc.info/gems/casual_support/Comparable:at_least)
|
26
|
+
- [#at_most](http://www.rubydoc.info/gems/casual_support/Comparable:at_most)
|
27
|
+
- [Date](http://www.rubydoc.info/gems/casual_support/Date)
|
28
|
+
- [#to_ymd](http://www.rubydoc.info/gems/casual_support/Date:to_ymd)
|
29
|
+
- [Enumerable](http://www.rubydoc.info/gems/casual_support/Enumerable)
|
30
|
+
- [#duplicates](http://www.rubydoc.info/gems/casual_support/Enumerable:duplicates)
|
31
|
+
- [#index_to](http://www.rubydoc.info/gems/casual_support/Enumerable:index_to)
|
32
|
+
- [Hash](http://www.rubydoc.info/gems/casual_support/Hash)
|
33
|
+
- [#displace](http://www.rubydoc.info/gems/casual_support/Hash:displace)
|
34
|
+
- [#put!](http://www.rubydoc.info/gems/casual_support/Hash:put%21)
|
35
|
+
- [Integer](http://www.rubydoc.info/gems/casual_support/Integer)
|
36
|
+
- [#to_hex](http://www.rubydoc.info/gems/casual_support/Integer:to_hex)
|
37
|
+
- [Numeric](http://www.rubydoc.info/gems/casual_support/Numeric)
|
38
|
+
- [#sign](http://www.rubydoc.info/gems/casual_support/Numeric:sign)
|
39
|
+
- [String](http://www.rubydoc.info/gems/casual_support/String)
|
40
|
+
- [#after](http://www.rubydoc.info/gems/casual_support/String:after)
|
41
|
+
- [#after_last](http://www.rubydoc.info/gems/casual_support/String:after_last)
|
42
|
+
- [#before](http://www.rubydoc.info/gems/casual_support/String:before)
|
43
|
+
- [#before_last](http://www.rubydoc.info/gems/casual_support/String:before_last)
|
44
|
+
- [#between](http://www.rubydoc.info/gems/casual_support/String:between)
|
45
|
+
- [#drop](http://www.rubydoc.info/gems/casual_support/String:drop)
|
46
|
+
- [#first](http://www.rubydoc.info/gems/casual_support/String:first)
|
47
|
+
- [#from](http://www.rubydoc.info/gems/casual_support/String:from)
|
48
|
+
- [#last](http://www.rubydoc.info/gems/casual_support/String:last)
|
49
|
+
- [#prefix](http://www.rubydoc.info/gems/casual_support/String:prefix)
|
50
|
+
- [#suffix](http://www.rubydoc.info/gems/casual_support/String:suffix)
|
51
|
+
- [#to](http://www.rubydoc.info/gems/casual_support/String:to)
|
52
|
+
- [Struct](http://www.rubydoc.info/gems/casual_support/Struct)
|
53
|
+
- [#assign_attributes](http://www.rubydoc.info/gems/casual_support/Struct:assign_attributes)
|
54
|
+
- [.from_h](http://www.rubydoc.info/gems/casual_support/Struct.from_h)
|
55
|
+
- [Time](http://www.rubydoc.info/gems/casual_support/Time)
|
56
|
+
- [#to_hms](http://www.rubydoc.info/gems/casual_support/Time:to_hms)
|
57
|
+
- [#to_ymd](http://www.rubydoc.info/gems/casual_support/Time:to_ymd)
|
52
58
|
|
53
59
|
|
54
60
|
## Installation
|
55
61
|
|
56
|
-
|
62
|
+
Install from [Ruby Gems](https://rubygems.org/gems/casual_support):
|
57
63
|
|
64
|
+
```bash
|
65
|
+
$ gem install casual_support
|
66
|
+
```
|
58
67
|
|
59
|
-
|
68
|
+
Then require in your Ruby script:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
require "casual_support"
|
72
|
+
```
|
73
|
+
|
74
|
+
|
75
|
+
## Contributing
|
60
76
|
|
61
77
|
Run `rake test` to run the tests. You can also run `rake irb` for an
|
62
78
|
interactive prompt that pre-loads the project code.
|
@@ -64,4 +80,4 @@ interactive prompt that pre-loads the project code.
|
|
64
80
|
|
65
81
|
## License
|
66
82
|
|
67
|
-
[MIT License](
|
83
|
+
[MIT License](https://opensource.org/licenses/MIT)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'benchmark/inputs'
|
2
|
+
require_relative '../../lib/casual_support'
|
3
|
+
|
4
|
+
|
5
|
+
class Struct
|
6
|
+
|
7
|
+
def assign_attributes_alt1(new_attributes)
|
8
|
+
self.members.each do |m|
|
9
|
+
if new_attributes.key?(m)
|
10
|
+
self[m] = new_attributes[m]
|
11
|
+
elsif new_attributes.key?(m.to_s)
|
12
|
+
self[m] = new_attributes[m.to_s]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def assign_attributes_alt2(new_attributes)
|
19
|
+
self.members.each do |m|
|
20
|
+
if new_attributes.key?(m)
|
21
|
+
self[m] = new_attributes[m]
|
22
|
+
elsif new_attributes.key?(m_s = m.to_s)
|
23
|
+
self[m] = new_attributes[m_s]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def assign_attributes_alt3(new_attributes)
|
30
|
+
new_attributes.each do |k, v|
|
31
|
+
self[k.to_sym] = v if self.members.include?(k.to_sym)
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def assign_attributes_alt4(new_attributes)
|
37
|
+
new_attributes.each do |k, v|
|
38
|
+
m = k.to_sym
|
39
|
+
self[m] = v if self.members.include?(m)
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
Subject = Struct.new(:attr1, :attr2, :attr3)
|
48
|
+
subject = Subject.new(1, 2, 3)
|
49
|
+
|
50
|
+
input_attributes = [
|
51
|
+
{ attr1: 11 },
|
52
|
+
{ attr1: 111, attr2: 222 },
|
53
|
+
{ attr1: 1111, attr2: 2222, attr3: 3333 },
|
54
|
+
{ attr1: 11111, attr2: 22222, attr3: 33333, attr9: 99999 },
|
55
|
+
{ "attr1" => 11 },
|
56
|
+
{ "attr1" => 111, "attr2" => 222 },
|
57
|
+
{ "attr1" => 1111, "attr2" => 2222, "attr3" => 3333 },
|
58
|
+
{ "attr1" => 11111, "attr2" => 22222, "attr3" => 33333, "attr9" => 99999 },
|
59
|
+
]
|
60
|
+
|
61
|
+
|
62
|
+
Benchmark.inputs(input_attributes) do |job|
|
63
|
+
job.report('Struct#assign_attributes'){|attrs| subject.assign_attributes(attrs) }
|
64
|
+
job.report('Struct#assign_attributes_alt1'){|attrs| subject.assign_attributes_alt1(attrs) }
|
65
|
+
job.report('Struct#assign_attributes_alt2'){|attrs| subject.assign_attributes_alt2(attrs) }
|
66
|
+
job.report('Struct#assign_attributes_alt3'){|attrs| subject.assign_attributes_alt3(attrs) }
|
67
|
+
job.report('Struct#assign_attributes_alt4'){|attrs| subject.assign_attributes_alt4(attrs) }
|
68
|
+
job.compare!
|
69
|
+
end
|
data/lib/casual_support.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require_relative 'casual_support/version'
|
2
|
+
require_relative 'casual_support/boolean'
|
2
3
|
require_relative 'casual_support/comparable'
|
3
4
|
require_relative 'casual_support/date'
|
4
5
|
require_relative 'casual_support/enumerable'
|
5
6
|
require_relative 'casual_support/hash'
|
6
7
|
require_relative 'casual_support/integer'
|
8
|
+
require_relative 'casual_support/numeric'
|
7
9
|
require_relative 'casual_support/string'
|
10
|
+
require_relative 'casual_support/struct'
|
8
11
|
require_relative 'casual_support/time'
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'boolean/to_z'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class FalseClass
|
2
|
+
|
3
|
+
# Returns 0 if +false+, 1 if +true+.
|
4
|
+
#
|
5
|
+
# @return [Integer]
|
6
|
+
def to_z
|
7
|
+
0
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
class TrueClass
|
14
|
+
|
15
|
+
# Returns 0 if +false+, 1 if +true+.
|
16
|
+
#
|
17
|
+
# @return [Integer]
|
18
|
+
def to_z
|
19
|
+
1
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -2,8 +2,12 @@ module Comparable
|
|
2
2
|
|
3
3
|
# Enforces a lower bound for the value.
|
4
4
|
#
|
5
|
-
# @
|
6
|
-
#
|
5
|
+
# @example
|
6
|
+
# -10.at_least(0) # == 0
|
7
|
+
# 99.at_least(0) # == 99
|
8
|
+
#
|
9
|
+
# @param limit [Comparable]
|
10
|
+
# @return [Comparable]
|
7
11
|
def at_least(limit)
|
8
12
|
self < limit ? limit : self
|
9
13
|
end
|
@@ -2,8 +2,12 @@ module Comparable
|
|
2
2
|
|
3
3
|
# Enforces an upper bound for the value.
|
4
4
|
#
|
5
|
-
# @
|
6
|
-
#
|
5
|
+
# @example
|
6
|
+
# 120.at_most(100) # == 100
|
7
|
+
# 90.at_most(100) # == 90
|
8
|
+
#
|
9
|
+
# @param limit [Comparable]
|
10
|
+
# @return [Comparable]
|
7
11
|
def at_most(limit)
|
8
12
|
self > limit ? limit : self
|
9
13
|
end
|
@@ -2,14 +2,17 @@ require 'date'
|
|
2
2
|
|
3
3
|
class Date
|
4
4
|
|
5
|
-
# Formats
|
6
|
-
#
|
5
|
+
# Formats the Date as "YYYY-MM-DD". Equivalent to
|
6
|
+
# +strftime("%Y-%m-%d")+.
|
7
7
|
#
|
8
|
-
# @
|
8
|
+
# @example
|
9
|
+
# Date.new(1999, 12, 31).to_ymd # == "1999-12-31"
|
10
|
+
#
|
11
|
+
# @return [String]
|
9
12
|
def to_ymd
|
10
13
|
# Date#strftime appears to be **much** faster than Time#strftime
|
11
|
-
# (nearly 3x faster!). Thus it used here, unlike Time#to_ymd
|
12
|
-
# uses sprintf.
|
14
|
+
# (nearly 3x faster!). Thus it is used here, unlike Time#to_ymd
|
15
|
+
# which uses sprintf.
|
13
16
|
self.strftime('%Y-%m-%d'.freeze)
|
14
17
|
end
|
15
18
|
|
@@ -1,24 +1,15 @@
|
|
1
1
|
module Enumerable
|
2
2
|
|
3
3
|
# Returns the first duplicate of each element, preserving order of
|
4
|
-
# appearance.
|
5
|
-
# value of the block for element comparison.
|
4
|
+
# appearance.
|
6
5
|
#
|
7
|
-
#
|
8
|
-
# %w[a a b c
|
9
|
-
# %w[A a B b].duplicates(&:upcase) == %w[a b]
|
6
|
+
# @example
|
7
|
+
# %w[a a b c c b a d].duplicates # == ["a", "c", "b"]
|
10
8
|
#
|
11
|
-
# @
|
12
|
-
# @yieldparam elem element from the +Enumerable+
|
13
|
-
# @yieldreturn value used for duplicate comparison
|
14
|
-
# @return [Enumerable] duplicate elements of the +Enumerable+
|
9
|
+
# @return [Enumerable]
|
15
10
|
def duplicates
|
16
11
|
seen = Hash.new(0)
|
17
|
-
|
18
|
-
self.select{|elem| (seen[yield(elem)] += 1) == 2 }
|
19
|
-
else
|
20
|
-
self.select{|elem| (seen[elem] += 1) == 2 }
|
21
|
-
end
|
12
|
+
self.select{|element| (seen[element] += 1) == 2 }
|
22
13
|
end
|
23
14
|
|
24
15
|
end
|
@@ -2,13 +2,15 @@ require_relative '../hash/putbang'
|
|
2
2
|
|
3
3
|
module Enumerable
|
4
4
|
|
5
|
-
# Converts
|
6
|
-
#
|
5
|
+
# Converts the Enumerable into a Hash, using its elements as keys and
|
6
|
+
# using the given block to compute an associated value for each key.
|
7
7
|
#
|
8
|
-
# @
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# @
|
8
|
+
# @example
|
9
|
+
# cache = id_list.index_to{|id| find_by_id(id) }
|
10
|
+
#
|
11
|
+
# @yieldparam element ['E] element from the Enumerable
|
12
|
+
# @yieldreturn ['V] value to associate with the +element+ key
|
13
|
+
# @return [Hash{'E => 'V}]
|
12
14
|
def index_to()
|
13
15
|
self.reduce({}){|h, k| h.put!(k, (yield k)) }
|
14
16
|
end
|
data/lib/casual_support/hash.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# Associates a key with a value, and returns the key's previously
|
4
|
+
# associated value. If the key had no previously associated value,
|
5
|
+
# returns +Hash#default+.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# visited = { "id1" => true, "id2" => false }
|
9
|
+
#
|
10
|
+
# visited.displace("id1", true) # == true
|
11
|
+
# visited # == { "id1" => true, "id2" => false }
|
12
|
+
# visited.displace("id2", true) # == false
|
13
|
+
# visited # == { "id1" => true, "id2" => true }
|
14
|
+
# visited.displace("id3", true) # == nil
|
15
|
+
# visited # == { "id1" => true, "id2" => true, "id3" => true }
|
16
|
+
#
|
17
|
+
# @param key
|
18
|
+
# @param value
|
19
|
+
# @return [Hash]
|
20
|
+
def displace(key, value)
|
21
|
+
old_value = self[key]
|
22
|
+
self[key] = value
|
23
|
+
old_value
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -1,12 +1,17 @@
|
|
1
1
|
class Hash
|
2
2
|
|
3
|
-
# Associates a key
|
4
|
-
# Hash instead of the value.
|
5
|
-
#
|
3
|
+
# Associates a key with a value. Similar to +Hash#[]=+, but returns
|
4
|
+
# the Hash instead of the value. Faster than +Hash#merge!+ for
|
5
|
+
# singular values in a loop.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# cache = id_list.reduce({}) do |hash, id|
|
9
|
+
# hash.put!(id, find_by_id(id))
|
10
|
+
# end
|
6
11
|
#
|
7
12
|
# @param key
|
8
13
|
# @param value
|
9
|
-
# @return [Hash]
|
14
|
+
# @return [Hash]
|
10
15
|
def put!(key, value)
|
11
16
|
self[key] = value
|
12
17
|
self
|
@@ -1,12 +1,17 @@
|
|
1
1
|
class Integer
|
2
2
|
|
3
|
-
#
|
3
|
+
# Formats the Integer as a zero-padded lower-case hexadecimal string.
|
4
4
|
# If the length of the raw hexadecimal string exceeds the desired
|
5
|
-
# width, the string will be returned
|
5
|
+
# width, the string will be returned as-is (without padding or
|
6
6
|
# truncation).
|
7
7
|
#
|
8
|
-
# @
|
9
|
-
#
|
8
|
+
# @example
|
9
|
+
# 250.to_hex # == "fa"
|
10
|
+
# 250.to_hex(4) # == "00fa"
|
11
|
+
# 250.to_hex(1) # == "fa"
|
12
|
+
#
|
13
|
+
# @param width [Integer]
|
14
|
+
# @return [String]
|
10
15
|
def to_hex(width = 0)
|
11
16
|
width > 1 ? self.to_s(16).rjust(width, '0'.freeze) : self.to_s(16)
|
12
17
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'numeric/sign'
|
@@ -7,7 +7,6 @@ require_relative 'string/drop'
|
|
7
7
|
require_relative 'string/first'
|
8
8
|
require_relative 'string/from'
|
9
9
|
require_relative 'string/last'
|
10
|
-
require_relative 'string/lchomp'
|
11
10
|
require_relative 'string/prefix'
|
12
11
|
require_relative 'string/suffix'
|
13
12
|
require_relative 'string/to'
|
@@ -1,15 +1,20 @@
|
|
1
1
|
class String
|
2
2
|
|
3
3
|
# Searches for the first occurrence of a delimiter, and returns the
|
4
|
-
# portion of the
|
5
|
-
# returns nil. Equivalent to
|
6
|
-
#
|
4
|
+
# portion of the String after that. If the delimiter is not found,
|
5
|
+
# returns nil. Equivalent to +split(delimiter, 2)[1]+ for non-empty
|
6
|
+
# delimiters.
|
7
7
|
#
|
8
|
-
# @
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
# @example
|
9
|
+
# "http://www.example.com".after("://") # == "www.example.com"
|
10
|
+
# "http://www.example.com".after("?") # == nil
|
11
|
+
# "http://www.example.com".after("") # == "http://www.example.com"
|
12
|
+
#
|
13
|
+
# @param delimiter [String]
|
14
|
+
# @return [String, nil]
|
15
|
+
def after(delimiter)
|
16
|
+
i = self.index(delimiter)
|
17
|
+
i && self[i + delimiter.length, self.length]
|
13
18
|
end
|
14
19
|
|
15
20
|
end
|
@@ -1,14 +1,20 @@
|
|
1
1
|
class String
|
2
2
|
|
3
3
|
# Searches for the last occurrence of a delimiter, and returns the
|
4
|
-
# portion of the
|
5
|
-
# returns nil. Equivalent to
|
4
|
+
# portion of the String after that. If the delimiter is not found,
|
5
|
+
# returns nil. Equivalent to +split(delimiter, -1).drop(1)[-1]+ for
|
6
|
+
# non-empty delimiters.
|
6
7
|
#
|
7
|
-
# @
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# @example
|
9
|
+
# "/path/to/file".after_last("/") # == "file"
|
10
|
+
# "/path/to/file".after_last(".") # == nil
|
11
|
+
# "/path/to/file".after_last("") # == ""
|
12
|
+
#
|
13
|
+
# @param delimiter [String]
|
14
|
+
# @return [String, nil]
|
15
|
+
def after_last(delimiter)
|
16
|
+
i = self.rindex(delimiter)
|
17
|
+
i && self[i + delimiter.length, self.length]
|
12
18
|
end
|
13
19
|
|
14
20
|
end
|
@@ -1,14 +1,19 @@
|
|
1
1
|
class String
|
2
2
|
|
3
3
|
# Searches for the first occurrence of a delimiter, and returns the
|
4
|
-
# portion of the
|
5
|
-
# returns a copy of the original
|
6
|
-
#
|
4
|
+
# portion of the String before that. If the delimiter is not found,
|
5
|
+
# returns a copy of the original String. Equivalent to
|
6
|
+
# +split(delimiter, 2)[0]+ for non-empty delimiters.
|
7
7
|
#
|
8
|
-
# @
|
9
|
-
#
|
10
|
-
|
11
|
-
|
8
|
+
# @example
|
9
|
+
# "http://www.example.com".before("://") # == "http"
|
10
|
+
# "http://www.example.com".before("?") # == "http://www.example.com"
|
11
|
+
# "http://www.example.com".before("") # == ""
|
12
|
+
#
|
13
|
+
# @param delimiter [String]
|
14
|
+
# @return [String]
|
15
|
+
def before(delimiter)
|
16
|
+
self[0, self.index(delimiter) || self.length]
|
12
17
|
end
|
13
18
|
|
14
19
|
end
|
@@ -1,15 +1,20 @@
|
|
1
1
|
class String
|
2
2
|
|
3
3
|
# Searches for the last occurrence of a delimiter, and returns the
|
4
|
-
# portion of the
|
5
|
-
# returns a copy of the original
|
6
|
-
#
|
4
|
+
# portion of the String before that. If the delimiter is not found,
|
5
|
+
# returns a copy of the original String. Equivalent to
|
6
|
+
# +split(delimiter, -1)[0..-2].join(delimiter)+ for existent
|
7
7
|
# delimiters.
|
8
8
|
#
|
9
|
-
# @
|
10
|
-
#
|
11
|
-
|
12
|
-
|
9
|
+
# @example
|
10
|
+
# "/path/to/file".before_last("/") # == "/path/to"
|
11
|
+
# "/path/to/file".before_last(".") # == "/path/to/file"
|
12
|
+
# "/path/to/file".before_last("") # == "/path/to/file"
|
13
|
+
#
|
14
|
+
# @param delimiter [String]
|
15
|
+
# @return [String]
|
16
|
+
def before_last(delimiter)
|
17
|
+
self[0, self.rindex(delimiter) || self.length]
|
13
18
|
end
|
14
19
|
|
15
20
|
end
|
@@ -1,16 +1,23 @@
|
|
1
1
|
class String
|
2
|
-
|
3
|
-
# Returns the portion of the
|
2
|
+
|
3
|
+
# Returns the portion of the String between the first occurrences of
|
4
4
|
# an opening and a closing delimiter. If either delimiter is not
|
5
5
|
# found, returns nil.
|
6
6
|
#
|
7
|
-
# @
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# @example
|
8
|
+
# "i <b><3</b> ruby".between("<b>", "</b>") # == "<3"
|
9
|
+
# "i <b><3<b> ruby".between("<b>", "</b>") # == nil
|
10
|
+
#
|
11
|
+
# @param open [String]
|
12
|
+
# @param close [String]
|
13
|
+
# @return [String, nil]
|
10
14
|
def between(open, close)
|
11
15
|
i = self.index(open)
|
12
|
-
|
13
|
-
|
16
|
+
if i
|
17
|
+
i += open.length
|
18
|
+
j = self.index(close, i)
|
19
|
+
self[i, j - i] if j
|
20
|
+
end
|
14
21
|
end
|
15
22
|
|
16
23
|
end
|
@@ -1,11 +1,17 @@
|
|
1
1
|
class String
|
2
2
|
|
3
|
-
# Drops characters from the beginning of
|
3
|
+
# Drops characters from the beginning of the String, and returns the
|
4
4
|
# remainder. If the number of characters to drop is greater than the
|
5
|
-
# length of the
|
5
|
+
# length of the String, an empty string is returned.
|
6
6
|
#
|
7
|
-
# @
|
8
|
-
#
|
7
|
+
# @example
|
8
|
+
# "abcdef".drop(0) # == "abcdef"
|
9
|
+
# "abcdef".drop(3) # == "def"
|
10
|
+
# "abcdef".drop(6) # == ""
|
11
|
+
# "abcdef".drop(7) # == ""
|
12
|
+
#
|
13
|
+
# @param n [Integer]
|
14
|
+
# @return [String]
|
9
15
|
def drop(n)
|
10
16
|
return self.dup if n <= 0
|
11
17
|
self[n, self.length] || ''
|
@@ -1,15 +1,31 @@
|
|
1
|
+
require 'active_support/core_ext/string/access'
|
2
|
+
|
1
3
|
class String
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
5
|
+
remove_method :first
|
6
|
+
|
7
|
+
# Returns the first +limit+ characters from the beginning of the
|
8
|
+
# String.
|
9
|
+
#
|
10
|
+
# This method replaces Active Support's +String#first+, except that it
|
11
|
+
# returns an empty string when given a negative +limit+ argument,
|
12
|
+
# whereas Active Support's +String#first+ removes +limit.abs+
|
13
|
+
# characters from the end of the String. Returning an empty string
|
14
|
+
# makes more sense if you interpret +first+ as "keep upto +limit+
|
15
|
+
# characters." (At most, a negative +limit+ should *keep* that many
|
16
|
+
# characters from the end of the String, rather than *remove* that
|
17
|
+
# many characters, but returning an empty string is a good
|
18
|
+
# conservative choice.) This method is also faster.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# "abcdef".first(0) # == ""
|
22
|
+
# "abcdef".first(3) # == "abc"
|
23
|
+
# "abcdef".first(6) # == "abcdef"
|
24
|
+
# "abcdef".first(7) # == "abcdef"
|
25
|
+
# "abcdef".first(-1) # == ""
|
11
26
|
#
|
12
|
-
#
|
27
|
+
# @param limit [Integer]
|
28
|
+
# @return [String]
|
13
29
|
def first(limit = 1)
|
14
30
|
self[0, limit] || ''
|
15
31
|
end
|
@@ -1,8 +1,24 @@
|
|
1
|
+
require 'active_support/core_ext/string/access'
|
2
|
+
|
1
3
|
class String
|
2
4
|
|
3
|
-
|
5
|
+
remove_method :from
|
6
|
+
|
7
|
+
# Returns the substring starting at a given position, spanning through
|
8
|
+
# the end of the String.
|
9
|
+
#
|
10
|
+
# This method replaces Active Support's +String#from+. It is faster.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# "abcdef".from(0) # == "abcdef"
|
14
|
+
# "abcdef".from(3) # == "def"
|
15
|
+
# "abcdef".from(6) # == ""
|
16
|
+
# "abcdef".from(7) # == ""
|
17
|
+
#
|
18
|
+
# @param position [Integer]
|
19
|
+
# @return [String]
|
4
20
|
def from(position)
|
5
|
-
self[position, length]
|
21
|
+
self[position, self.length]
|
6
22
|
end
|
7
23
|
|
8
24
|
end
|
@@ -1,16 +1,30 @@
|
|
1
|
+
require 'active_support/core_ext/string/access'
|
2
|
+
|
1
3
|
class String
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
5
|
+
remove_method :last
|
6
|
+
|
7
|
+
# Returns the last +limit+ characters from the end of the String.
|
8
|
+
#
|
9
|
+
# This method replaces Active Support's +String#last+, except that it
|
10
|
+
# returns an empty string when given a negative +limit+ argument,
|
11
|
+
# whereas Active Support's +String#last+ removes +limit.abs+
|
12
|
+
# characters from the beginning of the String. Returning an empty
|
13
|
+
# string makes more sense if you interpret +last+ as "keep upto
|
14
|
+
# +limit+ characters." (At most, a negative +limit+ should *keep*
|
15
|
+
# that many characters from the beginning of the String, rather than
|
16
|
+
# *remove* that many characters, but returning an empty string is a
|
17
|
+
# good conservative choice.) This method is also faster.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# "abcdef".last(0) # == ""
|
21
|
+
# "abcdef".last(3) # == "def"
|
22
|
+
# "abcdef".last(6) # == "abcdef"
|
23
|
+
# "abcdef".last(7) # == "abcdef"
|
24
|
+
# "abcdef".last(-1) # == ""
|
12
25
|
#
|
13
|
-
#
|
26
|
+
# @param limit [Integer]
|
27
|
+
# @return [String]
|
14
28
|
def last(limit = 1)
|
15
29
|
limit < 0 ? '' : (self[-limit, limit] || self.dup)
|
16
30
|
end
|
@@ -1,13 +1,17 @@
|
|
1
1
|
class String
|
2
2
|
|
3
|
-
# Prepends a prefix to the
|
4
|
-
# with that prefix. Otherwise returns a duplicate of the
|
5
|
-
# Equivalent to
|
3
|
+
# Prepends a prefix to the String only if the String does not already
|
4
|
+
# start with that prefix. Otherwise returns a duplicate of the
|
5
|
+
# String. Equivalent to +gsub(/^(?!prefix)/, "prefix")+.
|
6
6
|
#
|
7
|
-
# @
|
8
|
-
#
|
9
|
-
|
10
|
-
|
7
|
+
# @example
|
8
|
+
# "example.com".prefix("www.") # == "www.example.com"
|
9
|
+
# "www.example.com".prefix("www.") # == "www.example.com"
|
10
|
+
#
|
11
|
+
# @param affix [String]
|
12
|
+
# @return [String]
|
13
|
+
def prefix(affix)
|
14
|
+
self.start_with?(affix) ? self.dup : "#{affix}#{self}"
|
11
15
|
end
|
12
16
|
|
13
17
|
end
|
@@ -1,13 +1,17 @@
|
|
1
1
|
class String
|
2
2
|
|
3
|
-
# Appends a suffix to the
|
4
|
-
# with that suffix. Otherwise returns a duplicate of the
|
5
|
-
# Equivalent to
|
3
|
+
# Appends a suffix to the String only if the String does not already
|
4
|
+
# end with that suffix. Otherwise returns a duplicate of the String.
|
5
|
+
# Equivalent to +gsub(/(?<!affix)$/, "affix")+.
|
6
6
|
#
|
7
|
-
# @
|
8
|
-
#
|
9
|
-
|
10
|
-
|
7
|
+
# @example
|
8
|
+
# "example".suffix(".com") # == "example.com"
|
9
|
+
# "example.com".suffix(".com") # == "example.com"
|
10
|
+
#
|
11
|
+
# @param affix [String]
|
12
|
+
# @return [String]
|
13
|
+
def suffix(affix)
|
14
|
+
self.end_with?(affix) ? self.dup : "#{self}#{affix}"
|
11
15
|
end
|
12
16
|
|
13
17
|
end
|
@@ -1,6 +1,22 @@
|
|
1
|
+
require 'active_support/core_ext/string/access'
|
2
|
+
|
1
3
|
class String
|
2
4
|
|
3
|
-
|
5
|
+
remove_method :to
|
6
|
+
|
7
|
+
# Returns the substring from the start of the String, spanning through
|
8
|
+
# a given position.
|
9
|
+
#
|
10
|
+
# This method replaces Active Support's +String#to+. It is faster.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# "abcdef".to(0) # == "a"
|
14
|
+
# "abcdef".to(2) # == "abc"
|
15
|
+
# "abcdef".to(5) # == "abcdef"
|
16
|
+
# "abcdef".to(6) # == "abcdef"
|
17
|
+
#
|
18
|
+
# @param position [Integer]
|
19
|
+
# @return [String]
|
4
20
|
def to(position)
|
5
21
|
position += self.length if position < 0
|
6
22
|
self[0, position + 1] || ''
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Struct
|
2
|
+
|
3
|
+
# Assigns values in the given Hash to corresponding attributes of the
|
4
|
+
# Struct. Both Symbol keys and String keys are accepted. Keys which
|
5
|
+
# don't correspond to an attribute of the Struct are ignored. Mutates
|
6
|
+
# the Struct and returns it.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# Point = Struct.new(:x, :y, :z)
|
10
|
+
# point = Point.new(10, 20, 30)
|
11
|
+
#
|
12
|
+
# point.assign_attributes(y: 30, z: 50) # == point
|
13
|
+
# point.to_h # == { x: 10, y: 30, z: 50 }
|
14
|
+
#
|
15
|
+
# @param new_attributes [Hash<Symbol, Object>, Hash<String, Object>]
|
16
|
+
# @return [self]
|
17
|
+
def assign_attributes(new_attributes)
|
18
|
+
ms = self.members
|
19
|
+
new_attributes.each do |k, v|
|
20
|
+
m = k.to_sym
|
21
|
+
self[m] = v if ms.include?(m)
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'assign_attributes'
|
2
|
+
|
3
|
+
class Struct
|
4
|
+
|
5
|
+
# Constructs an instance of a subclass of Struct, and assigns the
|
6
|
+
# values of the given attribute Hash to the instance. This method is
|
7
|
+
# intended for use only with subclasses of Struct which do not alter
|
8
|
+
# the default signature of the +initialize+ method. See also
|
9
|
+
# {Struct#assign_attributes}.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# Point = Struct.new(:x, :y, :z)
|
13
|
+
# p = Point.from_h(x: 10, y: 20, z: 30) # == Point.new(10, 20, 30)
|
14
|
+
#
|
15
|
+
# @param attributes [Hash<Symbol, Object>, Hash<String, Object>]
|
16
|
+
# @return [self.new]
|
17
|
+
def self.from_h(attributes)
|
18
|
+
raise "Struct.from_h is for use only with subclasses of Struct" if self == Struct
|
19
|
+
self.new.assign_attributes(attributes)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
class Time
|
2
2
|
|
3
|
-
# Formats
|
4
|
-
#
|
3
|
+
# Formats the Time as "HH:MM:SS". Equivalent to
|
4
|
+
# +strftime("%H:%M:%S")+, but faster.
|
5
5
|
#
|
6
|
-
# @
|
6
|
+
# @example
|
7
|
+
# Time.new(1999, 12, 31, 23, 59, 59).to_hms # == "23:59:59"
|
8
|
+
#
|
9
|
+
# @return [String]
|
7
10
|
def to_hms
|
8
11
|
# Date#strftime appears to be **much** faster than Time#strftime
|
9
12
|
# (nearly 3x faster!). If Time#strftime becomes optimized to that
|
@@ -1,9 +1,12 @@
|
|
1
1
|
class Time
|
2
2
|
|
3
|
-
# Formats
|
4
|
-
#
|
3
|
+
# Formats the Time as "YYYY-MM-DD". Equivalent to
|
4
|
+
# +strftime("%Y-%m-%d")+, but faster.
|
5
5
|
#
|
6
|
-
# @
|
6
|
+
# @example
|
7
|
+
# Time.new(1999, 12, 31, 23, 59, 59).to_ymd # == "1999-12-31"
|
8
|
+
#
|
9
|
+
# @return [String]
|
7
10
|
def to_ymd
|
8
11
|
# Date#strftime appears to be **much** faster than Time#strftime
|
9
12
|
# (nearly 3x faster!). If Time#strftime becomes optimized to that
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: casual_support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Hefner
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -129,23 +129,28 @@ files:
|
|
129
129
|
- benchmarks/string/prefix.rb
|
130
130
|
- benchmarks/string/suffix.rb
|
131
131
|
- benchmarks/string/to.rb
|
132
|
+
- benchmarks/struct/assign_attributes.rb
|
132
133
|
- benchmarks/time/to_hms.rb
|
133
134
|
- benchmarks/time/to_ymd.rb
|
134
135
|
- casual_support.gemspec
|
135
136
|
- lib/casual_support.rb
|
137
|
+
- lib/casual_support/boolean.rb
|
138
|
+
- lib/casual_support/boolean/to_z.rb
|
136
139
|
- lib/casual_support/comparable.rb
|
137
140
|
- lib/casual_support/comparable/at_least.rb
|
138
141
|
- lib/casual_support/comparable/at_most.rb
|
139
|
-
- lib/casual_support/comparable/clamp.rb
|
140
142
|
- lib/casual_support/date.rb
|
141
143
|
- lib/casual_support/date/to_ymd.rb
|
142
144
|
- lib/casual_support/enumerable.rb
|
143
145
|
- lib/casual_support/enumerable/duplicates.rb
|
144
146
|
- lib/casual_support/enumerable/index_to.rb
|
145
147
|
- lib/casual_support/hash.rb
|
148
|
+
- lib/casual_support/hash/displace.rb
|
146
149
|
- lib/casual_support/hash/putbang.rb
|
147
150
|
- lib/casual_support/integer.rb
|
148
151
|
- lib/casual_support/integer/to_hex.rb
|
152
|
+
- lib/casual_support/numeric.rb
|
153
|
+
- lib/casual_support/numeric/sign.rb
|
149
154
|
- lib/casual_support/string.rb
|
150
155
|
- lib/casual_support/string/after.rb
|
151
156
|
- lib/casual_support/string/after_last.rb
|
@@ -156,10 +161,12 @@ files:
|
|
156
161
|
- lib/casual_support/string/first.rb
|
157
162
|
- lib/casual_support/string/from.rb
|
158
163
|
- lib/casual_support/string/last.rb
|
159
|
-
- lib/casual_support/string/lchomp.rb
|
160
164
|
- lib/casual_support/string/prefix.rb
|
161
165
|
- lib/casual_support/string/suffix.rb
|
162
166
|
- lib/casual_support/string/to.rb
|
167
|
+
- lib/casual_support/struct.rb
|
168
|
+
- lib/casual_support/struct/assign_attributes.rb
|
169
|
+
- lib/casual_support/struct/from_h.rb
|
163
170
|
- lib/casual_support/time.rb
|
164
171
|
- lib/casual_support/time/to_hms.rb
|
165
172
|
- lib/casual_support/time/to_ymd.rb
|
@@ -184,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
184
191
|
version: '0'
|
185
192
|
requirements: []
|
186
193
|
rubyforge_project:
|
187
|
-
rubygems_version: 2.
|
194
|
+
rubygems_version: 2.6.13
|
188
195
|
signing_key:
|
189
196
|
specification_version: 4
|
190
197
|
summary: Utility extensions to core objects, a la Active Support
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Comparable
|
2
|
-
|
3
|
-
# Clamps the value to a closed interval.
|
4
|
-
#
|
5
|
-
# Also see https://bugs.ruby-lang.org/issues/10594
|
6
|
-
#
|
7
|
-
# @param [Comparable] low lower bound
|
8
|
-
# @param [Comparable] high upper bound
|
9
|
-
# @return [Comparable] value constrained to the bounds
|
10
|
-
def clamp(low, high)
|
11
|
-
return low if self < low
|
12
|
-
return high if self > high
|
13
|
-
self
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
class String
|
2
|
-
|
3
|
-
# Removes the beginning of the string if that portion matches the
|
4
|
-
# argument. Otherwise returns a duplicate of the string. Like
|
5
|
-
# String#chomp, but operates on the left side (beginning) of the
|
6
|
-
# string.
|
7
|
-
#
|
8
|
-
# Also see https://bugs.ruby-lang.org/issues/10574
|
9
|
-
#
|
10
|
-
# @param [String] head substring to match and remove
|
11
|
-
# @return [String] chomped string
|
12
|
-
def lchomp(head)
|
13
|
-
self.start_with?(head) ? self[head.length, self.length] : self.dup
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|