judges 0.21.0 → 0.22.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
2
  SHA256:
3
- metadata.gz: f826572e575f1c40c556cb5c0de0a2ca5aaf39554ef9d4219e517c6d8492236b
4
- data.tar.gz: 7f16f70af23a1fb4903934939133015289f06d4a276c32a567356dd60e3fa75f
3
+ metadata.gz: 17df0b6c88dd19b576bc701737985db26986e9818eb3c87b36180be92fca54f6
4
+ data.tar.gz: 48ef9ea5656a6bd78264f0b1f7021555ffa92eaf5be71d311d60e59ab0d3c0c8
5
5
  SHA512:
6
- metadata.gz: f3b4e89fcd538caddf490ca8769c2142e6c06bec83eb99582bf426f48ce35617ce169d08d604a9d4ee2780d88f0cc877651fabd863efffc5cb2d1ee96cf6a6ec
7
- data.tar.gz: 4dd669683164fcd9402f9f4043b5fab0faa086cc68de9f0a6dc1f6e78f9959f5a8a2f18eb3c8fed23986d735010e71c964313090acba222966803b02bd8ba9e3
6
+ metadata.gz: 722ef79a6af057ba1f4cf37860212b6a3c08f7aff838dfb4e8034e7970e27c4efe98e66da9c774e45707999b9e70966eacd19e4a1478a54a798d12b1d03b662b
7
+ data.tar.gz: c9551f302a6f0f02ef81e4a2ef5631c98bc2ad08a39982563749b29867d0bf06258a5e7a167179f70f899031cf2ffe097a014e9480a0a767192915d68f78479a
data/.rubocop.yml CHANGED
@@ -58,3 +58,7 @@ Naming/MethodParameterName:
58
58
  MinNameLength: 2
59
59
  Layout/EndOfLine:
60
60
  EnforcedStyle: lf
61
+ Layout/MultilineAssignmentLayout:
62
+ Enabled: true
63
+ Layout/FirstHashElementIndentation:
64
+ EnforcedStyle: consistent
data/Gemfile CHANGED
@@ -30,7 +30,7 @@ gem 'net-ping', '2.0.8', require: false
30
30
  gem 'rake', '13.2.1', require: false
31
31
  gem 'random-port', '~>0.0', require: false
32
32
  gem 'rspec-rails', '6.1.3', require: false
33
- gem 'rubocop', '1.65.0', require: false
33
+ gem 'rubocop', '1.65.1', require: false
34
34
  gem 'rubocop-performance', '1.21.1', require: false
35
35
  gem 'rubocop-rspec', '3.0.3', require: false
36
36
  gem 'simplecov', '0.22.0', require: false
data/Gemfile.lock CHANGED
@@ -71,7 +71,7 @@ GEM
71
71
  multi_test (~> 1.1)
72
72
  sys-uname (~> 1.2)
73
73
  cucumber-ci-environment (10.0.1)
74
- cucumber-core (13.0.2)
74
+ cucumber-core (13.0.3)
75
75
  cucumber-gherkin (>= 27, < 28)
76
76
  cucumber-messages (>= 20, < 23)
77
77
  cucumber-tag-expressions (> 5, < 7)
@@ -85,12 +85,12 @@ GEM
85
85
  cucumber-tag-expressions (6.1.0)
86
86
  decoor (0.0.1)
87
87
  diff-lcs (1.5.1)
88
- docile (1.4.0)
88
+ docile (1.4.1)
89
89
  drb (2.2.1)
90
90
  erubi (1.13.0)
91
91
  ethon (0.16.0)
92
92
  ffi (>= 1.15.0)
93
- factbase (0.2.0)
93
+ factbase (0.2.1)
94
94
  backtrace (> 0)
95
95
  decoor (> 0)
96
96
  json (~> 2.7)
@@ -120,7 +120,7 @@ GEM
120
120
  loofah (2.22.0)
121
121
  crass (~> 1.0.2)
122
122
  nokogiri (>= 1.12.0)
123
- loog (0.5.2)
123
+ loog (0.5.3)
124
124
  mini_mime (1.1.5)
125
125
  minitest (5.24.1)
126
126
  minitest-reporters (1.7.1)
@@ -132,19 +132,19 @@ GEM
132
132
  multi_test (1.1.0)
133
133
  mutex_m (0.2.0)
