I have a model called hardships
with an array value for the users that have voted to approve that hardship ( :approvals => []
in the hardship_params
).
I'm trying to create a list of hardships
on the index page for hardships which any given current_user
(using devise) has NOT approved.
The array is formed with user ids, so a typical value for @hardship.approvals
would be ["2", "3"]
if the second and third users had approved it.
I'm having trouble with the logic to call this from the controller. I've looked at SO posts like this but can't glean an answer that works for Rails 5.2 and Postgres.
I'm looking for something like this:
@need_approval = Hardship.where.not(approvals.include?(current_user.id.to_s))
However, I know that doesn't work because approvals
isn't a variable and the syntax is all wrong. Can anyone help me Rails-ify this so it gets the info I need?
ADDITIONAL INFORMATION
Here's my hardships
table:
create_table "hardships", force: :cascade do |t|
t.string "full_name"
t.date "date"
t.string "position"
t.string "branch"
t.string "email_non_toca"
t.string "mobile"
t.string "address"
t.string "city"
t.string "state"
t.string "zip"
t.string "bank_name"
t.string "bank_phone"
t.string "bank_address"
t.date "start_date"
t.boolean "accident", default: false
t.boolean "catastrophe", default: false
t.boolean "counseling", default: false
t.boolean "family_emergency", default: false
t.boolean "health", default: false
t.boolean "memorial", default: false
t.boolean "other_hardship", default: false
t.string "other_hardship_description"
t.decimal "requested_amount"
t.text "hardship_description"
t.decimal "self_fund"
t.string "intent_signature"
t.date "intent_signature_date"
t.string "release_signature"
t.date "release_signature_date"
t.string "status", default: "Application Started"
t.string "final_decision", default: "Not Decided"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "returned", default: false
t.text "approvals", default: [], array: true
t.text "rejections", default: [], array: true
t.index ["user_id"], name: "index_hardships_on_user_id"
end
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
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.