judges 0.46.0 → 0.47.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: 5480ce66a6fbce4d6748a47f42240c77438ef09eed639f6744c762f94ce57ebe
4
- data.tar.gz: a445e36911b5dcc457a188fb1ff7a50251f6e19b60313c1185b4af70a773acdc
3
+ metadata.gz: 104c3aa90435ddf336588ca607ae35e029fbe1e8a03ac4a2efc0f9377be46f49
4
+ data.tar.gz: e3d467f54ff1df3f5f4c07b1f1545bbd8543bd00af58bea82e4f9160c00885ec
5
5
  SHA512:
6
- metadata.gz: d4a52cb96f912acb0a97d842f132c2daf855771406253e9ff0bd5b2fa2ba17cbdb15b4e12919b8e41092b7ebc07c954c7fafbaaf4029eb57387075d9cd46e679
7
- data.tar.gz: 59d5df2eea5c531989709a7e60c183a4f8f10723e6e0df320ca58ce2a6a2f167c236713a110814eab49d91996e659e2e3ac5fb255a95a6a8516ebec9f874351c
6
+ metadata.gz: 8b3d6c28de5880acb591607a3c2cc02d1c8cd1f1963df54c989d7719bc3dcd44963a711f9260bcde7c40c45937a80e485f6ff37a3dcff4932a618a94e9fa4e55
7
+ data.tar.gz: a92d912ba0a01291ed6b990d59f26ec6ac2e5d90ab0d5b92a6dad5e26ca7d9e3f412d79017c1db1effcbd18177376b87354d61f815aca14aeeb0ad87de73c96c
data/Gemfile.lock CHANGED
@@ -28,18 +28,18 @@ GEM
28
28
  ast (2.4.3)
29
29
  backtrace (0.4.1)
30
30
  base64 (0.3.0)
31
- baza.rb (0.5.1)
32
- backtrace (> 0)
33
- elapsed (> 0)
34
- faraday (> 0)
35
- faraday-http-cache (> 0)
36
- faraday-multipart (> 0)
37
- faraday-retry (> 0)
38
- iri (> 0)
39
- loog (> 0)
40
- retries (~> 0)
41
- tago (~> 0)
42
- typhoeus (~> 1.3)
31
+ baza.rb (0.6.0)
32
+ backtrace (~> 0.4)
33
+ elapsed (~> 0.0)
34
+ faraday (~> 2.13)
35
+ faraday-http-cache (~> 2.5)
36
+ faraday-multipart (~> 1.1)
37
+ faraday-retry (~> 2.3)
38
+ iri (~> 0.11)
39
+ loog (~> 0.6)
40
+ retries (~> 0.0)
41
+ tago (~> 0.0)
42
+ typhoeus (~> 1.4)
43
43
  bigdecimal (3.2.2)
44
44
  builder (3.3.0)
45
45
  concurrent-ruby (1.3.5)
data/bin/judges CHANGED
@@ -217,6 +217,44 @@ class JudgesGLI extend GLI::App
217
217
  c.flag([:retries], type: Integer, default_value: 3)
218
218
  run_it(c, 'pull')
219
219
  end
220
+
221
+ desc 'Download a durable from the server by ID'
222
+ command :download do |c|
223
+ c.desc 'Authentication token'
224
+ c.flag([:token])
225
+ c.desc 'Server IP/hostname'
226
+ c.flag([:host], default_value: 'api.zerocracy.com')
227
+ c.desc 'Server TCP port number'
228
+ c.flag([:port], default_value: 443, type: Integer)
229
+ c.desc 'Connection and read timeout in seconds'
230
+ c.flag([:timeout], default_value: 30, type: Integer)
231
+ c.desc 'Enable SSL connection'
232
+ c.switch([:ssl], default_value: true)
233
+ c.desc 'Unique identifier for the operation'
234
+ c.flag([:owner], default_value: 'default', type: String)
235
+ c.desc 'Number of retry attempts'
236
+ c.flag([:retries], type: Integer, default_value: 3)
237
+ run_it(c, 'download')
238
+ end
239
+
240
+ desc 'Upload a file as a durable to the server'
241
+ command :upload do |c|
242
+ c.desc 'Authentication token'
243
+ c.flag([:token])
244
+ c.desc 'Server IP/hostname'
245
+ c.flag([:host], default_value: 'api.zerocracy.com')
246
+ c.desc 'Server TCP port number'
247
+ c.flag([:port], default_value: 443, type: Integer)
248
+ c.desc 'Connection and read timeout in seconds'
249
+ c.flag([:timeout], default_value: 30, type: Integer)
250
+ c.desc 'Enable SSL connection'
251
+ c.switch([:ssl], default_value: true)
252
+ c.desc 'Unique identifier for the operation'
253
+ c.flag([:owner], default_value: 'default', type: String)
254
+ c.desc 'Number of retry attempts'
255
+ c.flag([:retries], type: Integer, default_value: 3)
256
+ run_it(c, 'upload')
257
+ end
220
258
  end
