boof-not-naughty 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +57 -0
- data/COPYING +18 -0
- data/README.rdoc +59 -0
- data/Rakefile +126 -0
- data/lib/core_extensions.rb +20 -0
- data/lib/not_naughty.rb +86 -0
- data/lib/not_naughty/class_methods.rb +78 -0
- data/lib/not_naughty/error_handler.rb +48 -0
- data/lib/not_naughty/instance_methods.rb +20 -0
- data/lib/not_naughty/validation.rb +132 -0
- data/lib/not_naughty/validations/acceptance_validation.rb +46 -0
- data/lib/not_naughty/validations/confirmation_validation.rb +49 -0
- data/lib/not_naughty/validations/format_validation.rb +71 -0
- data/lib/not_naughty/validations/length_validation.rb +91 -0
- data/lib/not_naughty/validations/numericality_validation.rb +43 -0
- data/lib/not_naughty/validations/presence_validation.rb +32 -0
- data/lib/not_naughty/validator.rb +125 -0
- data/lib/not_naughty/violation.rb +36 -0
- data/spec/error_handler_spec.rb +21 -0
- data/spec/not_naughty_spec.rb +82 -0
- data/spec/rcov.opts +4 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/validation_spec.rb +117 -0
- data/spec/validations_spec.rb +267 -0
- data/spec/validator_spec.rb +132 -0
- data/spec/violation_spec.rb +36 -0
- metadata +102 -0
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
=== 0.6.2 (2008-10-31)
|
2
|
+
* NotNaughty::Builder is now NotNaughty::ClassMethods
|
3
|
+
* NotNaughty::Builder::ValidationDelegator is now NotNaughty::ClassMethods::Builder
|
4
|
+
* NotNaughty::ClassMethods::Builder does not inherit from Delegation anymore, so { format :with => /rx/ } works
|
5
|
+
* NotNaughty::Validation loads validations from directories listed in load_paths
|
6
|
+
|
7
|
+
=== 0.6.1 (2008-10-10)
|
8
|
+
* builder/validation cleanups
|
9
|
+
* added gemspecs for github
|
10
|
+
* added support for predefined format expressions [resolves:#19814]
|
11
|
+
|
12
|
+
=== 0.6 (2008-10-08)
|
13
|
+
* fixed Rakefile
|
14
|
+
* removed whitespaces
|
15
|
+
* removed Ruby-Sequel adapter (becomes an extra gem)
|
16
|
+
* removed assistance gem dependency
|
17
|
+
|
18
|
+
=== 0.5.1 (2008-03-17)
|
19
|
+
* fixed missing dependency
|
20
|
+
|
21
|
+
=== 0.5 (2008-03-17)
|
22
|
+
* added an error handler, can now handle SQL validation errors
|
23
|
+
* renamed Errors to Violation and merged it with ValidationException
|
24
|
+
* changed specs for 0.4.1
|
25
|
+
|
26
|
+
=== 0.4.2 (2008-03-12)
|
27
|
+
* fixed bug that causes infinite recursion in some apps that reload classes
|
28
|
+
|
29
|
+
=== 0.4.1 (2008-03-12)
|
30
|
+
* some minor changes in validator method
|
31
|
+
|
32
|
+
=== 0.4 (2008-02-18)
|
33
|
+
* completed documentation
|
34
|
+
* added spec for :each in Builder::ValidationDelegator
|
35
|
+
|
36
|
+
=== 0.3.1 (2008-02-14)
|
37
|
+
* fixed missing require
|
38
|
+
|
39
|
+
=== 0.3 (2008-02-09)
|
40
|
+
* renamed to NotNaughty - The Validation Framework
|
41
|
+
* made predefined Validation optional, you can load them with:
|
42
|
+
|
43
|
+
::NotNaughty::Validation.load '*'
|
44
|
+
|
45
|
+
or ordered (should be preferred):
|
46
|
+
|
47
|
+
::NotNaughty::Validation.load :acceptance, :confirmation, :format, :length,
|
48
|
+
:numericality, :presence
|
49
|
+
|
50
|
+
* specs pass, 100% code coverage, 2 not complete, so pending specs
|
51
|
+
* docs added
|
52
|
+
|
53
|
+
=== 0.2 (2008-02-09)
|
54
|
+
* complete rewrite
|
55
|
+
|
56
|
+
=== 0.1 (2008-02-06)
|
57
|
+
* initial release
|
data/COPYING
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2007-2008 Florian Aßmann
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
= NotNaughty - The Validation Framework
|
2
|
+
|
3
|
+
NotNaughty extends your ruby Project with a highly custumizable validation API.
|
4
|
+
|
5
|
+
== Features
|
6
|
+
|
7
|
+
<b>Easy to adapt:</b>
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'not_naughty'
|
11
|
+
|
12
|
+
Person = Struct.new(:name) do
|
13
|
+
extend NotNaughty
|
14
|
+
validates(:name) { presence and length :minimum => 4 }
|
15
|
+
end
|
16
|
+
|
17
|
+
Person.new('Horst').valid? # => true
|
18
|
+
Person.new('Foo').valid? # => false
|
19
|
+
|
20
|
+
<b>Easy to extent:</b>
|
21
|
+
|
22
|
+
class ExampleValidation < NotNaughty::Validation
|
23
|
+
def initialize(options, attributes)
|
24
|
+
msg = options[:message] || '#{"%s".humanize} is not an example.'
|
25
|
+
|
26
|
+
super options, attributes do |record, attribute, value|
|
27
|
+
record.errors.add(attribute, msg) unless value.is_a? Example
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Person.instance_eval do
|
33
|
+
validates_example_of :example_attribute
|
34
|
+
end
|
35
|
+
|
36
|
+
<b>Handle SQL error gracefully:</b>
|
37
|
+
|
38
|
+
Person.instance_eval do
|
39
|
+
validator.error_handler.handle(SQLError) { |err| '...' }
|
40
|
+
end
|
41
|
+
|
42
|
+
<b>Syntactical Sugar with Builder methods:</b>
|
43
|
+
|
44
|
+
validates(:username, :password, :if => :new?) {length :minimum => 6}
|
45
|
+
validates(:password, :allow_blank => 1) {confirmation and complexity :high}
|
46
|
+
|
47
|
+
<b>Beautiful error messages:</b>
|
48
|
+
|
49
|
+
validates_presence_of :red_shoes,
|
50
|
+
:message => '#{"%s".humanize} are not here.' # => Red shoes are not here.
|
51
|
+
|
52
|
+
<b>Conditional Validations:</b>
|
53
|
+
|
54
|
+
validates(:if => :necessary?) {...}
|
55
|
+
validates(:unless => proc {|obj| obj.vip?}) { '...' }
|
56
|
+
|
57
|
+
== Copying
|
58
|
+
|
59
|
+
:include: COPYING
|
data/Rakefile
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "rake/clean"
|
3
|
+
require "rake/gempackagetask"
|
4
|
+
require "rake/rdoctask"
|
5
|
+
require "fileutils"
|
6
|
+
include FileUtils
|
7
|
+
|
8
|
+
##############################################################################
|
9
|
+
# Configuration
|
10
|
+
##############################################################################
|
11
|
+
NAME = "not-naughty"
|
12
|
+
VERS = "0.6.2"
|
13
|
+
CLEAN.include %w[ pkg doc coverage ]
|
14
|
+
RDOC_OPTS = [
|
15
|
+
"--quiet",
|
16
|
+
"--title", "NotNaughty: The Validation Framework",
|
17
|
+
"--opname", "index.html",
|
18
|
+
"--inline-source",
|
19
|
+
"--line-numbers",
|
20
|
+
"--main", "README.rdoc",
|
21
|
+
"--inline-source",
|
22
|
+
"--charset", "utf-8"
|
23
|
+
]
|
24
|
+
|
25
|
+
##############################################################################
|
26
|
+
# RDoc
|
27
|
+
##############################################################################
|
28
|
+
task :doc => [:rdoc]
|
29
|
+
|
30
|
+
Rake::RDocTask.new do |rdoc|
|
31
|
+
rdoc.rdoc_dir = "doc"
|
32
|
+
rdoc.options += RDOC_OPTS
|
33
|
+
rdoc.main = "README.rdoc"
|
34
|
+
rdoc.title = "NotNaughty: The Validation Framework"
|
35
|
+
rdoc.rdoc_files.add %w[README.rdoc COPYING CHANGELOG.rdoc lib/**/*.rb]
|
36
|
+
end
|
37
|
+
|
38
|
+
task :doc_rforge => [:doc]
|
39
|
+
|
40
|
+
desc "Update docs and upload to rubyforge.org"
|
41
|
+
task :doc_rforge do
|
42
|
+
sh %{scp -r doc/* boof@rubyforge.org:/var/www/gforge-projects/not-naughty}
|
43
|
+
end
|
44
|
+
|
45
|
+
##############################################################################
|
46
|
+
# specs
|
47
|
+
##############################################################################
|
48
|
+
require "spec/rake/spectask"
|
49
|
+
|
50
|
+
desc "Run specs with coverage"
|
51
|
+
Spec::Rake::SpecTask.new("spec") do |t|
|
52
|
+
t.spec_files = FileList["spec/**/*_spec.rb"]
|
53
|
+
t.spec_opts = File.read("spec/spec.opts").split("\n")
|
54
|
+
t.rcov_opts = File.read("spec/rcov.opts").split("\n")
|
55
|
+
t.rcov = true
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "Run specs without coverage"
|
59
|
+
Spec::Rake::SpecTask.new("spec_no_cov") do |t|
|
60
|
+
t.spec_files = FileList["spec/**/*_spec.rb"]
|
61
|
+
t.spec_opts = File.read("spec/spec.opts").split("\n")
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "check documentation coverage"
|
65
|
+
task :dcov do
|
66
|
+
sh "find lib -name '*.rb' | xargs dcov"
|
67
|
+
end
|
68
|
+
|
69
|
+
##############################################################################
|
70
|
+
# Gem packaging
|
71
|
+
##############################################################################
|
72
|
+
desc "Packages up NotNaughty."
|
73
|
+
task :default => [:package]
|
74
|
+
task :package => [:clean]
|
75
|
+
|
76
|
+
spec = Gem::Specification.new do |s|
|
77
|
+
s.name = NAME
|
78
|
+
s.rubyforge_project = NAME
|
79
|
+
s.version = VERS
|
80
|
+
s.platform = Gem::Platform::RUBY
|
81
|
+
s.has_rdoc = true
|
82
|
+
s.extra_rdoc_files = ["README.rdoc", "CHANGELOG.rdoc", "COPYING"]
|
83
|
+
s.summary = "Heavily armed validation framework."
|
84
|
+
s.description = s.summary
|
85
|
+
s.author = "Florian Aßmann"
|
86
|
+
s.email = "boof@monkey-patch.me"
|
87
|
+
s.homepage = "http://monkey-patch.me/p/notnaughty"
|
88
|
+
s.required_ruby_version = ">= 1.8.6"
|
89
|
+
s.add_dependency("rubytree", ">= 0.5.2")
|
90
|
+
|
91
|
+
s.files = %w(COPYING README.rdoc Rakefile) + Dir.glob("{spec,lib}/**/*")
|
92
|
+
|
93
|
+
s.require_path = "lib"
|
94
|
+
end
|
95
|
+
|
96
|
+
Rake::GemPackageTask.new(spec) do |p|
|
97
|
+
p.need_tar = true
|
98
|
+
p.gem_spec = spec
|
99
|
+
end
|
100
|
+
|
101
|
+
##############################################################################
|
102
|
+
# installation & removal
|
103
|
+
##############################################################################
|
104
|
+
task :install do
|
105
|
+
sh %{rake package}
|
106
|
+
sh %{sudo gem install pkg/#{NAME}-#{VERS}}
|
107
|
+
end
|
108
|
+
|
109
|
+
task :install_no_docs do
|
110
|
+
sh %{rake package}
|
111
|
+
sh %{sudo gem install pkg/#{NAME}-#{VERS} --no-rdoc --no-ri}
|
112
|
+
end
|
113
|
+
|
114
|
+
task :uninstall => [:clean] do
|
115
|
+
sh %{sudo gem uninstall #{NAME}}
|
116
|
+
end
|
117
|
+
|
118
|
+
##############################################################################
|
119
|
+
# gem and rdoc release
|
120
|
+
##############################################################################
|
121
|
+
task :release => [:package] do
|
122
|
+
sh %{rubyforge login}
|
123
|
+
sh %{rubyforge add_release #{NAME} #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.tgz}
|
124
|
+
sh %{rubyforge add_file #{NAME} #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.gem}
|
125
|
+
end
|
126
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Array #:nodoc:
|
2
|
+
instance_methods.include? 'extract_options!' or
|
3
|
+
define_method(:extract_options!) { Hash === last ? pop : {} }
|
4
|
+
end
|
5
|
+
class Object #:nodoc:
|
6
|
+
instance_methods.include? 'blank?' or
|
7
|
+
define_method(:blank?) { respond_to?(:empty?) ? empty? : !self }
|
8
|
+
end
|
9
|
+
class String #:nodoc:
|
10
|
+
instance_methods.include? 'blank?' or
|
11
|
+
define_method(:blank?) { self !~ /\S/ }
|
12
|
+
end
|
13
|
+
class Numeric #:nodoc:
|
14
|
+
instance_methods.include? 'blank?' or
|
15
|
+
define_method(:blank?) { false }
|
16
|
+
end
|
17
|
+
|
18
|
+
def nil.blank?() true end unless nil.respond_to? :blank?
|
19
|
+
def true.blank?() false end unless true.respond_to? :blank?
|
20
|
+
def false.blank?() true end unless false.respond_to? :blank?
|
data/lib/not_naughty.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'observer'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'rubytree', '>= 0.5.2'
|
6
|
+
require 'tree'
|
7
|
+
|
8
|
+
$:.unshift File.dirname(__FILE__)
|
9
|
+
require 'core_extensions'
|
10
|
+
|
11
|
+
module NotNaughty
|
12
|
+
require 'not_naughty/validator'
|
13
|
+
|
14
|
+
require 'not_naughty/class_methods'
|
15
|
+
require 'not_naughty/validation'
|
16
|
+
Validation.add_observer ClassMethods
|
17
|
+
|
18
|
+
require 'not_naughty/violation'
|
19
|
+
require 'not_naughty/error_handler'
|
20
|
+
require 'not_naughty/instance_methods'
|
21
|
+
|
22
|
+
# Extended classes get NotNaughty::ClassMethods and
|
23
|
+
# NotNaughty::InstanceMethods.
|
24
|
+
def self.extended(base)
|
25
|
+
base.instance_eval do
|
26
|
+
include InstanceMethods
|
27
|
+
extend ClassMethods
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# call-seq:
|
32
|
+
# validator(validator_klass = NotNaughty::Validator, *states = [:default])
|
33
|
+
#
|
34
|
+
# Returns instance of Validator. This is either the validator's clone of
|
35
|
+
# superclass, an instance of the the given descendant of or the
|
36
|
+
# <tt>NotNaughty:Validator</tt> himself.
|
37
|
+
#
|
38
|
+
# <b>Examples:</b>
|
39
|
+
# validator # => Instance of NotNaughty::Validator with :default state
|
40
|
+
# validator :create, :update # ~ - but with :create and :update states
|
41
|
+
# validator AnotherValidator # Instance of AnotherValidator
|
42
|
+
#
|
43
|
+
# validator AnotherValidator, :state_a, :state_b
|
44
|
+
#
|
45
|
+
# The above examples work as long validator is not already called. To reset
|
46
|
+
# an already assigned validator set <tt>@validator</tt> to nil.
|
47
|
+
def validator(*states)
|
48
|
+
@validator ||= if !states.empty?
|
49
|
+
validator_klass =
|
50
|
+
if states.first.is_a? Class and states.first <= NotNaughty::Validator
|
51
|
+
states.shift
|
52
|
+
else
|
53
|
+
NotNaughty::Validator
|
54
|
+
end
|
55
|
+
|
56
|
+
validator_klass.new(*states)
|
57
|
+
|
58
|
+
elsif superclass.respond_to? :validator
|
59
|
+
superclass.validator.clone
|
60
|
+
|
61
|
+
else
|
62
|
+
NotNaughty::Validator.new
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Prepends a call for validation before then given method. If, on call, the
|
68
|
+
# validation passes the method is called. Otherwise it raises an
|
69
|
+
# NotNaughty::ValidationException or returns false.
|
70
|
+
#
|
71
|
+
# <b>Example:</b>
|
72
|
+
# validated_before :save # raise ValidationException unless valid?
|
73
|
+
def validated_before(method)
|
74
|
+
__method = :"#{method}_without_validations"
|
75
|
+
alias_method __method, method
|
76
|
+
|
77
|
+
define_method method do |*params|
|
78
|
+
begin
|
79
|
+
if valid? then send __method else raise errors end
|
80
|
+
rescue Exception => error
|
81
|
+
self.class.validator.error_handler.raise error
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module NotNaughty
|
2
|
+
module ClassMethods
|
3
|
+
|
4
|
+
def self.update(validation) # :nodoc:
|
5
|
+
if basename = validation.name[/([^:]+)Validation$/, 1]
|
6
|
+
define_method 'validates_%s_of' % basename.downcase do |*params|
|
7
|
+
validator.add_validation(validation, *params)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# == Syntactic sugar.
|
13
|
+
#
|
14
|
+
# <b>Example:</b>
|
15
|
+
# validates { presence_of :example, :if => :reading? }
|
16
|
+
# # => validate_presence_of :example, :if => :reading?
|
17
|
+
# validates(:name) { presence and length :minimum => 6 }
|
18
|
+
# # => validates_presence_of :name
|
19
|
+
# validates_length_of :name, :minimum => 6
|
20
|
+
# validates(:temp, :if => water?) { value :lt => 100 }
|
21
|
+
# # => validates_value_of :temp, :lt => 100, :if => water?
|
22
|
+
def validates(*params, &block)
|
23
|
+
Builder.new(self, *params).instance_eval(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Allows adding validations the legacy way.
|
27
|
+
def validates_each(*attributes, &block)
|
28
|
+
validator.add_validation(*attributes, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# With this you get syntactical sugar for all descendants of Validation
|
32
|
+
# see validates for examples.
|
33
|
+
class Builder
|
34
|
+
|
35
|
+
def initialize(klass, *params) # :nodoc:
|
36
|
+
@_vd_obj = klass
|
37
|
+
@_vd_obj_opts = params.extract_options!
|
38
|
+
@_vd_obj_attrs = params
|
39
|
+
|
40
|
+
methods = ClassMethods.instance_methods(false).grep(/^validates_.+_of$/)
|
41
|
+
|
42
|
+
if @_vd_obj_attrs.empty?
|
43
|
+
for method in methods
|
44
|
+
eval <<-EOS
|
45
|
+
def self.#{ method[/^validates_(.+)$/, 1] }(*params)
|
46
|
+
params << @_vd_obj_opts.merge(params.extract_options!)
|
47
|
+
|
48
|
+
begin
|
49
|
+
@_vd_obj.__send__(:#{ method }, *params)
|
50
|
+
rescue Exception
|
51
|
+
$@.delete_if { |s| /^\\(eval\\):/ =~ s }
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
end
|
55
|
+
EOS
|
56
|
+
end
|
57
|
+
else
|
58
|
+
for method in methods
|
59
|
+
eval <<-EOS
|
60
|
+
def self.#{ method[/^validates_(.+)_of$/, 1] }(*args)
|
61
|
+
params = @_vd_obj_attrs.dup
|
62
|
+
params << @_vd_obj_opts.merge(args.extract_options!)
|
63
|
+
|
64
|
+
begin
|
65
|
+
@_vd_obj.__send__(:#{ method }, *params)
|
66
|
+
rescue Exception
|
67
|
+
$@.delete_if { |s| /^\\(eval\\):/ =~ s }
|
68
|
+
raise
|
69
|
+
end
|
70
|
+
end
|
71
|
+
EOS
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|