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,66 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module UniverseCompiler
|
4
|
+
module Entity
|
5
|
+
|
6
|
+
module Conversion
|
7
|
+
|
8
|
+
def as_path
|
9
|
+
to_composite_key.map(&:to_s).join '/'
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_composite_key
|
13
|
+
[type, name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_uniq_id
|
17
|
+
Digest::SHA256.hexdigest to_composite_key.join ':'
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
msg = "#<#{self.class.name}:#{object_id} composite_key=#{to_composite_key.inspect}"
|
22
|
+
msg << ", @universe='#{universe.name}'" unless universe.nil?
|
23
|
+
msg << '>'
|
24
|
+
msg
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(another_reference)
|
28
|
+
return false unless another_reference.respond_to? :to_composite_key
|
29
|
+
to_composite_key == another_reference.to_composite_key
|
30
|
+
end
|
31
|
+
|
32
|
+
def eql?(another_reference)
|
33
|
+
self == another_reference and universe == another_reference.universe
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_hash
|
37
|
+
{
|
38
|
+
self.class.name => {
|
39
|
+
type: type,
|
40
|
+
fields: dereferenced_fields
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_reference
|
46
|
+
UniverseCompiler::Entity::Reference.new_instance self
|
47
|
+
end
|
48
|
+
|
49
|
+
def encode_with(coder)
|
50
|
+
dereferenced_fields.each do |key, value|
|
51
|
+
coder[key] = value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def init_with(coder)
|
56
|
+
initialize
|
57
|
+
self.fully_resolved = false
|
58
|
+
coder.map.each do |key, value|
|
59
|
+
self[key] = value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
module FieldBinder
|
5
|
+
|
6
|
+
def field_accessor(*field_names)
|
7
|
+
field_names.each do |field_name|
|
8
|
+
field_reader field_name
|
9
|
+
field_writer field_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def field_reader(*field_names)
|
14
|
+
field_names.each do |field_name|
|
15
|
+
self.class_eval do
|
16
|
+
define_method field_name do
|
17
|
+
self.fields[field_name]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def field_writer(*field_names)
|
24
|
+
field_names.each do |field_name|
|
25
|
+
self.class_eval do
|
26
|
+
define_method "#{field_name}=" do |val|
|
27
|
+
self.fields[field_name] = val
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
module FieldConstraintManagement
|
5
|
+
|
6
|
+
BOOLEAN_CONSTRAINTS = %i(not_null not_empty is_array is_hash).freeze
|
7
|
+
PARAMETRIZED_CONSTRAINTS = %i(should_match class_name).freeze
|
8
|
+
|
9
|
+
INCOMPATIBLE_CONSTRAINTS = {
|
10
|
+
has_one: %i(has_many is_array is_hash),
|
11
|
+
has_many: %i(has_one is_hash),
|
12
|
+
is_array: %i(has_one is_hash),
|
13
|
+
is_hash: %i(has_one has_many is_array)
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def fields_constraints
|
17
|
+
@fields_constraints ||= {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Dynamically created constraint methods
|
21
|
+
BOOLEAN_CONSTRAINTS.each do |constraint_name|
|
22
|
+
self.class_eval do
|
23
|
+
define_method constraint_name do |*field_names|
|
24
|
+
field_names.each do |field_name|
|
25
|
+
define_constraint field_name, constraint_name, true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
PARAMETRIZED_CONSTRAINTS.each do |constraint_name|
|
31
|
+
self.class_eval do
|
32
|
+
define_method constraint_name do |field_name, param|
|
33
|
+
define_constraint field_name, constraint_name, param
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def field(field_name, *options)
|
39
|
+
['', '='].each do |suffix|
|
40
|
+
method_name = '%s%s' % [field_name, suffix]
|
41
|
+
if instance_methods.include? method_name
|
42
|
+
raise UniverseCompiler::Error,
|
43
|
+
"'#{method_name}' method cannot be defined on '#{self}' as it is already defined !"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
define_constraint field_name if options.empty?
|
48
|
+
|
49
|
+
options.each do |option|
|
50
|
+
case option
|
51
|
+
when Symbol || String
|
52
|
+
if BOOLEAN_CONSTRAINTS.include? option.to_sym
|
53
|
+
send option, field_name
|
54
|
+
else
|
55
|
+
raise UniverseCompiler::Error, "Unknown field option '#{option}' !"
|
56
|
+
end
|
57
|
+
when Hash
|
58
|
+
option.each do |constraint_name, value|
|
59
|
+
if PARAMETRIZED_CONSTRAINTS.include? constraint_name.to_sym
|
60
|
+
send constraint_name, field_name, value
|
61
|
+
else
|
62
|
+
raise UniverseCompiler::Error, "Unknown field option '#{constraint_name}' !"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
else
|
66
|
+
raise UniverseCompiler::Error, "Invalid option for '#{field_name}': #{option.class.name} => #{option.to_s}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def define_constraint(field_name, constraint_name = nil, value = nil)
|
75
|
+
fields_constraints[field_name] ||= {}
|
76
|
+
check_constraints_incompatibilities(field_name, constraint_name)
|
77
|
+
unless constraint_name.nil? and value.nil?
|
78
|
+
fields_constraints[field_name][constraint_name] = value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def check_constraints_incompatibilities(field_name, constraint_name)
|
83
|
+
unless INCOMPATIBLE_CONSTRAINTS[constraint_name].nil?
|
84
|
+
INCOMPATIBLE_CONSTRAINTS[constraint_name].each do |incompatible_constraint|
|
85
|
+
if fields_constraints[field_name].keys.include? incompatible_constraint
|
86
|
+
raise UniverseCompiler::Error, "Cannot set constraint '#{constraint_name}' on field '#{field_name}', incompatible with existing ones !"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
module FieldManagement
|
5
|
+
|
6
|
+
attr_accessor :auto_create_fields
|
7
|
+
|
8
|
+
def method_missing(method_name, *args)
|
9
|
+
if auto_create_fields
|
10
|
+
candidate_field_name = method_name
|
11
|
+
method_name.to_s.match (/^(?<field_name>[^=]+)\s*=?$/) do |md|
|
12
|
+
candidate_field_name = md['field_name'].to_sym
|
13
|
+
end
|
14
|
+
define_field_accessor candidate_field_name
|
15
|
+
raise "Invalid method '#{method_name}' in #{self}" unless self.respond_to? method_name
|
16
|
+
self.send method_name, *args
|
17
|
+
else
|
18
|
+
super(method_name, *args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def define_known_fields_accessors
|
25
|
+
self.class.fields_constraints.each do |field_name, constraints|
|
26
|
+
define_field_accessor field_name
|
27
|
+
if fields[field_name].nil?
|
28
|
+
fields[field_name] = [] if constraints[:has_many] || constraints[:is_array]
|
29
|
+
fields[field_name] = {} if constraints[:is_hash]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def define_field_accessor(field_name)
|
35
|
+
metaclass = class << self; self ; end
|
36
|
+
UniverseCompiler.logger.debug 'Defining field accessor %s on class %s (%s)' % [field_name, metaclass, self.type]
|
37
|
+
if self.respond_to? field_name or self.respond_to? "#{field_name}="
|
38
|
+
UniverseCompiler.logger.warn "Cannot define '#{field_name}' or '#{field_name}=' which already exist(s) on class '#{metaclass}' !"
|
39
|
+
else
|
40
|
+
metaclass.instance_eval do
|
41
|
+
define_method field_name do
|
42
|
+
fields[field_name]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
metaclass.instance_eval do
|
46
|
+
define_method "#{field_name}=" do |value|
|
47
|
+
fields[field_name] = value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
module Inheritance
|
5
|
+
|
6
|
+
extend UniverseCompiler::Entity::FieldBinder
|
7
|
+
|
8
|
+
field_accessor :extends
|
9
|
+
attr_reader :compiled
|
10
|
+
|
11
|
+
def apply_inheritance
|
12
|
+
return unless compiled.nil?
|
13
|
+
unless extends.nil?
|
14
|
+
valid_for_inheritance? raise_error: true
|
15
|
+
@fields = fields_inheritance_result
|
16
|
+
end
|
17
|
+
ensure
|
18
|
+
@compiled = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid_for_inheritance?(raise_error: false)
|
22
|
+
is_valid = true
|
23
|
+
unless extends.nil?
|
24
|
+
is_valid = false
|
25
|
+
if extends.respond_to? :type and extends.respond_to? :name
|
26
|
+
if extends.respond_to? :fields or extends.is_a? UniverseCompiler::Entity::Reference
|
27
|
+
is_valid = self.type == extends.type
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
return true if is_valid
|
32
|
+
false_or_raise "Invalid entity '#{to_composite_key}' ! Inheritance is invalid !", raise_error: raise_error
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def fields_inheritance_result
|
38
|
+
stack = inheritance_stack(self).reverse
|
39
|
+
return stack.first if stack.size == 1
|
40
|
+
merge_engine = SuperStack::Manager.new
|
41
|
+
merge_engine.merge_policy = SuperStack::MergePolicies::InheritanceMergePolicy
|
42
|
+
stack.each { |entity| merge_engine << entity.fields.dup }
|
43
|
+
merge_engine[]
|
44
|
+
end
|
45
|
+
|
46
|
+
def inheritance_stack(entity, stack=[], visited=[])
|
47
|
+
if visited.include? entity
|
48
|
+
display_as_path = display_path(*visited.map(&:to_composite_key), entity.to_composite_key)
|
49
|
+
raise UniverseCompiler::Error, "Circular reference detected for '#{to_composite_key}' (#{display_as_path}) !"
|
50
|
+
end
|
51
|
+
visited << entity
|
52
|
+
stack << entity
|
53
|
+
return stack unless entity.extends
|
54
|
+
parent = entity.extends
|
55
|
+
if parent.nil?
|
56
|
+
display_as_path = display_path(*visited.map(&:to_composite_key), entity.extends.to_composite_key)
|
57
|
+
err_msg = "Invalid inheritance defined in '#{entity.to_composite_key}' extending '#{entity.to_composite_key}' (#{display_as_path}) !"
|
58
|
+
raise UniverseCompiler::Error, err_msg
|
59
|
+
end
|
60
|
+
stack = inheritance_stack parent, stack, visited
|
61
|
+
stack
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def inherited_fields_stack(entity, stack=[], visited=[])
|
66
|
+
if visited.include? entity
|
67
|
+
raise UniverseCompiler::Error, "Circular reference detected for '#{self.to_composite_key}' (#{display_path(*visited.map(&:to_composite_key), entity.to_composite_key)}) !"
|
68
|
+
end
|
69
|
+
visited << entity
|
70
|
+
stack << entity.fields
|
71
|
+
return stack unless entity.extends
|
72
|
+
parent = entity.extends
|
73
|
+
if parent.nil?
|
74
|
+
raise UniverseCompiler::Error, "Invalid inheritance defined in '#{entity.to_composite_key}' extending '#{entity.to_composite_key}' (#{display_path(*visited.map(&:composite_key), entity.extends.to_composite_key)}) !"
|
75
|
+
end
|
76
|
+
stack = inherited_fields_stack parent, stack, visited
|
77
|
+
stack
|
78
|
+
end
|
79
|
+
|
80
|
+
def display_path(*names)
|
81
|
+
names.join ' -> '
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SuperStack
|
2
|
+
module MergePolicies
|
3
|
+
|
4
|
+
module InheritanceMergePolicy
|
5
|
+
|
6
|
+
extend UniverseCompiler::Utils::DeepTraverse
|
7
|
+
extend UniverseCompiler::Entity::Marshalling
|
8
|
+
|
9
|
+
def self.merge(h1, h2)
|
10
|
+
h1_dereferenced = dereferenced_fields h1
|
11
|
+
h2_dereferenced = dereferenced_fields h2
|
12
|
+
merged = h1_dereferenced.deep_merge! h2_dereferenced
|
13
|
+
resolve_fields_references merged
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
module Marshalling
|
5
|
+
|
6
|
+
|
7
|
+
def fully_resolved?
|
8
|
+
fully_resolved || false
|
9
|
+
end
|
10
|
+
|
11
|
+
def resolve_fields_references!(from_fields = fields)
|
12
|
+
UniverseCompiler.logger.debug "Starting resolution for '#{to_composite_key}'."
|
13
|
+
@fields = resolve_fields_references from_fields
|
14
|
+
UniverseCompiler.logger.debug "Completed resolution for '#{to_composite_key}'."
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def resolve_fields_references(from_fields = fields)
|
19
|
+
self.fully_resolved = true
|
20
|
+
deep_map from_fields do |leaf|
|
21
|
+
case leaf
|
22
|
+
when UniverseCompiler::Entity::Reference
|
23
|
+
res = leaf.to_entity raise_error: false
|
24
|
+
if res
|
25
|
+
res
|
26
|
+
else
|
27
|
+
self.fully_resolved = false
|
28
|
+
leaf
|
29
|
+
end
|
30
|
+
else
|
31
|
+
leaf
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def traverse_fields(fields_to_process = fields)
|
37
|
+
deep_traverse(fields_to_process) do |leaf|
|
38
|
+
yield leaf
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def dereferenced_fields(fields_to_dereference = fields)
|
43
|
+
deep_map(fields_to_dereference) do |leaf|
|
44
|
+
case leaf
|
45
|
+
when UniverseCompiler::Entity::Base
|
46
|
+
leaf.to_reference
|
47
|
+
when Symbol, Numeric, NilClass, TrueClass, FalseClass
|
48
|
+
leaf
|
49
|
+
else
|
50
|
+
leaf.clone
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_accessor :fully_resolved
|
58
|
+
|
59
|
+
def marshal_dump
|
60
|
+
{ fields: dereferenced_fields, universe: universe.name }
|
61
|
+
end
|
62
|
+
|
63
|
+
def marshal_load(data)
|
64
|
+
stored_universe = UniverseCompiler::Universe::Base.universes[data[:universe]]
|
65
|
+
initialize fields: data[:fields], universe: stored_universe
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
module Overridden
|
5
|
+
|
6
|
+
def overridden_by
|
7
|
+
@overridden_by ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply_override(override_fields, overrider)
|
11
|
+
merge_engine = SuperStack::Manager.new
|
12
|
+
merge_engine.merge_policy = SuperStack::MergePolicies::InheritanceMergePolicy
|
13
|
+
merge_engine << fields.to_hash
|
14
|
+
merge_engine << override_fields
|
15
|
+
add_overrider overrider
|
16
|
+
@fields = merge_engine[]
|
17
|
+
merge_engine.clear_layers
|
18
|
+
@fields
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def add_overrider(overrider)
|
24
|
+
raise "This object #{to_composite_key} is already overridden by #{overrider.to_composite_key}" if overridden_by.include? overrider
|
25
|
+
overridden_by << overrider
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module UniverseCompiler
|
4
|
+
module Entity
|
5
|
+
|
6
|
+
module Persistence
|
7
|
+
|
8
|
+
attr_accessor :source_uri
|
9
|
+
|
10
|
+
def self.load(uri)
|
11
|
+
entity = YAML.load_file uri
|
12
|
+
entity.source_uri = uri
|
13
|
+
entity
|
14
|
+
end
|
15
|
+
|
16
|
+
def save(uri = source_uri, raise_error: true)
|
17
|
+
valid? raise_error: raise_error
|
18
|
+
FileUtils.mkpath File.dirname(uri)
|
19
|
+
File.write uri, to_yaml, mode: 'w'
|
20
|
+
self.source_uri = uri
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete
|
25
|
+
universe.delete self
|
26
|
+
unless self.source_uri.nil?
|
27
|
+
FileUtils.rm self.source_uri
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module UniverseCompiler
|
4
|
+
module Entity
|
5
|
+
|
6
|
+
class Reference
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def references
|
11
|
+
@references ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def new_instance(entity = nil)
|
15
|
+
k = entity_to_cache_key entity
|
16
|
+
return references[k] if references[k]
|
17
|
+
e = new entity
|
18
|
+
references[k] = e
|
19
|
+
e
|
20
|
+
end
|
21
|
+
|
22
|
+
def entity_to_cache_key(entity_or_reference)
|
23
|
+
universe_name = entity_or_reference.universe.nil? ? '' : entity_or_reference.universe.name
|
24
|
+
universe_name ||= ''
|
25
|
+
[entity_or_reference.type, entity_or_reference.name, universe_name]
|
26
|
+
end
|
27
|
+
|
28
|
+
private :new
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
yaml_tag '!psref'
|
33
|
+
|
34
|
+
include UniverseCompiler::Utils::ErrorPropagation
|
35
|
+
include UniverseCompiler::Entity::Conversion
|
36
|
+
|
37
|
+
attr_reader :type, :name, :universe
|
38
|
+
|
39
|
+
def initialize(entity = nil)
|
40
|
+
self.entity = entity unless entity.nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def entity=(entity)
|
44
|
+
@type = entity.type
|
45
|
+
@name = entity.name
|
46
|
+
self.universe = entity.universe
|
47
|
+
end
|
48
|
+
|
49
|
+
def universe=(another_universe)
|
50
|
+
k = self.class.entity_to_cache_key self
|
51
|
+
self.class.references.delete k
|
52
|
+
@universe = another_universe
|
53
|
+
self.class.entity_to_cache_key self
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_entity(raise_error: true)
|
57
|
+
false_or_raise "Cannot get entity '#{to_composite_key.inspect}' if its universe is not defined!", raise_error: raise_error if universe.nil?
|
58
|
+
entity = universe.get_entity *to_composite_key
|
59
|
+
false_or_raise "Cannot find entity '#{to_composite_key.inspect}' in the universe '#{universe}'", raise_error: raise_error if entity.nil?
|
60
|
+
entity
|
61
|
+
end
|
62
|
+
|
63
|
+
def encode_with(coder)
|
64
|
+
coder['type'] = type
|
65
|
+
coder['name'] = name
|
66
|
+
end
|
67
|
+
|
68
|
+
def init_with(coder)
|
69
|
+
initialize
|
70
|
+
@type = coder['type']
|
71
|
+
@name = coder['name']
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
|
3
|
+
module UniverseCompiler
|
4
|
+
module Entity
|
5
|
+
|
6
|
+
module RelationsManagement
|
7
|
+
|
8
|
+
def has_one(entity_type, name: nil)
|
9
|
+
case entity_type
|
10
|
+
when Class
|
11
|
+
name = name.nil? ? entity_type.entity_type : name.to_sym
|
12
|
+
define_constraint name, :has_one, entity_type.entity_type
|
13
|
+
when Symbol, String
|
14
|
+
name = name.nil? ? entity_type.to_sym : name.to_sym
|
15
|
+
define_constraint name, :has_one, entity_type.to_sym
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_many(entity_type, name: nil)
|
20
|
+
name = case entity_type
|
21
|
+
when Class
|
22
|
+
name.nil? ? entity_type.entity_type : name.to_sym
|
23
|
+
when Symbol, String
|
24
|
+
name.nil? ? entity_type.to_sym : name.to_sym
|
25
|
+
end
|
26
|
+
field_name = name.to_s.pluralize.to_sym
|
27
|
+
|
28
|
+
case entity_type
|
29
|
+
when Class
|
30
|
+
define_constraint field_name, :has_many, entity_type.entity_type
|
31
|
+
when Symbol, String
|
32
|
+
define_constraint field_name, :has_many, entity_type.to_sym
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def entity_type_relations
|
37
|
+
fields_constraints.select do |_, constraints|
|
38
|
+
constraints.keys.include? :has_one or constraints.keys.include? :has_many
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module UniverseCompiler
|
2
|
+
module Entity
|
3
|
+
|
4
|
+
module TypeManagement
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
def entity_type(value = nil)
|
9
|
+
if value.nil?
|
10
|
+
@entity_type || name.underscore
|
11
|
+
else
|
12
|
+
self.entity_type = value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def entity_type=(value)
|
17
|
+
raise UniverseCompiler::Error, "You cannot change an entity type for class '#{self.name}'" unless @entity_type.nil?
|
18
|
+
raise UniverseCompiler::Error, 'Only Symbol is supported for entity_type !' unless value.is_a? Symbol
|
19
|
+
@entity_type = value
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.valid_for_type?(entity)
|
25
|
+
entity.respond_to? :type and entity.class.respond_to? :entity_type
|
26
|
+
end
|
27
|
+
|
28
|
+
def type
|
29
|
+
self.class.entity_type
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.included(base)
|
33
|
+
base.extend(ClassMethods)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.extended(base)
|
37
|
+
included base.class
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|