rails_best_practices 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/Gemfile +4 -0
  2. data/Gemfile.lock +18 -3
  3. data/Guardfile +19 -0
  4. data/README.md +5 -13
  5. data/lib/rails_best_practices.rb +3 -2
  6. data/lib/rails_best_practices/core.rb +1 -0
  7. data/lib/rails_best_practices/core/check.rb +37 -6
  8. data/lib/rails_best_practices/core/routes.rb +3 -1
  9. data/lib/rails_best_practices/core/runner.rb +4 -7
  10. data/lib/rails_best_practices/core_ext/erubis.rb +36 -0
  11. data/lib/rails_best_practices/core_ext/sexp.rb +1 -1
  12. data/lib/rails_best_practices/prepares/controller_prepare.rb +1 -1
  13. data/lib/rails_best_practices/prepares/model_prepare.rb +12 -6
  14. data/lib/rails_best_practices/prepares/route_prepare.rb +35 -16
  15. data/lib/rails_best_practices/prepares/schema_prepare.rb +2 -2
  16. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +3 -3
  17. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +2 -2
  18. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +3 -3
  19. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +25 -5
  20. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +7 -5
  21. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +3 -3
  22. data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +1 -1
  23. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +1 -1
  24. data/lib/rails_best_practices/version.rb +1 -1
  25. data/rails_best_practices.gemspec +4 -1
  26. data/spec/rails_best_practices/core/routes_spec.rb +12 -0
  27. data/spec/rails_best_practices/core_ext/erubis_spec.rb +24 -0
  28. data/spec/rails_best_practices/core_ext/sexp_spec.rb +5 -0
  29. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +60 -0
  30. data/spec/rails_best_practices/prepares/route_prepare_spec.rb +67 -27
  31. data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +68 -1
  32. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +1 -1
  33. data/spec/spec_helper.rb +20 -11
  34. metadata +65 -29
@@ -17,14 +17,14 @@ module RailsBestPractices
17
17
 
18
18
  def start_command(node)
19
19
  if "create_table" == node.message.to_s
20
- @last_klazz = node.arguments.all[0].to_s.classify
20
+ @last_klazz = node.arguments.all.first.to_s.classify
21
21
  end
22
22
  end
23
23
 
24
24
  # check command_call node to remember the model attributes.
25
25
  def start_command_call(node)
26
26
  if ATTRIBUTE_TYPES.include? node.message.to_s
27
- attribute_name = node.arguments.all[0].to_s
27
+ attribute_name = node.arguments.all.first.to_s
28
28
  @model_attributes.add_attribute(@last_klazz, attribute_name, node.message.to_s)
29
29
  end
30
30
  end
@@ -88,7 +88,7 @@ module RailsBestPractices
88
88
  private
89
89
  # remember the node as index columns
90
90
  def remember_index_columns(node)
91
- table_name = node.arguments.all[0].to_s
91
+ table_name = node.arguments.all.first.to_s
92
92
  index_column = node.arguments.all[1].to_object
93
93
 
94
94
  @index_columns[table_name] ||= []
@@ -97,7 +97,7 @@ module RailsBestPractices
97
97
 
98
98
  # remember table nodes
99
99
  def remember_table_nodes(node)
100
- @table_name = node.arguments.all[0].to_s
100
+ @table_name = node.arguments.all.first.to_s
101
101
  @table_nodes[@table_name] = node
102
102
  end
103
103
 
@@ -105,7 +105,7 @@ module RailsBestPractices
105
105
  # remember foreign key columns
106
106
  def remember_foreign_key_columns(node)
107
107
  table_name = @table_name
108
- foreign_key_column = node.arguments.all[0].to_s
108
+ foreign_key_column = node.arguments.all.first.to_s
109
109
  @foreign_keys[table_name] ||= []
110
110
  if foreign_key_column =~ /(.*?)_id$/
111
111
  if @foreign_keys[table_name].delete("#{$1}_type")
@@ -48,8 +48,8 @@ module RailsBestPractices
48
48
  # then it is complext.
49
49
  def complex_select_options?(node)
50
50
  "options_for_select" == node[1].message.to_s &&
51
- :array == node.arguments.all[0].sexp_type &&
52
- node.arguments.all[0].array_size > @array_count
51
+ :array == node.arguments.all.first.sexp_type &&
52
+ node.arguments.all.first.array_size > @array_count
53
53
  end
54
54
  end
55
55
  end
@@ -29,8 +29,8 @@ module RailsBestPractices
29
29
  # check all command call nodes, compare with rails2 default route
