more_core_extensions 3.7.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.travis.yml +7 -9
- data/CHANGELOG.md +40 -1
- data/Gemfile +10 -2
- data/README.md +22 -2
- data/lib/more_core_extensions/all.rb +2 -1
- data/lib/more_core_extensions/core_ext/array.rb +2 -0
- data/lib/more_core_extensions/core_ext/array/compact_map.rb +19 -0
- data/lib/more_core_extensions/core_ext/array/deletes.rb +9 -0
- data/lib/more_core_extensions/core_ext/array/sorting.rb +57 -0
- data/lib/more_core_extensions/core_ext/array/tableize.rb +37 -3
- data/lib/more_core_extensions/core_ext/class/hierarchy.rb +25 -0
- data/lib/more_core_extensions/core_ext/digest.rb +1 -0
- data/lib/more_core_extensions/core_ext/digest/uuid.rb +26 -0
- data/lib/more_core_extensions/core_ext/hash/deletes.rb +10 -0
- data/lib/more_core_extensions/core_ext/object.rb +1 -1
- data/lib/more_core_extensions/core_ext/object/deep_send.rb +28 -0
- data/lib/more_core_extensions/core_ext/process.rb +1 -0
- data/lib/more_core_extensions/core_ext/process/pause_resume.rb +110 -0
- data/lib/more_core_extensions/core_ext/shared/nested.rb +15 -0
- data/lib/more_core_extensions/core_ext/string.rb +1 -0
- data/lib/more_core_extensions/core_ext/string/to_i_with_method.rb +87 -0
- data/lib/more_core_extensions/version.rb +1 -1
- data/more_core_extensions.gemspec +1 -0
- metadata +28 -10
- data/lib/more_core_extensions/core_ext/enumerable.rb +0 -1
- data/lib/more_core_extensions/core_ext/enumerable/sorting.rb +0 -39
- data/lib/more_core_extensions/core_ext/object/descendants.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2b7f22572ca60d54c6ae412b47afec772d698d9289f047736abec4cff3204d03
|
4
|
+
data.tar.gz: 51037bedd097a1d44a590c4a4502e42a2bc79841ca240fdc8ae429bb7887114d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f5a9444c922416547bfb24e4a3efcb9bda6fcecccd29f38a2ac12c637ee463018c80d51d68695a822c76030066d2f04731e3eb9a40edea141f6f1ddf7ce5e13
|
7
|
+
data.tar.gz: 9077ad1ea1242be1e82293ee5698b457d70962c9c405c0df127ae796000c2b1b29749906d7d5ef41fece1163205b2c508f129a33e0b342512c68f144a329a2c6
|
data/.travis.yml
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- "2.
|
4
|
-
- "2.
|
5
|
-
- "2.
|
6
|
-
- "2.
|
7
|
-
- "2.
|
8
|
-
- "2.
|
9
|
-
- "2.
|
3
|
+
- "2.1"
|
4
|
+
- "2.2"
|
5
|
+
- "2.3"
|
6
|
+
- "2.4"
|
7
|
+
- "2.5.8"
|
8
|
+
- "2.6.6"
|
9
|
+
- "2.7.1"
|
10
10
|
- ruby-head
|
11
11
|
- jruby-head
|
12
|
-
sudo: false
|
13
12
|
cache: bundler
|
14
13
|
after_script: bundle exec codeclimate-test-reporter
|
15
14
|
matrix:
|
16
15
|
allow_failures:
|
17
|
-
- rvm: "2.6.0"
|
18
16
|
- rvm: ruby-head
|
19
17
|
- rvm: jruby-head
|
20
18
|
fast_finish: true
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,40 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
5
5
|
## [Unreleased]
|
6
6
|
|
7
|
+
## [4.3.0] - 2020-10-27
|
8
|
+
### Added
|
9
|
+
- Add String#to_i_with_method and friends [[#95](https://github.com/ManageIQ/more_core_extensions/pull/95)]
|
10
|
+
- Add Object#deep_send [[#94](https://github.com/ManageIQ/more_core_extensions/pull/94)]
|
11
|
+
|
12
|
+
## [4.2.0] - 2020-07-20
|
13
|
+
### Added
|
14
|
+
- Add bundler-inject allowing developers to override dependencies [[#89](https://github.com/ManageIQ/more_core_extensions/pull/89)]
|
15
|
+
- Add Array and Hash #deep_clone and #deep_delete [[#91](https://github.com/ManageIQ/more_core_extensions/pull/91)]
|
16
|
+
- Add Digest::UUID.clean to properly format UUID strings [[#81](https://github.com/ManageIQ/more_core_extensions/pull/81)]
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- Update ArrayTableize to properly set field width for color text [[#87](https://github.com/ManageIQ/more_core_extensions/pull/87)]
|
20
|
+
- Change Array#format_table header output to markdown vs postgres [[#83](https://github.com/ManageIQ/more_core_extensions/pull/83)]
|
21
|
+
|
22
|
+
## [4.1.0] - 2020-04-30
|
23
|
+
### Added
|
24
|
+
- Added Ruby 2.7 support [[#79](https://github.com/ManageIQ/more_core_extensions/pull/79)]
|
25
|
+
- Added Process#pause, Process#resume, and Process#alive? [[#73](https://github.com/ManageIQ/more_core_extensions/pull/73)]
|
26
|
+
|
27
|
+
## [4.0.0] - 2020-01-31
|
28
|
+
### Changed
|
29
|
+
- **BREAKING**: Moved Object#descendant_get to Class#descendant_get [[#75](https://github.com/ManageIQ/more_core_extensions/pull/75)]
|
30
|
+
- **BREAKING**: Removed deprecated Enumerable#stable_sort_by [[#76](https://github.com/ManageIQ/more_core_extensions/pull/76)]
|
31
|
+
|
32
|
+
## [3.8.0] - 2020-01-31
|
33
|
+
### Changed
|
34
|
+
- Renamed Enumerable#stable_sort_by to Array#tabular_sort [[#68](https://github.com/ManageIQ/more_core_extensions/pull/68)]
|
35
|
+
- Deprecated Enumerable#stable_sort_by [[#74](https://github.com/ManageIQ/more_core_extensions/pull/74)]
|
36
|
+
|
37
|
+
### Added
|
38
|
+
- Added Class#leaf_subclasses [[#71](https://github.com/ManageIQ/more_core_extensions/pull/71)]
|
39
|
+
- Added Array#compact_map [[#63](https://github.com/ManageIQ/more_core_extensions/pull/63)]
|
40
|
+
|
7
41
|
## [3.7.0] - 2019-02-04
|
8
42
|
### Added
|
9
43
|
- Added Enumerable#stable_sort_by [[#67](https://github.com/ManageIQ/more_core_extensions/pull/67)]
|
@@ -68,7 +102,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
68
102
|
- Upgraded to RSpec 3 [[#16](https://github.com/ManageIQ/more_core_extensions/pull/16)]
|
69
103
|
- Added the Change Log!
|
70
104
|
|
71
|
-
[Unreleased]: https://github.com/ManageIQ/more_core_extensions/compare/
|
105
|
+
[Unreleased]: https://github.com/ManageIQ/more_core_extensions/compare/v4.3.0...HEAD
|
106
|
+
[4.3.0]: https://github.com/ManageIQ/more_core_extensions/compare/v4.2.0...v4.3.0
|
107
|
+
[4.2.0]: https://github.com/ManageIQ/more_core_extensions/compare/v4.1.0...v4.2.0
|
108
|
+
[4.1.0]: https://github.com/ManageIQ/more_core_extensions/compare/v4.0.0...v4.1.0
|
109
|
+
[4.0.0]: https://github.com/ManageIQ/more_core_extensions/compare/v3.8.0...v4.0.0
|
110
|
+
[3.8.0]: https://github.com/ManageIQ/more_core_extensions/compare/v3.7.0...v3.8.0
|
72
111
|
[3.7.0]: https://github.com/ManageIQ/more_core_extensions/compare/v3.6.0...v3.7.0
|
73
112
|
[3.6.0]: https://github.com/ManageIQ/more_core_extensions/compare/v3.5.0...v3.6.0
|
74
113
|
[3.5.0]: https://github.com/ManageIQ/more_core_extensions/compare/v3.4.0...v3.5.0
|
data/Gemfile
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
+
plugin 'bundler-inject'
|
4
|
+
require File.join(Bundler::Plugin.index.load_paths("bundler-inject")[0], "bundler-inject") rescue nil
|
5
|
+
|
3
6
|
# Specify your gem's dependencies in more_core_extensions.gemspec
|
4
7
|
gemspec
|
5
8
|
|
6
|
-
#
|
7
|
-
|
9
|
+
# Rails 5 dropped support for Ruby < 2.2.2
|
10
|
+
# Rails 6 dropped support for Ruby < 2.4.4
|
11
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.2.2")
|
12
|
+
active_support_version = "< 5"
|
13
|
+
elsif Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.4.4")
|
14
|
+
active_support_version = "< 6"
|
15
|
+
end
|
8
16
|
gem 'activesupport', active_support_version
|
data/README.md
CHANGED
@@ -14,9 +14,12 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS
|
|
14
14
|
|
15
15
|
#### Array
|
16
16
|
|
17
|
+
* core_ext/array/compact_map.rb
|
18
|
+
* `#compact_map` - Collect non-nil results from the block
|
17
19
|
* core_ext/array/deletes.rb
|
18
20
|
* `#delete_blanks` - Deletes all items where the value is blank
|
19
21
|
* `#delete_nils` - Deletes all items where the value is nil
|
22
|
+
* `#deep_delete` - Deletes nested hash key elements
|
20
23
|
* core_ext/array/duplicates.rb
|
21
24
|
* `#duplicates` - Returns an Array of the duplicates found
|
22
25
|
* core_ext/array/element_counts.rb
|
@@ -35,6 +38,8 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS
|
|
35
38
|
* core_ext/array/random.rb
|
36
39
|
* `#random_index` - Picks a valid index randomly
|
37
40
|
* `#random_element` - Picks an element randomly
|
41
|
+
* core_ext/array/sorting.rb
|
42
|
+
* `#tabular_sort` - Sorts an Array of Hashes by specific columns
|
38
43
|
* core_ext/array/stretch.rb
|
39
44
|
* `.stretch` - Stretch all argument Arrays to make them the same size
|
40
45
|
* `.stretch!` - Stretch all argument Arrays to make them the same size. Modifies the arguments in place.
|
@@ -47,14 +52,17 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS
|
|
47
52
|
#### Class
|
48
53
|
|
49
54
|
* core_ext/class/hierarchy.rb
|
55
|
+
* `#descendant_get` - Returns the descendant with a given name
|
50
56
|
* `#hierarchy` - Returns a tree-like Hash structure of all descendants.
|
51
57
|
* `#lineage` - Returns an Array of all superclasses.
|
58
|
+
* `#leaf_subclasses` - Returns an Array of all descendants which have no subclasses.
|
52
59
|
|
53
60
|
#### Hash
|
54
61
|
|
55
62
|
* core_ext/hash/deletes.rb
|
56
63
|
* `#delete_blanks` - Deletes all keys where the value is blank
|
57
64
|
* `#delete_nils` - Deletes all keys where the value is nil
|
65
|
+
* `#deep_delete` - Deletes nested hash key elements
|
58
66
|
* core_ext/hash/nested.rb (see [Shared](#shared))
|
59
67
|
* `#delete_blank_paths` - Deletes all paths where the value is blank
|
60
68
|
* core_ext/hash/sorting.rb (see [Shared](#shared))
|
@@ -81,11 +89,18 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS
|
|
81
89
|
|
82
90
|
#### Object
|
83
91
|
|
84
|
-
* core_ext/module/
|
85
|
-
* `#
|
92
|
+
* core_ext/module/deep_send.rb
|
93
|
+
* `#deep_send` - Invokes the specified methods continuously, unless encountering a nil value.
|
86
94
|
* core_ext/module/namespace.rb
|
87
95
|
* `#in_namespace?` - Returns whether or not the object is in the given namespace
|
88
96
|
|
97
|
+
#### Process
|
98
|
+
|
99
|
+
* core_ext/process/pause_resume.rb
|
100
|
+
* `.pause` - Pauses a process
|
101
|
+
* `.resume` - Resumes a paused process
|
102
|
+
* `.alive?` - Returns whether or not a process is running
|
103
|
+
|
89
104
|
#### Range
|
90
105
|
|
91
106
|
* core_ext/range/step_value.rb
|
@@ -109,6 +124,10 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS
|
|
109
124
|
* `#hex_dump` - Dumps the string in a hex editor style format
|
110
125
|
* core_ext/string/iec60027_2.rb
|
111
126
|
* `#iec_60027_2_to_i` - Convert strings with an IEC60027-2 suffix to an integer
|
127
|
+
* core_ext/string/to_i_with_method.rb
|
128
|
+
* `#to_f_with_method` - Converts to a Float while also evaluating a method invocation
|
129
|
+
* `#to_i_with_method` - Converts to an Integer while also evaluating a method invocation
|
130
|
+
* `#number_with_method?` - Determines if the object contains a number with a method invocation
|
112
131
|
|
113
132
|
#### Symbol
|
114
133
|
|
@@ -118,6 +137,7 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS
|
|
118
137
|
#### Shared
|
119
138
|
|
120
139
|
* core_ext/shared/nested.rb
|
140
|
+
* `#deep_clone` - Performs a Marshal based deep clone
|
121
141
|
* `#delete_path` - Delete the value at the specified nesting
|
122
142
|
* `#fetch_path` - Fetch the value at the specified nesting
|
123
143
|
* `#find_path` - Detect which nesting holds the specified value
|
@@ -3,12 +3,13 @@ require 'more_core_extensions/version'
|
|
3
3
|
require 'more_core_extensions/core_ext/array'
|
4
4
|
require 'more_core_extensions/core_ext/benchmark'
|
5
5
|
require 'more_core_extensions/core_ext/class'
|
6
|
-
require 'more_core_extensions/core_ext/
|
6
|
+
require 'more_core_extensions/core_ext/digest'
|
7
7
|
require 'more_core_extensions/core_ext/hash'
|
8
8
|
require 'more_core_extensions/core_ext/math'
|
9
9
|
require 'more_core_extensions/core_ext/module'
|
10
10
|
require 'more_core_extensions/core_ext/numeric'
|
11
11
|
require 'more_core_extensions/core_ext/object'
|
12
|
+
require 'more_core_extensions/core_ext/process'
|
12
13
|
require 'more_core_extensions/core_ext/range'
|
13
14
|
require 'more_core_extensions/core_ext/string'
|
14
15
|
require 'more_core_extensions/core_ext/symbol'
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'more_core_extensions/core_ext/array/compact_map'
|
1
2
|
require 'more_core_extensions/core_ext/array/deletes'
|
2
3
|
require 'more_core_extensions/core_ext/array/duplicates'
|
3
4
|
require 'more_core_extensions/core_ext/array/element_counts'
|
@@ -5,5 +6,6 @@ require 'more_core_extensions/core_ext/array/inclusions'
|
|
5
6
|
require 'more_core_extensions/core_ext/array/math'
|
6
7
|
require 'more_core_extensions/core_ext/array/nested'
|
7
8
|
require 'more_core_extensions/core_ext/array/random'
|
9
|
+
require 'more_core_extensions/core_ext/array/sorting'
|
8
10
|
require 'more_core_extensions/core_ext/array/stretch'
|
9
11
|
require 'more_core_extensions/core_ext/array/tableize'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MoreCoreExtensions
|
2
|
+
module ArrayCompactMap
|
3
|
+
# Collect non-nil results from the block. Basically [].collect { |i| ... }.compact
|
4
|
+
#
|
5
|
+
# [1,2,3,4,5].compact_map { |i| i * 2 if i.odd?} # => [2,6,10]
|
6
|
+
def compact_map
|
7
|
+
return enum_for(:compact_map) unless block_given?
|
8
|
+
|
9
|
+
[].tap do |results|
|
10
|
+
each do |i|
|
11
|
+
result = yield(i)
|
12
|
+
results << result if result
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Array.send(:include, MoreCoreExtensions::ArrayCompactMap)
|
@@ -15,6 +15,15 @@ module MoreCoreExtensions
|
|
15
15
|
def delete_blanks
|
16
16
|
delete_if { |i| i.blank? }
|
17
17
|
end
|
18
|
+
|
19
|
+
# Deletes all keys and subkeys that match +key+.
|
20
|
+
#
|
21
|
+
# [{:a => {:b => 2, :c => 3}}].deep_delete(:b) # => [{:a => {:c => 3}}]
|
22
|
+
#
|
23
|
+
def deep_delete(key)
|
24
|
+
each { |i| i.deep_delete(key) if i.respond_to?(:deep_delete) }
|
25
|
+
self
|
26
|
+
end
|
18
27
|
end
|
19
28
|
end
|
20
29
|
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module MoreCoreExtensions
|
2
|
+
module StableSorting
|
3
|
+
# Sorts an Array of Hashes by specific columns.
|
4
|
+
#
|
5
|
+
# Rows are sorted by +col_names+, if given, otherwise by the given block.
|
6
|
+
# The +order+ parameter can be given :ascending or :descending and
|
7
|
+
# defaults to :ascending.
|
8
|
+
#
|
9
|
+
# Note:
|
10
|
+
# - Strings are sorted case-insensitively
|
11
|
+
# - nil values are sorted last
|
12
|
+
# - Boolean values are sorted alphabetically (i.e. false then true)
|
13
|
+
#
|
14
|
+
# [
|
15
|
+
# {:col1 => 'b', :col2 => 2},
|
16
|
+
# {:col1 => 'b', :col2 => 1},
|
17
|
+
# {:col1 => 'A', :col2 => 1}
|
18
|
+
# ].tabular_sort([:col1, :col2])
|
19
|
+
#
|
20
|
+
# # => [
|
21
|
+
# # {:col1 => 'A', :col2 => 1},
|
22
|
+
# # {:col1 => 'b', :col2 => 1},
|
23
|
+
# # {:col1 => 'b', :col2 => 2}
|
24
|
+
# # ]
|
25
|
+
def tabular_sort(col_names = nil, order = nil, &block)
|
26
|
+
# stabilizer is needed because of
|
27
|
+
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/170565
|
28
|
+
stabilizer = 0
|
29
|
+
nil_rows, sortable =
|
30
|
+
partition do |r|
|
31
|
+
Array(col_names).any? { |c| r[c].nil? }
|
32
|
+
end
|
33
|
+
|
34
|
+
data_array =
|
35
|
+
if col_names
|
36
|
+
sortable.sort_by do |r|
|
37
|
+
stabilizer += 1
|
38
|
+
[Array(col_names).map do |col|
|
39
|
+
val = r[col]
|
40
|
+
val = val.downcase if val.kind_of?(String)
|
41
|
+
val = val.to_s if val.kind_of?(FalseClass) || val.kind_of?(TrueClass)
|
42
|
+
val
|
43
|
+
end, stabilizer]
|
44
|
+
end
|
45
|
+
else
|
46
|
+
sortable.sort_by(&block)
|
47
|
+
end.to_a
|
48
|
+
|
49
|
+
data_array += nil_rows
|
50
|
+
|
51
|
+
data_array.reverse! if order == :descending
|
52
|
+
data_array
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
Array.send(:include, MoreCoreExtensions::StableSorting)
|
@@ -53,6 +53,9 @@ module MoreCoreExtensions
|
|
53
53
|
|
54
54
|
private
|
55
55
|
|
56
|
+
ANSI_ESCAPE_SEQUENCE = /\e\[[^m]+m/.freeze
|
57
|
+
ANSI_RESET = "\e[0m".freeze
|
58
|
+
|
56
59
|
def tableize_hashes
|
57
60
|
# Convert the target to an Array of Arrays
|
58
61
|
keys = options[:columns] || columns_from_hash_keys
|
@@ -102,7 +105,7 @@ module MoreCoreExtensions
|
|
102
105
|
end
|
103
106
|
|
104
107
|
def apply_width!(widths, field, field_i)
|
105
|
-
widths[field_i] = [widths[field_i].to_i, field.to_s.length].max
|
108
|
+
widths[field_i] = [widths[field_i].to_i, ansi_strip(field.to_s).length].max
|
106
109
|
widths[field_i] = [options[:max_width], widths[field_i].to_i].min if options[:max_width]
|
107
110
|
end
|
108
111
|
|
@@ -118,13 +121,44 @@ module MoreCoreExtensions
|
|
118
121
|
end
|
119
122
|
|
120
123
|
def format_field(field, width, justification)
|
121
|
-
field = field.to_s.gsub(/\n|\r/, '')
|
124
|
+
field = field.to_s.gsub(/\n|\r/, '')
|
125
|
+
field = ansi_truncate(field, width)
|
122
126
|
"%0#{justification}#{width}s" % field
|
123
127
|
end
|
124
128
|
|
129
|
+
def ansi_escapes?(field)
|
130
|
+
!!field.match(ANSI_ESCAPE_SEQUENCE)
|
131
|
+
end
|
132
|
+
|
133
|
+
def ansi_escapes(field)
|
134
|
+
field.to_enum(:scan, ANSI_ESCAPE_SEQUENCE).map { Regexp.last_match }
|
135
|
+
end
|
136
|
+
|
137
|
+
def ansi_strip(field)
|
138
|
+
field.gsub(ANSI_ESCAPE_SEQUENCE, '')
|
139
|
+
end
|
140
|
+
|
141
|
+
def ansi_truncate(field, width)
|
142
|
+
escapes = ansi_escapes(field)
|
143
|
+
if escapes.none?
|
144
|
+
field.slice(0, width)
|
145
|
+
else
|
146
|
+
escape_widths = 0
|
147
|
+
escapes.each do |e|
|
148
|
+
break if e.offset(0).first - escape_widths >= width
|
149
|
+
|
150
|
+
escape_widths += e[0].size
|
151
|
+
end
|
152
|
+
|
153
|
+
field = field.slice(0, width + escape_widths)
|
154
|
+
field << ANSI_RESET if ansi_escapes?(field) && !field.end_with?(ANSI_RESET)
|
155
|
+
field
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
125
159
|
def format_table(table, widths)
|
126
160
|
if options[:header] && table.size > 1
|
127
|
-
header_separator = widths.collect { |w| "-" * (w + 2) }.join("
|
161
|
+
header_separator = widths.collect { |w| "-" * (w + 2) }.join("|")
|
128
162
|
table.insert(1, header_separator)
|
129
163
|
end
|
130
164
|
table.join("\n") << "\n"
|
@@ -3,6 +3,22 @@ require 'active_support/core_ext/object/try'
|
|
3
3
|
|
4
4
|
module MoreCoreExtensions
|
5
5
|
module ClassHierarchy
|
6
|
+
# Returns the descendant with a given name
|
7
|
+
#
|
8
|
+
# require 'socket'
|
9
|
+
# IO.descendant_get("IO")
|
10
|
+
# # => IO
|
11
|
+
# IO.descendant_get("BasicSocket")
|
12
|
+
# # => BasicSocket
|
13
|
+
# IO.descendant_get("IPSocket")
|
14
|
+
# # => IPSocket
|
15
|
+
def descendant_get(desc_name)
|
16
|
+
return self if desc_name == name || desc_name.nil?
|
17
|
+
klass = descendants.find { |desc| desc.name == desc_name }
|
18
|
+
raise ArgumentError, "#{desc_name} is not a descendant of #{name}" unless klass
|
19
|
+
klass
|
20
|
+
end
|
21
|
+
|
6
22
|
# Returns a tree-like Hash structure of all descendants.
|
7
23
|
#
|
8
24
|
# require 'socket'
|
@@ -24,6 +40,15 @@ module MoreCoreExtensions
|
|
24
40
|
def lineage
|
25
41
|
superclass.nil? ? [] : superclass.lineage.unshift(superclass)
|
26
42
|
end
|
43
|
+
|
44
|
+
# Returns an Array of all descendants which have no subclasses
|
45
|
+
#
|
46
|
+
# require 'socket'
|
47
|
+
# BasicSocket.leaf_subclasses
|
48
|
+
# # => [Socket, TCPServer, UDPSocket, UNIXServer]
|
49
|
+
def leaf_subclasses
|
50
|
+
descendants.select { |d| d.subclasses.empty? }
|
51
|
+
end
|
27
52
|
end
|
28
53
|
end
|
29
54
|
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'more_core_extensions/core_ext/digest/uuid'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'active_support/core_ext/digest/uuid'
|
2
|
+
|
3
|
+
module Digest
|
4
|
+
module UUID
|
5
|
+
UUID_REGEX_FORMAT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.freeze
|
6
|
+
|
7
|
+
# Takes a UUID string of varying formats and cleans it. It will strip invalid characters,
|
8
|
+
# such as leading and trailing brackets as well as whitespace. The result is a lowercased,
|
9
|
+
# canonical UUID string.
|
10
|
+
#
|
11
|
+
# If the +guid+ argument is nil or blank, then nil is returned. If the +guid+ is already
|
12
|
+
# clean, then no additional cleaning occurs, and it is returned as-is.
|
13
|
+
#
|
14
|
+
# @param guid [String] A string that should more or less represent a UUID.
|
15
|
+
# @return [String] A lowercase v4 UUID string stripped of any extraneous characters.
|
16
|
+
#
|
17
|
+
def self.clean(guid)
|
18
|
+
return nil if guid.nil?
|
19
|
+
g = guid.to_s.downcase
|
20
|
+
return nil if g.strip.empty?
|
21
|
+
return g if g.length == 36 && g =~ UUID_REGEX_FORMAT
|
22
|
+
g.delete!('^0-9a-f')
|
23
|
+
g.sub!(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/, '\1-\2-\3-\4-\5')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -15,6 +15,16 @@ module MoreCoreExtensions
|
|
15
15
|
def delete_blanks
|
16
16
|
delete_if { |k, v| v.blank? }
|
17
17
|
end
|
18
|
+
|
19
|
+
# Deletes all keys and subkeys that match +key+.
|
20
|
+
#
|
21
|
+
# {:a => {:b => 2, :c => 3}}.deep_delete(:b) # => {:a => {:c => 3}}
|
22
|
+
#
|
23
|
+
def deep_delete(key)
|
24
|
+
key = [key] unless key.kind_of?(Array)
|
25
|
+
key.each { |k| delete(k) }
|
26
|
+
each_value { |v| v.deep_delete(key) if v.respond_to?(:deep_delete) }
|
27
|
+
end
|
18
28
|
end
|
19
29
|
end
|
20
30
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module MoreCoreExtensions
|
2
|
+
module ObjectDeepSend
|
3
|
+
#
|
4
|
+
# Invokes the specified methods continuously, unless encountering a nil value.
|
5
|
+
#
|
6
|
+
# 10.deep_send("to_s.length") # => 2
|
7
|
+
# 10.deep_send("to_s", "length") # => 2
|
8
|
+
# 10.deep_send(:to_s, :length) # => 2
|
9
|
+
# 10.deep_send(["to_s", "length"]) # => 2
|
10
|
+
# [].deep_send("first.length") # => nil
|
11
|
+
#
|
12
|
+
def deep_send(*args)
|
13
|
+
args = args.first.dup if args.length == 1 && args.first.kind_of?(Array)
|
14
|
+
args = args.shift.to_s.strip.split('.') + args
|
15
|
+
|
16
|
+
arg = args.shift
|
17
|
+
raise ArgumentError if arg.nil?
|
18
|
+
|
19
|
+
result = send(arg)
|
20
|
+
return nil if result.nil?
|
21
|
+
return result if args.empty?
|
22
|
+
|
23
|
+
result.deep_send(args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Object.send(:include, MoreCoreExtensions::ObjectDeepSend)
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'more_core_extensions/core_ext/process/pause_resume'
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module MoreCoreExtensions
|
2
|
+
module ProcessPauseResume
|
3
|
+
if Gem.win_platform?
|
4
|
+
require 'fiddle'
|
5
|
+
ntdll = Fiddle.dlopen('ntdll')
|
6
|
+
kernel32 = Fiddle.dlopen('kernel32')
|
7
|
+
|
8
|
+
NtSuspendProcess = Fiddle::Function.new(
|
9
|
+
ntdll['NtSuspendProcess'],
|
10
|
+
[Fiddle::TYPE_UINTPTR_T],
|
11
|
+
Fiddle::TYPE_INT
|
12
|
+
)
|
13
|
+
|
14
|
+
private_constant :NtSuspendProcess
|
15
|
+
|
16
|
+
NtResumeProcess = Fiddle::Function.new(
|
17
|
+
ntdll['NtResumeProcess'],
|
18
|
+
[Fiddle::TYPE_UINTPTR_T],
|
19
|
+
Fiddle::TYPE_INT
|
20
|
+
)
|
21
|
+
|
22
|
+
private_constant :NtResumeProcess
|
23
|
+
|
24
|
+
OpenProcess = Fiddle::Function.new(
|
25
|
+
kernel32['OpenProcess'],
|
26
|
+
[Fiddle::TYPE_LONG, Fiddle::TYPE_INT, Fiddle::TYPE_LONG],
|
27
|
+
Fiddle::TYPE_UINTPTR_T
|
28
|
+
)
|
29
|
+
|
30
|
+
private_constant :OpenProcess
|
31
|
+
|
32
|
+
CloseHandle = Fiddle::Function.new(
|
33
|
+
kernel32['CloseHandle'],
|
34
|
+
[Fiddle::TYPE_UINTPTR_T],
|
35
|
+
Fiddle::TYPE_INT
|
36
|
+
)
|
37
|
+
|
38
|
+
private_constant :CloseHandle
|
39
|
+
|
40
|
+
PROCESS_SUSPEND_RESUME = 0x00000800
|
41
|
+
|
42
|
+
private_constant :PROCESS_SUSPEND_RESUME
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns whether or not the given process is running.
|
46
|
+
#
|
47
|
+
def alive?(pid)
|
48
|
+
Process.kill(0, pid)
|
49
|
+
true
|
50
|
+
rescue Errno::ESRCH
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Suspend the process +pid+. If the process isn't running then this is no-op.
|
55
|
+
#
|
56
|
+
if Gem.win_platform?
|
57
|
+
def pause(pid)
|
58
|
+
return unless alive?(pid)
|
59
|
+
|
60
|
+
begin
|
61
|
+
handle = OpenProcess.call(PROCESS_SUSPEND_RESUME, 0, pid)
|
62
|
+
|
63
|
+
if handle == 0
|
64
|
+
raise SystemCallError, Fiddle.win32_last_error, "OpenProcess"
|
65
|
+
end
|
66
|
+
|
67
|
+
if NtSuspendProcess.call(handle) != 0
|
68
|
+
raise SystemCallError, Fiddle.win32_last_error, "NtSuspendProcess"
|
69
|
+
end
|
70
|
+
ensure
|
71
|
+
CloseHandle.call(handle) if handle
|
72
|
+
end
|
73
|
+
1 # For cross-platform compatibility
|
74
|
+
end
|
75
|
+
else
|
76
|
+
def pause(pid)
|
77
|
+
Process.kill('STOP', pid) if alive?(pid)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Resume the process +pid+. If the process isn't running then this is a no-op.
|
82
|
+
#
|
83
|
+
if Gem.win_platform?
|
84
|
+
def resume(pid)
|
85
|
+
return unless alive?(pid)
|
86
|
+
|
87
|
+
begin
|
88
|
+
handle = OpenProcess.call(PROCESS_SUSPEND_RESUME, 0, pid)
|
89
|
+
|
90
|
+
if handle == 0
|
91
|
+
raise SystemCallError, Fiddle.win32_last_error, "OpenProcess"
|
92
|
+
end
|
93
|
+
|
94
|
+
if NtResumeProcess.call(handle) != 0
|
95
|
+
raise SystemCallError, Fiddle.win32_last_error, "NtResumeProcess"
|
96
|
+
end
|
97
|
+
ensure
|
98
|
+
CloseHandle.call(handle) if handle
|
99
|
+
end
|
100
|
+
1 # For cross-platform compatibility
|
101
|
+
end
|
102
|
+
else
|
103
|
+
def resume(pid)
|
104
|
+
Process.kill('CONT', pid) if alive?(pid)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
Process.send(:extend, MoreCoreExtensions::ProcessPauseResume)
|
@@ -100,6 +100,21 @@ module MoreCoreExtensions
|
|
100
100
|
end
|
101
101
|
[]
|
102
102
|
end
|
103
|
+
|
104
|
+
# Create a deep clone of the object. This is similar to deep_dup
|
105
|
+
# but uses a Marshal based approach instead.
|
106
|
+
#
|
107
|
+
# h1 = {:a => "hello"}
|
108
|
+
# h2 = h1.deep_clone
|
109
|
+
#
|
110
|
+
# h1[:a] << " world"
|
111
|
+
#
|
112
|
+
# h1[:a] # "hello world"
|
113
|
+
# h2[:a] # "hello"
|
114
|
+
#
|
115
|
+
def deep_clone
|
116
|
+
Marshal.load(Marshal.dump(self))
|
117
|
+
end
|
103
118
|
end
|
104
119
|
end
|
105
120
|
end
|
@@ -2,3 +2,4 @@ require 'more_core_extensions/core_ext/string/formats'
|
|
2
2
|
require 'more_core_extensions/core_ext/string/hex_dump'
|
3
3
|
require 'more_core_extensions/core_ext/string/iec60027_2'
|
4
4
|
require 'more_core_extensions/core_ext/string/decimal_suffix'
|
5
|
+
require 'more_core_extensions/core_ext/string/to_i_with_method'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module MoreCoreExtensions
|
2
|
+
module StringToIWithMethod
|
3
|
+
NUMBER_WITH_METHOD_REGEX = /^([0-9\.,]+)\.([a-z]+)$/.freeze
|
4
|
+
|
5
|
+
# Converts to an Integer while also evaluating a method invocation
|
6
|
+
#
|
7
|
+
# This method is similar to #to_i, but does not support extraneous characters
|
8
|
+
# nor bases other than 10.
|
9
|
+
#
|
10
|
+
# "20".to_i_with_method # => 20
|
11
|
+
# "20.percent".to_i_with_method # => 20
|
12
|
+
# "20.megabytes".to_i_with_method # => 20_971_520
|
13
|
+
#
|
14
|
+
# "20.0".to_i_with_method # => 20
|
15
|
+
# "20.0.percent".to_i_with_method # => 20
|
16
|
+
# "20.0.megabytes".to_i_with_method # => 20_971_520
|
17
|
+
#
|
18
|
+
# 20.to_i_with_method # => 20
|
19
|
+
# 20.0.to_i_with_method # => 20
|
20
|
+
# nil.to_i_with_method # => 0
|
21
|
+
#
|
22
|
+
def to_i_with_method
|
23
|
+
to_x_with_method.to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
# Converts to a Float while also evaluating a method invocation
|
27
|
+
#
|
28
|
+
# This method is similar to #to_f, but does not support extraneous characters.
|
29
|
+
#
|
30
|
+
# "20".to_f_with_method # => 20.0
|
31
|
+
# "20.percent".to_f_with_method # => 20.0
|
32
|
+
# "20.megabytes".to_f_with_method # => 20_971_520.0
|
33
|
+
#
|
34
|
+
# "20.1".to_f_with_method # => 20.1
|
35
|
+
# "20.1.percent".to_f_with_method # => 20.1
|
36
|
+
# "20.1.megabytes".to_f_with_method # => 21_076_377.6
|
37
|
+
#
|
38
|
+
# 20.to_f_with_method # => 20.0
|
39
|
+
# 20.1.to_f_with_method # => 20.1
|
40
|
+
# nil.to_f_with_method # => 0.0
|
41
|
+
#
|
42
|
+
def to_f_with_method
|
43
|
+
to_x_with_method.to_f
|
44
|
+
end
|
45
|
+
|
46
|
+
private def to_x_with_method
|
47
|
+
n = delete(',')
|
48
|
+
return n unless n =~ NUMBER_WITH_METHOD_REGEX && $2 != "percent"
|
49
|
+
|
50
|
+
n = $1.include?('.') ? $1.to_f : $1.to_i
|
51
|
+
n.send($2)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Determines if the object contains a number with a method invocation
|
55
|
+
#
|
56
|
+
# "20".number_with_method? # => false
|
57
|
+
# "20.percent".number_with_method? # => true
|
58
|
+
# "20.0.percent".number_with_method? # => true
|
59
|
+
def number_with_method?
|
60
|
+
self =~ NUMBER_WITH_METHOD_REGEX
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module NumericAndNilToIWithMethod
|
65
|
+
# See String#to_i_with_method
|
66
|
+
def to_i_with_method
|
67
|
+
to_i
|
68
|
+
end
|
69
|
+
|
70
|
+
# See String#to_f_with_method
|
71
|
+
def to_f_with_method
|
72
|
+
to_f
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module ObjectToIWithMethod
|
77
|
+
# See String#number_with_method?
|
78
|
+
def number_with_method?
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
String.send(:prepend, MoreCoreExtensions::StringToIWithMethod)
|
85
|
+
Numeric.send(:prepend, MoreCoreExtensions::NumericAndNilToIWithMethod)
|
86
|
+
NilClass.send(:prepend, MoreCoreExtensions::NumericAndNilToIWithMethod)
|
87
|
+
Object.send(:prepend, MoreCoreExtensions::ObjectToIWithMethod)
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: more_core_extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 4.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Frey
|
8
8
|
- Brandon Dunne
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-10-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -109,6 +109,20 @@ dependencies:
|
|
109
109
|
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: sync
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :runtime
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
112
126
|
description: MoreCoreExtensions are a set of core extensions beyond those provided
|
113
127
|
by ActiveSupport.
|
114
128
|
email:
|
@@ -133,6 +147,7 @@ files:
|
|
133
147
|
- lib/more_core_extensions.rb
|
134
148
|
- lib/more_core_extensions/all.rb
|
135
149
|
- lib/more_core_extensions/core_ext/array.rb
|
150
|
+
- lib/more_core_extensions/core_ext/array/compact_map.rb
|
136
151
|
- lib/more_core_extensions/core_ext/array/deletes.rb
|
137
152
|
- lib/more_core_extensions/core_ext/array/duplicates.rb
|
138
153
|
- lib/more_core_extensions/core_ext/array/element_counts.rb
|
@@ -140,14 +155,15 @@ files:
|
|
140
155
|
- lib/more_core_extensions/core_ext/array/math.rb
|
141
156
|
- lib/more_core_extensions/core_ext/array/nested.rb
|
142
157
|
- lib/more_core_extensions/core_ext/array/random.rb
|
158
|
+
- lib/more_core_extensions/core_ext/array/sorting.rb
|
143
159
|
- lib/more_core_extensions/core_ext/array/stretch.rb
|
144
160
|
- lib/more_core_extensions/core_ext/array/tableize.rb
|
145
161
|
- lib/more_core_extensions/core_ext/benchmark.rb
|
146
162
|
- lib/more_core_extensions/core_ext/benchmark/realtime_store.rb
|
147
163
|
- lib/more_core_extensions/core_ext/class.rb
|
148
164
|
- lib/more_core_extensions/core_ext/class/hierarchy.rb
|
149
|
-
- lib/more_core_extensions/core_ext/
|
150
|
-
- lib/more_core_extensions/core_ext/
|
165
|
+
- lib/more_core_extensions/core_ext/digest.rb
|
166
|
+
- lib/more_core_extensions/core_ext/digest/uuid.rb
|
151
167
|
- lib/more_core_extensions/core_ext/hash.rb
|
152
168
|
- lib/more_core_extensions/core_ext/hash/deletes.rb
|
153
169
|
- lib/more_core_extensions/core_ext/hash/nested.rb
|
@@ -162,8 +178,10 @@ files:
|
|
162
178
|
- lib/more_core_extensions/core_ext/numeric/math.rb
|
163
179
|
- lib/more_core_extensions/core_ext/numeric/rounding.rb
|
164
180
|
- lib/more_core_extensions/core_ext/object.rb
|
165
|
-
- lib/more_core_extensions/core_ext/object/
|
181
|
+
- lib/more_core_extensions/core_ext/object/deep_send.rb
|
166
182
|
- lib/more_core_extensions/core_ext/object/namespace.rb
|
183
|
+
- lib/more_core_extensions/core_ext/process.rb
|
184
|
+
- lib/more_core_extensions/core_ext/process/pause_resume.rb
|
167
185
|
- lib/more_core_extensions/core_ext/range.rb
|
168
186
|
- lib/more_core_extensions/core_ext/range/step_value.rb
|
169
187
|
- lib/more_core_extensions/core_ext/shared/nested.rb
|
@@ -172,6 +190,7 @@ files:
|
|
172
190
|
- lib/more_core_extensions/core_ext/string/formats.rb
|
173
191
|
- lib/more_core_extensions/core_ext/string/hex_dump.rb
|
174
192
|
- lib/more_core_extensions/core_ext/string/iec60027_2.rb
|
193
|
+
- lib/more_core_extensions/core_ext/string/to_i_with_method.rb
|
175
194
|
- lib/more_core_extensions/core_ext/symbol.rb
|
176
195
|
- lib/more_core_extensions/core_ext/symbol/to_i.rb
|
177
196
|
- lib/more_core_extensions/version.rb
|
@@ -180,7 +199,7 @@ homepage: http://github.com/ManageIQ/more_core_extensions
|
|
180
199
|
licenses:
|
181
200
|
- MIT
|
182
201
|
metadata: {}
|
183
|
-
post_install_message:
|
202
|
+
post_install_message:
|
184
203
|
rdoc_options: []
|
185
204
|
require_paths:
|
186
205
|
- lib
|
@@ -195,9 +214,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
214
|
- !ruby/object:Gem::Version
|
196
215
|
version: '0'
|
197
216
|
requirements: []
|
198
|
-
|
199
|
-
|
200
|
-
signing_key:
|
217
|
+
rubygems_version: 3.0.3
|
218
|
+
signing_key:
|
201
219
|
specification_version: 4
|
202
220
|
summary: MoreCoreExtensions are a set of core extensions beyond those provided by
|
203
221
|
ActiveSupport.
|
@@ -1 +0,0 @@
|
|
1
|
-
require 'more_core_extensions/core_ext/enumerable/sorting'
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module MoreCoreExtensions
|
2
|
-
module StableSorting
|
3
|
-
def self.included(klass)
|
4
|
-
klass.class_eval do
|
5
|
-
def stable_sort_by(col_names = nil, order = nil, &block)
|
6
|
-
# stabilizer is needed because of
|
7
|
-
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/170565
|
8
|
-
stabilizer = 0
|
9
|
-
nil_rows, sortable =
|
10
|
-
partition do |r|
|
11
|
-
Array(col_names).any? { |c| r[c].nil? }
|
12
|
-
end
|
13
|
-
|
14
|
-
data_array =
|
15
|
-
if col_names
|
16
|
-
sortable.sort_by do |r|
|
17
|
-
stabilizer += 1
|
18
|
-
[Array(col_names).map do |col|
|
19
|
-
val = r[col]
|
20
|
-
val = val.downcase if val.kind_of?(String)
|
21
|
-
val = val.to_s if val.kind_of?(FalseClass) || val.kind_of?(TrueClass)
|
22
|
-
val
|
23
|
-
end, stabilizer]
|
24
|
-
end
|
25
|
-
else
|
26
|
-
sortable.sort_by(&block)
|
27
|
-
end.to_a
|
28
|
-
|
29
|
-
data_array += nil_rows
|
30
|
-
|
31
|
-
data_array.reverse! if order == :descending
|
32
|
-
data_array
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
Enumerable.send(:include, MoreCoreExtensions::StableSorting)
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/class/subclasses'
|
2
|
-
|
3
|
-
module MoreCoreExtensions
|
4
|
-
module Descendants
|
5
|
-
#
|
6
|
-
# Retrieve a descendant by its name
|
7
|
-
#
|
8
|
-
def descendant_get(desc_name)
|
9
|
-
return self if desc_name == name || desc_name.nil?
|
10
|
-
klass = descendants.find { |desc| desc.name == desc_name }
|
11
|
-
raise ArgumentError, "#{desc_name} is not a descendant of #{name}" unless klass
|
12
|
-
klass
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
Object.send(:include, MoreCoreExtensions::Descendants)
|