rubywmq 0.3.0

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.
@@ -0,0 +1,40 @@
1
+ ################################################################################
2
+ # Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ################################################################################
16
+
17
+ #
18
+ # NOTE: Do NOT use this file unless the platform being requested is not supported
19
+ # directly by Ruby WMQ. Ruby WMQ already supports automatic dynamic loading on
20
+ # Windows, Solaris and Linux
21
+ #
22
+ require 'mkmf'
23
+ require '../../generate/generate_reason'
24
+ require '../../generate/generate_const'
25
+ require '../../generate/generate_structs'
26
+
27
+ include_path = ''
28
+ unless (RUBY_PLATFORM =~ /win/i) || (RUBY_PLATFORM =~ /solaris/i) || (RUBY_PLATFORM =~ /linux/i)
29
+ include_path = '/opt/mqm/inc'
30
+ dir_config('mqm', include_path, '/opt/mqm/lib')
31
+ have_library('mqic')
32
+
33
+ # Generate Source Files # Could check if not already present
34
+ GenerateReason.generate(include_path+'/')
35
+ GenerateConst.generate(include_path+'/', '../../lib/wmq')
36
+ GenerateStructs.new(include_path+'/', '../../generate').generate
37
+
38
+ have_header('cmqc.h')
39
+ create_makefile('wmq_client')
40
+ end
@@ -0,0 +1,167 @@
1
+ class GenerateConst
2
+
3
+ #Extract Constants from Header files
4
+ def GenerateConst.extract_const(filename, const_prefix, start_exp=nil, end_exp=nil)
5
+ @constants = []
6
+ active = if start_exp then false else true end # Sure there is a better way
7
+ File.open(filename) do |file|
8
+ file.each do |line|
9
+ line.rstrip!
10
+ if line.length > 0 # Skip empty lines
11
+ if active
12
+ break if start_exp && line.match(end_exp)
13
+ # Skip Comment lines, then check for a match
14
+ if (line !~ /^\s*\/\*/) && (match = /\s*#define\s+(#{const_prefix}\w+)[\(\s]+([-\\\w\dx\'\"\s]+)/.match(line))
15
+ @constants << [match[1], match[2]]
16
+ end
17
+ else
18
+ next unless line.match(start_exp)
19
+ active = true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ @constants
25
+ end
26
+
27
+ #Extract MQ Constants from Header files
28
+ def GenerateConst.rb_const(filename, const_prefix, exclude_exp=nil)
29
+ str = ''
30
+ GenerateConst.extract_const(filename, const_prefix).each do |item|
31
+ const, val = item
32
+ next if const.include?('_STRUC_ID')
33
+ next if exclude_exp && const.match(exclude_exp)
34
+ if val.include?('"') || val.include?("'")
35
+ if match = val.match(/(\w+)/)
36
+ val = "'#{match[0]}'"
37
+ else
38
+ val = "''"
39
+ end
40
+ end
41
+ str << " %-30s = #{val}\n" % const
42
+ end
43
+ str
44
+ end
45
+
46
+ def GenerateConst.config(filename, prefix)
47
+ str = "# WMQ::QueueManager execute commands\n"
48
+ str << "execute_commands:\n"
49
+ GenerateConst.extract_const(filename,prefix).each do |item|
50
+ name = item[0].gsub(prefix,'').downcase
51
+ str << " :%-26s " % "#{name}:"
52
+ match = name.match(/\w+?_([\w_]+)/)
53
+ str << ":#{match[1]}" if match
54
+ str << "\n"
55
+ end
56
+ str
57
+ end
58
+
59
+ def GenerateConst.wmq_const(path)
60
+ str = <<END_OF_STRING
61
+ ################################################################################
62
+ # Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc.
63
+ #
64
+ # Licensed under the Apache License, Version 2.0 (the "License");
65
+ # you may not use this file except in compliance with the License.
66
+ # You may obtain a copy of the License at
67
+ #
68
+ # http://www.apache.org/licenses/LICENSE-2.0
69
+ #
70
+ # Unless required by applicable law or agreed to in writing, software
71
+ # distributed under the License is distributed on an "AS IS" BASIS,
72
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
73
+ # See the License for the specific language governing permissions and
74
+ # limitations under the License.
75
+ ################################################################################
76
+ #
77
+ # WARNING: DO NOT MODIFY THIS FILE
78
+ #
79
+ # This file was generated by generate_const.rb.
80
+ #
81
+ ################################################################################
82
+ module WMQ
83
+ END_OF_STRING
84
+ [ ['Connect Options','cmqc.h','MQCNO_',/(VERSION)|(CONN_TAG)|(HANDLE_SHARE)/],
85
+ ['Open Options', 'cmqc.h','MQOO_' ],
86
+ ['Close Options', 'cmqc.h','MQCO_'],
87
+ ['Match Options', 'cmqc.h','MQMO_'],
88
+ ['Message Format Options', 'cmqc.h','MQFMT_',/(_ARRAY)/],
89
+ ['Get Message Options', 'cmqc.h','MQGMO_',/(VERSION)/],
90
+ ['Transport Types', 'cmqxc.h','MQXPT_'] ,
91
+ ['Report Options', 'cmqc.h', 'MQRO_'],
92
+ ['Message Types', 'cmqc.h', 'MQMT_'],
93
+ ['Expiry', 'cmqc.h', 'MQEI_'],
94
+ ['Feedback Values', 'cmqc.h', 'MQFB_'],
95
+ ['Encoding Values', 'cmqc.h', 'MQENC_'],
96
+ ['Coded Character Set Identifiers', 'cmqc.h', 'MQCCSI_'],
97
+ ['Priority', 'cmqc.h', 'MQPRI_'],
98
+ ['Persistence', 'cmqc.h', 'MQPER_'],
99
+ ['Put Application Types', 'cmqc.h', 'MQAT_'],
100
+ ['Message Flags', 'cmqc.h', 'MQMF_'],
101
+ ['Original Length', 'cmqc.h', 'MQOL_'],
102
+ ['Put Message Options', 'cmqc.h', 'MQPMO_',/(VERSION)|(LENGTH)/],
103
+ ['Put Message Record Fields', 'cmqc.h', 'MQPMRF_',/(VERSION)|(LENGTH)/],
104
+ ['Reason Codes', 'cmqc.h','MQRC_'],
105
+ ].each do |item|
106
+ str << "\n# #{item[0]}\n"
107
+ str << GenerateConst.rb_const("#{path}/#{item[1]}", item[2], item[3])
108
+ end
109
+ str << "end\n"
110
+ end
111
+
112
+ def GenerateConst.admin_consts(path)
113
+ str = <<END_OF_STRING
114
+ ################################################################################
115
+ # Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc.
116
+ #
117
+ # Licensed under the Apache License, Version 2.0 (the "License");
118
+ # you may not use this file except in compliance with the License.
119
+ # You may obtain a copy of the License at
120
+ #
121
+ # http://www.apache.org/licenses/LICENSE-2.0
122
+ #
123
+ # Unless required by applicable law or agreed to in writing, software
124
+ # distributed under the License is distributed on an "AS IS" BASIS,
125
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
126
+ # See the License for the specific language governing permissions and
127
+ # limitations under the License.
128
+ ################################################################################
129
+ #
130
+ # WARNING: DO NOT MODIFY THIS FILE
131
+ #
132
+ # This file was generated by generate_const.rb.
133
+ #
134
+ ################################################################################
135
+ module WMQ
136
+ END_OF_STRING
137
+ str << "# Admin constants from cmqc.h\n"
138
+ [ ['Object Types', 'cmqc.h', 'MQOT_'],
139
+ ['Security Identifier Types', 'cmqc.h', 'MQSIDT_'],
140
+ ['Channel Types', 'cmqxc.h', 'MQCHT_'],
141
+ ].each do |item|
142
+ str << "\n# #{item[0]}\n"
143
+ str << GenerateConst.rb_const("#{path}/#{item[1]}", item[2], item[3])
144
+ end
145
+ GenerateConst.extract_const(path+'cmqc.h', 'MQ', /Queue\sAttributes/, /typedef/).each do |item|
146
+ str << " %-30s = #{item[1]}\n" % item[0]
147
+ end
148
+ str << "# Admin constants from cmqcfc.h\n"
149
+ GenerateConst.extract_const(path+'cmqcfc.h', 'MQ', /Parameter\s+Values/, /typedef\s+struct/).each do |item|
150
+ str << " %-30s = #{item[1]}\n" % item[0]
151
+ end
152
+ str << "end\n"
153
+ end
154
+
155
+ def self.generate(path, target_path='')
156
+ File.open(File.join(target_path,'wmq_const_admin.rb'), 'w') {|file| file.write(GenerateConst.admin_consts(path))}
157
+ puts 'Generated wmq_const_admin.rb'
158
+ File.open(File.join(target_path,'wmq_const.rb'), 'w') {|file| file.write(GenerateConst.wmq_const(path))}
159
+ puts 'Generated wmq_const.rb'
160
+ end
161
+ end
162
+
163
+ if $0 == __FILE__
164
+ path = ARGV[0] || raise("Mandatory parameter: 'WebSphere MQ Include path' is missing")
165
+ path = path + '/'
166
+ GenerateConst.generate(path)
167
+ end
@@ -0,0 +1,240 @@
1
+ class GenerateReason
2
+
3
+ #Extract Error Code Constants from Header files
4
+ def GenerateReason.extract_const(filename, const_prefix)
5
+ @constants = []
6
+ File.open(filename) do |file|
7
+ file.each do |line|
8
+ line.rstrip!
9
+ # Skip empty and comment lines
10
+ if line.length > 0 && line !~ /^\s*\/\*/
11
+ if match = /\s*#define\s+(#{const_prefix}\w+)[\(\s]+([-\dx]+)/.match(line)
12
+ @constants << [match[1], match[2]]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ @constants
18
+ end
19
+
20
+ #Extract Error Code Constants from Header files
21
+ # Uses lazy print to collect duplicate values
22
+ def GenerateReason.reason_case(filename, prefix)
23
+ last_rc = nil
24
+ last_reason = nil
25
+ str = ''
26
+ GenerateReason.extract_const(filename,prefix).each do |item|
27
+ if last_rc == item[1]
28
+ str << " case %-30s: return \"#{item[0]} or #{last_reason}[#{item[1]}]\";\n" % item[0]
29
+ last_rc = nil
30
+ last_reason = nil
31
+ else
32
+ str << " case %-30s: return \"#{last_reason}[#{last_rc}]\";\n" % last_reason if last_rc
33
+ last_rc = item[1]
34
+ last_reason = item[0]
35
+ end
36
+ end
37
+ str
38
+ end
39
+
40
+ # The WebSphere MQ header files have several duplicate values,
41
+ # so need to exclude duplicates.
42
+ def GenerateReason.selector_case(filename, prefix, excludes=nil, &block)
43
+ GenerateReason.extract_const(filename,prefix).each do |item|
44
+ next if item[0].include?('FIRST') || item[0].include?('LAST')
45
+ next if excludes && excludes.include?(item[0])
46
+ block.call item
47
+ end
48
+ end
49
+
50
+ def GenerateReason.wmq_reason(path)
51
+ str = <<END_OF_STRING
52
+ /* --------------------------------------------------------------------------
53
+ * Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc.
54
+ *
55
+ * Licensed under the Apache License, Version 2.0 (the "License");
56
+ * you may not use this file except in compliance with the License.
57
+ * You may obtain a copy of the License at
58
+ *
59
+ * http://www.apache.org/licenses/LICENSE-2.0
60
+ *
61
+ * Unless required by applicable law or agreed to in writing, software
62
+ * distributed under the License is distributed on an "AS IS" BASIS,
63
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64
+ * See the License for the specific language governing permissions and
65
+ * limitations under the License.
66
+ * --------------------------------------------------------------------------
67
+ *
68
+ * WARNING: DO NOT MODIFY THIS FILE
69
+ *
70
+ * This file was generated by generate_reason.rb.
71
+ *
72
+ * --------------------------------------------------------------------------*/
73
+
74
+ #include "wmq.h"
75
+
76
+ char* wmq_reason(MQLONG reason_code)
77
+ {
78
+ switch (reason_code)
79
+ {
80
+ END_OF_STRING
81
+
82
+ [['cmqc.h', 'MQRC_'],
83
+ ['cmqcfc.h', 'MQRCCF_']].each do |item|
84
+ str << GenerateReason.reason_case(path+item[0], item[1])
85
+ end
86
+ str << <<END_OF_STRING
87
+ }
88
+ printf("WMQ::wmq_reason Unknown Reason code:%ld\\n",reason_code);
89
+ return "Unknown reason code";
90
+ }
91
+
92
+ END_OF_STRING
93
+
94
+ str_switch = ''
95
+ str_id_init = ''
96
+ # Integer Selectors for Object Attributes
97
+ [['cmqc.h', 'MQIA_'],
98
+ ['cmqcfc.h', 'MQIACF_', ['MQIACF_ERROR_ID',
99
+ 'MQIACF_QUIESCE']],
100
+ ['cmqcfc.h', 'MQIACH_', ['MQIACH_CURRENT_SEQ_NUMBER',
101
+ 'MQIACH_BYTES_RCVD',
102
+ 'MQIACH_BUFFERS_RCVD']],
103
+ ['cmqcfc.h', 'MQIAMO_'],
104
+ ['cmqcfc.h', 'MQIAMO64_'],
105
+ # Integer System Selectors
106
+ ['cmqbc.h', 'MQIASY_'],
107
+ # Character Selectors for Object Attributes
108
+ ['cmqc.h', 'MQCA_'],
109
+ ['cmqcfc.h', 'MQCACF_'],
110
+ ['cmqcfc.h', 'MQCACH_'],
111
+ ['cmqcfc.h', 'MQCAMO_'],
112
+ # Byte String Selectors for Object Attributes
113
+ ['cmqc.h', 'MQBA_'],
114
+ ['cmqcfc.h', 'MQBACF_'],
115
+ # Group Selectors for Object Attributes
116
+ ['cmqcfc.h', 'MQGACF_'] ].each do |item|
117
+ str << "/* Constants #{item[1]}* from #{item[0]} */\n"
118
+ str_switch << " /* Constants #{item[1]}* from #{item[0]} */\n"
119
+ str_id_init << " /* Constants #{item[1]}* from #{item[0]} */\n"
120
+ GenerateReason.selector_case(path+item[0], item[1], item[2]) do |const|
121
+ id = const[0].gsub(item[1],'').downcase
122
+ str_switch << " case %-30s: return ID_#{id};\n" % const[0]
123
+ str_id_init << " ID_%-25s = rb_intern(\"#{id}\");\n" % id
124
+ str << "static ID ID_#{id};\n"
125
+ end
126
+ end
127
+
128
+ str << <<END_OF_STRING
129
+
130
+ void QueueManager_selector_id_init()
131
+ {
132
+ #{str_id_init}
133
+ }
134
+
135
+ ID wmq_selector_id(MQLONG selector)
136
+ {
137
+ switch (selector)
138
+ {
139
+ #{str_switch}
140
+ }
141
+ printf("WMQ::wmq_select_text Unknown Selector:%ld\\n",selector);
142
+ return rb_intern("unknown_selector_code");
143
+ }
144
+
145
+ void wmq_selector(ID selector_id, PMQLONG selector_type, PMQLONG selector)
146
+ {
147
+ *selector_type = MQIT_INTEGER;
148
+ END_OF_STRING
149
+ first = true
150
+ str_if = ''
151
+ str_id_init = ''
152
+ # Integer Selectors for Object Attributes
153
+ [['cmqc.h', 'MQIA_'],
154
+ ['cmqcfc.h', 'MQIACF_', ['MQIACF_ERROR_ID',
155
+ 'MQIACF_QUIESCE']],
156
+ ['cmqcfc.h', 'MQIACH_', ['MQIACH_CURRENT_SEQ_NUMBER',
157
+ 'MQIACH_BYTES_RCVD',
158
+ 'MQIACH_BUFFERS_RCVD']],
159
+ ['cmqcfc.h', 'MQIAMO_'],
160
+ ['cmqcfc.h', 'MQIAMO64_'],
161
+ # Integer System Selectors
162
+ # ['cmqbc.h', 'MQIASY_'],
163
+ ].each do |item|
164
+ str << " /* Consteants #{item[1]}* from #{item[0]} */\n"
165
+ GenerateReason.selector_case(path+item[0], item[1], item[2]) do |const|
166
+ str << " if(selector_id == %-32s{ *selector = #{const[0]}; return;}\n" % "ID_#{const[0].gsub(item[1],'').downcase})"
167
+ end
168
+ end
169
+ str << "\n *selector_type = MQIT_STRING;\n\n"
170
+ # Character Selectors for Object Attributes
171
+ [['cmqc.h', 'MQCA_'],
172
+ ['cmqcfc.h', 'MQCACF_'],
173
+ ['cmqcfc.h', 'MQCACH_'],
174
+ ['cmqcfc.h', 'MQCAMO_'],
175
+ # Byte String Selectors for Object Attributes
176
+ # ['cmqc.h', 'MQBA_'],
177
+ # ['cmqcfc.h', 'MQBACF_'],
178
+ # Group Selectors for Object Attributes
179
+ # ['cmqcfc.h', 'MQGACF_']
180
+ ].each do |item|
181
+ str << " /* Constants #{item[1]}* from #{item[0]} */\n"
182
+ GenerateReason.selector_case(path+item[0], item[1], item[2]) do |const|
183
+ str << " if(selector_id == %-32s{ *selector = #{const[0]}; return;}\n" % "ID_#{const[0].gsub(item[1],'').downcase})"
184
+ end
185
+ end
186
+
187
+ str << <<END_OF_STRING
188
+
189
+ rb_raise(rb_eArgError, "WMQ::QueueManager#execute [wmq_selector] Unknown selector supplied");
190
+ }
191
+
192
+ END_OF_STRING
193
+ first = true
194
+ str_if = ''
195
+ str_id_init = ''
196
+ GenerateReason.selector_case(path+'cmqcfc.h', 'MQCMD_') do |const|
197
+ if first
198
+ first = false
199
+ str_if << " "
200
+ else
201
+ str_if << " else "
202
+ end
203
+ id = const[0].gsub('MQCMD_','').downcase
204
+ str_id_init << " ID_%-25s = rb_intern(\"#{id}\");\n" % id
205
+ str_if << "if(command_id == %-28s{ command = #{const[0]}; }\n" % "ID_#{const[0].gsub('MQCMD_','').downcase})"
206
+ str << "static ID ID_#{id};\n"
207
+ end
208
+ str << <<END_OF_STRING
209
+
210
+ void QueueManager_command_id_init()
211
+ {
212
+ #{str_id_init}
213
+ }
214
+
215
+ MQLONG wmq_command_lookup(ID command_id)
216
+ {
217
+ MQLONG command = 0;
218
+ #{str_if}
219
+ else
220
+ {
221
+ rb_raise(rb_eArgError, "WMQ::QueueManager#execute [wmq_command_lookup] Unknown command supplied");
222
+ }
223
+
224
+ return command;
225
+ }
226
+ END_OF_STRING
227
+ str
228
+ end
229
+
230
+ def self.generate(path)
231
+ File.open('wmq_reason.c', 'w') {|file| file.write(GenerateReason.wmq_reason(path))}
232
+ puts 'Generated wmq_reason.c'
233
+ end
234
+ end
235
+
236
+ if $0 == __FILE__
237
+ path = ARGV[0] || raise("Mandatory parameter: 'WebSphere MQ Include path' is missing")
238
+ path = path + '/'
239
+ GenerateReason.generate(path)
240
+ end
@@ -0,0 +1,99 @@
1
+ ################################################################################
2
+ # Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ################################################################################
16
+ require 'erb'
17
+
18
+ class GenerateStructs
19
+
20
+ @@field_ignore_list = [ 'StrucId', 'Version' , 'StrucLength']
21
+
22
+ # Store path to WebSphere MQ Structures
23
+ def initialize(wmq_includepath, templates_path='.')
24
+ @path = wmq_includepath
25
+ @templates_path = templates_path
26
+ end
27
+
28
+ def extract_struct (filename, struct_name)
29
+ properties_list = []
30
+ line_number = 0
31
+ group_name = nil
32
+ found = false
33
+ File.open(filename) do |file|
34
+ file.each do |line|
35
+ line_number += 1
36
+ line.rstrip!
37
+ # Skip empty and comment lines
38
+ if line.length > 0 && line !~ /^\s*\/\*/
39
+ if !found
40
+ found = true if line =~ /^\s*struct\s*tag#{struct_name}/
41
+ else
42
+ return(properties_list) if line =~ /^\s*\};/
43
+ #puts line
44
+ match = /\s*(MQ\w+)\s+(\w+);/.match(line)
45
+ if match
46
+ type = match[1]
47
+ element = match[2]
48
+ properties_list.push([type, element])
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ properties_list
55
+ end
56
+
57
+ def rubyize_name(name)
58
+ name.gsub(/::/, '/').
59
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
60
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
61
+ tr("-", "_").
62
+ downcase
63
+ end
64
+
65
+ def self.test_rubyize_name
66
+ test = self.new
67
+ [['a', 'a'],
68
+ ['A', 'a'],
69
+ ['Aa', 'aa'],
70
+ ['AA', 'aa'],
71
+ ['AaA', 'aa_a'],
72
+ ['MyFieldName', 'my_field_name'],
73
+ ['MMyFieldNName', 'm_my_field_n_name'],
74
+ ['ABCdefGHIjKlMnOPQrSTuvwxYz', 'ab_cdef_gh_ij_kl_mn_op_qr_s_tuvwx_yz']
75
+ ].each do |item|
76
+ str = test.rubyize_name(item[0])
77
+ raise("rubyize_name('#{item[0]}') == #{str} != '#{item[1]})") if str != item[1]
78
+ end
79
+ end
80
+
81
+ def generate_structs(erb)
82
+ erb.result(binding)
83
+ end
84
+
85
+ def generate(target_filename = 'wmq_structs.c')
86
+ erb = nil
87
+ File.open(@templates_path+'/wmq_structs.erb') { |file| erb = ERB.new(file.read) }
88
+ File.open(target_filename, 'w') {|file| file.write(generate_structs(erb)) }
89
+ puts "Generated #{target_filename}"
90
+ end
91
+ end
92
+
93
+ # TODO Auto Daisy-Chain Headers: Format, CodedCharSetId, Encoding
94
+ # TODO During Deblock validate that data size left at least matches struct size
95
+ if $0 == __FILE__
96
+ path = ARGV[0] || raise("Mandatory parameter: 'WebSphere MQ Include path' is missing")
97
+ path = path + '/'
98
+ GenerateStructs.new(path, 'generate').generate
99
+ end