casual_support 1.0.0 → 2.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +52 -17
  4. data/Rakefile +13 -0
  5. data/benchmarks/comparable/at_least.rb +9 -0
  6. data/benchmarks/comparable/at_most.rb +9 -0
  7. data/benchmarks/hash/putbang.rb +13 -0
  8. data/benchmarks/integer/to_hex.rb +34 -0
  9. data/benchmarks/string/after.rb +22 -0
  10. data/benchmarks/string/after_last.rb +22 -0
  11. data/benchmarks/string/before.rb +23 -0
  12. data/benchmarks/string/before_last.rb +22 -0
  13. data/benchmarks/string/between.rb +21 -0
  14. data/benchmarks/string/drop.rb +18 -0
  15. data/benchmarks/string/first.rb +23 -0
  16. data/benchmarks/string/from.rb +15 -0
  17. data/benchmarks/string/last.rb +15 -0
  18. data/benchmarks/string/prefix.rb +12 -0
  19. data/benchmarks/string/suffix.rb +12 -0
  20. data/benchmarks/string/to.rb +15 -0
  21. data/benchmarks/time/to_hms.rb +12 -0
  22. data/benchmarks/time/to_ymd.rb +12 -0
  23. data/casual_support.gemspec +4 -3
  24. data/lib/casual_support.rb +8 -8
  25. data/lib/casual_support/comparable.rb +3 -0
  26. data/lib/casual_support/comparable/at_least.rb +11 -0
  27. data/lib/casual_support/comparable/at_most.rb +13 -0
  28. data/lib/casual_support/comparable/clamp.rb +16 -0
  29. data/lib/casual_support/date.rb +1 -1
  30. data/lib/casual_support/date/to_ymd.rb +1 -1
  31. data/lib/casual_support/enumerable.rb +2 -2
  32. data/lib/casual_support/enumerable/index_to.rb +2 -2
  33. data/lib/casual_support/hash.rb +1 -1
  34. data/lib/casual_support/hash/putbang.rb +15 -0
  35. data/lib/casual_support/integer.rb +1 -1
  36. data/lib/casual_support/integer/to_hex.rb +2 -8
  37. data/lib/casual_support/string.rb +13 -4
  38. data/lib/casual_support/string/after.rb +15 -0
  39. data/lib/casual_support/string/after_last.rb +14 -0
  40. data/lib/casual_support/string/before.rb +14 -0
  41. data/lib/casual_support/string/before_last.rb +15 -0
  42. data/lib/casual_support/string/between.rb +16 -0
  43. data/lib/casual_support/string/drop.rb +2 -1
  44. data/lib/casual_support/string/first.rb +17 -0
  45. data/lib/casual_support/string/from.rb +8 -0
  46. data/lib/casual_support/string/last.rb +18 -0
  47. data/lib/casual_support/string/lchomp.rb +16 -0
  48. data/lib/casual_support/string/prefix.rb +13 -0
  49. data/lib/casual_support/string/suffix.rb +13 -0
  50. data/lib/casual_support/string/to.rb +9 -0
  51. data/lib/casual_support/time.rb +2 -1
  52. data/lib/casual_support/time/to_hms.rb +14 -0
  53. data/lib/casual_support/time/to_ymd.rb +14 -0
  54. data/lib/casual_support/version.rb +1 -1
  55. metadata +71 -18
  56. data/bin/console +0 -14
  57. data/bin/setup +0 -8
  58. data/lib/casual_support/hash/setbang.rb +0 -16
  59. data/lib/casual_support/numeric.rb +0 -2
  60. data/lib/casual_support/numeric/at_least_most.rb +0 -21
  61. data/lib/casual_support/numeric/constrain.rb +0 -16
  62. data/lib/casual_support/string/before_after.rb +0 -59
  63. data/lib/casual_support/string/first_last.rb +0 -15
  64. data/lib/casual_support/string/from_to.rb +0 -15
  65. data/lib/casual_support/time/ymd_hms.rb +0 -27
@@ -0,0 +1,12 @@
1
+ require 'benchmark/inputs'
2
+ require_relative '../../lib/casual_support'
3
+
4
+
5
+ FORMAT = '%Y-%m-%d'
6
+
7
+ Benchmark.inputs([Time.new(1999, 12, 31, 23, 59, 59)]) do |job|
8
+ job.report('Time#to_ymd'){|t| t.to_ymd }
9
+ job.report('Time#strftime'){|t| t.strftime('%Y-%m-%d') }
10
+ job.report('Time#strftime(CONST)'){|t| t.strftime(FORMAT) }
11
+ job.compare!
12
+ end
@@ -9,8 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Jonathan Hefner"]
10
10
  spec.email = ["jonathan.hefner@gmail.com"]
11
11
 
12
- spec.summary = %q{Utility methods as core extensions.}
13
- spec.description = %q{Utility methods as extensions to Ruby core objects. Stuff I want in Active Support.}
12
+ spec.summary = %q{Utility extensions to core objects, a la Active Support}
14
13
  spec.homepage = "https://github.com/jonathanhefner/casual_support"
15
14
  spec.license = "MIT"
