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