tla-sbuilder 0.2.2 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +150 -116
  3. data/VERSION +1 -1
  4. data/lib/cli/cli-customer.rb +23 -3
  5. data/lib/cli/cli-pet.rb +66 -12
  6. data/lib/cli/cli-text.rb +127 -8
  7. data/lib/cli/cli.rb +49 -6
  8. data/lib/sbuilder.rb +26 -3
  9. data/lib/sbuilder/constants.rb +165 -6
  10. data/lib/sbuilder/controller.rb +943 -169
  11. data/lib/sbuilder/controller_utils.rb +122 -0
  12. data/lib/sbuilder/default-sbuilder.yaml +38 -44
  13. data/lib/sbuilder/domain.rb +160 -36
  14. data/lib/sbuilder/domain_cardinality.rb +1 -1
  15. data/lib/sbuilder/domain_range.rb +102 -0
  16. data/lib/sbuilder/domain_type.rb +150 -0
  17. data/lib/sbuilder/domain_value.rb +21 -13
  18. data/lib/sbuilder/exception.rb +16 -0
  19. data/lib/sbuilder/extension_loader.rb +67 -686
  20. data/lib/sbuilder/extension_loader_deprecated_step_extensions.rb +711 -0
  21. data/lib/sbuilder/extension_loader_step_generator.rb +876 -0
  22. data/lib/sbuilder/facade/{api_loader.rb → api_loader_facade.rb} +176 -45
  23. data/lib/sbuilder/facade/api_loader_plugin.rb +6 -32
  24. data/lib/sbuilder/facade/api_loader_plugin_mixer.rb +35 -0
  25. data/lib/sbuilder/facade/facade_constants.rb +23 -0
  26. data/lib/sbuilder/facade/loader_plugin_root.rb +56 -0
  27. data/lib/sbuilder/facade/param_set_root.rb +55 -0
  28. data/lib/sbuilder/facade/snippet_loader_facade.rb +600 -0
  29. data/lib/sbuilder/facade/snippet_loader_plugin.rb +76 -0
  30. data/lib/sbuilder/facade/snippet_loader_plugin_mixer.rb +56 -0
  31. data/lib/sbuilder/factory.rb +224 -45
  32. data/lib/sbuilder/model.rb +125 -45
  33. data/lib/sbuilder/mustache/template.rb +107 -58
  34. data/lib/sbuilder/mustache/template_reader.rb +56 -46
  35. data/lib/sbuilder/mustache/template_reader_context.rb +64 -234
  36. data/lib/sbuilder/mustache/template_resolve.rb +103 -0
  37. data/lib/sbuilder/mustache/template_root.rb +71 -0
  38. data/lib/sbuilder/param_set.rb +30 -15
  39. data/lib/sbuilder/param_set_db.rb +1 -1
  40. data/lib/sbuilder/param_set_def.rb +6 -1
  41. data/lib/sbuilder/param_set_def_func.rb +39 -0
  42. data/lib/sbuilder/param_set_if.rb +45 -10
  43. data/lib/sbuilder/param_set_loader_swagger.rb +56 -26
  44. data/lib/sbuilder/param_set_step.rb +1 -1
  45. data/lib/sbuilder/param_sets.rb +2 -1
  46. data/lib/sbuilder/parameter.rb +9 -3
  47. data/lib/sbuilder/parameter_container.rb +1 -1
  48. data/lib/sbuilder/parameter_dom.rb +17 -5
  49. data/lib/sbuilder/parameter_ref.rb +39 -10
  50. data/lib/sbuilder/parser/parser_facade.rb +310 -0
  51. data/lib/sbuilder/resolver.rb +11 -6
  52. data/lib/sbuilder/resolver_loader.rb +1 -1
  53. data/lib/sbuilder/resolver_loader_yaml.rb +1 -1
  54. data/lib/sbuilder/resolver_rule.rb +1 -1
  55. data/lib/sbuilder/resolver_rule_match.rb +10 -4
  56. data/lib/sbuilder/resolver_rule_ref.rb +1 -1
  57. data/lib/sbuilder/setup_loader.rb +49 -0
  58. data/lib/sbuilder/setup_loader_env.rb +478 -0
  59. data/lib/sbuilder/setup_loader_pref.rb +56 -0
  60. data/lib/sbuilder/snippet_loader_simple.rb +125 -0
  61. data/lib/sbuilder/spec/api_loader.rb +34 -0
  62. data/lib/sbuilder/spec/api_loader_facade.rb +169 -32
  63. data/lib/sbuilder/spec/loader_plugin.rb +98 -0
  64. data/lib/sbuilder/spec/snippet_loader.rb +228 -0
  65. data/lib/sbuilder/symbol_table.rb +279 -0
  66. data/lib/utils/{cache_lines.rb → fileio.rb} +8 -1
  67. data/lib/utils/logger.rb +2 -1
  68. data/lib/utils/powerset.rb +13 -0
  69. data/lib/utils/validate.rb +38 -0
  70. data/mustache/cfg/const_def.mustache +2 -0
  71. data/mustache/cfg/macro_run.mustache +1 -4
  72. data/mustache/data-model-header.mustache +1 -0
  73. data/mustache/definition_types.mustache +34 -4
  74. data/mustache/domains.mustache +1 -1
  75. data/mustache/domains_assign.mustache +1 -1
  76. data/mustache/infrastructure-service-init.mustache +1 -1
  77. data/mustache/interface_processes.mustache +16 -10
  78. data/mustache/interface_types.mustache +37 -11
  79. data/mustache/operator-infrastructure-service.mustache +1 -1
  80. data/mustache/resources/schedule_operator_new_step.tla +8 -0
  81. data/mustache/resources/schedule_process_macro.tla +37 -0
  82. data/mustache/resources/schedule_process_procedure.tla +22 -0
  83. data/mustache/resources/schedule_throw.tla +16 -0
  84. data/mustache/setup/domains_run.mustache +8 -2
  85. data/mustache/setup/operator_run.mustache +0 -4
  86. data/mustache/setup/steps_run.mustache +4 -3
  87. data/mustache/setup/steps_run_parameterBind.mustache +14 -6
  88. data/mustache/setup/steps_run_parameterExact.mustache +7 -3
  89. data/mustache/state_type_invariant-infrastructure-service.mustache +9 -4
  90. data/mustache/tla/const_def.mustache +1 -1
  91. data/mustache/tla/macro_run.mustache +7 -1
  92. data/mustache/tla/module_header.mustache +1 -1
  93. data/mustache/tla/operator_run.mustache +8 -5
  94. data/mustache/tla/plc_define_run.mustache +45 -36
  95. data/mustache/tla/plc_run_state.mustache +12 -5
  96. data/src-extend/extend/extend_assumptions.mustache +3 -0
  97. data/src-extend/extend/extend_const.mustache +3 -0
  98. data/src-extend/extend/extend_implementation.mustache +3 -0
  99. data/src-extend/extend/extend_invariant.mustache +3 -0
  100. data/src-extend/extend/extend_macros.mustache +3 -0
  101. data/src-extend/extend/extend_operations.mustache +3 -0
  102. data/src-extend/extend/extend_state.mustache +3 -0
  103. data/src/pet/extend/extend_assumptions.mustache +4 -0
  104. data/src/pet/extend/extend_implementation.mustache +3 -0
  105. data/src/pet/extend/extend_invariant.mustache +3 -0
  106. data/src/pet/extend/extend_macros.mustache +3 -0
  107. data/src/pet/extend/extend_operations.mustache +4 -0
  108. data/src/pet/extend/extend_state.mustache +3 -0
  109. data/src/pet/interface +5 -5
  110. data/src/pet/interface_delete_pet.tla +1 -1
  111. data/src/pet/interface_get_pet.tla +1 -1
  112. data/src/pet/interface_post_pet.tla +4 -2
  113. data/src/pet/interface_post_tag.tla +1 -1
  114. data/src/pet/interface_put_tag.tla +1 -1
  115. data/tla-sbuilder.gemspec +3 -3
  116. metadata +44 -19
  117. data/mustache/name_definition_type.mustache +0 -5
  118. data/mustache/name_domain.mustache +0 -5
  119. data/mustache/name_domain_value.mustache +0 -5
  120. data/mustache/name_domain_value_prefix.mustache +0 -5
  121. data/mustache/name_interface_response_type.mustache +0 -6
  122. data/mustache/name_interface_type.mustache +0 -6
  123. data/mustache/name_parameter_type.mustache +0 -6
  124. data/mustache/name_process.mustache +0 -6
  125. data/mustache/state_type_invariant.mustache +0 -17
  126. data/mustache/state_variables.mustache +0 -20
  127. data/src-extend/extend/extend_invariant_cfg.mustache +0 -7
