rails_best_practices 0.10.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/README.md +20 -0
  2. data/assets/result.html.haml +1 -1
  3. data/lib/rails_best_practices.rb +5 -3
  4. data/lib/rails_best_practices/core.rb +1 -1
  5. data/lib/rails_best_practices/core/check.rb +9 -12
  6. data/lib/rails_best_practices/core/checking_visitor.rb +9 -6
  7. data/lib/rails_best_practices/core/model_associations.rb +1 -1
  8. data/lib/rails_best_practices/core/nil.rb +5 -1
  9. data/lib/rails_best_practices/core/runner.rb +5 -5
  10. data/lib/rails_best_practices/core_ext/sexp.rb +688 -0
  11. data/lib/rails_best_practices/prepares/mailer_prepare.rb +4 -5
  12. data/lib/rails_best_practices/prepares/model_prepare.rb +16 -26
  13. data/lib/rails_best_practices/prepares/schema_prepare.rb +11 -17
  14. data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +24 -75
  15. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +39 -113
  16. data/lib/rails_best_practices/reviews/dry_bundler_in_capistrano_review.rb +6 -16
  17. data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +16 -32
  18. data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +11 -20
  19. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +7 -28
  20. data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +16 -14
  21. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +10 -28
  22. data/lib/rails_best_practices/reviews/move_code_into_model_review.rb +12 -11
  23. data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +13 -24
  24. data/lib/rails_best_practices/reviews/move_model_logic_into_model_review.rb +9 -9
  25. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +24 -68
  26. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +15 -22
  27. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +31 -91
  28. data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +4 -2
  29. data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +20 -18
  30. data/lib/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review.rb +5 -3
  31. data/lib/rails_best_practices/reviews/review.rb +8 -37
  32. data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +10 -6
  33. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +9 -6
  34. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +14 -72
  35. data/lib/rails_best_practices/reviews/use_model_association_review.rb +19 -31
  36. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +5 -5
  37. data/lib/rails_best_practices/reviews/use_observer_review.rb +22 -40
  38. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +34 -39
  39. data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +14 -38
  40. data/lib/rails_best_practices/reviews/use_scope_access_review.rb +13 -44
  41. data/lib/rails_best_practices/version.rb +1 -1
  42. data/spec/rails_best_practices/core/check_spec.rb +5 -5
  43. data/spec/rails_best_practices/core/checking_visitor_spec.rb +4 -4
  44. data/spec/rails_best_practices/core/model_associations_spec.rb +4 -4
  45. data/spec/rails_best_practices/core/nil_spec.rb +7 -1
  46. data/spec/rails_best_practices/core_ext/sexp_spec.rb +430 -0
  47. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +12 -12
  48. data/spec/rails_best_practices/prepares/schema_prepare_spec.rb +6 -6
  49. data/spec/rails_best_practices/reviews/move_code_into_controller_review_spec.rb +14 -2
  50. data/spec/rails_best_practices/reviews/move_code_into_helper_review_spec.rb +1 -1
  51. data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +3 -3
  52. data/spec/rails_best_practices/reviews/not_use_default_route_review_spec.rb +1 -1
  53. data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +15 -1
  54. data/spec/rails_best_practices/reviews/simplify_render_in_controllers_review_spec.rb +3 -3
  55. data/spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb +1 -1
  56. data/spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb +1 -1
  57. data/spec/rails_best_practices/reviews/use_scope_access_review_spec.rb +4 -4
  58. data/spec/rails_best_practices_spec.rb +1 -3
  59. data/spec/spec_helper.rb +4 -0
  60. metadata +6 -8
  61. data/lib/rails_best_practices/core/visitable_sexp.rb +0 -444
  62. data/spec/rails_best_practices/core/visitable_sexp_spec.rb +0 -272
  63. data/spec/rails_best_practices/reviews/review_spec.rb +0 -11
@@ -15,17 +15,16 @@ module RailsBestPractices
15
15
  end
16
16
 
17
17
  def initialize
18
- @mailers = Core::Mailers.new
18
+ @mailers = Prepares.mailers
19
19
  end
20
20
 
