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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 45280e85179a5cec518a1a078c3ef57502042771
4
- data.tar.gz: a38d6af1cc7f313f4f0254fd466ba3c8309d42cd
2
+ SHA256:
3
+ metadata.gz: 2b7f22572ca60d54c6ae412b47afec772d698d9289f047736abec4cff3204d03
4
+ data.tar.gz: 51037bedd097a1d44a590c4a4502e42a2bc79841ca240fdc8ae429bb7887114d
5
5
  SHA512:
6
- metadata.gz: ca86d8e6a9a9ff26fbc1a6a5ad96d2b7ef05dc637f9474f0f160bee51b38e97d625cbc6ff85c7765886f38947b924f528c54ea7c99fa3e2fe1a4fcadac2e706b
7
- data.tar.gz: 68af4c4bb3230ece550bc6908016bd4faf5f2f495f4c9e9871704203aeac84fa83b576fa6654d20444f3197b5ce4b57c9bb988f87a6cdbe466ce073e4ddca8ca
6
+ metadata.gz: 8f5a9444c922416547bfb24e4a3efcb9bda6fcecccd29f38a2ac12c637ee463018c80d51d68695a822c76030066d2f04731e3eb9a40edea141f6f1ddf7ce5e13
7
+ data.tar.gz: 9077ad1ea1242be1e82293ee5698b457d70962c9c405c0df127ae796000c2b1b29749906d7d5ef41fece1163205b2c508f129a33e0b342512c68f144a329a2c6
@@ -1,20 +1,18 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "2.0"
4
- - "2.1.10"
5
- - "2.2.10"
6
- - "2.3.8"
7
- - "2.4.5"
8
- - "2.5.3"
9
- - "2.6.0"
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
@@ -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/v3.7.0...HEAD
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
- # HACK: Rails 5 dropped support for Ruby < 2.2.2
7
- active_support_version = "< 5" if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.2.2")
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/descendants.rb
85
- * `#descendant_get` - Returns the descendant with a given name
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/enumerable'
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/, '').slice(0, width)
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
 
@@ -1,3 +1,3 @@
1
1
  require 'active_support/core_ext/object/blank'
2
+ require 'more_core_extensions/core_ext/object/deep_send'
2
3
  require 'more_core_extensions/core_ext/object/namespace'
3
- require 'more_core_extensions/core_ext/object/descendants'
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module MoreCoreExtensions
2
- VERSION = "3.7.0".freeze
2
+ VERSION = "4.3.0".freeze
3
3
  end
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency "timecop"
31
31
 
32
32
  spec.add_runtime_dependency "activesupport"
33
+ spec.add_runtime_dependency "sync"
33
34
  end
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.7.0
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: 2019-02-04 00:00:00.000000000 Z
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/enumerable.rb
150
- - lib/more_core_extensions/core_ext/enumerable/sorting.rb
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/descendants.rb
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
- rubyforge_project:
199
- rubygems_version: 2.6.14.3
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)