judges 0.0.10 → 0.0.12

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: 550115852b44ae26b7af9cb776bcbf9e5b7e043579ab8eec3930854f573a6768
4
- data.tar.gz: 7efe473542ffd401220d8e5b65d853ba144ad8ceab0273f9531e3d462e072afc
3
+ metadata.gz: 63754c5826506b6066688aa2373dafcd87fc20cd193631c15ce67f1496dc361b
4
+ data.tar.gz: a7c70946e0c24bffc91849d81e15866335ef0ebeb64b7f1cefd2c3d6c2d48506
5
5
  SHA512:
6
- metadata.gz: '096f25e43ee418b94ae0a2f4b3d678c170170dfda2228191a269931bcfa1150e4f50151830e1132b3282ccaa768b2129deb8f29e55faebefd0bc7c2bd5ce2700'
7
- data.tar.gz: d3f8922eeee30008f3883a7592667ce084a1d96785c4b04c51478c028ba99aa28eaddd151ac13a1bb7a101de8cc0f05434d22edcf6bbb1691b4bb76a4bd89067
6
+ metadata.gz: 6360db23fb5fc573d479a43d40b4c1d1e274bd21320cb4c2acba0e71a6ac8775c3398fef776dc1d7412ccd96782394e53d9631180c5a09314aab9e791738829a
7
+ data.tar.gz: e5b7a0c2460d589bf9cb69e42183585d13ac6ac1b400f338e3c73057be0d2fdb9c703bcc2896f73d8dbd5f850b22452d8f5a2b147e299a1ed9fd2b1aabe2f564
@@ -30,7 +30,7 @@ jobs:
30
30
  - uses: actions/checkout@v4
31
31
  - uses: ruby/setup-ruby@v1
32
32
  with:
33
- ruby-version: 2.7
33
+ ruby-version: 3.2
34
34
  - run: bundle update
35
35
  - run: bundle exec rake
36
36
  - uses: codecov/codecov-action@v4.0.0-beta.3
@@ -32,7 +32,7 @@ jobs:
32
32
  strategy:
33
33
  matrix:
34
34
  os: [ubuntu-20.04, macos-12]
35
- ruby: [2.7, 3.2]
35
+ ruby: [3.2]
36
36
  runs-on: ${{ matrix.os }}
37
37
  steps:
38
38
  - uses: actions/checkout@v4
data/.rubocop.yml CHANGED
@@ -49,3 +49,5 @@ Layout/EmptyLineAfterGuardClause:
49
49
  Enabled: false
50
50
  Layout/CaseIndentation:
51
51
  Enabled: false
52
+ Naming/MethodParameterName:
53
+ MinNameLength: 2
data/bin/judges CHANGED
@@ -29,53 +29,63 @@ require 'factbase'
29
29
  Encoding.default_external = Encoding::UTF_8
30
30
  Encoding.default_internal = Encoding::UTF_8
31
31
 
32
- class App
33
- extend GLI::App
32
+ include GLI::App
34
33
 
35
- loog = Loog::REGULAR
34
+ loog = Loog::REGULAR
36
35
 
37
- program_desc 'Automated executor of judges for a factbase'
36
+ program_desc('Automated executor of judges for a factbase')
38
37
 
39
- version '0.0.10'
38
+ version('0.0.12')
40
39
 
41
- desc 'Make it more verbose, logging as much as possible'
42
- switch([:v, :verbose])
40
+ synopsis_format(:full)
43
41
 
44
- pre do |global, command, options, args|
45
- if global[:verbose]
46
- loog = Loog::VERBOSE
47
- end
48
- true
42
+ subcommand_option_handling(:normal)
43
+
44
+ desc 'Make it more verbose, logging as much as possible'
45
+ switch([:v, :verbose])
46
+
47
+ pre do |global, command, options, args|
48
+ if global[:verbose]
49
+ loog = Loog::VERBOSE
50
+ end
51
+ true
52
+ end
53
+
54
+ desc 'Update the factbase by passing all judges one by one'
55
+ command :update do |c|
56
+ c.desc 'Options to pass to every judge'
57
+ c.flag([:o, :option], default_value: 'yes', type: Array, arg_name: '<key=value>')
58
+ c.action do |global, options, args|
59
+ require_relative '../lib/judges/commands/update'
60
+ Judges::Update.new(loog).run(options, args)
49
61
  end
62
+ end
50
63
 
