I am a RoR beginner and I need to check that the distance between user
and gym
Geocoder objects is less than 200 meters in order to create a check_in
object in the DB
Here is my code:
app/models/check_in.rb
class CheckIn < ApplicationRecord
belongs_to :user
belongs_to :gym
before_save :check_in_distance
protected
def check_in_distance
gym = Gym.find_by(id: params[:gym_id])
distance_to_gym = gym.distance_to([43.9,-98.6])
if distance_to_gym < 200
return true
end
end
end
app/controllers/api/v1/check_inscontroller.rb
class Api::V1::CheckInsController < ApplicationController
before_action :authenticate_request!
def check_in
@check_in = CheckIn.create!(check_in_params.merge( user: current_user))
render json: CheckInBlueprint.render(@check_in, root: :data)
end
private
def check_in_params
params.require(:check_in).permit(:gym_id, :check_in_latitude,
:check_in_longitude)
end
end
You want a custom validation and not a callback:
class CheckIn < ApplicationRecord
validate :distantance_to_gym
def distantance_to_gym
distance = gym.distance_to([check_in_latitude, check_in_longitude])
errors.add(:base, 'check in is too far from gym') if distance < 200
end
end
Don't use create!
in your controller. Sure its great for lazy debugging, but it raises an exception if the user passes invalid input. Invalid input is not an exceptional event and exceptions should not be used for normal control flow.
The "bang" methods such as .create!
should only be used in non-interactive contexts like seed files where creating the record should not be expected to fail or when wrapping it in a transaction to cause a rollback.
Use the normal "non-bang" methods such as save
and create
and check if the record was actually persisted and respond accordingly:
# see https://github.com/rubocop/ruby-style-guide#namespace-definition
module Api
module V1
class CheckInsController < ApplicationController
# this should be moved to the parent controller
# use a secure by default model where you opt out instead
before_action :authenticate_request!
def check_in
@check_in = CheckIn.new(check_in_params) do |c|
c.user = current_user
end
if @check_in.save
render json: CheckInBlueprint.render(@check_in, root: :data),
status: :created
else
render json: @check_in.errors.full_messages,
status: :unprocessable_entity
end
end
private
def check_in_params
params.require(:check_in).permit(:gym_id, :check_in_latitude,
:check_in_longitude)
end
end
end
end