validatable 1.2.1 → 1.2.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/README CHANGED
@@ -64,6 +64,20 @@ Validations can be given levels. If a validation fails on a level the validation
64
64
  person.errors.on(:name) #=> "name message"
65
65
  person.errors.on(:address) #=> nil
66
66
 
67
+ Validations can also be given groups. Groups can be used to validate an object when it can be valid in various states. For example a mortgage application may be valid for saving (saving a partial application), but that same mortgage application would not be valid for underwriting. In our example a application can be saved as long as a Social Security Number is present; however, an application can not be underwritten unless the name attribute contains a value.
68
+
69
+ class MortgageApplication
70
+ include Validatable
71
+ validates_presence_of :ssn, :groups => [:saving, :underwriting]
72
+ validates_presence_of :name, :groups => :underwriting
73
+ attr_accessor :name, :ssn
74
+ end
75
+
76
+ application = MortgageApplication.new
77
+ application.ssn = 377990118
78
+ application.valid_for_saving? #=> true
79
+ application.valid_for_underwriting? #=> false
80
+
67
81
  Similar to Rails, Validatable also supports conditional validation.
68
82
 
69
83
  class Person
@@ -101,8 +115,10 @@ See the tests for more examples
101
115
  == Contributors
102
116
  Rick Bradley (Revision 25)
103
117
 
104
- Zak Tamsen (Revision 29)
118
+ Anonymous Z (Revision 29)
105
119
 
106
120
  Jason Miller (Revision 31)
107
121
 
108
- Ali Aghareza (Revision 45)
122
+ Ali Aghareza (Revision 43)
123
+
124
+ Xavier Shay (Revision 48 & 49)
@@ -0,0 +1,25 @@
1
+ module Validatable
2
+ module Understandable
3
+ def understands(*args)
4
+ understandings.concat args
5
+ end
6
+
7
+ def understandings
8
+ @understandings ||= []
9
+ end
10
+
11
+ def all_understandings
12
+ return understandings + self.superclass.understandings if self.superclass.respond_to? :understandings
13
+ understandings
14
+ end
15
+
16
+ def must_understand(hash)
17
+ invalid_options = hash.inject([]) do |errors, (key, value)|
18
+ errors << key.to_s unless all_understandings.include?(key)
19
+ errors
20
+ end
21
+ raise ArgumentError.new("invalid options: #{invalid_options.join(', ')}") if invalid_options.any?
22
+ true
23
+ end
24
+ end
25
+ end
data/lib/validatable.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require 'forwardable'
2
2
  require File.expand_path(File.dirname(__FILE__) + '/errors')
3
- require File.expand_path(File.dirname(__FILE__) + '/base')
3
+ require File.expand_path(File.dirname(__FILE__) + '/validatable_class_methods')
4
+ require File.expand_path(File.dirname(__FILE__) + '/validatable_instance_methods')
4
5
  require File.expand_path(File.dirname(__FILE__) + '/child_validation')
6
+ require File.expand_path(File.dirname(__FILE__) + '/understandable')
5
7
  require File.expand_path(File.dirname(__FILE__) + '/validations/validation_base')
6
8
  require File.expand_path(File.dirname(__FILE__) + '/validations/validates_format_of')
7
9
  require File.expand_path(File.dirname(__FILE__) + '/validations/validates_presence_of')
@@ -48,6 +48,7 @@ module Validatable
48
48
  add_validations(args, ValidatesLengthOf) do |validation, options|
49
49
  validation.minimum = options[:minimum]
50
50
  validation.maximum = options[:maximum]
51
+ validation.is = options[:is]
51
52
  end
52
53
  end
53
54
 
@@ -213,7 +214,7 @@ module Validatable
213
214
  instance.errors.add(validation.attribute, validation.message) unless validation.valid?(instance)
214
215
  end
215
216
  end
216
- return unless instance.errors.empty?
217
+ return if instance.errors.any?
217
218
  end
218
219
  end
219
220
 
@@ -236,72 +237,26 @@ module Validatable
236
237
  def add_validations(args, klass) #:nodoc:
237
238
  options = args.last.is_a?(Hash) ? args.pop : {}
238
239
  args.each do |attribute|
239
- new_validation = klass.new(attribute, options)
240
+ klass.must_understand options
241
+ new_validation = klass.new attribute, options
240
242
  yield new_validation, options if block_given?
241
243
  self.validations << new_validation
244
+ self.create_valid_method_for_groups new_validation.groups
242
245
  end
