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