/lib/rails_best_practices/reviews/use_model_association_review.rb
http://github.com/flyerhzm/rails_best_practices · Ruby · 66 lines · 34 code · 5 blank · 27 comment · 3 complexity · 2b6ae4ade122521a652cc5e8a2e84c28 MD5 · raw file
- # frozen_string_literal: true
- module RailsBestPractices
- module Reviews
- # review a controller file to make sure to use model association instead of foreign key id assignment.
- #
- # See the best practice details here https://rails-bestpractices.com/posts/2010/07/19/use-model-association/
- #
- # Implementation:
- #
- # Review process:
- # check model define nodes in all controller files,
- # if there is an attribute assignment node with message xxx_id=,
- # and after it, there is a call node with message "save" or "save!",
- # and the receivers of attribute assignment node and call node are the same,
- # then model association should be used instead of xxx_id assignment.
- class UseModelAssociationReview < Review
- interesting_nodes :def
- interesting_files CONTROLLER_FILES
- url 'https://rails-bestpractices.com/posts/2010/07/19/use-model-association/'
- # check method define nodes to see if there are some attribute assignments that can use model association instead.
- #
- # it will check attribute assignment node with message xxx_id=, and call node with message "save" or "save!"
- #
- # 1. if there is an attribute assignment node with message xxx_id=,
- # then remember the receiver of attribute assignment node.
- # 2. after assignment, if there is a call node with message "save" or "save!",
- # and the receiver of call node is one of the receiver of attribute assignment node,
- # then the attribute assignment should be replaced by using model association.
- add_callback :start_def do |node|
- @assignments = {}
- node.recursive_children do |child|
- case child.sexp_type
- when :assign
- attribute_assignment(child)
- when :call
- call_assignment(child)
- end
- end
- @assignments = nil
- end
- private
- # check an attribute assignment node, if its message is xxx_id,
- # then remember the receiver of the attribute assignment in @assignments.
- def attribute_assignment(node)
- if node.left_value.message.is_a?(Sexp) && node.left_value.message.to_s =~ /_id$/
- receiver = node.left_value.receiver.to_s
- @assignments[receiver] = true
- end
- end
- # check a call node with message "save" or "save!",
- # if the receiver of call node exists in @assignments,
- # then the attribute assignment should be replaced by using model association.
- def call_assignment(node)
- if ['save', 'save!'].include? node.message.to_s
- receiver = node.receiver.to_s
- add_error "use model association (for #{receiver})" if @assignments[receiver]
- end
- end
- end
- end
- end