activefacts-compositions 1.9.10 → 1.9.12

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