dentaku 1.2.6 → 2.0.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 +4 -4
- data/README.md +52 -57
- data/Rakefile +1 -1
- data/lib/dentaku.rb +8 -0
- data/lib/dentaku/ast.rb +22 -0
- data/lib/dentaku/ast/addition.rb +15 -0
- data/lib/dentaku/ast/combinators.rb +15 -0
- data/lib/dentaku/ast/comparators.rb +47 -0
- data/lib/dentaku/ast/division.rb +15 -0
- data/lib/dentaku/ast/exponentiation.rb +15 -0
- data/lib/dentaku/ast/function.rb +54 -0
- data/lib/dentaku/ast/functions/if.rb +26 -0
- data/lib/dentaku/ast/functions/max.rb +5 -0
- data/lib/dentaku/ast/functions/min.rb +5 -0
- data/lib/dentaku/ast/functions/not.rb +5 -0
- data/lib/dentaku/ast/functions/round.rb +5 -0
- data/lib/dentaku/ast/functions/rounddown.rb +5 -0
- data/lib/dentaku/ast/functions/roundup.rb +5 -0
- data/lib/dentaku/ast/functions/ruby_math.rb +8 -0
- data/lib/dentaku/ast/grouping.rb +13 -0
- data/lib/dentaku/ast/identifier.rb +29 -0
- data/lib/dentaku/ast/multiplication.rb +15 -0
- data/lib/dentaku/ast/negation.rb +25 -0
- data/lib/dentaku/ast/nil.rb +9 -0
- data/lib/dentaku/ast/node.rb +13 -0
- data/lib/dentaku/ast/numeric.rb +17 -0
- data/lib/dentaku/ast/operation.rb +20 -0
- data/lib/dentaku/ast/string.rb +17 -0
- data/lib/dentaku/ast/subtraction.rb +15 -0
- data/lib/dentaku/bulk_expression_solver.rb +6 -11
- data/lib/dentaku/calculator.rb +26 -20
- data/lib/dentaku/parser.rb +131 -0
- data/lib/dentaku/token.rb +4 -0
- data/lib/dentaku/token_matchers.rb +29 -0
- data/lib/dentaku/token_scanner.rb +18 -3
- data/lib/dentaku/tokenizer.rb +10 -2
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/function_spec.rb +19 -0
- data/spec/ast/node_spec.rb +37 -0
- data/spec/bulk_expression_solver_spec.rb +12 -5
- data/spec/calculator_spec.rb +14 -1
- data/spec/external_function_spec.rb +12 -28
- data/spec/parser_spec.rb +88 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/token_scanner_spec.rb +4 -3
- data/spec/tokenizer_spec.rb +32 -6
- metadata +36 -16
- data/lib/dentaku/binary_operation.rb +0 -35
- data/lib/dentaku/evaluator.rb +0 -166
- data/lib/dentaku/expression.rb +0 -56
- data/lib/dentaku/external_function.rb +0 -10
- data/lib/dentaku/rule_set.rb +0 -153
- data/spec/binary_operation_spec.rb +0 -45
- data/spec/evaluator_spec.rb +0 -145
- data/spec/expression_spec.rb +0 -25
- data/spec/rule_set_spec.rb +0 -43
data/lib/dentaku/token.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Dentaku
|
2
|
+
module TokenMatchers
|
3
|
+
def self.token_matchers(*symbols)
|
4
|
+
symbols.map { |s| matcher(s) }
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.function_token_matchers(function_name, *symbols)
|
8
|
+
token_matchers(:fopen, *symbols, :close).unshift(
|
9
|
+
TokenMatcher.send(function_name)
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.matcher(symbol)
|
14
|
+
@matchers ||= [
|
15
|
+
:numeric, :string, :addsub, :subtract, :muldiv, :pow, :mod,
|
16
|
+
:comparator, :comp_gt, :comp_lt, :fopen, :open, :close, :comma,
|
17
|
+
:non_close_plus, :non_group, :non_group_star, :arguments,
|
18
|
+
:logical, :combinator, :if, :round, :roundup, :rounddown, :not,
|
19
|
+
:anchored_minus, :math_neg_pow, :math_neg_mul
|
20
|
+
].each_with_object({}) do |name, matchers|
|
21
|
+
matchers[name] = TokenMatcher.send(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
@matchers.fetch(symbol) do
|
25
|
+
raise "Unknown token symbol #{ symbol }"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,15 +1,17 @@
|
|
1
|
+
require 'bigdecimal'
|
1
2
|
require 'dentaku/token'
|
2
3
|
|
3
4
|
module Dentaku
|
4
5
|
class TokenScanner
|
5
|
-
def initialize(category, regexp, converter=nil)
|
6
|
+
def initialize(category, regexp, converter=nil, condition=nil)
|
6
7
|
@category = category
|
7
8
|
@regexp = %r{\A(#{ regexp })}i
|
8
9
|
@converter = converter
|
10
|
+
@condition = condition || ->(*) { true }
|
9
11
|
end
|
10
12
|
|
11
|
-
def scan(string)
|
12
|
-
if m = @regexp.match(string)
|
13
|
+
def scan(string, last_token=nil)
|
14
|
+
if (m = @regexp.match(string)) && @condition.call(last_token)
|
13
15
|
value = raw = m.to_s
|
14
16
|
value = @converter.call(raw) if @converter
|
15
17
|
|
@@ -28,6 +30,7 @@ module Dentaku
|
|
28
30
|
numeric,
|
29
31
|
double_quoted_string,
|
30
32
|
single_quoted_string,
|
33
|
+
negate,
|
31
34
|
operator,
|
32
35
|
grouping,
|
33
36
|
comparator,
|
@@ -53,6 +56,18 @@ module Dentaku
|
|
53
56
|
new(:string, "'[^']*'", lambda { |raw| raw.gsub(/^'|'$/, '') })
|
54
57
|
end
|
55
58
|
|
59
|
+
def negate
|
60
|
+
new(:operator, '-', lambda { |raw| :negate }, lambda { |last_token|
|
61
|
+
last_token.nil? ||
|
62
|
+
last_token.is?(:operator) ||
|
63
|
+
last_token.is?(:comparator) ||
|
64
|
+
last_token.is?(:combinator) ||
|
65
|
+
last_token.value == :open ||
|
66
|
+
last_token.value == :fopen ||
|
67
|
+
last_token.value == :comma
|
68
|
+
})
|
69
|
+
end
|
70
|
+
|
56
71
|
def operator
|
57
72
|
names = { pow: '^', add: '+', subtract: '-', multiply: '*', divide: '/', mod: '%' }.invert
|
58
73
|
new(:operator, '\^|\+|-|\*|\/|%', lambda { |raw| names[raw] })
|
data/lib/dentaku/tokenizer.rb
CHANGED
@@ -10,7 +10,7 @@ module Dentaku
|
|
10
10
|
def tokenize(string)
|
11
11
|
@nesting = 0
|
12
12
|
@tokens = []
|
13
|
-
input = string.to_s.dup
|
13
|
+
input = strip_comments(string.to_s.dup)
|
14
14
|
|
15
15
|
until input.empty?
|
16
16
|
raise "parse error at: '#{ input }'" unless TokenScanner.scanners.any? do |scanner|
|
@@ -24,8 +24,12 @@ module Dentaku
|
|
24
24
|
@tokens
|
25
25
|
end
|
26
26
|
|
27
|
+
def last_token
|
28
|
+
@tokens.last
|
29
|
+
end
|
30
|
+
|
27
31
|
def scan(string, scanner)
|
28
|
-
if tokens = scanner.scan(string)
|
32
|
+
if tokens = scanner.scan(string, last_token)
|
29
33
|
tokens.each do |token|
|
30
34
|
raise "unexpected zero-width match (:#{ token.category }) at '#{ string }'" if token.length == 0
|
31
35
|
|
@@ -42,5 +46,9 @@ module Dentaku
|
|
42
46
|
[false, string]
|
43
47
|
end
|
44
48
|
end
|
49
|
+
|
50
|
+
def strip_comments(input)
|
51
|
+
input.gsub(/\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//, '')
|
52
|
+
end
|
45
53
|
end
|
46
54
|
end
|
data/lib/dentaku/version.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/function'
|
3
|
+
|
4
|
+
describe Dentaku::AST::Function do
|
5
|
+
it 'maintains a function registry' do
|
6
|
+
expect(described_class).to respond_to(:get)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'raises an exception when trying to access an undefined function' do
|
10
|
+
expect { described_class.get("flarble") }.to raise_error
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'registers a custom function' do
|
14
|
+
described_class.register("flarble", -> { "flarble" })
|
15
|
+
expect { described_class.get("flarble") }.not_to raise_error
|
16
|
+
function = described_class.get("flarble").new
|
17
|
+
expect(function.value).to eq "flarble"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/node'
|
3
|
+
require 'dentaku/tokenizer'
|
4
|
+
require 'dentaku/parser'
|
5
|
+
|
6
|
+
describe Dentaku::AST::Node do
|
7
|
+
it 'returns list of dependencies' do
|
8
|
+
node = make_node('x + 5')
|
9
|
+
expect(node.dependencies).to eq ['x']
|
10
|
+
|
11
|
+
node = make_node('5 < x')
|
12
|
+
expect(node.dependencies).to eq ['x']
|
13
|
+
|
14
|
+
node = make_node('5 < 7')
|
15
|
+
expect(node.dependencies).to eq []
|
16
|
+
|
17
|
+
node = make_node('(y * 7)')
|
18
|
+
expect(node.dependencies).to eq ['y']
|
19
|
+
|
20
|
+
node = make_node('if(x > 5, y, z)')
|
21
|
+
expect(node.dependencies).to eq ['x', 'y', 'z']
|
22
|
+
|
23
|
+
node = make_node('if(x > 5, y, z)')
|
24
|
+
expect(node.dependencies('x' => 7)).to eq ['y', 'z']
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns unique list of dependencies' do
|
28
|
+
node = make_node('x + x')
|
29
|
+
expect(node.dependencies).to eq ['x']
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def make_node(expression)
|
35
|
+
Dentaku::Parser.new(Dentaku::Tokenizer.new.tokenize(expression)).parse
|
36
|
+
end
|
37
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'dentaku/bulk_expression_solver'
|
2
3
|
|
3
4
|
RSpec.describe Dentaku::BulkExpressionSolver do
|
@@ -17,34 +18,40 @@ RSpec.describe Dentaku::BulkExpressionSolver do
|
|
17
18
|
it "lets you know if a variable is unbound" do
|
18
19
|
expressions = {more_apples: "apples + 1"}
|
19
20
|
expect {
|
20
|
-
described_class.new(expressions, {}).solve!
|
21
|
+
described_class.new(expressions, {}).solve!
|
21
22
|
}.to raise_error(Dentaku::UnboundVariableError)
|
22
23
|
end
|
23
24
|
|
24
25
|
it "lets you know if the result is a div/0 error" do
|
25
26
|
expressions = {more_apples: "1/0"}
|
26
27
|
expect {
|
27
|
-
described_class.new(expressions, {}).solve!
|
28
|
+
described_class.new(expressions, {}).solve!
|
28
29
|
}.to raise_error(ZeroDivisionError)
|
29
30
|
end
|
31
|
+
|
32
|
+
it "does not require keys to be parseable" do
|
33
|
+
expressions = { "the value of x, incremented" => "x + 1" }
|
34
|
+
solver = described_class.new(expressions, "x" => 3)
|
35
|
+
expect(solver.solve!).to eq({ "the value of x, incremented" => 4 })
|
36
|
+
end
|
30
37
|
end
|
31
38
|
|
32
39
|
describe "#solve" do
|
33
40
|
it "returns :undefined when variables are unbound" do
|
34
41
|
expressions = {more_apples: "apples + 1"}
|
35
|
-
expect(described_class.new(expressions, {}).solve
|
42
|
+
expect(described_class.new(expressions, {}).solve)
|
36
43
|
.to eq(more_apples: :undefined)
|
37
44
|
end
|
38
45
|
|
39
46
|
it "allows passing in a custom value to an error handler when a variable is unbound" do
|
40
47
|
expressions = {more_apples: "apples + 1"}
|
41
|
-
expect(described_class.new(expressions, {}).solve
|
48
|
+
expect(described_class.new(expressions, {}).solve { :foo })
|
42
49
|
.to eq(more_apples: :foo)
|
43
50
|
end
|
44
51
|
|
45
52
|
it "allows passing in a custom value to an error handler when there is a div/0 error" do
|
46
53
|
expressions = {more_apples: "1/0"}
|
47
|
-
expect(described_class.new(expressions, {}).solve
|
54
|
+
expect(described_class.new(expressions, {}).solve { :foo })
|
48
55
|
.to eq(more_apples: :foo)
|
49
56
|
end
|
50
57
|
end
|
data/spec/calculator_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'dentaku/calculator'
|
2
3
|
|
3
4
|
describe Dentaku::Calculator do
|
@@ -19,7 +20,7 @@ describe Dentaku::Calculator do
|
|
19
20
|
expect(calculator.evaluate('(2 + 3) - 1')).to eq(4)
|
20
21
|
expect(calculator.evaluate('(-2 + 3) - 1')).to eq(0)
|
21
22
|
expect(calculator.evaluate('(-2 - 3) - 1')).to eq(-6)
|
22
|
-
expect(calculator.evaluate('1 + -2 ^ 2')).to eq(-3)
|
23
|
+
expect(calculator.evaluate('1 + -(2 ^ 2)')).to eq(-3)
|
23
24
|
expect(calculator.evaluate('3 + -num', num: 2)).to eq(1)
|
24
25
|
expect(calculator.evaluate('-num + 3', num: 2)).to eq(1)
|
25
26
|
expect(calculator.evaluate('10 ^ 2')).to eq(100)
|
@@ -198,4 +199,16 @@ describe Dentaku::Calculator do
|
|
198
199
|
expect(calculator.evaluate('NOT(some_boolean) AND -1 > 3', some_boolean: true)).to be_falsey
|
199
200
|
end
|
200
201
|
end
|
202
|
+
|
203
|
+
describe 'math functions' do
|
204
|
+
Math.methods(false).each do |method|
|
205
|
+
it method do
|
206
|
+
if Math.method(method).arity == 2
|
207
|
+
expect(calculator.evaluate("#{method}(1,2)")).to eq Math.send(method, 1, 2)
|
208
|
+
else
|
209
|
+
expect(calculator.evaluate("#{method}(1)")).to eq Math.send(method, 1)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
201
214
|
end
|
@@ -8,28 +8,12 @@ describe Dentaku::Calculator do
|
|
8
8
|
let(:with_external_funcs) do
|
9
9
|
c = described_class.new
|
10
10
|
|
11
|
-
|
12
|
-
c.add_function(now)
|
11
|
+
c.add_function(:now, -> { Time.now.to_s })
|
13
12
|
|
14
13
|
fns = [
|
15
|
-
{
|
16
|
-
|
17
|
-
|
18
|
-
signature: [ :numeric, :numeric ],
|
19
|
-
body: ->(mantissa, exponent) { mantissa ** exponent }
|
20
|
-
},
|
21
|
-
{
|
22
|
-
name: :max,
|
23
|
-
type: :numeric,
|
24
|
-
signature: [ :arguments ],
|
25
|
-
body: ->(*args) { args.max }
|
26
|
-
},
|
27
|
-
{
|
28
|
-
name: :min,
|
29
|
-
type: :numeric,
|
30
|
-
signature: [ :arguments ],
|
31
|
-
body: ->(*args) { args.min }
|
32
|
-
}
|
14
|
+
[:pow, ->(mantissa, exponent) { mantissa ** exponent }],
|
15
|
+
[:biggest, ->(*args) { args.max }],
|
16
|
+
[:smallest, ->(*args) { args.min }],
|
33
17
|
]
|
34
18
|
|
35
19
|
c.add_functions(fns)
|
@@ -41,18 +25,18 @@ describe Dentaku::Calculator do
|
|
41
25
|
expect(now).not_to be_empty
|
42
26
|
end
|
43
27
|
|
44
|
-
it 'includes
|
45
|
-
expect(with_external_funcs.evaluate('
|
46
|
-
expect(with_external_funcs.evaluate('
|
47
|
-
expect(with_external_funcs.evaluate('
|
28
|
+
it 'includes POW' do
|
29
|
+
expect(with_external_funcs.evaluate('POW(2,3)')).to eq(8)
|
30
|
+
expect(with_external_funcs.evaluate('POW(3,2)')).to eq(9)
|
31
|
+
expect(with_external_funcs.evaluate('POW(mantissa,exponent)', mantissa: 2, exponent: 4)).to eq(16)
|
48
32
|
end
|
49
33
|
|
50
|
-
it 'includes
|
51
|
-
expect(with_external_funcs.evaluate('
|
34
|
+
it 'includes BIGGEST' do
|
35
|
+
expect(with_external_funcs.evaluate('BIGGEST(8,6,7,5,3,0,9)')).to eq(9)
|
52
36
|
end
|
53
37
|
|
54
|
-
it 'includes
|
55
|
-
expect(with_external_funcs.evaluate('
|
38
|
+
it 'includes SMALLEST' do
|
39
|
+
expect(with_external_funcs.evaluate('SMALLEST(8,6,7,5,3,0,9)')).to eq(0)
|
56
40
|
end
|
57
41
|
end
|
58
42
|
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/parser'
|
3
|
+
|
4
|
+
describe Dentaku::Parser do
|
5
|
+
it 'is constructed from a token' do
|
6
|
+
token = Dentaku::Token.new(:numeric, 5)
|
7
|
+
node = described_class.new([token]).parse
|
8
|
+
expect(node.value).to eq 5
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'performs simple addition' do
|
12
|
+
five = Dentaku::Token.new(:numeric, 5)
|
13
|
+
plus = Dentaku::Token.new(:operator, :add)
|
14
|
+
four = Dentaku::Token.new(:numeric, 4)
|
15
|
+
|
16
|
+
node = described_class.new([five, plus, four]).parse
|
17
|
+
expect(node.value).to eq 9
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'compares two numbers' do
|
21
|
+
five = Dentaku::Token.new(:numeric, 5)
|
22
|
+
lt = Dentaku::Token.new(:comparator, :lt)
|
23
|
+
four = Dentaku::Token.new(:numeric, 4)
|
24
|
+
|
25
|
+
node = described_class.new([five, lt, four]).parse
|
26
|
+
expect(node.value).to eq false
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'performs multiple operations in one stream' do
|
30
|
+
five = Dentaku::Token.new(:numeric, 5)
|
31
|
+
plus = Dentaku::Token.new(:operator, :add)
|
32
|
+
four = Dentaku::Token.new(:numeric, 4)
|
33
|
+
times = Dentaku::Token.new(:operator, :multiply)
|
34
|
+
three = Dentaku::Token.new(:numeric, 3)
|
35
|
+
|
36
|
+
node = described_class.new([five, plus, four, times, three]).parse
|
37
|
+
expect(node.value).to eq 17
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'respects order of operations' do
|
41
|
+
five = Dentaku::Token.new(:numeric, 5)
|
42
|
+
times = Dentaku::Token.new(:operator, :multiply)
|
43
|
+
four = Dentaku::Token.new(:numeric, 4)
|
44
|
+
plus = Dentaku::Token.new(:operator, :add)
|
45
|
+
three = Dentaku::Token.new(:numeric, 3)
|
46
|
+
|
47
|
+
node = described_class.new([five, times, four, plus, three]).parse
|
48
|
+
expect(node.value).to eq 23
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'respects grouping by parenthesis' do
|
52
|
+
lpar = Dentaku::Token.new(:grouping, :open)
|
53
|
+
five = Dentaku::Token.new(:numeric, 5)
|
54
|
+
plus = Dentaku::Token.new(:operator, :add)
|
55
|
+
four = Dentaku::Token.new(:numeric, 4)
|
56
|
+
rpar = Dentaku::Token.new(:grouping, :close)
|
57
|
+
times = Dentaku::Token.new(:operator, :multiply)
|
58
|
+
three = Dentaku::Token.new(:numeric, 3)
|
59
|
+
|
60
|
+
node = described_class.new([lpar, five, plus, four, rpar, times, three]).parse
|
61
|
+
expect(node.value).to eq 27
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'evaluates functions' do
|
65
|
+
fn = Dentaku::Token.new(:function, :if)
|
66
|
+
fopen = Dentaku::Token.new(:grouping, :fopen)
|
67
|
+
five = Dentaku::Token.new(:numeric, 5)
|
68
|
+
lt = Dentaku::Token.new(:comparator, :lt)
|
69
|
+
four = Dentaku::Token.new(:numeric, 4)
|
70
|
+
comma = Dentaku::Token.new(:grouping, :comma)
|
71
|
+
three = Dentaku::Token.new(:numeric, 3)
|
72
|
+
two = Dentaku::Token.new(:numeric, 2)
|
73
|
+
rpar = Dentaku::Token.new(:grouping, :close)
|
74
|
+
|
75
|
+
node = described_class.new([fn, fopen, five, lt, four, comma, three, comma, two, rpar]).parse
|
76
|
+
expect(node.value).to eq 2
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'represents formulas with variables' do
|
80
|
+
five = Dentaku::Token.new(:numeric, 5)
|
81
|
+
times = Dentaku::Token.new(:operator, :multiply)
|
82
|
+
x = Dentaku::Token.new(:identifier, :x)
|
83
|
+
|
84
|
+
node = described_class.new([five, times, x]).parse
|
85
|
+
expect { node.value }.to raise_error
|
86
|
+
expect(node.value(x: 3)).to eq 15
|
87
|
+
end
|
88
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/token_scanner_spec.rb
CHANGED
@@ -2,7 +2,9 @@ require 'dentaku/token_scanner'
|
|
2
2
|
|
3
3
|
describe Dentaku::TokenScanner do
|
4
4
|
let(:whitespace) { described_class.new(:whitespace, '\s') }
|
5
|
-
let(:numeric) { described_class.new(:numeric, '(\d+(\.\d+)?|\.\d+)',
|
5
|
+
let(:numeric) { described_class.new(:numeric, '(\d+(\.\d+)?|\.\d+)',
|
6
|
+
lambda{|raw| raw =~ /\./ ? BigDecimal.new(raw) : raw.to_i })
|
7
|
+
}
|
6
8
|
|
7
9
|
it 'returns a token for a matching string' do
|
8
10
|
token = whitespace.scan(' ').first
|
@@ -21,7 +23,6 @@ describe Dentaku::TokenScanner do
|
|
21
23
|
end
|
22
24
|
|
23
25
|
it 'returns a list of all configured scanners' do
|
24
|
-
expect(described_class.scanners.length).to eq
|
26
|
+
expect(described_class.scanners.length).to eq 11
|
25
27
|
end
|
26
28
|
end
|
27
|
-
|