activefacts-compositions 1.9.10 → 1.9.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92a737c515247b8ec677c99cdfed1a550daf92e1
4
- data.tar.gz: 863d201ecf700a29c68a83bb373ead97382ca6f5
3
+ metadata.gz: 7ca6ad3d9944b1f64ffde699ef36bd7b26764b0e
4
+ data.tar.gz: 352761a4484292f70eeb90763d7518c892d9a562
5
5
  SHA512:
6
- metadata.gz: cacf53c424c21b0764ae8e2878bf1e083f1fd7bd1ba93c7075420393ee0e34a16057688298255bb21516c40b5eb1630a632cec5c778ef745388b4065e3d6eaca
7
- data.tar.gz: f179277f0ce2fc207466933f2e344260c86cf7acd995c902275787b70218aca7883f1565e7d068d30c90b76f56a66db2604dce46d49ddec82d7de4a2bb1601ed
6
+ metadata.gz: ee1a8f1fe902ab61728c16adadedb68fe4bc2b40339d478c9b1d59e27d73c14a57c4d760a6cd61787b3eb0fe86cd9dc24da0c03bcb3609893b62967e4a6bc1bb
7
+ data.tar.gz: f979de0913251692a2be5c7f115ce9d81fb58147c04af65746fd1e4574723513bd99f877fa0409df316fa8aa94ecf28c37498f6477432ca862d4a912129df761
data/.gitignore CHANGED
@@ -13,3 +13,4 @@
13
13
  *.orig
14
14
  *.diff
15
15
  actual
16
+ .byebug_history
@@ -23,11 +23,12 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "rake", "~> 10.0"
24
24
  spec.add_development_dependency "rspec", "~> 3.3"
25
25
 
26
- spec.add_development_dependency "activefacts", "~> 1", ">= 1.8"
27
-
28
- spec.add_runtime_dependency("activefacts-api", "~> 1", ">= 1.9.11")
29
- spec.add_runtime_dependency("activefacts-metamodel", "~> 1", ">= 1.9.12")
26
+ spec.add_runtime_dependency "activesupport", "~> 4", ">= 4.2.7"
27
+ spec.add_runtime_dependency "json", "~> 1.8"
30
28
  spec.add_runtime_dependency "tracing", "~> 2", ">= 2.0.6"
31
29
 
32
- spec.add_runtime_dependency "activefacts-cql", "~> 1", ">= 1.8"
30
+ spec.add_development_dependency "activefacts", "~> 1", ">= 1.8"
31
+ spec.add_runtime_dependency "activefacts-api", "~> 1", ">= 1.9.11"
32
+ spec.add_runtime_dependency "activefacts-metamodel", "~> 1", ">= 1.9.15"
33
+ spec.add_runtime_dependency "activefacts-cql", "~> 1", ">= 1.9"
33
34
  end
