dentaku 2.0.9 → 2.0.10

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 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