dentaku 3.3.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -10
- data/.travis.yml +3 -6
- data/CHANGELOG.md +38 -1
- data/README.md +2 -2
- data/dentaku.gemspec +0 -2
- data/lib/dentaku.rb +14 -6
- data/lib/dentaku/ast.rb +5 -0
- data/lib/dentaku/ast/access.rb +15 -1
- data/lib/dentaku/ast/arithmetic.rb +29 -6
- data/lib/dentaku/ast/array.rb +15 -1
- data/lib/dentaku/ast/case.rb +13 -3
- data/lib/dentaku/ast/case/case_conditional.rb +13 -2
- data/lib/dentaku/ast/case/case_else.rb +12 -4
- data/lib/dentaku/ast/case/case_switch_variable.rb +8 -0
- data/lib/dentaku/ast/case/case_then.rb +12 -4
- data/lib/dentaku/ast/case/case_when.rb +12 -4
- data/lib/dentaku/ast/function.rb +11 -2
- data/lib/dentaku/ast/function_registry.rb +21 -0
- data/lib/dentaku/ast/functions/all.rb +36 -0
- data/lib/dentaku/ast/functions/any.rb +36 -0
- data/lib/dentaku/ast/functions/avg.rb +2 -2
- data/lib/dentaku/ast/functions/count.rb +8 -0
- data/lib/dentaku/ast/functions/duration.rb +51 -0
- data/lib/dentaku/ast/functions/if.rb +15 -2
- data/lib/dentaku/ast/functions/map.rb +36 -0
- data/lib/dentaku/ast/functions/mul.rb +3 -2
- data/lib/dentaku/ast/functions/pluck.rb +29 -0
- data/lib/dentaku/ast/functions/round.rb +1 -1
- data/lib/dentaku/ast/functions/rounddown.rb +1 -1
- data/lib/dentaku/ast/functions/roundup.rb +1 -1
- data/lib/dentaku/ast/functions/ruby_math.rb +47 -3
- data/lib/dentaku/ast/functions/string_functions.rb +68 -4
- data/lib/dentaku/ast/functions/sum.rb +3 -2
- data/lib/dentaku/ast/grouping.rb +3 -1
- data/lib/dentaku/ast/identifier.rb +5 -1
- data/lib/dentaku/ast/negation.rb +3 -1
- data/lib/dentaku/ast/node.rb +4 -0
- data/lib/dentaku/ast/operation.rb +8 -0
- data/lib/dentaku/bulk_expression_solver.rb +34 -25
- data/lib/dentaku/calculator.rb +19 -6
- data/lib/dentaku/date_arithmetic.rb +45 -0
- data/lib/dentaku/exceptions.rb +4 -4
- data/lib/dentaku/flat_hash.rb +9 -2
- data/lib/dentaku/parser.rb +31 -14
- data/lib/dentaku/token_matcher.rb +1 -1
- data/lib/dentaku/token_scanner.rb +1 -1
- data/lib/dentaku/tokenizer.rb +7 -2
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/addition_spec.rb +7 -1
- data/spec/ast/and_function_spec.rb +6 -6
- data/spec/ast/and_spec.rb +1 -1
- data/spec/ast/arithmetic_spec.rb +57 -29
- data/spec/ast/avg_spec.rb +9 -5
- data/spec/ast/count_spec.rb +7 -7
- data/spec/ast/division_spec.rb +7 -1
- data/spec/ast/function_spec.rb +9 -9
- data/spec/ast/max_spec.rb +3 -3
- data/spec/ast/min_spec.rb +3 -3
- data/spec/ast/mul_spec.rb +10 -6
- data/spec/ast/negation_spec.rb +48 -0
- data/spec/ast/node_spec.rb +11 -8
- data/spec/ast/numeric_spec.rb +1 -1
- data/spec/ast/or_spec.rb +6 -6
- data/spec/ast/round_spec.rb +14 -4
- data/spec/ast/rounddown_spec.rb +14 -4
- data/spec/ast/roundup_spec.rb +14 -4
- data/spec/ast/string_functions_spec.rb +35 -0
- data/spec/ast/sum_spec.rb +10 -6
- data/spec/ast/switch_spec.rb +5 -5
- data/spec/bulk_expression_solver_spec.rb +18 -1
- data/spec/calculator_spec.rb +173 -28
- data/spec/dentaku_spec.rb +18 -5
- data/spec/external_function_spec.rb +29 -5
- data/spec/parser_spec.rb +85 -123
- data/spec/spec_helper.rb +6 -4
- data/spec/token_matcher_spec.rb +8 -8
- data/spec/token_scanner_spec.rb +4 -4
- data/spec/tokenizer_spec.rb +32 -13
- metadata +11 -4
@@ -0,0 +1,45 @@
|
|
1
|
+
module Dentaku
|
2
|
+
class DateArithmetic
|
3
|
+
def initialize(date)
|
4
|
+
@base = date
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(duration)
|
8
|
+
case duration
|
9
|
+
when Numeric
|
10
|
+
@base + duration
|
11
|
+
when Dentaku::AST::Duration::Value
|
12
|
+
case duration.unit
|
13
|
+
when :year
|
14
|
+
Time.local(@base.year + duration.value, @base.month, @base.day).to_datetime
|
15
|
+
when :month
|
16
|
+
@base >> duration.value
|
17
|
+
when :day
|
18
|
+
@base + duration.value
|
19
|
+
end
|
20
|
+
else
|
21
|
+
raise Dentaku::ArgumentError.for(:incompatible_type, value: duration, for: Numeric),
|
22
|
+
"'#{duration || duration.class}' is not coercible for date arithmetic"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def sub(duration)
|
27
|
+
case duration
|
28
|
+
when DateTime, Numeric
|
29
|
+
@base - duration
|
30
|
+
when Dentaku::AST::Duration::Value
|
31
|
+
case duration.unit
|
32
|
+
when :year
|
33
|
+
Time.local(@base.year - duration.value, @base.month, @base.day).to_datetime
|
34
|
+
when :month
|
35
|
+
@base << duration.value
|
36
|
+
when :day
|
37
|
+
@base - duration.value
|
38
|
+
end
|
39
|
+
else
|
40
|
+
raise Dentaku::ArgumentError.for(:incompatible_type, value: duration, for: Numeric),
|
41
|
+
"'#{duration || duration.class}' is not coercible for date arithmetic"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/dentaku/exceptions.rb
CHANGED
@@ -33,7 +33,7 @@ module Dentaku
|
|
33
33
|
private_class_method :new
|
34
34
|
|
35
35
|
VALID_REASONS = %i[
|
36
|
-
node_invalid too_few_operands undefined_function
|
36
|
+
node_invalid too_few_operands too_many_operands undefined_function
|
37
37
|
unprocessed_token unknown_case_token unbalanced_bracket
|
38
38
|
unbalanced_parenthesis unknown_grouping_token not_implemented_token_category
|
39
39
|
invalid_statement
|
@@ -44,7 +44,7 @@ module Dentaku
|
|
44
44
|
raise ::ArgumentError, "Unhandled #{reason}"
|
45
45
|
end
|
46
46
|
|
47
|
-
new
|
47
|
+
new(reason, **meta)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -68,7 +68,7 @@ module Dentaku
|
|
68
68
|
raise ::ArgumentError, "Unhandled #{reason}"
|
69
69
|
end
|
70
70
|
|
71
|
-
new
|
71
|
+
new(reason, **meta)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
@@ -92,7 +92,7 @@ module Dentaku
|
|
92
92
|
raise ::ArgumentError, "Unhandled #{reason}"
|
93
93
|
end
|
94
94
|
|
95
|
-
new
|
95
|
+
new(reason, **meta)
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
data/lib/dentaku/flat_hash.rb
CHANGED
@@ -6,6 +6,13 @@ module Dentaku
|
|
6
6
|
flatten_keys(acc)
|
7
7
|
end
|
8
8
|
|
9
|
+
def self.from_hash_with_intermediates(h, key = [], acc = {})
|
10
|
+
acc.update(key => h) unless key.empty?
|
11
|
+
return unless h.is_a? Hash
|
12
|
+
h.each { |k, v| from_hash_with_intermediates(v, key + [k], acc) }
|
13
|
+
flatten_keys(acc)
|
14
|
+
end
|
15
|
+
|
9
16
|
def self.flatten_keys(hash)
|
10
17
|
hash.each_with_object({}) do |(k, v), h|
|
11
18
|
h[flatten_key(k)] = v
|
@@ -19,8 +26,8 @@ module Dentaku
|
|
19
26
|
key
|
20
27
|
end
|
21
28
|
|
22
|
-
def self.expand(
|
23
|
-
|
29
|
+
def self.expand(hash)
|
30
|
+
hash.each_with_object({}) do |(k, v), r|
|
24
31
|
hash_levels = k.to_s.split('.')
|
25
32
|
hash_levels = hash_levels.map(&:to_sym) if k.is_a?(Symbol)
|
26
33
|
child_hash = hash_levels[0...-1].reduce(r) { |h, n| h[n] ||= {} }
|
data/lib/dentaku/parser.rb
CHANGED
@@ -40,12 +40,23 @@ module Dentaku
|
|
40
40
|
operator.peek(output)
|
41
41
|
|
42
42
|
args_size = operator.arity || count
|
43
|
-
|
44
|
-
|
43
|
+
min_size = operator.arity || operator.min_param_count || count
|
44
|
+
max_size = operator.arity || operator.max_param_count || count
|
45
|
+
|
46
|
+
if output.length < min_size
|
47
|
+
fail! :too_few_operands, operator: operator, expect: min_size, actual: output.length
|
48
|
+
end
|
49
|
+
|
50
|
+
if output.length > max_size && operations.empty?
|
51
|
+
fail! :too_many_operands, operator: operator, expect: max_size, actual: output.length
|
45
52
|
end
|
53
|
+
|
54
|
+
fail! :invalid_statement if output.size < args_size
|
46
55
|
args = Array.new(args_size) { output.pop }.reverse
|
47
56
|
|
48
57
|
output.push operator.new(*args)
|
58
|
+
rescue ::ArgumentError => e
|
59
|
+
raise Dentaku::ArgumentError, e.message
|
49
60
|
rescue NodeError => e
|
50
61
|
fail! :node_invalid, operator: operator, child: e.child, expect: e.expect, actual: e.actual
|
51
62
|
end
|
@@ -111,17 +122,19 @@ module Dentaku
|
|
111
122
|
open_cases = 0
|
112
123
|
case_end_index = nil
|
113
124
|
|
114
|
-
input.each_with_index do |
|
115
|
-
if
|
116
|
-
|
117
|
-
|
125
|
+
input.each_with_index do |input_token, index|
|
126
|
+
if input_token.category == :case
|
127
|
+
if input_token.value == :open
|
128
|
+
open_cases += 1
|
129
|
+
end
|
118
130
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
131
|
+
if input_token.value == :close
|
132
|
+
if open_cases > 0
|
133
|
+
open_cases -= 1
|
134
|
+
else
|
135
|
+
case_end_index = index
|
136
|
+
break
|
137
|
+
end
|
125
138
|
end
|
126
139
|
end
|
127
140
|
end
|
@@ -129,7 +142,8 @@ module Dentaku
|
|
129
142
|
subparser = Parser.new(
|
130
143
|
inner_case_inputs,
|
131
144
|
operations: [AST::Case],
|
132
|
-
arities: [0]
|
145
|
+
arities: [0],
|
146
|
+
function_registry: @function_registry
|
133
147
|
)
|
134
148
|
subparser.parse
|
135
149
|
output.concat(subparser.output)
|
@@ -253,6 +267,7 @@ module Dentaku
|
|
253
267
|
end
|
254
268
|
|
255
269
|
when :comma
|
270
|
+
fail! :invalid_statement if arities.empty?
|
256
271
|
arities[-1] += 1
|
257
272
|
while operations.any? && operations.last != AST::Grouping && operations.last != AST::Array
|
258
273
|
consume
|
@@ -299,6 +314,8 @@ module Dentaku
|
|
299
314
|
"#{meta.fetch(:operator)} requires #{meta.fetch(:expect).join(', ')} operands, but got #{meta.fetch(:actual)}"
|
300
315
|
when :too_few_operands
|
301
316
|
"#{meta.fetch(:operator)} has too few operands"
|
317
|
+
when :too_many_operands
|
318
|
+
"#{meta.fetch(:operator)} has too many operands"
|
302
319
|
when :undefined_function
|
303
320
|
"Undefined function #{meta.fetch(:function_name)}"
|
304
321
|
when :unprocessed_token
|
@@ -319,7 +336,7 @@ module Dentaku
|
|
319
336
|
raise ::ArgumentError, "Unhandled #{reason}"
|
320
337
|
end
|
321
338
|
|
322
|
-
raise ParseError.for(reason, meta), message
|
339
|
+
raise ParseError.for(reason, **meta), message
|
323
340
|
end
|
324
341
|
end
|
325
342
|
end
|
data/lib/dentaku/tokenizer.rb
CHANGED
@@ -12,7 +12,7 @@ module Dentaku
|
|
12
12
|
def tokenize(string, options = {})
|
13
13
|
@nesting = 0
|
14
14
|
@tokens = []
|
15
|
-
@aliases = options.fetch(:aliases,
|
15
|
+
@aliases = options.fetch(:aliases, global_aliases)
|
16
16
|
input = strip_comments(string.to_s.dup)
|
17
17
|
input = replace_aliases(input)
|
18
18
|
@case_sensitive = options.fetch(:case_sensitive, false)
|
@@ -84,6 +84,11 @@ module Dentaku
|
|
84
84
|
|
85
85
|
private
|
86
86
|
|
87
|
+
def global_aliases
|
88
|
+
return {} unless Dentaku.respond_to?(:aliases)
|
89
|
+
Dentaku.aliases
|
90
|
+
end
|
91
|
+
|
87
92
|
def fail!(reason, **meta)
|
88
93
|
message =
|
89
94
|
case reason
|
@@ -99,7 +104,7 @@ module Dentaku
|
|
99
104
|
raise ::ArgumentError, "Unhandled #{reason}"
|
100
105
|
end
|
101
106
|
|
102
|
-
raise TokenizerError.for(reason, meta), message
|
107
|
+
raise TokenizerError.for(reason, **meta), message
|
103
108
|
end
|
104
109
|
end
|
105
110
|
end
|
data/lib/dentaku/version.rb
CHANGED
data/spec/ast/addition_spec.rb
CHANGED
@@ -9,9 +9,15 @@ describe Dentaku::AST::Addition do
|
|
9
9
|
|
10
10
|
let(:t) { Dentaku::AST::Numeric.new Dentaku::Token.new(:logical, true) }
|
11
11
|
|
12
|
+
it 'allows access to its sub-trees' do
|
13
|
+
node = described_class.new(five, six)
|
14
|
+
expect(node.left).to eq(five)
|
15
|
+
expect(node.right).to eq(six)
|
16
|
+
end
|
17
|
+
|
12
18
|
it 'performs addition' do
|
13
19
|
node = described_class.new(five, six)
|
14
|
-
expect(node.value).to eq
|
20
|
+
expect(node.value).to eq(11)
|
15
21
|
end
|
16
22
|
|
17
23
|
it 'requires numeric operands' do
|
@@ -7,29 +7,29 @@ describe 'Dentaku::AST::And' do
|
|
7
7
|
|
8
8
|
it 'returns false if any of the arguments is false' do
|
9
9
|
result = Dentaku('AND(1 = 1, 0 = 1)')
|
10
|
-
expect(result).to eq
|
10
|
+
expect(result).to eq(false)
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'supports nested expressions' do
|
14
14
|
result = Dentaku('AND(y = 1, x = 1)', x: 1, y: 2)
|
15
|
-
expect(result).to eq
|
15
|
+
expect(result).to eq(false)
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'returns true if all of the arguments are true' do
|
19
19
|
result = Dentaku('AND(1 = 1, "2" = "2", true = true, true)')
|
20
|
-
expect(result).to eq
|
20
|
+
expect(result).to eq(true)
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'returns true if all nested AND functions return true' do
|
24
24
|
result = Dentaku('AND(AND(1 = 1), AND(true != false, AND(true)))')
|
25
|
-
expect(result).to eq
|
25
|
+
expect(result).to eq(true)
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'raises an error if no arguments are passed' do
|
29
|
-
expect { calculator.evaluate!('AND()') }.to raise_error(ArgumentError)
|
29
|
+
expect { calculator.evaluate!('AND()') }.to raise_error(Dentaku::ArgumentError)
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'raises an error if a non logical argument is passed' do
|
33
|
-
expect { calculator.evaluate!('AND("r")') }.to raise_error(ArgumentError)
|
33
|
+
expect { calculator.evaluate!('AND("r")') }.to raise_error(Dentaku::ArgumentError)
|
34
34
|
end
|
35
35
|
end
|
data/spec/ast/and_spec.rb
CHANGED
data/spec/ast/arithmetic_spec.rb
CHANGED
@@ -4,53 +4,81 @@ require 'dentaku/ast/arithmetic'
|
|
4
4
|
require 'dentaku/token'
|
5
5
|
|
6
6
|
describe Dentaku::AST::Arithmetic do
|
7
|
-
let(:one)
|
8
|
-
let(:two)
|
9
|
-
let(:x)
|
10
|
-
let(:y)
|
11
|
-
let(:ctx)
|
7
|
+
let(:one) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 1) }
|
8
|
+
let(:two) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 2) }
|
9
|
+
let(:x) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'x') }
|
10
|
+
let(:y) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'y') }
|
11
|
+
let(:ctx) { {'x' => 1, 'y' => 2} }
|
12
|
+
let(:date) { Dentaku::AST::DateTime.new Dentaku::Token.new(:datetime, DateTime.new(2020, 4, 16)) }
|
12
13
|
|
13
14
|
it 'performs an arithmetic operation with numeric operands' do
|
14
|
-
expect(add(one, two)).to eq
|
15
|
-
expect(sub(one, two)).to eq
|
16
|
-
expect(mul(one, two)).to eq
|
17
|
-
expect(div(one, two)).to eq
|
15
|
+
expect(add(one, two)).to eq(3)
|
16
|
+
expect(sub(one, two)).to eq(-1)
|
17
|
+
expect(mul(one, two)).to eq(2)
|
18
|
+
expect(div(one, two)).to eq(0.5)
|
19
|
+
expect(neg(one)).to eq(-1)
|
18
20
|
end
|
19
21
|
|
20
22
|
it 'performs an arithmetic operation with one numeric operand and one string operand' do
|
21
|
-
expect(add(one, x)).to eq
|
22
|
-
expect(sub(one, x)).to eq
|
23
|
-
expect(mul(one, x)).to eq
|
24
|
-
expect(div(one, x)).to eq
|
23
|
+
expect(add(one, x)).to eq(2)
|
24
|
+
expect(sub(one, x)).to eq(0)
|
25
|
+
expect(mul(one, x)).to eq(1)
|
26
|
+
expect(div(one, x)).to eq(1)
|
25
27
|
|
26
|
-
expect(add(y, two)).to eq
|
27
|
-
expect(sub(y, two)).to eq
|
28
|
-
expect(mul(y, two)).to eq
|
29
|
-
expect(div(y, two)).to eq
|
28
|
+
expect(add(y, two)).to eq(4)
|
29
|
+
expect(sub(y, two)).to eq(0)
|
30
|
+
expect(mul(y, two)).to eq(4)
|
31
|
+
expect(div(y, two)).to eq(1)
|
30
32
|
end
|
31
33
|
|
32
34
|
it 'performs an arithmetic operation with string operands' do
|
33
|
-
expect(add(x, y)).to eq
|
34
|
-
expect(sub(x, y)).to eq
|
35
|
-
expect(mul(x, y)).to eq
|
36
|
-
expect(div(x, y)).to eq
|
35
|
+
expect(add(x, y)).to eq(3)
|
36
|
+
expect(sub(x, y)).to eq(-1)
|
37
|
+
expect(mul(x, y)).to eq(2)
|
38
|
+
expect(div(x, y)).to eq(0.5)
|
39
|
+
expect(neg(x)).to eq(-1)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'correctly parses string operands to numeric values' do
|
43
|
+
expect(add(x, one, 'x' => '1')).to eq(2)
|
44
|
+
expect(add(x, one, 'x' => '1.1')).to eq(2.1)
|
45
|
+
expect(add(x, one, 'x' => '.1')).to eq(1.1)
|
46
|
+
expect { add(x, one, 'x' => 'invalid') }.to raise_error(Dentaku::ArgumentError)
|
47
|
+
expect { add(x, one, 'x' => '') }.to raise_error(Dentaku::ArgumentError)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'performs arithmetic on arrays' do
|
51
|
+
expect(add(x, y, 'x' => [1], 'y' => [2])).to eq([1, 2])
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'performs date arithmetic' do
|
55
|
+
expect(add(date, one)).to eq(DateTime.new(2020, 4, 17))
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'raises ArgumentError if given individually valid but incompatible arguments' do
|
59
|
+
expect { add(one, date) }.to raise_error(Dentaku::ArgumentError)
|
60
|
+
expect { add(x, one, 'x' => [1]) }.to raise_error(Dentaku::ArgumentError)
|
37
61
|
end
|
38
62
|
|
39
63
|
private
|
40
64
|
|
41
|
-
def add(left, right)
|
42
|
-
Dentaku::AST::Addition.new(left, right).value(
|
65
|
+
def add(left, right, context = ctx)
|
66
|
+
Dentaku::AST::Addition.new(left, right).value(context)
|
67
|
+
end
|
68
|
+
|
69
|
+
def sub(left, right, context = ctx)
|
70
|
+
Dentaku::AST::Subtraction.new(left, right).value(context)
|
43
71
|
end
|
44
72
|
|
45
|
-
def
|
46
|
-
Dentaku::AST::
|
73
|
+
def mul(left, right, context = ctx)
|
74
|
+
Dentaku::AST::Multiplication.new(left, right).value(context)
|
47
75
|
end
|
48
76
|
|
49
|
-
def
|
50
|
-
Dentaku::AST::
|
77
|
+
def div(left, right, context = ctx)
|
78
|
+
Dentaku::AST::Division.new(left, right).value(context)
|
51
79
|
end
|
52
80
|
|
53
|
-
def
|
54
|
-
Dentaku::AST::
|
81
|
+
def neg(node, context = ctx)
|
82
|
+
Dentaku::AST::Negation.new(node).value(context)
|
55
83
|
end
|
56
84
|
end
|
data/spec/ast/avg_spec.rb
CHANGED
@@ -5,29 +5,33 @@ require 'dentaku'
|
|
5
5
|
describe 'Dentaku::AST::Function::Avg' do
|
6
6
|
it 'returns the average of an array of Numeric values' do
|
7
7
|
result = Dentaku('AVG(1, x, 1.8)', x: 2.3)
|
8
|
-
expect(result).to eq
|
8
|
+
expect(result).to eq(1.7)
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'returns the average of a single entry array of a Numeric value' do
|
12
12
|
result = Dentaku('AVG(x)', x: 2.3)
|
13
|
-
expect(result).to eq
|
13
|
+
expect(result).to eq(2.3)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'returns the average even if a String is passed' do
|
17
17
|
result = Dentaku('AVG(1, x, 1.8)', x: '2.3')
|
18
|
-
expect(result).to eq
|
18
|
+
expect(result).to eq(1.7)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'returns the average even if an array is passed' do
|
22
22
|
result = Dentaku('AVG(1, x, 2.3)', x: [4, 5])
|
23
|
-
expect(result).to eq
|
23
|
+
expect(result).to eq(3.075)
|
24
24
|
end
|
25
25
|
|
26
26
|
context 'checking errors' do
|
27
27
|
let(:calculator) { Dentaku::Calculator.new }
|
28
28
|
|
29
29
|
it 'raises an error if no arguments are passed' do
|
30
|
-
expect { calculator.evaluate!('AVG()') }.to raise_error(ArgumentError)
|
30
|
+
expect { calculator.evaluate!('AVG()') }.to raise_error(Dentaku::ArgumentError)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'raises an error if an empty array is passed' do
|
34
|
+
expect { calculator.evaluate!('AVG(x)', x: []) }.to raise_error(Dentaku::ArgumentError)
|
31
35
|
end
|
32
36
|
end
|
33
37
|
end
|