data/bin/afcomp ADDED
@@ -0,0 +1,192 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # ActiveFacts: Read a model (CQL, ORM, etc), run a compositor, then a generator
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+ #
7
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
8
+ $:.unshift File.dirname(File.expand_path(__FILE__))+"/../lib"
9
+
10
+ require 'pathname'
11
+ require 'activefacts/loadable'
12
+ require 'activefacts/metamodel'
13
+ require 'activefacts/compositions'
14
+ require 'activefacts/generator'
15
+
16
+ class SchemaCompositor
17
+ EXTENSIONS = ['fiml', 'fidl', 'fiql', 'cql']
18
+
19
+ attr_reader :options
20
+ attr_reader :compositors
21
+ attr_reader :generators
22
+
23
+ # Parse options into a hash, and values for each option into a hash
24
+ def initialize argv
25
+ @options = {}
26
+ while argv[0] =~ /^-/
27
+ option, value = argv.shift.split(/:/, 2)
28
+ csv = (value =~ /,/ ? value.split(',') : Array(value))
29
+ modes = csv.inject({}){|h,s| k, v = s.split(/=/, 2); h[k] = v || true; h }
30
+ @options[option.sub(/^-*/,'')] = modes
31
+ end
32
+ end
33
+
34
+ # Load and enumerate all available modules in this path
35
+ def enumerate_available path
36
+ trace :loading, "Enumerating under #{path.inspect}" do
37
+ Loadable.new(path).
38
+ enumerate.
39
+ select do |filename|
40
+ begin
41
+ require(pathname = path+"/"+filename)
42
+ trace :loading, "Loaded #{pathname}"
43
+ # rescue LoadError => e
44
+ # trace :loading, "Can't load #{pathname}: #{e.class}: #{e.message} #{e.backtrace[0]}"
45
+ # rescue Exception => e
46
+ # $stderr.puts "Can't load #{pathname}: #{e.class}: #{e.message} #{e.backtrace[0]}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def arrange_actions
53
+ # Arrange the requested compositors and generators:
54
+ @compositors = []
55
+ @generators = []
56
+ @options.clone.each do |option, modes|
57
+
58
+ # Flip option and first mode if option is source or target
59
+ if option == 'source' || option == 'target'
60
+ compositor, flag = modes.shift
61
+ modes[option] = true
62
+ option = compositor
63
+ end
64
+
65
+ if action = ActiveFacts::Compositions.compositors[option]
66
+ options.delete(option)
67
+ check_options(action, modes)
68
+ @compositors << [action, modes, option]
69
+ elsif action = ActiveFacts::Generators.generators[option]
70
+ options.delete(option)
71
+ check_options(action, modes)
72
+ @generators << [action, modes, option]
73
+ else
74
+ $stderr.puts "Action --#{option} is not recognised"
75
+ exit 1
76
+ end
77
+ if modes['help']
78
+ puts "Help for #{option} is not yet available"
79
+ end
80
+ end
81
+ end
82
+
83
+ def process_files argv
84
+ # Process each input file:
85
+ argv.each do |arg|
86
+ filename, input_options = *arg.split(/=/, 2)
87
+
88
+ # Load the correct file type input method
89
+ pathname, basename, extension = * /(?:(.*)[\/\\])?(.*)\.([^.]*)$/.match(filename).captures
90
+ if EXTENSIONS.detect { |e| extension == e }
91
+ extension = "cql"
92
+ end
93
+ input_handler = "activefacts/input/#{extension}"
94
+ require input_handler
95
+
96
+ input_class = extension.upcase
97
+ input_klass = ActiveFacts::Input.const_get(input_class.to_sym)
98
+ raise "Expected #{input_handler} to define #{input_class}" unless input_klass
99
+
100
+ # Read the input file:
101
+ vocabulary =
102
+ if input_klass
103
+ begin
104
+ input_klass.readfile(filename, *input_options)
105
+ rescue => e
106
+ $stderr.puts "#{e.message}"
107
+ if trace :exception
108
+ $stderr.puts "\t#{e.backtrace*"\n\t"}"
109
+ else
110
+ $stderr.puts "\t#{e.backtrace[0]}"
111
+ end
112
+ exit 1
113
+ end
114
+ end
115
+ exit 0 unless vocabulary
116
+ vocabulary.finalise unless vocabulary == true
117
+
118
+ # Run one compositor
119
+ if @compositors.size != 1
120
+ raise "Expected one compositor"
121
+ end
122
+
123
+ compositor_klass, modes, option = @compositors[0]
124
+ compositor = compositor_klass.new(vocabulary.constellation, basename, modes)
125
+ compositor.generate
126
+
127
+ # Run each generator
128
+ @generators.each do |generator_klass, modes|
129
+ output = generator_klass.new(compositor.composition, modes).generate
130
+ puts output if output
131
+ end
132
+ end
133
+ end
134
+
135
+ def action_name action
136
+ action.name.sub(/ActiveFacts::[^:]+::/,'').gsub(/::/,'/').downcase
137
+ end
138
+
139
+ def display_options action, stream = $stdout
140
+ options = action.options
141
+ name = action.name.sub(/ActiveFacts::[^:]+::/,'').gsub(/::/,'/').downcase
142
+ if options.empty?
143
+ stream.puts "There are no options for --#{action_name action}"
144
+ else
145
+ stream.puts "Options for --#{name} (say e.g. --#{action_name action}=option1=value,option2)"
146
+ options.keys.sort.each do |key|
147
+ type, description = *options[key]
148
+ tag =
149
+ key.to_s +
150
+ case type
151
+ when NilClass,'Boolean', TrueClass
152
+ ''
153
+ when Numeric
154
+ ' num'
155
+ when Pathname
156
+ ' file'
157
+ else
158
+ ' str'
159
+ end
160
+
161
+ stream.puts "\t#{tag}#{' '*(24-tag.size)}#{description}"
162
+ end
163
+ end
164
+ end
165
+
166
+ # Ensure that the options provided are supported by the action
167
+ def check_options action, modes
168
+ if modes['help']
169
+ display_options(action)
170
+ exit
171
+ end
172
+ options = action.options
173
+ unsupported = modes.keys.select{|k| !options.has_key?(k.to_sym)}
174
+ return if unsupported.empty?
175
+ $stderr.puts "Action --#{action_name action} does not support #{unsupported.size >1 ? 'these options' : 'this option'}: #{unsupported*', '}"
176
+ display_options(action, $stderr)
177
+ exit 1
178
+ end
179
+ end
180
+
181
+ sc = SchemaCompositor.new(ARGV)
182
+ sc.enumerate_available('activefacts/compositions')
183
+ sc.enumerate_available('activefacts/generator')
184
+ if sc.options['help']
185
+ puts "Available compositors:\n\t#{ActiveFacts::Compositions.compositors.keys.sort*"\n\t"}\n\n"
186
+ puts "Available generators:\n\t#{ActiveFacts::Generators.generators.keys.sort*"\n\t"}\n\n"
187
+ puts "To get help for a particular action, follow it by =help, e.g. --relational=help"
188
+ exit
189
+ end
190
+
191
+ sc.arrange_actions
192
+ sc.process_files ARGV
@@ -14,6 +14,8 @@ require 'activefacts/compositions'
14
14
  require 'activefacts/generator'
