Help How to create DSL for conditional validations

Hi All,

I am new to Ruby and ROR
I were trying to create small DSL for conditional validations

valid_with_cond :bypass_validation do
if self.addresses > 3
errors[:base] << “Can not have more than 3 addresses”.
end
end

By this I wanted to create array of method and call them all in custom
validation method.

this above code I wanted to do

attr_accessor :bypass_validation

def svalid_cond
unless bypass_validation
if self.addresses > 3
errors[:base] << “Can not have more than 3 addresses”.
end
end
end

       This way I want in new and edit form I can ask user to click
       :bypass_validation attribute and I will bypass this 

validation.

I tried to create

def valid_with_cond(name, &block)
attr_accessor name
define_method “svalidator_#{name}” do |*arg|
if send name
yield *arg
end
end
end

But after trying it in

Class Person < ActibeRecord::Base
valid_with_cond :test do
if self.addresses > 3
errors[:base] << “Can not have more than 3 addresses”.
end
end
end

But when I check it on console

Person.test

it throw me error test is private method.

Anybody could let me know how to correct it.


Regards,
-sharad

On Jan 8, 10:18pm, Sharad P. [email protected] wrote:

Person.test

it throw me error test is private method.

Anybody could let me know how to correct it.

Your call to attr_accessor will create instance methods called test
and test=, but you tried to call test on the class (and so you end up
falling through to Kernel#test).
Have you seen that validations take :if and :unless options ?

Fred

Hi

Your call to attr_accessor will create instance methods called test
and test=, but you tried to call test on the class (and so you end up
falling through to Kernel#test).

No I have tested this is not the case, yes I have given wrong example
but with object also it is not working, I have given all details below
I guess I got it working by hit and trial way, so not sure what
exactly is happening around.

Have you seen that validations take :if and :unless options ?
I need to use coustom validation, for them I did not see any :if,
:unless option

But as I got it working I have lot of question in all stages
that I want to put else I will never able to know what is happening.

test 1:
ruby>
ruby> def cvalidation(name, &block)
ruby> attr_accessor name
ruby> define_method “svalidator_#{name}” do |*arg|
ruby> if send name
ruby> yield *arg
ruby> end
ruby> end
ruby> end
=>
ruby>
ruby> class Person
ruby> cvalidation :zen do
ruby> puts “Yes”
ruby> end
ruby> end
=> #<Proc:0x95e73fc@(irb):8 (lambda)>
ruby> x = Person.newq
NoMethodError: undefined method `newq’ for Person:Class

So this is not working now in test 2, I now test only attr_accessor

test 2:

ruby>   def k(name)
ruby>    attr_accessor name
ruby>   end
        => nil
ruby>  class Personq
ruby>   k :test
ruby>   end
       => nil
ruby>  x = Personq.new
       => #<Personq:0x917a4e0>
ruby>  x.test
       NoMethodError: private method `test' called for #<Personq:

0x917a4e0>

it again will not work.

test 3:
ruby> def k(name)
ruby> class_eval do
ruby> attr_accessor name
ruby> end
ruby> end
=> nil
ruby> class Pers
ruby> k :u
ruby> end
ruby> x = Pers.new
=> #Pers:0x8f9bf0c
ruby> x.u
=> nil
ruby> x.u = “sfdsg”
=> “sfdsg”
ruby> x.u
=> "sfdsg

Q1: Now it is working, but why it did not work in test 2 ?

test 4:
ruby> def cvalidation(name, &block)
ruby> class_eval do
ruby> attr_accessor name
ruby> define_method “svalidator_#{name}” do |*arg|
ruby> if name
ruby> yield *arg
ruby> end
ruby> end
ruby> end
ruby> end
=> nil
ruby>
ruby> class Work
ruby> cvalidation :done do
ruby> puts “Yes”
ruby> end
ruby> end
=> #<Proc:0xa337638@(irb):64 (lambda)>
ruby> x = Work.new
=> #Work:0xa32d200
ruby> x.svalidator_done
Yes
=> nil
ruby> x.done
=> nil

Here define_method is working but it is not using argument name' in if condition, it uses some other name method, than provided argument name’

Q2: Why it is like that and what `name’ method or variable it is
actually using in both lines ?
ruby> define_method “svalidator_#{name}” do |*arg|
ruby> if name

test 5:
ruby> def cvalidation(name, &block)
ruby> class_eval do
ruby> attr_accessor name
ruby> define_method “svalidator_#{name}” do |*arg|
ruby> if send “#{name}”
ruby> yield *arg
ruby> end
ruby> end
ruby> end
ruby> end
=> nil
ruby>
ruby> class QQ
ruby> cvalidation :xx do
ruby> puts “Yes”
ruby> end
ruby> end
=> #<Proc:0xa0aaf28@(irb):105 (lambda)>
ruby> x = QQ.new
=> #QQ:0xa0a09ec
ruby> x.svalidator_xx
=> nil
ruby> x.xx
=> nil
ruby> x.xx = “sfddsaf”
=> “sfddsaf”
ruby> x.svalidator_xx
Yes
=> nil
ruby> x.xx = nil
=> nil
ruby> x.svalidator_xx
=> nil
ruby> class NN
ruby> cvalidation :ww do |w|
ruby> puts w, " Hi"
ruby> end
ruby> end
=> #<Proc:0x9fbe830@(irb):105 (lambda)>
ruby> x = NN.new
=> #NN:0x9fabb40
ruby> x.ww
=> nil
ruby> x.svalidator_ww
=> nil
ruby> x.ww = “Sharad”
=> “Sharad”
ruby> x.svalidator_ww

      Hi
      => nil
ruby> x.svalidator_ww "Sharad"
      Sharad
      Hi
      => nil
ruby> puts "Sharad", "Hi"
      Sharad
      Hi
      => nil
ruby>

Here I guess as I see all is working, but
I really like to know about `name’ used in

ruby>                 if send "#{name}"

in test 5, uses the `name’ arguemnt provided.

But
ruby> if name
in test 4 it do not able to use same `name’ argument

Why ?


Regards,
-sharad

On Jan 9, 3:37am, Frederick C. [email protected]

Hi Frederick!

I think you can still do
validate :something, :if => :something+else?

I hope it could be used multiple time with different methods

ruby> x = Person.newq
NoMethodError: undefined method `newq’ for Person:Class

Not sure what this is showing - you haven’t attempted to define newq
anywhere

Sorry It was my typing mistake.

test 2:

I ran this in irb and this worked fine.

But it has not work fine, I again tested it now.
my ruby version
ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]
installed using Ruby Version Manager.

For all I got why this is happening.

Great thanks for all explanations.


Regards,
-sharad

On Jan 9, 1:22pm, sharad [email protected] wrote:

Have you seen that validations take :if and :unless options ?

I need to use coustom validation, for them I did not see any :if,
:unless option

I think you can still do

validate :something, :if => :something+else?

But as I got it working I have lot of question in all stages
that I want to put else I will never able to know what is happening.

[snip

ruby>
ruby> class Person
ruby> cvalidation :zen do
ruby> puts “Yes”
ruby> end
ruby> end
=> #<Proc:0x95e73fc@(irb):8 (lambda)>
ruby> x = Person.newq
NoMethodError: undefined method `newq’ for Person:Class

Not sure what this is showing - you haven’t attempted to define newq
anywhere

ruby> k :test
ruby> end
=> nil
ruby> x = Personq.new
=> #Personq:0x917a4e0
ruby> x.test
NoMethodError: private method `test’ called for #<Personq:
0x917a4e0>

I ran this in irb and this worked fine.

ruby> end
ruby> x.svalidator_done
actually using in both lines ?
blocks are closures, so it sees the same local variables as those that
were existent when define_method were called, so it picks up the name
that was first argument to cvalidation. self is special though - self
will be the value of the object the method is called on.

ruby> end
=> #<Proc:0xa0aaf28@(irb):105 (lambda)>
=> nil
ruby> x = NN.new
=> nil
Here I guess as I see all is working, but
I really like to know about `name’ used in

ruby> if send “#{name}”

in test 5, uses the `name’ arguemnt provided.

But
ruby> if name
in test 4 it do not able to use same `name’ argument

When you just write name, it picks up the local variable from the
closure (it’s ambiguous whether you mean the local variable or the
method, and in these cases ruby picks the local variable), but by
using send you’re forcing it to call the accessor method. You could
also have disambiguated by writing self.name or name()

Fred