sleeping_king_studios-tools 0.7.0 → 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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +54 -3
- data/CODE_OF_CONDUCT.md +132 -0
- data/DEVELOPMENT.md +7 -16
- data/LICENSE +1 -1
- data/README.md +71 -145
- data/lib/sleeping_king_studios/tools.rb +12 -6
- data/lib/sleeping_king_studios/tools/array_tools.rb +86 -58
- data/lib/sleeping_king_studios/tools/base.rb +20 -0
- data/lib/sleeping_king_studios/tools/core_tools.rb +78 -19
- data/lib/sleeping_king_studios/tools/hash_tools.rb +69 -42
- data/lib/sleeping_king_studios/tools/integer_tools.rb +97 -55
- data/lib/sleeping_king_studios/tools/object_tools.rb +75 -52
- data/lib/sleeping_king_studios/tools/string_tools.rb +69 -96
- data/lib/sleeping_king_studios/tools/toolbelt.rb +44 -23
- data/lib/sleeping_king_studios/tools/toolbox.rb +2 -2
- data/lib/sleeping_king_studios/tools/toolbox/constant_map.rb +75 -74
- data/lib/sleeping_king_studios/tools/toolbox/inflector.rb +124 -0
- data/lib/sleeping_king_studios/tools/toolbox/inflector/rules.rb +173 -0
- data/lib/sleeping_king_studios/tools/toolbox/mixin.rb +11 -11
- data/lib/sleeping_king_studios/tools/toolbox/semantic_version.rb +15 -14
- data/lib/sleeping_king_studios/tools/version.rb +14 -10
- metadata +106 -35
- data/lib/sleeping_king_studios/tools/all.rb +0 -5
- data/lib/sleeping_king_studios/tools/enumerable_tools.rb +0 -8
- data/lib/sleeping_king_studios/tools/semantic_version.rb +0 -15
- data/lib/sleeping_king_studios/tools/string_tools/plural_inflector.rb +0 -185
- data/lib/sleeping_king_studios/tools/toolbox/configuration.rb +0 -207
- data/lib/sleeping_king_studios/tools/toolbox/delegator.rb +0 -175
| @@ -1,19 +1,63 @@ | |
| 1 | 
            -
            #  | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'sleeping_king_studios/tools'
         | 
| 4 | 
            -
            require 'sleeping_king_studios/tools/string_tools'
         | 
| 5 4 |  | 
| 6 5 | 
             
            module SleepingKingStudios::Tools
         | 
| 7 6 | 
             
              # Tools for working with integers.
         | 
| 8 | 
            -
               | 
| 9 | 
            -
                extend self
         | 
| 10 | 
            -
             | 
| 7 | 
            +
              class IntegerTools < SleepingKingStudios::Tools::Base
         | 
| 11 8 | 
             
                # Minimum integer value that can be converted to a roman numeral.
         | 
| 12 9 | 
             
                ROMANIZE_MIN = 1
         | 
| 13 10 |  | 
| 14 11 | 
             
                # Maximum integer value that can be converted to a roman numeral.
         | 
| 15 12 | 
             
                ROMANIZE_MAX = 4999
         | 
| 16 13 |  | 
| 14 | 
            +
                ROMANIZE_NUMERALS = [
         | 
| 15 | 
            +
                  %w[I V X].freeze,
         | 
| 16 | 
            +
                  %w[X L C].freeze,
         | 
| 17 | 
            +
                  %w[C D M].freeze,
         | 
| 18 | 
            +
                  ['M', 'MMM', ''].freeze
         | 
| 19 | 
            +
                ].freeze
         | 
| 20 | 
            +
                private_constant :ROMANIZE_NUMERALS
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                ROMANIZE_RULES = [
         | 
| 23 | 
            +
                  '',
         | 
| 24 | 
            +
                  '1',
         | 
| 25 | 
            +
                  '11',
         | 
| 26 | 
            +
                  '111',
         | 
| 27 | 
            +
                  '15',
         | 
| 28 | 
            +
                  '5',
         | 
| 29 | 
            +
                  '51',
         | 
| 30 | 
            +
                  '511',
         | 
| 31 | 
            +
                  '5111',
         | 
| 32 | 
            +
                  '1a',
         | 
| 33 | 
            +
                  'a'
         | 
| 34 | 
            +
                ].freeze
         | 
| 35 | 
            +
                private_constant :ROMANIZE_RULES
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                ROMANIZE_RULES_ADDITIVE = [
         | 
| 38 | 
            +
                  '',
         | 
| 39 | 
            +
                  '1',
         | 
| 40 | 
            +
                  '11',
         | 
| 41 | 
            +
                  '111',
         | 
| 42 | 
            +
                  '1111',
         | 
| 43 | 
            +
                  '5',
         | 
| 44 | 
            +
                  '51',
         | 
| 45 | 
            +
                  '511',
         | 
| 46 | 
            +
                  '5111',
         | 
| 47 | 
            +
                  '51111',
         | 
| 48 | 
            +
                  'a'
         | 
| 49 | 
            +
                ].freeze
         | 
