Closed mainameiz closed 10 years ago
You can use strict mode that will raise an error when something can't be coerced:
class Foo
include Virtus.model(strict: true)
attribute :bar, Integer
end
foo = Foo.new(bar: 'asd')
# => BOOM ERROR
I dont want exception to be raised, these exceptions need to be catched everywhere i use this object, so I want values that cant be coerced to be nil
I'm trying now to subclass from Virtus::Attribute::Coercer and reimplement #call method
Don't go this route, you can have a sanitizer that goes through all attributes and set nils everywhere where something was not coerced:
class Foo
include Virtus.model
attribute :bar, Integer
end
foo = Foo.new(bar: 'asd')
Foo.attribute_set.each do |attribute|
foo[attribute.name] = nil unless attribute.value_coerced?(foo[attribute.name])
end
Thanks for example, but if Foo has other Virtus models as attributes this way becomes a hard way to solve the problem. Do you think about some sort of callbacks or hooks for coercion mechanism or setting to overwrite default Coercer class?
My solution:
class Coercer < Virtus::Attribute::Coercer
def call(value)
coercers[value.class].public_send(method, value)
rescue ::Coercible::UnsupportedCoercion
nil
end
end
class Virtus::Attribute
def self.build_coercer(type, options = {})
klass = options.fetch(:coercer_class) { Virtus::Attribute::Coercer }
klass.new(type, options.fetch(:configured_coercer) { Virtus.coercer })
end
end
class Foo
include Virtus.model
attribute :bar, Integer, coercer_class: Coercer
end
Not really, recursion to the rescue:
class Foo
include Virtus.model
attribute :bar, Integer
end
class Bar
include Virtus.model
attribute :bar, Integer
attribute :foo, Foo
end
object = Bar.new(bar: 'asd', foo: Foo.new(bar: 'asd'))
def sanitize(object)
object.class.attribute_set.each do |attribute|
value = object[attribute.name]
if value.class.respond_to?(:attribute_set)
sanitize(value)
else
object[attribute.name] = nil unless attribute.value_coerced?(value)
end
end
object
end
puts sanitize(object).inspect
I would still highly recommend putting this sanitization outside of the virtus object layer. You're mixing too many concerns in one place not to mention really hacky approach with monkey-patching.
Ok
class Foo
include Virtus.model
attribute :bar, Integer
def initialize(*)
super
self.class.sanitize(self)
end
def self.sanitize(object)
...
end
end
Thanks for help! Awesome gem, good job and best wishes! :-)
Sure :)
How to make this?