# frozen_string_literal: true module RailsBestPractices module Core # Method container. class Methods def initialize @methods = {} @possible_methods = {} end # Add a method. # # @param [String] class name # @param [String] method name # @param [Hash] method meta, file and line, {"file" => "app/models/post.rb", "line_number" => 5} # @param [String] access control, public, protected or private def add_method(class_name, method_name, meta = {}, access_control = 'public') return if class_name == '' return if has_method?(class_name, method_name) methods(class_name) << Method.new(class_name, method_name, access_control, meta) if access_control == 'public' @possible_methods[method_name] = false end end # Get methods of a class. # # @param [String] class name # @param [String] access control # @return [Array] all methods of a class for such access control, if access control is nil, return all public/protected/private methods def get_methods(class_name, access_control = nil) if access_control methods(class_name).select { |method| method.access_control == access_control } else methods(class_name) end end # If a class has a method. # # @param [String] class name # @param [String] method name # @param [String] access control # @return [Boolean] has a method or not def has_method?(class_name, method_name, access_control = nil) if access_control !!methods(class_name).find do |method| method.method_name == method_name && method.access_control == access_control end else !!methods(class_name).find { |method| method.method_name == method_name } end end # Mark parent class' method as used. # # @param [String] class name # @param [String] method name def mark_parent_class_method_used(class_name, method_name) klass = Prepares.klasses.find { |klass| klass.to_s == class_name } if klass&.extend_class_name mark_parent_class_method_used(klass.extend_class_name, method_name) method = get_method(klass.extend_class_name, method_name) method&.mark_used end end # Mark sub classes' method as used. # # @param [String] class name # @param [String] method name def mark_subclasses_method_used(class_name, method_name) Prepares.klasses.select { |klass| klass.extend_class_name == class_name }.each do |klass| mark_subclasses_method_used(klass.to_s, method_name) method = get_method(klass.to_s, method_name) method&.mark_used end end # Mark the method as public. # # @param [String] class name # @param [String] method name def mark_publicize(class_name, method_name) method = get_method(class_name, method_name) method&.publicize end # Mark parent classs' method as public. # # @param [String] class name # @param [String] method name def mark_parent_class_methods_publicize(class_name, method_name) klass = Prepares.klasses.find { |klass| klass.to_s == class_name } if klass&.extend_class_name mark_parent_class_methods_publicize(klass.extend_class_name, method_name) mark_publicize(class_name, method_name) end end # Remomber the method name, the method is probably be used for the class' public method. # # @param [String] method name def possible_public_used(method_name) @possible_methods[method_name] = true end # Get a method in a class. # # @param [String] class name # @param [String] method name # @param [String] access control # @return [Method] Method object def get_method(class_name, method_name, access_control = nil) if access_control methods(class_name).find do |method| method.method_name == method_name && method.access_control == access_control end else methods(class_name).find { |method| method.method_name == method_name } end end # Get all unused methods. # # @param [String] access control # @return [Array] array of Method def get_all_unused_methods(access_control = nil) @methods.inject([]) do |unused_methods, (_class_name, methods)| unused_methods += if access_control methods.select { |method| method.access_control == access_control && !method.used } else methods.reject(&:used) end end.reject { |method| method.access_control == 'public' && @possible_methods[method.method_name] } end private # Methods of a class. # # @param [String] class name # @return [Array] array of methods def methods(class_name) @methods[class_name] ||= [] end end # Method info includes class name, method name, access control, file, line_number, used. class Method attr_reader :access_control, :class_name, :method_name, :used, :file, :line_number def initialize(class_name, method_name, access_control, meta) @class_name = class_name @method_name = method_name @file = meta['file'] @line_number = meta['line_number'] @access_control = access_control @used = false end # Mark the method as used. def mark_used @used = true end # Mark the method as public def publicize @access_control = 'public' end end end end