21
21
  # check class node.
22
22
  #
23
23
  # if it is a subclass of ActionMailer::Base,
24
24
  # then remember its class name.
25
- def start_class(class_node)
26
- if s(:colon2, s(:const, :ActionMailer), :Base) == class_node.base_class
27
- @mailers << class_node.class_name.to_s
28
- Prepares.mailers = @mailers
25
+ def start_class(node)
26
+ if "ActionMailer::Base" == node.base_class.to_s
27
+ @mailers << node.class_name.to_s
29
28
  end
30
29
  end
31
30
  end
@@ -5,9 +5,10 @@ module RailsBestPractices
5
5
  module Prepares
6
6
  # Remember the model associations.
7
7
  class ModelPrepare < Core::Check
8
+ ASSOCIATION_METHODS = %w(belongs_to has_one has_many has_and_belongs_to_many)
8
9
 
9
10
  def interesting_nodes
10
- [:class, :call]
11
+ [:class, :command]
11
12
  end
12
13
 
13
14
  def interesting_files
@@ -15,23 +16,17 @@ module RailsBestPractices
15
16
  end
16
17
 
17
18
  def initialize
18
- @models = Core::Models.new
19
- @model_associations = Core::ModelAssociations.new
19
+ @models = Prepares.models
20
+ @model_associations = Prepares.model_associations
20
21
  end
21
22
 
22
23
  # check class node to remember the last class name.
23
- def start_class(class_node)
24
- @last_klazz = class_node.class_name.to_s
24
+ def start_class(node)
25
+ @last_klazz= node.class_name.to_s
25
26
  @models << @last_klazz
26
27
  end
27
28
 
28
- # assign @model_associations to Prepares.model_associations.
29
- def end_class(class_node)
30
- Prepares.models = @models
31
- Prepares.model_associations = @model_associations
32
- end
33
-
34
- # check call node to remember all assoications.
29
+ # check command node to remember all assoications.
35
30
  #
36
31
  # the remembered association names (@associations) are like
37
32
  # {
@@ -42,26 +37,21 @@ module RailsBestPractices
42
37
  # "milestones => {:has_many" => "Milestone"}
43
38
  # }
44
39
  # }
45
- def start_call(node)
46
- remember_association(node) if association_methods.include? node.message
40
+ def start_command(node)
41
+ remember_association(node) if ASSOCIATION_METHODS.include? node.message.to_s
47
42
  end
48
43
 
49
44
  # remember associations, with class to association names.
50
- def remember_association(association_node)
51
- association_meta = association_node.message
52
- association_name = association_node.arguments[1].to_s
53
- arguments_node = association_node.arguments[2]
54
- if arguments_node && :hash == arguments_node.node_type
55
- index = arguments_node.index(s(:lit, :class_name))
56
- association_class = arguments_node[index + 1].to_s if index
45
+ def remember_association(node)
46
+ association_meta = node.message.to_s
47
+ association_name = node.arguments.all[0].to_s
48
+ arguments_node = node.arguments.all[1]
49
+ if arguments_node && :bare_assoc_hash == arguments_node.sexp_type
50
+ association_class = arguments_node.hash_value("class_name").to_s
57
51
  end
52
+ association_class ||= association_name.classify
58
53
  @model_associations.add_association(@last_klazz, association_name, association_meta, association_class)
59
54
  end
60
-
61
- # default rails association methods.
62
- def association_methods
63
- [:belongs_to, :has_one, :has_many, :has_and_belongs_to_many]
64
- end
65
55
  end
66
56
  end
67
57
  end
@@ -6,10 +6,10 @@ module RailsBestPractices
6
6
  # Remember the model attributes.
7
7
  class SchemaPrepare < Core::Check
8
8
  # all attribute types
9
- ATTRIBUTE_TYPES = [:integer, :float, :boolean, :string, :text, :date, :time, :datetime, :binary]
9
+ ATTRIBUTE_TYPES = %w(integer float boolean string text date time datetime binary)
10
10
 
11
11
  def interesting_nodes
12
- [:call]
12
+ [:command, :command_call]
13
13
  end
14
14
 
15
15
  def interesting_files
