【问题标题】:ActiveModel dynamic attribute typeActiveModel 动态属性类型
【发布时间】:2021-04-17 08:27:53
【问题描述】:

是否可以在运行时决定哪种类型在ActiveModel::Attributes 中转换属性?我有以下代码

class DynamicType < ActiveModel::Type::Value
  def cast(value)
    value # here I don't have access to the underlying instance of MyModel
  end
end

class MyModel
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :value, DynamicType.new
  attribute :type, :string
end

MyModel.new(type: "Integer", value: "12.23").value

我想根据type 的赋值来决定如何转换value 属性。我尝试使用自定义类型,但事实证明,在 #cast 方法中,您无法访问 MyModel 的底层实例(考虑到关注点分离,这也可能是一件好事)

我还尝试使用 lambda 块,假设 ActiveModel 看到的是一个响应 #call 并在运行时调用此块的对象(它没有):

class MyModel
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :value, ->(my_model) {
    if my_model.type == "Integer"
      # some casting logic
    end
  }
  attribute :type, :string
end

# => throws error
# /usr/local/bundle/gems/activemodel-5.2.5/lib/active_model/attribute.rb:71:in `with_value_from_user': undefined method `assert_valid_value' for #<Proc:0x000056202c03cff8 foo.rb:15 (lambda)> (NoMethodError)

背景:type 来自 DB 字段,其中可以包含各种类,而不仅仅是转换整数。

我可以只做一个def value 并在那里构建自定义逻辑,但我需要多次这样做,而且我还使用其他 ActiveModel 功能,如验证、嵌套属性......所以我必须自己处理集成.

所以也许有一种方法可以用ActiveModel 本身来做到这一点。

【问题讨论】:

  • 正如你所说 ActiveModel::Type::Value 无法访问实例本身。 github.com/rails/rails/blob/main/activemodel/lib/active_model/…,确实是故意的。但是为什么不能把 value 方法作为一些共享模块/关注点并以这种方式共享呢?
  • 通过模块共享它当然是可能的,但我认为通过attribute 方法来实现它会更具声明性。是的,我同意,似乎故意无法访问底层实例。如果没有猴子补丁/增强 ActiveSupport,也许没有办法实现这一点。

标签: ruby-on-rails ruby activemodel


【解决方案1】:

您将值作为字符串存储在数据库中,因此 ActiveRecord 会将其作为字符串检索。您将需要手动转换它。 Rails 提供了这些方法,我相信你很熟悉:

"1".to_i => to integer => 1
"foo.to_sym => to symbol => :foo
"1".to_f => to float => "1.0"
123.to_s => to string => "123"
(1..10).to_a => to array => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

那么在你的模型中你可以这样做:

def value
  case type
  when "Integer"
   value.to_i
  when "Float"
   value.to_f
  ...
end

这可行,但我发现这种方法存在问题。您使用名称type 标记一个属性,您可能会遇到Rails 的一些错误,抱怨type 是STI 的保留词,或者出现奇怪的错误,我建议您重命名它。

【讨论】:

  • 感谢类型提示,但后端不是数据库,它是一个简单的 Ruby 哈希。铸造本身不是问题,动态类型是
  • 他具体说的是“ActiveModel”,而不是“ActiveRecord”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-07
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
  • 1970-01-01
  • 2017-05-27
  • 2016-02-15
相关资源
最近更新 更多