dentaku 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +119 -0
  3. data/.travis.yml +8 -9
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile +0 -5
  6. data/LICENSE +21 -0
  7. data/README.md +44 -3
  8. data/Rakefile +4 -1
  9. data/dentaku.gemspec +8 -4
  10. data/lib/dentaku.rb +15 -2
  11. data/lib/dentaku/ast.rb +1 -0
  12. data/lib/dentaku/ast/access.rb +2 -2
  13. data/lib/dentaku/ast/arithmetic.rb +7 -7
  14. data/lib/dentaku/ast/bitwise.rb +2 -2
  15. data/lib/dentaku/ast/case.rb +5 -5
  16. data/lib/dentaku/ast/case/case_conditional.rb +1 -1
  17. data/lib/dentaku/ast/case/case_else.rb +2 -2
  18. data/lib/dentaku/ast/case/case_switch_variable.rb +2 -2
  19. data/lib/dentaku/ast/case/case_then.rb +2 -2
  20. data/lib/dentaku/ast/case/case_when.rb +2 -2
  21. data/lib/dentaku/ast/combinators.rb +10 -2
  22. data/lib/dentaku/ast/comparators.rb +34 -6
  23. data/lib/dentaku/ast/function.rb +1 -1
  24. data/lib/dentaku/ast/function_registry.rb +1 -1
  25. data/lib/dentaku/ast/functions/if.rb +6 -2
  26. data/lib/dentaku/ast/functions/max.rb +1 -1
  27. data/lib/dentaku/ast/functions/min.rb +1 -1
  28. data/lib/dentaku/ast/functions/ruby_math.rb +1 -1
  29. data/lib/dentaku/ast/functions/string_functions.rb +8 -8
  30. data/lib/dentaku/ast/functions/sum.rb +12 -0
  31. data/lib/dentaku/ast/grouping.rb +2 -2
  32. data/lib/dentaku/ast/identifier.rb +8 -5
  33. data/lib/dentaku/ast/negation.rb +2 -2
  34. data/lib/dentaku/ast/node.rb +1 -1
  35. data/lib/dentaku/ast/operation.rb +1 -1
  36. data/lib/dentaku/bulk_expression_solver.rb +39 -20
  37. data/lib/dentaku/calculator.rb +38 -28
  38. data/lib/dentaku/dependency_resolver.rb +1 -1
  39. data/lib/dentaku/flat_hash.rb +31 -0
  40. data/lib/dentaku/parser.rb +7 -6
  41. data/lib/dentaku/string_casing.rb +7 -0
  42. data/lib/dentaku/token.rb +1 -1
  43. data/lib/dentaku/token_matcher.rb +4 -4
  44. data/lib/dentaku/token_scanner.rb +18 -7
  45. data/lib/dentaku/tokenizer.rb +26 -2
  46. data/lib/dentaku/version.rb +1 -1
  47. data/spec/ast/arithmetic_spec.rb +2 -2
  48. data/spec/ast/comparator_spec.rb +57 -0
  49. data/spec/ast/function_spec.rb +1 -1
  50. data/spec/ast/max_spec.rb +5 -0
  51. data/spec/ast/min_spec.rb +5 -0
  52. data/spec/ast/sum_spec.rb +38 -0
  53. data/spec/benchmark.rb +2 -2
  54. data/spec/bulk_expression_solver_spec.rb +89 -1
  55. data/spec/calculator_spec.rb +40 -7
  56. data/spec/dentaku_spec.rb +11 -0
  57. data/spec/external_function_spec.rb +7 -7
  58. data/spec/parser_spec.rb +11 -11
  59. data/spec/spec_helper.rb +21 -3
  60. data/spec/token_matcher_spec.rb +0 -1
  61. data/spec/token_spec.rb +6 -0
  62. data/spec/tokenizer_spec.rb +37 -0
  63. metadata +70 -5
@@ -4,16 +4,21 @@ require 'dentaku/token_scanner'
4
4
 
5
5
  module Dentaku
6
6
  class Tokenizer
7
+ attr_reader :case_sensitive, :aliases
8
+
7
9
  LPAREN = TokenMatcher.new(:grouping, :open)
8
10
  RPAREN = TokenMatcher.new(:grouping, :close)
9
11
 
10
- def tokenize(string)
12
+ def tokenize(string, options = {})
11
13
  @nesting = 0
12
14
  @tokens = []
