sleeping_king_studios-tools 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +32 -3
  3. data/DEVELOPMENT.md +5 -7
  4. data/README.md +3 -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 +68 -22
  10. data/lib/sleeping_king_studios/tools/enumerable_tools.rb +6 -3
  11. data/lib/sleeping_king_studios/tools/hash_tools.rb +59 -47
  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 +197 -122
  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 +6 -8
  25. metadata +84 -26
  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,16 +37,14 @@ 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."
27
-
28
- str = format % args
29
- str << ' ' << message if message
30
-
31
- str << "\n called from #{caller[1]}"
32
-
33
- Kernel.warn str
34
- end # method deprecate
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
35
48
 
36
49
  # Generates an empty Binding object with a BasicObject as the receiver.
37
50
  #
@@ -41,24 +54,57 @@ module SleepingKingStudios::Tools
41
54
 
42
55
  def context.binding
43
56
  Kernel.instance_method(:binding).bind(self).call
44
- end # singleton method binding
57
+ end
45
58
 
46
59
  context.binding
47
- end # method empty_binding
60
+ end
48
61
 
49
62
  # Expands each file pattern and requires each file.
50
63
  #
51
64
  # @param file_patterns [Array] The files to require.
52
- def require_each *file_patterns
65
+ def require_each(*file_patterns)
53
66
  file_patterns.each do |file_pattern|
54
67
  if file_pattern.include?('*')
55
68
  Dir[file_pattern].each do |file_name|
56
69
  Kernel.require file_name
57
- end # each
70
+ end
58
71
  else
59
72
  Kernel.require file_pattern
60
- end # unless
61
- end # each
62
- end # method require_each
63
- end # module
64
- 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,48 +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/core_tools'
6
- require 'sleeping_king_studios/tools/object_tools'
7
4
 
8
5
  module SleepingKingStudios::Tools
9
6
  # Tools for working with hash-like enumerable objects.
10
- module HashTools
11
- extend self
12
-
13
- 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
14
24
 
15
25
  # Returns a copy of the hash with the keys converted to strings.
16
26
  #
17
27
  # @param [Hash] hsh The hash to convert.
18
28
  #
19
29
  # @return [Hash] The converted copy of the hash.
20
- def convert_keys_to_strings hsh
30
+ def convert_keys_to_strings(hsh)
21
31
  require_hash! hsh
22
32
 
23
33
  hsh.each.with_object({}) do |(key, value), cpy|
24
34
  sym = key.to_s
25
35
 
26
36
  cpy[sym] = convert_value_to_stringified_hash(value)
27
- end # each
28
- end # method convert_keys_to_strings
29
- alias_method :stringify_keys, :convert_keys_to_strings
37
+ end
38
+ end
39
+ alias stringify_keys convert_keys_to_strings
30
40
 
31
41
  # Returns a copy of the hash with the keys converted to symbols.
32
42
  #
33
43
  # @param [Hash] hsh The hash to convert.
34
44
  #
35
45
  # @return [Hash] The converted copy of the hash.
36
- def convert_keys_to_symbols hsh
46
+ def convert_keys_to_symbols(hsh)
37
47
  require_hash! hsh
38
48
 
39
49
  hsh.each.with_object({}) do |(key, value), cpy|
40
50
  sym = key.to_s.intern
41
51
 
42
52
  cpy[sym] = convert_value_to_symbolic_hash(value)
43
- end # each
44
- end # method convert_keys_to_symbols
45
- alias_method :symbolize_keys, :convert_keys_to_symbols
53
+ end
54
+ end
55
+ alias symbolize_keys convert_keys_to_symbols
46
56
 
47
57
  # Creates a deep copy of the object by returning a new Hash with deep
48
58
  # copies of each key and value.
@@ -50,19 +60,19 @@ module SleepingKingStudios::Tools
50
60
  # @param [Hash<Object>] hsh The hash to copy.
51
61
  #
52
62
  # @return [Hash] The copy of the hash.
53
- def deep_dup hsh
63
+ def deep_dup(hsh)
54
64
  require_hash! hsh
55
65
 
56
- hsh.each.with_object(Hash.new) do |(key, value), copy|
66
+ hsh.each.with_object({}) do |(key, value), copy|
57
67
  copy[ObjectTools.deep_dup key] = ObjectTools.deep_dup(value)
