validation_profiler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 009ab57e0d9f853b5579576803bfd2fefa1369fe
4
+ data.tar.gz: 9c4f0fdff2487fdc274bb05bb6c977f3d6e10cef
5
+ SHA512:
6
+ metadata.gz: 04dc04f887ae68b8754ebbc5064ddc5d25ffc353b309084759d426d8e4de5d982643bfe950d4af4e9b7c611cf26af3f2590eddd86f19265b4e65bcf9b37540fb
7
+ data.tar.gz: 3e35caa074aa7e880cd1d6eb66764c44706790c39107faaa3b36de800876a742d478cda9aa7586cb197dcf6bf59d155a716bc4d82fedd48a077b12dd79b9ed55
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at vaughan.britton@sage.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in validation_profiler.gemspec
4
+ gemspec
5
+
6
+ require 'pry'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 vaughanbrittonsage
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,297 @@
1
+ # ValidationProfiler
2
+
3
+ Welcome to ValidationProfiler. This is a validation framework that allows you to seperate validation logic away from your objects and into validation profiles that can be re-used and changed without affecting your objects.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'validation_profiler'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install validation_profiler
20
+
21
+ ## Usage
22
+
23
+ First you need to create a validation profile to hold the validation logic you want to apply, validation profiles must inherit from the ValidationProfile base class or another validation profile.
24
+
25
+ class SignUpValidationProfile < ValidationProfile
26
+ .....
27
+ end
28
+
29
+ Then you specify validation rules that should be checked when this profile is validated against an object.
30
+
31
+ class SignUpValidationProfile < ValidationProfile
32
+ validates :age, :min, { value: 18 }
33
+ validates :email, :email
34
+ .....
35
+ end
36
+
37
+ When specifying a validation rule you need to specify the following arguments:
38
+
39
+ - Field name
40
+ - Rule key
41
+ - Hash containing any options required for the validation rule
42
+
43
+ So if we take another look at the first validation rule we specified in the **SignUpValidationProfile** above:
44
+
45
+ Field name = :age
46
+ Rule key = :min
47
+ Attributes Hash = { value: 18 }
48
+
49
+ This validation statement will be interpreted as:
50
+ *"The field :age must have a minimum value of 18"*
51
+
52
+ To use a validation profile you need to make a call to the **ValidationManager** class, and pass the object you want to validate along with the profile you want to use for the validation:
53
+
54
+ #create the validation manager
55
+ manager = ValidationManager.new
56
+
57
+ #call the validate method and pass the object and profile
58
+ result = manager.validate(user, profile)
59
+
60
+ Calls to the validate method will return a **ValidationManagerResult** that will detail the results of the validation.
61
+
62
+ A **ValidationManagerResult** has the following attributes:
63
+
64
+ - #outcome = [Boolean] overall outcome of the validation (passed or failed)
65
+ - #errors = [Array] containing details of each field error that occurred during validation.
66
+
67
+ Each item in the errors array has the following attributes:
68
+
69
+ - #field = The name of the field that this error occurred for.
70
+ - #message = A message that describes the validation error
71
+
72
+ ## Validation Rules
73
+
74
+ **RequiredValidationRule**
75
+
76
+ This rule is used to specify a field must contain a value:
77
+
78
+ validates :name, :required
79
+
80
+ Attributes:
81
+
82
+ - **:message** [String] [Optional]
83
+ This is used to allow a custom error message to be specified.
84
+
85
+
86
+ ----------
87
+
88
+
89
+ **LengthValidationRule**
90
+
91
+ This rule is used to specify a [String] or [Array] must be of a certain length:
92
+
93
+ validates :name, :length, { min: 5, max: 10 }
94
+
95
+ Attributes:
96
+
97
+ - **:min** [Numeric]
98
+ This is used to specify the minimum length of the field value.
99
+
100
+ - **:max** [Numeric]
101
+ This is used to specify the maximum length of the field value.
102
+
103
+ > **:min** & **:max** can be included together or independently providing at least 1 is specified.
104
+
105
+ - **:message** [String] [Optional]
106
+ This is used to allow a custom error message to be specified.
107
+
108
+ - **:required** [Boolean] [Default=True] [Optional]
109
+ This is used to specify if this rule should only be executed when the field contains a value.
110
+ > **True** always executes, **False** only executes when the field contains a value)*
111
+
112
+
113
+ ----------
114
+
115
+ **MinValidationRule**
116
+
117
+ This rule is used to specify a minimum value a [DateTime] or [Numeric] field must have.
118
+
119
+ Attributes:
120
+
121
+ - **:value** [Numeric/DateTime]
122
+ This is used to specify the minimum value of the field.
123
+
124
+ - **:message** [String] [Optional]
125
+ This is used to allow a custom error message to be specified.
126
+
127
+ - **:required** [Boolean] [Default=True] [Optional]
128
+ This is used to specify if this rule should only be executed when the field contains a value.
129
+ > **True** always executes, **False** only executes when the field contains a value)*
130
+
131
+ ----------
132
+
133
+ **MaxValidationRule**
134
+
135
+ This rule is used to specify a maximum value a [DateTime] or [Numeric] field must have.
136
+
137
+ Attributes:
138
+
139
+ - **:value** [Numeric/DateTime]
140
+ This is used to specify the maximum value of the field.
141
+
142
+ - **:message** [String] [Optional]
143
+ This is used to allow a custom error message to be specified.
144
+
145
+ - **:required** [Boolean] [Default=True] [Optional]
146
+ This is used to specify if this rule should only be executed the field contains a value.
147
+ > **True** always executes, **False** only executes when the field contains a value)*
148
+
149
+ ----------
150
+
151
+ **EmailValidationRule**
152
+
153
+ This rule is used to specify a field value must contain a valid email address.
154
+
155
+ Attributes:
156
+
157
+ - **:message** [String] [Optional]
158
+ This is used to allow a custom error message to be specified.
159
+
160
+ - **:required** [Boolean] [Default=True] [Optional]
161
+ This is used to specify if this rule should only be executed when the field contains a value.
162
+ > **True** always executes, **False** only executes when the field contains a value)*
163
+
164
+ ----------
165
+
166
+ **RegexValidationRule**
167
+
168
+ This rule is used to specify a regex pattern that a field value must validate against.
169
+
170
+ Attributes:
171
+
172
+ - **:regex** [Regex]
173
+ This is used to specify the regex pattern.
174
+
175
+ - **:message** [String] [Optional]
176
+ This is used to allow a custom error message to be specified.
177
+
178
+ - **:required** [Boolean] [Default=True] [Optional]
179
+ This is used to specify if this rule should only be executed when the field contains a value.
180
+ > **True** always executes, **False** only executes when the field contains a value)*
181
+
182
+ ----------
183
+
184
+ **MatchValidationRule**
185
+
186
+ This rule is used to specify a field value must match the value of another field.
187
+
188
+ Attributes:
189
+
190
+ - **:field** [Symbol]
191
+ This is used to specify the name of the other field this field's value must match.
192
+
193
+ - **:message** [String] [Optional]
194
+ This is used to allow a custom error message to be specified.
195
+
196
+ - **:required** [Boolean] [Default=True] [Optional]
197
+ This is used to specify if this rule should only be executed when the field contains a value.
198
+ > **True** always executes, **False** only executes when the field contains a value)
199
+
200
+ ----------
201
+
202
+ **ConditionValidationRule**
203
+
204
+ This rule is used to specify a condition statement.
205
+
206
+ e.g.
207
+
208
+ > format:
209
+ >
210
+ > When [:condition_field] [:condition_expression] [:condition_value] then [:field] [:field_expression] [:field_value]
211
+ >
212
+ >could be read as:
213
+ >
214
+ > When :age >= 18 then :accept == true
215
+
216
+ Attributes:
217
+
218
+ - **:condition_field** [Symbol]
219
+ This is used to specify the name of the condition field.
220
+
221
+ - **:condition_expression** [String]
222
+ This is used to specify the expression to use between the condition_field and the condition_value.
223
+
224
+ > **Supported expression types:**
225
+ > '=='
226
+ > '>'
227
+ > '>='
228
+ > '<'
229
+ > '<='
230
+ > '!='
231
+
232
+
233
+ - **:condition_value** [String/Numeric/DateTime/nil]
234
+ This is used to specify the value to use for the condition statement.
235
+
236
+ - **:field_expression** [String]
237
+ This is used to specify the expression to use between the field's value and the field_value attribute.
238
+
239
+ - **:field_value** [String/Numeric/DateTime/nil]
240
+ This is used to specify the value to use for the field statement.
241
+
242
+ - **:message** [String] [Optional]
243
+ This is used to allow a custom error message to be specified.
244
+
245
+ - **:required** [Boolean] [Default=True] [Optional]
246
+ This is used to specify if this rule should only be executed when the field contains a value.
247
+ > **True** always executes, **False** only executes when the field contains a value)
248
+
249
+ ----------
250
+
251
+ ##Custom Validation Rules
252
+
253
+ To create a custom validation rule you must create a class that inherits from the **ValidationRule** base class and implement the #error_message and #validate methods, see the **RequiredValidationRule** below as an example:
254
+
255
+ class RequiredValidationRule < ValidationRule
256
+ #implement this method to return the error message when
257
+ #this rule fails validation
258
+ def error_message(field, attributes = {})
259
+ #check if custom message was specified
260
+ if attributes[:message] == nil
261
+ #return default method
262
+ "#{field} is not valid"
263
+ else
264
+ #return custom message
265
+ attributes[:message]
266
+ end
267
+ end
268
+
269
+ def validate(obj, field, attributes = {})
270
+ #attempt to get the field value from the object
271
+ field_value = get_field_value(obj, field)
272
+
273
+ if field_value == nil
274
+ return false
275
+ end
276
+
277
+ return !field_value.empty?
278
+ end
279
+ end
280
+
281
+ The **ValidationRule** base class provides the *#get_field_value(obj, field)* method to cater for fetching the field value from the object to perform the validation against.
282
+
283
+ ## Development
284
+
285
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
286
+
287
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
288
+
289
+ ## Contributing
290
+
291
+ Bug reports and pull requests are welcome on GitHub at https://github.com/vaughanbrittonsage/validation_profiler. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
292
+
293
+
294
+ ## License
295
+
296
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
297
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "validation_profiler"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,437 @@
1
+ require 'pry'
2
+
3
+ class InvalidRuleAttributes < StandardError
4
+
5
+ def initialize(rule, field)
6
+ @rule = rule
7
+ @field = field
8
+ end
9
+
10
+ def to_s
11
+ rule = @rule
12
+ field = @field
13
+ "Incorrect attributes specified for Validation rule: #{rule} Field: #{field}."
14
+ end
15
+
16
+ end
17
+
18
+ class FieldNotFound < StandardError
19
+
20
+ def initialize(field)
21
+ @field = field
22
+ end
23
+
24
+ def to_s
25
+ field = @field
26
+ "Field: #{field} could not be found."
27
+ end
28
+
29
+ end
30
+
31
+ class InvalidFieldType < StandardError
32
+
33
+ def initialize(rule, field)
34
+ @rule = rule
35
+ @field = field
36
+ end
37
+
38
+ def to_s
39
+ rule = @rule
40
+ field = @field
41
+ "Field: #{field} has an incorrect value type for Validation Rule: #{rule}."
42
+ end
43
+
44
+ end
45
+
46
+ class ValidationRule
47
+
48
+ def get_field_value(obj, field)
49
+ #attempt to get the field value from the object
50
+ field_value = nil
51
+ #check if the object is a hash
52
+ if obj.is_a?(Hash)
53
+ #get the field value
54
+ field_value = obj[field]
55
+ else
56
+ #if the object does not contain the specified field raise an exception
57
+ if !obj.respond_to?(field)
58
+ raise FieldNotFound.new(field)
59
+ end
60
+
61
+ #get the field value
62
+ field_value = obj.send(field)
63
+ end
64
+ end
65
+
66
+ def is_required?(field_value, attributes = {})
67
+ required = attributes[:required]
68
+ if required == nil
69
+ required = true
70
+ end
71
+
72
+ #check if the field is required
73
+ if (field_value == nil || ((field_value.is_a?(String) || field_value.is_a?(Array)) && field_value.empty?)) && required == false
74
+ return false
75
+ else
76
+ return true
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ class LengthValidationRule < ValidationRule
83
+
84
+ def error_message(field, attributes)
85
+ #check if a custom error message has been specified in the attributes
86
+ if attributes[:message] == nil
87
+ #no custom error message has been specified so create the default message.
88
+ min = attributes[:min]
89
+ max = attributes[:max]
90
+ if min && max
91
+ "#{field} must have a min length of #{min} and a max length of #{max}"
92
+ elsif min
93
+ "#{field} must have a min length of #{min}"
94
+ else
95
+ "#{field} must have a max length of #{max}"
96
+ end
97
+ else
98
+ attributes[:message]
99
+ end
100
+ end
101
+
102
+ def validate(obj, field, attributes)
103
+
104
+ min = attributes[:min]
105
+ max = attributes[:max]
106
+
107
+ #verify the expected attributes have been specified.
108
+ if min == nil && max == nil
109
+ raise InvalidRuleAttributes.new(LengthValidationRule, field)
110
+ end
111
+
112
+ #attempt to get the field value from the object
113
+ field_value = get_field_value(obj, field)
114
+
115
+ if field_value.is_a?(Array)
116
+ length = field_value.length
117
+ else
118
+ length = field_value.to_s.length
119
+ end
120
+
121
+ #validate the field value
122
+ if min && max
123
+ return length >= min && length <= max
124
+ elsif min
125
+ return length >= min
126
+ elsif max
127
+ return length <= max
128
+ end
129
+
130
+ return false
131
+ end
132
+
133
+ end
134
+
135
+ class MinValidationRule < ValidationRule
136
+
137
+ def error_message(field, attributes)
138
+ #check if a custom error message has been specified in the attributes
139
+ if attributes[:message] == nil
140
+ #no custom error message has been specified so create the default message.
141
+ min = attributes[:value]
142
+ "#{field} must have a minimum value of #{min}"
143
+ else
144
+ attributes[:message]
145
+ end
146
+ end
147
+
148
+ def validate(obj, field, attributes)
149
+
150
+ min = attributes[:value]
151
+
152
+ #verify the expected attributes have been specified.
153
+ if min == nil
154
+ raise InvalidRuleAttributes.new(MinValidationRule, field)
155
+ end
156
+
157
+ #attempt to get the field value from the object
158
+ field_value = get_field_value(obj, field)
159
+
160
+ if !is_required?(field_value, attributes)
161
+ return true
162
+ end
163
+
164
+ if field_value.is_a?(DateTime) || field_value.is_a?(Numeric)
165
+ field_value >= min
166
+ else
167
+ raise InvalidFieldType.new(MinValidationRule, field)
168
+ end
169
+
170
+ end
171
+
172
+ end
173
+
174
+ class MaxValidationRule < ValidationRule
175
+
176
+ def error_message(field, attributes)
177
+ #check if a custom error message has been specified in the attributes
178
+ if attributes[:message] == nil
179
+ #no custom error message has been specified so create the default message.
180
+ max = attributes[:value]
181
+ "#{field} must not have a value greater than #{max}"
182
+ else
183
+ attributes[:message]
184
+ end
185
+ end
186
+
187
+ def validate(obj, field, attributes)
188
+
189
+ max = attributes[:value]
190
+
191
+ #verify the expected attributes have been specified.
192
+ if max == nil
193
+ raise InvalidRuleAttributes.new(MinValidationRule, field)
194
+ end
195
+
196
+ #attempt to get the field value from the object
197
+ field_value = get_field_value(obj, field)
198
+
199
+ if !is_required?(field_value, attributes)
200
+ return true
201
+ end
202
+
203
+ if field_value.is_a?(DateTime) || field_value.is_a?(Numeric)
204
+ field_value <= max
205
+ else
206
+ raise InvalidFieldType.new(MaxValidationRule, field)
207
+ end
208
+
209
+ end
210
+
211
+ end
212
+
213
+ class RequiredValidationRule < ValidationRule
214
+
215
+ def error_message(field, attributes = {})
216
+ #check if a custom error message has been specified in the attributes
217
+ if attributes[:message] == nil
218
+ #no custom error message has been specified so create the default message.
219
+ "#{field} is required"
220
+ else
221
+ attributes[:message]
222
+ end
223
+ end
224
+
225
+
226
+ def validate(obj, field, attributes = {})
227
+
228
+ #attempt to get the field value from the object
229
+ field_value = get_field_value(obj, field)
230
+
231
+ if field_value == nil
232
+ return false
233
+ end
234
+
235
+ return !field_value.empty?
236
+
237
+ end
238
+
239
+ end
240
+
241
+ class EmailValidationRule < ValidationRule
242
+
243
+ REGEX = /^[^@]+@[^@]+\.[^@]+$/
244
+
245
+ def error_message(field, attributes = {})
246
+ #check if a custom error message has been specified in the attributes
247
+ if attributes[:message] == nil
248
+ #no custom error message has been specified so create the default message.
249
+ "#{field} is not a valid email address"
250
+ else
251
+ attributes[:message]
252
+ end
253
+ end
254
+
255
+
256
+ def validate(obj, field, attributes = {})
257
+
258
+ #attempt to get the field value from the object
259
+ field_value = get_field_value(obj, field)
260
+
261
+ if !is_required?(field_value, attributes)
262
+ return true
263
+ end
264
+
265
+ #validate the value against the regex
266
+ if field_value =~ REGEX
267
+ return true
268
+ end
269
+
270
+ return false
271
+
272
+ end
273
+
274
+ end
275
+
276
+ class RegexValidationRule < ValidationRule
277
+
278
+ def error_message(field, attributes = {})
279
+ #check if a custom error message has been specified in the attributes
280
+ if attributes[:message] == nil
281
+ #no custom error message has been specified so create the default message.
282
+ "#{field} is not a valid"
283
+ else
284
+ attributes[:message]
285
+ end
286
+ end
287
+
288
+
289
+ def validate(obj, field, attributes)
290
+
291
+ #attempt to get the field value from the object
292
+ field_value = get_field_value(obj, field)
293
+
294
+ regex = attributes[:regex]
295
+ if regex == nil
296
+ raise InvalidRuleAttributes.new(RegexValidationRule, field)
297
+ end
298
+
299
+ if !is_required?(field_value, attributes)
300
+ return true
301
+ end
302
+
303
+ #validate the value against the regex
304
+ if field_value =~ regex
305
+ return true
306
+ end
307
+
308
+ return false
309
+
310
+ end
311
+
312
+ end
313
+
314
+ class MatchValidationRule < ValidationRule
315
+
316
+ def error_message(field, attributes)
317
+
318
+ match_field = attributes[:field]
319
+
320
+ #check if a custom error message has been specified in the attributes
321
+ if attributes[:message] == nil
322
+ #no custom error message has been specified so create the default message.
323
+ "#{field} does not match #{match_field}"
324
+ else
325
+ attributes[:message]
326
+ end
327
+ end
328
+
329
+
330
+ def validate(obj, field, attributes)
331
+
332
+ #attempt to get the field value from the object
333
+ field_value = get_field_value(obj, field)
334
+
335
+ match_field = attributes[:field]
336
+ if match_field == nil
337
+ raise InvalidRuleAttributes.new(MatchValidationRule, field)
338
+ end
339
+
340
+ if !is_required?(field_value, attributes)
341
+ return true
342
+ end
343
+
344
+ match_field_value = get_field_value(obj, match_field)
345
+
346
+ return field_value == match_field_value
347
+
348
+ end
349
+
350
+ end
351
+
352
+ class ConditionValidationRule < ValidationRule
353
+
354
+ def error_message(field, attributes = {})
355
+
356
+ match_field = attributes[:field]
357
+
358
+ #check if a custom error message has been specified in the attributes
359
+ if attributes[:message] == nil
360
+ #no custom error message has been specified so create the default message.
361
+ "#{field} is not valid"
362
+ else
363
+ attributes[:message]
364
+ end
365
+ end
366
+
367
+
368
+ def validate(obj, field, attributes)
369
+
370
+ #attempt to get the field value from the object
371
+ value = get_field_value(obj, field)
372
+
373
+ condition_field = attributes[:condition_field]
374
+ if condition_field == nil
375
+ raise InvalidRuleAttributes.new(ConditionValidationRule, field)
376
+ end
377
+
378
+ condition_value = attributes[:condition_value]
379
+ if condition_value == nil
380
+ raise InvalidRuleAttributes.new(ConditionValidationRule, field)
381
+ end
382
+
383
+ condition_expression = attributes[:condition_expression]
384
+ if condition_expression == nil
385
+ raise InvalidRuleAttributes.new(ConditionValidationRule, field)
386
+ end
387
+
388
+ field_expression = attributes[:field_expression]
389
+ if field_expression == nil
390
+ raise InvalidRuleAttributes.new(ConditionValidationRule, field)
391
+ end
392
+
393
+ field_value = attributes[:field_value]
394
+ if field_value == nil
395
+ raise InvalidRuleAttributes.new(ConditionValidationRule, field)
396
+ end
397
+
398
+ if !is_required?(value, attributes)
399
+ return true
400
+ end
401
+
402
+ condition_field_value = get_field_value(obj, condition_field)
403
+
404
+ #check if the condition is valid
405
+ if confirm_expression?(condition_field_value, condition_expression, condition_value)
406
+ #check if the field value is correct for the condition
407
+ if confirm_expression?(value, field_expression, field_value)
408
+ return true
409
+ else
410
+ return false
411
+ end
412
+ end
413
+
414
+ return true
415
+
416
+ end
417
+
418
+ def confirm_expression?(value_a, expression, value_b)
419
+ a = value_a
420
+ b = value_b
421
+
422
+ if value_a.is_a?(String)
423
+ a = "'#{value_a}'"
424
+ elsif value_a == nil
425
+ a = 'nil'
426
+ end
427
+
428
+ if value_b.is_a?(String)
429
+ b = "'#{value_b}'"
430
+ elsif value_b == nil
431
+ b = 'nil'
432
+ end
433
+
434
+ eval("#{a} #{expression} #{b}")
435
+ end
436
+
437
+ end
@@ -0,0 +1,108 @@
1
+ require 'pry'
2
+ require_relative '../../../lib/validation_profiler/rules/rules'
3
+
4
+ # This is the manager class that holds all registered validation rules.
5
+ class ValidationRuleManager
6
+
7
+ class << self
8
+ attr_accessor :instance
9
+ end
10
+
11
+ def initialize
12
+ @rules = []
13
+ ValidationRuleManager.instance = self
14
+ load_rules
15
+ end
16
+
17
+ # This method is called to get a validation rule by it's registered key.
18
+ #
19
+ # @params key [Symbol] This is the key of the validation rule you want to request.
20
+ #
21
+ # @return [ValidationRule] This is the requested ValidationRule instance.
22
+ def get_rule(key)
23
+
24
+ results = @rules.select { |r| r[:key] == key }
25
+ if !results.empty?
26
+ results[0][:instance]
27
+ else
28
+ raise ValidationRuleNotFound.new(key)
29
+ end
30
+ end
31
+
32
+ # This method is called to add a validation rule to the manager for use.
33
+ #
34
+ # @param key [Symbol] This is the key to register the validation rule for.
35
+ # @param rule [ClassName] This is the class name of the validation rule to register.
36
+ def add_rule(key, rule)
37
+
38
+ instance = rule.new
39
+
40
+ #verify the rule instance inherits ValidationRule
41
+ if instance == nil || !instance.is_a?(ValidationRule)
42
+ raise InvalidRuleType.new(instance.class)
43
+ end
44
+
45
+ #verify the rule name has not already been registered
46
+ if !@rules.select { |r| r[:key] == key }.empty?
47
+ raise RuleAlreadyExists.new(key)
48
+ end
49
+
50
+ @rules.push({ key: key, instance: instance})
51
+
52
+ end
53
+
54
+ private
55
+
56
+ def load_rules
57
+
58
+ @rules.push({ key: :required, instance: RequiredValidationRule.new })
59
+ @rules.push({ key: :length, instance: LengthValidationRule.new })
60
+ @rules.push({ key: :min, instance: MinValidationRule.new })
61
+ @rules.push({ key: :max, instance: MaxValidationRule.new })
62
+ @rules.push({ key: :email, instance: EmailValidationRule.new })
63
+ @rules.push({ key: :regex, instance: RegexValidationRule.new })
64
+ @rules.push({ key: :match, instance: MatchValidationRule.new })
65
+ @rules.push({ key: :condition, instance: ConditionValidationRule.new })
66
+
67
+ end
68
+
69
+ end
70
+
71
+ class ValidationRuleNotFound < StandardError
72
+
73
+ def initialize(rule)
74
+ @rule = rule
75
+ end
76
+
77
+ def to_s
78
+ rule = @rule
79
+ "Validation rule: #{rule} could not be found."
80
+ end
81
+
82
+ end
83
+
84
+ class InvalidRuleType < StandardError
85
+
86
+ def initialize(rule)
87
+ @rule = rule
88
+ end
89
+
90
+ def to_s
91
+ rule = @rule
92
+ "Validation rule: #{rule} is not a valid type. Rules must inherit from the ValidationRule base class."
93
+ end
94
+
95
+ end
96
+
97
+ class RuleAlreadyExists < StandardError
98
+
99
+ def initialize(key)
100
+ @key = key
101
+ end
102
+
103
+ def to_s
104
+ key = @key
105
+ "A Rule has already been registered for the key: #{key}."
106
+ end
107
+
108
+ end
@@ -0,0 +1,3 @@
1
+ module ValidationProfiler
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,70 @@
1
+ require "validation_profiler/version"
2
+ require 'pry'
3
+ class Class
4
+
5
+ # Specifies a validation rule to use within a validation profile.
6
+ #
7
+ # @param field [Symbol] The name of the field to validate
8
+ # @param rule [Symbol] The name of the validation rule to use
9
+ # @param attributes [Hash] A has containing the validation rule options
10
+ def validates(field, rule, attributes)
11
+
12
+ if !defined?(self.validation_rules)
13
+ self.class_variable_set(:@@validation_rules, [])
14
+ end
15
+
16
+ validation_rules = self.class_variable_get(:@@validation_rules)
17
+ validation_rules.push({ name: rule, field: field, attributes: attributes })
18
+
19
+ self.class_variable_set(:@@validation_rules, validation_rules)
20
+
21
+ end
22
+
23
+ end
24
+
25
+ class ValidationManager
26
+
27
+ # Called to validate an object against a validation profile.
28
+ #
29
+ # @param obj [Object] The object to validate
30
+ # @param profile [ClassName] The class name of the validation profile to validate against
31
+ #
32
+ # @return [ValidationManagerResult] The result of the validation
33
+ def validate(obj, profile)
34
+
35
+ result = ValidationManagerResult.new
36
+
37
+ validation_rules = profile.class_variable_get(:@@validation_rules)
38
+ validation_rules.each do |r|
39
+
40
+ rule = ValidationRuleManager.instance.get_rule(r[:name])
41
+ outcome = rule.validate(obj, r[:field], r[:attributes])
42
+
43
+ if !outcome
44
+ result.outcome = false
45
+ result.errors.push({ field: r[:field], message: rule.error_message(r[:field], r[:attributes]) })
46
+ end
47
+
48
+ end
49
+
50
+ result
51
+
52
+ end
53
+
54
+ end
55
+
56
+ # This is used to specify validation results from the #validate method of the ValidationManager
57
+ class ValidationManagerResult
58
+
59
+ # @!attribute outcome
60
+ # @return [Boolean] The outcome of the validation.
61
+ attr_accessor :outcome
62
+ # @!attribute errors
63
+ # @return [Array[{:field, :error_message}]] An array of field errors that occurred during validation.
64
+ attr_accessor :errors
65
+
66
+ def initialize
67
+ @errors = []
68
+ @outcome = true
69
+ end
70
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'validation_profiler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "validation_profiler"
8
+ spec.version = ValidationProfiler::VERSION
9
+ spec.authors = ["vaughanbrittonsage"]
10
+ spec.email = ["vaughanbritton@gmail.com"]
11
+
12
+ spec.summary = 'A Validation framework for creating validation profiles, allowing for the separation & reuse of validation logic from the object being validated.'
13
+ spec.description = 'A Validation framework for creating validation profiles, allowing for the separation & reuse of validation logic from the object being validated.'
14
+ spec.homepage = "https://github.com/vaughanbrittonsage/validation_profiler"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.11"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: validation_profiler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - vaughanbrittonsage
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: A Validation framework for creating validation profiles, allowing for
56
+ the separation & reuse of validation logic from the object being validated.
57
+ email:
58
+ - vaughanbritton@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".rspec"
65
+ - CODE_OF_CONDUCT.md
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/setup
72
+ - lib/validation_profiler.rb
73
+ - lib/validation_profiler/rules/rules.rb
74
+ - lib/validation_profiler/rules/validation_rule_manager.rb
75
+ - lib/validation_profiler/version.rb
76
+ - validation_profiler.gemspec
77
+ homepage: https://github.com/vaughanbrittonsage/validation_profiler
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.5.1
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: A Validation framework for creating validation profiles, allowing for the
101
+ separation & reuse of validation logic from the object being validated.
102
+ test_files: []
103
+ has_rdoc: