boof-not-naughty 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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(valid, attributes) #:nodoc:
31
+ valid[:with] = if valid[:only_integer]
32
+ valid[:message] ||= '%s is not an integer.'
33
+ /^[+-]?\d+$/
34
+ else
35
+ valid[:message] ||= '%s is not a number.'
36
+ /^[+-]?\d*\.?\d+$/
37
+ end
38
+
39
+ super valid, attributes
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,32 @@
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(valid, attributes) #:nodoc:
23
+ valid[:message] ||= '%s is not present.'
24
+
25
+ super valid, attributes do |obj, attr, value|
26
+ value.blank? and
27
+ obj.errors.add attr, valid[:message]
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,125 @@
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, :error_handler
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
+
26
+ @error_handler = NotNaughty::ErrorHandler.new
27
+ end
28
+
29
+ def clone #:nodoc:
30
+ states = [@initial_state.name] | @states.keys
31
+ error_handler = @error_handler.clone
32
+
33
+ clone = self.class.new(*states)
34
+ @states.each do |n, s|
35
+ s.validations.each { |a, v| clone.states[n].validations[a] = v.clone }
36
+ end
37
+ clone.instance_eval do
38
+ @initial_state = @states[states.first]
39
+ @error_handler = error_handler
40
+ end
41
+
42
+ clone
43
+ end
44
+
45
+ # Returns the state for the given object. By default it does return the
46
+ # initial state.
47
+ #
48
+ # <em>Adapters that provide multiple states should eventually overwrite
49
+ # this method</em>.
50
+ def get_state(obj = nil) @initial_state end
51
+
52
+ # Adds a validation to all/specified states.
53
+ #
54
+ # <b>Example:</b>
55
+ # add_validation(:firstname, :lastname, :on => :default) {|o, a, v|}
56
+ # # adds validation to :default state
57
+ # add_validation(:firstname, :lastname) {|o, a, v|}
58
+ # # adds validation to all states
59
+ # add_validation(:first, :last, :on => [:create, :update]) {|o, a, v|}
60
+ # # adds validation to :create and :update states
61
+ def add_validation(*p, &b)
62
+ options = (p.last.is_a? Hash) ? p.last : {}
63
+
64
+ if states = options.delete(:on)
65
+ @states.values_at(*states).each do |state|
66
+ state.add_validation(*p, &b) unless state.nil?
67
+ end
68
+ else
69
+ @states.each { |name, state| state.add_validation(*p, &b) }
70
+ end
71
+ end
72
+
73
+ # Returns true if given object has validations in its current state. If
74
+ # no object was given it returns true if any state has validations. It
75
+ # otherwise returns false.
76
+ def has_validations?(obj = nil)
77
+ if obj.nil? then @states.any? { |name, state| state.has_validations? }
78
+ else get_state(obj).has_validations? end
79
+ end
80
+
81
+ # Runs all validations on object for the object's state.
82
+ def invoke(obj)
83
+ get_state(obj).validations.each do |attr, validations|
84
+ val = obj.send attr
85
+ validations.each { |validation| validation.call obj, attr, val }
86
+ end
87
+ end
88
+
89
+ # == Container for attribute specific validations
90
+ #
91
+ # See Validator for details.
92
+ class State
93
+
94
+ attr_reader :validations, :name
95
+
96
+ # Initializes the state with given name.
97
+ def initialize(name = :default)
98
+ @name, @validations = name, Hash.new {|h, k| h[k] = []}
99
+ end
100
+
101
+ # Adds the validation that results from <tt>params</tt> and
102
+ # <tt>block</tt> to validated attributes (see Validation#new for
103
+ # details).
104
+ def add_validation(*params, &block)
105
+ validation = Validation.new(*params, &block)
106
+
107
+ validation.attributes.each do |attribute|
108
+ @validations[attribute] << validation if attribute.is_a? Symbol
109
+ end
110
+ end
111
+
112
+ # Returns validations for given attribute.
113
+ def [](attribute)
114
+ @validations[attribute]
115
+ end
116
+
117
+ # Returns true if a attributes has validations assigned, false
118
+ # otherwise.
119
+ def has_validations?
120
+ @validations.any? { |attribute, validations| validations.any? }
121
+ end
122
+
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,36 @@
1
+ module NotNaughty
2
+
3
+ # == Container for failed validations.
4
+ #
5
+ # ...
6
+ class Violation < RuntimeError
7
+ extend Forwardable
8
+
9
+ def_delegators :@errors, :empty?, :clear, :[], :each
10
+
11
+ include Enumerable
12
+
13
+ def initialize() #:nodoc:
14
+ @errors = Hash.new {|h, k| h[k] = []}
15
+ end
16
+ # Adds an error for the given attribute.
17
+ def add(k, msg) @errors[k] << msg end
18
+
19
+ # Returns an array of fully-formatted error messages.
20
+ def full_messages
21
+ @errors.inject([]) do |messages, k_errors| k, errors = *k_errors
22
+ errors.each {|e| messages << eval(e.inspect.delete('\\') % k) }
23
+ messages
24
+ end
25
+ end
26
+
27
+ # Returns an array of evaluated error messages for given attribute.
28
+ def on(attribute)
29
+ @errors[attribute].map do |message|
30
+ eval(message.inspect.delete('\\') % attribute)
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,21 @@
1
+ require "#{ File.dirname(__FILE__) }/spec_helper.rb"
2
+
3
+ describe subject::ErrorHandler do
4
+ before(:each) { @eh = subject::ErrorHandler.new }
5
+
6
+ it "passed all Exceptions to Kernel.raise" do
7
+ proc { @eh.raise Exception.new }.should raise_error(Exception)
8
+ end
9
+
10
+ it "calls the closest handle" do
11
+ @eh.handle(ArgumentError) {ArgumentError}
12
+ @eh.handle(StandardError) {StandardError}
13
+
14
+ @eh.raise(ArgumentError.new).should == ArgumentError
15
+ @eh.raise(StandardError.new).should == StandardError
16
+ @eh.raise(NoMethodError.new).should == StandardError
17
+
18
+ proc { @eh.raise ScriptError.new }.should raise_error(Exception)
19
+ end
20
+
21
+ end
@@ -0,0 +1,82 @@
1
+ require "#{ File.dirname(__FILE__) }/spec_helper.rb"
2
+
3
+ module subject::ClassMethods
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(:ClassMethods)
16
+ subject::should be_const_defined(:InstanceMethods)
17
+ subject::should be_const_defined(:Violation)
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(:state).should be_an_instance_of(subject::Validator)
23
+ validated.validator.states.should have_key(:state)
24
+
25
+ validator = Class.new(subject::Validator)
26
+ validated = Class.new(Object).extend subject
27
+ validated.validator validator, :create, :update
28
+ validated.validator.should be_an_instance_of(validator)
29
+ validated.validator.states.keys.should include(:create, :update)
30
+ end
31
+ it "should extend the receiver if validator is defined" do
32
+ validated = Class.new(Object)
33
+
34
+ subject::ClassMethods.should_receive(:extended).with(validated)
35
+ subject::InstanceMethods.should_receive(:included).with(validated)
36
+
37
+ validated.extend subject
38
+ end
39
+ it "should deep-clone the validator if inherited" do
40
+ super_validated = Class.new(Object).extend subject
41
+ super_validated.validator.add_validation(:name) {|o, a, v|}
42
+ validated = Class.new(super_validated)
43
+
44
+ validated.validator.should_not == super_validated.validator
45
+ validated.validator.should have_validations
46
+ validated.validator.add_validation(:name) {|o, a, v|}
47
+
48
+ super_validated.validator.get_state.validations[:name].length.
49
+ should < validated.validator.get_state.validations[:name].length
50
+ end
51
+ it "should prepend a validation before any method" do
52
+ validated = Class.new(Object).extend subject
53
+
54
+ validated.validated_before :clone
55
+ instance = validated.new
56
+ instance.should_receive(:valid?).once.and_return(true)
57
+ instance.should_receive(:clone_without_validations)
58
+ instance.clone
59
+ end
60
+ it "should raise an exception if invalid and method called" do
61
+ validated = Class.new(Object).extend subject
62
+
63
+ validated.validated_before :clone
64
+ instance = validated.new
65
+ instance.should_receive(:valid?).once.and_return(false)
66
+ lambda { instance.clone }.should raise_error(subject::Violation)
67
+ end
68
+ it "should return false if invalid and method called" do
69
+ validated = Class.new(Object).extend subject
70
+
71
+ validated.validated_before :clone
72
+ validated.validator.error_handler.handle(subject::Violation) {false}
73
+ instance = validated.new
74
+ instance.should_receive(:valid?).once.and_return(false)
75
+ instance.clone.should == false
76
+ end
77
+ it "should add ClassMethods to Validation observers" do
78
+ subject::Validation.instance_variable_get(:@observer_peers).
79
+ should include(subject::ClassMethods)
80
+ end
81
+
82
+ end
data/spec/rcov.opts ADDED
@@ -0,0 +1,4 @@
1
+ --exclude
2
+ gems
3
+ --exclude
4
+ spec
data/spec/spec.opts ADDED
@@ -0,0 +1,5 @@
1
+ --colour
2
+ --backtrace
3
+ --format
4
+ specdoc
5
+ --diff
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require "#{ File.dirname __FILE__ }/../lib/not_naughty.rb"
5
+ NotNaughty::Validation.load 'format', 'presence'
6
+
7
+ def subject() ::NotNaughty end
8
+ def h(something)
9
+ puts '<pre>%s</pre>' %
10
+ something.inspect.gsub(/[<>]/) {|m| (m == '<')? '&lt;': '&gt;'}
11
+ end
@@ -0,0 +1,117 @@
1
+ require "#{ File.dirname __FILE__ }/spec_helper.rb"
2
+
3
+ describe subject::Validation do
4
+
5
+ it "should register validations if inherited" do
6
+ subject::ClassMethods.
7
+ should_receive(:update).once.
8
+ with Class.new(subject::Validation)
9
+ end
10
+ it "should build validations with block" do
11
+ block = proc {|o, a, v|}
12
+ validation = subject::Validation.new({}, &block)
13
+
14
+ validation.instance_variable_get(:@block).should == block
15
+ end
16
+ it "should alias call to call_without_conditions" do
17
+ validation = subject::Validation.new({}) {|o, a, v|}
18
+
19
+ validation.method(:call).
20
+ should == validation.method(:call_without_conditions)
21
+ end
22
+ it "should not build conditions" do
23
+ validation = subject::Validation.
24
+ new({:if => nil, :unless => nil}) {|o, a, v|}
25
+
26
+ validation.instance_variable_get(:@conditions).should be_empty
27
+ end
28
+ it "should build conditions" do
29
+ validation = subject::Validation.new({:if => :nil?}) {|o, a, v|}
30
+ validation.instance_variable_get(:@conditions).should_not be_empty
31
+
32
+ validation.instance_variable_get(:@conditions).
33
+ first.instance_variable_get(:@condition).
34
+ should == :nil?
35
+ end
36
+ it "should alias call to call_with_conditions" do
37
+ validation = subject::Validation.new({:if => :nil?}) {|o, a, v|}
38
+
39
+ validation.method(:call).
40
+ should == validation.method(:call_with_conditions)
41
+ end
42
+ it "should call" do
43
+ probe = mock 'Probe'
44
+ probe.should_receive(:test).exactly(3).times
45
+
46
+ validation = subject::Validation.
47
+ new({}) { |o, a, v| [o, a, v].each { |p| p.test } }
48
+
49
+ validation.call probe, probe, probe
50
+ end
51
+ it "should call unless a condition passes" do
52
+ probe = mock 'Probe'
53
+ probe.stub!(:nil?).and_return(true)
54
+
55
+ validation = subject::Validation.
56
+ new({:unless => :nil?}) { |o, a, v| [o, a, v].each { |p| p.test } }
57
+
58
+ validation.call probe, probe, probe
59
+
60
+ probe.should_receive(:test).exactly(3).times
61
+ probe.stub!(:nil?).and_return(false)
62
+
63
+ validation.call probe, probe, probe
64
+ end
65
+ it "should not call" do
66
+ probe = mock 'Probe'
67
+ probe.should_not_receive(:test)
68
+
69
+ validation = subject::Validation.
70
+ new({:if => :nil?}) { |o, a, v| [o, a, v].each { |p| p.test } }
71
+
72
+ validation.call probe, probe, probe
73
+
74
+ end
75
+ it "should have validated attributes accessable" do
76
+ validation = subject::Validation.new(:probe)
77
+ validation.attributes.should include(:probe)
78
+ end
79
+ it "should clone observer peers to descendant" do
80
+ peers = subject::Validation.instance_variable_get :@observer_peers
81
+ peers.should_receive(:clone).and_return(true)
82
+ descendant = Class.new(subject::Validation)
83
+ descendant.instance_variable_get(:@observer_peers).should be_true
84
+ end
85
+
86
+ end
87
+
88
+ describe subject::Validation::Condition do
89
+
90
+ it "should evaluate positive callable" do
91
+ condition = subject::Validation::Condition.new proc {|o| o.nil?}
92
+ condition.evaluate(nil).should be_true
93
+ end
94
+ it "should evaluate negative callable" do
95
+ condition = subject::Validation::Condition.new proc {|o| o.nil?}, false
96
+ condition.evaluate(nil).should be_false
97
+ end
98
+ it "should evaluate positive Symbol" do
99
+ condition = subject::Validation::Condition.new :nil?
100
+ condition.evaluate(nil).should be_true
101
+ end
102
+ it "should evaluate negative Symbol" do
103
+ condition = subject::Validation::Condition.new :nil?, false
104
+ condition.evaluate(nil).should be_false
105
+ end
106
+ it "should evaluate positive UnboundMethod" do
107
+ um = NilClass.instance_method :nil?
108
+ condition = subject::Validation::Condition.new um
109
+ condition.evaluate(nil).should be_true
110
+ end
111
+ it "should evaluate negative UnboundMethod" do
112
+ um = NilClass.instance_method :nil?
113
+ condition = subject::Validation::Condition.new um, false
114
+ condition.evaluate(nil).should be_false
115
+ end
116
+
117
+ end