sbuilder-al 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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