calco 0.1.0
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 +7 -0
- data/.gitignore +34 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +22 -0
- data/README.md +360 -0
- data/Rakefile +1 -0
- data/calco.gemspec +23 -0
- data/examples/ages.ods +0 -0
- data/examples/compute_cells.rb +61 -0
- data/examples/data.csv +8 -0
- data/examples/example.rb +97 -0
- data/examples/multiplication_tables.ods +0 -0
- data/examples/multiplication_tables.rb +73 -0
- data/examples/register_function.rb +44 -0
- data/examples/using_date_functions.rb +42 -0
- data/examples/write_csv.rb +68 -0
- data/examples/write_ods.rb +69 -0
- data/lib/calco.rb +17 -0
- data/lib/calco/core_ext/fixnum.rb +22 -0
- data/lib/calco/core_ext/float.rb +22 -0
- data/lib/calco/core_ext/range.rb +15 -0
- data/lib/calco/core_ext/string.rb +20 -0
- data/lib/calco/date_functions.rb +13 -0
- data/lib/calco/definition_dsl.rb +127 -0
- data/lib/calco/elements/aggregator.rb +17 -0
- data/lib/calco/elements/builtin_function.rb +84 -0
- data/lib/calco/elements/constant.rb +31 -0
- data/lib/calco/elements/current.rb +19 -0
- data/lib/calco/elements/element.rb +31 -0
- data/lib/calco/elements/empty.rb +9 -0
- data/lib/calco/elements/formula.rb +42 -0
- data/lib/calco/elements/if.rb +26 -0
- data/lib/calco/elements/operation.rb +34 -0
- data/lib/calco/elements/operator.rb +17 -0
- data/lib/calco/elements/or.rb +26 -0
- data/lib/calco/elements/value_extractor.rb +42 -0
- data/lib/calco/elements/variable.rb +35 -0
- data/lib/calco/engines/calculator_builtin_functions.rb +32 -0
- data/lib/calco/engines/csv_engine.rb +80 -0
- data/lib/calco/engines/default_engine.rb +140 -0
- data/lib/calco/engines/office_engine.rb +263 -0
- data/lib/calco/engines/simple_calculator_engine.rb +151 -0
- data/lib/calco/math_functions.rb +9 -0
- data/lib/calco/sheet.rb +363 -0
- data/lib/calco/spreadsheet.rb +172 -0
- data/lib/calco/string_functions.rb +9 -0
- data/lib/calco/style.rb +15 -0
- data/lib/calco/time_functions.rb +12 -0
- data/lib/calco/version.rb +3 -0
- data/spec/absolute_references_spec.rb +86 -0
- data/spec/builtin_functions_spec.rb +161 -0
- data/spec/calculator_engine_spec.rb +251 -0
- data/spec/conditions_spec.rb +118 -0
- data/spec/content_change_spec.rb +190 -0
- data/spec/csv_engine_spec.rb +324 -0
- data/spec/default_engine_spec.rb +135 -0
- data/spec/definitions_spec.rb +65 -0
- data/spec/errors_spec.rb +189 -0
- data/spec/functions_spec.rb +251 -0
- data/spec/header_row_spec.rb +63 -0
- data/spec/range_spec.rb +189 -0
- data/spec/sheet_selections_spec.rb +49 -0
- data/spec/sheet_spec.rb +229 -0
- data/spec/smart_types_spec.rb +43 -0
- data/spec/spreadsheet_spec.rb +80 -0
- data/spec/styles_spec.rb +29 -0
- data/spec/variables_spec.rb +41 -0
- metadata +158 -0
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
require 'calco/engines/simple_calculator_engine'
|
5
|
+
|
6
|
+
module CalculatorSpecHelpers
|
7
|
+
|
8
|
+
def compute n = nil
|
9
|
+
|
10
|
+
buffer = StringIO.new
|
11
|
+
|
12
|
+
@doc.save(buffer) do |spreadsheet|
|
13
|
+
|
14
|
+
sheet = spreadsheet.current
|
15
|
+
|
16
|
+
if n == 0
|
17
|
+
|
18
|
+
sheet.write_row 0
|
19
|
+
|
20
|
+
elsif n
|
21
|
+
|
22
|
+
n.each {|i| sheet.write_row i}
|
23
|
+
|
24
|
+
else
|
25
|
+
|
26
|
+
sheet.write_row 1
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
@result = buffer.string.split("\n").map {|s| s.strip}
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
module Calco
|
39
|
+
|
40
|
+
describe SimpleCalculatorEngine do
|
41
|
+
|
42
|
+
include CalculatorSpecHelpers
|
43
|
+
|
44
|
+
it "does not return header" do
|
45
|
+
|
46
|
+
create_spreadsheet :default
|
47
|
+
|
48
|
+
compute 0
|
49
|
+
|
50
|
+
@result.should be_empty
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
it "computes simple function" do
|
55
|
+
|
56
|
+
create_spreadsheet :default
|
57
|
+
|
58
|
+
compute
|
59
|
+
|
60
|
+
@result.last.should == "C1 = 20"
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
it "uses variable names as names" do
|
65
|
+
|
66
|
+
create_spreadsheet :default
|
67
|
+
|
68
|
+
compute
|
69
|
+
|
70
|
+
@result == ["price = 10", "quantity = 2", "C0 = 20"]
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
it "uses titles as names" do
|
75
|
+
|
76
|
+
create_spreadsheet :with_title
|
77
|
+
|
78
|
+
compute
|
79
|
+
|
80
|
+
@result.should == ["price = 10", "quantity = 2", "P*Q = 20"]
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
it "computes sheets with absolute cell references" do
|
85
|
+
|
86
|
+
create_spreadsheet :with_title, :add_absolute
|
87
|
+
|
88
|
+
compute 1..3
|
89
|
+
|
90
|
+
@result[3].should == "tax = 100"
|
91
|
+
|
92
|
+
@result[11].should == "price = 10"
|
93
|
+
@result[12].should == "quantity = 2"
|
94
|
+
@result[13].should == "P*Q = 20"
|
95
|
+
@result[14].should == "F3 = 120"
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
it "uses string values" do
|
100
|
+
|
101
|
+
@doc = spreadsheet(@engine) do
|
102
|
+
|
103
|
+
definitions do
|
104
|
+
|
105
|
+
set first_name: "John"
|
106
|
+
set last_name: "Smith"
|
107
|
+
|
108
|
+
function name: first_name + " " + last_name
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
sheet do
|
113
|
+
|
114
|
+
column value_of(:first_name)
|
115
|
+
column value_of(:last_name)
|
116
|
+
|
117
|
+
column :name, title: 'name'
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
compute
|
124
|
+
|
125
|
+
@result.last.should == 'name = "John Smith"'
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
before do
|
130
|
+
@engine = Calco::SimpleCalculatorEngine.new
|
131
|
+
end
|
132
|
+
|
133
|
+
def create_spreadsheet which, add_absolute_row = false
|
134
|
+
|
135
|
+
@doc = spreadsheet(@engine) do
|
136
|
+
|
137
|
+
definitions do
|
138
|
+
|
139
|
+
set price: 10
|
140
|
+
set quantity: 2
|
141
|
+
|
142
|
+
function total: price * quantity
|
143
|
+
|
144
|
+
if add_absolute_row == :add_absolute
|
145
|
+
|
146
|
+
set tax: 100
|
147
|
+
|
148
|
+
function more: total + tax
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
sheet do
|
155
|
+
|
156
|
+
column value_of(:price)
|
157
|
+
column value_of(:quantity)
|
158
|
+
|
159
|
+
column :total if which == :default
|
160
|
+
column :total, title: 'P*Q' if which == :with_title
|
161
|
+
|
162
|
+
if add_absolute_row == :add_absolute
|
163
|
+
|
164
|
+
column skip
|
165
|
+
|
166
|
+
column value_of(:tax, :absolute)
|
167
|
+
column :more
|
168
|
+
|
169
|
+
tax.absolute_row = 1
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
describe SimpleCalculatorEngine do
|
182
|
+
|
183
|
+
include CalculatorSpecHelpers
|
184
|
+
|
185
|
+
it "computes functions with today and year" do
|
186
|
+
|
187
|
+
@doc = spreadsheet(@engine) do
|
188
|
+
|
189
|
+
definitions do
|
190
|
+
|
191
|
+
set some_date: "2013/09/06"
|
192
|
+
|
193
|
+
function some_year: year(some_date)
|
194
|
+
function today: year(today)
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
sheet do
|
199
|
+
|
200
|
+
column value_of(:some_date)
|
201
|
+
|
202
|
+
column :some_year
|
203
|
+
column :today
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
compute
|
210
|
+
|
211
|
+
year = Date.today.year
|
212
|
+
|
213
|
+
@result.should == ['some_date = "2013-09-06"', "B1 = 2013", "C1 = #{year}"]
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
it "computes functions with left" do
|
218
|
+
|
219
|
+
@doc = spreadsheet(@engine) do
|
220
|
+
|
221
|
+
definitions do
|
222
|
+
|
223
|
+
set name: "James"
|
224
|
+
|
225
|
+
function initials: left(name, 1)
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
sheet do
|
230
|
+
|
231
|
+
column value_of(:name)
|
232
|
+
|
233
|
+
column :initials
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
compute
|
240
|
+
|
241
|
+
@result.should == ['name = "James"', 'B1 = "J"']
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
before do
|
246
|
+
@engine = Calco::SimpleCalculatorEngine.new
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'calco'
|
2
|
+
|
3
|
+
describe "Spreadsheet with conditions" do
|
4
|
+
|
5
|
+
it "supports conditional functions" do
|
6
|
+
|
7
|
+
doc = spreadsheet do
|
8
|
+
|
9
|
+
definitions do
|
10
|
+
|
11
|
+
set score: 23
|
12
|
+
function opinion: _if(score < 20, 'Oops...', 'Great!')
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
sheet do
|
17
|
+
|
18
|
+
column value_of(:score)
|
19
|
+
column :opinion
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
row = doc.row(1)
|
26
|
+
|
27
|
+
expect(row[0]).to eq('23')
|
28
|
+
expect(row[1]).to eq('IF(A1<20;"Oops...";"Great!")')
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
it "supports conditions with strings" do
|
33
|
+
|
34
|
+
doc = spreadsheet do
|
35
|
+
|
36
|
+
definitions do
|
37
|
+
|
38
|
+
set who: "John"
|
39
|
+
function bonus: _if(who == 'John', 200, 0)
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
sheet do
|
44
|
+
|
45
|
+
column value_of(:who)
|
46
|
+
column :bonus
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
row = doc.row(1)
|
53
|
+
|
54
|
+
expect(row[0]).to eq('"John"')
|
55
|
+
expect(row[1]).to eq('IF(A1="John";200;0)')
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
it "accepts conditions within conditions" do
|
60
|
+
|
61
|
+
doc = spreadsheet do
|
62
|
+
|
63
|
+
definitions do
|
64
|
+
|
65
|
+
set age: 34
|
66
|
+
set who: "John"
|
67
|
+
|
68
|
+
function bonus: _if(who == 'John', _if(age > 25, 200, 0), 10)
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
sheet do
|
73
|
+
|
74
|
+
column value_of(:who)
|
75
|
+
column value_of(:age)
|
76
|
+
|
77
|
+
column :bonus
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
row = doc.row(1)
|
84
|
+
|
85
|
+
expect(row[0]).to eq('"John"')
|
86
|
+
expect(row[1]).to eq('34')
|
87
|
+
expect(row[2]).to eq('IF(A1="John";IF(B1>25;200;0);10)')
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
it "supports conditions with or" do
|
92
|
+
|
93
|
+
doc = spreadsheet do
|
94
|
+
|
95
|
+
definitions do
|
96
|
+
|
97
|
+
set who: "John"
|
98
|
+
function bonus: _if(_or(who == 'John', who == 'Mary'), 200, 0)
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
sheet do
|
103
|
+
|
104
|
+
column value_of(:who)
|
105
|
+
column :bonus
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
row = doc.row(1)
|
112
|
+
|
113
|
+
expect(row[0]).to eq('"John"')
|
114
|
+
expect(row[1]).to eq('IF(OR((A1="John");(A1="Mary"));200;0)')
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'calco'
|
2
|
+
|
3
|
+
RSpec.configure do |c|
|
4
|
+
c.alias_example_to :change_by
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'Content change' do
|
8
|
+
|
9
|
+
change_by "replacing content with a function" do
|
10
|
+
|
11
|
+
doc = spreadsheet do
|
12
|
+
|
13
|
+
definitions do
|
14
|
+
|
15
|
+
set empty: ''
|
16
|
+
set quantity: 17
|
17
|
+
|
18
|
+
function double: quantity * 2
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
sheet do
|
23
|
+
|
24
|
+
column value_of(:quantity)
|
25
|
+
column value_of(:empty), :id => :here
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
sheet = doc.current
|
32
|
+
|
33
|
+
sheet.replace_content :here, :double
|
34
|
+
|
35
|
+
row = sheet.row(2)
|
36
|
+
expect(row[0]).to eq('17')
|
37
|
+
expect(row[1]).to eq("A2*2")
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
change_by "replacing content with a variable" do
|
42
|
+
|
43
|
+
doc = spreadsheet do
|
44
|
+
|
45
|
+
definitions do
|
46
|
+
|
47
|
+
set yes: 'Yes'
|
48
|
+
set empty: ''
|
49
|
+
set quantity: 17
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
sheet do
|
54
|
+
|
55
|
+
column value_of(:quantity)
|
56
|
+
column value_of(:empty), :id => :here
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
sheet = doc.current
|
63
|
+
|
64
|
+
sheet.replace_content :here, :yes
|
65
|
+
|
66
|
+
row = sheet.row(2)
|
67
|
+
expect(row[0]).to eq('17')
|
68
|
+
expect(row[1]).to eq('"Yes"')
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
change_by "replacing content with a value" do
|
73
|
+
|
74
|
+
doc = spreadsheet do
|
75
|
+
|
76
|
+
definitions do
|
77
|
+
|
78
|
+
set yes: 'Yes'
|
79
|
+
set empty: ''
|
80
|
+
set quantity: 17
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
sheet do
|
85
|
+
|
86
|
+
column value_of(:quantity)
|
87
|
+
column value_of(:empty), :id => :here
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
sheet = doc.current
|
94
|
+
|
95
|
+
sheet.replace_content :here, 32
|
96
|
+
|
97
|
+
row = sheet.row(2)
|
98
|
+
expect(row[0]).to eq('17')
|
99
|
+
expect(row[1]).to eq("32")
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
change_by "changing and resetting content" do
|
104
|
+
|
105
|
+
doc = spreadsheet do
|
106
|
+
|
107
|
+
definitions do
|
108
|
+
|
109
|
+
set empty: ''
|
110
|
+
set quantity: 17
|
111
|
+
|
112
|
+
function double: quantity * 2
|
113
|
+
function triple: quantity * 3
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
sheet do
|
118
|
+
|
119
|
+
column value_of(:quantity)
|
120
|
+
column value_of(:empty), :id => :here
|
121
|
+
column value_of(:empty), :id => :here_also
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
sheet = doc.current
|
128
|
+
|
129
|
+
row = sheet.row(1)
|
130
|
+
expect(row[0]).to eq('17')
|
131
|
+
expect(row[1]).to eq('""')
|
132
|
+
expect(row[2]).to eq('""')
|
133
|
+
|
134
|
+
sheet.replace_content :here, :double
|
135
|
+
sheet.replace_content :here_also, :triple
|
136
|
+
|
137
|
+
row = sheet.row(2)
|
138
|
+
expect(row[0]).to eq('17')
|
139
|
+
expect(row[1]).to eq("A2*2")
|
140
|
+
expect(row[2]).to eq("A2*3")
|
141
|
+
|
142
|
+
sheet.restore_content
|
143
|
+
|
144
|
+
sheet[:empty] = "Yes"
|
145
|
+
|
146
|
+
row = sheet.row(3)
|
147
|
+
expect(row[0]).to eq('17')
|
148
|
+
expect(row[1]).to eq('"Yes"')
|
149
|
+
expect(row[2]).to eq('"Yes"')
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
change_by "mass-replacing content with values and nil values" do
|
154
|
+
|
155
|
+
doc = spreadsheet do
|
156
|
+
|
157
|
+
definitions do
|
158
|
+
|
159
|
+
set yes: 'Yes'
|
160
|
+
set empty: ''
|
161
|
+
set quantity: 17
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
sheet do
|
166
|
+
|
167
|
+
column value_of(:quantity)
|
168
|
+
column value_of(:empty), :id => :one
|
169
|
+
column value_of(:yes)
|
170
|
+
column value_of(:empty), :id => :two
|
171
|
+
column value_of(:quantity), :id => :three
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
sheet = doc.current
|
178
|
+
|
179
|
+
sheet.replace_and_clear :one => 19, :three => 7
|
180
|
+
|
181
|
+
row = sheet.row(2)
|
182
|
+
expect(row[0]).to eq('')
|
183
|
+
expect(row[1]).to eq("19")
|
184
|
+
expect(row[2]).to eq('')
|
185
|
+
expect(row[3]).to eq('')
|
186
|
+
expect(row[4]).to eq('7')
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|