| 50 | 
            +
                private_constant :ROMANIZE_RULES_ADDITIVE
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                class << self
         | 
| 53 | 
            +
                  def_delegators :instance,
         | 
| 54 | 
            +
                    :count_digits,
         | 
| 55 | 
            +
                    :digits,
         | 
| 56 | 
            +
                    :integer?,
         | 
| 57 | 
            +
                    :pluralize,
         | 
| 58 | 
            +
                    :romanize
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 17 61 | 
             
                # Returns the number of digits in the given integer when represented in the
         | 
| 18 62 | 
             
                # specified base. Ignores minus sign for negative numbers.
         | 
| 19 63 | 
             
                #
         | 
| @@ -38,9 +82,9 @@ module SleepingKingStudios::Tools | |
| 38 82 | 
             
                #   Defaults to 10.
         | 
| 39 83 | 
             
                #
         | 
| 40 84 | 
             
                # @return [Integer] The number of digits.
         | 
| 41 | 
            -
                def count_digits | 
| 42 | 
            -
                  digits(integer.abs, : | 
| 43 | 
            -
                end | 
| 85 | 
            +
                def count_digits(integer, base: 10)
         | 
| 86 | 
            +
                  digits(integer.abs, base: base).count
         | 
| 87 | 
            +
                end
         | 
| 44 88 |  | 
| 45 89 | 
             
                # Decomposes the given integer into its digits when represented in the
         | 
| 46 90 | 
             
                # given base.
         | 
| @@ -63,18 +107,18 @@ module SleepingKingStudios::Tools | |
| 63 107 | 
             
                #
         | 
| 64 108 | 
             
                # @return [Array<String>] The digits of the decomposed integer,
         | 
| 65 109 | 
             
                #   represented as a bigendian array of strings.
         | 
| 66 | 
            -
                def digits | 
| 110 | 
            +
                def digits(integer, base: 10)
         | 
| 67 111 | 
             
                  integer.to_s(base).split('')
         | 
| 68 | 
            -
                end | 
| 112 | 
            +
                end
         | 
| 69 113 |  | 
| 70 114 | 
             
                # Returns true if the object is an Integer.
         | 
| 71 115 | 
             
                #
         | 
| 72 116 | 
             
                # @param int [Object] The object to test.
         | 
| 73 117 | 
             
                #
         | 
| 74 118 | 
             
                # @return [Boolean] True if the object is an Integer, otherwise false.
         | 
| 75 | 
            -
                def integer? | 
| 76 | 
            -
                  Integer | 
| 77 | 
            -
                end | 
| 119 | 
            +
                def integer?(int)
         | 
| 120 | 
            +
                  int.is_a?(Integer)
         | 
| 121 | 
            +
                end
         | 
| 78 122 |  | 
| 79 123 | 
             
                # Returns the singular or the plural value, depending on the provided
         | 
| 80 124 | 
             
                # item count.
         | 
| @@ -89,11 +133,11 @@ module SleepingKingStudios::Tools | |
| 89 133 | 
             
                #
         | 
| 90 134 | 
             
                # @return [String] The single form if count == 1; otherwise the plural
         | 
| 91 135 | 
             
                #   form.
         | 
| 92 | 
            -
                def pluralize | 
| 136 | 
            +
                def pluralize(count, single, plural = nil)
         | 
| 93 137 | 
             
                  plural ||= StringTools.pluralize(single)
         | 
| 94 138 |  | 
| 95 | 
            -
                   | 
| 96 | 
            -
                end | 
| 139 | 
            +
                  count == 1 ? single : plural
         | 
| 140 | 
            +
                end
         | 
| 97 141 |  | 
| 98 142 | 
             
                # Represents an integer between 1 and 4999 (inclusive) as a Roman numeral.
         | 
| 99 143 | 
             
                #
         | 
| @@ -114,42 +158,40 @@ module SleepingKingStudios::Tools | |
| 114 158 | 
             
                # @return [String] The representation of the integer as a Roman numeral.
         | 
| 115 159 | 
             
                #
         | 
| 116 160 | 
             
                # @raise [RangeError] If the integer is less than 1 or greater than 4999.
         | 
| 117 | 
            -
                def romanize | 
| 118 | 
            -
                   | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
                     | 
| 126 | 
            -
                     | 
| 127 | 
            -
                     | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
                   | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
                  ] | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 147 | 
            -
                   | 
| 148 | 
            -
                     | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
              end # module
         | 
| 155 | 
            -
            end # module
         | 
| 161 | 
            +
                def romanize(integer, additive: false)
         | 
| 162 | 
            +
                  check_romanize_range(integer)
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                  digits(integer)
         | 
| 165 | 
            +
                    .reverse
         | 
| 166 | 
            +
                    .map
         | 
| 167 | 
            +
                    .with_index do |digit, index|
         | 
| 168 | 
            +
                      romanize_digit(additive: additive, digit: digit.to_i, tens: index)
         | 
