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 +4 -4
- data/.gitignore +1 -0
- data/activefacts-compositions.gemspec +6 -5
- data/bin/afcomp +192 -0
- data/bin/schema_compositor +26 -11
- data/lib/activefacts/compositions/binary.rb +4 -0
- data/lib/activefacts/compositions/compositor.rb +79 -7
- data/lib/activefacts/compositions/datavault.rb +308 -110
- data/lib/activefacts/compositions/docgraph.rb +798 -0
- data/lib/activefacts/compositions/relational.rb +8 -8
- data/lib/activefacts/compositions/staging.rb +3 -2
- data/lib/activefacts/compositions/version.rb +1 -1
- data/lib/activefacts/generator/doc/cwm.rb +11 -17
- data/lib/activefacts/generator/oo.rb +2 -3
- data/lib/activefacts/generator/rails/models.rb +2 -3
- data/lib/activefacts/generator/rails/schema.rb +2 -3
- data/lib/activefacts/generator/ruby.rb +1 -2
- data/lib/activefacts/generator/sql.rb +29 -15
- data/lib/activefacts/generator/summary.rb +22 -17
- data/lib/activefacts/generator/transgen.rb +144 -0
- data/lib/activefacts/generator/validate.rb +3 -3
- metadata +59 -21
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 7ca6ad3d9944b1f64ffde699ef36bd7b26764b0e
         | 
| 4 | 
            +
              data.tar.gz: 352761a4484292f70eeb90763d7518c892d9a562
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ee1a8f1fe902ab61728c16adadedb68fe4bc2b40339d478c9b1d59e27d73c14a57c4d760a6cd61787b3eb0fe86cd9dc24da0c03bcb3609893b62967e4a6bc1bb
         | 
| 7 | 
            +
              data.tar.gz: f979de0913251692a2be5c7f115ce9d81fb58147c04af65746fd1e4574723513bd99f877fa0409df316fa8aa94ecf28c37498f6477432ca862d4a912129df761
         | 
    
        data/.gitignore
    CHANGED
    
    
| @@ -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. | 
| 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. | 
| 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
         | 
    
        data/bin/schema_compositor
    CHANGED
    
    | @@ -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( | 
| 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  | 
| 106 | 
            -
                   | 
| 107 | 
            -
                     | 
| 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( | 
| 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} | 
| 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 | 
| 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 | 
            -
                    @ | 
| 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
         |