rails_best_practices 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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