| 169 | 
            +
                    end
         | 
| 170 | 
            +
                    .reverse
         | 
| 171 | 
            +
                    .join
         | 
| 172 | 
            +
                end
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                private
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                def check_romanize_range(integer)
         | 
| 177 | 
            +
                  return if (ROMANIZE_MIN..ROMANIZE_MAX).include?(integer)
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                  error_message =
         | 
| 180 | 
            +
                    "integer to romanize must be within range #{ROMANIZE_MIN} to" \
         | 
| 181 | 
            +
                    " #{ROMANIZE_MAX}"
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                  raise RangeError, error_message, caller(1..-1)
         | 
| 184 | 
            +
                end
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                def romanize_digit(additive:, digit:, tens:)
         | 
| 187 | 
            +
                  rules    = (additive ? ROMANIZE_RULES_ADDITIVE : ROMANIZE_RULES)
         | 
| 188 | 
            +
                  rule     = rules[digit]
         | 
| 189 | 
            +
                  numerals = ROMANIZE_NUMERALS[tens]
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                  rule
         | 
| 192 | 
            +
                    .gsub('1', numerals[0])
         | 
| 193 | 
            +
                    .gsub('5', numerals[1])
         | 
| 194 | 
            +
                    .gsub('a', numerals[2])
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
              end
         | 
| 197 | 
            +
            end
         | 
| @@ -1,16 +1,32 @@ | |
| 1 | 
            -
            #  | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'sleeping_king_studios/tools'
         | 
| 4 4 |  | 
| 5 5 | 
             
            module SleepingKingStudios::Tools
         | 
| 6 6 | 
             
              # Low-level tools for working with objects.
         | 
| 7 | 
            -
               | 
| 8 | 
            -
                 | 
| 7 | 
            +
              class ObjectTools < SleepingKingStudios::Tools::Base
         | 
| 8 | 
            +
                TEMPORARY_METHOD_NAME =
         | 
| 9 | 
            +
                  '__sleeping_king_studios_tools_apply_%i__'
         | 
| 10 | 
            +
                private_constant :TEMPORARY_METHOD_NAME
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                class << self
         | 
| 13 | 
            +
                  def_delegators :instance,
         | 
| 14 | 
            +
                    :apply,
         | 
| 15 | 
            +
                    :deep_dup,
         | 
| 16 | 
            +
                    :deep_freeze,
         | 
| 17 | 
            +
                    :dig,
         | 
| 18 | 
            +
                    :eigenclass,
         | 
| 19 | 
            +
                    :immutable?,
         | 
| 20 | 
            +
                    :metaclass,
         | 
| 21 | 
            +
                    :mutable?,
         | 
| 22 | 
            +
                    :object?,
         | 
| 23 | 
            +
                    :try
         | 
| 24 | 
            +
                end
         | 
| 9 25 |  | 
| 10 26 | 
             
                # Takes a proc or lambda and invokes it with the given object as
         | 
| 11 27 | 
             
                # receiver, with any additional arguments or block provided.
         | 
| 12 28 | 
             
                #
         | 
| 13 | 
            -
                # @param [Object]  | 
| 29 | 
            +
                # @param [Object] receiver The receiver. The proc will be called in the
         | 
| 14 30 | 
             
                #   context of this object.
         | 
| 15 31 | 
             
                # @param [Proc] proc The proc or lambda to call.
         | 
| 16 32 | 
             
                # @param [Array] args Optional. Additional arguments to pass in to the
         | 
| @@ -20,20 +36,16 @@ module SleepingKingStudios::Tools | |
| 20 36 | 
             
                #
         | 
| 21 37 | 
             
                # @return The result of calling the proc or lambda with the given
         | 
| 22 38 | 
             
                #   receiver and any additional arguments or block.
         | 
| 23 | 
            -
                def apply | 
| 24 | 
            -
                  return  | 
| 39 | 
            +
                def apply(receiver, proc, *args, &block)
         | 
| 40 | 
            +
                  return receiver.instance_exec(*args, &proc) unless block_given?
         | 
| 25 41 |  | 
| 26 | 
            -
                   | 
| 42 | 
            +
                  method_name =
         | 
| 43 | 
            +
                    Kernel.format(TEMPORARY_METHOD_NAME, Thread.current.object_id)
         | 
| 27 44 |  | 
| 28 | 
            -
                   | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
                  begin
         | 
| 32 | 
            -
                    base.send temporary_method_name, *args, &block
         | 
| 33 | 
            -
                  ensure
         | 
| 34 | 
            -
                    metaclass.send :remove_method, temporary_method_name if temporary_method_name && defined?(temporary_method_name)
         | 
| 45 | 
            +
                  with_temporary_method(receiver, method_name, proc) do
         | 
| 46 | 
            +
                    receiver.send(method_name, *args, &block)
         | 
| 35 47 | 
             
                  end
         | 
| 36 | 
            -
                end | 
| 48 | 
            +
                end
         | 
| 37 49 |  | 
| 38 50 | 
             
                # Creates a deep copy of the object. If the object is an Array, returns a
         | 
