activefacts 0.7.0 → 0.7.1

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.
Files changed (60) hide show
  1. data/README.rdoc +0 -3
  2. data/Rakefile +7 -5
  3. data/bin/afgen +5 -2
  4. data/bin/cql +3 -2
  5. data/examples/CQL/Genealogy.cql +3 -3
  6. data/examples/CQL/Metamodel.cql +10 -7
  7. data/examples/CQL/MultiInheritance.cql +2 -1
  8. data/examples/CQL/OilSupply.cql +4 -4
  9. data/examples/CQL/Orienteering.cql +2 -2
  10. data/lib/activefacts.rb +2 -1
  11. data/lib/activefacts/api.rb +21 -2
  12. data/lib/activefacts/api/concept.rb +52 -39
  13. data/lib/activefacts/api/constellation.rb +8 -6
  14. data/lib/activefacts/api/entity.rb +41 -37
  15. data/lib/activefacts/api/instance.rb +5 -3
  16. data/lib/activefacts/api/numeric.rb +28 -21
  17. data/lib/activefacts/api/role.rb +29 -43
  18. data/lib/activefacts/api/standard_types.rb +8 -3
  19. data/lib/activefacts/api/support.rb +4 -4
  20. data/lib/activefacts/api/value.rb +9 -3
  21. data/lib/activefacts/api/vocabulary.rb +17 -7
  22. data/lib/activefacts/cql.rb +10 -7
  23. data/lib/activefacts/cql/CQLParser.treetop +6 -0
  24. data/lib/activefacts/cql/Concepts.treetop +32 -26
  25. data/lib/activefacts/cql/DataTypes.treetop +6 -0
  26. data/lib/activefacts/cql/Expressions.treetop +6 -0
  27. data/lib/activefacts/cql/FactTypes.treetop +6 -0
  28. data/lib/activefacts/cql/Language/English.treetop +9 -3
  29. data/lib/activefacts/cql/LexicalRules.treetop +6 -0
  30. data/lib/activefacts/cql/Rakefile +8 -0
  31. data/lib/activefacts/cql/parser.rb +4 -2
  32. data/lib/activefacts/generate/absorption.rb +20 -28
  33. data/lib/activefacts/generate/cql.rb +28 -16
  34. data/lib/activefacts/generate/cql/html.rb +327 -321
  35. data/lib/activefacts/generate/null.rb +7 -3
  36. data/lib/activefacts/generate/oo.rb +19 -15
  37. data/lib/activefacts/generate/ordered.rb +457 -461
  38. data/lib/activefacts/generate/ruby.rb +12 -4
  39. data/lib/activefacts/generate/sql/server.rb +42 -10
  40. data/lib/activefacts/generate/text.rb +7 -3
  41. data/lib/activefacts/input/cql.rb +55 -28
  42. data/lib/activefacts/input/orm.rb +32 -22
  43. data/lib/activefacts/persistence.rb +5 -0
  44. data/lib/activefacts/persistence/columns.rb +66 -32
  45. data/lib/activefacts/persistence/foreignkey.rb +29 -5
  46. data/lib/activefacts/persistence/index.rb +57 -25
  47. data/lib/activefacts/persistence/reference.rb +65 -30
  48. data/lib/activefacts/persistence/tables.rb +28 -17
  49. data/lib/activefacts/support.rb +8 -0
  50. data/lib/activefacts/version.rb +7 -1
  51. data/lib/activefacts/vocabulary.rb +4 -2
  52. data/lib/activefacts/vocabulary/extensions.rb +12 -10
  53. data/lib/activefacts/vocabulary/metamodel.rb +24 -23
  54. data/spec/api/autocounter.rb +2 -2
  55. data/spec/api/entity_type.rb +2 -2
  56. data/spec/api/instance.rb +61 -30
  57. data/spec/api/roles.rb +9 -9
  58. data/spec/cql_parse_spec.rb +1 -0
  59. data/spec/norma_tables_spec.rb +3 -3
  60. metadata +8 -4
@@ -1,4 +1,9 @@
1
1
  #
2
+ # ActiveFacts Relational mapping and persistence.
3
+ # Reference from one Concept to another, used to decide the relational mapping.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
2
7
  # A Reference from one Concept to another is created for each many-1 or 1-1 relationship
