not_naughty 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,95 @@
1
+ module NotNaughty
2
+
3
+ # == Validates length of obj's attribute via the <tt>:length</tt> method.
4
+ #
5
+ # Unless the validation succeeds an error hash (:attribute => :message)
6
+ # is added to the obj's instance of Errors.
7
+ #
8
+ # <b>Options:</b>
9
+ # <tt>:message</tt>:: see NotNaughty::Errors for details
10
+ # <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
11
+ # <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
12
+ #
13
+ # <b>Boundaries (by precendence):</b>
14
+ # <tt>:is</tt>:: valid length
15
+ # <tt>:within</tt>:: valid range of length
16
+ # <tt>:minimum</tt>:: maximum length
17
+ # <tt>:maximum</tt>:: minimum length
18
+ #
19
+ # If both, <tt>:minimum</tt> and <tt>:maximum</tt> are provided they're
20
+ # converted to :within. Each boundary type has its own default message:
21
+ # precise:: "Length of %s is not equal to #{__length}."
22
+ # range:: "Length of %s is not within #{__range.first} and #{__range.last}."
23
+ # lower:: "Length of %s is smaller than #{__boundary}."
24
+ # upper:: "Length of %s is greater than #{__boundary}."
25
+ #
26
+ # <b>Example:</b>
27
+ #
28
+ # obj = %w[a sentence with five words] #
29
+ # def obj.errors() @errors ||= NotNauthy::Errors.new end
30
+ #
31
+ # LengthValidation.new({:minimum => 4}, :to_a).
32
+ # call obj, :to_a, %w[a sentence with five words]
33
+ # obj.errors.on(:to_s).any? # => false
34
+ #
35
+ # LengthValidation.new({:within => 1..4}, :to_a).
36
+ # call obj, :to_a, %w[a sentence with five words]
37
+ # obj.errors.on(:to_s).any? # => true
38
+ class LengthValidation < Validation
39
+
40
+ def initialize(opts, attributes) #:nodoc:
41
+
42
+ block = build_block opts
43
+
44
+ if opts[:allow_blank]
45
+ super opts, attributes do |o, a, v|
46
+ block[o, a, v] unless v.blank?
47
+ end
48
+ else
49
+ super opts, attributes do |o, a, v|
50
+ block[o, a, v] unless v.nil?
51
+ end
52
+ end
53
+ end
54
+
55
+ protected
56
+ def build_block(opts) #:nodoc:
57
+ if __length = opts[:is]
58
+ __message = opts[:message] ||
59
+ "Length of %s is not equal to #{__length}."
60
+ proc do |o, a, v|
61
+ o.errors.add a, __message unless __length.eql? v.length
62
+ end
63
+ elsif opts[:within] or opts[:minimum] && opts[:maximum]
64
+ __range = opts[:within]
65
+ __range ||= Range.new opts[:minimum], opts[:maximum]
66
+
67
+ __message = opts[:message] ||
68
+ "Length of %s is not within #{__range.first} and #{__range.last}."
69
+
70
+ proc do |o, a, v|
71
+ o.errors.add a, __message unless __range.include? v.length
72
+ end
73
+ elsif opts[:minimum]
74
+ __boundary = opts[:minimum]
75
+ __message = opts[:message] ||
76
+ "Length of %s is smaller than #{__boundary}."
77
+
78
+ proc do |o, a, v|
79
+ o.errors.add a, __message unless __boundary <= v.length
80
+ end
81
+ elsif opts[:maximum]
82
+ __boundary = opts[:maximum]
83
+ __message = opts[:message] ||
84
+ "Length of %s is greater than #{__boundary}."
85
+
86
+ proc do |o, a, v|
87
+ o.errors.add a, __message unless __boundary >= v.length
88
+ end
89
+ else
90
+ raise ArgumentError, 'no boundary given'
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,43 @@
1
+ NotNaughty::Validation.load :format
2
+
3
+ module NotNaughty
4
+
5
+ # == Validates numericality of obj's attribute via an regular expression.
6
+ #
7
+ # Unless the validation succeeds an error hash (:attribute => :message)
8
+ # is added to the obj's instance of Errors.
9
+ #
10
+ # <b>Options:</b>
11
+ # <tt>:only_integer</tt>:: validates with <tt>/^[+-]?\d+$/</tt> (false)
12
+ # <tt>:message</tt>:: see NotNaughty::Errors for details
13
+ # <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
14
+ # <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
15
+ #
16
+ # <b>Example:</b>
17
+ #
18
+ # obj = '-12.2' #
19
+ # def obj.errors() @errors ||= NotNauthy::Errors.new end
20
+ #
21
+ # NumericalityValidation.new({}, :to_s).call obj, :to_s, '-12.2'
22
+ # obj.errors.on(:to_s).any? # => false
23
+ #
24
+ # NumericalityValidation.new({:only_integer => true}, :to_s).
25
+ # call obj, :to_s, '-12.2'
26
+ #
27
+ # obj.errors.on(:to_s).any? # => true
28
+ class NumericalityValidation < FormatValidation
29
+
30
+ def initialize(opts, attributes) #:nodoc:
31
+ opts[:with] = if opts[:only_integer]
32
+ opts[:message] ||= '#{"%s".humanize} is not an integer.'
33
+ /^[+-]?\d+$/
34
+ else
35
+ opts[:message] ||= '#{"%s".humanize} is not a number.'
36
+ /^[+-]?\d*\.?\d+$/
37
+ end
38
+
39
+ super opts, attributes
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ module NotNaughty
2
+
3
+ # == Validates presence of obj's attribute via the <tt>:blank?</tt> method.
4
+ #
5
+ # Unless the validation succeeds an error hash (:attribute => :message)
6
+ # is added to the obj's instance of Errors.
7
+ #
8
+ # <b>Options:</b>
9
+ # <tt>:message</tt>:: see NotNaughty::Errors for details
10
+ # <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
11
+ # <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
12
+ #
13
+ # <b>Example:</b>
14
+ #
15
+ # obj = '' # blank? => true
16
+ # def obj.errors() @errors ||= NotNauthy::Errors.new end
17
+ #
18
+ # PresenceValidation.new({}, :to_s).call obj, :to_s, ''
19
+ # obj.errors.on(:to_s) # => ["To s is not present."]
20
+ class PresenceValidation < Validation
21
+
22
+ def initialize(opts, attributes) #:nodoc:
23
+ __message = opts[:message] || '#{"%s".humanize} is not present.'
24
+
25
+ super opts, attributes do |o, a, v|
26
+ o.errors.add a, __message if v.blank?
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,119 @@
1
+ module NotNaughty
2
+
3
+ # == Superclass for all Adapters.
4
+ #
5
+ # See new and get_state for more information.
6
+ class Validator
7
+
8
+ attr_reader :states
9
+
10
+ # By default it comes with the :default State unless other states are
11
+ # provided.
12
+ #
13
+ # <b>Example:</b>
14
+ # NotNaughty::Validator.new
15
+ # # has the :default state
16
+ # NotNaughty::Validator.new :create, :update
17
+ # # has the :create and :update states
18
+ #
19
+ # <em>Adapters should overwrite this method.</em>
20
+ def initialize(*states)
21
+ states << :default if states.empty?
22
+
23
+ @states = states.inject({}) {|m, s| m.update s => State.new(s)}
24
+ @initial_state = @states[states.first]
25
+ end
26
+
27
+ def clone #:nodoc:
28
+ states = [@initial_state.name] | @states.keys
29
+
30
+ clone = self.class.new(*states)
31
+ @states.each do |n, s|
32
+ s.validations.each { |a, v| clone.states[n].validations[a] = v.clone }
33
+ end
34
+ clone.instance_eval { @initial_state = @states[states.first] }
35
+
36
+ clone
37
+ end
38
+
39
+ # Returns the state for the given object. By default it does return the
40
+ # initial state.
41
+ #
42
+ # <em>Adapters that provide multiple states should eventually overwrite
43
+ # this method</em>.
44
+ def get_state(obj = nil) @initial_state end
45
+
46
+ # Adds a validation to all/specified states.
47
+ #
48
+ # <b>Example:</b>
49
+ # add_validation(:firstname, :lastname, :on => :default) {|o, a, v|}
50
+ # # adds validation to :default state
51
+ # add_validation(:firstname, :lastname) {|o, a, v|}
52
+ # # adds validation to all states
53
+ # add_validation(:first, :last, :on => [:create, :update]) {|o, a, v|}
54
+ # # adds validation to :create and :update states
55
+ def add_validation(*p, &b)
56
+ options = (p.last.is_a? Hash) ? p.last : {}
57
+
58
+ if states = options.delete(:on)
59
+ @states.values_at(*states).each do |state|
60
+ state.add_validation(*p, &b) unless state.nil?
61
+ end
62
+ else
63
+ @states.each { |name, state| state.add_validation(*p, &b) }
64
+ end
65
+ end
66
+
67
+ # Returns true if given object has validations in its current state. If
68
+ # no object was given it returns true if any state has validations. It
69
+ # otherwise returns false.
70
+ def has_validations?(obj = nil)
71
+ if obj.nil? then @states.any? { |name, state| state.has_validations? }
72
+ else get_state(obj).has_validations? end
73
+ end
74
+
75
+ # Runs all validations on object for the object's state.
76
+ def invoke(obj)
77
+ get_state(obj).validations.each do |attr, validations|
78
+ val = obj.send! attr
79
+ validations.each { |validation| validation.call obj, attr, val }
80
+ end
81
+ end
82
+
83
+ # == Container for attribute specific validations
84
+ #
85
+ # See Validator for details.
86
+ class State
87
+
88
+ attr_reader :validations, :name
89
+
90
+ # Initializes the state with given name.
91
+ def initialize(name = :default)
92
+ @name, @validations = name, Hash.new {|h, k| h[k] = []}
93
+ end
94
+
95
+ # Adds the validation that results from <tt>params</tt> and
96
+ # <tt>block</tt> to validated attributes (see Validation#new for
97
+ # details).
98
+ def add_validation(*params, &block)
99
+ validation = Validation.new(*params, &block)
100
+
101
+ validation.attributes.each do |attribute|
102
+ @validations[attribute] << validation if attribute.is_a? Symbol
103
+ end
104
+ end
105
+
106
+ # Returns validations for given attribute.
107
+ def [](attribute)
108
+ @validations[attribute]
109
+ end
110
+
111
+ # Returns true if a attributes has validations assigned, false
112
+ # otherwise.
113
+ def has_validations?
114
+ @validations.any? { |attribute, validations| validations.any? }
115
+ end
116
+
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,85 @@
1
+ require 'delegate'
2
+ require 'observer'
3
+
4
+ require 'rubygems'
5
+ require 'assistance'
6
+
7
+ module Kernel#:nodoc:all
8
+ methods.include? 'send!' or
9
+ alias_method :send!, :send
10
+ end
11
+
12
+ $:.unshift File.dirname(__FILE__)
13
+
14
+ module NotNaughty
15
+ require 'not_naughty/validator'
16
+
17
+ require 'not_naughty/builder'
18
+ require 'not_naughty/validation'
19
+ Validation.add_observer Builder
20
+
21
+ require 'not_naughty/errors'
22
+ require 'not_naughty/instance_methods'
23
+
24
+ # Extended classes get NotNaughty::Builder and NotNaughty::InstanceMethods.
25
+ def self.extended(base)
26
+ base.instance_eval do
27
+ include InstanceMethods
28
+ extend Builder
29
+ end
30
+ end
31
+
32
+ # call-seq:
33
+ # validator(validator_klass = NotNaughty::Validator, *states = [:default])
34
+ #
35
+ # Returns instance of Validator. This is either the validator's clone of
36
+ # superclass, an instance of the the given descendant of or the
37
+ # <tt>NotNaughty:Validator</tt> himself.
38
+ #
39
+ # <b>Examples:</b>
40
+ # validator # => Instance of NotNaughty::Validator with :default state
41
+ # validator :create, :update # ~ - but with :create and :update states
42
+ # validator AnotherValidator # Instance of AnotherValidator
43
+ #
44
+ # The above examples work as long validator is not already called. To reset
45
+ # an already assigned validator set <tt>@validator</tt> to nil.
46
+ def validator(*states)
47
+ @validator ||= if superclass.respond_to? :validator
48
+ superclass.validator.clone
49
+
50
+ else
51
+ validator_klass =
52
+ if states[0].is_a? Class and states[0] <= NotNaughty::Validator
53
+ states.shift
54
+ else
55
+ NotNaughty::Validator
56
+ end
57
+
58
+ validator_klass.new(*states)
59
+ end
60
+ end
61
+
62
+ # Prepends a call for validation before then given method. If, on call, the
63
+ # validation passes the method is called. Otherwise it raises an
64
+ # NotNaughty::ValidationException or returns false.
65
+ #
66
+ # <b>Example:</b>
67
+ # validated_before :save # raise ValidationException unless valid?
68
+ # validated_before :save, :without => :exception # => false unless valid?
69
+ def validated_before(method, *args)
70
+ __method = :"#{method}_without_validations"
71
+ alias_method __method, method
72
+
73
+ without = args.extract_options![:without]
74
+ if [*without].include? :exception
75
+ define_method method do |*params|
76
+ if valid? then send! __method else false end
77
+ end
78
+ else
79
+ define_method method do |*params|
80
+ if valid? then send! __method else raise errors.to_exception end
81
+ end
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,106 @@
1
+ require File.dirname(__FILE__) + '/not_naughty'
2
+ require 'sequel'
3
+
4
+ module Sequel #:nodoc:
5
+ module Plugins #:nodoc:
6
+ # == Adapter for Sequel ...
7
+ #
8
+ # ... is a Sequel::Plugin.
9
+ #
10
+ # ---
11
+ #
12
+ # <b>To make it overall available:</b>
13
+ #
14
+ # class Sequel::Model
15
+ # is :not_naughty
16
+ # end
17
+ #
18
+ # <b>To turn off before_validate and after_validate hooks:</b>
19
+ #
20
+ # class User < Sequel::Model
21
+ # is :not_naughty, :without => :hooks
22
+ # end
23
+ #
24
+ # <b>To turn off raised Exceptions if validation before save fails:</b>
25
+ #
26
+ # class User < Sequel::Model
27
+ # # save on invalid users will return false
28
+ # is :not_naughty, :without => :exceptions
29
+ # end
30
+ #
31
+ # <b>To combine those:</b>
32
+ #
33
+ # class User < Sequel::Model
34
+ # is :not_naughty, :without => [:exceptions, :hooks]
35
+ # end
36
+ #
37
+ class NotNaughty < NotNaughty::Validator
38
+
39
+ # Applies plugin to a Sequel::Model.
40
+ def self.apply(receiver, *args)
41
+ receiver.extend ::NotNaughty
42
+ receiver.validator self, :create, :update
43
+
44
+ ::NotNaughty::Validation.load(
45
+ :acceptance, :confirmation, :format,
46
+ :length, :numericality, :presence
47
+ )
48
+
49
+ receiver.extend ClassMethods
50
+ receiver.instance_eval { alias_method :save, :save! }
51
+ receiver.validated_before :save, *args
52
+
53
+ without = args.extract_options![:without]
54
+ receiver.send! :include, Hooks unless [*without].include? :hooks
55
+ end
56
+
57
+ # Returns state for given instance.
58
+ def get_state(instance)
59
+ if instance.new? then @states[:create] else @states[:update] end
60
+ end
61
+
62
+ # Adds validation hooks to Sequel::Model.
63
+ module Hooks
64
+ def self.included(base)
65
+ base.instance_eval do
66
+ def_hook_method :before_validate
67
+ def_hook_method :after_validate
68
+ alias_method :validate_without_hooks, :validate
69
+ alias_method :validate, :validate_with_hooks
70
+ end
71
+ end
72
+
73
+ def before_validate() end
74
+ def after_validate() end
75
+
76
+ def validate_with_hooks
77
+ before_validate
78
+ validate_without_hooks
79
+ after_validate
80
+ end
81
+ end
82
+
83
+ # Ensures Sequel::Model API compatibility.
84
+ module ClassMethods
85
+
86
+ # Returns the validations hash for the class.
87
+ def validations
88
+ validator.states.
89
+ inject({}) do |validations, state_with_name|
90
+ validations.merge(state_with_name[1].validations) {|k,o,n| o|n}
91
+ end
92
+ end
93
+ # Returns true if validations are defined.
94
+ def has_validations?()
95
+ validator.has_validations?
96
+ end
97
+ # Validates the given instance.
98
+ def validate(instance)
99
+ validator.invoke instance
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,69 @@
1
+ require "#{ File.dirname(__FILE__) }/spec_helper.rb"
2
+
3
+ describe subject::Builder do
4
+
5
+ before(:each) do
6
+ @validatable = Class.new(Object) do
7
+ def self.validator=(validator) @validator = validator end
8
+ def self.validator() @validator end
9
+ def initialize(state) @state = state end
10
+ def validation_state() state end
11
+ end
12
+ @validatable.validator = mock 'Validator'
13
+ @validatable.extend(subject::Builder)
14
+ end
15
+
16
+ it "should have a delegator class" do
17
+ subject::Builder.constants.
18
+ should include('ValidationDelegator')
19
+ subject::Builder.const_get('ValidationDelegator').
20
+ should < SimpleDelegator
21
+ end
22
+ it "should provide the :validates builder method" do
23
+ @validatable.should respond_to(:validates)
24
+ end
25
+ it "should add validation for :name via :validates" do
26
+ @validatable.validator.should_receive(:add_validation).
27
+ with(NotNaughty::PresenceValidation, :name, {}).twice
28
+ @validatable.validates { presence_of :name }
29
+ @validatable.validates(:name) { presence }
30
+ end
31
+ it "should add validation for :name on update via :validates" do
32
+ @validatable.validator.should_receive(:add_validation).
33
+ with(NotNaughty::PresenceValidation, :name, {:on => :update}).twice
34
+ @validatable.validates { presence_of :name, :on => :update }
35
+ @validatable.validates(:on => :update) { presence_of :name }
36
+ end
37
+ it "should add validation for :firstname and :lastname via :validates" do
38
+ @validatable.validator.should_receive(:add_validation).
39
+ with(NotNaughty::PresenceValidation, :firstname, :lastname, {}).twice
40
+ @validatable.validates { presence_of :firstname, :lastname }
41
+ @validatable.validates(:firstname, :lastname) { presence :name }
42
+ end
43
+ it "should register validation" do
44
+ validation = Class.new(subject::Validation) do
45
+ def self.name() 'TestValidation' end
46
+ def initialize(opts, &block) end
47
+ def call(obj, attr, value) end
48
+ end
49
+
50
+ @validatable.should respond_to(:validates_test_of)
51
+ end
52
+ it "should provide the :validates_each builder method" do
53
+ @validatable.should respond_to(:validates_each)
54
+ end
55
+ it "should build the Validations with :validates_each" do
56
+ @validatable.validator = mock 'Validator'
57
+ @validatable.validator.
58
+ should_receive(:add_validation).
59
+ with(:a, :b)
60
+ @validatable.validates_each(:a, :b) {|o, a, v|}
61
+
62
+ pending 'expect a block'
63
+ end
64
+ it "should raise a NoMethodError is builder method does not exist" do
65
+ lambda { @validatable.validates() { bunch_of :holy_crap } }.
66
+ should raise_error(NoMethodError)
67
+ end
68
+
69
+ end
@@ -0,0 +1,61 @@
1
+ require "#{ File.dirname(__FILE__) }/spec_helper.rb"
2
+
3
+ describe subject::Errors do
4
+
5
+ before(:each) { @errors = subject::Errors.new }
6
+
7
+ it "should add errors in a hash" do
8
+ @errors.add :attribute, 'Message'
9
+
10
+ @errors.instance_variable_get(:@errors)[:attribute].
11
+ should include('Message')
12
+ end
13
+ it "should return full messages" do
14
+ @errors.add :attribute, 'Message #{"%s".capitalize}'
15
+
16
+ @errors.full_messages.
17
+ should == ['Message Attribute']
18
+ end
19
+ it "should return an #{subject::ValidationException} with errors" do
20
+ exception = @errors.to_exception
21
+ exception.should be_an_instance_of(subject::ValidationException)
22
+ exception.errors.should == @errors
23
+ end
24
+ it "should have methods delegated" do
25
+ probe = mock 'Probe'
26
+ methods = [:empty?, :clear, :[], :each, :to_yaml]
27
+
28
+ methods.each { |m| probe.should_receive m }
29
+ @errors.instance_variable_set :@errors, probe
30
+
31
+ methods.each { |m| @errors.send! m }
32
+ end
33
+ it "should return evaluated errors messages" do
34
+ @errors.add :attribute, 'Message #{"%s".capitalize}'
35
+ @errors.on(:attribute).should == ['Message Attribute']
36
+ end
37
+
38
+ end
39
+
40
+ describe subject::ValidationException do
41
+
42
+ it "should return an exception with errors" do
43
+ probe = mock 'probe'
44
+ probe.should_receive(:any?).and_return(true)
45
+ exception = subject::ValidationException.new probe
46
+
47
+ lambda { raise exception }.should_not raise_error(TypeError)
48
+ exception.errors.should == probe
49
+ end
50
+ it "should have methods delegated" do
51
+ methods = [:on, :full_messages, :each]
52
+
53
+ probe = mock 'Probe'
54
+ probe.should_receive(:any?).and_return(true)
55
+ exception = subject::ValidationException.new probe
56
+
57
+ methods.each { |m| probe.should_receive m }
58
+ methods.each { |m| exception.send! m }
59
+ end
60
+
61
+ end
@@ -0,0 +1,80 @@
1
+ require "#{ File.dirname(__FILE__) }/spec_helper.rb"
2
+
3
+ module subject::Builder
4
+ def self.extended(base) end
5
+ end
6
+ module subject::InstanceMethods
7
+ def self.included(base) end
8
+ end
9
+
10
+ describe subject do
11
+
12
+ it "should load necessary files" do
13
+ subject::should be_const_defined(:Validation)
14
+ subject::should be_const_defined(:Validator)
15
+ subject::should be_const_defined(:Builder)
16
+ subject::should be_const_defined(:InstanceMethods)
17
+ subject::should be_const_defined(:Errors)
18
+ end
19
+ it "should define a validator" do
20
+ validated = Class.new(Object).extend subject
21
+ validated.should respond_to(:validator)
22
+ validated.validator.should be_an_instance_of(subject::Validator)
23
+
24
+ validator = Class.new(subject::Validator)
25
+ validated = Class.new(Object).extend subject
26
+ validated.validator validator, :create, :update
27
+ validated.validator.should be_an_instance_of(validator)
28
+ validated.validator.states.keys.should include(:create, :update)
29
+ end
30
+ it "should extend the receiver if validator is defined" do
31
+ validated = Class.new(Object)
32
+
33
+ subject::Builder.should_receive(:extended).with(validated)
34
+ subject::InstanceMethods.should_receive(:included).with(validated)
35
+
36
+ validated.extend subject
37
+ end
38
+ it "should deep-clone the validator if inherited" do
39
+ super_validated = Class.new(Object).extend subject
40
+ super_validated.validator.add_validation(:name) {|o, a, v|}
41
+ validated = Class.new(super_validated)
42
+
43
+ validated.validator.should_not == super_validated.validator
44
+ validated.validator.should have_validations
45
+ validated.validator.add_validation(:name) {|o, a, v|}
46
+
47
+ super_validated.validator.get_state.validations[:name].length.
48
+ should < validated.validator.get_state.validations[:name].length
49
+ end
50
+ it "should prepend a validation before any method" do
51
+ validated = Class.new(Object).extend subject
52
+
53
+ validated.validated_before :clone
54
+ instance = validated.new
55
+ instance.should_receive(:valid?).once.and_return(true)
56
+ instance.should_receive(:clone_without_validations)
57
+ instance.clone
58
+ end
59
+ it "should raise an exception if invalid and method called" do
60
+ validated = Class.new(Object).extend subject
61
+
62
+ validated.validated_before :clone
63
+ instance = validated.new
64
+ instance.should_receive(:valid?).once.and_return(false)
65
+ lambda { instance.clone }.should raise_error(subject::ValidationException)
66
+ end
67
+ it "should return false if invalid and method called" do
68
+ validated = Class.new(Object).extend subject
69
+
70
+ validated.validated_before :clone, :without => :exception
71
+ instance = validated.new
72
+ instance.should_receive(:valid?).once.and_return(false)
73
+ instance.clone.should == false
74
+ end
75
+ it "should add Builder to Validation observers" do
76
+ subject::Validation.instance_variable_get(:@observer_peers).
77
+ should include(subject::Builder)
78
+ end
79
+
80
+ end
data/spec/rcov.opts ADDED
@@ -0,0 +1,4 @@
1
+ --exclude
2
+ gems
3
+ --exclude
4
+ spec