rails_best_practices 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -42,6 +42,7 @@ OveruseRouteCustomizationsCheck: { customize_count: 3 }
42
42
  NeedlessDeepNestingCheck: { nested_count: 2 }
43
43
  NotUseDefaultRouteCheck: { }
44
44
  KeepFindersOnTheirOwnModelCheck: { }
45
+ LawOfDemeterCheck: { }
45
46
  </code></pre>
46
47
 
47
48
  *************************************************
@@ -67,8 +68,8 @@ h2. Progress
67
68
 
68
69
  * Lesson 3. Model
69
70
  ## [-Keep Finders on Their Own Model-]
70
- ## Love named_scope
71
- ## the Law of Demeter
71
+ ## [-Love named_scope-] # same as Move finder to named_scope
72
+ ## [-the Law of Demeter-]
72
73
  ## DRY: metaprogramming
73
74
  ## Extract into Module
74
75
  ## Extract to composed class
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.2.2
@@ -11,3 +11,4 @@ require 'rails_best_practices/checks/overuse_route_customizations_check'
11
11
  require 'rails_best_practices/checks/needless_deep_nesting_check'
12
12
  require 'rails_best_practices/checks/not_use_default_route_check'
13
13
  require 'rails_best_practices/checks/keep_finders_on_their_own_model_check'
14
+ require 'rails_best_practices/checks/law_of_demeter_check'
@@ -3,7 +3,7 @@ require 'rails_best_practices/core/error'
3
3
  module RailsBestPractices
4
4
  module Checks
5
5
  class Check
6
- NODE_TYPES = [:call, :defn, :if, :unless]
6
+ NODE_TYPES = [:call, :defn, :if, :unless, :class]
7
7
 
8
8
  def initialize
9
9
  @errors = []
@@ -2,6 +2,9 @@ require 'rails_best_practices/checks/check'
2
2
 
3
3
  module RailsBestPractices
4
4
  module Checks
5
+ # Check a model to make sure finders are on their own model.
6
+ #
7
+ # Implementation: check if :find is called by other model.
5
8
  class KeepFindersOnTheirOwnModelCheck < Check
6
9
 
7
10
  def interesting_nodes
@@ -0,0 +1,42 @@
1
+ require 'rails_best_practices/checks/check'
2
+
3
+ module RailsBestPractices
4
+ module Checks
5
+ class LawOfDemeterCheck < Check
6
+
7
+ def interesting_nodes
8
+ [:call, :class]
9
+ end
10
+
11
+ def initialize
12
+ super
13
+ @associations = {}
14
+ end
15
+
16
+ def evaluate_start(node)
17
+ if node.node_type == :class
18
+ remember_belongs_to(node)
19
+ elsif [:lvar, :ivar].include?(node.subject.node_type) and node.subject != s(:lvar, :_erbout)
20
+ add_error "law of demeter" if need_delegate?(node)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def remember_belongs_to(node)
27
+ node.class_body.grep_nodes(:message => :belongs_to).collect do |body_node|
28
+ class_name = node.subject.to_s.underscore
29
+ @associations[class_name] ||= []
30
+ @associations[class_name] << eval(body_node.arguments[1].to_ruby).to_s
31
+ end
32
+ end
33
+
34
+ def need_delegate?(node)
35
+ @associations.each do |class_name, associations|
36
+ return true if node.subject.to_ruby =~ /#{class_name}$/ and associations.include? node.message.to_s
37
+ end
38
+ false
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,11 +1,11 @@
1
1
  require 'optparse'
2
2
 
3
3
  def expand_dirs_to_files *dirs
4
- extensions = ['rb']
4
+ extensions = ['rb', 'builder']
5
5
 
