stats_package_syntax_file_generator 1.1.6 → 1.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/syntax_file/controller.rb +213 -221
- data/lib/syntax_file/maker.rb +91 -96
- data/lib/syntax_file/maker_rddi.rb +55 -55
- data/lib/syntax_file/maker_sas.rb +204 -205
- data/lib/syntax_file/maker_spss.rb +143 -144
- data/lib/syntax_file/maker_stata.rb +211 -211
- data/lib/syntax_file/maker_sts.rb +120 -120
- data/lib/syntax_file/value.rb +15 -16
- data/lib/syntax_file/variable.rb +40 -41
- data/tests/setup.rb +8 -8
- data/tests/tc_maker.rb +1 -1
- data/tests/tc_maker_stata.rb +1 -1
- metadata +12 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2006302f8dde73e249ee6175ef65b7c41439a14911f515f82f32f6f7dbf5403b
|
4
|
+
data.tar.gz: 4e3eaf39e8537c7e49ac3ea01a37c2e4440d4a07e80d5d8810b103e02257ec8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68dd339e0624e374c374d7c006a7747dcf3800b9661e976594a6f005acdb46a00d972ab1cea25becc3ddb4c945672b3cf15bd3ede23487f21bdd5e0f3685cb5e
|
7
|
+
data.tar.gz: 553b2e56987e63490a7684c00e2bd3a12b4b18027a7fd60343e0d7089efc00f75f6385a49c94cd8b4301fb489b04e469819897e9c3cb815c9baafa1255ac1170
|
@@ -4,284 +4,276 @@
|
|
4
4
|
# https://github.com/mnpopcenter/stats_package_syntax_file_generator
|
5
5
|
|
6
6
|
module SyntaxFile
|
7
|
-
class Controller
|
8
|
-
|
9
|
-
VERSION = "1.1.
|
10
|
-
|
11
|
-
ATTR = {
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
}
|
31
|
-
|
32
|
-
ATTR.each_key do |k|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
7
|
+
class Controller
|
8
|
+
|
9
|
+
VERSION = "1.1.7"
|
10
|
+
|
11
|
+
ATTR = {
|
12
|
+
:project => { :req => false, :rw => 'rw', :def => '', :yaml => true },
|
13
|
+
:caller => { :req => false, :rw => 'rw', :def => '', :yaml => true },
|
14
|
+
:data_dir_name => { :req => false, :rw => 'rw', :def => '.', :yaml => true },
|
15
|
+
:data_file_name => { :req => false, :rw => 'rw', :def => 'DATA_FILE', :yaml => true },
|
16
|
+
:output_formats => { :req => false, :rw => 'rw', :def => nil, :yaml => true },
|
17
|
+
:output_dir_name => { :req => false, :rw => 'rw', :def => '.', :yaml => true },
|
18
|
+
:output_file_stem => { :req => false, :rw => 'rw', :def => '%s', :yaml => true },
|
19
|
+
:output_file_ext => { :req => false, :rw => 'rw', :def => nil, :yaml => true },
|
20
|
+
:output_overwrite => { :req => false, :rw => 'rw', :def => false, :yaml => true },
|
21
|
+
:data_structure => { :req => false, :rw => 'rw', :def => 'rect', :yaml => true },
|
22
|
+
:record_types => { :req => false, :rw => 'rw', :def => nil, :yaml => true },
|
23
|
+
:record_type_var_name => { :req => false, :rw => 'rw', :def => '', :yaml => true },
|
24
|
+
:rectangularize => { :req => false, :rw => 'rw', :def => false, :yaml => true },
|
25
|
+
:all_vars_as_string => { :req => false, :rw => 'rw', :def => false, :yaml => true },
|
26
|
+
:select_vars_by_record_type => { :req => false, :rw => 'rw', :def => false, :yaml => true },
|
27
|
+
:variables => { :req => false, :rw => 'r', :def => nil, :yaml => false },
|
28
|
+
:yaml_files => { :req => false, :rw => 'r', :def => nil, :yaml => false },
|
29
|
+
:output_encoding => { :req => false, :rw => 'r', :def => "iso-8859-1",:yaml => true },
|
30
|
+
}
|
31
|
+
|
32
|
+
ATTR.each_key do |k|
|
33
|
+
attr_reader k if ATTR[k][:rw].include? 'r'
|
34
|
+
attr_writer k if ATTR[k][:rw].include? 'w'
|
35
|
+
end
|
36
36
|
|
37
|
-
def initialize
|
38
|
-
|
39
|
-
raise(ArgumentError, "Missing required parameter: '#{k}'.") if
|
40
|
-
ATTR[k][:req] and not args.has_key?(k)
|
37
|
+
def initialize(args = {})
|
38
|
+
ATTR.each_key { |k|
|
39
|
+
raise(ArgumentError, "Missing required parameter: '#{k}'.") if ATTR[k][:req] and not args.has_key?(k)
|
41
40
|
v = args.has_key?(k) ? args[k] : ATTR[k][:def]
|
42
41
|
instance_variable_set("@#{k}".to_sym, v)
|
43
|
-
|
42
|
+
}
|
44
43
|
|
45
|
-
|
46
|
-
'sas'
|
47
|
-
'spss'
|
44
|
+
@output_file_ext = {
|
45
|
+
'sas' => '.sas',
|
46
|
+
'spss' => '.sps',
|
48
47
|
'stata' => '.do',
|
49
|
-
'sts'
|
50
|
-
'rddi'
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
|
48
|
+
'sts' => '.sts',
|
49
|
+
'rddi' => '.R'
|
50
|
+
} if @output_file_ext.nil?
|
51
|
+
@output_formats = [] if @output_formats.nil?
|
52
|
+
@record_types = [] if @record_types.nil?
|
53
|
+
@variables = [] if @variables.nil?
|
54
|
+
@yaml_files = [] if @yaml_files.nil?
|
55
|
+
read_metadata_from_yaml
|
56
|
+
end
|
59
57
|
|
60
|
-
# Methods to import metadata from YAML files into the Controller object.
|
58
|
+
# Methods to import metadata from YAML files into the Controller object.
|
61
59
|
|
62
|
-
def yaml_files=
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
60
|
+
def yaml_files=(file_names)
|
61
|
+
# Caller can supply a file name or an array of file names.
|
62
|
+
@yaml_files = file_names.to_a
|
63
|
+
read_metadata_from_yaml
|
64
|
+
end
|
67
65
|
|
68
|
-
def read_metadata_from_yaml
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
66
|
+
def read_metadata_from_yaml
|
67
|
+
return if @yaml_files.empty?
|
68
|
+
md = {}
|
69
|
+
@yaml_files.each { |f| md.merge! YAML.load_file(f) }
|
70
|
+
md = symbolize_keys(md)
|
71
|
+
load_yaml_md(md)
|
72
|
+
end
|
75
73
|
|
76
|
-
def load_yaml_md
|
77
|
-
|
78
|
-
|
74
|
+
def load_yaml_md(md)
|
75
|
+
# Uses metadata from yaml to set metadata-related instance variables.
|
76
|
+
ATTR.each_key do |k|
|
79
77
|
next unless md.has_key?(k) and ATTR[k][:yaml]
|
80
78
|
instance_variable_set("@#{k}".to_sym, md[k])
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
79
|
+
end
|
80
|
+
return unless md.has_key?(:variables)
|
81
|
+
@variables = []
|
82
|
+
return unless md[:variables].size > 0
|
83
|
+
md[:variables].each do |md_var|
|
86
84
|
vals = md_var.delete(:values)
|
87
85
|
var = add_variable(md_var)
|
88
86
|
vals.each { |v| var.add_value(v) } unless vals.nil?
|
87
|
+
end
|
89
88
|
end
|
90
|
-
end
|
91
89
|
|
92
|
-
def symbolize_keys
|
93
|
-
|
94
|
-
|
95
|
-
h.inject({}) { |return_hash,(k,v)| return_hash[k.to_sym] = symbolize_keys(v); return_hash }
|
96
|
-
|
90
|
+
def symbolize_keys(h)
|
91
|
+
# Recursively converts hash keys from strings to symbols.
|
92
|
+
if h.instance_of? Hash
|
93
|
+
h.inject({}) { |return_hash, (k, v)| return_hash[k.to_sym] = symbolize_keys(v); return_hash }
|
94
|
+
elsif h.instance_of? Array
|
97
95
|
h.map { |v| symbolize_keys(v) }
|
98
|
-
|
96
|
+
else
|
99
97
|
h
|
98
|
+
end
|
100
99
|
end
|
101
|
-
end
|
102
100
|
|
103
|
-
# Methods to add or get variables.
|
101
|
+
# Methods to add or get variables.
|
104
102
|
|
105
|
-
def add_variable
|
106
|
-
|
107
|
-
|
108
|
-
end
|
103
|
+
def add_variable(args)
|
104
|
+
@variables.push Variable.new(args)
|
105
|
+
@variables[-1]
|
106
|
+
end
|
109
107
|
|
110
|
-
def clear_variables
|
111
|
-
|
112
|
-
end
|
108
|
+
def clear_variables
|
109
|
+
@variables = []
|
110
|
+
end
|
113
111
|
|
114
|
-
def get_var_by_name
|
115
|
-
|
116
|
-
end
|
112
|
+
def get_var_by_name(n)
|
113
|
+
@variables.find { |v| v.name == n }
|
114
|
+
end
|
117
115
|
|
118
|
-
def get_vars_by_record_type
|
119
|
-
|
120
|
-
end
|
116
|
+
def get_vars_by_record_type(rt)
|
117
|
+
@variables.find_all { |v| v.record_type == rt or v.is_common_var }
|
118
|
+
end
|
121
119
|
|
122
|
-
def get_vars_with_var_labels
|
123
|
-
|
124
|
-
end
|
120
|
+
def get_vars_with_var_labels
|
121
|
+
@variables.find_all { |v| v.label.length > 0 }
|
122
|
+
end
|
125
123
|
|
126
|
-
def get_vars_with_values
|
127
|
-
|
124
|
+
def get_vars_with_values
|
125
|
+
@variables.find_all { |var|
|
128
126
|
var.values.size > 0 and
|
129
|
-
|
130
|
-
|
131
|
-
end
|
127
|
+
not var.suppress_labels
|
128
|
+
}
|
129
|
+
end
|
132
130
|
|
133
|
-
def get_big_nums
|
134
|
-
|
131
|
+
def get_big_nums
|
132
|
+
@variables.find_all { |var|
|
135
133
|
var.width > 8 and
|
136
|
-
|
137
|
-
|
138
|
-
end
|
139
|
-
|
140
|
-
|
141
|
-
def record_type_var
|
142
|
-
get_var_by_name(@record_type_var_name)
|
143
|
-
end
|
134
|
+
not var.is_string_var
|
135
|
+
}
|
136
|
+
end
|
144
137
|
|
138
|
+
def record_type_var
|
139
|
+
get_var_by_name(@record_type_var_name)
|
140
|
+
end
|
145
141
|
|
146
|
-
# Methods for adding values to variables.
|
142
|
+
# Methods for adding values to variables.
|
147
143
|
|
148
|
-
def add_value
|
149
|
-
|
150
|
-
|
151
|
-
end
|
152
|
-
|
153
|
-
def new_values (*vals)
|
154
|
-
vals.flatten!
|
155
|
-
vals.map { |v| Value.new(v) }
|
156
|
-
end
|
144
|
+
def add_value(args)
|
145
|
+
@variables[-1].values.push Value.new(args)
|
146
|
+
@variables[-1].values[-1]
|
147
|
+
end
|
157
148
|
|
149
|
+
def new_values(*vals)
|
150
|
+
vals.flatten!
|
151
|
+
vals.map { |v| Value.new(v) }
|
152
|
+
end
|
158
153
|
|
159
|
-
# Methods for record types.
|
154
|
+
# Methods for record types.
|
160
155
|
|
161
|
-
def is_last_record_type
|
162
|
-
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
|
-
def rec_types_except_last
|
167
|
-
r = Array.new(@record_types)
|
168
|
-
r.pop
|
169
|
-
r
|
170
|
-
end
|
156
|
+
def is_last_record_type(rt)
|
157
|
+
return true if @record_types.size > 0 and @record_types[-1] == rt
|
158
|
+
return false
|
159
|
+
end
|
171
160
|
|
161
|
+
def rec_types_except_last
|
162
|
+
r = Array.new(@record_types)
|
163
|
+
r.pop
|
164
|
+
r
|
165
|
+
end
|
172
166
|
|
173
|
-
# Helper methods.
|
167
|
+
# Helper methods.
|
174
168
|
|
175
|
-
def max_var_name_length
|
176
|
-
|
177
|
-
|
178
|
-
end
|
169
|
+
def max_var_name_length
|
170
|
+
return 0 if @variables.empty?
|
171
|
+
@variables.map { |v| v.name.length }.max
|
172
|
+
end
|
179
173
|
|
180
|
-
def max_col_loc_width
|
181
|
-
|
182
|
-
|
183
|
-
end
|
174
|
+
def max_col_loc_width
|
175
|
+
return 0 if @variables.empty?
|
176
|
+
@variables.map { |v| v.end_column.to_s.length }.max
|
177
|
+
end
|
184
178
|
|
185
|
-
def data_file_name_stem
|
186
|
-
|
187
|
-
end
|
179
|
+
def data_file_name_stem
|
180
|
+
File.basename(@data_file_name, '.*')
|
181
|
+
end
|
188
182
|
|
189
|
-
def rec_type_lookup_hash
|
190
|
-
|
191
|
-
end
|
183
|
+
def rec_type_lookup_hash
|
184
|
+
Hash[* @record_types.map { |rt| [rt, 0] }.flatten]
|
185
|
+
end
|
192
186
|
|
193
|
-
def last_column_used
|
194
|
-
|
195
|
-
|
196
|
-
end
|
187
|
+
def last_column_used
|
188
|
+
return 0 if @variables.empty?
|
189
|
+
@variables.map { |v| v.end_column }.max
|
190
|
+
end
|
197
191
|
|
198
|
-
# Output methods.
|
192
|
+
# Output methods.
|
199
193
|
|
200
|
-
def to_s
|
201
|
-
|
202
|
-
end
|
194
|
+
def to_s
|
195
|
+
YAML.dump(self)
|
196
|
+
end
|
203
197
|
|
204
|
-
def generate_syntax_files
|
205
|
-
|
206
|
-
|
207
|
-
end
|
198
|
+
def generate_syntax_files
|
199
|
+
bad_metadata('no output formats') if @output_formats.empty?
|
200
|
+
@output_formats.each { |t| generate_syntax_file(t) }
|
201
|
+
end
|
208
202
|
|
209
|
-
def generate_syntax_file
|
210
|
-
|
211
|
-
|
212
|
-
|
203
|
+
def generate_syntax_file(syntax_type)
|
204
|
+
msg = "output directory does not exist => #{@output_dir_name}"
|
205
|
+
bad_metadata(msg) unless File.directory?(@output_dir_name)
|
206
|
+
file_name = File.join(
|
213
207
|
@output_dir_name,
|
214
208
|
sprintf(@output_file_stem, data_file_name_stem) + @output_file_ext[syntax_type]
|
215
|
-
|
216
|
-
|
209
|
+
)
|
210
|
+
if File.file?(file_name) and not @output_overwrite
|
217
211
|
$stderr.puts "Skipping file that aready exists => #{file_name}."
|
218
|
-
|
212
|
+
else
|
219
213
|
if RUBY_VERSION.start_with? "1.8"
|
220
214
|
File.open(file_name, 'w') { |f| f.puts syntax(syntax_type) }
|
221
215
|
else
|
222
216
|
File.open(file_name, "w:#{self.output_encoding}") { |f|
|
223
217
|
|
224
|
-
|
225
|
-
|
218
|
+
lines = syntax(syntax_type)
|
219
|
+
lines.each do |line|
|
226
220
|
begin
|
227
|
-
|
228
|
-
rescue Exception=>msg
|
229
|
-
|
221
|
+
f.puts line.rstrip.encode(self.output_encoding, line.encoding.to_s, :invalid => :replace, :undef => :replace, :replace => '?')
|
222
|
+
rescue Exception => msg
|
223
|
+
puts "Failed encoding on line #{line} #{msg}"
|
230
224
|
end
|
231
|
-
|
225
|
+
end
|
232
226
|
}
|
233
227
|
end
|
234
228
|
|
229
|
+
end
|
235
230
|
end
|
236
|
-
end
|
237
|
-
|
238
|
-
def syntax (syntax_type)
|
239
|
-
validate_metadata(:minimal => true)
|
240
|
-
modify_metadata
|
241
|
-
validate_metadata
|
242
231
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
232
|
+
def syntax(syntax_type)
|
233
|
+
validate_metadata(:minimal => true)
|
234
|
+
modify_metadata
|
235
|
+
validate_metadata
|
247
236
|
|
237
|
+
maker_class = 'Maker' + syntax_type.upcase
|
238
|
+
syntax_maker = eval(maker_class).new(self, syntax_type)
|
239
|
+
syntax_maker.syntax
|
240
|
+
end
|
248
241
|
|
249
|
-
# Before generating syntax, we need to handle some controller-level
|
250
|
-
# options that require global modification of the metadata.
|
242
|
+
# Before generating syntax, we need to handle some controller-level
|
243
|
+
# options that require global modification of the metadata.
|
251
244
|
|
252
|
-
def modify_metadata
|
253
|
-
|
254
|
-
|
245
|
+
def modify_metadata
|
246
|
+
# Force all variables to be strings.
|
247
|
+
if @all_vars_as_string
|
255
248
|
@variables.each do |var|
|
256
|
-
|
257
|
-
|
258
|
-
|
249
|
+
var.is_string_var = true
|
250
|
+
var.is_double_var = false
|
251
|
+
var.implied_decimals = 0
|
259
252
|
end
|
260
|
-
|
253
|
+
end
|
261
254
|
|
262
|
-
|
263
|
-
|
264
|
-
|
255
|
+
# If the user wants to rectangularize hierarchical data, the
|
256
|
+
# select_vars_by_record_type option is required.
|
257
|
+
@select_vars_by_record_type = true if @rectangularize
|
265
258
|
|
266
|
-
|
267
|
-
|
259
|
+
# Remove any variables not belonging to the declared record types.
|
260
|
+
if @select_vars_by_record_type
|
268
261
|
rt_lookup = rec_type_lookup_hash()
|
269
262
|
@variables = @variables.find_all { |var| var.is_common_var or rt_lookup[var.record_type] }
|
263
|
+
end
|
270
264
|
end
|
271
|
-
end
|
272
|
-
|
273
265
|
|
274
|
-
# Before generating syntax, run a sanity check on the metadata.
|
266
|
+
# Before generating syntax, run a sanity check on the metadata.
|
275
267
|
|
276
|
-
def validate_metadata
|
277
|
-
|
268
|
+
def validate_metadata(check = {})
|
269
|
+
bad_metadata('no variables') if @variables.empty?
|
278
270
|
|
279
|
-
|
271
|
+
if @rectangularize
|
280
272
|
msg = 'the rectangularize option requires data_structure=hier'
|
281
273
|
bad_metadata(msg) unless @data_structure == 'hier'
|
282
|
-
|
274
|
+
end
|
283
275
|
|
284
|
-
|
276
|
+
if @data_structure == 'hier' or @select_vars_by_record_type
|
285
277
|
bad_metadata('no record types') if @record_types.empty?
|
286
278
|
|
287
279
|
msg = 'record types must be unique'
|
@@ -292,34 +284,34 @@ def validate_metadata (check = {})
|
|
292
284
|
|
293
285
|
msg = 'with no common variables, every record type needs at least one variable ('
|
294
286
|
if @variables.find { |var| var.is_common_var }.nil?
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
287
|
+
@record_types.each do |rt|
|
288
|
+
next if get_vars_by_record_type(rt).size > 0
|
289
|
+
bad_metadata(msg + rt + ')')
|
290
|
+
end
|
299
291
|
end
|
300
|
-
|
292
|
+
end
|
301
293
|
|
302
|
-
|
294
|
+
if @data_structure == 'hier'
|
303
295
|
bad_metadata('no record type variable') if record_type_var.nil?
|
304
|
-
|
296
|
+
end
|
305
297
|
|
306
|
-
|
298
|
+
return if check[:minimal]
|
307
299
|
|
308
|
-
|
309
|
-
v.start_column
|
310
|
-
v.width
|
300
|
+
@variables.each do |v|
|
301
|
+
v.start_column = v.start_column.to_i
|
302
|
+
v.width = v.width.to_i
|
311
303
|
v.implied_decimals = v.implied_decimals.to_i
|
312
|
-
bad_metadata("#{v.name}, start_column"
|
313
|
-
bad_metadata("#{v.name}, width"
|
304
|
+
bad_metadata("#{v.name}, start_column") unless v.start_column > 0
|
305
|
+
bad_metadata("#{v.name}, width") unless v.width > 0
|
314
306
|
bad_metadata("#{v.name}, implied_decimals") unless v.implied_decimals >= 0
|
307
|
+
end
|
315
308
|
end
|
316
|
-
end
|
317
309
|
|
318
|
-
def bad_metadata
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
end
|
310
|
+
def bad_metadata(msg)
|
311
|
+
msg = 'Invalid metadata: ' + msg
|
312
|
+
abort(msg) if @caller == 'vb' or @caller == 'dcp'
|
313
|
+
raise(RuntimeError, msg)
|
314
|
+
end
|
323
315
|
|
324
|
-
end
|
316
|
+
end
|
325
317
|
end
|