Skip to content

Commit

Permalink
refactor(core): reorganize feature envy classes
Browse files Browse the repository at this point in the history
  • Loading branch information
marian13 committed Oct 30, 2022
1 parent ba3d5ca commit cd6ce74
Show file tree
Hide file tree
Showing 18 changed files with 797 additions and 536 deletions.
1 change: 1 addition & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@
| High | 🚧 | Factories for POROs ❗❗❗ | |
| High | 🚧 | Resolve warning during specs | |
| Low | 🚧 | Consider to change `delegate` backend | |
| Middle | 🚧 | Same order of attr macros, delegators, initialize, class methods, attr methods, queries, actions, to_*, comparison, inspect | |
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def defined?
# @return [Boolean]
#
def super_method_defined?
caller.super_method_defined?(method)
container.super_method_defined?(method)
end

##
Expand All @@ -68,7 +68,7 @@ def configure(&configuration_block)
# @return [Boolean]
#
def define!
container.define_method_middlewares_caller!(scope, method)
caller.define_method_middlewares_caller!(scope, method, container)
end

##
Expand All @@ -95,7 +95,7 @@ def call(env, original_method)
def resolve_super_method(entity)
klass.commit_config!

caller.resolve_super_method(method, entity)
container.resolve_super_method(method, entity)
end

##
Expand Down Expand Up @@ -158,7 +158,7 @@ def stack
# @return [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Caller]
#
def caller
@caller ||= Entities::Caller.new(container: container)
@caller ||= Entities::Caller.cast!({scope: scope})
end

##
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# frozen_string_literal: true

require_relative "caller/commands"
require_relative "caller/concern"

module ConvenientService
module Core
module Entities
Expand All @@ -8,144 +11,10 @@ module Entities
class MethodMiddlewares
module Entities
class Caller
include Support::Delegate

##
# @!attribute [r] container
# @return [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Container]
#
attr_reader :container

##
# @return [Class]
#
delegate :klass, to: :container

##
# @return [Module, nil]
#
delegate :methods_middlewares_callers, to: :container

##
# @return [Array<Class, Module>]
#
delegate :ancestors, to: :klass

##
# @param container [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Container]
# @return [void]
#
def initialize(container:)
@container = container
end

##
# @param method_name [Symbol, String]
# @return [UnboundMethod, nil]
#
def super_method_defined?(method_name)
Utils::Bool.to_bool(resolve_unbound_super_method(method_name))
end

##
# Returns ancestors before the `methods_middlewares_callers` module. See the example below.
# When ancestors do NOT contain `methods_middlewares_callers`, returns an empty array.
#
# @return [Array<Class, Module>]
#
# @example The entity is an object.
# class Service
# include ConvenientService::Core
#
# middlewares :result do
# use ConvenientService::Plugins::Common::NormalizesEnv
# end
# end
#
# entity = Service.new
#
# entity.class.ancestors
# # [Service::InstanceMethodsMiddlewaresCallers, Service, ConvenientService::Core::InstanceMethods, ConvenientService::Core, ConvenientService::Support::Concern, Object, Kernel, BasicObject]
#
# ancestors_greater_than_methods_middlewares_callers # For the entity defined above.
# # [Service, ConvenientService::Core::InstanceMethods, ConvenientService::Core, ConvenientService::Support::Concern, Object, Kernel, BasicObject]
#
# @example The entity is class.
# class Service
# include ConvenientService::Core
#
# middlewares :result, scope: :class do
# use ConvenientService::Plugins::Common::NormalizesEnv
# end
# end
#
# entity = Service
#
# entity.singleton_class.ancestors
# # [Service::ClassMethodsMiddlewaresCallers, #<Class:Service>, ConvenientService::Core::ClassMethods, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
#
# ancestors_greater_than_methods_middlewares_callers # For the entity defined above.
# # [#<Class:Service>, ConvenientService::Core::ClassMethods, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]# [Service::ClassMethodsMiddlewaresCallers, #<Class:Service>, ConvenientService::Core::ClassMethods, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
#
# @note Returns empty array when `methods_middlewares_callers` are NOT prepended.
# @note If you expect to receive not empty array, make sure the config is committed.
#
# @internal
# NOTE: greater than -> futher in the inheritance chain than
# https://ruby-doc.org/core-2.7.0/Module.html#method-i-3E
#
# NOTE: lower than -> closer in the inheritance chain than
# https://ruby-doc.org/core-2.7.0/Module.html#method-i-ancestors
#
def ancestors_greater_than_methods_middlewares_callers
Utils::Array.keep_after(ancestors, methods_middlewares_callers)
end

##
# @param method_name [Symbol, String]
# @return [UnboundMethod, nil]
#
# @note Returns `nil` when `methods_middlewares_callers` are NOT prepended.
# @note If you expect to receive not `nil`, make sure the config is committed.
#
def resolve_unbound_super_method(method_name)
Utils::Array.find_yield(ancestors_greater_than_methods_middlewares_callers) { |ancestor| Utils::Module.get_own_instance_method(ancestor, method_name, private: true) }
end

