Skip to content

Instantly share code, notes, and snippets.

@solnic
Last active August 19, 2016 08:46
Show Gist options
  • Save solnic/9b9deedd7891e2c7ac83 to your computer and use it in GitHub Desktop.
Save solnic/9b9deedd7891e2c7ac83 to your computer and use it in GitHub Desktop.
Virtus pg array type for Sequel
module Virtus
class PGArray < Attribute
primitive Sequel::Postgres::PGArray
def primitive
options[:primitive]
end
def coerce(value)
Sequel.pg_array(value)
rescue
value
end
def value_coerced?(value)
value.instance_of?(Sequel::Postgres::PGArray)
end
end
end
class Params
include Virtus.model(strict: true)
attribute :arr, Virtus::PGArray
end
irb(main):007:0> Virtus::Attribute.build(Virtus::PGArray, strict: true).coerce([1,2,3])
=> [1, 2, 3]
irb(main):008:0> Virtus::Attribute.build(Virtus::PGArray, strict: true).coerce('')
Virtus::CoercionError: Failed to coerce "" into Sequel::Postgres::PGArray
@ravicious
Copy link

ravicious commented Aug 18, 2016

tl;dr: You may not need to call primitive method within the custom attribute class body, unless you want to make Virtus use the custom attribute #coerce method to define all attributes of the given primitive.


I just found out that unless you want to write a generic coercer for any value of class Foo, you better omit the primitive Foo part.

For example, I wrote a custom coercer for BigDecimal, let's call it BigDecimalWithOneDecimalPoint. It tried to coerce the given value to a BigDecimal and then round it to one decimal point.

However, because I used primitive BigDecimal within the BigDecimalWithOneDecimalPoint definition, Virtus was using it for all BigDecimal values. Even if I specified that BigDecimal is the type of an attribute:

attribute :foo, BigDecimal

…Virtus would still use BigDecimalWithOneDecimalPoint for the coercion.

Removing the primitive call seems to solve the problem. However, in order to make the strict mode error message correct, you can no longer use options[:primitive] in the primitive method. For now, I just made mine method return the desired primitive.

I made a reproduction of the issue. However, I'm not sure if I should submit it as a separate Virtus issue, because I think calling primitive or defining value_coerced? is using the private API of Virtus. And yeah, I remember you're not working on Virtus. 😉

So I post it here to make people aware of the caveats of calling primitive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment