metric_fu-roodi 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +3 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +6 -0
  4. data/History.txt +93 -0
  5. data/Manifest.txt +56 -0
  6. data/README.txt +98 -0
  7. data/Rakefile +35 -0
  8. data/bin/metric_fu-roodi +21 -0
  9. data/bin/metric_fu-roodi-describe +7 -0
  10. data/lib/roodi.rb +3 -0
  11. data/lib/roodi/checks.rb +18 -0
  12. data/lib/roodi/checks/abc_metric_method_check.rb +79 -0
  13. data/lib/roodi/checks/assignment_in_conditional_check.rb +32 -0
  14. data/lib/roodi/checks/case_missing_else_check.rb +20 -0
  15. data/lib/roodi/checks/check.rb +76 -0
  16. data/lib/roodi/checks/class_line_count_check.rb +28 -0
  17. data/lib/roodi/checks/class_name_check.rb +31 -0
  18. data/lib/roodi/checks/class_variable_check.rb +24 -0
  19. data/lib/roodi/checks/control_coupling_check.rb +20 -0
  20. data/lib/roodi/checks/cyclomatic_complexity_block_check.rb +41 -0
  21. data/lib/roodi/checks/cyclomatic_complexity_check.rb +50 -0
  22. data/lib/roodi/checks/cyclomatic_complexity_method_check.rb +42 -0
  23. data/lib/roodi/checks/empty_rescue_body_check.rb +32 -0
  24. data/lib/roodi/checks/for_loop_check.rb +20 -0
  25. data/lib/roodi/checks/line_count_check.rb +22 -0
  26. data/lib/roodi/checks/method_line_count_check.rb +29 -0
  27. data/lib/roodi/checks/method_name_check.rb +31 -0
  28. data/lib/roodi/checks/missing_foreign_key_index_check.rb +99 -0
  29. data/lib/roodi/checks/module_line_count_check.rb +28 -0
  30. data/lib/roodi/checks/module_name_check.rb +31 -0
  31. data/lib/roodi/checks/name_check.rb +16 -0
  32. data/lib/roodi/checks/npath_complexity_check.rb +75 -0
  33. data/lib/roodi/checks/npath_complexity_method_check.rb +29 -0
  34. data/lib/roodi/checks/parameter_number_check.rb +34 -0
  35. data/lib/roodi/core.rb +1 -0
  36. data/lib/roodi/core/checking_visitor.rb +26 -0
  37. data/lib/roodi/core/error.rb +17 -0
  38. data/lib/roodi/core/parser.rb +30 -0
  39. data/lib/roodi/core/runner.rb +81 -0
  40. data/lib/roodi/core/visitable_sexp.rb +25 -0
  41. data/lib/roodi/version.rb +3 -0
  42. data/lib/roodi_task.rb +35 -0
  43. data/roodi.gemspec +26 -0
  44. data/roodi.yml +25 -0
  45. data/spec/roodi/checks/abc_metric_method_check_spec.rb +89 -0
  46. data/spec/roodi/checks/assignment_in_conditional_check_spec.rb +105 -0
  47. data/spec/roodi/checks/case_missing_else_check_spec.rb +32 -0
  48. data/spec/roodi/checks/class_line_count_check_spec.rb +39 -0
  49. data/spec/roodi/checks/class_name_check_spec.rb +39 -0
  50. data/spec/roodi/checks/class_variable_check_spec.rb +17 -0
  51. data/spec/roodi/checks/control_coupling_check_spec.rb +23 -0
  52. data/spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb +67 -0
  53. data/spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb +200 -0
  54. data/spec/roodi/checks/empty_rescue_body_check_spec.rb +140 -0
  55. data/spec/roodi/checks/for_loop_check_spec.rb +18 -0
  56. data/spec/roodi/checks/method_line_count_check_spec.rb +56 -0
  57. data/spec/roodi/checks/method_name_check_spec.rb +76 -0
  58. data/spec/roodi/checks/missing_foreign_key_index_check_spec.rb +33 -0
  59. data/spec/roodi/checks/module_line_count_check_spec.rb +39 -0
  60. data/spec/roodi/checks/module_name_check_spec.rb +27 -0
  61. data/spec/roodi/checks/npath_complexity_method_check_spec.rb +53 -0
  62. data/spec/roodi/checks/parameter_number_check_spec.rb +47 -0
  63. data/spec/roodi/core/runner_spec.rb +25 -0
  64. data/spec/roodi/roodi.yml +2 -0
  65. data/spec/spec_helper.rb +3 -0
  66. metadata +149 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ doc
