exists_condition.rb 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. module RuboCop
  2. module Cop
  3. module Zammad
  4. # This cop is used to identify usages of `find_by` in conditions and
  5. # changes them to use `exists?` instead.
  6. #
  7. # @example
  8. # # bad
  9. # if User.find_by(name: 'Rubocop')
  10. # if !User.find_by(name: 'Rubocop')
  11. # unless User.find_by(name: 'Rubocop')
  12. # if find_by(name: 'Rubocop')
  13. # if !find_by(name: 'Rubocop')
  14. # unless find_by(name: 'Rubocop')
  15. #
  16. # # good
  17. # if User.exists?(name: 'Rubocop')
  18. # if !User.exists?(name: 'Rubocop')
  19. # unless User.exists?(name: 'Rubocop')
  20. # if exists?(name: 'Rubocop')
  21. # if !exists?(name: 'Rubocop')
  22. # unless exists?(name: 'Rubocop')
  23. class ExistsCondition < Base
  24. def_node_matcher :find_by_condition?, <<-PATTERN
  25. {
  26. $(send $_ :find_by ...)
  27. (send $(send $_ :find_by ...) :!)
  28. }
  29. PATTERN
  30. MSG = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
  31. def on_if(node)
  32. check_for_find_by(node)
  33. end
  34. def on_while(node)
  35. check_for_find_by(node)
  36. end
  37. def on_while_post(node)
  38. check_for_find_by(node)
  39. end
  40. def on_until(node)
  41. check_for_find_by(node)
  42. end
  43. def on_until_post(node)
  44. check_for_find_by(node)
  45. end
  46. def autocorrect(node)
  47. lambda do |corrector|
  48. corrector.replace(node.loc.selector, 'exists?')
  49. end
  50. end
  51. private
  52. def check_for_find_by(node)
  53. cond = condition(node)
  54. handle_node(cond)
  55. end
  56. def check_node(node)
  57. return if !node
  58. if keyword_bang?(node)
  59. receiver, = *node
  60. handle_node(receiver)
  61. elsif node.operator_keyword?
  62. node.each_child_node { |op| handle_node(op) }
  63. elsif node.begin_type? && node.children.one?
  64. handle_node(node.children.first)
  65. end
  66. end
  67. def keyword_bang?(node)
  68. node.respond_to?(:keyword_bang?) && node.keyword_bang?
  69. end
  70. def handle_node(node)
  71. if node.send_type?
  72. check_offense(*find_by_condition?(node)) # rubocop:disable Rails/DynamicFindBy
  73. elsif %i[and or begin].include?(node.type)
  74. check_node(node)
  75. end
  76. end
  77. def condition(node)
  78. if node.send_type?
  79. node.receiver
  80. else
  81. node.condition
  82. end
  83. end
  84. def check_offense(method_call = nil, receiver = nil)
  85. return if method_call.nil?
  86. add_offense(method_call,
  87. message: format(MSG,
  88. prefer: replacement(receiver),
  89. current: current(receiver)))
  90. end
  91. def current(node)
  92. node.respond_to?(:source) ? "#{node.source}.find_by" : 'find_by'
  93. end
  94. def replacement(node)
  95. node.respond_to?(:source) ? "#{node.source}.exists?" : 'exists?'
  96. end
  97. end
  98. end
  99. end
  100. end