Rails: How to only allow User to apply to job only once? - ruby-on-rails

I am creating a job board, and I don't want to allow the users the option to apply for the same job twice. How can I limit this?
app/views/jobs/job.html.erb
<% if applied_to_this_job? %>
<div class="alert" role="alert">You have already applied to this job!</div>
<% else %>
<%= link_to 'Apply', new_job_application_path(#job) %>
<% end %>
app/helpers/jobs_helper.rb
def applied_to_this_job?
JobApplication.exists? user_id: current_user.id
end
Obviously this doesn't work because it checks if this user has applied to any job. How Can I check to see if the current user has applied to the job being viewed.
Also, how can I limit this at the controller level so that the user can't go to job_application/new and get to the form.

You would use a before_filter in the controller action.
class JobsController < ActionController::Base
before_filter :has_applied?, only: [new, create]
....
private
def has_applied?
if JobApplication.where(user_id: :current_user.id, job_id: params[:job_id]).any?
redirect_to :index, alert: "You have already applied"
end
end
end
This would allow the user to visit /jobs/new and post the application to /jobs/create unless they have applied. If they have applied, they will be redirected to the index in the sample code.
Also as another answer has noted, it would be wise to pass in the job id as well. Updated sample code above to reflect.

You need to check and see if the JobApplication object is for this #job try:
JobApplication.where( user_id: current_user.id, job_id: #job.id ).exists?

Although what you've accepted will work, I think it's somewhat of a surface-level fix.
You'll be much better using validators to determine if the user can actually create another job application. This will protect against any problems with the business logic in your "front-end" views
Here's how I'd handle it:
--
Uniq
#app/models/user.rb
class User < ActiveRecord::Base
has_one :job_application
end
#app/models/job_application.rb
class JobApplication < ActiveRecord::Base
belongs_to :user
validates :user_id, uniquness: true
end
You may also wish to give your database a uniq index for your user_id column:
> $ rails g migration AddUniqueIndex
#config/db/add_unique_index.rb
class AddUniqueIndex < ActiveRecord::Migration
def change
add_index :job_applications, [:job_id, :user_id], unique: true
end
end
This will give you a highly efficient DB-level uniqueness index - meaning that if you try and add any more applications than is permitted, it will either fail silently, or come back with an error.
Controller
The structure of the controller would allow you to be less stringent about the accessibility of the job_application functionality:
#app/views/jobs/job.html.erb
<% if current_user.has_applied?(params[:job_id]) %>
<div class="alert" role="alert">You have already applied to this job!</div>
<% else %>
<%= link_to 'Apply', new_job_application_path(#job) %>
<% end %>
#app/models/user.rb
class User < ActiveRecord::Base
has_many :job_applications
def has_applied?(job_id)
job_applications.find job_id
end
end

Related

RoR: Using a method defined in user model in a different controller

I'm new in RoR and hoping you experts can help me on this. Apologies in advance if my question sounds weird or stupid. Let me know if you need more clarification, thanks so much in advance.
I have a boolean method called is_pollie (set default to false) in the user model which I want to change it to true once a user completed a form in a different controller called profiles_controller.rb.
Now, I have a user model with a defined method:
class User < ApplicationRecord
has_many :profiles
def self.is_pollie?
is_pollie
end
And in a different controller called profiles_controller.rb:
class ProfilesController < ApplicationController
before_action :authenticate_user!, except: [:show]
def create
#pollie = User.is_pollie?
#profile = current_user.profiles.build(profile_params)
if #profile.save
# what should I put here if I want the is_pollie? to change to true upon
a user click the save button on the form?
redirect_to basic_profile_path(#profile)
else
flash[:alert] = "Oh no, something went wrong."
render :new
end
end
In the page where the form is:
<%= form_for #profile do |f| %>
<div class="form-group">
<label>Displayed name:</label>
<%= f.text_field :display_name,class: "form-control"%>
</div>
<%= f.submit "Save", class: "btn-submit" %>
<% end %>
Hope you understand my question and are able to help. Thanks very much again.
You can try:
current_user.update(is_pollie: true)
BTW, a couple of other points...
This:
class User < ApplicationRecord
has_many :profiles
def self.is_pollie?
is_pollie
end
end
doesn't make any sense because self makes is_pollie? a class method. But, is_pollie is an instance value.
Also, you don't even need is_pollie? because you can use do current_user.is_pollie which will return true of false.
Finally, you're not using #pollie = User.is_pollie? anywhere, so why do it?
Use current_user.update_column(:is_pollie, true)
update method will trigger the call_backs, it's recommended to use update_column for updating a selected attribute.
For multiples you can use update_columns(attributes1: value, attributes2: value)

Creating multiple unrelated associated model instances in Rails

I have what I believe is fairly simple model setup:
class Booking < ApplicationRecord
has_many :payments
end
class Payment < ApplicationRecord
belongs_to :booking
end
Now, I want to create a form that allows a user to register payments in batch. That is, the form should have a number of input rows, each one representing a payment for some booking (i.e., each row has some fields for the columns of Payment plus a booking_id field). Upon submitting, each row should cause the creation of a corresponding Payment, which should be associated with the Booking indicated by the user for that row.
This seems to be surprisingly tricky, and my Google-Fu is failing me. I've tried the following (inspired by this post describing a solution without associations), which I thought would work, but which, well, doesn't:
class Admin::PaymentController < Admin::Controller
def batch
#payments = []
5.times do
#payments << Payment.new
end
end
def submit
params["payments"].each do |payment|
if payment["booking_id"] != "" || payment["amount"] != ""
Payment.create(payment_params(payment))
end
end
end
private
def payment_params(p)
p.permit(:booking_id, :amount)
end
end
<%= form_tag admin_payment_submit_path do %>
<% #payments.each do |payment| %>
<%= fields_for 'payments[]', payment do |p| %>
<%=p.text_field :booking_id%>
<%=p.number_field :amount%>
<% end %>
<% end %>
<%= submit_tag %>
<% end %>
This renders the form without erroring out, but the HTML names work out such that only a single payment (the last one) is submitted (e.g., name="payments[booking_id]"). Furthermore, upon submitting, I get the error
undefined method `permit' for "booking_id":String Did you mean? print
Which is less than helpful.
I've tried other variations too, but I feel like at this point I'm just feeling my way in the dark. Any guidance would be greatly appreciated!
params in controller is a instance of ActiveController::Parameter that has permit method.
But params["payments"] is a just array as subset of params.
For multiple payment params
def submit
payment_params.each do |payment|
if payment["booking_id"].present? || payment["amount"].present?
Payment.create(payment)
end
end
end
private
def payment_params
params.permit(payments: [:booking_id, :amount])["payments"]
end
For Single payment param
def submit
if payment_param["booking_id"].present? || payment_param["amount"].present?
Payment.create(payment_param)
end
end
private
def payment_param
params.require(:payments).permit(:a, :b)
end

:update form is not saving boolean value

I'm a beginner building an app for my language students that checks whether the user copied a term correctly into a form. The Word model has a :term attribute, which is what the user will be copying. The Word Exposition model is associated with the Word model; upon enrollment each WordExposition-Word association's :completed attribute is set to false. I'm trying to implement a form in the WordExposition show view, such that if the user types the correct term, the WordExposition completed attribute will change to true from the default false.
In order to check whether the Word.term matches the term that the student copied I have a word_from_student_matches_word method in the WordExposition model, which I want to run before updating. As of now, I'm getting undefined local variable or methodword_from_student_matches_word'` upon submission of the form. How could I go about checking for matching spelling and updating the boolean attribute from the view?
WordExposition model:
class WordExposition < ActiveRecord::Base
belongs_to :enrollment
belongs_to :word
delegate :term, to: :word
delegate :reference, to: :word
delegate :image, to: :word
delegate :sound, to: :word
attr_accessor :term_given_by_student
validate :word_from_student_matches_word, on: :update
def word_from_student_matches_word
return true if word.term == term_given_by_student
errors.add(:term_given_by_student, "Terms don't match")
end
def next_word_exposition
WordExposition.where(["id > ? AND lesson_id = ?", id, lesson_id]).first
end
end
Word Expositions controller:
class WordExpositionsController < ApplicationController
before_action :authenticate_user!
before_action :require_enrollment_in_lesson
def show
#word = current_enrollment.word_expositions.find_by!(word_id: params[:id])
end
def update
current_word_exposition
if word_from_student_matches_word
current_word_exposition.completed = true
current_word_exposition.save
end
end
private
helper_method :current_lesson
def current_lesson
#current_lesson ||= Lesson.find(params[:lesson_id])
end
helper_method :current_enrollment
def current_enrollment
#current_enrollment ||= Enrollment.find_by!(lesson_id: params[:lesson_id], user_id: current_user.id)
end
def require_enrollment_in_lesson
if !(current_user.enrolled_in?(current_lesson))
redirect_to lesson_path(current_lesson), alert: 'You need to enroll in order to view the activities!'
end
end
def word_exposition_params
params.require(:word_exposition).permit(:completed)
end
def current_word_exposition
#current_word_exposition ||= WordExposition.find(params[:id])
end
end
Word Expositions show view:
<h1>Word Exposition</h1>
<!-- display the term to be copied -->
<div class="col-xs-10 col-xs-offset-1">
<h2><%= #word.term %></h2><br>
<!-- Form to check matching spelling and update WordExposition :completed to true if correct -->
<%= simple_form_for #word, url: lesson_word_exposition_path(current_lesson, #word), method: :patch do |f| %>
<%= f.input :term_given_by_student, label: "Enter the term exactly as above:" %><br>
<%= f.button :submit, class: 'btn btn-primary' %>
<% end %>
</div>
Rake routes:
lesson_enrollments POST /lessons/:lesson_id/enrollments(.:format) enrollments#create
lesson_word_exposition GET /lessons/:lesson_id/word_expositions/:id(.:format) word_expositions#show
PATCH /lessons/:lesson_id/word_expositions/:id(.:format) word_expositions#update
PUT /lessons/:lesson_id/word_expositions/:id(.:format) word_expositions#update
lessons GET /lessons(.:format) lessons#index
lesson GET /lessons/:id(.:format) lessons#show
word GET /words/:id(.:format) words#show
teacher_lesson_words POST /teacher/lessons/:lesson_id/words(.:format) teacher/words#create
new_teacher_lesson_word GET /teacher/lessons/:lesson_id/words/new(.:format) teacher/words#new
edit_teacher_lesson_word GET /teacher/lessons/:lesson_id/words/:id/edit(.:format) teacher/words#edit
teacher_lesson_word PATCH /teacher/lessons/:lesson_id/words/:id(.:format) teacher/words#update
PUT /teacher/lessons/:lesson_id/words/:id(.:format) teacher/words#update
DELETE /teacher/lessons/:lesson_id/words/:id(.:format) teacher/words#destroy
teacher_lessons POST /teacher/lessons(.:format) teacher/lessons#create
new_teacher_lesson GET /teacher/lessons/new(.:format) teacher/lessons#new
edit_teacher_lesson GET /teacher/lessons/:id/edit(.:format) teacher/lessons#edit
teacher_lesson GET /teacher/lessons/:id(.:format) teacher/lessons#show
PATCH /teacher/lessons/:id(.:format) teacher/lessons#update
PUT /teacher/lessons/:id(.:format) teacher/lessons#update
DELETE /teacher/lessons/:id(.:format) teacher/lessons#destroy
you are wrong in your controller
def update
current_word_exposition
current_word_exposition.completed = true #this line move to callback in your model changing the flag before save
if current_word_exposition.save
#nothing o something
else
#do something
end
end
Your model is validating the term on update, and return false if something went wrong. Just need to save the model instance and control the result

How to get assocated model's attribute values in views ? Rails

I have three models...
models/resident.rb
class Resident < ActiveRecord::Base
belongs_to :hostel
has_many :leaves,dependent: :delete_all
has_one :user,dependent: :delete
end
models/user.rb
class User < ActiveRecord::Base
belongs_to :resident
end
models/leave.rb
class Leave < ActiveRecord::Base
belongs_to :resident
end
Now when I am trying to access the value of leave's attribute in views/leave/show.html.erb
I am getting this:
app/views/leaves/show.html.erb
<%= #leaves %>
out put In Browser :
#<Leave::ActiveRecord_Associations_CollectionProxy:0x007fde611850f0>
My leave controller looks like :
leaves_controller.rb
class LeavesController < ApplicationController
def new
if logged_in?
#leave=Leave.new
else
flash[:info]="Please login to mark a leave"
redirect_to root_path
end
end
def show
#leaves= current_user.resident.leaves
end
def create
#leave=current_user.resident.leaves.create(leave_params)
if #leave.save
flash[:info] = "Leave successfully marked"
redirect_to new_leave_path
else
flash[:danger] = "Something wrong Happened try again"
redirect_to root_path
end
end
private
def leave_params
params.require(:leave).permit(:start_date,:end_date,:destination)
end
end
Am I making correct leaves for resident and related user (create method)?
Is show method correct ?
and How to assess the user's leaves attribute in show.html.erb of leaves views.
A Resident has_many Leaves so current_resident.leaves returns an array of all the current_resident's leaves. You will need to loop through leaves to show individual attributes. Try
#leaves.first.attribute_name
in your view to get an idea of how the data is represented. To show all the leaves you'll need to use a loop in the view
#leaves.each do |leave|
leave.inspect
end
You are doing everything fine, and show method is fine, and the template shows exactly what is was told to show.
#leaves is a collection. You probably want to show it’s elements? This should lead to the proper solution:
<% #leaves.each do |l| %>
<%= l.inspect %>
<% end %>

Passing additional information through controllers

I have a comment model and when a comment is created it makes a note of the profile_name of the user and saves it. So it is basically saving #user.profile_name as comment.profile_name.
If I now want to show additional information from the user who has that profile_name such as #user.avatar - how would I query it without having to add extra fields to be saved when a comment is created?
I Imagined I could do something like
#user = User.all
#comment_user = User.where(:profile_name => #user.profile_name)
And then run
<% #comment_user.each do |user| %>
<%= user.first_name %>
<% end %>
In the view but I get an error
undefined method `profile_name' for #<ActiveRecord::Relation::ActiveRecord_Relation_User:0x007fc2081c7290>
I'm not even sure if that is the correct way to proceed even if I didn't get the error.
The error is because you are trying to call profile_name on User.all, but what you want is a single user. Try:
#user = User.first
#comment_user = User.where(:profile_name => #user.profile_name)
That being said, you should be using ActiveRecord associations instead. Instead of storing the user's profile_name in the comment record, you should store the user_id . Create a belongs_to :user association in the Comment model and then you can access the other user attributes directly.
First add a :user_id integer column to the the comments table, and then define your associations:
class Comment < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :comments
end
When creating a new comment, you can do something like:
#comment = #user.comments.create(text: 'my awesome comment')
Then in your view:
<%= #comment.text %>
<%= #comment.user.profile_name %>

Resources