134
134
  net-ping (2.0.8)
135
- nokogiri (1.16.6-aarch64-linux)
135
+ nokogiri (1.16.7-aarch64-linux)
136
136
  racc (~> 1.4)
137
- nokogiri (1.16.6-arm-linux)
137
+ nokogiri (1.16.7-arm-linux)
138
138
  racc (~> 1.4)
139
- nokogiri (1.16.6-arm64-darwin)
139
+ nokogiri (1.16.7-arm64-darwin)
140
140
  racc (~> 1.4)
141
- nokogiri (1.16.6-x64-mingw-ucrt)
141
+ nokogiri (1.16.7-x64-mingw-ucrt)
142
142
  racc (~> 1.4)
143
- nokogiri (1.16.6-x86-linux)
143
+ nokogiri (1.16.7-x86-linux)
144
144
  racc (~> 1.4)
145
- nokogiri (1.16.6-x86_64-darwin)
145
+ nokogiri (1.16.7-x86_64-darwin)
146
146
  racc (~> 1.4)
147
- nokogiri (1.16.6-x86_64-linux)
147
+ nokogiri (1.16.7-x86_64-linux)
148
148
  racc (~> 1.4)
149
149
  others (0.0.3)
150
150
  parallel (1.25.1)
@@ -153,8 +153,8 @@ GEM
153
153
  racc
154
154
  psych (5.1.2)
155
155
  stringio
156
- public_suffix (6.0.0)
157
- racc (1.8.0)
156
+ public_suffix (6.0.1)
157
+ racc (1.8.1)
158
158
  rack (3.1.7)
159
159
  rack-session (2.0.0)
160
160
  rack (>= 3.0.0)
@@ -187,7 +187,7 @@ GEM
187
187
  reline (0.5.9)
188
188
  io-console (~> 0.5)
189
189
  retries (0.0.5)
190
- rexml (3.3.2)
190
+ rexml (3.3.4)
191
191
  strscan
192
192
  rspec-core (3.13.0)
193
193
  rspec-support (~> 3.13.0)
@@ -206,7 +206,7 @@ GEM
206
206
  rspec-mocks (~> 3.13)
207
207
  rspec-support (~> 3.13)
208
208
  rspec-support (3.13.1)
209
- rubocop (1.65.0)
209
+ rubocop (1.65.1)
210
210
  json (~> 2.3)
211
211
  language_server-protocol (>= 3.17.0)
212
212
  parallel (~> 1.10)
@@ -252,7 +252,7 @@ GEM
252
252
  webrick (1.8.1)
253
253
  yaml (0.3.0)
254
254
  yard (0.9.36)
255
- zeitwerk (2.6.16)
255
+ zeitwerk (2.6.17)
256
256
 
257
257
  PLATFORMS
258
258
  aarch64-linux
@@ -272,7 +272,7 @@ DEPENDENCIES
272
272
  rake (= 13.2.1)
273
273
  random-port (~> 0.0)
274
274
  rspec-rails (= 6.1.3)
275
- rubocop (= 1.65.0)
275
+ rubocop (= 1.65.1)
276
276
  rubocop-performance (= 1.21.1)
277
277
  rubocop-rspec (= 3.0.3)
278
278
  simplecov (= 0.22.0)
@@ -281,4 +281,4 @@ DEPENDENCIES
281
281
  yard (= 0.9.36)
282
282
 
283
283
  BUNDLED WITH
284
- 2.5.6
284
+ 2.5.16
data/bin/judges CHANGED
@@ -32,7 +32,15 @@ Encoding.default_internal = Encoding::UTF_8
32
32
 
33
33
  class JudgesGLI extend GLI::App
34
34
 
35
- loog = Loog::REGULAR
35
+ def self.run_it(cmd, ruby)
36
+ cmd.action do |global, options, args|
37
+ require_relative "../lib/judges/commands/#{ruby}"
38
+ @@loog.debug("Running '#{ruby}' command...")
39
+ Object.const_get("Judges::#{ruby.capitalize}").new(@@loog).run(options, args)
40
+ end
41
+ end
42
+
43
+ @@loog = Loog::REGULAR
36
44
 
37
45
  program_desc('Automated executor of judges for a factbase')
38
46
 
@@ -48,13 +56,13 @@ class JudgesGLI extend GLI::App
48
56
 
