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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -5
  3. data/eco-helpers.gemspec +2 -1
  4. data/lib/eco/api/common/class_helpers.rb +1 -136
  5. data/lib/eco/api/common/loaders/base.rb +1 -1
  6. data/lib/eco/api/common/loaders/case_base.rb +1 -1
  7. data/lib/eco/api/common/loaders/config/cli.rb +1 -1
  8. data/lib/eco/api/common/loaders/config/session.rb +1 -1
  9. data/lib/eco/api/common/loaders/config/workflow.rb +1 -1
  10. data/lib/eco/api/common/loaders/config.rb +2 -5
  11. data/lib/eco/api/common/loaders/error_handler.rb +1 -1
  12. data/lib/eco/api/common/loaders/parser.rb +2 -2
  13. data/lib/eco/api/common/loaders/policy.rb +1 -1
  14. data/lib/eco/api/common/loaders/use_case/target_model.rb +1 -1
  15. data/lib/eco/api/common/loaders/use_case/type.rb +1 -1
  16. data/lib/eco/api/common/loaders/use_case.rb +1 -1
  17. data/lib/eco/api/common/people/default_parsers/archived_parser.rb +19 -0
  18. data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +4 -4
  19. data/lib/eco/api/common/people/default_parsers/date_parser.rb +3 -3
  20. data/lib/eco/api/common/people/default_parsers/freemium_parser.rb +6 -6
  21. data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +3 -3
  22. data/lib/eco/api/common/people/default_parsers/multi_parser.rb +4 -4
  23. data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +9 -6
  24. data/lib/eco/api/common/people/default_parsers/send_invites_parser.rb +6 -5
  25. data/lib/eco/api/common/people/default_parsers/xls_parser.rb +2 -2
  26. data/lib/eco/api/common/people/default_parsers.rb +1 -0
  27. data/lib/eco/api/common/people/entries.rb +16 -15
  28. data/lib/eco/api/common/people/person_entry.rb +53 -37
  29. data/lib/eco/api/common/people/person_parser.rb +8 -6
  30. data/lib/eco/api/common/people/supervisor_helpers.rb +3 -1
  31. data/lib/eco/api/common/session/logger/channels.rb +2 -1
  32. data/lib/eco/api/common/session/logger.rb +2 -2
  33. data/lib/eco/api/common/session/mailer/aws_provider.rb +3 -2
  34. data/lib/eco/api/common/session/mailer/provider_base.rb +2 -1
  35. data/lib/eco/api/common/session/mailer/sendgrid_provider.rb +9 -9
  36. data/lib/eco/api/common/session/mailer.rb +5 -3
  37. data/lib/eco/api/common/session/sftp.rb +11 -4
  38. data/lib/eco/api/common/version_patches/object.rb +2 -1
  39. data/lib/eco/api/custom/mailer.rb +1 -1
  40. data/lib/eco/api/error/handlers.rb +3 -3
  41. data/lib/eco/api/error.rb +17 -17
  42. data/lib/eco/api/microcases/people/apply_changes/set_account/account_excluded.rb +3 -11
  43. data/lib/eco/api/microcases/people/apply_changes/set_core/core_excluded.rb +4 -1
  44. data/lib/eco/api/microcases/people/manage/search.rb +3 -1
  45. data/lib/eco/api/organization/people.rb +1 -0
  46. data/lib/eco/api/policies.rb +2 -2
  47. data/lib/eco/api/session/batch/job/sets.rb +1 -0
  48. data/lib/eco/api/session/batch/job/type.rb +1 -0
  49. data/lib/eco/api/session/batch/job.rb +2 -2
  50. data/lib/eco/api/session/batch/launcher/valid_methods.rb +3 -2
  51. data/lib/eco/api/session/batch/launcher.rb +4 -5
  52. data/lib/eco/api/session/batch/searcher.rb +4 -4
  53. data/lib/eco/api/session/config/api.rb +2 -2
  54. data/lib/eco/api/session/config/apis/enviro_spaces.rb +2 -2
  55. data/lib/eco/api/session/config/post_launch.rb +7 -4
  56. data/lib/eco/api/session/config/sftp.rb +1 -1
  57. data/lib/eco/api/session/config/tagtree.rb +1 -1
  58. data/lib/eco/api/session/config/workflow.rb +4 -4
  59. data/lib/eco/api/session/config.rb +25 -24
  60. data/lib/eco/api/session.rb +4 -4
  61. data/lib/eco/api/usecases/base_case/model.rb +2 -1
  62. data/lib/eco/api/usecases/base_case/type.rb +2 -1
  63. data/lib/eco/api/usecases/base_io/validations.rb +6 -4
  64. data/lib/eco/api/usecases/cli/option.rb +5 -2
  65. data/lib/eco/api/usecases/default/people/amend/clear_abilities_case.rb +2 -2
  66. data/lib/eco/api/usecases/default/people/amend/restore_db_case.rb +16 -9
  67. data/lib/eco/api/usecases/default/people/treat/analyse_people_case.rb +20 -20
  68. data/lib/eco/api/usecases/default.rb +5 -5
  69. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +8 -8
  70. data/lib/eco/api/usecases/default_cases/upsert_case.rb +4 -4
  71. data/lib/eco/api/usecases/graphql/helpers/base.rb +1 -1
  72. data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +5 -1
  73. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +5 -3
  74. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +3 -3
  75. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +3 -0
  76. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +2 -2
  77. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +9 -3
  78. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +5 -4
  79. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing.rb +5 -2
  80. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +3 -3
  81. data/lib/eco/api/usecases/graphql/utils/sftp.rb +1 -1
  82. data/lib/eco/api/usecases/lib/{file_pattern.rb → files/file_pattern.rb} +1 -1
  83. data/lib/eco/api/usecases/lib/{sftp.rb → files/sftp.rb} +19 -7
  84. data/lib/eco/api/usecases/lib/files.rb +7 -0
  85. data/lib/eco/api/usecases/lib.rb +1 -2
  86. data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +5 -5
  87. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_ooze.rb +14 -11
  88. data/lib/eco/api/usecases/ooze_samples/helpers_migration/copying.rb +14 -23
  89. data/lib/eco/api/usecases/ooze_samples/helpers_migration/typed_fields_pairing.rb +49 -27
  90. data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +9 -9
  91. data/lib/eco/api/usecases/ooze_samples/ooze_run_base_case.rb +1 -1
  92. data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +25 -17
  93. data/lib/eco/api/usecases/ooze_samples/register_migration_case.rb +41 -24
  94. data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +16 -15
  95. data/lib/eco/api/usecases/samples/drivers/cli/sftp_cli.rb +15 -15
  96. data/lib/eco/api/usecases/samples/drivers/cli/url_pull_cli.rb +5 -5
  97. data/lib/eco/api/usecases/samples/drivers/sftp_sample.rb +5 -3
  98. data/lib/eco/api/usecases/use_case.rb +6 -6
  99. data/lib/eco/api/usecases/use_case_chain/chaining.rb +6 -6
  100. data/lib/eco/api/usecases/use_case_chain.rb +4 -4
  101. data/lib/eco/api/usecases/use_case_io.rb +2 -1
  102. data/lib/eco/api/usecases.rb +9 -9
  103. data/lib/eco/cli/config/options_set.rb +4 -4
  104. data/lib/eco/cli/config/use_cases.rb +3 -3
  105. data/lib/eco/cli/scripting/argument.rb +1 -1
  106. data/lib/eco/cli_default/input.rb +9 -9
  107. data/lib/eco/cli_default/options.rb +125 -100
  108. data/lib/eco/cli_default/people.rb +3 -3
  109. data/lib/eco/cli_default/usecases.rb +83 -83
  110. data/lib/eco/cli_default/workflow.rb +7 -7
  111. data/lib/eco/data/files/helpers.rb +7 -5
  112. data/lib/eco/data/fuzzy_match/result.rb +69 -26
  113. data/lib/eco/data/fuzzy_match/results.rb +10 -10
  114. data/lib/eco/data/fuzzy_match/score.rb +13 -8
  115. data/lib/eco/data/fuzzy_match.rb +69 -50
  116. data/lib/eco/data/hashes/diff_result/meta.rb +2 -1
  117. data/lib/eco/data/locations/node_base/treeify.rb +13 -11
  118. data/lib/eco/data/locations/node_diff/accessors.rb +2 -1
  119. data/lib/eco/data/mapper.rb +4 -4
  120. data/lib/eco/language/auxiliar_logger.rb +4 -4
  121. data/lib/eco/language/delegation/chainable_delegator.rb +18 -0
  122. data/lib/eco/language/delegation/delegating_missing.rb +104 -0
  123. data/lib/eco/language/delegation/delegating_missing_const.rb +53 -0
  124. data/lib/eco/language/delegation/delegating_missing_on_class.rb +53 -0
  125. data/lib/eco/language/delegation/for_delegator/const_delegator.rb +66 -0
  126. data/lib/eco/language/delegation/for_delegator/const_lookup_hooks.rb +99 -0
  127. data/lib/eco/language/delegation/for_delegator/delegated_class.rb +71 -0
  128. data/lib/eco/language/delegation/for_delegator.rb +11 -0
  129. data/lib/eco/language/delegation.rb +10 -0
  130. data/lib/eco/language/klass/builder.rb +29 -0
  131. data/lib/eco/language/klass/helpers_built.rb +9 -0
  132. data/lib/eco/language/klass/hierarchy.rb +34 -0
  133. data/lib/eco/language/klass/inheritable_class_vars.rb +45 -0
  134. data/lib/eco/language/klass/naming.rb +21 -0
  135. data/lib/eco/language/klass/resolver.rb +30 -0
  136. data/lib/eco/language/klass/when_inherited.rb +11 -13
  137. data/lib/eco/language/klass.rb +6 -0
  138. data/lib/eco/language/methods.rb +0 -1
  139. data/lib/eco/language/models/class_helpers.rb +25 -23
  140. data/lib/eco/language/models/collection.rb +12 -2
  141. data/lib/eco/language/strings/underscore.rb +17 -0
  142. data/lib/eco/language/strings.rb +8 -0
  143. data/lib/eco/language.rb +2 -0
  144. data/lib/eco/version.rb +1 -1
  145. metadata +39 -7
  146. 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,9 @@
