Class: RuboCop::Cop::Rails::InverseOf
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::Rails::InverseOf
- Extended by:
- TargetRailsVersion
- Defined in:
- lib/rubocop/cop/rails/inverse_of.rb
Overview
Looks for has_(one|many) and belongs_to associations where
Active Record can't automatically determine the inverse association
because of a scope or the options used. Using the blog with order scope
example below, traversing the a Blog's association in both directions
with blog.posts.first.blog would cause the blog to be loaded from
the database twice.
:inverse_of must be manually specified for Active Record to use the
associated object in memory, or set to false to opt-out. Note that
setting nil does not stop Active Record from trying to determine the
inverse automatically, and is not considered a valid value for this.
Constant Summary collapse
- SPECIFY_MSG =
'Specify an `:inverse_of` option.'- NIL_MSG =
'You specified `inverse_of: nil`, you probably meant to use `inverse_of: false`.'- RESTRICT_ON_SEND =
%i[has_many has_one belongs_to].freeze
Constants included from TargetRailsVersion
TargetRailsVersion::DEFAULT_RAILS_VERSION, TargetRailsVersion::TARGET_GEM_NAME, TargetRailsVersion::USES_REQUIRES_GEM_API
Instance Method Summary collapse
- #dynamic_options?(options) ⇒ Boolean
-
#on_send(node) ⇒ Object
rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity.
- #options_contain_inverse_of?(options) ⇒ Boolean
- #options_ignoring_inverse_of?(options) ⇒ Boolean
- #options_requiring_inverse_of?(options) ⇒ Boolean
- #same_context_in_with_options?(arg, recv) ⇒ Boolean
- #scope?(arguments) ⇒ Boolean
- #with_options_arguments(recv, node) ⇒ Object
Methods included from TargetRailsVersion
extended, minimum_target_rails_version, resolve, support_target_rails_version?
Instance Method Details
#dynamic_options?(options) ⇒ Boolean
223 224 225 |
# File 'lib/rubocop/cop/rails/inverse_of.rb', line 223 def () .any? { |option| option&.kwsplat_type? } end |
#on_send(node) ⇒ Object
rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/rubocop/cop/rails/inverse_of.rb', line 184 def on_send(node) recv, arguments = association_recv_arguments(node) return unless arguments = (recv, node) = arguments.concat().flat_map do |arg| (arg) end return if () return unless scope?(arguments) || () return if () return if () && .none? { |opt| inverse_of_nil_option?(opt) } add_offense(node.loc.selector, message: ()) end |
#options_contain_inverse_of?(options) ⇒ Boolean
227 228 229 |
# File 'lib/rubocop/cop/rails/inverse_of.rb', line 227 def () .any? { |opt| inverse_of_option?(opt) } end |
#options_ignoring_inverse_of?(options) ⇒ Boolean
217 218 219 220 221 |
# File 'lib/rubocop/cop/rails/inverse_of.rb', line 217 def () .any? do |opt| through_option?(opt) || polymorphic_option?(opt) end end |
#options_requiring_inverse_of?(options) ⇒ Boolean
207 208 209 210 211 212 213 214 215 |
# File 'lib/rubocop/cop/rails/inverse_of.rb', line 207 def () required = .any? do |opt| conditions_option?(opt) || foreign_key_option?(opt) end return required if target_rails_version >= 5.2 required || .any? { |opt| as_option?(opt) } end |
#same_context_in_with_options?(arg, recv) ⇒ Boolean
238 239 240 241 242 |
# File 'lib/rubocop/cop/rails/inverse_of.rb', line 238 def (arg, recv) return true if arg.nil? && recv.nil? arg && recv && arg.children[0] == recv.children[0] end |
#scope?(arguments) ⇒ Boolean
203 204 205 |
# File 'lib/rubocop/cop/rails/inverse_of.rb', line 203 def scope?(arguments) !ignore_scopes? && arguments.any?(&:block_type?) end |
#with_options_arguments(recv, node) ⇒ Object
231 232 233 234 235 236 |
# File 'lib/rubocop/cop/rails/inverse_of.rb', line 231 def (recv, node) blocks = node.each_ancestor(:block).select do |block| block.send_node.command?(:with_options) && (block.first_argument, recv) end blocks.flat_map { |n| n.send_node.arguments } end |