judges 0.0.32 → 0.0.34
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 +4 -4
- data/.rultor.yml +2 -2
- data/Gemfile.lock +3 -3
- data/bin/judges +11 -8
- data/features/test.feature +34 -3
- data/features/trim.feature +6 -7
- data/fixtures/try/try.rb +1 -1
- data/judges.gemspec +8 -7
- data/lib/judges/categories.rb +51 -0
- data/lib/judges/commands/test.rb +29 -13
- data/lib/judges/commands/trim.rb +1 -10
- data/lib/judges/fb/if_absent.rb +7 -4
- data/lib/judges/fb/once.rb +24 -33
- data/lib/judges/options.rb +11 -1
- data/lib/judges/pack.rb +0 -1
- data/lib/judges.rb +3 -1
- data/test/commands/test_trim.rb +1 -1
- data/test/fb/test_if_absent.rb +8 -1
- data/test/fb/test_once.rb +52 -4
- data/test/test_categories.rb +50 -0
- data/test/test_options.rb +8 -0
- data/test/test_pack.rb +3 -3
- metadata +9 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d6543b9b405aa797aa98883b8294c15e61215db07121c7c0e4616081f9457de
|
|
4
|
+
data.tar.gz: 86c543a69bf748262f0f1072f151f8e1f3de9f8d015192699d811d809452328c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a487dade2d9e24c24298aab0132fdf653688a2133904c2cad712692762759e9fe8c3e9076e99d09b67a73b8a2580445600ffb2766db0a6d4ef01052de2163392
|
|
7
|
+
data.tar.gz: 76111d01a21353718aadde66a0af44d5b341bce928c670704298118ffa8cd0d147e5d50ce21329d7a1adb69e0241243a6a2c3af273d0f6e126bb49fff9618c82
|
data/.rultor.yml
CHANGED
|
@@ -33,8 +33,8 @@ release:
|
|
|
33
33
|
rm -rf *.gem
|
|
34
34
|
sed -i "s/0\.0\.0/${tag}/g" judges.gemspec
|
|
35
35
|
git add judges.gemspec
|
|
36
|
-
sed -i "s/0\.0\.0/${tag}/g"
|
|
37
|
-
git add
|
|
36
|
+
sed -i "s/0\.0\.0/${tag}/g" lib/judges.rb
|
|
37
|
+
git add lib/judges.rb
|
|
38
38
|
git commit -m "version set to ${tag}"
|
|
39
39
|
gem build judges.gemspec
|
|
40
40
|
chmod 0600 ../rubygems.yml
|
data/Gemfile.lock
CHANGED
|
@@ -3,7 +3,7 @@ PATH
|
|
|
3
3
|
specs:
|
|
4
4
|
judges (0.0.0)
|
|
5
5
|
backtrace (~> 0.3)
|
|
6
|
-
factbase (~> 0.0.
|
|
6
|
+
factbase (~> 0.0.38)
|
|
7
7
|
gli (~> 2.21)
|
|
8
8
|
loog (~> 0.2)
|
|
9
9
|
nokogiri (~> 1.10)
|
|
@@ -74,7 +74,7 @@ GEM
|
|
|
74
74
|
docile (1.4.0)
|
|
75
75
|
drb (2.2.1)
|
|
76
76
|
erubi (1.12.0)
|
|
77
|
-
factbase (0.0.
|
|
77
|
+
factbase (0.0.38)
|
|
78
78
|
json (~> 2.7)
|
|
79
79
|
loog (~> 0.2)
|
|
80
80
|
nokogiri (~> 1.10)
|
|
@@ -210,7 +210,7 @@ GEM
|
|
|
210
210
|
webrick (1.8.1)
|
|
211
211
|
yaml (0.3.0)
|
|
212
212
|
yard (0.9.36)
|
|
213
|
-
zeitwerk (2.6.
|
|
213
|
+
zeitwerk (2.6.15)
|
|
214
214
|
|
|
215
215
|
PLATFORMS
|
|
216
216
|
arm64-darwin-22
|
data/bin/judges
CHANGED
|
@@ -32,13 +32,12 @@ Encoding.default_internal = Encoding::UTF_8
|
|
|
32
32
|
class App
|
|
33
33
|
extend GLI::App
|
|
34
34
|
|
|
35
|
-
ver = '0.0.32'
|
|
36
|
-
|
|
37
35
|
loog = Loog::REGULAR
|
|
38
36
|
|
|
39
37
|
program_desc('Automated executor of judges for a factbase')
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
require_relative '../lib/judges'
|
|
40
|
+
version(Judges::VERSION)
|
|
42
41
|
|
|
43
42
|
synopsis_format(:full)
|
|
44
43
|
|
|
@@ -51,7 +50,7 @@ class App
|
|
|
51
50
|
if global[:verbose]
|
|
52
51
|
loog = Loog::VERBOSE
|
|
53
52
|
end
|
|
54
|
-
loog.debug("judges #{
|
|
53
|
+
loog.debug("judges #{Judges::VERSION}")
|
|
55
54
|
true
|
|
56
55
|
end
|
|
57
56
|
|
|
@@ -98,9 +97,7 @@ class App
|
|
|
98
97
|
desc 'Remove the facts that are too old'
|
|
99
98
|
command :trim do |c|
|
|
100
99
|
c.desc 'Only the facts that match the expression are deleted'
|
|
101
|
-
c.flag([:query])
|
|
102
|
-
c.desc 'Remove facts that are older than X days'
|
|
103
|
-
c.flag([:days], type: Integer)
|
|
100
|
+
c.flag([:query], default_value: '(never)')
|
|
104
101
|
c.action do |global, options, args|
|
|
105
102
|
require_relative '../lib/judges/commands/trim'
|
|
106
103
|
Judges::Trim.new(loog).run(options, args)
|
|
@@ -114,7 +111,7 @@ class App
|
|
|
114
111
|
c.desc 'Generate output name of the file automatically'
|
|
115
112
|
c.switch([:auto], default_value: false)
|
|
116
113
|
c.desc 'Only the facts that match the expression are printed'
|
|
117
|
-
c.flag([:query], default_value: '()')
|
|
114
|
+
c.flag([:query], default_value: '(always)')
|
|
118
115
|
c.desc 'Print even if target file already exists and is older than the factbase'
|
|
119
116
|
c.switch([:force], default_value: false)
|
|
120
117
|
c.action do |global, options, args|
|
|
@@ -133,6 +130,12 @@ class App
|
|
|
133
130
|
|
|
134
131
|
desc 'Run automated tests for all judges'
|
|
135
132
|
command :test do |c|
|
|
133
|
+
c.desc 'Options to pass to every judge (may be overwritten by YAML)'
|
|
134
|
+
c.flag([:o, :option], multiple: true, arg_name: '<key=value>')
|
|
135
|
+
c.desc 'Categories of tests to disable'
|
|
136
|
+
c.flag([:disable], multiple: true)
|
|
137
|
+
c.desc 'Categories of tests to enable'
|
|
138
|
+
c.flag([:enable], multiple: true)
|
|
136
139
|
c.desc 'Name of the test pack to run'
|
|
137
140
|
c.flag([:pack], multiple: true)
|
|
138
141
|
c.desc 'The location of a Ruby library (directory with .rb files to include)'
|
data/features/test.feature
CHANGED
|
@@ -4,12 +4,12 @@ Feature: Test
|
|
|
4
4
|
Scenario: Simple test of a few judges
|
|
5
5
|
Given I run bin/judges with "test ./fixtures"
|
|
6
6
|
Then Stdout contains "👉 Testing"
|
|
7
|
-
Then Stdout contains "judge(s)
|
|
7
|
+
Then Stdout contains "All 2 judge(s) and 2 tests passed"
|
|
8
8
|
And Exit code is zero
|
|
9
9
|
|
|
10
10
|
Scenario: Simple test of just one pack
|
|
11
11
|
Given I run bin/judges with "test --pack guess ./fixtures"
|
|
12
|
-
Then Stdout contains "judge(s)
|
|
12
|
+
Then Stdout contains "All 1 judge(s) and 1 tests passed"
|
|
13
13
|
And Exit code is zero
|
|
14
14
|
|
|
15
15
|
Scenario: Simple test of no packs
|
|
@@ -28,5 +28,36 @@ Feature: Test
|
|
|
28
28
|
$foo = 42
|
|
29
29
|
"""
|
|
30
30
|
Then I run bin/judges with "test --lib mylib mypacks"
|
|
31
|
-
Then Stdout contains "All 1 judge(s)
|
|
31
|
+
Then Stdout contains "All 1 judge(s) and 0 tests passed"
|
|
32
32
|
And Exit code is zero
|
|
33
|
+
|
|
34
|
+
Scenario: Enable only one category
|
|
35
|
+
Given I make a temp directory
|
|
36
|
+
Then I have a "mypacks/good/good.rb" file with content:
|
|
37
|
+
"""
|
|
38
|
+
n = $fb.insert
|
|
39
|
+
"""
|
|
40
|
+
Then I have a "mypacks/good/good.yml" file with content:
|
|
41
|
+
"""
|
|
42
|
+
---
|
|
43
|
+
category: good
|
|
44
|
+
input: []
|
|
45
|
+
"""
|
|
46
|
+
Then I have a "mypacks/bad/bad.rb" file with content:
|
|
47
|
+
"""
|
|
48
|
+
broken$ruby$syntax
|
|
49
|
+
"""
|
|
50
|
+
Then I have a "mypacks/bad/bad.yml" file with content:
|
|
51
|
+
"""
|
|
52
|
+
---
|
|
53
|
+
category: bad
|
|
54
|
+
"""
|
|
55
|
+
Then I run bin/judges with "test --enable good mypacks"
|
|
56
|
+
Then Stdout contains "All 2 judge(s) and 1 tests passed"
|
|
57
|
+
And Exit code is zero
|
|
58
|
+
Then I run bin/judges with "test --disable bad mypacks"
|
|
59
|
+
Then Stdout contains "All 2 judge(s) and 1 tests passed"
|
|
60
|
+
And Exit code is zero
|
|
61
|
+
Then I run bin/judges with "test --enable bad mypacks"
|
|
62
|
+
Then Stdout contains "Testing mypacks/bad/bad.yml"
|
|
63
|
+
And Exit code is not zero
|
data/features/trim.feature
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
Feature: Trim
|
|
2
2
|
I want to trim a factbase
|
|
3
3
|
|
|
4
|
-
Scenario: Simple trimming of a factbase
|
|
4
|
+
Scenario: Simple trimming of a factbase, with a query
|
|
5
5
|
Given I make a temp directory
|
|
6
|
-
Then I run bin/judges with "--verbose eval simple.fb '$fb.insert.
|
|
7
|
-
|
|
8
|
-
Given I run bin/judges with "trim --days 5 simple.fb"
|
|
6
|
+
Then I run bin/judges with "--verbose eval simple.fb '$fb.insert.foo = 42'"
|
|
7
|
+
Given I run bin/judges with "trim --query '(eq foo 42)' simple.fb"
|
|
9
8
|
Then Stdout contains "1 fact(s) deleted"
|
|
10
9
|
And Exit code is zero
|
|
11
10
|
|
|
12
|
-
Scenario:
|
|
11
|
+
Scenario: Delete nothing by default
|
|
13
12
|
Given I make a temp directory
|
|
14
13
|
Then I run bin/judges with "--verbose eval simple.fb '$fb.insert.foo = 42'"
|
|
15
|
-
Given I run bin/judges with "trim
|
|
16
|
-
Then Stdout contains "
|
|
14
|
+
Given I run bin/judges with "trim simple.fb"
|
|
15
|
+
Then Stdout contains "No facts deleted"
|
|
17
16
|
And Exit code is zero
|
data/fixtures/try/try.rb
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
each_once($fb, "(and (exists number) (lt time #{Time.now.utc.iso8601}))") do |f|
|
|
24
24
|
n = $fb.insert
|
|
25
25
|
n.guess = f.number
|
|
26
26
|
end
|
data/judges.gemspec
CHANGED
|
@@ -26,14 +26,15 @@ 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.
|
|
29
|
+
s.version = '0.0.34'
|
|
30
30
|
s.license = 'MIT'
|
|
31
31
|
s.summary = 'Command-Line Tool for a Factbase'
|
|
32
|
-
s.description =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
s.description =
|
|
33
|
+
'A command-line tool that runs a collection of \"judges\" ' \
|
|
34
|
+
'against a \"factbase,\" modifying ' \
|
|
35
|
+
'it and updating. Also, helps printing a factbase, merge with ' \
|
|
36
|
+
'another one, inspect, and so on. Also, helps run automated tests ' \
|
|
37
|
+
'for a set of judges.'
|
|
37
38
|
s.authors = ['Yegor Bugayenko']
|
|
38
39
|
s.email = 'yegor256@gmail.com'
|
|
39
40
|
s.homepage = 'http://github.com/yegor256/judges'
|
|
@@ -42,7 +43,7 @@ Gem::Specification.new do |s|
|
|
|
42
43
|
s.rdoc_options = ['--charset=UTF-8']
|
|
43
44
|
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
|
44
45
|
s.add_runtime_dependency 'backtrace', '~> 0.3'
|
|
45
|
-
s.add_runtime_dependency 'factbase', '~>0.0.
|
|
46
|
+
s.add_runtime_dependency 'factbase', '~>0.0.38'
|
|
46
47
|
s.add_runtime_dependency 'gli', '~>2.21'
|
|
47
48
|
s.add_runtime_dependency 'loog', '~>0.2'
|
|
48
49
|
s.add_runtime_dependency 'nokogiri', '~> 1.10'
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
# Categories of tests.
|
|
26
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
27
|
+
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
|
28
|
+
# License:: MIT
|
|
29
|
+
class Judges::Categories
|
|
30
|
+
# Ctor.
|
|
31
|
+
# @param [Array<String>] enable List of categories to enable
|
|
32
|
+
# @param [Array<String>] disable List of categories to enable
|
|
33
|
+
def initialize(enable, disable)
|
|
34
|
+
@enable = enable.is_a?(Array) ? enable : []
|
|
35
|
+
@disable = disable.is_a?(Array) ? disable : []
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# This test is good to go, with this list of categories?
|
|
39
|
+
# @param [Array<String>] cats List of them
|
|
40
|
+
# @return [Boolean] True if yes
|
|
41
|
+
def ok?(cats)
|
|
42
|
+
cats = [] if cats.nil?
|
|
43
|
+
cats = [cats] unless cats.is_a?(Array)
|
|
44
|
+
cats.each do |c|
|
|
45
|
+
return false if @disable.any? { |d| d == c }
|
|
46
|
+
return true if @enable.any? { |d| d == c }
|
|
47
|
+
end
|
|
48
|
+
return true if @enable.empty?
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/judges/commands/test.rb
CHANGED
|
@@ -29,6 +29,7 @@ require_relative '../../judges'
|
|
|
29
29
|
require_relative '../../judges/to_rel'
|
|
30
30
|
require_relative '../../judges/packs'
|
|
31
31
|
require_relative '../../judges/options'
|
|
32
|
+
require_relative '../../judges/categories'
|
|
32
33
|
require_relative '../../judges/elapsed'
|
|
33
34
|
|
|
34
35
|
# Test.
|
|
@@ -45,7 +46,8 @@ class Judges::Test
|
|
|
45
46
|
dir = args[0]
|
|
46
47
|
@loog.info("Testing judges in #{dir.to_rel}...")
|
|
47
48
|
errors = []
|
|
48
|
-
|
|
49
|
+
packs = 0
|
|
50
|
+
tests = 0
|
|
49
51
|
elapsed(@loog) do
|
|
50
52
|
Judges::Packs.new(dir, opts['lib'], @loog).each_with_index do |p, i|
|
|
51
53
|
next unless include?(opts, p.name)
|
|
@@ -56,22 +58,32 @@ class Judges::Test
|
|
|
56
58
|
@loog.info("Skippped #{f.to_rel}")
|
|
57
59
|
next
|
|
58
60
|
end
|
|
59
|
-
|
|
61
|
+
unless Judges::Categories.new(opts['enable'], opts['disable']).ok?(yaml['category'])
|
|
62
|
+
@loog.info("Skippped #{f.to_rel} because of its category")
|
|
63
|
+
next
|
|
64
|
+
end
|
|
65
|
+
@loog.info("🛠️ Testing #{f.to_rel}:")
|
|
60
66
|
begin
|
|
61
|
-
test_one(p, yaml)
|
|
67
|
+
test_one(opts, p, yaml)
|
|
68
|
+
tests += 1
|
|
62
69
|
rescue StandardError => e
|
|
63
70
|
@loog.warn(Backtrace.new(e))
|
|
64
71
|
errors << f
|
|
65
72
|
end
|
|
66
73
|
end
|
|
67
|
-
|
|
74
|
+
packs += 1
|
|
68
75
|
end
|
|
69
|
-
throw :'👍 No judges tested' if
|
|
70
|
-
throw :"👍 All #{
|
|
71
|
-
throw :"❌ #{
|
|
76
|
+
throw :'👍 No judges tested' if packs.zero?
|
|
77
|
+
throw :"👍 All #{packs} judge(s) and #{tests} tests passed" if errors.empty?
|
|
78
|
+
throw :"❌ #{packs} judge(s) tested, #{errors.size} of them failed"
|
|
79
|
+
end
|
|
80
|
+
unless errors.empty?
|
|
81
|
+
raise "#{errors.size} tests failed" unless opts['quiet']
|
|
82
|
+
@loog.debug('Not failing the build with tests failures, due to the --quiet option')
|
|
72
83
|
end
|
|
73
|
-
|
|
74
|
-
raise 'No judges tested :(' unless opts['quiet']
|
|
84
|
+
return unless packs.zero?
|
|
85
|
+
raise 'No judges tested :(' unless opts['quiet']
|
|
86
|
+
@loog.debug('Not failing the build with no judges tested, due to the --quiet option')
|
|
75
87
|
end
|
|
76
88
|
|
|
77
89
|
private
|
|
@@ -82,9 +94,10 @@ class Judges::Test
|
|
|
82
94
|
packs.include?(name)
|
|
83
95
|
end
|
|
84
96
|
|
|
85
|
-
def test_one(pack, yaml)
|
|
97
|
+
def test_one(opts, pack, yaml)
|
|
86
98
|
fb = Factbase.new
|
|
87
|
-
yaml['input']
|
|
99
|
+
inputs = yaml['input']
|
|
100
|
+
inputs&.each do |i|
|
|
88
101
|
f = fb.insert
|
|
89
102
|
i.each do |k, vv|
|
|
90
103
|
if vv.is_a?(Array)
|
|
@@ -96,9 +109,12 @@ class Judges::Test
|
|
|
96
109
|
end
|
|
97
110
|
end
|
|
98
111
|
end
|
|
99
|
-
|
|
112
|
+
options = Judges::Options.new(opts['option']) + Judges::Options.new(yaml['options'])
|
|
113
|
+
pack.run(Factbase::Looged.new(fb, @loog), {}, {}, options)
|
|
114
|
+
xpaths = yaml['expected']
|
|
115
|
+
return if xpaths.nil?
|
|
100
116
|
xml = Nokogiri::XML.parse(Factbase::ToXML.new(fb).xml)
|
|
101
|
-
|
|
117
|
+
xpaths.each do |xp|
|
|
102
118
|
raise "#{pack.script} doesn't match '#{xp}':\n#{xml}" if xml.xpath(xp).empty?
|
|
103
119
|
end
|
|
104
120
|
end
|
data/lib/judges/commands/trim.rb
CHANGED
|
@@ -38,17 +38,8 @@ class Judges::Trim
|
|
|
38
38
|
raise 'Exactly one argument required' unless args.size == 1
|
|
39
39
|
impex = Judges::Impex.new(@loog, args[0])
|
|
40
40
|
fb = impex.import
|
|
41
|
-
query = opts['query']
|
|
42
|
-
if query.nil?
|
|
43
|
-
days = opts['days']
|
|
44
|
-
day = Time.now - (days * 60 * 60 * 24)
|
|
45
|
-
query = "(lt time #{day.utc.iso8601})"
|
|
46
|
-
@loog.info("Deleting facts that are older than #{days} days")
|
|
47
|
-
else
|
|
48
|
-
raise 'Specify either --days or --query' unless opts['days'].nil?
|
|
49
|
-
end
|
|
50
41
|
elapsed(@loog) do
|
|
51
|
-
deleted = fb.query(query).delete!
|
|
42
|
+
deleted = fb.query(opts['query']).delete!
|
|
52
43
|
throw :'No facts deleted' if deleted.zero?
|
|
53
44
|
impex.export(fb)
|
|
54
45
|
throw :"🗑 #{deleted} fact(s) deleted"
|
data/lib/judges/fb/if_absent.rb
CHANGED
|
@@ -31,9 +31,9 @@ def if_absent(fb)
|
|
|
31
31
|
q = attrs.map do |k, v|
|
|
32
32
|
vv = v.to_s
|
|
33
33
|
if v.is_a?(String)
|
|
34
|
-
vv = "'#{vv.gsub('"', '
|
|
34
|
+
vv = "'#{vv.gsub('"', '\\\\"').gsub("'", "\\\\'")}'"
|
|
35
35
|
elsif v.is_a?(Time)
|
|
36
|
-
vv = v.iso8601
|
|
36
|
+
vv = v.utc.iso8601
|
|
37
37
|
end
|
|
38
38
|
"(eq #{k} #{vv})"
|
|
39
39
|
end.join(' ')
|
|
@@ -55,8 +55,11 @@ class Judges::Accumulator
|
|
|
55
55
|
|
|
56
56
|
def method_missing(*args)
|
|
57
57
|
k = args[0]
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
if k.end_with?('=')
|
|
59
|
+
@map[k[0..-2].to_sym] = args[1]
|
|
60
|
+
else
|
|
61
|
+
@map[k.to_sym]
|
|
62
|
+
end
|
|
60
63
|
end
|
|
61
64
|
|
|
62
65
|
# rubocop:disable Style/OptionalBooleanParameter
|
data/lib/judges/fb/once.rb
CHANGED
|
@@ -21,43 +21,34 @@
|
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
23
|
require 'judges'
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# License:: MIT
|
|
34
|
-
class Judges::Once
|
|
35
|
-
def initialize(fb, func)
|
|
36
|
-
@fb = fb
|
|
37
|
-
@func = func
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def query(expr)
|
|
41
|
-
expr = "(and #{expr} (not (eq seen '#{@func}')))"
|
|
42
|
-
After.new(@fb.query(expr), @func)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def insert
|
|
46
|
-
@fb.insert
|
|
24
|
+
require 'factbase/tuples'
|
|
25
|
+
|
|
26
|
+
# Returns a decorated global factbase, which only touches facts once.
|
|
27
|
+
def each_once(fb, query, judge: $judge)
|
|
28
|
+
return to_enum(__method__, fb, query, judge:) unless block_given?
|
|
29
|
+
q = "(and #{query} (not (eq seen '#{judge}')))"
|
|
30
|
+
fb.query(q).each do |f|
|
|
31
|
+
yield f
|
|
32
|
+
f.seen = judge
|
|
47
33
|
end
|
|
34
|
+
end
|
|
48
35
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
36
|
+
# Returns a decorated global factbase, which only touches a tuple once.
|
|
37
|
+
def each_tuple_once(fb, *queries, judge: $judge)
|
|
38
|
+
return to_enum(__method__, fb, *queries, judge:) unless block_given?
|
|
39
|
+
qq = queries.map { |q| "(and #{q} (not (eq seen '#{judge}')))" }
|
|
40
|
+
Factbase::Tuples.new(fb, qq).each do |fs|
|
|
41
|
+
yield fs
|
|
42
|
+
fs.each do |f|
|
|
43
|
+
f.seen = judge
|
|
54
44
|
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
48
|
+
def each_tuple_once_txn(fb, *queries, judge: $judge)
|
|
49
|
+
fb.txn do |fbt|
|
|
50
|
+
each_tuple_once(fb, *queries, judge:) do |fs|
|
|
51
|
+
yield [fbt] + fs
|
|
61
52
|
end
|
|
62
53
|
end
|
|
63
54
|
end
|
data/lib/judges/options.rb
CHANGED
|
@@ -28,11 +28,21 @@ require_relative '../judges'
|
|
|
28
28
|
# License:: MIT
|
|
29
29
|
class Judges::Options
|
|
30
30
|
# Ctor.
|
|
31
|
-
# @param
|
|
31
|
+
# @param [Array<String>] pairs List of pairs, like ["token=af73cd3", "max_speed=1"]
|
|
32
32
|
def initialize(pairs = nil)
|
|
33
33
|
@pairs = pairs
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
def +(other)
|
|
37
|
+
touch # this will trigger method_missing() method, which will create @hash
|
|
38
|
+
h = @hash.dup
|
|
39
|
+
other.touch # this will trigger method_missing() method, which will create @hash
|
|
40
|
+
other.instance_variable_get(:@hash).each do |k, v|
|
|
41
|
+
h[k] = v
|
|
42
|
+
end
|
|
43
|
+
Judges::Options.new(h.map { |k, v| "#{k}=#{v}" })
|
|
44
|
+
end
|
|
45
|
+
|
|
36
46
|
# Convert them all to a string (printable in a log).
|
|
37
47
|
def to_s
|
|
38
48
|
touch # this will trigger method_missing() method, which will create @hash
|
data/lib/judges/pack.rb
CHANGED
data/lib/judges.rb
CHANGED
data/test/commands/test_trim.rb
CHANGED
|
@@ -39,7 +39,7 @@ class TestTrim < Minitest::Test
|
|
|
39
39
|
before.insert.time = Time.now + 1
|
|
40
40
|
before.insert.time = Time.now - (100 * 24 * 60 * 60)
|
|
41
41
|
File.binwrite(file, before.export)
|
|
42
|
-
Judges::Trim.new(Loog::NULL).run({ '
|
|
42
|
+
Judges::Trim.new(Loog::NULL).run({ 'query' => "(lt time #{Time.now.utc.iso8601})" }, [file])
|
|
43
43
|
after = Factbase.new
|
|
44
44
|
after.import(File.binread(file))
|
|
45
45
|
assert_equal(1, after.size)
|
data/test/fb/test_if_absent.rb
CHANGED
|
@@ -60,6 +60,13 @@ class TestIfAbsent < Minitest::Test
|
|
|
60
60
|
assert_equal(42, n.foo)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
def test_injects_and_reads
|
|
64
|
+
if_absent(Factbase.new) do |f|
|
|
65
|
+
f.foo = 42
|
|
66
|
+
assert_equal(42, f.foo)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
63
70
|
def test_complex_ignores
|
|
64
71
|
fb = Factbase.new
|
|
65
72
|
f1 = fb.insert
|
|
@@ -86,7 +93,7 @@ class TestIfAbsent < Minitest::Test
|
|
|
86
93
|
f1.z = t
|
|
87
94
|
f1.bar = 3.14
|
|
88
95
|
n = if_absent(fb) do |f|
|
|
89
|
-
f.foo = "hello, \\\"dude\\\" \\' \\' ( \n\n ) (!"
|
|
96
|
+
f.foo = "hello, \\\"dude\\\" \\' \\' ( \n\n ) (! '"
|
|
90
97
|
f.abc = 42
|
|
91
98
|
f.z = t + 1
|
|
92
99
|
f.bar = 3.15
|
data/test/fb/test_once.rb
CHANGED
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
23
|
require 'minitest/autorun'
|
|
24
|
-
require 'tmpdir'
|
|
25
24
|
require 'factbase'
|
|
26
25
|
require_relative '../../lib/judges'
|
|
27
26
|
require_relative '../../lib/judges/fb/once'
|
|
@@ -32,9 +31,58 @@ require_relative '../../lib/judges/fb/once'
|
|
|
32
31
|
# License:: MIT
|
|
33
32
|
class TestOnce < Minitest::Test
|
|
34
33
|
def test_touch_once
|
|
35
|
-
fb =
|
|
34
|
+
fb = Factbase.new
|
|
36
35
|
fb.insert
|
|
37
|
-
fb
|
|
38
|
-
assert(fb
|
|
36
|
+
assert(!each_once(fb, '(always)', judge: 'something').to_a.empty?)
|
|
37
|
+
assert(each_once(fb, '(always)', judge: 'something').to_a.empty?)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_seen_property
|
|
41
|
+
fb = Factbase.new
|
|
42
|
+
f1 = fb.insert
|
|
43
|
+
f1.foo = 42
|
|
44
|
+
assert_equal(1, each_tuple_once(fb, '(eq foo 42)', judge: 'x').to_a.size)
|
|
45
|
+
assert(each_tuple_once(fb, '(eq foo 42)', judge: 'x').to_a.empty?)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_seen_all_or_nothing
|
|
49
|
+
fb = Factbase.new
|
|
50
|
+
f1 = fb.insert
|
|
51
|
+
f1.a = 1
|
|
52
|
+
assert(each_tuple_once(fb, '(exists a)', '(exists b)', judge: 'x').to_a.empty?)
|
|
53
|
+
f2 = fb.insert
|
|
54
|
+
f2.b = 1
|
|
55
|
+
assert(!each_tuple_once(fb, '(exists a)', '(exists b)', judge: 'x').to_a.empty?)
|
|
56
|
+
assert(each_tuple_once(fb, '(exists a)', '(exists b)', judge: 'x').to_a.empty?)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_with_txn
|
|
60
|
+
fb = Factbase.new
|
|
61
|
+
f1 = fb.insert
|
|
62
|
+
f1.foo = 42
|
|
63
|
+
each_tuple_once(fb, '(exists foo)', judge: 'xx') do |fs|
|
|
64
|
+
fb.txn do |fbt|
|
|
65
|
+
f = fbt.insert
|
|
66
|
+
f.bar = 1
|
|
67
|
+
end
|
|
68
|
+
fs[0].xyz = 'hey'
|
|
69
|
+
end
|
|
70
|
+
assert_equal(1, fb.query('(exists seen)').each.to_a.size)
|
|
71
|
+
assert_equal(1, fb.query('(exists bar)').each.to_a.size)
|
|
72
|
+
assert_equal(1, fb.query('(exists xyz)').each.to_a.size)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_with_chain_txn
|
|
76
|
+
fb = Factbase.new
|
|
77
|
+
f1 = fb.insert
|
|
78
|
+
f1.foo = 42
|
|
79
|
+
each_tuple_once_txn(fb, '(exists foo)', judge: 'xx') do |fbt, ff|
|
|
80
|
+
f = fbt.insert
|
|
81
|
+
f.bar = 1
|
|
82
|
+
ff.xyz = 'hey'
|
|
83
|
+
end
|
|
84
|
+
assert_equal(1, fb.query('(exists seen)').each.to_a.size)
|
|
85
|
+
assert_equal(1, fb.query('(exists bar)').each.to_a.size)
|
|
86
|
+
assert_equal(1, fb.query('(exists xyz)').each.to_a.size)
|
|
39
87
|
end
|
|
40
88
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
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/categories'
|
|
26
|
+
|
|
27
|
+
# Test.
|
|
28
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
29
|
+
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
|
30
|
+
# License:: MIT
|
|
31
|
+
class TestCategories < Minitest::Test
|
|
32
|
+
def test_basic
|
|
33
|
+
cats = Judges::Categories.new(%w[foo bar], ['bad'])
|
|
34
|
+
assert(cats.ok?(%w[foo other]))
|
|
35
|
+
assert(cats.ok?(%w[other more bar]))
|
|
36
|
+
assert(!cats.ok?(%w[bad other]))
|
|
37
|
+
assert(!cats.ok?(['other']))
|
|
38
|
+
assert(!cats.ok?('hey'))
|
|
39
|
+
assert(!cats.ok?(nil))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_all_enabled
|
|
43
|
+
cats = Judges::Categories.new([], ['bad'])
|
|
44
|
+
assert(cats.ok?(nil))
|
|
45
|
+
assert(cats.ok?('hey'))
|
|
46
|
+
assert(cats.ok?(%w[foo other]))
|
|
47
|
+
assert(cats.ok?(%w[other more bar]))
|
|
48
|
+
assert(!cats.ok?(%w[bad other]))
|
|
49
|
+
end
|
|
50
|
+
end
|
data/test/test_options.rb
CHANGED
|
@@ -75,4 +75,12 @@ class TestOptions < Minitest::Test
|
|
|
75
75
|
assert(s.include?('foo → "44"'))
|
|
76
76
|
assert(s.include?('"long********************"'))
|
|
77
77
|
end
|
|
78
|
+
|
|
79
|
+
def test_merge
|
|
80
|
+
left = Judges::Options.new(['a = 1', 'b = 4'])
|
|
81
|
+
right = Judges::Options.new(['a = 44', 'c = 3'])
|
|
82
|
+
opts = left + right
|
|
83
|
+
assert_equal(44, opts.a)
|
|
84
|
+
assert_equal(3, opts.c)
|
|
85
|
+
end
|
|
78
86
|
end
|
data/test/test_pack.rb
CHANGED
|
@@ -57,7 +57,7 @@ class TestPack < Minitest::Test
|
|
|
57
57
|
|
|
58
58
|
def test_with_supplemenary_functions
|
|
59
59
|
Dir.mktmpdir do |d|
|
|
60
|
-
File.write(File.join(d, 'x.rb'), '
|
|
60
|
+
File.write(File.join(d, 'x.rb'), 'each_once($fb, "(always)").to_a')
|
|
61
61
|
pack = Judges::Pack.new(d, nil, Loog::NULL)
|
|
62
62
|
pack.run(Factbase.new, {}, {}, {})
|
|
63
63
|
end
|
|
@@ -97,14 +97,14 @@ class TestPack < Minitest::Test
|
|
|
97
97
|
Dir.mktmpdir do |d|
|
|
98
98
|
dir = File.join(d, 'packs')
|
|
99
99
|
FileUtils.mkdir_p(dir)
|
|
100
|
-
File.write(File.join(dir, 'x.rb'), '
|
|
100
|
+
File.write(File.join(dir, 'x.rb'), '$fb.insert.bar = $foo; each_once($fb, "(always)").to_a')
|
|
101
101
|
lib = File.join(d, 'lib')
|
|
102
102
|
FileUtils.mkdir_p(lib)
|
|
103
103
|
File.write(File.join(lib, 'y.rb'), '$foo = 42')
|
|
104
104
|
pack = Judges::Pack.new(dir, lib, Loog::NULL)
|
|
105
105
|
fb = Factbase.new
|
|
106
106
|
pack.run(fb, {}, {}, {})
|
|
107
|
-
assert_equal(42, fb.query('()').each.to_a.first.bar)
|
|
107
|
+
assert_equal(42, fb.query('(always)').each.to_a.first.bar)
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
110
|
|
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.
|
|
4
|
+
version: 0.0.34
|
|
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-
|
|
11
|
+
date: 2024-05-27 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.38
|
|
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.38
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: gli
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,10 +80,9 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '1.10'
|
|
83
|
-
description:
|
|
84
|
-
modifying it and updating
|
|
85
|
-
|
|
86
|
-
\ "
|
|
83
|
+
description: A command-line tool that runs a collection of \"judges\" against a \"factbase,\"
|
|
84
|
+
modifying it and updating. Also, helps printing a factbase, merge with another one,
|
|
85
|
+
inspect, and so on. Also, helps run automated tests for a set of judges.
|
|
87
86
|
email: yegor256@gmail.com
|
|
88
87
|
executables:
|
|
89
88
|
- judges
|
|
@@ -131,6 +130,7 @@ files:
|
|
|
131
130
|
- fixtures/try/try.rb
|
|
132
131
|
- judges.gemspec
|
|
133
132
|
- lib/judges.rb
|
|
133
|
+
- lib/judges/categories.rb
|
|
134
134
|
- lib/judges/commands/eval.rb
|
|
135
135
|
- lib/judges/commands/import.rb
|
|
136
136
|
- lib/judges/commands/inspect.rb
|
|
@@ -159,6 +159,7 @@ files:
|
|
|
159
159
|
- test/fb/test_if_absent.rb
|
|
160
160
|
- test/fb/test_once.rb
|
|
161
161
|
- test/test__helper.rb
|
|
162
|
+
- test/test_categories.rb
|
|
162
163
|
- test/test_impex.rb
|
|
163
164
|
- test/test_judges.rb
|
|
164
165
|
- test/test_options.rb
|