If I were you, I think I would be tempted to create a ApplicationAction model. Something like:
# == Schema Information
#
# Table name: application_actions
#
# id :bigint not null, primary key
# user_id :integer
# application_type :string
# application_id :integer
# action :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class ApplicationAction < ApplicationRecord
belongs_to :user
belongs_to :application, polymorphic: true
enum action: {
approved: 0,
rejected: 1,
requested_to_modify: 2
}
end
Then, in your User model, do something like:
# == Schema Information
#
# Table name: users
#
# id :bigint not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
#
class User < ApplicationRecord
has_many :application_actions
%i(
approved
rejected
requested_to_modify
).each do |action_sym|
%w(
hardship
scholarship
charity
).each do |application_type|
# Create the associations:
# - hardship_applications
# - scholarship_applications
# - charity_applications
has_many "#{application_type}_applications".to_sym,
-> { distinct },
through: :application_actions,
source: :application,
source_type: application_type.camelize
# Define the methods:
# - approved_application_actions
# - rejected_application_actions
# - requested_to_modify_application_actions
define_method("#{action_sym}_application_actions") do
application_actions.send(action_sym)
end
# Define the methods:
# - approved_hardship_applications
# - rejected_hardship_applications
# - requested_to_modify_hardship_applications
# - approved_scholarship_applications
# - rejected_scholarship_applications
# - requested_to_modify_scholarship_applications
# - approved_charity_applications
# - rejected_charity_applications
# - requested_to_modify_charity_applications
define_method("#{action_sym}_#{application_type}_applications") do
send("#{application_type}_applications").
joins(:application_actions).
where(
application_actions: {
action: ApplicationAction.actions[action_sym]
}
)
end
# Define the methods:
# - hardship_applications_not_approved
# - hardship_applications_not_rejected
# - hardship_applications_not_requested_to_modify
# - scholarship_applications_not_approved
# - scholarship_applications_not_rejected
# - scholarship_applications_not_requested_to_modify
# - charity_applications_not_approved
# - charity_applications_not_rejected
# - charity_applications_not_requested_to_modify
define_method("#{application_type}_applications_not_#{action_sym}") do
application_type.
camelize.
constantize.
where.
not(id: send("#{action_sym}_#{application_type}_applications"))
end
end
end
end
And, in your Hardship model, do something like:
# == Schema Information
#
# Table name: hardships
#
# id :bigint not null, primary key
# application_type :integer default(NULL)
# created_at :datetime not null
# updated_at :datetime not null
#
class Hardship < ApplicationRecord
has_many :application_actions, as: :application
enum application_type: {
accident: 0,
catastrophe: 1,
counseling: 2,
family_emergency: 3,
health: 4,
memorial: 5,
other_hardship: 6
}
end
Then, if I run that through a quick RSpec test:
require 'rails_helper'
RSpec.describe 'Hardship Applications' do
before(:each) do
@user_1 = User.create!
@user_2 = User.create!
@hardship_1 = Hardship.create!
@user_1.
application_actions.
create!(application: @hardship_1).
approved!
@user_2.
application_actions.
create!(application: @hardship_1).
rejected!
end
it "user_1 approved_hardship_applications to include hardship_1" do
expect(@user_1.approved_hardship_applications).to include(@hardship_1)
end
it "user_1 hardship_applications_not_approved NOT to include hardship_1" do
expect(@user_1.hardship_applications_not_approved).not_to include(@hardship_1)
end
it "user_1 rejected_hardship_applications NOT to include hardship_1" do
expect(@user_1.rejected_hardship_applications).not_to include(@hardship_1)
end
it "user_2 approved_hardship_applications NOT to include hardship_1" do
expect(@user_2.approved_hardship_applications).not_to include(@hardship_1)
end
it "user_2 hardship_applications_not_approved to include hardship_1" do
expect(@user_2.hardship_applications_not_approved).to include(@hardship_1)
end
it "user_2 rejected_hardship_applications to include hardship_1" do
expect(@user_2.rejected_hardship_applications).to include(@hardship_1)
end
end
I get...
Hardship Applications
user_1 approved_hardship_applications to include hardship_1
user_1 hardship_applications_not_approved NOT to include hardship_1
user_1 rejected_hardship_applications NOT to include hardship_1
user_2 approved_hardship_applications NOT to include hardship_1
user_2 hardship_applications_not_approved to include hardship_1
user_2 rejected_hardship_applications to include hardship_1
Finished in 0.13431 seconds (files took 0.90021 seconds to load)
6 examples, 0 failures
So, you can do:
@need_approval = current_user.hardship_applications_not_approved
instead of:
@need_approval = Hardship.where.not(approvals.include?(current_user.id.to_s))
Also you'll note that in the Hardship model, I made all your application types into an enum. This will cut down on empty fields (assuming a Hardship application can be of only one application type).
There appear to be other opportunities for reducing redundancy. For instance, you could create PhysicalAddress, PhoneNumber, and EmailAddress models and associate these with each type of application. You could also create your application status to an enum and default it to application_started. Same with final_decision.