##
# @param method_name [Symbol, String]
# @param entity [Class, Object]
# @return [Method, nil]
#
# @note Returns `nil` when `methods_middlewares_callers` are NOT prepended.
# @note If you expect to receive not `nil`, make sure the config is committed.
#
def resolve_super_method(method_name, entity)
unbound_super_method = resolve_unbound_super_method(method_name)

return unless unbound_super_method

unbound_super_method.bind(entity)
end

##
# @param other [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Callers::Base, Object]
# @return [Boolean]
#
def ==(other)
return unless other.instance_of?(self.class)

return false if container != other.container

true
end
INSTANCE_PREFIX = "self.class."
CLASS_PREFIX = ""

##
# @return [Hash]
#
def to_kwargs
{container: container}
end
include Concern
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

require_relative "commands/cast_caller"
require_relative "commands/define_method_middlewares_caller"
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

module ConvenientService
module Core
module Entities
class Config
module Entities
class MethodMiddlewares
module Entities
class Caller
module Commands
class CastCaller < Support::Command
##
# @!attribute [r] other
# @return [Hash]
#
attr_reader :other

##
# @param other [Hash, ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Caller]
# @return [void]
#
def initialize(other:)
@other = other
end

##
# @return [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Caller, nil]
#
def call
case other
when ::Hash
cast_hash(other)
when Caller
cast_caller(other)
end
end

private

##
# @param hash [Hash]
# @return [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Caller, nil]
#
def cast_hash(hash)
return unless hash[:scope]

case hash[:scope]
when :instance
Caller.new(prefix: Caller::INSTANCE_PREFIX)
when :class
Caller.new(prefix: Caller::CLASS_PREFIX)
end
end

##
# @param caller [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Caller]
# @return [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Caller]
#
def cast_caller(caller)
caller.copy
end
end
end
end
end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Config
module Entities
class MethodMiddlewares
module Entities
class Container
class Caller
module Commands
class DefineMethodMiddlewaresCaller < Support::Command
include Support::Delegate
Expand All @@ -30,6 +30,12 @@ class DefineMethodMiddlewaresCaller < Support::Command
#
attr_reader :container

##
# @!attribute [r] caller
# @return [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Caller]
#
attr_reader :caller

##
# @return [void]
#
Expand All @@ -41,14 +47,22 @@ class DefineMethodMiddlewaresCaller < Support::Command
delegate :methods_middlewares_callers, to: :container

##
# @return [String]
#
delegate :prefix, to: :caller

##
# @param scope [:instance, :class]
# @param method [String, Symbol]
# @param container [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Container]
# @param caller [ConvenientService::Core::Entities::Config::Entities::MethodMiddlewares::Entities::Caller]
# @return [void]
#
def initialize(scope:, method:, container:)
def initialize(scope:, method:, container:, caller:)
@scope = scope
@method = method
@container = container
@caller = caller
end

##
Expand All @@ -70,7 +84,10 @@ def call
# @return [void]
#
# @internal
# NOTE: Assignment of `scope` and `method` in the beginning for easier debugging.
# NOTE: Make assignment of `scope`, `method`, `prefix` in the beginning for easier debugging. For example:
# method = :#{method}
# method = :#{scope}
# prefix = "#{prefix}"
#
# NOTE: Check the following link in order to get an idea why two versions of `define_method_middlewares_caller` exist.
# https://gist.github.com/marian13/9c25041f835564e945d978839097d419
Expand All @@ -79,10 +96,7 @@ def call
def define_method_middlewares_caller
<<~RUBY.tap { |code| methods_middlewares_callers.module_eval(code, __FILE__, __LINE__ + 1) }
def #{method}(*args, **kwargs, &block)
method = :#{method}
scope = :#{scope}
method_middlewares = middlewares(method, scope: scope)
method_middlewares = #{prefix}middlewares(:#{method}, scope: :#{scope})
env = {args: args, kwargs: kwargs, block: block, entity: self}
original_method = proc { |env| super(*env[:args], **env[:kwargs], &env[:block]) }
Expand All @@ -95,10 +109,7 @@ def #{method}(*args, **kwargs, &block)
def define_method_middlewares_caller
<<~RUBY.tap { |code| methods_middlewares_callers.module_eval(code, __FILE__, __LINE__ + 1) }
def #{method}(*args, **kwargs, &block)
method = :#{method}
scope = :#{scope}
method_middlewares = middlewares(method, scope: scope)
method_middlewares = #{prefix}middlewares(:#{method}, scope: :#{scope})
env = {args: args, kwargs: kwargs, block: block, entity: self}
super_method = method_middlewares.resolve_super_method(self)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require_relative "concern/instance_methods"
require_relative "concern/class_methods"

module ConvenientService
module Core
module Entities
class Config
module Entities
class MethodMiddlewares
module Entities
class Caller
module Concern
include Support::Concern

included do |container_klass|
container_klass.include Support::Castable

container_klass.include InstanceMethods

container_klass.extend ClassMethods
end
end
end
end
end
end
end
end
end
end
Loading

0 comments on commit cd6ce74

Please sign in to comment.