如何在 Rails before_save 回调中验证两个 Geocoder 对象的 distance_to 小于 200 mts?

How to validate that the distance_to of two Geocoder objects is less than 200 mts within a Rails before_save callback?

我是 RoR 初学者,我需要检查 usergym Geocoder 对象之间的距离是否小于 200 米,以便在数据库

这是我的代码:

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

您需要自定义验证而不是回调:

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

不要在控制器中使用 create!。当然它非常适合惰性调试,但如果用户传递无效输入,它会引发异常。无效输入不是异常事件,不应将异常用于正常控制流。

诸如 .create! 之类的“bang”方法只能用于非交互式上下文,例如种子文件,在这些上下文中,不应期望创建记录会失败,或者将其包装在事务中以导致回滚.

使用正常的“非 bang”方法,例如 savecreate 并检查记录是否实际保存并做出相应响应:

# 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