15
+ @aliases = options.fetch(:aliases, Dentaku.aliases)
13
16
  input = strip_comments(string.to_s.dup)
17
+ input = replace_aliases(input)
18
+ @case_sensitive = options.fetch(:case_sensitive, false)
14
19
 
15
20
  until input.empty?
16
- scanned = TokenScanner.scanners.any? do |scanner|
21
+ scanned = TokenScanner.scanners(case_sensitive: case_sensitive).any? do |scanner|
17
22
  scanned, input = scan(input, scanner)
18
23
  scanned
19
24
  end
@@ -58,6 +63,25 @@ module Dentaku
58
63
  input.gsub(/\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//, '')
59
64
  end
60
65
 
66
+ def replace_aliases(string)
67
+ return string unless @aliases.any?
68
+
69
+ string.gsub!(alias_regex) do |match|
70
+ match_regex = /^#{Regexp.escape(match)}$/i
71
+
72
+ @aliases.detect do |(_key, aliases)|
73
+ !aliases.grep(match_regex).empty?
74
+ end.first
75
+ end
76
+
77
+ string
78
+ end
79
+
80
+ def alias_regex
81
+ values = @aliases.values.flatten.join('|')
82
+ /(?<=\p{Punct}|[[:space:]]|\A)(#{values})(?=\()/i
83
+ end
84
+
61
85
  private
62
86
 
63
87
  def fail!(reason, **meta)
@@ -1,3 +1,3 @@
1
1
  module Dentaku
2
- VERSION = "3.0.0"
2
+ VERSION = "3.1.0"
3
3
  end
@@ -8,10 +8,10 @@ describe Dentaku::AST::Arithmetic do
8
8
  let(:two) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 2) }
9
9
  let(:x) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'x') }
10
10
  let(:y) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'y') }
11
- let(:ctx) {{'x' => 1, 'y' => 2}}
11
+ let(:ctx) { {'x' => 1, 'y' => 2} }
12
12
 
13
13
  it 'performs an arithmetic operation with numeric operands' do
14
- expect(add(one, two)).to eq 3
14
+ expect(add(one, two)).to eq 3
15
15
  expect(sub(one, two)).to eq -1
16
16
  expect(mul(one, two)).to eq 2
17
17
  expect(div(one, two)).to eq 0.5
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'dentaku/ast/comparators'
3
+
4
+ require 'dentaku/token'
5
+
6
+ describe Dentaku::AST::Comparator do
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' => 'hello', 'y' => 'world' } }
12
+
13
+ it 'performs comparison with numeric operands' do
14
+ expect(less_than(one, two).value(ctx)).to be_truthy
15
+ expect(less_than(two, one).value(ctx)).to be_falsey
16
+ expect(greater_than(two, one).value(ctx)).to be_truthy
17
+ expect(not_equal(x, y).value(ctx)).to be_truthy
18
+ expect(equal(x, y).value(ctx)).to be_falsey
19
+ end
20
+
21
+ it 'returns correct operator symbols' do
22
+ expect(less_than(one, two).operator).to eq(:<)
23
+ expect(less_than_or_equal(one, two).operator).to eq(:<=)
24
+ expect(greater_than(one, two).operator).to eq(:>)
25
+ expect(greater_than_or_equal(one, two).operator).to eq(:>=)
26
+ expect(not_equal(x, y).operator).to eq(:!=)
27
+ expect(equal(x, y).operator).to eq(:==)
28
+ expect { Dentaku::AST::Comparator.new(one, two).operator }
29
+ .to raise_error(NotImplementedError)
30
+ end
31
+
32
+ private
33
+
34
+ def less_than(left, right)
35
+ Dentaku::AST::LessThan.new(left, right)
36
+ end
37
+
38
+ def less_than_or_equal(left, right)
39
+ Dentaku::AST::LessThanOrEqual.new(left, right)
40
+ end
41
+
42
+ def greater_than(left, right)
43
+ Dentaku::AST::GreaterThan.new(left, right)
44
+ end
45
+
46
+ def greater_than_or_equal(left, right)
47
+ Dentaku::AST::GreaterThanOrEqual.new(left, right)
48
+ end
49
+
50
+ def not_equal(left, right)
51
+ Dentaku::AST::NotEqual.new(left, right)
52
+ end
53
+
54
+ def equal(left, right)
55
+ Dentaku::AST::Equal.new(left, right)
56
+ end
57
+ end
@@ -29,7 +29,7 @@ describe Dentaku::AST::Function do
29
29
  one = described_class.register("one", :numeric, ->(x) { x * 2 })
