not-naughty 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,46 @@
1
+ === 0.6 (2008-10-08)
2
+ * fixed Rakefile
3
+ * removed whitespaces
4
+ * removed Ruby-Sequel adapter (becomes an extra gem)
5
+ * removed assistance gem dependency
6
+
7
+ === 0.5.1 (2008-03-17)
8
+ * fixed missing dependency
9
+
10
+ === 0.5 (2008-03-17)
11
+ * added an error handler, can now handle SQL validation errors
12
+ * renamed Errors to Violation and merged it with ValidationException
13
+ * changed specs for 0.4.1
14
+
15
+ === 0.4.2 (2008-03-12)
16
+ * fixed bug that causes infinite recursion in some apps that reload classes
17
+
18
+ === 0.4.1 (2008-03-12)
19
+ * some minor changes in validator method
20
+
21
+ === 0.4 (2008-02-18)
22
+ * completed documentation
23
+ * added spec for :each in Builder::ValidationDelegator
24
+
25
+ === 0.3.1 (2008-02-14)
26
+ * fixed missing require
27
+
28
+ === 0.3 (2008-02-09)
29
+ * renamed to NotNaughty - The Validation Framework
30
+ * made predefined Validation optional, you can load them with:
31
+
32
+ ::NotNaughty::Validation.load '*'
33
+
34
+ or ordered (should be preferred):
35
+
36
+ ::NotNaughty::Validation.load :acceptance, :confirmation, :format, :length,
37
+ :numericality, :presence
38
+
39
+ * specs pass, 100% code coverage, 2 not complete, so pending specs
40
+ * docs added
41
+
42
+ === 0.2 (2008-02-09)
43
+ * complete rewrite
44
+
45
+ === 0.1 (2008-02-06)
46
+ * 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.
@@ -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
@@ -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.0"
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/rdoc/* 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?
@@ -0,0 +1,86 @@
1
+ require 'delegate'
2
+ require 'forwardable'
3
+ require 'observer'
4
+
5
+ require 'rubygems'
6
+ gem 'rubytree', '>= 0.5.2'
7
+ require 'tree'
8
+
9
+ $:.unshift File.dirname(__FILE__)
10
+ require 'core_extensions'
11
+
12
+ module NotNaughty
13
+ require 'not_naughty/validator'
14
+
15
+ require 'not_naughty/builder'
16
+ require 'not_naughty/validation'
17
+ Validation.add_observer Builder
18
+
19
+ require 'not_naughty/violation'
20
+ require 'not_naughty/error_handler'
21
+ require 'not_naughty/instance_methods'
22
+
23
+ # Extended classes get NotNaughty::Builder and NotNaughty::InstanceMethods.
24
+ def self.extended(base)
25
+ base.instance_eval do
26
+ include InstanceMethods
27
+ extend Builder
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,68 @@
1
+ module NotNaughty
2
+
3
+ # == Builder that builds
4
+ #
5
+ # With this you get syntactical sugar for all descendants of Validation, see
6
+ # validates for examples.
7
+ module Builder
8
+
9
+ # Observer method that creates Validation builder methods.
10
+ #
11
+ # A Validation with the class name TestValidation will get the
12
+ # builder method <tt>validate_test_of</tt> that adds a validation via
13
+ # add_validation in the current <tt>validator</tt>.
14
+ def self.update(validation)
15
+ if basename = validation.name[/([^:]+)Validation$/, 1]
16
+ define_method 'validates_%s_of' % basename.downcase do |*params|
17
+ validator.add_validation(validation, *params)
18
+ end
19
+ end
20
+ end
21
+
22
+ # == Syntactic sugar.
23
+ #
24
+ # <b>Example:</b>
25
+ # validates { presence_of :example, :if => :reading? }
26
+ # # => validate_presence_of :example, :if => :reading?
27
+ # validates(:name) { presence and length :minimum => 6 }
28
+ # # => validates_presence_of :name
29
+ # validates_length_of :name, :minimum => 6
30
+ # validates(:temp, :if => water?) { value :lt => 100 }
31
+ # # => validates_value_of :temp, :lt => 100, :if => water?
32
+ def validates(*params, &block)
33
+ ValidationDelegator.new(self, *params).instance_eval(&block)
34
+ end
35
+
36
+ # Allows adding validations the legacy way.
37
+ def validates_each(*attributes, &block)
38
+ validator.add_validation(*attributes, &block)
39
+ end
40
+
41
+ class ValidationDelegator < SimpleDelegator #:nodoc:all
42
+ def initialize(receiver, *params)
43
+ @_sd_obj_opts = params.extract_options!
44
+ @_sd_obj_params = params
45
+
46
+ super receiver
47
+ end
48
+ def method_missing(method_sym, *params) #:nodoc:
49
+ method_sym, params = unless @_sd_obj_params.empty?
50
+ opts = @_sd_obj_opts.update params.extract_options!
51
+ [:"validates_#{method_sym}_of", @_sd_obj_params + [opts]]
52
+ else
53
+ opts = @_sd_obj_opts.update params.extract_options!
54
+ [:"validates_#{method_sym}", params + [opts]]
55
+ end
56
+
57
+ if @_sd_obj.respond_to? method_sym
58
+ @_sd_obj.send(method_sym, *params)
59
+ return true
60
+ else
61
+ raise NoMethodError,
62
+ "unable to evaluate ´#{method_sym}(*#{params.inspect})'"
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -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