lib/tailor/ruler.rb
require_relative 'logger'
require_relative 'problem'
require_relative 'runtime_error'
require_relative '../ext/string_ext'
class Tailor
# This is a composite class, geared for getting at or managing the Rulers
# that should be used for measuring style. To do so, create a new object of
# this type, then add child rulers to that object using +#add_child_ruler+.
# After using the Ruler, you'll have access to all of the problems found by
# all of the child rulers via +#problems+.
#
# Example:
# ruler = Ruler.new
# file_length_ruler = FileLengthRuler.new
# ruler.add_child_ruler(file_length_ruler)
# # use ruler
# ruler.problems # => [{ all of your file length problems... }]
#
# There's really no measuring functionality in this base class--it's merely
# for aggregating child data and for providing a base class to create child
# Rulers from. Speaking of... if you want, you can create your own rulers.
# A Ruler requires a few things:
#
# First, it needs a list of Lexer events to observer. Tailor uses its Lexer
# to publish events (in this case, characters or string Ruby constructs) of
# interest to its observers. Rulers subscribe to those events so that they
# can detect the problems they're looking for. These are defined as a Set in
# +@lexer_observers+. Adding to that list means the Ruler will subscribe to
# those events.
#
# Example:
# class MyRuler < Tailor::Ruler
# def initialize
# add_lexer_observers = :nl_observer, :kw_observer
# end
# end
class Ruler
include Tailor::Logger::Mixin
attr_reader :lexer_observers
# @param [Object] config
# @param [Hash] options
def initialize(config=nil, options={ level: :error })
@config = config
@options = options
@do_measurement = true
log "Ruler initialized with style setting: #{@config}"
log "Ruler initialized with problem level setting: #{@options[:level]}"
@child_rulers = []
@lexer_observers = []
@problems = []
end
# Adds the {Tailor::Ruler} object to the list of child rulers.
#
# @param [Tailor::Ruler] ruler
def add_child_ruler(ruler)
@child_rulers << ruler
log "Added child ruler: #{ruler}"
end
# Gets all of the problems from all child rulers.
#
# @return [Array] The list of problems.
def problems
@problems = @child_rulers.inject(@problems) do |problems, ruler|
problems + ruler.problems
end
@problems.sort_by! { |problem| problem[:line].to_i }
end
# Each ruler should redefine this for its needs.
def measure(*args)
raise RuntimeError,
'Ruler#measure called, but should be redefined by a real ruler.'
end
# Converts the {Tailor::Ruler} name to snake case.
#
# @return [String] The ruler name as snake-case that represents the problem
# that was found.
def problem_type
self.class.to_s =~ /^.+::(\S+)Ruler$/
$1.underscore
end
private
def add_lexer_observers(*lexer_observer)
@lexer_observers = lexer_observer
end
end
end