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