agreement-design-prototype 0.0.3 → 0.0.4
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 +4 -4
- data/README.md +15 -0
- data/agreement-design.gemspec +2 -1
- data/build/build_models.rb +24 -9
- data/gen/data/data.json +1 -0
- data/gen/data/data.yaml +46 -0
- data/gen/data/fm_agreements.jsonlines +2 -0
- data/gen/data/fm_catalogue.jsonlines +1 -0
- data/gen/diagrams/metamodel.dot +45 -0
- data/gen/doc/data.md +44 -0
- data/gen/doc/metamodel.md +99 -57
- data/gen/images/metamodel.jpg +0 -0
- data/model/agreement.rb +95 -43
- data/model/fm.rb +56 -24
- data/model/geographic.rb +30 -0
- data/model/party.rb +14 -12
- data/out/test/data/datatest.json +1 -0
- data/out/test/diagrams/d.dot +16 -30
- data/out/test/doc/doctest.md +17 -19
- data/out/test/doc/modeldoctest.md +62 -0
- data/out/test/images/d.jpg +0 -0
- data/src/api.rb +33 -0
- data/src/data.rb +89 -0
- data/src/data_model.rb +102 -38
- data/src/diagram.rb +61 -95
- data/src/doc.rb +55 -151
- data/src/transform.rb +144 -0
- data/test/data_model_test.rb +43 -65
- data/test/data_test.rb +26 -0
- data/test/diagram_test.rb +2 -5
- data/test/doc_test.rb +15 -9
- data/test/test_model.rb +88 -0
- data/test/transform_test.rb +83 -0
- metadata +21 -5
- data/gen/diagrams/data_model.dot +0 -34
- data/gen/doc/frameworks.md +0 -19
- data/gen/images/data_model.jpg +0 -0
data/model/fm.rb
CHANGED
@@ -1,33 +1,65 @@
|
|
1
1
|
require_relative 'agreement'
|
2
2
|
|
3
|
-
|
3
|
+
domain(:FM) {
|
4
|
+
datatype(:FM_Offering, extends: Agreements::Offering,
|
5
|
+
description: " An offer for FM elements ") {
|
6
|
+
attribute :sc_cleared, String
|
7
|
+
}
|
8
|
+
}
|
4
9
|
|
5
|
-
|
6
|
-
|
10
|
+
Agreements.new :FM_Agreements do
|
11
|
+
|
12
|
+
FM_ID = "FM"
|
13
|
+
agreement {
|
14
|
+
kind :Framework
|
7
15
|
id FM_ID
|
8
|
-
|
9
|
-
version "1.0
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
16
|
+
fwk_number "RM8330"
|
17
|
+
version "0.1.0"
|
18
|
+
description "This agreement is for the provision of Facilities Management"
|
19
|
+
start_date date(2018, 10, 01)
|
20
|
+
}
|
21
|
+
|
22
|
+
ENV_CLEANING = "#{FM_ID}.1a-C.3"
|
23
|
+
agreement {
|
24
|
+
kind :Lot
|
25
|
+
id "FM lot 1"
|
26
|
+
name "low-to-mid value Facilities Management Lot"
|
27
|
+
part_of_id FM_ID
|
28
|
+
min_value 0
|
29
|
+
max_value 70000000
|
30
|
+
version "0.1.0"
|
31
|
+
item_type {
|
32
|
+
id ENV_CLEANING
|
33
|
+
scheme_id :CCS
|
34
|
+
unit :Currency
|
35
|
+
code "CCS-building-area-method"
|
36
|
+
keyword "cleaning"
|
37
|
+
keyword "washing"
|
38
|
+
keyword "janitor"
|
39
|
+
}
|
40
|
+
}
|
28
41
|
|
29
42
|
end
|
30
43
|
|
31
|
-
|
44
|
+
FM.new(:FM_Catalogue) {
|
45
|
+
fm_offering {
|
46
|
+
supplier_id "XYZ corp"
|
47
|
+
name "XYZ's nifty school cleaning service"
|
48
|
+
agreement_id FM_ID
|
49
|
+
location_id UK.name
|
50
|
+
sector :Education
|
51
|
+
sc_cleared "TRUE"
|
52
|
+
item {
|
53
|
+
type ENV_CLEANING
|
54
|
+
value 3000
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
32
64
|
|
33
65
|
|
data/model/geographic.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../src/data_model'
|
2
|
+
include DataModel
|
3
|
+
|
4
|
+
domain :Geographic do
|
5
|
+
|
6
|
+
datatype :AreaCode do
|
7
|
+
attribute :name, String
|
8
|
+
attribute :description, String
|
9
|
+
attribute :subcode, :AreaCode, ZERO_TO_MANY, "UUID or Salesforce ID?"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
#TODO load these from NUTS config file - the following is just an example
|
15
|
+
Geographic.new :NUTS do
|
16
|
+
|
17
|
+
# made up codes
|
18
|
+
UK = areacode do
|
19
|
+
name :UK
|
20
|
+
NE = subcode do
|
21
|
+
name :UKC; description "North East";
|
22
|
+
subcode do
|
23
|
+
name :UKC1; description "Tees Valley and County Durham";
|
24
|
+
end
|
25
|
+
end
|
26
|
+
London = subcode do
|
27
|
+
name :UKL; description "London";
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/model/party.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
require_relative '../src/data_model'
|
2
2
|
include DataModel
|
3
3
|
|
4
|
-
domain
|
4
|
+
domain(:Parties) {
|
5
5
|
|
6
|
-
datatype
|
6
|
+
datatype(:Party, description: "
|
7
|
+
The party is used to identify buyers and suppliers. Since some organisations act as
|
8
|
+
both buyers and suppliers we use the same record for both, but most organisations will
|
9
|
+
be one or the other. The onvolvement of the party with an agreement determine the role in
|
10
|
+
that contenxt.
|
11
|
+
Details still to be added") {
|
7
12
|
attribute :id, String, "UUID or Salesforce ID?"
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
13
|
+
#TODO add all the usual fields
|
14
|
+
# Supplier registration
|
15
|
+
attribute :supplier_registration_completed, Date
|
16
|
+
# Buyer registration
|
17
|
+
attribute :buyer_registration_completed, Date
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"complextype":[{"string":"ID1","things":[{"thingname":"thing1"},{"thingname":"thing2"}]}],"anothertype":[{"string":"Anotherthing"}],"table":[{"vals":[1,2]}],"referencingtype":[{"id":"owner","mate":{"id":"content"}}],"kindly":[{"kind":"Framework"}]}
|
data/out/test/diagrams/d.dot
CHANGED
@@ -1,34 +1,20 @@
|
|
1
1
|
strict digraph {
|
2
|
-
subgraph
|
2
|
+
subgraph cluster_TestModel {
|
3
3
|
node [shape=plaintext margin=0];
|
4
|
-
label=
|
5
|
-
"
|
6
|
-
"
|
7
|
-
"
|
8
|
-
"
|
9
|
-
"
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
4
|
+
label=TestModel;
|
5
|
+
"ArrayParam" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>ArrayParam</TD></TH><TR><TD ALIGN="LEFT">-thingname</TD></TR></table>>];
|
6
|
+
"Table" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Table</TD></TH><TR><TD ALIGN="LEFT">-vals</TD></TR><TR><TD ALIGN="LEFT">-morevals</TD></TR></table>>];
|
7
|
+
"BasicType" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>BasicType</TD></TH><TR><TD ALIGN="LEFT">-id</TD></TR></table>>];
|
8
|
+
"Kindly" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Kindly</TD></TH><TR><TD ALIGN="LEFT">-kind</TD></TR></table>>];
|
9
|
+
"ReferencingType" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>ReferencingType</TD></TH><TR><TD ALIGN="LEFT">-id</TD></TR><TR><TD ALIGN="LEFT">-mate</TD></TR></table>>];
|
10
|
+
"ComplexType" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>ComplexType</TD></TH><TR><TD ALIGN="LEFT">-string</TD></TR><TR><TD ALIGN="LEFT">-things</TD></TR><TR><TD ALIGN="LEFT">-thing_id</TD></TR><TR><TD ALIGN="LEFT">-strings</TD></TR><TR><TD ALIGN="LEFT">-mustbeafter</TD></TR></table>>];
|
11
|
+
"AnotherType" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>AnotherType</TD></TH><TR><TD ALIGN="LEFT">-string</TD></TR></table>>];
|
12
|
+
"DerivedType" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>DerivedType</TD></TH><TR><TD ALIGN="LEFT">-id</TD></TR><TR><TD ALIGN="LEFT">-mate</TD></TR><TR><TD ALIGN="LEFT">-more</TD></TR></table>>];
|
13
|
+
"Empty" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>Empty</TD></TH></table>>];
|
13
14
|
}
|
14
|
-
|
15
|
-
|
16
|
-
label=
|
17
|
-
"
|
18
|
-
"
|
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" ];
|
15
|
+
"ReferencingType" -> "BasicType" [label="{contains} mate" arrowhead = "none" arrowtail = "diamond" ];
|
16
|
+
"ComplexType" -> "ArrayParam" [label="{contains} things" arrowhead = "none" arrowtail = "diamond" ];
|
17
|
+
"ComplexType" -> "ArrayParam" [label="thing_id" arrowhead = "open" arrowtail = "none" ];
|
18
|
+
"DerivedType" -> "ReferencingType" [label="extends" arrowhead = "none" arrowtail = "normal" ];
|
19
|
+
"DerivedType" -> "BasicType" [label="{contains} mate" arrowhead = "none" arrowtail = "diamond" ];
|
34
20
|
}
|
data/out/test/doc/doctest.md
CHANGED
@@ -1,19 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
-
|
16
|
-
|
17
|
-
|
18
|
-
- id 2
|
19
|
-
- fwk_id RM123
|
1
|
+
### complextype
|
2
|
+
- string ID1
|
3
|
+
#### things
|
4
|
+
- thingname thing1
|
5
|
+
#### things
|
6
|
+
- thingname thing2
|
7
|
+
### anothertype
|
8
|
+
- string Anotherthing
|
9
|
+
### table
|
10
|
+
- vals 1
|
11
|
+
- vals 2
|
12
|
+
### referencingtype owner
|
13
|
+
- id owner
|
14
|
+
#### mate content
|
15
|
+
- id content
|
16
|
+
### kindly
|
17
|
+
- kind Framework
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Data model: TestModel
|
2
|
+
## ArrayParam
|
3
|
+
Thing
|
4
|
+
|
5
|
+
|attribute|type|multiplicity|description|
|
6
|
+
|---------|----|------------|-----------|
|
7
|
+
|thingname|String|1||
|
8
|
+
## Table
|
9
|
+
Test type
|
10
|
+
|
11
|
+
|attribute|type|multiplicity|description|
|
12
|
+
|---------|----|------------|-----------|
|
13
|
+
|vals|Integer|0..10|table of values|
|
14
|
+
|morevals|String|2..5|array of strings|
|
15
|
+
## BasicType
|
16
|
+
|
17
|
+
|
18
|
+
|attribute|type|multiplicity|description|
|
19
|
+
|---------|----|------------|-----------|
|
20
|
+
|id|String|1||
|
21
|
+
## Kindly
|
22
|
+
|
23
|
+
|
24
|
+
|attribute|type|multiplicity|description|
|
25
|
+
|---------|----|------------|-----------|
|
26
|
+
|kind|(Framework,Lot,Contract)|1|semantic version id of the form X.Y.Z|
|
27
|
+
## ReferencingType
|
28
|
+
|
29
|
+
|
30
|
+
|attribute|type|multiplicity|description|
|
31
|
+
|---------|----|------------|-----------|
|
32
|
+
|id|String|1||
|
33
|
+
|mate|TestModel::BasicType|1||
|
34
|
+
## ComplexType
|
35
|
+
Thing with things
|
36
|
+
|
37
|
+
|attribute|type|multiplicity|description|
|
38
|
+
|---------|----|------------|-----------|
|
39
|
+
|string|String|1||
|
40
|
+
|things|TestModel::ArrayParam|*||
|
41
|
+
|thing_id|String -> TestModel::ArrayParam|*||
|
42
|
+
|strings|String|*||
|
43
|
+
|mustbeafter|Date|1||
|
44
|
+
## AnotherType
|
45
|
+
Thing with things
|
46
|
+
|
47
|
+
|attribute|type|multiplicity|description|
|
48
|
+
|---------|----|------------|-----------|
|
49
|
+
|string|String|1||
|
50
|
+
## DerivedType extends TestModel::ReferencingType
|
51
|
+
|
52
|
+
|
53
|
+
|attribute|type|multiplicity|description|
|
54
|
+
|---------|----|------------|-----------|
|
55
|
+
|id|String|1||
|
56
|
+
|mate|TestModel::BasicType|1||
|
57
|
+
|more|String|1||
|
58
|
+
## Empty
|
59
|
+
|
60
|
+
|
61
|
+
|attribute|type|multiplicity|description|
|
62
|
+
|---------|----|------------|-----------|
|
data/out/test/images/d.jpg
CHANGED
Binary file
|
data/src/api.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative 'transform'
|
2
|
+
include Transform
|
3
|
+
|
4
|
+
class API < Output
|
5
|
+
|
6
|
+
# Not implemented yet
|
7
|
+
#
|
8
|
+
# we will spit out OpenAPI format files and stubs
|
9
|
+
#
|
10
|
+
# TODO : define API endpoint commands, similar to datatype commands, in domain
|
11
|
+
|
12
|
+
# def openapi2 *models
|
13
|
+
# FileUtils.mkpath doc_path
|
14
|
+
# File.open(self.docfile, "w") do |file|
|
15
|
+
# transform_datamodel(
|
16
|
+
# {
|
17
|
+
# :before_type => lambda do |type:, depth: 0|
|
18
|
+
# end,
|
19
|
+
# :attribute => lambda do |id:, val:, depth: 0, type: nil|
|
20
|
+
# end
|
21
|
+
# }, *models)
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def docfile
|
26
|
+
# File.join(doc_path, "#{self.name}.json")
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def doc_path
|
30
|
+
# File.join(self.path, "api")
|
31
|
+
# end
|
32
|
+
|
33
|
+
end
|
data/src/data.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative 'transform'
|
2
|
+
require 'pp'
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
include Transform
|
7
|
+
|
8
|
+
class DataFile < Output
|
9
|
+
|
10
|
+
attr_accessor :fmt
|
11
|
+
|
12
|
+
def initialize dir, name, fmt: :json
|
13
|
+
super File.join(dir, "data"), name,
|
14
|
+
(fmt == :json ? "json" : (fmt == :jsonlines ? "jsonlines" : "yaml"))
|
15
|
+
self.fmt = fmt
|
16
|
+
end
|
17
|
+
|
18
|
+
def output *models
|
19
|
+
|
20
|
+
map = Hash.new
|
21
|
+
stack = [map]
|
22
|
+
transform_datamodel(
|
23
|
+
{
|
24
|
+
:before_group => lambda do |name:, depth:|
|
25
|
+
last = stack.last
|
26
|
+
n = Array.new
|
27
|
+
last[name] = n
|
28
|
+
stack.push(n) # add a new container to the stack to use next
|
29
|
+
end,
|
30
|
+
:before_type => lambda do |type:, depth:, index:, total:|
|
31
|
+
last = stack.last
|
32
|
+
n = Hash.new
|
33
|
+
stack.push(n) # add a new container to the stack to use next
|
34
|
+
if last.class <= Hash
|
35
|
+
last[type.name] = n
|
36
|
+
elsif last.class <= Array
|
37
|
+
last.push(n)
|
38
|
+
end
|
39
|
+
end,
|
40
|
+
:before_array => lambda do |name:, decl:, depth:, total:|
|
41
|
+
last = stack.last
|
42
|
+
n = Array.new
|
43
|
+
last[name] = n
|
44
|
+
stack.push(n) # add a new container to the stack to use next
|
45
|
+
end,
|
46
|
+
:attribute => lambda do |id:, val:, depth:, type:, index:, total:|
|
47
|
+
last = stack.last
|
48
|
+
if last.class <= Hash
|
49
|
+
last[id] = val
|
50
|
+
elsif last.class <= Array
|
51
|
+
last.push val
|
52
|
+
end
|
53
|
+
end,
|
54
|
+
:after_group => lambda do |name:, depth:, before:|
|
55
|
+
stack.pop
|
56
|
+
end,
|
57
|
+
:after_type => lambda do |type:, depth:, before:|
|
58
|
+
stack.pop
|
59
|
+
end,
|
60
|
+
:after_array => lambda do |index:, decl:, depth:, before: nil|
|
61
|
+
stack.pop
|
62
|
+
end,
|
63
|
+
}, *models)
|
64
|
+
|
65
|
+
file do |file|
|
66
|
+
if fmt == :jsonlines
|
67
|
+
for type in map.keys
|
68
|
+
for decl in map[type]
|
69
|
+
file.print(JSON.generate(decl))
|
70
|
+
file.print("\n")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
elsif fmt == :json
|
74
|
+
file.print(JSON.generate(map))
|
75
|
+
elsif fmt == :yaml
|
76
|
+
file.print(map.to_yaml)
|
77
|
+
else
|
78
|
+
raise "unknown data file format"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def indent(depth)
|
88
|
+
" " * depth
|
89
|
+
end
|
data/src/data_model.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
1
3
|
module DataModel
|
2
4
|
|
3
5
|
SINGLE = 1..1
|
6
|
+
ZERO_OR_ONE = 0..1
|
4
7
|
ONE_TO_MANY = 1..-1
|
5
8
|
ZERO_TO_MANY = 0..-1
|
6
9
|
|
@@ -22,7 +25,7 @@ module DataModel
|
|
22
25
|
@description
|
23
26
|
end
|
24
27
|
|
25
|
-
self.define_singleton_method(:attributes) do |inherited=true|
|
28
|
+
self.define_singleton_method(:attributes) do |inherited = true|
|
26
29
|
if inherited && self.superclass.respond_to?(:attributes)
|
27
30
|
self.superclass.attributes.merge @attributes
|
28
31
|
else
|
@@ -41,10 +44,9 @@ module DataModel
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def attribute(name, type, *args, multiplicity: SINGLE, description: "", links: nil)
|
47
|
+
type = getType(type)
|
44
48
|
options = {:multiplicity => multiplicity, :description => description, :name => name, :type => type}
|
45
|
-
|
46
|
-
options[:links]= links
|
47
|
-
end
|
49
|
+
options[:links] = getType(links)
|
48
50
|
|
49
51
|
for opt in args
|
50
52
|
if opt.is_a? Range
|
@@ -55,33 +57,46 @@ module DataModel
|
|
55
57
|
raise "optional arguments should be string (description), or range\n " << opt.to_s
|
56
58
|
end
|
57
59
|
end
|
60
|
+
|
58
61
|
@attributes[name] = options
|
59
62
|
end
|
60
63
|
|
61
|
-
|
64
|
+
private
|
62
65
|
|
63
|
-
|
66
|
+
def getType(typeref)
|
67
|
+
domain.getType typeref
|
68
|
+
end
|
64
69
|
|
65
|
-
def initialize(name)
|
66
|
-
@name = name
|
67
|
-
@attributes = {}
|
68
70
|
end
|
69
71
|
|
72
|
+
attr_reader :name, :attributes
|
70
73
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
74
|
+
def initialize(name, attributes = {}, &block)
|
75
|
+
@name = name
|
76
|
+
@attributes = attributes
|
77
|
+
|
78
|
+
self.class.attributes.keys.each do |k|
|
79
|
+
# add a definition / accessor for each attribute
|
80
|
+
self.define_singleton_method(k) do |*args, &block|
|
81
|
+
if args.length == 0 && !block
|
82
|
+
unless @attributes[k]
|
83
|
+
puts ("Warning - reading unset attribute '#{k}' on #{self.class}")
|
84
|
+
end
|
85
|
+
return @attributes[k]
|
86
|
+
end
|
87
|
+
if self.class.attributes[k][:multiplicity].end != 1
|
88
|
+
@attributes[k] = [] unless @attributes[k]
|
89
|
+
@attributes[k] << valueof(k, *args, &block)
|
90
|
+
else
|
91
|
+
@attributes[k] = valueof(k, *args, &block)
|
92
|
+
end
|
93
|
+
return @attributes[k]
|
81
94
|
end
|
82
|
-
return @attributes[sym]
|
83
95
|
end
|
84
|
-
|
96
|
+
|
97
|
+
if block_given?
|
98
|
+
self.instance_exec &block
|
99
|
+
end
|
85
100
|
end
|
86
101
|
|
87
102
|
def to_s
|
@@ -94,7 +109,7 @@ module DataModel
|
|
94
109
|
if self.class.attributes[sym][:type] < DataType
|
95
110
|
at = self.class.attributes[sym][:type].new(self.class.attributes[sym][:name])
|
96
111
|
if nil == block
|
97
|
-
raise "Need a block for a nested type #{sym}"
|
112
|
+
raise "Need a block for a nested type #{sym} in #{self}"
|
98
113
|
end
|
99
114
|
at.instance_exec &block
|
100
115
|
return at
|
@@ -109,14 +124,13 @@ module DataModel
|
|
109
124
|
|
110
125
|
class << self
|
111
126
|
def datatype(name, extends: DataType, description: "", &block)
|
112
|
-
|
113
|
-
|
114
|
-
|
127
|
+
unless instance_variable_defined? :@types
|
128
|
+
@types = {}
|
129
|
+
self.define_singleton_method(:types) {@types}
|
115
130
|
end
|
131
|
+
extends = getType(extends)
|
116
132
|
type = self.const_set name, Class.new(extends)
|
117
|
-
# puts "defined #{type} from #{name} on #{self }"
|
118
133
|
@types[name] = type
|
119
|
-
self.define_singleton_method(:types) {@types}
|
120
134
|
dom = self
|
121
135
|
type.instance_exec do
|
122
136
|
init name, dom, extends, description
|
@@ -125,6 +139,32 @@ module DataModel
|
|
125
139
|
type
|
126
140
|
end
|
127
141
|
|
142
|
+
def getType(typeref)
|
143
|
+
if !typeref
|
144
|
+
return nil
|
145
|
+
end
|
146
|
+
if typeref.class == Symbol
|
147
|
+
if !types[typeref]
|
148
|
+
raise "Can't find type #{typeref} in #{typeref}"
|
149
|
+
end
|
150
|
+
return types[typeref]
|
151
|
+
elsif typeref.class == Class
|
152
|
+
return typeref
|
153
|
+
else
|
154
|
+
raise "type refs must be symbol or DataType class"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def code( *context,
|
159
|
+
description: nil, title: nil, uri: nil)
|
160
|
+
unless instance_variable_defined? :@codes
|
161
|
+
@codes = {}
|
162
|
+
self.define_singleton_method(:codes) {@codes}
|
163
|
+
end
|
164
|
+
id = "#{context.join '.'}"
|
165
|
+
@codes[id] = {:id => id, :description => description, :title => title, :uri => uri}
|
166
|
+
end
|
167
|
+
|
128
168
|
end
|
129
169
|
|
130
170
|
attr_reader :contents, :name
|
@@ -133,21 +173,31 @@ module DataModel
|
|
133
173
|
@contents = {}
|
134
174
|
@name = name
|
135
175
|
self.class.const_set name, self
|
136
|
-
self.instance_exec &block
|
137
|
-
end
|
138
176
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
177
|
+
self.class.types.values.each do |t|
|
178
|
+
# add a definition / accessor for each type
|
179
|
+
k = t.typename.downcase
|
180
|
+
self.define_singleton_method(k.to_sym) do |*args, &block|
|
181
|
+
if args.length == 0 && !block
|
182
|
+
unless @contents[k]
|
183
|
+
raise("reading unset type decl #{k} on #{self}")
|
184
|
+
end
|
185
|
+
return @contents[k]
|
186
|
+
end
|
187
|
+
decl = t.new(k)
|
188
|
+
@contents[k] = [] unless @contents[k]
|
189
|
+
@contents[k] << decl
|
190
|
+
if block_given?
|
191
|
+
decl.instance_exec &block
|
192
|
+
else
|
193
|
+
puts "warning: no block given for type #{k} in #{self}"
|
194
|
+
end
|
147
195
|
return decl
|
148
196
|
end
|
197
|
+
|
149
198
|
end
|
150
|
-
|
199
|
+
|
200
|
+
self.instance_exec &block
|
151
201
|
end
|
152
202
|
|
153
203
|
end
|
@@ -161,5 +211,19 @@ module DataModel
|
|
161
211
|
return dom
|
162
212
|
end
|
163
213
|
|
214
|
+
module_function
|
215
|
+
|
216
|
+
def date(day, month, year)
|
217
|
+
Date.new(day, month, year)
|
218
|
+
end
|
219
|
+
|
220
|
+
def Selection(*args)
|
221
|
+
typename = "Selection_#{args.join '_'}"
|
222
|
+
selclass = Object.const_set typename, Class.new(Symbol)
|
223
|
+
selclass.define_singleton_method(:selection) {args}
|
224
|
+
selclass.define_singleton_method(:validate) {|s| selection.member? s.to_sym}
|
225
|
+
selclass.define_singleton_method(:to_s) {"(#{self.selection.join(',')})"}
|
226
|
+
return selclass
|
227
|
+
end
|
164
228
|
|
165
229
|
end # DataModel
|