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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +52 -36
  3. data/benchmarks/struct/assign_attributes.rb +69 -0
  4. data/lib/casual_support.rb +3 -0
  5. data/lib/casual_support/boolean.rb +1 -0
  6. data/lib/casual_support/boolean/to_z.rb +22 -0
  7. data/lib/casual_support/comparable.rb +0 -1
  8. data/lib/casual_support/comparable/at_least.rb +6 -2
  9. data/lib/casual_support/comparable/at_most.rb +6 -2
  10. data/lib/casual_support/date/to_ymd.rb +8 -5
  11. data/lib/casual_support/enumerable/duplicates.rb +5 -14
  12. data/lib/casual_support/enumerable/index_to.rb +8 -6
  13. data/lib/casual_support/hash.rb +1 -0
  14. data/lib/casual_support/hash/displace.rb +26 -0
  15. data/lib/casual_support/hash/putbang.rb +9 -4
  16. data/lib/casual_support/integer/to_hex.rb +9 -4
  17. data/lib/casual_support/numeric.rb +1 -0
  18. data/lib/casual_support/numeric/sign.rb +15 -0
  19. data/lib/casual_support/string.rb +0 -1
  20. data/lib/casual_support/string/after.rb +13 -8
  21. data/lib/casual_support/string/after_last.rb +13 -7
  22. data/lib/casual_support/string/before.rb +12 -7
  23. data/lib/casual_support/string/before_last.rb +12 -7
  24. data/lib/casual_support/string/between.rb +14 -7
  25. data/lib/casual_support/string/drop.rb +10 -4
  26. data/lib/casual_support/string/first.rb +25 -9
  27. data/lib/casual_support/string/from.rb +18 -2
  28. data/lib/casual_support/string/last.rb +24 -10
  29. data/lib/casual_support/string/prefix.rb +11 -7
  30. data/lib/casual_support/string/suffix.rb +11 -7
  31. data/lib/casual_support/string/to.rb +17 -1
  32. data/lib/casual_support/struct.rb +2 -0
  33. data/lib/casual_support/struct/assign_attributes.rb +26 -0
  34. data/lib/casual_support/struct/from_h.rb +22 -0
  35. data/lib/casual_support/time/to_hms.rb +6 -3
  36. data/lib/casual_support/time/to_ymd.rb +6 -3
  37. data/lib/casual_support/version.rb +1 -1
  38. metadata +12 -5
  39. data/lib/casual_support/comparable/clamp.rb +0 -16
  40. data/lib/casual_support/string/lchomp.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a1a0d4b85acc5ebea33e00189e06584cdba7c1d
4
- data.tar.gz: f7622e76c1255b6058499d9fa96c5cfa23ba101e
3
+ metadata.gz: 312ae68b392788d7e86e0ca1c60cac0342f51b29
4
+ data.tar.gz: 0b734117b2ff24d89fd7a26d53256798ab946dba
5
5
  SHA512:
