validatable2 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -17,7 +17,7 @@ Rake::TestTask.new(:test) do |test|
17
17
  test.libs << 'test'
18
18
  test.ruby_opts << '-rubygems'
19
19
  test.pattern = 'test/**/test_*.rb'
20
- # test.pattern = 'test/**/unit/test_validates_true_for.rb'
20
+ # test.pattern = 'test/**/unit/test_validates_true_for.rb'
21
21
  test.verbose = true
22
22
  end
23
23
 
data/lib/validatable.rb CHANGED
@@ -1,14 +1,8 @@
1
1
  # gem 'activesupport', '2.3.5'
2
2
 
3
- require 'validatable/object_extension'
4
3
  require 'validatable/errors'
5
- require 'validatable/validatable_class_methods'
6
4
  require 'validatable/macros'
7
- require 'validatable/validatable_instance_methods'
8
- require 'validatable/included_validation'
9
- require 'validatable/child_validation'
10
- require 'validatable/understandable'
11
- require 'validatable/requireable'
5
+ require 'validatable/validatable'
12
6
  require 'validatable/validations/validation_base'
13
7
  require 'validatable/validations/validates_format_of'
14
8
  require 'validatable/validations/validates_presence_of'
@@ -1,111 +1,7 @@
1
1
  module Validatable
2
- class Errors
3
- # extend Forwardable
4
- include Enumerable
5
-
6
- %w(clear each each_pair empty? length size).each do |m|
7
- define_method m do |*a, &b|
8
- errors.send(m, *a, &b)
9
- end
10
- end
11
- # def_delegators :errors, :clear, :each, :each_pair, :empty?, :length, :size
12
-
13
- # Returns true if the specified +attribute+ has errors associated with it.
14
- #
15
- # class Company < ActiveRecord::Base
16
- # validates_presence_of :name, :address, :email
17
- # validates_length_of :name, :in => 5..30
18
- # end
19
- #
20
- # company = Company.create(:address => '123 First St.')
21
- # company.errors.invalid?(:name) # => true
22
- # company.errors.invalid?(:address) # => false
23
- def invalid?(attribute)
24
- !@errors[attribute.to_sym].nil?
25
- end
26
-
27
- # Adds an error to the base object instead of any particular attribute. This is used
28
- # to report errors that don't tie to any specific attribute, but rather to the object
29
- # as a whole. These error messages don't get prepended with any field name when iterating
30
- # with +each_full+, so they should be complete sentences.
31
- def add_to_base(msg)
32
- add(:base, msg)
33
- end
34
-
35
- # Returns errors assigned to the base object through +add_to_base+ according to the normal rules of <tt>on(attribute)</tt>.
36
- def on_base
37
- on(:base)
38
- end
39
-
40
- # call-seq: on(attribute)
41
- #
42
- # * Returns nil, if no errors are associated with the specified +attribute+.
43
- # * Returns the error message, if one error is associated with the specified +attribute+.
44
- # * Returns an array of error messages, if more than one error is associated with the specified +attribute+.
45
- def on(attribute)
46
- return nil if errors[attribute.to_sym].nil?
47
- errors[attribute.to_sym].size == 1 ? errors[attribute.to_sym].first : errors[attribute.to_sym]
48
- end
49
-
50
- # Rails 3 API for errors, always return array.
51
- def [](attribute)
52
- errors[attribute.to_sym] || []
53
- end
54
-
55
- def add(attribute, message) #:nodoc:
56
- errors[attribute.to_sym] = [] if errors[attribute.to_sym].nil?
57
- errors[attribute.to_sym] << message
58
- end
59
-
60
- def merge!(errors) #:nodoc:
61
- errors.each_pair{|k, v| add(k,v)}
62
- self
63
- end
64
-
65
- # call-seq: replace(attribute)
66
- #
67
- # * Replaces the errors value for the given +attribute+
68
- def replace(attribute, value)
69
- errors[attribute.to_sym] = value
70
- end
71
-
72
- # call-seq: raw(attribute)
73
- #
74
- # * Returns an array of error messages associated with the specified +attribute+.
75
- def raw(attribute)
76
- errors[attribute.to_sym]
77
- end
78
-
79
- def errors #:nodoc:
80
- @errors ||= {}
81
- end
82
-
83
- def count #:nodoc:
84
- errors.values.flatten.size
85
- end
86
-
87
- # call-seq: full_messages -> an_array_of_messages
88
- #
89
- # Returns an array containing the full list of error messages.
90
- def full_messages
91
- full_messages = []
92
-
93
- errors.each_key do |attribute|
94
- errors[attribute].each do |msg|
95
- next if msg.nil?
96
-
97
- if attribute.to_s == "base"
98
- full_messages << msg
99
- else
100
- full_messages << humanize(attribute.to_s) + " " + msg
101
- end
102
- end
103
- end
104
- full_messages
105
- end
106
-
107
- def humanize(lower_case_and_underscored_word) #:nodoc:
108
- lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
2
+ class Errors < Hash
3
+ def add attribute, message
4
+ (self[attribute] ||= []) << message
109
5
  end
110
6
  end
111
7
  end
@@ -1,9 +1,9 @@
1
1
  module Validatable
2
2
  module Macros
3
3
  # call-seq: validates_each(*args)
