universe_compiler 0.2.11
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 +7 -0
- data/.gitignore +183 -0
- data/.rspec +2 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +318 -0
- data/Rakefile +6 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/lib/universe_compiler/entity/auto_named.rb +31 -0
- data/lib/universe_compiler/entity/conversion.rb +66 -0
- data/lib/universe_compiler/entity/field_binder.rb +36 -0
- data/lib/universe_compiler/entity/field_constraint_management.rb +95 -0
- data/lib/universe_compiler/entity/field_management.rb +57 -0
- data/lib/universe_compiler/entity/inheritance.rb +87 -0
- data/lib/universe_compiler/entity/inheritance_merge_policy.rb +19 -0
- data/lib/universe_compiler/entity/marshalling.rb +71 -0
- data/lib/universe_compiler/entity/overridden.rb +31 -0
- data/lib/universe_compiler/entity/persistence.rb +34 -0
- data/lib/universe_compiler/entity/reference.rb +77 -0
- data/lib/universe_compiler/entity/relations_management.rb +46 -0
- data/lib/universe_compiler/entity/type_management.rb +43 -0
- data/lib/universe_compiler/entity/validation.rb +92 -0
- data/lib/universe_compiler/entity.rb +64 -0
- data/lib/universe_compiler/error.rb +15 -0
- data/lib/universe_compiler/override.rb +24 -0
- data/lib/universe_compiler/package/bootstrap.rb +46 -0
- data/lib/universe_compiler/package.rb +17 -0
- data/lib/universe_compiler/persistence/basic_yaml_engine.rb +68 -0
- data/lib/universe_compiler/persistence/management.rb +30 -0
- data/lib/universe_compiler/universe/compile.rb +45 -0
- data/lib/universe_compiler/universe/duplication.rb +62 -0
- data/lib/universe_compiler/universe/entities.rb +52 -0
- data/lib/universe_compiler/universe/index.rb +40 -0
- data/lib/universe_compiler/universe/multiverse.rb +44 -0
- data/lib/universe_compiler/universe/persistence.rb +23 -0
- data/lib/universe_compiler/universe/query.rb +45 -0
- data/lib/universe_compiler/universe/validation.rb +30 -0
- data/lib/universe_compiler/universe.rb +38 -0
- data/lib/universe_compiler/utils/array_utils.rb +59 -0
- data/lib/universe_compiler/utils/basic_logger.rb +24 -0
- data/lib/universe_compiler/utils/deep_traverse.rb +61 -0
- data/lib/universe_compiler/utils/error_propagation.rb +20 -0
- data/lib/universe_compiler/utils/with_unique_name.rb +75 -0
- data/lib/universe_compiler/version.rb +3 -0
- data/lib/universe_compiler.rb +60 -0
- data/universe_compiler.gemspec +32 -0
- metadata +218 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
module Validation
|
5
|
+
|
6
|
+
def invalid_fields
|
7
|
+
invalid = {}
|
8
|
+
self.class.fields_constraints.each do |field_name, constraints|
|
9
|
+
constraints.each do |constraint_name, value|
|
10
|
+
case constraint_name
|
11
|
+
when :not_null
|
12
|
+
invalid_for_constraint invalid, field_name, constraint_name, value if fields[field_name].nil?
|
13
|
+
when :not_empty
|
14
|
+
invalid_for_constraint invalid, field_name, constraint_name, value if fields[field_name].nil? or fields[field_name].empty?
|
15
|
+
when :should_match
|
16
|
+
unless fields[field_name].nil?
|
17
|
+
invalid_for_constraint invalid, field_name, constraint_name, value unless fields[field_name].to_s.match value
|
18
|
+
end
|
19
|
+
when :class_name
|
20
|
+
unless fields[field_name].nil?
|
21
|
+
case value
|
22
|
+
when String
|
23
|
+
invalid_for_constraint invalid, field_name, constraint_name, value unless fields[field_name].class.name == value
|
24
|
+
else
|
25
|
+
invalid_for_constraint invalid, field_name, constraint_name, value unless fields[field_name].class == value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
when :is_array
|
29
|
+
if fields[field_name].nil? or not fields[field_name].is_a? Array
|
30
|
+
invalid_for_constraint invalid, field_name, constraint_name, value
|
31
|
+
end
|
32
|
+
when :is_hash
|
33
|
+
if fields[field_name].nil? or not fields[field_name].is_a? Hash
|
34
|
+
invalid_for_constraint invalid, field_name, constraint_name, value
|
35
|
+
end
|
36
|
+
when :has_one
|
37
|
+
unless fields[field_name].nil?
|
38
|
+
if fields[field_name].respond_to? :type
|
39
|
+
invalid_for_constraint invalid, field_name, constraint_name, value unless fields[field_name].type == value
|
40
|
+
else
|
41
|
+
invalid_for_constraint invalid, field_name, constraint_name, value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
when :has_many
|
45
|
+
if fields[field_name].nil? or not fields[field_name].is_a? Array
|
46
|
+
invalid_for_constraint invalid, field_name, constraint_name, value
|
47
|
+
else
|
48
|
+
fields[field_name].each do |related_object|
|
49
|
+
if related_object.respond_to? :type
|
50
|
+
invalid_for_constraint invalid, field_name, constraint_name, value unless related_object.type == value
|
51
|
+
else
|
52
|
+
invalid_for_constraint invalid, field_name, constraint_name, value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
else
|
57
|
+
UniverseCompiler.logger.warn "Cannot handle unknown constraint '#{constraint_name}'! Skipping..."
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
invalid
|
62
|
+
end
|
63
|
+
|
64
|
+
def valid_for_fields_constraints?(raise_error: false)
|
65
|
+
return true if invalid_fields.empty?
|
66
|
+
invalid_fields.each do |name,value|
|
67
|
+
UniverseCompiler.logger.error "Invalid field '#{name}' not compliant with '#{value}'"
|
68
|
+
end
|
69
|
+
fields_labels = invalid_fields.keys.join ', '
|
70
|
+
false_or_raise "Invalid entity '#{to_composite_key}' for fields #{fields_labels} !", raise_error: raise_error
|
71
|
+
end
|
72
|
+
|
73
|
+
def valid?(raise_error: false)
|
74
|
+
return false unless valid_for_fields_constraints? raise_error: raise_error
|
75
|
+
return false unless valid_for_inheritance? raise_error: raise_error
|
76
|
+
if respond_to? :specifically_valid?
|
77
|
+
return false unless specifically_valid? raise_error: raise_error
|
78
|
+
end
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def invalid_for_constraint(invalid_fields_definition, field_name, constraint_name, value)
|
85
|
+
invalid_fields_definition[field_name] ||= []
|
86
|
+
invalid_fields_definition[field_name] << { constraint_name => value}
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
class Base
|
5
|
+
|
6
|
+
include UniverseCompiler::Utils::ErrorPropagation
|
7
|
+
include UniverseCompiler::Utils::DeepTraverse
|
8
|
+
include UniverseCompiler::Entity::TypeManagement
|
9
|
+
include UniverseCompiler::Entity::FieldManagement
|
10
|
+
extend UniverseCompiler::Entity::AutoNamed
|
11
|
+
extend UniverseCompiler::Entity::FieldConstraintManagement
|
12
|
+
extend UniverseCompiler::Entity::RelationsManagement
|
13
|
+
extend UniverseCompiler::Entity::FieldBinder
|
14
|
+
include UniverseCompiler::Entity::Validation
|
15
|
+
include UniverseCompiler::Entity::Marshalling
|
16
|
+
include UniverseCompiler::Entity::Inheritance
|
17
|
+
include UniverseCompiler::Entity::Conversion
|
18
|
+
include UniverseCompiler::Entity::Overridden
|
19
|
+
include UniverseCompiler::Entity::Persistence
|
20
|
+
|
21
|
+
attr_reader :fields
|
22
|
+
attr_accessor :universe
|
23
|
+
|
24
|
+
field_accessor :name
|
25
|
+
|
26
|
+
def initialize(fields: {}, universe: nil)
|
27
|
+
@fields = fields
|
28
|
+
define_known_fields_accessors
|
29
|
+
self.universe = universe
|
30
|
+
self.fully_resolved = true
|
31
|
+
self.name = self.class.get_unique_name if self.class.auto_named_entity_type?
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](key)
|
35
|
+
fields[key]
|
36
|
+
end
|
37
|
+
|
38
|
+
def []=(key, value)
|
39
|
+
fields[key] = value
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
class Override < UniverseCompiler::Entity::Base
|
5
|
+
|
6
|
+
UNMERGEABLE_FIELDS = %i(name type overrides scenario extends)
|
7
|
+
|
8
|
+
entity_type :entity_override
|
9
|
+
|
10
|
+
field :overrides, :is_array
|
11
|
+
field_accessor :scenario
|
12
|
+
|
13
|
+
def apply_overrides
|
14
|
+
overrides.each do |override|
|
15
|
+
fields_to_be_merged = fields.reject { |key, _| UNMERGEABLE_FIELDS.include? key }
|
16
|
+
UniverseCompiler.logger.debug "Overriding '#{override.to_composite_key}' from overrides defined in '#{to_composite_key}'."
|
17
|
+
override.apply_override fields_to_be_merged, self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Package
|
3
|
+
|
4
|
+
module Bootstrap
|
5
|
+
|
6
|
+
DEFAULT_BOOTSTRAP_FILE = 'main.rb'.freeze
|
7
|
+
|
8
|
+
attr_reader :path
|
9
|
+
|
10
|
+
def path=(path)
|
11
|
+
@path = path if path_valid? path
|
12
|
+
end
|
13
|
+
|
14
|
+
def path_valid?(path = self.path)
|
15
|
+
return false unless path.is_a?(String) && File.readable?(path)
|
16
|
+
return true if File.file? path
|
17
|
+
File.exist? default_bootstrap_file(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def bootstrap_file
|
21
|
+
return nil unless path_valid?
|
22
|
+
if File.file? path
|
23
|
+
path
|
24
|
+
else
|
25
|
+
default_bootstrap_file
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def load
|
30
|
+
require bootstrap_file
|
31
|
+
rescue ScriptError => e
|
32
|
+
msg = "Invalid package: '#{bootstrap_file}': #{e.message}"
|
33
|
+
UniverseCompiler.logger.error msg
|
34
|
+
raise UniverseCompiler::Error.from(e, msg)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def default_bootstrap_file(path = self.path)
|
40
|
+
File.join(path, DEFAULT_BOOTSTRAP_FILE)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module UniverseCompiler
|
4
|
+
module Persistence
|
5
|
+
|
6
|
+
class BasicYamlEngine
|
7
|
+
|
8
|
+
attr_accessor :universe, :universe_uri
|
9
|
+
|
10
|
+
def initialize(universe, universe_uri)
|
11
|
+
@universe = universe
|
12
|
+
@universe_uri = universe_uri
|
13
|
+
end
|
14
|
+
|
15
|
+
def export_universe
|
16
|
+
FileUtils.mkdir_p universe_uri
|
17
|
+
universe.get_entities.each do |entity|
|
18
|
+
export_entity entity
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def import_universe(recursive: false, stop_on_error: true, &block)
|
23
|
+
sub_search_path = recursive ? '**' : '*'
|
24
|
+
glob_pattern = File.join universe_uri, sub_search_path, '*.yaml'
|
25
|
+
Dir.glob(glob_pattern) do |file|
|
26
|
+
UniverseCompiler.logger.debug "Importing '#{file}' entity file."
|
27
|
+
new_entity = nil
|
28
|
+
begin
|
29
|
+
new_entity = import_entity file
|
30
|
+
rescue => e
|
31
|
+
raise e if stop_on_error
|
32
|
+
end
|
33
|
+
if new_entity.nil?
|
34
|
+
UniverseCompiler.logger.debug "#{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
|
35
|
+
UniverseCompiler.logger.warn "Could not load entity from file '#{file}' because '#{e.message}'"
|
36
|
+
else
|
37
|
+
block.call new_entity if block_given?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def export_entity(entity)
|
43
|
+
dir = File.join(universe_uri, entity.type.to_s)
|
44
|
+
FileUtils.mkdir_p dir
|
45
|
+
entity_file = entity.source_uri || File.join(dir, "#{entity.name}.yaml")
|
46
|
+
entity.save entity_file
|
47
|
+
UniverseCompiler.logger.info 'Exporting entity "%s" to file "%s"' % [entity.name, entity_file]
|
48
|
+
UniverseCompiler.logger.debug "Saved entity:\n%s" % [entity.to_yaml]
|
49
|
+
end
|
50
|
+
|
51
|
+
def import_entity(uri)
|
52
|
+
entity = UniverseCompiler::Entity::Persistence.load uri
|
53
|
+
universe.add entity
|
54
|
+
# Fix references link to universe
|
55
|
+
entity.traverse_fields do |leaf|
|
56
|
+
if leaf.is_a? UniverseCompiler::Entity::Reference
|
57
|
+
leaf.universe = universe
|
58
|
+
end
|
59
|
+
end
|
60
|
+
entity
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Persistence
|
3
|
+
|
4
|
+
module Management
|
5
|
+
|
6
|
+
DEFAULT_ENGINE_NAME = 'BasicYamlEngine'.freeze
|
7
|
+
|
8
|
+
def persistence_engine
|
9
|
+
if @persistence_engine.nil?
|
10
|
+
self.persistence_engine_name = DEFAULT_ENGINE_NAME
|
11
|
+
end
|
12
|
+
@persistence_engine
|
13
|
+
end
|
14
|
+
|
15
|
+
def persistence_engines
|
16
|
+
UniverseCompiler::Persistence.constants.map(&:to_s).grep(/Engine$/).map do |engine_name|
|
17
|
+
self.persistence_engine_name = engine_name
|
18
|
+
persistence_engine
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def persistence_engine_name=(engine_name)
|
23
|
+
@persistence_engine = UniverseCompiler::Persistence.const_get engine_name
|
24
|
+
@persistence_engine_name = engine_name
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Universe
|
3
|
+
|
4
|
+
module Compile
|
5
|
+
|
6
|
+
include UniverseCompiler::Utils::DeepTraverse
|
7
|
+
|
8
|
+
def compile(scenario: nil)
|
9
|
+
valid? raise_error: true
|
10
|
+
UniverseCompiler.logger.info "Starting compilation of universe '#{name}'"
|
11
|
+
# Pass 1 - Universe duplication
|
12
|
+
target_universe = dup
|
13
|
+
|
14
|
+
# Pass 2 - Applying entities inheritance
|
15
|
+
target_universe.apply_entities_inheritance
|
16
|
+
|
17
|
+
# Apply scenario
|
18
|
+
target_universe.apply_entities_overrides scenario unless scenario.nil?
|
19
|
+
|
20
|
+
UniverseCompiler.logger.info "Completed compilation of universe '#{name}' into '#{target_universe.name}'"
|
21
|
+
target_universe
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def apply_entities_overrides(scenario)
|
27
|
+
UniverseCompiler.logger.debug "Starting override process for universe '#{name}'."
|
28
|
+
candidates = by_type.fetch(UniverseCompiler::Entity::Override.entity_type, []).select { |overrider| overrider.scenario == scenario }
|
29
|
+
UniverseCompiler.logger.warn "No override found for scenario '#{scenario}' in universe '#{name}'." if candidates.empty?
|
30
|
+
candidates.each(&:apply_overrides)
|
31
|
+
UniverseCompiler.logger.debug "Completed override process for universe '#{name}'."
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def apply_entities_inheritance
|
36
|
+
UniverseCompiler.logger.debug "Starting inheritance process for universe '#{name}'."
|
37
|
+
entities.each(&:apply_inheritance)
|
38
|
+
UniverseCompiler.logger.debug "Completed inheritance process for universe '#{name}'."
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Universe
|
3
|
+
|
4
|
+
module Duplication
|
5
|
+
|
6
|
+
def dup
|
7
|
+
UniverseCompiler.logger.debug "Starting '#{name}' universe duplication"
|
8
|
+
duplicated_universe = build_duplicated_universe_with_references
|
9
|
+
duplicated_universe.resolve_entities_reference
|
10
|
+
raise UniverseCompiler::Error, "Compilation error: Generated universe '#{duplicated_universe.name}' is invalid!" unless duplicated_universe.valid?
|
11
|
+
UniverseCompiler.logger.debug "Completed '#{name}' universe duplication"
|
12
|
+
duplicated_universe
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def resolve_entities_reference
|
19
|
+
entities.map!(&:resolve_fields_references!)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def duplicate_entities(target_universe = nil)
|
26
|
+
entities.map do |entity|
|
27
|
+
debug_msg = "Duplicating '#{entity.to_composite_key}' from universe '#{self.name}'"
|
28
|
+
debug_msg += " to universe '#{target_universe.name}'" unless target_universe.nil?
|
29
|
+
UniverseCompiler.logger.debug debug_msg
|
30
|
+
fields = Marshal::load(Marshal.dump entity).fields
|
31
|
+
entity_copy = entity.class.new fields: fields
|
32
|
+
entity_copy.universe = target_universe
|
33
|
+
deep_map entity_copy.fields do |leaf|
|
34
|
+
case leaf
|
35
|
+
when UniverseCompiler::Entity::Base
|
36
|
+
# All entities are supposed to be now references instead after the Marshall dump/load process
|
37
|
+
raise UniverseCompiler::Error, 'Should never happen!!'
|
38
|
+
when UniverseCompiler::Entity::Reference
|
39
|
+
leaf.universe = target_universe
|
40
|
+
else
|
41
|
+
raise UniverseCompiler::Error, 'Should never happen too!!' if leaf.respond_to? :universe
|
42
|
+
leaf
|
43
|
+
end
|
44
|
+
end
|
45
|
+
target_universe.add entity_copy unless target_universe.nil?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_duplicated_universe_with_references
|
50
|
+
target_name = '%s - COMPILED #%s' % [name, '%s']
|
51
|
+
duplicated_universe = self.class.new target_name
|
52
|
+
duplicated_universe.name = duplicated_universe.name % [duplicated_universe.object_id]
|
53
|
+
UniverseCompiler.logger.debug "Generating new universe '#{duplicated_universe.name}'."
|
54
|
+
duplicate_entities duplicated_universe
|
55
|
+
duplicated_universe
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Universe
|
3
|
+
|
4
|
+
module Entities
|
5
|
+
|
6
|
+
def empty?
|
7
|
+
entities.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(entity)
|
11
|
+
raise UniverseCompiler::Error, 'An entity cannot be null !' if entity.nil?
|
12
|
+
raise UniverseCompiler::Error, 'An entity must have a name !' unless entity.respond_to? :name
|
13
|
+
raise UniverseCompiler::Error, 'An entity must have a name !' if entity.name.nil? or entity.name.empty?
|
14
|
+
unless UniverseCompiler::Entity::TypeManagement.valid_for_type? entity
|
15
|
+
entity.extend UniverseCompiler::Entity::TypeManagement
|
16
|
+
end
|
17
|
+
raise UniverseCompiler::Error, "An entity named '#{entity.name}' already exists with the type '#{entity.type}' !" if by_uniq_key.keys.include? [entity.type, entity.name]
|
18
|
+
raise UniverseCompiler::Error, 'Invalid type specified' if entity.type.nil?
|
19
|
+
raise UniverseCompiler::Error, 'Type cannot contain spaces' if entity.type =~ /\s/
|
20
|
+
# Normally here the entity is valid
|
21
|
+
# We add it to the universe and index it
|
22
|
+
entities << entity
|
23
|
+
if entity.respond_to? :'universe='
|
24
|
+
entity.universe = self
|
25
|
+
end
|
26
|
+
index entity
|
27
|
+
entity
|
28
|
+
end
|
29
|
+
|
30
|
+
def <<(entity)
|
31
|
+
add entity
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete(entity)
|
36
|
+
entities.delete entity
|
37
|
+
reindex_all entities
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear
|
41
|
+
entities.clear
|
42
|
+
clear_indices
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :entities
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Universe
|
3
|
+
|
4
|
+
module Index
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
attr_reader :by_name, :by_type, :by_uniq_key
|
9
|
+
|
10
|
+
def setup_indices
|
11
|
+
@by_name = {}
|
12
|
+
@by_type = {}
|
13
|
+
@by_uniq_key = {}
|
14
|
+
end
|
15
|
+
alias_method :clear_indices, :setup_indices
|
16
|
+
|
17
|
+
|
18
|
+
def index(entity)
|
19
|
+
# Non unique indices
|
20
|
+
by_name[entity.name] ||= []
|
21
|
+
by_type[entity.type] ||= []
|
22
|
+
by_name[entity.name] << entity
|
23
|
+
by_type[entity.type] << entity
|
24
|
+
# Unique index
|
25
|
+
ref = entity.respond_to?(:to_composite_key) ? entity.to_composite_key : [entity.type, entity.name]
|
26
|
+
by_uniq_key[ref] = entity
|
27
|
+
end
|
28
|
+
|
29
|
+
def reindex_all(entities)
|
30
|
+
clear_indices
|
31
|
+
entities.each do |entity|
|
32
|
+
index entity
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Universe
|
3
|
+
|
4
|
+
module Multiverse
|
5
|
+
|
6
|
+
DEFAULT_UNIVERSE_NAME = 'Unnamed Universe'.freeze
|
7
|
+
|
8
|
+
def universes
|
9
|
+
@universes ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def register(universe)
|
13
|
+
raise UniverseCompiler::Error, "Universe '#{universe.name}' already exists in this continuum !" if universes.keys.include? universe.name
|
14
|
+
universes[universe.name] = universe
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_unique_name(seed = DEFAULT_UNIVERSE_NAME)
|
18
|
+
max_index = 1
|
19
|
+
universes.keys.each do |universe_name|
|
20
|
+
universe_name.match /^#{seed}(?:(?: - #)(?<index>\d+))?$/ do |md|
|
21
|
+
index = md['index'] || '1'
|
22
|
+
index = index.to_i
|
23
|
+
max_index = index > max_index ? index : max_index
|
24
|
+
end
|
25
|
+
end
|
26
|
+
if max_index == 1
|
27
|
+
seed
|
28
|
+
else
|
29
|
+
UniverseCompiler.logger.debug "Universe #{seed} reached its #{max_index} iteration."
|
30
|
+
format_name(seed, max_index)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def format_name(name, index)
|
37
|
+
'%s - #%d' % [name, index]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Universe
|
3
|
+
|
4
|
+
module Persistence
|
5
|
+
|
6
|
+
include UniverseCompiler::Persistence::Management
|
7
|
+
|
8
|
+
def export(uri)
|
9
|
+
engine = persistence_engine.new self, uri
|
10
|
+
engine.export_universe
|
11
|
+
end
|
12
|
+
|
13
|
+
def import(uri, force: false, stop_on_error: true, &block)
|
14
|
+
engine = persistence_engine.new self, uri
|
15
|
+
clear if force
|
16
|
+
engine.import_universe recursive: true, stop_on_error: stop_on_error, &block
|
17
|
+
resolve_entities_reference
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Universe
|
3
|
+
|
4
|
+
module Query
|
5
|
+
|
6
|
+
def basic_criteria
|
7
|
+
self.private_methods.grep /^by_/
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_entity(type, name)
|
11
|
+
res = get_entities criterion: :by_uniq_key, value: [type, name]
|
12
|
+
res.empty? ? nil : res.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_entities(criterion: nil, value: nil, &filter_block)
|
16
|
+
res = if criterion.nil? then
|
17
|
+
entities.clone
|
18
|
+
else
|
19
|
+
raise "Invalid criterion '#{criterion}' !" unless basic_criteria.include? criterion
|
20
|
+
if value.nil?
|
21
|
+
self.send(criterion).clone
|
22
|
+
else
|
23
|
+
self.send(criterion).clone[value]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
res = case res
|
27
|
+
when NilClass
|
28
|
+
[]
|
29
|
+
when Array
|
30
|
+
res
|
31
|
+
else
|
32
|
+
[res]
|
33
|
+
end
|
34
|
+
if block_given?
|
35
|
+
res.select! do |entity|
|
36
|
+
filter_block.call entity
|
37
|
+
end
|
38
|
+
end
|
39
|
+
res
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|