6
6
  dirs.flatten.map { |p|
7
7
  if File.directory? p
8
- Dir[File.join(p, '**', "*.{#{extensions.join(',')}}")]
8
+ Dir[File.join(p, '**', "*{#{extensions.join(',')}}")]
9
9
  else
10
10
  p
11
11
  end
@@ -29,4 +29,4 @@ expand_dirs_to_files(ARGV).each.each { |file| runner.check_file(file) }
29
29
  runner.errors.each {|error| puts error}
30
30
  puts "\nFound #{runner.errors.size} errors."
31
31
 
32
- exit runner.errors.size
32
+ exit runner.errors.size
@@ -4,8 +4,19 @@ module Enumerable
4
4
  end
5
5
  end
6
6
 
7
+ class String
8
+ # copy from rails
9
+ def underscore
10
+ self.gsub(/::/, '/').
11
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
12
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
13
+ tr("-", "_").
14
+ downcase
15
+ end
16
+ end
17
+
7
18
  class NilClass
8
19
  def method_missing(method_sym, *arguments, &block)
9
20
  return nil
10
21
  end
11
- end
22
+ end
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'ruby_parser'
3
+ require 'erb'
3
4
  require 'yaml'
4
5
 
5
6
  module RailsBestPractices
@@ -21,6 +22,9 @@ module RailsBestPractices
21
22
  end
22
23
 
23
24
  def check(filename, content)
25
+ if filename =~ /.*erb/
26
+ content = ERB.new(content).src
27
+ end
24
28
  node = parse(filename, content)
25
29
  node.accept(@checker) if node
26
30
  end
@@ -47,7 +47,7 @@ class Sexp
47
47
  end
48
48
 
49
49
  def subject
50
- if [:attrasgn, :call, :iasgn, :lasgn].include? node_type
50
+ if [:attrasgn, :call, :iasgn, :lasgn, :class].include? node_type
51
51
  self[1]
52
52
  end
53
53
  end
@@ -85,11 +85,16 @@ class Sexp
85
85
  def method_body
86
86
  if :block == node_type
87
87
  self[1..-1]
88
- else
88
+ end
89
+ end
90
+
91
+ def class_body
92
+ if :class == node_type
93
+ self[3]
89
94
  end
90
95
  end
91
96
 
92
97
  def to_ruby
93
- Ruby2Ruby.new.process(self)
98
+ Ruby2Ruby.new.process(self) unless self.empty?
94
99
  end
95
100
  end
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{rails_best_practices}
8
- s.version = "0.2.1"
8
+ s.version = "0.2.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Richard Huang"]
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
30
30
  "lib/rails_best_practices/checks/add_model_virtual_attribute_check.rb",
31
31
  "lib/rails_best_practices/checks/check.rb",
32
32
  "lib/rails_best_practices/checks/keep_finders_on_their_own_model_check.rb",
33
+ "lib/rails_best_practices/checks/law_of_demeter_check.rb",
33
34
  "lib/rails_best_practices/checks/many_to_many_collection_check.rb",
34
35
  "lib/rails_best_practices/checks/move_finder_to_named_scope_check.rb",
35
36
  "lib/rails_best_practices/checks/move_model_logic_into_model_check.rb",
@@ -52,6 +53,7 @@ Gem::Specification.new do |s|
52
53
  "rails_best_practices.yml",
53
54
  "spec/rails_best_practices/checks/add_model_virtual_attribute_check_spec.rb",
54
55
  "spec/rails_best_practices/checks/keep_finders_on_their_own_model_check_spec.rb",
56
+ "spec/rails_best_practices/checks/law_of_demeter_check_spec.rb",
55
57
  "spec/rails_best_practices/checks/many_to_many_collection_check_spec.rb",
56
58
  "spec/rails_best_practices/checks/move_finder_to_named_scope_check_spec.rb",
57
59
  "spec/rails_best_practices/checks/move_model_logic_into_model_check_spec.rb",
@@ -80,6 +82,7 @@ Gem::Specification.new do |s|
80
82
  "spec/rails_best_practices/checks/many_to_many_collection_check_spec.rb",
81
83
  "spec/rails_best_practices/checks/use_model_association_check_spec.rb",
82
84
  "spec/rails_best_practices/checks/move_model_logic_into_model_check_spec.rb",
85
+ "spec/rails_best_practices/checks/law_of_demeter_check_spec.rb",
83
86
  "spec/rails_best_practices/checks/replace_complex_creation_with_factory_method_check_spec.rb",