6
- metadata.gz: 4938a0152fa5a60b49be69f3e76ac26a96e988478a789933de3d1ca999e05d38220d0b71f46e8f5909c230c264837b5053e84f405ad7a7bb3c8d11e13c7a87cd
7
- data.tar.gz: ef2a424c9e9ef9bfb2039d7c3e02f2c736fffc9dd5fa274d8c231d6173ee29765e351d5f3e7df36852c843855893fcf3e47ec3d56475f4d0698504744db00529
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 itself
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 requiring cherry-picking. Cherry-picking
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
- - Comparable
23
- - [at_least](http://www.rubydoc.info/gems/casual_support/Comparable%3Aat_least)
24
- - [at_most](http://www.rubydoc.info/gems/casual_support/Comparable%3Aat_most)
25
- - [clamp](http://www.rubydoc.info/gems/casual_support/Comparable%3Aclamp)
26
- - Date
27
- - [to_ymd](http://www.rubydoc.info/gems/casual_support/Date%3Ato_ymd)
28
- - Enumerable
29
- - [duplicates](http://www.rubydoc.info/gems/casual_support/Enumerable%3Aduplicates)
30
- - [index_to](http://www.rubydoc.info/gems/casual_support/Enumerable%3Aindex_to)
31
- - Hash
32
- - [put!](http://www.rubydoc.info/gems/casual_support/Hash%3Aput%21)
33
- - Integer
34
- - [to_hex](http://www.rubydoc.info/gems/casual_support/Integer%3Ato_hex)
35
- - String
36
- - [after](http://www.rubydoc.info/gems/casual_support/String%3Aafter)
37
- - [after_last](http://www.rubydoc.info/gems/casual_support/String%3Aafter_last)
38
- - [before](http://www.rubydoc.info/gems/casual_support/String%3Abefore)
39
- - [before_last](http://www.rubydoc.info/gems/casual_support/String%3Abefore_last)
40
- - [between](http://www.rubydoc.info/gems/casual_support/String%3Abetween)
41
- - [drop](http://www.rubydoc.info/gems/casual_support/String%3Adrop)
42
- - [first](http://www.rubydoc.info/gems/casual_support/String%3Afirst)
43
- - [from](http://www.rubydoc.info/gems/casual_support/String%3Afrom)
44
- - [last](http://www.rubydoc.info/gems/casual_support/String%3Alast)
45
- - [lchomp](http://www.rubydoc.info/gems/casual_support/String%3Alchomp)
46
- - [prefix](http://www.rubydoc.info/gems/casual_support/String%3Aprefix)
47
- - [suffix](http://www.rubydoc.info/gems/casual_support/String%3Asuffix)
48
- - [to](http://www.rubydoc.info/gems/casual_support/String%3Ato)
49
- - Time
50
- - [to_hms](http://www.rubydoc.info/gems/casual_support/Time%3Ato_hms)
51
- - [to_ymd](http://www.rubydoc.info/gems/casual_support/Time%3Ato_ymd)
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
- $ gem install casual_support
62
+ Install from [Ruby Gems](https://rubygems.org/gems/casual_support):
57
63
 
64
+ ```bash
65
+ $ gem install casual_support
66
+ ```
58
67
 
59
- ## Development
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](http://opensource.org/licenses/MIT)
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
@@ -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
@@ -1,3 +1,2 @@
1
1
  require_relative 'comparable/at_least'
2
2
  require_relative 'comparable/at_most'
3
- require_relative 'comparable/clamp'
@@ -2,8 +2,12 @@ module Comparable
2
2
 
3
3
  # Enforces a lower bound for the value.
4
4
  #
5
- # @param [Comparable] limit lower bound
6
- # @return [Comparable] value constrained by the lower bound
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
- # @param [Comparable] limit upper bound
6
- # @return [Comparable] value constrained by the upper bound
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 date as "YYYY-MM-DD" (e.g. "1999-12-31"). Shortcut for
6
- # <code>strftime("%Y-%m-%d")</code>.
5
+ # Formats the Date as "YYYY-MM-DD". Equivalent to
6
+ # +strftime("%Y-%m-%d")+.
7
7
  #
8
- # @return [String] the date formatted as "YYYY-MM-DD"
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 which
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. If a block is given, it will use the return
5
- # value of the block for element comparison.
4
+ # appearance.
6
5
  #
7
- # Examples:
8
- # %w[a a b c d c b a].duplicates == %w[a c b]
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
- # @yield [elem] computes an indentifying value
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
- if block_given?
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 an +Enumerable+ into a +Hash+ by using its elements as keys
6
- # and extracting values from each.
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
- # @yield [elem] extracts a value
9
- # @yieldparam elem element from the +Enumerable+
10
- # @yieldreturn value to associate with the +elem+ key
11
- # @return [Hash] hash with the +Enumerable+'s elements as keys
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
@@ -1 +1,2 @@
1
+ require_relative 'hash/displace'
1
2
  require_relative 'hash/putbang'
@@ -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-value a la +Hash#[]=+, but returns the modified
4
- # Hash instead of the value. Useful with +#reduce+, and faster than
5
- # +Hash#merge!+.
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] the modified 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
- # Converts integer to a zero-padded lower-case hexadecimal string.
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 unaltered (without padding or
5
+ # width, the string will be returned as-is (without padding or
6
6
  # truncation).
7
7
  #
8
- # @param [Integer] width desired width of the zero-padded string
9
- # @return [String] hexadecimal string
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'
@@ -0,0 +1,15 @@
1
+ class Numeric
2
+
3
+ # Returns the sign of the number expressed as +-1+, +0+, or +1+.
4
+ #
5
+ # @example
6
+ # -4.sign # == -1
7
+ # 0.sign # == 0
8
+ # 16.sign # == 1
9
+ #
10
+ # @return [Integer]
11
+ def sign
12
+ self <=> 0
13
+ end
14
+
15
+ end
@@ -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 string after that. If the delimiter is not found,
5
- # returns nil. Equivalent to <code>split(delim, 2)[1]</code> for
6
- # non-empty delimiters.
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
- # @param [String] delim delimiter to search for
9
- # @return [String] portion of the string after the first +delim+
10
- def after(delim)
11
- i = self.index(delim)
12
- i && self[i + delim.length, self.length]
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 string after that. If the delimiter is not found,
5
- # returns nil. Equivalent to <code>split(delim, -1).drop(1)[-1]</code>.
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
- # @param [String] delim delimiter to search for
8
- # @return [String] portion of the string after the last +delim+
9
- def after_last(delim)
10
- i = self.rindex(delim)
11
- i && self[i + delim.length, self.length]
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 string before that. If the delimiter is not found,
5
- # returns a copy of the original string. Equivalent to
6
- # <code>split(delim, 2)[0]</code> for non-empty delimiters.
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
- # @param [String] delim delimiter to search for
9
- # @return [String] portion of the string before the first +delim+
10
- def before(delim)
11
- self[0, self.index(delim) || self.length]
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 string before that. If the delimiter is not found,
5
- # returns a copy of the original string. Equivalent to
6
- # <code>split(delim, -1)[0..-2].join(delim)</code> for existent
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
- # @param [String] delim delimiter to search for
10
- # @return [String] portion of the string before the last +delim+
11
- def before_last(delim)
12
- self[0, self.rindex(delim) || self.length]
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 string between the first occurrences of
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
- # @param [String] open opening delimiter
8
- # @param [String] close closing delimiter
9
- # @return [String] portion of the string between the delimiters
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
- j = self.index(close, i + 1) if i
13
- j && self[i + 1, j - i - 1]
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 a string, and returns the
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 string, an empty string is returned.
5
+ # length of the String, an empty string is returned.
6
6
  #
7
- # @param [Integer] n number of characters to drop
8
- # @return [String] string of remaining characters
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
- # This replaces Active Support's +String#first+, but it returns an
4
- # empty string when given a negative +limit+ argument, whereas Active
5
- # Support's +String#first+ removes +limit.abs+ characters from the
6
- # end of the string. Returning an empty string makes more sense if
7
- # you interpret +first+ as "keep upto +limit+ characters." At most, a
8
- # negative +limit+ should *keep* that many characters from the end of
9
- # the string, rather than *remove* that many characters (but returning
10
- # an empty string is a good conservative choice).
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
- # This method is also faster.
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
- # This replaces Active Support's +String#from+. It is faster.
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
- # This replaces Active Support's +String#last+, but it returns an
4
- # empty string when given a negative +limit+ argument, whereas Active
5
- # Support's +String#last+ removes +limit.abs+ characters from the
6
- # beginning of the string. Returning an empty string makes more sense
7
- # if you interpret +last+ as "keep upto +limit+ characters." At most,
8
- # a negative +limit+ should *keep* that many characters from the
9
- # beginning of the string, rather than *remove* that many characters
10
- # (but returning an empty string is a good conservative choice). Also
11
- # see +String#first+.
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
- # This method is also faster.
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 string if the string does not already start
4
- # with that prefix. Otherwise returns a duplicate of the string.
5
- # Equivalent to <code>gsub(/^(?!fix)/, "fix")</code>.
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
- # @param [String] fix prefix to prepend
8
- # @return [String] prefixed string
9
- def prefix(fix)
10
- self.start_with?(fix) ? self.dup : "#{fix}#{self}"
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 string if the string does not already end
4
- # with that suffix. Otherwise returns a duplicate of the string.
5
- # Equivalent to <code>gsub(/(?<!fix)$/, "fix")</code>.
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
- # @param [String] fix suffix to append
8
- # @return [String] suffixed string
9
- def suffix(fix)
10
- self.end_with?(fix) ? self.dup : "#{self}#{fix}"
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
- # This replaces Active Support's +String#to+. It is faster.
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,2 @@
1
+ require_relative 'struct/assign_attributes'
2
+ require_relative 'struct/from_h'
@@ -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 time as "HH:MM:SS" (e.g. "23:59:59"). Equivalent to
4
- # <code>strftime("%H:%M:%S")</code>, but faster.
3
+ # Formats the Time as "HH:MM:SS". Equivalent to
4
+ # +strftime("%H:%M:%S")+, but faster.
5
5
  #
6
- # @return [String] the time formatted as "HH:MM:SS"
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 time as "YYYY-MM-DD" (e.g. "1999-12-31"). Equivalent to
4
- # <code>strftime("%Y-%m-%d")</code>, but faster.
3
+ # Formats the Time as "YYYY-MM-DD". Equivalent to
4
+ # +strftime("%Y-%m-%d")+, but faster.
5
5
  #
6
- # @return [String] the time formatted as "YYYY-MM-DD"
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
@@ -1,3 +1,3 @@
1
1
  module CasualSupport
2
- VERSION = "2.0.0"
2
+ VERSION = "3.0.0"
3
3
  end
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: 2.0.0
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: 2016-08-06 00:00:00.000000000 Z
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.4.8
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