243
246
  end
244
247
 
245
- def children_to_validate #:nodoc:
246
- @children_to_validate ||= []
247
- end
248
- end
249
-
250
- def self.included(klass) #:nodoc:
251
- klass.extend Validatable::ClassMethods
252
- end
253
-
254
- # call-seq: valid?
255
- #
256
- # Returns true if no errors were added otherwise false.
257
- def valid?(*groups)
258
- errors.clear
259
- self.class.validate_children(self, groups)
260
- self.validate(groups)
261
- errors.empty?
262
- end
263
-
264
- # call-seq: errors
265
- #
266
- # Returns the Errors object that holds all information about attribute error messages.
267
- def errors
268
- @errors ||= Validatable::Errors.new
269
- end
270
-
271
- protected
272
- def validate(groups) #:nodoc:
273
- validations_for_groups(groups).each do |validation|
274
- validation_levels.sort.each do |level|
275
- validations_for_level(level).each do |validation|
276
- if validation.should_validate?(self)
277
- run_validation(validation)
248
+ def create_valid_method_for_groups(groups)
249
+ groups.each do |group|
250
+ self.class_eval do
251
+ define_method :"valid_for_#{group}?" do
252
+ valid_for_group?(group)
278
253
  end
279
254
  end
280
- return unless self.errors.empty?
281
255
  end
282
256
  end
283
- end
284
-
285
- def run_validation(validation) #:nodoc:
286
- validation_result = validation.valid?(self)
287
- self.errors.add(validation.attribute, validation.message) unless validation_result
288
- validation.run_after_validate(validation_result, self, validation.attribute)
289
- end
290
-
291
- def validation_levels #:nodoc:
292
- self.validations.collect { |validation| validation.level }.uniq
293
- end
294
-
295
- def validations_for_level(level) #:nodoc:
296
- self.validations.select { |validation| validation.level == level }
297
- end
298
-
299
- def validations_for_groups(groups) #:nodoc:
300
- return self.validations if groups.empty?
301
- self.validations.select { |validation| (groups & validation.groups).any? }
302
- end
303
-
304
- def validations #:nodoc:
305
- @validations ||= self.class.validations.collect { |validation| validation.dup }
257
+
258
+ def children_to_validate #:nodoc:
259
+ @children_to_validate ||= []
260
+ end
306
261
  end
307
262
  end
@@ -0,0 +1,56 @@
1
+ module Validatable
2
+ def self.included(klass) #:nodoc:
3
+ klass.extend Validatable::ClassMethods
4
+ end
5
+
6
+ # call-seq: valid?
7
+ #
8
+ # Returns true if no errors were added otherwise false.
9
+ def valid?
10
+ valid_for_group?
11
+ end
12
+
13
+ # call-seq: errors
14
+ #
15
+ # Returns the Errors object that holds all information about attribute error messages.
16
+ def errors
17
+ @errors ||= Validatable::Errors.new
18
+ end
19
+
20
+ protected
21
+ def valid_for_group?(*groups)
22
+ errors.clear
23
+ self.class.validate_children(self, groups)
24
+ self.validate(groups)
25
+ errors.empty?
26
+ end
27
+
28
+ def validate(groups) #:nodoc:
29
+ validation_levels.each do |level|
30
+ validations_for_level_and_groups(level, groups).each do |validation|
31
+ run_validation(validation) if validation.should_validate?(self)
32
+ end
33
+ return unless self.errors.empty?
34
+ end
35
+ end
36
+
37
+ def run_validation(validation) #:nodoc:
38
+ validation_result = validation.valid?(self)
39
+ self.errors.add(validation.attribute, validation.message) unless validation_result
40
+ validation.run_after_validate(validation_result, self, validation.attribute)
41
+ end
42
+
43
+ def validations_for_level_and_groups(level, groups)
44
+ validations_for_level = self.validations.select { |validation| validation.level == level }
45
+ return validations_for_level if groups.empty?
46
+ validations_for_level.select { |validation| (groups & validation.groups).any? }
47
+ end
48
+
49
+ def validation_levels #:nodoc:
50
+ self.validations.collect { |validation| validation.level }.uniq.sort
51
+ end
52
+
53
+ def validations #:nodoc:
54
+ @validations ||= self.class.validations.collect { |validation| validation.dup }
55
+ end
56
+ end
@@ -1,6 +1,7 @@
1
1
  module Validatable
2
2
  class ValidatesConfirmationOf < ValidationBase #:nodoc:
3
3
  attr_accessor :case_sensitive
4
+ understands :case_sensitive
4
5
 
5
6
  def valid?(instance)
6
7
  return instance.send(self.attribute) == instance.send("#{self.attribute}_confirmation".to_sym) if case_sensitive
@@ -1,9 +1,10 @@
1
1
  module Validatable
2
2
  class ValidatesFormatOf < ValidationBase #:nodoc:
3
3
  attr_accessor :with
4
+ understands :with
4
5
 
5
6
  def valid?(instance)
6
- instance.send(self.attribute) =~ self.with && true
7
+ not (instance.send(self.attribute).to_s =~ self.with).nil?
7
8
  end
8
9
 
9
10
  def message
@@ -1,6 +1,7 @@
1
1
  module Validatable
2
2
  class ValidatesLengthOf < ValidationBase #:nodoc:
3
- attr_accessor :minimum, :maximum
3
+ attr_accessor :minimum, :maximum, :is
4
+ understands :minimum, :maximum, :is
4
5
 
5
6
  def message
6
7
  super || "is invalid"
@@ -11,6 +12,7 @@ module Validatable
11
12
  value = instance.send(self.attribute) || ""
12
13
  valid &&= value.length <= maximum unless maximum.nil?
13
14
  valid &&= value.length >= minimum unless minimum.nil?
15
+ valid &&= value.length == is unless is.nil?
14
16
  valid
15
17
  end
16
18
  end
@@ -1,6 +1,7 @@
1
1
  module Validatable
2
2
  class ValidatesTrueFor < ValidationBase #:nodoc:
3
3
  attr_accessor :logic
4
+ understands :logic
4
5
 
5
6
  def valid?(instance)
6
7
  instance.instance_eval(&logic) == true
@@ -1,5 +1,8 @@
1
1
  module Validatable
2
2
  class ValidationBase #:nodoc:
3
+ extend Understandable
4
+ understands :message, :if, :times, :level, :groups
5
+
3
6
  attr_accessor :attribute, :message, :times
4
7
  attr_reader :level, :groups
5
8
 
@@ -9,7 +12,11 @@ module Validatable
9
12
  @conditional = options[:if] || Proc.new { true }
10
13
  @times = options[:times]
11
14
  @level = options[:level] || 1
12
- @groups = options[:groups].is_a?(Array) ? options[:groups] : [options[:groups]]
15
+ @groups = case options[:groups]
16
+ when nil then []
17
+ when Array then options[:groups]
18
+ else [options[:groups]]
19
+ end
13
20
  end
14
21
 
15
22
  def should_validate?(instance)
data/rakefile.rb CHANGED
@@ -29,7 +29,7 @@ Gem::manage_gems
29
29
  specification = Gem::Specification.new do |s|
30
30
  s.name = "validatable"
31
31
  s.summary = "Validatable is a library for adding validations."
32
- s.version = "1.2.1"
32
+ s.version = "1.2.2"
33
33
  s.author = 'Jay Fields'
34
34
  s.description = "Validatable is a library for adding validations."
35
35
  s.email = 'validatable-developer@rubyforge.org'
@@ -2,6 +2,19 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
3
  module Functional
4
4
  class ValidatableTest < Test::Unit::TestCase
5
+ test "validations are only executed once" do
6
+ if_condition = mock
7
+ if_condition.expects(:where?).times 2
8
+ klass = Class.new do
9
+ include Validatable
10
+ attr_accessor :name, :address
11
+ validates_presence_of :name, :if => lambda { if_condition.where? }
12
+ validates_presence_of :address, :if => lambda { if_condition.where? }
13
+ end
14
+ instance = klass.new
15
+ instance.valid?
16
+ end
17
+
5
18
  test "given a child class with validations, when parent class is validated, then the error is in the parent objects error collection" do
6
19
  child_class = Class.new do
7
20
  include Validatable
@@ -75,14 +88,30 @@ module Functional
75
88
  assert_equal true, instance.valid?
76
89
  end
77
90
 
91
+ test "classes only have valid_for_* methods for groups that appear in their validations" do
92
+ class_with_group_one = Class.new do
93
+ include Validatable
94
+ validates_presence_of :name, :groups => :group_one
95
+ attr_accessor :name
96
+ end
97
+ class_with_group_two = Class.new do
98
+ include Validatable
99
+ validates_presence_of :name, :groups => :group_two
100
+ attr_accessor :name
101
+ end
102
+ assert_equal false, class_with_group_one.public_instance_methods.include?(:valid_for_group_two?)
103
+ assert_equal false, class_with_group_two.public_instance_methods.include?(:valid_for_group_one?)
104
+ end
105
+
78
106
  test "nonmatching groups are not used as validations" do