30
30
  expect(one.arity).to eq 1
31
31
 
32
- two = described_class.register("two", :numeric, ->(x,y) { x + y })
32
+ two = described_class.register("two", :numeric, ->(x, y) { x + y })
33
33
  expect(two.arity).to eq 2
34
34
 
35
35
  many = described_class.register("many", :numeric, ->(*args) { args.max })
@@ -12,4 +12,9 @@ describe 'Dentaku::AST::Function::Max' do
12
12
  result = Dentaku('MAX(1, x, 1.8)', x: '2.3')
13
13
  expect(result).to eq 2.3
14
14
  end
15
+
16
+ it 'returns the largest value even if an Array is passed' do
17
+ result = Dentaku('MAX(1, x, 1.8)', x: [1.5, 2.3, 1.7])
18
+ expect(result).to eq 2.3
19
+ end
15
20
  end
@@ -12,4 +12,9 @@ describe 'Dentaku::AST::Function::Min' do
12
12
  result = Dentaku('MIN(1, x, 1.8)', x: '0.3')
13
13
  expect(result).to eq 0.3
14
14
  end
15
+
16
+ it 'returns the smallest value even if an Array is passed' do
17
+ result = Dentaku('MIN(1, x, 1.8)', x: [1.5, 0.3, 1.7])
18
+ expect(result).to eq 0.3
19
+ end
15
20
  end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'dentaku/ast/functions/sum'
3
+ require 'dentaku'
4
+
5
+ describe 'Dentaku::AST::Function::Sum' do
6
+ it 'returns the sum of an array of Numeric values' do
7
+ result = Dentaku('SUM(1, x, 1.8)', x: 2.3)
8
+ expect(result).to eq 5.1
9
+ end
10
+
11
+ it 'returns the sum of a single entry array of a Numeric value' do
12
+ result = Dentaku('SUM(x)', x: 2.3)
13
+ expect(result).to eq 2.3
14
+ end
15
+
16
+ it 'returns the sum even if a String is passed' do
17
+ result = Dentaku('SUM(1, x, 1.8)', x: '2.3')
18
+ expect(result).to eq 5.1
19
+ end
20
+
21
+ it 'returns the sum even if an array is passed' do
22
+ result = Dentaku('SUM(1, x, 2.3)', x: [4, 5])
23
+ expect(result).to eq 12.3
24
+ end
25
+
26
+ it 'returns the sum of nested sums' do
27
+ result = Dentaku('SUM(1, x, SUM(4, 5))', x: '2.3')
28
+ expect(result).to eq 12.3
29
+ end
30
+
31
+ context 'checking errors' do
32
+ let(:calculator) { Dentaku::Calculator.new }
33
+
34
+ it 'raises an error if no arguments are passed' do
35
+ expect { calculator.evaluate!('SUM()') }.to raise_error(ArgumentError)
36
+ end
37
+ end
38
+ end
@@ -9,12 +9,12 @@ puts "Ruby version #{RUBY_VERSION}"
9
9
 
10
10
  with_duplicate_variables = [
11
11
  "R1+R2+R3+R4+R5+R6",
12
- {"R1"=>100000, "R2"=>0, "R3"=>200000, "R4"=>0, "R5"=>500000, "R6"=>0, "r1"=>100000, "r2"=>0, "r3"=>200000, "r4"=>0, "r5"=>500000, "r6"=>0}
12
+ {"R1" => 100000, "R2" => 0, "R3" => 200000, "R4" => 0, "R5" => 500000, "R6" => 0, "r1" => 100000, "r2" => 0, "r3" => 200000, "r4" => 0, "r5" => 500000, "r6" => 0}
13
13
  ]
14
14
 
15
15
  without_duplicate_variables = [
16
16
  "R1+R2+R3+R4+R5+R6",
17
- {"R1"=>100000, "R2"=>0, "R3"=>200000, "R4"=>0, "R5"=>500000, "R6"=>0}
17
+ {"R1" => 100000, "R2" => 0, "R3" => 200000, "R4" => 0, "R5" => 500000, "R6" => 0}
18
18
  ]
19
19
 
20
20
  def test(args, custom_function: true)
