hospodar 1.0.0
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/.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
|