Apply Rails/InverseOf rubocop rule

Not doing so has a few gotchas when working with relations, particularly
with records which are not stored in the database.

I'm excluding the related content file because it's got a very peculiar
relationship with itself: the `has_one :opposite_related_content` has no
inverse; the relation itself is its inverse. It's a false positive since
the inverse condition is true:

```
content.opposite_related_content.opposite_related_content.object_id ==
  content.object_id
```
This commit is contained in:
Javi Martín
2019-10-24 04:41:04 +02:00
parent 94d2496f8f
commit 42d2e5b3ad
37 changed files with 107 additions and 65 deletions

View File

@@ -21,9 +21,6 @@ Rails/HasAndBelongsToMany:
Rails/HasManyOrHasOneDependent:
Enabled: true
Rails/InverseOf:
Enabled: true
Rails/SkipsModelValidations:
Enabled: true

View File

@@ -206,6 +206,11 @@ Rails/FindEach:
Rails/HttpPositionalArguments:
Enabled: true
Rails/InverseOf:
Enabled: true
Exclude:
- "app/models/related_content.rb"
Rails/NotNullColumn:
Enabled: true
Exclude:

View File

@@ -32,7 +32,7 @@ class Budget
translates :description, touch: true
include Globalizable
belongs_to :author, -> { with_hidden }, class_name: "User"
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :budget_investments
belongs_to :heading
belongs_to :group
belongs_to :budget
@@ -44,8 +44,11 @@ class Budget
has_many :valuator_group_assignments, dependent: :destroy
has_many :valuator_groups, through: :valuator_group_assignments
has_many :comments, -> { where(valuation: false) }, as: :commentable
has_many :valuations, -> { where(valuation: true) }, as: :commentable, class_name: "Comment"
has_many :comments, -> { where(valuation: false) }, as: :commentable, inverse_of: :commentable
has_many :valuations, -> { where(valuation: true) },
as: :commentable,
inverse_of: :commentable,
class_name: "Comment"
has_many :tracker_assignments, dependent: :destroy
has_many :trackers, through: :tracker_assignments

View File

@@ -12,8 +12,8 @@ class Budget
include Sanitizable
belongs_to :budget
belongs_to :next_phase, class_name: self.name
has_one :prev_phase, class_name: self.name, foreign_key: :next_phase_id
belongs_to :next_phase, class_name: self.name, inverse_of: :prev_phase
has_one :prev_phase, class_name: self.name, foreign_key: :next_phase_id, inverse_of: :next_phase
validates_translation :summary, length: { maximum: SUMMARY_MAX_LENGTH }
validates_translation :description, length: { maximum: DESCRIPTION_MAX_LENGTH }

View File

@@ -30,7 +30,7 @@ class Comment < ApplicationRecord
validate :comment_valuation, if: -> { valuation }
belongs_to :commentable, -> { with_hidden }, polymorphic: true, counter_cache: true, touch: true
belongs_to :user, -> { with_hidden }
belongs_to :user, -> { with_hidden }, inverse_of: :comments
before_save :calculate_confidence_score

View File

@@ -2,7 +2,7 @@ module Documentable
extend ActiveSupport::Concern
included do
has_many :documents, as: :documentable, dependent: :destroy
has_many :documents, as: :documentable, inverse_of: :documentable, dependent: :destroy
accepts_nested_attributes_for :documents, allow_destroy: true
end

View File

@@ -2,7 +2,7 @@ module Flaggable
extend ActiveSupport::Concern
included do
has_many :flags, as: :flaggable
has_many :flags, as: :flaggable, inverse_of: :flaggable
scope :flagged, -> { where("flags_count > 0") }
scope :pending_flag_review, -> { flagged.where(ignored_flag_at: nil, hidden_at: nil) }
scope :with_ignored_flag, -> { flagged.where.not(ignored_flag_at: nil).where(hidden_at: nil) }

View File

