formula_eval 0.0.1
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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/formula_eval.rb +168 -0
- data/lib/formula_eval/calculating_collection.rb +55 -0
- data/lib/formula_eval/multi_eval.rb +56 -0
- data/lib/formula_eval/wrapper.rb +111 -0
- data/lib/spec.opts +1 -0
- data/spec/calculating_collection_spec.rb +26 -0
- data/spec/formula_eval_spec.rb +60 -0
- data/spec/spec_helper.rb +9 -0
- metadata +91 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Mike Harris
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= formula_eval
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Mike Harris. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "formula_eval"
|
8
|
+
gem.summary = %Q{formula_eval}
|
9
|
+
gem.description = %Q{formula_eval}
|
10
|
+
gem.email = "mharris717@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/mharris717/formula_eval"
|
12
|
+
gem.authors = ["Mike Harris"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "formula_eval #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/formula_eval.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'mharris_ext'
|
2
|
+
require 'active_support'
|
3
|
+
|
4
|
+
class Object
|
5
|
+
attr_accessor :calculating_formula
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
class FormulaEval
|
12
|
+
attr_accessor :row, :formula, :row_index, :rows
|
13
|
+
include FromHash
|
14
|
+
def current_user
|
15
|
+
$current_user
|
16
|
+
end
|
17
|
+
def wrapped_row
|
18
|
+
Wrapper.new(row)
|
19
|
+
end
|
20
|
+
def method_missing(sym,*args,&b)
|
21
|
+
res = 'errored'
|
22
|
+
res = if row.respond_to?(sym)
|
23
|
+
wrapped_row.send(sym,*args,&b)
|
24
|
+
elsif row.respond_to?('[]')
|
25
|
+
wrapped_row[sym.to_s]
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
ensure
|
30
|
+
# puts "Formula mm #{sym} #{args.inspect} #{res.inspect}"
|
31
|
+
end
|
32
|
+
def safe_eval_result
|
33
|
+
instance_eval(formula).to_unwrapped
|
34
|
+
# rescue => exp
|
35
|
+
# t = exp.backtrace.join("\n").gsub("/Users/mharris/.rvm/gems/ruby-1.9.1-p378/gems","gems").gsub("/Users/mharris/Code/smartlist/vendor/mongo_ui","mongo_ui")
|
36
|
+
# t = t.gsub("/Users/mharris/Code/smartlist","smartlist")
|
37
|
+
# mylog "formula_eval", :formula => formula, :row => row, :message => exp.message, :trace => t
|
38
|
+
# "Error #{exp.message}"
|
39
|
+
rescue => exp
|
40
|
+
# puts "error evaling #{formula} against #{row.inspect}, #{exp.message}"
|
41
|
+
raise exp
|
42
|
+
end
|
43
|
+
def result
|
44
|
+
self.formula = formula[1..-1] if formula[0..0] == '='
|
45
|
+
res = safe_eval_result
|
46
|
+
eat_exceptions { res.calculating_formula = formula }
|
47
|
+
res
|
48
|
+
end
|
49
|
+
def call(row)
|
50
|
+
self.row = row
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class FormulaEval
|
56
|
+
def next_row
|
57
|
+
res = rows[row_index+1] || {}
|
58
|
+
HashWrapper.new(:hash => res)
|
59
|
+
end
|
60
|
+
def prev_row_inner
|
61
|
+
return {} if row_index == 0
|
62
|
+
rows[row_index-1] || {}
|
63
|
+
end
|
64
|
+
def prev_row
|
65
|
+
HashWrapper.new(:hash => prev_row_inner)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
load File.dirname(__FILE__) + "/formula_eval/wrapper.rb"
|
70
|
+
load File.dirname(__FILE__) + "/formula_eval/calculating_collection.rb"
|
71
|
+
load File.dirname(__FILE__) + "/formula_eval/multi_eval.rb"
|
72
|
+
|
73
|
+
def mylog(*args)
|
74
|
+
yield if block_given?
|
75
|
+
#puts args.inspect if args.first == 'enriched_doc' or args.first == 'dot_set'
|
76
|
+
end
|
77
|
+
|
78
|
+
class Object
|
79
|
+
def klass
|
80
|
+
self.class
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Object
|
85
|
+
def blank?
|
86
|
+
to_s.strip == ''
|
87
|
+
end
|
88
|
+
def present?
|
89
|
+
!blank?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Object
|
94
|
+
def dot_get(str)
|
95
|
+
str = str.split(".") if str.is_a?(String)
|
96
|
+
res = self
|
97
|
+
last_f = last_res = nil
|
98
|
+
str.each do |f|
|
99
|
+
if f.num? && !res.kind_of?(Array)
|
100
|
+
last_res[last_f] = res = []
|
101
|
+
end
|
102
|
+
last_res = res
|
103
|
+
if res.kind_of?(Array)
|
104
|
+
temp = res[f.safe_to_i]
|
105
|
+
if !temp
|
106
|
+
res << {}
|
107
|
+
temp = res.last
|
108
|
+
raise "can only add new row at end" unless res.size-1 == f.safe_to_i
|
109
|
+
end
|
110
|
+
res = temp
|
111
|
+
else
|
112
|
+
res = res[f]
|
113
|
+
end
|
114
|
+
last_f = f
|
115
|
+
end
|
116
|
+
res
|
117
|
+
end
|
118
|
+
def dot_set(str,val)
|
119
|
+
mylog 'dot_set', :str => str, :val => val, :self => self do
|
120
|
+
return self[str] = val if str.split(".").size == 1
|
121
|
+
strs = str.split(".")[0..-2]
|
122
|
+
lst = str.split(".")[-1]
|
123
|
+
obj = dot_get(strs)
|
124
|
+
return obj unless obj
|
125
|
+
#puts "dot_set, obj is #{obj.inspect}, str is #{str}, val is #{val}, lst is #{lst}"
|
126
|
+
obj.nested_set(lst,val)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Object
|
132
|
+
def nested_set(k,v)
|
133
|
+
self[k] = v
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Array
|
138
|
+
def nested_set(k,v)
|
139
|
+
mylog 'dot_set', :context => 'nested', :klass => klass, :self => self, :k => k, :v => v do
|
140
|
+
each { |x| x.nested_set(k,v) }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class String
|
146
|
+
def num?
|
147
|
+
size > 0 && self =~ /^[\d\.]*$/
|
148
|
+
end
|
149
|
+
def date?
|
150
|
+
matches = (self =~ /\/\d+\//) || (self =~ /-\d+-/)
|
151
|
+
matches2 = self =~ /^[ \d\-\/:]+$/
|
152
|
+
!!(matches && matches2 && Time.parse(self))
|
153
|
+
rescue
|
154
|
+
return false
|
155
|
+
end
|
156
|
+
def to_time
|
157
|
+
Time.parse(self)
|
158
|
+
end
|
159
|
+
def tmo
|
160
|
+
if num?
|
161
|
+
to_f.tmo
|
162
|
+
elsif blank?
|
163
|
+
nil
|
164
|
+
else
|
165
|
+
self
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class CalculatingCollection
|
2
|
+
attr_accessor :coll
|
3
|
+
include FromHash
|
4
|
+
fattr(:column_hash) do
|
5
|
+
res = {}
|
6
|
+
res[:zzz] = lambda { |doc| (doc['abc']||0).to_i + 7 }
|
7
|
+
res[:xyz] = FormulaEval.new(:formula => '=(abc||0)+8')
|
8
|
+
res[:percs] = FormulaEval.new(:formula => '=pension.perc')
|
9
|
+
res[:percs2] = FormulaEval.new(:formula => '=pension.perc * 2.0')
|
10
|
+
res['pension.just_perc'] = FormulaEval.new(:formula => '=perc')
|
11
|
+
res['pension.just_perc_plus'] = FormulaEval.new(:formula => '=perc+1')
|
12
|
+
res['pension.perc_plus_salary'] = FormulaEval.new(:formula => '=perc+salary')
|
13
|
+
res
|
14
|
+
end
|
15
|
+
def enriched_doc(doc)
|
16
|
+
column_hash.each do |col,blk|
|
17
|
+
# mylog 'enriched_doc', :col => col, :blk_class => blk.class, :doc => doc
|
18
|
+
if col.to_s.split('.').size == 1
|
19
|
+
val = blk.call(doc)
|
20
|
+
mylog 'dot_set',:col => col, :val => val, :doc => doc
|
21
|
+
doc.dot_set(col.to_s,val)
|
22
|
+
else
|
23
|
+
base = col.to_s.split('.')[0..-2].join(".")
|
24
|
+
obj = MultiEval.get_nested(doc,base)
|
25
|
+
val = blk.call(obj)
|
26
|
+
mylog 'dot_set',:col => col, :val => val, :doc => doc, :obj => obj
|
27
|
+
doc.dot_set(col.to_s,val)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
doc.to_unwrapped
|
31
|
+
end
|
32
|
+
def save(doc,ops={})
|
33
|
+
mylog 'calc_save', :doc => doc, :doc_class => doc.class, :unwrapped => doc.to_unwrapped.class
|
34
|
+
coll.save(enriched_doc(doc),ops)
|
35
|
+
end
|
36
|
+
def update_calcs!
|
37
|
+
find({},{:limit => 9999}).each do |row|
|
38
|
+
save(row)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
def update_row(row_id,fields)
|
42
|
+
row = (row_id == 'NEW') ? {} : find_by_id(row_id)
|
43
|
+
raise "can't find row #{row_id} #{row_id.class} in coll #{name}. Count is #{find.count} IDs are "+find.to_a.map { |x| x['_id'] }.inspect + "Trying to update with #{fields.inspect}" unless row
|
44
|
+
fields.each do |k,v|
|
45
|
+
row.dot_set(k,mongo_value(v))
|
46
|
+
row.delete(k) if v.blank?
|
47
|
+
end
|
48
|
+
row = enriched_doc(row)
|
49
|
+
save(row)
|
50
|
+
row
|
51
|
+
end
|
52
|
+
def method_missing(sym,*args,&b)
|
53
|
+
coll.send(sym,*args,&b)
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class MultiEval
|
2
|
+
attr_accessor :objs
|
3
|
+
include FromHash
|
4
|
+
def methodf_missing(sym,*args,&b)
|
5
|
+
# puts "multi_eval mm #{sym}"
|
6
|
+
objs.each do |obj|
|
7
|
+
begin
|
8
|
+
res = obj.send(sym,*args,&b)
|
9
|
+
if res
|
10
|
+
#other = objs - [obj]
|
11
|
+
# puts "success sending #{sym} to #{obj}"
|
12
|
+
return MultiEval.new(:objs => [res])
|
13
|
+
end
|
14
|
+
rescue => exp
|
15
|
+
# puts "error sending #{sym} to #{obj} #{exp.message}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
objs.first.send(sym,*args,&b)
|
19
|
+
end
|
20
|
+
def without_obj(obj)
|
21
|
+
res = objs.reject { |x| x == obj }
|
22
|
+
raise "didn't find" unless objs.size == res.size+1
|
23
|
+
res
|
24
|
+
rescue => exp
|
25
|
+
puts "#{objs.inspect} #{obj.inspect}"
|
26
|
+
raise exp
|
27
|
+
end
|
28
|
+
def method_missing(sym,*args,&b)
|
29
|
+
objs.each do |obj|
|
30
|
+
if obj.respond_to?(sym)
|
31
|
+
res = obj.send(sym,*args,&b)
|
32
|
+
return MultiEval.new(:objs => [res]+without_obj(obj)) if res
|
33
|
+
end
|
34
|
+
end
|
35
|
+
raise 'none respond'
|
36
|
+
objs.first.send(sym,*args,&b)
|
37
|
+
end
|
38
|
+
def respond_to?(sym)
|
39
|
+
objs.any? { |x| x.respond_to?(sym) }
|
40
|
+
end
|
41
|
+
def coerce(x)
|
42
|
+
objs.first.coerce(x)
|
43
|
+
end
|
44
|
+
def +(x)
|
45
|
+
objs.first + x
|
46
|
+
end
|
47
|
+
def *(x)
|
48
|
+
objs.first * x
|
49
|
+
end
|
50
|
+
def self.get_nested(obj,method_str)
|
51
|
+
other = obj.instance_eval(method_str)
|
52
|
+
mylog 'get_nested', :obj => obj, :method_str => method_str, :other => other
|
53
|
+
arr = [other,obj]
|
54
|
+
new(:objs => arr)
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# class Wrapper
|
2
|
+
# attr_accessor :obj
|
3
|
+
# include FromHash
|
4
|
+
# def method_missing(sym,*args,&b)
|
5
|
+
# obj.send(sym,*args,&b).to_wrapped
|
6
|
+
# end
|
7
|
+
# end
|
8
|
+
|
9
|
+
class Wrapper
|
10
|
+
def respond_to?(sym)
|
11
|
+
obj.respond_to?(sym)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class HashWrapper
|
16
|
+
attr_accessor :hash
|
17
|
+
include FromHash
|
18
|
+
def method_missing(sym,*args,&b)
|
19
|
+
if obj.keys.include?(sym.to_s)
|
20
|
+
self[sym.to_s]
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
def [](x)
|
26
|
+
raise 'dot_set' if x.to_s == 'dot_set'
|
27
|
+
res = hash[x.to_s]
|
28
|
+
res = nil if res.blank?
|
29
|
+
# raise "nil key #{x}" unless res
|
30
|
+
res.to_wrapped
|
31
|
+
end
|
32
|
+
def []=(k,v)
|
33
|
+
obj[k.to_s] = v
|
34
|
+
end
|
35
|
+
def obj; hash; end
|
36
|
+
def to_unwrapped
|
37
|
+
hash.to_unwrapped
|
38
|
+
end
|
39
|
+
def respond_to?(sym)
|
40
|
+
obj.keys.include?(sym.to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class ArrayWrapper
|
45
|
+
attr_accessor :obj
|
46
|
+
include FromHash
|
47
|
+
def hash_mm(sym)
|
48
|
+
map { |h| h[sym.to_s] }.select { |x| x }.flatten.to_wrapped
|
49
|
+
end
|
50
|
+
def method_missing(sym,*args,&b)
|
51
|
+
if obj.respond_to?(sym)
|
52
|
+
obj.send(sym,*args,&b).to_wrapped
|
53
|
+
elsif contains_all_hashes?
|
54
|
+
hash_mm(sym)
|
55
|
+
else
|
56
|
+
obj.send(sym,*args,&b).to_wrapped
|
57
|
+
end
|
58
|
+
end
|
59
|
+
def *(arg)
|
60
|
+
map { |x| x * arg }
|
61
|
+
end
|
62
|
+
def to_unwrapped
|
63
|
+
obj.to_unwrapped
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Array
|
68
|
+
def to_unwrapped
|
69
|
+
map { |x| x.to_unwrapped }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Hash
|
74
|
+
def to_unwrapped
|
75
|
+
map_value { |v| v.to_unwrapped }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Wrapper
|
80
|
+
def self.new_inner(obj)
|
81
|
+
if obj.kind_of?(Array)
|
82
|
+
ArrayWrapper.new(:obj => obj)
|
83
|
+
elsif obj.kind_of?(Hash)
|
84
|
+
HashWrapper.new(:hash => obj)
|
85
|
+
else
|
86
|
+
obj
|
87
|
+
end
|
88
|
+
end
|
89
|
+
def self.new(obj)
|
90
|
+
new_inner(obj).tap { |x| mylog 'wrapper', :obj => obj, :wrapper_class => x.class }
|
91
|
+
end
|
92
|
+
def self.wrapped?(obj)
|
93
|
+
[ArrayWrapper,HashWrapper].any? { |cls| obj.kind_of?(cls) }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Object
|
98
|
+
def to_wrapped
|
99
|
+
Wrapper.new(self)
|
100
|
+
end
|
101
|
+
def to_unwrapped_inner
|
102
|
+
if Wrapper.wrapped?(self)
|
103
|
+
obj.to_unwrapped
|
104
|
+
else
|
105
|
+
self
|
106
|
+
end
|
107
|
+
end
|
108
|
+
def to_unwrapped
|
109
|
+
to_unwrapped_inner.tap { |x| mylog 'unwrap', :klass => self.klass, :unwrapped => x.class }
|
110
|
+
end
|
111
|
+
end
|
data/lib/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "CalculatingCollection" do
|
4
|
+
fattr(:row) { {'pension' => {'year' => 2025, 'perc' => 0.65}, 'salary' => 42000, 'year' => 2010} }
|
5
|
+
fattr(:wrapped_row) { Wrapper.new(row) }
|
6
|
+
fattr(:formula) { FormulaEval.new(:row => row) }
|
7
|
+
fattr(:column) { 'pension.perc_plus_salary' }
|
8
|
+
fattr(:str) { '=perc*salary' }
|
9
|
+
fattr(:calc) do
|
10
|
+
CalculatingCollection.new(:column_hash => {column => FormulaEval.new(:formula => str)})
|
11
|
+
end
|
12
|
+
fattr(:enriched) do
|
13
|
+
calc.enriched_doc(wrapped_row)
|
14
|
+
end
|
15
|
+
it 'enriched' do
|
16
|
+
enriched['pension'].should == {'year' => 2025, 'perc' => 0.65, 'perc_plus_salary' => 42000.0*0.65}
|
17
|
+
end
|
18
|
+
it 'enriched 2' do
|
19
|
+
calc.column_hash['double_year'] = FormulaEval.new(:formula => '=year*2')
|
20
|
+
enriched['double_year'].should == 4020
|
21
|
+
end
|
22
|
+
it 'uses sub first' do
|
23
|
+
self.str = '=year+1'
|
24
|
+
enriched['pension']['perc_plus_salary'].should == 2026
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class A
|
4
|
+
def a
|
5
|
+
1
|
6
|
+
end
|
7
|
+
def [](k)
|
8
|
+
#puts "sending #{k} to #{self}"
|
9
|
+
send(k)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class B
|
14
|
+
def b
|
15
|
+
2
|
16
|
+
end
|
17
|
+
def [](k)
|
18
|
+
#puts "sending #{k} to #{self}"
|
19
|
+
send(k)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def bt
|
24
|
+
raise 'foo'
|
25
|
+
rescue => exp
|
26
|
+
puts exp.message
|
27
|
+
puts exp.backtrace.join("\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "FormulaEval" do
|
31
|
+
fattr(:str) { '=2+2' }
|
32
|
+
fattr(:row) { {'year' => 2010} }
|
33
|
+
fattr(:formula) { FormulaEval.new(:row => row, :formula => str) }
|
34
|
+
it '2+2' do
|
35
|
+
formula.result.should == 4
|
36
|
+
end
|
37
|
+
it 'double_year' do
|
38
|
+
self.str = '=year*2'
|
39
|
+
formula.result.should == 4020
|
40
|
+
end
|
41
|
+
it 'nested' do
|
42
|
+
|
43
|
+
end
|
44
|
+
it 'double' do
|
45
|
+
self.row = MultiEval.new(:objs => [A.new,B.new])
|
46
|
+
self.str = '=a+b'
|
47
|
+
formula.result.should == 3
|
48
|
+
end
|
49
|
+
it 'double 2' do
|
50
|
+
hash = {'year' => 2010, 'pension' => {'start_year' => 2025, 'perc' => 0.65}}
|
51
|
+
self.row = MultiEval.get_nested(Wrapper.new(hash),'pension')
|
52
|
+
self.str = '=perc*year + perc*year'
|
53
|
+
formula.result.should == 0.65 * 2010 * 2
|
54
|
+
end
|
55
|
+
it 'with hash' do
|
56
|
+
self.row = {'year' => 2010, 'pension' => {'start_year' => 2025, 'perc' => 0.65}}
|
57
|
+
self.str = '=year'
|
58
|
+
formula.result.should == 2010
|
59
|
+
end
|
60
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: formula_eval
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Mike Harris
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-20 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 9
|
31
|
+
version: 1.2.9
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
description: formula_eval
|
35
|
+
email: mharris717@gmail.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- LICENSE
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- .document
|
45
|
+
- .gitignore
|
46
|
+
- LICENSE
|
47
|
+
- README.rdoc
|
48
|
+
- Rakefile
|
49
|
+
- VERSION
|
50
|
+
- lib/formula_eval.rb
|
51
|
+
- lib/formula_eval/calculating_collection.rb
|
52
|
+
- lib/formula_eval/multi_eval.rb
|
53
|
+
- lib/formula_eval/wrapper.rb
|
54
|
+
- lib/spec.opts
|
55
|
+
- spec/calculating_collection_spec.rb
|
56
|
+
- spec/formula_eval_spec.rb
|
57
|
+
- spec/spec_helper.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: http://github.com/mharris717/formula_eval
|
60
|
+
licenses: []
|
61
|
+
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options:
|
64
|
+
- --charset=UTF-8
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
requirements: []
|
82
|
+
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 1.3.6
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: formula_eval
|
88
|
+
test_files:
|
89
|
+
- spec/calculating_collection_spec.rb
|
90
|
+
- spec/formula_eval_spec.rb
|
91
|
+
- spec/spec_helper.rb
|