activefacts 1.6.0 → 1.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.
- checksums.yaml +4 -4
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +60 -0
- data/Rakefile +3 -80
- data/activefacts.gemspec +36 -0
- data/bin/afgen +4 -2
- data/bin/cql +5 -1
- data/lib/activefacts.rb +3 -12
- data/lib/activefacts/{vocabulary/query_evaluator.rb → query/evaluator.rb} +0 -0
- data/lib/activefacts/version.rb +2 -2
- metadata +48 -296
- data/History.txt +0 -4
- data/LICENSE +0 -19
- data/Manifest.txt +0 -165
- data/README.rdoc +0 -81
- data/css/offline.css +0 -3
- data/css/orm2.css +0 -124
- data/css/print.css +0 -8
- data/css/style-print.css +0 -357
- data/css/style.css +0 -387
- data/download.html +0 -110
- data/examples/CQL/Address.cql +0 -44
- data/examples/CQL/Blog.cql +0 -54
- data/examples/CQL/CompanyDirectorEmployee.cql +0 -56
- data/examples/CQL/Death.cql +0 -17
- data/examples/CQL/Diplomacy.cql +0 -48
- data/examples/CQL/Genealogy.cql +0 -98
- data/examples/CQL/Insurance.cql +0 -320
- data/examples/CQL/Marriage.cql +0 -18
- data/examples/CQL/Metamodel.cql +0 -493
- data/examples/CQL/Monogamy.cql +0 -24
- data/examples/CQL/MultiInheritance.cql +0 -22
- data/examples/CQL/NonRoleId.cql +0 -14
- data/examples/CQL/OddIdentifier.cql +0 -18
- data/examples/CQL/OilSupply.cql +0 -53
- data/examples/CQL/OneToOnes.cql +0 -17
- data/examples/CQL/Orienteering.cql +0 -111
- data/examples/CQL/PersonPlaysGame.cql +0 -18
- data/examples/CQL/RedundantDependency.cql +0 -34
- data/examples/CQL/SchoolActivities.cql +0 -33
- data/examples/CQL/SeparateSubtype.cql +0 -30
- data/examples/CQL/ServiceDirector.cql +0 -276
- data/examples/CQL/SimplestUnary.cql +0 -12
- data/examples/CQL/Supervision.cql +0 -34
- data/examples/CQL/WaiterTips.cql +0 -33
- data/examples/CQL/Warehousing.cql +0 -101
- data/examples/CQL/WindowInRoomInBldg.cql +0 -28
- data/examples/CQL/unit.cql +0 -474
- data/examples/index.html +0 -420
- data/examples/intro.html +0 -327
- data/examples/local.css +0 -24
- data/index.html +0 -111
- data/lib/activefacts/cql.rb +0 -35
- data/lib/activefacts/cql/CQLParser.treetop +0 -158
- data/lib/activefacts/cql/Context.treetop +0 -48
- data/lib/activefacts/cql/Expressions.treetop +0 -67
- data/lib/activefacts/cql/FactTypes.treetop +0 -358
- data/lib/activefacts/cql/Language/English.treetop +0 -315
- data/lib/activefacts/cql/LexicalRules.treetop +0 -253
- data/lib/activefacts/cql/ObjectTypes.treetop +0 -210
- data/lib/activefacts/cql/Rakefile +0 -14
- data/lib/activefacts/cql/Terms.treetop +0 -183
- data/lib/activefacts/cql/ValueTypes.treetop +0 -202
- data/lib/activefacts/cql/compiler.rb +0 -156
- data/lib/activefacts/cql/compiler/clause.rb +0 -1137
- data/lib/activefacts/cql/compiler/constraint.rb +0 -581
- data/lib/activefacts/cql/compiler/entity_type.rb +0 -457
- data/lib/activefacts/cql/compiler/expression.rb +0 -443
- data/lib/activefacts/cql/compiler/fact.rb +0 -390
- data/lib/activefacts/cql/compiler/fact_type.rb +0 -421
- data/lib/activefacts/cql/compiler/query.rb +0 -106
- data/lib/activefacts/cql/compiler/shared.rb +0 -161
- data/lib/activefacts/cql/compiler/value_type.rb +0 -174
- data/lib/activefacts/cql/nodes.rb +0 -49
- data/lib/activefacts/cql/parser.rb +0 -241
- data/lib/activefacts/dependency_analyser.rb +0 -182
- data/lib/activefacts/generate/absorption.rb +0 -70
- data/lib/activefacts/generate/composition.rb +0 -118
- data/lib/activefacts/generate/cql.rb +0 -714
- data/lib/activefacts/generate/dm.rb +0 -279
- data/lib/activefacts/generate/help.rb +0 -64
- data/lib/activefacts/generate/helpers/inject.rb +0 -16
- data/lib/activefacts/generate/helpers/oo.rb +0 -162
- data/lib/activefacts/generate/helpers/ordered.rb +0 -605
- data/lib/activefacts/generate/helpers/rails.rb +0 -57
- data/lib/activefacts/generate/html/glossary.rb +0 -461
- data/lib/activefacts/generate/json.rb +0 -337
- data/lib/activefacts/generate/null.rb +0 -32
- data/lib/activefacts/generate/rails/models.rb +0 -246
- data/lib/activefacts/generate/rails/schema.rb +0 -216
- data/lib/activefacts/generate/records.rb +0 -46
- data/lib/activefacts/generate/ruby.rb +0 -133
- data/lib/activefacts/generate/sql/mysql.rb +0 -280
- data/lib/activefacts/generate/sql/server.rb +0 -273
- data/lib/activefacts/generate/stats.rb +0 -69
- data/lib/activefacts/generate/text.rb +0 -27
- data/lib/activefacts/generate/topics.rb +0 -265
- data/lib/activefacts/generate/traits/datavault.rb +0 -241
- data/lib/activefacts/generate/traits/oo.rb +0 -73
- data/lib/activefacts/generate/traits/ordered.rb +0 -33
- data/lib/activefacts/generate/traits/ruby.rb +0 -210
- data/lib/activefacts/generate/transform/datavault.rb +0 -266
- data/lib/activefacts/generate/transform/surrogate.rb +0 -214
- data/lib/activefacts/generate/version.rb +0 -26
- data/lib/activefacts/input/cql.rb +0 -43
- data/lib/activefacts/input/orm.rb +0 -1636
- data/lib/activefacts/mapping/rails.rb +0 -132
- data/lib/activefacts/persistence.rb +0 -15
- data/lib/activefacts/persistence/columns.rb +0 -446
- data/lib/activefacts/persistence/foreignkey.rb +0 -187
- data/lib/activefacts/persistence/index.rb +0 -240
- data/lib/activefacts/persistence/object_type.rb +0 -198
- data/lib/activefacts/persistence/reference.rb +0 -434
- data/lib/activefacts/persistence/tables.rb +0 -380
- data/lib/activefacts/registry.rb +0 -11
- data/lib/activefacts/support.rb +0 -132
- data/lib/activefacts/vocabulary.rb +0 -9
- data/lib/activefacts/vocabulary/extensions.rb +0 -1348
- data/lib/activefacts/vocabulary/metamodel.rb +0 -570
- data/lib/activefacts/vocabulary/verbaliser.rb +0 -804
- data/script/txt2html +0 -71
- data/spec/absorption_spec.rb +0 -95
- data/spec/cql/comparison_spec.rb +0 -89
- data/spec/cql/context_spec.rb +0 -94
- data/spec/cql/contractions_spec.rb +0 -224
- data/spec/cql/deontic_spec.rb +0 -88
- data/spec/cql/entity_type_spec.rb +0 -320
- data/spec/cql/expressions_spec.rb +0 -66
- data/spec/cql/fact_type_matching_spec.rb +0 -338
- data/spec/cql/french_spec.rb +0 -21
- data/spec/cql/parser/bad_literals_spec.rb +0 -86
- data/spec/cql/parser/constraints_spec.rb +0 -19
- data/spec/cql/parser/entity_types_spec.rb +0 -106
- data/spec/cql/parser/expressions_spec.rb +0 -199
- data/spec/cql/parser/fact_types_spec.rb +0 -44
- data/spec/cql/parser/literals_spec.rb +0 -312
- data/spec/cql/parser/pragmas_spec.rb +0 -89
- data/spec/cql/parser/value_types_spec.rb +0 -42
- data/spec/cql/role_matching_spec.rb +0 -148
- data/spec/cql/samples_spec.rb +0 -244
- data/spec/cql_cql_spec.rb +0 -73
- data/spec/cql_dm_spec.rb +0 -136
- data/spec/cql_mysql_spec.rb +0 -69
- data/spec/cql_parse_spec.rb +0 -34
- data/spec/cql_ruby_spec.rb +0 -73
- data/spec/cql_sql_spec.rb +0 -72
- data/spec/cql_symbol_tables_spec.rb +0 -261
- data/spec/cqldump_spec.rb +0 -170
- data/spec/helpers/array_matcher.rb +0 -23
- data/spec/helpers/ctrl_c_support.rb +0 -52
- data/spec/helpers/diff_matcher.rb +0 -39
- data/spec/helpers/file_matcher.rb +0 -34
- data/spec/helpers/parse_to_ast_matcher.rb +0 -80
- data/spec/helpers/string_matcher.rb +0 -30
- data/spec/helpers/test_parser.rb +0 -15
- data/spec/norma_cql_spec.rb +0 -66
- data/spec/norma_ruby_spec.rb +0 -62
- data/spec/norma_ruby_sql_spec.rb +0 -107
- data/spec/norma_sql_spec.rb +0 -57
- data/spec/norma_tables_spec.rb +0 -95
- data/spec/ruby_api_spec.rb +0 -23
- data/spec/spec_helper.rb +0 -35
- data/spec/transform_surrogate_spec.rb +0 -59
- data/status.html +0 -138
- data/why.html +0 -60
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Generators.
|
|
3
|
-
# Generate json output from a vocabulary, for loading into APRIMO
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
require 'json'
|
|
8
|
-
require 'digest/sha1'
|
|
9
|
-
|
|
10
|
-
module ActiveFacts
|
|
11
|
-
module Generate
|
|
12
|
-
# Generate json output from a vocabulary, for loading into APRIMO.
|
|
13
|
-
# Invoke as
|
|
14
|
-
# afgen --json <file>.cql=diagrams
|
|
15
|
-
class JSON
|
|
16
|
-
private
|
|
17
|
-
def initialize(vocabulary)
|
|
18
|
-
@vocabulary = vocabulary
|
|
19
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def puts(*a)
|
|
23
|
-
@out.puts *a
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
public
|
|
27
|
-
def generate(out = $>)
|
|
28
|
-
@out = out
|
|
29
|
-
uuids = {}
|
|
30
|
-
|
|
31
|
-
puts "{ model: '#{@vocabulary.name}',\n" +
|
|
32
|
-
"diagrams: [\n#{
|
|
33
|
-
@vocabulary.all_diagram.sort_by{|o| o.name.gsub(/ /,'')}.map do |d|
|
|
34
|
-
j = {:uuid => (uuids[d] ||= uuid_from_id(d)), :name => d.name}
|
|
35
|
-
" #{j.to_json}"
|
|
36
|
-
end*",\n"
|
|
37
|
-
}\n ],"
|
|
38
|
-
|
|
39
|
-
object_types = @vocabulary.all_object_type.sort_by{|o| o.name.gsub(/ /,'')}
|
|
40
|
-
puts " object_types: [\n#{
|
|
41
|
-
object_types.sort_by{|o|o.identifying_role_values.inspect}.map do |o|
|
|
42
|
-
uuids[o] ||= uuid_from_id(o)
|
|
43
|
-
ref_mode = nil
|
|
44
|
-
if o.is_a?(ActiveFacts::Metamodel::EntityType) and
|
|
45
|
-
p = o.preferred_identifier and
|
|
46
|
-
(rrs = p.role_sequence.all_role_ref).size == 1 and
|
|
47
|
-
(r = rrs.single.role).fact_type != o.fact_type and
|
|
48
|
-
r.object_type.is_a?(ActiveFacts::Metamodel::ValueType) and
|
|
49
|
-
!r.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
|
50
|
-
ref_mode = "#{r.object_type.name}"
|
|
51
|
-
ref_mode.sub!(%r{#{o.name} *}, '.')
|
|
52
|
-
end
|
|
53
|
-
j = {
|
|
54
|
-
:uuid => uuids[o],
|
|
55
|
-
:name => o.name,
|
|
56
|
-
:shapes => o.all_object_type_shape.sort_by{|s| [s.location.x, s.location.y]}.map do |shape|
|
|
57
|
-
x = { :diagram => uuids[shape.orm_diagram],
|
|
58
|
-
:is_expanded => shape.is_expanded,
|
|
59
|
-
:uuid => uuid_from_id(shape),
|
|
60
|
-
:x => shape.location.x,
|
|
61
|
-
:y => shape.location.y
|
|
62
|
-
}
|
|
63
|
-
x[:is_expanded] = true if ref_mode && shape.is_expanded # Don't show the reference mode
|
|
64
|
-
x
|
|
65
|
-
end
|
|
66
|
-
}
|
|
67
|
-
j[:ref_mode] = ref_mode if ref_mode
|
|
68
|
-
j[:independent] = true if o.is_independent
|
|
69
|
-
|
|
70
|
-
if o.is_a?(ActiveFacts::Metamodel::EntityType)
|
|
71
|
-
# Entity Type may be objectified, and may have supertypes:
|
|
72
|
-
if o.fact_type
|
|
73
|
-
uuid = (uuids[o.fact_type] ||= uuid_from_id(o.fact_type))
|
|
74
|
-
j[:objectifies] = uuid
|
|
75
|
-
j[:implicit] = true if o.concept.implication_rule
|
|
76
|
-
end
|
|
77
|
-
if o.all_type_inheritance_as_subtype.size > 0
|
|
78
|
-
j[:supertypes] = o.
|
|
79
|
-
all_type_inheritance_as_subtype.
|
|
80
|
-
sort_by{|ti| ti.provides_identification ? 0 : 1}.
|
|
81
|
-
map{|ti|
|
|
82
|
-
[ uuids[ti.supertype] ||= uuid_from_id(ti.supertype),
|
|
83
|
-
uuids[ti.supertype_role] = uuid_from_id(ti.supertype_role)
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
end
|
|
87
|
-
else
|
|
88
|
-
# ValueType usually has a supertype:
|
|
89
|
-
if (o.supertype)
|
|
90
|
-
j[:supertype] = (uuids[o.supertype] ||= uuid_from_id(o.supertype))
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
# REVISIT: Place a ValueConstraint and shape
|
|
94
|
-
" #{j.to_json}"
|
|
95
|
-
end*",\n"
|
|
96
|
-
}\n ],"
|
|
97
|
-
|
|
98
|
-
fact_types = @vocabulary.constellation.
|
|
99
|
-
FactType.values.
|
|
100
|
-
reject{|ft|
|
|
101
|
-
ActiveFacts::Metamodel::LinkFactType === ft || ActiveFacts::Metamodel::TypeInheritance === ft
|
|
102
|
-
}
|
|
103
|
-
puts " fact_types: [\n#{
|
|
104
|
-
fact_types.sort_by{|f| f.identifying_role_values.inspect}.map do |f|
|
|
105
|
-
uuids[f] ||= uuid_from_id(f)
|
|
106
|
-
j = {:uuid => uuids[f]}
|
|
107
|
-
|
|
108
|
-
if f.entity_type
|
|
109
|
-
j[:objectified_as] = uuids[f.entity_type]
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Emit roles
|
|
113
|
-
roles = f.all_role.sort_by{|r| r.ordinal }
|
|
114
|
-
j[:roles] = roles.map do |role|
|
|
115
|
-
uuid = (uuids[role] ||= uuid_from_id(role))
|
|
116
|
-
# REVISIT: Internal Mandatory Constraints
|
|
117
|
-
# REVISIT: Place a ValueConstraint and shape
|
|
118
|
-
# REVISIT: Place a RoleName shape
|
|
119
|
-
{:uuid => uuid, :player => uuids[role.object_type]}
|
|
120
|
-
# N.B. The object_type shape to which this role is attached is not in the meta-model
|
|
121
|
-
# Attach to the closest instance on this diagram (if any)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Emit readings. Each is a [role_order, text] pair
|
|
125
|
-
j[:readings] = f.all_reading.map do |r|
|
|
126
|
-
role_refs = r.role_sequence.all_role_ref_in_order
|
|
127
|
-
[
|
|
128
|
-
role_order(uuids, role_refs.map{|rr| rr.role}, roles),
|
|
129
|
-
r.text.gsub(/\{([0-9])\}/) do |insert|
|
|
130
|
-
role_ref = role_refs[$1.to_i]
|
|
131
|
-
la = role_ref.leading_adjective
|
|
132
|
-
la = nil if la == ''
|
|
133
|
-
ta = role_ref.trailing_adjective
|
|
134
|
-
ta = nil if ta == ''
|
|
135
|
-
(la ? la+'-' : '') +
|
|
136
|
-
(la && la.index(' ') ? ' ' : '') +
|
|
137
|
-
insert +
|
|
138
|
-
(ta && ta.index(' ') ? ' ' : '') +
|
|
139
|
-
(ta ? '-'+ta : '')
|
|
140
|
-
end
|
|
141
|
-
]
|
|
142
|
-
end.sort_by{|(ro,text)| ro }.map do |(ro,text)|
|
|
143
|
-
[ ro, text ]
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Emit shapes
|
|
147
|
-
j[:shapes] = f.all_fact_type_shape.sort_by{|s| [s.location.x, s.location.y]}.map do |shape|
|
|
148
|
-
sj = {
|
|
149
|
-
:diagram => uuids[shape.orm_diagram],
|
|
150
|
-
:uuid => uuid_from_id(shape),
|
|
151
|
-
:x => shape.location.x,
|
|
152
|
-
:y => shape.location.y
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
# Add the role_order, if specified
|
|
156
|
-
if shape.all_role_display.size > 0
|
|
157
|
-
if shape.all_role_display.size != roles.size
|
|
158
|
-
raise "Invalid RoleDisplay for #{f.default_reading} in #{shape.orm_diagram.name} diagram"
|
|
159
|
-
end
|
|
160
|
-
ro = role_order(
|
|
161
|
-
uuids,
|
|
162
|
-
shape.all_role_display.sort_by{|rd| rd.ordinal }.map{|rd| rd.role },
|
|
163
|
-
roles
|
|
164
|
-
)
|
|
165
|
-
sj[:role_order] = ro if ro
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# REVISIT: Place the ReadingShape
|
|
169
|
-
|
|
170
|
-
# Emit the location of the name, if objectified
|
|
171
|
-
if n = shape.objectified_fact_type_name_shape
|
|
172
|
-
sj[:name_shape] = {:x => n.location.x, :y => n.location.y}
|
|
173
|
-
end
|
|
174
|
-
sj
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# Emit Internal Presence Constraints
|
|
178
|
-
f.internal_presence_constraints.to_a.sort_by{|ipc, z|
|
|
179
|
-
[ipc.is_preferred_identifier ? 0 : 1, ipc.is_mandatory ? 0 : 1, ipc.min_frequency || 0, ipc.max_frequency || 1_000]
|
|
180
|
-
}.each do |ipc|
|
|
181
|
-
uuid = (uuids[ipc] ||= uuid_from_id(ipc))
|
|
182
|
-
|
|
183
|
-
constraint = {
|
|
184
|
-
:uuid => uuid,
|
|
185
|
-
:min => ipc.min_frequency,
|
|
186
|
-
:max => ipc.max_frequency,
|
|
187
|
-
:is_preferred => ipc.is_preferred_identifier,
|
|
188
|
-
:mandatory => ipc.is_mandatory
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
# Get the role (or excluded role, for a UC)
|
|
192
|
-
roles = ipc.role_sequence.all_role_ref_in_order.map{|r| r.role}
|
|
193
|
-
if roles.size > 1 || (!ipc.is_mandatory && ipc.max_frequency == 1)
|
|
194
|
-
# This can be only a uniqueness constraint. Record the missing role, if any
|
|
195
|
-
role = (f.all_role.to_a - roles)[0]
|
|
196
|
-
constraint[:uniqueExcept] = uuids[role]
|
|
197
|
-
else
|
|
198
|
-
# An internal mandatory or frequency constraint applies to only one role.
|
|
199
|
-
# If it's also unique (max == 1), that applies on the counterpart role.
|
|
200
|
-
# You can also have a mandatory frequency constraint, but that applies on this role.
|
|
201
|
-
constraint[:role] = uuids[roles[0]]
|
|
202
|
-
end
|
|
203
|
-
(j[:constraints] ||= []) << constraint
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
# Add ring constraints
|
|
207
|
-
f.all_role_in_order.
|
|
208
|
-
map{|r| r.all_ring_constraint.to_a+r.all_ring_constraint_as_other_role.to_a }.
|
|
209
|
-
flatten.uniq.each do |ring|
|
|
210
|
-
(j[:constraints] ||= []) << {
|
|
211
|
-
:uuid => (uuids[ring] ||= uuid_from_id(ring)),
|
|
212
|
-
:shapes => ring.all_constraint_shape.sort_by{|s| [s.location.x, s.location.y]}.map do |shape|
|
|
213
|
-
{ :diagram => uuids[shape.orm_diagram],
|
|
214
|
-
:uuid => uuid_from_id(shape),
|
|
215
|
-
:x => shape.location.x,
|
|
216
|
-
:y => shape.location.y
|
|
217
|
-
}
|
|
218
|
-
end,
|
|
219
|
-
:ringKind => ring.ring_type,
|
|
220
|
-
:roles => [uuids[ring.role], uuids[ring.other_role]]
|
|
221
|
-
# REVISIT: Deontic, enforcement
|
|
222
|
-
}
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# REVISIT: RotationSetting
|
|
226
|
-
|
|
227
|
-
" #{j.to_json}"
|
|
228
|
-
end*",\n"
|
|
229
|
-
}\n ],"
|
|
230
|
-
|
|
231
|
-
constraints = @vocabulary.constellation.
|
|
232
|
-
Constraint.values
|
|
233
|
-
puts " constraints: [\n#{
|
|
234
|
-
constraints.sort_by{|c|c.identifying_role_values.inspect}.select{|c| !uuids[c]}.map do |c|
|
|
235
|
-
uuid = uuids[c] ||= uuid_from_id(c)
|
|
236
|
-
j = {
|
|
237
|
-
:uuid => uuid,
|
|
238
|
-
:type => c.class.basename,
|
|
239
|
-
:shapes => c.all_constraint_shape.sort_by{|s| [s.location.x, s.location.y]}.map do |shape|
|
|
240
|
-
{ :diagram => uuids[shape.orm_diagram],
|
|
241
|
-
:uuid => uuid_from_id(shape),
|
|
242
|
-
:x => shape.location.x,
|
|
243
|
-
:y => shape.location.y
|
|
244
|
-
}
|
|
245
|
-
end
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (c.enforcement)
|
|
249
|
-
# REVISIT: Deontic constraint
|
|
250
|
-
end
|
|
251
|
-
if (c.concept.all_context_note_as_relevant_concept.size > 0)
|
|
252
|
-
# REVISIT: Context Notes
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
case c
|
|
256
|
-
when ActiveFacts::Metamodel::PresenceConstraint
|
|
257
|
-
j[:min_frequency] = c.min_frequency
|
|
258
|
-
j[:max_frequency] = c.max_frequency
|
|
259
|
-
j[:is_mandatory] = c.is_mandatory
|
|
260
|
-
j[:is_preferred_identifier] = c.is_preferred_identifier
|
|
261
|
-
rss = [c.role_sequence.all_role_ref_in_order.map(&:role)]
|
|
262
|
-
|
|
263
|
-
# Ignore internal presence constraints on TypeInheritance fact types
|
|
264
|
-
next nil if !c.role_sequence.all_role_ref.
|
|
265
|
-
detect{|rr|
|
|
266
|
-
!rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
when ActiveFacts::Metamodel::RingConstraint
|
|
270
|
-
next nil # These are emitted with the corresponding fact type
|
|
271
|
-
|
|
272
|
-
when ActiveFacts::Metamodel::SetComparisonConstraint
|
|
273
|
-
rss = c.
|
|
274
|
-
all_set_comparison_roles.sort_by{|scr| scr.ordinal}.
|
|
275
|
-
map{|scr| scr.role_sequence.all_role_ref_in_order.map(&:role) }
|
|
276
|
-
if (ActiveFacts::Metamodel::SetExclusionConstraint === c)
|
|
277
|
-
j[:is_mandatory] = c.is_mandatory
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
when ActiveFacts::Metamodel::SubsetConstraint
|
|
281
|
-
rss = [c.subset_role_sequence, c.superset_role_sequence].
|
|
282
|
-
map{|rs| rs.all_role_ref_in_order.map(&:role) }
|
|
283
|
-
|
|
284
|
-
when ActiveFacts::Metamodel::ValueConstraint
|
|
285
|
-
next nil # REVISIT: Should have been handled elsewhere
|
|
286
|
-
if (c.role)
|
|
287
|
-
# Should have been handled as role.role_value_constraint
|
|
288
|
-
elsif (c.value_type)
|
|
289
|
-
# Should have been handled as object_type.value_constraint
|
|
290
|
-
end
|
|
291
|
-
j[:allowed_ranges] = c.all_allowed_range.map{|ar|
|
|
292
|
-
[ ar.value_range.minimum_bound, ar.value_range.maximum_bound ].
|
|
293
|
-
map{|b| [b.value.literal, b.value.unit.name, b.is_inclusive] }
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
else
|
|
297
|
-
raise "REVISIT: Constraint type not yet dumped to JSON"
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
# rss contains the constrained role sequences; map to uuids
|
|
301
|
-
j[:role_sequences] = rss.map{|rs|
|
|
302
|
-
rs.map do |role|
|
|
303
|
-
uuids[role]
|
|
304
|
-
end
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
" #{j.to_json}"
|
|
308
|
-
end.compact*",\n"
|
|
309
|
-
}\n ]"
|
|
310
|
-
|
|
311
|
-
puts "}"
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
def role_order(uuids, roles, order)
|
|
315
|
-
if (roles.size > 9)
|
|
316
|
-
roles.map{|r| uuids[r] }
|
|
317
|
-
else
|
|
318
|
-
roles.map{|r| order.index(r).to_s }*''
|
|
319
|
-
end
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
def uuid_from_id o
|
|
323
|
-
irvs = o.identifying_role_values.inspect
|
|
324
|
-
d = Digest::SHA1.digest irvs
|
|
325
|
-
# $stderr.puts "#{o.class.basename}: #{irvs}"
|
|
326
|
-
d[0,4].unpack("H8")[0]+'-'+
|
|
327
|
-
d[4,2].unpack("H4")[0]+'-'+
|
|
328
|
-
d[6,2].unpack("H4")[0]+'-'+
|
|
329
|
-
d[8,2].unpack("H4")[0]+'-'+
|
|
330
|
-
d[10,6].unpack("H6")[0]
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
ActiveFacts::Registry.generator('json', ActiveFacts::Generate::JSON)
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Generators.
|
|
3
|
-
# Generate *no* output for ActiveFacts vocabularies; i.e. just a stub
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
require 'activefacts/persistence'
|
|
8
|
-
|
|
9
|
-
module ActiveFacts
|
|
10
|
-
module Generate
|
|
11
|
-
# Generate nothing from an ActiveFacts vocabulary. This is useful to check the file can be read ok.
|
|
12
|
-
# Invoke as
|
|
13
|
-
# afgen --null <file>.cql
|
|
14
|
-
class NULL
|
|
15
|
-
private
|
|
16
|
-
def initialize(vocabulary, *options)
|
|
17
|
-
@vocabulary = vocabulary
|
|
18
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
|
19
|
-
@tables = options.include? "tables"
|
|
20
|
-
@columns = options.include? "columns"
|
|
21
|
-
@indices = options.include? "indices"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
public
|
|
25
|
-
def generate(out = $>)
|
|
26
|
-
@vocabulary.tables if @tables || @columns || @indices
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
ActiveFacts::Registry.generator('null', ActiveFacts::Generate::NULL)
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Generators.
|
|
3
|
-
# Generate models for Rails from an ActiveFacts vocabulary.
|
|
4
|
-
#
|
|
5
|
-
# Models should normally be generated into "app/models/auto",
|
|
6
|
-
# then extend(ed) into your real models.
|
|
7
|
-
#
|
|
8
|
-
# Copyright (c) 2013 Clifford Heath. Read the LICENSE file.
|
|
9
|
-
#
|
|
10
|
-
require 'activefacts/vocabulary'
|
|
11
|
-
require 'activefacts/persistence'
|
|
12
|
-
#require 'activefacts/generate/helpers/rails'
|
|
13
|
-
require 'activefacts/mapping/rails'
|
|
14
|
-
require 'active_support'
|
|
15
|
-
|
|
16
|
-
module ActiveFacts
|
|
17
|
-
module Generate
|
|
18
|
-
module Rails
|
|
19
|
-
# Generate Rails models for the vocabulary
|
|
20
|
-
# Invoke as
|
|
21
|
-
# afgen --rails/schema[=options] <file>.cql
|
|
22
|
-
class Models
|
|
23
|
-
|
|
24
|
-
HEADER = "# Auto-generated from CQL, edits will be lost"
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
def initialize(vocabulary, *options)
|
|
29
|
-
@vocabulary = vocabulary
|
|
30
|
-
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
|
31
|
-
help if options.include? "help"
|
|
32
|
-
options.delete_if { |option| @output = $1 if option =~ /^output=(.*)/ }
|
|
33
|
-
@concern = nil
|
|
34
|
-
options.delete_if { |option| @concern = $1 if option =~ /^concern=(.*)/ }
|
|
35
|
-
@validations = true
|
|
36
|
-
options.delete_if { |option| @validations = eval($1) if option =~ /^validation=(.*)/ }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def help
|
|
40
|
-
@helping = true
|
|
41
|
-
warn %Q{Options for --rails/schema:
|
|
42
|
-
output=dir Overwrite model files into this output directory
|
|
43
|
-
concern=name Namespace for the concerns
|
|
44
|
-
validation=false Disable generation of validations
|
|
45
|
-
}
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def warn *a
|
|
49
|
-
$stderr.puts *a
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def puts s
|
|
53
|
-
@out.puts s
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
public
|
|
57
|
-
def generate(out = $>) #:nodoc:
|
|
58
|
-
return if @helping
|
|
59
|
-
@out = out
|
|
60
|
-
list_extant_files if @output
|
|
61
|
-
|
|
62
|
-
# Populate all foreignkeys first:
|
|
63
|
-
@vocabulary.tables.each { |table| table.foreign_keys }
|
|
64
|
-
ok = true
|
|
65
|
-
@vocabulary.tables.each do |table|
|
|
66
|
-
ok &= generate_table(table)
|
|
67
|
-
end
|
|
68
|
-
$stderr.puts "\# #{@vocabulary.name} generated with errors" unless ok
|
|
69
|
-
delete_old_generated_files if @output
|
|
70
|
-
ok
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def list_extant_files
|
|
74
|
-
@preexisting_files = Dir[@output+'/*.rb']
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def delete_old_generated_files
|
|
78
|
-
remaining = []
|
|
79
|
-
cleaned = 0
|
|
80
|
-
@preexisting_files.each do |pathname|
|
|
81
|
-
if generated_file_exists(pathname) == true
|
|
82
|
-
File.unlink(pathname)
|
|
83
|
-
cleaned += 1
|
|
84
|
-
else
|
|
85
|
-
remaining << pathname
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
$stderr.puts "Cleaned up #{cleaned} old generated files" if @preexisting_files.size > 0
|
|
89
|
-
$stderr.puts "Remaining non-generated files:\n\t#{remaining*"\n\t"}" if remaining.size > 0
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def generated_file_exists pathname
|
|
93
|
-
File.open(pathname, 'r') do |existing|
|
|
94
|
-
first_lines = existing.read(1024) # Make it possible to pass over a magic charset comment
|
|
95
|
-
if first_lines.length == 0 or first_lines =~ %r{^#{HEADER}}
|
|
96
|
-
return true
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
return false # File exists, but is not generated
|
|
100
|
-
rescue Errno::ENOENT
|
|
101
|
-
return nil # File does not exist
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def create_if_ok filename
|
|
105
|
-
# Create a file in the output directory, being careful not to overwrite carelessly
|
|
106
|
-
if @output
|
|
107
|
-
pathname = (@output+'/'+filename).gsub(%r{//+}, '/')
|
|
108
|
-
@preexisting_files.reject!{|f| f == pathname } # Don't clean up this file
|
|
109
|
-
if generated_file_exists(pathname) == false
|
|
110
|
-
$stderr.puts "not overwriting non-generated file #{pathname}"
|
|
111
|
-
@individual_file = nil
|
|
112
|
-
return
|
|
113
|
-
end
|
|
114
|
-
@individual_file = @out = File.open(pathname, 'w')
|
|
115
|
-
puts "#{HEADER}"
|
|
116
|
-
end
|
|
117
|
-
true
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def to_associations table
|
|
121
|
-
# belongs_to Associations
|
|
122
|
-
table.foreign_keys.map do |fk|
|
|
123
|
-
association_name = fk.rails_from_association_name
|
|
124
|
-
|
|
125
|
-
if association_name != fk.to.rails_singular_name
|
|
126
|
-
# A different class_name is implied, emit an explicit one:
|
|
127
|
-
class_name = ", :class_name => '#{fk.to.rails_class_name}'"
|
|
128
|
-
end
|
|
129
|
-
foreign_key = ", :foreign_key => :#{fk.from_columns[0].rails_name}"
|
|
130
|
-
if foreign_key == fk.to.rails_singular_name+'_id'
|
|
131
|
-
# See lib/active_record/reflection.rb, method #derive_foreign_key
|
|
132
|
-
foreign_key = ''
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
%Q{
|
|
136
|
-
\# #{fk.verbalised_path}
|
|
137
|
-
belongs_to :#{association_name}#{class_name}#{foreign_key}}
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def from_associations table
|
|
142
|
-
# has_one/has_many Associations
|
|
143
|
-
table.foreign_keys_to.sort_by{|fk| fk.describe}.map do |fk|
|
|
144
|
-
# Get the jump reference
|
|
145
|
-
|
|
146
|
-
if fk.from_columns.size > 1
|
|
147
|
-
raise "Can't emit Rails associations for multi-part foreign key with #{fk.references.inspect}. Did you mean to use --transform/surrogate"
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
association_type, association_name = *fk.rails_to_association
|
|
151
|
-
|
|
152
|
-
ref = fk.jump_reference
|
|
153
|
-
[
|
|
154
|
-
"\n \# #{fk.verbalised_path(true)}" +
|
|
155
|
-
"\n" +
|
|
156
|
-
%Q{ #{association_type} :#{association_name}} +
|
|
157
|
-
%Q{, :class_name => '#{fk.from.rails_class_name}'} +
|
|
158
|
-
%Q{, :foreign_key => :#{fk.from_columns[0].rails_name}} +
|
|
159
|
-
%Q{, :dependent => :destroy}
|
|
160
|
-
] +
|
|
161
|
-
# If ref.from is a join table, we can emit a has_many :through for each other key
|
|
162
|
-
# REVISIT Could alternately do this for all belongs_to's in ref.from
|
|
163
|
-
if ref.from.identifier_columns.length > 1
|
|
164
|
-
ref.from.identifier_columns.map do |ic|
|
|
165
|
-
next nil if ic.references[0] == ref or # Skip the back-reference
|
|
166
|
-
ic.references[0].is_unary # or use rails_plural_name(ic.references[0].to_names) ?
|
|
167
|
-
# This far association name needs to be augmented for its role name
|
|
168
|
-
far_association_name = ic.references[0].to.rails_name
|
|
169
|
-
%Q{ has_many :#{far_association_name}, :through => :#{association_name}} # \# via #{ic.name}}
|
|
170
|
-
end
|
|
171
|
-
else
|
|
172
|
-
[]
|
|
173
|
-
end
|
|
174
|
-
end.flatten.compact
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def column_constraints table
|
|
178
|
-
return [] unless @validations
|
|
179
|
-
ccs =
|
|
180
|
-
table.columns.map do |column|
|
|
181
|
-
name = column.rails_name
|
|
182
|
-
column.is_mandatory &&
|
|
183
|
-
!column.is_auto_assigned && !column.is_auto_timestamp ? [
|
|
184
|
-
" validates :#{name}, :presence => true"
|
|
185
|
-
] : []
|
|
186
|
-
end.flatten
|
|
187
|
-
ccs.unshift("") unless ccs.empty?
|
|
188
|
-
ccs
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def model_body table
|
|
192
|
-
%Q{module #{table.rails_class_name}
|
|
193
|
-
extend ActiveSupport::Concern
|
|
194
|
-
included do} +
|
|
195
|
-
(table.identifier_columns.length == 1 ? %Q{
|
|
196
|
-
self.primary_key = '#{table.identifier_columns[0].rails_name}'
|
|
197
|
-
} : ''
|
|
198
|
-
) +
|
|
199
|
-
|
|
200
|
-
(
|
|
201
|
-
to_associations(table) +
|
|
202
|
-
from_associations(table) +
|
|
203
|
-
column_constraints(table)
|
|
204
|
-
) * "\n" +
|
|
205
|
-
%Q{
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
}
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def generate_table table
|
|
212
|
-
old_out = @out
|
|
213
|
-
filename = table.rails_singular_name+'.rb'
|
|
214
|
-
|
|
215
|
-
return unless create_if_ok filename
|
|
216
|
-
|
|
217
|
-
puts "\n"
|
|
218
|
-
puts "module #{@concern}" if @concern
|
|
219
|
-
puts model_body(table).gsub(/^./, @concern ? ' \0' : '\0')
|
|
220
|
-
puts 'end' if @concern
|
|
221
|
-
|
|
222
|
-
true # We succeeded
|
|
223
|
-
ensure
|
|
224
|
-
@out = old_out
|
|
225
|
-
@individual_file.close if @individual_file
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
module Persistence
|
|
233
|
-
class Column
|
|
234
|
-
def is_auto_timestamp
|
|
235
|
-
case name('_')
|
|
236
|
-
when /\A(created|updated)_(at|on)\Z/i
|
|
237
|
-
true
|
|
238
|
-
else
|
|
239
|
-
false
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
ActiveFacts::Registry.generator('rails/models', ActiveFacts::Generate::Rails::Models)
|