activefacts-compositions 1.9.1 → 1.9.4

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: c0c06e12306cedaf28c2c97bb4402eefcca710b1
4
- data.tar.gz: 3e04e43481658439e3ede1b0617e7894b3396f6b
3
+ metadata.gz: bad26d18580fa095acc7cbb4a2722694f0020c30
4
+ data.tar.gz: 45c9fc0cf6516095c7794bd7fb291ce33c80b074
5
5
  SHA512:
6
- metadata.gz: bb18efed6ff85d453708918973589714f30d39e7e65132337d64919146e21be7a28a6e79b9769f4ff94c45dfed9fc7b9abe22a0999bdd76044666ea7151ee348
7
- data.tar.gz: f32477f6a63fec1b57c7d387f4ef9a61371e1ff0944105e47ceff312cddd8099fc9563e2cd50b66c762279c0885e7cba19a09441438cc9e6abdac6325a2e0155
6
+ metadata.gz: 76fafc569a0516de38ddb19e604c243ec7b87318823fffac3bd3b8b84ec491e131d4429dae139944322a2882574e2c6d9693fad176fa9962dcf2f5495851f13b
7
+ data.tar.gz: ca70f4978bffffea9737812c3199503a81242809f0f0a27674be0c769b96d82aef6937a7cbd0213ac50ac971684ee2ec476e9c6c517729de1d92a109b45bd782
data/Gemfile CHANGED
@@ -2,10 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- if ENV['PWD'] =~ %r{\A#{ENV['HOME']}/work}
5
+ if ENV['PWD'] =~ %r{\A#{ENV['HOME']}/work}i
6
6
  $stderr.puts "Using work area gems for #{File.basename(File.dirname(__FILE__))} from activefacts-compositions"
7
- gem 'activefacts-api', path: '/Users/cjh/work/activefacts/api'
8
- gem 'activefacts-metamodel', path: '/Users/cjh/work/activefacts/metamodel'
9
- gem 'activefacts-cql', path: '/Users/cjh/work/activefacts/cql'
10
- # gem 'activefacts-metamodel', git: 'git://github.com/cjheath/activefacts-metamodel.git'
7
+ gem 'activefacts-api', path: '../api'
8
+ gem 'activefacts-metamodel', path: '../metamodel'
9
+ gem 'activefacts-cql', path: '../cql'
11
10
  end
data/README.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # ActiveFacts::Compositions
2
2
 
3
- Create and represent composite schemas, schema transforms and data transforms over a fact-based model.
3
+ Fact-based schemas are always in highly normalised or *elementary* form.
4
+ Most other schemas are composite (object-oriented, relational, warehousing, analytical, messaging, APIs, etc).
5
+ This gem provides the framework for *Compositions*, which are representations of the two-way mapping between an elementary schema and a composite schema.
6
+ As such, it supports any-to-any mappings between different composite forms.
7
+
8
+ It also provides automated generators for some types of composite schemas, especially relational and Data Vault schemas.
4
9
 
5
10
  This gem works with the Fact Modeling tools as part of ActiveFacts.
6
11
 
7
12
  ## Installation
8
13
 
9
- Install via the activefacts gem bundle, or add this line to your application's Gemfile:
14
+ Install as part of activefacts, just "gem install" directly, or add this line to your application's Gemfile:
10
15
 
11
16
  ```ruby
12
17
  gem 'activefacts-compositions'
@@ -16,16 +21,17 @@ And then execute:
16
21
 
17
22
  $ bundle
18
23
 
19
- Or install it yourself as:
20
-
21
- $ gem install activefacts-compositions
22
-
23
24
  ## Usage
24
25
 
25
- This gem adds schema manipulation tools (mappers, composers, transformations, generators) to the generator framework for activefacts. Refer to the afgen command-line tool for help:
26
+ This gem adds schema manipulation tools (mappers, composers, transformations) to the generator framework for *activefacts*.
27
+ Refer to the afgen command-line tool for help:
26
28
 
27
29
  $ afgen --help
28
30
 
31
+ A stand-alone relational generator program is provided, mostly for exploratory purposes; use tracing to see what it is doing, e.g.:
32
+
33
+ $ TRACE=relational bin/schema_compositor --surrogate spec/relational/CompanyDirectorEmployee.cql
34
+
29
35
  ## Development
30
36
 
31
37
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests.
@@ -36,7 +42,6 @@ To install this gem onto your local machine, run `bundle exec rake install`.
36
42
 
37
43
  Bug reports and pull requests are welcome on GitHub at https://github.com/cjheath/activefacts-compositions.
38
44
 
39
-
40
45
  ## License
41
46
 
42
47
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -4,3 +4,20 @@ require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  task :default => :spec
7
+
8
+ desc "Bump gem version patch number"
9
+ task :bump do
10
+ path = File.expand_path('../lib/activefacts/compositions/version.rb', __FILE__)
11
+ lines = File.open(path) do |fp| fp.readlines; end
12
+ File.open(path, "w") do |fp|
13
+ fp.write(
14
+ lines.map do |line|
15
+ line.gsub(/(VERSION *= *"[0-9.]*\.)([0-9]+)"\n/) do
16
+ version = "#{$1}#{$2.to_i+1}"
17
+ puts "Version bumped to #{version}\""
18
+ version+"\"\n"
19
+ end
20
+ end*''
21
+ )
22
+ end
23
+ end
@@ -23,7 +23,10 @@ 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_runtime_dependency("activefacts-metamodel", ">= 1.9", "~> 1.9.0")
27
- spec.add_development_dependency "activefacts", "~> 1.8", "~> 1.8.0"
28
- spec.add_development_dependency "activefacts-cql", "~> 1.8", "~> 1.8.0"
26
+ spec.add_runtime_dependency("activefacts-api", "~> 1", ">= 1.9.2")
27
+ spec.add_runtime_dependency("activefacts-metamodel", "~> 1", ">= 1.9.1")
28
+ spec.add_runtime_dependency "tracing", "~> 2", ">= 2.0.5"
29
+
30
+ spec.add_development_dependency "activefacts", "~> 1", ">= 1.8"
31
+ spec.add_development_dependency "activefacts-cql", "~> 1", ">= 1.8"
29
32
  end
@@ -11,12 +11,19 @@ $:.unshift File.dirname(File.expand_path(__FILE__))+"/../lib"
11
11
  require 'activefacts/metamodel'
12
12
  require 'activefacts/compositions/binary'
13
13
  require 'activefacts/compositions/relational'
14
+ require 'activefacts/compositions/validator'
14
15
 
15
- arg = ARGV.shift
16
+ options = {}
17
+ while arg = ARGV.shift and arg =~ /^-/
18
+ option, value = arg.split(/=/, 2)
19
+ options[option.sub(/^-*/,'')] = value =~ /,/ ? value.split(',') : (value || true)
20
+ end
21
+
22
+ validate = options.delete('validate')
16
23
 
17
24
  # Load the file type input method
18
25
  if arg
19
- arg, *options = *arg.split(/=/)
26
+ arg, input_options = *arg.split(/=/, 2)
20
27
  extension = arg.sub(/\A.*\./,'').downcase
21
28
  input_handler = "activefacts/input/#{extension}"
22
29
  require input_handler
@@ -29,7 +36,7 @@ end
29
36
  # Read the input file:
30
37
  begin
31
38
  if input_klass
32
- vocabulary = input_klass.readfile(arg, *options)
39
+ vocabulary = input_klass.readfile(arg, *input_options)
33
40
  else
34
41
  vocabulary = true
35
42
  end
@@ -38,8 +45,12 @@ begin
38
45
 
39
46
  vocabulary.finalise unless vocabulary == true
40
47
 
41
- compositor = ActiveFacts::Compositions::Relational.new(vocabulary.constellation, "test")
48
+ compositor = ActiveFacts::Compositions::Relational.new(vocabulary.constellation, "test", options)
42
49
  compositor.generate
50
+ trace.enable 'composition_validator'
51
+ compositor.validate do |component, problem|
52
+ trace :composition_validator, "!!PROBLEM!! #{component.inspect}: #{problem}"
53
+ end if validate
43
54
 
44
55
  rescue => e
45
56
  $stderr.puts "#{e.message}"
@@ -13,14 +13,14 @@ module ActiveFacts
13
13
  def generate
14
14
  super
15
15
 
16
- trace :binary_, "Constructing Binary Composition" do
16
+ trace :binary!, "Constructing Binary Composition" do
17
17
  @binary_mappings.keys.sort_by(&:name).each do |object_type|
18
18
  mapping = @binary_mappings[object_type]
19
19
  mapping.re_rank
20
20
  end
21
21
  end
22
22
 
23
- trace :binary_, "Full binary composition" do
23
+ trace :binary!, "Full binary composition" do
24
24
  @binary_mappings.keys.sort_by(&:name).each do |object_type|
25
25
  mapping = @binary_mappings[object_type]
26
26
  mapping.show_trace
@@ -16,6 +16,8 @@ require "activefacts/metamodel"
16
16
  module ActiveFacts
17
17
  module Compositions
18
18
  class Compositor
19
+ attr_reader :options, :name
20
+
19
21
  def initialize constellation, name, options = {}
20
22
  @constellation = constellation
21
23
  @name = name
@@ -32,10 +34,17 @@ module ActiveFacts
32
34
  end
33
35
 
34
36
  @composition = @constellation.Composition(:new, :name => @name)
37
+ preload_preferred_identifiers
35
38
  populate_references
36
39
  end
37
40
 
38
41
  private
42
+ # Preferred identifiers are cached, but the process produces trace output
43
+ # that appears in the "tutti" mode used in testing. This precludes that.
44
+ def preload_preferred_identifiers
45
+ @constellation.EntityType.map{|k, et| et.preferred_identifier }
46
+ end
47
+
39
48
  def populate_reference object_type, role
40
49
  parent = @binary_mappings[role.object_type]
41
50
 
@@ -44,12 +53,12 @@ module ActiveFacts
44
53
  counterpart = role.counterpart
45
54
  rt = role_type(counterpart)
46
55
  if rt == :many_many
47
- raise "Can't absorb many-to-many (until we absorb derived fact types, or don't require explicit objectification)"
56
+ raise "Fact type must be objectified: #{role.fact_type.default_reading}"
48
57
  end
49
58
 
50
59
  a = @constellation.Absorption(
51
60
  :new,
52
- name: counterpart.name,
61
+ name: role_name(counterpart),
53
62
  parent: parent,
54
63
  object_type: counterpart.object_type,
55
64
  parent_role: role,
@@ -59,9 +68,9 @@ module ActiveFacts
59
68
  if r = @component_by_fact[role.fact_type]
60
69
  # Second occurrence of this fact type, set the direction:
61
70
  if a.is_preferred_direction
62
- a.absorption = r
63
- else # Set this as the reverse absorption
64
71
  a.reverse_absorption = r
72
+ else # Set this as the reverse absorption
73
+ a.forward_absorption = r
65
74
  end
66
75
  else
67
76
  # First occurrence of this fact type
@@ -95,7 +104,8 @@ module ActiveFacts
95
104
  @binary_mappings[object_type] # Ensure we create the top Mapping even if it has no references
96
105
 
97
106
  object_type.all_role.each do |role|
98
- next if role.mirror_role_as_base_role # Exclude base roles, just use link fact types
107
+ # Exclude base roles in objectified fact types (unless unary); just use link fact types
108
+ next if role.fact_type.entity_type && role.fact_type.all_role.size != 1
99
109
  next if role.variable_as_projection # REVISIT: Continue to ignore roles in derived fact types?
100
110
  populate_reference object_type, role
101
111
  end
@@ -112,6 +122,13 @@ module ActiveFacts
112
122
  end
113
123
  end
114
124
 
125
+ def role_name role
126
+ # if role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) && role == role.fact_type.subtype_role
127
+ # return "Is "+role.object_type.name
128
+ # end
129
+ String::Words.new(role.base_role.preferred_reference.role_name(nil)).capwords*' '
130
+ end
131
+
115
132
  def role_type role
116
133
  fact_type = role.fact_type
117
134
  if fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)