51
- desc 'Update the factbase by passing all judges one by one'
52
- command :update do |c|
53
- c.desc 'Environment variable to pass to every judge'
54
- c.flag([:e, :environment], default_value: 'yes')
55
- c.action do |global, options, args|
56
- require_relative '../lib/judges/commands/update'
57
- Judges::Update.new(loog).run(options, args)
58
- end
64
+ desc 'Join two factbases'
65
+ command :print do |c|
66
+ c.action do |global, options, args|
67
+ require_relative '../lib/judges/commands/join'
68
+ Judges::Join.new(loog).run(options, args)
59
69
  end
70
+ end
60
71
 
61
- desc 'Print the factbase into a human-readable format (YAML, JSON, etc.)'
62
- command :print do |c|
63
- c.desc 'Output format (xml, json, or yaml)'
64
- c.flag([:format], default_value: 'yaml')
65
- c.switch([:auto])
66
- c.action do |global, options, args|
67
- require_relative '../lib/judges/commands/print'
68
- Judges::Print.new(loog).run(options, args)
69
- end
72
+ desc 'Print the factbase into a human-readable format (YAML, JSON, etc.)'
73
+ command :print do |c|
74
+ c.desc 'Output format (xml, json, or yaml)'
75
+ c.flag([:format], default_value: 'yaml')
76
+ c.switch([:auto])
77
+ c.action do |global, options, args|
78
+ require_relative '../lib/judges/commands/print'
79
+ Judges::Print.new(loog).run(options, args)
70
80
  end
81
+ end
71
82
 
72
- desc 'Run automated tests for all judges'
73
- command :test do |c|
74
- c.action do |global, options, args|
75
- require_relative '../lib/judges/commands/test'
76
- Judges::Test.new(loog).run(options, args)
77
- end
83
+ desc 'Run automated tests for all judges'
84
+ command :test do |c|
85
+ c.action do |global, options, args|
86
+ require_relative '../lib/judges/commands/test'
87
+ Judges::Test.new(loog).run(options, args)
78
88
  end
79
89
  end
80
90
 
81
- exit App.run(ARGV)
91
+ exit run(ARGV)
data/features/cli.feature CHANGED
@@ -24,8 +24,8 @@ Feature: Simple Run
24
24
  And Exit code is zero
25
25
 
26
26
  Scenario: Simple test of a few judges
27
- Given I run bin/judges with "update ./fixtures temp/simple.fb"
28
- Then Stdout contains "judges processed"
27
+ Given I run bin/judges with "test ./fixtures"
28
+ Then Stdout contains "judges tested"
29
29
  And Exit code is zero
30
30
 
31
31
  Scenario: Simple print of a small factbase
@@ -47,7 +47,7 @@ end
47
47
 
48
48
  When(%r{^I run bin/judges with "([^"]*)"$}) do |arg|
49
49
  home = File.join(File.dirname(__FILE__), '../..')
50
- @stdout = `ruby -I#{home}/lib #{home}/bin/judges #{arg}`
50
+ @stdout = `GLI_DEBUG=true ruby -I#{home}/lib #{home}/bin/judges #{arg}`
51
51
  @exitstatus = $CHILD_STATUS.exitstatus
52
52
  end
53
53
 
@@ -25,7 +25,7 @@ input:
25
25
  time: 2024-01-01T03:15:45
26
26
  seen:
27
27
  - one
28
- - this judge
28
+ - reward_for_good_bug
29
29
  - two
30
30
  expected:
31
31
  - /fb[count(f)=1]
@@ -20,7 +20,10 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- $fb.query("(and (eq kind 'bug was accepted') (not (eq seen 'this judge')))").each do |f|
23
+ $loog.info("Trying to reward a good reported bug (judge=#{$judge})...")
24
+
25
+ once($fb).query("(eq kind 'bug was accepted')").each do |f|
26
+ $loog.info('Good candidate found!')
24
27
  n = $fb.insert
25
28
  n.kind = 'nominate for good bug'
26
29
  n.payee = f.reporter
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.0.10'
29
+ s.version = '0.0.12'
30
30
  s.license = 'MIT'
31
31
  s.summary = 'Command-Line Tool for a Factbase'
32
32
  s.description = '
@@ -41,8 +41,8 @@ Gem::Specification.new do |s|
41
41
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
42
42
  s.rdoc_options = ['--charset=UTF-8']
43
43
  s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
44
- s.add_runtime_dependency 'backtrace', '~>0.3'
45
- s.add_runtime_dependency 'factbase', '~>0.0'
44
+ s.add_runtime_dependency 'backtrace', '~> 0.3'
45
+ s.add_runtime_dependency 'factbase', '~>0.0.12'
46
46
  s.add_runtime_dependency 'gli', '~>2.21'
