sleeping_king_studios-tools 0.4.0 → 0.5.0.rc.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -2
- data/DEVELOPMENT.md +5 -25
- data/README.md +307 -4
- data/lib/sleeping_king_studios/tools/array_tools.rb +67 -3
- data/lib/sleeping_king_studios/tools/core_tools.rb +13 -0
- data/lib/sleeping_king_studios/tools/hash_tools.rb +57 -0
- data/lib/sleeping_king_studios/tools/integer_tools.rb +9 -0
- data/lib/sleeping_king_studios/tools/object_tools.rb +73 -2
- data/lib/sleeping_king_studios/tools/semantic_version.rb +9 -102
- data/lib/sleeping_king_studios/tools/string_tools.rb +27 -1
- data/lib/sleeping_king_studios/tools/toolbox/delegator.rb +175 -0
- data/lib/sleeping_king_studios/tools/toolbox/semantic_version.rb +108 -0
- data/lib/sleeping_king_studios/tools/toolbox.rb +10 -0
- data/lib/sleeping_king_studios/tools/version.rb +5 -5
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cae6265d25b7d2972d938909435e59949c77b23
|
4
|
+
data.tar.gz: 3471511cbcdd63bfed0a61eb5681a8b8c0328f71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4860968042c42af8a2309a5c178fe0885e69efc645fb87880f8fb2266607a14b0b312d71c4993a392f516c1a155acd044d1c23b2344238b9c3b73c0455409a9a
|
7
|
+
data.tar.gz: dc6074d5dac36aeebb47fccd5e904e1e6deac3aa48c07c95217b1f601d2cf4ac83681b0e5b107ea4fa17d36a2e92183cd091479ef3cadc11920aefc248f68707
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,29 @@
|
|
2
2
|
|
3
3
|
## Pre-release Versions
|
4
4
|
|
5
|
+
### 0.5.0
|
6
|
+
|
7
|
+
- Implement CoreTools#require_each.
|
8
|
+
- Implement StringTools#camelize.
|
9
|
+
- Add an optional block argument to ArrayTools#humanize_list.
|
10
|
+
|
11
|
+
#### Toolbox
|
12
|
+
|
13
|
+
- Implement Delegator#delegate, Delegator#wrap_delegate.
|
14
|
+
- Refactor SemanticVersion to the Toolbox namespace. Accessing SemanticVersion as Tools::SemanticVersion is now deprecated, use Tools::Toolbox::SemanticVersion.
|
15
|
+
|
16
|
+
#### Identity Methods
|
17
|
+
|
18
|
+
- Implement a set of methods to classify objects by type: ArrayTools#array?, HashTools#hash?, IntegerTools#integer?, ObjectTools#object?, and StringTools#string?.
|
19
|
+
|
20
|
+
#### Mutability Methods
|
21
|
+
|
22
|
+
Implement #immutable? and #mutable? for ObjectTools, ArrayTools, and HashTools, which indicate whether or not a given object is mutable.
|
23
|
+
|
24
|
+
Implement #deep_freeze for ObjectTools, ArrayTools, and HashTools which recursively freezes the object and any children (array items, hash keys, and hash values).
|
25
|
+
|
26
|
+
## Current Release
|
27
|
+
|
5
28
|
### 0.4.0
|
6
29
|
|
7
30
|
#### CoreTools
|
@@ -16,8 +39,6 @@ Implement #pluralize.
|
|
16
39
|
|
17
40
|
Implement #pluralize and #singularize. The previous behavior of #pluralize is deprecated; use IntegerTools#pluralize.
|
18
41
|
|
19
|
-
## Current Release
|
20
|
-
|
21
42
|
### 0.3.0
|
22
43
|
|
23
44
|
Implement ArrayTools#bisect and ArrayTools#splice.
|
data/DEVELOPMENT.md
CHANGED
@@ -1,30 +1,5 @@
|
|
1
1
|
# Development Notes
|
2
2
|
|
3
|
-
## Version 0.5.0 - The Cold Update
|
4
|
-
|
5
|
-
- ObjectTools#immutable? - delegates to specific toolset implementations.
|
6
|
-
- Values of `nil`, `false`, and `true` are always immutable, as are instances of `Numeric` and `Symbol`.
|
7
|
-
- Arrays are immutable if the array is frozen and all items are immutable.
|
8
|
-
- Hashes are immutable if the hash is frozen and all keys and values are immutable.
|
9
|
-
- All other objects are only immutable if the object is frozen.
|
10
|
-
- ObjectTools#freeze - delegates to specific toolset implementation
|
11
|
-
- Arrays freeze the collection and each item
|
12
|
-
- Hashes freeze the collection and each key and value
|
13
|
-
- ObjectTools#frozen_copy
|
14
|
-
|
15
|
-
## Version 0.6.0 - The Identity Update
|
16
|
-
|
17
|
-
### Features
|
18
|
-
|
19
|
-
- Identity Methods
|
20
|
-
- ArrayTools#array? - true if object is array-like
|
21
|
-
- HashTools#hash? - true if object is hash-like
|
22
|
-
- IntegerTools#integer? - true if object is integer
|
23
|
-
- ObjectTools#object? - true if object is object-like (not BasicObject!)
|
24
|
-
- StringTools#string? - true if object is string
|
25
|
-
- s/is/is or claims to be a/ - see http://guides.rubyonrails.org/active_support_core_extensions.html#acts-like-questionmark-duck
|
26
|
-
- StringTools#humanize_list - accept a block. If block given, yield each item and join the results.
|
27
|
-
|
28
3
|
## Future Tasks
|
29
4
|
|
30
5
|
- Remove 'extend self' from Tools modules.
|
@@ -33,6 +8,11 @@
|
|
33
8
|
|
34
9
|
#### Tools
|
35
10
|
|
11
|
+
- StringTools#chain |
|
12
|
+
|
13
|
+
tools.chain(str, :underscore, :pluralize)
|
14
|
+
#=> shorthand for tools.pluralize(tools.underscore str)
|
15
|
+
|
36
16
|
- ObjectTools#pretty - returns user-friendly string representation. :multiline option? Delegates to specific toolset implementations.
|
37
17
|
- RegexpTools#matching_string - generates a string that matches the regular expression. Does not support advanced Regexp features.
|
38
18
|
- RegexpTools#nonmatching_strings - generates a set of strings that do not match the regular expression.
|
data/README.md
CHANGED
@@ -35,6 +35,17 @@ The tools can be accessed in a convenient form using the Toolbelt class.
|
|
35
35
|
|
36
36
|
Tools for working with array-like enumerable objects.
|
37
37
|
|
38
|
+
#### `#array?`
|
39
|
+
|
40
|
+
Returns true if the object is or appears to be an Array.
|
41
|
+
|
42
|
+
ArrayTools.array?(nil)
|
43
|
+
#=> false
|
44
|
+
ArrayTools.array?([])
|
45
|
+
#=> true
|
46
|
+
ArrayTools.array?({})
|
47
|
+
#=> false
|
48
|
+
|
38
49
|
#### `#bisect`
|
39
50
|
|
40
51
|
Separates the array into two arrays, the first containing all items in the original array that matches the provided block, and the second containing all items in the original array that do not match the provided block.
|
@@ -76,6 +87,18 @@ Creates a deep copy of the object by returning a new Array with deep copies of e
|
|
76
87
|
ary
|
77
88
|
#=> ['one', 'two', 'three']
|
78
89
|
|
90
|
+
#### `#deep_freeze`
|
91
|
+
|
92
|
+
Freezes the array and performs a deep freeze on each array item. See also ObjectTools#deep_freeze[#label-Object+Tools].
|
93
|
+
|
94
|
+
ary = ['one', 'two', 'three']
|
95
|
+
ArrayTools.deep_freeze ary
|
96
|
+
|
97
|
+
ary.frozen?
|
98
|
+
#=> true
|
99
|
+
ary.first.frozen?
|
100
|
+
#=> true
|
101
|
+
|
79
102
|
#### `#humanize_list`
|
80
103
|
|
81
104
|
Accepts a list of values and returns a human-readable string of the values, with the format based on the number of items.
|
@@ -92,6 +115,33 @@ Accepts a list of values and returns a human-readable string of the values, with
|
|
92
115
|
ArrayTools.humanize_list(['spam', 'eggs', 'bacon', 'spam'])
|
93
116
|
#=> 'spam, eggs, bacon, and spam'
|
94
117
|
|
118
|
+
# With A Block
|
119
|
+
ArrayTools.humanize_list(['bronze', 'silver', 'gold'], { |str| str.capitalize })
|
120
|
+
#=> 'Bronze, Silver, and Gold'
|
121
|
+
|
122
|
+
#### `#immutable?`
|
123
|
+
|
124
|
+
Returns true if the array is immutable, i.e. the array is frozen and each array item is immutable.
|
125
|
+
|
126
|
+
ArrayTools.immutable?([1, 2, 3])
|
127
|
+
#=> false
|
128
|
+
|
129
|
+
ArrayTools.immutable?([1, 2, 3].freeze)
|
130
|
+
#=> true
|
131
|
+
|
132
|
+
ArrayTools.immutable?(['ichi', 'ni', 'san'])
|
133
|
+
#=> false
|
134
|
+
|
135
|
+
ArrayTools.immutable?(['ichi', 'ni', 'san'].freeze)
|
136
|
+
#=> false
|
137
|
+
|
138
|
+
ArrayTools.immutable?(['ichi'.freeze, 'ni'.freeze, 'san'.freeze].freeze)
|
139
|
+
#=> true
|
140
|
+
|
141
|
+
#### `#mutable?`
|
142
|
+
|
143
|
+
Returns true if the array is mutable (see `#immutable?`, above).
|
144
|
+
|
95
145
|
#### `#splice`
|
96
146
|
|
97
147
|
Accepts an array, a start value, a number of items to delete, and zero or more items to insert at that index. Deletes the specified number of items, then inserts the given items at the index and returns the array of deleted items.
|
@@ -121,7 +171,7 @@ Accepts an array, a start value, a number of items to delete, and zero or more i
|
|
121
171
|
|
122
172
|
Tools for working with an application or working environment.
|
123
173
|
|
124
|
-
####
|
174
|
+
#### `#deprecate`
|
125
175
|
|
126
176
|
Prints a deprecation warning.
|
127
177
|
|
@@ -137,6 +187,16 @@ Prints a deprecation warning.
|
|
137
187
|
ObjectTools#old_method was deprecated in version 0.1.0.
|
138
188
|
called from /path/to/file.rb:4: in something_or_other
|
139
189
|
|
190
|
+
#### `#require_each`
|
191
|
+
|
192
|
+
Takes a file pattern or a list of file names and requires each file.
|
193
|
+
|
194
|
+
CoreTools.require_each '/path/to/one', '/path/to/two', '/path/to/three'
|
195
|
+
#=> Requires each file.
|
196
|
+
|
197
|
+
CoreTools.require_each '/path/to/directory/**/*.rb'
|
198
|
+
#=> Requires each file matching the pattern.
|
199
|
+
|
140
200
|
### Hash Tools
|
141
201
|
|
142
202
|
Tools for working with array-like enumerable objects.
|
@@ -158,6 +218,46 @@ Creates a deep copy of the object by returning a new Hash with deep copies of ea
|
|
158
218
|
hsh
|
159
219
|
#=> { :one => 'one', :two => 'two', :three => 'three' }
|
160
220
|
|
221
|
+
#### `#deep_freeze`
|
222
|
+
|
223
|
+
Freezes the hash and performs a deep freeze on each hash key and value.
|
224
|
+
|
225
|
+
hsh = { :one => 'one', :two => 'two', :three => 'three' }
|
226
|
+
HashTools.deep_freeze hsh
|
227
|
+
|
228
|
+
hsh.frozen?
|
229
|
+
#=> true
|
230
|
+
hsh[:one].frozen?
|
231
|
+
#=> true
|
232
|
+
|
233
|
+
#### `#hash?`
|
234
|
+
|
235
|
+
Returns true if the object is or appears to be a Hash.
|
236
|
+
|
237
|
+
HashTools.hash?(nil)
|
238
|
+
#=> false
|
239
|
+
HashTools.hash?([])
|
240
|
+
#=> false
|
241
|
+
HashTools.hash?({})
|
242
|
+
#=> true
|
243
|
+
|
244
|
+
#### `#immutable?`
|
245
|
+
|
246
|
+
Returns true if the hash is immutable, i.e. if the hash is frozen and each hash key and hash value are immutable.
|
247
|
+
|
248
|
+
HashTools.immutable?({ :id => 0, :title => 'The Ramayana' })
|
249
|
+
#=> false
|
250
|
+
|
251
|
+
HashTools.immutable?({ :id => 0, :title => 'The Ramayana' }.freeze)
|
252
|
+
#=> false
|
253
|
+
|
254
|
+
HashTools.immutable?({ :id => 0, :title => 'The Ramayana'.freeze }.freeze)
|
255
|
+
#=> true
|
256
|
+
|
257
|
+
#### `#mutable?`
|
258
|
+
|
259
|
+
Returns true if the hash is mutable (see `#immutable?`, above).
|
260
|
+
|
161
261
|
### Integer Tools
|
162
262
|
|
163
263
|
Tools for working with integers and fixnums.
|
@@ -198,6 +298,19 @@ Decomposes the given integer into its digits when represented in the given base.
|
|
198
298
|
IntegerTools.digits(16724838)
|
199
299
|
#=> ['f', 'f', '3', '3', '6', '6']
|
200
300
|
|
301
|
+
#### `#integer?`
|
302
|
+
|
303
|
+
Returns true if the object is an Integer.
|
304
|
+
|
305
|
+
IntegerTools.integer?(nil)
|
306
|
+
#=> false
|
307
|
+
IntegerTools.integer?([])
|
308
|
+
#=> false
|
309
|
+
IntegerTools.integer?({})
|
310
|
+
#=> false
|
311
|
+
IntegerTools.integer?(1)
|
312
|
+
#=> true
|
313
|
+
|
201
314
|
#### `#pluralize`
|
202
315
|
|
203
316
|
Returns the singular or the plural value, depending on the provided item count.
|
@@ -262,6 +375,40 @@ Creates a deep copy of the object. If the object is an Array, returns a new Arra
|
|
262
375
|
data[:songs][1]
|
263
376
|
#=> { :name => 'Hells Bells', :artist => 'AC/DC', :album => 'Back in Black' }
|
264
377
|
|
378
|
+
#### `#deep_freeze`
|
379
|
+
|
380
|
+
Performs a deep freeze of the object. If the object is an Array, freezes the array and performs a deep freeze on each array item (see ArrayTools#deep_dup[#label-Array+Tools]). If the object is a hash, freezes the hash and performs a deep freeze on each hash key and value (see HashTools#deep_dup[#label-Hash+Tools]). Otherwise, calls Object#freeze unless the object is already immutable.
|
381
|
+
|
382
|
+
data = {
|
383
|
+
:songs = [
|
384
|
+
{
|
385
|
+
:name => 'Welcome to the Jungle',
|
386
|
+
:artist => "Guns N' Roses",
|
387
|
+
:album => 'Appetite for Destruction'
|
388
|
+
}, # end hash
|
389
|
+
{
|
390
|
+
:name => 'Hells Bells',
|
391
|
+
:artist => 'AC/DC',
|
392
|
+
:album => 'Back in Black'
|
393
|
+
}, # end hash
|
394
|
+
{
|
395
|
+
:name => "Knockin' on Heaven's Door",
|
396
|
+
:artist => 'Bob Dylan',
|
397
|
+
:album => 'Pat Garrett & Billy The Kid'
|
398
|
+
} # end hash
|
399
|
+
] # end array
|
400
|
+
} # end hash
|
401
|
+
ObjectTools.deep_freeze(data)
|
402
|
+
|
403
|
+
data.frozen?
|
404
|
+
#=> true
|
405
|
+
data[:songs].frozen?
|
406
|
+
#=> true
|
407
|
+
data[:songs][0].frozen?
|
408
|
+
#=> true
|
409
|
+
data[:songs][0].name.frozen?
|
410
|
+
#=> true
|
411
|
+
|
265
412
|
#### `#eigenclass`, `#metaclass`
|
266
413
|
|
267
414
|
Returns the object's eigenclass.
|
@@ -269,12 +416,75 @@ Returns the object's eigenclass.
|
|
269
416
|
ObjectTools.eigenclass my_object
|
270
417
|
#=> Shortcut for class << self; self; end.
|
271
418
|
|
419
|
+
#### `#immutable?`
|
420
|
+
|
421
|
+
Returns true if the object is immutable. Values of nil, false, and true are always immutable, as are instances of Numeric and Symbol. Arrays are immutable if the array is frozen and each array item is immutable. Hashes are immutable if the hash is frozen and each hash key and hash value are immutable. Otherwise, objects are immutable if they are frozen.
|
422
|
+
|
423
|
+
ObjectTools.immutable?(nil)
|
424
|
+
#=> true
|
425
|
+
|
426
|
+
ObjectTools.immutable?(false)
|
427
|
+
#=> true
|
428
|
+
|
429
|
+
ObjectTools.immutable?(0)
|
430
|
+
#=> true
|
431
|
+
|
432
|
+
ObjectTools.immutable?(:hello)
|
433
|
+
#=> true
|
434
|
+
|
435
|
+
ObjectTools.immutable?("Greetings, programs!")
|
436
|
+
#=> false
|
437
|
+
|
438
|
+
ObjectTools.immutable?([1, 2, 3])
|
439
|
+
#=> false
|
440
|
+
|
441
|
+
ObjectTools.immutable?([1, 2, 3].freeze)
|
442
|
+
#=> false
|
443
|
+
|
444
|
+
#### `#mutable?`
|
445
|
+
|
446
|
+
Returns true if the object is mutable (see `#immutable?`, above).
|
447
|
+
|
448
|
+
#### `#object?`
|
449
|
+
|
450
|
+
Returns true if the object is an Object.
|
451
|
+
|
452
|
+
ObjectTools.object?(nil)
|
453
|
+
#=> true
|
454
|
+
ObjectTools.object?([])
|
455
|
+
#=> true
|
456
|
+
ObjectTools.object?({})
|
457
|
+
#=> true
|
458
|
+
ObjectTools.object?(1)
|
459
|
+
#=> true
|
460
|
+
ObjectTools.object?(BasicObject.new)
|
461
|
+
#=> false
|
462
|
+
|
463
|
+
#### `#try`
|
464
|
+
|
465
|
+
As #send, but returns nil if the object does not respond to the method.
|
466
|
+
|
467
|
+
ObjectTools.try(%w(ichi ni san), :count)
|
468
|
+
#=> 3
|
469
|
+
ObjectTools.try(nil, :count)
|
470
|
+
#=> nil
|
471
|
+
|
272
472
|
### String Tools
|
273
473
|
|
274
474
|
require 'sleeping_king_studios/tools/string_tools'
|
275
475
|
|
276
476
|
Tools for working with strings.
|
277
477
|
|
478
|
+
#### `#camelize`
|
479
|
+
|
480
|
+
Converts a lowercase, underscore separated string to a mixed-case string expression, as per ActiveSupport::Inflector#camelize.
|
481
|
+
|
482
|
+
StringTools#camelize 'valhalla'
|
483
|
+
#=> 'Valhalla'
|
484
|
+
|
485
|
+
StringTools#camelize 'muspelheimr_and_niflheimr'
|
486
|
+
#=> 'MuspelheimrAndNiflheimr'
|
487
|
+
|
278
488
|
#### `#pluralize`
|
279
489
|
|
280
490
|
Takes a word in singular form and returns the plural form, based on the defined rules and known irregular/uncountable words.
|
@@ -316,20 +526,113 @@ Takes a word in plural form and returns the singular form, based on the defined
|
|
316
526
|
StringTools.singularize 'elves'
|
317
527
|
#=> 'elf'
|
318
528
|
|
529
|
+
#### `#string?`
|
530
|
+
|
531
|
+
Returns true if the object is a String.
|
532
|
+
|
533
|
+
StringTools.string?(nil)
|
534
|
+
#=> false
|
535
|
+
StringTools.string?([])
|
536
|
+
#=> false
|
537
|
+
StringTools.string?('Greetings, programs!')
|
538
|
+
#=> true
|
539
|
+
StringTools.string?(:greetings_starfighter)
|
540
|
+
#=> false
|
541
|
+
|
319
542
|
#### `#underscore`
|
320
543
|
|
321
544
|
Converts a mixed-case string expression to a lowercase, underscore separated string, as per ActiveSupport::Inflector#underscore.
|
322
545
|
|
323
|
-
|
546
|
+
StringTools#underscore 'Bifrost'
|
547
|
+
#=> 'bifrost'
|
548
|
+
|
549
|
+
StringTools#underscore 'FenrisWolf'
|
550
|
+
#=> 'fenris_wolf'
|
551
|
+
|
552
|
+
## Toolbox
|
553
|
+
|
554
|
+
Common objects or patterns that are useful across projects but are larger than or do not fit the functional paradigm of the tools.* pattern.
|
555
|
+
|
556
|
+
### Delegator
|
557
|
+
|
558
|
+
require 'sleeping_king_studios/tools/delegator'
|
559
|
+
|
560
|
+
require 'sleeping_king_studios/tools/toolbox/delegator'
|
561
|
+
|
562
|
+
class MyClass
|
563
|
+
extend SleepingKingStudios::Tools::Delegator
|
564
|
+
|
565
|
+
delegate :my_method, :to => MyService
|
566
|
+
end # class
|
567
|
+
|
568
|
+
Module for extending classes with basic delegation.
|
569
|
+
|
570
|
+
#### `::delegate`
|
571
|
+
|
572
|
+
Defines a wrapper method to delegate implementation of the specified method or methods to an object, to the object at another specified method, or to the object at a specified instance variable.
|
573
|
+
|
574
|
+
# Delegate to an object
|
575
|
+
class MyModule
|
576
|
+
extend SleepingKingStudios::Tools::Delegator
|
577
|
+
|
578
|
+
delegate :my_method, :to => MyService
|
579
|
+
end class
|
580
|
+
|
581
|
+
# Delegate to a method
|
582
|
+
class MyModule
|
583
|
+
extend SleepingKingStudios::Tools::Delegator
|
584
|
+
|
585
|
+
def my_service
|
586
|
+
MyService.new
|
587
|
+
end method my_service
|
588
|
+
|
589
|
+
delegate :my_method, :to => :my_service
|
590
|
+
end class
|
591
|
+
|
592
|
+
# Delegate to an instance variable
|
593
|
+
class MyModule
|
594
|
+
extend SleepingKingStudios::Tools::Delegator
|
595
|
+
|
596
|
+
def initialize
|
597
|
+
@my_service = MyService.new
|
598
|
+
end constructor
|
599
|
+
|
600
|
+
delegate :my_method, :to => :@my_service
|
601
|
+
end class
|
602
|
+
|
603
|
+
Expects one or more method names and a delegation target, which can be an object literal, or the name of an method or instance variable as a String or Symbol. Defines a wrapper method to delegate implementation of the specified method or methods to an object, to the object at the another specified method, or to the object at the specified instance variable.
|
604
|
+
|
605
|
+
#### `::wrap_delegate`
|
606
|
+
|
607
|
+
Wraps a delegate object by automatically delegating each method that is defined on the delegate class from the instance to the delegate. The
|
608
|
+
delegate can be specified with an object literal or with the name of an instance method or instance variable.
|
609
|
+
|
610
|
+
Only methods that are defined at the time #wrap_delegate is called will be delegated, so make sure to call #wrap_delegate after loading any gems or libraries that extend your delegate class, such as ActiveSupport.
|
611
|
+
|
612
|
+
# Create a class that wraps a Hash
|
613
|
+
class Errors
|
614
|
+
extend SleepingKingStudios::Tools::Delegator
|
615
|
+
|
616
|
+
wrap_delegate Hash.new { |hsh, key| hsh[key] = Errors.new }, :klass => Hash
|
617
|
+
|
618
|
+
def messages
|
619
|
+
@messages ||= []
|
620
|
+
end # method messages
|
621
|
+
end # class
|
622
|
+
|
623
|
+
errors = Errors.new
|
624
|
+
errors[:post].messages << "title can't be blank"
|
625
|
+
|
626
|
+
Expects a delegation target and optionally a class or module (to determine which methods to delegate), as well as optional :except and :only arrays to limit which methods are delegated. The delegation target can be an object literal, or the name of an method or instance variable as a String or Symbol (in which case a class or module must be specified in :klass or an ArgumentError will be raised).
|
324
627
|
|
325
628
|
### Semantic Version
|
326
629
|
|
327
|
-
require 'sleeping_king_studios/tools/semantic_version'
|
630
|
+
require 'sleeping_king_studios/tools/toolbox/semantic_version'
|
328
631
|
|
329
632
|
Module mixin for using semantic versioning (see http://semver.org) with helper methods for generating strict and gem-compatible version strings.
|
330
633
|
|
331
634
|
module Version
|
332
|
-
extend SleepingKingStudios::Tools::SemanticVersion
|
635
|
+
extend SleepingKingStudios::Tools::Toolbox::SemanticVersion
|
333
636
|
|
334
637
|
MAJOR = 3
|
335
638
|
MINOR = 1
|
@@ -8,6 +8,28 @@ module SleepingKingStudios::Tools
|
|
8
8
|
module ArrayTools
|
9
9
|
extend self
|
10
10
|
|
11
|
+
ARRAY_METHODS = [:[], :count, :each].freeze
|
12
|
+
OTHER_METHODS = [:each_key, :each_pair].freeze
|
13
|
+
|
14
|
+
# Returns true if the object is or appears to be an Array.
|
15
|
+
#
|
16
|
+
# @param ary [Object] The object to test.
|
17
|
+
#
|
18
|
+
# @return [Boolean] True if the object is an Array, otherwise false.
|
19
|
+
def array? ary
|
20
|
+
return true if Array === ary
|
21
|
+
|
22
|
+
ARRAY_METHODS.each do |method_name|
|
23
|
+
return false unless ary.respond_to?(method_name)
|
24
|
+
end # each
|
25
|
+
|
26
|
+
OTHER_METHODS.each do |method_name|
|
27
|
+
return false if ary.respond_to?(method_name)
|
28
|
+
end # each
|
29
|
+
|
30
|
+
true
|
31
|
+
end # method array?
|
32
|
+
|
11
33
|
# Separates the array into two arrays, the first containing all items in the
|
12
34
|
# original array that matches the provided block, and the second containing
|
13
35
|
# all items in the original array that do not match the provided block.
|
@@ -96,6 +118,17 @@ module SleepingKingStudios::Tools
|
|
96
118
|
ary.map { |obj| ObjectTools.deep_dup obj }
|
97
119
|
end # method deep_dup
|
98
120
|
|
121
|
+
# Freezes the array and performs a deep freeze on each array item.
|
122
|
+
#
|
123
|
+
# @param [Array] ary The object to freeze.
|
124
|
+
def deep_freeze ary
|
125
|
+
require_array! ary
|
126
|
+
|
127
|
+
ary.freeze
|
128
|
+
|
129
|
+
ary.each { |obj| ObjectTools.deep_freeze obj }
|
130
|
+
end # method deep_freeze
|
131
|
+
|
99
132
|
# Accepts a list of values and returns a human-readable string of the
|
100
133
|
# values, with the format based on the number of items.
|
101
134
|
#
|
@@ -133,9 +166,11 @@ module SleepingKingStudios::Tools
|
|
133
166
|
# @raise ArgumentError If the first argument is not an Array-like object.
|
134
167
|
#
|
135
168
|
# @return [String] The formatted string.
|
136
|
-
def humanize_list ary, options = {}
|
169
|
+
def humanize_list ary, options = {}, &block
|
137
170
|
require_array! ary
|
138
171
|
|
172
|
+
ary = ary.map(&block) if block_given?
|
173
|
+
|
139
174
|
separator = options.fetch(:separator, ', ')
|
140
175
|
last_separator = options.fetch(:last_separator, ' and ')
|
141
176
|
|
@@ -157,6 +192,35 @@ module SleepingKingStudios::Tools
|
|
157
192
|
end # case
|
158
193
|
end # method humanize_list
|
159
194
|
|
195
|
+
# Returns true if the array is immutable, i.e. the array is frozen and each
|
196
|
+
# array item is immutable.
|
197
|
+
#
|
198
|
+
# @param ary [Array] The array to test.
|
199
|
+
#
|
200
|
+
# @return [Boolean] True if the array is immutable, otherwise false.
|
201
|
+
#
|
202
|
+
# @see ObjectTools#immutable?
|
203
|
+
def immutable? ary
|
204
|
+
require_array! ary
|
205
|
+
|
206
|
+
return false unless ary.frozen?
|
207
|
+
|
208
|
+
ary.each { |item| return false unless ObjectTools.immutable?(item) }
|
209
|
+
|
210
|
+
true
|
211
|
+
end # method immutable?
|
212
|
+
|
213
|
+
# Returns true if the array is mutable.
|
214
|
+
#
|
215
|
+
# @param ary [Array] The array to test.
|
216
|
+
#
|
217
|
+
# @return [Boolean] True if the array is mutable, otherwise false.
|
218
|
+
#
|
219
|
+
# @see #immutable?
|
220
|
+
def mutable? ary
|
221
|
+
!immutable?(ary)
|
222
|
+
end # method mutable?
|
223
|
+
|
160
224
|
# Accepts an array, a start value, a number of items to delete, and zero or
|
161
225
|
# more items to insert at that index. Deletes the specified number of items,
|
162
226
|
# then inserts the given items at the index and returns the array of deleted
|
@@ -208,9 +272,9 @@ module SleepingKingStudios::Tools
|
|
208
272
|
private
|
209
273
|
|
210
274
|
def require_array! value
|
211
|
-
|
275
|
+
return if array?(value)
|
212
276
|
|
213
|
-
raise ArgumentError
|
277
|
+
raise ArgumentError, 'argument must be an array', caller[1..-1]
|
214
278
|
end # method require_array
|
215
279
|
end # module
|
216
280
|
end # module
|
@@ -32,5 +32,18 @@ module SleepingKingStudios::Tools
|
|
32
32
|
|
33
33
|
Kernel.warn str
|
34
34
|
end # method deprecate
|
35
|
+
|
36
|
+
# Expands each file pattern and requires each file.
|
37
|
+
def require_each *file_patterns
|
38
|
+
file_patterns.each do |file_pattern|
|
39
|
+
if file_pattern.include?('*')
|
40
|
+
Dir[file_pattern].each do |file_name|
|
41
|
+
Kernel.require file_name
|
42
|
+
end # each
|
43
|
+
else
|
44
|
+
Kernel.require file_pattern
|
45
|
+
end # unless
|
46
|
+
end # each
|
47
|
+
end # method require_each
|
35
48
|
end # module
|
36
49
|
end # module
|
@@ -8,6 +8,8 @@ module SleepingKingStudios::Tools
|
|
8
8
|
module HashTools
|
9
9
|
extend self
|
10
10
|
|
11
|
+
HASH_METHODS = [:[], :count, :each, :each_key, :each_pair].freeze
|
12
|
+
|
11
13
|
# Creates a deep copy of the object by returning a new Hash with deep
|
12
14
|
# copies of each key and value.
|
13
15
|
#
|
@@ -19,5 +21,60 @@ module SleepingKingStudios::Tools
|
|
19
21
|
copy[ObjectTools.deep_dup key] = ObjectTools.deep_dup(value)
|
20
22
|
end # each
|
21
23
|
end # method deep_dup
|
24
|
+
|
25
|
+
# Freezes the hash and performs a deep freeze on each hash key and
|
26
|
+
# value.
|
27
|
+
#
|
28
|
+
# @param [Hash] hsh The object to freeze.
|
29
|
+
def deep_freeze hsh
|
30
|
+
hsh.freeze
|
31
|
+
|
32
|
+
hsh.each do |key, value|
|
33
|
+
ObjectTools.deep_freeze key
|
34
|
+
ObjectTools.deep_freeze value
|
35
|
+
end # each
|
36
|
+
end # method deep_freeze
|
37
|
+
|
38
|
+
# Returns true if the object is or appears to be a Hash.
|
39
|
+
#
|
40
|
+
# @param hsh [Object] The object to test.
|
41
|
+
#
|
42
|
+
# @return [Boolean] True if the object is a Hash, otherwise false.
|
43
|
+
def hash? hsh
|
44
|
+
return true if Hash === hsh
|
45
|
+
|
46
|
+
HASH_METHODS.each do |method_name|
|
47
|
+
return false unless hsh.respond_to?(method_name)
|
48
|
+
end # each
|
49
|
+
|
50
|
+
true
|
51
|
+
end # method hash?
|
52
|
+
|
53
|
+
# Returns true if the hash is immutable, i.e. if the hash is frozen and each
|
54
|
+
# hash key and hash value are immutable.
|
55
|
+
#
|
56
|
+
# @param hsh [Hash] The hash to test.
|
57
|
+
#
|
58
|
+
# @return [Boolean] True if the hash is immutable, otherwise false.
|
59
|
+
def immutable? hsh
|
60
|
+
return false unless hsh.frozen?
|
61
|
+
|
62
|
+
hsh.each do |key, value|
|
63
|
+
return false unless ObjectTools.immutable?(key) && ObjectTools.immutable?(value)
|
64
|
+
end # each
|
65
|
+
|
66
|
+
true
|
67
|
+
end # method immutable
|
68
|
+
|
69
|
+
# Returns true if the hash is mutable.
|
70
|
+
#
|
71
|
+
# @param hsh [Array] The hash to test.
|
72
|
+
#
|
73
|
+
# @return [Boolean] True if the hash is mutable, otherwise false.
|
74
|
+
#
|
75
|
+
# @see #immutable?
|
76
|
+
def mutable? hsh
|
77
|
+
!immutable?(hsh)
|
78
|
+
end # method mutable?
|
22
79
|
end # module
|
23
80
|
end # module
|
@@ -66,6 +66,15 @@ module SleepingKingStudios::Tools
|
|
66
66
|
integer.to_s(base).split('')
|
67
67
|
end # method digits
|
68
68
|
|
69
|
+
# Returns true if the object is an Integer.
|
70
|
+
#
|
71
|
+
# @param int [Object] The object to test.
|
72
|
+
#
|
73
|
+
# @return [Boolean] True if the object is an Integer, otherwise false.
|
74
|
+
def integer? int
|
75
|
+
Integer === int
|
76
|
+
end # method integer?
|
77
|
+
|
69
78
|
# Returns the singular or the plural value, depending on the provided
|
70
79
|
# item count.
|
71
80
|
#
|
@@ -21,6 +21,14 @@ module SleepingKingStudios::Tools
|
|
21
21
|
# @return The result of calling the proc or lambda with the given
|
22
22
|
# receiver and any additional arguments or block.
|
23
23
|
def apply base, proc, *args, **kwargs, &block
|
24
|
+
unless block_given?
|
25
|
+
if kwargs.empty?
|
26
|
+
return base.instance_exec *args, &proc
|
27
|
+
else
|
28
|
+
return base.instance_exec *args, **kwargs, &proc
|
29
|
+
end # if-else
|
30
|
+
end # unless
|
31
|
+
|
24
32
|
temporary_method_name = :__sleeping_king_studios_tools_object_tools_temporary_method_for_applying_proc__
|
25
33
|
|
26
34
|
metaclass = class << base; self; end
|
@@ -49,15 +57,34 @@ module SleepingKingStudios::Tools
|
|
49
57
|
case obj
|
50
58
|
when FalseClass, Fixnum, Float, NilClass, Symbol, TrueClass
|
51
59
|
obj
|
52
|
-
when
|
60
|
+
when ->(_) { ArrayTools.array?(obj) }
|
53
61
|
ArrayTools.deep_dup obj
|
54
|
-
when
|
62
|
+
when ->(_) { HashTools.hash?(obj) }
|
55
63
|
HashTools.deep_dup obj
|
56
64
|
else
|
57
65
|
obj.respond_to?(:deep_dup) ? obj.deep_dup : obj.dup
|
58
66
|
end # case
|
59
67
|
end # method deep_dup
|
60
68
|
|
69
|
+
# Performs a deep freeze of the object. If the object is an Array, freezes
|
70
|
+
# the array and performs a deep freeze on each array item. If the object is
|
71
|
+
# a hash, freezes the hash and performs a deep freeze on each hash key and
|
72
|
+
# value. Otherwise, calls Object#freeze.
|
73
|
+
#
|
74
|
+
# @param [Object] obj The object to freeze.
|
75
|
+
def deep_freeze obj
|
76
|
+
case obj
|
77
|
+
when FalseClass, Fixnum, Float, NilClass, Symbol, TrueClass
|
78
|
+
# Object is inherently immutable; do nothing here.
|
79
|
+
when ->(_) { ArrayTools.array?(obj) }
|
80
|
+
ArrayTools.deep_freeze(obj)
|
81
|
+
when ->(_) { HashTools.hash?(obj) }
|
82
|
+
HashTools.deep_freeze obj
|
83
|
+
else
|
84
|
+
obj.respond_to?(:deep_freeze) ? obj.deep_freeze : obj.freeze
|
85
|
+
end # case
|
86
|
+
end # method deep_freeze
|
87
|
+
|
61
88
|
# Returns the object's eigenclass.
|
62
89
|
#
|
63
90
|
# @param [Object] object The object for which an eigenclass is required.
|
@@ -68,6 +95,50 @@ module SleepingKingStudios::Tools
|
|
68
95
|
end # method eigenclass
|
69
96
|
alias_method :metaclass, :eigenclass
|
70
97
|
|
98
|
+
# Returns true if the object is immutable. Values of nil, false, and true
|
99
|
+
# are always immutable, as are instances of Numeric and Symbol. Arrays are
|
100
|
+
# immutable if the array is frozen and each array item is immutable. Hashes
|
101
|
+
# are immutable if the hash is frozen and each hash key and hash value are
|
102
|
+
# immutable. Otherwise, objects are immutable if they are frozen.
|
103
|
+
#
|
104
|
+
# @param obj [Object] The object to test.
|
105
|
+
#
|
106
|
+
# @return [Boolean] True if the object is immutable, otherwise false.
|
107
|
+
def immutable? obj
|
108
|
+
case obj
|
109
|
+
when NilClass, FalseClass, TrueClass, Numeric, Symbol
|
110
|
+
true
|
111
|
+
when ->(_) { ArrayTools.array?(obj) }
|
112
|
+
ArrayTools.immutable? obj
|
113
|
+
when ->(_) { HashTools.hash?(obj) }
|
114
|
+
HashTools.immutable? obj
|
115
|
+
else
|
116
|
+
obj.frozen?
|
117
|
+
end # case
|
118
|
+
end # method immutable?
|
119
|
+
|
120
|
+
# Returns true if the object is mutable.
|
121
|
+
#
|
122
|
+
# @param obj [Object] The object to test.
|
123
|
+
#
|
124
|
+
# @return [Boolean] True if the object is mutable, otherwise false.
|
125
|
+
#
|
126
|
+
# @see #immutable?
|
127
|
+
def mutable? obj
|
128
|
+
!immutable?(obj)
|
129
|
+
end # method mutable?
|
130
|
+
|
131
|
+
# Returns true if the object is an Object. This should return true only for
|
132
|
+
# objects that have an alternate inheritance chain from BasicObject, such as
|
133
|
+
# a Proxy.
|
134
|
+
#
|
135
|
+
# @param obj [Object] The object to test.
|
136
|
+
#
|
137
|
+
# @return [Boolean] True if the object is an Object, otherwise false.
|
138
|
+
def object? obj
|
139
|
+
Object === obj
|
140
|
+
end # method object?
|
141
|
+
|
71
142
|
# As #send, but returns nil if the object does not respond to the method.
|
72
143
|
#
|
73
144
|
# @param [Object] object The receiver of the message.
|
@@ -1,108 +1,15 @@
|
|
1
1
|
# lib/sleeping_king_studios/tools/semantic_version.rb
|
2
2
|
|
3
|
-
|
4
|
-
module Tools
|
5
|
-
# Helper for generating semantic version strings with optional prerelease and
|
6
|
-
# build parameters.
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
# module Version
|
10
|
-
# extend SleepingKingStudios::Tools::SemanticVersion
|
11
|
-
#
|
12
|
-
# MAJOR = 3
|
13
|
-
# MINOR = 1
|
14
|
-
# PATCH = 4
|
15
|
-
# PRERELEASE = 'beta'
|
16
|
-
# BUILD = 1
|
17
|
-
# end # module
|
18
|
-
#
|
19
|
-
# VERSION = Version.to_gem_version
|
20
|
-
#
|
21
|
-
# @see http://semver.org
|
22
|
-
module SemanticVersion
|
23
|
-
# Error class for handling missing constants in a version definition.
|
24
|
-
class InvalidVersionError < StandardError; end
|
3
|
+
require 'sleeping_king_studios/tools/core_tools'
|
25
4
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
#
|
31
|
-
# @example
|
32
|
-
# module Version
|
33
|
-
# extend SleepingKingStudios::Tools::SemanticVersion
|
34
|
-
#
|
35
|
-
# MAJOR = 3
|
36
|
-
# MINOR = 1
|
37
|
-
# PATCH = 4
|
38
|
-
# PRERELEASE = 'beta'
|
39
|
-
# BUILD = 1
|
40
|
-
# end # module
|
41
|
-
#
|
42
|
-
# VERSION = Version.to_gem_version
|
43
|
-
# #=> '3.1.4.beta.1'
|
44
|
-
#
|
45
|
-
# @return [String] The modified semantic version string.
|
46
|
-
#
|
47
|
-
# @raise InvalidVersionError If MAJOR, MINOR, or PATCH is undefined.
|
48
|
-
def to_gem_version
|
49
|
-
str = "#{const_fetch :MAJOR}.#{const_fetch :MINOR}.#{const_fetch :PATCH}"
|
5
|
+
SleepingKingStudios::Tools::CoreTools.deprecate(
|
6
|
+
'SleepingKingStudios::Tools::SemanticVersion',
|
7
|
+
:message => 'Use SleepingKingStudios::Tools::Toolbox::SemanticVersion instead.'
|
8
|
+
) # end deprecate
|
50
9
|
|
51
|
-
|
52
|
-
str << ".#{prerelease}" unless prerelease.nil? || prerelease.empty?
|
10
|
+
require 'sleeping_king_studios/tools/toolbox/semantic_version'
|
53
11
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
str
|
58
|
-
end # method to_version
|
59
|
-
|
60
|
-
# Concatenates the MAJOR, MINOR, and PATCH constant values with PRERELEASE
|
61
|
-
# and BUILD (if available) to generate a semantic version string. The
|
62
|
-
# major, minor, and patch values are separated by dots (.), then the
|
63
|
-
# prerelease (if available) preceded by a hyphen (-), and the build (if
|
64
|
-
# available) preceded by a plus (+).
|
65
|
-
#
|
66
|
-
# @example
|
67
|
-
# module Version
|
68
|
-
# extend SleepingKingStudios::Tools::SemanticVersion
|
69
|
-
#
|
70
|
-
# MAJOR = 3
|
71
|
-
# MINOR = 1
|
72
|
-
# PATCH = 4
|
73
|
-
# PRERELEASE = 'beta'
|
74
|
-
# BUILD = 1
|
75
|
-
# end # module
|
76
|
-
#
|
77
|
-
# VERSION = Version.to_version
|
78
|
-
# #=> '3.1.4-beta+1'
|
79
|
-
#
|
80
|
-
# @return [String] The semantic version string.
|
81
|
-
#
|
82
|
-
# @raise InvalidVersionError If MAJOR, MINOR, or PATCH is undefined.
|
83
|
-
def to_version
|
84
|
-
str = "#{const_fetch :MAJOR}.#{const_fetch :MINOR}.#{const_fetch :PATCH}"
|
85
|
-
|
86
|
-
prerelease = const_fetch(:PRERELEASE, nil)
|
87
|
-
str << "-#{prerelease}" unless prerelease.nil? || prerelease.empty?
|
88
|
-
|
89
|
-
build = const_fetch(:BUILD, nil)
|
90
|
-
str << "+#{build}" unless build.nil? || build.empty?
|
91
|
-
|
92
|
-
str
|
93
|
-
end # method to_version
|
94
|
-
|
95
|
-
private
|
96
|
-
|
97
|
-
FETCH_DEFAULT = Object.new.freeze
|
98
|
-
|
99
|
-
def const_fetch name, default = FETCH_DEFAULT
|
100
|
-
if self.const_defined?(name)
|
101
|
-
return self.const_get(name).to_s
|
102
|
-
elsif default == FETCH_DEFAULT
|
103
|
-
raise InvalidVersionError.new "undefined constant for #{name.downcase} version"
|
104
|
-
end # if-else
|
105
|
-
end # method const_fetch
|
106
|
-
end # module
|
107
|
-
end # module
|
12
|
+
module SleepingKingStudios::Tools
|
13
|
+
# (see SleepingKingStudios::Tools::Toolbox::SemanticVersion)
|
14
|
+
SemanticVersion = Toolbox::SemanticVersion
|
108
15
|
end # module
|
@@ -10,6 +10,21 @@ module SleepingKingStudios::Tools
|
|
10
10
|
|
11
11
|
autoload :PluralInflector, 'sleeping_king_studios/tools/string_tools/plural_inflector'
|
12
12
|
|
13
|
+
# Converts a lowercase, underscore-separated string to CamelCase.
|
14
|
+
#
|
15
|
+
# @param str [String] The string to convert.
|
16
|
+
#
|
17
|
+
# @return [String] The converted string.
|
18
|
+
#
|
19
|
+
# @see ActiveSupport::Inflector#camelize.
|
20
|
+
def camelize str
|
21
|
+
require_string! str
|
22
|
+
|
23
|
+
str = str.dup
|
24
|
+
str.gsub!(/(\b|[_-])([a-z])/) { |match| $2.upcase }
|
25
|
+
str
|
26
|
+
end # method camelize
|
27
|
+
|
13
28
|
# (see PluralInflector#define_irregular_word)
|
14
29
|
def define_irregular_word singular, plural
|
15
30
|
plural_inflector.define_irregular_word singular, plural
|
@@ -76,6 +91,15 @@ module SleepingKingStudios::Tools
|
|
76
91
|
plural_inflector.singularize str
|
77
92
|
end # method singularize
|
78
93
|
|
94
|
+
# Returns true if the object is a String.
|
95
|
+
#
|
96
|
+
# @param str [Object] The object to test.
|
97
|
+
#
|
98
|
+
# @return [Boolean] True if the object is a String, otherwise false.
|
99
|
+
def string? str
|
100
|
+
String === str
|
101
|
+
end # method string?
|
102
|
+
|
79
103
|
# Converts a mixed-case string expression to a lowercase, underscore
|
80
104
|
# separated string.
|
81
105
|
#
|
@@ -102,7 +126,9 @@ module SleepingKingStudios::Tools
|
|
102
126
|
end # method plural_inflector
|
103
127
|
|
104
128
|
def require_string! value
|
105
|
-
|
129
|
+
return if string?(value)
|
130
|
+
|
131
|
+
raise ArgumentError, 'argument must be a string', caller[1..-1]
|
106
132
|
end # method require_array
|
107
133
|
end # module
|
108
134
|
end # module
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# lib/sleeping_king_studios/tools/toolbox/delegator.rb
|
2
|
+
|
3
|
+
require 'sleeping_king_studios/tools/toolbox'
|
4
|
+
|
5
|
+
module SleepingKingStudios::Tools::Toolbox
|
6
|
+
# Module for extending classes with basic delegation. Supports passing
|
7
|
+
# arguments, keywords, and blocks to the delegated method.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class MyClass
|
11
|
+
# extend SleepingKingStudios::Tools::Delegator
|
12
|
+
#
|
13
|
+
# delegate :my_method, :to => MyService
|
14
|
+
# end # class
|
15
|
+
module Delegator
|
16
|
+
# Defines a wrapper method to delegate implementation of the specified
|
17
|
+
# method or methods to an object, to the object at another specified method,
|
18
|
+
# or to the object at a specified instance variable.
|
19
|
+
#
|
20
|
+
# @example Delegate to an object
|
21
|
+
# class MyModule
|
22
|
+
# extend SleepingKingStudios::Tools::Delegator
|
23
|
+
#
|
24
|
+
# delegate :my_method, :to => MyService
|
25
|
+
# end # class
|
26
|
+
#
|
27
|
+
# @example Delegate to a method
|
28
|
+
# class MyModule
|
29
|
+
# extend SleepingKingStudios::Tools::Delegator
|
30
|
+
#
|
31
|
+
# def my_service
|
32
|
+
# MyService.new
|
33
|
+
# end # method my_service
|
34
|
+
#
|
35
|
+
# delegate :my_method, :to => :my_service
|
36
|
+
# end # class
|
37
|
+
#
|
38
|
+
# @example Delegate to an instance variable
|
39
|
+
# class MyModule
|
40
|
+
# extend SleepingKingStudios::Tools::Delegator
|
41
|
+
#
|
42
|
+
# def initialize
|
43
|
+
# @my_service = MyService.new
|
44
|
+
# end # constructor
|
45
|
+
#
|
46
|
+
# delegate :my_method, :to => :@my_service
|
47
|
+
# end # class
|
48
|
+
#
|
49
|
+
# @param method_names [Array<String, Symbol>] The names of the methods to
|
50
|
+
# delegate.
|
51
|
+
# @param to [Object, String, Symbol] The object, method, or instance
|
52
|
+
# variable to delegate to. If the object is not a string or symbol, the
|
53
|
+
# generated method will call `method_name` on the object. If the object is
|
54
|
+
# a string or symbol, but does not start with an `@`, the generated method
|
55
|
+
# will call the method of that name on the instance, and then call
|
56
|
+
# `method_name` on the result. If the object is a string or symbol and
|
57
|
+
# does start with an `@`, the generated method will get the instance
|
58
|
+
# variable of that name and call `method_name` on the result.
|
59
|
+
#
|
60
|
+
# @raise ArgumentError if no delegate is specified.
|
61
|
+
def delegate *method_names, to: nil, allow_nil: false
|
62
|
+
raise ArgumentError.new('must specify a delegate') if to.nil? && !allow_nil
|
63
|
+
|
64
|
+
method_names.each do |method_name|
|
65
|
+
delegate_method method_name, to, { :allow_nil => !!allow_nil }
|
66
|
+
end # each
|
67
|
+
end # method delegate
|
68
|
+
|
69
|
+
# Wraps a delegate object by automatically delegating each method that is
|
70
|
+
# defined on the delegate class from the instance to the delegate. The
|
71
|
+
# delegate can be specified with an object literal or with the name of an
|
72
|
+
# instance method or instance variable.
|
73
|
+
#
|
74
|
+
# Only methods that are defined at the time #wrap_delegate is called will be
|
75
|
+
# delegated, so make sure to call #wrap_delegate after loading any gems or
|
76
|
+
# libraries that extend your delegate class, such as ActiveSupport.
|
77
|
+
#
|
78
|
+
# @example Create a class that wraps a Hash
|
79
|
+
# class Errors
|
80
|
+
# extend SleepingKingStudios::Tools::Delegator
|
81
|
+
#
|
82
|
+
# wrap_delegate Hash.new { |hsh, key| hsh[key] = Errors.new }, :klass => Hash
|
83
|
+
#
|
84
|
+
# def messages
|
85
|
+
# @messages ||= []
|
86
|
+
# end # method messages
|
87
|
+
# end # class
|
88
|
+
#
|
89
|
+
# errors = Errors.new
|
90
|
+
# errors[:post].messages << "title can't be blank"
|
91
|
+
#
|
92
|
+
# @param target [Object, String, Symbol] The object, method, or instance
|
93
|
+
# variable to delegate to. If the object is not a string or symbol, the
|
94
|
+
# generated method will call each method on the object. If the object is
|
95
|
+
# a string or symbol, but does not start with an `@`, the generated method
|
96
|
+
# will call the method of that name on the instance, and then call
|
97
|
+
# each method on the result. If the object is a string or symbol and
|
98
|
+
# does start with an `@`, the generated method will get the instance
|
99
|
+
# variable of that name and call each method on the result.
|
100
|
+
# @param klass [Module] The class or module whose methods are delegated to
|
101
|
+
# the target. If target is the name of an instance variable or an instance
|
102
|
+
# method, the klass must be specified. If target is an object literal, the
|
103
|
+
# klass is optional, in which case all methods from the target will be
|
104
|
+
# delegated to the target.
|
105
|
+
# @param except [Array<String, Symbol>] An optional list of method names.
|
106
|
+
# Any names on the list will not be delegated, even if the method is
|
107
|
+
# defined by the klass or defined on the target literal.
|
108
|
+
# @param only [Array<String, Symbol>] An optional list of method names.
|
109
|
+
# Only names on the list will be delegated, and only if the method is
|
110
|
+
# defined by the klass or defined on the target literal.
|
111
|
+
#
|
112
|
+
# @raise ArgumentError if no delegate is specified.
|
113
|
+
# @raise ArgumentError if the target is the name of an instance method or an
|
114
|
+
# instance variable and no klass is specified.
|
115
|
+
# @raise ArgumentError if the target is an object literal that does not
|
116
|
+
# belong to the specified klass.
|
117
|
+
#
|
118
|
+
# @see #delegate
|
119
|
+
def wrap_delegate target, klass: nil, except: [], only: []
|
120
|
+
if klass.is_a?(Module)
|
121
|
+
unless target.is_a?(String) || target.is_a?(Symbol) || target.is_a?(klass)
|
122
|
+
raise ArgumentError.new "expected delegate to be a #{klass.name}"
|
123
|
+
end # unless
|
124
|
+
|
125
|
+
method_names = klass.instance_methods - Object.instance_methods
|
126
|
+
elsif target.is_a?(String) || target.is_a?(Symbol)
|
127
|
+
raise ArgumentError.new 'must specify a delegate class'
|
128
|
+
else
|
129
|
+
method_names = target.methods - Object.new.methods
|
130
|
+
end # if-elsif-else
|
131
|
+
|
132
|
+
if except.is_a?(Array) && !except.empty?
|
133
|
+
method_names = method_names - except.map(&:intern)
|
134
|
+
end # if
|
135
|
+
|
136
|
+
if only.is_a?(Array) && !only.empty?
|
137
|
+
method_names = method_names & only.map(&:intern)
|
138
|
+
end # if
|
139
|
+
|
140
|
+
delegate *method_names, :to => target
|
141
|
+
end # method wrap_delegate
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def delegate_method method_name, target, options = {}
|
146
|
+
if target.is_a?(String) || target.is_a?(Symbol)
|
147
|
+
target = target.intern
|
148
|
+
|
149
|
+
if target.to_s =~ /\A@/
|
150
|
+
define_method method_name do |*args, &block|
|
151
|
+
receiver = instance_variable_get(target)
|
152
|
+
|
153
|
+
return nil if receiver.nil? && options[:allow_nil]
|
154
|
+
|
155
|
+
receiver.send(method_name, *args, &block)
|
156
|
+
end # define_method
|
157
|
+
else
|
158
|
+
define_method method_name do |*args, &block|
|
159
|
+
receiver = send(target)
|
160
|
+
|
161
|
+
return nil if receiver.nil? && options[:allow_nil]
|
162
|
+
|
163
|
+
receiver.send(method_name, *args, &block)
|
164
|
+
end # define_method
|
165
|
+
end # if-else
|
166
|
+
else
|
167
|
+
define_method method_name do |*args, &block|
|
168
|
+
return nil if target.nil? && options[:allow_nil]
|
169
|
+
|
170
|
+
target.send(method_name, *args, &block)
|
171
|
+
end # define_method
|
172
|
+
end # if
|
173
|
+
end # method delegate_method
|
174
|
+
end # module
|
175
|
+
end # module
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# lib/sleeping_king_studios/tools/toolbox/semantic_version.rb
|
2
|
+
|
3
|
+
require 'sleeping_king_studios/tools/toolbox'
|
4
|
+
|
5
|
+
module SleepingKingStudios::Tools::Toolbox
|
6
|
+
# Helper for generating semantic version strings with optional prerelease and
|
7
|
+
# build parameters.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# module Version
|
11
|
+
# extend SleepingKingStudios::Tools::SemanticVersion
|
12
|
+
#
|
13
|
+
# MAJOR = 3
|
14
|
+
# MINOR = 1
|
15
|
+
# PATCH = 4
|
16
|
+
# PRERELEASE = 'beta'
|
17
|
+
# BUILD = 1
|
18
|
+
# end # module
|
19
|
+
#
|
20
|
+
# VERSION = Version.to_gem_version
|
21
|
+
#
|
22
|
+
# @see http://semver.org
|
23
|
+
module SemanticVersion
|
24
|
+
# Error class for handling missing constants in a version definition.
|
25
|
+
class InvalidVersionError < StandardError; end
|
26
|
+
|
27
|
+
# Concatenates the MAJOR, MINOR, and PATCH constant values with PRERELEASE
|
28
|
+
# and BUILD (if available) to generate a modified semantic version string
|
29
|
+
# compatible with Rubygems. The major, minor, patch, prerelease, and build
|
30
|
+
# values (if available) are separated by dots (.).
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# module Version
|
34
|
+
# extend SleepingKingStudios::Tools::SemanticVersion
|
35
|
+
#
|
36
|
+
# MAJOR = 3
|
37
|
+
# MINOR = 1
|
38
|
+
# PATCH = 4
|
39
|
+
# PRERELEASE = 'beta'
|
40
|
+
# BUILD = 1
|
41
|
+
# end # module
|
42
|
+
#
|
43
|
+
# VERSION = Version.to_gem_version
|
44
|
+
# #=> '3.1.4.beta.1'
|
45
|
+
#
|
46
|
+
# @return [String] The modified semantic version string.
|
47
|
+
#
|
48
|
+
# @raise InvalidVersionError If MAJOR, MINOR, or PATCH is undefined.
|
49
|
+
def to_gem_version
|
50
|
+
str = "#{const_fetch :MAJOR}.#{const_fetch :MINOR}.#{const_fetch :PATCH}"
|
51
|
+
|
52
|
+
prerelease = const_fetch(:PRERELEASE, nil)
|
53
|
+
str << ".#{prerelease}" unless prerelease.nil? || prerelease.empty?
|
54
|
+
|
55
|
+
build = const_fetch(:BUILD, nil)
|
56
|
+
str << ".#{build}" unless build.nil? || build.empty?
|
57
|
+
|
58
|
+
str
|
59
|
+
end # method to_version
|
60
|
+
|
61
|
+
# Concatenates the MAJOR, MINOR, and PATCH constant values with PRERELEASE
|
62
|
+
# and BUILD (if available) to generate a semantic version string. The
|
63
|
+
# major, minor, and patch values are separated by dots (.), then the
|
64
|
+
# prerelease (if available) preceded by a hyphen (-), and the build (if
|
65
|
+
# available) preceded by a plus (+).
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# module Version
|
69
|
+
# extend SleepingKingStudios::Tools::SemanticVersion
|
70
|
+
#
|
71
|
+
# MAJOR = 3
|
72
|
+
# MINOR = 1
|
73
|
+
# PATCH = 4
|
74
|
+
# PRERELEASE = 'beta'
|
75
|
+
# BUILD = 1
|
76
|
+
# end # module
|
77
|
+
#
|
78
|
+
# VERSION = Version.to_version
|
79
|
+
# #=> '3.1.4-beta+1'
|
80
|
+
#
|
81
|
+
# @return [String] The semantic version string.
|
82
|
+
#
|
83
|
+
# @raise InvalidVersionError If MAJOR, MINOR, or PATCH is undefined.
|
84
|
+
def to_version
|
85
|
+
str = "#{const_fetch :MAJOR}.#{const_fetch :MINOR}.#{const_fetch :PATCH}"
|
86
|
+
|
87
|
+
prerelease = const_fetch(:PRERELEASE, nil)
|
88
|
+
str << "-#{prerelease}" unless prerelease.nil? || prerelease.empty?
|
89
|
+
|
90
|
+
build = const_fetch(:BUILD, nil)
|
91
|
+
str << "+#{build}" unless build.nil? || build.empty?
|
92
|
+
|
93
|
+
str
|
94
|
+
end # method to_version
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
FETCH_DEFAULT = Object.new.freeze
|
99
|
+
|
100
|
+
def const_fetch name, default = FETCH_DEFAULT
|
101
|
+
if self.const_defined?(name)
|
102
|
+
return self.const_get(name).to_s
|
103
|
+
elsif default == FETCH_DEFAULT
|
104
|
+
raise InvalidVersionError.new "undefined constant for #{name.downcase} version"
|
105
|
+
end # if-else
|
106
|
+
end # method const_fetch
|
107
|
+
end # module
|
108
|
+
end # module
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# lib/sleeping_king_studios/tools/toolbox.rb
|
2
|
+
|
3
|
+
require 'sleeping_king_studios/tools'
|
4
|
+
|
5
|
+
module SleepingKingStudios::Tools
|
6
|
+
# Namespace for common objects or patterns that are useful across projects but
|
7
|
+
# are larger than or do not fit the functional paradigm of the tools.*
|
8
|
+
# pattern.
|
9
|
+
module Toolbox; end
|
10
|
+
end # module
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# lib/sleeping_king_studios/tools/version.rb
|
2
2
|
|
3
|
-
require 'sleeping_king_studios/tools/semantic_version'
|
3
|
+
require 'sleeping_king_studios/tools/toolbox/semantic_version'
|
4
4
|
|
5
5
|
module SleepingKingStudios
|
6
6
|
module Tools
|
@@ -8,15 +8,15 @@ module SleepingKingStudios
|
|
8
8
|
#
|
9
9
|
# @see http://semver.org
|
10
10
|
module Version
|
11
|
-
extend SleepingKingStudios::Tools::SemanticVersion
|
11
|
+
extend SleepingKingStudios::Tools::Toolbox::SemanticVersion
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
15
|
MAJOR = 0
|
16
|
-
MINOR =
|
16
|
+
MINOR = 5
|
17
17
|
PATCH = 0
|
18
|
-
PRERELEASE =
|
19
|
-
BUILD =
|
18
|
+
PRERELEASE = :rc
|
19
|
+
BUILD = 0
|
20
20
|
end # module
|
21
21
|
|
22
22
|
VERSION = Version.to_gem_version
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sleeping_king_studios-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0.rc.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob "Merlin" Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -103,6 +103,9 @@ files:
|
|
103
103
|
- lib/sleeping_king_studios/tools/string_tools.rb
|
104
104
|
- lib/sleeping_king_studios/tools/string_tools/plural_inflector.rb
|
105
105
|
- lib/sleeping_king_studios/tools/toolbelt.rb
|
106
|
+
- lib/sleeping_king_studios/tools/toolbox.rb
|
107
|
+
- lib/sleeping_king_studios/tools/toolbox/delegator.rb
|
108
|
+
- lib/sleeping_king_studios/tools/toolbox/semantic_version.rb
|
106
109
|
- lib/sleeping_king_studios/tools/version.rb
|
107
110
|
homepage: http://sleepingkingstudios.com
|
108
111
|
licenses:
|
@@ -119,9 +122,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
122
|
version: '0'
|
120
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
124
|
requirements:
|
122
|
-
- - "
|
125
|
+
- - ">"
|
123
126
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
127
|
+
version: 1.3.1
|
125
128
|
requirements: []
|
126
129
|
rubyforge_project:
|
127
130
|
rubygems_version: 2.5.1
|