@@ -1,5 +1,8 @@
1
1
  require 'spec_helper'
2
+ require 'dentaku'
2
3
  require 'dentaku/bulk_expression_solver'
4
+ require 'dentaku/calculator'
5
+ require 'dentaku/exceptions'
3
6
 
4
7
  RSpec.describe Dentaku::BulkExpressionSolver do
5
8
  let(:calculator) { Dentaku::Calculator.new }
@@ -33,7 +36,46 @@ RSpec.describe Dentaku::BulkExpressionSolver do
33
36
  it "does not require keys to be parseable" do
34
37
  expressions = { "the value of x, incremented" => "x + 1" }
35
38
  solver = described_class.new(expressions, calculator.store("x" => 3))
36
- expect(solver.solve!).to eq({ "the value of x, incremented" => 4 })
39
+ expect(solver.solve!).to eq("the value of x, incremented" => 4)
40
+ end
41
+
42
+ it "evaluates expressions in hashes and arrays, and expands the results" do
43
+ calculator.store(
44
+ fruit_quantities: {
45
+ apple: 5,
46
+ pear: 9
47
+ },
48
+ fruit_prices: {
49
+ apple: 1.66,
50
+ pear: 2.50
51
+ }
52
+ )
53
+ expressions = {
54
+ weekly_budget: {
55
+ fruit: "weekly_budget.apples + weekly_budget.pears",
56
+ apples: "fruit_quantities.apple * discounted_fruit_prices.apple",
57
+ pears: "fruit_quantities.pear * discounted_fruit_prices.pear",
58
+ },
59
+ discounted_fruit_prices: {
60
+ apple: "round(fruit_prices.apple * discounts[0], 2)",
61
+ pear: "round(fruit_prices.pear * discounts[1], 2)"
62
+ },
63
+ discounts: ["0.4 * 2", "0.3 * 2"],
64
+ }
65
+ solver = described_class.new(expressions, calculator)
66
+
67
+ expect(solver.solve!).to eq(
68
+ weekly_budget: {
69
+ fruit: 20.15,
70
+ apples: 6.65,
71
+ pears: 13.50
72
+ },
73
+ discounted_fruit_prices: {
74
+ apple: 1.33,
75
+ pear: 1.50
76
+ },
77
+ discounts: [0.8, 0.6]
78
+ )
37
79
  end
38
80
  end
39
81
 
@@ -73,5 +115,51 @@ RSpec.describe Dentaku::BulkExpressionSolver do
73
115
  end
74
116
  expect(exception.recipient_variable).to eq('more_apples')
75
117
  end
118
+
119
+ it 'safely handles argument errors' do
120
+ expressions = {i: "a / 5 + d", a: "m * 12", d: "a + b"}
121
+ result = described_class.new(expressions, calculator.store(m: 3)).solve
122
+ expect(result).to eq(
123
+ i: :undefined,
124
+ d: :undefined,
125
+ a: 36,
126
+ )
127
+ end
128
+
129
+ it 'supports nested hashes of expressions using dot notation' do
130
+ expressions = {
131
+ a: "25",
132
+ b: {
133
+ c: "a / 5",
134
+ d: [3, 4, 5]
135
+ },
136
+ e: ["b.c + b.d[1]"],
137
+ f: "e[0] + 1"
138
+ }
139
+ results = described_class.new(expressions, calculator).solve
140
+ expect(results[:f]).to eq 10
141
+ end
142
+
143
+ it 'uses stored values for expressions when they are known' do
144
+ calculator.store(Force: 50, Mass: 25)
145
+ expressions = {
146
+ Force: "Mass * Acceleration",
147
+ Mass: "Force / Acceleration",
148
+ Acceleration: "Force / Mass",
149
+ }
150
+ solver = described_class.new(expressions, calculator)
151
+ results = solver.solve
152
+ expect(results).to eq(Force: 50, Mass: 25, Acceleration: 2)
153
+ end
154
+
155
+ it 'solves all array expressions for which context exists, returning :undefined for the rest' do
156
+ calculator.store(first: 1, equation: 3)
157
+ system = {'key' => ['first * equation', 'second * equation'] }
158
+ solver = described_class.new(system, calculator)
159
+ expect(solver.dependencies).to eq('key' => ['second'])
160
+ results = solver.solve
161
+ expect(results).to eq('key' => [3, :undefined])
162
+ expect { solver.solve! }.to raise_error(Dentaku::UnboundVariableError)
163
+ end
76
164
  end
77
165
  end
@@ -2,8 +2,10 @@ require 'spec_helper'
2
2
  require 'dentaku'
3
3
 
4
4
  describe Dentaku::Calculator do
5
- let(:calculator) { described_class.new }
6
- let(:with_memory) { described_class.new.store(apples: 3) }
5
+ let(:calculator) { described_class.new }
6
+ let(:with_memory) { described_class.new.store(apples: 3) }
7
+ let(:with_aliases) { described_class.new(aliases: { round: ['rrround'] }) }
8
+ let(:without_nested_data) { described_class.new(nested_data_support: false) }
7
9
 
8
10
  it 'evaluates an expression' do
9
11
  expect(calculator.evaluate('7+3')).to eq(10)
@@ -32,6 +34,7 @@ describe Dentaku::Calculator do
32
34
  expect(calculator.evaluate('0.253/0.253')).to eq(1)
33
35
  expect(calculator.evaluate('0.253/d', d: 0.253)).to eq(1)
34
36
  expect(calculator.evaluate('10 + x', x: 'abc')).to be_nil
37
+ expect(calculator.evaluate('x * y', x: '.123', y: '100')).to eq(12.3)
35
38
  expect(calculator.evaluate('a/b', a: '10', b: '2')).to eq(5)
36
39
  expect(calculator.evaluate('t + 1*24*60*60', t: Time.local(2017, 1, 1))).to eq(Time.local(2017, 1, 2))
37
40
  expect(calculator.evaluate("2 | 3 * 9")).to eq (27)
@@ -65,17 +68,21 @@ describe Dentaku::Calculator do
65
68
  end
66
69
 
67
70
  it 'stores nested hashes' do
68
- calculator.store({a: {basket: {of: 'apples'}}, b: 2})
71
+ calculator.store(a: {basket: {of: 'apples'}}, b: 2)
69
72
  expect(calculator.evaluate!('a.basket.of')).to eq 'apples'
70
73
  expect(calculator.evaluate!('b')).to eq 2
71
74
  end
72
75
 
73
76
  it 'stores arrays' do
74
- calculator.store({a: [1, 2, 3]})
77
+ calculator.store(a: [1, 2, 3])
75
78
  expect(calculator.evaluate!('a[0]')).to eq 1
76
79
  expect(calculator.evaluate!('a[x]', x: 1)).to eq 2
77
80
  expect(calculator.evaluate!('a[x+1]', x: 1)).to eq 3
78
81
  end
82
+
83
+ it 'evalutates arrays' do
84
+ expect(calculator.evaluate([1, 2, 3])).to eq([1, 2, 3])
85
+ end
79
86
  end
80
87
 
81
88
  describe 'dependencies' do
@@ -92,6 +99,10 @@ describe Dentaku::Calculator do
92
99
  it "doesn't consider variables in memory as dependencies" do
93
100
  expect(with_memory.dependencies("apples + oranges")).to eq(['oranges'])
94
101
  end
102
+
103
+ it "finds no dependencies in array literals" do
104
+ expect(calculator.dependencies([1, 2, 3])).to eq([])
105
+ end
95
106
  end
96
107
 
97
108
  describe 'solve!' do
@@ -286,7 +297,7 @@ describe Dentaku::Calculator do
286
297
  expect(calculator.evaluate('round(8.8)')).to eq(9)
287
298
  expect(calculator.evaluate('round(8.75, 1)')).to eq(BigDecimal.new('8.8'))
288
299
 
289
- expect(calculator.evaluate('ROUND(apples * 0.93)', { apples: 10 })).to eq(9)
300
+ expect(calculator.evaluate('ROUND(apples * 0.93)', apples: 10)).to eq(9)
290
301
  end
291
302
 
292
303
  it 'include NOT' do
@@ -483,11 +494,11 @@ describe Dentaku::Calculator do
483
494
  end
484
495
 
485
496
  it 'disables the AST cache' do
486
- expect(calculator.disable_cache{ |c| c.cache_ast? }).to be false
497
+ expect(calculator.disable_cache { |c| c.cache_ast? }).to be false
487
498
  end
488
499
 
489
500
  it 'calculates normally' do
490
- expect(calculator.disable_cache{ |c| c.evaluate("2 + 2") }).to eq(4)
501
+ expect(calculator.disable_cache { |c| c.evaluate("2 + 2") }).to eq(4)
491
502
  end