4
- #
4
+ #
5
5
  # Validates that the logic evaluates to true
6
- #
6
+ #
7
7
  # class Address
8
8
  # include Validatable
9
9
  # validates_each :zip_code, :logic => lambda { errors.add(:zip_code, "is not valid") if ZipCodeService.allows(zip_code) }
@@ -12,7 +12,7 @@ module Validatable
12
12
  # The logic option is required.
13
13
  #
14
14
  # Configuration options:
15
- #
15
+ #
16
16
  # * after_validate - A block that executes following the run of a validation
17
17
  # * group - The group that this validation belongs to. A validation can belong to multiple groups
18
18
  # * if - A block that when executed must return true of the validation will not occur
@@ -23,21 +23,21 @@ module Validatable
23
23
  def validates_each(*args)
24
24
  add_validations(args, ValidatesEach)
25
25
  end
26
-
26
+
27
27
  # call-seq: validates_format_of(*args)
28
- #
29
- # Validates whether the value of the specified attribute is of the
28
+ #
29
+ # Validates whether the value of the specified attribute is of the
30
30
  # correct form by matching it against the regular expression provided.
31
- #
31
+ #
32
32
  # class Person
33
- # include Validatable
33
+ # include Validatable
34
34
  # validates_format_of :first_name, :with => /[ A-Za-z]/
35
35
  # end
36
- #
36
+ #
37
37
  # A regular expression must be provided or else an exception will be raised.
38
- #
38
+ #
39
39
  # Configuration options:
40
- #
40
+ #
41
41
  # * after_validate - A block that executes following the run of a validation
42
42
  # * message - The message to add to the errors collection when the validation fails
43
43
  # * times - The number of times the validation applies
@@ -48,19 +48,19 @@ module Validatable
48
48
  def validates_format_of(*args)
49
49
  add_validations(args, ValidatesFormatOf)
50
50
  end
51
-
51
+
52
52
  # call-seq: validates_length_of(*args)
53
- #
53
+ #
54
54
  # Validates that the specified attribute matches the length restrictions supplied.
55
- #
55
+ #
56
56
  # class Person
57
57
  # include Validatable
58
58
  # validates_length_of :first_name, :maximum=>30
59
59
  # validates_length_of :last_name, :minimum=>30
60
60
  # end
61
- #
61
+ #
62
62
  # Configuration options:
63
- #
63
+ #
64
64
  # * after_validate - A block that executes following the run of a validation
65
65
  # * message - The message to add to the errors collection when the validation fails
66
66
  # * times - The number of times the validation applies
@@ -76,16 +76,16 @@ module Validatable
76
76
  end
77
77
 
78
78
  # call-seq: validates_numericality_of(*args)
79
- #
79
+ #
80
80
  # Validates that the specified attribute is numeric.
81
- #
81
+ #
82
82
  # class Person
83
83
  # include Validatable
84
84
  # validates_numericality_of :age
85
85
  # end
86
- #
86
+ #
87
87
  # Configuration options:
88
- #
88
+ #
89
89
  # * after_validate - A block that executes following the run of a validation
90
90
  # * message - The message to add to the errors collection when the validation fails
91
91
  # * times - The number of times the validation applies
@@ -100,7 +100,7 @@ module Validatable
100
100
  # call-seq: validates_acceptance_of(*args)
101
101
  #
102
102
  # Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
103
- #
103
+ #
104
104
  # class Person
105
105
  # include Validatable
106
106
  # validates_acceptance_of :terms_of_service
@@ -108,7 +108,7 @@ module Validatable
108
108
  # end
109
109
  #
110
110
  # Configuration options:
111
- #
111
+ #
112
112
  # * after_validate - A block that executes following the run of a validation
113
113
  # * message - The message to add to the errors collection when the validation fails
114
114
  # * times - The number of times the validation applies
@@ -122,20 +122,20 @@ module Validatable
122
122
  # call-seq: validates_confirmation_of(*args)
123
123
  #
124
124
  # Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
125
- #
125
+ #
126
126
  # Class:
127
127
  # class PersonPresenter
128
128
  # include Validatable
129
129
  # validates_confirmation_of :user_name, :password
130
130
  # validates_confirmation_of :email_address, :message => "should match confirmation"
131
131
  # end
132
- #
132
+ #
133
133
  # View:
134
134
  # <%= password_field "person", "password" %>
135
135
  # <%= password_field "person", "password_confirmation" %>
136
136
  #
137
137
  # Configuration options:
138
- #
138
+ #
139
139
  # * after_validate - A block that executes following the run of a validation
140
140
  # * case_sensitive - Whether or not to apply case-sensitivity on the comparison. Defaults to true.
141
141
  # * message - The message to add to the errors collection when the validation fails
@@ -146,11 +146,11 @@ module Validatable
146
146
  def validates_confirmation_of(*args)
147
147
  add_validations(args, ValidatesConfirmationOf)
148
148
  end
149
-
149
+
150
150
  # call-seq: validates_presence_of(*args)
151
- #
151
+ #
152
152
  # Validates that the specified attributes are not nil or an empty string
153
- #
153
+ #
154
154
  # class Person
155
155
  # include Validatable
156
156
  # validates_presence_of :first_name