15
15
 
16
16
  class SchemaCompositor
17
+ EXTENSIONS = ['fiml', 'fidl', 'fiql', 'cql']
18
+
17
19
  attr_reader :options
18
20
  attr_reader :compositors
19
21
  attr_reader :generators
@@ -22,7 +24,7 @@ class SchemaCompositor
22
24
  def initialize argv
23
25
  @options = {}
24
26
  while argv[0] =~ /^-/
25
- option, value = argv.shift.split(/=/, 2)
27
+ option, value = argv.shift.split(/[=:]/, 2)
26
28
  csv = (value =~ /,/ ? value.split(',') : Array(value))
27
29
  modes = csv.inject({}){|h,s| k, v = s.split(/=/, 2); h[k] = v || true; h }
28
30
  @options[option.sub(/^-*/,'')] = modes
@@ -52,14 +54,22 @@ class SchemaCompositor
52
54
  @compositors = []
53
55
  @generators = []
54
56
  @options.clone.each do |option, modes|
57
+
58
+ # Flip option and first mode if option is source or target
59
+ if option == 'source' || option == 'target'
60
+ compositor, flag = modes.shift
61
+ modes[option] = true
62
+ option = compositor
63
+ end
64
+
55
65
  if action = ActiveFacts::Compositions.compositors[option]
56
66
  options.delete(option)
57
67
  check_options(action, modes)
58
- @compositors << [action, modes]
68
+ @compositors << [action, modes, option]
59
69
  elsif action = ActiveFacts::Generators.generators[option]
60
70
  options.delete(option)
61
71
  check_options(action, modes)
62
- @generators << [action, modes]
72
+ @generators << [action, modes, option]
63
73
  else
64
74
  $stderr.puts "Action --#{option} is not recognised"
65
75
  exit 1
@@ -77,6 +87,9 @@ class SchemaCompositor
77
87
 
78
88
  # Load the correct file type input method
79
89
  pathname, basename, extension = * /(?:(.*)[\/\\])?(.*)\.([^.]*)$/.match(filename).captures