84
87
  "spec/rails_best_practices/checks/nested_model_forms_check_spec.rb",
85
88
  "spec/rails_best_practices/checks/move_finder_to_named_scope_check_spec.rb",
@@ -11,3 +11,4 @@ OveruseRouteCustomizationsCheck: { customize_count: 3 }
11
11
  NeedlessDeepNestingCheck: { nested_count: 2 }
12
12
  NotUseDefaultRouteCheck: { }
13
13
  KeepFindersOnTheirOwnModelCheck: { }
14
+ LawOfDemeterCheck: { }
@@ -0,0 +1,37 @@
1
+ require File.join(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe RailsBestPractices::Checks::LawOfDemeterCheck do
4
+ before(:each) do
5
+ @runner = RailsBestPractices::Core::Runner.new(RailsBestPractices::Checks::LawOfDemeterCheck.new)
6
+
7
+ content = <<-EOF
8
+ class Invoice < ActiveRecord::Base
9
+ belongs_to :user
10
+ end
11
+ EOF
12
+ @runner.check('app/models/invoice.rb', content)
13
+ end
14
+
15
+ it "should law of demeter" do
16
+ content = <<-EOF
17
+ <%= @invoice.user.name %>
18
+ <%= @invoice.user.address %>
19
+ <%= @invoice.user.cellphone %>
20
+ EOF
21
+ @runner.check('app/views/invoices/show.html.erb', content)
22
+ errors = @runner.errors
23
+ errors.should_not be_empty
24
+ errors[0].to_s.should == "app/views/invoices/show.html.erb:1 - law of demeter"
25
+ end
26
+
27
+ it "should no law of demeter" do
28
+ content = <<-EOF
29
+ <%= @invoice.user_name %>
30
+ <%= @invoice.user_address %>
31
+ <%= @invoice.user_cellphone %>
32
+ EOF
33
+ @runner.check('app/views/invoices/show.html.erb', content)
34
+ errors = @runner.errors
35
+ errors.should be_empty
36
+ end
37
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_best_practices
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
@@ -53,6 +53,7 @@ files:
53
53
  - lib/rails_best_practices/checks/add_model_virtual_attribute_check.rb
54
54
  - lib/rails_best_practices/checks/check.rb
55
55
  - lib/rails_best_practices/checks/keep_finders_on_their_own_model_check.rb
56
+ - lib/rails_best_practices/checks/law_of_demeter_check.rb
56
57
  - lib/rails_best_practices/checks/many_to_many_collection_check.rb
57
58
  - lib/rails_best_practices/checks/move_finder_to_named_scope_check.rb
58
59
  - lib/rails_best_practices/checks/move_model_logic_into_model_check.rb
@@ -75,6 +76,7 @@ files:
75
76
  - rails_best_practices.yml
76
77
  - spec/rails_best_practices/checks/add_model_virtual_attribute_check_spec.rb
77
78
  - spec/rails_best_practices/checks/keep_finders_on_their_own_model_check_spec.rb
79
+ - spec/rails_best_practices/checks/law_of_demeter_check_spec.rb
78
80
  - spec/rails_best_practices/checks/many_to_many_collection_check_spec.rb
79
81
  - spec/rails_best_practices/checks/move_finder_to_named_scope_check_spec.rb
80
82
  - spec/rails_best_practices/checks/move_model_logic_into_model_check_spec.rb
@@ -125,6 +127,7 @@ test_files:
125
127
  - spec/rails_best_practices/checks/many_to_many_collection_check_spec.rb
126
128
  - spec/rails_best_practices/checks/use_model_association_check_spec.rb
127
129
  - spec/rails_best_practices/checks/move_model_logic_into_model_check_spec.rb
130
+ - spec/rails_best_practices/checks/law_of_demeter_check_spec.rb
128
131
  - spec/rails_best_practices/checks/replace_complex_creation_with_factory_method_check_spec.rb
129
132
  - spec/rails_best_practices/checks/nested_model_forms_check_spec.rb
130
133
  - spec/rails_best_practices/checks/move_finder_to_named_scope_check_spec.rb