sleeping_king_studios-tools 0.7.0.beta.0 → 0.8.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 (27) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +37 -5
  3. data/DEVELOPMENT.md +5 -7
  4. data/README.md +27 -64
  5. data/lib/sleeping_king_studios/tools.rb +12 -6
  6. data/lib/sleeping_king_studios/tools/all.rb +8 -3
  7. data/lib/sleeping_king_studios/tools/array_tools.rb +83 -58
  8. data/lib/sleeping_king_studios/tools/base.rb +18 -0
  9. data/lib/sleeping_king_studios/tools/core_tools.rb +76 -17
  10. data/lib/sleeping_king_studios/tools/enumerable_tools.rb +6 -3
  11. data/lib/sleeping_king_studios/tools/hash_tools.rb +65 -42
  12. data/lib/sleeping_king_studios/tools/integer_tools.rb +97 -55
  13. data/lib/sleeping_king_studios/tools/object_tools.rb +67 -50
  14. data/lib/sleeping_king_studios/tools/string_tools.rb +81 -63
  15. data/lib/sleeping_king_studios/tools/toolbelt.rb +46 -22
  16. data/lib/sleeping_king_studios/tools/toolbox.rb +2 -2
  17. data/lib/sleeping_king_studios/tools/toolbox/configuration.rb +206 -118
  18. data/lib/sleeping_king_studios/tools/toolbox/constant_map.rb +24 -51
  19. data/lib/sleeping_king_studios/tools/toolbox/delegator.rb +50 -29
  20. data/lib/sleeping_king_studios/tools/toolbox/inflector.rb +130 -0
  21. data/lib/sleeping_king_studios/tools/toolbox/inflector/rules.rb +171 -0
  22. data/lib/sleeping_king_studios/tools/toolbox/mixin.rb +10 -10
  23. data/lib/sleeping_king_studios/tools/toolbox/semantic_version.rb +15 -14
  24. data/lib/sleeping_king_studios/tools/version.rb +7 -9
  25. metadata +86 -28
  26. data/lib/sleeping_king_studios/tools/semantic_version.rb +0 -15
  27. data/lib/sleeping_king_studios/tools/string_tools/plural_inflector.rb +0 -185
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ require 'sleeping_king_studios/tools'
6
+
7
+ module SleepingKingStudios::Tools
8
+ # Abstract base class for Tools classes.
9
+ class Base
10
+ class << self
11
+ extend Forwardable
12
+ end
13
+
14
+ def self.instance
15
+ @instance ||= new
16
+ end
17
+ end
18
+ end
@@ -1,11 +1,26 @@
1
- # lib/sleeping_king_studios/tools/core_tools.rb
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'sleeping_king_studios/tools'
4
4
 
5
5
  module SleepingKingStudios::Tools
6
6
  # Tools for working with an application or working environment.
7
- module CoreTools
8
- extend self
7
+ class CoreTools < Base
8
+ class DeprecationError < StandardError; end
9
+
10
+ class << self
11
+ def_delegators :instance,
12
+ :deprecate,
13
+ :empty_binding,
14
+ :require_each
15
+ end
16
+
17
+ def initialize(deprecation_strategy: nil)
18
+ @deprecation_strategy =
19
+ deprecation_strategy || ENV.fetch('DEPRECATION_STRATEGY', 'warn')
20
+ end
21
+
22
+ # @return [String] The current deprecation strategy.
23
+ attr_reader :deprecation_strategy
9
24
 
10
25
  # @overload deprecate(name, message: nil)
11
26
  # Prints a deprecation warning.
@@ -22,30 +37,74 @@ module SleepingKingStudios::Tools
22
37
  # @param format [String] The format string.
23
38
  # @param message [String] An optional message to print after the formatted
24
39
  # string. Defaults to nil.
25
- def deprecate *args, format: nil, message: nil
26
- format ||= "[WARNING] %s has been deprecated."
40
+ def deprecate(*args, format: nil, message: nil)
41
+ send(
42
+ :"deprecate_as_#{deprecation_strategy}",
43
+ *args,
44
+ format: format,
45
+ message: message
46
+ )
47
+ end
27
48
 