@@ -159,7 +159,7 @@ module Validatable
159
159
  # The first_name attribute must be in the object and it cannot be nil or empty.
160
160
  #
161
161
  # Configuration options:
162
- #
162
+ #
163
163
  # * after_validate - A block that executes following the run of a validation
164
164
  # * message - The message to add to the errors collection when the validation fails
165
165
  # * times - The number of times the validation applies
@@ -169,11 +169,11 @@ module Validatable
169
169
  def validates_presence_of(*args)
170
170
  add_validations(args, ValidatesPresenceOf)
171
171
  end
172
-
172
+
173
173
  # call-seq: validates_true_for(*args)
174
- #
174
+ #
175
175
  # Validates that the logic evaluates to true
176
- #
176
+ #
177
177
  # class Person
178
178
  # include Validatable
179
179
  # validates_true_for :first_name, :logic => lambda { first_name == 'Jamie' }
@@ -182,7 +182,7 @@ module Validatable
182
182
  # The logic option is required.
183
183
  #
184
184
  # Configuration options:
185
- #
185
+ #
186
186
  # * after_validate - A block that executes following the run of a validation
187
187
  # * message - The message to add to the errors collection when the validation fails
188
188
  # * times - The number of times the validation applies
@@ -193,7 +193,7 @@ module Validatable
193
193
  def validates_true_for(*args)
194
194
  add_validations(args, ValidatesTrueFor)
195
195
  end
196
-
196
+
197
197
  def validates_exclusion_of(*args)
198
198
  add_validations(args, ValidatesExclusionOf)
199
199
  end
@@ -226,13 +226,13 @@ module Validatable
226
226
  end
227
227
 
228
228
  # call-seq: include_validations_from(attribute)
229
- #
229
+ #
230
230
  # Includes all the validations that are defined on the attribute.
231
231
  # class Person
232
232
  # include Validatable
233
233
  # validates_presence_of :name
234
234
  # end
235
- #
235
+ #
236
236
  # class PersonPresenter
237
237
  # include Validatable
238
238
  # include_validataions_from :person
@@ -245,70 +245,65 @@ module Validatable
245
245
  # @person = person
246
246
  # end
247
247
  # end
248
- #
248
+ #
249
249
  # presenter = PersonPresenter.new(Person.new)
250
250
  # presenter.valid? #=> false
251
251
  # presenter.errors.on(:name) #=> "can't be blank"
252
252
  #
253
253
  # The name attribute whose validations should be added.
254
- def include_validations_from(attribute_to_validate, options = {})
255
- validations_to_include << IncludedValidation.new(attribute_to_validate)
256
- end
254
+ # def include_validations_from(attribute_to_validate, options = {})
255
+ # validations_to_include << IncludedValidation.new(attribute_to_validate)
256
+ # end
257
257
 
258
258
  # call-seq: include_errors_from(attribute_to_validate, options = {})
259
- #
259
+ #
260
260
  # Validates the specified attributes.
261
261
  # class Person
262
262
  # include Validatable
263
263
  # validates_presence_of :name
264
264
  # attr_accessor :name
265
265
  # end
266
- #
266
+ #
267
267
  # class PersonPresenter
268
268
  # include Validatable
269
269
  # include_errors_from :person, :map => { :name => :namen }, :if => lambda { not person.nil? }
270
270
  # attr_accessor :person
271
- #
271
+ #
272
272
  # def initialize(person)
273
273
  # @person = person
274
274
  # end
275
275
  # end
276
- #
276
+ #
277
277
  # presenter = PersonPresenter.new(Person.new)
278
278
  # presenter.valid? #=> false
279
279
  # presenter.errors.on(:namen) #=> "can't be blank"
280
280
  #
281
- # The person attribute will be validated.
281
+ # The person attribute will be validated.
282
282
  # If person is invalid the errors will be added to the PersonPresenter errors collection.
283
283
  #
284
284
  # Configuration options:
285
- #
285
+ #
286
286
  # * map - A hash that maps attributes of the child to attributes of the parent.
287
287
  # * if - A block that when executed must return true of the validation will not occur.
288
- def include_errors_from(attribute_to_validate, options = {})
289
- children_to_validate << ChildValidation.new(attribute_to_validate, options[:map] || {}, options[:if] || lambda {|_self| true })
290
- end
291
-
292
- def include_validations_for(attribute_to_validate, options = {}) #:nodoc:
293
- puts "include_validations_for is deprecated; use include_errors_from instead"
294
- children_to_validate << ChildValidation.new(attribute_to_validate, options[:map] || {}, options[:if] || lambda {|_self| true })
295
- end
296
-
288
+ # def include_errors_from(attribute_to_validate, options = {})
289
+ # children_to_validate << ChildValidation.new(attribute_to_validate, options[:map] || {}, options[:if] || lambda {|_self| true })
290
+ # end
291
+
297
292
  # call-seq: before_validation(&block)
298
- #
293
+ #
299
294
  # Is called before valid? or valid_for_*?
300
- #
295
+ #
301
296
  # class Person
302
297
  # include Validatable
303
298
  # before_validation do
304
299
  # self.name = "default name"
305
300
  # end
306
- #
301
+ #
307
302
  # attr_accessor :name
308
303
  # end
