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,8 +1,11 @@
1
1
  #
2
- # The ActiveFacts Runtime API Standard types extensions.
3
- # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
2
+ # ActiveFacts Runtime API
3
+ # Standard types extensions.
4
4
  #
5
- # These extensions add ActiveFacts Concept and Instance behaviour into base Ruby classes.
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
7
+ # These extensions add ActiveFacts Concept and Instance behaviour into base Ruby value classes,
8
+ # and allow any Class to become an Entity.
6
9
  #
7
10
  require 'date'
8
11
 
@@ -37,6 +40,8 @@ class NilClass #:nodoc:
37
40
  end
38
41
 
39
42
  class Class
43
+ # Make this Class into a Concept and if necessary its module into a Vocabulary.
44
+ # The parameters are the names (Symbols) of the identifying roles.
40
45
  def identified_by *args
41
46
  raise "not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
42
47
  include ActiveFacts::API::Entity
@@ -1,8 +1,8 @@
1
1
  #
2
- # ActiveFacts runtime API.
3
- # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
2
+ # ActiveFacts Runtime API.
3
+ # Various additions or patches to Ruby built-in classes, and some global support methods
4
4
  #
5
- # Note that we still require facets/basicobject, see numeric.rb
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
6
  #
7
7
 
8
8
  class Symbol #:nodoc:
@@ -21,7 +21,7 @@ class String #:nodoc:
21
21
  end
22
22
 
23
23
  def snakecase
24
- gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
24
+ gsub(/([a-z])([A-Z])/,'\1_\2').downcase
25
25
  end
26
26
  end
27
27
 
@@ -1,6 +1,8 @@
1
1
  #
2
- # The ActiveFacts Runtime API Value extension module
3
- # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
2
+ # ActiveFacts Runtime API
3
+ # Value module (mixins for ValueType classes and instances)
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
4
6
  #
5
7
  # The methods of this module are added to Value type classes.
6
8
  #
@@ -13,6 +15,7 @@ module ActiveFacts
13
15
 
14
16
  # Value instance methods:
15
17
  def initialize(*args) #:nodoc:
18
+ args[0] = args[0].__getobj__ if RoleProxy === args[0]
16
19
  super(args)
17
20
  end
18
21
 
@@ -59,7 +62,10 @@ module ActiveFacts
59
62
  def identifying_role_values(*args) #:nodoc:
60
63
  # If the single arg is the correct class or a subclass, use it directly
61
64
  #puts "#{basename}.identifying_role_values#{args.inspect}"
62
- return args[0] if (args.size == 1 and self.class === args[0]) # No secondary supertypes allowed for value types
65
+ if (args.size == 1 and (arg = args[0]).is_a?(self.class)) # No secondary supertypes allowed for value types
66
+ arg = arg.__getobj__ if RoleProxy === arg
67
+ return arg
68
+ end
63
69
  new(*args)
64
70
  end
65
71
 
@@ -1,15 +1,21 @@
1
1
  #
2
- # The ActiveFacts Runtime API Vocabulary extension module.
3
- # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
2
+ # ActiveFacts Runtime API
3
+ # Vocabulary module (mixin for any Module that contains classes having Concept mixed in)
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
4
6
  #
5
7
  # The methods of this module are extended into any module that contains
6
8
  # a Concept class (Entity type or Value type).
7
9
  #
8
10
  module ActiveFacts
9
11
  module API
12
+ # Vocabulary is a mixin that adds methods to any Module which has any Concept classes (ValueType or EntityType).
13
+ # A Vocabulary knows all the Concept classes including forward-referenced ones,
14
+ # and can resolve the forward references when the class is finally defined.
15
+ # Construction of a Constellation requires a Vocabuary as argument.
10
16
  module Vocabulary
11
17
  # With a parameter, look up a concept class by name.
12
- # Without, return the hash of all concepts in this vocabulary
18
+ # Without, return the hash (keyed by the class' basename) of all concepts in this vocabulary
13
19
  def concept(name = nil)
14
20
  @concept ||= {}
15
21
  return @concept unless name
@@ -61,6 +67,7 @@ module ActiveFacts
61
67
  }*"\n\t"
62
68
  end
63
69
 
70
+ =begin
64
71
  # Create or find an instance of klass in constellation using value to identify it
65
72
  def adopt(klass, constellation, value) #:nodoc:
66
73
  puts "Adopting #{ value.verbalise rescue value.class.to_s+' '+value.inspect} as #{klass} into constellation #{constellation.object_id}"
