universe_compiler 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|