首页 > 解决方案 > 使用 Rails 5 的 Attributes API 进行本地化输入的自定义十进制类型

问题描述

我正在尝试使用 Rails 5 的 Attributes API 创建自定义十进制类型以接受本地化的用户输入。如下所示:

  class Decimal < ActiveRecord::Type::Decimal

    def cast(value)
      return unless value
      cast_value(value.is_a?(String) ? parse_from_string(value) : value)
    end

    def changed_in_place?(raw_old_value, new_value)
      raw_old_value != serialize(new_value)
    end

    def parse_from_string(value)
      delimiter = I18n.t('number.format.delimiter')
      separator = I18n.t('number.format.separator')
      value.gsub(delimiter, '_').gsub(separator, '.')
    end
  end

我还有一个自定义表单构建器来向用户显示格式化的值。提交表单以创建资源(模型实体)时,它工作正常。但是,当提交表单以更新资源时,validates_numericality_of验证器会将我的自定义属性标记为无效 (not_a_number)。在对活动模型的源代码进行了一些研究之后,我在NumericalityValidator.

https://github.com/rails/rails/blob/6a1b7985602c5bfab4c8875ca9bf0d598e063a65/activemodel/lib/active_model/validations/numericality.rb#L26-L49

但我不明白我可以改变什么来使它起作用。有任何想法吗?!

标签: ruby-on-railsruby-on-rails-5

解决方案


验证器使用一个名为 的变量raw_value。它试图从你的对象中获取原始值,检查第 35 到 38 行。

我想您可以使用带有“_before_type_cast”的属性名称在模型上定义一个方法,以返回验证器可以使用的数值。

如果您的属性被调用,可以说amount,您可以执行以下操作:

def amount_before_type_cast
  amount.to_number
end

然后你必须在你的自定义类型上定义一个方法来把它变成一个数字,可能是这样的:

def to_number
  value.gsub(/\D/,'').to_i #remove all non-digit and turn it into an integer
end

推荐阅读