@@ -68,20 +75,21 @@ module ActiveFacts
68
75
  path = "unknown"
69
76
  # Create a value instance we can hack if the value isn't already in this constellation
70
77
  if (c = constellation)
71
- if klass === value # Right class?
78
+ if value.is_a?(klass) # Right class?
79
+ value = value.__getobj__ if RoleProxy === value
72
80
  vc = value.constellation rescue nil
73
81
  if (c == vc) # Right constellation?
74
82
  # Already right class, in the right constellation
75
83
  path = "right constellation, right class, just use it"
76
84
  else
77
85
  # We need a new object from our constellation, so copy the value.
78
- if klass.respond_to?(:identifying_roles)
86
+ if klass.respond_to?(:identifying_role_names)
79
87
  # Make a new entity having only the identifying roles set.
80
88
  # Someone will complain that this is wrong, and all functional role values should also
81
89
  # be cloned, and I'm listening... but not there yet. Why just those?
82
90
  cloned = c.send(
83
91
  :"#{klass.basename}",
84
- *klass.identifying_roles.map{|role| value.send(role) }
92
+ *klass.identifying_role_names.map{|role| value.send(role) }
85
93
  )
86
94
  path = "wrong constellation, right class, cloned entity"
87
95
  else
@@ -99,7 +107,8 @@ module ActiveFacts
99
107
  end
100
108
  else
101
109
  # This object's not in a constellation
102
- if klass === value # Right class?
110
+ if value.is_a?(klass) # Right class?
111
+ value = value.__getobj__ if RoleProxy === value
103
112
  if vc = value.constellation rescue nil
104
113
  raise "REVISIT: Assigning to #{self.class.basename}.#{role_name} with constellation=#{c.inspect}: Can't dis-associate object from its constellation #{vc.object_id} yet"
105
114
  end
@@ -114,6 +123,7 @@ module ActiveFacts
114
123
  # print "#{path}"; puts ", adopted as #{value.verbalise rescue value.inspect}"
115
124
  value
116
125
  end
126
+ =end
117
127
 
118
128
  end
119
129
  end
@@ -1,6 +1,8 @@
1
1
  #
2
- # ActiveFacts CQL loader.
3
- # Copyright (c) 2007 Clifford Heath. Read the LICENSE file.
2
+ # ActiveFacts CQL loader.
3
+ # Use Polyglot to patch things so you can *require* a CQL file and have it define a Ruby module.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
4
6
  #
5
7
  require 'rubygems'
6
8
  require 'polyglot'
@@ -9,12 +11,13 @@ require 'activefacts/input/cql'
9
11
  require 'activefacts/generate/ruby'
10
12
 
11
13
  module ActiveFacts
12
- # Extend the generated parser:
14
+ # This class has a load method for Polyglot to tell it how to _require_ a CQL file.
15
+ # The CQL file is parsed to a vocabulary constellation, which is generated
16
+ # to Ruby code and eval'd, making the generated classes available.
17
+ # To make this Loader available, simply
18
+ # require 'activefacts/cql'
13
19
  class CQLLoader
14
- # This load method for Polyglot tells it how to _require_ a CQL file.
15
- # The CQL file is parsed to a vocabulary constellation, which is generated
16
- # to Ruby code and eval'd, making the generated classes available.
17
- def self.load(file)
20
+ def self.load(file) #:nodoc:
18
21
  debug "Loading #{file}" do
19
22
  vocabulary = ActiveFacts::Input::CQL.readfile(file)
20
23
 
@@ -1,3 +1,9 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # Parse rules relating to high-level CQL definitions and constraints.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
1
7
  module ActiveFacts
2
8
  grammar CQL
3
9
  include LexicalRules
@@ -1,3 +1,9 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # Parse rules relating to Concept definitions.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
1
7
  module ActiveFacts
2
8
  module CQL
3
9
  grammar Concepts
@@ -22,8 +28,8 @@ module ActiveFacts
22
28
 
23
29
  rule basetype
24
30
  is s
25
- # independency?
26
- identification
31
+ # independency?
32
+ identification
27
33
  { def supers; [] end
28
34
  def identifier; identification.value; end
29
35
  }
@@ -31,8 +37,8 @@ module ActiveFacts
31
37
 
32
38
  rule subtype
33
39
  subtype_prefix