@@ -17,26 +17,20 @@ module RailsBestPractices
17
17
  end
18
18
 
19
19
  def initialize
20
- @model_attributes = Core::ModelAttributes.new
20
+ @model_attributes = Prepares.model_attributes
21
21
  end
22
22
 
23
- # check call node to remember the model attributes.
24
- def start_call(call_node)
25
- case call_node.message
26
- when :create_table
27
- @last_klazz = call_node.arguments[1].to_s.classify
28
- when *ATTRIBUTE_TYPES
29
- attribute_name = call_node.arguments[1].to_s
30
- @model_attributes.add_attribute(@last_klazz, attribute_name, call_node.message)
31
- else
32
- # nothing to do
23
+ def start_command(node)
24
+ if "create_table" == node.message.to_s
25
+ @last_klazz = node.arguments.all[0].to_s.classify
33
26
  end
34
27
  end
35
28
 
36
- # assign @model_attributes to Prepares.model_attributes.
37
- def end_call(call_node)
38
- if :create_table == call_node.message
39
- Prepares.model_attributes = @model_attributes
29
+ # check command_call node to remember the model attributes.
30
+ def start_command_call(node)
31
+ if ATTRIBUTE_TYPES.include? node.message.to_s
32
+ attribute_name = node.arguments.all[0].to_s
33
+ @model_attributes.add_attribute(@last_klazz, attribute_name, node.message.to_s)
40
34
  end
41
35
  end
42
36
  end
@@ -13,15 +13,7 @@ module RailsBestPractices
13
13
  # check method define nodes in all controller files,
14
14
  # if there are more than one [] method calls with the same subject and arguments,
15
15
  # but assigned to one model's different attribute.
16
- # and after these method calls, there is a save method call for that model, like
17
- #
18
- # def create
19
- # @user = User.new(params[:user])
20
- # @user.first_name = params[:full_name].split(' ', 2).first
21
- # @user.last_name = params[:full_name].split(' ', 2).last
22
- # @user.save
23
- # end
24
- #
16
+ # and after these method calls, there is a save method call for that model,
25
17
  # then the model needs to add a virtual attribute.
26
18
  class AddModelVirtualAttributeReview < Review
27
19
  def url
@@ -29,7 +21,7 @@ module RailsBestPractices
29
21
  end
30
22
 
31
23
  def interesting_nodes
32
- [:defn]
24
+ [:def]
33
25
  end
34
26
 
35
27
  def interesting_files
@@ -40,89 +32,46 @@ module RailsBestPractices
40
32
  #
41
33
  # it will check every attribute assignment nodes and call node of message :save or :save!, if
42
34
  #
43
- # 1. there are more than one arguments who contain call node with messages :[] in attribute assignment nodes, e.g.
44
- # @user.first_name = params[:full_name].split(' ').first
45
- # @user.last_name = params[:full_name].split(' ').last
35
+ # 1. there are more than one arguments who contain array reference node in the right value of assignment nodes,
46
36
  # 2. the messages of attribute assignment nodes housld be different (:first_name= , :last_name=)
47
37
  # 3. the argument of call nodes with message :[] should be same (:full_name)
48
38
  # 4. there should be a call node with message :save or :save! after attribute assignment nodes
49
- # @user.save
50
39
  # 5. and the subject of save or save! call node should be the same with the subject of attribute assignment nodes
51
40
  #
52
41
  # then the attribute assignment nodes can add model virtual attribute instead.
53
- def start_defn(node)
54
- @attrasgns = {}
42
+ def start_def(node)
43
+ @assignments = {}
55
44
  node.recursive_children do |child|
56
- case child.node_type
57
- when :attrasgn
58
- attribute_assignment(child)
45
+ case child.sexp_type
46
+ when :assign
47
+ assign(child)
59
48
  when :call
60
49
  call_assignment(child)
61
- else
62
50
  end
63
51
  end
64
52
  end
65
53
 
66
54
  private
67
- # check an attribute assignment node, if there is a :[] message of call node in the attribute assignment node,
55
+ # check an attribute assignment node, if there is a array reference node in the right value of assignment node,
68
56
  # then remember this attribute assignment.
