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.
- 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
|