ActiveRecord :: Base.store自动类型转换

时间:2012-07-20 15:13:34

标签: ruby-on-rails ruby casting ruby-on-rails-3.2

有没有办法自动对使用ActiveRecord :: Base.store存储的值进行类型转换?

采取这个完全不切实际的例子:

class User < ActiveRecord::Base
  store :settings, accessors: [ :age ]
end

user = User.new(age: '10')
user.age # => '10'

我知道我可以覆盖年龄的读者方法将其转换为整数,但我很好奇是否有一种无证的方法。

尽量避免这种情况:

class User < ActiveRecord::Base
  store :settings, accessors: [ :age ]

  def age
    settings[:age].to_i
  end
end

user = User.new(age: '10')
user.age # => 10

更新

寻找类似的东西:

class User < ActiveRecord::Base
  store :settings, accessors: {:age => :to_i}
end

或者:

class User < ActiveRecord::Base
  store :settings, accessors: {:age => Integer}
end

6 个答案:

答案 0 :(得分:1)

我知道有两种方法可以做到。其中一个你每次设置它就转换它。另一个只有在将其保存到数据库时才转换它。

选项一:自定义设置器

class User < ActiveRecord::Base

  ...

  # public method
  def age=(age)
    self.settings[:age] = age.to_i
  end

  ...

end

在控制台中:

$ user.age = '12'     # => "12"
$ user.age            # => 12
$ user.age.class      # => Fixnum
$ user = User.new age: '5'
$ user.age.class      # => Fixnum

选项二:before_save调用(或调用前不同)

class User < ActiveRecord::Base
  before_save :age_to_int

  ...

  private

    def age_to_int
      # uncomment the if statement to avoid age being set to 0
      # if you create a user without an age
      self.age = self.age.to_i # if self.age 
    end

end

在控制台

$ user = User.new(age: '10')
$ user.save
$ user.age            # => 10
$ user.age.class      # => Fixnum

选项二的缺点:

$ user.age = '12'
$ user.age            # => "12"

如果我是你,我会使用自定义setter。如果需要独立于数据库列的默认值(字符串),除了setter之外还要使用before_save。

答案 1 :(得分:1)

从Rails 3.2.7开始,没有办法自动对类值进行类型转换。如果我遇到过这样的问题,我会更新这个问题:/

答案 2 :(得分:0)

链接商店访问者方法而不是覆盖它们会更好,因为这些神奇咒语创建的方法绝不像你想象的那样简单:

define_method(key) do
  send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
  send(store_attribute)[key]
end

因此,例如,在整数的情况下,我执行此操作:

def age_with_typecasting
  ActiveRecord::ConnectionAdapters::Mysql2Adapter::Column.value_to_integer(age_without_typecasting)
end

alias_method_chain :age, :typecasting

同样,它没有内置,但它可以做到这一点。它还利用数据库连接适配器从存储在数据库中的字符串转换为您希望值类型的转换。根据您使用的数据库更改适配器。

答案 3 :(得分:0)

StorextActiveRecord::Store.store_accessor之上添加了类型转换和其他功能。

答案 4 :(得分:0)

我们没有发现任何未记录的方式,但是即使您这样做,也不建议您将代码依赖于Rails私有方法。

这是我们在Rails 5.2中使用受支持的API进行操作的方式。假设您有一个汇率模型,并且想将每日汇率存储在一个名为:rate_per_day的数据库列中(我们使用的是MySQL,因此该列的类型为TEXT):

class Rate < ApplicationRecord

  DAILY_RATES = [
    :monday_rate,
    :tuesday_rate,
    :wednesday_rate,
    :thursday_rate,
    :friday_rate,
    :saturday_rate,
    :sunday_rate,
  ]

  store :rate_per_day, accessors: DAILY_RATES, coder: JSON

  DAILY_RATES.each do |method|

    define_method(method) do
      super().try(:to_i)
    end

    define_method("#{method}=") do |value|
      super(value.try(:to_i))
    end

  end

这是官方支持的。阅读https://api.rubyonrails.org/classes/ActiveRecord/Store.html中的覆盖默认访问者。

顺便说一下,我们使用新的Attributes API进行了调查,因为它支持自动类型转换,但是除非我们创建了自定义类型对象,否则找不到将所有数据存储在单个列中的方法。

答案 5 :(得分:0)

activerecord-typedstore对此非常有效,并且可以防止空值和空白值

https://github.com/byroot/activerecord-typedstore