rails_best_practices 1.4.0 → 1.5.0

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