agreement-design-prototype 0.0.3
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 +7 -0
- data/README.md +17 -0
- data/Rakefile +22 -0
- data/agreement-design.gemspec +18 -0
- data/build/build_models.rb +16 -0
- data/gen/diagrams/data_model.dot +34 -0
- data/gen/doc/frameworks.md +19 -0
- data/gen/doc/metamodel.md +98 -0
- data/gen/images/data_model.jpg +0 -0
- data/model/agreement.rb +69 -0
- data/model/fm.rb +33 -0
- data/model/party.rb +17 -0
- data/out/test/diagrams/d.dot +34 -0
- data/out/test/doc/doctest.md +19 -0
- data/out/test/images/d.jpg +0 -0
- data/src/data_model.rb +165 -0
- data/src/diagram.rb +146 -0
- data/src/doc.rb +181 -0
- data/test/data_model_test.rb +91 -0
- data/test/diagram_test.rb +34 -0
- data/test/doc_test.rb +27 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9b8b0ee3c86692db54196238eb53be579e4b3f8b2bc24dee7a6dc67ca89d7068
|
4
|
+
data.tar.gz: 511c0e41fd44633b68f48efc18cb00a29b7692db569eb3ef112adcb6b7392e87
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7b167ee34fc849c9d4e4470a93ef8b21283f50c8e2059a5e862e1b1239255122cf605eb397e59da8b308b14138237f632a591330b8f3c72cca570e6afa2de3f8
|
7
|
+
data.tar.gz: feeca74a1f808b7663062910a89719db1c551d9a57b100a4f9088219e17b3141bb6eb88a0c6952bb958c9d75a5371c36279a7edc0223596b16c6bfe37bcc33f9
|
data/README.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
# Agreement Design
|
3
|
+
|
4
|
+
this is a prototype for an agreement design system. In line with
|
5
|
+
[ADR 0010 - use shared drfinition of cmp agreement](https://github.com/Crown-Commercial-Service/CCS-Architecture-Decision-Records/blob/master/doc/adr/0010-use-shared-definition-of-cmp-agreement-when-building-all-cmp-services.md) we want to define the metamodel
|
6
|
+
for Commertial Agreements and supporting data in a common way, so all
|
7
|
+
services, interfaces and data types are consistent.
|
8
|
+
|
9
|
+
This prototype
|
10
|
+
|
11
|
+
- defines a common metamodel
|
12
|
+
- models a number of agreement categories (as alpha prototypes, not as definitive representations)
|
13
|
+
- produces API definitions such as Swagger
|
14
|
+
- possibly produces domain objects (though these should probably be created from API specs)
|
15
|
+
- possibly produces data domain descriptions, e.g. Ruby-on-Rails schema records
|
16
|
+
|
17
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
task default: %w[test build]
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.test_files = FileList['test/*.rb']
|
8
|
+
t.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
task :build do
|
12
|
+
ruby "build/build_models.rb"
|
13
|
+
end
|
14
|
+
|
15
|
+
task :deploy do
|
16
|
+
`gem build agreement-design.gemspec`
|
17
|
+
`gem push *.gem`
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/clean'
|
21
|
+
|
22
|
+
CLEAN.include FileList['out', 'gen', '*.gem']
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# agreement-design.gemspec
|
2
|
+
Gem::Specification.new do |spec|
|
3
|
+
spec.name = "agreement-design-prototype"
|
4
|
+
spec.version = "0.0.3"
|
5
|
+
spec.authors = ["CCS"]
|
6
|
+
spec.homepage = 'https://github.com/Crown-Commercial-Service/cmp-design-prototype/agreement-design'
|
7
|
+
spec.email = ["rubygems@humphries.tech"]
|
8
|
+
spec.description = %q{A prototype of techniques to manage agreement specifications.}
|
9
|
+
spec.summary = %q{Agreement prototype .}
|
10
|
+
spec.license = "MIT"
|
11
|
+
|
12
|
+
spec.files = Dir["./**/*"]
|
13
|
+
spec.executables = Dir["./bin/*"]
|
14
|
+
spec.test_files = Dir["./test/**/*"]
|
15
|
+
spec.require_paths = ["src", "model"]
|
16
|
+
|
17
|
+
spec.add_development_dependency "rake", "12.3.0"
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative "../src/diagram"
|
2
|
+
require_relative "../model/agreement"
|
3
|
+
|
4
|
+
path= File.join(File.dirname(__FILE__), '../', "gen")
|
5
|
+
|
6
|
+
# TODO: escape the path somehow so it works when called direct. Works ok from Rake
|
7
|
+
diagram= Diagram.new( path, "data_model")
|
8
|
+
diagram.describe Category, Parties
|
9
|
+
|
10
|
+
doc= Doc.new( path, "metamodel")
|
11
|
+
doc.document_metamodel Category, Parties
|
12
|
+
|
13
|
+
doc= Doc.new( path, "frameworks")
|
14
|
+
require_relative '../model/fm'
|
15
|
+
doc.document Category::FM
|
16
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
strict digraph {
|
2
|
+
subgraph cluster_Category {
|
3
|
+
node [shape=plaintext margin=0];
|
4
|
+
label=Category;
|
5
|
+
"ItemParameter" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>ItemParameter</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-detail </TD></TR><TR><TD ALIGN="LEFT">-keyword [*]</TD></TR><TR><TD ALIGN="LEFT">-valueMin </TD></TR><TR><TD ALIGN="LEFT">-valueMax </TD></TR><TR><TD ALIGN="LEFT">-type </TD></TR><TR><TD ALIGN="LEFT">-standard </TD></TR><TR><TD ALIGN="LEFT">-reference </TD></TR></table>>];
|
6
|
+
"Agreement" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Agreement</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-item_params [*]</TD></TR><TR><TD ALIGN="LEFT">-version </TD></TR><TR><TD ALIGN="LEFT">-start_date </TD></TR><TR><TD ALIGN="LEFT">-end_date </TD></TR></table>>];
|
7
|
+
"Framework" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Framework</TD></TH><TR><TD ALIGN="LEFT">-fwk_id </TD></TR></table>>];
|
8
|
+
"Lot" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Lot</TD></TH><TR><TD ALIGN="LEFT">-fwk_id </TD></TR></table>>];
|
9
|
+
"Item" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Item</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-params </TD></TR><TR><TD ALIGN="LEFT">-description </TD></TR><TR><TD ALIGN="LEFT">-value </TD></TR></table>>];
|
10
|
+
"Catalogue" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Catalogue</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-items [*]</TD></TR><TR><TD ALIGN="LEFT">-agreement_id </TD></TR></table>>];
|
11
|
+
"Offer" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Offer</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-item_id </TD></TR><TR><TD ALIGN="LEFT">-catalogue_id </TD></TR><TR><TD ALIGN="LEFT">-supplier_id </TD></TR><TR><TD ALIGN="LEFT">-description </TD></TR></table>>];
|
12
|
+
"Award" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Award</TD></TH><TR><TD ALIGN="LEFT">-buyer_id </TD></TR></table>>];
|
13
|
+
}
|
14
|
+
subgraph cluster_Parties {
|
15
|
+
node [shape=plaintext margin=0];
|
16
|
+
label=Parties;
|
17
|
+
"Party" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Party</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR></table>>];
|
18
|
+
"Supplier" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Supplier</TD></TH></table>>];
|
19
|
+
"Buyer" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Buyer</TD></TH></table>>];
|
20
|
+
}
|
21
|
+
"Agreement" -> "ItemParameter" [label="{contains} item_params" arrowhead = "none" arrowtail = "diamond" ];
|
22
|
+
"Framework" -> "Agreement" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
23
|
+
"Lot" -> "Framework" [label="fwk_id" arrowhead = "open" arrowtail = "none" ];
|
24
|
+
"Lot" -> "Agreement" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
25
|
+
"Item" -> "ItemParameter" [label="params" arrowhead = "open" arrowtail = "none" ];
|
26
|
+
"Catalogue" -> "Item" [label="{contains} items" arrowhead = "none" arrowtail = "diamond" ];
|
27
|
+
"Catalogue" -> "Agreement" [label="agreement_id" arrowhead = "open" arrowtail = "none" ];
|
28
|
+
"Offer" -> "Item" [label="item_id" arrowhead = "open" arrowtail = "none" ];
|
29
|
+
"Offer" -> "Catalogue" [label="catalogue_id" arrowhead = "open" arrowtail = "none" ];
|
30
|
+
"Offer" -> "Supplier" [label="supplier_id" arrowhead = "open" arrowtail = "none" ];
|
31
|
+
"Award" -> "Buyer" [label="buyer_id" arrowhead = "open" arrowtail = "none" ];
|
32
|
+
"Supplier" -> "Party" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
33
|
+
"Buyer" -> "Party" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
34
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Category: FM
|
2
|
+
## framework
|
3
|
+
### framework RM123
|
4
|
+
- id RM123
|
5
|
+
- fwk_id RM123
|
6
|
+
- version 1.0.0
|
7
|
+
## lot
|
8
|
+
### lot 1
|
9
|
+
- id 1
|
10
|
+
- fwk_id RM123
|
11
|
+
#### item_params 1
|
12
|
+
- id 1
|
13
|
+
- valueMin 0
|
14
|
+
- valueMax 100
|
15
|
+
- keyword.1 thing
|
16
|
+
- keyword.2 thing2
|
17
|
+
### lot 2
|
18
|
+
- id 2
|
19
|
+
- fwk_id RM123
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Category
|
2
|
+
## ItemParameter
|
3
|
+
Defines the meaning of Items in Catalogues
|
4
|
+
|
5
|
+
|attribute|type|multiplicity|description|
|
6
|
+
|---------|----|------------|-----------|
|
7
|
+
|id|String|1||
|
8
|
+
|detail|String|1||
|
9
|
+
|keyword|String|*||
|
10
|
+
|valueMin|String|1||
|
11
|
+
|valueMax|String|1||
|
12
|
+
|type|String|1|One of: String, Currency, Location, Amount|
|
13
|
+
|standard|String|1|which standard defines the item type, such as UBL2.1|
|
14
|
+
|reference|String|1|reference within standard, such as UBL2.1|
|
15
|
+
## Agreement
|
16
|
+
General definition of Commercial Agreements
|
17
|
+
|
18
|
+
|attribute|type|multiplicity|description|
|
19
|
+
|---------|----|------------|-----------|
|
20
|
+
|id|String|1||
|
21
|
+
|item_params|Category::ItemParameter|*||
|
22
|
+
|version|String|1|semantic version id of the form X.Y.Z|
|
23
|
+
|start_date|Date|1||
|
24
|
+
|end_date|Date|1||
|
25
|
+
## Framework extends Category::Agreement
|
26
|
+
A kind of Framework used for calloffs, composed of Lots
|
27
|
+
|
28
|
+
|attribute|type|multiplicity|description|
|
29
|
+
|---------|----|------------|-----------|
|
30
|
+
|id|String|1||
|
31
|
+
|item_params|Category::ItemParameter|*||
|
32
|
+
|version|String|1|semantic version id of the form X.Y.Z|
|
33
|
+
|start_date|Date|1||
|
34
|
+
|end_date|Date|1||
|
35
|
+
|fwk_id|String|1|Such as the RM number|
|
36
|
+
## Lot extends Category::Agreement
|
37
|
+
|
38
|
+
|
39
|
+
|attribute|type|multiplicity|description|
|
40
|
+
|---------|----|------------|-----------|
|
41
|
+
|id|String|1||
|
42
|
+
|item_params|Category::ItemParameter|*||
|
43
|
+
|version|String|1|semantic version id of the form X.Y.Z|
|
44
|
+
|start_date|Date|1||
|
45
|
+
|end_date|Date|1||
|
46
|
+
|fwk_id|String|1|Part of framework|
|
47
|
+
## Item
|
48
|
+
Something offered to a buyer as part of a contract.Items are defined in Catalogues.
|
49
|
+
|
50
|
+
|attribute|type|multiplicity|description|
|
51
|
+
|---------|----|------------|-----------|
|
52
|
+
|id|String|1|Item id, possibly a concatentation of the standard (in params) and the catalogue and an incrementatl id?|
|
53
|
+
|params|String|1||
|
54
|
+
|description|String|1||
|
55
|
+
|value|String|1||
|
56
|
+
## Catalogue
|
57
|
+
A collection of items that can be bought via an Agreement.
|
58
|
+
|
59
|
+
|attribute|type|multiplicity|description|
|
60
|
+
|---------|----|------------|-----------|
|
61
|
+
|id|String|1||
|
62
|
+
|items|Category::Item|*||
|
63
|
+
|agreement_id|String|1||
|
64
|
+
## Offer
|
65
|
+
|
66
|
+
|
67
|
+
|attribute|type|multiplicity|description|
|
68
|
+
|---------|----|------------|-----------|
|
69
|
+
|id|String|1|Offer id, probably a UUID|
|
70
|
+
|item_id|String|1||
|
71
|
+
|catalogue_id|String|1||
|
72
|
+
|supplier_id|String|1||
|
73
|
+
|description|String|1|Description of the offer|
|
74
|
+
## Award
|
75
|
+
|
76
|
+
|
77
|
+
|attribute|type|multiplicity|description|
|
78
|
+
|---------|----|------------|-----------|
|
79
|
+
|buyer_id|String|1||
|
80
|
+
# Parties
|
81
|
+
## Party
|
82
|
+
|
83
|
+
|
84
|
+
|attribute|type|multiplicity|description|
|
85
|
+
|---------|----|------------|-----------|
|
86
|
+
|id|String|1|UUID or Salesforce ID?|
|
87
|
+
## Supplier extends Parties::Party
|
88
|
+
|
89
|
+
|
90
|
+
|attribute|type|multiplicity|description|
|
91
|
+
|---------|----|------------|-----------|
|
92
|
+
|id|String|1|UUID or Salesforce ID?|
|
93
|
+
## Buyer extends Parties::Party
|
94
|
+
|
95
|
+
|
96
|
+
|attribute|type|multiplicity|description|
|
97
|
+
|---------|----|------------|-----------|
|
98
|
+
|id|String|1|UUID or Salesforce ID?|
|
Binary file
|
data/model/agreement.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative '../src/data_model'
|
2
|
+
require_relative 'party'
|
3
|
+
require 'date'
|
4
|
+
include DataModel
|
5
|
+
|
6
|
+
domain :Category do
|
7
|
+
|
8
|
+
datatype(:ItemParameter,
|
9
|
+
description: "Defines the meaning of Items in Catalogues") {
|
10
|
+
attribute :id, String
|
11
|
+
attribute :detail, String
|
12
|
+
attribute :keyword, String, ZERO_TO_MANY
|
13
|
+
attribute :valueMin, String
|
14
|
+
attribute :valueMax, String
|
15
|
+
attribute :type, String, "One of: String, Currency, Location, Amount"
|
16
|
+
attribute :standard, String, "which standard defines the item type, such as UBL2.1"
|
17
|
+
attribute :reference, String, "reference within standard, such as UBL2.1"
|
18
|
+
}
|
19
|
+
|
20
|
+
datatype(:Agreement,
|
21
|
+
description: "General definition of Commercial Agreements") {
|
22
|
+
attribute :id, String
|
23
|
+
attribute :item_params, Category::ItemParameter, ZERO_TO_MANY, links: Category::ItemParameter
|
24
|
+
attribute :version, String, "semantic version id of the form X.Y.Z"
|
25
|
+
attribute :start_date, Date
|
26
|
+
attribute :end_date, Date
|
27
|
+
}
|
28
|
+
|
29
|
+
datatype(:Framework, extends: Category::Agreement,
|
30
|
+
description: "A kind of Framework used for calloffs, composed of Lots") {
|
31
|
+
attribute :fwk_id, String, "Such as the RM number"
|
32
|
+
}
|
33
|
+
|
34
|
+
datatype(:Lot, extends: Category::Agreement) {
|
35
|
+
attribute :fwk_id, String, "Part of framework", links: Category::Framework
|
36
|
+
}
|
37
|
+
|
38
|
+
datatype(:Item,
|
39
|
+
description: "Something offered to a buyer as part of a contract."\
|
40
|
+
"Items are defined in Catalogues.") {
|
41
|
+
attribute :id, String, "Item id, possibly a concatentation of the standard (in params) and the catalogue and an incrementatl id?"
|
42
|
+
attribute :params, String, links: Category::ItemParameter
|
43
|
+
attribute :description, String
|
44
|
+
attribute :value, String
|
45
|
+
}
|
46
|
+
|
47
|
+
datatype(:Catalogue,
|
48
|
+
description: "A collection of items that can be bought via an Agreement.") {
|
49
|
+
attribute :id, String
|
50
|
+
attribute :items, Category::Item, ZERO_TO_MANY, links: Category::Item
|
51
|
+
attribute :agreement_id, String, links: Category::Agreement
|
52
|
+
}
|
53
|
+
|
54
|
+
datatype(:Offer,
|
55
|
+
description: "") {
|
56
|
+
attribute :id, String, "Offer id, probably a UUID"
|
57
|
+
attribute :item_id, String, links: Category::Item
|
58
|
+
attribute :catalogue_id, String, links: Category::Catalogue
|
59
|
+
attribute :supplier_id, String, links: Parties::Supplier
|
60
|
+
attribute :description, String, "Description of the offer"
|
61
|
+
}
|
62
|
+
|
63
|
+
datatype(:Award,
|
64
|
+
description: "") {
|
65
|
+
attribute :buyer_id, String, links: Parties::Buyer
|
66
|
+
}
|
67
|
+
|
68
|
+
end
|
69
|
+
|
data/model/fm.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative 'agreement'
|
2
|
+
|
3
|
+
Category.new :FM do
|
4
|
+
|
5
|
+
FM_ID = "RM123"
|
6
|
+
framework do
|
7
|
+
id FM_ID
|
8
|
+
fwk_id FM_ID
|
9
|
+
version "1.0.0"
|
10
|
+
end
|
11
|
+
|
12
|
+
lot do
|
13
|
+
id "1"; fwk_id FM_ID
|
14
|
+
item_params do
|
15
|
+
id 1; valueMin 0; valueMax 100
|
16
|
+
keyword :thing
|
17
|
+
keyword :thing2
|
18
|
+
end
|
19
|
+
item_params do
|
20
|
+
id 2; valueMin 22; valueMax 33
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
lot do
|
25
|
+
id "2"
|
26
|
+
fwk_id FM_ID
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
puts Category::FM.contents[:lot]
|
32
|
+
|
33
|
+
|
data/model/party.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative '../src/data_model'
|
2
|
+
include DataModel
|
3
|
+
|
4
|
+
domain :Parties do
|
5
|
+
|
6
|
+
datatype :Party do
|
7
|
+
attribute :id, String, "UUID or Salesforce ID?"
|
8
|
+
end
|
9
|
+
|
10
|
+
datatype :Supplier, extends: Parties::Party do
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
datatype :Buyer, extends: Parties::Party do
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
strict digraph {
|
2
|
+
subgraph cluster_Category {
|
3
|
+
node [shape=plaintext margin=0];
|
4
|
+
label=Category;
|
5
|
+
"ItemParameter" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>ItemParameter</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-detail </TD></TR><TR><TD ALIGN="LEFT">-keyword [*]</TD></TR><TR><TD ALIGN="LEFT">-valueMin </TD></TR><TR><TD ALIGN="LEFT">-valueMax </TD></TR><TR><TD ALIGN="LEFT">-type </TD></TR><TR><TD ALIGN="LEFT">-standard </TD></TR><TR><TD ALIGN="LEFT">-reference </TD></TR></table>>];
|
6
|
+
"Agreement" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Agreement</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-item_params [*]</TD></TR><TR><TD ALIGN="LEFT">-version </TD></TR><TR><TD ALIGN="LEFT">-start_date </TD></TR><TR><TD ALIGN="LEFT">-end_date </TD></TR></table>>];
|
7
|
+
"Framework" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Framework</TD></TH><TR><TD ALIGN="LEFT">-fwk_id </TD></TR></table>>];
|
8
|
+
"Lot" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Lot</TD></TH><TR><TD ALIGN="LEFT">-fwk_id </TD></TR></table>>];
|
9
|
+
"Item" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Item</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-params </TD></TR><TR><TD ALIGN="LEFT">-description </TD></TR><TR><TD ALIGN="LEFT">-value </TD></TR></table>>];
|
10
|
+
"Catalogue" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Catalogue</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-items [*]</TD></TR><TR><TD ALIGN="LEFT">-agreement_id </TD></TR></table>>];
|
11
|
+
"Offer" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Offer</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR><TR><TD ALIGN="LEFT">-item_id </TD></TR><TR><TD ALIGN="LEFT">-catalogue_id </TD></TR><TR><TD ALIGN="LEFT">-supplier_id </TD></TR><TR><TD ALIGN="LEFT">-description </TD></TR></table>>];
|
12
|
+
"Award" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Award</TD></TH><TR><TD ALIGN="LEFT">-buyer_id </TD></TR></table>>];
|
13
|
+
}
|
14
|
+
subgraph cluster_Parties {
|
15
|
+
node [shape=plaintext margin=0];
|
16
|
+
label=Parties;
|
17
|
+
"Party" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Party</TD></TH><TR><TD ALIGN="LEFT">-id </TD></TR></table>>];
|
18
|
+
"Supplier" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Supplier</TD></TH></table>>];
|
19
|
+
"Buyer" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Buyer</TD></TH></table>>];
|
20
|
+
}
|
21
|
+
"Agreement" -> "ItemParameter" [label="{contains} item_params" arrowhead = "none" arrowtail = "diamond" ];
|
22
|
+
"Framework" -> "Agreement" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
23
|
+
"Lot" -> "Framework" [label="fwk_id" arrowhead = "open" arrowtail = "none" ];
|
24
|
+
"Lot" -> "Agreement" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
25
|
+
"Item" -> "ItemParameter" [label="params" arrowhead = "open" arrowtail = "none" ];
|
26
|
+
"Catalogue" -> "Item" [label="{contains} items" arrowhead = "none" arrowtail = "diamond" ];
|
27
|
+
"Catalogue" -> "Agreement" [label="agreement_id" arrowhead = "open" arrowtail = "none" ];
|
28
|
+
"Offer" -> "Item" [label="item_id" arrowhead = "open" arrowtail = "none" ];
|
29
|
+
"Offer" -> "Catalogue" [label="catalogue_id" arrowhead = "open" arrowtail = "none" ];
|
30
|
+
"Offer" -> "Supplier" [label="supplier_id" arrowhead = "open" arrowtail = "none" ];
|
31
|
+
"Award" -> "Buyer" [label="buyer_id" arrowhead = "open" arrowtail = "none" ];
|
32
|
+
"Supplier" -> "Party" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
33
|
+
"Buyer" -> "Party" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
34
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Category: FM
|
2
|
+
## framework
|
3
|
+
### framework RM123
|
4
|
+
- id RM123
|
5
|
+
- fwk_id RM123
|
6
|
+
- version 1.0.0
|
7
|
+
## lot
|
8
|
+
### lot 1
|
9
|
+
- id 1
|
10
|
+
- fwk_id RM123
|
11
|
+
#### item_params 1
|
12
|
+
- id 1
|
13
|
+
- valueMin 0
|
14
|
+
- valueMax 100
|
15
|
+
- keyword.1 thing
|
16
|
+
- keyword.2 thing2
|
17
|
+
### lot 2
|
18
|
+
- id 2
|
19
|
+
- fwk_id RM123
|
Binary file
|
data/src/data_model.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
module DataModel
|
2
|
+
|
3
|
+
SINGLE = 1..1
|
4
|
+
ONE_TO_MANY = 1..-1
|
5
|
+
ZERO_TO_MANY = 0..-1
|
6
|
+
|
7
|
+
class DataType
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def init(name, domain, extends, description)
|
12
|
+
@attributes = {}
|
13
|
+
@typename = name
|
14
|
+
@domain = domain
|
15
|
+
@extends = extends
|
16
|
+
@description = description
|
17
|
+
|
18
|
+
self.define_singleton_method :domain do
|
19
|
+
@domain
|
20
|
+
end
|
21
|
+
self.define_singleton_method :description do
|
22
|
+
@description
|
23
|
+
end
|
24
|
+
|
25
|
+
self.define_singleton_method(:attributes) do |inherited=true|
|
26
|
+
if inherited && self.superclass.respond_to?(:attributes)
|
27
|
+
self.superclass.attributes.merge @attributes
|
28
|
+
else
|
29
|
+
@attributes
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def typename
|
36
|
+
@typename
|
37
|
+
end
|
38
|
+
|
39
|
+
def extends
|
40
|
+
@extends < DataType ? @extends.name : nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def attribute(name, type, *args, multiplicity: SINGLE, description: "", links: nil)
|
44
|
+
options = {:multiplicity => multiplicity, :description => description, :name => name, :type => type}
|
45
|
+
if links
|
46
|
+
options[:links]= links
|
47
|
+
end
|
48
|
+
|
49
|
+
for opt in args
|
50
|
+
if opt.is_a? Range
|
51
|
+
options[:multiplicity] = opt
|
52
|
+
elsif opt.is_a? String
|
53
|
+
options[:description] = opt
|
54
|
+
else
|
55
|
+
raise "optional arguments should be string (description), or range\n " << opt.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
@attributes[name] = options
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
attr_reader :name, :attributes
|
64
|
+
|
65
|
+
def initialize(name)
|
66
|
+
@name = name
|
67
|
+
@attributes = {}
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def method_missing(sym, *args, &block)
|
72
|
+
if (args.length==0) && @attributes[sym]
|
73
|
+
return @attributes[sym]
|
74
|
+
end
|
75
|
+
if self.class.attributes[sym]
|
76
|
+
if self.class.attributes[sym][:multiplicity] != SINGLE
|
77
|
+
@attributes[sym] = [] unless @attributes[sym]
|
78
|
+
@attributes[sym] << valueof(sym, *args, &block)
|
79
|
+
else
|
80
|
+
@attributes[sym] = valueof(sym, *args, &block)
|
81
|
+
end
|
82
|
+
return @attributes[sym]
|
83
|
+
end
|
84
|
+
puts "Warning: unknown attribute #{sym}?"
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_s
|
88
|
+
"#{self.class.typename} #{self.name} #{@attributes}"
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def valueof(sym, *args, &block)
|
94
|
+
if self.class.attributes[sym][:type] < DataType
|
95
|
+
at = self.class.attributes[sym][:type].new(self.class.attributes[sym][:name])
|
96
|
+
if nil == block
|
97
|
+
raise "Need a block for a nested type #{sym}"
|
98
|
+
end
|
99
|
+
at.instance_exec &block
|
100
|
+
return at
|
101
|
+
else
|
102
|
+
args[0]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
class Domain
|
109
|
+
|
110
|
+
class << self
|
111
|
+
def datatype(name, extends: DataType, description: "", &block)
|
112
|
+
@types = {} unless instance_variable_defined? :@types
|
113
|
+
if extends.class != Class
|
114
|
+
extends = @types.fetch(extends, DataType)
|
115
|
+
end
|
116
|
+
type = self.const_set name, Class.new(extends)
|
117
|
+
# puts "defined #{type} from #{name} on #{self }"
|
118
|
+
@types[name] = type
|
119
|
+
self.define_singleton_method(:types) {@types}
|
120
|
+
dom = self
|
121
|
+
type.instance_exec do
|
122
|
+
init name, dom, extends, description
|
123
|
+
end
|
124
|
+
type.instance_exec &block
|
125
|
+
type
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
attr_reader :contents, :name
|
131
|
+
|
132
|
+
def initialize name, &block
|
133
|
+
@contents = {}
|
134
|
+
@name = name
|
135
|
+
self.class.const_set name, self
|
136
|
+
self.instance_exec &block
|
137
|
+
end
|
138
|
+
|
139
|
+
def method_missing(sym, &block)
|
140
|
+
# create a new datatype for each matching type name, and run its block
|
141
|
+
for t in self.class.types.values
|
142
|
+
if t.typename.downcase == sym
|
143
|
+
decl = t.new(sym)
|
144
|
+
@contents[sym] = [] unless @contents[sym]
|
145
|
+
@contents[sym] << decl
|
146
|
+
decl.instance_exec &block
|
147
|
+
return decl
|
148
|
+
end
|
149
|
+
end
|
150
|
+
puts "Warning unknown type #{sym} ignored"
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
module_function
|
157
|
+
|
158
|
+
def domain(name, &block)
|
159
|
+
dom = Object.const_set name, Class.new(Domain)
|
160
|
+
dom.instance_exec &block
|
161
|
+
return dom
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
end # DataModel
|
data/src/diagram.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require_relative 'doc'
|
3
|
+
|
4
|
+
|
5
|
+
class TableElement < Element
|
6
|
+
def initialize file, node
|
7
|
+
super(file)
|
8
|
+
@node = node
|
9
|
+
pput %Q("#{@node}" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>#{@node}</TD></TH>)
|
10
|
+
@items = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def item i
|
14
|
+
@items << i
|
15
|
+
self.pput %Q(<TR><TD ALIGN="LEFT">-#{i}</TD></TR>)
|
16
|
+
end
|
17
|
+
|
18
|
+
def finish
|
19
|
+
# self.pput " " << @items.join( ",")
|
20
|
+
self.pput "</table>"
|
21
|
+
self.pput ">];\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class Graph < Element
|
28
|
+
def initialize file
|
29
|
+
super(file)
|
30
|
+
self.pput "strict digraph {\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def finish
|
34
|
+
self.pput "}\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class SubGraph < Element
|
40
|
+
def initialize file, model
|
41
|
+
super(file)
|
42
|
+
self.pput "subgraph cluster_#{model} {\n"
|
43
|
+
self.pput "node [shape=plaintext margin=0];\n"
|
44
|
+
self.pput "label=#{model};\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
def finish
|
48
|
+
self.pput "}\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class Links < Element
|
54
|
+
|
55
|
+
def link el1, el2, label: el, arrowhead: nil, arrowtail: nil
|
56
|
+
ah = arrowhead ? %Q!arrowhead = "#{arrowhead}"! : ""
|
57
|
+
at = arrowtail ? %Q!arrowtail = "#{arrowtail}"! : ""
|
58
|
+
pput %Q!"#{el1}" -> "#{el2}" [label="#{label}" #{ah} #{at} ];\n!
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
class Diagram < Doc
|
64
|
+
|
65
|
+
def dotfile
|
66
|
+
File.join(diagram_path, "#{self.name}.dot")
|
67
|
+
end
|
68
|
+
|
69
|
+
def jpgfile
|
70
|
+
File.join(image_path, "#{self.name}.jpg")
|
71
|
+
end
|
72
|
+
|
73
|
+
def describe *models
|
74
|
+
|
75
|
+
FileUtils.mkpath diagram_path
|
76
|
+
FileUtils.mkpath image_path
|
77
|
+
File.open(self.dotfile, "w") do |file|
|
78
|
+
graph = Graph.new(file)
|
79
|
+
for model in models
|
80
|
+
subgraph = SubGraph.new(file, modelname(model))
|
81
|
+
for type in model.types.values
|
82
|
+
table = TableElement.new(file, typename(type))
|
83
|
+
for att in type.attributes(false).values
|
84
|
+
table.item att_detail(att)
|
85
|
+
end
|
86
|
+
table.finish
|
87
|
+
end
|
88
|
+
subgraph.finish
|
89
|
+
end
|
90
|
+
for model in models
|
91
|
+
links = Links.new(file)
|
92
|
+
for type in model.types.values
|
93
|
+
for att in type.attributes(false).keys
|
94
|
+
if type.attributes[att][:links]
|
95
|
+
contains = (type.attributes[att][:type] < DataType)
|
96
|
+
links.link typename(type),
|
97
|
+
typename(type.attributes[att][:links]),
|
98
|
+
label: %Q!#{contains ? "{contains} " : ""}#{type.attributes[att][:name]}!,
|
99
|
+
arrowhead: contains ? "none": "open",
|
100
|
+
arrowtail: contains ? "diamond": "none"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
if type.superclass < DataType
|
104
|
+
links.link typename(type),
|
105
|
+
typename(type.superclass),
|
106
|
+
label: "extends",
|
107
|
+
arrowhead: "none",
|
108
|
+
arrowtail: "normal"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
links.finish
|
112
|
+
end
|
113
|
+
graph.finish
|
114
|
+
end
|
115
|
+
|
116
|
+
unless system("dot -Tjpg #{self.dotfile} > #{self.jpgfile}")
|
117
|
+
puts "Error: couldn't create jpeg: #{self.jpgfile}"
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
def att_detail(att)
|
123
|
+
mult = att[:multiplicity]
|
124
|
+
mstring = (mult == ZERO_TO_MANY ? "[*]" : mult == ONE_TO_MANY ? "[1..*]" : mult == SINGLE ? "" : "[#{mult.to_s}]")
|
125
|
+
"#{att[:name]} #{mstring}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def modelname m
|
129
|
+
m.name.gsub(/^#{DataModel.name}::/, "").gsub /::/, "-"
|
130
|
+
end
|
131
|
+
|
132
|
+
def typename t
|
133
|
+
t.name.gsub(/^#{t.domain}::/, "").gsub /::/, "-"
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def image_path
|
139
|
+
File.join(self.path, "images")
|
140
|
+
end
|
141
|
+
|
142
|
+
def diagram_path
|
143
|
+
File.join(self.path, "diagrams")
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
data/src/doc.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
class Element
|
2
|
+
def initialize file
|
3
|
+
@file = file
|
4
|
+
end
|
5
|
+
|
6
|
+
def finish
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def pput(string)
|
13
|
+
@file.print string
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def cond_call(depth, id, value, lam, lambdas)
|
20
|
+
if lambdas[lam]
|
21
|
+
return lambdas[lam].(depth, id, value)
|
22
|
+
end
|
23
|
+
return 0
|
24
|
+
end
|
25
|
+
|
26
|
+
# each lambda takes the depth, id, value
|
27
|
+
# Lambdas are:
|
28
|
+
# :before_type_lambda,
|
29
|
+
# :after_type_lambda,
|
30
|
+
# :before_array_lambda,
|
31
|
+
# :after_array_lambda,
|
32
|
+
# :attribute_lambda
|
33
|
+
# which takes a type and returns an object (the id)
|
34
|
+
# depth is the number counting from zero, incremented with array elements and nested types.
|
35
|
+
# ID is a nested int of the type a for types and a.b for array types,
|
36
|
+
# and is the name of the attribute for attributes
|
37
|
+
# decl is the type or attribute or array
|
38
|
+
|
39
|
+
def transform_type depth, id, decl, lambdas
|
40
|
+
|
41
|
+
if decl.class <= Array
|
42
|
+
cond_call(depth, id, decl, :before_array_lambda, lambdas)
|
43
|
+
j = 1
|
44
|
+
for aa in decl
|
45
|
+
transform_type(depth , "#{id}.#{j}", aa, lambdas)
|
46
|
+
j = j + 1
|
47
|
+
end
|
48
|
+
cond_call(depth, id, decl, :after_array_lambda, lambdas)
|
49
|
+
elsif decl.class <= DataType
|
50
|
+
cond_call(depth, id, decl, :before_type_lambda, lambdas)
|
51
|
+
for ak in decl.attributes.keys
|
52
|
+
av = decl.attributes[ak]
|
53
|
+
transform_type(depth + 1, ak, av, lambdas)
|
54
|
+
end
|
55
|
+
cond_call(depth, id, decl, :after_type_lambda, lambdas)
|
56
|
+
else
|
57
|
+
cond_call(depth, id, decl, :attribute_lambda, lambdas)
|
58
|
+
end
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
class ClassNameElement < Element
|
63
|
+
|
64
|
+
def write cat
|
65
|
+
pput %Q!\# #{cat.class}: #{cat.name}\n!
|
66
|
+
if (cat.respond_to?(:description))
|
67
|
+
pput %Q! #{cat.description}\n!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
class GroupElement < Element
|
74
|
+
|
75
|
+
def write grpname, level: 2
|
76
|
+
pput %Q! #{'#' * level} #{grpname}\n!
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
class TypeDeclElement < Element
|
82
|
+
|
83
|
+
def write indent, id, decl
|
84
|
+
transform_type(indent, id, decl,
|
85
|
+
{
|
86
|
+
:before_type_lambda => lambda do |indent, id, decl|
|
87
|
+
pput %Q!####{'#' * indent} #{decl.name} #{decl.attributes[:id] || id} \n!
|
88
|
+
end,
|
89
|
+
:attribute_lambda => lambda do |indent, id, decl|
|
90
|
+
pput %Q!#{" " * indent} - #{id} #{decl}\n!
|
91
|
+
end
|
92
|
+
})
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
class MetaTypeElement < Element
|
99
|
+
|
100
|
+
def write type
|
101
|
+
pput "## #{type.typename}"
|
102
|
+
if type.extends
|
103
|
+
pput " extends #{type.extends}"
|
104
|
+
end
|
105
|
+
pput "\n #{type.description}\n\n"
|
106
|
+
pput "|attribute|type|multiplicity|description|\n"
|
107
|
+
pput "|---------|----|------------|-----------|\n"
|
108
|
+
for a in type.attributes.values
|
109
|
+
pput "|#{a[:name]}|#{a[:type]}|#{self.multiplicity(a)}|#{a[:description]}|\n"
|
110
|
+
end
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def multiplicity m
|
115
|
+
m = m[:multiplicity]
|
116
|
+
if m.end == -1
|
117
|
+
if m.begin == 0
|
118
|
+
return "*"
|
119
|
+
else
|
120
|
+
return "#{m.begin}..*"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
if m.end == m.begin
|
124
|
+
return m.end.to_s
|
125
|
+
end
|
126
|
+
return m.to_s
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Doc
|
131
|
+
attr_accessor :path, :name
|
132
|
+
|
133
|
+
def initialize path, name
|
134
|
+
self.path = path
|
135
|
+
self.name = name
|
136
|
+
end
|
137
|
+
|
138
|
+
def document *models
|
139
|
+
FileUtils.mkpath doc_path
|
140
|
+
File.open(self.docfile, "w") do |file|
|
141
|
+
for model in models
|
142
|
+
dom = ClassNameElement.new(file)
|
143
|
+
dom.write model
|
144
|
+
for typename in model.contents.keys
|
145
|
+
grp = GroupElement.new(file)
|
146
|
+
grp.write typename
|
147
|
+
i = 1
|
148
|
+
for decl in model.contents[typename]
|
149
|
+
TypeDeclElement.new(file).write(0, i, decl).finish
|
150
|
+
i = i + 1
|
151
|
+
end
|
152
|
+
grp.finish
|
153
|
+
end
|
154
|
+
dom.finish
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def document_metamodel *models
|
160
|
+
FileUtils.mkpath doc_path
|
161
|
+
File.open(self.docfile, "w") do |file|
|
162
|
+
for model in models
|
163
|
+
dom = GroupElement.new(file)
|
164
|
+
dom.write model.name, level: 1
|
165
|
+
for type in model.types.values
|
166
|
+
MetaTypeElement.new(file).write(type).finish
|
167
|
+
end
|
168
|
+
dom.finish
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def docfile
|
174
|
+
File.join(doc_path, "#{self.name}.md")
|
175
|
+
end
|
176
|
+
|
177
|
+
def doc_path
|
178
|
+
File.join(self.path, "doc")
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require_relative '../src/data_model'
|
3
|
+
include DataModel
|
4
|
+
|
5
|
+
DataModel::domain :TestMetamodel do
|
6
|
+
|
7
|
+
datatype :BasicType do
|
8
|
+
attribute :id, String
|
9
|
+
end
|
10
|
+
|
11
|
+
datatype :Table, description: "Test type" do
|
12
|
+
MULT = [0..10]
|
13
|
+
DESC = "table of values"
|
14
|
+
attribute :vals, Integer,
|
15
|
+
multiplicity: MULT,
|
16
|
+
:description => DESC
|
17
|
+
attribute :morevals, String, 2..5, "array of strings"
|
18
|
+
end
|
19
|
+
|
20
|
+
datatype :ReferencingType do
|
21
|
+
attribute :id, String
|
22
|
+
attribute :mate, TestMetamodel::BasicType
|
23
|
+
end
|
24
|
+
|
25
|
+
datatype :DerivedType, extends: TestMetamodel::ReferencingType do
|
26
|
+
attribute :more, String
|
27
|
+
end
|
28
|
+
|
29
|
+
datatype :DerivedTypeNamingClass, extends: TestMetamodel::ReferencingType do
|
30
|
+
attribute :othermore, String
|
31
|
+
end
|
32
|
+
|
33
|
+
datatype :Empty do
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class DataModelTest < Test::Unit::TestCase
|
40
|
+
|
41
|
+
|
42
|
+
def test_metamodel
|
43
|
+
assert(contains(TestMetamodel::BasicType, :id), 'has id attribute')
|
44
|
+
id_t = TestMetamodel::BasicType.attributes[:id]
|
45
|
+
assert_equal(1..1, TestMetamodel::BasicType.attributes[:id][:multiplicity], "has default multiplicity")
|
46
|
+
assert(id_t[:type] == String)
|
47
|
+
|
48
|
+
assert_equal(MULT, TestMetamodel::Table.attributes[:vals][:multiplicity]);
|
49
|
+
assert_equal(2..5, TestMetamodel::Table.attributes[:morevals][:multiplicity]);
|
50
|
+
assert_equal("Test type", TestMetamodel::Table.description);
|
51
|
+
|
52
|
+
assert_equal(MULT, TestMetamodel::Table.attributes[:vals][:multiplicity], "has multiplicity")
|
53
|
+
assert_equal(DESC, TestMetamodel::Table.attributes[:vals][:description], "has description")
|
54
|
+
|
55
|
+
mate_t = TestMetamodel::ReferencingType.attributes[:mate]
|
56
|
+
assert(mate_t[:type] == TestMetamodel::BasicType)
|
57
|
+
|
58
|
+
assert(contains(TestMetamodel::DerivedType, :more), "has derived attribute")
|
59
|
+
assert(contains(TestMetamodel::DerivedType, :mate), "has derived attribute")
|
60
|
+
assert(contains(TestMetamodel::DerivedType, :id), "has derived attribute")
|
61
|
+
assert(contains(TestMetamodel::DerivedTypeNamingClass, :othermore), "has derived attribute")
|
62
|
+
assert(contains(TestMetamodel::DerivedTypeNamingClass, :id), "has derived attribute")
|
63
|
+
end
|
64
|
+
|
65
|
+
TestMetamodel.new :Test_Model do
|
66
|
+
table do
|
67
|
+
vals 1
|
68
|
+
vals 2
|
69
|
+
end
|
70
|
+
referencingtype do
|
71
|
+
id :owner
|
72
|
+
mate do
|
73
|
+
id :content
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_model
|
79
|
+
assert_equal(1, TestMetamodel::Test_Model.contents[:table][0].attributes[:vals][0], "has vals")
|
80
|
+
assert_equal(2, TestMetamodel::Test_Model.contents[:table][0].attributes[:vals][1], "has vals")
|
81
|
+
assert_equal(:content, TestMetamodel::Test_Model.contents[:referencingtype][0].attributes[:mate].attributes[:id], "has vals")
|
82
|
+
assert_equal(:content, TestMetamodel::Test_Model.contents[:referencingtype][0].mate.id, "has vals")
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def contains(type, attr)
|
88
|
+
type.attributes.keys.one? {|k| k == attr}
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require_relative '../src/diagram'
|
3
|
+
require_relative '../model/agreement'
|
4
|
+
include DataModel
|
5
|
+
|
6
|
+
|
7
|
+
class DiagramTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
# Called before every test method runs. Can be used
|
10
|
+
# to set up fixture information.
|
11
|
+
PATH = "out/test/"
|
12
|
+
NAME = "d"
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@d = Diagram.new(PATH, NAME)
|
16
|
+
if File.file?(@d.dotfile)
|
17
|
+
File.delete(@d.dotfile)
|
18
|
+
end
|
19
|
+
if File.file?(@d.jpgfile)
|
20
|
+
File.delete(@d.jpgfile)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_dot
|
25
|
+
assert_equal("out/test/diagrams/d.dot", @d.dotfile, "file format")
|
26
|
+
assert_equal("out/test/images/d.jpg", @d.jpgfile, "file format")
|
27
|
+
assert(!File.file?(@d.dotfile), "no dotfile")
|
28
|
+
@d.describe( Category)
|
29
|
+
assert(File.file?(@d.dotfile), "file created")
|
30
|
+
assert(File.file?(@d.jpgfile), "file created")
|
31
|
+
# TODO test the features in the diagram
|
32
|
+
@d.describe( Category, Parties)
|
33
|
+
end
|
34
|
+
end
|
data/test/doc_test.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require_relative '../src/doc'
|
3
|
+
require_relative '../model/fm'
|
4
|
+
include DataModel
|
5
|
+
|
6
|
+
class DocTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
# Called before every test method runs. Can be used
|
9
|
+
# to set up fixture information.
|
10
|
+
PATH = "out/test/"
|
11
|
+
NAME = "doctest"
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@d = Doc.new(PATH, NAME)
|
15
|
+
if File.file?(@d.docfile)
|
16
|
+
File.delete(@d.docfile)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def test_dot
|
22
|
+
assert_equal("#{PATH}doc/#{NAME}.md", @d.docfile, "file name")
|
23
|
+
assert(!File.file?(@d.docfile), "no dotfile")
|
24
|
+
@d.document( Category::FM)
|
25
|
+
assert(File.file?(@d.docfile), "file created")
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: agreement-design-prototype
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- CCS
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-08-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 12.3.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 12.3.0
|
27
|
+
description: A prototype of techniques to manage agreement specifications.
|
28
|
+
email:
|
29
|
+
- rubygems@humphries.tech
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- "./README.md"
|
35
|
+
- "./Rakefile"
|
36
|
+
- "./agreement-design.gemspec"
|
37
|
+
- "./build/build_models.rb"
|
38
|
+
- "./gen/diagrams/data_model.dot"
|
39
|
+
- "./gen/doc/frameworks.md"
|
40
|
+
- "./gen/doc/metamodel.md"
|
41
|
+
- "./gen/images/data_model.jpg"
|
42
|
+
- "./model/agreement.rb"
|
43
|
+
- "./model/fm.rb"
|
44
|
+
- "./model/party.rb"
|
45
|
+
- "./out/test/diagrams/d.dot"
|
46
|
+
- "./out/test/doc/doctest.md"
|
47
|
+
- "./out/test/images/d.jpg"
|
48
|
+
- "./src/data_model.rb"
|
49
|
+
- "./src/diagram.rb"
|
50
|
+
- "./src/doc.rb"
|
51
|
+
- "./test/data_model_test.rb"
|
52
|
+
- "./test/diagram_test.rb"
|
53
|
+
- "./test/doc_test.rb"
|
54
|
+
homepage: https://github.com/Crown-Commercial-Service/cmp-design-prototype/agreement-design
|
55
|
+
licenses:
|
56
|
+
- MIT
|
57
|
+
metadata: {}
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- src
|
62
|
+
- model
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 2.7.6
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: Agreement prototype .
|
79
|
+
test_files:
|
80
|
+
- "./test/diagram_test.rb"
|
81
|
+
- "./test/data_model_test.rb"
|
82
|
+
- "./test/doc_test.rb"
|