hospodar 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +9 -0
- data/.rspec +3 -0
- data/.rubocop.yml +80 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +86 -0
- data/LICENSE.txt +21 -0
- data/README.md +49 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/hospodar.gemspec +47 -0
- data/lib/hospodar/builder/assembler.rb +96 -0
- data/lib/hospodar/builder/error.rb +15 -0
- data/lib/hospodar/builder/exeptional.rb +20 -0
- data/lib/hospodar/builder/flatten.rb +36 -0
- data/lib/hospodar/builder/helpers.rb +21 -0
- data/lib/hospodar/builder/id.rb +15 -0
- data/lib/hospodar/builder/instantiation_error.rb +24 -0
- data/lib/hospodar/builder/nested.rb +18 -0
- data/lib/hospodar/builder/proxy.rb +34 -0
- data/lib/hospodar/builder/strategies/enumerate.rb +26 -0
- data/lib/hospodar/builder/strategies/execute.rb +76 -0
- data/lib/hospodar/builder/strategies/init.rb +55 -0
- data/lib/hospodar/builder/strategies/inject.rb +35 -0
- data/lib/hospodar/builder/strategies/link.rb +31 -0
- data/lib/hospodar/builder/strategies/mount.rb +56 -0
- data/lib/hospodar/builder/strategies/translate.rb +60 -0
- data/lib/hospodar/builder/wrapped.rb +40 -0
- data/lib/hospodar/builder.rb +113 -0
- data/lib/hospodar/dsl.rb +84 -0
- data/lib/hospodar/factories.rb +28 -0
- data/lib/hospodar/group_definition.rb +22 -0
- data/lib/hospodar/inheritance_helpers.rb +36 -0
- data/lib/hospodar/module_builder.rb +88 -0
- data/lib/hospodar/registry.rb +34 -0
- data/lib/hospodar/subclassing_helpers.rb +29 -0
- data/lib/hospodar/version.rb +5 -0
- data/lib/hospodar.rb +54 -0
- metadata +255 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
# Error for signaling creating error
|
6
|
+
class InstantiationError < StandardError
|
7
|
+
attr_reader :init_attrs, :step_id
|
8
|
+
|
9
|
+
def initialize(id, init_attrs)
|
10
|
+
@init_attrs = init_attrs
|
11
|
+
@step_id = id
|
12
|
+
super(error_message)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def error_message
|
18
|
+
msg = "Can't create an instance of #{step_id} class"
|
19
|
+
msg += "with the folloving attributes: #{init_attrs}" unless init_attrs.empty?
|
20
|
+
msg
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
# Implements nest(&block) strategy in schemas
|
6
|
+
class Nested < Wrapped
|
7
|
+
def on_planing(receiver)
|
8
|
+
execution_plan(receiver, reverse: true, &dsl_block)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def type
|
14
|
+
:nested
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
# User accessible objects for firing up building process
|
6
|
+
class Proxy
|
7
|
+
def initialize(target)
|
8
|
+
@target = target
|
9
|
+
@condition = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
return @target.call if @condition.empty?
|
14
|
+
|
15
|
+
type, block = @condition.to_a.first
|
16
|
+
@target.call(type, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def do_while(&block)
|
20
|
+
return if @condition.one?
|
21
|
+
|
22
|
+
@condition[:do_while] = block
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def do_until(&block)
|
27
|
+
return if @condition.one?
|
28
|
+
|
29
|
+
@condition[:do_until] = block
|
30
|
+
self
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
module Strategies
|
6
|
+
# Does final steps after completing building process for nested structures
|
7
|
+
class Enumerate < Execute
|
8
|
+
attr_reader :delegate
|
9
|
+
|
10
|
+
def initialize(strategy, target, factory, delegate)
|
11
|
+
super(strategy, target, factory)
|
12
|
+
@delegate = delegate
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def prepare_top
|
18
|
+
return target if strategy.object.nil?
|
19
|
+
|
20
|
+
Hospodar::Builder.def_accessor(strategy.last_step, on: target, to: strategy.object, delegate: delegate)
|
21
|
+
target
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
module Strategies
|
6
|
+
# Runs building process
|
7
|
+
class Execute
|
8
|
+
attr_reader :strategy, :target, :factory
|
9
|
+
|
10
|
+
def initialize(strategy, target, factory)
|
11
|
+
@strategy = strategy
|
12
|
+
@target = target
|
13
|
+
@factory = factory
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(type = nil, &block)
|
17
|
+
run_with_error_handling { process(type, &block) }
|
18
|
+
prepare_top
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def process(type = nil, &block)
|
24
|
+
build_plan.take_while do |object, id|
|
25
|
+
next true unless block
|
26
|
+
|
27
|
+
run_build_step(object, id, type, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_build_step(object, id, type)
|
32
|
+
run_with_error_handling(return_value: false) do
|
33
|
+
res = yield(object, id)
|
34
|
+
type == :do_while ? res : !res
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def run_with_error_handling(return_value: nil)
|
39
|
+
yield
|
40
|
+
rescue StandardError => e
|
41
|
+
handle_exception(e)
|
42
|
+
return_value
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_plan
|
46
|
+
@build_plan ||= strategy.call
|
47
|
+
end
|
48
|
+
|
49
|
+
def handle_exception(exc)
|
50
|
+
target.on_exception ||= factory.on_exception
|
51
|
+
target.exception = wrap_error(exc)
|
52
|
+
exetute_exception_strategy(target.exception, target.on_exception, target)
|
53
|
+
end
|
54
|
+
|
55
|
+
def wrap_error(exc)
|
56
|
+
return exc if exc.is_a?(Hospodar::Builder::InstantiationError)
|
57
|
+
|
58
|
+
Hospodar::Builder.create_error(exc, strategy.step_id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def exetute_exception_strategy(exc, on_exception, target)
|
62
|
+
case on_exception
|
63
|
+
when Proc
|
64
|
+
on_exception.call(exc, target)
|
65
|
+
when :raise
|
66
|
+
raise(exc)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def prepare_top
|
71
|
+
target
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
module Strategies
|
6
|
+
# Creates objects for nest and wrap strategies
|
7
|
+
class Init
|
8
|
+
attr_reader :strategy, :pipe, :target
|
9
|
+
|
10
|
+
def initialize(strategy, pipe, target)
|
11
|
+
@strategy = strategy
|
12
|
+
@pipe = pipe
|
13
|
+
@target = target
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
Enumerator.new do |yielder|
|
18
|
+
instantiate_objects(yielder)
|
19
|
+
end.lazy
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def instantiate_objects(yielder)
|
25
|
+
pipe.each do |title, init_proc, id|
|
26
|
+
object = create_object(init_proc, id)
|
27
|
+
next if object.nil? && target.ignore_exceptions?
|
28
|
+
|
29
|
+
yielder << [title, create_object(init_proc, id), id]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_object(init_proc, id)
|
34
|
+
init_proc.call(*init_attrs_for(id))
|
35
|
+
rescue StandardError => e
|
36
|
+
handle_exception(e, id, init_attrs_for(id))
|
37
|
+
end
|
38
|
+
|
39
|
+
def init_attrs_for(id)
|
40
|
+
Array(init_params[id.to_sym])
|
41
|
+
end
|
42
|
+
|
43
|
+
def init_params
|
44
|
+
@init_params ||= strategy.call
|
45
|
+
end
|
46
|
+
|
47
|
+
def handle_exception(exc, id, init_attrs)
|
48
|
+
exception = Hospodar::Builder.create_init_error(exc, id, init_attrs)
|
49
|
+
target.exception = exception
|
50
|
+
raise exception unless target.ignore_exceptions?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
module Strategies
|
6
|
+
# Allows user to pass parameters for creating new objects via method calls
|
7
|
+
class Inject
|
8
|
+
# User use it as configuration DSL for builder
|
9
|
+
class Builder
|
10
|
+
attr_reader :__result__, :__methods_allowlist__
|
11
|
+
|
12
|
+
def initialize(list)
|
13
|
+
@__result__ = {}
|
14
|
+
@__methods_allowlist__ = list
|
15
|
+
list.each do |method_name|
|
16
|
+
define_singleton_method(method_name) do |*attrs|
|
17
|
+
@__result__[method_name] = attrs
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(list, &block)
|
24
|
+
@builder = Builder.new(list.map(&:to_sym))
|
25
|
+
@block = block
|
26
|
+
end
|
27
|
+
|
28
|
+
def call
|
29
|
+
@block&.call(@builder)
|
30
|
+
@builder.__result__
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
module Strategies
|
6
|
+
Link = Struct.new(:strategy, :delegate) do
|
7
|
+
attr_reader :object, :step_id
|
8
|
+
|
9
|
+
def call
|
10
|
+
Enumerator.new do |yielder|
|
11
|
+
strategy.call.inject(nil) do |object, (title, layer_object, id)|
|
12
|
+
@object = layer_object
|
13
|
+
@step_id = id
|
14
|
+
if title.nil?
|
15
|
+
yielder << [layer_object, id]
|
16
|
+
next layer_object
|
17
|
+
end
|
18
|
+
Hospodar::Builder.def_accessor(title, on: layer_object, to: object, delegate: delegate)
|
19
|
+
yielder << [layer_object, id]
|
20
|
+
layer_object
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def last_step
|
26
|
+
step_id.title
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
module Strategies
|
6
|
+
# Creates objects and provides readers on target objects
|
7
|
+
class Mount
|
8
|
+
attr_reader :strategy, :pipe, :target, :step, :object, :step_id
|
9
|
+
|
10
|
+
def initialize(strategy, pipe, target, step)
|
11
|
+
@strategy = strategy
|
12
|
+
@pipe = pipe
|
13
|
+
@target = target
|
14
|
+
@step = step
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
Enumerator.new do |yielder|
|
19
|
+
instantiate_objects(yielder)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def instantiate_objects(yielder)
|
24
|
+
pipe.each do |id, init_proc|
|
25
|
+
@step_id = id
|
26
|
+
object = create_object(init_proc, id)
|
27
|
+
next if object.nil? && target.ignore_exceptions?
|
28
|
+
|
29
|
+
target.define_singleton_method(id.title.to_sym) { object }
|
30
|
+
yielder << [object, id]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_object(init_proc, id)
|
35
|
+
init_proc.call(*init_attrs_for(id))
|
36
|
+
rescue StandardError => e
|
37
|
+
handle_exception(e, id, init_attrs_for(id))
|
38
|
+
end
|
39
|
+
|
40
|
+
def init_attrs_for(id)
|
41
|
+
Array(init_params[id.to_sym])
|
42
|
+
end
|
43
|
+
|
44
|
+
def init_params
|
45
|
+
@init_params ||= strategy.call
|
46
|
+
end
|
47
|
+
|
48
|
+
def handle_exception(exc, id, init_attrs)
|
49
|
+
exception = Hospodar::Builder.create_init_error(exc, id, init_attrs)
|
50
|
+
target.exception = exception
|
51
|
+
raise exception unless target.ignore_exceptions?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
module Strategies
|
6
|
+
# Singleton for parsing schemas
|
7
|
+
module Translate
|
8
|
+
# Collects method calls in a given block
|
9
|
+
Tracer = Struct.new(:yielder) do
|
10
|
+
def method_missing(method_name, *attrs)
|
11
|
+
yielder << [method_name, attrs.first]
|
12
|
+
end
|
13
|
+
|
14
|
+
def respond_to_missing?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ID_CLASS = Hospodar::Builder::Id
|
20
|
+
|
21
|
+
ExecutionPlanMatrix = Struct.new(:collection) do
|
22
|
+
def with_creation_procs(factory)
|
23
|
+
self.collection = collection.map do |group, title|
|
24
|
+
[group, title, factory.mother_ship_assembler_new_instance(group, title)]
|
25
|
+
end
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_nested_form(builder_params)
|
30
|
+
data = collection
|
31
|
+
data.unshift(builder_params.to_a) unless builder_params.object.nil?
|
32
|
+
intermediate_form = data.transpose
|
33
|
+
tobe_assembled = intermediate_form[1..-1]
|
34
|
+
keys = intermediate_form[0..1].transpose.map { |g, e| ID_CLASS.new(g, e) }
|
35
|
+
tobe_assembled.first.pop
|
36
|
+
tobe_assembled.first.unshift(nil)
|
37
|
+
tobe_assembled << keys
|
38
|
+
self.collection = tobe_assembled.transpose
|
39
|
+
self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def call(reverse: false, &block)
|
45
|
+
enum = trace(&block)
|
46
|
+
return ExecutionPlanMatrix.new(enum) unless reverse
|
47
|
+
|
48
|
+
ExecutionPlanMatrix.new(enum.reverse)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def trace(&block)
|
54
|
+
Tracer.new([]).tap { |tracer| tracer.instance_eval(&block) }.yielder
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
module Builder
|
5
|
+
# Implements wrap(&block) strategy in schemas
|
6
|
+
class Wrapped < Assembler
|
7
|
+
def initialize(factory, on_exception, delegate)
|
8
|
+
super(factory, on_exception)
|
9
|
+
@delegate = delegate
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_planing(receiver)
|
13
|
+
execution_plan(receiver, &dsl_block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_building(receiver, plan, &on_create)
|
17
|
+
building_steps(receiver, plan, &on_create)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :delegate
|
23
|
+
|
24
|
+
def type
|
25
|
+
:wrapped
|
26
|
+
end
|
27
|
+
|
28
|
+
def execution_plan(receiver, reverse: false)
|
29
|
+
creation_matrix_from_dsl(receiver, reverse: reverse, &dsl_block).to_nested_form(builder_params).collection
|
30
|
+
end
|
31
|
+
|
32
|
+
def building_steps(receiver, plan, &on_create)
|
33
|
+
inject = Strategies::Inject.new(plan.map(&:last), &on_create)
|
34
|
+
init = Strategies::Init.new(inject, plan, target)
|
35
|
+
link = Strategies::Link.new(init, delegate)
|
36
|
+
Strategies::Enumerate.new(link, target, receiver, delegate)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'hospodar/builder/id'
|
4
|
+
require 'hospodar/builder/proxy'
|
5
|
+
require 'hospodar/builder/assembler'
|
6
|
+
require 'hospodar/builder/flatten'
|
7
|
+
require 'hospodar/builder/wrapped'
|
8
|
+
require 'hospodar/builder/nested'
|
9
|
+
require 'hospodar/builder/helpers'
|
10
|
+
require 'hospodar/builder/instantiation_error'
|
11
|
+
require 'hospodar/builder/error'
|
12
|
+
require 'hospodar/builder/exeptional'
|
13
|
+
|
14
|
+
module Hospodar
|
15
|
+
# Introduces possibilty to build complex objects by schema
|
16
|
+
module Builder
|
17
|
+
# Handles method delegation
|
18
|
+
module MethodMissingDelegation
|
19
|
+
def method_missing(name, *attrs, &block)
|
20
|
+
return super unless methods.detect { |m| m == :ms_predecessor }
|
21
|
+
|
22
|
+
public_send(ms_predecessor).public_send(name, *attrs, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond_to_missing?(method_name, _include_private = false)
|
26
|
+
return super unless methods.detect { |m| m == :ms_predecessor }
|
27
|
+
|
28
|
+
public_send(ms_predecessor).respond_to?(method_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private_constant :Assembler
|
33
|
+
private_constant :Proxy
|
34
|
+
private_constant :Helpers
|
35
|
+
private_constant :Exceptional
|
36
|
+
private_constant :Strategies
|
37
|
+
private_constant :MethodMissingDelegation
|
38
|
+
|
39
|
+
class Error < StandardError; end
|
40
|
+
|
41
|
+
def self.create_init_error(exc, id, init_attrs)
|
42
|
+
new_error = InstantiationError.new(id, init_attrs)
|
43
|
+
new_error.set_backtrace(exc.backtrace)
|
44
|
+
new_error
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.create_error(exc, id)
|
48
|
+
new_error = Error.new(exc.message, id)
|
49
|
+
new_error.set_backtrace(exc.backtrace)
|
50
|
+
new_error
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.def_accessor(accessor, on:, to:, delegate: false)
|
54
|
+
on.define_singleton_method(:ms_predecessor) { accessor }
|
55
|
+
on.define_singleton_method(accessor) { to }
|
56
|
+
on.extend(MethodMissingDelegation) if delegate
|
57
|
+
end
|
58
|
+
|
59
|
+
# DSL for describing objects schemas
|
60
|
+
module ClassMethods
|
61
|
+
class << self
|
62
|
+
attr_accessor :__mf_assembler_name__
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.included(receiver)
|
66
|
+
receiver.extend self
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.extended(receiver)
|
70
|
+
receiver.produces :flatten_structs, :wrapped_structs, :nested_structs
|
71
|
+
receiver.extend Helpers
|
72
|
+
end
|
73
|
+
|
74
|
+
def wrap(title, base_class: Class.new(Object), init: nil, delegate: false, on_exception: nil, &block)
|
75
|
+
wrapped_struct(title, base_class: base_class.include(Exceptional), init: init)
|
76
|
+
class_eval(&block)
|
77
|
+
Wrapped.new(self, on_exception, delegate).for(title, &block).inject_method
|
78
|
+
end
|
79
|
+
|
80
|
+
def nest(title, base_class: Class.new(Object), init: nil, delegate: false, on_exception: nil, &block)
|
81
|
+
nested_struct(title, base_class: base_class.include(Exceptional), init: init)
|
82
|
+
class_eval(&block)
|
83
|
+
Nested.new(self, on_exception, delegate).for(title, &block).inject_method
|
84
|
+
end
|
85
|
+
|
86
|
+
def flat(title, base_class: Class.new(Object), init: nil, on_exception: nil, &block)
|
87
|
+
flatten_struct(title, base_class: base_class.include(Exceptional), init: init)
|
88
|
+
class_eval(&block)
|
89
|
+
Flatten.new(self, on_exception).for(title, &block).inject_method
|
90
|
+
end
|
91
|
+
|
92
|
+
def on_exception(&block)
|
93
|
+
block ? @on_exception = block : @on_exception
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Copies on_exception callback definition to subclass
|
98
|
+
module InheritanceHelpers
|
99
|
+
def inherited(subclass)
|
100
|
+
super
|
101
|
+
subclass.on_exception(&on_exception)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.included(receiver)
|
106
|
+
receiver.extend ClassMethods
|
107
|
+
receiver.extend InheritanceHelpers
|
108
|
+
receiver.on_exception do |e, _result|
|
109
|
+
raise e
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/hospodar/dsl.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
# Core logic for creating objects
|
5
|
+
module DSL
|
6
|
+
def define_component_store_method(receiver, title)
|
7
|
+
mod = self
|
8
|
+
receiver.define_singleton_method(mod.store_method_name(title)) do |klass|
|
9
|
+
base_class = public_send(:"#{mod.components_storage_name}")[title]
|
10
|
+
mother_ship_check_inheritance!(klass, base_class)
|
11
|
+
send(mod.simple_store_method_name(title), klass)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def define_component_simple_store_method(receiver, title)
|
16
|
+
mod = self
|
17
|
+
receiver.define_singleton_method(mod.simple_store_method_name(title)) do |klass|
|
18
|
+
send(:"write_#{mod.component_class_reader(title)}", klass)
|
19
|
+
public_send(:"#{mod.components_storage_name}")[title] = klass
|
20
|
+
end
|
21
|
+
receiver.private_class_method mod.simple_store_method_name(title)
|
22
|
+
end
|
23
|
+
|
24
|
+
def define_component_activation_method
|
25
|
+
mod = self
|
26
|
+
define_method(mod.activation_method_name) do |title, base_class, klass, init = nil, &block|
|
27
|
+
raise(ArgumentError, 'please provide a block or class') if klass.nil? && block.nil?
|
28
|
+
|
29
|
+
mother_ship_check_inheritance!(klass, base_class)
|
30
|
+
|
31
|
+
target_class = klass || base_class
|
32
|
+
|
33
|
+
patched_class = mother_ship_patch_class(target_class, &block)
|
34
|
+
mother_ship_define_init(patched_class, &init)
|
35
|
+
public_send(mod.store_method_name(title), patched_class)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_component_new_instance_method(title)
|
40
|
+
mod = self
|
41
|
+
define_method mod.new_instance_method_name(title) do |*args|
|
42
|
+
klass = public_send(mod.component_class_reader(title))
|
43
|
+
klass.__ms_init__(klass, *args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def define_component_configure_method(title)
|
48
|
+
mod = self
|
49
|
+
define_method mod.configure_component_method_name(title) do |klass = nil, init: nil, &block|
|
50
|
+
base_class = public_send(:"#{mod.component_class_reader(title)}")
|
51
|
+
public_send(mod.activation_method_name, title, base_class, klass, init, &block)
|
52
|
+
end
|
53
|
+
private mod.configure_component_method_name(title)
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_component_adding_method
|
57
|
+
mod = self
|
58
|
+
define_method(component_name) do |title, base_class: nil, init: nil|
|
59
|
+
singleton_class.class_eval do
|
60
|
+
reader_name = mod.component_class_reader(title)
|
61
|
+
attr_accessor reader_name
|
62
|
+
alias_method :"write_#{reader_name}", :"#{reader_name}="
|
63
|
+
private :"write_#{reader_name}"
|
64
|
+
end
|
65
|
+
klass = base_class || mod.default_base_class || Class.new(Object)
|
66
|
+
mother_ship_define_init(klass, &(init || mod.default_init))
|
67
|
+
mod.define_component_store_method(self, title)
|
68
|
+
mod.define_component_simple_store_method(self, title)
|
69
|
+
send(mod.simple_store_method_name(title), klass)
|
70
|
+
mod.define_component_configure_method(title)
|
71
|
+
mod.define_component_new_instance_method(title)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def define_components_registry
|
76
|
+
mod = self
|
77
|
+
module_eval <<-METHOD, __FILE__, __LINE__ + 1
|
78
|
+
def #{mod.components_storage_name} # def parts
|
79
|
+
@#{mod.components_storage_name} ||= {} # @parts ||= {}
|
80
|
+
end # end
|
81
|
+
METHOD
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hospodar
|
4
|
+
# Singleton that collects generted modudes
|
5
|
+
module Factories
|
6
|
+
class << self
|
7
|
+
def add_module(components_name, **attrs)
|
8
|
+
registry.resolve(components_name, **attrs)
|
9
|
+
end
|
10
|
+
|
11
|
+
def memoized_modules
|
12
|
+
registry.registered_modules
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def build_module
|
18
|
+
lambda do |components_name, **attrs|
|
19
|
+
ModuleBuilder.call(components_name, **attrs)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def registry
|
24
|
+
@registry ||= Registry.new(on_missing_key: build_module)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|