16
15
 
@@ -22,6 +21,8 @@ Gem::Specification.new do |spec|
22
21
  spec.add_runtime_dependency 'activesupport', '>= 3.1', '< 5.1'
23
22
 
24
23
  spec.add_development_dependency "bundler", "~> 1.12"
25
- spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rake", "~> 11.1"
26
25
  spec.add_development_dependency "minitest", "~> 5.0"
26
+ spec.add_development_dependency "yard", "~> 0.9"
27
+ spec.add_development_dependency "benchmark-inputs", "~> 1.0"
27
28
  end
@@ -1,8 +1,8 @@
1
- require 'casual_support/version'
2
- require 'casual_support/date'
3
- require 'casual_support/enumerable'
4
- require 'casual_support/hash'
5
- require 'casual_support/integer'
6
- require 'casual_support/numeric'
7
- require 'casual_support/string'
8
- require 'casual_support/time'
1
+ require_relative 'casual_support/version'
2
+ require_relative 'casual_support/comparable'
3
+ require_relative 'casual_support/date'
4
+ require_relative 'casual_support/enumerable'
5
+ require_relative 'casual_support/hash'
6
+ require_relative 'casual_support/integer'
7
+ require_relative 'casual_support/string'
8
+ require_relative 'casual_support/time'
@@ -0,0 +1,3 @@
1
+ require_relative 'comparable/at_least'
2
+ require_relative 'comparable/at_most'
3
+ require_relative 'comparable/clamp'
@@ -0,0 +1,11 @@
1
+ module Comparable
2
+
3
+ # Enforces a lower bound for the value.
4
+ #
5
+ # @param [Comparable] limit lower bound
6
+ # @return [Comparable] value constrained by the lower bound
7
+ def at_least(limit)
8
+ self < limit ? limit : self
9
+ end
10
+
11
+ end
@@ -0,0 +1,13 @@
1
+ module Comparable
2
+
3
+ # Enforces an upper bound for the value.
4
+ #
5
+ # @param [Comparable] limit upper bound
6
+ # @return [Comparable] value constrained by the upper bound
7
+ def at_most(limit)
8
+ self > limit ? limit : self
9
+ end
10
+
11
+ alias_method :cap, :at_most
12
+
13
+ end
@@ -0,0 +1,16 @@
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 +1 @@
1
- require 'casual_support/date/to_ymd'
1
+ require_relative 'date/to_ymd'
@@ -3,7 +3,7 @@ require 'date'
3
3
  class Date
4
4
 
5
5
  # Formats date as "YYYY-MM-DD" (e.g. "1999-12-31"). Shortcut for
6
- # +strftime("%Y-%m-%d")+.
6
+ # <code>strftime("%Y-%m-%d")</code>.
7
7
  #
8
8
  # @return [String] the date formatted as "YYYY-MM-DD"
9
9
  def to_ymd
@@ -1,2 +1,2 @@
1
- require 'casual_support/enumerable/duplicates'
2
- require 'casual_support/enumerable/index_to'
1
+ require_relative 'enumerable/duplicates'
2
+ require_relative 'enumerable/index_to'
@@ -1,4 +1,4 @@
1
- require 'casual_support/hash/setbang'
1
+ require_relative '../hash/putbang'
2
2
 
3
3
  module Enumerable
4
4
 
@@ -10,7 +10,7 @@ module Enumerable
10
10
  # @yieldreturn value to associate with the +elem+ key
11
11
  # @return [Hash] hash with the +Enumerable+'s elements as keys
12
12
  def index_to()
13
- self.reduce({}){|h, k| h.set!(k, (yield k)) }
13
+ self.reduce({}){|h, k| h.put!(k, (yield k)) }
14
14
  end
15
15
 
16
16
  end
@@ -1 +1 @@
1
- require 'casual_support/hash/setbang'
1
+ require_relative 'hash/putbang'
@@ -0,0 +1,15 @@
1
+ class Hash
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!+.
6
+ #
7
+ # @param key
8
+ # @param value
9
+ # @return [Hash] the modified hash
10
+ def put!(key, value)
11
+ self[key] = value
12
+ self
13
+ end
14
+
15
+ end
@@ -1 +1 @@
1
- require 'casual_support/integer/to_hex'
1
+ require_relative 'integer/to_hex'
@@ -5,16 +5,10 @@ class Integer
5
5
  # width, the string will be returned unaltered (without padding or
6
6
  # truncation).
7
7
  #
8
- # Without the need for zero-padding, this method is ~7% slower than
9
- # just +to_s(16)+. However, when zero-padding is required, it is
10
- # ~230% faster than +sprintf("%0#{width}x", self)+, and ~30% faster
11
- # than +sprintf(FIXED_FORMAT_STRING, self)+.
12
- #
13
8
  # @param [Integer] width desired width of the zero-padded string
14
9
  # @return [String] hexadecimal string
15
- def to_hex(width = nil)
16
- h = self.to_s(16)
17
- width ? h.rjust(width, '0'.freeze) : h
10
+ def to_hex(width = 0)
11
+ width > 1 ? self.to_s(16).rjust(width, '0'.freeze) : self.to_s(16)
18
12
  end