309
- #
310
- def before_validation(&block)
311
- before_validations << block
312
- end
304
+ #
305
+ # def before_validation(&block)
306
+ # before_validations << block
307
+ # end
313
308
  end
314
309
  end
@@ -0,0 +1,43 @@
1
+ module Validatable
2
+ # call-seq: valid?
3
+ #
4
+ # Returns true if no errors were added otherwise false. Only executes validations that have no :groups option specified
5
+ def valid?
6
+ errors.clear
7
+ self.class.all_validations.each do |validation|
8
+ validation.validate self
9
+ end
10
+ errors.empty?
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
+ module ClassMethods #:nodoc:
21
+ include ::Validatable::Macros
22
+
23
+ def all_validations
24
+ if self.respond_to?(:superclass) && self.superclass.respond_to?(:all_validations)
25
+ return validations + self.superclass.all_validations
26
+ end
27
+ validations
28
+ end
29
+
30
+ def validations
31
+ @validations ||= []
32
+ end
33
+
34
+ protected
35
+ def add_validations(args, klass)
36
+ options = args.last.is_a?(Hash) ? args.pop : {}
37
+ args.each do |attribute|
38
+ new_validation = klass.new self, attribute, options
39
+ self.validations << new_validation
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,12 +1,12 @@
1
1
  module Validatable
2
- class ValidatesAcceptanceOf < ValidationBase #:nodoc:
2
+ class ValidatesAcceptanceOf < ValidationBase #:nodoc:
3
3
  def valid?(instance)
4
4
  value = instance.send(self.attribute)
5
5
  return true if allow_nil && value.nil?
6
6
  return true if allow_blank && (!value or (value.respond_to?(:empty?) and value.empty?))
7
7
  %w(1 true t).include?(value)
8
8
  end
9
-
9
+
10
10
  def message(instance)
11
11
  super || "must be accepted"
12
12
  end
@@ -1,13 +1,13 @@
1
1
  module Validatable
2
2
  class ValidatesConfirmationOf < ValidationBase #:nodoc:
3
- option :case_sensitive
4
- default :case_sensitive => true
5
-
3
+ attr_accessor :case_sensitive
4
+
6
5
  def initialize(klass, attribute, options={})
7
6
  klass.class_eval { attr_accessor "#{attribute}_confirmation" }
8
7
  super
8
+ self.case_sensitive = true if self.case_sensitive == nil
9
9
  end
10
-
10
+
11
11
  def valid?(instance)
12
12
  confirmation_value = instance.send("#{self.attribute}_confirmation")
13
13
  return true if allow_nil && confirmation_value.nil?
@@ -15,7 +15,7 @@ module Validatable
15
15
  return instance.send(self.attribute) == instance.send("#{self.attribute}_confirmation".to_sym) if case_sensitive
16
16
  instance.send(self.attribute).to_s.casecmp(instance.send("#{self.attribute}_confirmation".to_sym).to_s) == 0
17
17
  end
18
-
18
+
19
19
  def message(instance)
20
20
  super || "doesn't match confirmation"
21
21
  end
@@ -1,12 +1,12 @@
1
1
  module Validatable
2
2
  class ValidatesEach < ValidationBase #:nodoc:
3
- required_option :logic
4
-
3
+ attr_accessor :logic
4
+
5
5
  def valid?(instance)
6
6
  instance.instance_eval(&logic)
7
7
  true # return true so no error is added. should look in the future at doing this different.
8
8
  end
9
-
9
+
10
10
  def message(instance)
11
11
  super || "is invalid"
12
12
  end
@@ -1,6 +1,6 @@
1
1
  module Validatable
2
2
  class ValidatesExclusionOf < ValidationBase #:nodoc:
3
- required_option :within
3
+ attr_accessor :within
4
4
 
5
5
  def valid?(instance)
6
6
  value = instance.send(attribute)
@@ -1,14 +1,14 @@
1
1
  module Validatable
2
2
  class ValidatesFormatOf < ValidationBase #:nodoc:
3
- required_option :with
4
-
3
+ attr_accessor :with
4
+
5
5
  def valid?(instance)
6
6
  value = instance.send(self.attribute)
7
7
  return true if allow_nil && value.nil?
8
8
  return true if allow_blank && (!value or (value.respond_to?(:empty?) and value.empty?))
9
9
  not (value.to_s =~ self.with).nil?
10
10
  end
11
-
11
+
12
12
  def message(instance)
13
13
  super || "is invalid"
14
14
  end
@@ -1,15 +1,15 @@
1
1
  module Validatable
2
2
  class ValidatesInclusionOf < ValidationBase
3
- required_option :within
4
-
3
+ attr_accessor :within
4
+
5
5
  def valid?(instance)
6
6
  value = instance.send(attribute)
7
7
  return true if allow_nil && value.nil?
8
8
  return true if allow_blank && (!value or (value.respond_to?(:empty?) and value.empty?))
9
-
9
+
10
10
  within.include?(value)
11
11
  end
12
-
12
+
13
13
  def message(instance)
14
14
  super || "is not in the list"
15
15
  end
@@ -1,15 +1,15 @@
1
1
  module Validatable
2
2
  class ValidatesLengthOf < ValidationBase #:nodoc:
