Is there an approved way to add or register a new ARel visitor without adding it to the ARel source? Looking at the some of the source code it appears that new visitors are just hard coded as a new methods or aliases.
The reason I’m asking is because I get “TypeError: Cannot visit ClassName” when I override reader methods with some sort of composed object (similar to Active Record’s composed_of) and use that attribute in a uniqueness validation.
Take the following for example:
class Cat < ActiveRecord::Base
validates :name, :uniqueness => { :scope => [:breed] }
def breed
CatBreed.new(super)
end
end
class CatBreed
def initialize(breed)
@breed = breed
end
def to_s
@breed
end
end
The above code will raise “TypeError: Cannot visit CatBreed” when I try to create a new cat unless I add a visitor method to Arel::Visitors::ToSql called visit_CatBreed:
class Arel::Visitors::ToSql
def visit_CatBreed(value)
quoted(value.to_s)
end
end
I’m hoping there is a better way to deal with this problem, either with a less hacky ARel approach or something different all together.
Is there an approved way to add or register a new ARel visitor without
adding it to the ARel source? Looking at the some of the source code<https://github.com/rails/arel/blob/master/lib/arel/visitors/to_sql.rb#L434-466> it
appears that new visitors are just hard coded as a new methods or aliases.
The reason I'm asking is because I get "TypeError: Cannot visit ClassName"
when I override reader methods with some sort of composed object (similar
to Active Record's composed_of) and use that attribute in a uniqueness
validation.
Take the following for example:
class Cat < ActiveRecord::Base
validates :name, :uniqueness => { :scope => [:breed] }
def breed
CatBreed.new(super)
end
end
class CatBreed
def initialize(breed)
@breed = breed
end
def to_s
@breed
end
end
The above code will raise "TypeError: Cannot visit CatBreed" when I try to
create a new cat unless I add a visitor method to Arel::Visitors::ToSql
called visit_CatBreed:
class Arel::Visitors::ToSql
def visit_CatBreed(value)
quoted(value.to_s)
end
end
Doing this is fine for now. The way the AST is translated to SQL won't
change, so this will probably not break in the future.
I'm hoping there is a better way to deal with this problem, either with a
less hacky ARel approach or something different all together.
It seems like we should be using the underlying database value (the
attribute value rather than the return value of the method call) for the
unique constraint. I think this might be a bug. Jon, wdyt?
Aaron,
Thanks for the reply. I played around with it a little and it seems like changing how the scope_value is retrieved in ActiveRecordmodule::Validationsclass::UniquenessValidator would fix the issue (though I’m not sure what sort of repercussions this would have elsewhere)
scope_value = record.send(scope_item)
to either of these
record[scope_item]
record.read_attribute(scope_item)
I'm hoping there is a better way to deal with this problem, either with a
less hacky ARel approach or something different all together.
It seems like we should be using the underlying database value (the
attribute value rather than the return value of the method call) for the
unique constraint. I think this might be a bug. Jon, wdyt?
Yes, I agree. Who wants to create a patch?