79
107
  klass = Class.new do
80
108
  include Validatable
81
109
  validates_presence_of :name, :groups => :group_one
82
- attr_accessor :name
110
+ validates_presence_of :address, :groups => :group_two
111
+ attr_accessor :name, :address
83
112
  end
84
113
  instance = klass.new
85
- assert_equal true, instance.valid?(:group_two)
114
+ assert_equal nil, instance.errors.on(:name)
86
115
  end
87
116
 
88
117
  test "after validate is called following a validation" do
@@ -116,7 +145,7 @@ module Functional
116
145
  attr_accessor :name
117
146
  end
118
147
  instance = klass.new
119
- assert_equal false, instance.valid?(:group_one)
148
+ assert_equal false, instance.valid_for_group_one?
120
149
  end
121
150
 
122
151
  test "matching groups are used as validations when validations are part of multiple groups" do
@@ -126,7 +155,7 @@ module Functional
126
155
  attr_accessor :name
127
156
  end
128
157
  instance = klass.new
129
- assert_equal false, instance.valid?(:group_one)
158
+ assert_equal false, instance.valid_for_group_one?
130
159
  end
131
160
 
132
161
  test "no group given then all validations are used" do
@@ -139,18 +168,6 @@ module Functional
139
168
  assert_equal false, instance.valid?
140
169
  end
141
170
 
142
- test "matching multiple groups for validations" do
143
- klass = Class.new do
144
- include Validatable
145
- validates_presence_of :name, :groups => :group_one
146
- validates_presence_of :address, :groups => :group_two
147
- attr_accessor :name, :address
148
- end
149
- instance = klass.new
150
- instance.valid?(:group_one, :group_two)
151
- assert_equal 2, instance.errors.size
152
- end
153
-
154
171
  expect true do
155
172
  klass = Class.new do
156
173
  include Validatable
@@ -12,5 +12,27 @@ module Functional
12
12
  instance.valid?
13
13
  assert_equal "is invalid", instance.errors.on(:name)
14
14
  end
15
+
16
+ test "given exact value, when validated, then error is in the objects error collection" do
17
+ klass = Class.new do
18
+ include Validatable
19
+ attr_accessor :name
20
+ validates_length_of :name, :is => 2
21
+ end
22
+ instance = klass.new
23
+ instance.valid?
24
+ assert_equal "is invalid", instance.errors.on(:name)
25
+ end
26
+
27
+ test "given exact value is met, when validated, then valid is true" do
28
+ klass = Class.new do
29
+ include Validatable
30
+ attr_accessor :name
31
+ validates_length_of :name, :is => 2
32
+ end
33
+ instance = klass.new
34
+ instance.name = "bk"
35
+ assert_equal true, instance.valid?
36
+ end
15
37
  end
16
38
  end
@@ -21,7 +21,7 @@ module Unit
21
21
  instance.expects(:errors).returns(errors=mock).times 3
22
22
  errors.expects(:add).with('attribute', 'message')
23
23
  errors.expects(:add).with('attribute2', 'message2')
24
- errors.expects(:empty?).returns true
24
+ errors.expects(:any?).returns false
25
25
  klass.validate(instance)
26
26
  end
27
27
 
@@ -12,4 +12,9 @@ class ValidatesAcceptanceOfTest < Test::Unit::TestCase
12
12
  instance = stub(:acceptance=>'false')
13
13
  assert_equal false, validation.valid?(instance)
14
14
  end
15
+
16
+ expect true do
17
+ Validatable::ValidatesAcceptanceOf.must_understand(:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil)
18
+ end
19
+
15
20
  end
@@ -62,4 +62,10 @@ class ValidatesConfirmationOfTest < Test::Unit::TestCase
62
62
  validation.case_sensitive = false
63
63
  assert_equal true, validation.valid?(stub(:username => nil, :username_confirmation => nil))
64
64
  end
65
+
66
+ expect true do
67
+ options = { :message => nil, :if => nil, :times => nil, :level => nil, :groups => nil, :case_sensitive => nil }
68
+ Validatable::ValidatesConfirmationOf.must_understand(options)
69
+ end
70
+
65
71
  end
@@ -12,4 +12,15 @@ class ValidatesFormatOfTest < Test::Unit::TestCase
12
12
  validation.with = /book/