221
259
 
222
260
  exit JudgesGLI.run(ARGV) if ENV['GLI_TESTING'].nil?
@@ -7,6 +7,6 @@ Feature: Pull
7
7
  Scenario: Pull a small factbase, which is absent on the server
8
8
  Given We are online
9
9
  Given I make a temp directory
10
- Then I run bin/judges with "--verbose pull --token 00000000-0000-0000-0000-000000000000 --wait=15 {FAKE-NAME} simple.fb"
10
+ Then I run bin/judges with "--verbose pull --token ZRCY-00000000-0000-0000-0000-000000000000 --wait=15 {FAKE-NAME} simple.fb"
11
11
  Then Stdout contains "doesn't exist at api.zerocracy.com"
12
12
  And Exit code is zero
@@ -9,6 +9,6 @@ Feature: Push
9
9
  Given I make a temp directory
10
10
  Then I run bin/judges with "--verbose eval simple.fb '(0..1000).each { $fb.insert.foo = 42 }'"
11
11
  And Exit code is zero
12
- Then I run bin/judges with "push --token 00000000-0000-0000-0000-000000000000 --meta a:b --meta foo:bar --meta=pages_url:https://zerocracy.github.io/zerocracy.html --meta=duration:1055 {FAKE-NAME} simple.fb"
12
+ Then I run bin/judges with "push --token ZRCY-00000000-0000-0000-0000-000000000000 --meta a:b --meta foo:bar --meta=pages_url:https://zerocracy.github.io/zerocracy.html --meta=duration:1055 {FAKE-NAME} simple.fb"
13
13
  Then Stdout contains "Pushed"
14
14
  And Exit code is zero
data/judges.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
10
10
  s.required_ruby_version = '>=3.2'
11
11
  s.name = 'judges'
12
- s.version = '0.46.0'
12
+ s.version = '0.47.0'
13
13
  s.license = 'MIT'
14
14
  s.summary = 'Command-Line Tool for a Factbase'
