eco-helpers 3.0.27 → 3.0.29
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 +4 -4
- data/CHANGELOG.md +52 -1
- data/eco-helpers.gemspec +1 -0
- data/lib/eco/api/common/class_helpers.rb +1 -136
- data/lib/eco/api/common/loaders/config/cli.rb +1 -1
- data/lib/eco/api/common/loaders/config/session.rb +1 -1
- data/lib/eco/api/common/loaders/config/workflow.rb +1 -1
- data/lib/eco/api/common/loaders/config.rb +2 -5
- data/lib/eco/api/common/loaders/use_case/target_model.rb +1 -1
- data/lib/eco/api/common/loaders/use_case/type.rb +1 -1
- data/lib/eco/api/common/people/supervisor_helpers.rb +3 -1
- data/lib/eco/api/common/session/helpers/prompt_user.rb +3 -1
- data/lib/eco/api/common/session/logger/channels.rb +1 -0
- data/lib/eco/api/common/session/sftp.rb +9 -2
- data/lib/eco/api/session/batch/job/sets.rb +1 -0
- data/lib/eco/api/session/batch/job/type.rb +1 -0
- data/lib/eco/api/session/batch/launcher/valid_methods.rb +3 -2
- data/lib/eco/api/usecases/base_case/model.rb +2 -1
- data/lib/eco/api/usecases/base_case/type.rb +2 -1
- data/lib/eco/api/usecases/base_io/validations.rb +2 -1
- data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +3 -2
- data/lib/eco/api/usecases/lib/files/file_pattern.rb +0 -2
- data/lib/eco/api/usecases/lib/files/sftp.rb +9 -6
- data/lib/eco/api/usecases/samples/drivers/sftp_sample.rb +3 -1
- data/lib/eco/api/usecases/service/sftp.rb +36 -0
- data/lib/eco/api/usecases/service.rb +15 -0
- data/lib/eco/api/usecases.rb +1 -0
- data/lib/eco/data/files/helpers.rb +4 -2
- data/lib/eco/data/fuzzy_match.rb +4 -2
- data/lib/eco/data/hashes/diff_result/meta.rb +2 -1
- data/lib/eco/data/locations/node_diff/accessors.rb +2 -1
- data/lib/eco/language/delegation/chainable_delegator.rb +18 -0
- data/lib/eco/language/delegation/delegating_blank.rb +162 -0
- data/lib/eco/language/delegation/delegating_missing.rb +104 -0
- data/lib/eco/language/delegation/delegating_missing_const.rb +53 -0
- data/lib/eco/language/delegation/delegating_missing_on_class.rb +53 -0
- data/lib/eco/language/delegation/{const_delegator.rb → for_delegator/const_delegator.rb} +8 -6
- data/lib/eco/language/delegation/{const_lookup_hooks.rb → for_delegator/const_lookup_hooks.rb} +37 -19
- data/lib/eco/language/delegation/{delegated_class.rb → for_delegator/delegated_class.rb} +6 -19
- data/lib/eco/language/delegation/for_delegator.rb +11 -0
- data/lib/eco/language/delegation.rb +6 -3
- data/lib/eco/language/klass/builder.rb +29 -0
- data/lib/eco/language/klass/helpers_built.rb +9 -0
- data/lib/eco/language/klass/hierarchy.rb +34 -0
- data/lib/eco/language/klass/inheritable_class_vars.rb +45 -0
- data/lib/eco/language/klass/naming.rb +21 -0
- data/lib/eco/language/klass/resolver.rb +30 -0
- data/lib/eco/language/klass/when_inherited.rb +10 -13
- data/lib/eco/language/klass.rb +6 -0
- data/lib/eco/language/methods.rb +0 -1
- data/lib/eco/language/strings/underscore.rb +17 -0
- data/lib/eco/language/strings.rb +8 -0
- data/lib/eco/language.rb +1 -0
- data/lib/eco/version.rb +1 -1
- metadata +35 -6
- data/lib/eco/language/methods/delegate_missing.rb +0 -30
@@ -0,0 +1,162 @@
|
|
1
|
+
# rubocop:disable Naming/MethodParameterName
|
2
|
+
|
3
|
+
module Eco::Language::Delegation
|
4
|
+
# Installs a middleware for each delegated entity
|
5
|
+
# @note It adds an implicit delegate missing to the target,
|
6
|
+
# unless it has already been defined.
|
7
|
+
module DelegatingBlank
|
8
|
+
class << self
|
9
|
+
def included(base)
|
10
|
+
super
|
11
|
+
|
12
|
+
base.include DelegatingMissing
|
13
|
+
base.extend ClassMethods
|
14
|
+
base.inheritable_class_vars :_delegating_blank
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
include Eco::Language::Strings::Underscore
|
20
|
+
|
21
|
+
def delegating_blank(*these, to:, safe: false)
|
22
|
+
msg = "Delegating to: should be String or Symbol. Given: #{to.class}"
|
23
|
+
raise ArgumentError, msg unless sym_or_string?(to)
|
24
|
+
|
25
|
+
these.map do |this|
|
26
|
+
msg = "Expecting String or Symbol. Given: #{this}."
|
27
|
+
raise ArgumentError, msg unless sym_or_string?(this)
|
28
|
+
|
29
|
+
this.to_sym
|
30
|
+
end.each do |this|
|
31
|
+
delegating_missing(this, to: to) unless delegating_missing?(this)
|
32
|
+
install_blank_delegator(at: this, safe: safe)
|
33
|
+
|
34
|
+
_delegating_blank[this] = to
|
35
|
+
_safe_blank_delegation[this] = safe
|
36
|
+
end
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def delegating_blank?(sym)
|
42
|
+
return false unless sym_or_string?(sym)
|
43
|
+
return true if _delegating_blank.key?(sym.to_sym)
|
44
|
+
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def safe_blank_delegation?(sym)
|
49
|
+
return false unless sym_or_string?(sym)
|
50
|
+
return false unless _safe_blank_delegation.key?(sym.to_sym)
|
51
|
+
|
52
|
+
_safe_blank_delegation[sym.to_sym]
|
53
|
+
end
|
54
|
+
|
55
|
+
def delegating_blank_target(instance, at: nil)
|
56
|
+
return unless delegating_blank?(at)
|
57
|
+
return unless (subject_ref = _delegating_blank[at])
|
58
|
+
|
59
|
+
instance_has_method =
|
60
|
+
instance.methods.include?(subject_ref) ||
|
61
|
+
instance.private_methods.include?(subject_ref)
|
62
|
+
|
63
|
+
return instance.method(subject_ref).call if instance_has_method
|
64
|
+
return unless at.to_s.start_with?('@')
|
65
|
+
return unless instance.instance_variable_defined?(at)
|
66
|
+
|
67
|
+
instance.instance_variable_get(at)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def method_added(method_name)
|
73
|
+
return super if (@installing ||= false)
|
74
|
+
|
75
|
+
method_name = method_name.to_sym
|
76
|
+
return super unless delegating_blank?(method_name)
|
77
|
+
|
78
|
+
@installing = true
|
79
|
+
install_blank_delegator(at: method_name)
|
80
|
+
@installing = false
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
def _delegating_blank
|
85
|
+
@_delegating_blank ||= {}
|
86
|
+
end
|
87
|
+
|
88
|
+
def _safe_blank_delegation
|
89
|
+
@_safe_blank_delegation ||= {}
|
90
|
+
end
|
91
|
+
|
92
|
+
# If `base` does NOT have the method, don't install the middleware.
|
93
|
+
def install_blank_delegator(base = self, at:, safe: safe_blank_delegation?(at))
|
94
|
+
method_name = underscore(at).gsub('@', '').to_sym
|
95
|
+
install_safe = safe
|
96
|
+
|
97
|
+
return unless instance_method?(base, name: method_name, include_private: true)
|
98
|
+
|
99
|
+
method_was_inherited = inherited_method?(base, name: method_name)
|
100
|
+
original_method = :"delegated_blank_original_#{method_name}"
|
101
|
+
|
102
|
+
return if instance_method?(base, name: original_method, include_private: true, inherited: false)
|
103
|
+
|
104
|
+
base.alias_method(original_method, method_name) unless method_was_inherited
|
105
|
+
|
106
|
+
method_def = proc do |*args, **kargs, &block|
|
107
|
+
if method_was_inherited
|
108
|
+
super(*args, **kargs, &block)
|
109
|
+
else
|
110
|
+
send(original_method, *args, **kargs, &block)
|
111
|
+
end.then do |value|
|
112
|
+
next value unless blank?(value)
|
113
|
+
|
114
|
+
target = self.class.delegating_blank_target(self, at: at)
|
115
|
+
|
116
|
+
if at.to_s.start_with?('@')
|
117
|
+
target.instance_variable_get(at)
|
118
|
+
elsif !install_safe || target.respond_to?(method_name, true)
|
119
|
+
target.send(method_name, *args, **kargs, &block)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
base.define_method(method_name, &method_def)
|
125
|
+
end
|
126
|
+
|
127
|
+
def instance_method?(base = self, name:, include_private: false, inherited: true)
|
128
|
+
return true if base.method_defined?(name, inherited)
|
129
|
+
return false unless include_private
|
130
|
+
|
131
|
+
base.private_instance_methods(inherited).include?(name.to_sym)
|
132
|
+
end
|
133
|
+
|
134
|
+
def inherited_method?(base = self, name:)
|
135
|
+
return false if base.public_instance_methods(false).include?(name.to_sym)
|
136
|
+
return false if base.private_instance_methods(false).include?(name.to_sym)
|
137
|
+
|
138
|
+
true
|
139
|
+
end
|
140
|
+
|
141
|
+
def sym_or_string?(value)
|
142
|
+
return false unless value.is_a?(Symbol) || value.is_a?(String)
|
143
|
+
return false if value.to_s.strip.empty?
|
144
|
+
|
145
|
+
true
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# INSTANCE Methods
|
150
|
+
|
151
|
+
def blank?(value)
|
152
|
+
return super if defined?(super)
|
153
|
+
return true if value.nil?
|
154
|
+
return true if value.to_s.strip.empty?
|
155
|
+
return true if value.respond_to?(:empty?) && value.empty?
|
156
|
+
|
157
|
+
false
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# rubocop:enable Naming/MethodParameterName
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# rubocop:disable Naming/MethodParameterName
|
2
|
+
|
3
|
+
module Eco::Language::Delegation
|
4
|
+
module DelegatingMissing
|
5
|
+
class << self
|
6
|
+
def included(base)
|
7
|
+
super
|
8
|
+
|
9
|
+
base.extend Eco::Language::Klass::HelpersBuilt
|
10
|
+
base.extend ClassMethods
|
11
|
+
base.inheritable_class_vars :_delegating_missing, :delegating_missing_to
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def delegating_missing(*these, to:)
|
17
|
+
msg = "Delegating to: should be String or Symbol. Given: #{to.class}"
|
18
|
+
raise ArgumentError, msg unless sym_or_string?(to)
|
19
|
+
|
20
|
+
these.map do |this|
|
21
|
+
msg = "Expecting String or Symbol. Given: #{this}."
|
22
|
+
raise ArgumentError, msg unless sym_or_string?(this)
|
23
|
+
|
24
|
+
this.to_sym
|
25
|
+
end.each do |this|
|
26
|
+
_delegating_missing[this] = to
|
27
|
+
end
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def delegating_missing_to(to = nil)
|
33
|
+
return @delegating_missing_to if to.nil?
|
34
|
+
|
35
|
+
msg = "Expecting String or Symbol. Given: #{to}."
|
36
|
+
raise ArgumentError, msg unless sym_or_string?(to)
|
37
|
+
|
38
|
+
|
39
|
+
@delegating_missing_to = to.to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def delegating_missing?(sym)
|
43
|
+
return true if sym.nil? && delegating_missing_to
|
44
|
+
return false unless sym_or_string?(sym)
|
45
|
+
return true if _delegating_missing.key?(sym.to_sym)
|
46
|
+
return true if delegating_missing_to
|
47
|
+
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def delegating_missing_target(instance, at: nil)
|
52
|
+
return unless delegating_missing?(at)
|
53
|
+
return unless (subject_ref = _delegating_missing[at] || delegating_missing_to)
|
54
|
+
|
55
|
+
instance_has_method =
|
56
|
+
instance.methods.include?(subject_ref) ||
|
57
|
+
instance.private_methods.include?(subject_ref)
|
58
|
+
|
59
|
+
return instance.method(subject_ref).call if instance_has_method
|
60
|
+
return unless at.to_s.start_with?('@')
|
61
|
+
return unless instance.instance_variable_defined?(at)
|
62
|
+
|
63
|
+
instance.instance_variable_get(at)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def _delegating_missing
|
69
|
+
@_delegating_missing ||= {}
|
70
|
+
end
|
71
|
+
|
72
|
+
def sym_or_string?(value)
|
73
|
+
return false unless value.is_a?(Symbol) || value.is_a?(String)
|
74
|
+
return false if value.to_s.strip.empty?
|
75
|
+
|
76
|
+
true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# INSTANCE Methods
|
81
|
+
|
82
|
+
def respond_to_missing?(method_name, include_private = false)
|
83
|
+
super
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# @note if missing, redirect all parameters to `receiver`,
|
89
|
+
# as long as responds_to `method_name`
|
90
|
+
def method_missing(method_name, *args, **kargs, &block)
|
91
|
+
return super unless self.class.delegating_missing?(method_name)
|
92
|
+
|
93
|
+
target = self.class.delegating_missing_target(self, at: method_name)
|
94
|
+
|
95
|
+
if method_name.to_s.start_with?('@')
|
96
|
+
target.instance_variable_get(method_name)
|
97
|
+
else
|
98
|
+
target.send(method_name, *args, **kargs, &block)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# rubocop:enable Naming/MethodParameterName
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Eco::Language::Delegation
|
2
|
+
# Class to lookup constants through `delegated_class`.
|
3
|
+
# @note it uses `DelegatingMissing`.
|
4
|
+
# @note it tries to lookup on the `class` of the `delegating_missing_to`
|
5
|
+
# object. Which requires an instance of the delegator to exist, with
|
6
|
+
# the aim of scoping its class (so it can chain constant lookup), unless
|
7
|
+
# `delegated_class` is explicitly stated.
|
8
|
+
# @note that not setting `delegated_class` explicitly could fail
|
9
|
+
# to chain the looup.
|
10
|
+
module DelegatingMissingConst
|
11
|
+
class << self
|
12
|
+
def included(base)
|
13
|
+
super
|
14
|
+
|
15
|
+
base.send :include, DelegatingMissing
|
16
|
+
base.send :include, ForDelegator::ConstDelegator
|
17
|
+
base.extend ClassMethods
|
18
|
+
|
19
|
+
# because we inject `#initialize`
|
20
|
+
base.send :prepend, InstanceMethods
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
# Set always `delegated_class` when fetching the
|
26
|
+
# `delegating_missing_to` object.
|
27
|
+
def delegating_missing_target(instance, at: nil)
|
28
|
+
super.tap do |target|
|
29
|
+
next unless target
|
30
|
+
next unless at.nil?
|
31
|
+
|
32
|
+
delegated_class target.class
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module InstanceMethods
|
38
|
+
# Set the `delegated_class` to chain **constant lookup**
|
39
|
+
# via `delegating_missing_to.class`.
|
40
|
+
# @note this requires that the target can be fetched without passing
|
41
|
+
# parameters (as it is typically the case with `delegating_missing_to`).
|
42
|
+
def initialize(...)
|
43
|
+
super if defined?(super)
|
44
|
+
|
45
|
+
delegated_class.tap do |klass|
|
46
|
+
next unless (target = self.class.delegating_missing_target(self))
|
47
|
+
|
48
|
+
self.class.delegated_class target.class
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Eco::Language::Delegation
|
2
|
+
module DelegatingMissingOnClass
|
3
|
+
class << self
|
4
|
+
def included(base)
|
5
|
+
super
|
6
|
+
|
7
|
+
base.extend Eco::Language::Klass::HelpersBuilt
|
8
|
+
base.extend ClassMethods
|
9
|
+
base.inheritable_class_vars :delegating_missing_on_class_to
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def delegating_missing_on_class_to(to = nil)
|
15
|
+
return @delegating_missing_on_class_to if to.nil?
|
16
|
+
|
17
|
+
msg = "Expecting String or Symbol. Given: #{to}."
|
18
|
+
raise ArgumentError, msg unless sym_or_string?(to)
|
19
|
+
|
20
|
+
@delegating_missing_on_class_to = to
|
21
|
+
end
|
22
|
+
|
23
|
+
def respond_to_missing?(method_name, include_private = false)
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# @note if missing, redirect all parameters to `target`,
|
30
|
+
# as long as responds_to `method_name`
|
31
|
+
def method_missing(method_name, *args, **kargs, &block)
|
32
|
+
return super unless (target = delegating_missing_on_class_target)
|
33
|
+
|
34
|
+
target.send(method_name, *args, **kargs, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
# retrieves the delegating_missing_on_class_to object
|
38
|
+
def delegating_missing_on_class_target
|
39
|
+
return unless @delegating_missing_on_class_to
|
40
|
+
|
41
|
+
# fetch the object behind the method
|
42
|
+
method(@delegating_missing_on_class_to).call
|
43
|
+
end
|
44
|
+
|
45
|
+
def sym_or_string?(value)
|
46
|
+
return false unless value.is_a?(Symbol) || value.is_a?(String)
|
47
|
+
return false if value.to_s.strip.empty?
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
|
-
module Eco::Language::Delegation
|
1
|
+
module Eco::Language::Delegation::ForDelegator
|
2
2
|
# Module that is to be used with ruby `Delegator` or related helpers,
|
3
3
|
# such as `SimpleDelegator` or `Forwardable`.
|
4
|
-
# @note
|
4
|
+
# @note
|
5
|
+
# 1. It includes `DelegatedClass`
|
6
|
+
# 2. This class only provides lookups integration.
|
5
7
|
# @note it looks up the constant via `delegated_class`. This is because
|
6
8
|
# on `Delegator` approaches, the intance object (`self`) points to the
|
7
9
|
# delegated object, which doesn't allow to refer to `self.class` to
|
@@ -15,13 +17,13 @@ module Eco::Language::Delegation
|
|
15
17
|
def included(base)
|
16
18
|
super
|
17
19
|
|
18
|
-
base.send :include,
|
19
|
-
base.extend
|
20
|
+
base.send :include, DelegatedClass
|
21
|
+
base.extend ClassMethods
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
25
|
module ClassMethods
|
24
|
-
def const_defined?(str, inherit = true, delegated: true)
|
26
|
+
def const_defined?(str, inherit = true, delegated: true) # rubocop:disable Style/OptionalBooleanParameter
|
25
27
|
return super(str, inherit) if super(str, inherit) || !delegated
|
26
28
|
return true if delegated_class&.const_defined?(str, inherit)
|
27
29
|
|
@@ -41,7 +43,7 @@ module Eco::Language::Delegation
|
|
41
43
|
const_get(str)
|
42
44
|
end
|
43
45
|
|
44
|
-
def constants(inherited = true, delegated: true)
|
46
|
+
def constants(inherited = true, delegated: true) # rubocop:disable Style/OptionalBooleanParameter
|
45
47
|
super(inherited).dup.tap do |consts|
|
46
48
|
next unless delegated && delegated_class
|
47
49
|
|
data/lib/eco/language/delegation/{const_lookup_hooks.rb → for_delegator/const_lookup_hooks.rb}
RENAMED
@@ -1,6 +1,8 @@
|
|
1
|
-
module Eco::Language::Delegation
|
1
|
+
module Eco::Language::Delegation::ForDelegator
|
2
2
|
# This approach only makes sense to be used in `Delegator` and related helpers.
|
3
|
-
# @note
|
3
|
+
# @note
|
4
|
+
# 1. It includes `ConstDelegator` (which includes `DelegatedClass`).
|
5
|
+
# 2. This class installs new instance methods.
|
4
6
|
# @note for each not inherited constant that isn't part of the delegated object's
|
5
7
|
# class, it will create a undercore/snake named instance method, to ease
|
6
8
|
# constant lookup througout chained/wrapped delegators.
|
@@ -11,22 +13,49 @@ module Eco::Language::Delegation
|
|
11
13
|
def included(base)
|
12
14
|
super
|
13
15
|
|
14
|
-
|
16
|
+
msg = 'Only to be included in Delegator subclasses (i.e. SimpleDelegator). '
|
17
|
+
msg << 'As this concern is just a work around to not being possible to fetch '
|
18
|
+
msg << 'the Decorator class, because self.class refers to the class of the '
|
19
|
+
msg << 'decorated object. Meaning that it is not necessary on other patterns.'
|
20
|
+
raise ArgumentError, msg unless base <= Delegator
|
15
21
|
|
16
|
-
base.
|
22
|
+
base.send :include, ConstDelegator
|
23
|
+
|
24
|
+
base.extend ClassMethods
|
17
25
|
base.send :install_constant_lookup_hooks
|
18
26
|
end
|
19
27
|
end
|
20
28
|
|
21
29
|
module ClassMethods
|
30
|
+
include Eco::Language::Strings::Underscore
|
31
|
+
|
32
|
+
# @note
|
33
|
+
# - We don't install const hook methods when `delegated_class` is (re)defined,
|
34
|
+
# because these hooks aim only the delegator (not the delegated).
|
35
|
+
# @todo **However**It is still to be seen if we should include this concern
|
36
|
+
# to the `delegated_class`, provided that const lookup methods are aligned
|
37
|
+
# (as it is a requirement).
|
38
|
+
# When `delegated_class` is **defined**, install const hook methods too it.
|
39
|
+
# def delegated_class(klass = nil)
|
40
|
+
# super.tap do
|
41
|
+
# next if klass.nil?
|
42
|
+
# next if klass < ConstLookupHooks
|
43
|
+
|
44
|
+
# klass.send :include, ConstLookupHooks
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
|
22
48
|
private
|
23
49
|
|
50
|
+
# When **inherited**, install const hook methods to the `subclass`.
|
24
51
|
def inherited(subclass)
|
25
52
|
super
|
26
53
|
|
27
54
|
subclass.send :install_constant_lookup_hooks
|
28
55
|
end
|
29
56
|
|
57
|
+
# When a constant is **added** install const hook methods to it.
|
58
|
+
# to the `subclass`.
|
30
59
|
def const_added(sym)
|
31
60
|
install_constant_lookup_hook(const: sym)
|
32
61
|
end
|
@@ -45,6 +74,7 @@ module Eco::Language::Delegation
|
|
45
74
|
end
|
46
75
|
end
|
47
76
|
|
77
|
+
# If `base` does NOT have the constant hook method, it defines it.
|
48
78
|
def install_constant_lookup_hook(base = self, const:)
|
49
79
|
meth_name = underscore(const).to_sym
|
50
80
|
return if instance_method?(base, name: meth_name, include_private: true)
|
@@ -58,23 +88,11 @@ module Eco::Language::Delegation
|
|
58
88
|
base.define_method(meth_name, &method_def)
|
59
89
|
end
|
60
90
|
|
61
|
-
|
62
|
-
|
63
|
-
return super if defined?(super)
|
64
|
-
return sym.to_s.dup unless /[A-Z-]|::/.match?(sym)
|
65
|
-
|
66
|
-
word = sym.to_s.gsub('::', '/')
|
67
|
-
word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, '_')
|
68
|
-
word.tr!('-', '_')
|
69
|
-
word.downcase!
|
70
|
-
word
|
71
|
-
end
|
72
|
-
|
73
|
-
def instance_method?(base = self, name:, include_private: false)
|
74
|
-
return true if base.method_defined?(name)
|
91
|
+
def instance_method?(base = self, name:, include_private: false, inherited: true)
|
92
|
+
return true if base.method_defined?(name, inherited)
|
75
93
|
return false unless include_private
|
76
94
|
|
77
|
-
base.private_instance_methods.include?(name.to_sym)
|
95
|
+
base.private_instance_methods(inherited).include?(name.to_sym)
|
78
96
|
end
|
79
97
|
end
|
80
98
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Eco::Language::Delegation
|
1
|
+
module Eco::Language::Delegation::ForDelegator
|
2
2
|
module DelegatedClass
|
3
3
|
class << self
|
4
4
|
private
|
@@ -6,7 +6,11 @@ module Eco::Language::Delegation
|
|
6
6
|
def included(base)
|
7
7
|
super
|
8
8
|
|
9
|
-
base.extend
|
9
|
+
base.extend Eco::Language::Klass::HelpersBuilt
|
10
|
+
base.extend ClassMethods
|
11
|
+
|
12
|
+
base.inheritable_class_vars :delegated_class
|
13
|
+
|
10
14
|
base.send :patch_class_compare
|
11
15
|
end
|
12
16
|
end
|
@@ -20,12 +24,6 @@ module Eco::Language::Delegation
|
|
20
24
|
|
21
25
|
private
|
22
26
|
|
23
|
-
def inherited(subclass)
|
24
|
-
super
|
25
|
-
|
26
|
-
subclass.instance_variable_set(:@delegated_class, @delegated_class)
|
27
|
-
end
|
28
|
-
|
29
27
|
def patch_class_compare(base = self)
|
30
28
|
%i[< <= ==].each do |op|
|
31
29
|
base.singleton_class.define_method(op) do |other|
|
@@ -40,17 +38,6 @@ module Eco::Language::Delegation
|
|
40
38
|
|
41
39
|
# Instance methods
|
42
40
|
|
43
|
-
# It allows to chain delegators, when the first parameter is an
|
44
|
-
# instance of `delegated_class`.
|
45
|
-
# @note it also allows to create an instance of `delegated_class`
|
46
|
-
# on initialization.
|
47
|
-
def initialize(*args, **kargs, &block)
|
48
|
-
obj = args.first
|
49
|
-
obj = new(*args, **kargs, &block) unless of_kind?(obj)
|
50
|
-
|
51
|
-
super(obj)
|
52
|
-
end
|
53
|
-
|
54
41
|
def delegated_class
|
55
42
|
self.class.delegated_class
|
56
43
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Eco::Language::Delegation
|
4
|
+
# Helpers to be included on native `Delegator` subclasses.
|
5
|
+
module ForDelegator
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require_relative 'for_delegator/delegated_class'
|
10
|
+
require_relative 'for_delegator/const_delegator'
|
11
|
+
require_relative 'for_delegator/const_lookup_hooks'
|
@@ -3,6 +3,9 @@ module Eco::Language
|
|
3
3
|
end
|
4
4
|
end
|
5
5
|
|
6
|
-
require_relative 'delegation/
|
7
|
-
require_relative 'delegation/
|
8
|
-
require_relative 'delegation/
|
6
|
+
require_relative 'delegation/for_delegator'
|
7
|
+
require_relative 'delegation/chainable_delegator'
|
8
|
+
require_relative 'delegation/delegating_missing_on_class'
|
9
|
+
require_relative 'delegation/delegating_missing'
|
10
|
+
require_relative 'delegation/delegating_missing_const'
|
11
|
+
require_relative 'delegation/delegating_blank'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Eco::Language::Klass
|
2
|
+
module Builder
|
3
|
+
include Resolver
|
4
|
+
include Naming
|
5
|
+
|
6
|
+
# If the class for `name` exists, it returns it. Otherwise it generates it.
|
7
|
+
# @param name [String, Symbol] the name of the new class
|
8
|
+
# @param inherits [Class] the parent class to _inherit_ from
|
9
|
+
# @param parent_space [String] parent namespace of the generated class, if not given: `self`
|
10
|
+
# @yield [child_class] configure the new class
|
11
|
+
# @yieldparam child_class [Class] the new class
|
12
|
+
# @return [Class] the new generated class
|
13
|
+
def new_class(name, inherits:, parent_space: nil)
|
14
|
+
name = name.to_sym.freeze
|
15
|
+
class_name = to_constant(name)
|
16
|
+
parent_space = parent_space ? resolve_class(parent_space) : self
|
17
|
+
full_class_name = "#{parent_space}::#{class_name}"
|
18
|
+
|
19
|
+
unless (target_class = resolve_class(full_class_name, exception: false))
|
20
|
+
target_class = Class.new(inherits)
|
21
|
+
parent_space.const_set class_name, target_class
|
22
|
+
end
|
23
|
+
|
24
|
+
target_class.tap do |klass|
|
25
|
+
yield(klass) if block_given?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Eco::Language::Klass
|
2
|
+
module Hierarchy
|
3
|
+
# Finds all child classes of the current class.
|
4
|
+
# @param parent_class [Class] the parent class we want to find children of.
|
5
|
+
# @param direct [Boolean] it will only include direct child classes.
|
6
|
+
# @param scope [nil, Array] to only look for descendants among the ones in `scope`.
|
7
|
+
# @return [Arrary<Class>] the child classes in hierarchy order.
|
8
|
+
def descendants(parent_class: self, direct: false, scope: nil)
|
9
|
+
scope ||= ObjectSpace.each_object(::Class)
|
10
|
+
return [] if scope.empty?
|
11
|
+
|
12
|
+
scope.select do |klass|
|
13
|
+
klass < parent_class
|
14
|
+
end.sort do |k_1, k_2|
|
15
|
+
next -1 if k_2 < k_1
|
16
|
+
next 1 if k_1 < k_2
|
17
|
+
0
|
18
|
+
end.tap do |siblings|
|
19
|
+
next unless direct
|
20
|
+
|
21
|
+
siblings.reject! do |si|
|
22
|
+
siblings.any? {|s| si < s}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param parent_class [Class] the parent class we want to find children of.
|
28
|
+
# @param direct [Boolean] it will only include direct child classes.
|
29
|
+
# @return [Boolean] `true` if the current class has child classes, and `false` otherwise.
|
30
|
+
def descendants?(parent_class: self, direct: false)
|
31
|
+
descendants(parent_class: parent_class, direct: direct).length.positive?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|