more_core_extensions 3.1.1 → 3.2.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
2
  SHA1:
3
- metadata.gz: 813c1118ca8bd1502d467502dbc10c1965cfccb8
4
- data.tar.gz: 44f901136763832691e94be69e73cc90e82d3a16
3
+ metadata.gz: a1add9ba7e2ca9ea1cd89792999f0a40b1ae00a0
4
+ data.tar.gz: 21149cb02025e1c91ad29e50a68bc4df68746808
5
5
  SHA512:
6
- metadata.gz: 82cbb225f4ba697fc29cc1b6b6ef0d1e00561de1fb6071d8078cae1981aa0209f983b843e787841706b4f584121b1a0c9d29078942ea1f606b682c58f79a4e65
7
- data.tar.gz: 94645bbb55387932f89711371041e09bc4c00a7d7c43fe6224d52acf2d9ce00bb7f416ff2eb308ef3a3a8d8f26789657c178e8e095fa58e4d4294769708347e9
6
+ metadata.gz: 40638498bbcb894d6722018c78c9ce10eecb32eb7a64b60233735c6f442cc8a18d3685020348db60fb658a0c8b6f3bcc7cd4e6ead1cb407355ff9298c1758374
7
+ data.tar.gz: 8644345821bb5bef19fecd89e4e5575dd36ff6210146cfcbd029a2dd9310c607b7ad4ca1bf0e6776ff1fa2ddbfb402ce6c052b4d08978cba5a9ee7fee4bbbf82
data/README.md CHANGED
@@ -59,6 +59,8 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS
59
59
 
60
60
  #### Numeric
61
61
 
62
+ * core_ext/numeric/clamp.rb
63
+ * `#clamp` - Clamp a number to a minimum and/or maximum value
62
64
  * core_ext/numeric/math.rb
63
65
  * `#square` - Returns the square of a Numeric
64
66
  * core_ext/numeric/rounding.rb
@@ -82,6 +84,8 @@ MoreCoreExtensions are a set of core extensions beyond those provided by ActiveS
82
84
  * `#guid?` - Returns whether or not the String is a valid GUID
83
85
  * core_ext/string/hex_dump.rb
84
86
  * `#hex_dump` - Dumps the string in a hex editor style format
87
+ * core_ext/string/iec60027_2.rb
88
+ * `#iec_60027_2_to_i` - Convert strings with an IEC60027-2 suffix to an integer
85
89
 
86
90
  #### Shared
87
91
 
@@ -31,59 +31,104 @@ module MoreCoreExtensions
31
31
  # Value3 | Value4
32
32
  #
33
33
  def tableize(options = {})
34
- case self.first
35
- when Array; tableize_arrays(options)
36
- when Hash; tableize_hashes(options)
37
- else raise "must be an Array of Arrays or Array of Hashes"
38
- end
34
+ Tableizer.new(self, options).tableize
39
35
  end
40
36
 
41
- private
37
+ # This class is a private implementation and not part of the public API.
38
+ class Tableizer
39
+ attr_accessor :target, :options
42
40
 
43
- def tableize_arrays(options)
44
- options[:header] = true unless options.has_key?(:header)
41
+ def initialize(target, options)
42
+ @target = target
43
+ @options = options
44
+ end
45
45
 
46
- widths = []
47
- justifications = []
48
- self.each do |row|
49
- row.each_with_index do |field, field_i|
50
- widths[field_i] = [widths[field_i].to_i, field.to_s.length].max
51
- widths[field_i] = [options[:max_width], widths[field_i].to_i].min if options[:max_width]
46
+ def tableize
47
+ case target.first
48
+ when Array then tableize_arrays
49
+ when Hash then tableize_hashes
50
+ else raise "must be an Array of Arrays or Array of Hashes"
51
+ end
52
+ end
52
53
 
53
- justifications[field_i] = field.kind_of?(Numeric) ? "" : "-"
54
+ private
55
+
56
+ def tableize_hashes
57
+ # Convert the target to an Array of Arrays
58
+ keys = options[:columns] || columns_from_hash_keys
59
+ self.target = target.collect { |h| h.values_at(*keys) }.unshift(keys)
60
+ options[:header] = true
61
+
62
+ tableize_arrays
63
+ end
64
+
65
+ def columns_from_hash_keys
66
+ target.first.keys.sort_by(&:to_s).tap do |keys|
67
+ apply_leading_columns!(keys)
68
+ apply_trailing_columns!(keys)
54
69
  end
55
70
  end
56
71
 
57
- header_separator = widths.collect { |w| "-" * (w + 2) }.join("+")
72
+ def apply_leading_columns!(keys)
73
+ return unless options[:leading_columns]
74
+ options[:leading_columns].reverse_each { |h| keys.unshift(keys.delete(h)) }
75
+ end
76
+
77
+ def apply_trailing_columns!(keys)
78
+ return unless options[:trailing_columns]
79
+ options[:trailing_columns].each { |h| keys.push(keys.delete(h)) }
80
+ end
81
+
82
+ def tableize_arrays
83
+ options[:header] = true unless options.key?(:header)
58
84
 
59
- table = []
60
- self.each_with_index do |row, row_i|
61
- r = []
62
- row.each_with_index do |field, field_i|
63
- r << sprintf("%0#{justifications[field_i]}#{widths[field_i]}s", field.to_s.gsub(/\n|\r/, '').slice(0, widths[field_i]))
85
+ widths, justifications = widths_and_justifications
86
+ table = target.collect { |row| format_row(row, widths, justifications) }
87
+ format_table(table, widths)
88
+ end
89
+
90
+ def widths_and_justifications
91
+ widths = []
92
+ justifications = []
93
+
94
+ target.each do |row|
95
+ row.each.with_index do |field, field_i|
96
+ apply_width!(widths, field, field_i)
97
+ apply_justification!(justifications, field, field_i)
98
+ end
64
99
  end
65
- r = " #{r.join(' | ')} ".rstrip
66
100
 
67
- table << r
68
- table << header_separator if row_i == 0 && options[:header]
101
+ return widths, justifications
102
+ end
103
+
104
+ def apply_width!(widths, field, field_i)
105
+ widths[field_i] = [widths[field_i].to_i, field.to_s.length].max
106
+ widths[field_i] = [options[:max_width], widths[field_i].to_i].min if options[:max_width]
107
+ end
108
+
109
+ def apply_justification!(justifications, field, field_i)
110
+ justifications[field_i] = field.kind_of?(Numeric) ? "" : "-"
111
+ end
112
+
113
+ def format_row(row, widths, justifications)
114
+ formatted_fields = row.collect.with_index do |field, field_i|
115
+ format_field(field, widths[field_i], justifications[field_i])
116
+ end
117
+ " #{formatted_fields.join(' | ')} ".rstrip
69
118
  end
70
- table.join("\n") << "\n"
71
- end
72
119
 
73
- def tableize_hashes(options)
74
- if options[:columns]
75
- keys = options[:columns]
76
- elsif options[:leading_columns] || options[:trailing_columns]
77
- keys = self.first.keys.sort_by(&:to_s)
78
- options[:leading_columns].reverse.each { |h| keys.unshift(keys.delete(h)) } if options[:leading_columns]
79
- options[:trailing_columns].each { |h| keys.push(keys.delete(h)) } if options[:trailing_columns]
80
- else
81
- keys = self.first.keys.sort_by(&:to_s)
120
+ def format_field(field, width, justification)
121
+ field = field.to_s.gsub(/\n|\r/, '').slice(0, width)
122
+ "%0#{justification}#{width}s" % field
82
123
  end
83
124
 
84
- options = options.dup
85
- options[:header] = true
86
- self.collect { |h| h.values_at(*keys) }.unshift(keys).tableize(options)
125
+ def format_table(table, widths)
126
+ if options[:header] && table.size > 1
127
+ header_separator = widths.collect { |w| "-" * (w + 2) }.join("+")
128
+ table.insert(1, header_separator)
129
+ end
130
+ table.join("\n") << "\n"
131
+ end
87
132
  end
88
133
  end
89
134
  end
@@ -1,2 +1,3 @@
1
+ require 'more_core_extensions/core_ext/numeric/clamp'
1
2
  require 'more_core_extensions/core_ext/numeric/math'
2
3
  require 'more_core_extensions/core_ext/numeric/rounding'
@@ -0,0 +1,18 @@
1
+ module MoreCoreExtensions
2
+ module NumericClamp
3
+ #
4
+ # Clamp a number to a minimum and/or maximum value.
5
+ #
6
+ # 8.clamp(nil, nil) #=> 8
7
+ # 8.clamp(9, nil) #=> 9
8
+ # 8.clamp(nil, 6) #=> 6
9
+ def clamp(min, max)
10
+ value = self
11
+ value = [value, min].max if min
12
+ value = [value, max].min if max
13
+ value
14
+ end
15
+ end
16
+ end
17
+
18
+ Numeric.send(:prepend, MoreCoreExtensions::NumericClamp)
@@ -1,2 +1,3 @@
1
1
  require 'more_core_extensions/core_ext/string/formats'
2
2
  require 'more_core_extensions/core_ext/string/hex_dump'
3
+ require 'more_core_extensions/core_ext/string/iec60027_2'
@@ -1,7 +1,7 @@
1
1
  module MoreCoreExtensions
2
2
  module StringFormats
3
3
  # From: http://www.regular-expressions.info/email.html
4
- RE_EMAIL = %r{\A[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z}
4
+ RE_EMAIL = %r{\A[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z}i
5
5
 
6
6
  def email?
7
7
  !!(self =~ RE_EMAIL)
@@ -9,47 +9,59 @@ module MoreCoreExtensions
9
9
  # instead of returning as a string.
10
10
  # :meth:: Used in conjunction with _:obj_ to send each line to an object
11
11
  # instead of returning as a string.
12
- def hex_dump(*opts)
13
- opts = opts[0] if opts.empty? || (opts.length == 1 && opts[0].kind_of?(Hash))
14
- raise ArgumentError, "opts must be a Hash" unless opts.nil? || opts.kind_of?(Hash)
12
+ def hex_dump(options = {})
13
+ HexDumper.new(self, options).dump
14
+ end
15
+
16
+ # This class is a private implementation and not part of the public API.
17
+ class HexDumper
18
+ attr_reader :target, :options
15
19
 
16
- opts = {:grouping => 16, :newline => true, :start_pos => 0}.merge!(opts || {})
17
- obj, meth, grouping, newline, pos = opts.values_at(:obj, :meth, :grouping, :newline, :start_pos)
18
- raise ArgumentError, "obj and meth must both be set, or both not set" if (obj.nil? && !meth.nil?) || (!obj.nil? && meth.nil?)
20
+ DEFAULT_OPTIONS = {
21
+ :grouping => 16,
22
+ :newline => true,
23
+ :start_pos => 0
24
+ }.freeze
19
25
 
20
- row_format = "0x%08x #{"%02x " * grouping} "
26
+ def initialize(target, options)
27
+ @target = target
28
+ @options = DEFAULT_OPTIONS.merge(options)
21
29
 
22
- i = 0
23
- last_i = self.length - 1
30
+ if !!options[:obj] ^ !!options[:meth] # rubocop:disable Style/DoubleNegation
31
+ raise ArgumentError, "obj and meth must both be set, or both not set"
32
+ end
33
+ end
24
34
 
25
- ret = ''
26
- row_vals = []
27
- row_chars = ''
35
+ def dump
36
+ obj, meth, grouping, pos = options.values_at(:obj, :meth, :grouping, :start_pos)
28
37
 
29
- self.each_byte do |c|
30
- row_vals << c
31
- row_chars << (c < 0x20 || (c >= 0x7F && c < 0xA0) ? '.' : c.chr)
38
+ ret = ""
32
39
 
33
- if (i + 1) % grouping == 0 || i == last_i
34
- row_format = "0x%08x #{"%02x " * row_vals.length}#{" " * (grouping - row_vals.length)} " if i == last_i
40
+ target.each_byte.each_slice(grouping) do |bytes|
41
+ row = format_row(pos, bytes)
42
+ ret << row
35
43
 
36
- row_vals.unshift(pos)
37
- ret << (row_format % row_vals) << row_chars
38
- ret << "\n" if newline
39
44
  if obj
40
- obj.send(meth, ret)
41
- ret.replace('')
45
+ obj.send(meth, row)
46
+ ret = ""
42
47
  end
43
48
 
44
49
  pos += grouping
45
- row_vals.clear
46
- row_chars = ''
47
50
  end
48
51
 
49
- i += 1
52
+ ret
50
53
  end
51
54
 
52
- return ret
55
+ def format_row(pos, bytes)
56
+ padding = " " * (options[:grouping] - bytes.size)
57
+ byte_chars = bytes.collect { |byte| row_char(byte) }.join
58
+ newline = "\n" if options[:newline]
59
+ "0x%08x #{"%02x " * bytes.size}#{padding} " % [pos, *bytes] << "#{byte_chars}#{newline}"
60
+ end
61
+
62
+ def row_char(byte)
63
+ byte < 0x20 || (byte >= 0x7F && byte < 0xA0) ? '.' : byte.chr
64
+ end
53
65
  end
54
66
  end
55
67
  end
@@ -0,0 +1,19 @@
1
+ module MoreCoreExtensions
2
+ module IEC60027_2
3
+ # Support converting strings with an IEC60027-2 suffix to an integer
4
+ # http://physics.nist.gov/cuu/Units/binary.html
5
+ # Example: "1 Ki".iec_60027_2_to_i => 1024
6
+
7
+ IEC_60027_2_SIZE_SUFFIXES = %w(Ki Mi Gi Ti Pi Ei Zi Yi).freeze
8
+ def iec_60027_2_to_i
9
+ suffix_index = IEC_60027_2_SIZE_SUFFIXES.index(self[-2..-1])
10
+ if suffix_index.nil?
11
+ Integer(self)
12
+ else
13
+ Integer(self[0..-3]) * (2**10)**(suffix_index + 1)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ String.send(:prepend, MoreCoreExtensions::IEC60027_2)
@@ -1,3 +1,3 @@
1
1
  module MoreCoreExtensions
2
- VERSION = "3.1.1".freeze
2
+ VERSION = "3.2.0".freeze
3
3
  end
@@ -0,0 +1,17 @@
1
+ describe Numeric do
2
+ it "#clamp" do
3
+ expect(8.clamp(nil, nil)).to eq(8)
4
+ expect(8.clamp(3, nil)).to eq(8)
5
+ expect(8.clamp(13, nil)).to eq(13)
6
+ expect(8.clamp(nil, 6)).to eq(6)
7
+ expect(8.clamp(13, 16)).to eq(13)
8
+ expect(20.clamp(13, 16)).to eq(16)
9
+
10
+ expect(8.0.clamp(nil, nil)).to eq(8.0)
11
+ expect(8.0.clamp(3.0, nil)).to eq(8.0)
12
+ expect(8.0.clamp(13.0, nil)).to eq(13.0)
13
+ expect(8.0.clamp(nil, 6.0)).to eq(6.0)
14
+ expect(8.0.clamp(13.0, 16.0)).to eq(13.0)
15
+ expect(20.0.clamp(13.0, 16.0)).to eq(16.0)
16
+ end
17
+ end
@@ -4,6 +4,8 @@ describe String do
4
4
  expect("john.doe@example.com").to be_email
5
5
  expect("john.doe@my-company.prestidigitation").to be_email
6
6
  expect("john.o'doe@example.com").to be_email
7
+ expect("john.doe@EXAMPLE.COM").to be_email
8
+ expect("John.Doe@example.com").to be_email
7
9
 
8
10
  expect("john\ndoe@example.com").not_to be_email
9
11
  expect("").not_to be_email
@@ -0,0 +1,13 @@
1
+ describe String do
2
+ it '#iec60027_2' do
3
+ expect("1 ".iec_60027_2_to_i).to eq(1)
4
+ expect("1 Ki".iec_60027_2_to_i).to eq(1_024)
5
+ expect("1 Mi".iec_60027_2_to_i).to eq(1_048_576)
6
+ expect("1 Gi".iec_60027_2_to_i).to eq(1_073_741_824)
7
+ expect("1 Ti".iec_60027_2_to_i).to eq(1_099_511_627_776)
8
+ expect("1 Pi".iec_60027_2_to_i).to eq(1_125_899_906_842_624)
9
+ expect("1 Ei".iec_60027_2_to_i).to eq(1_152_921_504_606_846_976)
10
+ expect("1 Zi".iec_60027_2_to_i).to eq(1_180_591_620_717_411_303_424)
11
+ expect("1 Yi".iec_60027_2_to_i).to eq(1_208_925_819_614_629_174_706_176)
12
+ end
13
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: more_core_extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Frey
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-12-16 00:00:00.000000000 Z
12
+ date: 2017-03-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -111,6 +111,7 @@ files:
111
111
  - lib/more_core_extensions/core_ext/module.rb
112
112
  - lib/more_core_extensions/core_ext/module/namespace.rb
113
113
  - lib/more_core_extensions/core_ext/numeric.rb
114
+ - lib/more_core_extensions/core_ext/numeric/clamp.rb
114
115
  - lib/more_core_extensions/core_ext/numeric/math.rb
115
116
  - lib/more_core_extensions/core_ext/numeric/rounding.rb
116
117
  - lib/more_core_extensions/core_ext/object.rb
@@ -119,6 +120,7 @@ files:
119
120
  - lib/more_core_extensions/core_ext/string.rb
120
121
  - lib/more_core_extensions/core_ext/string/formats.rb
121
122
  - lib/more_core_extensions/core_ext/string/hex_dump.rb
123
+ - lib/more_core_extensions/core_ext/string/iec60027_2.rb
122
124
  - lib/more_core_extensions/version.rb
123
125
  - spec/core_ext/array/deletes_spec.rb
124
126
  - spec/core_ext/array/duplicates_spec.rb
@@ -131,11 +133,13 @@ files:
131
133
  - spec/core_ext/array/tableize_spec.rb
132
134
  - spec/core_ext/hash/deletes_spec.rb
133
135
  - spec/core_ext/hash/nested_spec.rb
136
+ - spec/core_ext/numeric/clamp_spec.rb
134
137
  - spec/core_ext/numeric/math_spec.rb
135
138
  - spec/core_ext/numeric/rounding_spec.rb
136
139
  - spec/core_ext/object/namespace_spec.rb
137
140
  - spec/core_ext/string/formats_spec.rb
138
141
  - spec/core_ext/string/hex_dump_spec.rb
142
+ - spec/core_ext/string/iec60027_2_spec.rb
139
143
  - spec/spec_helper.rb
140
144
  homepage: http://github.com/ManageIQ/more_core_extensions
141
145
  licenses:
@@ -174,10 +178,12 @@ test_files:
174
178
  - spec/core_ext/array/tableize_spec.rb
175
179
  - spec/core_ext/hash/deletes_spec.rb
176
180
  - spec/core_ext/hash/nested_spec.rb
181
+ - spec/core_ext/numeric/clamp_spec.rb
177
182
  - spec/core_ext/numeric/math_spec.rb
178
183
  - spec/core_ext/numeric/rounding_spec.rb
179
184
  - spec/core_ext/object/namespace_spec.rb
180
185
  - spec/core_ext/string/formats_spec.rb
181
186
  - spec/core_ext/string/hex_dump_spec.rb
187
+ - spec/core_ext/string/iec60027_2_spec.rb
182
188
  - spec/spec_helper.rb
183
189
  - ".rspec"