69
- #
70
- # s(:attrasgn, s(:ivar, :@user), :first_name=,
71
- # s(:arglist,
72
- # s(:call,
73
- # s(:call,
74
- # s(:call, s(:call, nil, :params, s(:arglist)), :[], s(:arglist, s(:lit, :full_name))),
75
- # :split,
76
- # s(:arglist, s(:str, " "), s(:lit, 2))
77
- # ),
78
- # :first,
79
- # s(:arglist)
80
- # )
81
- # )
82
- # )
83
- #
84
- # The remember attribute assignments (@attrasgns) are as follows
85
- #
86
- # {
87
- # s(:ivar, :@user) =>
88
- # [{
89
- # :message=>:first_name=,
90
- # :arguments=>s(:call, s(:call, nil, :params, s(:arglist)), :[], s(:arglist, s(:lit, :full_name)))
91
- # }]
92
- # }
93
- def attribute_assignment(node)
94
- subject = node.subject
95
- arguments_node = node.arguments.grep_node(:message => :[])
96
- return if subject.nil? or arguments_node.nil?
97
- attrasgns(subject) << {:message => node.message, :arguments => arguments_node}
57
+ def assign(node)
58
+ left_value = node.left_value
59
+ right_value = node.right_value
60
+ return unless :field == left_value.sexp_type && :call == right_value.sexp_type
61
+ aref_node = right_value.grep_node(:sexp_type => :aref)
62
+ if aref_node
63
+ assignments(left_value.subject.to_s) << {:message => left_value.message.to_s, :arguments => aref_node.to_s}
64
+ end
98
65
  end
99
66
 
100
- # check a call node with message :save or :save!,
67
+ # check a call node with message "save" or "save!",
101
68
  # if there exists an attribute assignment for the subject of this call node,
102
69
  # and if the arguments of this attribute assignments has duplicated entries (different message and same arguments),
103
70
  # then this node needs to add a virtual attribute.
104
- #
105
- # e.g. this is @attrasgns
106
- # {
107
- # s(:ivar, :@user)=>
108
- # [{
109
- # :message=>:first_name=,
110
- # :arguments=>s(:call, s(:call, nil, :params, s(:arglist)), :[], s(:arglist, s(:lit, :full_name)))
111
- # }, {
112
- # :message=>:last_name=,
113
- # :arguments=>s(:call, s(:call, nil, :params, s(:arglist)), :[], s(:arglist, s(:lit, :full_name)))
114
- # }]
115
- # }
116
- # and this is the call node
117
- # s(:call, s(:ivar, :@user), :save, s(:arglist))
118
- #
119
- # The message of call node is :save,
120
- # and the key of @attrasgns is the same as the subject of call node,
121
- # and the value of @aatrasgns has different message and same arguments.
122
71
  def call_assignment(node)
123
- if [:save, :save!].include? node.message
124
- subject = node.subject
125
- add_error "add model virtual attribute (for #{subject})" if params_dup?(attrasgns(subject).collect {|h| h[:arguments]})
72
+ if ["save", "save!"].include? node.message.to_s
73
+ subject = node.subject.to_s
74
+ add_error "add model virtual attribute (for #{subject})" if params_dup?(assignments(subject).collect {|h| h[:arguments]})
126
75
  end
127
76
  end
128
77
 
@@ -132,9 +81,9 @@ module RailsBestPractices
132
81
  !nodes.dups.empty?
133
82
  end
134
83
 
135
- # get the attrasgns of subject, or empty array.
136
- def attrasgns(subject)
137
- @attrasgns[subject] ||= []
84
+ # get the assignments of subject.
85
+ def assignments(subject)
86
+ @assignments[subject] ||= []
138
87
  end
139
88
  end
140
89
  end
@@ -10,12 +10,12 @@ module RailsBestPractices
10
10
  # Implementation:
11
11
  #
12
12
  # Review process:
13
- # only check the call nodes and at the end of iter node in db/schema file,
14
- # if the subject of call node is :create_table, then remember the table names
15
- # if the subject of call node is :integer, then remember it as foreign key
16
- # if the sujbect of call node is :string, the name of it is _type suffixed and there is an integer column _id suffixed, then remember it as polymorphic foreign key
17
- # if the subject of call node is :add_index, then remember the index columns
18
- # after all of these, at the end of iter node
13
+ # only check the command and command_calls nodes and at the end of program node in db/schema file,
14
+ # if the subject of command node is "create_table", then remember the table names
15
+ # if the subject of command_call node is "integer" and suffix with id, then remember it as foreign key
16
+ # if the sujbect of command_call node is "string", the name of it is _type suffixed and there is an integer column _id suffixed, then remember it as polymorphic foreign key
17
+ # if the subject of command node is "add_index", then remember the index columns
18
+ # after all of these, at the end of program node
19
19
  #
20
20
  # ActiveRecord::Schema.define(:version => 20101201111111) do
21
21
  # ......
@@ -29,7 +29,7 @@ module RailsBestPractices
29
29
  end
30
30
 
31
31
  def interesting_nodes
32
- [:call, :iter]
32
+ [:command, :command_call, :program]
33
33
  end
34
34
 
35
35
  def interesting_files
@@ -43,83 +43,46 @@ module RailsBestPractices
43
43
  @table_nodes = {}
44
44
  end
45
45
 
46
- # check call node.
46
+ # check command_call node.
47
47
  #
48
- # if the message of call node is :create_table,
49
- # then remember the table name (@table_nodes) like
50
- # {
51
- # "comments" =>
52
- # s(:call, nil, :create_table, s(:arglist, s(:str, "comments"), s(:hash, s(:lit, :force), s(:true))))
53
- # }
48
+ # if the message of command_call node is "create_table", then remember the table name.
49
+ # if the message of command_call node is "add_index", then remember it as index columns.
50
+ def start_command_call(node)
51
+ case node.message.to_s
52
+ when "integer", "string"
53
+ remember_foreign_key_columns(node)
54
+ else
55
+ end
56
+ end
57
+
58
+ # check command node.
54
59
  #
55
- # if the message of call node is :integer,
60
+ # if the message of command node is "integer",
56
61
  # then remember it as a foreign key of last create table name.
57
62
  #
58
- # if the message of call node is :type and the name of argument is _type suffixed,
63
+ # if the message of command node is "type" and the name of argument is _type suffixed,
59
64
  # then remember it with _id suffixed column as polymorphic foreign key.
60
- #
61
- # the remember foreign keys (@foreign_keys) like
62
- #
63
- # {
64
- # "taggings" =>
65
- # ["tag_id", ["taggable_id", "taggable_type"]]
66
- # }
67
- #
68
- # if the message of call node is :add_index,
69
- # then remember it as index columns (@index_columns) like
70
- #
71
- # {
72
- # "comments" =>
73
- # ["post_id", "user_id"]
74
- # }
75
- def start_call(node)
76
- case node.message
77
- when :create_table
65
+ def start_command(node)
66
+ case node.message.to_s
67
+ when "create_table"
78
68
  remember_table_nodes(node)
79
- when :integer, :string
80
- remember_foreign_key_columns(node)
81
- when :add_index
69
+ when "add_index"
82
70
  remember_index_columns(node)
83
- else
84
71
  end
85
72
  end
86
73
 
87
- # check at the end of iter node, like
88
- #
89
- # s(:iter,
90
- # s(:call,
91
- # s(:colon2, s(:const, :ActiveRecord), :Schema),
92
- # :define,
93
- # s(:arglist, s(:hash, s(:lit, :version), s(:lit, 20100603080629)))
94
- # ),
95
- # nil,
96
- # s(:iter,
97
- # s(:call, nil, :create_table,
98
- # s(:arglist, s(:str, "comments"), s(:hash, s(:lit, :force), s(:true)))
99
- # ),
100
- # s(:lasgn, :t),
101
- # s(:block,
102
- # s(:call, s(:lvar, :t), :string, s(:arglist, s(:str, "content")))
103
- # )
104
- # )
105
- # )
106
- #
107
- # if the subject of iter node is with subject ActiveRecord::Schema,
108
- # it means we have completed the foreign keys and index columns parsing,
109
- # then we compare foreign keys and index columns.
74
+ # check at the end of program node.
110
75
  #
