activefacts 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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