15
15
  s.description =
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'typhoeus'
7
+ require 'iri'
8
+ require 'baza-rb'
9
+ require 'elapsed'
10
+ require_relative '../../judges'
11
+
12
+ # The +download+ command.
13
+ #
14
+ # This class is instantiated by the +bin/judge+ command line interface. You
15
+ # are not supposed to instantiate it yourself.
16
+ #
17
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
18
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
19
+ # License:: MIT
20
+ class Judges::Download
21
+ # Initialize.
22
+ # @param [Loog] loog Logging facility
23
+ def initialize(loog)
24
+ @loog = loog
25
+ end
26
+
27
+ # Run the download command (called by the +bin/judges+ script).
28
+ # @param [Hash] opts Command line options (start with '--')
29
+ # @param [Array] args List of command line arguments
30
+ # @raise [RuntimeError] If not exactly two arguments provided
31
+ def run(opts, args)
32
+ raise 'Exactly two arguments required' unless args.size == 2
33
+ jname = args[0]
34
+ path = args[1]
35
+ name = File.basename(path)
36
+ baza = BazaRb.new(
37
+ opts['host'], opts['port'].to_i, opts['token'],
38
+ ssl: opts['ssl'],
39
+ timeout: (opts['timeout'] || 30).to_i,
40
+ loog: @loog,
41
+ retries: (opts['retries'] || 3).to_i
42
+ )
43
+ elapsed(@loog, level: Logger::INFO) do
44
+ id = baza.durable_find(jname, name)
45
+ if id.nil?
46
+ @loog.info("Durable '#{name}' not found in '#{jname}'")
47
+ return
48
+ end
49
+ @loog.info("Durable ##{id} ('#{name}') found in '#{jname}'")
50
+ baza.durable_lock(id, opts['owner'] || 'default')
51
+ begin
52
+ baza.durable_load(id, path)
53
+ size = File.size(path)
54
+ throw :"👍 Downloaded durable ##{id} to #{path} (#{size} bytes)"
55
+ ensure
56
+ baza.durable_unlock(id, opts['owner'] || 'default')
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'typhoeus'
7
+ require 'iri'
8
+ require 'baza-rb'
9
+ require 'elapsed'
10
+ require_relative '../../judges'
11
+
12
+ # The +upload+ command.
13
+ #
14
+ # This class is instantiated by the +bin/judge+ command line interface. You
15
+ # are not supposed to instantiate it yourself.
16
+ #
17
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
18
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
19
+ # License:: MIT
20
+ class Judges::Upload
21
+ # Initialize.
22
+ # @param [Loog] loog Logging facility
23
+ def initialize(loog)
24
+ @loog = loog
25
+ end
26
+
27
+ # Run the upload command (called by the +bin/judges+ script).
28
+ # @param [Hash] opts Command line options (start with '--')
29
+ # @param [Array] args List of command line arguments
30
+ # @raise [RuntimeError] If not exactly two arguments provided
31
+ def run(opts, args)
32
+ raise 'Exactly two arguments required' unless args.size == 2
33
+ jname = args[0]
34
+ path = args[1]
35
+ raise "File not found: #{path}" unless File.exist?(path)
36
+ name = File.basename(path)
37
+ baza = BazaRb.new(
38
+ opts['host'], opts['port'].to_i, opts['token'],
39
+ ssl: opts['ssl'],
40
+ timeout: (opts['timeout'] || 30).to_i,
41
+ loog: @loog,
42
+ retries: (opts['retries'] || 3).to_i
43
+ )
44
+ elapsed(@loog, level: Logger::INFO) do
45
+ id = baza.durable_find(jname, name)
46
+ size = File.size(path)
47
+ if id.nil? || id.to_s.strip.empty?
48
+ id = baza.durable_place(jname, path)
49
+ throw :"👍 Uploaded #{path} to new durable '#{name}' in '#{jname}' (ID: #{id}, #{size} bytes)"
50
+ else
51
+ id = id.to_i
52
+ baza.durable_lock(id, opts['owner'] || 'default')
53
+ begin
54
+ baza.durable_save(id, path)
55
+ throw :"👍 Uploaded #{path} to existing durable '#{name}' in '#{jname}' (ID: #{id}, #{size} bytes)"
56
+ ensure
57
+ baza.durable_unlock(id, opts['owner'] || 'default')
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
data/lib/judges.rb CHANGED
@@ -8,5 +8,5 @@
8
8
  # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
9
9
  # License:: MIT
10
10
  module Judges
11
- VERSION = '0.46.0' unless const_defined?(:VERSION)
11
+ VERSION = '0.47.0' unless const_defined?(:VERSION)
12
12
  end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'loog'
