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