judges 0.21.0 → 0.22.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
  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