rsmart_toolbox 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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +14 -0
- data/Gemfile +20 -0
- data/LICENSE.txt +663 -0
- data/README.md +31 -0
- data/Rakefile +26 -0
- data/lib/rsmart_toolbox.rb +23 -0
- data/lib/rsmart_toolbox/etl.rb +240 -0
- data/lib/rsmart_toolbox/etl/grm.rb +190 -0
- data/lib/rsmart_toolbox/version.rb +19 -0
- data/rsmart_toolbox.gemspec +43 -0
- data/spec/rsmart_toolbox/etl/grm_spec.rb +620 -0
- data/spec/rsmart_toolbox/etl_spec.rb +447 -0
- data/spec/rsmart_toolbox_spec.rb +27 -0
- data/spec/spec_helper.rb +39 -0
- metadata +142 -0
- metadata.gz.sig +2 -0
@@ -0,0 +1,447 @@
|
|
1
|
+
# rSmart client library and command-line tool to help interact with rSmart's cloud APIs.
|
2
|
+
# Copyright (C) 2014 The rSmart Group, Inc.
|
3
|
+
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU Affero General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU Affero General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU Affero General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'spec_helper'
|
18
|
+
require 'rsmart_toolbox/etl'
|
19
|
+
|
20
|
+
ETL = RsmartToolbox::ETL
|
21
|
+
|
22
|
+
RSpec.describe "RsmartToolbox::ETL" do
|
23
|
+
|
24
|
+
describe "#error" do
|
25
|
+
it "it returns a TextParseError when passed a String" do
|
26
|
+
expect(ETL::error("foo")).to be_kind_of TextParseError
|
27
|
+
end
|
28
|
+
|
29
|
+
it "reformats the message with additional context information" do
|
30
|
+
e = ETL::error("foo")
|
31
|
+
expect(e.message).to include "foo"
|
32
|
+
expect(e.message).to match /^ERROR:\s+Line\s+(\d+):\s+.+$/
|
33
|
+
end
|
34
|
+
|
35
|
+
it "supports passing Exceptions and maintains type" do
|
36
|
+
e1 = NotImplementedError.new "foo"
|
37
|
+
e2 = ETL::error(e1)
|
38
|
+
expect(ETL::error(e2)).to be_kind_of NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
it "supports passing Exceptions and maintains message" do
|
42
|
+
e1 = NotImplementedError.new "foo"
|
43
|
+
e2 = ETL::error(e1)
|
44
|
+
expect(e2.message).to include e1.message
|
45
|
+
expect(e2.message).to match /^ERROR:\s+Line\s+(\d+):\s+.+$/
|
46
|
+
end
|
47
|
+
|
48
|
+
it "raises an ArgumentError if passed an unsupported type" do
|
49
|
+
expect { ETL::error("foo".to_i) }.to raise_error(ArgumentError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#warning" do
|
54
|
+
it "it returns a TextParseError when passed a String" do
|
55
|
+
expect(ETL::warning("foo")).to be_kind_of TextParseError
|
56
|
+
end
|
57
|
+
|
58
|
+
it "reformats the message with additional context information" do
|
59
|
+
e = ETL::warning("foo")
|
60
|
+
expect(e.message).to include "foo"
|
61
|
+
expect(e.message).to match /^WARN:\s+Line\s+(\d+):\s+.+$/
|
62
|
+
end
|
63
|
+
|
64
|
+
it "supports passing Exceptions and maintains type" do
|
65
|
+
e1 = NotImplementedError.new "foo"
|
66
|
+
e2 = ETL::warning(e1)
|
67
|
+
expect(ETL::warning(e2)).to be_kind_of NotImplementedError
|
68
|
+
end
|
69
|
+
|
70
|
+
it "supports passing Exceptions and maintains message" do
|
71
|
+
e1 = NotImplementedError.new "foo"
|
72
|
+
e2 = ETL::warning(e1)
|
73
|
+
expect(e2.message).to include e1.message
|
74
|
+
expect(e2.message).to match /^WARN:\s+Line\s+(\d+):\s+.+$/
|
75
|
+
end
|
76
|
+
|
77
|
+
it "raises an ArgumentError if passed an unsupported type" do
|
78
|
+
expect { ETL::warning("foo".to_i) }.to raise_error(ArgumentError)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#valid_value' do
|
83
|
+
it "tests semantic equality against a set of valid values" do
|
84
|
+
expect(ETL.valid_value(1, [1, 2, 3])).to eq(true)
|
85
|
+
expect(ETL.valid_value(2, [1, 2, 3])).to eq(true)
|
86
|
+
expect(ETL.valid_value(3, [1, 2, 3])).to eq(true)
|
87
|
+
expect(ETL.valid_value(1, [2, 3])).to eq(false)
|
88
|
+
expect(ETL.valid_value("1", ["1"])).to eq(true)
|
89
|
+
expect(ETL.valid_value("1", ["2"])).to eq(false)
|
90
|
+
expect(ETL.valid_value("1", [1])).to eq(false)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "provides a case_sensitive option" do
|
94
|
+
expect(ETL.valid_value("one", ["ONE"], case_sensitive: false)).to eq(true)
|
95
|
+
expect(ETL.valid_value("one", ["ONE"], case_sensitive: true)).to eq(false)
|
96
|
+
expect(ETL.valid_value("one", ["one"], case_sensitive: true)).to eq(true)
|
97
|
+
expect(ETL.valid_value("one", ["ONE"], case_sensitive: "foo")).to eq(false)
|
98
|
+
expect(ETL.valid_value("one", ["ONE"])).to eq(false)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "allows for a valid_values that is a regular expression" do
|
102
|
+
expect(ETL.valid_value("word", /^(\w+)$/)).to eq(true)
|
103
|
+
expect(ETL.valid_value("Z", /^(B|A|Z)?$/)).to eq(true)
|
104
|
+
expect(ETL.valid_value("", /^(B|A|Z)?$/)).to eq(true)
|
105
|
+
expect(ETL.valid_value("upper", /^(UPPER)$/i)).to eq(true)
|
106
|
+
|
107
|
+
expect(ETL.valid_value("false", /^(true)$/)).to eq(false)
|
108
|
+
expect(ETL.valid_value("", "^(B|A|Z)+$")).to eq(false)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#parse_boolean" do
|
113
|
+
true_valid_values = ['active', 'a', 'true', 't', 'yes', 'y', '1']
|
114
|
+
false_valid_values = ['inactive', 'i', 'false', 'f', 'no', 'n', '0']
|
115
|
+
|
116
|
+
it "converts all valid, exact case 'true' Strings to true Booleans" do
|
117
|
+
true_valid_values.each do |valid_value|
|
118
|
+
expect(ETL.parse_boolean(valid_value)).to eq(true)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "converts all valid, lowercase 'true' Strings to true Booleans" do
|
123
|
+
true_valid_values.each do |valid_value|
|
124
|
+
expect(ETL.parse_boolean(valid_value.downcase)).to eq(true)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "converts all valid, mixed case 'true' Strings to true Booleans" do
|
129
|
+
true_valid_values.each do |valid_value|
|
130
|
+
expect(ETL.parse_boolean(valid_value.capitalize)).to eq(true)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
it "converts all valid, exact case 'false' Strings to false Booleans" do
|
135
|
+
false_valid_values.each do |valid_value|
|
136
|
+
expect(ETL.parse_boolean(valid_value)).to eq(false)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "converts all valid, lowercase 'false' Strings to false Booleans" do
|
141
|
+
false_valid_values.each do |valid_value|
|
142
|
+
expect(ETL.parse_boolean(valid_value.downcase)).to eq(false)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it "converts all valid, mixed case 'false' Strings to false Booleans" do
|
147
|
+
false_valid_values.each do |valid_value|
|
148
|
+
expect(ETL.parse_boolean(valid_value.capitalize)).to eq(false)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it "handles Booleans in addition to Strings" do
|
153
|
+
expect(ETL.parse_boolean(true)).to eq(true)
|
154
|
+
expect(ETL.parse_boolean(false)).to eq(false)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "converts '' Strings to nil" do
|
158
|
+
expect(ETL.parse_boolean('')).to eq(nil)
|
159
|
+
expect { ETL.parse_boolean('') }.not_to raise_error
|
160
|
+
end
|
161
|
+
|
162
|
+
it "converts nil to nil" do
|
163
|
+
expect(ETL.parse_boolean(nil)).to eq(nil)
|
164
|
+
expect { ETL.parse_boolean(nil) }.not_to raise_error
|
165
|
+
end
|
166
|
+
|
167
|
+
it "throws an Exception when an invalid string is passed" do
|
168
|
+
expect { ETL.parse_boolean("foober") }.to raise_error(TextParseError)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "supports use of the :required option" do
|
172
|
+
expect { ETL.parse_boolean(nil, required: true) }.to raise_error(TextParseError)
|
173
|
+
expect { ETL.parse_boolean(nil, required: false) }.not_to raise_error
|
174
|
+
end
|
175
|
+
|
176
|
+
it "supports use of the :default option" do
|
177
|
+
expect(ETL.parse_boolean("", default: true)).to eq true
|
178
|
+
expect(ETL.parse_boolean(nil, default: true)).to eq true
|
179
|
+
expect(ETL.parse_boolean("", default: "yes")).to eq true
|
180
|
+
expect(ETL.parse_boolean(nil, default: "yes")).to eq true
|
181
|
+
|
182
|
+
expect(ETL.parse_boolean("", default: false)).to eq false
|
183
|
+
expect(ETL.parse_boolean(nil, default: false)).to eq false
|
184
|
+
expect(ETL.parse_boolean("", default: "no")).to eq false
|
185
|
+
expect(ETL.parse_boolean(nil, default: "no")).to eq false
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "#escape_single_quotes" do
|
190
|
+
it "Escapes any single quotes in a String with a '\' character" do
|
191
|
+
expect(ETL.escape_single_quotes("That's it")).to eq("That\\\'s it")
|
192
|
+
expect(ETL.escape_single_quotes("Thats it")).to eq("Thats it")
|
193
|
+
expect(ETL.escape_single_quotes("")).to eq("")
|
194
|
+
expect(ETL.escape_single_quotes(nil)).to eq(nil)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "#parse_string" do
|
199
|
+
it "Escapes any single quotes in a String with a '\' character" do
|
200
|
+
expect(ETL.parse_string("That's it")).to eq("That\\\'s it")
|
201
|
+
expect(ETL.parse_string("Thats it")).to eq("Thats it")
|
202
|
+
end
|
203
|
+
|
204
|
+
it "Returns empty string if nil or an empty string is passed" do
|
205
|
+
expect(ETL.parse_string("")).to eq("")
|
206
|
+
expect(ETL.parse_string(nil)).to eq("")
|
207
|
+
end
|
208
|
+
|
209
|
+
it "Supports a :required option" do
|
210
|
+
expect { ETL.parse_string("", required: true) }.to raise_error(TextParseError)
|
211
|
+
expect { ETL.parse_string(nil, required: true) }.to raise_error(TextParseError)
|
212
|
+
expect { ETL.parse_string("", required: false) }.not_to raise_error
|
213
|
+
expect { ETL.parse_string(nil, required: false) }.not_to raise_error
|
214
|
+
end
|
215
|
+
|
216
|
+
it "Supports a :default option if no String is found" do
|
217
|
+
expect(ETL.parse_string("", default: "foo")).to eq("foo")
|
218
|
+
expect(ETL.parse_string(nil, default: "foo")).to eq("foo")
|
219
|
+
end
|
220
|
+
|
221
|
+
it "Ignores the :default option if a String is found" do
|
222
|
+
expect(ETL.parse_string("bar", default: "foo")).to eq("bar")
|
223
|
+
end
|
224
|
+
|
225
|
+
it "performs a :length validation" do
|
226
|
+
expect { ETL.parse_string("123", length: 1) }.to raise_error(TextParseError)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "allows you to disable :strict :length validation" do
|
230
|
+
expect { ETL.parse_string("123", length: 1, strict: false) }.not_to raise_error
|
231
|
+
end
|
232
|
+
|
233
|
+
it "Supports a :valid_values validation semantics" do
|
234
|
+
expect { ETL.parse_string("123", valid_values: /456/) }.to raise_error(TextParseError)
|
235
|
+
expect { ETL.parse_string("123", valid_values: ['456']) }.to raise_error(TextParseError)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe "#parse_string!" do
|
240
|
+
it "Modifies the insert_str and values_str based on a CSV::Row match" do
|
241
|
+
insert_str = ""; values_str = "";
|
242
|
+
row = CSV::Row.new(['rolodex_id'.to_sym], ['123ABC'], true)
|
243
|
+
ETL.parse_string!(row, insert_str, values_str, name: "ROLODEX_ID")
|
244
|
+
expect(insert_str).to eq "ROLODEX_ID,"
|
245
|
+
expect(values_str).to eq "'123ABC',"
|
246
|
+
end
|
247
|
+
|
248
|
+
it "is not required by default and mutates with an empty string" do
|
249
|
+
insert_str = ""; values_str = "";
|
250
|
+
row = CSV::Row.new(['rolodex_id'.to_sym], [''], true)
|
251
|
+
ETL.parse_string!(row, insert_str, values_str, name: "ROLODEX_ID")
|
252
|
+
expect(insert_str).to eq "ROLODEX_ID,"
|
253
|
+
expect(values_str).to eq "'',"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "#parse_integer" do
|
258
|
+
it "Converts Strings into Integers" do
|
259
|
+
expect(ETL.parse_integer("1")).to eq(1)
|
260
|
+
expect(ETL.parse_integer("0")).to eq(0)
|
261
|
+
end
|
262
|
+
|
263
|
+
it "Supports passing Integers for convenience" do
|
264
|
+
expect(ETL.parse_integer(1)).to eq(1)
|
265
|
+
expect(ETL.parse_integer(0)).to eq(0)
|
266
|
+
end
|
267
|
+
|
268
|
+
it "Returns nil if no value is found instead of 0" do
|
269
|
+
expect(ETL.parse_integer("")).to eq(nil)
|
270
|
+
expect(ETL.parse_integer(nil)).to eq(nil)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "Raises an TextParseError if String is nil or empty and is required" do
|
274
|
+
expect { ETL.parse_integer(nil, required: true) }.to raise_error(TextParseError)
|
275
|
+
expect { ETL.parse_integer("", required: true ) }.to raise_error(TextParseError)
|
276
|
+
end
|
277
|
+
|
278
|
+
it "Supports :default option" do
|
279
|
+
expect(ETL.parse_integer("", default: '1', required: false)).to eq(1)
|
280
|
+
expect(ETL.parse_integer("", default: 2, required: false)).to eq(2)
|
281
|
+
end
|
282
|
+
|
283
|
+
it "Enforces strict length validation to avoid loss of precision" do
|
284
|
+
expect { ETL.parse_integer("22", length: 1, strict: true) }.to raise_error(TextParseError)
|
285
|
+
end
|
286
|
+
|
287
|
+
it "Supports a :valid_values validation semantics" do
|
288
|
+
expect { ETL.parse_integer("123", valid_values: /456/) }.to raise_error(TextParseError)
|
289
|
+
expect { ETL.parse_integer("123", valid_values: ['456']) }.to raise_error(TextParseError)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe "#parse_integer!" do
|
294
|
+
it "Modifies the insert_str and values_str based on a CSV::Row match" do
|
295
|
+
insert_str = ""; values_str = ""; name = "VALID_CLASS_REPORT_FREQ_ID"
|
296
|
+
row = CSV::Row.new([name.downcase.to_sym], ['123'], true)
|
297
|
+
ETL.parse_integer!(row, insert_str, values_str, name: name)
|
298
|
+
expect(insert_str).to eq "#{name},"
|
299
|
+
expect(values_str).to eq "123,"
|
300
|
+
end
|
301
|
+
|
302
|
+
# TODO how to handle mutation of column name and value when nil is returned from parse_integer?
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "#parse_float" do
|
306
|
+
it "Converts Strings into Floats" do
|
307
|
+
expect(ETL.parse_float("1.1")).to eq(1.1)
|
308
|
+
expect(ETL.parse_float("0.0")).to eq(0.0)
|
309
|
+
end
|
310
|
+
|
311
|
+
it "Supports passing floats for convenience" do
|
312
|
+
expect(ETL.parse_float(1.1)).to eq(1.1)
|
313
|
+
expect(ETL.parse_float(0.0)).to eq(0.0)
|
314
|
+
end
|
315
|
+
|
316
|
+
it "Returns nil if no value is found instead of 0" do
|
317
|
+
expect(ETL.parse_float("")).to eq(nil)
|
318
|
+
expect(ETL.parse_float(nil)).to eq(nil)
|
319
|
+
end
|
320
|
+
|
321
|
+
it "Raises an TextParseError if String is nil or empty and is required" do
|
322
|
+
expect { ETL.parse_float(nil, required: true) }.to raise_error(TextParseError)
|
323
|
+
expect { ETL.parse_float("", required: true ) }.to raise_error(TextParseError)
|
324
|
+
end
|
325
|
+
|
326
|
+
it "Supports :default option" do
|
327
|
+
expect(ETL.parse_float("", default: '3.3', required: false)).to eq(3.3)
|
328
|
+
expect(ETL.parse_float("", default: 2.2, required: false)).to eq(2.2)
|
329
|
+
end
|
330
|
+
|
331
|
+
it "Enforces strict length validation to avoid loss of precision" do
|
332
|
+
expect { ETL.parse_float("2.2", length: 1, strict: true) }.to raise_error(TextParseError)
|
333
|
+
end
|
334
|
+
|
335
|
+
it "Supports a :valid_values validation semantics" do
|
336
|
+
expect { ETL.parse_float("123.1", valid_values: /456/) }.to raise_error(TextParseError)
|
337
|
+
expect { ETL.parse_float("123.1", valid_values: ['456']) }.to raise_error(TextParseError)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
describe "#parse_actv_ind!" do
|
342
|
+
it "Modifies the insert_str and values_str based on a CSV::Row match" do
|
343
|
+
insert_str = ""; values_str = "";
|
344
|
+
row = CSV::Row.new(['actv_ind'.to_sym], ['Y'], true)
|
345
|
+
ETL.parse_actv_ind!(row, insert_str, values_str)
|
346
|
+
expect(insert_str).to eq("ACTV_IND,")
|
347
|
+
expect(values_str).to eq("'Y',")
|
348
|
+
end
|
349
|
+
|
350
|
+
it "allows for lowercase input Strings" do
|
351
|
+
insert_str = ""; values_str = "";
|
352
|
+
row = CSV::Row.new(['actv_ind'.to_sym], ['n'], true)
|
353
|
+
ETL.parse_actv_ind!(row, insert_str, values_str)
|
354
|
+
expect(insert_str).to eq("ACTV_IND,")
|
355
|
+
expect(values_str).to eq("'N',")
|
356
|
+
end
|
357
|
+
|
358
|
+
it "Returns a default value of 'Y' and does not raise an TextParseError if nil or empty" do
|
359
|
+
insert_str = ""; values_str = "";
|
360
|
+
row = CSV::Row.new(['actv_ind'.to_sym], [nil], true)
|
361
|
+
expect { ETL.parse_actv_ind!(row, insert_str, values_str) }.not_to raise_error
|
362
|
+
expect(insert_str).to eq("ACTV_IND,")
|
363
|
+
expect(values_str).to eq("'Y',")
|
364
|
+
insert_str = ""; values_str = "";
|
365
|
+
row = CSV::Row.new(['actv_ind'.to_sym], [''], true)
|
366
|
+
expect { ETL.parse_actv_ind!(row, insert_str, values_str) }.not_to raise_error
|
367
|
+
expect(insert_str).to eq("ACTV_IND,")
|
368
|
+
expect(values_str).to eq("'Y',")
|
369
|
+
end
|
370
|
+
|
371
|
+
it "Raises an TextParseError if not a valid 'Y/N' value" do
|
372
|
+
insert_str = ""; values_str = "";
|
373
|
+
row = CSV::Row.new(['actv_ind'.to_sym], ["Q"], true)
|
374
|
+
expect { ETL.parse_actv_ind!(row, insert_str, values_str) }.to raise_error(TextParseError)
|
375
|
+
end
|
376
|
+
|
377
|
+
it "Raises an TextParseError if length exceeds 1 characters" do
|
378
|
+
insert_str = ""; values_str = "";
|
379
|
+
row = CSV::Row.new(['actv_ind'.to_sym], ["x" * 2], true)
|
380
|
+
expect { ETL.parse_actv_ind!(row, insert_str, values_str) }.to raise_error(TextParseError)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
describe "#to_symbol" do
|
385
|
+
it "is downcased" do
|
386
|
+
sample = ETL.to_symbol "ROLODEX_ID"
|
387
|
+
expect(sample).to eq("rolodex_id".to_sym)
|
388
|
+
end
|
389
|
+
|
390
|
+
it "spaces are replaced with underscores" do
|
391
|
+
sample = ETL.to_symbol "ROLODEX_ID "
|
392
|
+
expect(sample).to eq("rolodex_id_".to_sym)
|
393
|
+
end
|
394
|
+
|
395
|
+
it "non-word characters are dropped" do
|
396
|
+
sample = ETL.to_symbol "ROLODEX_ID&"
|
397
|
+
expect(sample).to eq("rolodex_id".to_sym)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
describe "#parse_csv_command_line_options" do
|
402
|
+
executable = "foo.rb"
|
403
|
+
args = ["/some/file.csv"]
|
404
|
+
|
405
|
+
it "sets opt[:sql_filename] to a reasonable default value when none is suppied" do
|
406
|
+
opt = ETL.parse_csv_command_line_options(executable, args)
|
407
|
+
expect(opt[:sql_filename]).to eq("/some/file.sql")
|
408
|
+
end
|
409
|
+
|
410
|
+
it "sets opt[:csv_filename] to args[0] when a value is supplied" do
|
411
|
+
opt = ETL.parse_csv_command_line_options(executable, args)
|
412
|
+
expect(opt[:csv_filename]).to eq("/some/file.csv")
|
413
|
+
end
|
414
|
+
|
415
|
+
it "allows the caller to specify opt[:sql_filename] on the command line" do
|
416
|
+
subject = { sql_filename: "/some/other/file.sql" }
|
417
|
+
opt = ETL.parse_csv_command_line_options(executable, args, subject)
|
418
|
+
expect(opt[:sql_filename]).to eq("/some/other/file.sql")
|
419
|
+
end
|
420
|
+
|
421
|
+
it "provides good default CSV parsing options" do
|
422
|
+
opt = ETL.parse_csv_command_line_options(executable, args)
|
423
|
+
expect(opt[:csv_options][:headers]).to eq(:first_row)
|
424
|
+
expect(opt[:csv_options][:header_converters]).to eq(:symbol)
|
425
|
+
expect(opt[:csv_options][:skip_blanks]).to eq(true)
|
426
|
+
expect(opt[:csv_options][:col_sep]).to eq(',')
|
427
|
+
expect(opt[:csv_options][:quote_char]).to eq('"')
|
428
|
+
end
|
429
|
+
|
430
|
+
it "allows you to override the CSV parsing options" do
|
431
|
+
opt = ETL.parse_csv_command_line_options(executable, args, csv_options: {col_sep: '|', quote_char: '`'})
|
432
|
+
expect(opt[:csv_options][:col_sep]).to eq('|')
|
433
|
+
expect(opt[:csv_options][:quote_char]).to eq('`')
|
434
|
+
end
|
435
|
+
|
436
|
+
it "exits with code 1 if no csv_filename is provided on the command line" do
|
437
|
+
begin
|
438
|
+
ETL.parse_csv_command_line_options(executable, [])
|
439
|
+
rescue SystemExit => e
|
440
|
+
expect(e.status).to eq 1 # exited with failure status
|
441
|
+
else
|
442
|
+
raise "Unexpected Exception found: #{e.class}"
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# rSmart client library and command-line tool to help interact with rSmart's cloud APIs.
|
2
|
+
# Copyright (C) 2014 The rSmart Group, Inc.
|
3
|
+
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU Affero General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU Affero General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU Affero General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'spec_helper'
|
18
|
+
require 'rsmart_toolbox'
|
19
|
+
|
20
|
+
RSpec.describe "RsmartToolbox" do
|
21
|
+
|
22
|
+
it "has a VERSION number" do
|
23
|
+
expect( RsmartToolbox::VERSION ).not_to be_nil
|
24
|
+
expect( RsmartToolbox::VERSION ).to match /^(\d+)\.*(\d+)\.*(\d+)*\.*(\d+)*$/
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|