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.
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
@@ -1,16 +1,33 @@
1
- # lib/sleeping_king_studios/tools/object_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
  # Low-level tools for working with objects.
7
- module ObjectTools
8
- extend self
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] base The receiver. The proc will be called in the
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 base, proc, *args, &block
24
- return base.instance_exec *args, &proc unless block_given?
40
+ def apply(receiver, proc, *args, &block)
41
+ return receiver.instance_exec(*args, &proc) unless block_given?
25
42
 
26
- temporary_method_name = :__sleeping_king_studios_tools_object_tools_temporary_method_for_applying_proc__
43
+ method_name =
44
+ Kernel.format(TEMPORARY_METHOD_NAME, Thread.current.object_id)
27
45
 
28
- metaclass = class << base; self; end
29
- metaclass.send :define_method, temporary_method_name, &proc
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 # method apply
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 obj
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 # case
57
- end # method deep_dup
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 obj
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 # case
76
- end # method deep_freeze
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 object, *method_names
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 # reduce
92
- end # method object
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 object
100
- class << object; self; end
101
- end # method eigenclass
102
- alias_method :metaclass, :eigenclass
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? obj
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 # case
124
- end # method immutable?
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? obj
146
+ def mutable?(obj)
134
147
  !immutable?(obj)
135
- end # method mutable?
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? obj
145
- Object === obj
146
- end # method object?
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 active_support/core_ext/object/try.rb
155
- def try object, method_name, *args
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
- rescue NoMethodError => exception
167
- nil
168
- end # method try
169
- end # module
170
- end # module
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
- # lib/sleeping_king_studios/tools/string_tools.rb
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'sleeping_king_studios/tools'
4
- require 'sleeping_king_studios/tools/object_tools'
4
+ require 'sleeping_king_studios/tools/toolbox/inflector'
5
5
 
6
6
  module SleepingKingStudios::Tools
7
7
  # Tools for working with strings.
8
- module StringTools
9
- extend self
10
-
11
- autoload :PluralInflector, 'sleeping_king_studios/tools/string_tools/plural_inflector'
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 str
41
+ def camelize(str)
21
42
  str = require_string! str
22
43
 
23
- str = str.dup
24
- str.gsub!(/(\b|[_-])([a-z])/) { |match| $2.upcase }
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 str, *commands
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 # method chain
58
+ end
40
59
 
41
60
  # (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
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 match, replace
48
- plural_inflector.define_plural_rule match, replace
49
- end # method define_plural_rule
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 match, replace
53
- plural_inflector.define_singular_rule match, replace
54
- end # method define_singular_rule
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 word
58
- plural_inflector.define_uncountable_word word
59
- end # method define_uncountable_word
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 str, count = 2
95
+ def indent(str, count = 2)
69
96
  str = require_string! str
70
- pre = " " * count
97
+ pre = ' ' * count
71
98
 
72
99
  map_lines(str) { |line| "#{pre}#{line}" }
73
- end # method indent
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 str
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 # each_line
90
- end # method map_lines
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? word
123
+ def plural?(word)
97
124
  word = require_string!(word)
98
125
 
99
126
  word == pluralize(word)
100
- end # method plural?
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 *args
155
+ def pluralize(*args)
129
156
  if args.count == 3
130
157
  CoreTools.deprecate 'StringTools#pluralize with 3 arguments',
131
- :message => 'Use IntegerTools#pluralize instead.'
158
+ message: 'Use IntegerTools#pluralize instead.'
132
159
 
133
160
  return IntegerTools.pluralize(*args)
134
- end # if
161
+ end
135
162
 
136
163
  str = require_string! args.first
137
164
 
138
- plural_inflector.pluralize str
139
- end # method pluralize
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? word
172
+ def singular?(word)
146
173
  word = require_string!(word)
147
174
 
148
175
  word == singularize(word)
149
- end # method singular?
176
+ end
150
177
 
151
178
  # (see PluralInflector#singularize)
152
- def singularize str
179
+ def singularize(str)
153
180
  require_string! str
154
181
 
155
- plural_inflector.singularize str
156
- end # method singularize
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? str
164
- String === str
165
- end # method string?
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 str
202
+ def underscore(str)
176
203
  str = require_string! str
177
204
 
178
- str = str.dup
179
- str.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
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 plural_inflector
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 # method require_array
199
- end # module
200
- end # module
216
+ end
217
+ end
218
+ end