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.
data/model/fm.rb CHANGED
@@ -1,33 +1,65 @@
1
1
  require_relative 'agreement'
2
2
 
3
- Category.new :FM do
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
- FM_ID = "RM123"
6
- framework do
10
+ Agreements.new :FM_Agreements do
11
+
12
+ FM_ID = "FM"
13
+ agreement {
14
+ kind :Framework
7
15
  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
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
- puts Category::FM.contents[:lot]
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
 
@@ -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 :Parties do
4
+ domain(:Parties) {
5
5
 
6
- datatype :Party do
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
- 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
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"}]}
@@ -1,34 +1,20 @@
1
1
  strict digraph {
2
- subgraph cluster_Category {
2
+ subgraph cluster_TestModel {
3
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>>];
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
- 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" ];
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
  }
@@ -1,19 +1,17 @@
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
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
+ |---------|----|------------|-----------|
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
- if links
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
- end
64
+ private
62
65
 
63
- attr_reader :name, :attributes
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 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)
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
- puts "Warning: unknown attribute #{sym}?"
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
- @types = {} unless instance_variable_defined? :@types
113
- if extends.class != Class
114
- extends = @types.fetch(extends, DataType)
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
- 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
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
- puts "Warning unknown type #{sym} ignored"
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