3
8
  # (including subtyping), and also for a unary role (implicitly to Boolean concept).
4
9
  # A 1-1 or subtyping reference should be created in only one direction, and may be flipped
@@ -11,12 +16,25 @@
11
16
  #
12
17
  # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
13
18
  #
14
- # REVISIT: References need is_mandatory
15
- # REVISIT: Need to index References by to_role, to help in finding PK references etc.
16
19
 
17
20
  module ActiveFacts
18
- module Metamodel
19
-
21
+ module Persistence
22
+
23
+ # This class contains the core data structure used in composing a relational schema.
24
+ #
25
+ # A Reference is *from* one Concept *to* another Concept, and relates to the *from_role* and the *to_role*.
26
+ # When either Concept is an objectified fact type, the corresponding role is nil.
27
+ # When the Reference from_role is of a unary fact type, there's no to_role or to Concept.
28
+ # The final kind of Reference is a self-reference which is added to a ValueType that becomes a table.
29
+ #
30
+ # When the underlying fact type is a one-to-one (including an inheritance fact type), the Reference may be flipped.
31
+ #
32
+ # Each Reference has a name; an array of names in fact, in case of adjectives, etc.
33
+ # Each Refererence can produce the reading of the underlying fact type.
34
+ #
35
+ # A Reference is indexed in the player's *references_from* and *references_to*, and flipping updates those.
36
+ # Finally, a Reference may be marked as absorbing the whole referenced object, and that can flip too.
37
+ #
20
38
  class Reference
21
39
  attr_reader :from, :to # A "from" instance is related to one "to" instance
22
40
  attr_reader :from_role, :to_role # For objectified facts, one role will be nil (a phantom)
@@ -46,44 +64,53 @@ module ActiveFacts
46
64
  end
47
65
  end
48
66
 
67
+ # What type of Role did this Reference arise from?
49
68
  def role_type
50
69
  role = @from_role||@to_role
51
70
  role && role.role_type
52
71
  end
53
72
 
73
+ # Is this Reference covered by a mandatory constraint (implicitly or explicitly)
54
74
  def is_mandatory
55
75
  !@from_role || # All phantom roles of fact types are mandatory
56
76
  is_unary || # Unary fact types become booleans, which must be true or false
57
77
  @from_role.is_mandatory
58
78
  end
59
79
 
80
+ # Is this Reference from a unary Role?
60
81
  def is_unary
61
82
  !@to && @to_role && @to_role.fact_type.all_role.size == 1
62
83
  end
63
84
 
64
- # This case is the only one that cannot be used in the preferred identifier of @from
85
+ # If this Reference is to an objectified FactType, there is no *to_role*
65
86
  def is_to_objectified_fact
87
+ # This case is the only one that cannot be used in the preferred identifier of @from
66
88
  @to && !@to_role && @from_role
67
89
  end
68
90
 
91
+ # If this Reference is from an objectified FactType, there is no *from_role*
69
92
  def is_from_objectified_fact
70
93
  @to && @to_role && !@from_role
71
94
  end
72
95
 
96
+ # Is this reference an injected role as a result a ValueType being a table?
73
97
  def is_self_value
74
98
  !@to && !@to_role
75
99
  end
76
100
 
101
+ # Is the *to* concept fully absorbed through this reference?
77
102
  def is_absorbing
78
103
  @to && @to.absorbed_via == self
79
104
  end
80
105
 
106
+ # Is this a simple reference?
81
107
  def is_simple_reference
82
108
  # It's a simple reference to a thing if that thing is a table,
83
109
  # or is fully absorbed into another table but not via this reference.
84
110
  @to && (@to.is_table or @to.absorbed_via && !is_absorbing)
85
111
  end
86
112
 
113
+ # Return the array of names for the (perhaps implicit) *to_role* of this Reference
87
114
  def to_names
88
115
  case
89
116
  when is_unary
@@ -100,8 +127,8 @@ module ActiveFacts
100
127
  end
101
128
  end
102
129
 
