class RailsBestPractices::Reviews::AddModelVirtualAttributeReview

Make sure to add a model virual attribute to simplify model creation.

See the best practice details here rails-bestpractices.com/posts/2010/07/21/add-model-virtual-attribute/

Implementation:

Review process:

check method define nodes in all controller files,
if there are more than one [] method calls with the same receiver and arguments,
but assigned to one model's different attribute.
and after these method calls, there is a save method call for that model,
then the model needs to add a virtual attribute.

Private Instance Methods

assign(node) click to toggle source

check an attribute assignment node, if there is a array reference node in the right value of assignment node, then remember this attribute assignment.

# File lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb, line 51
def assign(node)
  left_value = node.left_value
  right_value = node.right_value
  return unless left_value.sexp_type == :field && right_value.sexp_type == :call

  aref_node = right_value.grep_node(sexp_type: :aref)
  if aref_node
    assignments(left_value.receiver.to_s) << { message: left_value.message.to_s, arguments: aref_node.to_s }
  end
end
assignments(receiver) click to toggle source

get the assignments of receiver.

# File lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb, line 83
def assignments(receiver)
  @assignments[receiver] ||= []
end
call_assignment(node) click to toggle source

check a call node with message “save” or “save!”, if there exists an attribute assignment for the receiver of this call node, and if the arguments of this attribute assignments has duplicated entries (different message and same arguments), then this node needs to add a virtual attribute.

# File lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb, line 66
def call_assignment(node)
  if ['save', 'save!'].include? node.message.to_s
    receiver = node.receiver.to_s
    add_error "add model virtual attribute (for #{receiver})" if params_dup?(
      assignments(receiver).collect { |h| h[:arguments] }
    )
  end
end
dups(nodes) click to toggle source

Get the duplicate entries from an Enumerable.

@return [Enumerable] the duplicate entries.

# File lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb, line 90
def dups(nodes)
  nodes.each_with_object({}) { |v, h| h[v] = h[v].to_i + 1 }.reject { |_k, v| v == 1 }.keys
end
params_dup?(nodes) click to toggle source

if the nodes are duplicated.

# File lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb, line 76
def params_dup?(nodes)
  return false if nodes.nil?

  !dups(nodes).empty?
end