sleeping_king_studios-tools 0.7.1 → 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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +32 -3
- data/DEVELOPMENT.md +5 -7
- data/README.md +3 -64
- data/lib/sleeping_king_studios/tools.rb +12 -6
- data/lib/sleeping_king_studios/tools/all.rb +8 -3
- data/lib/sleeping_king_studios/tools/array_tools.rb +83 -58
- data/lib/sleeping_king_studios/tools/base.rb +18 -0
- data/lib/sleeping_king_studios/tools/core_tools.rb +68 -22
- data/lib/sleeping_king_studios/tools/enumerable_tools.rb +6 -3
- data/lib/sleeping_king_studios/tools/hash_tools.rb +59 -47
- data/lib/sleeping_king_studios/tools/integer_tools.rb +97 -55
- data/lib/sleeping_king_studios/tools/object_tools.rb +67 -50
- data/lib/sleeping_king_studios/tools/string_tools.rb +81 -63
- data/lib/sleeping_king_studios/tools/toolbelt.rb +46 -22
- data/lib/sleeping_king_studios/tools/toolbox.rb +2 -2
- data/lib/sleeping_king_studios/tools/toolbox/configuration.rb +197 -122
- data/lib/sleeping_king_studios/tools/toolbox/constant_map.rb +24 -51
- data/lib/sleeping_king_studios/tools/toolbox/delegator.rb +50 -29
- data/lib/sleeping_king_studios/tools/toolbox/inflector.rb +130 -0
- data/lib/sleeping_king_studios/tools/toolbox/inflector/rules.rb +171 -0
- data/lib/sleeping_king_studios/tools/toolbox/mixin.rb +10 -10
- data/lib/sleeping_king_studios/tools/toolbox/semantic_version.rb +15 -14
- data/lib/sleeping_king_studios/tools/version.rb +6 -8
- metadata +84 -26
- data/lib/sleeping_king_studios/tools/semantic_version.rb +0 -15
- 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
|
-
#
|
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
|
-
|
8
|
-
|
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
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
57
|
+
end
|
45
58
|
|
46
59
|
context.binding
|
47
|
-
end
|
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
|
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
|
70
|
+
end
|
58
71
|
else
|
59
72
|
Kernel.require file_pattern
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
|
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
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'sleeping_king_studios/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
|
11
|
+
end
|
@@ -1,48 +1,58 @@
|
|
1
|
-
#
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
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
|
28
|
-
end
|
29
|
-
|
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
|
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
|
44
|
-
end
|
45
|
-
|
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
|
63
|
+
def deep_dup(hsh)
|
54
64
|
require_hash! hsh
|
55
65
|
|
56
|
-
hsh.each.with_object(
|
66
|
+
hsh.each.with_object({}) do |(key, value), copy|
|
57
67
|
copy[ObjectTools.deep_dup key] = ObjectTools.deep_dup(value)
|
58
|
-
end
|
59
|
-
end
|
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
|
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
|
74
|
-
end
|
83
|
+
end
|
84
|
+
end
|
75
85
|
|
76
|
-
def generate_binding
|
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
|
83
|
-
end
|
84
|
-
end
|
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?
|
92
|
-
return true if Hash
|
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
|
106
|
+
end
|
97
107
|
|
98
108
|
true
|
99
|
-
end
|
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?
|
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
|
-
|
114
|
-
|
123
|
+
unless ObjectTools.immutable?(key) && ObjectTools.immutable?(value)
|
124
|
+
return false
|
125
|
+
end
|
126
|
+
end
|
115
127
|
|
116
128
|
true
|
117
|
-
end
|
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?
|
138
|
+
def mutable?(hsh)
|
127
139
|
!immutable?(hsh)
|
128
|
-
end
|
140
|
+
end
|
129
141
|
|
130
142
|
private
|
131
143
|
|
132
|
-
def convert_value_to_stringified_hash
|
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
|
140
|
-
end
|
151
|
+
end
|
152
|
+
end
|
141
153
|
|
142
|
-
def convert_value_to_symbolic_hash
|
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
|
150
|
-
end
|
161
|
+
end
|
162
|
+
end
|
151
163
|
|
152
|
-
def require_hash!
|
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
|
157
|
-
end
|
158
|
-
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -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
|