103
- # For a one-to-one (or a subtyping fact type), reverse the direction:
104
- def flip
130
+ # For a one-to-one (or a subtyping fact type), reverse the direction.
131
+ def flip #:nodoc:
105
132
  raise "Illegal flip of #{self}" unless @to and [:one_one, :subtype, :supertype].include?(role_type)
106
133
 
107
134
  detabulate
@@ -118,7 +145,7 @@ module ActiveFacts
118
145
  tabulate
119
146
  end
120
147
 
121
- def tabulate
148
+ def tabulate #:nodoc:
122
149
  # Add to @to and @from's reference lists
123
150
  @from.references_from << self
124
151
  @to.references_to << self if @to # Guard against self-values
@@ -127,7 +154,7 @@ module ActiveFacts
127
154
  self
128
155
  end
129
156
 
130
- def detabulate
157
+ def detabulate #:nodoc:
131
158
  # Remove from @to and @from's reference lists if present
132
159
  return unless @from.references_from.delete(self)
133
160
  @to.references_to.delete self if @to # Guard against self-values
@@ -135,96 +162,104 @@ module ActiveFacts
135
162
  self
136
163
  end
137
164
 
138
- def to_s
165
+ def to_s #:nodoc:
139
166
  "reference from #{@from.name}#{@to ? " to #{@to.name}" : ""}" + (@fact_type ? " in '#{@fact_type.default_reading}'" : "")
140
167
  end
141
168
 
169
+ # The reading for the fact type underlying this Reference
142
170
  def reading
143
171
  is_self_value ? "#{from.name} has value" : @fact_type.default_reading
144
172
  end
145
173
 
146
- def inspect; to_s; end
174
+ def inspect #:nodoc:
175
+ to_s
176
+ end
147
177
  end
178
+ end
148
179
 
180
+ module Metamodel #:nodoc:
149
181
  class Concept
150
182
  # Say whether the independence of this object is still under consideration
151
183
  # This is used in detecting dependency cycles, such as occurs in the Metamodel
152
- attr_accessor :tentative
153
- attr_writer :is_table # The two Concept subclasses provide the reader
184
+ attr_accessor :tentative #:nodoc:
185
+ attr_writer :is_table # The two Concept subclasses provide the attr_reader method
154
186
 
155
- def show_tabular
187
+ def show_tabular #:nodoc:
156
188
  (tentative ? "tentatively " : "") +
157
189
  (is_table ? "" : "not ")+"a table"
158
190
  end
159
191
 
160
- def definitely_table
192
+ def definitely_table #:nodoc:
161
193
  @is_table = true
162
194
  @tentative = false
163
195
  end
164
196
 
165
- def definitely_not_table
197
+ def definitely_not_table #:nodoc:
166
198
  @is_table = false
167
199
  @tentative = false
168
200
  end
169
201
 
170
- def probably_table
202
+ def probably_table #:nodoc:
171
203
  @is_table = true
172
204
  @tentative = true
173
205
  end
174
206
 
175
- def probably_not_table
207
+ def probably_not_table #:nodoc:
176
208
  @is_table = false
177
209
  @tentative = true
178
210
  end
179
211
 
212
+ # References from this Concept
180
213
  def references_from
181
214
  @references_from ||= []
182
215
  end
183
216
 
217
+ # References to this Concept
184
218
  def references_to
185
219
  @references_to ||= []
186
220
  end
187
221
 
188
- def has_references
222
+ # True if this Concept has any References (to or from)
223
+ def has_references #:nodoc:
189
224
  @references_from || @references_to
190
225
  end
191
226
 
192
- def clear_references
227
+ def clear_references #:nodoc:
193
228
  # Clear any previous references:
194
229
  @references_to = nil
195
230
  @references_from = nil
196
231
  end
197
232
 
198
- def populate_references
233
+ def populate_references #:nodoc:
199
234
  all_role.each do |role|
200
235
  populate_reference role
201
236
  end
202
237
  end
203
238
 
204
- def populate_reference role
239
+ def populate_reference role #:nodoc:
205
240
  role_type = role.role_type
206
241
  debug :references, "#{name} has #{role_type} role in '#{role.fact_type.describe}'"
207
242
  case role_type
208
243
  when :many_one