| 39 51 | 
             
                # new Array with deep copies of each array item. If the object is a Hash,
         | 
| @@ -43,7 +55,7 @@ module SleepingKingStudios::Tools | |
| 43 55 | 
             
                # @param [Object] obj The object to copy.
         | 
| 44 56 | 
             
                #
         | 
| 45 57 | 
             
                # @return The copy of the object.
         | 
| 46 | 
            -
                def deep_dup | 
| 58 | 
            +
                def deep_dup(obj)
         | 
| 47 59 | 
             
                  case obj
         | 
| 48 60 | 
             
                  when FalseClass, Integer, Float, NilClass, Symbol, TrueClass
         | 
| 49 61 | 
             
                    obj
         | 
| @@ -53,8 +65,8 @@ module SleepingKingStudios::Tools | |
| 53 65 | 
             
                    HashTools.deep_dup obj
         | 
| 54 66 | 
             
                  else
         | 
| 55 67 | 
             
                    obj.respond_to?(:deep_dup) ? obj.deep_dup : obj.dup
         | 
| 56 | 
            -
                  end | 
| 57 | 
            -
                end | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 58 70 |  | 
| 59 71 | 
             
                # Performs a deep freeze of the object. If the object is an Array, freezes
         | 
| 60 72 | 
             
                # the array and performs a deep freeze on each array item. If the object is
         | 
| @@ -62,7 +74,7 @@ module SleepingKingStudios::Tools | |
| 62 74 | 
             
                # value. Otherwise, calls Object#freeze.
         | 
| 63 75 | 
             
                #
         | 
| 64 76 | 
             
                # @param [Object] obj The object to freeze.
         | 
| 65 | 
            -
                def deep_freeze | 
| 77 | 
            +
                def deep_freeze(obj)
         | 
| 66 78 | 
             
                  case obj
         | 
| 67 79 | 
             
                  when FalseClass, Integer, Float, NilClass, Symbol, TrueClass
         | 
| 68 80 | 
             
                    # Object is inherently immutable; do nothing here.
         | 
| @@ -72,34 +84,41 @@ module SleepingKingStudios::Tools | |
| 72 84 | 
             
                    HashTools.deep_freeze obj
         | 
| 73 85 | 
             
                  else
         | 
| 74 86 | 
             
                    obj.respond_to?(:deep_freeze) ? obj.deep_freeze : obj.freeze
         | 
| 75 | 
            -
                  end | 
| 76 | 
            -
                end | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                end
         | 
| 77 89 |  | 
| 78 90 | 
             
                # Accesses deeply nested attributes by calling the first named method on the
         | 
| 79 91 | 
             
                # given object, and each subsequent method on the result of the previous
         | 
| 80 92 | 
             
                # method call. If the object does not respond to the method name, nil is
         | 
| 81 93 | 
             
                # returned instead of calling the method.
         | 
| 82 94 | 
             
                #
         | 
| 83 | 
            -
                # @param [Object]  | 
| 95 | 
            +
                # @param [Object] object The object to dig.
         | 
| 84 96 | 
             
                # @param [Array] method_names The names of the methods to call.
         | 
| 85 97 | 
             
                #
         | 
| 86 98 | 
             
                # @return [Object] The result of the last method call, or nil if the last
         | 
| 87 99 | 
             
                #   object does not respond to the last method.
         | 
| 88 | 
            -
                def dig | 
| 100 | 
            +
                def dig(object, *method_names)
         | 
| 89 101 | 
             
                  method_names.reduce(object) do |memo, method_name|
         | 
| 90 102 | 
             
                    memo.respond_to?(method_name) ? memo.send(method_name) : nil
         | 
| 91 | 
            -
                  end | 
| 92 | 
            -
                end | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
                end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                # @!method metaclass(object)
         | 
| 107 | 
            +
                #   Returns the object's singleton class.
         | 
| 108 | 
            +
                #
         | 
| 109 | 
            +
                #   @param [Object] object The object for which an eigenclass is required.
         | 
| 110 | 
            +
                #
         | 
| 111 | 
            +
                #   @return [Class] The object's eigenclass.
         | 
| 93 112 |  | 
| 94 113 | 
             
                # Returns the object's eigenclass.
         | 
| 95 114 | 
             
                #
         | 
| 96 115 | 
             
                # @param [Object] object The object for which an eigenclass is required.
         | 
| 97 116 | 
             
                #
         | 
| 98 | 
            -
                # @return [Class] The object's  | 
| 99 | 
            -
                def eigenclass | 
| 100 | 
            -
                   | 
| 101 | 
            -
                end | 
| 102 | 
            -
                 | 
| 117 | 
            +
                # @return [Class] The object's singleton class.
         | 
| 118 | 
            +
                def eigenclass(object)
         | 
| 119 | 
            +
                  object.singleton_class
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
                alias metaclass eigenclass
         | 
| 103 122 |  | 
| 104 123 | 
             
                # Returns true if the object is immutable. Values of nil, false, and true
         | 