47
47
  s.add_runtime_dependency 'loog', '~>0.2'
48
48
  s.add_runtime_dependency 'nokogiri', '~> 1.10'
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'factbase'
24
+ require 'fileutils'
25
+ require_relative '../../judges'
26
+ require_relative '../../judges/to_rel'
27
+ require_relative '../../judges/packs'
28
+ require_relative '../../judges/options'
29
+
30
+ # Join.
31
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
32
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
33
+ # License:: MIT
34
+ class Judges::Join
35
+ def initialize(loog)
36
+ @loog = loog
37
+ end
38
+
39
+ def run(_opts, args)
40
+ raise 'Exactly two arguments required' unless args.size == 2
41
+ master = args[0]
42
+ raise "The master factbase is absent: #{master.to_rel}" unless File.exist?(master)
43
+ slave = args[1]
44
+ raise "The slave factbase is absent: #{slave.to_rel}" unless File.exist?(slave)
45
+ fb = Factbase.new
46
+ fb.import(File.read(master))
47
+ @loog.info("Master factbase imported from #{master.to_rel} (#{File.size(master)} bytes)")
48
+ fb.import(File.read(slave))
49
+ @loog.info("Slave factbase imported from #{slave.to_rel} (#{File.size(slave)} bytes)")
50
+ File.write(master, fb.export)
51
+ @loog.info("Master factbase exported to #{master.to_rel} (#{File.size(master)} bytes)")
52
+ end
53
+ end
@@ -23,6 +23,7 @@
23
23
  require 'fileutils'
24
24
  require 'factbase'
25
25
  require_relative '../../judges'
26
+ require_relative '../../judges/to_rel'
26
27
  require_relative '../../judges/packs'
27
28
 
28
29
  # Update.
@@ -37,7 +38,7 @@ class Judges::Print
37
38
  def run(opts, args)
38
39
  raise 'At lease one argument required' if args.empty?
39
40
  f = args[0]
40
- raise "The file is absent: #{f}" unless File.exist?(f)
41
+ raise "The file is absent: #{f.to_rel}" unless File.exist?(f)
41
42
  o = args[1]
42
43
  if o.nil?
43
44
  raise 'Either provide output file name or use --auto' unless opts[:auto]
@@ -46,7 +47,7 @@ class Judges::Print
46
47
  end
47
48
  fb = Factbase.new
48
49
  fb.import(File.read(f))
49
- @loog.info("Factbase imported from #{f} (#{File.size(f)} bytes)")
50
+ @loog.info("Factbase imported from #{f.to_rel} (#{File.size(f)} bytes)")
50
51
  FileUtils.mkdir_p(File.dirname(o))
51
52
  output =
52
53
  case opts[:format].downcase
@@ -58,6 +59,6 @@ class Judges::Print
58
59
  fb.to_xml
59
60
  end
60
61
  File.write(o, output)
61
- @loog.info("Factbase printed to #{o} (#{File.size(o)} bytes)")
62
+ @loog.info("Factbase printed to #{o.to_rel} (#{File.size(o)} bytes)")
62
63
  end
63
64
  end
@@ -20,10 +20,13 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- require 'factbase'
24
23
  require 'nokogiri'
24
+ require 'factbase'
25
+ require 'backtrace'
25
26
  require_relative '../../judges'
27
+ require_relative '../../judges/to_rel'
26
28
  require_relative '../../judges/packs'
29
+ require_relative '../../judges/options'
27
30
 
28
31
  # Test.
29
32
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -37,13 +40,27 @@ class Judges::Test
37
40
  def run(_opts, args)
38
41
  raise 'Exactly one argument required' unless args.size == 1
39
42
  dir = args[0]
40
- done = Judges::Packs.new(dir).each_with_index do |p, i|
41
- p.tests.each do |t|
42
- test_one(p, t)
43
+ @loog.info("Testing judges in '#{dir.to_rel}'...")
44
+ errors = []
45
+ done = Judges::Packs.new(dir, @loog).each_with_index do |p, i|
46
+ @loog.info("\n👉 Testing '#{p.script}' (##{i}) in '#{p.dir.to_rel}'...")
47
+ p.tests.each do |f|
48
+ yaml = YAML.load_file(f, permitted_classes: [Time])
49
+ @loog.info("Testing '#{f.to_rel}':")
50
+ begin
51
+ test_one(p, yaml)
52
+ rescue StandardError => e
53
+ @loog.warn(Backtrace.new(e))
54
+ errors << f
55
+ end
43
56
  end