28
- str = format % args
29
- str << ' ' << message if message
49
+ # Generates an empty Binding object with a BasicObject as the receiver.
50
+ #
51
+ # @return [Binding] The empty binding object.
52
+ def empty_binding
53
+ context = Object.new
30
54
 
31
- str << "\n called from #{caller[1]}"
55
+ def context.binding
56
+ Kernel.instance_method(:binding).bind(self).call
57
+ end
32
58
 
33
- Kernel.warn str
34
- end # method deprecate
59
+ context.binding
60
+ end
35
61
 
36
62
  # Expands each file pattern and requires each file.
37
63
  #
38
64
  # @param file_patterns [Array] The files to require.
39
- def require_each *file_patterns
65
+ def require_each(*file_patterns)
40
66
  file_patterns.each do |file_pattern|
41
67
  if file_pattern.include?('*')
42
68
  Dir[file_pattern].each do |file_name|
43
69
  Kernel.require file_name
44
- end # each
70
+ end
45
71
  else
46
72
  Kernel.require file_pattern
47
- end # unless
48
- end # each
49
- end # method require_each
50
- end # module
51
- end # module
73
+ end
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def deprecate_as_ignore(*_args, **_kwargs); end
80
+
81
+ def deprecate_as_raise(*args, format: nil, message: nil)
82
+ format ||= '%s has been deprecated.'
83
+
84
+ str = format % args
85
+ str << ' ' << message if message
86
+
87
+ raise DeprecationError, str, caller(2..-1)
88
+ end
89
+
90
+ def deprecate_as_warn(*args, format: nil, message: nil)
91
+ format ||= '[WARNING] %s has been deprecated.'
92
+
93
+ str = format % args
94
+ str << ' ' << message if message
95
+
96
+ str << "\n called from #{external_caller}"
97
+
98
+ Kernel.warn str
99
+ end
100
+
101
+ def external_caller
102
+ caller.find do |line|
103
+ !(
104
+ line.include?('forwardable.rb') ||
105
+ line.include?('sleeping_king_studios-tools')
106
+ )
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,8 +1,11 @@
1
- # lib/sleeping_king_studios/tools/enumerable_tools.rb
1
+ # frozen_string_literal: true
2
2
 
3
- require 'sleeping_king_studios/tools/array_tools'
3
+ require 'sleeping_king_studios/tools'
4
+
5
+ SleepingKingStudios::Tools::CoreTools
6
+ .deprecate('SleepingKingStudios::Tools::EnumerableTools')
4
7
 
5
8
  module SleepingKingStudios::Tools
6
9
  # Alias for ArrayTools to ensure backward compatibility.
7
10
  EnumerableTools = ArrayTools
8
- end # module
11
+ end
@@ -1,47 +1,58 @@
1
- # lib/sleeping_king_studios/tools/hash_tools.rb
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'sleeping_king_studios/tools'
4
- require 'sleeping_king_studios/tools/array_tools'
5
- require 'sleeping_king_studios/tools/object_tools'
6
4
 
7
5
  module SleepingKingStudios::Tools
8
6
  # Tools for working with hash-like enumerable objects.
9
- module HashTools
10
- extend self
11
-
12
- HASH_METHODS = [:[], :count, :each, :each_key, :each_pair].freeze
7
+ class HashTools < SleepingKingStudios::Tools::Base
8
+ HASH_METHODS = %i[[] count each each_key each_pair].freeze
9
+
10
+ class << self
11
+ def_delegators :instance,
12
+ :convert_keys_to_strings,
13
+ :convert_keys_to_symbols,
14
+ :deep_dup,
15
+ :deep_freeze,
16
+ :generate_binding,
17
+ :hash?,
18
+ :immutable?,
19
+ :mutable?
20
+
21
+ alias stringify_keys convert_keys_to_strings
22
+ alias symbolize_keys convert_keys_to_symbols
23
+ end
13
24
 
14
25
  # Returns a copy of the hash with the keys converted to strings.
15
26
  #
16
27
  # @param [Hash] hsh The hash to convert.
17
28
  #
18
29
  # @return [Hash] The converted copy of the hash.