209
- Reference.new(self, role).tabulate # A simple reference
244
+ ActiveFacts::Persistence::Reference.new(self, role).tabulate # A simple reference
210
245
 
211
246
  when :one_many
212
247
  if role.fact_type.entity_type == self # A Role of this objectified FactType
213
- Reference.new(self, role).tabulate # A simple reference; check that
248
+ ActiveFacts::Persistence::Reference.new(self, role).tabulate # A simple reference; check that
214
249
  else
215
250
  # Can't absorb many of these into one of those
216
251
  #debug :references, "Ignoring #{role_type} reference from #{name} to #{Reference.new(self, role).to.name}"
217
252
  end
218
253
 
219
254
  when :unary
220
- Reference.new(self, role).tabulate # A simple reference
255
+ ActiveFacts::Persistence::Reference.new(self, role).tabulate # A simple reference
221
256
 
222
257
  when :supertype # A subtype absorbs a reference to its supertype when separate, or all when partitioned
223
258
  # REVISIT: Or when partitioned
224
259
  if role.fact_type.subtype.is_independent
225
260
  debug :references, "supertype #{name} doesn't absorb a reference to separate subtype #{role.fact_type.subtype.name}"
226
261
  else
227
- r = Reference.new(self, role)
262
+ r = ActiveFacts::Persistence::Reference.new(self, role)
228
263
  r.to.absorbed_via = r
229
264
  debug :references, "supertype #{name} absorbs subtype #{r.to.name}"
230
265
  r.tabulate
@@ -232,14 +267,14 @@ module ActiveFacts
232
267
 
233
268
  when :subtype # This object is a supertype, which can absorb the subtype unless that's independent
234
269
  if is_independent # REVISIT: Or when partitioned
235
- Reference.new(self, role).tabulate
270
+ ActiveFacts::Persistence::Reference.new(self, role).tabulate
236
271
  # If partitioned, the supertype is absorbed into *each* subtype; a reference to the supertype needs to know which
237
272
  else
238
273
  # debug :references, "subtype #{name} is absorbed into #{role.fact_type.supertype.name}"
239
274
  end
240
275
 
241
276
  when :one_one
242
- r = Reference.new(self, role)
277
+ r = ActiveFacts::Persistence::Reference.new(self, role)
243
278
 
244
279
  # Decide which way the one-to-one is likely to go; it will be flipped later if necessary.
245
280
  # Force the decision if just one is independent:
@@ -281,7 +316,7 @@ module ActiveFacts
281
316
  end
282
317
 
283
318
  class EntityType
284
- def populate_references
319
+ def populate_references #:nodoc:
285
320
  if fact_type && fact_type.all_role.size > 1
286
321
  # NOT: fact_type.all_role.each do |role| # Place roles in the preferred order instead:
287
322
  fact_type.preferred_reading.role_sequence.all_role_ref.map(&:role).each do |role|
@@ -293,7 +328,7 @@ module ActiveFacts
293
328
  end
294
329
 
295
330
  class Vocabulary
296
- def populate_all_references
331
+ def populate_all_references #:nodoc:
297
332
  debug :references, "Populating all concept references" do
298
333
  all_feature.each do |feature|
299
334
  next unless feature.is_a? Concept
@@ -1,14 +1,15 @@
1
1
  #
2
- # Calculate the relational composition of a given Vocabulary
3
- # The composition consists of decisiona about which Concepts are tables,
4
- # and what columns (absorbed roled) those tables will have.
2
+ # ActiveFacts Relational mapping and persistence.
3
+ # Tables; Calculate the relational composition of a given Vocabulary.
4
+ # The composition consists of decisions about which Concepts are tables,
5
+ # and what columns (absorbed roled) those tables will have.
5
6
  #
6
- # This module has the following known problems:
7
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
7
8
  #
8
- # * Some one-to-ones absorb in both directions (ET<->FT in Metamodel, Blog model)
9
+ # This module has the following known problems:
9
10
  #
10
- # * When a subtype has no mandatory roles, we should introduce
11
- # a binary (is_subtype) to indicate it's that subtype.
11
+ # * When a subtype has no mandatory roles, we should support an optional schema transformation step
12
+ # that introduces a boolean (is_subtype) to indicate it's that subtype.
12
13
  #