49
57
  pre do |global, command, options, args|
50
58
  if global[:verbose]
51
- loog = Loog::VERBOSE
59
+ @@loog = Loog::VERBOSE
52
60
  end
53
- loog.debug("Judges #{Judges::VERSION}")
54
- loog.debug("Factbase #{Factbase::VERSION}")
55
- loog.debug("Ruby: #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}")
56
- loog.debug("Current directory: #{Dir.getwd}")
57
- loog.debug("Time: #{Time.now.utc.iso8601}")
61
+ @@loog.debug("Judges #{Judges::VERSION}")
62
+ @@loog.debug("Factbase #{Factbase::VERSION}")
63
+ @@loog.debug("Ruby: #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}")
64
+ @@loog.debug("Current directory: #{Dir.getwd}")
65
+ @@loog.debug("Time: #{Time.now.utc.iso8601}")
58
66
  true
59
67
  end
60
68
 
@@ -72,44 +80,29 @@ class JudgesGLI extend GLI::App
72
80
  c.switch([:summary], default_value: false)
73
81
  c.desc 'Use default logging facility'
74
82
  c.switch([:log], default_value: true)
75
- c.action do |global, options, args|
76
- require_relative '../lib/judges/commands/update'
77
- Judges::Update.new(loog).run(options, args)
78
- end
83
+ run_it(c, 'update')
79
84
  end
80
85
 
81
86
  desc 'Evaluate a single Ruby expression on the factbase'
82
87
  command :eval do |c|
83
- c.action do |global, options, args|
84
- require_relative '../lib/judges/commands/eval'
85
- Judges::Eval.new(loog).run(options, args)
86
- end
88
+ run_it(c, 'eval')
87
89
  end
88
90
 
89
91
  desc 'Join two factbases'
90
92
  command :join do |c|
91
- c.action do |global, options, args|
92
- require_relative '../lib/judges/commands/join'
93
- Judges::Join.new(loog).run(options, args)
94
- end
93
+ run_it(c, 'join')
95
94
  end
96
95
 
97
96
  desc 'Import YAML into a factbase'
98
97
  command :import do |c|
99
- c.action do |global, options, args|
100
- require_relative '../lib/judges/commands/import'
101
- Judges::Import.new(loog).run(options, args)
102
- end
98
+ run_it(c, 'import')
103
99
  end
104
100
 
105
101
  desc 'Remove the facts that are too old'
106
102
  command :trim do |c|
107
103
  c.desc 'Only the facts that match the expression are deleted'
108
104
  c.flag([:query], default_value: '(never)')
109
- c.action do |global, options, args|
110
- require_relative '../lib/judges/commands/trim'
111
- Judges::Trim.new(loog).run(options, args)
112
- end
105
+ run_it(c, 'trim')
113
106
  end
114
107
 
115
108
  desc 'Print the factbase into a human-readable format (YAML, JSON, etc.)'
@@ -128,18 +121,12 @@ class JudgesGLI extend GLI::App
128
121
  c.flag([:hidden], default_value: '_id,_time,_version')
129
122
  c.desc 'Print even if target file already exists and is older than the factbase'
130
123
  c.switch([:force], default_value: false)
131
- c.action do |global, options, args|
132
- require_relative '../lib/judges/commands/print'
133
- Judges::Print.new(loog).run(options, args)
134
- end
124
+ run_it(c, 'print')
135
125
  end
136
126
 
137
127
  desc 'Inspect the factbase and print all its possible meta-data'
138
128
  command :inspect do |c|
139
- c.action do |global, options, args|
140
- require_relative '../lib/judges/commands/inspect'
141
- Judges::Inspect.new(loog).run(options, args)
142
- end
129
+ run_it(c, 'inspect')
143
130
  end
144
131
 
145
132
  desc 'Run automated tests for all judges'
@@ -160,10 +147,7 @@ class JudgesGLI extend GLI::App
160
147
  c.switch([:quiet], default_value: false)
161
148
  c.desc 'Use default logging facility'
162
149
  c.switch([:log], default_value: true)
163
- c.action do |global, options, args|
164
- require_relative '../lib/judges/commands/test'
165
- Judges::Test.new(loog).run(options, args)
166
- end
150
+ run_it(c, 'test')
167
151
  end
168
152
 