| 105 124 | 
             
                # are always immutable, as are instances of Numeric and Symbol. Arrays are
         | 
| @@ -110,7 +129,7 @@ module SleepingKingStudios::Tools | |
| 110 129 | 
             
                # @param obj [Object] The object to test.
         | 
| 111 130 | 
             
                #
         | 
| 112 131 | 
             
                # @return [Boolean] True if the object is immutable, otherwise false.
         | 
| 113 | 
            -
                def immutable? | 
| 132 | 
            +
                def immutable?(obj)
         | 
| 114 133 | 
             
                  case obj
         | 
| 115 134 | 
             
                  when NilClass, FalseClass, TrueClass, Numeric, Symbol
         | 
| 116 135 | 
             
                    true
         | 
| @@ -120,8 +139,8 @@ module SleepingKingStudios::Tools | |
| 120 139 | 
             
                    HashTools.immutable? obj
         | 
| 121 140 | 
             
                  else
         | 
| 122 141 | 
             
                    obj.frozen?
         | 
| 123 | 
            -
                  end | 
| 124 | 
            -
                end | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
                end
         | 
| 125 144 |  | 
| 126 145 | 
             
                # Returns true if the object is mutable.
         | 
| 127 146 | 
             
                #
         | 
| @@ -130,9 +149,9 @@ module SleepingKingStudios::Tools | |
| 130 149 | 
             
                # @return [Boolean] True if the object is mutable, otherwise false.
         | 
| 131 150 | 
             
                #
         | 
| 132 151 | 
             
                # @see #immutable?
         | 
| 133 | 
            -
                def mutable? | 
| 152 | 
            +
                def mutable?(obj)
         | 
| 134 153 | 
             
                  !immutable?(obj)
         | 
| 135 | 
            -
                end | 
| 154 | 
            +
                end
         | 
| 136 155 |  | 
| 137 156 | 
             
                # Returns true if the object is an Object. This should return true only for
         | 
| 138 157 | 
             
                # objects that have an alternate inheritance chain from BasicObject, such as
         | 
| @@ -141,9 +160,9 @@ module SleepingKingStudios::Tools | |
| 141 160 | 
             
                # @param obj [Object] The object to test.
         | 
| 142 161 | 
             
                #
         | 
| 143 162 | 
             
                # @return [Boolean] True if the object is an Object, otherwise false.
         | 
| 144 | 
            -
                def object? | 
| 145 | 
            -
                  Object | 
| 146 | 
            -
                end | 
| 163 | 
            +
                def object?(obj)
         | 
| 164 | 
            +
                  Object.instance_method(:is_a?).bind(obj).call(Object)
         | 
| 165 | 
            +
                end
         | 
| 147 166 |  | 
| 148 167 | 
             
                # As #send, but returns nil if the object does not respond to the method.
         | 
| 149 168 | 
             
                #
         | 
| @@ -151,23 +170,27 @@ module SleepingKingStudios::Tools | |
| 151 170 | 
             
                # @param [String, Symbol] method_name The name of the method to call.
         | 
| 152 171 | 
             
                # @param [Array] args The arguments to the message.
         | 
| 153 172 | 
             
                #
         | 
| 154 | 
            -
                # @see  | 
| 155 | 
            -
                def try | 
| 156 | 
            -
                  if object.nil?
         | 
| 157 | 
            -
                    return object.respond_to?(method_name) ?
         | 
| 158 | 
            -
                      object.send(method_name, *args) :
         | 
| 159 | 
            -
                      nil
         | 
| 160 | 
            -
                  end # if
         | 
| 161 | 
            -
             | 
| 162 | 
            -
                  # Delegate to ActiveSupport::CoreExt::Object#try.
         | 
| 173 | 
            +
                # @see ActiveSupport::CoreExt::Object#try.
         | 
| 174 | 
            +
                def try(object, method_name, *args)
         | 
| 163 175 | 
             
                  return object.try(method_name, *args) if object.respond_to?(:try)
         | 
| 164 176 |  | 
| 177 | 
            +
                  return nil unless object.respond_to?(method_name)
         | 
| 178 | 
            +
             | 
| 165 179 | 
             
                  object.send method_name, *args
         | 
| 166 | 
            -
                 | 
| 167 | 
            -
             | 
| 168 | 
            -
                 | 
| 169 | 
            -
             | 
| 170 | 
            -
             | 
| 180 | 
            +
                end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                private
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                def with_temporary_method(receiver, method_name, proc)
         | 
| 185 | 
            +
                  metaclass = class << receiver; self; end
         | 
| 186 | 
            +
                  metaclass.send :define_method, method_name, &proc
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                  yield
         | 
| 189 | 
            +
                ensure
         | 
| 190 | 
            +
                  metaclass.send :remove_method, method_name
         | 
| 191 | 
            +
                end
         | 
| 192 | 
            +
              end
         | 
| 193 | 
            +
            end
         | 
| 171 194 |  | 
| 172 195 | 
             
            require 'sleeping_king_studios/tools/array_tools'
         | 
