more_core_extensions 3.7.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
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)