dentaku 1.2.5 → 1.2.6

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