| 173 196 | 
             
            require 'sleeping_king_studios/tools/hash_tools'
         | 
| @@ -1,14 +1,40 @@ | |
| 1 | 
            -
            #  | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'sleeping_king_studios/tools'
         | 
| 4 | 
            -
            require 'sleeping_king_studios/tools/ | 
| 4 | 
            +
            require 'sleeping_king_studios/tools/toolbox/inflector'
         | 
| 5 5 |  | 
| 6 6 | 
             
            module SleepingKingStudios::Tools
         | 
| 7 7 | 
             
              # Tools for working with strings.
         | 
| 8 | 
            -
               | 
| 9 | 
            -
                 | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 8 | 
            +
              class StringTools < SleepingKingStudios::Tools::Base
         | 
| 9 | 
            +
                class << self
         | 
| 10 | 
            +
                  def_delegators :instance,
         | 
| 11 | 
            +
                    :camelize,
         | 
| 12 | 
            +
                    :chain,
         | 
| 13 | 
            +
                    :define_irregular_word,
         | 
| 14 | 
            +
                    :define_plural_rule,
         | 
| 15 | 
            +
                    :define_singular_rule,
         | 
| 16 | 
            +
                    :define_uncountable_word,
         | 
| 17 | 
            +
                    :indent,
         | 
| 18 | 
            +
                    :map_lines,
         | 
| 19 | 
            +
                    :plural?,
         | 
| 20 | 
            +
                    :pluralize,
         | 
| 21 | 
            +
                    :singular?,
         | 
| 22 | 
            +
                    :singularize,
         | 
| 23 | 
            +
                    :string?,
         | 
| 24 | 
            +
                    :underscore
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # @param inflector [Object] An object that conforms to the interface used
         | 
| 28 | 
            +
                #   by SleepingKingStudios::Tools::Toolbox::Inflector, such as
         | 
| 29 | 
            +
                #   ActiveSupport::Inflector .
         | 
| 30 | 
            +
                def initialize(inflector: nil)
         | 
| 31 | 
            +
                  super()
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  @inflector =
         | 
| 34 | 
            +
                    inflector || SleepingKingStudios::Tools::Toolbox::Inflector.new
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                attr_reader :inflector
         | 
| 12 38 |  | 
| 13 39 | 
             
                # Converts a lowercase, underscore-separated string to CamelCase.
         | 
| 14 40 | 
             
                #
         | 
| @@ -17,13 +43,11 @@ module SleepingKingStudios::Tools | |
| 17 43 | 
             
                # @return [String] The converted string.
         | 
| 18 44 | 
             
                #
         | 
| 19 45 | 
             
                # @see ActiveSupport::Inflector#camelize.
         | 
| 20 | 
            -
                def camelize | 
| 46 | 
            +
                def camelize(str)
         | 
| 21 47 | 
             
                  str = require_string! str
         | 
| 22 48 |  | 
| 23 | 
            -
                  str | 
| 24 | 
            -
             | 
| 25 | 
            -
                  str
         | 
| 26 | 
            -
                end # method camelize
         | 
| 49 | 
            +
                  inflector.camelize(str)
         | 
| 50 | 
            +
                end
         | 
| 27 51 |  | 
| 28 52 | 
             
                # Performs multiple string tools operations in sequence, starting with the
         | 
| 29 53 | 
             
                # given string and passing the result of each operation to the next.
         | 
| @@ -32,31 +56,11 @@ module SleepingKingStudios::Tools | |
| 32 56 | 
             
                # @param commands [Array<String, Symbol>] The string operations to apply.
         | 
| 33 57 | 
             
                #
         | 
| 34 58 | 
             
                # @return [String] The processed string.
         | 
| 35 | 
            -
                def chain | 
| 59 | 
            +
                def chain(str, *commands)
         | 
| 36 60 | 
             
                  str = require_string! str
         | 
| 37 61 |  | 
| 38 62 | 
             
                  commands.reduce(str) { |memo, command| send(command, memo) }
         | 
| 39 | 
            -
                end | 
| 40 | 
            -
             | 
| 41 | 
            -
                # (see PluralInflector#define_irregular_word)
         | 
| 42 | 
            -
                def define_irregular_word singular, plural
         | 
| 43 | 
            -
                  plural_inflector.define_irregular_word singular, plural
         | 
| 44 | 
            -
                end # method define_irregular_word
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                # (see PluralInflector#define_plural_rule)
         | 
| 47 | 
            -
                def define_plural_rule match, replace
         | 
| 48 | 
            -
                  plural_inflector.define_plural_rule match, replace
         | 
| 49 | 
            -
                end # method define_plural_rule
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                # (see PluralInflector#define_singular_rule)
         | 
| 52 | 
            -
                def define_singular_rule match, replace
         | 
| 53 | 
            -
                  plural_inflector.define_singular_rule match, replace
         | 
| 54 | 
            -
                end # method define_singular_rule
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                # (see PluralInflector#define_uncountable_word)
         | 