19
- def convert_keys_to_strings hsh
30
+ def convert_keys_to_strings(hsh)
20
31
  require_hash! hsh
21
32
 
22
33
  hsh.each.with_object({}) do |(key, value), cpy|
23
34
  sym = key.to_s
24
35
 
25
36
  cpy[sym] = convert_value_to_stringified_hash(value)
26
- end # each
27
- end # method convert_keys_to_strings
28
- alias_method :stringify_keys, :convert_keys_to_strings
37
+ end
38
+ end
39
+ alias stringify_keys convert_keys_to_strings
29
40
 
30
41
  # Returns a copy of the hash with the keys converted to symbols.
31
42
  #
32
43
  # @param [Hash] hsh The hash to convert.
33
44
  #
34
45
  # @return [Hash] The converted copy of the hash.
35
- def convert_keys_to_symbols hsh
46
+ def convert_keys_to_symbols(hsh)
36
47
  require_hash! hsh
37
48
 
38
49
  hsh.each.with_object({}) do |(key, value), cpy|
39
50
  sym = key.to_s.intern
40
51
 
41
52
  cpy[sym] = convert_value_to_symbolic_hash(value)
42
- end # each
43
- end # method convert_keys_to_symbols
44
- alias_method :symbolize_keys, :convert_keys_to_symbols
53
+ end
54
+ end
55
+ alias symbolize_keys convert_keys_to_symbols
45
56
 
46
57
  # Creates a deep copy of the object by returning a new Hash with deep
47
58
  # copies of each key and value.
@@ -49,19 +60,19 @@ module SleepingKingStudios::Tools
49
60
  # @param [Hash<Object>] hsh The hash to copy.
50
61
  #
51
62
  # @return [Hash] The copy of the hash.
52
- def deep_dup hsh
63
+ def deep_dup(hsh)
53
64
  require_hash! hsh
54
65
 
55
- hsh.each.with_object(Hash.new) do |(key, value), copy|
66
+ hsh.each.with_object({}) do |(key, value), copy|
56
67
  copy[ObjectTools.deep_dup key] = ObjectTools.deep_dup(value)
57
- end # each
58
- end # method deep_dup
68
+ end
69
+ end
59
70
 
60
71
  # Freezes the hash and performs a deep freeze on each hash key and
61
72
  # value.
62
73
  #
63
74
  # @param [Hash] hsh The object to freeze.
64
- def deep_freeze hsh
75
+ def deep_freeze(hsh)
65
76
  require_hash! hsh
66
77
 
67
78
  hsh.freeze
@@ -69,23 +80,33 @@ module SleepingKingStudios::Tools
69
80
  hsh.each do |key, value|
70
81
  ObjectTools.deep_freeze key
71
82
  ObjectTools.deep_freeze value
72
- end # each
73
- end # method deep_freeze
83
+ end
84
+ end
85
+
86
+ def generate_binding(hsh)
87
+ require_hash! hsh
88
+
89
+ CoreTools.empty_binding.tap do |binding|
90
+ hsh.each do |key, value|
91
+ binding.local_variable_set key, value
92
+ end
93
+ end
94
+ end
74
95
 
75
96
  # Returns true if the object is or appears to be a Hash.
76
97
  #
77
98
  # @param hsh [Object] The object to test.
78
99
  #
79
100
  # @return [Boolean] True if the object is a Hash, otherwise false.
80
- def hash? hsh
81
- return true if Hash === hsh
101
+ def hash?(hsh)
102
+ return true if hsh.is_a?(Hash)
82
103
 
83
104
  HASH_METHODS.each do |method_name|
84
105
  return false unless hsh.respond_to?(method_name)
85
- end # each
106
+ end
86
107
 
87
108
  true
88
- end # method hash?
109
+ end
89
110
 
90
111
  # Returns true if the hash is immutable, i.e. if the hash is frozen and each
91
112
  # hash key and hash value are immutable.
@@ -93,17 +114,19 @@ module SleepingKingStudios::Tools
93
114
  # @param hsh [Hash] The hash to test.
94
115
  #
95
116
  # @return [Boolean] True if the hash is immutable, otherwise false.
96
- def immutable? hsh
117
+ def immutable?(hsh)
97
118
  require_hash! hsh