58
- end # each
59
- end # method deep_dup
68
+ end
69
+ end
60
70
 
61
71
  # Freezes the hash and performs a deep freeze on each hash key and
62
72
  # value.
63
73
  #
64
74
  # @param [Hash] hsh The object to freeze.
65
- def deep_freeze hsh
75
+ def deep_freeze(hsh)
66
76
  require_hash! hsh
67
77
 
68
78
  hsh.freeze
@@ -70,33 +80,33 @@ module SleepingKingStudios::Tools
70
80
  hsh.each do |key, value|
71
81
  ObjectTools.deep_freeze key
72
82
  ObjectTools.deep_freeze value
73
- end # each
74
- end # method deep_freeze
83
+ end
84
+ end
75
85
 
76
- def generate_binding hsh
86
+ def generate_binding(hsh)
77
87
  require_hash! hsh
78
88
 
79
89
  CoreTools.empty_binding.tap do |binding|
80
90
  hsh.each do |key, value|
81
91
  binding.local_variable_set key, value
82
- end # each
83
- end # tap
84
- end # method generate_binding
92
+ end
93
+ end
94
+ end
85
95
 
86
96
  # Returns true if the object is or appears to be a Hash.
87
97
  #
88
98
  # @param hsh [Object] The object to test.
89
99
  #
90
100
  # @return [Boolean] True if the object is a Hash, otherwise false.
91
- def hash? hsh
92
- return true if Hash === hsh
101
+ def hash?(hsh)
102
+ return true if hsh.is_a?(Hash)
93
103
 
94
104
  HASH_METHODS.each do |method_name|
95
105
  return false unless hsh.respond_to?(method_name)
96
- end # each
106
+ end
97
107
 
98
108
  true
99
- end # method hash?
109
+ end
100
110
 
101
111
  # Returns true if the hash is immutable, i.e. if the hash is frozen and each
102
112
  # hash key and hash value are immutable.
@@ -104,17 +114,19 @@ module SleepingKingStudios::Tools
104
114
  # @param hsh [Hash] The hash to test.
105
115
  #
106
116
  # @return [Boolean] True if the hash is immutable, otherwise false.
107
- def immutable? hsh
117
+ def immutable?(hsh)
108
118
  require_hash! hsh
109
119
 
110
120
  return false unless hsh.frozen?
111
121
 
112
122
  hsh.each do |key, value|
113
- return false unless ObjectTools.immutable?(key) && ObjectTools.immutable?(value)
114
- end # each
123
+ unless ObjectTools.immutable?(key) && ObjectTools.immutable?(value)
124
+ return false
125
+ end
126
+ end
115
127
 
116
128
  true
117
- end # method immutable
129
+ end
118
130
 
119
131
  # Returns true if the hash is mutable.
120
132
  #
@@ -123,36 +135,36 @@ module SleepingKingStudios::Tools
123
135
  # @return [Boolean] True if the hash is mutable, otherwise false.
124
136
  #
125
137
  # @see #immutable?
126
- def mutable? hsh
138
+ def mutable?(hsh)
127
139
  !immutable?(hsh)
128
- end # method mutable?
140
+ end
129
141
 
130
142
  private
131
143
 
132
- def convert_value_to_stringified_hash value
144
+ def convert_value_to_stringified_hash(value)
133
145
  if hash?(value)
134
146
  convert_keys_to_strings(value)
135
147
  elsif ArrayTools.array?(value)
136
148
  value.map { |item| convert_value_to_stringified_hash(item) }
137
149
  else
138
150
  value
139
- end # if-else
140
- end # method convert_value_to_stringified_hash
151
+ end
152
+ end
141
153
 
142
- def convert_value_to_symbolic_hash value
154
+ def convert_value_to_symbolic_hash(value)
143
155
  if hash?(value)
144
156
  convert_keys_to_symbols(value)
145
157
  elsif ArrayTools.array?(value)
146
158
  value.map { |item| convert_value_to_symbolic_hash(item) }
147
159
  else
148
160
  value
149
- end # if-else
150
- end # method convert_value_to_symbolic_hash
161
+ end
162
+ end
151
163
 
152
- def require_hash! value
164
+ def require_hash!(value)
153
165
  return if hash?(value)
154
166
 
155
167
  raise ArgumentError, 'argument must be a hash', caller[1..-1]
156
- end # method require_array
157
- end # module
158
- 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