dentaku 2.0.9 → 2.0.10

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
  SHA1:
3
- metadata.gz: cc6a362189b735fe68596e1b94e28787d7290ac8
4
- data.tar.gz: cec7635943905eaa4cb8cd888649f5c34a0c1871
3
+ metadata.gz: c2b19bb69372e3700ffb09ca14b5f3fbcb630b06
4
+ data.tar.gz: 4a67960044c94875d8a0ab2487e9464c820af5ca
5
5
  SHA512:
6
- metadata.gz: d82af702ad0a2b009680e7a3292388174915408f7d8e67c5b5cb0d545bd24544edd40a66a0611268bbb5614920c2de386784f8c7132c420802a1577d30b67a43
7
- data.tar.gz: 417cd814e7f46973c312568e8dcd9dd4bbe9fe1dfa27abcb8ac178c58e78850c477d99f9a76abd285abd8141d1e647dceb52b40edcb54681743d9af95cde9684
6
+ metadata.gz: 017f2d92d2b8110151d51ea2c93abe8964a82a662b40ce1f38a8133b75dea64b51ba774f64d06ffb24353024046eeb59457d239527560a57eeb0671b6dff8cc1
7
+ data.tar.gz: cd39bb1acee73e4b05d65940310749601bf758c8797c2cca5ed4e45f6850b59a0637b132a8c4eafd0b60ac52a8714ad5b6933ca38fe3dcc0e60ef9588d2565c0
@@ -10,4 +10,3 @@ rvm:
10
10
  - 2.2.2
11
11
  - 2.2.3
12
12
  - 2.3.0
13
- - rbx-2
@@ -1,5 +1,10 @@
1
1
  # Change Log
2
2
 
3
+ ## [v2.0.10] 2016-12-30
4
+ - fix string function initialization bug
5
+ - fix issues with CASE statements
6
+ - allow injecting AST cache
7
+
3
8
  ## [v2.0.9] 2016-09-19
4
9
  - namespace tokenization errors
5
10
  - automatically coerce arguments to string functions as strings
@@ -111,6 +116,7 @@
111
116
  ## [v0.1.0] 2012-01-20
112
117
  - initial release
113
118
 
119
+ [HEAD]: https://github.com/rubysolo/dentaku/compare/v2.0.9...HEAD
114
120
  [v2.0.9]: https://github.com/rubysolo/dentaku/compare/v2.0.8...v2.0.9
115
121
  [v2.0.8]: https://github.com/rubysolo/dentaku/compare/v2.0.7...v2.0.8
116
122
  [v2.0.7]: https://github.com/rubysolo/dentaku/compare/v2.0.6...v2.0.7
@@ -43,9 +43,8 @@ module Dentaku
43
43
  def dependencies(context={})
44
44
  # TODO: should short-circuit
45
45
  @switch.dependencies(context) +
46
- @conditions.flat_map do |condition|
47
- condition.dependencies(context)
48
- end
46
+ @conditions.flat_map { |condition| condition.dependencies(context) } +
47
+ @else.dependencies(context)
49
48
  end
50
49
  end
51
50
  end
@@ -46,7 +46,7 @@ module Dentaku
46
46
  end
47
47
 
48
48
  function_class = name.to_s.capitalize
49
- Dentaku::AST.send(:remove_const, function_class) if Dentaku::AST.const_defined?(function_class)
49
+ Dentaku::AST.send(:remove_const, function_class) if Dentaku::AST.const_defined?(function_class, false)
50
50
  Dentaku::AST.const_set(function_class, function)
51
51
 
52
52
  function.implementation = implementation
@@ -4,9 +4,9 @@ module Dentaku
4
4
  module AST
5
5
  module StringFunctions
6
6
  class Left < Function
7
- def initialize(string, length)
8
- @string = string
9
- @length = length
7
+ def initialize(*args)
8
+ super
9
+ @string, @length = *@args
10
10
  end