30
30
  def start_command_call(node)
31
31
  if "map" == node.subject.to_s && "connect" == node.message.to_s &&
32
- (":controller/:action/:id" == node.arguments.all[0].to_s ||
33
- ":controller/:action/:id.:format" == node.arguments.all[0].to_s)
32
+ (":controller/:action/:id" == node.arguments.all.first.to_s ||
33
+ ":controller/:action/:id.:format" == node.arguments.all.first.to_s)
34
34
  add_error "not use default route"
35
35
  end
36
36
  end
@@ -38,7 +38,7 @@ module RailsBestPractices
38
38
  # check all command nodes, compare with rails3 default route
39
39
  def start_command(node)
40
40
  if "match" == node.message.to_s &&
41
- ":controller(/:action(/:id(.:format)))" == node.arguments.all[0].to_s
41
+ ":controller(/:action(/:id(.:format)))" == node.arguments.all.first.to_s
42
42
  add_error "not use default route"
43
43
  end
44
44
  end
@@ -7,19 +7,19 @@ module RailsBestPractices
7
7
  include Klassable
8
8
  include Completeable
9
9
  include Callable
10
+ include Exceptable
10
11
  include InheritedResourcesable
11
12
 
12
- interesting_nodes :class
13
- interesting_files CONTROLLER_FILES
13
+ interesting_nodes :class, :command, :method_add_arg
14
+ interesting_files CONTROLLER_FILES, VIEW_FILES
14
15
 
15
- EXCEPT_METHODS = %w(rescue_action)
16
16
  INHERITED_RESOURCES_METHODS = %w(resource collection begin_of_association_chain build_resource)
17
17
 
18
18
  def initialize(options={})
19
+ super
19
20
  @controller_methods = Prepares.controller_methods
20
21
  @routes = Prepares.routes
21
22
  @inherited_resources = false
22
- @except_methods = EXCEPT_METHODS + options['except_methods']
23
23
  end
24
24
 
25
25
  # mark custom inherited_resources methods as used.
@@ -31,6 +31,22 @@ module RailsBestPractices
31
31
  end
32
32
  end
33
33
 
34
+ def start_command(node)
35
+ case node.message.to_s
36
+ when "render_cell"
37
+ controller_name, action_name, _ = *node.arguments.all.map(&:to_s)
38
+ call_method(action_name, "#{controller_name}_cell".classify)
39
+ when "render"
40
+ first_argument = node.arguments.all.first
41
+ if first_argument.present? && first_argument.hash_value("state").present?
42
+ action_name = first_argument.hash_value("state").to_s
43
+ call_method(action_name, current_class_name)
44
+ end
45
+ end
46
+ end
47
+
48
+ alias :start_method_add_arg :start_command
49
+
34
50
  # get all unused methods at the end of review process.
35
51
  def on_complete
36
52
  @routes.each do |route|
@@ -42,7 +58,7 @@ module RailsBestPractices
42
58
  end
43
59
  end
44
60
  @controller_methods.get_all_unused_methods.each do |method|
45
- if !@except_methods.include?(method.method_name)
61
+ if !excepted?(method)
46
62
  add_error "remove unused methods (#{method.class_name}##{method.method_name})", method.file, method.line
47
63
  end
48
64
  end
@@ -52,6 +68,10 @@ module RailsBestPractices
52
68
  def methods
53
69
  @controller_methods
54
70
  end
71
+
72
+ def internal_except_methods
73
+ %w(rescue_action).map { |method_name| "*\##{method_name}" }
74
+ end
55
75
  end
56
76
  end
57
77
  end
@@ -15,21 +15,19 @@ module RailsBestPractices
15
15
  include Klassable
16
16
  include Completeable
17
17
  include Callable
18
+ include Exceptable
18
19
 
19
20
  interesting_files ALL_FILES
20
21
 
21
- EXCEPT_METHODS = %w(initialize validate to_xml to_json assign_attributes after_find after_initialize)
22
-
23
22
  def initialize(options={})
24
- super()
23
+ super
25
24
  @model_methods = Prepares.model_methods
26
- @except_methods = EXCEPT_METHODS + options['except_methods']
27
25
  end
28
26
 
29
27
  # get all unused methods at the end of review process.
30
28
  def on_complete
31
29
  @model_methods.get_all_unused_methods.each do |method|
