forj 0.0.48 → 1.0.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,441 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+
19
+ class BaseDefinition
20
+
21
+ private
22
+
23
+ ###################################################
24
+ # Class management Section
25
+ ###################################################
26
+
27
+ # Meta Object declaration structure
28
+ # <Object>
29
+ # :query_mapping List of keypath mapped.
30
+ # <keypath> = <keypath mapped>
31
+ # :lambdas:
32
+ # :create_e: function to call at 'Create' task
33
+ # :delete_e: function to call at 'Delete' task
34
+ # :update_e: function to call at 'Update' task
35
+ # :get_e: function to call at 'Get' task
36
+ # :query_e: function to call at 'Query' task
37
+ # :value_mapping: Define list of Object's key values mapping.
38
+ # <keypath> key value mapping lists
39
+ # <value> = <map> Define the value mapping.
40
+ # :returns
41
+ # <keypath> key value to extract from controller object.
42
+ # :params: Defines CloudData (:data) or CloudObj (:CloudObj) needs by the <Object>
43
+ # :keys: Contains keys in a tree of hash.
44
+ # <keypath>: String. One element (string with : and /) of :list defining the key
45
+ # :type: :data or :CloudObj
46
+ # :for: Array of events which requires the data or CloudObj to work.
47
+ # :mapping: To automatically create a provider hash data mapped (hdata).
48
+ # :required: True if this parameter is required.
49
+ # :extract_from: Array. Build the keypath value from another hParams value.
50
+ # Ex: This example will extract :id from :security_groups object
51
+ # :extract_from => [:security_groups, :id]
52
+ #
53
+ @@meta_obj = {}
54
+
55
+ # meta data are defined in defaults.yaml and loaded in ForjDefault class definition
56
+ # Cloud provider can redefine ForjData defaults and add some extra parameters.
57
+ # To get Application defaults, read defaults.yaml, under :sections:
58
+ @@meta_data = {}
59
+ # <Section>:
60
+ # <Data>: Required. Symbol/String. default: nil
61
+ # => Data name. This symbol must be unique, across sections.
62
+ # :desc: Required. String. default: nil
63
+ # => Description
64
+ # :readonly: Optional. true/false. Default: false
65
+ # => oForjConfig.set() will fail if readonly is true.
66
+ # Can be set, only thanks to oForjConfig.setup()
67
+ # or using private oForjConfig._set()
68
+ # :account_exclusive: Optional. true/false. Default: false
69
+ # => Only oConfig.account_get/set() can handle the value
70
+ # oConfig.set/get cannot.
71
+ # :account: Optional. default: False
72
+ # => setup will configure the account with this <Data>
73
+ # :depends_on:
74
+ # => Identify :data type required to be set before the current one.
75
+ # :validate: Regular expression to validate end user input during setup.
76
+ # :value_mapping: list of values to map as defined by the controller
77
+ # :controller: mapping for get controller value from process values
78
+ # <value> : <map> value map equivalence. See data_value_mapping function
79
+ # :process: mapping for get process value from controller values
80
+ # <value> : <map> value map equivalence. See data_value_mapping function
81
+ # :defaut: Default value
82
+ # :list_values: Defines a list of valid values for the current data.
83
+ # :query_type :controller_call to execute a function defined in the controller object.
84
+ # :process_call to execute a function defined in the process object.
85
+ # :values to get list of values from :values.
86
+ # :object Object to load before calling the function. Only :query_type = :*_call
87
+ # :query_call Symbol. function name to call. Only :query_type = :*_call
88
+ # function must return an Array.
89
+ # :query_params Hash. Controler function parameters. Only :query_type = :*_call
90
+ # :validate :list_strict. valid only if value is one of those listed.
91
+ # :values:
92
+ # to retrieve from.
93
+ # otherwise define simply a list of possible values.
94
+
95
+ # The Generic Process can pre-define some data and value (function predefine_data)
96
+ # The Generic Process (and external framework call) only knows about Generic data.
97
+ # information used
98
+ #
99
+ @@meta_predefined_values = {}
100
+
101
+ # <Data>: Data name
102
+ # :values: List of possible values
103
+ # <Value>: Value Name attached to the data
104
+ # options: Options
105
+ # :desc: Description of that predefine value.
106
+
107
+ @@Context = {
108
+ :oCurrentObj => nil, # Defines the Current Object to manipulate
109
+ :needs_optional => nil, # set optional to true for any next needs declaration
110
+ :ClassProcess => nil # Current Process Class declaration
111
+ }
112
+
113
+ # Available functions for:
114
+ # - BaseDefinition class declaration
115
+ # - Controler (derived from BaseDefinition) class declaration
116
+
117
+ def self.current_process (cProcessClass)
118
+ @@Context[:ClassProcess] = cProcessClass
119
+ end
120
+
121
+ def self.obj_needs_optional
122
+ @@Context[:needs_optional] = true
123
+ end
124
+
125
+ def self.obj_needs_requires
126
+ @@Context[:needs_optional] = false
127
+ end
128
+
129
+ def self.process_default(hOptions)
130
+ aSupportedOptions = [:use_controller]
131
+ unless hOptions.nil?
132
+ hOptions.each_key { | key |
133
+ case key
134
+ when :use_controller
135
+ value = rhGet(hOptions, :use_controller)
136
+ next unless value.is_a?(TrueClass) or value.is_a?(FalseClass)
137
+ rhSet(@@Context, hOptions[key], :options, key)
138
+ else
139
+ raise ForjError.new, "Unknown default process options '%s'. Supported are '%s'" % [key, aSupportedOptions.join(',')]
140
+ end
141
+ }
142
+ end
143
+
144
+ end
145
+
146
+ # Defines Object and connect to functions events
147
+ def self.define_obj(sCloudObj, hParam = nil)
148
+ return nil if not sCloudObj
149
+ return nil if not [String, Symbol].include?(sCloudObj.class)
150
+
151
+ aCaller = caller
152
+ aCaller.pop
153
+
154
+ sCloudObj = sCloudObj.to_sym if sCloudObj.class == String
155
+ @@Context[:oCurrentObj] = sCloudObj
156
+ @@Context[:needs_optional] = false
157
+ @@Context[:needs_setup] = false
158
+ bController = rhGet(@@Context, :options, :use_controller)
159
+ bController = true if bController.nil?
160
+
161
+ if not [Hash].include?(hParam.class)
162
+ if rhExist?(@@meta_obj, sCloudObj) != 1
163
+ raise ForjError.new(), "New undefined object '%s' requires at least one handler. Ex: define_obj :%s, :create_e => myhandler " % [sCloudObj, sCloudObj]
164
+ end
165
+ hParam = {}
166
+ end
167
+
168
+ oCloudObj = rhGet(@@meta_obj, sCloudObj)
169
+ if not oCloudObj
170
+ oCloudObj = {
171
+ :lambdas => {:create_e => nil, :delete_e => nil, :update_e => nil, :get_e => nil, :query_e => nil, :get_attr_e => nil},
172
+ :params => {},
173
+ :options => {:controller => bController },
174
+ :query_mapping => { ":id" => ":id", ":name" => ":name"},
175
+ :returns => {":id" => ":id", ":name" => ":name"}
176
+ }
177
+ msg = nil
178
+ else
179
+ msg = ""
180
+ end
181
+
182
+ sObjectName = "'%s.%s'" % [self.class, sCloudObj]
183
+
184
+ # Checking hParam data
185
+ if not rhGet(hParam, :nohandler)
186
+ hParam.each_key do | key |
187
+ raise ForjError.new(), "'%s' parameter is invalid. Use '%s'" % [key, oCloudObj[:lambdas].keys.join(', ')], aCaller if rhExist?(oCloudObj, :lambdas, key)!= 2
188
+ end
189
+ msg = "%-28s object declared." % [sObjectName] if not msg
190
+ else
191
+ msg = "%-28s meta object declared." % [sObjectName] if not msg
192
+ end
193
+ ForjLib.debug(2, msg) if msg != ""
194
+
195
+ # Setting procs
196
+ rhGet(oCloudObj, :lambdas).each_key { |key|
197
+ next if not hParam.key?(key)
198
+
199
+ if not @@Context[:ClassProcess].instance_methods.include?(hParam[key])
200
+ raise ForjError.new(), "'%s' parameter requires a valid instance method '%s' in the process '%s'." % [key, hParam[key], @@Context[:ClassProcess]], aCaller
201
+ end
202
+ if hParam[key] == :default
203
+ # By default, we use the event name as default function to call.
204
+ # Those function are predefined in ForjController
205
+ # The Provider needs to derive from ForjController and redefine those functions.
206
+ oCloudObj[:lambdas][key] = key
207
+ else
208
+ # If needed, ForjProviver redefined can contains some additionnal functions
209
+ # to call.
210
+ oCloudObj[:lambdas][key] = hParam[key]
211
+ end
212
+ }
213
+ rhSet(@@meta_obj, oCloudObj, sCloudObj)
214
+ end
215
+
216
+ def self.def_query_attribute(key)
217
+ self.query_mapping(key, key)
218
+ end
219
+
220
+ def self.query_mapping(key, map)
221
+ return nil if not [String, Symbol].include?(key.class)
222
+ return nil if not [NilClass, Symbol, String].include?(map.class)
223
+
224
+ aCaller = caller
225
+ aCaller.pop
226
+
227
+ raise ForjError.new(), "%s: No Object defined. Missing define_obj?" % [ self.class], aCaller if @@Context[:oCurrentObj].nil?
228
+
229
+ sCloudObj = @@Context[:oCurrentObj]
230
+ oKeyPath = KeyPath.new(key)
231
+ oMapPath = KeyPath.new(map)
232
+
233
+ @@Context[:oCurrentKey] = oKeyPath
234
+
235
+ rhSet(@@meta_obj, oMapPath.sFullPath, sCloudObj, :query_mapping, oKeyPath.sFullPath)
236
+ end
237
+
238
+ # Available functions exclusively for Controler (derived from BaseDefinition) class declaration
239
+
240
+ # Following functions are related to Object Attributes
241
+ # ----------------------------------------------------
242
+
243
+ # Defines Object CloudData/CloudObj dependency
244
+ def self.obj_needs(sType, sParam, hParams = {})
245
+ return nil if not [String, Symbol].include?(sType.class)
246
+ return nil if not [String, Symbol, Array].include?(sParam.class)
247
+
248
+ hParams = {} if not hParams
249
+
250
+ hParams[:required] = not(@@Context[:needs_optional]) if rhExist?(hParams, :required) != 1
251
+
252
+ aCaller = caller
253
+ aCaller.pop
254
+
255
+ raise ForjError.new(), "%s: No Object defined. Missing define_obj?" % [ self.class], aCaller if @@Context[:oCurrentObj].nil?
256
+
257
+ sCloudObj = @@Context[:oCurrentObj]
258
+
259
+ aForEvents = rhGet(@@meta_obj, sCloudObj, :lambdas).keys
260
+ hParams = hParams.merge({ :for => aForEvents}) if not hParams.key?(:for)
261
+ sType = sType.to_sym if sType.class == String
262
+
263
+
264
+ raise ForjError.new(), "%s: '%s' not declared. Missing define_obj(%s)?" % [ self.class, sCloudObj, sCloudObj], aCaller if rhExist?(@@meta_obj, sCloudObj) != 1
265
+
266
+ oObjTopParam = rhGet(@@meta_obj, sCloudObj, :params)
267
+ if not oObjTopParam.key?(:keys)
268
+ # Initialize top structure
269
+
270
+ oObjTopParam.merge!({ :keys => {} })
271
+ end
272
+
273
+ oKeyPath = KeyPath.new(sParam)
274
+ sKeyAccess = oKeyPath.sFullPath
275
+
276
+ @@Context[:oCurrentKey] = oKeyPath
277
+
278
+ oCloudObjParam = rhGet(oObjTopParam, :keys, sKeyAccess)
279
+ if oCloudObjParam.nil?
280
+ sMsgAction = "New"
281
+ oObjTopParam[:keys][sKeyAccess] = {}
282
+ oCloudObjParam = oObjTopParam[:keys][sKeyAccess]
283
+ else
284
+ sMsgAction = "Upd"
285
+ end
286
+ sObjectName = "'%s.%s'" % [self.class, sCloudObj]
287
+ case sType
288
+ when :data
289
+ if ForjDefault.meta_exist?(sParam)
290
+ ForjLib.debug(2, "%-28s: %s predefined config '%s'." % [sObjectName, sMsgAction, sParam])
291
+ else
292
+ ForjLib.debug(2, "%-28s: %s runtime config '%s'." % [sObjectName, sMsgAction, sParam])
293
+ end
294
+ oCloudObjParam.merge!( hParams.merge({:type => sType}) ) # Merge from predefined params, but ensure type is never updated.
295
+ when :CloudObject
296
+ raise ForjError.new(), "%s: '%s' not declared. Missing define_obj(%s)?" % [self.class, sParam, sParam], aCaller if not @@meta_obj.key?(sParam)
297
+ oCloudObjParam.merge!( hParams.merge({:type => sType}) ) # Merge from predefined params, but ensure type is never updated.
298
+ else
299
+ raise ForjError.new(), "%s: Object parameter type '%s' unknown." % [ self.class, sType ], aCaller
300
+ end
301
+ end
302
+
303
+ def self.attr_value_mapping(value, map)
304
+ return nil if not [String, Symbol].include?(value.class)
305
+ return nil if not [NilClass, Symbol, String].include?(map.class)
306
+
307
+ aCaller = caller
308
+ aCaller.pop
309
+
310
+ sCloudObj = @@Context[:oCurrentObj]
311
+ raise ForjError.new, "attr_value_mapping: mapping '%s' needs object context definition. You need to call define_obj to get the context." % value if sCloudObj.nil?
312
+
313
+ oKeypath = @@Context[:oCurrentKey]
314
+ raise ForjError.new, "attr_value_mapping: mapping '%s' needs object data context definition. You need to call define_obj, then obj_needs to get the context." % value if oKeypath.nil?
315
+
316
+ keypath = oKeypath.sFullPath
317
+ ForjLib.debug(2, "%s-%s: Value mapping definition '%s' => '%s'" % [sCloudObj, oKeypath.to_s, value, map])
318
+ rhSet(@@meta_obj, map, sCloudObj, :value_mapping, keypath, value)
319
+ end
320
+
321
+
322
+ def self.def_attribute(key, options = nil)
323
+ self.get_attr_mapping(key, options)
324
+ #~ self.def_query_attribute(key) unless options and options.key?(:not_queriable) and options[:not_queriable]== true
325
+ end
326
+
327
+ # Function used by the controler to define mapping.
328
+ # By default, any attributes are queriable as well. No need to call
329
+ # query_mapping
330
+ def self.get_attr_mapping(key, map = nil, options = nil)
331
+ return nil if not [String, Symbol].include?(key.class)
332
+ return nil if not [NilClass, Symbol, String, Array].include?(map.class)
333
+
334
+ aCaller = caller
335
+ aCaller.pop
336
+
337
+ raise ForjError.new(), "%s: No Object defined. Missing define_obj?" % [ self.class], aCaller if @@Context[:oCurrentObj].nil?
338
+
339
+ sCloudObj = @@Context[:oCurrentObj]
340
+ oKeyPath = KeyPath.new(key)
341
+ oMapPath = oKeyPath
342
+ oMapPath = KeyPath.new(map) unless map.nil?
343
+
344
+ rhSet(@@meta_obj, oMapPath.sFullPath, sCloudObj, :returns, oKeyPath.sFullPath)
345
+ @@Context[:oCurrentKey] = oKeyPath
346
+ if oMapPath == oKeyPath
347
+ ForjLib::debug(4, "%s: Defining object attribute '%s'" % [sCloudObj, oKeyPath.sFullPath])
348
+ else
349
+ ForjLib::debug(4, "%s: Defining object attribute mapping '%s' => '%s'" % [sCloudObj, oKeyPath.sFullPath, oMapPath.sFullPath])
350
+ end
351
+
352
+ self.query_mapping(key, map) unless options and options.key?(:not_queriable) and options[:not_queriable]== true
353
+ end
354
+
355
+ def self.undefine_attribute(key)
356
+ return nil if not [String, Symbol].include?(key.class)
357
+
358
+ aCaller = caller
359
+ aCaller.pop
360
+
361
+ raise ForjError.new(), "%s: No Object defined. Missing define_obj?" % [ self.class], aCaller if @@Context[:oCurrentObj].nil?
362
+
363
+ sCloudObj = @@Context[:oCurrentObj]
364
+ oKeyPath = KeyPath.new(key)
365
+
366
+ rhSet(@@meta_obj, nil, sCloudObj, :returns, oKeyPath.sFullPath)
367
+ @@Context[:oCurrentKey] = oKeyPath
368
+ ForjLib::debug(4, "%s: Undefining attribute mapping '%s'" % [sCloudObj, oKeyPath.sFullPath])
369
+
370
+ self.query_mapping(key, nil)
371
+ end
372
+
373
+ # Defines/update CloudData parameters
374
+ def self.define_data(sData, hMeta)
375
+ return nil if not sData or not hMeta
376
+ return nil if not [String, Symbol].include?(sData.class)
377
+ return nil if hMeta.class != Hash
378
+
379
+ aCaller = caller
380
+ aCaller.pop
381
+
382
+ sData = sData.to_sym if sData.class == String
383
+ raise ForjError.new(), "%s: Config data '%s' unknown" % [self.class, sData], aCaller if not ForjDefault.meta_exist?(sData)
384
+
385
+ @@Context[:oCurrentData] = sData
386
+
387
+ section = ForjDefault.get_meta_section(sData)
388
+ section = :runtime if section.nil?
389
+
390
+ if rhExist?(@@meta_data, section, sData) == 2
391
+ rhGet(@@meta_data, section, sData).merge!(hMeta)
392
+ else
393
+ rhSet(@@meta_data, hMeta, section, sData)
394
+ end
395
+
396
+ end
397
+
398
+ def self.data_value_mapping(value, map)
399
+ return nil if not [String, Symbol].include?(value.class)
400
+ return nil if not [NilClass, Symbol, String].include?(map.class)
401
+
402
+ aCaller = caller
403
+ aCaller.pop
404
+ sData = @@Context[:oCurrentData]
405
+ raise ForjError.new, "Config data context not set. at least, you need to call define_data before." if sData.nil?
406
+
407
+ section = ForjDefault.get_meta_section(sData)
408
+ section = :runtime if section.nil?
409
+
410
+ ForjLib.debug(2, "%s/%s: Define config data value mapping: '%s' => '%s'" % [section, sData, value, map])
411
+ rhSet(@@meta_data, map, section, sData, :value_mapping, :controller, value)
412
+ rhSet(@@meta_data, value, section, sData, :value_mapping, :process, map)
413
+ end
414
+
415
+ def self.provides(aObjType)
416
+ @aObjType = aObjType
417
+ end
418
+
419
+ def self.defined?(objType)
420
+ @aObjType.include?(objType)
421
+ end
422
+
423
+ # Internal BaseDefinition function
424
+
425
+ def self.predefine_data_value(data, hOptions)
426
+ return nil if self.class != BaseDefinition # Refuse to run if not a BaseDefinition call
427
+ return nil if not [String, Symbol].include?(value.class)
428
+ return nil if not [NilClass, Symbol, String].include?(map.class)
429
+
430
+ aCaller = caller
431
+ aCaller.pop
432
+
433
+ oKeyPath = @@Context[:oCurrentKey]
434
+
435
+ value = {data => {:options => hOptions} }
436
+
437
+ rhSet(@@predefine_data_value, value, oKeyPath.sFullPath, :values)
438
+ end
439
+
440
+
441
+ end