stats_package_syntax_file_generator 1.1.2 → 1.1.3
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 +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 -143
- data/lib/syntax_file/maker_stata.rb +210 -200
- 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
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 979787a0351769e1f7ba25a38e169a8f8a13ae0091e7b5faad0a858b8d501cb7
|
4
|
+
data.tar.gz: 79ba66410caaed9dec8d450ae28137a8328405987985cf9d472ecff8b6929873
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07c600f733ed4bef35d689d3486f51dbac9e914a774ae0e442b1c900a3e74cc8b82afe49bba534a655671d12a217dd9393c0a9611430f4b0ad9da1687ffe425e
|
7
|
+
data.tar.gz: 5f5c54efe960a92abe98f808e2c8829efca9e03eddef54705a5d04c9e339246653a628e5b1bed059cc3153ebd53fee438d14be50866d8543ecef180c47bc5c14
|
@@ -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.3"
|
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
|