eco-helpers 3.0.26 → 3.0.28
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 +76 -5
- data/eco-helpers.gemspec +2 -1
- data/lib/eco/api/common/class_helpers.rb +1 -136
- data/lib/eco/api/common/loaders/base.rb +1 -1
- data/lib/eco/api/common/loaders/case_base.rb +1 -1
- 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/error_handler.rb +1 -1
- data/lib/eco/api/common/loaders/parser.rb +2 -2
- data/lib/eco/api/common/loaders/policy.rb +1 -1
- 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/loaders/use_case.rb +1 -1
- data/lib/eco/api/common/people/default_parsers/archived_parser.rb +19 -0
- data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +4 -4
- data/lib/eco/api/common/people/default_parsers/date_parser.rb +3 -3
- data/lib/eco/api/common/people/default_parsers/freemium_parser.rb +6 -6
- data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +3 -3
- data/lib/eco/api/common/people/default_parsers/multi_parser.rb +4 -4
- data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +9 -6
- data/lib/eco/api/common/people/default_parsers/send_invites_parser.rb +6 -5
- data/lib/eco/api/common/people/default_parsers/xls_parser.rb +2 -2
- data/lib/eco/api/common/people/default_parsers.rb +1 -0
- data/lib/eco/api/common/people/entries.rb +16 -15
- data/lib/eco/api/common/people/person_entry.rb +53 -37
- data/lib/eco/api/common/people/person_parser.rb +8 -6
- data/lib/eco/api/common/people/supervisor_helpers.rb +3 -1
- data/lib/eco/api/common/session/logger/channels.rb +2 -1
- data/lib/eco/api/common/session/logger.rb +2 -2
- data/lib/eco/api/common/session/mailer/aws_provider.rb +3 -2
- data/lib/eco/api/common/session/mailer/provider_base.rb +2 -1
- data/lib/eco/api/common/session/mailer/sendgrid_provider.rb +9 -9
- data/lib/eco/api/common/session/mailer.rb +5 -3
- data/lib/eco/api/common/session/sftp.rb +11 -4
- data/lib/eco/api/common/version_patches/object.rb +2 -1
- data/lib/eco/api/custom/mailer.rb +1 -1
- data/lib/eco/api/error/handlers.rb +3 -3
- data/lib/eco/api/error.rb +17 -17
- data/lib/eco/api/microcases/people/apply_changes/set_account/account_excluded.rb +3 -11
- data/lib/eco/api/microcases/people/apply_changes/set_core/core_excluded.rb +4 -1
- data/lib/eco/api/microcases/people/manage/search.rb +3 -1
- data/lib/eco/api/organization/people.rb +1 -0
- data/lib/eco/api/policies.rb +2 -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/job.rb +2 -2
- data/lib/eco/api/session/batch/launcher/valid_methods.rb +3 -2
- data/lib/eco/api/session/batch/launcher.rb +4 -5
- data/lib/eco/api/session/batch/searcher.rb +4 -4
- data/lib/eco/api/session/config/api.rb +2 -2
- data/lib/eco/api/session/config/apis/enviro_spaces.rb +2 -2
- data/lib/eco/api/session/config/post_launch.rb +7 -4
- data/lib/eco/api/session/config/sftp.rb +1 -1
- data/lib/eco/api/session/config/tagtree.rb +1 -1
- data/lib/eco/api/session/config/workflow.rb +4 -4
- data/lib/eco/api/session/config.rb +25 -24
- data/lib/eco/api/session.rb +4 -4
- 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 +6 -4
- data/lib/eco/api/usecases/cli/option.rb +5 -2
- data/lib/eco/api/usecases/default/people/amend/clear_abilities_case.rb +2 -2
- data/lib/eco/api/usecases/default/people/amend/restore_db_case.rb +16 -9
- data/lib/eco/api/usecases/default/people/treat/analyse_people_case.rb +20 -20
- data/lib/eco/api/usecases/default.rb +5 -5
- data/lib/eco/api/usecases/default_cases/to_csv_case.rb +8 -8
- data/lib/eco/api/usecases/default_cases/upsert_case.rb +4 -4
- data/lib/eco/api/usecases/graphql/helpers/base.rb +1 -1
- data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +5 -1
- data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +5 -3
- data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +3 -3
- data/lib/eco/api/usecases/graphql/helpers/location/command.rb +3 -0
- data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +2 -2
- data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +9 -3
- data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +5 -4
- data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing.rb +5 -2
- data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +3 -3
- data/lib/eco/api/usecases/graphql/utils/sftp.rb +1 -1
- data/lib/eco/api/usecases/lib/{file_pattern.rb → files/file_pattern.rb} +1 -1
- data/lib/eco/api/usecases/lib/{sftp.rb → files/sftp.rb} +19 -7
- data/lib/eco/api/usecases/lib/files.rb +7 -0
- data/lib/eco/api/usecases/lib.rb +1 -2
- data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +5 -5
- data/lib/eco/api/usecases/ooze_samples/helpers/exportable_ooze.rb +14 -11
- data/lib/eco/api/usecases/ooze_samples/helpers_migration/copying.rb +14 -23
- data/lib/eco/api/usecases/ooze_samples/helpers_migration/typed_fields_pairing.rb +49 -27
- data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +9 -9
- data/lib/eco/api/usecases/ooze_samples/ooze_run_base_case.rb +1 -1
- data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +25 -17
- data/lib/eco/api/usecases/ooze_samples/register_migration_case.rb +41 -24
- data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +16 -15
- data/lib/eco/api/usecases/samples/drivers/cli/sftp_cli.rb +15 -15
- data/lib/eco/api/usecases/samples/drivers/cli/url_pull_cli.rb +5 -5
- data/lib/eco/api/usecases/samples/drivers/sftp_sample.rb +5 -3
- data/lib/eco/api/usecases/use_case.rb +6 -6
- data/lib/eco/api/usecases/use_case_chain/chaining.rb +6 -6
- data/lib/eco/api/usecases/use_case_chain.rb +4 -4
- data/lib/eco/api/usecases/use_case_io.rb +2 -1
- data/lib/eco/api/usecases.rb +9 -9
- data/lib/eco/cli/config/options_set.rb +4 -4
- data/lib/eco/cli/config/use_cases.rb +3 -3
- data/lib/eco/cli/scripting/argument.rb +1 -1
- data/lib/eco/cli_default/input.rb +9 -9
- data/lib/eco/cli_default/options.rb +125 -100
- data/lib/eco/cli_default/people.rb +3 -3
- data/lib/eco/cli_default/usecases.rb +83 -83
- data/lib/eco/cli_default/workflow.rb +7 -7
- data/lib/eco/data/files/helpers.rb +7 -5
- data/lib/eco/data/fuzzy_match/result.rb +69 -26
- data/lib/eco/data/fuzzy_match/results.rb +10 -10
- data/lib/eco/data/fuzzy_match/score.rb +13 -8
- data/lib/eco/data/fuzzy_match.rb +69 -50
- data/lib/eco/data/hashes/diff_result/meta.rb +2 -1
- data/lib/eco/data/locations/node_base/treeify.rb +13 -11
- data/lib/eco/data/locations/node_diff/accessors.rb +2 -1
- data/lib/eco/data/mapper.rb +4 -4
- data/lib/eco/language/auxiliar_logger.rb +4 -4
- data/lib/eco/language/delegation/chainable_delegator.rb +18 -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/for_delegator/const_delegator.rb +66 -0
- data/lib/eco/language/delegation/for_delegator/const_lookup_hooks.rb +99 -0
- data/lib/eco/language/delegation/for_delegator/delegated_class.rb +71 -0
- data/lib/eco/language/delegation/for_delegator.rb +11 -0
- data/lib/eco/language/delegation.rb +10 -0
- 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 +11 -13
- data/lib/eco/language/klass.rb +6 -0
- data/lib/eco/language/methods.rb +0 -1
- data/lib/eco/language/models/class_helpers.rb +25 -23
- data/lib/eco/language/models/collection.rb +12 -2
- data/lib/eco/language/strings/underscore.rb +17 -0
- data/lib/eco/language/strings.rb +8 -0
- data/lib/eco/language.rb +2 -0
- data/lib/eco/version.rb +1 -1
- metadata +39 -7
- data/lib/eco/language/methods/delegate_missing.rb +0 -29
@@ -0,0 +1,66 @@
|
|
1
|
+
module Eco::Language::Delegation::ForDelegator
|
2
|
+
# Module that is to be used with ruby `Delegator` or related helpers,
|
3
|
+
# such as `SimpleDelegator` or `Forwardable`.
|
4
|
+
# @note
|
5
|
+
# 1. It includes `DelegatedClass`
|
6
|
+
# 2. This class only provides lookups integration.
|
7
|
+
# @note it looks up the constant via `delegated_class`. This is because
|
8
|
+
# on `Delegator` approaches, the intance object (`self`) points to the
|
9
|
+
# delegated object, which doesn't allow to refer to `self.class` to
|
10
|
+
# implement a delegation of constants that other way.
|
11
|
+
# For example, `self.class::CONSTANT` will not point to the delegator
|
12
|
+
# class constant, but to the constant of the delegated object's class.
|
13
|
+
module ConstDelegator
|
14
|
+
class << self
|
15
|
+
private
|
16
|
+
|
17
|
+
def included(base)
|
18
|
+
super
|
19
|
+
|
20
|
+
base.send :include, DelegatedClass
|
21
|
+
base.extend ClassMethods
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def const_defined?(str, inherit = true, delegated: true) # rubocop:disable Style/OptionalBooleanParameter
|
27
|
+
return super(str, inherit) if super(str, inherit) || !delegated
|
28
|
+
return true if delegated_class&.const_defined?(str, inherit)
|
29
|
+
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def const_get(str)
|
34
|
+
return super if const_defined?(str, delegated: false)
|
35
|
+
return delegated_class.const_get(str) if delegated_class&.const_defined?(str)
|
36
|
+
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def const_missing(str)
|
41
|
+
return super unless const_defined?(str)
|
42
|
+
|
43
|
+
const_get(str)
|
44
|
+
end
|
45
|
+
|
46
|
+
def constants(inherited = true, delegated: true) # rubocop:disable Style/OptionalBooleanParameter
|
47
|
+
super(inherited).dup.tap do |consts|
|
48
|
+
next unless delegated && delegated_class
|
49
|
+
|
50
|
+
delegated_class.constants(inherited).each do |const|
|
51
|
+
consts.unshift(const)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def remove_const(str)
|
59
|
+
return super unless const_defined?(str)
|
60
|
+
return super if const_defined?(str, delegated: false)
|
61
|
+
|
62
|
+
raise NameError, "#{str} not defined (is delegated to #{delegated_class})"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Eco::Language::Delegation::ForDelegator
|
2
|
+
# This approach only makes sense to be used in `Delegator` and related helpers.
|
3
|
+
# @note
|
4
|
+
# 1. It includes `ConstDelegator` (which includes `DelegatedClass`).
|
5
|
+
# 2. This class installs new instance methods.
|
6
|
+
# @note for each not inherited constant that isn't part of the delegated object's
|
7
|
+
# class, it will create a undercore/snake named instance method, to ease
|
8
|
+
# constant lookup througout chained/wrapped delegators.
|
9
|
+
module ConstLookupHooks
|
10
|
+
class << self
|
11
|
+
private
|
12
|
+
|
13
|
+
def included(base)
|
14
|
+
super
|
15
|
+
|
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
|
21
|
+
|
22
|
+
base.send :include, ConstDelegator
|
23
|
+
|
24
|
+
base.extend ClassMethods
|
25
|
+
base.send :install_constant_lookup_hooks
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
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
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# When **inherited**, install const hook methods to the `subclass`.
|
51
|
+
def inherited(subclass)
|
52
|
+
super
|
53
|
+
|
54
|
+
subclass.send :install_constant_lookup_hooks
|
55
|
+
end
|
56
|
+
|
57
|
+
# When a constant is **added** install const hook methods to it.
|
58
|
+
# to the `subclass`.
|
59
|
+
def const_added(sym)
|
60
|
+
install_constant_lookup_hook(const: sym)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Drawback is this will also create methods for modules and classes hanging
|
64
|
+
# from `base` namespace. Unfortunatelly, we need to go with them, because
|
65
|
+
# it might happen a constant points to one of them to implement some behaviour.
|
66
|
+
def install_constant_lookup_hooks(base = self)
|
67
|
+
# Only method-hook constants that are not inherited,
|
68
|
+
# nor delegated to the decorated class.
|
69
|
+
# This constraint ensure that we don't overpopulate with hook methods,
|
70
|
+
# and that the lookup respects the order in which the decorators are
|
71
|
+
# wrapped (i.e. Sugar.new(Milk.new(Coffe.new)) < Sugar(Milk(Coffee)) ).
|
72
|
+
base.constants(false, delegated: false).each do |const|
|
73
|
+
install_constant_lookup_hook(base, const: const)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# If `base` does NOT have the constant hook method, it defines it.
|
78
|
+
def install_constant_lookup_hook(base = self, const:)
|
79
|
+
meth_name = underscore(const).to_sym
|
80
|
+
return if instance_method?(base, name: meth_name, include_private: true)
|
81
|
+
|
82
|
+
method_def = proc do |*args, **kargs, &block|
|
83
|
+
return super(*args, **kargs, &block) if defined?(super)
|
84
|
+
|
85
|
+
base.const_get(const)
|
86
|
+
end
|
87
|
+
|
88
|
+
base.define_method(meth_name, &method_def)
|
89
|
+
end
|
90
|
+
|
91
|
+
def instance_method?(base = self, name:, include_private: false)
|
92
|
+
return true if base.method_defined?(name)
|
93
|
+
return false unless include_private
|
94
|
+
|
95
|
+
base.private_instance_methods.include?(name.to_sym)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Eco::Language::Delegation::ForDelegator
|
2
|
+
module DelegatedClass
|
3
|
+
class << self
|
4
|
+
private
|
5
|
+
|
6
|
+
def included(base)
|
7
|
+
super
|
8
|
+
|
9
|
+
base.extend Eco::Language::Klass::HelpersBuilt
|
10
|
+
base.extend ClassMethods
|
11
|
+
|
12
|
+
base.inheritable_class_vars :delegated_class
|
13
|
+
|
14
|
+
base.send :patch_class_compare
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def delegated_class(klass = nil)
|
20
|
+
return @delegated_class if klass.nil?
|
21
|
+
|
22
|
+
@delegated_class = klass
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def patch_class_compare(base = self)
|
28
|
+
%i[< <= ==].each do |op|
|
29
|
+
base.singleton_class.define_method(op) do |other|
|
30
|
+
return super(other) unless delegated_class
|
31
|
+
return true if super(other)
|
32
|
+
|
33
|
+
delegated_class.send(op, other)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Instance methods
|
40
|
+
|
41
|
+
def delegated_class
|
42
|
+
self.class.delegated_class
|
43
|
+
end
|
44
|
+
|
45
|
+
def new(...)
|
46
|
+
delegated_class.new(...)
|
47
|
+
end
|
48
|
+
|
49
|
+
def of_kind?(obj)
|
50
|
+
return true if obj.is_a?(delegated_class)
|
51
|
+
return false unless obj.is_a(Delegator)
|
52
|
+
return true if obj.__get_obj__.is_a?(delegated_class)
|
53
|
+
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def is_a?(base)
|
58
|
+
return true if super
|
59
|
+
return false unless delegated_class
|
60
|
+
return true if delegated_class <= base
|
61
|
+
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def instance_of?(base)
|
66
|
+
return true if base == delegated_class
|
67
|
+
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
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'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Eco::Language
|
2
|
+
module Delegation
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
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'
|
@@ -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
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Eco::Language::Klass
|
2
|
+
module InheritableClassVars
|
3
|
+
include Naming
|
4
|
+
|
5
|
+
# Keeps track on class instance variables that should be inherited by child classes.
|
6
|
+
# @note
|
7
|
+
# - subclasses will inherit the value as is at that moment
|
8
|
+
# - any change afterwards will be only on the specific class (in line with class instance variables)
|
9
|
+
# - adapted from https://stackoverflow.com/a/10729812/4352306
|
10
|
+
# TODO: this separates the logic of the method to the instance var.
|
11
|
+
# Think if would be possible to join them somehow.
|
12
|
+
def inheritable_class_vars(*vars)
|
13
|
+
@inheritable_class_vars ||= [:inheritable_class_vars]
|
14
|
+
@inheritable_class_vars += vars
|
15
|
+
end
|
16
|
+
|
17
|
+
# Builds the attr_reader and attr_writer of `attrs` and registers
|
18
|
+
# the associated instance variable as inheritable.
|
19
|
+
def inheritable_attrs(*attrs)
|
20
|
+
attrs.each do |attr|
|
21
|
+
class_eval %(
|
22
|
+
class << self; attr_accessor :#{attr} end
|
23
|
+
), __FILE__, __LINE__ - 2
|
24
|
+
end
|
25
|
+
|
26
|
+
inheritable_class_vars(*attrs)
|
27
|
+
end
|
28
|
+
|
29
|
+
# This callback method is called whenever a subclass of the current class is created.
|
30
|
+
# @note
|
31
|
+
# - values of the instance variables are copied as they are (no dups or clones)
|
32
|
+
# - the above means: avoid methods that change the state of the mutable object on it
|
33
|
+
# - mutating methods would reflect the changes on other classes as well
|
34
|
+
# - therefore, `freeze` will be called on the values that are inherited.
|
35
|
+
def inherited(subclass)
|
36
|
+
super
|
37
|
+
|
38
|
+
inheritable_class_vars.each do |var|
|
39
|
+
instance_var = instance_variable_name(var)
|
40
|
+
value = instance_variable_get(instance_var)
|
41
|
+
subclass.instance_variable_set(instance_var, value.freeze)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Eco::Language::Klass
|
2
|
+
module Naming
|
3
|
+
# Helper to normalize `key` into a correct `ruby` **constant name**
|
4
|
+
# @param key [String, Symbol] to be normalized
|
5
|
+
# @return [String] a correct constant name
|
6
|
+
def to_constant(key)
|
7
|
+
key.to_s.strip.split(/[\-\_ ]/i).compact.map do |str|
|
8
|
+
str.slice(0).upcase + str.slice(1..-1).downcase
|
9
|
+
end.join
|
10
|
+
end
|
11
|
+
|
12
|
+
# Helper to create an instance variable `name`
|
13
|
+
# @param [String, Symbol] the name of the variable
|
14
|
+
# @reutrn [String] the name of the created instance variable
|
15
|
+
def instance_variable_name(name)
|
16
|
+
str = name.to_s
|
17
|
+
str = "@#{str}" unless str.start_with?("@")
|
18
|
+
str
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Eco::Language::Klass
|
2
|
+
module Resolver
|
3
|
+
# Creates a class and instance object methods with name `name` to resolve `klass` name
|
4
|
+
def class_resolver(name, klass)
|
5
|
+
define_singleton_method(name) { resolve_class(klass) }
|
6
|
+
define_method(name) { self.class.resolve_class(klass) }
|
7
|
+
end
|
8
|
+
|
9
|
+
# With given a `klass` name it resolves to an actual `Class`
|
10
|
+
# @return [Class] the class that was being searched by name `klass`.
|
11
|
+
def resolve_class(klass, exception: true)
|
12
|
+
@resolved ||= {}
|
13
|
+
@resolved[klass] ||=
|
14
|
+
case klass
|
15
|
+
when Class
|
16
|
+
klass
|
17
|
+
when String
|
18
|
+
begin
|
19
|
+
Kernel.const_get(klass)
|
20
|
+
rescue NameError
|
21
|
+
raise if exception
|
22
|
+
end
|
23
|
+
when Symbol
|
24
|
+
resolve_class(send(klass))
|
25
|
+
else
|
26
|
+
raise "Unknown class: #{klass}" if exception
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,17 +1,15 @@
|
|
1
|
-
module Eco
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
def inherited(subclass)
|
6
|
-
super
|
7
|
-
subclass.instance_exec(&when_inherited)
|
8
|
-
end
|
1
|
+
module Eco::Language::Klass
|
2
|
+
module WhenInherited
|
3
|
+
def inherited(subclass)
|
4
|
+
super
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
subclass.instance_exec(&when_inherited)
|
7
|
+
end
|
8
|
+
|
9
|
+
def when_inherited(&block)
|
10
|
+
return @when_inherited unless block_given?
|
11
|
+
|
12
|
+
@when_inherited = block
|
15
13
|
end
|
16
14
|
end
|
17
15
|
end
|
data/lib/eco/language/klass.rb
CHANGED
@@ -5,4 +5,10 @@ module Eco
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
require_relative 'klass/resolver'
|
9
|
+
require_relative 'klass/naming'
|
10
|
+
require_relative 'klass/builder'
|
11
|
+
require_relative 'klass/hierarchy'
|
8
12
|
require_relative 'klass/when_inherited'
|
13
|
+
require_relative 'klass/inheritable_class_vars'
|
14
|
+
require_relative 'klass/helpers_built'
|
data/lib/eco/language/methods.rb
CHANGED
@@ -2,7 +2,7 @@ module Eco
|
|
2
2
|
module Language
|
3
3
|
module Models
|
4
4
|
module ClassHelpers
|
5
|
-
NOT_USED =
|
5
|
+
NOT_USED = 'no_used!'.freeze
|
6
6
|
|
7
7
|
def redef_without_warning(const, value)
|
8
8
|
self.class.send(:remove_const, const) if self.class.const_defined?(const)
|
@@ -22,24 +22,24 @@ module Eco
|
|
22
22
|
# @param exception [Boolean] if it should raise exception when could not resolve
|
23
23
|
# @return [Class] the `Class` constant
|
24
24
|
def resolve_class(klass, source_class: self, exception: true)
|
25
|
-
@resolved
|
25
|
+
@resolved ||= {}
|
26
26
|
@resolved[klass] ||=
|
27
27
|
case klass
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
28
|
+
when Class
|
29
|
+
klass
|
30
|
+
when String
|
31
|
+
begin
|
32
|
+
Kernel.const_get(klass)
|
33
|
+
rescue NameError
|
34
|
+
raise if exception
|
35
|
+
end
|
36
|
+
when Symbol
|
37
|
+
source_class.resolve_class(source_class.send(klass))
|
38
|
+
when Hash
|
39
|
+
referrer, referred = klass.first
|
40
|
+
resolve_class(referred, source_class: referrer, exception: exception)
|
41
|
+
else
|
42
|
+
raise "Unknown class: #{klass}" if exception
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -48,11 +48,11 @@ module Eco
|
|
48
48
|
# @param key [String, Symbol] to be normalized
|
49
49
|
# @return [String] a correct constant name
|
50
50
|
def to_constant(key)
|
51
|
-
|
51
|
+
key.to_s.strip.split('::').compact.map do |str|
|
52
52
|
str.slice(0).upcase + str.slice(1..-1)
|
53
|
-
end.join
|
53
|
+
end.join.split(/[\-\_ :]+/i).compact.map do |str|
|
54
54
|
str.slice(0).upcase + str.slice(1..-1)
|
55
|
-
end.join
|
55
|
+
end.join
|
56
56
|
end
|
57
57
|
|
58
58
|
# Helper to create an instance variable `name`
|
@@ -75,7 +75,7 @@ module Eco
|
|
75
75
|
name = name.to_s.to_sym.freeze
|
76
76
|
class_name = to_constant(name)
|
77
77
|
|
78
|
-
unless target_class = resolve_class("#{namespace}::#{class_name}", exception: false)
|
78
|
+
unless (target_class = resolve_class("#{namespace}::#{class_name}", exception: false))
|
79
79
|
target_class = Class.new(inherits)
|
80
80
|
Kernel.const_get(namespace.to_s).const_set class_name, target_class
|
81
81
|
end
|
@@ -108,9 +108,9 @@ module Eco
|
|
108
108
|
# Builds the attr_reader and attr_writer of `attrs` and registers the associated instance variable as inheritable.
|
109
109
|
def inheritable_attrs(*attrs)
|
110
110
|
attrs.each do |attr|
|
111
|
-
class_eval %(
|
111
|
+
class_eval %( # rubocop:disable Style/DocumentDynamicEvalDefinition
|
112
112
|
class << self; attr_accessor :#{attr} end
|
113
|
-
)
|
113
|
+
), __FILE__, __LINE__ - 2
|
114
114
|
end
|
115
115
|
inheritable_class_vars(*attrs)
|
116
116
|
end
|
@@ -122,6 +122,8 @@ module Eco
|
|
122
122
|
# - mutating methods would reflect the changes on other classes as well
|
123
123
|
# - therefore, `freeze` will be called on the values that are inherited.
|
124
124
|
def inherited(subclass)
|
125
|
+
super
|
126
|
+
|
125
127
|
inheritable_class_vars.each do |var|
|
126
128
|
instance_var = instance_variable_name(var)
|
127
129
|
value = instance_variable_get(instance_var)
|
@@ -22,11 +22,13 @@ module Eco
|
|
22
22
|
def attrs_create_method(attrs, method)
|
23
23
|
attrs.each do |attr|
|
24
24
|
attr = attr.to_s
|
25
|
+
|
25
26
|
if method.include?("attr")
|
26
27
|
attr_method = method.sub("attr", attr)
|
27
28
|
else
|
28
29
|
attr_method = "#{attr}_#{method}"
|
29
30
|
end
|
31
|
+
|
30
32
|
define_method attr_method do |*args|
|
31
33
|
send(method, attr, *args)
|
32
34
|
end
|
@@ -36,6 +38,7 @@ module Eco
|
|
36
38
|
|
37
39
|
def initialize(data = [], klass:, factory: nil, handy: Eco::Assets::Language.new)
|
38
40
|
raise "Raise klass required, given: #{klass}" unless klass
|
41
|
+
|
39
42
|
@klass = klass
|
40
43
|
@factory = factory
|
41
44
|
@handy = handy
|
@@ -70,6 +73,7 @@ module Eco
|
|
70
73
|
|
71
74
|
def each(&block)
|
72
75
|
return to_enum(:each) unless block
|
76
|
+
|
73
77
|
@items.each(&block)
|
74
78
|
end
|
75
79
|
|
@@ -104,6 +108,7 @@ module Eco
|
|
104
108
|
|
105
109
|
def attr(attr, value = true, modifier = default_modifier)
|
106
110
|
return present(attr, value) if boolean?(value)
|
111
|
+
|
107
112
|
select do |object|
|
108
113
|
match?(attr_value(object, attr), value, modifier)
|
109
114
|
end.then do |matching|
|
@@ -113,6 +118,7 @@ module Eco
|
|
113
118
|
|
114
119
|
def attr?(attr, value = true, modifier = default_modifier)
|
115
120
|
return present(attr, value).length == length if boolean?(value)
|
121
|
+
|
116
122
|
match?(attrs(attr), value, modifier.new.reverse)
|
117
123
|
end
|
118
124
|
|
@@ -130,6 +136,7 @@ module Eco
|
|
130
136
|
|
131
137
|
def group_by(attr = nil, &block)
|
132
138
|
return to_h(attr) if attr
|
139
|
+
|
133
140
|
to_a.group_by(&block) if block
|
134
141
|
end
|
135
142
|
|
@@ -138,6 +145,7 @@ module Eco
|
|
138
145
|
def to_h(attr, &block)
|
139
146
|
return to_a.group_by(&block) if block
|
140
147
|
raise "And attr or a block are required. Given attr: #{attr}" unless attr
|
148
|
+
|
141
149
|
to_a.group_by { |object| object.method(attr).call }
|
142
150
|
end
|
143
151
|
# @!endgroup
|
@@ -175,16 +183,18 @@ module Eco
|
|
175
183
|
private
|
176
184
|
|
177
185
|
def attr_value(obj, attr)
|
178
|
-
return
|
186
|
+
return unless obj && attr
|
187
|
+
|
179
188
|
if obj.is_a?(Hash)
|
180
189
|
obj[attr]
|
181
|
-
elsif obj.respond_to?(attr.to_sym)
|
190
|
+
elsif obj.respond_to?(attr.to_sym, true)
|
182
191
|
obj.send(attr)
|
183
192
|
end
|
184
193
|
end
|
185
194
|
|
186
195
|
def attr_value_present?(obj, attr)
|
187
196
|
return false unless (value = attr_value(obj, attr))
|
197
|
+
|
188
198
|
if value.is_a?(Enumerable)
|
189
199
|
value.count > 1
|
190
200
|
elsif value.is_a?(String)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Eco::Language::Strings
|
2
|
+
module Underscore
|
3
|
+
private
|
4
|
+
|
5
|
+
# @see https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-underscore
|
6
|
+
def underscore(sym)
|
7
|
+
return super if defined?(super)
|
8
|
+
return sym.to_s.dup unless /[A-Z-]|::/.match?(sym)
|
9
|
+
|
10
|
+
word = sym.to_s.gsub('::', '/')
|
11
|
+
word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, '_')
|
12
|
+
word.tr!('-', '_')
|
13
|
+
word.downcase!
|
14
|
+
word
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|