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 +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
|