class RuboCop::Cop::Rails::InverseOf

end
has_many :posts, -> { order(published_at: :desc) }
class Blog < ApplicationRecord
# good
@example IgnoreScopes: true
end
has_many :posts, -> { order(published_at: :desc) }
class Blog < ApplicationRecord
# bad
@example IgnoreScopes: false (default)
end
has_many :physicians, through: :appointments
has_many :appointments
class Patient < ApplicationRecord
end
belongs_to :patient, inverse_of: :appointments
belongs_to :physician, inverse_of: :appointments
class Appointment < ApplicationRecord
end
has_many :patients, through: :appointments
has_many :appointments
class Physician < ApplicationRecord
# good
end
has_many :physicians, through: :appointments
has_many :appointments
class Patient < ApplicationRecord
end
belongs_to :patient
belongs_to :physician
class Appointment < ApplicationRecord
end
has_many :patients, through: :appointments
has_many :appointments
class Physician < ApplicationRecord
# However, RuboCop can not detect this pattern…
# bad
@example
end
has_many :pictures, as: :imageable, inverse_of: :imageable
class Product < ApplicationRecord
end
has_many :pictures, as: :imageable, inverse_of: :imageable
class Employee < ApplicationRecord
end
belongs_to :imageable, polymorphic: true
class Picture < ApplicationRecord
# good
end
has_many :pictures, as: :imageable
class Product < ApplicationRecord
end
has_many :pictures, as: :imageable
class Employee < ApplicationRecord
end
belongs_to :imageable, polymorphic: true
class Picture < ApplicationRecord
# bad
@example
end
inverse_of: false)
-> { order(published_at: :desc) },
has_many(:posts,
class Blog < ApplicationRecord
# When you don’t want to use the inverse association.
# good
end
belongs_to :blog
class Post < ApplicationRecord
end
end
has_many :posts, -> { order(published_at: :desc) }
with_options inverse_of: :blog do
class Blog < ApplicationRecord
# good
end
belongs_to :blog
class Post < ApplicationRecord
end
inverse_of: :blog)
-> { order(published_at: :desc) },
has_many(:posts,
class Blog < ApplicationRecord
# good
end
belongs_to :blog
class Post < ApplicationRecord
end
has_many :posts, -> { order(published_at: :desc) }
class Blog < ApplicationRecord
# bad
@example
end
belongs_to :blog
class Post < ApplicationRecord
end
has_many :posts
class Blog < ApplicationRecord
# good
@example
inverse automatically, and is not considered a valid value for this.
setting ‘nil` does not stop Active Record from trying to determine the
associated object in memory, or set to `false` to opt-out. Note that
`:inverse_of` must be manually specified for Active Record to use the
the database twice.
with `blog.posts.first.blog` would cause the `blog` to be loaded from
example below, traversing the a Blog’s association in both directions
because of a scope or the options used. Using the blog with order scope
Active Record can’t automatically determine the inverse association
Looks for has_(one|many) and belongs_to associations where

def ignore_scopes?

def ignore_scopes?
  cop_config['IgnoreScopes'] == true
end

def message(options)

def message(options)
  if options.any? { |opt| inverse_of_nil_option?(opt) }
    NIL_MSG
  else
    SPECIFY_MSG
  end
end

def on_send(node)

def on_send(node)
  recv, arguments = association_recv_arguments(node)
  return unless arguments
  with_options = with_options_arguments(recv, node)
  options = arguments.concat(with_options).flat_map do |arg|
    options_from_argument(arg)
  end
  return if options_ignoring_inverse_of?(options)
  return unless scope?(arguments) || options_requiring_inverse_of?(options)
  return if options_contain_inverse_of?(options)
  add_offense(node.loc.selector, message: message(options))
end

def options_contain_inverse_of?(options)

def options_contain_inverse_of?(options)
  options.any? { |opt| inverse_of_option?(opt) }
end

def options_ignoring_inverse_of?(options)

def options_ignoring_inverse_of?(options)
  options.any? do |opt|
    through_option?(opt) || polymorphic_option?(opt)
  end
end

def options_requiring_inverse_of?(options)

def options_requiring_inverse_of?(options)
  required = options.any? do |opt|
    conditions_option?(opt) || foreign_key_option?(opt)
  end
  return required if target_rails_version >= 5.2
  required || options.any? { |opt| as_option?(opt) }
end

def same_context_in_with_options?(arg, recv)

def same_context_in_with_options?(arg, recv)
  return true if arg.nil? && recv.nil?
  arg && recv && arg.children[0] == recv.children[0]
end

def scope?(arguments)

def scope?(arguments)
  !ignore_scopes? && arguments.any?(&:block_type?)
end

def with_options_arguments(recv, node)

def with_options_arguments(recv, node)
  blocks = node.each_ancestor(:block).select do |block|
    block.send_node.command?(:with_options) && same_context_in_with_options?(block.first_argument, recv)
  end
  blocks.flat_map { |n| n.send_node.arguments }
end