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