boof-not-naughty 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,48 @@
1
+ module Tree #:nodoc:
2
+ class TreeNode #:nodoc:
3
+
4
+ def closest(obj)
5
+ if @name == obj then self
6
+ elsif @name > obj
7
+ closest = self
8
+ @children.any? { |child| node = child.closest(obj) and closest = node }
9
+
10
+ closest
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
+
17
+ module NotNaughty
18
+ class ErrorHandler
19
+
20
+ def initialize(handler = Kernel)
21
+ @handles = Tree::TreeNode.new Exception, proc { |e| handler.raise e }
22
+ end
23
+
24
+ # Calls closest handle with exception.
25
+ def raise(exception)
26
+ handle = @handles.closest exception.class
27
+ handle.content.call exception
28
+ end
29
+
30
+ # Inserts handle into the ordered tree.
31
+ def handle(exception_class, &block)
32
+ closest_handle = @handles.closest exception_class
33
+
34
+ if closest_handle == exception_class then closest_handle.content = block
35
+ else
36
+ new_handle = Tree::TreeNode.new exception_class, block
37
+
38
+ closest_handle.children do |child|
39
+ exception_class > child.name and
40
+ new_handle << closest_handle.remove!(child)
41
+ end
42
+
43
+ closest_handle << new_handle
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ module NotNaughty
2
+ module InstanceMethods
3
+
4
+ # Returns instance of Errors.
5
+ def errors() @errors ||= ::NotNaughty::Violation.new end
6
+
7
+ # Returns true if all validations passed
8
+ def valid?
9
+ validate
10
+ errors.empty?
11
+ end
12
+
13
+ # Clears errors and validates.
14
+ def validate
15
+ errors.clear
16
+ self.class.validator.invoke self
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,132 @@
1
+ module NotNaughty
2
+
3
+ # == The superclass for Validations.
4
+ #
5
+ # See new for more information.
6
+ class Validation
7
+
8
+ # Returns array of paths which are scanned for validations by load.
9
+ def self.load_paths
10
+ @load_paths
11
+ end
12
+ @load_paths = [File.join(%W[#{ File.dirname __FILE__ } validations])]
13
+ PATTERN = File.join %w[%s ** %s_validation.rb]
14
+
15
+ # Loads validations from load_paths.
16
+ def self.load(*validations)
17
+ validations.each do |validation|
18
+ @load_paths.each do |load_path|
19
+ pattern = PATTERN % [load_path, validation]
20
+ Dir[pattern].each { |validation_path| require validation_path }
21
+ end
22
+ end
23
+ end
24
+
25
+ extend Observable
26
+ def self.inherited(descendant) #:nodoc:
27
+ changed and notify_observers(descendant)
28
+
29
+ descendant.
30
+ instance_variable_set :@observer_peers, @observer_peers.clone
31
+ end
32
+
33
+ # Builds validations.
34
+ #
35
+ # <b>Example:</b>
36
+ # NotNaughty::Validation.new :temp, :if => :water? do |obj, attr, val|
37
+ # obj.errors.add attr, 'too hot' unless val < 100
38
+ # end
39
+ #
40
+ # <b>Like:</b>
41
+ # class TempValidation < NotNaughty::Validation
42
+ # def initialize(opts, attributes)
43
+ # super opts, attributes method(:temp_validation)
44
+ # end
45
+ # def temp_validation(obj, attr, val)
46
+ # obj.errors.add attr, 'too hot' unless val < 100
47
+ # end
48
+ # end
49
+ #
50
+ # Validation.new TempValidation, :temp, :if => :water?
51
+ #
52
+ # The last one also notifies all Observers of Validation (see
53
+ # Builder#update). If Builder#update is called because <Name>Validation
54
+ # is inherited from Validation the ValidationBuilder gets the method
55
+ # validates_<name>_of and so does the classes that included the Builder.
56
+ def self.new(*params, &block)
57
+ attributes = if params.first.is_a? Class and params.first < self
58
+ klass = params.shift
59
+ klass.new(*params, &block)
60
+ else
61
+ options = params.extract_options!
62
+ instance = allocate
63
+ instance.send :initialize, options, params.map {|p|p.to_sym}, &block
64
+ instance
65
+ end
66
+ end
67
+
68
+ attr_reader :attributes
69
+
70
+ def initialize(opts, attributes, &block) #:nodoc:
71
+ build_conditions opts[:if], opts[:unless]
72
+ @attributes, @block, @opts = attributes, block, opts
73
+ end
74
+
75
+ def call_without_conditions(obj, attr, value) #:nodoc:
76
+ @block.call obj, attr, value
77
+ end
78
+ alias_method :call, :call_without_conditions
79
+
80
+ def call_with_conditions(obj, attr, value) #:nodoc:
81
+ if @conditions.all? { |c| c.evaluate obj }
82
+ call_without_conditions obj, attr, value
83
+ end
84
+ end
85
+
86
+ protected
87
+ def build_conditions(p, n) #:nodoc:
88
+ @conditions = []
89
+ [p].flatten.each {|c| @conditions << Condition.new(c) if c }
90
+ [n].flatten.each {|c| @conditions << Condition.new(c, false) if c }
91
+
92
+ (class << self; self; end).module_eval do
93
+ alias_method :call, :call_with_conditions
94
+ end unless @conditions.empty?
95
+ end
96
+
97
+ # Conditions for use in Validations are usually used with Validations.
98
+ class Condition
99
+
100
+ # An instance of Condition accepts Symbols, UnboundMethods or anything
101
+ # that responds to :call.
102
+ #
103
+ # The following examples are similiar to each other:
104
+ #
105
+ # NotNaughty::Validation::Condition.new proc {|o| o.nil?}
106
+ # NotNaughty::Validation::Condition.new :nil?
107
+ # NotNaughty::Validation::Condition.new Object.instance_method(:nil?)
108
+ def self.new(condition, positive = true)
109
+ instance = allocate
110
+ instance.instance_variable_set :@condition, condition
111
+
112
+ block = case condition
113
+ when Symbol then positive ?
114
+ proc { |o| o.send @condition } :
115
+ proc { |o| not o.send @condition }
116
+ when UnboundMethod then positive ?
117
+ proc { |o| @condition.bind(o).call } :
118
+ proc { |o| not @condition.bind(o).call }
119
+ else positive ?
120
+ proc { |o| @condition.call o } :
121
+ proc { |o| not @condition.call o }
122
+ end
123
+
124
+ (class << instance; self; end).
125
+ module_eval { define_method(:evaluate, &block) }
126
+
127
+ instance
128
+ end
129
+
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,46 @@
1
+ module NotNaughty
2
+
3
+ # == Validates acceptance of obj's attribute against a fixed value via <tt>:eql?</tt> call.
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>:accept</tt>:: object to check against (via :eql?)
10
+ # <tt>:message</tt>:: see NotNaughty::Errors for details
11
+ # <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
12
+ # <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
13
+ #
14
+ # <b>Example:</b>
15
+ #
16
+ # invalid = 'abc'
17
+ # valid = '1'
18
+ # def invalid.errors() @errors ||= NotNauthy::Errors.new end
19
+ # def valid.errors() @errors ||= NotNauthy::Errors.new end
20
+ #
21
+ # AcceptanceValidation.new({}, :to_s).call invalid, :to_s, 'abc'
22
+ # invalid.errors.on(:to_s).any? # => true
23
+ #
24
+ # AcceptanceValidation.new({}, :to_s).call valid, :to_s, '1'
25
+ # valid.errors.on(:to_s).any? # => false
26
+ class AcceptanceValidation < Validation
27
+
28
+ def initialize(opts, attributes) #:nodoc:
29
+ __message, __accept =
30
+ opts[:message] || '#{"%s".humanize} not accepted.',
31
+ opts[:accept] || '1'
32
+
33
+ if opts[:allow_blank] or opts.fetch(:allow_nil, true)
34
+ __allow = if opts[:allow_blank] then :blank? else :nil? end
35
+ super opts, attributes do |o, a, v|
36
+ o.errors.add a, __message unless v.send __allow or __accept.eql? v
37
+ end
38
+ else
39
+ super opts, attributes do |o, a, v|
40
+ o.errors.add a, __message unless __accept.eql? v
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,49 @@
1
+ module NotNaughty
2
+
3
+ # == Validates confirmaton of obj's attribute via <tt>:eql?</tt> call against the _appropiate_ confirmation attribute.
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 = 'abc'
16
+ # def obj.errors() @errors ||= NotNauthy::Errors.new end
17
+ # def obj.to_s_confirmation() '123 end
18
+ #
19
+ # ConfirmationValidation.new({}, :to_s).call obj, :to_s, 'abc'
20
+ # obj.errors.on(:to_s).any? # => true
21
+ class ConfirmationValidation < Validation
22
+
23
+ def initialize(valid, attributes) #:nodoc:
24
+ valid[:message] ||= '%s could not be confirmed.'
25
+
26
+ if valid[:allow_blank] || valid[:allow_nil]
27
+ valid[:allow] = valid[:allow_blank] ? :blank? : :nil?
28
+ super valid, attributes, &confirmation_block_with_exception(valid)
29
+ else
30
+ super valid, attributes, &confirmation_block(valid)
31
+ end
32
+ end
33
+
34
+ protected
35
+ def confirmation_block_with_exception(valid)
36
+ proc do |obj, attr, value|
37
+ value.send valid[:allow] or
38
+ obj.send(:"#{ attr }_confirmation") == value or
39
+ obj.errors.add attr, valid[:message]
40
+ end
41
+ end
42
+ def confirmation_block(valid)
43
+ proc do |obj, attr, value|
44
+ obj.send(:"#{ attr }_confirmation") == value or
45
+ obj.errors.add attr, valid[:message]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,71 @@
1
+ module NotNaughty
2
+
3
+ # == Validates format of obj's attribute via the <tt>:match</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>:with</tt>:: object that checks via a <tt>:match</tt> call
10
+ # <tt>:message</tt>:: see NotNaughty::Errors for details
11
+ # <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
12
+ # <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
13
+ #
14
+ # <b>Example:</b>
15
+ #
16
+ # obj = 'abc'
17
+ # def obj.errors() @errors ||= NotNauthy::Errors.new end
18
+ #
19
+ # FormatValidation.new({:with => /[a-z]/}, :to_s).call obj, :to_s, obj
20
+ # obj.errors.on(:to_s).any? # => false
21
+ #
22
+ # FormatValidation.new({:with => /[A-Z]/}, :to_s).call obj, :to_s, obj
23
+ # obj.errors.on(:to_s) # => ["Format of to_s does not match."]
24
+ #
25
+ # <b>Use predefined format expressions:</b>
26
+ #
27
+ # obj = 'Valid Address <foo@bar.baz>'
28
+ # def obj.errors() @errors ||= NotNauthy::Errors.new end
29
+ #
30
+ # FormatValidation.new({:with => :email}, :to_s).call obj, :to_s, obj
31
+ # obj.errors.on(:to_s).any? # => false
32
+ class FormatValidation < Validation
33
+
34
+ # Predefined matchers.
35
+ PREDEFINED = {
36
+ :email => /[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/,
37
+ :ip => /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/
38
+ }
39
+
40
+ def initialize(valid, attributes) #:nodoc:
41
+ valid[:with] = PREDEFINED.fetch valid[:with] if valid[:with].is_a? Symbol
42
+ valid[:with].respond_to? :match or
43
+ raise ArgumentError, "#{ valid[:with].inspect } doesn't :match"
44
+
45
+ valid[:message] ||= '%s does not match format.'
46
+
47
+ if valid[:allow_blank] || valid[:allow_nil]
48
+ valid[:allow] = valid[:allow_blank] ? :blank? : :nil?
49
+ super valid, attributes, &matching_block_with_exception(valid)
50
+ else
51
+ super valid, attributes, &matching_block(valid)
52
+ end
53
+ end
54
+
55
+ protected
56
+ def matching_block_with_exception(valid)
57
+ proc do |obj, attr, value|
58
+ value.send valid[:allow] or
59
+ valid[:with].match value or
60
+ obj.errors.add attr, valid[:message]
61
+ end
62
+ end
63
+ def matching_block(valid)
64
+ proc do |obj, attr, value|
65
+ valid[:with].match value or
66
+ obj.errors.add attr, valid[:message]
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,91 @@
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
+ #
39
+ # I'm sorry for using eval here...
40
+ class LengthValidation < Validation
41
+
42
+ TEMPLATE = <<-BLOCK
43
+ proc do |obj, attr, value|
44
+ value.%s or %s value.length or
45
+ obj.errors.add attr, %s
46
+ end
47
+ BLOCK
48
+
49
+ def initialize(valid, attributes) #:nodoc:
50
+ super valid, attributes, &build_block(valid)
51
+ end
52
+
53
+ protected
54
+ def build_block(valid) #:nodoc:
55
+ if valid[:is] then is_block valid
56
+ elsif valid[:within] or valid[:minimum] && valid[:maximum] then within_block valid
57
+ elsif valid[:minimum] then minimum_block valid
58
+ elsif valid[:maximum] then maximum_block valid
59
+ else
60
+ raise ArgumentError, 'no boundary given'
61
+ end
62
+ end
63
+
64
+ def is_block(valid)
65
+ valid[:message] ||= "Length of %s is not equal to #{ valid[:is] }."
66
+
67
+ eval TEMPLATE % [ valid[:allow_blank] ? :blank? : :nil?,
68
+ "#{ valid[:is].inspect } ==", valid[:message].inspect ]
69
+ end
70
+ def within_block(valid)
71
+ valid[:within] ||= Range.new valid[:minimum], valid[:maximum]
72
+ valid[:message] ||= "Length of %s is not within #{ valid[:within].min } and #{ valid[:within].max }."
73
+
74
+ eval TEMPLATE % [ valid[:allow_blank] ? :blank? : :nil?,
75
+ "(#{ valid[:within].inspect }).include?", valid[:message].inspect ]
76
+ end
77
+ def minimum_block(valid)
78
+ valid[:message] ||= "Length of %s is less than #{ valid[:minimum] }."
79
+
80
+ eval TEMPLATE % [ valid[:allow_blank] ? :blank? : :nil?,
81
+ "#{ valid[:minimum].inspect } <=", valid[:message].inspect ]
82
+ end
83
+ def maximum_block(valid)
84
+ valid[:message] ||= "Length of %s is greater than #{ valid[:maximum] }."
85
+
86
+ eval TEMPLATE % [ valid[:allow_blank] ? :blank? : :nil?,
87
+ "#{ valid[:maximum].inspect } >=", valid[:message].inspect ]
88
+ end
89
+
90
+ end
91
+ end