Before_validation attribute modification

I want to strip out embedded separators from a harmonized tariff code
before validating it. I thought (naively) that I could do this:

class Product < ActiveRecord::Base

validates_format_of :hs_class_code, :with =>
/\A(\d{6}|\d{8}|\d{10})?\Z/

protected

def before_validation
hs_class_code = hs_class_code.tr(".","").strip if
!hs_class_code.nil?
end

but evidently hs_class_code is always nil. What is the proper syntax to
use here?

Thanks,

Try this:

class Product < ActiveRecord::Base

validates_format_of :hs_class_code, :with =>
/\A(\d{6}|\d{8}|\d{10})?\Z/

before_validation :clean_tariff_code

protected

def clean_tariff_code
self.hs_class_code.gsub!(".","") unless self.hs_class_code.nil?
end

It makes it more clear if you name the method. Also, it is a good idea
to use self. to be sure that what are intended to be accessor method
calls are not interpreted as local variables.

Another problem was the use of tr which returns nil if no substitution
is made. The was your initial method was written, if hs_class_code did
not contain the “.” character, the result would be to call strip on nil.

Hope this helps
-Bill

William P. wrote:

Try this:

class Product < ActiveRecord::Base

validates_format_of :hs_class_code, :with =>
/\A(\d{6}|\d{8}|\d{10})?\Z/

before_validation :clean_tariff_code

protected

def clean_tariff_code
self.hs_class_code.gsub!(".","") unless self.hs_class_code.nil?
end

It makes it more clear if you name the method. Also, it is a good idea
to use self. to be sure that what are intended to be accessor method
calls are not interpreted as local variables.

Thanks, I am having a deal of difficulty in mentally switching between
symbols and such in this context. This makes it very clear what I need
to do in similar situation. Yet doubtless I will continue to have
problems like this for a while yet.

Another problem was the use of tr which returns nil if no substitution
is made. The was your initial method was written, if hs_class_code did
not contain the “.” character, the result would be to call strip on nil.

I believe that .tr returns the original string while it is .tr! that
returns nil in this case. (paralleling .sub and .sub!)

Thanks for you help. It is greatly appreciated.

Jim

James B. wrote:

William P. wrote:

Try this:

class Product < ActiveRecord::Base

validates_format_of :hs_class_code, :with =>
/\A(\d{6}|\d{8}|\d{10})?\Z/

before_validation :clean_tariff_code

protected

def clean_tariff_code
self.hs_class_code.gsub!(".","") unless self.hs_class_code.nil?
end

How would one turn this into a generalized case? Given this:

before_validation   :delouse,  :hs_class_code,  :with => 

/\A\Z/

would this work?

protected

def delouse(attr,regx)
  self.(attr).gssub(regx,"") unless self.(attr).nil?
end

Well, I am not sure about :with on before_validation as I don’t see it
in the docs or examples anywhere, but I’m not saying it won’t work, I
just don’t know. What I can do is show you how I do it and others may
chime in with alternatives / better solutions if some exist. First, what
I think you are asking is how to generically call your clean method by
passing in the attribute and the regex to use. Here are two ways, one
more verbose than the other, but it’s more self documenting.

before_validation :delouse_hs_class_code
before_validation :delouse_another_one

protected

def delouse_another_one
delouse(:another_one,/"/)
end

def delouse_hs_class_code
delouse(:hs_class_code,/./)
end

def delouse(attr,regex)

self.send(attr).gsub!(regex,"") unless self.send(attr).nil?

end

Or you could use Proc’s

before_validation Proc.new{|o| o.delouse(:hs_class_code,/./)}
before_validation Proc.new{|o| o.delouse(:another_one,/"/)}

protected

def delouse(attr,regex)

self.send(attr).gsub!(regex,"") unless self.send(attr).nil?

end

This should get you going in the right direction. This is just a mock up
that I didn’t test exactly and this was also written before my first cup
of coffee :slight_smile:

HTH,
-Bill

James B. wrote:

How would one turn this into a generalized case? Given this:
end


Sincerely,

William P.

As for the regex, I agree, always use what you want to allow rather than
attempting to guess everything you need to take out. In the examples, I
was just using what you had in the original method. As far as passing
options, you will need to use one of the 2 methods I presented, both of
which will allow multiple arguments.

-Bill

James B. wrote:

However, I found this sample in the callback module documentation.

Which seems a more comprehensive regex to use for the purpose I intend
(retain digits only). I suppose something like digits_only is a more
descriptive method name than delouse as well. There does not seem to be
any way to pass any additional parameters beyond the first to the
before_validate call.


Sincerely,

William P.

William P. wrote:

This should get you going in the right direction. This is just a mock up
that I didn’t test exactly and this was also written before my first cup
of coffee :slight_smile:

I will try this approach out later when I get a minute to myself.
However, I found this sample in the callback module documentation.

class CreditCard < ActiveRecord::Base
# Strip everything but digits, so the user can specify “555 234 34”
or
# “5552-3434” or both will mean “55523434”
def before_validation_on_create
self.number = number.gsub(/[^0-9]/, “”) if
attribute_present?(“number”)
end
end

Which seems a more comprehensive regex to use for the purpose I intend
(retain digits only). I suppose something like digits_only is a more
descriptive method name than delouse as well. There does not seem to be
any way to pass any additional parameters beyond the first to the
before_validate call.

This is what I ended up with. It seems to work.

before_validation :digits_only_hs_class_code

protected

Strip everything except digits from input string

def digits_only(attr)
attr.gsub(/[^0-9]/, “”) unless attr.nil?
end

def digits_only_hs_class_code
# only do this if not nil
self.hs_class_code = digits_only(self.hs_class_code) if
self.hs_class_code
end