13
14
 
14
15
  require 'activefacts/persistence/reference'
@@ -17,9 +18,12 @@ module ActiveFacts
17
18
  module Metamodel
18
19
 
19
20
  class ValueType
20
- def absorbed_via; nil; end # ValueTypes aren't absorbed in the way EntityTypes are
21
+ def absorbed_via #:nodoc:
22
+ # ValueTypes aren't absorbed in the way EntityTypes are
23
+ nil
24
+ end
21
25
 
22
- # Say whether this object is currently considered a table or not:
26
+ # Returns true if this ValueType is a table
23
27
  def is_table
24
28
  return @is_table if @is_table != nil
25
29
 
@@ -42,20 +46,27 @@ module ActiveFacts
42
46
  @is_table
43
47
  end
44
48
 
45
- # REVISIT: Find a better way to determine AutoCounters (ValueType unary role?)
49
+ # Is this ValueType auto-assigned on first save to the database?
46
50
  def is_auto_assigned
47
- type = self;
51
+ # REVISIT: Find a better way to determine AutoCounters (ValueType unary role?)
52
+ type = self
48
53
  type = type.supertype while type.supertype
49
54
  type.name =~ /^Auto/
50
55
  end
51
56
  end
52
57
 
53
58
  class EntityType
54
- attr_accessor :absorbed_via # A reference from an entity type that fully absorbs this one
59
+ # A Reference from an entity type that fully absorbs this one
60
+ def absorbed_via; @absorbed_via; end
61
+ def absorbed_via=(r) #:nodoc:
62
+ @absorbed_via = r
63
+ end
55
64
 
56
- def is_auto_assigned; false; end
65
+ def is_auto_assigned #:nodoc:
66
+ false
67
+ end
57
68
 
58
- # Decide whether this object is currently considered a table or not:
69
+ # Returns true if this EntityType is a table
59
70
  def is_table
60
71
  return @is_table if @is_table != nil # We already make a guess or decision
61
72
 
@@ -99,7 +110,7 @@ module ActiveFacts
99
110
  end
100
111
  end # EntityType class
101
112
 
102
- class Role
113
+ class Role #:nodoc:
103
114
  def role_type
104
115
  # TypeInheritance roles are always 1:1
105
116
  if TypeInheritance === fact_type
@@ -123,7 +134,7 @@ module ActiveFacts
123
134
  all_uniqueness_constraints.
124
135
  detect do |c|
125
136
  c.role_sequence.all_role_ref.size == 1 and
126
- c.role_sequence.all_role_ref[0].role == self
137
+ c.role_sequence.all_role_ref.only.role == self
127
138
  end
128
139
 
129
140
  if fact_type.entity_type
@@ -152,7 +163,7 @@ module ActiveFacts
152
163
  @tables
153
164
  end
154
165
 
155
- def decide_tables
166
+ def decide_tables #:nodoc:
156
167
  # Strategy:
157
168
  # 1) Populate references for all Concepts
158
169
  # 2) Decide which Concepts must be and must not be tables
@@ -1,3 +1,10 @@
1
+ #
2
+ # ActiveFacts Support code.
3
+ # The debug method supports indented tracing.
4
+ # Set the DEBUG environment variable to enable it. Search the code to find the DEBUG keywords, or use "all".
5
+ #
6
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
7
+ #
1
8
  #module ActiveFacts
2
9
  $debug_indent = nil
3
10
  $debug_nested = false
@@ -38,6 +45,7 @@
38
45
  end
39
46
  #end
40
47
 
48
+ # Return all duplicate objects in the array (using hash-equality)
41
49
  class Array
42
50
  def duplicates(&b)
43
51
  inject({}) do |h,e|
@@ -1,3 +1,9 @@
1
+ #
2
+ # ActiveFacts Support code.
3
+ # Version number file.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
1
7
  module ActiveFacts
2
- VERSION = '0.7.0'
8
+ VERSION = '0.7.1'
3
9
  end
@@ -1,6 +1,8 @@
1
1
  #
