assistance 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ === 0.1.2 (2008-01-21)
2
+
3
+ * Imported Validation from Sequel.
4
+
1
5
  === 0.1.1 (2008-01-18)
2
6
 
3
7
  * Added Array#extract_options! method.
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ include FileUtils
9
9
  # Configuration
10
10
  ##############################################################################
11
11
  NAME = "assistance"
12
- VERS = "0.1.1"
12
+ VERS = "0.1.2"
13
13
  CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
14
14
  RDOC_OPTS = [
15
15
  "--quiet",
@@ -6,4 +6,5 @@ dir = File.join(File.dirname(__FILE__), "assistance")
6
6
  inflector
7
7
  blank
8
8
  extract_options
9
+ validation
9
10
  ].each {|f| require(File.join(dir, f))}
@@ -0,0 +1,239 @@
1
+ # The Validations module provides validation capabilities as a mixin. When
2
+ # included into a class, it enhances the class with class and instance
3
+ # methods for defining validations and validating class instances.
4
+ #
5
+ # The Validation emulates the validation capabilities of ActiveRecord, and
6
+ # provides methods for validating acceptance, confirmation, presence, format,
7
+ # length and numericality of attributes.
8
+ #
9
+ # To use validations, you need to include the Validation module in your
10
+ # class:
11
+ #
12
+ # class MyClass
13
+ # include Validation
14
+ # validates_length_of :password, :minimum => 6
15
+ # end
16
+ module Validation
17
+ # Includes the Validation class methods into the including class.
18
+ def self.included(c)
19
+ c.extend ClassMethods
20
+ end
21
+
22
+ # Returns the validation errors associated with the object.
23
+ def errors
24
+ @errors ||= Errors.new
25
+ end
26
+
27
+ # Validates the object.
28
+ def validate
29
+ errors.clear
30
+ self.class.validate(self)
31
+ end
32
+
33
+ # Validates the object and returns true if no errors are reported.
34
+ def valid?
35
+ validate
36
+ errors.empty?
37
+ end
38
+
39
+ # Validation::Errors represents validation errors.
40
+ class Errors
41
+ # Initializes a new instance of validation errors.
42
+ def initialize
43
+ @errors = Hash.new {|h, k| h[k] = []}
44
+ end
45
+
46
+ # Returns true if no errors are stored.
47
+ def empty?
48
+ @errors.empty?
49
+ end
50
+
51
+ # Clears all errors.
52
+ def clear
53
+ @errors.clear
54
+ end
55
+
56
+ # Returns the errors for the given attribute.
57
+ def on(att)
58
+ @errors[att]
59
+ end
60
+ alias_method :[], :on
61
+
62
+ # Adds an error for the given attribute.
63
+ def add(att, msg)
64
+ @errors[att] << msg
65
+ end
66
+
67
+ # Returns an array of fully-formatted error messages.
68
+ def full_messages
69
+ @errors.inject([]) do |m, kv| att, errors = *kv
70
+ errors.each {|e| m << "#{att} #{e}"}
71
+ m
72
+ end
73
+ end
74
+ end
75
+
76
+ # The Generator class is used to generate validation definitions using
77
+ # the validates {} idiom.
78
+ class Generator
79
+ # Initializes a new generator.
80
+ def initialize(receiver ,&block)
81
+ @receiver = receiver
82
+ instance_eval(&block)
83
+ end
84
+
85
+ # Delegates method calls to the receiver by calling receiver.validates_xxx.
86
+ def method_missing(m, *args)
87
+ @receiver.send(:"validates_#{m}", *args)
88
+ end
89
+ end
90
+
91
+ # Validation class methods.
92
+ module ClassMethods
93
+ # Defines validations by converting a longhand block into a series of
94
+ # shorthand definitions. For example:
95
+ #
96
+ # class MyClass
97
+ # include Validation
98
+ # validates do
99
+ # length_of :name, :minimum => 6
100
+ # length_of :password, :minimum => 8
101
+ # end
102
+ # end
103
+ #
104
+ # is equivalent to:
105
+ # class MyClass
106
+ # include Validation
107
+ # validates_length_of :name, :minimum => 6
108
+ # validates_length_of :password, :minimum => 8
109
+ # end
110
+ def validates(&block)
111
+ Generator.new(self, &block)
112
+ end
113
+
114
+ # Returns the validations hash for the class.
115
+ def validations
116
+ @validations ||= Hash.new {|h, k| h[k] = []}
117
+ end
118
+
119
+ # Returns true if validations are defined.
120
+ def has_validations?
121
+ !validations.empty?
122
+ end
123
+
124
+ # Validates the given instance.
125
+ def validate(o)
126
+ validations.each do |att, procs|
127
+ v = o.send(att)
128
+ procs.each {|p| p[o, att, v]}
129
+ end
130
+ end
131
+
132
+ # Adds a validation for each of the given attributes using the supplied
133
+ # block. The block must accept three arguments: instance, attribute and
134
+ # value, e.g.:
135
+ #
136
+ # validates_each :name, :password do |object, attribute, value|
137
+ # object.errors[attribute] << 'is not nice' unless value.nice?
138
+ # end
139
+ def validates_each(*atts, &block)
140
+ atts.each {|a| validations[a] << block}
141
+ end
142
+
143
+ # Validates acceptance of an attribute.
144
+ def validates_acceptance_of(*atts)
145
+ opts = {
146
+ :message => 'is not accepted',
147
+ :allow_nil => true,
148
+ :accept => '1'
149
+ }.merge!(atts.extract_options!)
150
+
151
+ validates_each(*atts) do |o, a, v|
152
+ next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
153
+ o.errors[a] << opts[:message] unless v == opts[:accept]
154
+ end
155
+ end
156
+
157
+ # Validates confirmation of an attribute.
158
+ def validates_confirmation_of(*atts)
159
+ opts = {
160
+ :message => 'is not confirmed',
161
+ }.merge!(atts.extract_options!)
162
+
163
+ validates_each(*atts) do |o, a, v|
164
+ next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
165
+ c = o.send(:"#{a}_confirmation")
166
+ o.errors[a] << opts[:message] unless v == c
167
+ end
168
+ end
169
+
170
+ # Validates the format of an attribute.
171
+ def validates_format_of(*atts)
172
+ opts = {
173
+ :message => 'is invalid',
174
+ }.merge!(atts.extract_options!)
175
+
176
+ unless opts[:with].is_a?(Regexp)
177
+ raise ArgumentError, "A regular expression must be supplied as the :with option of the options hash"
178
+ end
179
+
180
+ validates_each(*atts) do |o, a, v|
181
+ next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
182
+ o.errors[a] << opts[:message] unless v.to_s =~ opts[:with]
183
+ end
184
+ end
185
+
186
+ # Validates the length of an attribute.
187
+ def validates_length_of(*atts)
188
+ opts = {
189
+ :too_long => 'is too long',
190
+ :too_short => 'is too short',
191
+ :wrong_length => 'is the wrong length'
192
+ }.merge!(atts.extract_options!)
193
+
194
+ validates_each(*atts) do |o, a, v|
195
+ next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
196
+ if m = opts[:maximum]
197
+ o.errors[a] << (opts[:message] || opts[:too_long]) unless v && v.size <= m
198
+ end
199
+ if m = opts[:minimum]
200
+ o.errors[a] << (opts[:message] || opts[:too_short]) unless v && v.size >= m
201
+ end
202
+ if i = opts[:is]
203
+ o.errors[a] << (opts[:message] || opts[:wrong_length]) unless v && v.size == i
204
+ end
205
+ if w = opts[:within]
206
+ o.errors[a] << (opts[:message] || opts[:wrong_length]) unless v && w.include?(v.size)
207
+ end
208
+ end
209
+ end
210
+
211
+ NUMBER_RE = /^\d*\.{0,1}\d+$/
212
+ INTEGER_RE = /\A[+-]?\d+\Z/
213
+
214
+ # Validates whether an attribute is a number.
215
+ def validates_numericality_of(*atts)
216
+ opts = {
217
+ :message => 'is not a number',
218
+ }.merge!(atts.extract_options!)
219
+
220
+ re = opts[:only_integer] ? INTEGER_RE : NUMBER_RE
221
+
222
+ validates_each(*atts) do |o, a, v|
223
+ next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
224
+ o.errors[a] << opts[:message] unless v.to_s =~ re
225
+ end
226
+ end
227
+
228
+ # Validates the presence of an attribute.
229
+ def validates_presence_of(*atts)
230
+ opts = {
231
+ :message => 'is not present',
232
+ }.merge!(atts.extract_options!)
233
+
234
+ validates_each(*atts) do |o, a, v|
235
+ o.errors[a] << opts[:message] unless v && !v.blank?
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,291 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe "Validation::Errors" do
4
+ setup do
5
+ @errors = Validation::Errors.new
6
+ class Validation::Errors
7
+ attr_accessor :errors
8
+ end
9
+ end
10
+
11
+ specify "should be clearable using #clear" do
12
+ @errors.errors = {1 => 2, 3 => 4}
13
+ @errors.clear
14
+ @errors.errors.should == {}
15
+ end
16
+
17
+ specify "should be empty if no errors are added" do
18
+ @errors.should be_empty
19
+ @errors[:blah] << "blah"
20
+ @errors.should_not be_empty
21
+ end
22
+
23
+ specify "should return errors for a specific attribute using #on or #[]" do
24
+ @errors[:blah].should == []
25
+ @errors.on(:blah).should == []
26
+
27
+ @errors[:blah] << 'blah'
28
+ @errors[:blah].should == ['blah']
29
+ @errors.on(:blah).should == ['blah']
30
+
31
+ @errors[:bleu].should == []
32
+ @errors.on(:bleu).should == []
33
+ end
34
+
35
+ specify "should accept errors using #[] << or #add" do
36
+ @errors[:blah] << 'blah'
37
+ @errors[:blah].should == ['blah']
38
+
39
+ @errors.add :blah, 'zzzz'
40
+ @errors[:blah].should == ['blah', 'zzzz']
41
+ end
42
+
43
+ specify "should return full messages using #full_messages" do
44
+ @errors.full_messages.should == []
45
+
46
+ @errors[:blow] << 'blieuh'
47
+ @errors[:blow] << 'blich'
48
+ @errors[:blay] << 'bliu'
49
+ msgs = @errors.full_messages
50
+ msgs.size.should == 3
51
+ msgs.should include('blow blieuh', 'blow blich', 'blay bliu')
52
+ end
53
+ end
54
+
55
+ describe Validation do
56
+ setup do
57
+ @c = Class.new do
58
+ include Validation
59
+
60
+ def self.validates_coolness_of(attr)
61
+ validates_each(attr) {|o, a, v| o.errors[a] << 'is not cool' if v != :cool}
62
+ end
63
+ end
64
+
65
+ @d = Class.new do
66
+ attr_accessor :errors
67
+ def initialize; @errors = Validation::Errors.new; end
68
+ end
69
+ end
70
+
71
+ specify "should respond to validates, validations, has_validations?" do
72
+ @c.should respond_to(:validations)
73
+ @c.should respond_to(:has_validations?)
74
+ end
75
+
76
+ specify "should acccept validation definitions using validates_each" do
77
+ @c.validates_each(:xx, :yy) {|o, a, v| o.errors[a] << 'too low' if v < 50}
78
+
79
+ @c.validations[:xx].size.should == 1
80
+ @c.validations[:yy].size.should == 1
81
+
82
+ o = @d.new
83
+ @c.validations[:xx].first.call(o, :aa, 40)
84
+ @c.validations[:yy].first.call(o, :bb, 60)
85
+
86
+ o.errors.full_messages.should == ['aa too low']
87
+ end
88
+
89
+ specify "should return true/false for has_validations?" do
90
+ @c.has_validations?.should == false
91
+ @c.validates_each(:xx) {1}
92
+ @c.has_validations?.should == true
93
+ end
94
+
95
+ specify "should provide a validates method that takes block with validation definitions" do
96
+ @c.validates do
97
+ coolness_of :blah
98
+ end
99
+ @c.validations[:blah].should_not be_empty
100
+
101
+ o = @d.new
102
+ @c.validations[:blah].first.call(o, :ttt, 40)
103
+ o.errors.full_messages.should == ['ttt is not cool']
104
+ o.errors.clear
105
+ @c.validations[:blah].first.call(o, :ttt, :cool)
106
+ o.errors.should be_empty
107
+ end
108
+ end
109
+
110
+ describe "A Validation instance" do
111
+ setup do
112
+ @c = Class.new do
113
+ attr_accessor :score
114
+
115
+ include Validation
116
+
117
+ validates_each :score do |o, a, v|
118
+ o.errors[a] << 'too low' if v < 87
119
+ end
120
+ end
121
+
122
+ @o = @c.new
123
+ end
124
+
125
+ specify "should supply a #valid? method that returns true if validations pass" do
126
+ @o.score = 50
127
+ @o.should_not be_valid
128
+ @o.score = 100
129
+ @o.should be_valid
130
+ end
131
+
132
+ specify "should provide an errors object" do
133
+ @o.score = 100
134
+ @o.should be_valid
135
+ @o.errors.should be_empty
136
+
137
+ @o.score = 86
138
+ @o.should_not be_valid
139
+ @o.errors[:score].should == ['too low']
140
+ @o.errors[:blah].should be_empty
141
+ end
142
+ end
143
+
144
+ describe Validation::Generator do
145
+ setup do
146
+ $testit = nil
147
+
148
+ @c = Class.new do
149
+ include Validation
150
+
151
+ def self.validates_blah
152
+ $testit = 1324
153
+ end
154
+ end
155
+ end
156
+
157
+ specify "should instance_eval the block, sending everything to its receiver" do
158
+ Validation::Generator.new(@c) do
159
+ blah
160
+ end
161
+ $testit.should == 1324
162
+ end
163
+ end
164
+
165
+ describe "Validations" do
166
+ setup do
167
+ @c = Class.new do
168
+ attr_accessor :value
169
+ include Validation
170
+ end
171
+ @m = @c.new
172
+ end
173
+
174
+ specify "should validate acceptance_of" do
175
+ @c.validates_acceptance_of :value
176
+ @m.should be_valid
177
+ @m.value = '1'
178
+ @m.should be_valid
179
+ end
180
+
181
+ specify "should validate acceptance_of with accept" do
182
+ @c.validates_acceptance_of :value, :accept => 'true'
183
+ @m.value = '1'
184
+ @m.should_not be_valid
185
+ @m.value = 'true'
186
+ @m.should be_valid
187
+ end
188
+
189
+ specify "should validate acceptance_of with allow_nil => false" do
190
+ @c.validates_acceptance_of :value, :allow_nil => false
191
+ @m.should_not be_valid
192
+ end
193
+
194
+ specify "should validate confirmation_of" do
195
+ @c.send(:attr_accessor, :value_confirmation)
196
+ @c.validates_confirmation_of :value
197
+
198
+ @m.value = 'blah'
199
+ @m.should_not be_valid
200
+
201
+ @m.value_confirmation = 'blah'
202
+ @m.should be_valid
203
+ end
204
+
205
+ specify "should validate format_of" do
206
+ @c.validates_format_of :value, :with => /.+_.+/
207
+ @m.value = 'abc_'
208
+ @m.should_not be_valid
209
+ @m.value = 'abc_def'
210
+ @m.should be_valid
211
+ end
212
+
213
+ specify "should raise for validate_format_of without regexp" do
214
+ proc {@c.validates_format_of :value}.should raise_error(ArgumentError)
215
+ proc {@c.validates_format_of :value, :with => :blah}.should raise_error(ArgumentError)
216
+ end
217
+
218
+ specify "should validate length_of with maximum" do
219
+ @c.validates_length_of :value, :maximum => 5
220
+ @m.should_not be_valid
221
+ @m.value = '12345'
222
+ @m.should be_valid
223
+ @m.value = '123456'
224
+ @m.should_not be_valid
225
+ end
226
+
227
+ specify "should validate length_of with minimum" do
228
+ @c.validates_length_of :value, :minimum => 5
229
+ @m.should_not be_valid
230
+ @m.value = '12345'
231
+ @m.should be_valid
232
+ @m.value = '1234'
233
+ @m.should_not be_valid
234
+ end
235
+
236
+ specify "should validate length_of with within" do
237
+ @c.validates_length_of :value, :within => 2..5
238
+ @m.should_not be_valid
239
+ @m.value = '12345'
240
+ @m.should be_valid
241
+ @m.value = '1'
242
+ @m.should_not be_valid
243
+ @m.value = '123456'
244
+ @m.should_not be_valid
245
+ end
246
+
247
+ specify "should validate length_of with is" do
248
+ @c.validates_length_of :value, :is => 3
249
+ @m.should_not be_valid
250
+ @m.value = '123'
251
+ @m.should be_valid
252
+ @m.value = '12'
253
+ @m.should_not be_valid
254
+ @m.value = '1234'
255
+ @m.should_not be_valid
256
+ end
257
+
258
+ specify "should validate length_of with allow_nil" do
259
+ @c.validates_length_of :value, :is => 3, :allow_nil => true
260
+ @m.should be_valid
261
+ end
262
+
263
+ specify "should validate numericality_of" do
264
+ @c.validates_numericality_of :value
265
+ @m.value = 'blah'
266
+ @m.should_not be_valid
267
+ @m.value = '123'
268
+ @m.should be_valid
269
+ @m.value = '123.1231'
270
+ @m.should be_valid
271
+ end
272
+
273
+ specify "should validate numericality_of with only_integer" do
274
+ @c.validates_numericality_of :value, :only_integer => true
275
+ @m.value = 'blah'
276
+ @m.should_not be_valid
277
+ @m.value = '123'
278
+ @m.should be_valid
279
+ @m.value = '123.1231'
280
+ @m.should_not be_valid
281
+ end
282
+
283
+ specify "should validate presence_of" do
284
+ @c.validates_presence_of :value
285
+ @m.should_not be_valid
286
+ @m.value = ''
287
+ @m.should_not be_valid
288
+ @m.value = 1234
289
+ @m.should be_valid
290
+ end
291
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assistance
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz, Sam Smoot, Sharon Rosner
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-01-18 00:00:00 +02:00
12
+ date: 2008-01-21 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -36,6 +36,7 @@ files:
36
36
  - spec/spec.opts
37
37
  - spec/spec_helper.rb
38
38
  - spec/time_calculations_spec.rb
39
+ - spec/validation_spec.rb
39
40
  - lib/assistance
40
41
  - lib/assistance/blank.rb
41
42
  - lib/assistance/connection_pool.rb
@@ -44,6 +45,7 @@ files:
44
45
  - lib/assistance/inflections.rb
45
46
  - lib/assistance/inflector.rb
46
47
  - lib/assistance/time_calculations.rb
48
+ - lib/assistance/validation.rb
47
49
  - lib/assistance.rb
48
50
  - CHANGELOG
49
51
  has_rdoc: true