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,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
|