2
+ pkg
3
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem "rake"
6
+ gem "rspec", "~> 2.11.0"
data/History.txt ADDED
@@ -0,0 +1,93 @@
1
+ = 2.2.0
2
+
3
+ * Pull down updates from https://github.com/zdennis/roodi that includes updates from https://github.com/hooroo/roodi and https://github.com/aselder/roodi re: pull request https://github.com/martinjandrews/roodi/pull/12 https://github.com/martinjandrews/roodi/pull/11
4
+ * Did not pull in updates from https://github.com/ssassi/roodi/commits/master re: https://github.com/martinjandrews/roodi/pull/10
5
+
6
+ = 2.0.1
7
+
8
+ * Fixed a bug where roodi.yml was not being loaded. Patch supplied by Rob Mitchell.
9
+
10
+ = 2.0.0
11
+
12
+ * Changed internal structure to use a more pure visitor like pattern.
13
+ * Got *much* faster as a result of the change.
14
+ * Design change fixed 'feature' where nested blocks would all get listed if the inner one exceeded complexity.
15
+ * Outline for NPath complexity check is now possible. Not working yet though.
16
+ * Removed dependency on facets library.
17
+
18
+ = 1.4.0
19
+
20
+ * Upgraded from ParseTree to ruby_parser.
21
+
22
+ = 1.3.7
23
+
24
+ * Fixed a bug in the rake task where it always failed even if no errors existed.
25
+
26
+ = 1.3.6
27
+
28
+ * Added nil as a valid response for an empty rescue block
29
+
30
+ = 1.3.5
31
+
32
+ * Fixed bug in rake task
33
+
34
+ = 1.3.4
35
+
36
+ * Minor cleanup
37
+
38
+ = 1.3.3
39
+
40
+ * Added a rake task
41
+
42
+ = 1.3.1
43
+
44
+ * wrapped errors in an object to become more usable as an API.
45
+
46
+ = 1.3.0
47
+
48
+ * added case missing else check.
49
+ * updated checks to take a hash of options with built-in defaults.
50
+ * added support for complete configuration via external file.
51
+ * added support for passing in a custom config file via 'roodi -config=<filename> [pattern]'
52
+ * added assignment in conditional check.
53
+ * refactored checks to remove duplicate code.
54
+
55
+ = 1.2.0
56
+
57
+ * added module name check.
58
+ * added parameter number check.
59
+ * added module line count check.
60
+ * added class line count check.
61
+
62
+ = 1.1.1
63
+
64
+ * I'd initially published to Rubyforge under a 1.0.0 gem, and I've since tried to retrospectively fix up the version number system. It turns out that Rubyforge caches old gems permanently, so I have to re-start at a larger number again.
65
+ * class name check no longer gets confused about scoped class names like Module::Classname.
66
+
67
+ = 0.5
68
+
69
+ * expanded regex matching for method name check.
70
+ * suppressed noisy output from ParseTree using facets API.
71
+ * updated dependencies and version as a result of facets change.
72
+ * made Roodi tolerant of being asked to parse files which aren't really Ruby files.
73
+ * updated the documentation with usage examples.
74
+
75
+ = 0.4
76
+
77
+ * Added support back in for line numbers in error messages.
78
+ * Re-enabled MethodLineCountCheck as part of the default check set.
79
+
80
+ = 0.3
81
+
82
+ * First version of Roodi to be published to Rubyforge.
83
+
84
+ = 0.2
85
+
86
+ * Now use ParseTree instead of JRuby, which makes the tool much more accessible.
87
+ * Removed MagicNumberCheck
88
+ * Line numbers no longer supported as a result of the move.
89
+
90
+ = 0.1
91
+
92
+ * A first version of a design checking tool for Ruby, with a few checks built in to get started.
93
+
data/Manifest.txt ADDED
@@ -0,0 +1,56 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/roodi
6
+ bin/roodi-describe
7
+ lib/roodi.rb
8
+ lib/roodi/checks.rb
9
+ lib/roodi/checks/abc_metric_method_check.rb
10
+ lib/roodi/checks/assignment_in_conditional_check.rb
11
+ lib/roodi/checks/case_missing_else_check.rb
12
+ lib/roodi/checks/check.rb
13
+ lib/roodi/checks/class_line_count_check.rb
14
+ lib/roodi/checks/class_name_check.rb
15
+ lib/roodi/checks/class_variable_check.rb
16
+ lib/roodi/checks/control_coupling_check.rb
17
+ lib/roodi/checks/cyclomatic_complexity_block_check.rb
18
+ lib/roodi/checks/cyclomatic_complexity_check.rb
19
+ lib/roodi/checks/cyclomatic_complexity_method_check.rb
20
+ lib/roodi/checks/empty_rescue_body_check.rb
21
+ lib/roodi/checks/for_loop_check.rb
22
+ lib/roodi/checks/line_count_check.rb
23
+ lib/roodi/checks/method_line_count_check.rb
24
+ lib/roodi/checks/method_name_check.rb
25
+ lib/roodi/checks/module_line_count_check.rb
26
+ lib/roodi/checks/module_name_check.rb
27
+ lib/roodi/checks/name_check.rb
28
+ lib/roodi/checks/npath_complexity_check.rb
29
+ lib/roodi/checks/npath_complexity_method_check.rb
30
+ lib/roodi/checks/parameter_number_check.rb
31
+ lib/roodi/core.rb
32
+ lib/roodi/core/checking_visitor.rb
33
+ lib/roodi/core/error.rb
34
+ lib/roodi/core/parser.rb
35
+ lib/roodi/core/runner.rb
36
+ lib/roodi/core/visitable_sexp.rb
37
+ lib/roodi_task.rb
38
+ roodi.yml
39
+ spec/roodi/checks/abc_metric_method_check_spec.rb
40
+ spec/roodi/checks/assignment_in_conditional_check_spec.rb
41
+ spec/roodi/checks/case_missing_else_check_spec.rb
42
+ spec/roodi/checks/class_line_count_check_spec.rb
43
+ spec/roodi/checks/class_name_check_spec.rb
44
+ spec/roodi/checks/class_variable_check_spec.rb
45
+ spec/roodi/checks/control_coupling_check_spec.rb
46
+ spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb
47
+ spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb
48
+ spec/roodi/checks/empty_rescue_body_check_spec.rb
49
+ spec/roodi/checks/for_loop_check_spec.rb
50
+ spec/roodi/checks/method_line_count_check_spec.rb
51
+ spec/roodi/checks/method_name_check_spec.rb
52
+ spec/roodi/checks/module_line_count_check_spec.rb
53
+ spec/roodi/checks/module_name_check_spec.rb
54
+ spec/roodi/checks/npath_complexity_method_check_spec.rb
55
+ spec/roodi/checks/parameter_number_check_spec.rb
56
+ spec/spec_helper.rb
data/README.txt ADDED
@@ -0,0 +1,98 @@
1
+ = roodi
2
+
3
+ * http://roodi.rubyforge.org
4
+
5
+ == DESCRIPTION:
6
+
7
+ Roodi stands for Ruby Object Oriented Design Inferometer. It parses your Ruby code and warns you about design issues you have based on the checks that is has configured.
8
+
9
+ This is a fork for use with metric_fu that includes 1.9 compatibility and continued development
10
+
11
+ == INSTALL:
12
+
13
+ * gem install roodi
14
+
15
+ == SYNOPSIS:
16
+
17
+ To check one or more files using the default configuration that comes with Roodi, use:
18
+ metric_fu-roodi [-config=file] [pattern ...]
19
+
20
+ === EXAMPLE USAGE
21
+
22
+ Check all ruby files in a rails app:
23
+ metric_fu-roodi "rails_app/**/*.rb"
24
+
25
+ Check one controller and one model file in a rails app:
26
+ metric_fu-roodi app/controller/sample_controller.rb app/models/sample.rb
27
+
28
+ Check one controller and all model files in a rails app:
29
+ metric_fu-roodi app/controller/sample_controller.rb "app/models/*.rb"
30
+
31
+ Check all ruby files in a rails app with a custom configuration file:
32
+ metric_fu-roodi -config=my_roodi_config.yml "rails_app/**/*.rb"
33
+
34
+ If you're writing a check, it is useful to see the structure of a file the way that Roodi tokenizes it (via ruby_parser). Use:
35
+ metric_fu-roodi-describe [filename]
36
+
37
+ == CUSTOM CONFIGURATION
38
+
39
+ To change the set of checks included, or to change the default values of the checks, you can provide your own config file. The config file is a YAML file that lists the checks to be included. Each check can optionally include a hash of options that are passed to the check to configure it. For example, the default config file looks like this:
40
+
41
+ AssignmentInConditionalCheck: { }
42
+ CaseMissingElseCheck: { }
43
+ ClassLineCountCheck: { line_count: 300 }
44
+ ClassNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
45
+ CyclomaticComplexityBlockCheck: { complexity: 4 }
46
+ CyclomaticComplexityMethodCheck: { complexity: 8 }
47
+ EmptyRescueBodyCheck: { }
48
+ ForLoopCheck: { }
49
+ MethodLineCountCheck: { line_count: 20 }
50
+ MethodNameCheck: { pattern: !ruby/regexp /^[_a-z<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ }
51
+ ModuleLineCountCheck: { line_count: 300 }
52
+ ModuleNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
53
+ ParameterNumberCheck: { parameter_count: 5 }
54
+
55
+ == SUPPORTED CHECKS:
56
+
57
+ * AssignmentInConditionalCheck - Check for an assignment inside a conditional. It's probably a mistaken equality comparison.
58
+ * CaseMissingElseCheck - Check that case statements have an else statement so that all cases are covered.
59
+ * ClassLineCountCheck - Check that the number of lines in a class is below the threshold.
60
+ * ClassNameCheck - Check that class names match convention.
61
+ * CyclomaticComplexityBlockCheck - Check that the cyclomatic complexity of all blocks is below the threshold.
62
+ * CyclomaticComplexityMethodCheck - Check that the cyclomatic complexity of all methods is below the threshold.
63
+ * EmptyRescueBodyCheck - Check that there are no empty rescue blocks.
64
+ * ForLoopCheck - Check that for loops aren't used (Use Enumerable.each instead)
65
+ * MethodLineCountCheck - Check that the number of lines in a method is below the threshold.
66
+ * MethodNameCheck - Check that method names match convention.
67
+ * ModuleLineCountCheck - Check that the number of lines in a module is below the threshold.
68
+ * ModuleNameCheck - Check that module names match convention.
69
+ * ParameterNumberCheck - Check that the number of parameters on a method is below the threshold.
70
+
71
+ == SUGGESTED CHECKS:
72
+
73
+ * BlockVariableShadowCheck - Check that a block variable does not have the same name as a method parameter or local variable. It may be mistakenly referenced within the block.
74
+
75
+ == LICENSE:
76
+
77
+ (The MIT License)
78
+
79
+ Copyright (c) 2008 Marty Andrews
80
+
81
+ Permission is hereby granted, free of charge, to any person obtaining
82
+ a copy of this software and associated documentation files (the
83
+ 'Software'), to deal in the Software without restriction, including
84
+ without limitation the rights to use, copy, modify, merge, publish,
85
+ distribute, sublicense, and/or sell copies of the Software, and to
86
+ permit persons to whom the Software is furnished to do so, subject to
87
+ the following conditions:
88
+
89
+ The above copyright notice and this permission notice shall be
90
+ included in all copies or substantial portions of the Software.
91
+
92
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
93
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
94
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
95
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
96
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
97
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
98
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'bundler/gem_tasks'
3
+ begin
4
+ require 'spec/rake/spectask'
5
+ desc "Run all specs in spec directory"
6
+ Spec::Rake::SpecTask.new(:spec) do |t|
7
+ t.spec_files = FileList['spec/**/*_spec.rb']
8
+ end
9
+ rescue LoadError
10
+ require 'rspec/core/rake_task'
11
+ desc "Run all specs in spec directory"
12
+ RSpec::Core::RakeTask.new(:spec)
13
+ end
14
+
15
+ require File.expand_path('lib/roodi',File.dirname(__FILE__))
16
+
17
+ def roodi(ruby_files)
18
+ roodi = Roodi::Core::Runner.new
19
+ ruby_files.each { |file| roodi.check_file(file) }
20
+ roodi.errors.each {|error| puts error}
21
+ puts "\nFound #{roodi.errors.size} errors."
22
+ end
23
+
24
+ desc "Run all specs"
25
+ Spec::Rake::SpecTask.new('spec') do |t|
26
+ t.spec_files = FileList['spec/**/*spec.rb']
27
+ end
28
+
29
+ desc "Run Roodi against all source files"
30
+ task :roodi do
31
+ pattern = File.join(File.dirname(__FILE__), "**", "*.rb")
32
+ roodi(Dir.glob(pattern))
33
+ end
34
+
35
+ task :default => :spec
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
4
+
5
+ require 'roodi'
6
+
7
+ runner = Roodi::Core::Runner.new
8
+
9
+ config_param = ARGV.detect {|arg| arg =~ /-config=.*/}
10
+ runner.config = config_param.split("=")[1] if config_param
11
+ ARGV.delete config_param
12
+
13
+ ARGV.each do |arg|
14
+ Dir.glob(arg).each { |file| runner.check_file(file) }
15
+ end
16
+
17
+ runner.errors.each {|error| puts error}
18
+
19
+ puts "\nFound #{runner.errors.size} errors."
20
+
21
+ exit runner.errors.size
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
4
+ require 'roodi'
5
+
6
+ roodi = Roodi::Core::Runner.new
7
+ roodi.print_file(ARGV[0])
data/lib/roodi.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'roodi/checks'
2
+ require 'roodi/core'
3
+ require 'roodi/version'
@@ -0,0 +1,18 @@
1
+ require 'roodi/checks/abc_metric_method_check'
2
+ require 'roodi/checks/assignment_in_conditional_check'
3
+ require 'roodi/checks/case_missing_else_check'
4
+ require 'roodi/checks/class_line_count_check'
5
+ require 'roodi/checks/class_name_check'
6
+ require 'roodi/checks/class_variable_check'
7
+ require 'roodi/checks/control_coupling_check'
8
+ require 'roodi/checks/cyclomatic_complexity_block_check'
9
+ require 'roodi/checks/cyclomatic_complexity_method_check'
10
+ require 'roodi/checks/empty_rescue_body_check'
11
+ require 'roodi/checks/for_loop_check'
12
+ require 'roodi/checks/method_line_count_check'
13
+ require 'roodi/checks/method_name_check'
14
+ require 'roodi/checks/missing_foreign_key_index_check'
15
+ require 'roodi/checks/module_line_count_check'
16
+ require 'roodi/checks/module_name_check'
17
+ require 'roodi/checks/npath_complexity_method_check'
18
+ require 'roodi/checks/parameter_number_check'
@@ -0,0 +1,79 @@
1
+ require 'roodi/checks/check'
2
+
3
+ module Roodi
4
+ module Checks
5
+ # TODO: Add summary
6
+ #
7
+ # TODO: Add detail
8
+ class AbcMetricMethodCheck < Check
9
+ # ASSIGNMENTS = [:attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn]
10
+ ASSIGNMENTS = [:lasgn]
11
+ # BRANCHES = [:if, :else, :while, :until, :for, :rescue, :case, :when, :and, :or]
12
+ BRANCHES = [:vcall, :call]
13
+ # CONDITIONS = [:and, :or]
14
+ CONDITIONS = [:==, :<=, :>=, :<, :>]
15
+ # = *= /= %= += <<= >>= &= |= ^=
16
+ OPERATORS = [:*, :/, :%, :+, :<<, :>>, :&, :|, :^]
17
+ DEFAULT_SCORE = 10
18
+
19
+ attr_accessor :score
20
+
21
+ def initialize
22
+ super()
23
+ self.score = DEFAULT_SCORE
24
+ end
25
+
26
+ def interesting_nodes
27
+ [:defn]
28
+ end
29
+
30
+ def evaluate_start(node)
31
+ method_name = node[1]
32
+ a = count_assignments(node)
33
+ b = count_branches(node)
34
+ c = count_conditionals(node)
35
+ score = Math.sqrt(a*a + b*b + c*c)
36
+ add_error "Method name \"#{method_name}\" has an ABC metric score of <#{a},#{b},#{c}> = #{score}. It should be #{@score} or less." unless score <= @score
37
+ end
38
+
39
+ private
40
+
41
+ def count_assignments(node)
42
+ count = 0
43
+ count = count + 1 if assignment?(node)
44
+ node.children.each {|node| count += count_assignments(node)}
45
+ count
46
+ end
47
+
48
+ def count_branches(node)
49
+ count = 0
50
+ count = count + 1 if branch?(node)
51
+ node.children.each {|node| count += count_branches(node)}
52
+ count
53
+ end
54
+
55
+ def count_conditionals(node)
56
+ count = 0
57
+ count = count + 1 if conditional?(node)
58
+ node.children.each {|node| count += count_conditionals(node)}
59
+ count
60
+ end
61
+
62
+ def assignment?(node)
63
+ ASSIGNMENTS.include?(node.node_type)
64
+ end
65
+
66
+ def branch?(node)
67
+ BRANCHES.include?(node.node_type) && !conditional?(node) && !operator?(node)
68
+ end
69
+
70
+ def conditional?(node)
71
+ (:call == node.node_type) && CONDITIONS.include?(node[2])
72
+ end
73
+
74
+ def operator?(node)
75
+ (:call == node.node_type) && OPERATORS.include?(node[2])
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,32 @@
1
+ require 'roodi/checks/check'
2
+
3
+ module Roodi
4
+ module Checks
5
+ # Checks a conditional to see if it contains an assignment.
6
+ #
7
+ # A conditional containing an assignment is likely to be a mistyped equality check. You
8
+ # should either fix the typo or factor out the assignment so that the code is clearer.
9
+ class AssignmentInConditionalCheck < Check
10
+
11
+ def interesting_nodes
12
+ [:if, :while]
13
+ end
14
+
15
+ def evaluate_start(node)
16
+ add_error("Found = in conditional. It should probably be an ==") if has_assignment?(node[1])
17
+ end
18
+
19
+ private
20
+
21
+ def has_assignment?(node)
22
+ found_assignment = false
23
+ found_assignment = found_assignment || node.node_type == :lasgn
24
+ if (node.node_type == :and or node.node_type == :or)
25
+ node.children.each { |child| found_assignment = found_assignment || has_assignment?(child) }
26
+ end
27
+ found_assignment
28
+ end
29
+
30
+ end
31
+ end
32
+ end