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