44
- @loog.info("Pack ##{i} found in #{p.dir}")
45
57
  end
46
- @loog.info("#{done} judges tested")
58
+ if errors.empty?
59
+ @loog.info("\nAll #{done} judges tested successfully")
60
+ else
61
+ @loog.info("\n#{done} judges tested, #{errors.size} of them failed")
62
+ raise "#{errors.size} tests failed" unless errors.empty?
63
+ end
47
64
  end
48
65
 
49
66
  private
@@ -55,17 +72,17 @@ class Judges::Test
55
72
  i.each do |k, vv|
56
73
  if vv.is_a?(Array)
57
74
  vv.each do |v|
58
- send(f, "#{k}=", v)
75
+ f.send("#{k}=", v)
59
76
  end
60
77
  else
61
- f.send('foo=', 42)
78
+ f.send("#{k}=", vv)
62
79
  end
63
80
  end
64
81
  end
65
- pack.run(fb, {})
82
+ pack.run(fb, Judges::Options.new(yaml['options']))
66
83
  xml = Nokogiri::XML.parse(fb.to_xml)
67
84
  yaml['expected'].each do |xp|
68
- raise "#{pack.script} with '#{xp}' doesn't match:\n#{xml}" if xml.xpath(xp).empty?
85
+ raise "#{pack.script} doesn't match '#{xp}':\n#{xml}" if xml.xpath(xp).empty?
69
86
  end
70
87
  end
71
88
  end
@@ -22,8 +22,11 @@
22
22
 
23
23
  require 'factbase'
24
24
  require 'fileutils'
25
+ require 'backtrace'
25
26
  require_relative '../../judges'
27
+ require_relative '../../judges/to_rel'
26
28
  require_relative '../../judges/packs'
29
+ require_relative '../../judges/options'
27
30
 
28
31
  # Update.
29
32
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -34,7 +37,7 @@ class Judges::Update
34
37
  @loog = loog
35
38
  end
36
39
 
37
- def run(_opts, args)
40
+ def run(opts, args)
38
41
  raise 'Exactly two arguments required' unless args.size == 2
39
42
  dir = args[0]
40
43
  raise "The directory is absent: #{dir}" unless File.exist?(dir)
@@ -42,17 +45,26 @@ class Judges::Update
42
45
  fb = Factbase.new
43
46
  if File.exist?(file)
44
47
  fb.import(File.read(file))
45
- @loog.info("Factbase imported from #{file} (#{File.size(file)} bytes)")
48
+ @loog.info("Factbase imported from '#{file.to_rel}' (#{File.size(file)} bytes)")
46
49
  else
47
- @loog.info("There is no Factbase to import from #{file}")
50
+ @loog.info("There is no Factbase to import from '#{file.to_rel}' (file is absent)")
48
51
  end
49
- done = Judges::Packs.new(dir).each_with_index do |p, i|
50
- @loog.info("Pack ##{i} found in #{p.dir}")
51
- p.run(fb, {})
52
+ options = Judges::Options.new(opts['options'])
53
+ @loog.debug("The following options provided:\n\t#{options.to_s.gsub("\n", "\n\t")}")
54
+ errors = []
55
+ done = Judges::Packs.new(dir, @loog).each_with_index do |p, i|
56
+ @loog.info("Pack ##{i} found in #{p.dir.to_rel}")
57
+ begin
58
+ p.run(fb, options)
59
+ rescue StandardError => e
60
+ @loog.warn(Backtrace.new(e))
61
+ errors << p.script
62
+ end
52
63
  end
53
- @loog.info("#{done} judges processed")
64
+ @loog.info("#{done} judges processed (#{errors.size} errors)")
54
65
  FileUtils.mkdir_p(File.dirname(file))
55
66
  File.write(file, fb.export)
56
- @loog.info("Factbase exported to #{file} (#{File.size(file)} bytes)")
67
+ @loog.info("Factbase exported to '#{file.to_rel}' (#{File.size(file)} bytes)")
68
+ raise "Failed to update correctly (#{errors.size} errors)" unless errors.empty?
57
69
  end