98
119
 
99
120
  return false unless hsh.frozen?
100
121
 
101
122
  hsh.each do |key, value|
102
- return false unless ObjectTools.immutable?(key) && ObjectTools.immutable?(value)
103
- end # each
123
+ unless ObjectTools.immutable?(key) && ObjectTools.immutable?(value)
124
+ return false
125
+ end
126
+ end
104
127
 
105
128
  true
106
- end # method immutable
129
+ end
107
130
 
108
131
  # Returns true if the hash is mutable.
109
132
  #
@@ -112,36 +135,36 @@ module SleepingKingStudios::Tools
112
135
  # @return [Boolean] True if the hash is mutable, otherwise false.
113
136
  #
114
137
  # @see #immutable?
115
- def mutable? hsh
138
+ def mutable?(hsh)
116
139
  !immutable?(hsh)
117
- end # method mutable?
140
+ end
118
141
 
119
142
  private
120
143
 
121
- def convert_value_to_stringified_hash value
144
+ def convert_value_to_stringified_hash(value)
122
145
  if hash?(value)
123
146
  convert_keys_to_strings(value)
124
147
  elsif ArrayTools.array?(value)
125
148
  value.map { |item| convert_value_to_stringified_hash(item) }
126
149
  else
127
150
  value
128
- end # if-else
129
- end # method convert_value_to_stringified_hash
151
+ end
152
+ end
130
153
 
131
- def convert_value_to_symbolic_hash value
154
+ def convert_value_to_symbolic_hash(value)
132
155
  if hash?(value)
133
156
  convert_keys_to_symbols(value)
134
157
  elsif ArrayTools.array?(value)
135
158
  value.map { |item| convert_value_to_symbolic_hash(item) }
136
159
  else
137
160
  value
138
- end # if-else
139
- end # method convert_value_to_symbolic_hash
161
+ end
162
+ end
140
163
 
141
- def require_hash! value
164
+ def require_hash!(value)
142
165
  return if hash?(value)
143
166
 
144
167
  raise ArgumentError, 'argument must be a hash', caller[1..-1]
145
- end # method require_array
146
- end # module
147
- end # module
168
+ end
169
+ end
170
+ end
@@ -1,19 +1,63 @@
1
- # lib/sleeping_king_studios/tools/integer_tools.rb
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
- module IntegerTools
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 integer, base: 10
42
- digits(integer.abs, :base => base).count
43
- end # method count_digits
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 integer, base: 10
110
+ def digits(integer, base: 10)
67
111
  integer.to_s(base).split('')
68
- end # method digits
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? int
76
- Integer === int
77
- end # method integer?
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 count, single, plural = nil
136
+ def pluralize(count, single, plural = nil)
93
137
  plural ||= StringTools.pluralize(single)
94
138
 
95
- 1 == count ? single : plural
96
- end # method pluralize
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 integer, additive: false
118
- # Validate input value.
119
- unless (ROMANIZE_MIN..ROMANIZE_MAX).include? integer
120
- raise RangeError.new "integer to romanize must be within range #{ROMANIZE_MIN} to #{ROMANIZE_MAX}"
121
- end # unless
122
-
123
- # Define conversion rules.
124
- rules = [
125
- '',
126
- '%one',
127
- '%one%one',
128
- '%one%one%one',
129
- additive ? '%one%one%one%one' : '%one%five',
130
- '%five',
131
- '%five%one',
132
- '%five%one%one',
133
- '%five%one%one%one',
134
- additive ? '%five%one%one%one%one' : '%one%ten',
135
- '%ten'
136
- ] # end array
137
-
138
- # Define numeral values.
139
- numerals = [
140
- %w(I V X),
141
- %w(X L C),
142
- %w(C D M),
143
- ['M', 'MMM', '']
144
- ] # end array numerals
145
-
146
- # Generate string representation.
147
- digits(integer).reverse.map.with_index do |digit, index|
148
- rules[digit.to_i]
149
- .gsub('%one', numerals[index][0])
150
- .gsub('%five', numerals[index][1])
151
- .gsub('%ten', numerals[index][2])
152
- end.reverse.join ''
153
- end # method romanize
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