34
- # independency?
35
- supertype_list ident:identification?
40
+ # independency?
41
+ supertype_list ident:identification?
36
42
  {
37
43
  def supers; supertype_list.value; end
38
44
  def identifier; ident.empty? ? nil : ident.value; end
@@ -41,7 +47,7 @@ module ActiveFacts
41
47
 
42
48
  # REVISIT: This doesn't work, and I don't know why.
43
49
  rule independency
44
- ('independent' S / 'separate' S / 'partitioned' S)
50
+ ('independent' S / 'separate' S / 'partitioned' S)
45
51
  end
46
52
 
47
53
  rule supertype_list
@@ -54,12 +60,12 @@ module ActiveFacts
54
60
  end
55
61
 
56
62
  rule identification
57
- # REVISIT: Consider distinguishing "-Id" from just "Id", and not prepending the entity type name if no "-"
58
- identified_by its s id s # Reference Mode
59
- { def value; {:mode => id.text_value }; end }
60
- /
63
+ # REVISIT: Consider distinguishing "-Id" from just "Id", and not prepending the entity type name if no "-"
64
+ identified_by its s id s # Reference Mode
65
+ { def value; {:mode => id.text_value }; end }
66
+ /
61
67
  identified_by role_list
62
- { def value; {:roles => role_list.roles }; end }
68
+ { def value; {:roles => role_list.roles }; end }
63
69
  end
64
70
 
65
71
  # Identified by roles... also used for constraints, beware
@@ -75,24 +81,24 @@ module ActiveFacts
75
81
  # We can't tell which word is an adjective and which is a concept here.
76
82
  # The concept might be forward-referenced, but not if adjectives are used.
77
83
  # REVISIT: This accepts double-adjective expressions (three words) but they don't work elsewhere
78
- rule lead_adj
79
- role_player_id '-'
80
- end
84
+ rule lead_adj
85
+ role_player_id '-'
86
+ end
81
87
 
82
- rule trail_adj
83
- '-' role_player_id
84
- end
88
+ rule trail_adj
89
+ '-' role_player_id
90
+ end
85
91
 
86
- rule role_player
87
- l:lead_adj? tail:(s role_player_id)+ s t:trail_adj?
88
- {
89
- def value
90
- (l.empty? ? [] : [l.role_player_id.text_value]) +
91
- tail.elements.map{|e| e.role_player_id.text_value } +
92
- (t.empty? ? [] : [t.role_player_id.text_value])
93
- end
94
- }
95
- end
92
+ rule role_player
93
+ l:lead_adj? tail:(s role_player_id)+ s t:trail_adj?
94
+ {
95
+ def value
96
+ (l.empty? ? [] : [l.role_player_id.text_value]) +
97
+ tail.elements.map{|e| e.role_player_id.text_value } +
98
+ (t.empty? ? [] : [t.role_player_id.text_value])
99
+ end
100
+ }
101
+ end
96
102
 
97
103
  rule role_player_id
98
104
  !(role_list_sep / quantifier) id
@@ -1,3 +1,9 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # Parse rules relating to ValueType definitions.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
1
7
  module ActiveFacts
2
8
  module CQL
3
9
  grammar DataTypes
@@ -1,3 +1,9 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # Parse rules relating to Expressions
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
1
7
  module ActiveFacts
2
8
  module CQL
3
9
  grammar Expressions
@@ -1,3 +1,9 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # Parse rules relating to FactType definitions.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
1
7
  module ActiveFacts
2
8
  module CQL
3
9
  grammar FactTypes
@@ -1,3 +1,9 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # Parse rules the English syntax of CQL.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
1
7
  module ActiveFacts
2
8
  module CQL
3
9
  grammar Language
@@ -17,9 +23,9 @@ module ActiveFacts
17
23
  rule quantifier
18
24
  each s { def value; [1, nil]; end }
19
25
  / some s { def value; nil; end }
20
- # REVISIT: "Some" means that any prior occurrence of this role player is to be ignored; this is a new occurrence
21
- # "that" on the other hand means that this role player was *previously* designated using "some".
22
- # These distinctions are lost here
26
+ # REVISIT: "Some" means that any prior occurrence of this role player is to be ignored; this is a new occurrence
27
+ # "that" on the other hand means that this role player was *previously* designated using "some".
28
+ # These distinctions are lost here
23
29
  / that s { def value; nil; end }
24
30
  / one s { def value; [1, 1]; end }
25
31
  / no s { def value; [0, 0]; end }
@@ -1,3 +1,9 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # Various lexical rules for CQL.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
1
7
  module ActiveFacts
2
8
  module CQL
3
9
  grammar LexicalRules
@@ -1,3 +1,11 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # A Rakefile to run Treetop when the ActiveFacts gem is installed.
4
+ # This isn't mandatory but makes it much faster to start the parser.
5
+ # Delete the generated files during parser development.
6
+ #
7
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
8
+ #
1
9
  task :default do
2
10
  pattern = File.dirname(__FILE__) + '**/*.treetop'
3
11
  files = Dir[pattern]
@@ -1,6 +1,8 @@
1
1
  #
2
- # ActiveFacts CQL parser and loader.
3
- # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
2
+ # ActiveFacts CQL Parser.
3
+ # The parser turns CQL strings into abstract syntax trees ready for semantic analysis.
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
4
6
  #
5
7
  require 'rubygems'
6
8
  require 'treetop'
@@ -1,17 +1,27 @@
1
1
  #
2
- # Generate text output for ActiveFacts vocabularies.
2
+ # ActiveFacts Generators.
3
+ # Absorption generator.
3
4
  #
4
- # Copyright (c) 2007 Clifford Heath. Read the LICENSE file.
5
- # Author: Clifford Heath.
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
6
  #
7
7
  require 'activefacts/persistence'
8
8
 
9
9
  module ActiveFacts
10
10
  module Generate
11
+ # Emit the absorption (Relational summary) for vocabulary.
12
+ # Not currently working, it relies on the old relational composition code.
13
+ # Invoke as
14
+ # afgen --absorption[=options] <file>.cql"
15
+ # Options are comma or space separated:
16
+ # * no_columns Don't emit the columns
17
+ # * dependent Show Concepts that are not tables as well
18
+ # * paths Show the references paths through which each column was defined
19
+ # * no_identifier Don't show the identified_by columns for an EntityType
20
+
11
21
  class ABSORPTION
12
22
  include Metamodel
13
23
 
14
- def initialize(vocabulary, *options)
24
+ def initialize(vocabulary, *options) #:nodoc:
15
25
  @vocabulary = vocabulary
16
26
  @vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
17
27
  @no_columns = options.include? "no_columns"
@@ -20,7 +30,7 @@ module ActiveFacts
20
30
  @no_identifier = options.include? "no_identifier"
21
31
  end
22
32
 
23
- def generate(out = $>)
33
+ def generate(out = $>) #:nodoc:
24
34
  no_absorption = 0
25
35
  single_absorption_vts = 0
26
36
  single_absorption_ets = 0
@@ -31,42 +41,24 @@ module ActiveFacts
31
41
  # Don't dump imported (base) ValueTypes:
32
42
  next if ValueType === o && !o.supertype
33
43
  show(o)
34
-
35
- case o.absorption_paths.size
36
- when 0; no_absorption += 1
37
- when 1
38
- if ValueType === o
39
- single_absorption_vts += 1
40
- else
41
- single_absorption_ets += 1
42
- end
43
- else
44
- if ValueType === o
45
- multi_absorption_vts += 1
46
- else
47
- multi_absorption_ets += 1
48
- end
49
- end
50
-
51
44
  end
52
- puts "#{no_absorption} concepts have no absorption paths, #{single_absorption_vts}/#{single_absorption_ets} value/entity types have only one path, #{multi_absorption_vts}/#{multi_absorption_ets} have more than one"
53
45
  end
54
46
 
55
- def show concept
56
- return unless concept.independent || @dependent
47
+ def show concept #:nodoc:
48
+ return unless concept.is_table || @dependent
57
49
 
58
50
  print "#{concept.name}"
59
- print " (#{concept.tentative ? "tentatively " : ""}#{concept.independent ? "in" : ""}dependent)" if @dependent
51
+ print " (#{concept.tentative ? "tentatively " : ""}#{concept.is_table ? "in" : ""}dependent)" if @dependent
60
52
 
61
53
  if !@no_identifier && concept.is_a?(EntityType)
62
54
  print " is identified by:\n\t#{
63
- concept.absorbed_reference_roles.all_role_ref.map { |rr| rr.column_name(".") } * ",\n\t"
55
+ "REVISIT" # concept.references_from.to_s
64
56
  }"
65
57
  end
66
58
  print "\n"
67
59
 
68
60
  unless @no_columns
69
- puts "#{ concept.absorbed_roles.all_role_ref.map do |role_ref|
61
+ puts "#{ concept.references_from.map do |role_ref|
70
62
  "\t#{role_ref.column_name(".")}\n"
71
63
  end*"" }"
72
64
  end