58
70
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ # Returns a decorated global factbase, which only touches facts once
24
+ def once(fb, judge: $judge)
25
+ Factbase::Once.new(fb, judge)
26
+ end
27
+
28
+ # Runs only once.
29
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
31
+ # License:: MIT
32
+ class Factbase::Once
33
+ def initialize(fb, func)
34
+ @fb = fb
35
+ @func = func
36
+ end
37
+
38
+ def query(expr)
39
+ expr = "(and #{expr} (not (eq seen '#{@func}')))"
40
+ After.new(@fb.query(expr), @func)
41
+ end
42
+
43
+ def insert
44
+ @fb.insert
45
+ end
46
+
47
+ # What happens after a fact is processed.
48
+ class After
49
+ def initialize(query, func)
50
+ @query = query
51
+ @func = func
52
+ end
53
+
54
+ def each
55
+ @query.each do |f|
56
+ yield f
57
+ f.seen = @func
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require_relative '../judges'
24
+
25
+ # Options for Ruby scripts in the judges.
26
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
27
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # License:: MIT
29
+ class Judges::Options
30
+ # Ctor.
31
+ # @param pairs [Array<String>] List of pairs, like ["token=af73cd3", "max_speed=1"]
32
+ def initialize(pairs)
33
+ @pairs = pairs
34
+ end
35
+
36
+ # Convert them all to a string (printable in a log).
37
+ def to_s
38
+ touch # this will trigger method_missing() method, which will create @hash
39
+ @hash.map do |k, v|
40
+ v = v.to_s
41
+ v = "#{v[0..3]}#{'*' * (v.length - 4)}" if v.length > 8
42
+ "#{k}=#{v}"
43
+ end.join("\n")
44
+ end
45
+
46
+ # Get option by name.
47
+ def method_missing(*args)
48
+ @hash ||= begin
49
+ pp = @pairs || []
50
+ pp = @pairs.map { |k, v| "#{k}=#{v}" } if pp.is_a?(Hash)
51
+ pp.to_h do |pair|
52
+ p = pair.split('=', 2)
53
+ [p[0].to_sym, p[1].match?(/^[0-9]+$/) ? p[1].to_i : p[1]]
54
+ end
55
+ end
56
+ k = args[0].downcase
57
+ @hash[k]
58
+ end
59
+
60
+ # rubocop:disable Style/OptionalBooleanParameter
61
+ def respond_to?(_method, _include_private = false)
62
+ # rubocop:enable Style/OptionalBooleanParameter
63
+ true
64
+ end
65
+
66
+ def respond_to_missing?(_method, _include_private = false)
67
+ true
68
+ end
69
+ end
data/lib/judges/pack.rb CHANGED
@@ -21,7 +21,9 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'yaml'
24
+ require 'time'
24
25
  require_relative '../judges'
26
+ require_relative '../judges/fb/once'
25
27
 
26
28
  # A single pack.
27
29
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -30,21 +32,24 @@ require_relative '../judges'
30
32
  class Judges::Pack
31
33
  attr_reader :dir
32
34
 
33
- def initialize(dir)
35
+ def initialize(dir, loog)
34
36
  @dir = dir
37
+ @loog = loog
35
38
  end
36
39
 
37
40
  # Run it with the given Factbase and environment variables.
38
- def run(fbase, env)
41
+ def run(fbase, options)
39
42
  $fb = fbase
40
- env.each do |k, v|
41
- # rubocop:disable Security/Eval
42
- eval("$#{k} = '#{v}'", binding, __FILE__, __LINE__) # $foo = 42
43
- # rubocop:enable Security/Eval
44
- end
43
+ $judge = File.basename(@dir)
44
+ $options = options
45
+ $loog = @loog
45
46
  s = File.join(@dir, script)
46
47
  raise "Can't load '#{s}'" unless File.exist?(s)
47
- load s
48
+ begin
49
+ load(s, true)
50
+ ensure
51
+ $fb = $judge = $options = $loog = nil
52
+ end
48
53
  end
49
54
 
50
55
  # Get the name of the .rb script in the pack.
@@ -52,10 +57,8 @@ class Judges::Pack
52
57
  File.basename(Dir.glob(File.join(@dir, '*.rb')).first)
53
58
  end
54
59
 
55
- # Iterate over .yml tests.
60
+ # Return all .yml tests files.
56
61
  def tests
57
- Dir.glob(File.join(@dir, '*.yml')).map do |f|
58
- YAML.load_file(f)
59
- end
62
+ Dir.glob(File.join(@dir, '*.yml'))
60
63
  end
61
64
  end
data/lib/judges/packs.rb CHANGED
@@ -28,8 +28,9 @@ require_relative 'pack'
28
28
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  class Judges::Packs
31
- def initialize(dir)
31
+ def initialize(dir, loog)
32
32
  @dir = dir
33
+ @loog = loog
33
34
  end
34
35
 
35
36
  # Iterate over them all.