32
- if !@except_methods.include?(method.method_name) && method.method_name !~ /=$/
30
+ if !excepted?(method) && method.method_name !~ /=$/
33
31
  add_error "remove unused methods (#{method.class_name}##{method.method_name})", method.file, method.line
34
32
  end
35
33
  end
@@ -39,6 +37,10 @@ module RailsBestPractices
39
37
  def methods
40
38
  @model_methods
41
39
  end
40
+
41
+ def internal_except_methods
42
+ %w(initialize validate to_xml to_json assign_attributes after_find after_initialize).map { |method_name| "*\##{method_name}" }
43
+ end
42
44
  end
43
45
  end
44
46
  end
@@ -42,7 +42,7 @@ module RailsBestPractices
42
42
  # remember the namespace.
43
43
  def start_method_add_block(node)
44
44
  if "namespace" == node.message.to_s
45
- @namespaces << node.arguments.all[0].to_s
45
+ @namespaces << node.arguments.all.first.to_s
46
46
  end
47
47
  end
48
48
 
@@ -86,10 +86,10 @@ module RailsBestPractices
86
86
  if hash_key_exist?(option_node,"controller")
87
87
  name = option_node.hash_value("controller").to_s
88
88
  else
89
- name = node.arguments.all[0].to_s.tableize
89
+ name = node.arguments.all.first.to_s.tableize
90
90
  end
91
91
  else
92
- name = node.arguments.all[0].to_s.tableize
92
+ name = node.arguments.all.first.to_s.tableize
93
93
  end
94
94
  namespaced_class_name(name)
95
95
  end
@@ -26,7 +26,7 @@ module RailsBestPractices
26
26
  # then it should be replaced by simplified syntax.
27
27
  def start_command(node)
28
28
  if "render" == node.message.to_s
29
- keys = node.arguments.all[0].hash_keys
29
+ keys = node.arguments.all.first.hash_keys
30
30
  if keys && keys.size == 1 &&
31
31
  (keys.include?("action") || keys.include?("template") || keys.include?("file"))
32
32
  add_error 'simplify render in controllers'
@@ -25,7 +25,7 @@ module RailsBestPractices
25
25
  # then it should be replaced by simplified syntax.
26
26
  def start_command(node)
27
27
  if "render" == node.message.to_s
28
- hash_node = node.arguments.all[0]
28
+ hash_node = node.arguments.all.first
29
29
  if hash_node && :bare_assoc_hash == hash_node.sexp_type &&
30
30
  hash_node.hash_keys.include?("partial") &&
31
31
  !hash_node.hash_value("partial").to_s.include?('/')
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module RailsBestPractices
3
- VERSION = "1.4.0"
3
+ VERSION = "1.5.0"
4
4
  end
@@ -22,9 +22,12 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_development_dependency("rake")
24
24
  s.add_development_dependency("rspec")
25
- s.add_development_dependency("watchr")
26
25
  s.add_development_dependency("haml")
27
26
  s.add_development_dependency("bundler")
27
+ s.add_development_dependency("spork", "0.9.0.rc9")
28
+ s.add_development_dependency("guard")
29
+ s.add_development_dependency("guard-spork")
30
+ s.add_development_dependency("guard-rspec")
28
31
 
29
32
  s.files = `git ls-files`.split("\n")
30
33
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -7,4 +7,16 @@ describe RailsBestPractices::Core::Routes do
7
7
  routes.add_route(["admin", "test"], "posts", "new")
8
8
  routes.map(&:to_s).should == ["Admin::Test::PostsController#new"]
9
9
  end
10
+
11
+ context "route" do
12
+ it "should add namesapces, controller name and action name" do
13
+ route = RailsBestPractices::Core::Route.new(['admin', 'test'], 'posts', 'new')
14
+ route.to_s.should == "Admin::Test::PostsController#new"
15
+ end
16
+
17
+ it "should add controller name with namespace" do
18
+ route = RailsBestPractices::Core::Route.new(['admin'], 'test/posts', 'new')
19
+ route.to_s.should == "Admin::Test::PostsController#new"
20
+ end
21
+ end
10
22
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Erubis::OnlyRuby do
4
+ subject {
5
+ content =<<-EOF
6
+ <h1>Title</h1>
7
+ <% if current_user %>
8
+ <%= link_to 'account', edit_user_path(current_user) %>
9
+ <%= "Hello \#{current_user.email}" %>
10
+ <% else %>
11
+ Not logged in
12
+ <% end %>
13
+ EOF
14
+ Erubis::OnlyRuby.new(content).src
15
+ }
16
+
17
+ it { should_not include("h1") }
18
+ it { should_not include("Title") }
19
+ it { should_not include("Not logged in") }
20
+ it { should include("current_user") }
21
+ it { should include("if") }
22
+ it { should include("else") }
23
+ it { should include("end") }
24
+ end
@@ -463,6 +463,11 @@ describe Sexp do
463
463
  node.to_object.should == ["first_name", "last_name"]
