# coding: utf-8 class ReekAnalyzer include ScoringStrategies REEK_ISSUE_INFO = { 'Uncommunicative Name' => {'link' => 'http://wiki.github.com/kevinrutherford/reek/uncommunicative-name', 'info' => 'An Uncommunicative Name is a name that doesn’t communicate its intent well enough.'}, 'Class Variable' => {'link' => 'http://wiki.github.com/kevinrutherford/reek/class-variable', 'info' => 'Class variables form part of the global runtime state, and as such make it ' + 'easy for one part of the system to accidentally or inadvertently depend on ' + 'another part of the system.'}, 'Duplication' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/duplication', 'info' => 'Duplication occurs when two fragments of code look nearly identical, or when ' + 'two fragments of code have nearly identical effects at some conceptual level.'}, 'Low Cohesion' => {'link' => 'http://en.wikipedia.org/wiki/Cohesion_(computer_science)', 'info' => 'Low cohesion is associated with undesirable traits such as being difficult to ' + 'maintain, difficult to test, difficult to reuse, and even difficult to understand.'}, 'Nested Iterators' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/nested-iterators', 'info' => 'Nested Iterator occurs when a block contains another block.'}, 'Control Couple' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/control-couple', 'info' => 'Control coupling occurs when a method or block checks the value of a parameter in ' + 'order to decide which execution path to take. The offending parameter is often called a “Control Couple”.'}, 'Irresponsible Module' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/irresponsible-module', 'info' => 'Classes and modules are the units of reuse and release. It is therefore considered ' + 'good practice to annotate every class and module with a brief comment outlining its responsibilities.'}, 'Long Parameter List' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/long-parameter-list', 'info' => 'A Long Parameter List occurs when a method has more than one or two parameters, ' + 'or when a method yields more than one or two objects to an associated block.'}, 'Data Clump' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/data-clump', 'info' => 'In general, a Data Clump occurs when the same two or three items frequently appear ' + 'together in classes and parameter lists, or when a group of instance variable names ' + 'start or end with similar substrings.'}, 'Simulated Polymorphism' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/simulated-polymorphism', 'info' => 'Simulated Polymorphism occurs when, code uses a case statement (especially on a ' + 'type field) or code uses instance_of?, kind_of?, is_a?, or === to decide what code to execute'}, 'Large Class' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/large-class', 'info' => 'A Large Class is a class or module that has a large number of instance variables, ' + 'methods or lines of code in any one piece of its specification.'}, 'Long Method' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/long-method', 'info' => 'Long methods can be hard to read and understand. They often are harder to test and ' + 'maintain as well, which can lead to buggier code.'}, 'Feature Envy' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/feature-envy', 'info' => 'Feature Envy occurs when a code fragment references another object more often than ' + 'it references itself, or when several clients do the same series of manipulations ' + 'on a particular type of object.'}, 'Utility Function' => {'link' =>'http://wiki.github.com/kevinrutherford/reek/utility-function', 'info' => 'A Utility Function is any instance method that has no dependency on the state of the ' + 'instance. It reduces the code’s ability to communicate intent. Code that “belongs” on ' + 'one class but which is located in another can be hard to find.'}, 'Attribute' => {'link' => 'http://wiki.github.com/kevinrutherford/reek/attribute', 'info' => 'A class that publishes a getter or setter for an instance variable invites client ' + 'classes to become too intimate with its inner workings, and in particular with its ' + 'representation of state.'} } # Note that in practice, the prefix reek__ is appended to each one # This was a partially implemented idea to avoid column name collisions # but it is only done in the ReekAnalyzer COLUMNS = %w{type_name message value value_description comparable_message} def self.issue_link(issue) REEK_ISSUE_INFO[issue] end def columns COLUMNS.map{|column| "#{name}__#{column}"} end def name :reek end def map(row) ScoringStrategies.present(row) end def reduce(scores) ScoringStrategies.sum(scores) end def score(metric_ranking, item) ScoringStrategies.percentile(metric_ranking, item) end def generate_records(data, table) return if data==nil data[:matches].each do |match| file_path = match[:file_path] match[:code_smells].each do |smell| location = MetricFu::Location.for(smell[:method]) smell_type = smell[:type] message = smell[:message] table << { "metric" => name, # important "file_path" => file_path, # important # NOTE: ReekAnalyzer is currently different than other analyzers with regard # to column name. Note the COLUMNS constant and #columns method "reek__message" => message, "reek__type_name" => smell_type, "reek__value" => parse_value(message), "reek__value_description" => build_value_description(smell_type, message), "reek__comparable_message" => comparable_message(smell_type, message), "class_name" => location.class_name, # important "method_name" => location.method_name, # important } end end end def self.numeric_smell?(type) ["Large Class", "Long Method", "Long Parameter List"].include?(type) end private def comparable_message(type_name, message) if self.class.numeric_smell?(type_name) match = message.match(/\d+/) if(match) match.pre_match + match.post_match else message end else message end end def build_value_description(type_name, message) item_type = message.match(/\d+ (.*)$/) if(item_type) "number of #{item_type[1]} in #{type_name.downcase}" else nil end end def parse_value(message) # mf_debug "parsing #{message}" match = message.match(/\d+/) if(match) match[0].to_i else nil end end end