11
11
 
12
12
  def value(context={})
@@ -17,9 +17,9 @@ module Dentaku
17
17
  end
18
18
 
19
19
  class Right < Function
20
- def initialize(string, length)
21
- @string = string
22
- @length = length
20
+ def initialize(*args)
21
+ super
22
+ @string, @length = *@args
23
23
  end
24
24
 
25
25
  def value(context={})
@@ -30,10 +30,9 @@ module Dentaku
30
30
  end
31
31
 
32
32
  class Mid < Function
33
- def initialize(string, offset, length)
34
- @string = string
35
- @offset = offset
36
- @length = length
33
+ def initialize(*args)
34
+ super
35
+ @string, @offset, @length = *@args
37
36
  end
38
37
 
39
38
  def value(context={})
@@ -45,8 +44,9 @@ module Dentaku
45
44
  end
46
45
 
47
46
  class Len < Function
48
- def initialize(string)
49
- @string = string
47
+ def initialize(*args)
48
+ super
49
+ @string = @args[0]
50
50
  end
51
51
 
52
52
  def value(context={})
@@ -56,9 +56,9 @@ module Dentaku
56
56
  end
57
57
 
58
58
  class Find < Function
59
- def initialize(needle, haystack)
60
- @needle = needle
61
- @haystack = haystack
59
+ def initialize(*args)
60
+ super
61
+ @needle, @haystack = *@args
62
62
  end
63
63
 
64
64
  def value(context={})
@@ -71,10 +71,9 @@ module Dentaku
71
71
  end
72
72
 
73
73
  class Substitute < Function
74
- def initialize(original, search, replacement)
75
- @original = original
76
- @search = search
77
- @replacement = replacement
74
+ def initialize(*args)
75
+ super
76
+ @original, @search, @replacement = *@args
78
77
  end
79
78
 
80
79
  def value(context={})
@@ -87,9 +86,9 @@ module Dentaku
87
86
  end
88
87
 
89
88
  class Concat < Function
90
- def initialize(left, right)
91
- @left = left
92
- @right = right
89
+ def initialize(*args)
90
+ super
91
+ @left, @right = *@args
93
92
  end
94
93
 
95
94
  def value(context={})
@@ -8,10 +8,10 @@ module Dentaku
8
8
  class Calculator
9
9
  attr_reader :result, :memory, :tokenizer
10
10
 
11
- def initialize
11
+ def initialize(ast_cache={})
12
12
  clear
13
13
  @tokenizer = Tokenizer.new
14
- @ast_cache = {}
14
+ @ast_cache = ast_cache
15
15
  @disable_ast_cache = false
16
16
  end
17
17
 
@@ -67,15 +67,24 @@ module Dentaku
67
67
  # special handling for case nesting: strip out inner case
68
68
  # statements and parse their AST segments recursively
69
69
  if operations.include?(AST::Case)
70
- last_case_close_index = nil
71
- first_nested_case_close_index = nil
70
+ open_cases = 0
71
+ case_end_index = nil
72
+
72
73
  input.each_with_index do |token, index|
73
- first_nested_case_close_index = last_case_close_index
74
+ if token.category == :case && token.value == :open
75
+ open_cases += 1
76
+ end
77
+
74
78
  if token.category == :case && token.value == :close
75
- last_case_close_index = index
79
+ if open_cases > 0
80
+ open_cases -= 1
81
+ else
82
+ case_end_index = index
83
+ break
84
+ end
76
85
  end
77
86
  end
