dentaku 1.2.5 → 1.2.6

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: cca185ca8ad75d5c0c0cd13241ad8356a6d046f0
4
- data.tar.gz: 469a8af2de92efdb3b75297ec7de268e6b066492
3
+ metadata.gz: 3007daa3af2e8cf5e738f4d98d046f23422004e3
4
+ data.tar.gz: 7994b1e07961a589580275cfb14978a9fb6ab091
5
5
  SHA512:
6
- metadata.gz: b0348e2c7b06c318ab67f88bfddc5f6c6e5ac44efdaeacd53d35a23764f3d961530524c7929673ce3737375f0b93671dedadccb7ab5d0d363f4916b218ce0b30
7
- data.tar.gz: 36043cafe22a9f2dd60f926a13832dc3175e3f7000c6eac081925d234501f287c6289adeed51b45334d9c7353a4a29a5458b7414cd612ecbb05039e852590914
6
+ metadata.gz: b5287d0c47550c976e373a1b291526e79da3fae5aa4e78ebd6e479e3ee467a701f577b45d9acacf9949466186f429f6b9d0ec211917a776e0e29230bba6075d7
7
+ data.tar.gz: f7ac853d45ed2a50b3b5d0b4ee8cab110db7adea2dce84f291a328051a19da1c955e058d2d68d23ea1cb367deac0387b94b6039b836f7310a625a7a599861236
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ vendor/*
8
8
 
9
9
  /.ruby-gemset
10
10
  /.ruby-version
11
+ /.rspec
data/.pryrc ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler"
2
+ Bundler.require
data/README.md CHANGED
@@ -247,11 +247,12 @@ contributors:
247
247
 
248
248
  * [0xCCD](https://github.com/0xCCD)
249
249
  * [AlexeyMK](https://github.com/AlexeyMK)
250
- * [CraigCottingham](https://github.com/CraigCottingham)
251
250
  * [antonversal](https://github.com/antonversal)
252
251
  * [arnaudl](https://github.com/arnaudl)
253
252
  * [bernardofire](https://github.com/bernardofire)
254
253
  * [brixen](https://github.com/brixen)
254
+ * [CraigCottingham](https://github.com/CraigCottingham)
255
+ * [glanotte](https://github.com/glanotte)
255
256
  * [jasonhutchens](https://github.com/jasonhutchens)
256
257
  * [jmangs](https://github.com/jmangs)
257
258
  * [mvbrocato](https://github.com/mvbrocato)
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_development_dependency('rake')
20
20
  s.add_development_dependency('rspec')
21
+ s.add_development_dependency('pry')
21
22
 
22
23
  s.files = `git ls-files`.split("\n")
23
24
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -0,0 +1,75 @@
1
+ require 'dentaku/calculator'
2
+ require 'dentaku/dependency_resolver'
3
+ require 'dentaku/exceptions'
4
+ require 'dentaku/expression'
5
+
6
+ module Dentaku
7
+ class BulkExpressionSolver
8
+ def initialize(expression_hash, memory)
9
+ self.expression_hash = expression_hash
10
+ self.memory = memory
11
+ end
12
+
13
+ def solve!
14
+ solve(&raise_exception_handler)
15
+ end
16
+
17
+ def solve(&block)
18
+ error_handler = block || return_undefined_handler
19
+ results = load_results(&error_handler)
20
+
21
+ expression_hash.each_with_object({}) do |(k, _), r|
22
+ r[k] = results[k.to_s]
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_accessor :expression_hash, :memory
29
+
30
+ def return_undefined_handler
31
+ ->(*) { :undefined }
32
+ end
33
+
34
+ def raise_exception_handler
35
+ ->(ex) { raise ex }
36
+ end
37
+
38
+ def load_results(&block)
39
+ variables_in_resolve_order.each_with_object({}) do |var_name, r|
40
+ begin
41
+ r[var_name] = evaluate!(expressions[var_name], r)
42
+ rescue Dentaku::UnboundVariableError, ZeroDivisionError => ex
43
+ r[var_name] = block.call(ex)
44
+ end
45
+ end
46
+ end
47
+
48
+ def dependencies(expression)
49
+ Expression.new(expression, memory).identifiers
50
+ end
51
+
52
+ def expressions
53
+ @expressions ||= Hash[expression_hash.map { |k,v| [k.to_s, v] }]
54
+ end
55
+
56
+ def expression_dependencies
57
+ Hash[expressions.map { |var, expr| [var, dependencies(expr)] }]
58
+ end
59
+
60
+ def variables_in_resolve_order
61
+ @variables_in_resolve_order ||=
62
+ DependencyResolver::find_resolve_order(expression_dependencies)
63
+ end
64
+
65
+ def evaluate!(expression, results)
66
+ expr = Expression.new(expression, memory.merge(expressions))
67
+ raise UnboundVariableError.new(expr.identifiers) if expr.unbound?
68
+ calculator.evaluate!(expression, results)
69
+ end
70
+
71
+ def calculator
72
+ @calculator ||= Calculator.new.store(memory)
73
+ end
74
+ end
75
+ end
@@ -1,3 +1,4 @@
1
+ require 'dentaku/bulk_expression_solver'
1
2
  require 'dentaku/evaluator'
2
3
  require 'dentaku/exceptions'
3
4
  require 'dentaku/expression'
@@ -40,24 +41,11 @@ module Dentaku
40
41
  end
41
42
 
42
43
  def solve!(expression_hash)
43
- expressions = Hash[expression_hash.map { |k,v| [k.to_s, v] }]
44
-
45
- # expression_hash: { variable_name: "string expression" }
46
- # TSort thru the expressions' dependencies, then evaluate all
47
- expression_dependencies = Hash[expressions.map do |var, expr|
48
- [var, dependencies(expr)]
49
- end]
50
-
51
- variables_in_resolve_order = DependencyResolver::find_resolve_order(
52
- expression_dependencies)
53
-
54
- results = variables_in_resolve_order.each_with_object({}) do |var_name, r|
55
- r[var_name] = evaluate!(expressions[var_name], r)
56
- end
44
+ BulkExpressionSolver.new(expression_hash, @memory).solve!
45
+ end
57
46
 
58
- expression_hash.each_with_object({}) do |(k, _), r|
59
- r[k] = results[k.to_s]
60
- end
47
+ def solve(expression_hash, &block)
48
+ BulkExpressionSolver.new(expression_hash, @memory).solve(&block)
61
49
  end
62
50
 
63
51
  def dependencies(expression)
@@ -1,3 +1,3 @@
1
1
  module Dentaku
2
- VERSION = "1.2.5"
2
+ VERSION = "1.2.6"
3
3
  end
@@ -0,0 +1,51 @@
1
+ require 'dentaku/bulk_expression_solver'
2
+
3
+ RSpec.describe Dentaku::BulkExpressionSolver do
4
+ describe "#solve!" do
5
+ it "evaluates properly with variables, even if some in memory" do
6
+ expressions = {
7
+ weekly_fruit_budget: "weekly_apple_budget + pear * 4",
8
+ weekly_apple_budget: "apples * 7",
9
+ pear: "1"
10
+ }
11
+ memory = {apples: 3}
12
+ solver = described_class.new(expressions, memory)
13
+ expect(solver.solve!)
14
+ .to eq(pear: 1, weekly_apple_budget: 21, weekly_fruit_budget: 25)
15
+ end
16
+
17
+ it "lets you know if a variable is unbound" do
18
+ expressions = {more_apples: "apples + 1"}
19
+ expect {
20
+ described_class.new(expressions, {}).solve!()
21
+ }.to raise_error(Dentaku::UnboundVariableError)
22
+ end
23
+
24
+ it "lets you know if the result is a div/0 error" do
25
+ expressions = {more_apples: "1/0"}
26
+ expect {
27
+ described_class.new(expressions, {}).solve!()
28
+ }.to raise_error(ZeroDivisionError)
29
+ end
30
+ end
31
+
32
+ describe "#solve" do
33
+ it "returns :undefined when variables are unbound" do
34
+ expressions = {more_apples: "apples + 1"}
35
+ expect(described_class.new(expressions, {}).solve())
36
+ .to eq(more_apples: :undefined)
37
+ end
38
+
39
+ it "allows passing in a custom value to an error handler when a variable is unbound" do
40
+ expressions = {more_apples: "apples + 1"}
41
+ expect(described_class.new(expressions, {}).solve() { :foo })
42
+ .to eq(more_apples: :foo)
43
+ end
44
+
45
+ it "allows passing in a custom value to an error handler when there is a div/0 error" do
46
+ expressions = {more_apples: "1/0"}
47
+ expect(described_class.new(expressions, {}).solve() { :foo })
48
+ .to eq(more_apples: :foo)
49
+ end
50
+ end
51
+ end
@@ -87,6 +87,25 @@ describe Dentaku::Calculator do
87
87
  result = with_memory.solve!(total_fruit: "Apples + pears", pears: 10)
88
88
  expect(result[:total_fruit]).to eq 13
89
89
  end
90
+
91
+ it "lets you know if a variable is unbound" do
92
+ expect {
93
+ calculator.solve!(more_apples: "apples + 1")
94
+ }.to raise_error(Dentaku::UnboundVariableError)
95
+ end
96
+ end
97
+
98
+ describe 'solve' do
99
+ it "returns :undefined when variables are unbound" do
100
+ expressions = {more_apples: "apples + 1"}
101
+ expect(calculator.solve(expressions)).to eq(more_apples: :undefined)
102
+ end
103
+
104
+ it "allows passing in a custom value to an error handler" do
105
+ expressions = {more_apples: "apples + 1"}
106
+ expect(calculator.solve(expressions) { :foo })
107
+ .to eq(more_apples: :foo)
108
+ end
90
109
  end
91
110
 
92
111
  it 'evaluates a statement with no variables' 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: 1.2.5
4
+ version: 1.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Solomon White
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-23 00:00:00.000000000 Z
11
+ date: 2015-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  description: |2
42
56
  Dentaku is a parser and evaluator for mathematical formulas
43
57
  email:
@@ -47,6 +61,7 @@ extensions: []
47
61
  extra_rdoc_files: []
48
62
  files:
49
63
  - ".gitignore"
64
+ - ".pryrc"
50
65
  - ".travis.yml"
51
66
  - Gemfile
52
67
  - README.md
@@ -54,6 +69,7 @@ files:
54
69
  - dentaku.gemspec
55
70
  - lib/dentaku.rb
56
71
  - lib/dentaku/binary_operation.rb
72
+ - lib/dentaku/bulk_expression_solver.rb
57
73
  - lib/dentaku/calculator.rb
58
74
  - lib/dentaku/dependency_resolver.rb
59
75
  - lib/dentaku/evaluator.rb
@@ -68,6 +84,7 @@ files:
68
84
  - lib/dentaku/version.rb
69
85
  - spec/benchmark.rb
70
86
  - spec/binary_operation_spec.rb
87
+ - spec/bulk_expression_solver_spec.rb
71
88
  - spec/calculator_spec.rb
72
89
  - spec/dentaku_spec.rb
73
90
  - spec/evaluator_spec.rb
@@ -106,6 +123,7 @@ summary: A formula language parser and evaluator
106
123
  test_files:
107
124
  - spec/benchmark.rb
108
125
  - spec/binary_operation_spec.rb
126
+ - spec/bulk_expression_solver_spec.rb
109
127
  - spec/calculator_spec.rb
110
128
  - spec/dentaku_spec.rb
111
129
  - spec/evaluator_spec.rb