calco 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +26 -0
  5. data/LICENSE +21 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +360 -0
  8. data/Rakefile +1 -0
  9. data/calco.gemspec +23 -0
  10. data/examples/ages.ods +0 -0
  11. data/examples/compute_cells.rb +61 -0
  12. data/examples/data.csv +8 -0
  13. data/examples/example.rb +97 -0
  14. data/examples/multiplication_tables.ods +0 -0
  15. data/examples/multiplication_tables.rb +73 -0
  16. data/examples/register_function.rb +44 -0
  17. data/examples/using_date_functions.rb +42 -0
  18. data/examples/write_csv.rb +68 -0
  19. data/examples/write_ods.rb +69 -0
  20. data/lib/calco.rb +17 -0
  21. data/lib/calco/core_ext/fixnum.rb +22 -0
  22. data/lib/calco/core_ext/float.rb +22 -0
  23. data/lib/calco/core_ext/range.rb +15 -0
  24. data/lib/calco/core_ext/string.rb +20 -0
  25. data/lib/calco/date_functions.rb +13 -0
  26. data/lib/calco/definition_dsl.rb +127 -0
  27. data/lib/calco/elements/aggregator.rb +17 -0
  28. data/lib/calco/elements/builtin_function.rb +84 -0
  29. data/lib/calco/elements/constant.rb +31 -0
  30. data/lib/calco/elements/current.rb +19 -0
  31. data/lib/calco/elements/element.rb +31 -0
  32. data/lib/calco/elements/empty.rb +9 -0
  33. data/lib/calco/elements/formula.rb +42 -0
  34. data/lib/calco/elements/if.rb +26 -0
  35. data/lib/calco/elements/operation.rb +34 -0
  36. data/lib/calco/elements/operator.rb +17 -0
  37. data/lib/calco/elements/or.rb +26 -0
  38. data/lib/calco/elements/value_extractor.rb +42 -0
  39. data/lib/calco/elements/variable.rb +35 -0
  40. data/lib/calco/engines/calculator_builtin_functions.rb +32 -0
  41. data/lib/calco/engines/csv_engine.rb +80 -0
  42. data/lib/calco/engines/default_engine.rb +140 -0
  43. data/lib/calco/engines/office_engine.rb +263 -0
  44. data/lib/calco/engines/simple_calculator_engine.rb +151 -0
  45. data/lib/calco/math_functions.rb +9 -0
  46. data/lib/calco/sheet.rb +363 -0
  47. data/lib/calco/spreadsheet.rb +172 -0
  48. data/lib/calco/string_functions.rb +9 -0
  49. data/lib/calco/style.rb +15 -0
  50. data/lib/calco/time_functions.rb +12 -0
  51. data/lib/calco/version.rb +3 -0
  52. data/spec/absolute_references_spec.rb +86 -0
  53. data/spec/builtin_functions_spec.rb +161 -0
  54. data/spec/calculator_engine_spec.rb +251 -0
  55. data/spec/conditions_spec.rb +118 -0
  56. data/spec/content_change_spec.rb +190 -0
  57. data/spec/csv_engine_spec.rb +324 -0
  58. data/spec/default_engine_spec.rb +135 -0
  59. data/spec/definitions_spec.rb +65 -0
  60. data/spec/errors_spec.rb +189 -0
  61. data/spec/functions_spec.rb +251 -0
  62. data/spec/header_row_spec.rb +63 -0
  63. data/spec/range_spec.rb +189 -0
  64. data/spec/sheet_selections_spec.rb +49 -0
  65. data/spec/sheet_spec.rb +229 -0
  66. data/spec/smart_types_spec.rb +43 -0
  67. data/spec/spreadsheet_spec.rb +80 -0
  68. data/spec/styles_spec.rb +29 -0
  69. data/spec/variables_spec.rb +41 -0
  70. metadata +158 -0