90
+ if EXTENSIONS.detect { |e| extension == e }
91
+ extension = "cql"
92
+ end
80
93
  input_handler = "activefacts/input/#{extension}"
81
94
  require input_handler
82
95
 
@@ -102,16 +115,18 @@ class SchemaCompositor
102
115
  exit 0 unless vocabulary
103
116
  vocabulary.finalise unless vocabulary == true
104
117
 
105
- # Run one or more compositors
106
- compositions = @compositors.map do |compositor_klass, modes|
107
- compositor = compositor_klass.new(vocabulary.constellation, basename, modes)
108
- compositor.generate
109
- compositor.composition
118
+ # Run one compositor
119
+ if @compositors.size != 1
120
+ raise "Expected one compositor, use --help for a list"
110
121
  end
111
122
 
123
+ compositor_klass, modes, option = @compositors[0]
124
+ compositor = compositor_klass.new(vocabulary.constellation, basename, modes)
125
+ compositor.generate
126
+
112
127
  # Run each generator
113
128
  @generators.each do |generator_klass, modes|
114
- output = generator_klass.new(compositions, modes).generate
129
+ output = generator_klass.new(compositor.composition, modes).generate
115
130
  puts output if output
116
131
  end
117
132
  end
@@ -127,7 +142,7 @@ class SchemaCompositor
127
142
  if options.empty?
128
143
  stream.puts "There are no options for --#{action_name action}"
129
144
  else
130
- stream.puts "Options for --#{name} (say e.g. --#{action_name action}=option1=value,option2)"
145
+ stream.puts "Options for --#{name} (say e.g. --#{action_name action}:option1=value,option2)"
131
146
  options.keys.sort.each do |key|
132
147
  type, description = *options[key]
133
148
  tag =
@@ -169,7 +184,7 @@ sc.enumerate_available('activefacts/generator')
169
184
  if sc.options['help']
170
185
  puts "Available compositors:\n\t#{ActiveFacts::Compositions.compositors.keys.sort*"\n\t"}\n\n"
171
186
  puts "Available generators:\n\t#{ActiveFacts::Generators.generators.keys.sort*"\n\t"}\n\n"
172
- puts "To get help for a particular action, follow it by =help, e.g. --relational=help"
187
+ puts "To get help for a particular action, follow it by =help, e.g. --relational:help"
173
188
  exit
174
189
  end
175
190
 
@@ -10,6 +10,10 @@ require "activefacts/compositions"
10
10
  module ActiveFacts
11
11
  module Compositions
12
12
  class Binary < Compositor
13
+ def initialize constellation, name, options = {}, compositor_name = 'Binary'
14
+ super constellation, name, options, compositor_name
15
+ end
16
+
13
17
  def self.options
14
18
  {}
15
19
  end
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # ActiveFacts Compositions, Fundamental Compositor
3
- #
3
+ #
4
4
  # All Compositors derive from this one, which can calculate the basic binary bi-directional mapping
5
5
  #
6
6
  # The term "reference" used here means either an Absorption
@@ -19,13 +19,21 @@ module ActiveFacts
19
19
  attr_reader :options, :name, :composition
20
20
 
21
21
  def self.options
22
- {}
22
+ {
23
+ # source: ['Boolean', "Generate composition for source schema"],
24
+ # target: ['Boolean', "Generate composition for target schema"],
25
+ # transform: ['Boolean', "Generate composition for transform schema"]
26
+ }
23
27
  end
24
28
 
25
- def initialize constellation, name, options = {}
29
+ def initialize constellation, name, options = {}, compositor_name
26
30
  @constellation = constellation
27
31
  @name = name
32
+ @compositor_name = compositor_name
28
33
  @options = options
34
+ # @option_source = options.delete('source')
35
+ # @option_target = options.delete('target')
36
+ # @option_transform = options.delete('transform')
29
37
  end
30
38
 
31
39
  # Generate all Mappings into @binary_mappings for a binary composition of all ObjectTypes in this constellation
@@ -37,7 +45,7 @@ module ActiveFacts
37
45
  @composition.retract
38
46
  end
39
47
 