492
503
  end
493
504
 
@@ -539,4 +550,26 @@ describe Dentaku::Calculator do
539
550
  expect(calculator.evaluate("max(1, two())")).to eq 2
540
551
  end
541
552
  end
553
+
554
+ describe 'aliases' do
555
+ it 'accepts aliases as instance option' do
556
+ expect(with_aliases.evaluate('rrround(5.1)')).to eq 5
557
+ end
558
+ end
559
+
560
+ describe 'nested_data' do
561
+ it 'default to nested data enabled' do
562
+ expect(calculator.nested_data_support).to be_truthy
563
+ end
564
+
565
+ it 'allow opt out of nested data support' do
566
+ expect(without_nested_data.nested_data_support).to be_falsy
567
+ end
568
+
569
+ it 'should allow optout of nested hash' do
570
+ expect do
571
+ without_nested_data.solve!('a.b.c')
572
+ end.to raise_error(Dentaku::UnboundVariableError)
573
+ end
574
+ end
542
575
  end
@@ -25,4 +25,15 @@ describe Dentaku do
25
25
  Dentaku('true AND')
26
26
  }.to raise_error(Dentaku::ParseError)
27
27
  end
28
+
29
+ it 'evaluates with class-level shortcut functions' do
30
+ expect(Dentaku.evaluate('2+2')).to eq(4)
31
+ expect(Dentaku.evaluate!('2+2')).to eq(4)
32
+ expect { Dentaku.evaluate!('a+1') }.to raise_error(Dentaku::UnboundVariableError)
33
+ end
34
+
35
+ it 'evaluates with class-level aliases' do
36
+ Dentaku.aliases = { roundup: ['roundupup'] }
37
+ expect(Dentaku.evaluate('roundupup(6.1)')).to eq(7)
38
+ end
28
39
  end
@@ -11,7 +11,7 @@ describe Dentaku::Calculator do
11
11
  c.add_function(:now, :string, -> { Time.now.to_s })
12
12
 
13
13
  fns = [
14
- [:pow, :numeric, ->(mantissa, exponent) { mantissa ** exponent }],
14
+ [:pow, :numeric, ->(mantissa, exponent) { mantissa**exponent }],
15
15
  [:biggest, :numeric, ->(*args) { args.max }],
16
16
  [:smallest, :numeric, ->(*args) { args.min }],
17
17
  ]
@@ -49,7 +49,7 @@ describe Dentaku::Calculator do
49
49
  }
50
50
  )
51
51
 
52
- expect(calculator.evaluate("INCLUDES(list, 2)", list: [1,2,3])).to eq(true)
52
+ expect(calculator.evaluate("INCLUDES(list, 2)", list: [1, 2, 3])).to eq(true)
53
53
  end
54
54
  end
55
55
 
@@ -61,15 +61,15 @@ describe Dentaku::Calculator do
61
61
 
62
62
  it 'does not store functions across all calculators' do
63
63
  calculator1 = Dentaku::Calculator.new
64
- calculator1.add_function(:my_function, :numeric, ->(x) { 2*x + 1 })
64
+ calculator1.add_function(:my_function, :numeric, ->(x) { 2 * x + 1 })
65
65
 
66
66
  calculator2 = Dentaku::Calculator.new
67
- calculator2.add_function(:my_function, :numeric, ->(x) { 4*x + 3 })
67
+ calculator2.add_function(:my_function, :numeric, ->(x) { 4 * x + 3 })
68
68
 
69
- expect(calculator1.evaluate("1 + my_function(2)")). to eq (1 + 2*2 + 1)
70
- expect(calculator2.evaluate("1 + my_function(2)")). to eq (1 + 4*2 + 3)
69
+ expect(calculator1.evaluate("1 + my_function(2)")). to eq (1 + 2 * 2 + 1)
70
+ expect(calculator2.evaluate("1 + my_function(2)")). to eq (1 + 4 * 2 + 3)
71
71
 
72
- expect{Dentaku::Calculator.new.evaluate("1 + my_function(2)")}.to raise_error(Dentaku::ParseError)
72
+ expect { Dentaku::Calculator.new.evaluate("1 + my_function(2)") }.to raise_error(Dentaku::ParseError)
73
73
  end
74
74
 
75
75
  it 'self.add_function adds to default/global function registry' do