@@ -37,7 +38,7 @@ class Judges::Packs
37
38
  def each
38
39
  Dir.glob(File.join(@dir, '**/*.rb')).each do |f|
39
40
  d = File.dirname(File.absolute_path(f))
40
- yield Judges::Pack.new(d)
41
+ yield Judges::Pack.new(d, @loog)
41
42
  end
42
43
  end
43
44
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2024 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -17,26 +19,22 @@
17
19
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
21
  # SOFTWARE.
20
- ---
21
- name: zerocracy
22
- on:
23
- schedule:
24
- - cron: '0,10,20,30,50,50 * * * *'
25
- concurrency:
26
- group: zerocracy
27
- cancel-in-progress: true
28
- jobs:
29
- yamllint:
30
- runs-on: ubuntu-22.04
31
- steps:
32
- - uses: actions/checkout@v4
33
- - uses: zerocracy/judges-action@master
34
- with:
35
- factbase: recent.fb
36
- - run: mkdir gh-pages && cp recent.yml gh-pages
37
- - uses: JamesIves/github-pages-deploy-action@v4.6.0
38
- with:
39
- branch: gh-pages
40
- folder: gh-pages/recent.yml
41
- factbase: recent.fb
42
- clean: true
22
+
23
+ require 'pathname'
24
+
25
+ # Adding method +to_rel+ to all Ruby objects.
26
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
27
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
28
+ # License:: MIT
29
+ class Object
30
+ # Generates a relative name of a file (to the current dir).
31
+ def to_rel
32
+ s = File.absolute_path(to_s)
33
+ p = Pathname.new(s).relative_path_from(Dir.getwd)
34
+ if p.directory?
35
+ "#{p}/"
36
+ else
37
+ p.to_s
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require 'loog'
25
+ require 'nokogiri'
26
+ require_relative '../../lib/judges'
27
+ require_relative '../../lib/judges/commands/join'
28
+
29
+ # Test.
30
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
31
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
32
+ # License:: MIT
33
+ class TestJoin < Minitest::Test
34
+ def test_simple_join
35
+ Dir.mktmpdir do |d|
36
+ master = File.join(d, 'master.fb')
37
+ fb1 = Factbase.new
38
+ fb1.insert.zz = 5
39
+ File.write(master, fb1.export)
40
+ slave = File.join(d, 'slave.fb')
41
+ fb2 = Factbase.new
42
+ fb2.insert.foo_bar = 42
43
+ File.write(slave, fb2.export)
44
+ Judges::Join.new(Loog::VERBOSE).run({}, [master, slave])
45
+ fb = Factbase.new
46
+ fb.import(File.read(master))
47
+ xml = Nokogiri::XML.parse(fb.to_xml)
48
+ assert(!xml.xpath('/fb/f[zz="5"]').empty?, fb.to_xml)
49
+ assert(!xml.xpath('/fb/f[foo_bar="42"]').empty?, fb.to_xml)
50
+ end
51
+ end
52
+ end
@@ -69,4 +69,22 @@ class TestTest < Minitest::Test
69
69
  end
70
70
  end
71
71
  end
72
+
73
+ def test_with_options
74
+ Dir.mktmpdir do |d|
75
+ File.write(File.join(d, 'foo.rb'), '$fb.insert.foo = $options.bar')
76
+ File.write(
77
+ File.join(d, 'something.yml'),
78
+ <<-YAML
79
+ input: []
80
+ options:
81
+ bar: 42
82
+ expected:
83
+ - /fb[count(f)=1]
84
+ - /fb/f[foo='42']
85
+ YAML
86
+ )
87
+ Judges::Test.new(Loog::VERBOSE).run(nil, [d])
88
+ end
89
+ end
72
90
  end
@@ -22,6 +22,7 @@
22
22
 
23
23
  require 'minitest/autorun'
24
24
  require 'loog'
25
+ require 'nokogiri'
25
26
  require_relative '../../lib/judges'
26
27
  require_relative '../../lib/judges/commands/update'
27
28
 
@@ -30,12 +31,31 @@ require_relative '../../lib/judges/commands/update'
30
31
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
31
32
  # License:: MIT
32
33
  class TestUpdate < Minitest::Test
33
- def test_simple_update
34
+ def test_build_factbase_from_scratch
34
35
  Dir.mktmpdir do |d|