3
- option :minimum, :maximum, :is, :within
4
-
3
+ attr_accessor :minimum, :maximum, :is, :within
4
+
5
5
  def message(instance)
6
6
  super || "is invalid"
7
7
  end
8
-
8
+
9
9
  def valid?(instance)
10
10
  valid = true
11
11
  value = instance.send(self.attribute)
12
-
12
+
13
13
  if value.nil?
14
14
  return true if allow_nil
15
15
  value = ''
@@ -19,8 +19,8 @@ module Validatable
19
19
  return true if allow_blank
20
20
  value = ''
21
21
  end
22
-
23
- valid &&= value.length <= maximum unless maximum.nil?
22
+
23
+ valid &&= value.length <= maximum unless maximum.nil?
24
24
  valid &&= value.length >= minimum unless minimum.nil?
25
25
  valid &&= value.length == is unless is.nil?
26
26
  valid &&= within.include?(value.length) unless within.nil?
@@ -1,21 +1,21 @@
1
- module Validatable
1
+ module Validatable
2
2
  class ValidatesNumericalityOf < ValidationBase #:nodoc:
3
- option :only_integer
4
-
3
+ attr_accessor :only_integer
4
+
5
5
  def valid?(instance)
6
6
  value = value_for(instance)
7
7
  return true if allow_nil && value.nil?
8
8
  return true if allow_blank && (!value or (value.respond_to?(:empty?) and value.empty?))
9
-
9
+
10
10
  value = value.to_s
11
11
  regex = self.only_integer ? /\A[+-]?\d+\Z/ : /^\d*\.{0,1}\d+$/
12
12
  not (value =~ regex).nil?
13
13
  end
14
-
14
+
15
15
  def message(instance)
16
16
  super || "must be a number"
17
17
  end
18
-
18
+
19
19
  private
20
20
  def value_for(instance)
21
21
  before_typecast_method = "#{self.attribute}_before_typecast"
@@ -1,14 +1,14 @@
1
- module Validatable
1
+ module Validatable
2
2
  class ValidatesPresenceOf < ValidationBase #:nodoc:
3
3
  def valid?(instance)
4
4
  value = instance.send(self.attribute)
5
5
  return true if allow_nil && value.nil?
6
6
  return true if allow_blank && (!value or (value.respond_to?(:empty?) and value.empty?))
7
-
7
+
8
8
  return false if instance.send(self.attribute).nil?
9
9
  value.respond_to?(:strip) ? instance.send(self.attribute).strip.length != 0 : true
10
10
  end
11
-
11
+
12
12
  def message(instance)
13
13
  super || "can't be empty"
14
14
  end
@@ -1,11 +1,11 @@
1
1
  module Validatable
2
2
  class ValidatesTrueFor < ValidationBase #:nodoc:
3
- required_option :logic
4
-
3
+ attr_accessor :logic
4
+
5
5
  def valid?(instance)
6
6
  instance.instance_eval(&logic) == true
7
7
  end
8
-
8
+
9
9
  def message(instance)
10
10
  super || "is invalid"
11
11
  end
@@ -1,92 +1,33 @@
1
1
  module Validatable
2
2
  class ValidationBase #:nodoc:
3
- def self.required_option(*args)
4
- option(*args)
5
- requires(*args)
6
- end
7
-
8
- def self.option(*args)
9
- attr_accessor(*args)
10
- understands(*args)
11
- end
12
-
13
- def self.default(hash)
14
- defaults.merge! hash
15
- end
16
-
17
- def self.defaults
18
- @defaults ||= {}
19
- end
20
-
21
- def self.all_defaults
22
- return defaults.merge(self.superclass.all_defaults) if self.superclass.respond_to? :all_defaults
23
- defaults
24
- end
25
-
26
- def self.after_validate(&block)
27
- after_validations << block
28
- end
29
-
30
- def self.after_validations
31
- @after_validations ||= []
32
- end
33
-
34
- def self.all_after_validations
35
- return after_validations + self.superclass.all_after_validations if self.superclass.respond_to? :all_after_validations
36
- after_validations
37
- end
38
-
39
- include Understandable
40
- include Requireable
41
-
42
- option :message, :if, :times, :level, :groups, :key, :after_validate, :allow_nil, :allow_blank
43
- default :level => 1, :groups => []
3
+ attr_accessor :message, :if, :after_validate, :allow_nil, :allow_blank
44
4
  attr_accessor :attribute
45
-
5
+
46
6
  def initialize(klass, attribute, options={})
47
- must_understand options
48
- requires options
49
- self.class.all_understandings.each do |understanding|
50
- options[understanding] = self.class.all_defaults[understanding] unless options.has_key? understanding
51
- self.instance_variable_set("@#{understanding}", options[understanding])
52
- end
7
+ options.each{|k, v| self.send :"#{k}=", v}
53
8
  self.attribute = attribute
54
- self.groups = [self.groups] unless self.groups.is_a?(Array)
55
- self.key = "#{klass.name}/#{self.class.name}/#{self.key || self.attribute}"
56
- raise_error_if_key_is_dup(klass)
57
- end
58
-
59
- def raise_error_if_key_is_dup(klass)
60
- message = "key #{self.key} must be unique, provide the :key option to specify a unique key"
61
- raise ArgumentError.new(message) if klass.validation_keys_include? self.key
62
9
  end
