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 +3 -2
- data/VERSION +1 -1
- data/lib/rails_best_practices/checks.rb +1 -0
- data/lib/rails_best_practices/checks/check.rb +1 -1
- data/lib/rails_best_practices/checks/keep_finders_on_their_own_model_check.rb +3 -0
- data/lib/rails_best_practices/checks/law_of_demeter_check.rb +42 -0
- data/lib/rails_best_practices/command.rb +3 -3
- data/lib/rails_best_practices/core/core_ext.rb +12 -1
- data/lib/rails_best_practices/core/runner.rb +4 -0
- data/lib/rails_best_practices/core/visitable_sexp.rb +8 -3
- data/rails_best_practices.gemspec +4 -1
- data/rails_best_practices.yml +1 -0
- data/spec/rails_best_practices/checks/law_of_demeter_check_spec.rb +37 -0
- metadata +4 -1
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
|
+
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'
|
@@ -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, '**', "
|
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
|
-
|
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.
|
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",
|
data/rails_best_practices.yml
CHANGED
@@ -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.
|
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
|