35
- File.write(File.join(d, 'foo.rb'), '$fb.query("(eq foo 42)").each { |f| f.bar = 4 }')
36
- fb = File.join(d, 'base.fb')
37
- Judges::Update.new(Loog::VERBOSE).run(nil, [d, fb])
38
- assert(File.exist?(fb))
36
+ File.write(File.join(d, 'foo.rb'), '$fb.insert.zzz = $options.foo_bar + 1')
37
+ file = File.join(d, 'base.fb')
38
+ Judges::Update.new(Loog::VERBOSE).run({ 'options' => ['foo_bar=42'] }, [d, file])
39
+ fb = Factbase.new
40
+ fb.import(File.read(file))
41
+ xml = Nokogiri::XML.parse(fb.to_xml)
42
+ assert(!xml.xpath('/fb/f[zzz="43"]').empty?)
43
+ end
44
+ end
45
+
46
+ def test_extend_existing_factbase
47
+ Dir.mktmpdir do |d|
48
+ file = File.join(d, 'base.fb')
49
+ fb = Factbase.new
50
+ fb.insert.foo_bar = 42
51
+ File.write(file, fb.export)
52
+ File.write(File.join(d, 'foo.rb'), '$fb.insert.tt = 4')
53
+ Judges::Update.new(Loog::VERBOSE).run({}, [d, file])
54
+ fb = Factbase.new
55
+ fb.import(File.read(file))
56
+ xml = Nokogiri::XML.parse(fb.to_xml)
57
+ assert(!xml.xpath('/fb/f[tt="4"]').empty?)
58
+ assert(!xml.xpath('/fb/f[foo_bar="42"]').empty?)
39
59
  end
40
60
  end
41
61
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require 'tmpdir'
25
+ require 'factbase'
26
+ require_relative '../../lib/judges'
27
+ require_relative '../../lib/judges/fb/once'
28
+
29
+ # Test.
30
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
31
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
32
+ # License:: MIT
33
+ class TestOnce < Minitest::Test
34
+ def test_touch_once
35
+ fb = once(Factbase.new, judge: 'something')
36
+ fb.insert
37
+ fb.query('()').each { |f| f.foo = 42 }
38
+ assert(fb.query('()').extend(Enumerable).to_a.empty?)
39
+ end
40
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require_relative '../lib/judges'
25
+ require_relative '../lib/judges/options'
26
+
27
+ # Test.
28
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
+ # License:: MIT
31
+ class TestOptions < Minitest::Test
32
+ def test_basic
33
+ opts = Judges::Options.new(['token=a77', 'max=42'])
34
+ assert_equal('a77', opts.token)
35
+ assert_equal(42, opts.max)
36
+ end
37
+
38
+ def test_with_nil
39
+ opts = Judges::Options.new(nil)
40
+ assert(opts.foo.nil?)
41
+ end
42
+
43
+ def test_with_hash
44
+ opts = Judges::Options.new('foo' => 42, 'bar' => 'hello')
45
+ assert_equal(42, opts.foo)
46
+ assert_equal('hello', opts.bar)
47
+ assert(opts.xxx.nil?)
48
+ end
49
+
50
+ def test_converts_to_string
51
+ opts = Judges::Options.new('foo' => 44, 'bar' => 'long-string-maybe-secret')
52
+ assert_equal("foo=44\nbar=long********************", opts.to_s)
53
+ end
54
+ end
data/test/test_pack.rb CHANGED
@@ -22,6 +22,7 @@
22
22
 
23
23
  require 'minitest/autorun'
24
24
  require 'tmpdir'
25
+ require 'loog'
25
26
  require 'factbase'
26
27
  require_relative '../lib/judges'
27
28
  require_relative '../lib/judges/pack'
@@ -34,7 +35,7 @@ class TestPack < Minitest::Test
34
35
  def test_basic_run
35
36
  Dir.mktmpdir do |d|
36
37
  File.write(File.join(d, 'foo.rb'), '$fb.insert')
37
- pack = Judges::Pack.new(d)
38
+ pack = Judges::Pack.new(d, Loog::VERBOSE)
38
39
  fb = Factbase.new
39
40
  pack.run(fb, {})
40
41
  assert_equal(1, fb.size)
@@ -44,7 +45,7 @@ class TestPack < Minitest::Test
44
45
  def test_run_isolated
45
46
  Dir.mktmpdir do |d|
46
47
  File.write(File.join(d, 'bar.rb'), '$fb.insert')
47
- pack = Judges::Pack.new(d)
48
+ pack = Judges::Pack.new(d, Loog::VERBOSE)
48
49
  fb1 = Factbase.new
49
50
  pack.run(fb1, {})
50
51
  assert_equal(1, fb1.size)