@@ -0,0 +1,63 @@
1
+ require 'calco'
2
+
3
+ describe 'Header row' do
4
+
5
+ it "row[0] is an empty header/title row by default" do
6
+
7
+ doc = spreadsheet do
8
+
9
+ definitions do
10
+
11
+ set price: 14.4
12
+
13
+ end
14
+
15
+ sheet do
16
+
17
+ column value_of(:price)
18
+
19
+ end
20
+
21
+ end
22
+
23
+ row = doc.row(0)
24
+
25
+ expect(row).to be_empty
26
+
27
+ end
28
+
29
+ it "can be set with titles to columns" do
30
+
31
+ doc = spreadsheet do
32
+
33
+ definitions do
34
+
35
+ set price: 14.4
36
+ set product: 'USB 8Gb'
37
+ set tax: 13
38
+ set quantity: 30
39
+
40
+ function total: price * quantity * (tax / 100 + 1)
41
+
42
+ end
43
+
44
+ sheet do
45
+
46
+ column value_of(:product), title: 'Product'
47
+ column value_of(:price), title: 'Price'
48
+ column value_of(:quantity), title: 'Qty'
49
+ column value_of(:tax), title: 'Tax rate'
50
+ column skip
51
+ column :total, title: 'Total'
52
+
53
+ end
54
+
55
+ end
56
+
57
+ row = doc.row(0)
58
+
59
+ expect(row).to match_array(['Product', 'Price', 'Qty', 'Tax rate', nil, 'Total'])
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,189 @@
1
+ require 'calco/engines/default_engine'
2
+
3
+ describe 'Spreadsheet' do
4
+
5
+ it "accepts column range" do
6
+
7
+ doc = spreadsheet do
8
+
9
+ definitions do
10
+
11
+ set quantity: 17
12
+ function total: sum(quantity[1..-1])
13
+
14
+ end
15
+
16
+ sheet do
17
+
18
+ column value_of(:quantity)
19
+ column :total
20
+
21
+ total.absolute_row = 1
22
+
23
+ end
24
+
25
+ end
26
+
27
+ sheet = doc.current
28
+
29
+ row = sheet.row(1)
30
+ expect(row[0]).to eq('17')
31
+ expect(row[1]).to eq("SUM(A1:A#{Calco::DefaultEngine.row_infinity})")
32
+
33
+ row = sheet.row(10)
34
+ expect(row[0]).to eq('17')
35
+ expect(row[1]).to be_nil
36
+
37
+ end
38
+
39
+ it "accepts range linked to variable" do
40
+
41
+ doc = spreadsheet do
42
+
43
+ definitions do
44
+
45
+ set quantity: 17
46
+ function total: sum(quantity[10..21])
47
+
48
+ end
49
+
50
+ sheet do
51
+
52
+ column value_of(:quantity)
53
+ column :total
54
+
55
+ total.absolute_row = 1
56
+
57
+ end
58
+
59
+ end
60
+
61
+ sheet = doc.current
62
+
63
+ row = sheet.row(1)
64
+ expect(row[0]).to eq('17')
65
+ expect(row[1]).to eq("SUM(A10:A21)")
66
+
67
+ row = sheet.row(10)
68
+ expect(row[0]).to eq('17')
69
+ expect(row[1]).to be_nil
70
+
71
+ end
72
+
73
+ it "accepts aggregate in same column with function" do
74
+
75
+ doc = spreadsheet do
76
+
77
+ definitions do
78
+
79
+ set unit_price: 33
80
+ set quantity: 17
81
+
82
+ function price: unit_price * quantity
83
+ function total: sum(price[(1..-1).as_grouping])
84
+
85
+ end
86
+
87
+ sheet do
88
+
89
+ column value_of(:unit_price), :id => :up
90
+ column value_of(:quantity), :id => :q
91
+ column :price, :id => :p
92
+
93
+ end
94
+
95
+ end
96
+
97
+ sheet = doc.current
98
+
99
+ 5.times {|i| sheet.row(i);}
100
+
101
+ row = sheet.row(6)
102
+ expect(row[0]).to eq('33')
103
+ expect(row[1]).to eq('17')
104
+ expect(row[2]).to eq('A6*B6')
105
+
106
+ sheet.replace_content :up, nil
107
+ sheet.replace_content :q, nil
108
+ sheet.replace_content :p, :total
109
+
110
+ row = sheet.row(7)
111
+ expect(row[2]).to eq("SUM(C1:C6)")
112
+
113
+ expect(row[0]).to eq('')
114
+ expect(row[1]).to eq('')
115
+
116
+ end
117
+
118
+ it "accepts aggregate in same column" do
119
+
120
+ doc = spreadsheet do
121
+
122
+ definitions do
123
+
124
+ set quantity: 17
125
+
126
+ function sub_total: sum(quantity[(1..-1).as_grouping])
127
+
128
+ end
129
+
130
+ sheet do
131
+
132
+ column value_of(:quantity), :id => :here
133
+
134
+ end
135
+
136
+ end
137
+
138
+ sheet = doc.current
139
+
140
+ row = sheet.row(10)
141
+ expect(row[0]).to eq('17')
142
+
143
+ sheet.replace_content :here, :sub_total
144
+
145
+ row = sheet.row(11)
146
+ expect(row[0]).to eq("SUM(A1:A10)")
147
+
148
+ sheet.restore_content
149
+
150
+ row = sheet.row(12)
151
+ expect(row[0]).to eq('17')
152
+
153
+ end
154
+
155
+ it "accepts range reference to following column" do
156
+
157
+ doc = spreadsheet do
158
+
159
+ definitions do
160
+
161
+ set quantity: 17
162
+ function total: sum(quantity[1..-1])
163
+
164
+ end
165
+
166
+ sheet do
167
+
168
+ column :total
169
+ column value_of(:quantity)
170
+
171
+ total.absolute_row = 1
172
+
173
+ end
174
+
175
+ end
176
+
177
+ sheet = doc.current
178
+
179
+ row = sheet.row(1)
180
+ expect(row[0]).to eq("SUM(B1:B#{Calco::DefaultEngine.row_infinity})")
181
+ expect(row[1]).to eq('17')
182
+
183
+ row = sheet.row(10)
184
+ expect(row[0]).to be_nil
185
+ expect(row[1]).to eq('17')
186
+
187
+ end
188
+
189
+ end
@@ -0,0 +1,49 @@
1
+ require 'calco'
2
+
3
+ describe "Sheet selection" do
4
+
5
+ it "a default sheet exists" do
6
+
7
+ doc = spreadsheet do
8
+
9
+ end
10
+
11
+ expect(doc.current).to be_a(Calco::Sheet)
12
+
13
+ end
14
+
15
+ it "last sheet is the current one" do
16
+
17
+ doc = spreadsheet do
18
+
19
+ sheet "a" do
20
+ end
21
+
22
+ sheet "b" do
23
+ end
24
+
25
+ end
26
+
27
+ expect(doc.current).to be(doc.sheet["b"])
28
+
29
+ end
30
+
31
+ it "selecting a sheet makes it current" do
32
+
33
+ doc = spreadsheet do
34
+
35
+ sheet "a" do
36
+ end
37
+
38
+ sheet "b" do
39
+ end
40
+
41
+ end
42
+
43
+ doc.sheet("a")
44
+
45
+ expect(doc.current).to eq(doc.sheet["a"])
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,229 @@
1
+ require 'stringio'
2
+
3
+ require 'calco'
4
+
5
+ describe "Sheet" do
6
+
7
+ it "produces one cell with some assigned literal" do
8
+
9
+ doc = spreadsheet do
10
+ sheet do
11
+ column 77
12
+ end
13
+ end
14
+
15
+ expect(doc.row(1)).to include('77')
16
+
17
+ end
18
+
19
+ it "produces one cell with the value of a variable" do
20
+
21
+ doc = spreadsheet do
22
+
23
+ definitions do
24
+
25
+ set name: 'John'
26
+
27
+ end
28
+
29
+ sheet do
30
+
31
+ column value_of(:name)
32
+
33
+ end
34
+
35
+ end
36
+
37
+ expect(doc.row(1)).to include('"John"')
38
+
39
+ end
40
+
41
+ it "produces a reference to a variable" do
42
+
43
+ doc = spreadsheet do
44
+
45
+ definitions do
46
+
47
+ set name: 'John'
48
+ function john: name
49
+
50
+ end
51
+
52
+ sheet do
53
+
54
+ column value_of(:name)
55
+ column :john
56
+
57
+ end
58
+
59
+ end
60
+
61
+ row = doc.row(1)
62
+
63
+ expect(row[0]).to eq('"John"')
64
+ expect(row[1]).to eq('A1')
65
+
66
+ end
67
+
68
+ it "produces references with row index" do
69
+
70
+ doc = spreadsheet do
71
+
72
+ definitions do
73
+
74
+ set name: 'John'
75
+ function john: name
76
+
77
+ end
78
+
79
+ sheet do
80
+
81
+ column value_of(:name)
82
+ column :john
83
+
84
+ end
85
+
86
+ end
87
+
88
+ sheet = doc.current
89
+
90
+ sheet[:name] = 'James'
91
+
92
+ row = sheet.row(4)
93
+
94
+ expect(row[0]).to eq('"James"')
95
+ expect(row[1]).to eq('A4')
96
+
97
+ end
98
+
99
+ it "accepts references to higher cells" do
100
+
101
+ doc = spreadsheet do
102
+
103
+ definitions do
104
+
105
+ set name: 'John'
106
+ function john: name
107
+
108
+ end
109
+
110
+ sheet do
111
+
112
+ column :john
113
+ column value_of(:name)
114
+
115
+ end
116
+
117
+ end
118
+
119
+ sheet = doc.current
120
+
121
+ sheet[:name] = 'James'
122
+
123
+ row = sheet.row(2)
124
+
125
+ expect(row[0]).to eq('B2')
126
+ expect(row[1]).to eq('"James"')
127
+
128
+ end
129
+
130
+ it "supports blank cells" do
131
+
132
+ doc = spreadsheet do
133
+
134
+ definitions do
135
+
136
+ set name: 'John'
137
+ function john: name
138
+
139
+ end
140
+
141
+ sheet do
142
+
143
+ column value_of(:name)
144
+ column skip
145
+ column :john
146
+
147
+ end
148
+
149
+ end
150
+
151
+ row = doc.row(1)
152
+
153
+ expect(row[0]).to eq('"John"')
154
+ expect(row[1]).to eq('')
155
+ expect(row[2]).to eq('A1')
156
+
157
+ end
158
+
159
+ it "implements #record_assign" do
160
+
161
+ doc = spreadsheet do
162
+
163
+ definitions do
164
+
165
+ set name: ''
166
+ set last_name: ''
167
+
168
+ end
169
+
170
+ sheet do
171
+
172
+ column value_of(:name)
173
+ column value_of(:last_name)
174
+
175
+ end
176
+
177
+ end
178
+
179
+ sheet = doc.current
180
+
181
+ sheet.record_assign :name => "John", :last_name => "Smith"
182
+
183
+ row = doc.row(1)
184
+ expect(row[0]).to eq('"John"')
185
+ expect(row[1]).to eq('"Smith"')
186
+
187
+ sheet.record_assign :name => "James", :last_name => "Black"
188
+
189
+ row = doc.row(2)
190
+ expect(row[0]).to eq('"James"')
191
+ expect(row[1]).to eq('"Black"')
192
+
193
+ end
194
+
195
+ it "implements #write_row with data" do
196
+
197
+ doc = spreadsheet do
198
+
199
+ definitions do
200
+
201
+ set name: ''
202
+ set last_name: ''
203
+
204
+ end
205
+
206
+ sheet do
207
+
208
+ column value_of(:name)
209
+ column value_of(:last_name)
210
+
211
+ end
212
+
213
+ end
214
+
215
+ out = StringIO.new
216
+
217
+ doc.save out do |spreadsheet|
218
+
219
+ sheet = spreadsheet.current
220
+
221
+ sheet.write_row 1, :name => "John", :last_name => "Smith"
222
+
223
+ end
224
+
225
+ expect(out.string).to eq(%|A1: "John"\nB1: "Smith"\n\n|)
226
+
227
+ end
228
+
229
+ end