Permalink
Browse files

Nested scopes

  • Loading branch information...
1 parent 93c455b commit c39c2199cf1012b0437b9642d9e7132c795b60bb @stevehodgkiss committed Mar 3, 2011
Showing with 75 additions and 32 deletions.
  1. +30 −16 lib/validation_scopes.rb
  2. +45 −16 spec/validation_scopes_spec.rb
View
@@ -7,48 +7,62 @@ module ValidationScopes
module ClassMethods
def validation_scope(options, &block)
- @_in_validation_scope = true
- @_validation_scope_options = options
+ @_nested_level_count ||= 0
+ @_validation_scope_options ||= {}
+ parent_options = @_validation_scope_options[@_nested_level_count]
+ @_nested_level_count += 1
+ @_validation_scope_options[@_nested_level_count] = parent_options.nil? ? options : merge_options(parent_options.dup, options)
block.call
- @_validation_scope_options = nil
- @_in_validation_scope = false
+ @_nested_level_count -= 1
@_handled_by_with = false
end
def validates_with(*args, &block)
- if @_in_validation_scope
+ if in_validation_scope?
merge_args(args)
@_handled_by_with = true
end
super(*args, &block)
end
def validate(*args, &block)
- if @_in_validation_scope & !@_handled_by_with
+ if in_validation_scope? & !@_handled_by_with
merge_args(args)
end
+ @_handled_by_with = false
super(*args, &block)
end
- protected
+ private
+
+ def in_validation_scope?
+ !@_nested_level_count.nil? && @_nested_level_count > 0
+ end
def merge_args(args)
if args.empty?
- args << @_validation_scope_options.dup
+ args << @_validation_scope_options[@_nested_level_count].dup
elsif args.last.is_a?(Hash) && args.last.extractable_options?
options = args.extract_options!
options = options.dup
- @_validation_scope_options.each_key do |key|
- if options[key].nil?
- options[key] = @_validation_scope_options[key]
- else
- options[key] = Array.wrap(options[key])
- options[key] << @_validation_scope_options[key]
- end
- end
+ merge_options(options, @_validation_scope_options[@_nested_level_count])
args << options
end
end
+
+ def merge_options(options_a, options_b)
+ return options_b if options_a.nil?
+ options_a = options_a
+ options_b.each_key do |key|
+ if options_a[key].nil?
+ options_a[key] = options_b[key]
+ else
+ options_a[key] = Array.wrap(options_a[key]) unless options_a[key].is_a?(Array)
+ options_a[key] << options_b[key]
+ end
+ end
+ options_a
+ end
end
end
@@ -38,6 +38,9 @@ def validate(record)
validates_presence_of :name
end
validation_scope :if => Proc.new { |u| u.step == 3 } do
+ validation_scope :if => Proc.new { |u| !u.height.nil? && u.height > 6 } do
+ validates_presence_of :weight
+ end
validates_inclusion_of :eye_colour, :in => ["blue", "brown"], :if => Proc.new { |u| !u.age.nil? && u.age > 20 }
end
end
@@ -47,25 +50,41 @@ def validate(record)
end
it "should only validate name when on step 2" do
- @user.name = nil
- @user.should be_valid
- @user.step = 2
- @user.should be_invalid
- @user.name = "Steve"
- @user.should be_valid
+ @user.tap do |u|
+ u.name = nil
+ u.should be_valid
+ u.step = 2
+ u.should be_invalid
+ u.name = "Steve"
+ u.should be_valid
+ end
end
it "only validates eye colour when on step 3 and age is above 20" do
- @user.eye_colour = nil
- @user.should be_valid
- @user.age = 20
- @user.step = 3
- @user.eye_colour = "red"
- @user.should be_valid
- @user.age = 21
- @user.should be_invalid
- @user.eye_colour = "blue"
- @user.should be_valid
+ @user.tap do |u|
+ u.eye_colour = "red"
+ u.should be_valid
+ u.age = 20
+ u.step = 3
+ u.should be_valid
+ u.age = 21
+ u.valid?
+ u.should be_invalid
+ u.eye_colour = "blue"
+ u.should be_valid
+ end
+ end
+
+ it "allows nesting of scopes" do
+ @user.tap do |u|
+ u.weight = nil
+ u.should be_valid
+ u.height = 7
+ u.should be_valid
+ u.step = 3
+ u.should be_invalid
+ u.errors[:weight].should be
+ end
end
it "calls validates_with with merged scope options" do
@@ -103,6 +122,9 @@ def validate(record)
User = Class.new(TestUser) do
validation_scope :unless => :step_2?, :some_config_var => 5 do
validates_with TestValidator, {:attributes => [:name], :some_config_var => 6}
+ validation_scope :unless => Proc.new { |u| u.step == 3 } do
+ validates_with TestValidator, {:attributes => [:weight], :some_config_var => 7}
+ end
end
end
@validator = User.validators_on(:name).first
@@ -116,6 +138,13 @@ def validate(record)
it "turns the duplicate options into an array" do
@validator.options[:some_config_var].should eq([6, 5])
end
+
+ it "works for nested scopes" do
+ option = User.validators_on(:weight).first.options[:unless]
+ option.size.should eq(2)
+ option[0].should eq(:step_2?)
+ option[1].should be_an_instance_of(Proc)
+ end
end
after { Object.send(:remove_const, :User) if defined?(User) }

0 comments on commit c39c219

Please sign in to comment.