63
-
64
- def should_validate?(instance)
65
- result = validate_this_time?(instance)
66
- case self.if
67
- when Proc
68
- result &&= instance.instance_eval(&self.if)
69
- when Symbol, String
70
- result &&= instance.instance_eval(self.if.to_s)
10
+
11
+ def validate instance
12
+ if should_validate?(instance) and !valid?(instance)
13
+ instance.errors.add attribute, message(self)
71
14
  end
72
- result
73
- end
74
-
75
- def message(instance)
76
- @message.respond_to?(:call) ? instance.instance_eval(&@message) : @message
77
- end
78
-
79
- def validate_this_time?(instance)
80
- return true if @times.nil?
81
- self.times > instance.times_validated(self.key)
82
15
  end
83
-
84
- def run_after_validate(result, instance, attribute)
85
- self.class.all_after_validations.each do |block|
86
- block.call result, instance, attribute
16
+
17
+ protected
18
+ def message instance
19
+ @message.respond_to?(:call) ? instance.instance_eval(&@message) : @message
20
+ end
21
+
22
+ def should_validate? instance
23
+ result = true # validate_this_time?(instance)
24
+ case self.if
25
+ when Proc
26
+ result &&= instance.instance_eval(&self.if)
27
+ when Symbol, String
28
+ result &&= instance.instance_eval(self.if.to_s)
29
+ end
30
+ result
87
31
  end
88
- # instance.instance_eval_with_params result, attribute, &self.after_validate unless self.after_validate.nil?
89
- instance.instance_eval &self.after_validate unless self.after_validate.nil?
90
- end
91
32
  end
92
33
  end
data/readme.md CHANGED
@@ -1,16 +1,34 @@
1
+ # V2 Changes:
2
+
1
3
  Validatable is a library for adding validations.
2
4
 
3
- Version 2:
5
+ In version 2 I removed all dependencies and extra stuff and left only pure validation logic.
6
+ You can use these validations with any plain Ruby object, and not only wit ActiveRecord-compatible models.
7
+
8
+ Also, I removed the "self.included ..." hook (because it caused problems with some libraries), if You need it (and You probably do) add this line after requiring 'validatable':
9
+
10
+ ``` ruby
11
+ require 'validatable'
12
+
13
+ module Validatable
14
+ def self.included(klass) #:nodoc:
15
+ klass.extend ClassMethods
16
+ end
17
+ end
18
+ ```
19
+
20
+ For contributors - right now it uses TestUnit framework, it would be great if someone rewrite all tests using RSpec.
4
21
 
5
- - active_support dependency removed
6
- - compatible with ruby 1.9.2
22
+ # Old Docs
23
+
24
+ Validatable is a library for adding validations.
7
25
 
8
26
  == Installation
9
-
27
+
10
28
  $ gem install validatable2
11
29
 
12
30
  == License