169
153
  desc 'Push the factbase to the server'
@@ -184,10 +168,9 @@ class JudgesGLI extend GLI::App
184
168
  c.flag([:meta], type: String, multiple: true)
185
169
  c.desc 'How many times to retry'
186
170
  c.flag([:retries], type: Integer, default_value: 3)
187
- c.action do |global, options, args|
188
- require_relative '../lib/judges/commands/push'
189
- Judges::Push.new(loog).run(options, args)
190
- end
171
+ c.desc 'Turn on the package compression'
172
+ c.switch([:zip], default_value: true)
173
+ run_it(c, 'push')
191
174
  end
192
175
 
193
176
  desc 'Pull the factbase from the server'
@@ -208,10 +191,7 @@ class JudgesGLI extend GLI::App
208
191
  c.flag([:owner], default_value: 'default', type: String)
209
192
  c.desc 'How many times to retry'
210
193
  c.flag([:retries], type: Integer, default_value: 3)
211
- c.action do |global, options, args|
212
- require_relative '../lib/judges/commands/pull'
213
- Judges::Pull.new(loog).run(options, args)
214
- end
194
+ run_it(c, 'pull')
215
195
  end
216
196
  end
217
197
 
data/judges.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
27
27
  s.required_ruby_version = '>=3.2'
28
28
  s.name = 'judges'
29
- s.version = '0.21.0'
29
+ s.version = '0.22.0'
30
30
  s.license = 'MIT'
31
31
  s.summary = 'Command-Line Tool for a Factbase'
32
32
  s.description =
data/lib/judges/baza.rb CHANGED
@@ -38,7 +38,7 @@ require_relative '../judges/elapsed'
38
38
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
39
39
  # License:: MIT
40
40
  class Judges::Baza
41
- def initialize(host, port, token, ssl: true, timeout: 30, retries: 3, loog: Loog::NULL)
41
+ def initialize(host, port, token, ssl: true, timeout: 30, retries: 3, loog: Loog::NULL, compression: true)
42
42
  @host = host
43
43
  @port = port
44
44
  @ssl = ssl
@@ -46,6 +46,7 @@ class Judges::Baza
46
46
  @timeout = timeout
47
47
  @loog = loog
48
48
  @retries = retries
49
+ @compression = compression
49
50
  end
50
51
 
51
52
  # Push factbase to the server.
@@ -62,18 +63,22 @@ class Judges::Baza
62
63
  unless meta.empty?
63
64
  hdrs = hdrs.merge('X-Zerocracy-Meta' => meta.map { |v| Base64.encode64(v).gsub("\n", '') }.join(' '))
64
65
  end
66
+ params = {
67
+ connecttimeout: @timeout,
68
+ timeout: @timeout,
69
+ body: data,
70
+ headers: hdrs
71
+ }
65
72
  elapsed(@loog) do