| 57 | 
            -
                def define_uncountable_word word
         | 
| 58 | 
            -
                  plural_inflector.define_uncountable_word word
         | 
| 59 | 
            -
                end # method define_uncountable_word
         | 
| 63 | 
            +
                end
         | 
| 60 64 |  | 
| 61 65 | 
             
                # Adds the specified number of spaces to the start of each line of the
         | 
| 62 66 | 
             
                # string. Defaults to 2 spaces.
         | 
| @@ -65,12 +69,12 @@ module SleepingKingStudios::Tools | |
| 65 69 | 
             
                # @param count [Integer] The number of spaces to add.
         | 
| 66 70 | 
             
                #
         | 
| 67 71 | 
             
                # @return [String] The indented string.
         | 
| 68 | 
            -
                def indent | 
| 72 | 
            +
                def indent(str, count = 2)
         | 
| 69 73 | 
             
                  str = require_string! str
         | 
| 70 | 
            -
                  pre =  | 
| 74 | 
            +
                  pre = ' ' * count
         | 
| 71 75 |  | 
| 72 76 | 
             
                  map_lines(str) { |line| "#{pre}#{line}" }
         | 
| 73 | 
            -
                end | 
| 77 | 
            +
                end
         | 
| 74 78 |  | 
| 75 79 | 
             
                # Yields each line of the string to the provided block and combines the
         | 
| 76 80 | 
             
                # results into a new multiline string.
         | 
| @@ -81,23 +85,23 @@ module SleepingKingStudios::Tools | |
| 81 85 | 
             
                # @yieldparam index [Integer] The index of the current line.
         | 
| 82 86 | 
             
                #
         | 
| 83 87 | 
             
                # @return [String] The mapped string.
         | 
| 84 | 
            -
                def map_lines | 
| 88 | 
            +
                def map_lines(str)
         | 
| 85 89 | 
             
                  str = require_string! str
         | 
| 86 90 |  | 
| 87 | 
            -
                  str.each_line.with_index.reduce('') do |memo, (line, index)|
         | 
| 91 | 
            +
                  str.each_line.with_index.reduce(+'') do |memo, (line, index)|
         | 
| 88 92 | 
             
                    memo << yield(line, index)
         | 
| 89 | 
            -
                  end | 
| 90 | 
            -
                end | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
                end
         | 
| 91 95 |  | 
| 92 96 | 
             
                # Determines whether or not the given word is in plural form. If calling
         | 
| 93 97 | 
             
                # #pluralize(word) is equal to word, the word is considered plural.
         | 
| 94 98 | 
             
                #
         | 
| 95 99 | 
             
                # @return [Boolean] True if the word is in plural form, otherwise false.
         | 
| 96 | 
            -
                def plural? | 
| 100 | 
            +
                def plural?(word)
         | 
| 97 101 | 
             
                  word = require_string!(word)
         | 
| 98 102 |  | 
| 99 103 | 
             
                  word == pluralize(word)
         | 
| 100 | 
            -
                end | 
| 104 | 
            +
                end
         | 
| 101 105 |  | 
| 102 106 | 
             
                # @overload pluralize(str)
         | 
| 103 107 | 
             
                #   Takes a word in singular form and returns the plural form, based on the
         | 
| @@ -106,63 +110,41 @@ module SleepingKingStudios::Tools | |
| 106 110 | 
             
                #   @param str [String] The word to pluralize.
         | 
| 107 111 | 
             
                #
         | 
| 108 112 | 
             
                #   @return [String] The pluralized word.
         | 
| 109 | 
            -
                 | 
| 110 | 
            -
                # @overload pluralize(count, single, plural)
         | 
| 111 | 
            -
                #   @deprecated This functionality is deprecated as of version 0.4.0 and
         | 
| 112 | 
            -
                #     will be removed in a future version. Use IntegerTools#pluralize
         | 
| 113 | 
            -
                #     instead.
         | 
| 114 | 
            -
                #
         | 
| 115 | 
            -
                #   Returns the singular or the plural value, depending on the provided
         | 
| 116 | 
            -
                #   item count.
         | 
| 117 | 
            -
                #
         | 
| 118 | 
            -
                #   @example
         | 
| 119 | 
            -
                #     "There are four #{StringTools.pluralize 4, 'light', 'lights'}!"
         | 
| 120 | 
            -
                #     #=> 'There are four lights!'
         | 
| 121 | 
            -
                #
         | 
| 122 | 
            -
                #   @param [Integer] count The number of items.
         | 
| 123 | 
            -
                #   @param [String] single The singular form of the word or phrase.
         | 
| 124 | 
            -
                #   @param [String] plural The plural form of the word or phrase.
         | 
| 125 | 
            -
                #
         | 
| 126 | 
            -
                #   @return [String] The single form if count == 1; otherwise the plural
         | 
| 127 | 
            -
                #     form.
         | 
| 128 | 
            -
                def pluralize *args
         | 
| 129 | 
            -
                  if args.count == 3
         | 