1
+ module Eco::Language::Klass
2
+ module HelpersBuilt
3
+ include Resolver
4
+ include Naming
5
+ include Builder
6
+ include Hierarchy
7
+ include InheritableClassVars
8
+ end
9
+ 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 Language
3
- module Klass
4
- module WhenInherited
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
- def when_inherited(&block)
11
- return @when_inherited unless block_given?
12
- @when_inherited = block
13
- end
14
- end
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
@@ -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'
@@ -6,5 +6,4 @@ module Eco
6
6
  end
7
7
 
8
8
  require_relative 'methods/call_detector'
9
- require_relative 'methods/delegate_missing'
10
9
  require_relative 'methods/dsl_able'
@@ -2,7 +2,7 @@ module Eco
2
2
  module Language
3
3
  module Models
4
4
  module ClassHelpers
5
- NOT_USED = "no_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
- when Class
29
- klass
30
- when String
31
- begin
32
- Kernel.const_get(klass)
33
- rescue NameError => e
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
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
- str_name = key.to_s.strip.split(/::/).compact.map do |str|
51
+ key.to_s.strip.split('::').compact.map do |str|
52
52
  str.slice(0).upcase + str.slice(1..-1)
53
- end.join("").split(/[\-\_ :]+/i).compact.map do |str|
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 nil unless obj && attr
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