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
@@ -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