464
464
  end
465
465
 
466
+ it "should to array with symbols" do
467
+ node = parse_content("[:first_name, :last_name]").grep_node(:sexp_type => :array)
468
+ node.to_object.should == ["first_name", "last_name"]
469
+ end
470
+
466
471
  it "should to empty array" do
467
472
  node = parse_content("[]").grep_node(:sexp_type => :array)
468
473
  node.to_object.should == []
@@ -94,6 +94,44 @@ describe RailsBestPractices::Prepares::ModelPrepare do
94
94
  model_associations.get_association("Citizen", "nations").should == {"meta" => "has_and_belongs_to_many", "class_name" => "Country"}
95
95
  end
96
96
  end
97
+
98
+ context "mongoid embed" do
99
+ it "should parse embeds_many" do
100
+ content =<<-EOF
101
+ class Person
102
+ include Mongoid::Document
103
+ embeds_many :addresses
104
+ end
105
+ EOF
106
+ runner.prepare("app/models/person.rb", content)
107
+ model_associations = RailsBestPractices::Prepares.model_associations
108
+ model_associations.get_association("Person", "addresses").should == {"meta" => "embeds_many", "class_name" => "Address"}
109
+ end
110
+
111
+ it "should parse embeds_one" do
112
+ content =<<-EOF
113
+ class Lush
114
+ include Mongoid::Document
115
+ embeds_one :whiskey, class_name: "Drink", inverse_of: :alcoholic
116
+ end
117
+ EOF
118
+ runner.prepare("app/models/lush.rb", content)
119
+ model_associations = RailsBestPractices::Prepares.model_associations
120
+ model_associations.get_association("Lush", "whiskey").should == {"meta" => "embeds_one", "class_name" => "Drink"}
121
+ end
122
+
123
+ it "should parse embedded_in" do
124
+ content =<<-EOF
125
+ class Drink
126
+ include Mongoid::Document
127
+ embedded_in :alcoholic, class_name: "Lush", inverse_of: :whiskey
128
+ end
129
+ EOF
130
+ runner.prepare("app/models/drink.rb", content)
131
+ model_associations = RailsBestPractices::Prepares.model_associations
132
+ model_associations.get_association("Drink", "alcoholic").should == {"meta" => "embedded_in", "class_name" => "Lush"}
133
+ end
134
+ end
97
135
  end
98
136
 
99
137
  context "methods" do
@@ -216,6 +254,28 @@ describe RailsBestPractices::Prepares::ModelPrepare do
216
254
  end
217
255
  end
218
256
 
257
+ context "attributes" do
258
+ it "should parse mongoid field" do
259
+ content =<<-EOF
260
+ class Post
261
+ include Mongoid::Document
262
+ field :title
263
+ field :tags, type: Array
264
+ field :comments_count, type: Integer
265
+ field :deleted_at, type: DateTime
266
+ field :active, type: Boolean
267
+ end
268
+ EOF
269
+ runner.prepare("app/models/post.rb", content)
270
+ model_attributes = RailsBestPractices::Prepares.model_attributes
271
+ model_attributes.get_attribute_type("Post", "title").should == "String"
272
+ model_attributes.get_attribute_type("Post", "tags").should == "Array"
273
+ model_attributes.get_attribute_type("Post", "comments_count").should == "Integer"
274
+ model_attributes.get_attribute_type("Post", "deleted_at").should == "DateTime"
275
+ model_attributes.get_attribute_type("Post", "active").should == "Boolean"
276
+ end
277
+ end
278
+
219
279
  context "no error" do
220
280
  it "should raised for finder_sql option" do
221
281
  content =<<-EOF
@@ -242,6 +242,19 @@ describe RailsBestPractices::Prepares::RoutePrepare do
242
242
  routes = RailsBestPractices::Prepares.routes
243
243
  routes.map(&:to_s).should == ["SessionsController#new"]
244
244
  end
