类型转换 数组值,如果可以查询



我目前正在寻求使用Rails 5.0.0.beta1.1在枚举实现之后使用ActiveRecord::Type::Value实现自定义类型。

到目前为止,我已经让它可以正确地将所有内容从数据库转换为数据库。

class BitFieldType < ActiveRecord::Type::Value
  def initialize(name, mapping, subtype)
    @name = name
    @mapping = mapping
    @subtype = subtype
  end
  def cast(value)
    [...]
  end
  def deserialize(value)
    [...]
  end
  def serialize(value)
    [...]
  end
  def assert_valid_value(value)
    [...]
  end
  protected
  attr_reader :name, :mapping, :subtype
end
class SomeModel < ActiveRecord::Base
  decorate_attribute_type(:my_attribute_name, :bit_field) do |subtype|
    BitFieldType.new(:my_attribute_name, [...], subtype)
  end
end

我唯一的问题是:我想在where()调用中将数组作为一个整体,而不是每个值。

现在,当我打电话举例:

SomeModel.where(my_attribute_name: [1, 2, 3])

。它将每个值(123(传递给我的serialize方法,然后构建一个查询,例如

[...] where my_attribute_name IN (<serialized 1>, <serialized 2>, <serialized 3>)

我宁愿决定该调用的结果如何,因此基本上数组[1, 2, 3]传递给我的serialize方法,而不是它的每个值。然后我可以将其serialize为一个值,该值应简单地与=进行比较:

[...] where my_attribute_name = <serialized [1, 2, 3]>

这可能吗?


编辑:我或

多或少地通过猴子修补存档了我想要的东西 活动记录::活动记录::数组处理程序

diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
index 95dbd6a..33077c1 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -6,6 +6,9 @@ module ActiveRecord
       end
       def call(attribute, value)
+        if attribute.relation.send(:type_caster).type_cast_where_array?(attribute.name)
+          value = Array.wrap(attribute.type_cast_for_database(value))
+        end
         values = value.map { |x| x.is_a?(Base) ? x.id : x }
         nils, values = values.partition(&:nil?)

现在,它使用受保护的方法:type_caster和新添加的方法.type_cast_where_array?

这个新方法必须添加到 ActiveRecord::TypeCaster::Map:

diff --git a/activerecord/lib/active_record/type_caster/map.rb b/activerecord/lib/active_record/type_caster/map.rb
index 3a367b3..b710081 100644
--- a/activerecord/lib/active_record/type_caster/map.rb
+++ b/activerecord/lib/active_record/type_caster/map.rb
@@ -11,6 +11,10 @@ module ActiveRecord
         type.serialize(value)
       end
+      def type_cast_where_array?(attr_name)
+        types.type_for_attribute(attr_name.to_s).type_cast_where_array?
+      end
+
       protected
       attr_reader :types

为了使它与默认类型一起使用

,还必须修补ActiveModel::Type::Value:
diff --git a/activemodel/lib/active_model/type/value.rb b/activemodel/lib/active_model/type/value.rb
index 0d2d687..114f85d 100644
--- a/activemodel/lib/active_model/type/value.rb
+++ b/activemodel/lib/active_model/type/value.rb
@@ -50,6 +50,9 @@ module ActiveModel
         value.inspect
       end
+      def type_cast_where_array?
+      end
+
       # These predicates are not documented, as I need to look further into
       # their use, and see if they can be removed entirely.
       def binary? # :nodoc:

现在在我的BitFieldType中,我可以覆盖该方法以返回 true:

class BitFieldType < ActiveRecord::Type::Value
  def type_cast_where_array?
    true
  end
end

最后但并非最不重要的一点是,ActiveRecord::TypeCaster::连接也必须使用新方法进行修补,以使所有测试通过。

diff --git a/activerecord/lib/active_record/type_caster/connection.rb b/activerecord/lib/active_record/type_caster/connection.rb
index 7ed8dcc..f5623b4 100644
--- a/activerecord/lib/active_record/type_caster/connection.rb
+++ b/activerecord/lib/active_record/type_caster/connection.rb
@@ -12,6 +12,9 @@ module ActiveRecord
         connection.type_cast_from_column(column, value)
       end
+      def type_cast_where_array?(_attr_name)
+      end
+
       protected
       attr_reader :table_name

我知道方法名称可能不是最好的,使用受保护的方法也很糟糕,但这似乎是一种键入强制转换数组的方法,其中调用没有太多代码更改并且所有 activerecord 测试都通过。

对此有何评论?

不,这是不可能的。数组和哈希在传递给where时具有特殊含义。PG Array、JSON 和 HStore 类型以及 serialize 类宏也存在完全相同的问题。

相关内容

最新更新