rulix 0.3.0 → 0.4.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 +4 -4
- data/README.md +34 -7
- data/lib/rulix/base.rb +56 -0
- data/lib/rulix/mutator.rb +37 -7
- data/lib/rulix/mutators/default.rb +25 -0
- data/lib/rulix/validator.rb +4 -49
- data/lib/rulix/validators/date_validator.rb +31 -0
- data/lib/rulix/validators/length_validator.rb +8 -2
- data/lib/rulix/validators/number_validator.rb +2 -2
- data/lib/rulix/validators/required_validator.rb +3 -1
- data/lib/rulix/version.rb +1 -1
- data/lib/rulix.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77d2eb6688f2010c1c5d0457fda19e1ef4becf75
|
|
4
|
+
data.tar.gz: 02d79ce8f98981bbac840758380bc89e8db7f3d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de1c6349f17be45abe08bd63c53484d5594c6a6ec226b62a1c67a4e8f199c363404dd8339dcc5ea4fc2776b30aae7e9b198765a398dd38db18a3fb725b31c77c
|
|
7
|
+
data.tar.gz: 160e46d5561b930ce12d451a1d4789ec735c3b37c896f57be41214fde7a79bcaac3e629c24b1403b4e840efa044ad979b9be21a71c91b095e72dc99244eb4aa4
|
data/README.md
CHANGED
|
@@ -88,7 +88,11 @@ Some rules may need additional definition or configuration based on context. If
|
|
|
88
88
|
|
|
89
89
|
```ruby
|
|
90
90
|
# Configurable proc
|
|
91
|
-
-> (options, value) { value[0..options[:trim_to] - 1] }
|
|
91
|
+
trimmer = -> (options, value) { value[0..options[:trim_to] - 1] }
|
|
92
|
+
|
|
93
|
+
Rulix::Mutator.register :trim, trimmer
|
|
94
|
+
|
|
95
|
+
{ first_name: { trim: { trim_to: 5 } } }
|
|
92
96
|
|
|
93
97
|
# Configured procable object
|
|
94
98
|
class CharacterRemover
|
|
@@ -176,6 +180,8 @@ Rulix::Mutator.run dataset, ruleset
|
|
|
176
180
|
#=> { first_name: 'Bob', last_name: 'Johnson' }
|
|
177
181
|
```
|
|
178
182
|
|
|
183
|
+
See the wiki for a [list of included mutator functions](https://github.com/blarshk/rulix/wiki/List-of-Included-Mutator-Functions).
|
|
184
|
+
|
|
179
185
|
## Validation
|
|
180
186
|
|
|
181
187
|
Use `Rulix::Validator` to validate a dataset. You can test if a dataset is valid with `Rulix::Validator.valid?(dataset, ruleset)`. If your dataset fails validation, you can extract errors with `Rulix::Validator.errors(dataset, ruleset)`
|
|
@@ -199,6 +205,31 @@ Rulix::Validator.errors dataset, ruleset
|
|
|
199
205
|
#=> { phone: { number: ['does not match format'] } }
|
|
200
206
|
```
|
|
201
207
|
|
|
208
|
+
See the wiki for a [list of included validation functions](https://github.com/blarshk/rulix/wiki/List-of-Included-Validation-Functions). If you have a validation function that you like that you think should be included in Rulix's base set, submit a pull request!
|
|
209
|
+
|
|
210
|
+
### Validating with ActiveRecord
|
|
211
|
+
|
|
212
|
+
Rulix comes with a plugin class that you can use to validate ActiveRecord models.
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
class User < ActiveRecord::Base
|
|
216
|
+
validates :email, uniqueness: true
|
|
217
|
+
|
|
218
|
+
validates_with Rulix::ActiveRecordValidator, ruleset: {
|
|
219
|
+
email: { format: /.*@.*/, message: 'is not an email address' }
|
|
220
|
+
}
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
user = User.new email: 'foobar'
|
|
224
|
+
|
|
225
|
+
user.valid?
|
|
226
|
+
#=> false
|
|
227
|
+
user.errors.full_messages
|
|
228
|
+
#=> ['Email is not an email address']
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The adapter can be used alongside ActiveRecord's own validators, so you can keep using ActiveRecord to validate the things it's good at, and delegate the rest to Rulix!
|
|
232
|
+
|
|
202
233
|
## Custom Validators and Mutators
|
|
203
234
|
|
|
204
235
|
You can write your own mutators and validators and make them available to Rulix for building rulesets as long as they implement their respective interfaces.
|
|
@@ -229,20 +260,16 @@ Rulix::Validator.register :doesnt_end_in_oo do |val|
|
|
|
229
260
|
val.end_with?('oo') ? [false, 'ends in oo'] : true
|
|
230
261
|
end
|
|
231
262
|
|
|
232
|
-
Rulix::Validator.register :digits_only do |val|
|
|
233
|
-
/^[0-9]+$/ === val ? true : [false, 'contains non-digits']
|
|
234
|
-
end
|
|
235
|
-
|
|
236
263
|
data = {
|
|
237
264
|
my_field: 'foo'
|
|
238
265
|
}
|
|
239
266
|
|
|
240
267
|
rules = {
|
|
241
|
-
my_field: [:
|
|
268
|
+
my_field: [:number, :doesnt_end_in_oo]
|
|
242
269
|
}
|
|
243
270
|
|
|
244
271
|
Rulix::Validator.run data, rules
|
|
245
|
-
#=> { my_field: ['
|
|
272
|
+
#=> { my_field: ['is not a number', 'ends in oo'] }
|
|
246
273
|
```
|
|
247
274
|
|
|
248
275
|
## Contributing
|
data/lib/rulix/base.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Rulix
|
|
2
|
+
class Base
|
|
3
|
+
include Rulix::Registry
|
|
4
|
+
|
|
5
|
+
def self.run dataset, ruleset
|
|
6
|
+
return to_enum(__callee__) unless block_given?
|
|
7
|
+
|
|
8
|
+
dataset = data_for_ruleset dataset, ruleset
|
|
9
|
+
|
|
10
|
+
dataset.deep_merge ruleset do |key, val1, val2|
|
|
11
|
+
val2 = [val2] unless val2.is_a? Array
|
|
12
|
+
|
|
13
|
+
ops = get_operations val2
|
|
14
|
+
|
|
15
|
+
yield val1, ops
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.data_for_ruleset dataset, ruleset
|
|
20
|
+
seed = {}
|
|
21
|
+
|
|
22
|
+
reduce_into_hash seed, dataset, ruleset
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.reduce_into_hash hash, dataset, ruleset
|
|
26
|
+
ruleset.reduce(hash) do |data_hash, values|
|
|
27
|
+
key, rule_val = values
|
|
28
|
+
|
|
29
|
+
nested_value = value_from_dataset dataset, key
|
|
30
|
+
|
|
31
|
+
if rule_val.is_a?(Hash) && (nested_value.is_a?(Hash) || rule_val.keys.all? { |k| nested_value.respond_to?(k) })
|
|
32
|
+
seed = {}
|
|
33
|
+
|
|
34
|
+
data_hash[key] = reduce_into_hash seed, nested_value, rule_val
|
|
35
|
+
else
|
|
36
|
+
data_hash[key] = nested_value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
data_hash
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.value_from_dataset dataset, key
|
|
44
|
+
# Not a fan of type-checking here, would rather do a :respond_to? :[]
|
|
45
|
+
# However, that scenario breaks when validating sensitive data
|
|
46
|
+
# on an ActiveRecord model (like password) or any other attribute
|
|
47
|
+
# that is manipulated in the biz logic layer before being set on
|
|
48
|
+
# the db (e.g.: Model(:ssn) -> Database(:encrypted_ssn))
|
|
49
|
+
if dataset.is_a? Hash
|
|
50
|
+
dataset[key]
|
|
51
|
+
else
|
|
52
|
+
dataset.public_send(key)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/rulix/mutator.rb
CHANGED
|
@@ -1,16 +1,46 @@
|
|
|
1
|
-
require_relative './registry'
|
|
2
|
-
|
|
3
1
|
module Rulix
|
|
4
|
-
class Mutator
|
|
2
|
+
class Mutator < Rulix::Base
|
|
5
3
|
include Rulix::Registry
|
|
6
4
|
|
|
7
5
|
def self.run dataset, ruleset
|
|
8
|
-
dataset
|
|
9
|
-
|
|
6
|
+
super dataset, ruleset do |value, operations|
|
|
7
|
+
operations.reduce(value) { |val, op| op.call(val) }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.reduce_into_hash hash, dataset, ruleset
|
|
12
|
+
# Migrate over any keys the ruleset has that the dataset doesn't
|
|
13
|
+
# so we can set defaults and handle cases where no data exists
|
|
14
|
+
if dataset.respond_to? :keys
|
|
15
|
+
key_diff = ruleset.keys - dataset.keys
|
|
16
|
+
|
|
17
|
+
key_diff.each do |key|
|
|
18
|
+
hash[key] = nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
10
21
|
|
|
11
|
-
|
|
22
|
+
new_ruleset = ruleset.reduce(hash) do |data_hash, values|
|
|
23
|
+
key, rule_val = values
|
|
24
|
+
|
|
25
|
+
nested_value = value_from_dataset dataset, key
|
|
26
|
+
|
|
27
|
+
if rule_val.is_a?(Hash) && (nested_value.is_a?(Hash) || rule_val.keys.all? { |k| nested_value.respond_to?(k) })
|
|
28
|
+
seed = {}
|
|
29
|
+
|
|
30
|
+
data_hash[key] = reduce_into_hash seed, nested_value, rule_val
|
|
31
|
+
else
|
|
32
|
+
data_hash[key] = nested_value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
data_hash
|
|
36
|
+
end
|
|
12
37
|
|
|
13
|
-
|
|
38
|
+
# For mutation, we need to ensure that the resulting dataset
|
|
39
|
+
# contains all original, unmodified values as well as mutated values
|
|
40
|
+
if dataset.is_a? Hash
|
|
41
|
+
new_ruleset.deep_merge dataset
|
|
42
|
+
else
|
|
43
|
+
new_ruleset
|
|
14
44
|
end
|
|
15
45
|
end
|
|
16
46
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Rulix
|
|
2
|
+
module Mutators
|
|
3
|
+
class Default
|
|
4
|
+
attr_accessor :default_val
|
|
5
|
+
|
|
6
|
+
def initialize default_val
|
|
7
|
+
self.default_val = default_val
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_proc
|
|
11
|
+
method(:call)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call string
|
|
15
|
+
if string
|
|
16
|
+
string
|
|
17
|
+
else
|
|
18
|
+
default_val
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Rulix::Mutator.register :default, Rulix::Mutators::Default
|
data/lib/rulix/validator.rb
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
module Rulix
|
|
2
|
-
class Validator
|
|
2
|
+
class Validator < Rulix::Base
|
|
3
3
|
include Rulix::Registry
|
|
4
4
|
|
|
5
5
|
def self.run dataset, ruleset
|
|
6
|
-
dataset
|
|
7
|
-
|
|
8
|
-
dataset.deep_merge ruleset do |key, val1, val2|
|
|
9
|
-
val2 = [val2] unless val2.is_a? Array
|
|
10
|
-
|
|
11
|
-
ops = get_operations val2
|
|
12
|
-
|
|
13
|
-
success, errors = ops.reduce([true, []]) do |result, op|
|
|
6
|
+
super dataset, ruleset do |value, operations|
|
|
7
|
+
success, errors = operations.reduce([true, []]) do |result, op|
|
|
14
8
|
success, errors = result
|
|
15
9
|
|
|
16
|
-
new_success, *new_errors = op.call(
|
|
10
|
+
new_success, *new_errors = op.call(value)
|
|
17
11
|
|
|
18
12
|
[success && new_success, errors.concat(new_errors)]
|
|
19
13
|
end
|
|
@@ -33,44 +27,5 @@ module Rulix
|
|
|
33
27
|
|
|
34
28
|
run.deep_compact
|
|
35
29
|
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
def self.data_for_ruleset dataset, ruleset
|
|
40
|
-
seed = {}
|
|
41
|
-
|
|
42
|
-
reduce_into_hash seed, dataset, ruleset
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def self.reduce_into_hash hash, dataset, ruleset
|
|
46
|
-
ruleset.reduce(hash) do |data_hash, values|
|
|
47
|
-
key, rule_val = values
|
|
48
|
-
|
|
49
|
-
nested_value = value_from_dataset dataset, key
|
|
50
|
-
|
|
51
|
-
if rule_val.is_a?(Hash) && (nested_value.is_a?(Hash) || rule_val.keys.all? { |k| nested_value.respond_to?(k) })
|
|
52
|
-
seed = {}
|
|
53
|
-
|
|
54
|
-
data_hash[key] = reduce_into_hash seed, nested_value, rule_val
|
|
55
|
-
else
|
|
56
|
-
data_hash[key] = nested_value
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
data_hash
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def self.value_from_dataset dataset, key
|
|
64
|
-
# Not a fan of type-checking here, would rather do a :respond_to? :[]
|
|
65
|
-
# However, that scenario breaks when validating sensitive data
|
|
66
|
-
# on an ActiveRecord model (like password) or any other attribute
|
|
67
|
-
# that is manipulated in the biz logic layer before being set on
|
|
68
|
-
# the db (e.g.: Model(:ssn) -> Database(:encrypted_ssn))
|
|
69
|
-
if dataset.is_a? Hash
|
|
70
|
-
dataset[key]
|
|
71
|
-
else
|
|
72
|
-
dataset.public_send(key)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
30
|
end
|
|
76
31
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
|
|
3
|
+
module Rulix
|
|
4
|
+
module Validators
|
|
5
|
+
class DateValidator
|
|
6
|
+
def self.to_proc
|
|
7
|
+
new.method(:call)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call date
|
|
11
|
+
if date
|
|
12
|
+
begin
|
|
13
|
+
Date.parse date
|
|
14
|
+
|
|
15
|
+
true
|
|
16
|
+
rescue ArgumentError
|
|
17
|
+
[false, "is not a date"]
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
[false, 'is not a date']
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def error_message value = nil
|
|
25
|
+
"is not a date"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Rulix::Validator.register :date, Rulix::Validators::DateValidator
|
|
@@ -6,7 +6,7 @@ module Rulix
|
|
|
6
6
|
def initialize options = nil
|
|
7
7
|
options ||= {}
|
|
8
8
|
min = options[:min] || 0
|
|
9
|
-
max = options[:max]
|
|
9
|
+
max = options[:max]
|
|
10
10
|
|
|
11
11
|
self.exactly = options[:exactly]
|
|
12
12
|
self.min = min
|
|
@@ -17,7 +17,13 @@ module Rulix
|
|
|
17
17
|
return [false, "can't be nil"] unless string
|
|
18
18
|
|
|
19
19
|
if exactly.nil?
|
|
20
|
-
|
|
20
|
+
if min && max
|
|
21
|
+
(min..max).cover?(string.length) || [false, error_message(string)]
|
|
22
|
+
elsif min && !max
|
|
23
|
+
string.length >= min || [false, error_message(string)]
|
|
24
|
+
elsif max && !min
|
|
25
|
+
string.length <= max || [false, error_message(string)]
|
|
26
|
+
end
|
|
21
27
|
else
|
|
22
28
|
exactly == string.length || [false, exact_error_message]
|
|
23
29
|
end
|
|
@@ -9,10 +9,10 @@ module Rulix
|
|
|
9
9
|
return [false, error_message] unless value
|
|
10
10
|
|
|
11
11
|
case value
|
|
12
|
-
when Integer, Fixnum
|
|
12
|
+
when Integer, Float, Bignum, Fixnum
|
|
13
13
|
true
|
|
14
14
|
when String
|
|
15
|
-
|
|
15
|
+
/^\d+$/ === value || [false, error_message(value)]
|
|
16
16
|
else
|
|
17
17
|
[false, error_message(value)]
|
|
18
18
|
end
|
data/lib/rulix/version.rb
CHANGED
data/lib/rulix.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rulix
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mitch Monsen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-06-
|
|
11
|
+
date: 2016-06-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -69,8 +69,10 @@ files:
|
|
|
69
69
|
- bin/setup
|
|
70
70
|
- lib/rulix.rb
|
|
71
71
|
- lib/rulix/active_record_validator.rb
|
|
72
|
+
- lib/rulix/base.rb
|
|
72
73
|
- lib/rulix/core_ext/hash.rb
|
|
73
74
|
- lib/rulix/mutator.rb
|
|
75
|
+
- lib/rulix/mutators/default.rb
|
|
74
76
|
- lib/rulix/mutators/replace.rb
|
|
75
77
|
- lib/rulix/mutators/squeeze_spaces.rb
|
|
76
78
|
- lib/rulix/mutators/strip.rb
|
|
@@ -79,6 +81,7 @@ files:
|
|
|
79
81
|
- lib/rulix/validator.rb
|
|
80
82
|
- lib/rulix/validators/alpha_numeric_validator.rb
|
|
81
83
|
- lib/rulix/validators/alpha_validator.rb
|
|
84
|
+
- lib/rulix/validators/date_validator.rb
|
|
82
85
|
- lib/rulix/validators/email_validator.rb
|
|
83
86
|
- lib/rulix/validators/format_validator.rb
|
|
84
87
|
- lib/rulix/validators/length_validator.rb
|