2
- # The ActiveFacts Vocabulary API, generated from Metamodel.orm with extensions:
3
- # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
2
+ # ActiveFacts Vocabulary Metamodel.
3
+ # The ActiveFacts Vocabulary API is generated from Metamodel.orm with extensions:
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
4
6
  #
5
7
  require 'activefacts/vocabulary/metamodel'
6
8
  require 'activefacts/vocabulary/extensions'
@@ -1,6 +1,8 @@
1
1
  #
2
- # Extensions to the ActiveFacts Vocabulary API (which is generated from the Metamodel)
3
- # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
2
+ # ActiveFacts Vocabulary Metamodel.
3
+ # Extensions to the ActiveFacts Vocabulary classes (which are generated from the Metamodel)
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
4
6
  #
5
7
  module ActiveFacts
6
8
  module Metamodel
@@ -112,7 +114,7 @@ module ActiveFacts
112
114
  end
113
115
 
114
116
  def subtypes
115
- all_value_type_by_supertype
117
+ all_value_type_as_supertype
116
118
  end
117
119
  end
118
120
 
@@ -122,7 +124,7 @@ module ActiveFacts
122
124
  if fact_type
123
125
 
124
126
  # For a nested fact type, the PI is a unique constraint over N or N-1 roles
125
- fact_roles = fact_type.all_role
127
+ fact_roles = Array(fact_type.all_role)
126
128
  debug :pi, "Looking for PI on nested fact type #{name}" do
127
129
  pi = catch :pi do
128
130
  fact_roles[0,2].each{|r| # Try the first two roles of the fact type, that's enough
@@ -171,7 +173,7 @@ module ActiveFacts
171
173
  debug :pi, "PI roles must be played by one of #{all_supertypes.map(&:name)*", "}" if all_supertypes.size > 1
172
174
  all_role.each{|role|
173
175
  next unless role.unique || fact_type
174
- ftroles = role.fact_type.all_role
176
+ ftroles = Array(role.fact_type.all_role)
175
177
 
176
178
  # Skip roles in ternary and higher fact types, they're objectified, and in unaries, they can't identify us.
177
179
  next if ftroles.size != 2
@@ -262,11 +264,11 @@ module ActiveFacts
262
264
  # An array of all direct subtypes:
263
265
  def subtypes
264
266
  # REVISIT: There's no sorting here. Should there be?
265
- all_type_inheritance_by_supertype.map{|ti| ti.subtype }
267
+ all_type_inheritance_as_supertype.map{|ti| ti.subtype }
266
268
  end
267
269
 
268
270
  def all_supertype_inheritance
269
- all_type_inheritance_by_subtype.sort_by{|ti|
271
+ all_type_inheritance_as_subtype.sort_by{|ti|
270
272
  [ti.provides_identification ? 0 : 1, ti.supertype.name]
271
273
  }
272
274
  end
@@ -280,7 +282,7 @@ module ActiveFacts
280
282
 
281
283
  # An array of self followed by all supertypes in order:
282
284
  def supertypes_transitive
283
- ([self] + all_type_inheritance_by_subtype.map{|ti|
285
+ ([self] + all_type_inheritance_as_subtype.map{|ti|
284
286
  # debug ti.class.roles.verbalise; exit
285
287
  ti.supertype.supertypes_transitive
286
288
  }).flatten.uniq
@@ -289,7 +291,7 @@ module ActiveFacts
289
291
  # A subtype does not have a identifying_supertype if it defines its own identifier
290
292
  def identifying_supertype
291
293
  debug "Looking for identifying_supertype of #{name}"
292
- all_type_inheritance_by_subtype.detect{|ti|
294
+ all_type_inheritance_as_subtype.detect{|ti|
293
295
  debug "considering supertype #{ti.supertype.name}"
294
296
  next unless ti.provides_identification
295
297
  debug "found identifying supertype of #{name}, it's #{ti.supertype.name}"
@@ -356,7 +358,7 @@ module ActiveFacts
356
358
  reject{|s| s==' '}. # Remove white space
357
359
  map do |frag| # and go through the bits
358
360
  if frag =~ /\{([0-9]+)\}/
359
- role_sequence.all_role_ref[$1.to_i]
361
+ role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i}
360
362
  else
361
363
  frag
362
364
  end