@@ -53,4 +54,24 @@ class TestPack < Minitest::Test
53
54
  assert_equal(1, fb2.size)
54
55
  end
55
56
  end
57
+
58
+ def test_with_supplemenary_functions
59
+ Dir.mktmpdir do |d|
60
+ File.write(File.join(d, 'x.rb'), 'once($fb).insert')
61
+ pack = Judges::Pack.new(d, Loog::VERBOSE)
62
+ pack.run(Factbase.new, {})
63
+ end
64
+ end
65
+
66
+ def test_sets_judge_value_correctly
67
+ Dir.mktmpdir do |d|
68
+ j = 'this_is_it'
69
+ dir = File.join(d, j)
70
+ FileUtils.mkdir(dir)
71
+ File.write(File.join(dir, 'foo.rb'), '$loog.info("judge=" + $judge)')
72
+ log = Loog::Buffer.new
73
+ Judges::Pack.new(dir, log).run(Factbase.new, {})
74
+ assert(log.to_s.include?("judge=#{j}"))
75
+ end
76
+ end
56
77
  end
data/test/test_packs.rb CHANGED
@@ -22,6 +22,7 @@
22
22
 
23
23
  require 'minitest/autorun'
24
24
  require 'tmpdir'
25
+ require 'loog'
25
26
  require_relative '../lib/judges'
26
27
  require_relative '../lib/judges/packs'
27
28
 
@@ -35,10 +36,10 @@ class TestPacks < Minitest::Test
35
36
  File.write(File.join(d, 'foo.rb'), 'hey')
36
37
  File.write(File.join(d, 'something.yml'), "---\nfoo: 42")
37
38
  found = 0
38
- Judges::Packs.new(d).each do |p|
39
+ Judges::Packs.new(d, Loog::VERBOSE).each do |p|
39
40
  assert_equal('foo.rb', p.script)
40
41
  found += 1
41
- assert_equal(42, p.tests.first['foo'])
42
+ assert_equal('something.yml', File.basename(p.tests.first))
42
43
  end
43
44
  assert_equal(1, found)
44
45
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require_relative '../lib/judges'
25
+ require_relative '../lib/judges/to_rel'
26
+
27
+ # Test.
28
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
+ # License:: MIT
31
+ class TestToRel < Minitest::Test
32
+ def test_simple_mapping
33
+ n = File.absolute_path(File.join('.', 'lib/../lib/commands/update.rb'))
34
+ assert_equal('lib/commands/update.rb', n.to_rel)
35
+ end
36
+
37
+ def test_maps_dir_name
38
+ n = File.absolute_path(File.join('.', 'lib/../lib/judges/commands'))
39
+ assert_equal('lib/judges/commands/', n.to_rel)
40
+ end
41
+ end
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.0.10
4
+ version: 0.0.12
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-05-12 00:00:00.000000000 Z
11
+ date: 2024-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.0'
33
+ version: 0.0.12
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.0'
40
+ version: 0.0.12
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: gli
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -101,7 +101,6 @@ files:
101
101
  - ".github/workflows/rake.yml"
102
102
  - ".github/workflows/xcop.yml"
103
103
  - ".github/workflows/yamllint.yml"
104
- - ".github/workflows/zerocracy.yaml"
105
104
  - ".gitignore"
106
105
  - ".pdd"
107
106
  - ".rubocop.yml"
@@ -122,19 +121,27 @@ files:
122
121
  - fixtures/reward_for_good_bug/simple-reward.yml
123
122
  - judges.gemspec
124
123
  - lib/judges.rb
124
+ - lib/judges/commands/join.rb
125
125
  - lib/judges/commands/print.rb
126
126
  - lib/judges/commands/test.rb
127
127
  - lib/judges/commands/update.rb
128
+ - lib/judges/fb/once.rb
129
+ - lib/judges/options.rb
128
130
  - lib/judges/pack.rb
129
131
  - lib/judges/packs.rb
132
+ - lib/judges/to_rel.rb
130
133
  - renovate.json
134
+ - test/commands/test_join.rb
131
135
  - test/commands/test_print.rb
132
136
  - test/commands/test_test.rb
133
137
  - test/commands/test_update.rb
138
+ - test/fb/test_once.rb
134
139
  - test/test__helper.rb
135
140
  - test/test_judges.rb
141
+ - test/test_options.rb
136
142
  - test/test_pack.rb
137
143
  - test/test_packs.rb
144
+ - test/test_to_rel.rb
138
145
  homepage: http://github.com/yegor256/judges
139
146
  licenses:
140
147
  - MIT