66
- ret = with_retries(max_tries: @retries) do
67
- checked(
68
- Typhoeus::Request.put(
69
- home.append('push').append(name).to_s,
70
- body: data,
71
- headers: hdrs,
72
- connecttimeout: @timeout,
73
- timeout: @timeout
73
+ ret =
74
+ with_retries(max_tries: @retries) do
75
+ checked(
76
+ Typhoeus::Request.put(
77
+ home.append('push').append(name).to_s,
78
+ @compression ? zipped(params) : params
79
+ )
74
80
  )
75
- )
76
- end
81
+ end
77
82
  id = ret.body.to_i
78
83
  throw :"Pushed #{data.size} bytes to #{@host}, job ID is ##{id}"
79
84
  end
@@ -117,20 +122,63 @@ class Judges::Baza
117
122
  def finished?(id)
118
123
  finished = false
119
124
  elapsed(@loog) do
120
- ret = with_retries(max_tries: @retries) do
121
- checked(
122
- Typhoeus::Request.get(
123
- home.append('finished').append(id).to_s,
124
- headers:
125
+ ret =
126
+ with_retries(max_tries: @retries) do
127
+ checked(
128
+ Typhoeus::Request.get(
129
+ home.append('finished').append(id).to_s,
130
+ headers:
131
+ )
125
132
  )
126
- )
127
- end
133
+ end
128
134
  finished = ret.body == 'yes'
129
135
  throw :"The job ##{id} is #{finished ? '' : 'not yet '}finished at #{@host}"
130
136
  end
131
137
  finished
132
138
  end
133
139
 
140
+ # Read and return the stdout of the job.
141
+ # @param [Integer] id The ID of the job on the server
142
+ # @return [String] The stdout, as a text
143
+ def stdout(id)
144
+ stdout = ''
145
+ elapsed(@loog) do
146
+ ret =
147
+ with_retries(max_tries: @retries) do
148
+ checked(
149
+ Typhoeus::Request.get(
150
+ home.append('stdout').append(id).to_s,
151
+ headers:
152
+ )
153
+ )
154
+ end
155
+ ret.body
156
+ throw :"The stdout of the job ##{id} has #{stdout.split("\n")} lines"
157
+ end
158
+ stdout
159
+ end
160
+
161
+ # Read and return the exit code of the job.
162
+ # @param [Integer] id The ID of the job on the server
163
+ # @return [Integer] The exit code
164
+ def exit_code(id)
165
+ code = 0
166
+ elapsed(@loog) do
167
+ ret =
168
+ with_retries(max_tries: @retries) do
169
+ checked(
170
+ Typhoeus::Request.get(
171
+ home.append('exit').append(id).to_s,
172
+ headers:
173
+ )
174
+ )
175
+ end
176
+ code = ret.body.to_i
177
+ throw :"The exit code of the job ##{id} is #{code}"
178
+ end
179
+ code
180
+ end
181
+
134
182
  # Lock the name.
135
183
  # @param [String] name The name of the job on the server
136
184
  # @param [String] owner The owner of the lock (any string)
@@ -171,14 +219,15 @@ class Judges::Baza
171
219
  def recent(name)
172
220
  job = 0
173
221
  elapsed(@loog) do
174
- ret = with_retries(max_tries: @retries) do
175
- checked(
176
- Typhoeus::Request.get(
177
- home.append('recent').append("#{name}.txt").to_s,
178
- headers:
222
+ ret =
223
+ with_retries(max_tries: @retries) do
224
+ checked(
225
+ Typhoeus::Request.get(
226
+ home.append('recent').append("#{name}.txt").to_s,
227
+ headers:
228
+ )
179
229
  )
180
- )
181
- end
230
+ end
182
231
  job = ret.body.to_i
183
232
  throw :"The recent \"#{name}\" job's ID is ##{job} at #{@host}"
184
233
  end
@@ -191,14 +240,15 @@ class Judges::Baza
191
240
  def name_exists?(name)
192
241
  exists = 0
193
242
  elapsed(@loog) do
194
- ret = with_retries(max_tries: @retries) do
195
- checked(
196
- Typhoeus::Request.get(
197
- home.append('exists').append(name).to_s,
198
- headers:
243
+ ret =
244
+ with_retries(max_tries: @retries) do
245
+ checked(
246
+ Typhoeus::Request.get(
247
+ home.append('exists').append(name).to_s,
248
+ headers:
249
+ )
199
250
  )
200
- )
201
- end
251
+ end
202
252
  exists = ret.body == 'yes'
203
253
  throw :"The name \"#{name}\" #{exists ? 'exists' : "doesn't exist"} at #{@host}"
204
254
  end
@@ -215,6 +265,27 @@ class Judges::Baza
215
265
  }
216
266
  end
217
267
 
268
+ def zipped(params)
269
+ body = gzip(params.fetch(:body))
270
+ headers = params
271
+ .fetch(:headers)
272
+ .merge({
273
+ 'Content-Type' => 'application/zip',
274
+ 'Content-Encoding' => 'gzip',
275
+ 'Content-Length' => body.size
276
+ })
277
+ params.merge(body:, headers:)
278
+ end
279
+
280
+ def gzip(data)
281
+ ''.dup.tap do |result|
282
+ io = StringIO.new(result)
283
+ gz = Zlib::GzipWriter.new(io)
284
+ gz.write(data)
285
+ gz.close
286
+ end
287
+ end
288
+
218
289
  def home
219
290
  Iri.new('')
220
291
  .host(@host)
@@ -51,9 +51,14 @@ class Judges::Pull
51
51
  )
52
52
  name = args[0]
53
53
  elapsed(@loog) do
54
+ baza.lock(name, opts['owner'])
54
55
  if baza.name_exists?(name)
55
- baza.lock(name, opts['owner'])
56
- fb.import(baza.pull(wait(name, baza, baza.recent(name), opts['wait'])))
56
+ jid = baza.recent(name)
57
+ unless baza.exit_code(jid).zero?
58
+ @loog.warn("STDOUT of the job ##{jid} (from the server):\n#{baza.stdout(jid)}")
59
+ raise "The job ##{jid} ('#{name}') is broken, maybe you should expire it"
60
+ end
61
+ fb.import(baza.pull(wait(name, baza, jid, opts['wait'])))
57
62
  Judges::Impex.new(@loog, args[1]).export(fb)
58
63
  throw :"Pulled #{fb.size} facts by the name '#{name}'"
59
64
  else
@@ -48,7 +48,8 @@ class Judges::Push
48
48
  ssl: opts['ssl'],
49
49
  timeout: (opts['timeout'] || 30).to_i,
50
50
  loog: @loog,
51
- retries: (opts['retries'] || 3).to_i
51
+ retries: (opts['retries'] || 3).to_i,
52
+ compression: opts.fetch('zip', true)
52
53
  )
53
54
  elapsed(@loog) do
54
55
  baza.lock(name, opts['owner'])
@@ -64,7 +64,7 @@ class Judges::Update
64
64
  loop do
65
65
  c += 1
66
66
  if c > 1
67
- @loog.info("\n\nStarting cycle ##{c}#{opts['max-cycles'] ? " (out of #{opts['max-cycles']})" : ''}...")
67
+ @loog.info("\nStarting cycle ##{c}#{opts['max-cycles'] ? " (out of #{opts['max-cycles']})" : ''}...")
68
68
  end
69
69
  delta = cycle(opts, judges, fb, options)
70
70
  churn += delta
@@ -103,17 +103,18 @@ class Judges::Update
103
103
  churn = Judges::Churn.new(0, 0)
104
104
  global = {}
105
105
  elapsed(@loog) do
106
- done = judges.each_with_index do |p, i|
107
- @loog.info("\n👉 Running #{p.name} (##{i}) at #{p.dir.to_rel}...")
108
- elapsed(@loog) do
109
- c = one_judge(fb, p, global, options)
110
- churn += c
111
- throw :"👍 The judge #{p.name} modified #{c} facts out of #{fb.size}"
106
+ done =
107
+ judges.each_with_index do |p, i|
108
+ @loog.info("\n👉 Running #{p.name} (##{i}) at #{p.dir.to_rel}...")
109
+ elapsed(@loog) do
110
+ c = one_judge(fb, p, global, options)
111
+ churn += c
112
+ throw :"👍 The judge #{p.name} modified #{c} facts out of #{fb.size}"
113
+ end
114
+ rescue StandardError, SyntaxError => e
115
+ @loog.warn(Backtrace.new(e))
116
+ churn << e.message
112
117
  end
113
- rescue StandardError, SyntaxError => e
114
- @loog.warn(Backtrace.new(e))
115
- churn << e.message
116
- end
117
118
  throw :"👍 #{done} judge(s) processed" if churn.errors.empty?
118
119
  throw :"❌ #{done} judge(s) processed with #{churn.errors.size} errors"
119
120
  end
@@ -52,32 +52,33 @@ class Judges::Options
52
52
  v = v.to_s
53
53
  v = "#{v[0..3]}#{'*' * (v.length - 8)}#{v[-4..]}" if v.length > 8
54
54
  "#{k} → \"#{v}\""
55
- end.join("\n")
55
+ end.sort.join("\n")
56
56
  end
57
57
 
58
58
  def to_h
59
- @to_h ||= begin
60
- pp = @pairs || []
61
- pp = pp.split(',') if pp.is_a?(String)
62
- if pp.is_a?(Array)
63
- pp = pp
64
- .compact
65
- .map(&:strip)
66
- .reject(&:empty?)
67
- .map { |s| s.split('=', 2) }
68
- .map { |a| a.size == 1 ? [a[0], nil] : a }
69
- .reject { |a| a[0].empty? }
59
+ @to_h ||=
60
+ begin
61
+ pp = @pairs || []
62
+ pp = pp.split(',') if pp.is_a?(String)
63
+ if pp.is_a?(Array)
64
+ pp = pp
65
+ .compact
66
+ .map(&:strip)
67
+ .reject(&:empty?)
68
+ .map { |s| s.split('=', 2) }
69
+ .map { |a| a.size == 1 ? [a[0], nil] : a }
70
+ .reject { |a| a[0].empty? }
71
+ .to_h
72
+ end
73
+ pp
74
+ .reject { |k, _| k.nil? }
75
+ .reject { |k, _| k.is_a?(String) && k.empty? }
70
76
  .to_h
77
+ .transform_values { |v| v.nil? ? 'true' : v }
78
+ .transform_values { |v| v.is_a?(String) ? v.strip : v }
79
+ .transform_values { |v| v.is_a?(String) && v.match?(/^[0-9]+$/) ? v.to_i : v }
80
+ .transform_keys { |k| k.to_s.strip.upcase.to_sym }
71
81
  end
72
- pp
73
- .reject { |k, _| k.nil? }
74
- .reject { |k, _| k.is_a?(String) && k.empty? }
75
- .to_h
76
- .transform_values { |v| v.nil? ? 'true' : v }
77
- .transform_values { |v| v.is_a?(String) ? v.strip : v }
78
- .transform_values { |v| v.is_a?(String) && v.match?(/^[0-9]+$/) ? v.to_i : v }
79
- .transform_keys { |k| k.to_s.strip.upcase.to_sym }
80
- end
81
82
  end
82
83
 
83
84
  # Get option by name.
data/lib/judges.rb CHANGED
@@ -25,5 +25,5 @@
25
25
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  module Judges
28
- VERSION = '0.21.0'
28
+ VERSION = '0.22.0'
29
29
  end
@@ -38,6 +38,7 @@ class TestPull < Minitest::Test
38
38
  stub_request(:get, 'http://example.org/exists/foo').to_return(body: 'yes')
39
39
  stub_request(:get, 'http://example.org/recent/foo.txt').to_return(body: '42')
40
40
  stub_request(:get, 'http://example.org/finished/42').to_return(body: 'yes')
41
+ stub_request(:get, 'http://example.org/exit/42').to_return(body: '0')
41
42
  fb = Factbase.new
42
43
  fb.insert.foo = 42
43
44
  stub_request(:get, 'http://example.org/pull/42.fb').to_return(body: fb.export)
@@ -58,4 +59,32 @@ class TestPull < Minitest::Test
58
59
  fb.import(File.binread(file))
59
60
  end
60
61
  end
62
+
63
+ def test_fail_pull_when_job_is_broken
64
+ WebMock.disable_net_connect!
65
+ stub_request(:get, 'http://example.org/lock/foo?owner=none').to_return(status: 302)
66
+ stub_request(:get, 'http://example.org/exists/foo').to_return(body: 'yes')
67
+ stub_request(:get, 'http://example.org/recent/foo.txt').to_return(body: '42')
68
+ stub_request(:get, 'http://example.org/finished/42').to_return(body: 'yes')
69
+ stub_request(:get, 'http://example.org/exit/42').to_return(body: '1')
70
+ stub_request(:get, 'http://example.org/stdout/42').to_return(body: 'oops, some trouble here')
71
+ Dir.mktmpdir do |d|
72
+ file = File.join(d, 'base.fb')
73
+ e =
74
+ assert_raises do
75
+ Judges::Pull.new(Loog::NULL).run(
76
+ {
77
+ 'token' => '000',
78
+ 'host' => 'example.org',
79
+ 'port' => 80,
80
+ 'ssl' => false,
81
+ 'wait' => 10,
82
+ 'owner' => 'none'
83
+ },
84
+ ['foo', file]
85
+ )
86
+ end
87
+ assert(e.message.include?('expire it'), e)
88
+ end
89
+ end
61
90
  end
data/test/test_baza.rb CHANGED
@@ -22,8 +22,10 @@
22
22
 
23
23
  require 'minitest/autorun'
24
24
  require 'webmock/minitest'
25
+ require 'webrick'
25
26
  require 'loog'
26
27
  require 'socket'
28
+ require 'stringio'
27
29
  require 'random-port'
28
30
  require_relative '../lib/judges'
29
31
  require_relative '../lib/judges/baza'
@@ -76,53 +78,75 @@ class TestBaza < Minitest::Test
76
78
  end
77
79
 
78
80
  def test_real_http
79
- req = with_http_server(200, 'yes') do |baza|
80
- baza.name_exists?('simple')
81
- end
82
- assert(req.include?("User-Agent: judges #{Judges::VERSION}\r\n"))
81
+ req =
82
+ with_http_server(200, 'yes') do |baza|
83
+ baza.name_exists?('simple')
84
+ end
85
+ assert_equal("judges #{Judges::VERSION}", req['user-agent'])
83
86
  end
84
87
 
85
88
  def test_push_with_meta
86
- req = with_http_server(200, 'yes') do |baza|
87
- baza.push('simple', 'hello, world!', ['boom!', 'хей!'])
88
- end
89
- assert(req.include?("X-Zerocracy-Meta: Ym9vbSE= 0YXQtdC5IQ==\r\n"))
89
+ req =
90
+ with_http_server(200, 'yes') do |baza|
91
+ baza.push('simple', 'hello, world!', ['boom!', 'хей!'])
92
+ end
93
+ assert_equal('Ym9vbSE= 0YXQtdC5IQ==', req['x-zerocracy-meta'])
90
94
  end
91
95
 
92
96
  def test_push_with_big_meta
93
- req = with_http_server(200, 'yes') do |baza|
94
- baza.push(
95
- 'simple',
96
- 'hello, world!',
97
- [
98
- 'pages_url:https://zerocracy.github.io/zerocracy.html',
99
- 'others:https://zerocracy.github.io/zerocracy.html',
100
- 'duration:59595'
101
- ]
102
- )
103
- end
104
- assert(req.join.include?('X-Zerocracy-Meta: '))
97
+ req =
98
+ with_http_server(200, 'yes') do |baza|
99
+ baza.push(
100
+ 'simple',
101
+ 'hello, world!',
102
+ [
103
+ 'pages_url:https://zerocracy.github.io/zerocracy.html',
104
+ 'others:https://zerocracy.github.io/zerocracy.html',
105
+ 'duration:59595'
106
+ ]
107
+ )
108
+ end
109
+ assert(req['x-zerocracy-meta'])
110
+ end
111
+
112
+ def test_push_compressed_content
113
+ req =
114
+ with_http_server(200, 'yes') do |baza|
115
+ baza.push('simple', 'hello, world!', %w[meta1 meta2 meta3])
116
+ end
117
+ assert_equal('application/zip', req.content_type)
118
+ assert_equal('gzip', req['content-encoding'])
119
+ body = Zlib::GzipReader.zcat(StringIO.new(req.body))
120
+ assert_equal('hello, world!', body)
121
+ end
122
+
123
+ def test_push_compression_disabled
124
+ req =
125
+ with_http_server(200, 'yes', compression: false) do |baza|
126
+ baza.push('simple', 'hello, world!', %w[meta1 meta2 meta3])
127
+ end
128
+ assert_equal('application/octet-stream', req.content_type)
129
+ assert_equal('hello, world!', req.body)
105
130
  end
106
131
 
107
132
  private
108
133
 
109
- def with_http_server(code, response)
134
+ def with_http_server(code, response, opts = {})
135
+ opts = { ssl: false, timeout: 1 }.merge(opts)
110
136
  WebMock.enable_net_connect!
111
- req = []
137
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
112
138
  host = '127.0.0.1'
113
139
  RandomPort::Pool::SINGLETON.acquire do |port|
114
140
  server = TCPServer.new(host, port)
115
- t = Thread.new do
116
- socket = server.accept
117
- loop do
118
- line = socket.gets
119
- break if line == "\r\n"
120
- req << line
141
+ t =
142
+ Thread.new do
143
+ socket = server.accept
144
+ req.parse(socket)
145
+ req.body
146
+ socket.puts "HTTP/1.1 #{code} OK\r\nContent-Length: #{response.length}\r\n\r\n#{response}"
147
+ socket.close
121
148
  end
122
- socket.puts "HTTP/1.1 #{code} OK\r\nContent-Length: #{response.length}\r\n\r\n#{response}"
123
- socket.close
124
- end
125
- yield Judges::Baza.new(host, port, '0000', ssl: false, timeout: 1)
149
+ yield Judges::Baza.new(host, port, '0000', **opts)
126
150
  t.join
127
151
  end
128
152
  req
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: judges
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-29 00:00:00.000000000 Z
11
+ date: 2024-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace