validation_profiler 0.1.0

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.
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: