epitools 0.5.122 → 0.5.128

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
2
  SHA256:
3
- metadata.gz: 8fee320e00e56ea94488a469e34add3705b207b5da4e01b255cd0b643122b1b9
4
- data.tar.gz: 61181faf99b9ac4c5e98339a7a99b2233914877f25c376999b09ab05657ad444
3
+ metadata.gz: 55db8684ff41f2465660f1b532823595eda3c5ffadcd15e43538c29a5ea5ef20
4
+ data.tar.gz: e866e2ebc77c18506142528d9de6da16db99a03f31f1caed72960329439d2a8e
5
5
  SHA512:
6
- metadata.gz: a475668151f44db4b2025d6de6138eeed1686c9f44d0addb836b5ea406b672ed325121822081990eefa030941e70d496aca6aa5c51138d70494c4bab262f7fc3
7
- data.tar.gz: 5ee5027d1ff20292ae0956623f9a630a398416c80ec3a62321ac55e7b34b9e2ca0ba3bfda904f90684c30c20ddf93fda0bade1966a0698b605c8fa7d2d425cf1
6
+ metadata.gz: 4d3c32dd1040271ae4eaf4e6d4a190d0d3518ceff7eb91a668ab243a306410460bc6336262e12f2d8db2b6e5f5ad87ae91c13e3eff23a2f31b4fb999b671cf29
7
+ data.tar.gz: '086ea2d99290c419af5cecd34bcfd367517429bee37f2ff0a404178b8ae50f46ba2fd68b147f456bd0ea946b89665f592c463c47b4423f6d953d317b3cc5ea5f'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.122
1
+ 0.5.128
@@ -1,7 +1,6 @@
1
1
  ## Standard library
2
2
 
3
3
  autoload :Set, 'set'
4
- autoload :URI, 'uri'
5
4
  autoload :Zlib, 'zlib'
6
5
  autoload :FileUtils, 'fileutils'
7
6
  autoload :Tempfile, 'tempfile'
@@ -23,6 +22,10 @@ autoload :CSV, 'csv'
23
22
  autoload :SDBM, 'sdbm'
24
23
  autoload :StringScanner, 'strscan'
25
24
 
25
+ autoreq :URI do
26
+ require 'epitools/core_ext/uri'
27
+ end
28
+
26
29
  module Digest
27
30
  autoload :SHA1, 'digest/sha1'
28
31
  autoload :SHA2, 'digest/sha2'
@@ -80,8 +83,13 @@ autoload :SemanticVersion, 'epitools/semantic_version'
80
83
  autoload :Matrix, 'epitools/core_ext/matrix'
81
84
  autoreq(:Vector) { Matrix }
82
85
 
83
- module Epi; autoload :Slop, 'epitools/slop'; end
84
- autoreq(:Slop) { Slop = Epi::Slop }
86
+ ## Bundled slop
87
+ module Epi
88
+ autoload :Slop, 'epitools/slop'
89
+ end
90
+ autoreq(:Slop) do
91
+ Slop = Epi::Slop
92
+ end
85
93
 
86
94
  ## Gems (common)
87
95
 
@@ -230,3 +230,20 @@ def notify_send(title, body=nil, icon: nil, time: 5)
230
230
  fork { system *cmd }
231
231
  end
232
232
 
233
+ def curl(url)
234
+ curl_open(url).read
235
+ end
236
+
237
+ def curl_open(url, headers={})
238
+ # headers["User-Agent"] ||= "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/85 Version/11.1.1 Safari/605.1.15"
239
+ cmd = ["curl", "-LSs"]
240
+ headers.each { |k,v| cmd += ["-H", "#{k}: #{v}"] }
241
+ cmd << url
242
+ IO.popen(cmd)
243
+ rescue Errno::ENOENT
244
+ raise "Error: 'curl' isn't installed."
245
+ end
246
+
247
+ def curl_json(url)
248
+ JSON.parse(curl(url))
249
+ end
@@ -5,16 +5,23 @@ require 'epitools'
5
5
  require 'epitools/core_ext/object'
6
6
  require 'epitools/core_ext/class'
7
7
  require 'epitools/core_ext/module'
8
+
8
9
  require 'epitools/core_ext/string'
10
+
9
11
  require 'epitools/core_ext/array'
10
- require 'epitools/core_ext/file'
11
- require 'epitools/core_ext/io'
12
12
  require 'epitools/core_ext/enumerable'
13
13
  require 'epitools/core_ext/hash'
14
+
14
15
  require 'epitools/core_ext/numbers'
15
16
  require 'epitools/core_ext/truthiness'
16
17
  require 'epitools/core_ext/range'
17
18
  require 'epitools/core_ext/time'
19
+
20
+ require 'epitools/core_ext/argv'
21
+ require 'epitools/core_ext/file'
22
+ require 'epitools/core_ext/io'
23
+ require 'epitools/core_ext/uri'
24
+
18
25
  require 'epitools/core_ext/misc'
19
26
 
20
27
  ##############################################################################
@@ -0,0 +1,59 @@
1
+ class << ARGV
2
+
3
+ def parse!
4
+ unless @opts or @args
5
+ @opts, @args = partition { |arg| arg[/^--?\w{1,2}/].nil? }
6
+ @opts = @opts.map { |opt| key, val = opt.strip(/^-+/).split("=") }.to_ostruct
7
+ if @opts.help
8
+ @help_handler.call
9
+ exit
10
+ end
11
+ end
12
+
13
+ [@opts, @args]
14
+ end
15
+
16
+ def parse
17
+ parse!
18
+ [@opts, @args]
19
+ end
20
+
21
+ def help?
22
+ !!@opts["help"]
23
+ end
24
+
25
+ def opts
26
+ parse! unless @opts
27
+ @opts
28
+ end
29
+
30
+ def args
31
+ @args ? @args : opts && @args
32
+ end
33
+
34
+ def paths
35
+ map(&:to_Path)
36
+ end
37
+
38
+ def paths_R
39
+ recursive_proc = proc do |paths|
40
+ paths.map { |path| path.dir? ? the_expander.(path.ls_R) : path }
41
+ end
42
+
43
+ recursive_proc.(paths)
44
+ end
45
+ alias_method :recursive_paths, :paths_R
46
+
47
+ def on_help(&block)
48
+ @help_handler = block
49
+ end
50
+
51
+ def regexes(escaped: true, case_sensitive: false)
52
+ if case_sensitive
53
+ map { |arg| /#{escaped ? Regexp.escape(arg) : arg}/ } # NO 'i'
54
+ else
55
+ map { |arg| /#{escaped ? Regexp.escape(arg) : arg}/i }
56
+ end
57
+ end
58
+
59
+ end
@@ -324,6 +324,33 @@ class Array
324
324
  super.to_a
325
325
  end
326
326
 
327
- end
327
+ module ToCSV
328
+ #
329
+ # Convert this enumerable into a CSV string (nb: enumerable must contain either all Hashes or all Arrays)
330
+ #
331
+ def to_csv(delimiter=",")
332
+ types = count_by(&:class)
333
+
334
+ unless types.size == 1 and (types[Array] > 0 or types[Hash] > 0)
335
+ raise "Error: this array must contain nothing but arrays, or nothing but hashes (actually contains: #{types.inspect})"
336
+ end
337
+
338
+ options = {}
339
+ options[:col_sep] = delimiter
340
+ options[:headers] = flat_map(&:keys).uniq if types[Hash] > 0
328
341
 
342
+ CSV.generate(nil, **options) do |csv|
343
+ each { |obj| csv << obj }
344
+ end
345
+ end
329
346
 
347
+ #
348
+ # Like #to_csv, but with tab-separated CSV fields
349
+ #
350
+ def to_tsv
351
+ to_csv("\t")
352
+ end
353
+ end
354
+
355
+ prepend ToCSV # dirty hack to stop the CSV module from clobbering the to_csv method
356
+ end
@@ -1,4 +1,7 @@
1
+ require 'epitools/core_ext/array'
2
+
1
3
  module Enumerable
4
+ include Array::ToCSV
2
5
 
3
6
  #
4
7
  # 'true' if the Enumerable has no elements
@@ -128,79 +128,6 @@ end
128
128
 
129
129
 
130
130
 
131
- module URI
132
-
133
- #
134
- # Return a Hash of the variables in the query string
135
- #
136
- def params
137
- query ? query.to_params : {}
138
- end
139
-
140
- #
141
- # URIs are strings, dammit!
142
- #
143
- def to_str
144
- to_s
145
- end
146
-
147
- #
148
- # Default user agent for the 'get' method
149
- #
150
- # USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0"
151
-
152
- #
153
- # Get this URI using Net::HTTP
154
- #
155
- def get(headers={}, redirect_limit=10)
156
- raise "Sorry, URI can't get from #{scheme.inspect} URIs yet" unless scheme =~ /^https?$/
157
- raise 'Too many HTTP redirections' if redirect_limit == 0
158
-
159
- # headers['User-Agent'] ||= USER_AGENT
160
-
161
- # response = Net::HTTP.start(host, port) do |http|
162
- # # the_path = path.empty? ? "/" : path
163
- # req = Net::HTTP::Get.new(self, headers)
164
- # http.request(req)
165
- # end
166
-
167
- response = Net::HTTP.get_response(self)
168
-
169
- case response
170
- when Net::HTTPSuccess
171
- response
172
- when Net::HTTPRedirection
173
- # puts "redirect: #{response['location']}"
174
- URI(response['location']).get(headers, redirect_limit-1)
175
- else
176
- response.error!
177
- end
178
- end
179
-
180
- end
181
-
182
- #
183
- # Stupid workaround for URI blowing up when it receives a [ or ] character
184
- #
185
- module Better_URI_RFC3986_Parser # ::RFC3986_relative_ref
186
- ESCAPE_ME_PLZ = "[]{}!"
187
-
188
- def split(uri)
189
- subsitutions = ESCAPE_ME_PLZ.chars.map { |c| [c, CGI.escape(c)] }
190
- subsitutions << [" ", "%20"]
191
-
192
- subsitutions.each do |find, replace|
193
- uri = uri.gsub(find, replace)
194
- end
195
-
196
- super(uri)
197
- end
198
-
199
- end
200
-
201
- URI::RFC3986_Parser.prepend(Better_URI_RFC3986_Parser)
202
-
203
-
204
131
 
205
132
  #
206
133
  # Give ObjectSpace Enumerable powers (select, map, etc.)
@@ -21,6 +21,13 @@ class String
21
21
  gsub("\r\n", "\n")
22
22
  end
23
23
 
24
+ #
25
+ # Escape shell characters (globs, quotes, parens, etc.)
26
+ #
27
+ def shellescape
28
+ Shellwords.escape(self)
29
+ end
30
+
24
31
  #
25
32
  # Remove redundant whitespaces (not including newlines).
26
33
  #
@@ -0,0 +1,97 @@
1
+ require 'uri'
2
+
3
+ module URI
4
+
5
+ #
6
+ # Return a Hash of the variables in the query string
7
+ #
8
+ def params
9
+ (@query ? @query.to_params : {})
10
+ end
11
+
12
+ #
13
+ # Update all the params at once
14
+ #
15
+ def params=(new_params)
16
+ self.query = new_params.to_params
17
+ end
18
+
19
+ #
20
+ # Update one URI parameter
21
+ #
22
+ def set_param(key, value)
23
+ current = params
24
+ current[key] = value
25
+ self.query = current.to_query
26
+ end
27
+
28
+ #
29
+ # Get the query string
30
+ #
31
+ def query
32
+ params.to_query
33
+ end
34
+
35
+ #
36
+ # URIs *are* strings, dammit!
37
+ #
38
+ def to_str
39
+ to_s
40
+ end
41
+
42
+ #
43
+ # Default user agent for the 'get' method
44
+ #
45
+ # USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0"
46
+
47
+ #
48
+ # Get this URI using Net::HTTP
49
+ #
50
+ def get(headers={}, redirect_limit=10)
51
+ raise "Sorry, URI can't get from #{scheme.inspect} URIs yet" unless scheme =~ /^https?$/
52
+ raise 'Too many HTTP redirections' if redirect_limit == 0
53
+
54
+ # headers['User-Agent'] ||= USER_AGENT
55
+
56
+ # response = Net::HTTP.start(host, port) do |http|
57
+ # # the_path = path.empty? ? "/" : path
58
+ # req = Net::HTTP::Get.new(self, headers)
59
+ # http.request(req)
60
+ # end
61
+
62
+ response = Net::HTTP.get_response(self)
63
+
64
+ case response
65
+ when Net::HTTPSuccess
66
+ response
67
+ when Net::HTTPRedirection
68
+ # puts "redirect: #{response['location']}"
69
+ URI(response['location']).get(headers, redirect_limit-1)
70
+ else
71
+ response.error!
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ #
78
+ # Stupid workaround for URI blowing up when it receives a [ or ] character
79
+ #
80
+ module Better_URI_RFC3986_Parser # ::RFC3986_relative_ref
81
+ ESCAPE_ME_PLZ = "[]{}!"
82
+
83
+ def split(uri)
84
+ subsitutions = ESCAPE_ME_PLZ.chars.map { |c| [c, CGI.escape(c)] }
85
+ subsitutions << [" ", "%20"]
86
+
87
+ subsitutions.each do |find, replace|
88
+ uri = uri.gsub(find, replace)
89
+ end
90
+
91
+ super(uri)
92
+ end
93
+
94
+ end
95
+
96
+ URI::RFC3986_Parser.prepend(Better_URI_RFC3986_Parser)
97
+
@@ -1,8 +1,10 @@
1
+ ####################################################################################
1
2
  #
2
3
  # If you require 'epitools/minimal' instead of 'epitools',
3
4
  # this is what you get: core_ext monkeypatches and all the
4
5
  # autoloads; the barest essentials for survival.
5
6
  #
7
+ ####################################################################################
6
8
 
7
9
  if RUBY_VERSION[/^1.8/]
8
10
  require 'enumerator'
@@ -14,6 +16,7 @@ RbConfig = Config unless defined? RbConfig
14
16
  Infinity = Float::INFINITY
15
17
  Inf = Float::INFINITY
16
18
 
19
+ ####################################################################################
17
20
  class Object
18
21
 
19
22
  unless defined?(__DIR__)
@@ -164,14 +167,13 @@ class Object
164
167
  #
165
168
  # eg: stuff.group_by(&:self)
166
169
  #
167
- define_method :self do
170
+ def self
168
171
  if block_given?
169
172
  yield self
170
173
  else
171
174
  self
172
175
  end
173
176
  end
174
- alias_method :fap, :self
175
177
 
176
178
  def ancestors
177
179
  self.class.ancestors
@@ -179,6 +181,7 @@ class Object
179
181
  end
180
182
 
181
183
 
184
+ ####################################################################################
182
185
  #
183
186
  # Patch 'Module#const_missing' to support 'autoreq' (which can autoload gems)
184
187
  #
@@ -213,6 +216,8 @@ class Module
213
216
  end
214
217
 
215
218
 
219
+
220
+ ####################################################################################
216
221
  module Kernel
217
222
 
218
223
  #
@@ -254,10 +259,8 @@ module Kernel
254
259
  end
255
260
 
256
261
  end
257
- ####################################################################################
258
-
259
-
260
262
 
263
+ ####################################################################################
261
264
  class String
262
265
 
263
266
  #
@@ -270,6 +273,7 @@ class String
270
273
 
271
274
  end
272
275
 
276
+ ####################################################################################
273
277
  #
274
278
  # Path("/some/path") is an alias for Path["/some/path"]
275
279
  #
@@ -277,43 +281,7 @@ def Path(arg)
277
281
  Path[arg]
278
282
  end
279
283
 
280
- ####################################################################
281
-
282
- # class << ARGV
283
-
284
- # def opts
285
- # @opts, @args ||= partition { |arg| arg[/^--?\w{1,2}/].nil? }
286
- # end
287
-
288
- # def args
289
- # @args ? @args : opts && @args
290
- # end
291
-
292
- # def paths
293
- # map(&:to_Path)
294
- # end
295
-
296
- # def paths_R
297
- # recursive_proc = proc do |paths|
298
- # paths.map { |path| path.dir? ? the_expander.(path.ls_R) : path }
299
- # end
300
-
301
- # recursive_proc.(paths)
302
- # end
303
- # alias_method :recursive_paths, :paths_R
304
-
305
- # def regexes(escaped: true, case_sensitive: false)
306
- # if case_sensitive
307
- # map { |arg| /#{escaped ? Regexp.escape(arg) : arg}/ } # NO 'i'
308
- # else
309
- # map { |arg| /#{escaped ? Regexp.escape(arg) : arg}/i }
310
- # end
311
- # end
312
-
313
- # end
314
284
 
315
285
  ####################################################################
316
-
317
286
  require 'epitools/autoloads'
318
-
319
287
  ####################################################################
@@ -117,7 +117,8 @@ class Numeric
117
117
  factor = 10**pow
118
118
 
119
119
  if is_a?(Float)
120
- (BigDecimal.new(to_s) * factor).to_i
120
+ BigDecimal
121
+ (BigDecimal(to_s) * factor).to_i
121
122
  else
122
123
  self * factor
123
124
  end
@@ -1110,6 +1110,25 @@ class Path
1110
1110
  }
1111
1111
  end
1112
1112
 
1113
+ #
1114
+ # Path.mkcd(path) creates a path if it doesn't exist, and changes to it (temporarily, if a block is provided)
1115
+ #
1116
+ def self.mkcd(path, &block)
1117
+ path = path.to_Path unless path.is_a? Path
1118
+ path.mkdir_p unless path.exists?
1119
+
1120
+ raise "Error: #{path} couldn't be created." unless path.dir?
1121
+
1122
+ self.cd(path, &block)
1123
+ end
1124
+
1125
+ #
1126
+ # Path.mkcd(self)
1127
+ #
1128
+ def mkcd(&block)
1129
+ Path.mkcd(self, &block)
1130
+ end
1131
+
1113
1132
  def cp_r(dest)
1114
1133
  FileUtils.cp_r(path, dest) #if Path[dest].exists?
1115
1134
  dest
@@ -61,7 +61,7 @@ module WM
61
61
 
62
62
  def self.all
63
63
  # FIXME: Windows owned by linux-namespaced processes (firejail) report their namspaced pid to X11. `window.process` ends up pointing at either nil, or the wrong process.
64
- `wmctrl -lpG`.lines.map(&:strip).map { |line| Window.from_line(line) }
64
+ `wmctrl -lpG`.lines.map(&:strip).map { |line| Window.from_line(line) unless line.blank? }.compact
65
65
  end
66
66
 
67
67
  def self.from_line(line)
@@ -106,23 +106,6 @@ module WM
106
106
  system "wmctrl", "-i", "-a", window_id
107
107
  end
108
108
 
109
- def maximized?
110
- end
111
-
112
- def maximize!
113
- system "wmctrl", "-i", "-a", window_id
114
- end
115
-
116
-
117
- def unmazimize!
118
- end
119
-
120
- def minimized?
121
- end
122
-
123
- def minimize!
124
- end
125
-
126
109
  #
127
110
  # string is made up of regular text, plus <>'d keypresses
128
111
  # eg: "Hello<Ctrl-T><Ctrl-L><Ctrl-Shift-K><Return>!!!"
@@ -795,6 +795,38 @@ describe Enumerable do
795
795
  ps.sort_numerically.map(&:filename).should == proper
796
796
  end
797
797
 
798
+ it "to_csvs and to_tsvs" do
799
+ data = [
800
+ ["1", "2", "3"],
801
+ ["4", "5", "6"],
802
+ ["7", "8", "9", "10"],
803
+ ]
804
+
805
+ hash_data = [
806
+ {a: 1, b: 2, c: 3},
807
+ {a: 4, b: 5, c: 6},
808
+ {a: 7, b: 8, c: 9, d: 10},
809
+ ]
810
+
811
+ class EnumedData
812
+ include Enumerable
813
+ def initialize(data); @data=data; end
814
+ def each(&block); @data.each(&block); end
815
+ end
816
+
817
+ [data, hash_data, EnumedData.new(data)].each do |a|
818
+ str = a.to_csv
819
+ str.each_line.count.should == 3
820
+ str.should_not be_nil
821
+ str["1,2,3"].should_not be_nil
822
+
823
+ str = a.to_tsv
824
+ str.should_not be_nil
825
+ str["1\t2\t3"].should_not be_nil
826
+ end
827
+
828
+ end
829
+
798
830
  end
799
831
 
800
832
 
@@ -1242,6 +1274,16 @@ describe File do
1242
1274
 
1243
1275
  end
1244
1276
 
1277
+ describe ARGV do
1278
+
1279
+ # it "parses args" do
1280
+ # ARGV.clear
1281
+ # ARGV << "-d"
1282
+ # ARGV.parse!
1283
+ # p ARGV.opts
1284
+ # end
1285
+
1286
+ end
1245
1287
 
1246
1288
  describe "Anything" do
1247
1289
 
@@ -1275,5 +1317,13 @@ describe URI do
1275
1317
  (response.size > 0).should == true
1276
1318
  end
1277
1319
 
1320
+ it "params=" do
1321
+ u = "http://butt.com/?q=1".to_uri
1322
+ u.query.should == "q=1"
1323
+ u.params["q"] = 2
1324
+ u.params["q"].should == 2
1325
+ u.query.should == "q=2"
1326
+ end
1327
+
1278
1328
  end
1279
1329
 
@@ -348,6 +348,25 @@ describe Path do
348
348
  lambda { tmp2.mkdir_p }.should_not raise_error
349
349
  end
350
350
 
351
+ it "mkcds" do
352
+ tmp = Path.tmpdir
353
+ tmp.dir?.should == true
354
+ tmp.rm
355
+ tmp.exists?.should == false
356
+
357
+ Path.mkcd(tmp) do
358
+ Path.pwd.path.should == tmp.path
359
+ end
360
+
361
+ tmp.rm
362
+
363
+ tmp.mkcd do
364
+ Path.pwd.path.should == tmp.path
365
+ end
366
+
367
+ tmp.rm
368
+ end
369
+
351
370
  it "has classmethods" do
352
371
  path = Path.tmpfile
353
372
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: epitools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.122
4
+ version: 0.5.128
5
5
  platform: ruby
6
6
  authors:
7
7
  - epitron
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-30 00:00:00.000000000 Z
11
+ date: 2020-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -51,6 +51,7 @@ files:
51
51
  - lib/epitools/clitools.rb
52
52
  - lib/epitools/colored.rb
53
53
  - lib/epitools/core_ext.rb
54
+ - lib/epitools/core_ext/argv.rb
54
55
  - lib/epitools/core_ext/array.rb
55
56
  - lib/epitools/core_ext/class.rb
56
57
  - lib/epitools/core_ext/enumerable.rb
@@ -66,6 +67,7 @@ files:
66
67
  - lib/epitools/core_ext/string.rb
67
68
  - lib/epitools/core_ext/time.rb
68
69
  - lib/epitools/core_ext/truthiness.rb
70
+ - lib/epitools/core_ext/uri.rb
69
71
  - lib/epitools/daemonize.rb
70
72
  - lib/epitools/hexdump.rb
71
73
  - lib/epitools/iter.rb
@@ -124,7 +126,7 @@ homepage: http://github.com/epitron/epitools
124
126
  licenses:
125
127
  - WTFPL
126
128
  metadata: {}
127
- post_install_message:
129
+ post_install_message:
128
130
  rdoc_options: []
129
131
  require_paths:
130
132
  - lib
@@ -139,8 +141,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
141
  - !ruby/object:Gem::Version
140
142
  version: '0'
141
143
  requirements: []
142
- rubygems_version: 3.0.4
143
- signing_key:
144
+ rubygems_version: 3.1.4
145
+ signing_key:
144
146
  specification_version: 3
145
147
  summary: Not utils... METILS!
146
148
  test_files: []