7
+ require 'webmock/minitest'
8
+ require_relative '../../lib/judges'
9
+ require_relative '../../lib/judges/commands/download'
10
+ require_relative '../test__helper'
11
+
12
+ # Test.
13
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
14
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
15
+ # License:: MIT
16
+ class TestDownload < Minitest::Test
17
+ def test_download_simple_durable
18
+ WebMock.disable_net_connect!
19
+ content = 'Hello, World!'
20
+ stub_request(:get, 'https://example.org/durables/find?file=downloaded.txt&jname=myjudge').to_return(
21
+ status: 200, body: '42'
22
+ )
23
+ stub_request(:get, 'https://example.org/durables/42/lock?owner=default').to_return(status: 302)
24
+ stub_request(:get, 'https://example.org/durables/42').to_return(
25
+ status: 200, body: content
26
+ )
27
+ stub_request(:get, 'https://example.org/durables/42/unlock?owner=default').to_return(status: 302)
28
+ Dir.mktmpdir do |d|
29
+ file = File.join(d, 'downloaded.txt')
30
+ Judges::Download.new(Loog::NULL).run(
31
+ {
32
+ 'token' => '000',
33
+ 'host' => 'example.org',
34
+ 'port' => 443,
35
+ 'ssl' => true,
36
+ 'owner' => 'default'
37
+ },
38
+ ['myjudge', file]
39
+ )
40
+ assert_equal(content, File.read(file))
41
+ end
42
+ end
43
+
44
+ def test_download_with_custom_owner
45
+ WebMock.disable_net_connect!
46
+ content = 'Custom content'
47
+ stub_request(:get, 'http://example.org/durables/find?file=data.bin&jname=judge1').to_return(
48
+ status: 200, body: '123'
49
+ )
50
+ stub_request(:get, 'http://example.org/durables/123/lock?owner=custom').to_return(status: 302)
51
+ stub_request(:get, 'http://example.org/durables/123').to_return(
52
+ status: 200, body: content
53
+ )
54
+ stub_request(:get, 'http://example.org/durables/123/unlock?owner=custom').to_return(status: 302)
55
+ Dir.mktmpdir do |d|
56
+ file = File.join(d, 'data.bin')
57
+ Judges::Download.new(Loog::NULL).run(
58
+ {
59
+ 'token' => '000',
60
+ 'host' => 'example.org',
61
+ 'port' => 80,
62
+ 'ssl' => false,
63
+ 'owner' => 'custom'
64
+ },
65
+ ['judge1', file]
66
+ )
67
+ assert_equal(content, File.read(file))
68
+ end
69
+ end
70
+
71
+ def test_fails_on_http_error
72
+ WebMock.disable_net_connect!
73
+ stub_request(:get, 'http://example.org/durables/find?file=test.txt&jname=somejudge').to_return(
74
+ status: 200, body: '99'
75
+ )
76
+ stub_request(:get, 'http://example.org/durables/99/lock?owner=none').to_return(status: 302)
77
+ stub_request(:get, 'http://example.org/durables/99').to_return(status: 404)
78
+ stub_request(:get, 'http://example.org/durables/99/unlock?owner=none').to_return(status: 302)
79
+ Dir.mktmpdir do |d|
80
+ file = File.join(d, 'test.txt')
81
+ assert_raises(StandardError) do
82
+ Judges::Download.new(Loog::NULL).run(
83
+ {
84
+ 'token' => '000',
85
+ 'host' => 'example.org',
86
+ 'port' => 80,
87
+ 'ssl' => false,
88
+ 'owner' => 'none'
89
+ },
90
+ ['somejudge', file]
91
+ )
92
+ end
93
+ end
94
+ end
95
+
96
+ def test_fails_with_wrong_number_of_arguments
97
+ assert_raises(RuntimeError) do
98
+ Judges::Download.new(Loog::NULL).run({}, ['only_one_arg'])
99
+ end
100
+ assert_raises(RuntimeError) do
101
+ Judges::Download.new(Loog::NULL).run({}, %w[too many args])
102
+ end
103
+ end
104
+
105
+ def test_handles_not_found_durable
106
+ WebMock.disable_net_connect!
107
+ stub_request(:get, 'http://example.org/durables/find?file=missing.txt&jname=notfound').to_return(
108
+ status: 404
109
+ )
110
+ Dir.mktmpdir do |d|
111
+ file = File.join(d, 'missing.txt')
112
+ Judges::Download.new(Loog::NULL).run(
113
+ {
114
+ 'token' => '000',
115
+ 'host' => 'example.org',
116
+ 'port' => 80,
117
+ 'ssl' => false
118
+ },
119
+ ['notfound', file]
120
+ )
121
+ refute_path_exists(file)
122
+ end
123
+ end
124
+ end
@@ -162,7 +162,7 @@ class TestUpdate < Minitest::Test
162
162
  save_it(File.join(d, 'first/first.rb'), '$global[:fb] ||= $fb; 2 + 2')
163
163
  save_it(File.join(d, 'second/second.rb'), '$global[:fb] ||= $fb; $global[:fb].insert')
164
164
  file = File.join(d, 'base.fb')
