lib/inch/evaluation/proxy.rb
module Inch
# The Evaluation module concerns itself with the evaluation of code objects
# with regard to their inline code documentation
module Evaluation
# Base class for evaluations. This class provides the evaluation's
# process structure.
#
# @abstract
class Proxy
# Returns a Proxy object for the given +code_object+
#
# @param language [String,Symbol]
# @param object [Codebase::Object]
# @return [Evaluation::Proxy]
def self.for(language, object)
class_for(language, object.code_object).new(object)
end
extend Forwardable
MIN_SCORE = 0
MAX_SCORE = 100
# @return [Codebase::Object]
attr_reader :object
# @return [Array<Evaluation::Role::Base>]
attr_reader :roles
# @param object [Codebase::Object]
def initialize(object)
@object = object
evaluation_config = Config.for(object.language).evaluation
@criteria = eval_criteria(evaluation_config)
@roles = []
evaluate
end
# Evaluates the objects and assigns roles
# @note This is its own method so it can be overridden.
# @return [void]
def evaluate
__evaluate(object, relevant_roles)
end
# @return [Float] the max score that is assignable to +object+
def max_score
@__max_score = __max_score
end
# @return [Float] the min score that is assignable to +object+
def min_score
@__min_score = __min_score
end
# @return [Fixnum] the final score for +object+
def score
@__score ||= __score
end
# @return [Fixnum] the priority for +object+
def priority
@__priority ||= __priority
end
protected
def add_role(role)
@roles << role
end
# Evaluates a Criteria object with the object so that it can
# give us scores for docstring, return_type, etc.
#
# @param config [Config::Evaluation]
# @return [Evaluation::Criteria]
def eval_criteria(config)
object_type = self.class.to_s.split('::').last
c = config.criteria_for(object_type)
c.evaluate(object)
c
end
# Returns a key-value pair of Role classes and potential scores for
# each role (can be nil)
#
# @see #evaluate
# @return [Hash]
def relevant_roles
{}
end
# Returns a score for a given criterion.
#
# @param criterion_name [String] e.g. 'docstring' or 'return_type'
# @return [Float]
def score_for(criterion_name)
@criteria.send(criterion_name) * MAX_SCORE
end
# Iterates over the given +role_classes+ and assigns the individual
# roles, if applicable.
#
# @param object [Codebase::Object]
# @param role_classes [Hash]
# @return [void]
def __evaluate(object, role_classes)
role_classes.each do |role_class, score|
next unless role_class.applicable?(object)
add_role role_class.new(object, score)
end
end
# @return [Float] the max score that is assignable to +object+
def __max_score
arr = @roles.map(&:max_score).compact
[MAX_SCORE].concat(arr).min.to_i
end
# @return [Float] the max score that is assignable to +object+
def __min_score
arr = @roles.map(&:min_score).compact
[MIN_SCORE].concat(arr).max.to_i
end
# @return [Float]
def __score
value = @roles.reduce(0) { |sum, r| sum + r.score.to_f }.to_i
if value < min_score
min_score
elsif value > max_score
max_score
else
value
end
end
# @return [Fixnum]
def __priority
@roles.reduce(0) { |sum, r| sum + r.priority.to_i }
end
def self.class_for(language, code_object)
class_name = code_object.class.to_s.split('::').last
Config.namespace(language, :Evaluation).const_get(class_name)
end
private_class_method :class_for
end
end
end