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.
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