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.
- 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
@@ -1,16 +1,33 @@
|
|
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
|
+
:mutable?,
|
21
|
+
:object?,
|
22
|
+
:try
|
23
|
+
|
24
|
+
alias metaclass eigenclass
|
25
|
+
end
|
9
26
|
|
10
27
|
# Takes a proc or lambda and invokes it with the given object as
|
11
28
|
# receiver, with any additional arguments or block provided.
|
12
29
|
#
|
13
|
-
# @param [Object]
|
30
|
+
# @param [Object] receiver The receiver. The proc will be called in the
|
14
31
|
# context of this object.
|
15
32
|
# @param [Proc] proc The proc or lambda to call.
|
16
33
|
# @param [Array] args Optional. Additional arguments to pass in to the
|
@@ -20,20 +37,16 @@ module SleepingKingStudios::Tools
|
|
20
37
|
#
|
21
38
|
# @return The result of calling the proc or lambda with the given
|
22
39
|
# receiver and any additional arguments or block.
|
23
|
-
def apply
|
24
|
-
return
|
40
|
+
def apply(receiver, proc, *args, &block)
|
41
|
+
return receiver.instance_exec(*args, &proc) unless block_given?
|
25
42
|
|
26
|
-
|
43
|
+
method_name =
|
44
|
+
Kernel.format(TEMPORARY_METHOD_NAME, Thread.current.object_id)
|
27
45
|
|
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)
|
46
|
+
with_temporary_method(receiver, method_name, proc) do
|
47
|
+
receiver.send(method_name, *args, &block)
|
35
48
|
end
|
36
|
-
end
|
49
|
+
end
|
37
50
|
|
38
51
|
# Creates a deep copy of the object. If the object is an Array, returns a
|
39
52
|
# new Array with deep copies of each array item. If the object is a Hash,
|
@@ -43,7 +56,7 @@ module SleepingKingStudios::Tools
|
|
43
56
|
# @param [Object] obj The object to copy.
|
44
57
|
#
|
45
58
|
# @return The copy of the object.
|
46
|
-
def deep_dup
|
59
|
+
def deep_dup(obj)
|
47
60
|
case obj
|
48
61
|
when FalseClass, Integer, Float, NilClass, Symbol, TrueClass
|
49
62
|
obj
|
@@ -53,8 +66,8 @@ module SleepingKingStudios::Tools
|
|
53
66
|
HashTools.deep_dup obj
|
54
67
|
else
|
55
68
|
obj.respond_to?(:deep_dup) ? obj.deep_dup : obj.dup
|
56
|
-
end
|
57
|
-
end
|
69
|
+
end
|
70
|
+
end
|
58
71
|
|
59
72
|
# Performs a deep freeze of the object. If the object is an Array, freezes
|
60
73
|
# the array and performs a deep freeze on each array item. If the object is
|
@@ -62,7 +75,7 @@ module SleepingKingStudios::Tools
|
|
62
75
|
# value. Otherwise, calls Object#freeze.
|
63
76
|
#
|
64
77
|
# @param [Object] obj The object to freeze.
|
65
|
-
def deep_freeze
|
78
|
+
def deep_freeze(obj)
|
66
79
|
case obj
|
67
80
|
when FalseClass, Integer, Float, NilClass, Symbol, TrueClass
|
68
81
|
# Object is inherently immutable; do nothing here.
|
@@ -72,8 +85,8 @@ module SleepingKingStudios::Tools
|
|
72
85
|
HashTools.deep_freeze obj
|
73
86
|
else
|
74
87
|
obj.respond_to?(:deep_freeze) ? obj.deep_freeze : obj.freeze
|
75
|
-
end
|
76
|
-
end
|
88
|
+
end
|
89
|
+
end
|
77
90
|
|
78
91
|
# Accesses deeply nested attributes by calling the first named method on the
|
79
92
|
# given object, and each subsequent method on the result of the previous
|
@@ -85,21 +98,21 @@ module SleepingKingStudios::Tools
|
|
85
98
|
#
|
86
99
|
# @return [Object] The result of the last method call, or nil if the last
|
87
100
|
# object does not respond to the last method.
|
88
|
-
def dig
|
101
|
+
def dig(object, *method_names)
|
89
102
|
method_names.reduce(object) do |memo, method_name|
|
90
103
|
memo.respond_to?(method_name) ? memo.send(method_name) : nil
|
91
|
-
end
|
92
|
-
end
|
104
|
+
end
|
105
|
+
end
|
93
106
|
|
94
107
|
# Returns the object's eigenclass.
|
95
108
|
#
|
96
109
|
# @param [Object] object The object for which an eigenclass is required.
|
97
110
|
#
|
98
111
|
# @return [Class] The object's eigenclass.
|
99
|
-
def eigenclass
|
100
|
-
|
101
|
-
end
|
102
|
-
|
112
|
+
def eigenclass(object)
|
113
|
+
object.singleton_class
|
114
|
+
end
|
115
|
+
alias metaclass eigenclass
|
103
116
|
|
104
117
|
# Returns true if the object is immutable. Values of nil, false, and true
|
105
118
|
# are always immutable, as are instances of Numeric and Symbol. Arrays are
|
@@ -110,7 +123,7 @@ module SleepingKingStudios::Tools
|
|
110
123
|
# @param obj [Object] The object to test.
|
111
124
|
#
|
112
125
|
# @return [Boolean] True if the object is immutable, otherwise false.
|
113
|
-
def immutable?
|
126
|
+
def immutable?(obj)
|
114
127
|
case obj
|
115
128
|
when NilClass, FalseClass, TrueClass, Numeric, Symbol
|
116
129
|
true
|
@@ -120,8 +133,8 @@ module SleepingKingStudios::Tools
|
|
120
133
|
HashTools.immutable? obj
|
121
134
|
else
|
122
135
|
obj.frozen?
|
123
|
-
end
|
124
|
-
end
|
136
|
+
end
|
137
|
+
end
|
125
138
|
|
126
139
|
# Returns true if the object is mutable.
|
127
140
|
#
|
@@ -130,9 +143,9 @@ module SleepingKingStudios::Tools
|
|
130
143
|
# @return [Boolean] True if the object is mutable, otherwise false.
|
131
144
|
#
|
132
145
|
# @see #immutable?
|
133
|
-
def mutable?
|
146
|
+
def mutable?(obj)
|
134
147
|
!immutable?(obj)
|
135
|
-
end
|
148
|
+
end
|
136
149
|
|
137
150
|
# Returns true if the object is an Object. This should return true only for
|
138
151
|
# objects that have an alternate inheritance chain from BasicObject, such as
|
@@ -141,9 +154,9 @@ module SleepingKingStudios::Tools
|
|
141
154
|
# @param obj [Object] The object to test.
|
142
155
|
#
|
143
156
|
# @return [Boolean] True if the object is an Object, otherwise false.
|
144
|
-
def object?
|
145
|
-
Object
|
146
|
-
end
|
157
|
+
def object?(obj)
|
158
|
+
Object.instance_method(:is_a?).bind(obj).call(Object)
|
159
|
+
end
|
147
160
|
|
148
161
|
# As #send, but returns nil if the object does not respond to the method.
|
149
162
|
#
|
@@ -151,23 +164,27 @@ module SleepingKingStudios::Tools
|
|
151
164
|
# @param [String, Symbol] method_name The name of the method to call.
|
152
165
|
# @param [Array] args The arguments to the message.
|
153
166
|
#
|
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.
|
167
|
+
# @see ActiveSupport::CoreExt::Object#try.
|
168
|
+
def try(object, method_name, *args)
|
163
169
|
return object.try(method_name, *args) if object.respond_to?(:try)
|
164
170
|
|
171
|
+
return nil unless object.respond_to?(method_name)
|
172
|
+
|
165
173
|
object.send method_name, *args
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def with_temporary_method(receiver, method_name, proc)
|
179
|
+
metaclass = class << receiver; self; end
|
180
|
+
metaclass.send :define_method, method_name, &proc
|
181
|
+
|
182
|
+
yield
|
183
|
+
ensure
|
184
|
+
metaclass.send :remove_method, method_name
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
171
188
|
|
172
189
|
require 'sleeping_king_studios/tools/array_tools'
|
173
190
|
require 'sleeping_king_studios/tools/hash_tools'
|
@@ -1,14 +1,35 @@
|
|
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
|
+
def initialize(inflector: nil)
|
28
|
+
@inflector =
|
29
|
+
inflector || SleepingKingStudios::Tools::Toolbox::Inflector.new
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :inflector
|
12
33
|
|
13
34
|
# Converts a lowercase, underscore-separated string to CamelCase.
|
14
35
|
#
|
@@ -17,13 +38,11 @@ module SleepingKingStudios::Tools
|
|
17
38
|
# @return [String] The converted string.
|
18
39
|
#
|
19
40
|
# @see ActiveSupport::Inflector#camelize.
|
20
|
-
def camelize
|
41
|
+
def camelize(str)
|
21
42
|
str = require_string! str
|
22
43
|
|
23
|
-
str
|
24
|
-
|
25
|
-
str
|
26
|
-
end # method camelize
|
44
|
+
inflector.camelize(str)
|
45
|
+
end
|
27
46
|
|
28
47
|
# Performs multiple string tools operations in sequence, starting with the
|
29
48
|
# given string and passing the result of each operation to the next.
|
@@ -32,31 +51,39 @@ module SleepingKingStudios::Tools
|
|
32
51
|
# @param commands [Array<String, Symbol>] The string operations to apply.
|
33
52
|
#
|
34
53
|
# @return [String] The processed string.
|
35
|
-
def chain
|
54
|
+
def chain(str, *commands)
|
36
55
|
str = require_string! str
|
37
56
|
|
38
57
|
commands.reduce(str) { |memo, command| send(command, memo) }
|
39
|
-
end
|
58
|
+
end
|
40
59
|
|
41
60
|
# (see PluralInflector#define_irregular_word)
|
42
|
-
def define_irregular_word
|
43
|
-
|
44
|
-
|
61
|
+
def define_irregular_word(singular, plural)
|
62
|
+
CoreTools.deprecate 'StringTools#define_irregular_word'
|
63
|
+
|
64
|
+
inflector.rules.define_irregular_word singular, plural
|
65
|
+
end
|
45
66
|
|
46
67
|
# (see PluralInflector#define_plural_rule)
|
47
|
-
def define_plural_rule
|
48
|
-
|
49
|
-
|
68
|
+
def define_plural_rule(match, replace)
|
69
|
+
CoreTools.deprecate 'StringTools#define_plural_rule'
|
70
|
+
|
71
|
+
inflector.rules.define_plural_rule match, replace
|
72
|
+
end
|
50
73
|
|
51
74
|
# (see PluralInflector#define_singular_rule)
|
52
|
-
def define_singular_rule
|
53
|
-
|
54
|
-
|
75
|
+
def define_singular_rule(match, replace)
|
76
|
+
CoreTools.deprecate 'StringTools#define_singular_rule'
|
77
|
+
|
78
|
+
inflector.rules.define_singular_rule match, replace
|
79
|
+
end
|
55
80
|
|
56
81
|
# (see PluralInflector#define_uncountable_word)
|
57
|
-
def define_uncountable_word
|
58
|
-
|
59
|
-
|
82
|
+
def define_uncountable_word(word)
|
83
|
+
CoreTools.deprecate 'StringTools#define_uncountable_word'
|
84
|
+
|
85
|
+
inflector.rules.define_uncountable_word word
|
86
|
+
end
|
60
87
|
|
61
88
|
# Adds the specified number of spaces to the start of each line of the
|
62
89
|
# string. Defaults to 2 spaces.
|
@@ -65,12 +92,12 @@ module SleepingKingStudios::Tools
|
|
65
92
|
# @param count [Integer] The number of spaces to add.
|
66
93
|
#
|
67
94
|
# @return [String] The indented string.
|
68
|
-
def indent
|
95
|
+
def indent(str, count = 2)
|
69
96
|
str = require_string! str
|
70
|
-
pre =
|
97
|
+
pre = ' ' * count
|
71
98
|
|
72
99
|
map_lines(str) { |line| "#{pre}#{line}" }
|
73
|
-
end
|
100
|
+
end
|
74
101
|
|
75
102
|
# Yields each line of the string to the provided block and combines the
|
76
103
|
# results into a new multiline string.
|
@@ -81,23 +108,23 @@ module SleepingKingStudios::Tools
|
|
81
108
|
# @yieldparam index [Integer] The index of the current line.
|
82
109
|
#
|
83
110
|
# @return [String] The mapped string.
|
84
|
-
def map_lines
|
111
|
+
def map_lines(str)
|
85
112
|
str = require_string! str
|
86
113
|
|
87
|
-
str.each_line.with_index.reduce('') do |memo, (line, index)|
|
114
|
+
str.each_line.with_index.reduce(+'') do |memo, (line, index)|
|
88
115
|
memo << yield(line, index)
|
89
|
-
end
|
90
|
-
end
|
116
|
+
end
|
117
|
+
end
|
91
118
|
|
92
119
|
# Determines whether or not the given word is in plural form. If calling
|
93
120
|
# #pluralize(word) is equal to word, the word is considered plural.
|
94
121
|
#
|
95
122
|
# @return [Boolean] True if the word is in plural form, otherwise false.
|
96
|
-
def plural?
|
123
|
+
def plural?(word)
|
97
124
|
word = require_string!(word)
|
98
125
|
|
99
126
|
word == pluralize(word)
|
100
|
-
end
|
127
|
+
end
|
101
128
|
|
102
129
|
# @overload pluralize(str)
|
103
130
|
# Takes a word in singular form and returns the plural form, based on the
|
@@ -125,44 +152,44 @@ module SleepingKingStudios::Tools
|
|
125
152
|
#
|
126
153
|
# @return [String] The single form if count == 1; otherwise the plural
|
127
154
|
# form.
|
128
|
-
def pluralize
|
155
|
+
def pluralize(*args)
|
129
156
|
if args.count == 3
|
130
157
|
CoreTools.deprecate 'StringTools#pluralize with 3 arguments',
|
131
|
-
:
|
158
|
+
message: 'Use IntegerTools#pluralize instead.'
|
132
159
|
|
133
160
|
return IntegerTools.pluralize(*args)
|
134
|
-
end
|
161
|
+
end
|
135
162
|
|
136
163
|
str = require_string! args.first
|
137
164
|
|
138
|
-
|
139
|
-
end
|
165
|
+
inflector.pluralize str
|
166
|
+
end
|
140
167
|
|
141
168
|
# Determines whether or not the given word is in singular form. If calling
|
142
169
|
# #singularize(word) is equal to word, the word is considered singular.
|
143
170
|
#
|
144
171
|
# @return [Boolean] True if the word is in singular form, otherwise false.
|
145
|
-
def singular?
|
172
|
+
def singular?(word)
|
146
173
|
word = require_string!(word)
|
147
174
|
|
148
175
|
word == singularize(word)
|
149
|
-
end
|
176
|
+
end
|
150
177
|
|
151
178
|
# (see PluralInflector#singularize)
|
152
|
-
def singularize
|
179
|
+
def singularize(str)
|
153
180
|
require_string! str
|
154
181
|
|
155
|
-
|
156
|
-
end
|
182
|
+
inflector.singularize str
|
183
|
+
end
|
157
184
|
|
158
185
|
# Returns true if the object is a String.
|
159
186
|
#
|
160
187
|
# @param str [Object] The object to test.
|
161
188
|
#
|
162
189
|
# @return [Boolean] True if the object is a String, otherwise false.
|
163
|
-
def string?
|
164
|
-
String
|
165
|
-
end
|
190
|
+
def string?(str)
|
191
|
+
str.is_a?(String)
|
192
|
+
end
|
166
193
|
|
167
194
|
# Converts a mixed-case string expression to a lowercase, underscore
|
168
195
|
# separated string.
|
@@ -172,29 +199,20 @@ module SleepingKingStudios::Tools
|
|
172
199
|
# @return [String] The converted string.
|
173
200
|
#
|
174
201
|
# @see ActiveSupport::Inflector#underscore.
|
175
|
-
def underscore
|
202
|
+
def underscore(str)
|
176
203
|
str = require_string! str
|
177
204
|
|
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
|
205
|
+
inflector.underscore(str)
|
206
|
+
end
|
185
207
|
|
186
208
|
private
|
187
209
|
|
188
|
-
def
|
189
|
-
@plural_inflector ||= PluralInflector.new
|
190
|
-
end # method plural_inflector
|
191
|
-
|
192
|
-
def require_string! value
|
210
|
+
def require_string!(value)
|
193
211
|
return value if string?(value)
|
194
212
|
|
195
213
|
return value.to_s if value.is_a?(Symbol)
|
196
214
|
|
197
215
|
raise ArgumentError, 'argument must be a string', caller[1..-1]
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|