@@ -2,7 +2,7 @@ module Followable
extend ActiveSupport::Concern
included do
has_many :follows, as: :followable, dependent: :destroy
has_many :follows, as: :followable, inverse_of: :followable, dependent: :destroy
has_many :followers, through: :follows, source: :user
scope :followed_by_user, ->(user) {

View File

@@ -2,7 +2,7 @@ module Galleryable
extend ActiveSupport::Concern
included do
has_many :images, as: :imageable, dependent: :destroy
has_many :images, as: :imageable, inverse_of: :imageable, dependent: :destroy
accepts_nested_attributes_for :images, allow_destroy: true, update_only: true
def image_url(style)

View File

@@ -2,7 +2,7 @@ module Imageable
extend ActiveSupport::Concern
included do
has_one :image, as: :imageable, dependent: :destroy
has_one :image, as: :imageable, inverse_of: :imageable, dependent: :destroy
accepts_nested_attributes_for :image, allow_destroy: true, update_only: true
def image_url(style)

View File

@@ -2,7 +2,7 @@ module Linkable
extend ActiveSupport::Concern
included do
has_many :links, as: :linkable, dependent: :destroy
has_many :links, as: :linkable, inverse_of: :linkable, dependent: :destroy
accepts_nested_attributes_for :links, allow_destroy: true
end
end

View File

@@ -2,11 +2,11 @@ module Milestoneable
extend ActiveSupport::Concern
included do
has_many :milestones, as: :milestoneable, dependent: :destroy
has_many :milestones, as: :milestoneable, inverse_of: :milestoneable, dependent: :destroy
scope :with_milestones, -> { joins(:milestones).distinct }
has_many :progress_bars, as: :progressable
has_many :progress_bars, as: :progressable, inverse_of: :progressable
acts_as_taggable_on :milestone_tags

View File

@@ -2,7 +2,10 @@ module Relationable
extend ActiveSupport::Concern
included do
has_many :related_contents, as: :parent_relationable, dependent: :destroy
has_many :related_contents,
as: :parent_relationable,
inverse_of: :parent_relationable,
dependent: :destroy
end
def find_related_content(relationable)

View File

@@ -2,7 +2,7 @@ module Reportable
extend ActiveSupport::Concern
included do
has_one :report, as: :process, dependent: :destroy
has_one :report, as: :process, inverse_of: :process, dependent: :destroy
accepts_nested_attributes_for :report
end

View File

@@ -2,7 +2,7 @@ module StatsVersionable
extend ActiveSupport::Concern
included do
has_one :stats_version, as: :process
has_one :stats_version, as: :process, inverse_of: :process
end
def find_or_create_stats_version

View File

@@ -2,7 +2,7 @@ class Dashboard::ExecutedAction < ApplicationRecord
belongs_to :proposal
belongs_to :action
has_many :administrator_tasks, as: :source, dependent: :destroy
has_many :administrator_tasks, as: :source, inverse_of: :source, dependent: :destroy
validates :proposal, presence: true, uniqueness: { scope: :action }
validates :action, presence: true

View File

@@ -24,9 +24,9 @@ class Debate < ApplicationRecord
translates :description, touch: true
include Globalizable
belongs_to :author, -> { with_hidden }, class_name: "User"
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :debates
belongs_to :geozone
has_many :comments, as: :commentable
has_many :comments, as: :commentable, inverse_of: :commentable
extend DownloadSettings::DebateCsv
delegate :name, :email, to: :author, prefix: true

View File

@@ -1,6 +1,6 @@
class DirectMessage < ApplicationRecord
belongs_to :sender, class_name: "User"
belongs_to :receiver, class_name: "User"
belongs_to :sender, class_name: "User", inverse_of: :direct_messages_sent
belongs_to :receiver, class_name: "User", inverse_of: :direct_messages_received
validates :title, presence: true
validates :body, presence: true

View File

@@ -5,9 +5,9 @@ class Legislation::Annotation < ApplicationRecord
serialize :ranges, Array
belongs_to :draft_version, foreign_key: "legislation_draft_version_id"
belongs_to :draft_version, foreign_key: "legislation_draft_version_id", inverse_of: :annotations
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :legislation_annotations
has_many :comments, as: :commentable, dependent: :destroy
has_many :comments, as: :commentable, inverse_of: :commentable, dependent: :destroy
validates :text, presence: true
validates :quote, presence: true

View File

@@ -9,8 +9,11 @@ class Legislation::DraftVersion < ApplicationRecord
translates :body, touch: true
include Globalizable
belongs_to :process, foreign_key: "legislation_process_id"
has_many :annotations, foreign_key: "legislation_draft_version_id", dependent: :destroy
belongs_to :process, foreign_key: "legislation_process_id", inverse_of: :draft_versions
has_many :annotations,
foreign_key: "legislation_draft_version_id",
inverse_of: :draft_version,
dependent: :destroy
validates_translation :title, presence: true
validates_translation :body, presence: true

View File

@@ -19,9 +19,9 @@ class Legislation::PeopleProposal < ApplicationRecord
acts_as_votable
acts_as_paranoid column: :hidden_at
belongs_to :process, foreign_key: "legislation_process_id"
belongs_to :author, -> { with_hidden }, class_name: "User"
has_many :comments, as: :commentable
belongs_to :process, foreign_key: "legislation_process_id", inverse_of: :people_proposals
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :people_proposals
has_many :comments, as: :commentable, inverse_of: :commentable
validates :title, presence: true
validates :summary, presence: true

View File

@@ -25,13 +25,26 @@ class Legislation::Process < ApplicationRecord
CSS_HEX_COLOR = /\A#?(?:[A-F0-9]{3}){1,2}\z/i
has_many :draft_versions, -> { order(:id) }, foreign_key: "legislation_process_id", dependent: :destroy
has_many :draft_versions, -> { order(:id) },
foreign_key: "legislation_process_id",
inverse_of: :process,
dependent: :destroy
has_one :final_draft_version, -> { where final_version: true, status: "published" },
class_name: "Legislation::DraftVersion",
foreign_key: "legislation_process_id"
has_many :questions, -> { order(:id) }, foreign_key: "legislation_process_id", dependent: :destroy
has_many :proposals, -> { order(:id) }, foreign_key: "legislation_process_id", dependent: :destroy
has_many :people_proposals, -> { order(:id) }, foreign_key: "legislation_process_id", dependent: :destroy
foreign_key: "legislation_process_id",
inverse_of: :process
has_many :questions, -> { order(:id) },
foreign_key: "legislation_process_id",
inverse_of: :process,
dependent: :destroy
has_many :proposals, -> { order(:id) },
foreign_key: "legislation_process_id",
inverse_of: :process,
dependent: :destroy
has_many :people_proposals, -> { order(:id) },
foreign_key: "legislation_process_id",
inverse_of: :process,
dependent: :destroy
validates_translation :title, presence: true
validates :start_date, presence: true

View File

@@ -19,10 +19,10 @@ class Legislation::Proposal < ApplicationRecord
acts_as_votable
acts_as_paranoid column: :hidden_at
belongs_to :process, foreign_key: "legislation_process_id"
belongs_to :process, foreign_key: "legislation_process_id", inverse_of: :proposals
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :legislation_proposals
belongs_to :geozone
has_many :comments, as: :commentable
has_many :comments, as: :commentable, inverse_of: :commentable
validates :title, presence: true
validates :summary, presence: true

View File

@@ -7,12 +7,12 @@ class Legislation::Question < ApplicationRecord
include Globalizable
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :legislation_questions
belongs_to :process, foreign_key: "legislation_process_id"
belongs_to :process, foreign_key: "legislation_process_id", inverse_of: :questions
has_many :question_options, -> { order(:id) }, class_name: "Legislation::QuestionOption", foreign_key: "legislation_question_id",
dependent: :destroy, inverse_of: :question
has_many :answers, class_name: "Legislation::Answer", foreign_key: "legislation_question_id", dependent: :destroy, inverse_of: :question
has_many :comments, as: :commentable, dependent: :destroy
has_many :comments, as: :commentable, inverse_of: :commentable, dependent: :destroy
accepts_nested_attributes_for :question_options, reject_if: proc { |attributes| attributes.all? { |k, v| v.blank? } }, allow_destroy: true

View File

@@ -1,5 +1,5 @@
class Newsletter < ApplicationRecord
has_many :activities, as: :actionable
has_many :activities, as: :actionable, inverse_of: :actionable
validates :subject, presence: true
validates :segment_recipient, presence: true

View File

@@ -24,7 +24,7 @@ class Poll < ApplicationRecord
has_many :officer_assignments, through: :booth_assignments
has_many :officers, through: :officer_assignments
has_many :questions, inverse_of: :poll, dependent: :destroy
has_many :comments, as: :commentable
has_many :comments, as: :commentable, inverse_of: :commentable
has_many :ballot_sheets
has_and_belongs_to_many :geozones

View File

@@ -1,5 +1,5 @@
class Poll::Answer < ApplicationRecord
belongs_to :question, -> { with_hidden }
belongs_to :question, -> { with_hidden }, inverse_of: :answers
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :poll_answers
delegate :poll, :poll_id, to: :question

View File

@@ -3,7 +3,7 @@ class Poll
belongs_to :user
has_many :officer_assignments
has_many :shifts
has_many :failed_census_calls, foreign_key: :poll_officer_id
has_many :failed_census_calls, foreign_key: :poll_officer_id, inverse_of: :poll_officer
validates :user_id, presence: true, uniqueness: true

View File

@@ -1,5 +1,5 @@
class Poll::PairAnswer < ApplicationRecord
belongs_to :question, -> { with_hidden }
belongs_to :question, -> { with_hidden }, inverse_of: :pair_answers
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :poll_pair_answers
belongs_to :answer_right, class_name: "Poll::Question::Answer"
belongs_to :answer_left, class_name: "Poll::Question::Answer"

View File

@@ -1,7 +1,7 @@
class Poll::PartialResult < ApplicationRecord
VALID_ORIGINS = %w[web booth]
belongs_to :question, -> { with_hidden }
belongs_to :question, -> { with_hidden }, inverse_of: :partial_results
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :poll_partial_results
belongs_to :booth_assignment
belongs_to :officer_assignment

View File

@@ -11,12 +11,15 @@ class Poll::Question < ApplicationRecord
belongs_to :poll
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :poll_questions
has_many :comments, as: :commentable
has_many :comments, as: :commentable, inverse_of: :commentable
has_many :answers, class_name: "Poll::Answer"
has_many :question_answers, -> { order "given_order asc" }, class_name: "Poll::Question::Answer", dependent: :destroy
has_many :question_answers, -> { order "given_order asc" },
class_name: "Poll::Question::Answer",
inverse_of: :question,
dependent: :destroy
has_many :partial_results
has_many :pair_answers
has_one :votation_type, as: :questionable
has_one :votation_type, as: :questionable, inverse_of: :questionable
belongs_to :proposal
attr_accessor :enum_type, :max_votes, :prioritization_type

View File

@@ -35,13 +35,13 @@ class Proposal < ApplicationRecord
include Globalizable
translation_class_delegate :retired_at
belongs_to :author, -> { with_hidden }, class_name: "User"
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :proposals
belongs_to :geozone
has_many :comments, as: :commentable, dependent: :destroy
has_many :comments, as: :commentable, inverse_of: :commentable, dependent: :destroy
has_many :proposal_notifications, dependent: :destroy
has_many :dashboard_executed_actions, dependent: :destroy, class_name: "Dashboard::ExecutedAction"
has_many :dashboard_actions, through: :dashboard_executed_actions, class_name: "Dashboard::Action"
has_many :polls, as: :related
has_many :polls, as: :related, inverse_of: :related
extend DownloadSettings::ProposalCsv
delegate :name, :email, to: :author, prefix: true

View File

@@ -1,6 +1,9 @@
class SiteCustomization::Page < ApplicationRecord
VALID_STATUSES = %w[draft published]
has_many :cards, class_name: "Widget::Card", foreign_key: "site_customization_page_id"
has_many :cards,
class_name: "Widget::Card",
foreign_key: "site_customization_page_id",
inverse_of: :page
translates :title, touch: true
translates :subtitle, touch: true

View File

@@ -6,7 +6,7 @@ class Topic < ApplicationRecord
belongs_to :community
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :topics
has_many :comments, as: :commentable
has_many :comments, as: :commentable, inverse_of: :commentable
validates :title, presence: true
validates :description, presence: true

View File

@@ -21,20 +21,29 @@ class User < ApplicationRecord
has_one :lock
has_many :flags
has_many :identities, dependent: :destroy
has_many :debates, -> { with_hidden }, foreign_key: :author_id
has_many :proposals, -> { with_hidden }, foreign_key: :author_id
has_many :people_proposals, -> { with_hidden }, foreign_key: :author_id
has_many :debates, -> { with_hidden }, foreign_key: :author_id, inverse_of: :author
has_many :proposals, -> { with_hidden }, foreign_key: :author_id, inverse_of: :author
has_many :people_proposals, -> { with_hidden }, foreign_key: :author_id, inverse_of: :author
has_many :activities
has_many :budget_investments, -> { with_hidden }, foreign_key: :author_id, class_name: "Budget::Investment"
has_many :budget_investments, -> { with_hidden },
class_name: "Budget::Investment",
foreign_key: :author_id,
inverse_of: :author
has_many :budget_investment_change_logs,
foreign_key: :author_id,
inverse_of: :author,
class_name: "Budget::Investment::ChangeLog"
has_many :comments, -> { with_hidden }
has_many :comments, -> { with_hidden }, inverse_of: :user
has_many :failed_census_calls
has_many :notifications
has_many :direct_messages_sent, class_name: "DirectMessage", foreign_key: :sender_id
has_many :direct_messages_received, class_name: "DirectMessage", foreign_key: :receiver_id
has_many :direct_messages_sent,
class_name: "DirectMessage",
foreign_key: :sender_id,
inverse_of: :sender
has_many :direct_messages_received,
class_name: "DirectMessage",
foreign_key: :receiver_id,
inverse_of: :receiver
has_many :legislation_answers, class_name: "Legislation::Answer", dependent: :destroy, inverse_of: :user
has_many :follows
has_many :budget_rol_assignments

View File

@@ -1,6 +1,6 @@
class VotationSetAnswer < ApplicationRecord
belongs_to :votation_type
belongs_to :author, -> { with_hidden }, class_name: "User"
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :votation_set_answers
scope :by_author, -> (author) { where(author: author) }
end

View File

@@ -1,6 +1,9 @@
class Widget::Card < ApplicationRecord
include Imageable
belongs_to :page, class_name: "SiteCustomization::Page", foreign_key: "site_customization_page_id"
belongs_to :page,
class_name: "SiteCustomization::Page",
foreign_key: "site_customization_page_id",
inverse_of: :cards
# table_name must be set before calls to 'translates'
self.table_name = "widget_cards"