Permalink
Browse files

Rails 3 compatibility: add alternate implementation for new validatio…

…n code.
  • Loading branch information...
1 parent bd151a3 commit eca6e5a6218a63a0c07fd0292e23ac285488a9d0 @Manfred Manfred committed Feb 24, 2011
View
@@ -1,61 +1,4 @@
module ValidationSets
- autoload :ValidationSet, 'validation_sets/validation_set'
-
- # Used to set the current validation set during definition of the model
- attr_accessor :current_validation_set
-
- # Returns the name of the validation callback method for a save method and the label of a
- # validation set.
- def validation_set_method(on, label)
- case on
- when :save then "validate_#{label}_set".to_sym
- when :create then "validate_on_create_#{label}_set".to_sym
- when :update then "validate_on_update_#{label}_set".to_sym
- end
- end
-
- # Returns the name of the validation callback method for a save method.
- def validation_method(on)
- if current_validation_set
- validation_set_method(on, current_validation_set)
- else
- super
- end
- end
-
- # Returns true if the validation set is defined on this class
- def validation_set_defined?(set)
- [:save, :create, :update].any? do |on|
- respond_to?(validation_set_method(on, set))
- end
- end
-
- # Add a validation set to the model.
- #
- # class Account < ActiveRecord::Base
- # validation_set_for(:activation) do |set|
- # set.validates_presence_of :username
- # end
- # end
- def validation_set_for(label, &block)
- [:save, :create, :update].each do |save_method|
- callback_chain = validation_method(save_method)
- callback_chain_for_set = validation_set_method(save_method, label)
- unless respond_to?(callback_chain_for_set)
- define_callbacks(callback_chain_for_set)
- define_method(callback_chain_for_set) { run_callbacks(callback_chain_for_set) }
- send(callback_chain, callback_chain_for_set, :if => Proc.new { |r| label.to_sym == r._validation_set })
- end
- end
-
- validation_set = ValidationSet.new(self, label)
-
- before = current_validation_set
- self.current_validation_set = label.to_sym
- block.call(validation_set)
- self.current_validation_set = before
- end
-
module InstanceMethods
attr_reader :_validation_set
@@ -71,14 +14,97 @@ module InstanceMethods
# account.use_validation_set(nil) # Turns off all validation sets
def use_validation_set(set)
if self.class.validation_set_defined?(set)
- @_validation_set = set
+ @_validation_set = set.to_sym
else
raise ArgumentError, "There is no validation set `#{set}'"
end
end
end
end
+if ActiveRecord::Base.respond_to?(:set_callback)
+ module ValidationSets
+ autoload :ValidationProxy, 'validation_sets/validation_proxy'
+
+ # Add a validation set to the model.
+ #
+ # class Account < ActiveRecord::Base
+ # validation_set_for(:activation) do |set|
+ # set.validates_presence_of :username
+ # end
+ # end
+ def validation_set_for(set, &block)
+ @_validation_sets ||= Set.new
+ @_validation_sets << set
+ yield ValidationProxy.new(self, set)
+ end
+
+ # Returns true if the validation set is defined on this class
+ def validation_set_defined?(set)
+ @_validation_sets.include?(set)
+ end
+ end
+else
+ module ValidationSets
+ autoload :ValidationSet, 'validation_sets/validation_set'
+
+ # Used to set the current validation set during definition of the model
+ attr_accessor :current_validation_set
+
+ # Returns the name of the validation callback method for a save method and the label of a
+ # validation set.
+ def validation_set_method(on, label)
+ case on
+ when :save then "validate_#{label}_set".to_sym
+ when :create then "validate_on_create_#{label}_set".to_sym
+ when :update then "validate_on_update_#{label}_set".to_sym
+ end
+ end
+
+ # Returns the name of the validation callback method for a save method.
+ def validation_method(on)
+ if current_validation_set
+ validation_set_method(on, current_validation_set)
+ else
+ super
+ end
+ end
+
+ # Returns true if the validation set is defined on this class
+ def validation_set_defined?(set)
+ [:save, :create, :update].any? do |on|
+ respond_to?(validation_set_method(on, set))
+ end
+ end
+
+ # Add a validation set to the model.
+ #
+ # class Account < ActiveRecord::Base
+ # validation_set_for(:activation) do |set|
+ # set.validates_presence_of :username
+ # end
+ # end
+ def validation_set_for(label, &block)
+ [:save, :create, :update].each do |save_method|
+ callback_chain = validation_method(save_method)
+ callback_chain_for_set = validation_set_method(save_method, label)
+ unless respond_to?(callback_chain_for_set)
+ define_callbacks(callback_chain_for_set)
+ define_method(callback_chain_for_set) { run_callbacks(callback_chain_for_set) }
+ send(callback_chain, callback_chain_for_set, :if => Proc.new { |r| label.to_sym == r._validation_set })
+ end
+ end
+
+ validation_set = ValidationSet.new(self, label)
+
+ before = current_validation_set
+ self.current_validation_set = label.to_sym
+ block.call(validation_set)
+ self.current_validation_set = before
+ end
+ end
+end
+
module ActiveRecord #:nodoc:
class Base #:nodoc:
extend ValidationSets
@@ -0,0 +1,26 @@
+module ValidationSets
+ # A ValidationSet instance is used to add extra options to validations to make
+ # sure the only run when a certain validation set is active.
+ class ValidationProxy
+ def initialize(model, set)
+ @model = model
+ @set = set
+ end
+
+ # Forwards all other methods (ie. validates_presence_of) to the model class
+ # with extra options
+ def method_missing(method, *args, &block)
+ options = args.extract_options!
+ options[:if] = Array.wrap(options[:if])
+ options[:if] << "_validation_set == :#{@set}"
+ # BUG: Callbacks get deleted from their chain when they're the same as a previous callback
+ if (method == :validate) and !block and args[0].kind_of?(Symbol)
+ callback = args.shift
+ block = lambda { send(callback) }
+ end
+ args << options
+ @model.send(method, *args, &block)
+ end
+ end
+end
+
@@ -2,24 +2,24 @@ module ValidationSets
# A ValidationSet instance is used to redirect the original validation methods (validate,
# validate_on_create, and validate_on_update) to the callback chain for the set.
class ValidationSet
- def initialize(model, label)
+ def initialize(model, set)
@model = model
- @label = label
+ @set = set
end
# Adds a validation method or proc that always runs
def validate(*params, &block)
- send(validation_set_method(:save, @label), *params, &block)
+ send(validation_set_method(:save, @set), *params, &block)
end
# Adds a validation method or proc that runs on create
def validate_on_create(*params, &block)
- send(validation_set_method(:create, @label), *params, &block)
+ send(validation_set_method(:create, @set), *params, &block)
end
# Adds a validation method or proc that runs on update
def validate_on_update(*params, &block)
- send(validation_set_method(:update, @label), *params, &block)
+ send(validation_set_method(:update, @set), *params, &block)
end
# Forwards all other methods (ie. validates_presence_of) to the model class.
@@ -1,9 +1,11 @@
require 'test_helper'
-class ValidationSetTest < ActiveSupport::TestCase
- test "forwards missing methods to the model" do
- model = 'Model'
- validation_set = ValidationSets::ValidationSet.new(model, :admin)
- assert_equal 5, validation_set.length
+if !ActiveRecord::Base.respond_to?(:set_callback) # Rails 2
+ class ValidationSetTest < ActiveSupport::TestCase
+ test "forwards missing methods to the model" do
+ model = 'Model'
+ validation_set = ValidationSets::ValidationSet.new(model, :admin)
+ assert_equal 5, validation_set.length
+ end
end
end
@@ -0,0 +1,17 @@
+require 'test_helper'
+
+if ActiveRecord::Base.respond_to?(:set_callback) # Rails 3
+ class Person
+ def self.arguments(*args)
+ args
+ end
+ end
+
+ class ValidationProxyTest < ActiveSupport::TestCase
+ test "forwards missing methods to the model" do
+ validation_proxy = ValidationSets::ValidationProxy.new(Person, :admin)
+ args = validation_proxy.arguments
+ assert args.last.has_key?(:if)
+ end
+ end
+end
@@ -98,7 +98,7 @@ class ValidationSetsTest < ActiveSupport::TestCase
def assert_error(object, attribute)
message = "Expected #{attribute} to have a validation error"
if object.errors[:base].kind_of?(Array)
- assert object.errors[attribute].empty?, message
+ assert !object.errors[attribute].empty?, message
else
assert object.errors.on(attribute), message
end

0 comments on commit eca6e5a

Please sign in to comment.