13
-
31
+
14
32
  You may use, copy and redistribute this library under the same terms as Ruby itself (see http://www.ruby-lang.org/en/LICENSE.txt).
15
33
 
16
34
  == Examples
@@ -27,7 +45,7 @@ Validation of an entire hierarchy of objects with errors aggregated at the root
27
45
  include Validatable
28
46
  include_validations_for :person
29
47
  attr_accessor :person
30
-
48
+
31
49
  def initialize(person)
32
50
  @person = person
33
51
  end
@@ -71,12 +89,12 @@ Validations can also be given groups. Groups can be used to validate an object w
71
89
  validates_presence_of :name, :groups => :underwriting
72
90
  attr_accessor :name, :ssn
73
91
  end
74
-
92
+
75
93
  application = MortgageApplication.new
76
94
  application.ssn = 377990118
77
95
  application.valid_for_saving? #=> true
78
96
  application.valid_for_underwriting? #=> false
79
-
97
+
80
98
  As you can see, you can use an array if the validation needs to be part of various groups. However, if the validation only applies to one group you can simply use a symbol for the group name.
81
99
 
82
100
  Similar to Rails, Validatable also supports conditional validation.
@@ -87,7 +105,7 @@ Similar to Rails, Validatable also supports conditional validation.
87
105
  validates_format_of :name, :with => /.+/, :if => Proc.new { !name.nil? }
88
106
  end
89
107
  Person.new.valid? #=> true
90
-
108
+
91
109
  Validatable also exposes an after_validate hook method.
92
110
 
93
111
  class Person
@@ -95,7 +113,7 @@ Validatable also exposes an after_validate hook method.
95
113
  validates_presence_of :name
96
114
  attr_accessor :name
97
115
  end
98
-
116
+
99
117
  class ValidatesPresenceOf
100
118
  after_validate do |result, instance, attribute|
101
119
  instance.errors.add("#{attribute} can't be blank") unless result
@@ -105,8 +123,8 @@ Validatable also exposes an after_validate hook method.
105
123
  person = Person.new
106
124
  person.valid? #=> false
107
125
  person.errors.on(:name) #=> "name can't be blank"
108
-
109
- The after_validate hook yields the result of the validation being run,
126
+
127
+ The after_validate hook yields the result of the validation being run,
110
128
  the instance the validation was run on, and the attribute that was validated
111
129
 
112
130
  In the above example the attribute "name" is appended to the message.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validatable2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-24 00:00:00.000000000Z
12
+ date: 2011-08-29 00:00:00.000000000Z
13
13
  dependencies: []
14
14
  description:
15
15
  email:
@@ -19,15 +19,9 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - Rakefile
21
21
  - readme.md
22
- - lib/validatable/child_validation.rb
23
22
  - lib/validatable/errors.rb
24
- - lib/validatable/included_validation.rb
25
23
  - lib/validatable/macros.rb
26
- - lib/validatable/object_extension.rb
27
- - lib/validatable/requireable.rb
28
- - lib/validatable/understandable.rb
29
- - lib/validatable/validatable_class_methods.rb
30
- - lib/validatable/validatable_instance_methods.rb
24
+ - lib/validatable/validatable.rb
31
25
  - lib/validatable/validations/validates_acceptance_of.rb
32
26
  - lib/validatable/validations/validates_associated.rb
33
27
  - lib/validatable/validations/validates_confirmation_of.rb
@@ -1,15 +0,0 @@
1
- module Validatable
2
- class ChildValidation #:nodoc:
3
- attr_accessor :attribute, :map, :should_validate_proc
4
-
5
- def initialize(attribute, map, should_validate_proc)
6
- @attribute = attribute
7
- @map = map
8
- @should_validate_proc = should_validate_proc
9
- end
10
-
11
- def should_validate?(instance)
12
- instance.instance_eval &should_validate_proc
13
- end
14
- end
15
- end
@@ -1,9 +0,0 @@
1
- module Validatable
2
- class IncludedValidation #:nodoc:
3
- attr_accessor :attribute
4
-
5
- def initialize(attribute)
6
- @attribute = attribute
7
- end
8
- end
9
- end
@@ -1,21 +0,0 @@
1
- # class Object #:nodoc:
2
- # module InstanceExecHelper #:nodoc:
3
- # end
4
- # include InstanceExecHelper
5
- # def instance_eval_with_params(*args, &block)
6
- # begin
7
- # old_critical, Thread.critical = Thread.critical, true
8
- # n = 0
9
- # n += 1 while respond_to?(mname="__instance_exec#{n}")
10
- # InstanceExecHelper.module_eval{ define_method(mname, &block) }
11
- # ensure
12
- # Thread.critical = old_critical
13
- # end
14
- # begin
15
- # ret = send(mname, *args)
16
- # ensure
17
- # InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
18
- # end
19
- # ret
20
- # end
21
- # end
@@ -1,26 +0,0 @@
1
- module Validatable
2
- module Requireable #:nodoc:
3
- module ClassMethods #:nodoc:
4
- def requires(*args)
5
- required_options.concat args
6
- end
7
-
8
- def required_options
9
- @required_options ||= []
10
- end
11
- end
12
-
13
- def self.included(klass)
14
- klass.extend ClassMethods
15
- end
16
-
17
- def requires(options)
18
- required_options = self.class.required_options.inject([]) do |errors, attribute|
19
- errors << attribute.to_s unless options.has_key?(attribute)
20
- errors
21
- end
22
- raise ArgumentError.new("#{self.class} requires options: #{required_options.join(', ')}") if required_options.any?
23
- true
24
- end
25
- end
26
- end
@@ -1,31 +0,0 @@
1
- module Validatable
2
- module Understandable #:nodoc:
3
- module ClassMethods #:nodoc:
4
- def understands(*args)
5
- understandings.concat args
6
- end
7
-
8
- def understandings
9
- @understandings ||= []
10
- end
11
-
12
- def all_understandings
13
- return understandings + self.superclass.all_understandings if self.superclass.respond_to? :all_understandings
14
- understandings
15
- end
16
- end
17
-
18
- def self.included(klass)
19
- klass.extend ClassMethods
20
- end
21
-
22
- def must_understand(hash)
23
- invalid_options = hash.inject([]) do |errors, (key, value)|
24
- errors << key.to_s unless self.class.all_understandings.include?(key)
25
- errors
26
- end
27
- raise ArgumentError.new("invalid options: #{invalid_options.join(', ')}") if invalid_options.any?
28
- true
29
- end
30
- end
31
- end
@@ -1,87 +0,0 @@
1
- module Validatable
2
- module ClassMethods #:nodoc:
3
-
4
- def validate_children(instance, group)
5
- self.children_to_validate.each do |child_validation|
6
- next unless child_validation.should_validate?(instance)
7
- child_or_children = instance.send child_validation.attribute
8
- [child_or_children].flatten.each do |child|
9
- if (child.respond_to?(:valid_for_group?))
10
- child.valid_for_group?(group)
11
- else
12
- child.valid?
13
- end
14
- child.errors.each do |attribute, messages|
15
- if messages.is_a?(String)
16
- add_error(instance, child_validation.map[attribute.to_sym] || attribute, messages)
17
- else
18
- messages.each do |message|
19
- add_error(instance, child_validation.map[attribute.to_sym] || attribute, message)
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
26
-
27
- def all_before_validations
28
- if self.superclass.respond_to? :all_before_validations
29
- return before_validations + self.superclass.all_before_validations
30
- end
31
- before_validations
32
- end
33
-
34
- def before_validations
35
- @before_validations ||= []
36
- end
37
-
38
- def all_validations
39
- if self.respond_to?(:superclass) && self.superclass.respond_to?(:all_validations)
40
- return validations + self.superclass.all_validations
41
- end
42
- validations
43
- end
44
-
45
- def validations
46
- @validations ||= []
47
- end
48
-
49
- def add_error(instance, attribute, msg)
50
- instance.errors.add(attribute, msg)
51
- end
52
-
53
- def validation_keys_include?(key)
54
- validations.map { |validation| validation.key }.include?(key)
55
- end
56
-
57
- def validations_to_include
58
- @validations_to_include ||= []
59
- end
60
-
61
- protected
62
-
63
- def add_validations(args, klass)
64
- options = args.last.is_a?(Hash) ? args.pop : {}
65
- args.each do |attribute|
66
- new_validation = klass.new self, attribute, options
67
- self.validations << new_validation
68
- self.create_valid_method_for_groups new_validation.groups
69
- end
70
- end
71
-
72
- def create_valid_method_for_groups(groups)
73
- groups.each do |group|
74
- self.class_eval do
75
- define_method "valid_for_#{group}?".to_sym do
76
- valid_for_group?(group)
77
- end
78
- end
79
- end
80
- end
81
-
82
- def children_to_validate
83
- @children_to_validate ||= []
84
- end
85
-
86
- end
87
- end
@@ -1,106 +0,0 @@
1
- module Validatable
2
- def self.included(klass) #:nodoc:
3
- klass.extend Validatable::ClassMethods
4
- klass.extend Validatable::Macros
5
- end
6
-
7
- # call-seq: valid?
8
- #
9
- # Returns true if no errors were added otherwise false. Only executes validations that have no :groups option specified
10
- def valid?
11
- valid_for_group?(nil)
12
- end
13
-
14
- # call-seq: errors
15
- #
16
- # Returns the Errors object that holds all information about attribute error messages.
17
- def errors
18
- @errors ||= Validatable::Errors.new
19
- end
20
-
21
- def valid_for_group?(group) #:nodoc:
22
- errors.clear
23
- run_before_validations
24
- self.class.validate_children(self, group)
25
- self.validate_group(group)
26
- errors.empty?
27
- end
28
-
29
- def times_validated(key) #:nodoc:
30
- times_validated_hash[key] || 0
31
- end
32
-
33
- def increment_times_validated_for(validation) #:nodoc:
34
- if validation.key != nil
35
- if times_validated_hash[validation.key].nil?
36
- times_validated_hash[validation.key] = 1
37
- else
38
- times_validated_hash[validation.key] += 1
39
- end
40
- end
41
- end
42
-
43
- # call-seq: validate_only(key)
44
- #
45
- # Only executes a specified validation. The argument should follow a pattern based on the key of the validation.
46
- # Examples:
47
- # * validates_presence_of :name can be run with obj.validate_only("presence_of/name")
48
- # * validates_presence_of :birthday, :key => "a key" can be run with obj.validate_only("presence_of/a key")
49
- def validate_only(key)
50
- validation_name, attribute_name = key.split("/")
51
- validation_name = validation_name.split("_").collect{|word| word.capitalize}.join
52
- validation_key = "#{self.class.name}/Validatable::Validates#{validation_name}/#{attribute_name}"
53
- validation = self.class.all_validations.find { |validation| validation.key == validation_key }
54
- raise ArgumentError.new("validation with key #{validation_key} could not be found") if validation.nil?
55
- errors.clear
56
- run_validation(validation)
57
- end
58
-
59
- protected
60
- def times_validated_hash #:nodoc:
61
- @times_validated_hash ||= {}
62
- end
63
-
64
- def validate_group(group) #:nodoc:
65
- validation_levels.each do |level|
66
- validations_for_level_and_group(level, group).each do |validation|
67
- run_validation(validation) if validation.should_validate?(self)
68
- end
69
- return unless self.errors.empty?
70
- end
71
- end
72
-
73
- def run_validation(validation) #:nodoc:
74
- validation_result = validation.valid?(self)
75
- add_error(validation.attribute, validation.message(self)) unless validation_result
76
- increment_times_validated_for(validation)
77
- validation.run_after_validate(validation_result, self, validation.attribute)
78
- end
79
-
80
- def run_before_validations #:nodoc:
81
- self.class.all_before_validations.each do |block|
82
- instance_eval &block
83
- end
84
- end
85
-
86
- def add_error(attribute, message) #:nodoc:
87
- self.class.add_error(self, attribute, message)
88
- end
89
-
90
- def validations_for_level_and_group(level, group) #:nodoc:
91
- validations_for_level = self.all_validations.select { |validation| validation.level == level }
92
- return validations_for_level.select { |validation| validation.groups.empty? } if group.nil?
93
- validations_for_level.select { |validation| validation.groups.include?(group) }
94
- end
95
-
96
- def all_validations #:nodoc:
97
- res = self.class.validations_to_include.inject(self.class.all_validations) do |result, included_validation_class|
98
- result += self.send(included_validation_class.attribute).all_validations
99
- result
100
- end
101
- end
102
-
103
- def validation_levels #:nodoc:
104
- self.class.all_validations.inject([1]) { |result, validation| result << validation.level }.uniq.sort
105
- end
106
- end