activefacts 1.1.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/bin/cql +137 -91
- data/css/style.css +3 -3
- data/examples/CQL/Insurance.cql +1 -1
- data/examples/CQL/SeparateSubtype.cql +2 -2
- data/lib/activefacts/cql/Language/English.treetop +9 -0
- data/lib/activefacts/cql/ObjectTypes.treetop +1 -1
- data/lib/activefacts/cql/Terms.treetop +3 -1
- data/lib/activefacts/cql/ValueTypes.treetop +10 -4
- data/lib/activefacts/cql/compiler.rb +1 -0
- data/lib/activefacts/cql/compiler/clause.rb +53 -23
- data/lib/activefacts/cql/compiler/entity_type.rb +0 -4
- data/lib/activefacts/cql/compiler/expression.rb +9 -13
- data/lib/activefacts/cql/compiler/fact.rb +49 -48
- data/lib/activefacts/cql/compiler/fact_type.rb +23 -20
- data/lib/activefacts/cql/compiler/query.rb +49 -121
- data/lib/activefacts/cql/compiler/shared.rb +5 -1
- data/lib/activefacts/cql/compiler/value_type.rb +4 -2
- data/lib/activefacts/generate/rails/schema.rb +138 -108
- data/lib/activefacts/generate/transform/surrogate.rb +1 -2
- data/lib/activefacts/mapping/rails.rb +52 -45
- data/lib/activefacts/persistence/columns.rb +5 -5
- data/lib/activefacts/persistence/tables.rb +6 -4
- data/lib/activefacts/support.rb +0 -2
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +64 -42
- data/lib/activefacts/vocabulary/metamodel.rb +14 -12
- data/lib/activefacts/vocabulary/verbaliser.rb +98 -92
- data/spec/cql/expressions_spec.rb +8 -3
- data/spec/cql/parser/entity_types_spec.rb +1 -1
- data/spec/cql/parser/expressions_spec.rb +66 -52
- data/spec/cql/parser/fact_types_spec.rb +1 -1
- data/spec/cql/parser/literals_spec.rb +10 -10
- data/spec/cql/parser/pragmas_spec.rb +3 -3
- data/spec/cql/parser/value_types_spec.rb +1 -1
- metadata +2 -2
@@ -12,6 +12,20 @@ module ActiveFacts
|
|
12
12
|
@returning = returning || []
|
13
13
|
end
|
14
14
|
|
15
|
+
def to_s
|
16
|
+
inspect
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"Query: " +
|
21
|
+
if @conditions.empty?
|
22
|
+
''
|
23
|
+
else
|
24
|
+
'where ' + @conditions.map{|c| ((j=c.conjunction) ? j+' ' : '') + c.inspect}*' '
|
25
|
+
end
|
26
|
+
# REVISIT: @returning = returning
|
27
|
+
end
|
28
|
+
|
15
29
|
def prepare_roles clauses = nil
|
16
30
|
trace :binding, "preparing roles" do
|
17
31
|
@context ||= CompilationContext.new(@vocabulary)
|
@@ -27,8 +41,8 @@ module ActiveFacts
|
|
27
41
|
match_condition_fact_types
|
28
42
|
|
29
43
|
# Build the query:
|
30
|
-
unless @conditions.empty? and
|
31
|
-
trace :query, "building query for derived fact type" do
|
44
|
+
unless @conditions.empty? and @returning.empty?
|
45
|
+
trace :query, "building query for derived fact type (returning #{@returning}) with #{@conditions.size} conditions: (#{@conditions.map{|c|c.inspect}*', '})" do
|
32
46
|
@query = build_variables(@conditions.flatten)
|
33
47
|
@roles_by_binding = build_all_steps(@conditions)
|
34
48
|
@query.validate
|
@@ -41,12 +55,10 @@ module ActiveFacts
|
|
41
55
|
|
42
56
|
def match_condition_fact_types
|
43
57
|
@conditions.each do |condition|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
raise "Unrecognised fact type #{condition.inspect} in #{self.class}" unless fact_type
|
49
|
-
end
|
58
|
+
trace :projection, "matching condition fact_type #{condition.inspect}" do
|
59
|
+
fact_type = condition.match_existing_fact_type @context
|
60
|
+
raise "Unrecognised fact type #{condition.inspect} in #{self.class}" unless fact_type
|
61
|
+
end
|
50
62
|
end
|
51
63
|
end
|
52
64
|
|
@@ -105,8 +117,6 @@ module ActiveFacts
|
|
105
117
|
# REVISIT: Compiling the conditions here make it impossible to define a self-referential (transitive) query.
|
106
118
|
return super if @clauses.empty? # It's a query
|
107
119
|
|
108
|
-
# Ignore any useless clauses:
|
109
|
-
@clauses.reject!{|clause| clause.is_existential_type }
|
110
120
|
return true unless @clauses.size > 0 # Nothing interesting was said.
|
111
121
|
|
112
122
|
if @entity_type
|
@@ -396,16 +406,9 @@ module ActiveFacts
|
|
396
406
|
end
|
397
407
|
end
|
398
408
|
|
399
|
-
def
|
400
|
-
|
401
|
-
|
402
|
-
end
|
403
|
-
"FactType: #{(s = super and !s.empty?) ? "#{s} " : '' }#{@clauses.inspect}" +
|
404
|
-
if @conditions && !@conditions.empty?
|
405
|
-
" where "+@conditions.map{|c| ((j=c.conjunction) ? j+' ' : '') + c.to_s}*' '
|
406
|
-
else
|
407
|
-
''
|
408
|
-
end +
|
409
|
+
def inspect
|
410
|
+
s = super
|
411
|
+
"FactType: #{@conditions.size > 0 ? super+' ' : '' }#{@clauses.inspect}" +
|
409
412
|
(@pragmas && @pragmas.size > 0 ? ", pragmas [#{@pragmas.flatten.sort*','}]" : '')
|
410
413
|
|
411
414
|
# REVISIT: @returning = returning
|
@@ -27,139 +27,67 @@ module ActiveFacts
|
|
27
27
|
roles_by_binding = {}
|
28
28
|
trace :query, "Building steps" do
|
29
29
|
clauses_list.each do |clause|
|
30
|
-
|
31
|
-
build_steps(clause, roles_by_binding)
|
30
|
+
build_step(clause, roles_by_binding)
|
32
31
|
end
|
33
32
|
end
|
34
33
|
roles_by_binding
|
35
34
|
end
|
36
35
|
|
37
|
-
def
|
38
|
-
|
39
|
-
incidental_roles = []
|
40
|
-
trace :query, "Creating Role Sequence for #{clause.inspect} with #{clause.refs.size} role refs" do
|
41
|
-
objectification_step = nil
|
42
|
-
clause.refs.each do |ref|
|
43
|
-
# These refs are the Compiler::References, which have associated Metamodel::RoleRefs,
|
44
|
-
# but we need to create Plays for those roles.
|
45
|
-
# REVISIT: Plays may need to save residual_adjectives
|
46
|
-
binding = ref.binding
|
47
|
-
role = (ref && ref.role) || (ref.role_ref && ref.role_ref.role)
|
48
|
-
play = nil
|
36
|
+
def build_step clause, roles_by_binding = {}, parent_variable = nil
|
37
|
+
return unless clause.refs.size > 0 # Empty clause... really?
|
49
38
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
objectification_ref_count = 0
|
58
|
-
if ref.nested_clauses
|
59
|
-
ref.nested_clauses.each do |ojc|
|
60
|
-
objectification_ref_count += ojc.refs.select{|ref| ref.binding.refs.size > 1}.size
|
61
|
-
end
|
62
|
-
end
|
63
|
-
refs_count += objectification_ref_count
|
39
|
+
step = @constellation.Step(
|
40
|
+
:guid => :new,
|
41
|
+
:fact_type => clause.fact_type,
|
42
|
+
:alternative_set => nil,
|
43
|
+
:is_disallowed => clause.certainty == false,
|
44
|
+
:is_optional => clause.certainty == nil
|
45
|
+
)
|
64
46
|
|
65
|
-
|
47
|
+
trace :query, "Creating Plays for #{clause.inspect} with #{clause.refs.size} refs" do
|
48
|
+
is_input = true
|
49
|
+
clause.refs.each do |ref|
|
50
|
+
# These refs are the Compiler::References, which have associated Metamodel::RoleRefs,
|
51
|
+
# but we need to create Plays for those roles.
|
52
|
+
# REVISIT: Plays may need to save residual_adjectives
|
53
|
+
binding = ref.binding
|
54
|
+
role = (ref && ref.role) || (ref.role_ref && ref.role_ref.role)
|
66
55
|
|
67
|
-
|
68
|
-
|
56
|
+
objectification_step = nil
|
57
|
+
if ref.nested_clauses
|
58
|
+
ref.nested_clauses.each do |nested_clause|
|
59
|
+
objectification_step = build_step nested_clause, roles_by_binding
|
60
|
+
if ref.binding.player.is_a?(ActiveFacts::Metamodel::EntityType) and
|
61
|
+
ref.binding.player.fact_type == nested_clause.fact_type
|
62
|
+
objectification_step.objectification_variable = binding.variable
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if clause.is_naked_object_type
|
67
|
+
raise "#{self} lacks a proper objectification" if clause.refs[0].nested_clauses and !objectification_step
|
68
|
+
return objectification_step
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
if binding.variable.object_type != role.object_type # Type mismatch
|
72
|
+
if binding.variable.object_type.common_supertype(role.object_type)
|
73
|
+
# REVISIT: there's an implicit subtyping step here, create it; then always raise the error here.
|
74
|
+
# I don't want to do this for now because the verbaliser will always verbalise all steps.
|
75
|
+
raise "Disallowing implicit subtyping step from #{role.object_type.name} to #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
|
76
|
+
end
|
77
|
+
raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
|
78
|
+
end
|
78
79
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
query = binding.variable.query
|
84
|
-
trace :query, "Creating JN#{query.all_variable.size} for #{clause.fact_type.entity_type.name} in objectification"
|
85
|
-
objectification_node = @constellation.Variable(query, query.all_variable.size, :object_type => clause.fact_type.entity_type)
|
86
|
-
end
|
87
|
-
raise "Internal error: Trying to add role of #{role.link_fact_type.all_role.single.object_type.name} to variable for #{objectification_node.object_type.name}" unless objectification_node.object_type == role.link_fact_type.all_role.single.object_type
|
80
|
+
trace :query, "Creating Play for #{ref.inspect}"
|
81
|
+
play = @constellation.Play(:step => step, :role => role, :variable => binding.variable)
|
82
|
+
play.is_input = is_input
|
83
|
+
is_input = false
|
88
84
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
objectification_step = @constellation.Step(
|
93
|
-
objectification_role,
|
94
|
-
play,
|
95
|
-
:fact_type => role.link_fact_type,
|
96
|
-
:is_optional => false,
|
97
|
-
:is_disallowed => clause.certainty == false
|
98
|
-
)
|
99
|
-
if clause.certainty == nil
|
100
|
-
objectification_step.is_optional = true
|
101
|
-
end
|
102
|
-
trace :query, "New #{objectification_step.describe}"
|
103
|
-
trace :query, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{objectification_step.describe}" if incidental_roles.size > 0
|
104
|
-
incidental_roles.each { |jr| jr.step = objectification_step }
|
105
|
-
incidental_roles = []
|
106
|
-
plays = []
|
107
|
-
end
|
108
|
-
else
|
109
|
-
trace :query, "Creating Reference for #{ref.inspect}" do
|
110
|
-
# REVISIT: If there's an implicit subtyping step here, create it; then always raise the error here.
|
111
|
-
# I don't want to do this for now because the verbaliser will always verbalise all steps.
|
112
|
-
if binding.variable.object_type != role.object_type and
|
113
|
-
0 == (binding.variable.object_type.supertypes_transitive & role.object_type.supertypes_transitive).size
|
114
|
-
raise "Internal error: Trying to add role of #{role.object_type.name} to variable #{binding.variable.ordinal} for #{binding.variable.object_type.name} in '#{clause.fact_type.default_reading}'"
|
115
|
-
end
|
116
|
-
raise "Internal error: Trying to add role of #{role.object_type.name} to variable #{binding.variable.ordinal} for #{binding.variable.object_type.name}" unless binding.variable.object_type == role.object_type
|
117
|
-
begin
|
118
|
-
play = @constellation.Play(binding.variable, role)
|
119
|
-
rescue ArgumentError => e
|
120
|
-
play = @constellation.Play(binding.variable, role)
|
121
|
-
end
|
122
|
-
plays << play
|
123
|
-
end
|
124
|
-
end
|
85
|
+
roles_by_binding[binding] = [role, play]
|
86
|
+
end
|
87
|
+
end
|
125
88
|
|
126
|
-
|
127
|
-
|
128
|
-
# which will have ImplicitFactTypes for each role.
|
129
|
-
# Each of these ImplicitFactTypes has a single phantom role played by the objectifying entity type
|
130
|
-
# One of these phantom roles is likely to be the subject of an objectification step.
|
131
|
-
ref.nested_clauses.each do |r|
|
132
|
-
trace :query, "Building objectification step for #{ref.nested_clauses.inspect}" do
|
133
|
-
build_steps r, roles_by_binding, binding.variable
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
roles_by_binding[binding] = [role, play]
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
if plays.size > 0
|
142
|
-
end_node = plays[-1].variable
|
143
|
-
if !clause.fact_type.entity_type and role = clause.fact_type.all_role.single
|
144
|
-
# Don't give the ImplicitBoolean a variable. We can live without one, for now.
|
145
|
-
# The Step will have a duplicate node, and the fact type will tell us what's happening
|
146
|
-
plays << plays[0]
|
147
|
-
end
|
148
|
-
# We aren't talking about objectification here, so there must be exactly two roles.
|
149
|
-
raise "REVISIT: Internal error constructing step for #{clause.inspect}" if plays.size != 2
|
150
|
-
js = @constellation.Step(
|
151
|
-
plays[0],
|
152
|
-
plays[1],
|
153
|
-
:fact_type => clause.fact_type,
|
154
|
-
:is_disallowed => clause.certainty == false,
|
155
|
-
:is_optional => clause.certainty == nil
|
156
|
-
)
|
157
|
-
trace :query, "New #{js.describe}"
|
158
|
-
trace :query, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{js.describe}" if incidental_roles.size > 0
|
159
|
-
incidental_roles.each { |jr| jr.step = js }
|
160
|
-
end
|
161
|
-
roles_by_binding
|
162
|
-
end
|
89
|
+
step
|
90
|
+
end
|
163
91
|
|
164
92
|
# Return the unique array of all bindings in these clauses, including in objectification steps
|
165
93
|
def all_bindings_in_clauses clauses
|
@@ -15,7 +15,7 @@ module ActiveFacts
|
|
15
15
|
attr_reader :refs # an array of the References
|
16
16
|
attr_reader :role_name
|
17
17
|
attr_accessor :rebound_to # Loose binding may set this to another binding
|
18
|
-
|
18
|
+
attr_reader :variable
|
19
19
|
attr_accessor :instance # When binding fact instances, the instance goes here
|
20
20
|
|
21
21
|
def initialize player, role_name = nil
|
@@ -35,6 +35,10 @@ module ActiveFacts
|
|
35
35
|
def <=>(other)
|
36
36
|
key <=> other.key
|
37
37
|
end
|
38
|
+
|
39
|
+
def variable= v
|
40
|
+
@variable = v # A place for a breakpoint :)
|
41
|
+
end
|
38
42
|
|
39
43
|
def add_ref ref
|
40
44
|
@refs << ref
|
@@ -87,7 +87,7 @@ module ActiveFacts
|
|
87
87
|
end
|
88
88
|
|
89
89
|
class ValueType < ObjectType
|
90
|
-
def initialize name, base, parameters, unit, value_constraint, pragmas, context_note
|
90
|
+
def initialize name, base, parameters, unit, value_constraint, pragmas, context_note, auto_assigned_at
|
91
91
|
super name
|
92
92
|
@base_type_name = base
|
93
93
|
@parameters = parameters
|
@@ -95,6 +95,7 @@ module ActiveFacts
|
|
95
95
|
@value_constraint = value_constraint
|
96
96
|
@pragmas = pragmas
|
97
97
|
@context_note = context_note
|
98
|
+
@auto_assigned_at = auto_assigned_at
|
98
99
|
end
|
99
100
|
|
100
101
|
def compile
|
@@ -116,11 +117,12 @@ module ActiveFacts
|
|
116
117
|
vt.supertype = base_type if base_type
|
117
118
|
vt.length = length if length
|
118
119
|
vt.scale = scale if scale
|
120
|
+
vt.transaction_phase = @auto_assigned_at
|
119
121
|
|
120
122
|
unless @unit.empty?
|
121
123
|
unit_name, exponent = *@unit[0]
|
122
124
|
unit = @constellation.Name[unit_name].unit ||
|
123
|
-
@constellation.Name[unit_name].
|
125
|
+
@constellation.Name[unit_name].plural_named_unit
|
124
126
|
raise "Unit #{unit_name} for value type #{@name} is not defined" unless unit
|
125
127
|
if exponent != 1
|
126
128
|
base_unit = unit
|
@@ -30,7 +30,7 @@ module ActiveFacts
|
|
30
30
|
def help
|
31
31
|
@helping = true
|
32
32
|
warn %Q{Options for --rails/schema:
|
33
|
-
exclude_fks Don't generate foreign key definitions for use with the foreigner gem
|
33
|
+
exclude_fks Don't generate foreign key definitions for use with Rails 4 or the foreigner gem
|
34
34
|
include_comments Generate a comment for each column showing the absorption path
|
35
35
|
closed_world Set this if your DBMS only allows one null in a unique index (MS SQL)
|
36
36
|
}
|
@@ -45,128 +45,158 @@ module ActiveFacts
|
|
45
45
|
end
|
46
46
|
|
47
47
|
public
|
48
|
-
def generate(out = $>) #:nodoc:
|
49
|
-
return if @helping
|
50
|
-
@out = out
|
51
48
|
|
52
|
-
|
49
|
+
# We sort the columns here, not in the persistence layer, because it affects
|
50
|
+
# the ordering of columns in an index :-(.
|
51
|
+
def sorted_columns table, pk, fk_columns
|
52
|
+
table.columns.sort_by do |column|
|
53
|
+
[ # Emit columns alphabetically, but PK first, then FKs, then others
|
54
|
+
case
|
55
|
+
when i = pk.index(column)
|
56
|
+
i
|
57
|
+
when fk_columns.include?(column)
|
58
|
+
pk.size+1
|
59
|
+
else
|
60
|
+
pk.size+2
|
61
|
+
end,
|
62
|
+
column.rails_name
|
63
|
+
]
|
64
|
+
end
|
65
|
+
end
|
53
66
|
|
54
|
-
|
55
|
-
|
67
|
+
def generate_column table, pk, column
|
68
|
+
name = column.rails_name
|
69
|
+
type, params, constraints = *column.type
|
70
|
+
length = params[:length]
|
71
|
+
length &&= length.to_i
|
72
|
+
scale = params[:scale]
|
73
|
+
scale &&= scale.to_i
|
74
|
+
rails_type, length = *column.rails_type
|
75
|
+
|
76
|
+
length_name = rails_type == 'decimal' ? 'precision' : 'limit'
|
77
|
+
length_option = length ? ", :#{length_name} => #{length}" : ''
|
78
|
+
scale_option = scale ? ", :scale => #{scale}" : ''
|
79
|
+
|
80
|
+
comment = column.comment
|
81
|
+
null_option = ", :null => #{!column.is_mandatory}"
|
82
|
+
if pk.size == 1 && pk[0] == column
|
83
|
+
case rails_type
|
84
|
+
when 'serial'
|
85
|
+
rails_type = "primary_key"
|
86
|
+
when 'uuid'
|
87
|
+
rails_type = "uuid, :default => 'gen_random_uuid()', :primary_key => true"
|
88
|
+
end
|
89
|
+
else
|
90
|
+
case rails_type
|
91
|
+
when 'serial'
|
92
|
+
rails_type = 'integer' # An integer foreign key
|
93
|
+
end
|
94
|
+
end
|
56
95
|
|
57
|
-
|
58
|
-
|
96
|
+
(@include_comments ? [" \# #{comment}"] : []) +
|
97
|
+
[
|
98
|
+
%Q{ t.column "#{name}", :#{rails_type}#{length_option}#{scale_option}#{null_option}}
|
99
|
+
]
|
100
|
+
end
|
59
101
|
|
60
|
-
|
61
|
-
|
102
|
+
def generate_columns table, pk, fk_columns
|
103
|
+
sc = sorted_columns(table, pk, fk_columns)
|
104
|
+
lines = sc.map do |column|
|
105
|
+
generate_column table, pk, column
|
106
|
+
end
|
107
|
+
lines.flatten
|
108
|
+
end
|
62
109
|
|
63
|
-
|
64
|
-
|
110
|
+
def generate_table table, foreign_keys
|
111
|
+
ar_table_name = table.rails_name
|
112
|
+
|
113
|
+
pk = table.identifier_columns
|
114
|
+
if pk[0].is_auto_assigned
|
115
|
+
identity_column = pk[0]
|
116
|
+
warn "Warning: redundant column(s) after #{identity_column.name} in primary key of #{ar_table_name}" if pk.size > 1
|
117
|
+
end
|
118
|
+
|
119
|
+
# Get the list of references that give rise to foreign keys:
|
120
|
+
fk_refs = table.references_from.select{|ref| ref.is_simple_reference }
|
121
|
+
|
122
|
+
# Get the list of columns that embody the foreign keys:
|
123
|
+
fk_columns = table.columns.select do |column|
|
124
|
+
column.references[0].is_simple_reference
|
125
|
+
end
|
65
126
|
|
66
|
-
|
67
|
-
|
68
|
-
|
127
|
+
# Detect if this table is a join table.
|
128
|
+
# Join tables have multi-part primary keys that are made up only of foreign keys
|
129
|
+
is_join_table = pk.length > 1 and
|
130
|
+
!pk.detect do |pk_column|
|
131
|
+
!fk_columns.include?(pk_column)
|
69
132
|
end
|
133
|
+
warn "Warning: #{table.name} has a multi-part primary key" if pk.length > 1 and !is_join_table
|
70
134
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
# We sort the columns here, not in the persistence layer, because it affects
|
95
|
-
# the ordering of columns in an index :-(.
|
96
|
-
|
97
|
-
columns = table.
|
98
|
-
columns.
|
99
|
-
sort_by do |column|
|
100
|
-
[ # Emit columns alphabetically, but PK first, then FKs, then others
|
101
|
-
case
|
102
|
-
when column == identity_column
|
103
|
-
0
|
104
|
-
when fk_columns.include?(column)
|
105
|
-
1
|
106
|
-
else
|
107
|
-
2
|
108
|
-
end,
|
109
|
-
column.rails_name
|
135
|
+
puts %Q{ create_table "#{ar_table_name}", :id => false, :force => true do |t|}
|
136
|
+
|
137
|
+
columns = generate_columns table, pk, fk_columns
|
138
|
+
|
139
|
+
unless @exclude_fks
|
140
|
+
table.foreign_keys.each do |fk|
|
141
|
+
from_columns = fk.from_columns.map{|column| column.rails_name}
|
142
|
+
to_columns = fk.to_columns.map{|column| column.rails_name}
|
143
|
+
|
144
|
+
foreign_keys.concat(
|
145
|
+
if (from_columns.length == 1)
|
146
|
+
[
|
147
|
+
" add_foreign_key :#{fk.from.rails_name}, :#{fk.to.rails_name}, :column => :#{from_columns[0]}, :primary_key => :#{to_columns[0]}, :on_delete => :cascade"
|
148
|
+
]+
|
149
|
+
Array(
|
150
|
+
# Index it non-uniquely only if it's not unique already:
|
151
|
+
fk.jump_reference.to_role.unique ? nil :
|
152
|
+
" add_index :#{fk.from.rails_name}, [:#{from_columns[0]}], :unique => false"
|
153
|
+
)
|
154
|
+
else
|
155
|
+
# This probably isn't going to work without Dr Nic's CPK gem:
|
156
|
+
[
|
157
|
+
" add_foreign_key :#{fk.to.rails_name}, :#{fk.from.rails_name}, :column => [:#{from_columns.join(':, ')}], :primary_key => [:#{to_columns.join(':, ')}], :on_delete => :cascade"
|
110
158
|
]
|
111
|
-
end
|
112
|
-
|
113
|
-
next [] if move_pk_to_create_table_call and column == pk[0]
|
114
|
-
name = column.rails_name
|
115
|
-
type, params, constraints = column.type
|
116
|
-
length = params[:length]
|
117
|
-
length &&= length.to_i
|
118
|
-
scale = params[:scale]
|
119
|
-
scale &&= scale.to_i
|
120
|
-
type, length = Persistence::rails_type(type, length)
|
121
|
-
|
122
|
-
length_name = type == 'decimal' ? 'precision' : 'limit'
|
123
|
-
|
124
|
-
primary = (!is_join_table && pk.include?(column)) ? ", :primary => true" : ''
|
125
|
-
comment = column.comment
|
126
|
-
(@include_comments ? [" \# #{comment}"] : []) +
|
127
|
-
[
|
128
|
-
%Q{ t.#{type}\t"#{name}"#{
|
129
|
-
length ? ", :#{length_name} => #{length}" : ''
|
130
|
-
}#{
|
131
|
-
scale ? ", :scale => #{scale}" : ''
|
132
|
-
}#{
|
133
|
-
column.is_mandatory ? ', :null => false' : ''
|
134
|
-
}#{primary}}
|
135
|
-
]
|
136
|
-
end.flatten
|
137
|
-
|
138
|
-
unless @exclude_fks
|
139
|
-
table.foreign_keys.each do |fk|
|
140
|
-
from_columns = fk.from_columns.map{|column| column.rails_name}
|
141
|
-
to_columns = fk.to_columns.map{|column| column.rails_name}
|
142
|
-
foreign_keys <<
|
143
|
-
if (from_columns.length == 1)
|
144
|
-
" add_foreign_key :#{fk.from.rails_name}, :#{fk.to.rails_name}, :column => :#{from_columns[0]}, :primary_key => :#{to_columns[0]}, :dependent => :cascade"
|
145
|
-
else
|
146
|
-
# This probably isn't going to work without Dr Nic's CPK gem:
|
147
|
-
" add_foreign_key :#{fk.to.rails_name}, :#{fk.from.rails_name}, :column => [:#{from_columns.join(':, ')}], :primary_key => [:#{to_columns.join(':, ')}], :dependent => :cascade"
|
148
|
-
end
|
149
|
-
end
|
159
|
+
end
|
160
|
+
)
|
150
161
|
end
|
162
|
+
end
|
151
163
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
164
|
+
indices = table.indices
|
165
|
+
index_text = []
|
166
|
+
indices.each do |index|
|
167
|
+
next if index.is_primary && index.columns.size == 1 # We've handled this already
|
156
168
|
|
157
|
-
|
169
|
+
index_name = index.rails_name
|
158
170
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
171
|
+
unique = !index.columns.detect{|column| !column.is_mandatory} and !@closed_world
|
172
|
+
index_text << %Q{ add_index "#{ar_table_name}", ["#{index.columns.map{|c| c.rails_name}*'", "'}"], :name => :#{index_name}#{
|
173
|
+
unique ? ", :unique => true" : ''
|
174
|
+
}}
|
175
|
+
end
|
176
|
+
|
177
|
+
puts columns.join("\n")
|
178
|
+
puts " end\n\n"
|
179
|
+
|
180
|
+
puts index_text.join("\n")
|
181
|
+
puts "\n" unless index_text.empty?
|
182
|
+
end
|
183
|
+
|
184
|
+
def generate(out = $>) #:nodoc:
|
185
|
+
return if @helping
|
186
|
+
@out = out
|
164
187
|
|
165
|
-
|
166
|
-
puts " end\n\n"
|
188
|
+
foreign_keys = []
|
167
189
|
|
168
|
-
|
169
|
-
|
190
|
+
# If we get index names that need to be truncated, add a counter to ensure uniqueness
|
191
|
+
dup_id = 0
|
192
|
+
|
193
|
+
puts "#\n# schema.rb auto-generated using ActiveFacts for #{@vocabulary.name} on #{Date.today}\n#\n\n"
|
194
|
+
puts "ActiveRecord::Base.logger = Logger.new(STDOUT)\n"
|
195
|
+
puts "ActiveRecord::Schema.define(:version => #{Time.now.strftime('%Y%m%d%H%M%S')}) do"
|
196
|
+
puts " enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')\n"
|
197
|
+
|
198
|
+
@vocabulary.tables.each do |table|
|
199
|
+
generate_table table, foreign_keys
|
170
200
|
end
|
171
201
|
|
172
202
|
unless @exclude_fks
|