# frozen_string_literal: true require_relative "concerns/entities" module ConvenientService module Core module Entities class Config module Entities class Concerns ## # @param klass [Class] # @return [void] # def initialize(klass:) @klass = klass end ## # @param method_name [Symbol, String] # @return [Boolean] # def instance_method_defined?(method_name) return true if instance_method_defined_in_instance_methods_modules?(method_name) return true if instance_method_defined_directly?(method_name) false end ## # @param method_name [Symbol, String] # @return [Boolean] # def private_instance_method_defined?(method_name) return true if private_instance_method_defined_in_instance_methods_modules?(method_name) return true if private_instance_method_defined_directly?(method_name) false end ## # @param method_name [Symbol, String] # @return [Boolean] # def class_method_defined?(method_name) class_method_defined_in_class_methods_modules?(method_name) end ## # @param method_name [Symbol, String] # @return [Boolean] # def private_class_method_defined?(method_name) private_class_method_defined_in_class_methods_modules?(method_name) end ## # Checks whether concerns are included into klass (include! was called at least once). # # @return [Boolean] # # @internal # IMPORTANT: `included?` should be thread-safe. # def included? klass.include?(Entities::DefaultConcern) end ## # @param configuration_block [Proc] # @return [ConvenientService::Core::Entities::Config::Entities::Concerns] # def configure(&configuration_block) Utils::Proc.exec_config(configuration_block, stack) self end ## # Includes concerns into klass when called for the first time. # Does nothing for the subsequent calls. # # @return [Boolean] true if called for the first time, false otherwise (similarly as Kernel#require). # # @see https://ruby-doc.org/core-3.1.2/Kernel.html#method-i-require # # @internal # NOTE: Modification of instance variable is NOT thread-safe, that is why `Entities::DefaultConcern` is added. # NOTE: `dup` is used for thread-safety as well. # def include! return false if included? stack.dup.insert_before(0, Entities::DefaultConcern).call(klass: klass) true end ## # @param other [ConvenientService::Core::Entities::Config::Entities::Concerns, Object] # @return [Boolean, nil] # def ==(other) return unless other.instance_of?(self.class) return false if klass != other.klass return false if stack != other.stack true end ## # @return [Array<Module>] concerns as plain modules. # def to_a plain_concerns end protected ## # @!attribute [r] klass # @return [Class] # attr_reader :klass ## # @return [ConvenientService::Core::Entities::Config::Entities::Concerns::Entities::Stack] # def stack @stack ||= Entities::Stack.new(name: stack_name) end private ## # @return [Array<Module>] # def plain_concerns stack.to_a.map(&:first).map(&:concern) end ## # @param method_name [Symbol, String] # @return [Boolean] # def instance_method_defined_directly?(method_name) plain_concerns.any? { |concern| concern.method_defined?(method_name) } end ## # @param method_name [Symbol, String] # @return [Boolean] # def private_instance_method_defined_directly?(method_name) plain_concerns.any? { |concern| concern.private_method_defined?(method_name) } end ## # @param method_name [Symbol, String] # @return [Boolean] # def instance_method_defined_in_instance_methods_modules?(method_name) instance_methods_modules.any? { |mod| mod.method_defined?(method_name) } end ## # @param method_name [Symbol, String] # @return [Boolean] # def private_instance_method_defined_in_instance_methods_modules?(method_name) instance_methods_modules.any? { |mod| mod.private_method_defined?(method_name) } end ## # @param method_name [Symbol, String] # @return [Boolean] # def class_method_defined_in_class_methods_modules?(method_name) class_methods_modules.any? { |mod| mod.method_defined?(method_name) } end ## # @param method_name [Symbol, String] # @return [Boolean] # def private_class_method_defined_in_class_methods_modules?(method_name) class_methods_modules.any? { |mod| mod.private_method_defined?(method_name) } end ## # @return [Array<Module>] # def instance_methods_modules plain_concerns.filter_map { |concern| Utils::Module.get_own_const(concern, :InstanceMethods) } end ## # @return [Array<Module>] # def class_methods_modules plain_concerns.filter_map { |concern| Utils::Module.get_own_const(concern, :ClassMethods) } end ## # @return [String] # def stack_name "Concerns::#{klass}" end end end end end end end