@@ -0,0 +1,711 @@
1
+ module Sbuilder
2
+
3
+ # To complete runnable code Sbuilder uses setups. A setup
4
+ # is a sequence of interface operations, and their input parameters.
5
+ # In addition, a setup may define domain cardinalities, or domain
6
+ # values. The tools allows defining multiple setups, each setup
7
+ # resulting to a complete, runnable TLA+ code, allowing system model
8
+ # to be run in various environment context
9
+ #
10
+ # User defines sequence of interface operations, and their input
11
+ # parameters using 'step-extension'. A 'step-extension' is an array
12
+ # with
13
+ #
14
+ # - 'interface' property
15
+ #
16
+ # - 'input' property (or 'inputs' array with elements containing 'input' propperty)
17
+ #
18
+ # - 'bindExact' optional boolean property (default false) controlling,
19
+ # how 'input' properties are intepreted. In short 'bindExact' == true results to
20
+ # 'bindSet', and 'bindExact' == false results to 'bindRule' (see description below)
21
+ #
22
+ #
23
+ # In sbuilder, 'setup' 'step-extension' is used to create
24
+ # environment context for the setup as follows:
25
+ #
26
+ # * sbuilder maps 'step-extension' to a template data array 'steps',
27
+ # which can be passed to mustache rendering. Elements in 'steps'
28
+ # array define properties 'interface_operation', 'process',
29
+ # 'bindRule', and 'bindSet'.
30
+ #
31
+ # * sbuilder uses template data array 'steps' to render 'RunSteps' -array
32
+ # in 'setup.tla'. Array elements have the same properties as
33
+ # 'steps' array described above.
34
+ #
35
+ # * In specification code, 'process' -field of the head element in
36
+ # 'RunSteps' array chooses the TLA process to execute. The process
37
+ # models interface operation of the 'interface' property in
38
+ # 'step-extension'. Input to the process is controlled either
39
+ # using 'bindRule' or 'bindSet' -field on the head element. When
40
+ # using 'bindRule', input to the process can be any value in
41
+ # process input domain satistfying 'bindRule' condition. When
42
+ # using 'bindSet', process input is chosen from the elements in
43
+ # the 'bindSet'.
44
+
45
+
46
+ module ExtensionLoader_Deprecated_Step_Extensions
47
+
48
+ @@validStepExtension_required = ["interface" ]
49
+ @@validStepExtension_allowed = @@validStepExtension_required + ["input", "bindExact", "inputs"]
50
+
51
+ @@validStepExtension_input = [ "input" ]
52
+
53
+ # ------------------------------------------------------------------
54
+ # extend steps: recurse 'stepExtensionDef' and create a hash, which
55
+ # can passed to mustache templates
56
+
57
+ def extendStep( stepExtensionDef )
58
+ @logger.info( "#{__method__} stepExtensionDef=#{stepExtensionDef}" )
59
+
60
+ validateProperties( stepExtensionDef, @@validStepExtension_required, @@validStepExtension_allowed )
61
+
62
+ # access paramSet for interface being extenedd && assert that it also exists
63
+ interface = controller.getInterface( stepExtensionDef['interface'] )
64
+
65
+ # create new param-set && configure it
66
+ stepParamSet = controller.createParamSet( Sbuilder::Constants::PARAM_SET_STEPS )
67
+ stepParamSet.setInterfaceReference( interface )
68
+ stepParamSet.setBindExact( stepExtensionDef["bindExact"] )
69
+
70
+ # iterate stepParamSet['inputs']/stepParamSet['input'] to create
71
+ # mustacheTemplateData & add use 'stepParamSet.addInput' add it
72
+ # to 'stepParamSet'
73
+ extendStep_Inputs( interface, stepParamSet, stepExtensionDef )
74
+
75
+ # pass extension for controller - to delegate to model
76
+ controller.extendStep( stepParamSet )
77
+
78
+ end
79
+
80
+ # ------------------------------------------------------------------
81
+ # Implementation
82
+
83
+
84
+ def extendStep_Inputs( interface, stepParamSet, stepExtensionDef )
85
+
86
+ raise ExtensionException.new <<-EOS if stepExtensionDef['input'] && stepExtensionDef['inputs']
87
+ Property 'input' cannot be defined together with 'inputs' in #{stepExtensionDef.to_yaml}
88
+
89
+ Use
90
+
91
+ inputs:
92
+ - input:
93
+ ...
94
+ - input:
95
+ ...
96
+
97
+ or for a single input
98
+
99
+ input:
100
+ ....
101
+ EOS
102
+
103
+ raise ExtensionException.new <<-EOS if stepExtensionDef['inputs'] && stepExtensionDef['bindExact'] != true
104
+ Property 'inputs' cannot be defined unless 'bindExact' true.
105
+
106
+ Error in in #{stepExtensionDef.to_yaml}
107
+
108
+ EOS
109
+
110
+
111
+ # process either 'inputs' or 'input'
112
+ stepExtensionDefInputs = stepExtensionDef['inputs'] ? stepExtensionDef['inputs'] : [ { 'input' => stepExtensionDef['input'] } ]
113
+
114
+ # loop
115
+ stepExtensionDefInputs.each do |stepExtensionDefInput|
116
+
117
+ # single case: create 'mustacheTemplateData' and add it to
118
+ # 'stepParamSet'
119
+ begin
120
+
121
+ validateProperties( stepExtensionDefInput, @@validStepExtension_input )
122
+
123
+ mustacheTemplateData = extendStep_Input( interface, stepParamSet, stepExtensionDefInput['input'] )
124
+ @logger.debug "#{__method__}, mustacheTemplateData=#{mustacheTemplateData.to_yaml}"
125
+ stepParamSet.addInput( mustacheTemplateData )
126
+
127
+ rescue ExtensionException => ee
128
+ msg = "Error #{ee} caused by #{ee.backtrace.join("\n")} when extending interface '#{stepExtensionDef['interface']}'\n\n"
129
+ @logger.error( "#{__method__} #{msg}" )
130
+ raise ExtensionException.new( msg )
131
+ end
132
+
133
+ end # iterate
134
+
135
+ end # def extendStep_Inputs( interface, stepParamSet, stepExtensionDef )
136
+
137
+
138
+ # expand one input to a mustache template
139
+ def extendStep_Input( interface, stepParamSet, stepExtensionDefInputs )
140
+
141
+ # convert input paramters for the interface to datastructure, which
142
+ # can passed to template rendering
143
+ mustacheTemplateData = extendStepInputs( interface, stepExtensionDefInputs )
144
+
145
+ # add missing sub-documents in 'stepExtensionDef'
146
+ expandedStepExtensionDef = expandStepInputForDefaults( interface, stepExtensionDefInputs )
147
+
148
+ # add _default values for parameters not defined
149
+ # NB: expandedStepExtensionDef = stepExtensionDef['input']
150
+ mustacheTemplateData = extendStepDefaults( interface, expandedStepExtensionDef, mustacheTemplateData )
151
+
152
+ # assign unique number to hash element in 'mustacheTemplateData'
153
+ mustacheTemplateData = extendNumbering( mustacheTemplateData )
154
+
155
+ # added to stepParameterSet
156
+ return mustacheTemplateData
157
+
158
+ end
159
+
160
+
161
+ # 'expandedInputs' includes all 'interaface' fields (recurisively)
162
+ def expandStepInputForDefaults( interface, stepExtensionInputs )
163
+
164
+ # make a deeeep copy
165
+ # puts( "expandStepInputForDefaults: stepExtensionInputs=#{stepExtensionInputs.to_yaml}\n\n" )
166
+ expandedInputs = Marshal.load( Marshal.dump( stepExtensionInputs ))
167
+
168
+ # recurse 'interface' and ensure that 'expandedInputs' structure
169
+ # corresponds 'interface' structure
170
+ expandStepInputForDefaultsRecursion( interface, expandedInputs )
171
+
172
+ # puts( "expandStepInputForDefaults: expandedInputs=#{expandedInputs.to_yaml}\n\n" )
173
+ return expandedInputs
174
+
175
+ end
176
+
177
+ # recurs 'paramSet' in synch with 'stepExtensionDef': for each
178
+ # non-leaf paramter (i.e. a a parameter referencing to an other
179
+ # paramset) ensure that 'stepExtensionDef' also this entry
180
+ def expandStepInputForDefaultsRecursion( paramSet, stepExtensionInput )
181
+
182
+ @logger.debug( "#{__method__} paramSet=#{paramSet}, stepExtensionInput=#{stepExtensionInput}, stepExtensionInput.nil?=#{stepExtensionInput.nil?}" )
183
+
184
+ # when no 'input' on 'step-extension'
185
+ stepExtensionInput = {} if stepExtensionInput.nil?
186
+
187
+ paramSet.parameters.each do |parameter|
188
+ if parameter.isReference && stepExtensionInput.is_a?( Hash ) then
189
+ # add empty input parameter (if not defined) for records (ie. Hashe).
190
+ # rows are not expanded (we do not know many we should add)
191
+ # stepExtensionInput[parameter.name] = initRecord( parameter.name ) unless stepExtensionInput.has_key?( parameter.name )
192
+ stepExtensionInput[parameter.name] = {} unless stepExtensionInput.has_key?( parameter.name )
193
+ # recurse referenced 'paramSet' together with 'stepExtensionDef'
194
+ expandStepInputForDefaultsRecursion( parameter.getResolvedReference, stepExtensionInput[parameter.name] )
195
+ end
196
+ end
197
+ end
198
+
199
+
200
+
201
+ # ------------------------------------------------------------------
202
+ # recurse 'stepExtensionInputs' and use it to set default values
203
+ # in 'mustacheTemplateData' structure.
204
+
205
+ def extendStepDefaults( interface, stepExtensionInputs, mustacheTemplateData )
206
+ # puts "extendStepDefaults - starting"
207
+ # recrurse 'stepExtensionInputs' and yield 'rows' && 'records'
208
+ recurseStepDefaults( stepExtensionInputs ) do |type, keys, indexes, stepExtensionInput, defaultValues|
209
+ # puts "type=#{type}, keys=#{keys}, indexes=#{indexes}, stepExtensionInput=#{stepExtensionInput}, defaultValues=#{defaultValues}"
210
+
211
+ # configuration in model
212
+ interfaceParameters = locateParameter( interface, keys )
213
+
214
+ # what we have expanded so far - using 'stepExtensionInputs'
215
+ templateData = locateTemplateData( mustacheTemplateData, keys, indexes )
216
+
217
+ # default value is on the top of the stack
218
+ # defaultValue = defaultValues.last
219
+ # defaultValue is the last non-nil value on stack 'defaultValues'
220
+ defaultValue = defaultValues.reverse.find { |v| !v.nil? }
221
+
222
+ next unless defaultValue
223
+
224
+ # create an entry in 'mustacheTemplateData' if path 'keys'/'indexes' no configuration
225
+ # in 'step-extension' 'input'
226
+ templateData = addTemplateData( mustacheTemplateData, keys, indexes ) unless templateData
227
+ expandDefaults( interfaceParameters, defaultValue, templateData )
228
+
229
+ # case type
230
+ # when "row"
231
+ # next unless defaultValue
232
+ # # access data row 'index'
233
+ # index = indexes.last
234
+ # # expandDefaults( interfaceParameters, defaultValue, templateData['rows'][index] )
235
+ # puts "row-expansion templateData=#{templateData}"
236
+ # expandDefaults( interfaceParameters, defaultValue, templateData )
237
+ # when "record"
238
+ # # possibly no default value defined
239
+ # next unless defaultValue
240
+ # expandDefaults( interfaceParameters, defaultValue, templateData )
241
+ # else
242
+ # # should not happen
243
+ # raise "Unknown type #{type}"
244
+ # end
245
+ end
246
+ # empty is returnet as boolean false
247
+ mustacheTemplateData = false if mustacheTemplateData == {}
248
+
249
+ mustacheTemplateData
250
+ end # def extendStepDefaults
251
+
252
+ # recurse 'stepExtensionInputs' and yield
253
+ # - record|row, [keys], parameterDef
254
+ def recurseStepDefaults( stepExtensionInputs, keys=[], indexes=[], defaultValues=nil, &block )
255
+ # puts "recurseStepDefaults(enter) stepExtensionInputs=#{stepExtensionInputs}"
256
+ return unless stepExtensionInputs
257
+ # init defaultValues
258
+ defaultValues = [ stepExtensionInputs["_default"] ] unless defaultValues
259
+ if !keys.any? then
260
+ # puts "recurseStepDefaults(init) #{stepExtensionInputs}"
261
+ yield "record", keys, indexes, stepExtensionInputs, defaultValues
262
+ end
263
+ stepExtensionInputs.each do |parameterName, parameterDef|
264
+ if parameterDef.is_a?( Array ) then
265
+ parameterDef.each.with_index do |parameterDefRow,index|
266
+ defaultValues.push( parameterDefRow["_default"] )
267
+ # puts "recurseStepDefaults:(array) resolved-defaultValues=#{defaultValues} index=#{index}, for parameterDefRow=#{parameterDefRow}"
268
+ yield "row", keys + [parameterName], indexes + [index], parameterDefRow, defaultValues
269
+ recurseStepDefaults( parameterDefRow, keys + [parameterName], indexes + [index], defaultValues, &block)
270
+ defaultValues.pop
271
+ end
272
+ elsif parameterDef.is_a?( Hash ) then
273
+ defaultValues.push( parameterDef["_default"] )
274
+ # puts "recurseStepDefaults:(hash): resolved-defaultValues=#{defaultValues} keys=#{keys}, for parameterDef=#{parameterDef}"
275
+ yield "record", keys + [parameterName], indexes + [nil], parameterDef, defaultValues
276
+ recurseStepDefaults( parameterDef, keys + [parameterName], indexes + [nil], defaultValues, &block)
277
+ defaultValues.pop
278
+ else
279
+ # puts "recurseStepDefaults:(else): resolved-defaultValues=#{defaultValues} keys=#{keys}, for parameterDef=#{parameterDef}"
280
+ # leaf entry
281
+ end
282
+
283
+ end
284
+
285
+ end # def recurseStepDefaults
286
+
287
+
288
+ # add expanded parameters to 'templateData'
289
+ def expandDefaults( interfaceParameters, defaultValue, templateData )
290
+
291
+ # puts "\nexpandDefaults defaultValue=#{defaultValue} on '#{templateData}' missing from #{interfaceParameters}"
292
+ # return unless templateData
293
+
294
+ # array of 'parameter_name' fields found on templateData
295
+ definedParameters = templateData && templateData['columns'] ? templateData['columns'].map { |column| column[:parameter_name] } : []
296
+
297
+ # select parameters, which are not defined on 'templateData'
298
+ # (i.e. missing from 'definedParameters') append new entries in
299
+ # 'templateData' with 'defaultValue' expanded
300
+
301
+ parametersToBe( interfaceParameters ).select do |param|
302
+
303
+ # puts "expandDefaults param=#{param} of #{param.class}"
304
+ # select for expsion
305
+ # if NOT already defined i.e. NOT in definedParameters
306
+ # if CARDNIALITY defined (ie. paramter_dom object)
307
+
308
+ !definedParameters.include?( param[:parameter_name] ) && !param[:cardinality].nil?
309
+
310
+ end.each do |param|
311
+ # one more added - modify comm
312
+ templateData['columns'].last["_comma"] = ',' if templateData && templateData['columns'] && templateData['columns'].length > 0
313
+
314
+ # puts "added fied #{param[:parameter_name]}"
315
+ # create default entry
316
+ expadedElement = {
317
+ :parameter_name => param[:parameter_name],
318
+ "_comma" => '',
319
+ "rows" => false,
320
+ "columns" => false,
321
+ }
322
+
323
+ expadedElement = expandDefaultValue( expadedElement, defaultValue, param )
324
+ # ensure that 'columns' array exists (espeically case when
325
+ # only _default defined in step extension)
326
+ templateData['columns'] = [] unless templateData && templateData['columns']
327
+ templateData['columns'] << expadedElement
328
+
329
+ end # apped new entries to 'templateData'
330
+
331
+ templateData
332
+
333
+ end
334
+
335
+ # retrurn array of hashes(:parameter_name, :cardinality, :domain_prefix)
336
+ # on an interface parameters
337
+ def parametersToBe( interfaceParameters )
338
+ @logger.debug( "#{__method__} interfaceParameters=#{interfaceParameters}" )
339
+ parameters = interfaceParameters.parameters.map do |parameter|
340
+ # puts "parameter=#{parameter}"
341
+ {
342
+ :parameter_name => parameter.name,
343
+ :cardinality => parameter.respond_to?( :resolvedDomain) ? parameter.resolvedDomain.cardinality : nil,
344
+ # :domain_prefix => parameter.respond_to?( :resolvedDomain) ? parameter.resolvedDomain.domain_prefix : nil,
345
+ :domain_name => parameter.respond_to?( :resolvedDomain) ? parameter.resolvedDomain.domain_name : nil,
346
+ }
347
+
348
+ end
349
+ return parameters
350
+ end
351
+
352
+
353
+ # update :domain_value (whewn 'defaultValue' is String), or :domain_element= #{param[:domain_prefix]}#{:domain_prefix}" otherwise
354
+ def expandDefaultValue( expadedElement, defaultValue, param )
355
+
356
+ # Case 'String'
357
+ return expadedElement.merge( { :domain_value => defaultValue, :domain_element => false }) if defaultValue.is_a?( String )
358
+
359
+ # Case 'Interger'
360
+ # puts "expandDefaultValue param=#{param}"
361
+ if defaultValue.is_a?( Integer ) then
362
+ raise ExtensionException.new <<-EOS if defaultValue > param[:cardinality]
363
+ Cardinality of the default exceeds the cardinality of the parameter domain
364
+
365
+ cardinality of default : #{defaultValue}
366
+ cardinality on domain : #{param[:cardinality]}
367
+ name of parameter : #{param[:parameter_name]}
368
+ domain name : #{param[:domain_name]}
369
+
370
+ EOS
371
+ return expadedElement.merge( { :domain_element => getDomainElement( param, defaultValue ), :domain_value => false } )
372
+ end
373
+
374
+ # else - unknown value
375
+ raise raise ExtensionException.new <<-EOS
376
+ Unknown type #{defaultValue.class} for default #{defaultValue} value encountered.
377
+
378
+ Exteding paramter #{param}
379
+ EOS
380
+ end
381
+
382
+ def getDomainElement2( extededParameter, domainElement )
383
+ # puts( "extededParameter=#{extededParameter}, domainElement=#{domainElement}" )
384
+ return extededParameter.resolvedDomain.domain_entry( domainElement )
385
+ # "#{extededParameter.resolvedDomain.name}_#{domainElement}"
386
+ end
387
+ def getDomainElement( param, defaultValue )
388
+ # ret = "#{param[:domain_prefix]}#{defaultValue}"
389
+ ret = model.getDomain(param[:domain_name]).domain_entry( defaultValue )
390
+ # puts( "getDomainElement: param=#{param}, defaultValue=#{defaultValue} --> #{ret}" )
391
+ return ret
392
+
393
+ end
394
+
395
+ # ------------------------------------------------------------------
396
+ # number elements
397
+
398
+ # assign '_first' element for hash within 'mustacheTemplateData'
399
+ #
400
+ # @param mustacheTemplateData [Hash]
401
+ # @return [Hash] 'mustacheTemplateData' with record elements numbered
402
+ def extendNumbering( mustacheTemplateData )
403
+
404
+ recurseHash( mustacheTemplateData ) do |h,k,v,i|
405
+ v["_index"] = i
406
+ end
407
+ return mustacheTemplateData
408
+ end
409
+
410
+ # recurse hash, and yield 'hash', 'key', 'value', 'index' for each
411
+ # entry within the hash structure, which is_a?(Hash).
412
+
413
+ def recurseHash( hash, &block )
414
+
415
+ hash && hash.each_with_index do |(k,v),index|
416
+ if v.is_a?( Hash ) then
417
+ yield hash, k, v, index
418
+ recurseHash( v, &block)
419
+ elsif v.is_a?( Array ) then
420
+ v.each_with_index do |array_element,i|
421
+ if array_element.is_a?(Hash)
422
+ yield hash, k, array_element, i
423
+ recurseHash( array_element, &block)
424
+ end
425
+ end
426
+ end
427
+ end
428
+ end
429
+
430
+ # ------------------------------------------------------------------
431
+ # recurse 'stepExtensionInputs' configuration and create a hash
432
+ # structure, which can be passed to mustache template
433
+
434
+ def extendStepInputs( interface, stepExtensionInputs )
435
+ @logger.debug( "#{__method__} stepExtensionInputs=#{stepExtensionInputs}" )
436
+
437
+ # recursion uses this as a stack
438
+ subDocumentStack = []
439
+ subDocumentStack.push( {} )
440
+
441
+
442
+ stepExtensionInputs && recurseStepInputs( stepExtensionInputs ) do |act,keys,domainElement,row|
443
+ # puts "#{act}, keys=#{keys},domainElement=#{domainElement}, #{row}"
444
+
445
+ # uses path [keys] in 'interface' to locate parameter to extend
446
+ extededParameter = locateParameter( interface, keys )
447
+ parameterName = keys.last
448
+ next if parameterName[0] == "_"
449
+
450
+ case act
451
+ when "="
452
+ @logger.debug( "#{__method__} extededParameter=#{extededParameter.name}, extededParameter.resolvedDomain=#{extededParameter.resolvedDomain}" )
453
+ input = {
454
+ :parameter_name => parameterName,
455
+ :domain_element => getDomainElement2(extededParameter, domainElement),
456
+ "rows" => false,
457
+ "columns" => false,
458
+ "_comma" => ','
459
+ }
460
+ # create new parameter && add it to the paramSet
461
+ raise ExtensionException.new <<-EOS if domainElement.is_a?( Integer ) && extededParameter.resolvedDomain.cardinality < domainElement
462
+ The cardinality on step exceeds the cardinality of the parameter domain
463
+
464
+ Name of parameter : #{extededParameter.name}
465
+ Name of parameter domain : #{extededParameter.resolvedDomain.name}
466
+ Cardinality of the parameter domain : #{extededParameter.resolvedDomain.cardinality}
467
+ Cardinality defined: #{domainElement}
468
+
469
+ EOS
470
+
471
+ subDocumentStack.last["columns"] = [] unless subDocumentStack.last["columns"]
472
+ subDocumentStack.last["columns"] << input
473
+ when "["
474
+ input = initRecord( parameterName )
475
+ # {
476
+ # :parameter_name => parameterName,
477
+ # "rows" => false,
478
+ # "columns" => false,
479
+ # }
480
+ subDocumentStack.last["columns"] = [] unless subDocumentStack.last["columns"]
481
+ subDocumentStack.last["columns"] << input
482
+ subDocumentStack.push( input )
483
+ when "]"
484
+ # end of sub-record
485
+ input = subDocumentStack.pop
486
+ # last element has no comma
487
+ input['columns'].last["_comma"] = '' if input['columns']
488
+ when "<<"
489
+ if row == 0 then
490
+ # start of array processing = start of row
491
+ input = initRecord( parameterName )
492
+ # {
493
+ # :parameter_name => parameterName,
494
+ # "rows" => false,
495
+ # "columns" => false,
496
+ # }
497
+ subDocumentStack.push( addColumn( subDocumentStack.last, input ))
498
+
499
+ # subDocumentStack.last["columns"] = [] unless subDocumentStack.last["columns"]
500
+ # subDocumentStack.last["columns"] << input
501
+ # subDocumentStack.push( input )
502
+ else
503
+ # start a new row in an array
504
+ input = {
505
+ "rows" => false,
506
+ "columns" => false,
507
+ "_comma" => ",",
508
+ }
509
+ subDocumentStack.push( input )
510
+ end
511
+
512
+ when ">>"
513
+ if row == 0 then
514
+ # end of array
515
+ input = subDocumentStack.pop
516
+ input['rows'].last["_comma"] = '' if input['rows']
517
+ input['columns'].last["_comma"] = '' if input['columns']
518
+
519
+ else
520
+ input = subDocumentStack.pop
521
+ input['rows'].last["_comma"] = '' if input['rows']
522
+ input['columns'].last["_comma"] = '' if input['columns']
523
+ # puts "subDocumentStack=#{subDocumentStack}"
524
+ subDocumentStack.last["rows"] = [] unless subDocumentStack.last["rows"]
525
+ subDocumentStack.last["rows"] << input
526
+ end
527
+ else
528
+ raise "Unknown act #{act}"
529
+ end
530
+ end
531
+
532
+ raise "subDocumentStack should have been empty #{subDocumentStack}" unless subDocumentStack.length == 1
533
+ ret = subDocumentStack.first
534
+
535
+ # last element has no _comma always
536
+ ret['columns'].last["_comma"] = '' if ret['columns']
537
+
538
+ @logger.info( "#{__method__} return=#{ret}" )
539
+ return ret
540
+
541
+ end # def extendStepInputs( interface, stepExtensionInputs )
542
+
543
+ # Recurse 'stepExtensionInputs' (i.e. hash under key 'step-extension' ).
544
+ # During recursion yield:
545
+ # = : reference parameter, domainElement
546
+ # [ : starts hash
547
+ # ] : end hash
548
+ # << : array
549
+ # >> : end array
550
+ def recurseStepInputs( stepExtensionInputs, keys=[], row=0, &block )
551
+ stepExtensionInputs.each do |parameterName, parameterDef|
552
+ begin
553
+ if parameterDef.is_a?( Array ) then
554
+ yield "<<", keys + [parameterName], nil, 0
555
+ parameterDef.each.with_index do |parameterDefRow,index|
556
+ yield "<<", keys + [parameterName], nil, index+1
557
+ recurseStepInputs( parameterDefRow, keys + [parameterName], index+1, &block )
558
+ yield ">>", keys + [parameterName], nil, index+1
559
+ end
560
+ yield ">>", keys + [parameterName], nil, 0
561
+
562
+ elsif parameterDef.is_a?( Hash ) then
563
+ yield "[", keys + [parameterName], nil, row
564
+ recurseStepInputs( parameterDef, keys + [parameterName], row, &block )
565
+ yield "]", keys + [parameterName], nil, row
566
+ else
567
+ # do not process "meta fields" e.g '_default'
568
+ yield "=", keys + [parameterName], parameterDef, row
569
+ end
570
+ rescue SbuilderException => e
571
+ msg = <<-EOS.gsub( /^\s*/, '' )
572
+ Error:
573
+
574
+ #{e.message}
575
+
576
+
577
+ when processing parameter #{parameterName} with definition:
578
+
579
+ #{parameterDef.to_yaml}
580
+
581
+ EOS
582
+ @logger.error( "#{__method__} #{msg}" )
583
+ raise Sbuilder::LoaderException.new, msg, e.backtrace
584
+ end
585
+ end
586
+ end # recurseStepInputs
587
+
588
+
589
+ # uses path [keys] in 'interface' to locate parameter
590
+ def locateParameter( interface, keys )
591
+ extededParameter = interface
592
+ if keys.any?
593
+ extededParameter = keys.inject(interface) { |p,key|
594
+ # _default paramtes not real parameters
595
+ next if key == "_default"
596
+ p = p.locateParameter( key )
597
+ # resolve in model context - possible for paramter_ref
598
+ # p = p.getResolvedReference( controller.model ) if p.respond_to?( :getResolvedReference )
599
+ p = p.getResolvedReference if p.respond_to?( :getResolvedReference )
600
+ p
601
+ }
602
+ @logger.debug( "#{__method__} keys #{keys}->extededParameter=#{extededParameter}" )
603
+ end
604
+ return extededParameter
605
+ end
606
+
607
+ # ensure that templateData element in ['keys'] and ['indexes']
608
+ # exists in 'mustacheTemplateData'.
609
+ def addTemplateData( mustacheTemplateData, keys, indexes )
610
+
611
+ templateData = mustacheTemplateData
612
+ prev = templateData
613
+ keys.each_with_index do |key,i|
614
+ index = indexes[i]
615
+ # puts "addTemplateData: key=#{key}, #{i}, index #{index}"
616
+ templateData = templateData['columns'].select{ |column| column[:parameter_name] == key }.first
617
+ if templateData.nil? then
618
+ templateData = initRecord( key )
619
+ addColumn( prev, templateData )
620
+ # puts "addTemplateData: added #{templateData} for key #{key} --> #{mustacheTemplateData}"
621
+ else
622
+ # puts "addTemplateData: exists #{templateData} for key #{key}"
623
+ end
624
+
625
+ prev = templateData
626
+
627
+ # # puts "data1=#{data}"
628
+ # if !index.nil? then
629
+ # templateData = templateData['rows'][index]
630
+ # # puts "data2=#{data}"
631
+ # end
632
+ end # iterate
633
+
634
+ return templateData
635
+ end
636
+
637
+
638
+ # uses path in [keys] and ['indexes'] to locate data 'mustacheTemplateData'.
639
+ def locateTemplateData( mustacheTemplateData, keys, indexes )
640
+ ret = mustacheTemplateData
641
+ i = 0
642
+ if keys.any?
643
+ ret = keys.inject(ret) { |data,key|
644
+ index = indexes[i]
645
+ i += 1
646
+ # puts "\n\nindex=#{index} of #{indexes}, i=#{i}, key=#{key} on '#{data}'"
647
+ break unless data
648
+ data = data['columns'].select{ |column| column[:parameter_name] == key }.first
649
+ # puts "data1=#{data}"
650
+ if !index.nil? then
651
+ data = data['rows'][index]
652
+ # puts "data2=#{data}"
653
+ end
654
+
655
+ # if index.nil? then
656
+ # else
657
+ # data = data['rows'][index]['columns'].select{ |column| column[:parameter_name] == key }.first
658
+ # end
659
+ data
660
+ }
661
+ end
662
+ return ret
663
+ end
664
+
665
+ # ------------------------------------------------------------------
666
+ # Build a hash structure, which can be passed to mustache template
667
+
668
+ # create record entry
669
+ private
670
+ def initRecord( parameterName )
671
+ {
672
+ :parameter_name => parameterName,
673
+ "rows" => false,
674
+ "columns" => false,
675
+ "_comma" => ",", # assume that comma get removed if -needed
676
+ }
677
+ end
678
+
679
+ def addColumn( columns, column )
680
+ # subDocumentStack.last["columns"] = [] unless subDocumentStack.last["columns"]
681
+ # subDocumentStack.last["columns"] << input
682
+ # subDocumentStack.push( input )
683
+ columns['columns'] = [] unless columns['columns']
684
+
685
+ # la
686
+ columns['columns'].last["_comma"] = ',' if columns['columns'].length > 0
687
+
688
+ # no comma here
689
+ column["_comma"] = ''
690
+
691
+ columns['columns'] << column
692
+ return column
693
+ end
694
+
695
+ # # validate 'defintionHash' all 'required'/only 'allowed' props set
696
+ private def validateProperties( defintionHash, required, allowed=nil )
697
+
698
+ allowed = required unless allowed
699
+
700
+ missingProps = required - defintionHash.keys
701
+ raise ExtensionException.new "Missing properties #{missingProps} in #{defintionHash} - required #{required}" if missingProps.any?
702
+
703
+ invalidProps = defintionHash.keys - allowed
704
+ raise ExtensionException.new "Unknown properties #{invalidProps} in #{defintionHash} - allowed #{allowed}" if invalidProps.any?
705
+
706
+ end
707
+
708
+ end
709
+
710
+ end
711
+