40
- @composition = @constellation.Composition(:new, :name => @name)
48
+ @composition = @constellation.Composition(:new, :name => @name, :compositor_name => @compositor_name)
41
49
  preload_preferred_identifiers
42
50
  populate_references
43
51
  end
@@ -92,6 +100,35 @@ module ActiveFacts
92
100
  trace :binarize, "Populating #{a.inspect}"
93
101
  end
94
102
 
103
+ def find_topic import_role
104
+ if import = @constellation.Import.values.select{ |import| import.import_role == import_role }.first
105
+ import.precursor_topic
106
+ else
107
+ nil
108
+ end
109
+ end
110
+
111
+ def build_binary_mappings schema_topic
112
+ trace :binarize, "Build_binary_mappings for #{schema_topic.topic_name}"
113
+ if @schema_topics.key?(schema_topic)
114
+ trace :binarize, "already built, skip"
115
+ return
116
+ end
117
+ @schema_topics[schema_topic] = true
118
+
119
+ # add binary mapping for all object types in this schema
120
+ schema_topic.all_concept.each do |concept|
121
+ if concept.object_type
122
+ @binary_mappings[concept.object_type]
123
+ end
124
+ end
125
+
126
+ # recurse through precursor schemas
127
+ schema_topic.all_import.each do |import|
128
+ build_binary_mappings(import.precursor_topic)
129
+ end
130
+ end
131
+
95
132
  def populate_references
96
133
  # A table of Mappings by object type, with a default Mapping for each:
97
134
  @binary_mappings = Hash.new do |h, object_type|
@@ -103,11 +140,46 @@ module ActiveFacts
103
140
  end
104
141
  @component_by_fact = {}
105
142
 
106
- @constellation.ObjectType.each do |key, object_type|
143
+ @schema_topics = {}
144
+ =begin
145
+ if @option_source || @option_target
146
+ import_role = @option_source ? 'source' : 'target'
147
+ trace :binarize, "Build binary bindings for #{import_role} schema" do
148
+ if schema_topic = find_topic(import_role)
149
+ build_binary_mappings(schema_topic)
150
+ else
151
+ raise "Could not find #{import_role} schema"
152
+ end
153
+ end
154
+ elsif @option_transform
155
+ trace :binarize, "Build binary bindings for transform schema" do
156
+ transform_topic = @constellation.Topic.values.select do |topic|
157
+ nr_importers = @constellation.Import.values.select{|i| i.precursor_topic == topic}.size
158
+ nr_importers == 0 # This topic is not imported by anything, it's the one
159
+ end.first
160
+
161
+ # add binary mapping for all object types in the transform schema
162
+ transform_topic.all_concept.each do |concept|
163
+ if concept.object_type
164
+ @binary_mappings[concept.object_type]
165
+ end
166
+ end
167
+ end
168
+ else
169
+ =end
170
+ trace :binarize, "Build binary bindings for full schema" do
171
+ @constellation.ObjectType.each do |key, object_type|
172
+ @binary_mappings[object_type] # Ensure we create the top Mapping even if it has no references
173
+ end
174
+ end
175
+ # end
176
+
177
+ @binary_mappings.each do |object_type, mapping|
107
178
  trace :binarize, "Populating possible absorptions for #{object_type.name}" do
108
- @binary_mappings[object_type] # Ensure we create the top Mapping even if it has no references
109
179
 
110
180
  object_type.all_role.each do |role|
181
+ # Exclude fact types not in @schema_topics
182
+ next if @schema_topics.size > 0 && !@schema_topics.key?(role.fact_type.concept.topic)
111
183
  # Exclude base roles in objectified fact types (unless unary); just use link fact types
112
184
  next if role.fact_type.entity_type && role.fact_type.all_role.size != 1
113
185
  next if role.variable # REVISIT: Continue to ignore roles in derived fact types?
@@ -166,7 +238,7 @@ module ActiveFacts
166
238
  detect do |c|
167
239
  (rr = c.role_sequence.all_role_ref.single) and
168
240
  rr.role == role
169
- end
241
+ end
170
242
 
171
243
  if from_1 || fact_type.entity_type
172
244
  # This is a role in an objectified fact type