78
- inner_case_inputs = input.slice!(0..first_nested_case_close_index)
87
+ inner_case_inputs = input.slice!(0..case_end_index)
79
88
  subparser = Parser.new(
80
89
  inner_case_inputs,
81
90
  operations: [AST::Case],
@@ -1,3 +1,3 @@
1
1
  module Dentaku
2
- VERSION = "2.0.9"
2
+ VERSION = "2.0.10"
3
3
  end
@@ -67,14 +67,18 @@ describe Dentaku::AST::Case do
67
67
  let!(:tax) do
68
68
  Dentaku::AST::Identifier.new(Dentaku::Token.new(:identifier, :tax))
69
69
  end
70
+ let!(:fallback) do
71
+ Dentaku::AST::Identifier.new(Dentaku::Token.new(:identifier, :fallback))
72
+ end
70
73
  let!(:addition) { Dentaku::AST::Addition.new(two, tax) }
71
74
  let!(:when2) { Dentaku::AST::CaseWhen.new(banana) }
72
75
  let!(:then2) { Dentaku::AST::CaseThen.new(addition) }
76
+ let!(:else2) { Dentaku::AST::CaseElse.new(fallback) }
73
77
  let!(:conditional2) { Dentaku::AST::CaseConditional.new(when2, then2) }
74
78
 
75
79
  it 'gathers dependencies from switch and conditionals' do
76
- node = described_class.new(switch, conditional1, conditional2)
77
- expect(node.dependencies).to eq([:fruit, :tax])
80
+ node = described_class.new(switch, conditional1, conditional2, else2)
81
+ expect(node.dependencies).to eq([:fruit, :tax, :fallback])
78
82
  end
79
83
  end
80
84
  end
@@ -1,6 +1,8 @@
1
1
  require 'spec_helper'
2
2
  require 'dentaku/ast/function'
3
3
 
4
+ class Clazz; end
5
+
4
6
  describe Dentaku::AST::Function do
5
7
  it 'maintains a function registry' do
6
8
  expect(described_class).to respond_to(:get)
@@ -18,4 +20,8 @@ describe Dentaku::AST::Function do
18
20
  function = described_class.get("flarble").new
19
21
  expect(function.value).to eq "flarble"
20
22
  end
23
+
24
+ it 'does not throw an error when registering a function with a name that matches a currently defined constant' do
25
+ expect { described_class.register("clazz", :string, -> { "clazzified" }) }.not_to raise_error
26
+ end
21
27
  end
@@ -66,6 +66,12 @@ describe Dentaku::Calculator do
66
66
  expect(calculator.dependencies("bob + dole / 3")).to eq(['bob', 'dole'])
67
67
  end
68
68
 
69
+ it "finds dependencies in formula arguments" do
70
+ allow(Dentaku).to receive(:cache_ast?) { true }
71
+
72
+ expect(calculator.dependencies("CONCAT(bob, dole)")).to eq(['bob', 'dole'])
73
+ end
74
+
69
75
  it "doesn't consider variables in memory as dependencies" do
70
76
  expect(with_memory.dependencies("apples + oranges")).to eq(['oranges'])
71
77
  end
@@ -379,6 +385,34 @@ describe Dentaku::Calculator do
379
385
  fruit: 'banana')
380
386
  expect(value).to eq(5)
381
387
  end
388
+
389
+ it 'handles multiple nested case statements' do
390
+ formula = <<-FORMULA
391
+ CASE fruit
392
+ WHEN 'apple'
393
+ THEN
394
+ CASE quantity
395
+ WHEN 2 THEN 3
396
+ END
397
+ WHEN 'banana'
398
+ THEN
399
+ CASE quantity
400
+ WHEN 1 THEN 2
401
+ END
402
+ END
403
+ FORMULA
404
+ value = calculator.evaluate(
405
+ formula,
406
+ quantity: 1,
407
+ fruit: 'banana')
408
+ expect(value).to eq(2)
409
+
410
+ value = calculator.evaluate(
411
+ formula,
412
+ quantity: 2,
413
+ fruit: 'apple')
414
+ expect(value).to eq(3)
415
+ end
382
416
  end
383
417
 
384
418
  describe 'math functions' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dentaku
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.9
4
+ version: 2.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Solomon White
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-19 00:00:00.000000000 Z
11
+ date: 2016-12-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake