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 ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
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
@@ -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
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'formula_eval'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
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