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