rails_best_practices 0.10.1 → 1.0.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 (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")