lib/pelusa/lint/demeter_law.rb in pelusa-0.2.2 vs lib/pelusa/lint/demeter_law.rb in pelusa-0.2.3

- old
+ new

@@ -4,11 +4,10 @@ def initialize @violations = Set.new end def check(klass) - initialize iterate_lines!(klass) return SuccessfulAnalysis.new(name) if @violations.empty? FailedAnalysis.new(name, @violations) do |violations| @@ -21,20 +20,47 @@ def name "Respects Demeter law" end def iterate_lines!(klass) - iterator = Iterator.new do |node| + ClassAnalyzer.walk(klass) do |node| if node.is_a?(Rubinius::AST::Send) && node.receiver.is_a?(Rubinius::AST::Send) @violations << node.line unless white_listed?(node.receiver.name) end end - Array(klass).each(&iterator) end + # Internal: Default modules whose methods are whitelisted. + DEFAULT_WHITELIST = [Class, Fixnum, Enumerable] + + # Internal: Methods on these common, fundamental classes and modules are + # allowed to be called on other objects. + # + # Note: this doesn't currently work with namespaced objects, but would be + # easy to extend. + # + # Returns an array of classes specified in .pelusa.yml (a comma-separated + # list of class names) or the default values above. + def whitelist_sources + if whitelist = Pelusa.configuration['DemeterLaw']['whitelist'] + whitelist.split(",").map {|k| Kernel.const_get(k.strip)} + else + DEFAULT_WHITELIST + end + end + + # Internal: Allow conversion methods -- matching /^(to_|as_)/ to violate + # the law of Demeter. + def allow_conversions? + Pelusa.configuration['DemeterLaw'].fetch('allow_conversions', false) + end + def white_listed? method - [Class, Fixnum, Enumerable].any? do |enclosing_module| - enclosing_module.instance_methods.any? {|instance_method| instance_method == method } + whitelist_sources.any? do |enclosing_module| + enclosing_module.instance_methods.any? do |instance_method| + instance_method == method or + allow_conversions? && instance_method =~ /^(to_|as_)/ + end end end end end end