views:

44

answers:

3

User should insert all the values either positive or negative.

How may i set same sign validation ?

Right i have written this on before_save ..

unless (self.alt_1 >= 0 && self.alt_2 >=0 && self.alt_3 >= 0 &&
      self.alt_4  >= 0 && self.alt_5 >= 0 && self.alt_6 >= 0) ||
    (self.alt_1 <= 0 && self.alt_2 <=0 && self.alt_3 <= 0 &&
      self.alt_4  <= 0 && self.alt_5 <= 0 && self.alt_6 <= 0)
  self.errors.add_to_base(_("All values sign should be same."))
end
+3  A: 
first_sign = self.alt_1 <=> 0

(2..6).each do |n|
  unless (self.send("alt_#{n}") <=> 0) == first_sign
    errors.add_to_base(_("All values' signs should be same."))
    break
  end
end

With this method we first get the sign of alt_1, and then see if the signs of the rest of the elements (alt_2 through alt_6) match. As soon as we find one that doesn't match we add the validation error and stop. It will run a maximum of 6 iterations and a minimum of 2.


Another more clever, but less efficient method, is to use the handy method Enumerable#all?, which returns true if the block passed to it returns true for all elements:

range = 1..6

errors.add_to_base(_("All values' signs should be same.")) unless
  range.all? {|n| self.send("alt_#{n}") >= 0 } ||
  range.all? {|n| self.send("alt_#{n}") <= 0 }

Here we first check if all of the elements are greater than 0 and then if all of the elements are less than 0. This method iterates a maximum of 12 times and a minimum of 6.

Jordan
Since self.send `Invokes the method identified by symbol` and alt_1 seems to be a variable I wonder how this supposed to work.
Jonas Elfström
send also accepts a string and alt_1 is a method on self.
Jordan
A: 
unless 
  [:<=, :>=].any? do |check|
    # Check either <= or >= for all values
    [self.alt1, self.alt2, self.alt3, self.alt4, self.alt5, self.alt6].all? do |v|
      v.send(check, 0)
    end
  end
  self.errors.add_to_base(_("All values sign should be same."))
end
Shadwell
I considered a similar solution, since it's more DRY, but I think it sacrifices readability for cleverness.
Jordan
+1  A: 

Here's a slightly different approach for you:

irb(main):020:0> def all_same_sign?(ary)
irb(main):021:1>   ary.map { |x| x <=> 0 }.each_cons(2).all? { |x| x[0] == x[1] }
irb(main):022:1> end
=> nil
irb(main):023:0> all_same_sign? [1,2,3]
=> true
irb(main):024:0> all_same_sign? [1,2,0]
=> false
irb(main):025:0> all_same_sign? [-1, -5]
=> true

We use the spaceship operator to obtain the sign of each number, and we make sure that each element has the same sign as the element following it. You could also rewrite it to be more lazy by doing

ary.each_cons(2).all? { |x| (x[0] <=> 0) == (x[1] <=> 0) }

but that's less readable in my opinion.

Mark Rushakoff