76
+ # compare foreign keys and index columns,
111
77
  # if there are any foreign keys not existed in index columns,
112
78
  # then we should add db index for that foreign keys.
113
- def end_iter(node)
114
- first_node = node.subject
115
- if :call == first_node.node_type && s(:colon2, s(:const, :ActiveRecord), :Schema) == first_node.subject
116
- remove_only_type_foreign_keys
117
- @foreign_keys.each do |table, foreign_key|
118
- table_node = @table_nodes[table]
119
- foreign_key.each do |column|
120
- if indexed?(table, column)
121
- add_error "always add db index (#{table} => [#{Array(column).join(', ')}])", table_node.file, table_node.line
122
- end
79
+ def end_program(node)
80
+ remove_only_type_foreign_keys
81
+ @foreign_keys.each do |table, foreign_key|
82
+ table_node = @table_nodes[table]
83
+ foreign_key.each do |column|
84
+ if indexed?(table, column)
85
+ add_error "always add db index (#{table} => [#{Array(column).join(', ')}])", table_node.file, table_node.line
123
86
  end
124
87
  end
125
88
  end
@@ -127,62 +90,25 @@ module RailsBestPractices
127
90
 
128
91
  private
129
92
  # remember the node as index columns
130
- #
131
- # s(:call, nil, :add_index,
132
- # s(:arglist,
133
- # s(:str, "comments"),
134
- # s(:array, s(:str, "post_id")),
135
- # s(:hash, s(:lit, :name), s(:str, "index_comments_on_post_id"))
136
- # )
137
- # )
138
- #
139
- # the remember index columns are like
140
- # {
141
- # "comments" =>
142
- # ["post_id", "user_id"]
143
- # }
144
93
  def remember_index_columns(node)
145
- table_name = node.arguments[1].to_s
146
- index_column = eval(node.arguments[2].to_s)
94
+ table_name = node.arguments.all[0].to_s
95
+ index_column = node.arguments.all[1].to_object
147
96
 
148
97
  @index_columns[table_name] ||= []
149
- @index_columns[table_name] << (index_column.size == 1 ? index_column[0] : index_column)
98
+ @index_columns[table_name] << index_column
150
99
  end
151
100
 
152
101
  # remember table nodes
153
- #
154
- # if the node is
155
- #
156
- # s(:call, nil, :create_table,
157
- # s(:arglist, s(:str, "comments"), s(:hash, s(:lit, :force), s(:true))))
158
- #
159
- # then the table nodes will be
160
- #
161
- # {
162
- # "comments" =>
163
- # s(:call, nil, :create_table, s(:arglist, s(:str, "comments"), s(:hash, s(:lit, :force), s(:true))))
164
- # }
165
102
  def remember_table_nodes(node)
166
- @table_name = node.arguments[1].to_s
103
+ @table_name = node.arguments.all[0].to_s
167
104
  @table_nodes[@table_name] = node
168
105
  end
169
106
 
170
107
 
171
108
  # remember foreign key columns
172
- #
173
- # if the message of node is :integer,
174
- # then it is a foreign key, like
175
- #
176
- # s(:call, s(:lvar, :t), :integer, s(:arglist, s(:str, "post_id")))
177
- #
178
- # if the message of node is :string, with _type suffixed and there is a _id suffixed column,
179
- # then they are polymorphic foreign key
180
- #
181
- # s(:call, s(:lvar, :t), :integer, s(:arglist, s(:str, "taggable_id")))
182
- # s(:call, s(:lvar, :t), :string, s(:arglist, s(:str, "taggable_type")))
183
109
  def remember_foreign_key_columns(node)
184
110
  table_name = @table_name
185
- foreign_key_column = node.arguments[1].to_s
111
+ foreign_key_column = node.arguments.all[0].to_s
186
112
  @foreign_keys[table_name] ||= []
187
113
  if foreign_key_column =~ /(.*?)_id$/
188
114
  if @foreign_keys[table_name].delete("#{$1}_type")