class RailsBestPractices::Reviews::LawOfDemeterReview

Review to make sure not to avoid the law of demeter.

See the best practice details here rails-bestpractices.com/posts/2010/07/24/the-law-of-demeter/

Implementation:

Review process:

check all method calls to see if there is method call to the association object.
if there is a call node whose receiver is an object of model (compare by name),
and whose message is an association of that model (also compare by name),
and outer the call node, it is also a call node,
then it violate the law of demeter.

Constants

ASSOCIATION_METHODS

Private Instance Methods

is_association_attribute?(association_class, association_name, attribute_name) click to toggle source
# File lib/rails_best_practices/reviews/law_of_demeter_review.rb, line 54
def is_association_attribute?(association_class, association_name, attribute_name)
  if association_name =~ /able$/
    models.each do |class_name|
      if model_associations.is_association?(class_name, association_name.sub(/able$/, '')) ||
           model_associations.is_association?(class_name, association_name.sub(/able$/, 's'))
        return true if model_attributes.is_attribute?(class_name, attribute_name)
      end
    end
  else
    model_attributes.is_attribute?(association_class, attribute_name)
  end
end
need_delegate?(node) click to toggle source

check if the call node can use delegate to avoid violating law of demeter.

if the receiver of receiver of the call node matchs any in model names, and the message of receiver of the call node matchs any in association names, then it needs delegate.

# File lib/rails_best_practices/reviews/law_of_demeter_review.rb, line 43
def need_delegate?(node)
  return unless variable(node)

  class_name = variable(node).to_s.sub('@', '').classify
  association_name = node.receiver.message.to_s
  association = model_associations.get_association(class_name, association_name)
  attribute_name = node.message.to_s
  association && ASSOCIATION_METHODS.include?(association['meta']) &&
    is_association_attribute?(association['class_name'], association_name, attribute_name)
end