19
13
 
20
14
  end
@@ -1,4 +1,13 @@
1
- require 'casual_support/string/before_after'
2
- require 'casual_support/string/drop'
3
- require 'casual_support/string/first_last'
4
- require 'casual_support/string/from_to'
1
+ require_relative 'string/after'
2
+ require_relative 'string/after_last'
3
+ require_relative 'string/before'
4
+ require_relative 'string/before_last'
5
+ require_relative 'string/between'
6
+ require_relative 'string/drop'
7
+ require_relative 'string/first'
8
+ require_relative 'string/from'
9
+ require_relative 'string/last'
10
+ require_relative 'string/lchomp'
11
+ require_relative 'string/prefix'
12
+ require_relative 'string/suffix'
13
+ require_relative 'string/to'
@@ -0,0 +1,15 @@
1
+ class String
2
+
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.
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]
13
+ end
14
+
15
+ end
@@ -0,0 +1,14 @@
1
+ class String
2
+
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>.
6
+ #
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]
12
+ end
13
+
14
+ end
@@ -0,0 +1,14 @@
1
+ class String
2
+
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.
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]
12
+ end
13
+
14
+ end
@@ -0,0 +1,15 @@
1
+ class String
2
+
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
7
+ # delimiters.
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]
13
+ end
14
+
15
+ end
@@ -0,0 +1,16 @@
1
+ class String
2
+
3
+ # Returns the portion of the string between the first occurrences of
4
+ # an opening and a closing delimiter. If either delimiter is not
5
+ # found, returns nil.
6
+ #
7
+ # @param [String] open opening delimiter
8
+ # @param [String] close closing delimiter
9
+ # @return [String] portion of the string between the delimiters
10
+ def between(open, close)
11
+ i = self.index(open)
12
+ j = self.index(close, i + 1) if i
13
+ j && self[i + 1, j - i - 1]
14
+ end
15
+
16
+ end
@@ -7,7 +7,8 @@ class String
7
7
  # @param [Integer] n number of characters to drop
8
8
  # @return [String] string of remaining characters
9
9
  def drop(n)
10
- self[n.constrain(0, self.length), self.length]
10
+ return self.dup if n <= 0
11
+ self[n, self.length] || ''
11
12
  end
12
13
 
13
14
  end
@@ -0,0 +1,17 @@
1
+ class String
2
+
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).
11
+ #
12
+ # This method is also faster.
13
+ def first(limit = 1)
14
+ self[0, limit] || ''
15
+ end
16
+
17
+ end
@@ -0,0 +1,8 @@
1
+ class String
2
+
3
+ # This replaces Active Support's +String#from+. It is faster.
4
+ def from(position)
5
+ self[position, length]
6
+ end
7
+
8
+ end
@@ -0,0 +1,18 @@
1
+ class String
2
+
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+.
12
+ #
13
+ # This method is also faster.
14
+ def last(limit = 1)
15
+ limit < 0 ? '' : (self[-limit, limit] || self.dup)
16
+ end
17
+
18
+ end
@@ -0,0 +1,16 @@
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
@@ -0,0 +1,13 @@
1
+ class String
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>.
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}"
11
+ end
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ class String
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>.
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}"
11
+ end
12
+
13
+ end
@@ -0,0 +1,9 @@
1
+ class String
2
+
3
+ # This replaces Active Support's +String#to+. It is faster.
4
+ def to(position)
5
+ position += self.length if position < 0
6
+ self[0, position + 1] || ''
7
+ end
8
+
9
+ end
@@ -1 +1,2 @@
1
- require 'casual_support/time/ymd_hms'
1
+ require_relative 'time/to_hms'
2
+ require_relative 'time/to_ymd'
@@ -0,0 +1,14 @@
1
+ class Time
2
+
3
+ # Formats time as "HH:MM:SS" (e.g. "23:59:59"). Equivalent to
4
+ # <code>strftime("%H:%M:%S")</code>, but faster.
5
+ #
6
+ # @return [String] the time formatted as "HH:MM:SS"
7
+ def to_hms
8
+ # Date#strftime appears to be **much** faster than Time#strftime
9
+ # (nearly 3x faster!). If Time#strftime becomes optimized to that
10
+ # level in the future, it should be used instead of sprintf.
11
+ sprintf('%02d:%02d:%02d'.freeze, hour, min, sec)
12
+ end
13
+
14
+ end
@@ -0,0 +1,14 @@
1
+ class Time
2
+
3
+ # Formats time as "YYYY-MM-DD" (e.g. "1999-12-31"). Equivalent to
4
+ # <code>strftime("%Y-%m-%d")</code>, but faster.
5
+ #
6
+ # @return [String] the time formatted as "YYYY-MM-DD"
7
+ def to_ymd
8
+ # Date#strftime appears to be **much** faster than Time#strftime
9
+ # (nearly 3x faster!). If Time#strftime becomes optimized to that
10
+ # level in the future, it should be used instead of sprintf.
11
+ sprintf('%04d-%02d-%02d'.freeze, year, month, day)
12
+ end
13
+
14
+ end