assistance 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG 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