13
13
  assert_equal true, validation.valid?(stub(:name=>"book"))
14
14
  end
15
+
16
+ test "when attribute value is an integer it should be converted to a string before matching" do
17
+ validation = Validatable::ValidatesFormatOf.new :age
18
+ validation.with = /14/
19
+ assert_equal true, validation.valid?(stub(:age=>14))
20
+ end
21
+
22
+ expect true do
23
+ Validatable::ValidatesFormatOf.must_understand(:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil, :with => nil)
24
+ end
25
+
15
26
  end
@@ -17,6 +17,20 @@ module Unit
17
17
  assert_equal false, validation.valid?(instance)
18
18
  end
19
19
 
20
+ test "is length is false" do
21
+ validation = Validatable::ValidatesLengthOf.new :username
22
+ validation.is = 2
23
+ instance = stub(:username=>"u")
24
+ assert_equal false, validation.valid?(instance)
25
+ end
26
+
27
+ test "is length is true" do
28
+ validation = Validatable::ValidatesLengthOf.new :username
29
+ validation.is = 2
30
+ instance = stub(:username=>"uu")
31
+ assert_equal true, validation.valid?(instance)
32
+ end
33
+
20
34
  test "valid length" do
21
35
  validation = Validatable::ValidatesLengthOf.new :username
22
36
  validation.minimum = 2
@@ -24,5 +38,11 @@ module Unit
24
38
  instance = stub(:username=>"udfgdf")
25
39
  assert_equal true, validation.valid?(instance)
26
40
  end
41
+
42
+ expect true do
43
+ options = {:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil, :maximum => nil, :minimum => nil, :is => nil}
44
+ Validatable::ValidatesLengthOf.must_understand(options)
45
+ end
46
+
27
47
  end
28
48
  end
@@ -32,5 +32,10 @@ module Unit
32
32
  instance = stub(:multiple_dots => "50.0.0")
33
33
  assert_equal false, validation.valid?(instance)
34
34
  end
35
+
36
+ expect true do
37
+ Validatable::ValidatesNumericalityOf.must_understand(:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil)
38
+ end
39
+
35
40
  end
36
41
  end
@@ -16,4 +16,8 @@ class ValidatesPresenceOfTest < Test::Unit::TestCase
16
16
  assert_equal true, validation.valid?(stub(:employee => stub(:nil? => false)))
17
17
  end
18
18
 
19
+ expect true do
20
+ Validatable::ValidatesPresenceOf.must_understand(:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil)
21
+ end
22
+
19
23
  end
@@ -13,4 +13,8 @@ class ValidatesTrueForTest < Test::Unit::TestCase
13
13
  assert_equal true, validation.valid?(stub_everything)
14
14
  end
15
15
 
16
+ expect true do
17
+ Validatable::ValidatesTrueFor.must_understand(:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil, :logic => nil)
18
+ end
19
+
16
20
  end
@@ -32,4 +32,15 @@ class ValidationBaseTest < Test::Unit::TestCase
32
32
  validation = Validatable::ValidationBase.new :base
33
33
  validation.level
34
34
  end
35
+
36
+ test "invalid option causes raise" do
37
+ assert_raises ArgumentError do
38
+ Validatable::ValidationBase.must_understand(:foo => 1, :bar => 2)
39
+ end
40
+ end
41
+
42
+ expect true do
43
+ Validatable::ValidationBase.must_understand(:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil)
44
+ end
45
+
35
46
  end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.2
2
+ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: validatable
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.2.1
7
- date: 2007-04-26 00:00:00 -04:00
6
+ version: 1.2.2
7
+ date: 2007-04-27 00:00:00 -04:00
8
8
  summary: Validatable is a library for adding validations.
9
9
  require_paths:
10
10
  - lib
@@ -25,14 +25,15 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
25
25
  platform: ruby
26
26
  signing_key:
27
27
  cert_chain:
28
- post_install_message:
29
28
  authors:
30
29
  - Jay Fields
31
30
  files:
32
- - lib/base.rb
33
31
  - lib/child_validation.rb
34
32
  - lib/errors.rb
33
+ - lib/understandable.rb
35
34
  - lib/validatable.rb
35
+ - lib/validatable_class_methods.rb
36
+ - lib/validatable_instance_methods.rb
36
37
  - lib/validations/validates_acceptance_of.rb
37
38
  - lib/validations/validates_confirmation_of.rb
38
39
  - lib/validations/validates_format_of.rb