165
- Judges::Update.new(Loog::VERBOSE).run(
165
+ Judges::Update.new(Loog::NULL).run(
166
166
  { 'max-cycles' => 3, 'boost' => 'first' },
167
167
  [d, file]
168
168
  )
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'loog'
7
+ require 'webmock/minitest'
8
+ require_relative '../../lib/judges'
9
+ require_relative '../../lib/judges/commands/upload'
10
+ require_relative '../test__helper'
11
+
12
+ # Test.
13
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
14
+ # Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
15
+ # License:: MIT
16
+ class TestUpload < Minitest::Test
17
+ def test_upload_simple_durable
18
+ WebMock.disable_net_connect!
19
+ content = 'Hello, World!'
20
+ stub_request(:get, 'https://example.org/durables/find?file=upload.txt&jname=myjudge').to_return(
21
+ status: 404
22
+ )
23
+ stub_request(:get, 'https://example.org/csrf').to_return(
24
+ status: 200, body: 'test-csrf-token'
25
+ )
26
+ stub_request(:post, 'https://example.org/durables/place').to_return(
27
+ status: 302, headers: { 'X-Zerocracy-DurableId' => '42' }
28
+ )
29
+ Dir.mktmpdir do |d|
30
+ file = File.join(d, 'upload.txt')
31
+ File.write(file, content)
32
+ Judges::Upload.new(Loog::NULL).run(
33
+ {
34
+ 'token' => '000',
35
+ 'host' => 'example.org',
36
+ 'port' => 443,
37
+ 'ssl' => true,
38
+ 'owner' => 'default'
39
+ },
40
+ ['myjudge', file]
41
+ )
42
+ end
43
+ end
44
+
45
+ def test_upload_with_custom_owner
46
+ WebMock.disable_net_connect!
47
+ content = 'Binary data here'
48
+ stub_request(:get, 'http://example.org/durables/find?file=data.bin&jname=judge1').to_return(
49
+ status: 200, body: '123'
50
+ )
51
+ stub_request(:get, 'http://example.org/durables/123/lock?owner=custom').to_return(status: 302)
52
+ stub_request(:put, 'http://example.org/durables/123').to_return(status: 200)
53
+ stub_request(:get, 'http://example.org/durables/123/unlock?owner=custom').to_return(status: 302)
54
+ Dir.mktmpdir do |d|
55
+ file = File.join(d, 'data.bin')
56
+ File.write(file, content)
57
+ Judges::Upload.new(Loog::NULL).run(
58
+ {
59
+ 'token' => '000',
60
+ 'host' => 'example.org',
61
+ 'port' => 80,
62
+ 'ssl' => false,
63
+ 'owner' => 'custom'
64
+ },
65
+ ['judge1', file]
66
+ )
67
+ end
68
+ end
69
+
70
+ def test_fails_on_http_error
71
+ WebMock.disable_net_connect!
72
+ stub_request(:get, 'http://example.org/durables/find?file=test.txt&jname=somejudge').to_return(
73
+ status: 404
74
+ )
75
+ stub_request(:get, 'http://example.org/csrf').to_return(
76
+ status: 200, body: 'test-csrf-token'
77
+ )
78
+ stub_request(:post, 'http://example.org/durables/place').to_return(status: 500)
79
+ Dir.mktmpdir do |d|
80
+ file = File.join(d, 'test.txt')
81
+ File.write(file, 'content')
82
+ assert_raises(StandardError) do
83
+ Judges::Upload.new(Loog::NULL).run(
84
+ {
85
+ 'token' => '000',
86
+ 'host' => 'example.org',
87
+ 'port' => 80,
88
+ 'ssl' => false,
89
+ 'owner' => 'none'
90
+ },
91
+ ['somejudge', file]
92
+ )
93
+ end
94
+ end
95
+ end
96
+
97
+ def test_fails_with_wrong_number_of_arguments
98
+ assert_raises(RuntimeError) do
99
+ Judges::Upload.new(Loog::NULL).run({}, ['only_one_arg'])
100
+ end
101
+ assert_raises(RuntimeError) do
102
+ Judges::Upload.new(Loog::NULL).run({}, %w[too many args])
103
+ end
104
+ end
105
+
106
+ def test_fails_when_file_does_not_exist
107
+ assert_raises(RuntimeError) do
108
+ Judges::Upload.new(Loog::NULL).run(
109
+ {
110
+ 'token' => '000',
111
+ 'host' => 'example.org',
112
+ 'port' => 80,
113
+ 'ssl' => false
114
+ },
115
+ ['myjudge', '/nonexistent/file.txt']
116
+ )
117
+ end
118
+ end
119
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: judges
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.46.0
4
+ version: 0.47.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -297,6 +297,7 @@ files:
297
297
  - judges.gemspec
298
298
  - lib/judges.rb
299
299
  - lib/judges/categories.rb
300
+ - lib/judges/commands/download.rb
300
301
  - lib/judges/commands/eval.rb
301
302
  - lib/judges/commands/import.rb
302
303
  - lib/judges/commands/inspect.rb
@@ -307,6 +308,7 @@ files:
307
308
  - lib/judges/commands/test.rb
308
309
  - lib/judges/commands/trim.rb
309
310
  - lib/judges/commands/update.rb
311
+ - lib/judges/commands/upload.rb
310
312
  - lib/judges/impex.rb
311
313
  - lib/judges/judge.rb
312
314
  - lib/judges/judges.rb
@@ -315,6 +317,7 @@ files:
315
317
  - package-lock.json
316
318
  - package.json
317
319
  - renovate.json
320
+ - test/commands/test_download.rb
318
321
  - test/commands/test_eval.rb
319
322
  - test/commands/test_import.rb
320
323
  - test/commands/test_inspect.rb
@@ -325,6 +328,7 @@ files:
325
328
  - test/commands/test_test.rb
326
329
  - test/commands/test_trim.rb
327
330
  - test/commands/test_update.rb
331
+ - test/commands/test_upload.rb
328
332
  - test/test__helper.rb
329
333
  - test/test_bin.rb
330
334
  - test/test_categories.rb