| 130 | 
            -
                    CoreTools.deprecate 'StringTools#pluralize with 3 arguments',
         | 
| 131 | 
            -
                      :message => 'Use IntegerTools#pluralize instead.'
         | 
| 132 | 
            -
             | 
| 133 | 
            -
                    return IntegerTools.pluralize(*args)
         | 
| 134 | 
            -
                  end # if
         | 
| 135 | 
            -
             | 
| 113 | 
            +
                def pluralize(*args)
         | 
| 136 114 | 
             
                  str = require_string! args.first
         | 
| 137 115 |  | 
| 138 | 
            -
                   | 
| 139 | 
            -
                end | 
| 116 | 
            +
                  inflector.pluralize str
         | 
| 117 | 
            +
                end
         | 
| 140 118 |  | 
| 141 119 | 
             
                # Determines whether or not the given word is in singular form. If calling
         | 
| 142 120 | 
             
                # #singularize(word) is equal to word, the word is considered singular.
         | 
| 143 121 | 
             
                #
         | 
| 144 122 | 
             
                # @return [Boolean] True if the word is in singular form, otherwise false.
         | 
| 145 | 
            -
                def singular? | 
| 123 | 
            +
                def singular?(word)
         | 
| 146 124 | 
             
                  word = require_string!(word)
         | 
| 147 125 |  | 
| 148 126 | 
             
                  word == singularize(word)
         | 
| 149 | 
            -
                end | 
| 127 | 
            +
                end
         | 
| 150 128 |  | 
| 151 | 
            -
                #  | 
| 152 | 
            -
                 | 
| 129 | 
            +
                # Transforms the word to a singular, lowercase form.
         | 
| 130 | 
            +
                #
         | 
| 131 | 
            +
                # @param str [String] The word to transform.
         | 
| 132 | 
            +
                #
         | 
| 133 | 
            +
                # @return [String] The word in singular form.
         | 
| 134 | 
            +
                def singularize(str)
         | 
| 153 135 | 
             
                  require_string! str
         | 
| 154 136 |  | 
| 155 | 
            -
                   | 
| 156 | 
            -
                end | 
| 137 | 
            +
                  inflector.singularize str
         | 
| 138 | 
            +
                end
         | 
| 157 139 |  | 
| 158 140 | 
             
                # Returns true if the object is a String.
         | 
| 159 141 | 
             
                #
         | 
| 160 142 | 
             
                # @param str [Object] The object to test.
         | 
| 161 143 | 
             
                #
         | 
| 162 144 | 
             
                # @return [Boolean] True if the object is a String, otherwise false.
         | 
| 163 | 
            -
                def string? | 
| 164 | 
            -
                  String | 
| 165 | 
            -
                end | 
| 145 | 
            +
                def string?(str)
         | 
| 146 | 
            +
                  str.is_a?(String)
         | 
| 147 | 
            +
                end
         | 
| 166 148 |  | 
| 167 149 | 
             
                # Converts a mixed-case string expression to a lowercase, underscore
         | 
| 168 150 | 
             
                # separated string.
         | 
| @@ -172,29 +154,20 @@ module SleepingKingStudios::Tools | |
| 172 154 | 
             
                # @return [String] The converted string.
         | 
| 173 155 | 
             
                #
         | 
| 174 156 | 
             
                # @see ActiveSupport::Inflector#underscore.
         | 
| 175 | 
            -
                def underscore | 
| 157 | 
            +
                def underscore(str)
         | 
| 176 158 | 
             
                  str = require_string! str
         | 
| 177 159 |  | 
| 178 | 
            -
                  str | 
| 179 | 
            -
             | 
| 180 | 
            -
                  str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
         | 
| 181 | 
            -
                  str.tr!("-", "_")
         | 
| 182 | 
            -
                  str.downcase!
         | 
| 183 | 
            -
                  str
         | 
| 184 | 
            -
                end # method underscore
         | 
| 160 | 
            +
                  inflector.underscore(str)
         | 
| 161 | 
            +
                end
         | 
| 185 162 |  | 
| 186 163 | 
             
                private
         | 
| 187 164 |  | 
| 188 | 
            -
                def  | 
| 189 | 
            -
                  @plural_inflector ||= PluralInflector.new
         | 
| 190 | 
            -
                end # method plural_inflector
         | 
| 191 | 
            -
             | 
| 192 | 
            -
                def require_string! value
         | 
| 165 | 
            +
                def require_string!(value)
         | 
| 193 166 | 
             
                  return value if string?(value)
         | 
| 194 167 |  | 
| 195 168 | 
             
                  return value.to_s if value.is_a?(Symbol)
         | 
| 196 169 |  | 
| 197 170 | 
             
                  raise ArgumentError, 'argument must be a string', caller[1..-1]
         | 
| 198 | 
            -
                end | 
| 199 | 
            -
              end | 
| 200 | 
            -
            end | 
| 171 | 
            +
                end
         | 
| 172 | 
            +
              end
         | 
| 173 | 
            +
            end
         |