sbuilder-al 0.0.8

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6f8c4a390b98790cf7eebeae8c39af57fff98657
4
+ data.tar.gz: f89df2d19fb85e6f1ccdbb6c19caa8ad38a87f61
5
+ SHA512:
6
+ metadata.gz: 69fe015460b662fc1e07f7b7e6cf6d308dcf4bc0062243b1cabed7e22a96774d5556f29e0a83e5c73022762d552e261e7c23a36fa6c026e5d827dceaa30da10b
7
+ data.tar.gz: 30d6f16c6884da322c0cd63b62b241d2c8c3b5687ebcd39c43b20136704e10fe55a91b45b9813bfddeebc0e17e4766c575425e6f8574fa43f633a237bb66bfb7
data/README.org ADDED
@@ -0,0 +1,217 @@
1
+ * =sbuilder-al=: an API Language Interface to =tla-sbuilder= -Tool
2
+
3
+ =tla-sbuilder= is tool to generate runnable formal models in [[http://research.microsoft.com/en-us/um/people/lamport/tla/book.html][TLA+
4
+ language]] for [[https://github.com/jarjuk/tla-sbuilder#TARGET-SYSTEM][business IT systems]]. A formal model can be [[https://en.wikipedia.org/wiki/Model_checking][model
5
+ checked]] using [[http://research.microsoft.com/en-us/um/people/lamport/tla/tools.html][TLA+ Tools]].
6
+
7
+ =sbuillder-al= defines an [[design/al-language-bnf.org][API Language]] (AL), which it maps to [[https://github.com/jarjuk/tla-sbuilder#API_LOADER][API
8
+ Loader]] and [[https://github.com/jarjuk/tla-sbuilder#SNIPPET_LOADER][Snippet Loader]] extension points in =tla-sbuilder=.
9
+
10
+ Normally, there is no need to install =sbuilder-al= separately
11
+ because =sbuilder-al= is included as dependency for higher level
12
+ interfaces. For example, [[https://github.com/jarjuk/sbuilder-eth][sbuilder-eth]] GEM, Ethereum Solidity
13
+ interface to =tla-sbuilder=, uses =sbuilder-al= to interface with
14
+ =tla-sbuilder=.
15
+
16
+ * Development
17
+
18
+ ** Run Rspec Unit Tests
19
+
20
+ To run =rspec= unit tests
21
+
22
+ #+name rspec
23
+ #+BEGIN_SRC sh :eval no-export :results output :exports code
24
+ bundle exec rspec
25
+ #+END_SRC
26
+
27
+ #+RESULTS:
28
+ #+begin_example
29
+ ...................*.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
30
+
31
+ Pending: (Failures listed here are expected and do not affect your suite's status)
32
+
33
+ 1) Sbuilder::Al::Meta::Etc.alObject2ApiString domain
34
+ # causes dump
35
+ # ./spec/meta/etc_spec.rb:113
36
+
37
+ Finished in 0.4775 seconds (files took 0.49587 seconds to load)
38
+ 1659 examples, 0 failures, 1 pending
39
+
40
+ #+end_example
41
+
42
+
43
+ ** Run Cucumber Tests
44
+
45
+ To run =cucumber= tests
46
+
47
+ #+name cucumber
48
+ #+BEGIN_SRC sh :eval no-export :results output :exports code
49
+ bundle exec cucumber
50
+ #+END_SRC
51
+
52
+ #+RESULTS:
53
+ : Using the default profile...
54
+ : ...........................................................................
55
+ :
56
+ : 6 scenarios (6 passed)
57
+ : 75 steps (75 passed)
58
+ : 0m8.028s
59
+
60
+
61
+ * Usage (Normally not needed)
62
+ :PROPERTIES:
63
+ :CUSTOM_ID: MANUAL-USAGE
64
+ :END:
65
+
66
+ Following short steps demonstrate, how =sbuilder-al= loads AL snippets
67
+ to =tla-sbuilder= context.
68
+
69
+ ** Create Gemfile
70
+
71
+ Configure =sbuilder-al= into =Gemfile=
72
+
73
+ #+BEGIN_SRC ruby :eval no
74
+ source "https://rubygems.org"
75
+ gem "sbuilder-al"
76
+ #+END_SRC
77
+
78
+ and install it together with dependencies:
79
+
80
+ #+BEGIN_SRC sh :eval no
81
+ bundle install
82
+ #+END_SRC
83
+
84
+ ** Initialize =tla-sbuilder= directory structure
85
+
86
+ Command
87
+
88
+ #+name: sbuilder-init
89
+ #+BEGIN_SRC sh :eval no-export :results output :exports code
90
+ bundle exec sbuilder.rb init
91
+ #+END_SRC
92
+
93
+ creates directory structure for running tla-sbuilder.
94
+
95
+ #+RESULTS: sbuilder-init
96
+ #+begin_example
97
+ Directory cnf - created
98
+ Directory cache - already exists - no need to create
99
+ Directory src - created
100
+ Directory src/extend - created
101
+ Directory gen - created
102
+ File cnf/sbuilder.yaml.example - created
103
+ File cnf/resolver_customer.yaml.example - created
104
+ File cnf/extend_customer_doms.yaml.example - created
105
+ File cnf/extend_customer_if.yaml.example - created
106
+ File cnf/extend_customer_run1.yaml.example - created
107
+ File cnf/extend_customer_run2.yaml.example - created
108
+ File cnf/interface_customer.yaml.example - created
109
+ #+end_example
110
+
111
+
112
+ ** Create Configuration File =cnf/sbuider.yaml=
113
+
114
+ Create a configuration file =cnf/sbuilder.yaml= with the following YAML content
115
+
116
+ #+BEGIN_SRC yaml :eval no :tangle cnf/sbuilder.yaml
117
+
118
+ # Define 'sbuilder-al' extension point
119
+ extend:
120
+ loaders:
121
+ - className: Sbuilder::Al::Plugin::Plugin
122
+ gem: sbuilder-al
123
+ objects:
124
+ # define object 'alLoader' and configure it
125
+ - objectName: alLoader
126
+ configuration:
127
+ metatypes:
128
+ example:
129
+ description: Example metatype using ex prefix
130
+ prefix: ex
131
+ rubypaths:
132
+ - src/al.rb
133
+
134
+ # Run as API loader extension point
135
+ interfaces:
136
+ - objectName: alLoader
137
+
138
+ # Run as Snippet loader extension point
139
+ snippets:
140
+ - objectName: alLoader
141
+
142
+
143
+ # Define setup with name 'setup1'
144
+ setups:
145
+ - setupDirectory: setup1
146
+
147
+ #+END_SRC
148
+
149
+ ** Create Api Langauage file =src/al=
150
+
151
+ Create a simple AL language module, which defines an API object
152
+ =alApi= to construct AL language objects in an array
153
+
154
+ #+BEGIN_SRC ruby eval :no exports :code :tangle src/al.rb
155
+
156
+ # Include GEM
157
+ require('sbuilder-al')
158
+
159
+ # Define api interface to tla-sbuilder
160
+ class AlApi
161
+ include Sbuilder::Al::Model::Api
162
+ end
163
+
164
+ nameSpaces = { "example" => { :description=>"Example namespace", :prefix=>"ex_" } }
165
+ alApi = AlApi.start( nameSpaces )
166
+
167
+
168
+ # Application is an array of AL elements
169
+ [
170
+ alApi.variable( "example") do
171
+ name "var1"
172
+ init alApi.constantExpression { value "Hello World" }
173
+ end,
174
+
175
+ ]
176
+
177
+
178
+ #+END_SRC
179
+
180
+ ** Generate sbuilder
181
+
182
+ Running
183
+
184
+ #+name: sbuilder-gen
185
+ #+BEGIN_SRC sh :eval no-export :results output :exports code
186
+ bundle exec sbuilder.rb generate setup1
187
+ #+END_SRC
188
+
189
+ #+RESULTS: sbuilder-gen
190
+
191
+ creates a file
192
+
193
+ #+name: sbuilder-ls
194
+ #+BEGIN_SRC sh :eval no-export :results output :exports code
195
+ ls -l gen/setup1/tla/model.tla
196
+ #+END_SRC
197
+
198
+ #+RESULTS: sbuilder-ls
199
+ : -rw-rw-r-- 1 jj jj 16242 joulu 18 10:36 gen/setup1/tla/model.tla
200
+
201
+ which defines variable =ev_var1=
202
+
203
+ #+name: sbuilder-grep
204
+ #+BEGIN_SRC sh :eval no-export :results output :exports code
205
+ grep 'Hello' gen/setup1/tla/model.tla
206
+ #+END_SRC
207
+
208
+ #+RESULTS: sbuilder-grep
209
+ : ex_var1 = "Hello World";
210
+
211
+
212
+ for the AL snippet created in =src/al.rb= module.
213
+
214
+
215
+ * License
216
+
217
+ MIT
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.8
@@ -0,0 +1,216 @@
1
+ #+BEGIN_EXAMPLE
2
+
3
+ # Top level application
4
+ <application> :: <snippet>*
5
+ <snippet> :: <apiSnippet> | <tlaSnippet>
6
+
7
+ # Api snippets
8
+ <apiSnippet> :: <transaction> | <definition> | <domain>
9
+
10
+ <domain> :: =domain= { =name= STRING,
11
+ [
12
+ =value= STRING |
13
+ =value= INT |
14
+ =value= true |
15
+ =value= false |
16
+ =literal= STRING
17
+ ]*
18
+ }
19
+
20
+ <definition> :: =definition= {
21
+ =name= STRING
22
+ <property>*
23
+ }
24
+
25
+
26
+ <property> :: =property= {
27
+ =name= STRING
28
+ =domain= STRING
29
+ =isArray= BOOLEAN
30
+ } |
31
+ =property= {
32
+ =name= STRING
33
+ =definition= STRING
34
+ =isArray= BOOLEAN
35
+ }
36
+
37
+ # TLA & API snippets
38
+ <transaction> :: =transaction= <meta> {
39
+ =name= STRING
40
+ (=local= STRING)*
41
+ =interfaceOption= <booleanExpression>
42
+ =returnOption= <booleanExpression>
43
+ =request= <request>
44
+ =response= <response>
45
+ =operation= STRING
46
+ =block= <block>
47
+ }
48
+
49
+ <request> :: =request= {
50
+ <reqResponseParam>*
51
+ }
52
+ <response> :: =response= {
53
+ <reqResponseParam>*
54
+ }
55
+ <reqResponseParam> :: =parameter= <name> <isArray> [ <domainName> ] |
56
+ =reference= <name> <isArray> <definitionName>
57
+ <isArray> :: true | false
58
+
59
+
60
+ # TLA snippets
61
+ <tlaSnippet> :: <transaction> | <macro> | <operator> | <variable> | <alias>
62
+ <macro> :: =macro= <meta> {
63
+ =name= STRING
64
+ (=parameter= <STRING>)*
65
+ =block= <block>
66
+ }
67
+
68
+ <alias> :: =aliased=(<meta> ) {
69
+ =name= STRING
70
+ =specName= STRING
71
+ }
72
+
73
+ <operator> :: =operator= <meta> {
74
+ =name= STRING
75
+ (=parameter= <STRING>)*
76
+ <operatorParameter>*
77
+ =expression= <expression>
78
+ }
79
+
80
+ # Block && statements
81
+ <block> :: <statement>*
82
+ <statement> :: <outputStmt> | <skipStmt> | <labelStmt> |
83
+ <callService> | <callMacro> | <conditionalStmt> | <assignTo> |
84
+ <ReturnValue> | <Return> | <jump> | <aborted> | <done> | <failure>
85
+
86
+ <skipStmt> :: =nop=
87
+ <labelStmt> :: =label=
88
+ <outputStmt> :: =output= <expression>*
89
+
90
+ <assignTo> :: =assignTo= {
91
+ =variable= <parameterRVal>
92
+ =rval= <expression>
93
+ } |
94
+ =assignTo= {
95
+ =variable= <reference>
96
+ =rval= <expression>
97
+ }
98
+
99
+ <reference> :: <referenceLocation> ('.' STRING)*
100
+ <referenceLocation> :: <txParameterRef> | <simpleRef> | <variableRef>
101
+ <simpleRef> :: STRING
102
+ <variableRef> :: <meta> STRING
103
+ <txParameterRef> :: <meta> STRING STRING
104
+
105
+ <lval> :: <parameterReference> | <variableReference>
106
+
107
+ <callService> :: =callService= <meta> <name> <operation> {
108
+ (=parameter= <expression>)*
109
+ }
110
+ <callMacro> :: =callMacro= <meta> <name> {
111
+ (=parameter= <expression>)*
112
+ }
113
+ <conditionalStmt> :: =ifThen= <expression> <block> ( =elseThen <block> )?
114
+
115
+
116
+ <jump> :: =jump= STRING
117
+
118
+ <aborted> :: =aborted= <interfaceOpration>
119
+ <done> :: =done= <interfaceOpration>
120
+ <failure> :: =done= <interfaceOpration>
121
+
122
+ <return> :: =ret= <booleanExpression>
123
+
124
+ <ReturnValue> :: =returnValue= STRING <expression>
125
+
126
+ # expressions
127
+ <expression> :: <constantExpression> |
128
+ <referenceExpression> |
129
+ <tlaExpression> |
130
+ <operatorCall> |
131
+ <binaryExpression>
132
+ <unaryExpression>
133
+
134
+ <operatorCall> :: *TBD*
135
+
136
+ <referenceExpression>:: =reference= <reference>
137
+
138
+
139
+ <operatorExpression> :: =parameterReference= STRING
140
+
141
+ <tlaExpression> :: =tlaExpression= {
142
+ =template= <tlaTemplate>
143
+ =args= <expression>*
144
+ }
145
+ <tlaTemplate> :: =:set= | =:sequence= | =:nil= | =:parenthesis=
146
+
147
+ <binaryExpression> :: =binaryExpression= {
148
+ =lval= <expression>
149
+ =op= <binaryOp>
150
+ =rval= <expression>
151
+ }
152
+
153
+ <unaryExpression> :: =unaryExpression= {
154
+ =op= <unaryOp>
155
+ =expr= <expression>
156
+ }
157
+
158
+ <binaryOp> :: =:plus= | =:minus= | =:equal= | =:mult= | =:div= | =:lt= | =:le=
159
+ <unaryOp> :: =:not
160
+
161
+ <constantExpression> :: <integerExpression> | <stringExpression> | <booleanExpression>
162
+ <booleanExpression> :: <trueExpression> | <falseExpression>
163
+ <trueExpression> :: TRUE
164
+ <falseExpression> :: FALSE
165
+ <stringExpression> :: STRING
166
+ <integerExpression> :: INT
167
+
168
+
169
+ # Namespace meta
170
+ <meta> :: <sbuilderMetatype> | STRING
171
+ <name> :: STRING
172
+ <domainName> :: STRING
173
+ <definitionName> :: STRING
174
+ <sbuilderMetatype> :: 'domains' | 'service_implementation' | 'service_completion' | 'framework-svc'
175
+
176
+ #+END_EXAMPLE
177
+
178
+ TLA-templates
179
+
180
+ #+BEGIN_EXAMPLE
181
+ tlaSet(expression) :: tlaExpression {
182
+ tempalate :tlaSet
183
+ args expr
184
+ }
185
+
186
+
187
+ tlaNil :: tlaExpression {
188
+ tempalate :nil
189
+ }
190
+
191
+ tlaSequence(expr) :: =tlaExpression= {
192
+ tempalate :sequence
193
+ arg= expr
194
+ }
195
+ tlaParenthesis(expr) :: =tlaExpression= {
196
+ tempalate :parenthesis
197
+ arg= expr
198
+ }
199
+
200
+ tlaPlainname :: =tlaPlainname= STRING
201
+ tlaRecodrIndex :: =tlaRecordIndex= STRING <tlaExpression>
202
+ tlaIF :: =tlaIF= <tlaExpression> <tlaExpression> <tlaExpression>
203
+ tlaEXCEPT :: =tlaEXCEPT= STRING <tlaExpression>
204
+ tlaSetGenerator :: =tlaEXCEPT= <tlaExpression> STRING <tlaExpression>
205
+ tlaSetIterate(v,set) :: tlaExpression {
206
+ tempalate :set_iterate
207
+ args v
208
+ args set
209
+ }
210
+
211
+ =plus=(l,r) :: =binaryExpression= { =lval= l; =op= :plus; =rval= r }
212
+ =minus(l,r) :: =binaryExpression= { =lval= l; =op= :minus; =rval r }
213
+ =equal(l,r) :: =binaryExpression= { =lval= l; =op= :equal; =rval= r }
214
+ =unequal(l,r) :: =binaryExpression= { =lval= l; =op= :unequal; =rval= r }
215
+
216
+ #+END_EXAMPLE
data/lib/meta/etc.rb ADDED
@@ -0,0 +1,265 @@
1
+ require "sbuilder"
2
+ module Sbuilder
3
+ module Al
4
+ module Meta
5
+ module Etc
6
+
7
+ ## Define lambdas for plantUml strings
8
+ STR_CLASSNODE =
9
+ lambda do |kl, attributes=false|
10
+ "
11
+ class #{kl.respond_to?(:demodulize) ? kl.demodulize : kl } {
12
+ #{ kl.attributes.join("\n") if (attributes && kl.respond_to?(:attributes) ) }
13
+ }
14
+ "
15
+ end
16
+
17
+ STR_MODULENODE =
18
+ lambda do |modu, attributes=false|
19
+ "
20
+ package #{modu.to_s.split("::").last } {
21
+ }
22
+ "
23
+ end
24
+
25
+
26
+ STR_EDGE =
27
+ lambda do |kl, chld|
28
+ "#{kl.respond_to?(:demodulize) ? kl.demodulize : kl } <|-- #{chld.respond_to?(:demodulize) ? chld.demodulize : chld}"
29
+ end
30
+
31
+
32
+
33
+ # @return [Array<class>] Class hierarchy recursable using
34
+ # 'descendants' method without classess below array 'prune'
35
+ #
36
+ # @param klass [class] to start of hierarchy collection
37
+ #
38
+ # @param classes [Array<class>] collected so far
39
+ #
40
+ # @param prune [Array<class>] hierarchy pruned away below the
41
+ # classes in 'prune' - array
42
+ def self.hierarchy( klass, prune=[], classes=[] )
43
+ classes += klass.descendants.map { |kl| hierarchy( kl, prune, classes ) }.flatten if klass.respond_to?( :descendants ) && !prune.include?(klass)
44
+ classes = classes << klass
45
+ classes.uniq
46
+ end
47
+
48
+ # @return [Array<class>] of ancestor classes defining
49
+ # ':descendants' method
50
+ def self.ancestors( klass, classes=[] )
51
+ if klass.superclass.respond_to?( :descendants )
52
+ classes += ancestors( klass.superclass, classes )
53
+ classes.unshift( klass.superclass)
54
+ end
55
+ classes
56
+ end
57
+
58
+ # @return [Array<[Class,Class]>] Array of child, parent arrays
59
+ def self.parentEdges( klasses )
60
+ klasses.reduce([] ) do |memo, kl|
61
+ # puts "kl.class.ancestors=#{kl.class.ancestors}"
62
+ #memo = memo + ((klasses - [kl]) & kl.class.ancestors ).map{ |kl2| [kl,kl2]}
63
+ memo << [kl,kl.superclass] if klasses.include?(kl.superclass)
64
+ memo
65
+ end
66
+ end
67
+
68
+ # @return [Array<Class>] of classes in myModule, excluding
69
+ # classes in 'excluded'
70
+ def self.moduleClasses( myModule, excluded=[] )
71
+ myModule.
72
+ # acces module constants
73
+ constants.
74
+ # select not excluded classes
75
+ select do |c|
76
+ klass = myModule.const_get(c, inherit=false)
77
+ klass.is_a?(Class) && ( klass.ancestors & excluded ).empty?
78
+ end.
79
+ # map 'em class symbols to Class objects
80
+ map { |c| myModule.const_get(c, inherit=false) }
81
+
82
+ end
83
+
84
+ # @return [Array<Module>] of Module in myModule, excluding
85
+ # classes in 'excluded'
86
+ def self.moduleSubModules( myModule, excluded=[] )
87
+ myModule.constants.
88
+ map { |c| myModule.const_get(c, inherit=false) }.
89
+ select { |c| c.class == Module } # && !excluded.include?( c ) }
90
+ # # map 'em class symbols to Class objects
91
+ # map { |c| myModule.const_get(c, inherit=false) }
92
+ end
93
+
94
+
95
+ ##
96
+ # Render 'alObject' as a string
97
+ #
98
+ # @param alObject [AlObject] AL language object to render as atring
99
+ #
100
+ def self.snippetString( alObject )
101
+ Sbuilder::Al::Plugin::Controller.start.alObject2tlaString( alObject )
102
+ end
103
+
104
+ # Class overriding configuration reader for Sbuilder
105
+ class MyController < Sbuilder::Controller
106
+ def initialize( factory, sbuilderConfigs )
107
+ @sbuilderConfigs = sbuilderConfigs
108
+ super( factory )
109
+ end
110
+ # Method to reaturn 'sbuilder.yaml' sbuilder overridden
111
+ def getConfigSbuilder
112
+ @sbuilderConfigs
113
+ end
114
+ end
115
+
116
+
117
+
118
+ ##
119
+ # Render 'apiElement' as a string
120
+ #
121
+ # @param apiElement [Hash] A hash to define api element
122
+ #
123
+ def self.alObject2ApiString( alObject, alObjectCtx = [])
124
+
125
+
126
+ # Configure
127
+ apiType2Config = {
128
+ :domain => {
129
+ "modelData" => "domains",
130
+ "template" => "domains.mustache",
131
+ },
132
+ :definition => {
133
+ "modelData" => "definitions",
134
+ "template" => "definition_types.mustache",
135
+ },
136
+ }
137
+
138
+ # Init sbuilder factory & controller & define configuration
139
+ sbuilderConfigs = {
140
+ "setups" => [
141
+ {
142
+ "setupDirectory": "example"
143
+ }
144
+ ]
145
+ }
146
+ factory = Sbuilder::Factory.getFactory()
147
+ sbuilderController = MyController.new( factory, sbuilderConfigs )
148
+
149
+ # use sbuilder factory to instantiate API plugin && configure it
150
+ apiFacade = factory.getApiFacade(sbuilderController)
151
+ plugin = factory.getLoaderPlugin( Sbuilder::Al::Plugin::Plugin )
152
+ plugin.setFacade(apiFacade)
153
+
154
+ # Add translated element of interest to plugin 'application'
155
+ # (=state kept in controller)
156
+ apiElement = plugin.controller.alObject2tlaSexp( alObject )
157
+ raise "Expect :apiType property in #{apiElement}" unless apiElement.has_key?( :apiType )
158
+
159
+ apiElementCtx = alObjectCtx.map{ |o| plugin.controller.alObject2tlaSexp( alObject ) }
160
+
161
+ plugin.controller.addAlObjects( apiElementCtx )
162
+ plugin.controller.addAlObjects( apiElement )
163
+
164
+ # sbuilderController.load
165
+
166
+ sbuilderController.baseExtensionDefine
167
+
168
+ # invoke plugin API loader interface to pass data to sbuilder
169
+ plugin.load( nil )
170
+
171
+ # After load prepare for snippets stuff
172
+ sbuilderController.resolve
173
+ sbuilderController.initializeSymbolTableMetaModel
174
+ sbuilderController.initializeSymbolTable
175
+
176
+
177
+ # render template in sbuilder
178
+ setupName = "example"
179
+ outputPath = "output"
180
+ # choose template && data which to show
181
+ raise "Unknown apiType '#{apiElement[:apiType]}', valid apitype #{apiType2Config.keys.join(',')}" if apiType2Config[apiElement[:apiType]].nil?
182
+ generateDef = { "template" => apiType2Config[apiElement[:apiType]]["template"],
183
+ "desc"=>"example",
184
+ "modelData" => apiType2Config[apiElement[:apiType]]["modelData"],
185
+ }
186
+ mustache = sbuilderController.prepareRender( setupName )
187
+ sbuilderController.generateTemplateString( setupName, outputPath, generateDef, mustache)
188
+ end
189
+
190
+
191
+
192
+ # @return [Array<String>] Class hierarchy edges and nodes for
193
+ # class 'klass' with classes in 'prune'
194
+ #
195
+ # @param attributes [Boolean] true when include attributes to class default
196
+ #
197
+ # @param parents [Boolean] true when include also parent to hierarchy
198
+ def self.plantUmlClassHierarchy( klass, prune=[], attributes=false, parents=false )
199
+ klass = [klass] unless klass.is_a?( Array )
200
+
201
+ # Find all classes in hierarchy 'klass'
202
+ klassesInHierarchy =
203
+ klass.map do |kl|
204
+ hierarchy( kl, prune )
205
+ end.flatten
206
+
207
+ ## Create edge strings for hierarch down
208
+ hierarchyEdges =
209
+ klassesInHierarchy.
210
+ # do not look into descendants of a pruned class
211
+ select{ |kl| !prune.include?(kl) }.
212
+ map do |kl|
213
+ # kl.descendants.map { |chld| "#{kl.demodulize} <|-- #{chld.demodulize}" }
214
+ kl.descendants.map { |chld| STR_EDGE.call( kl, chld ) }
215
+ end.flatten
216
+
217
+ ## Create node string
218
+ hierarchyNodes =
219
+ klassesInHierarchy.map do |kl|
220
+ STR_CLASSNODE.call( kl, attributes )
221
+ end
222
+
223
+
224
+ parentNodes = []
225
+ parentEdges = []
226
+
227
+ if ( parents ) then
228
+
229
+ # string for all parent nodes
230
+ parentNodes = klass.map do |kl|
231
+ # find all ancestors for all classes
232
+ ancestors( kl )
233
+ end.flatten.uniq.map do |kl|
234
+ # and a node for them
235
+ STR_CLASSNODE.call( kl, attributes )
236
+ end
237
+
238
+ parentEdges = klass.reduce([]) do |memo,kl|
239
+
240
+ klWithAncestors = [kl] + ancestors( kl )
241
+ # puts( "klWithAncestors=#{klWithAncestors.join(',')}")
242
+ memo << (0..(klWithAncestors.length-2)).map do |i|
243
+ # puts( "i=#{i}, klWithAncestors.length=#{klWithAncestors.length}, klWithAncestors[i]=#{klWithAncestors[i]}, #{klWithAncestors[i+1]}")
244
+ # template 'strEdge' produces parent/sub-class edge
245
+ # [ klWithAncestors[i+1], klWithAncestors[i] ]
246
+ STR_EDGE.call(klWithAncestors[i+1], klWithAncestors[i])
247
+ end
248
+ memo
249
+ end.reject{ |arr| arr.empty? }
250
+ # puts( "parentEdges=#{parentEdges.length}=#{parentEdges.join(',')},#{parentEdges}")
251
+
252
+
253
+ end # parents
254
+
255
+ # make edges unique
256
+ parentEdges = parentEdges.flatten.uniq
257
+ hierarchyEdges = hierarchyEdges.flatten.uniq
258
+
259
+ hierarchyNodes + parentNodes + parentEdges + hierarchyEdges
260
+ end
261
+
262
+ end
263
+ end
264
+ end # Al
265
+ end # Sbuilder