245
+
246
+ it "should add named route with with_options" do
247
+ content =<<-EOF
248
+ ActionController::Routing::Routes.draw do |map|
249
+ map.with_options(:controller => "admin_session") do |session|
250
+ session.login '/login', :action => 'new', :method => :get
251
+ end
252
+ end
253
+ EOF
254
+ runner.prepare('config/routes.rb', content)
255
+ routes = RailsBestPractices::Prepares.routes
256
+ routes.map(&:to_s).should == ["AdminSessionController#new"]
257
+ end
245
258
  end
246
259
 
247
260
  context "rails3" do
@@ -464,39 +477,66 @@ describe RailsBestPractices::Prepares::RoutePrepare do
464
477
  routes = RailsBestPractices::Prepares.routes
465
478
  routes.map(&:to_s).should == ["Admin::Test::PostsController#index"]
466
479
  end
480
+ end
467
481
 
468
- it "should add match route" do
469
- content =<<-EOF
470
- RailsBestPracticesCom::Application.routes.draw do
471
- match '/auth/:provider/callback' => 'authentications#create'
472
- end
473
- EOF
474
- runner.prepare('config/routes.rb', content)
475
- routes = RailsBestPractices::Prepares.routes
476
- routes.map(&:to_s).should == ["AuthenticationsController#create"]
482
+ it "should add route for direct get/post" do
483
+ content =<<-EOF
484
+ RailsBestPracticesCom::Application.routes.draw do
485
+ get 'posts/show'
486
+ post '/posts' => 'posts#create'
487
+ put '/posts/:id' => 'posts#update'
488
+ delete '/post/:id' => 'posts#destroy'
489
+ get '/agb' => 'high_voltage/pages#show', :id => 'agb'
477
490
  end
491
+ EOF
492
+ runner.prepare('config/routes.rb', content)
493
+ routes = RailsBestPractices::Prepares.routes
494
+ routes.size.should == 5
495
+ routes.map(&:to_s).should == ["PostsController#show", "PostsController#create", "PostsController#update", "PostsController#destroy", "HighVoltage::PagesController#show"]
496
+ end
478
497
 
479
- it "should add match route with all actions" do
480
- content =<<-EOF
481
- RailsBestPracticesCom::Application.routes.draw do
482
- match 'internal/:action/*whatever', :controller => "internal"
483
- end
484
- EOF
485
- runner.prepare('config/routes.rb', content)
486
- routes = RailsBestPractices::Prepares.routes
487
- routes.map(&:to_s).should == ["InternalController#*"]
498
+ it "should add match route" do
499
+ content =<<-EOF
500
+ RailsBestPracticesCom::Application.routes.draw do
501
+ match '/auth/:provider/callback' => 'authentications#create'
488
502
  end
503
+ EOF
504
+ runner.prepare('config/routes.rb', content)
505
+ routes = RailsBestPractices::Prepares.routes
506
+ routes.map(&:to_s).should == ["AuthenticationsController#create"]
507
+ end
489
508
 
490
- it "should add root route" do
491
- content =<<-EOF
492
- RailsBestPracticesCom::Application.routes.draw do
493
- root :to => 'home#index'
494
- end
495
- EOF
496
- runner.prepare('config/routes.rb', content)
497
- routes = RailsBestPractices::Prepares.routes
498
- routes.map(&:to_s).should == ["HomeController#index"]
509
+ it "should add match route with all actions" do
510
+ content =<<-EOF
511
+ RailsBestPracticesCom::Application.routes.draw do
512
+ match 'internal/:action/*whatever', :controller => "internal"
513
+ end
514
+ EOF
515
+ runner.prepare('config/routes.rb', content)
516
+ routes = RailsBestPractices::Prepares.routes
517
+ routes.map(&:to_s).should == ["InternalController#*"]
518
+ end
519
+
520
+ it "should add root route" do
521
+ content =<<-EOF
522
+ RailsBestPracticesCom::Application.routes.draw do
523
+ root :to => 'home#index'
524
+ end
525
+ EOF
526
+ runner.prepare('config/routes.rb', content)
527
+ routes = RailsBestPractices::Prepares.routes
528
+ routes.map(&:to_s).should == ["HomeController#index"]
529
+ end
530
+
531
+ it "should do nothing for default route" do
532
+ content =<<-EOF
533
+ RailsBestPracticesCom::Application.routes.draw do
534
+ match ':controller(/:action(/:id(.:format)))'
499
535
  end
536
+ EOF
537
+ runner.prepare('config/routes.rb', content)
538
+ routes = RailsBestPractices::Prepares.routes
539
+ routes.size.should == 0
500
540
  end
501
541
  end
502
542
  end