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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d3a105f632d0c729e93bc8ed9cb1389f8a3386f5
4
- data.tar.gz: 1b83a610f1aed3fe46d65ff75a459e155fb3827b
3
+ metadata.gz: 77d2eb6688f2010c1c5d0457fda19e1ef4becf75
4
+ data.tar.gz: 02d79ce8f98981bbac840758380bc89e8db7f3d8
5
5
  SHA512:
6
- metadata.gz: b9e44f9296c40ccd8cfbcc5e822f30ba90b378a8e603791f3e786caedc3e0e2299c45b172913374fcffde163f7723cbc4b7694194279304d04d9d9a583c3e5b9
7
- data.tar.gz: 3b7f4e2de34f8a6a8008f12cd9ede722a79f4f08af9b516237ca373eab509cbd25007ff121f6f59191bf95acfb7df4f0a8b758ca669ce6b944befdd0485f9ffb
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: [:digits_only, :doesnt_end_in_oo]
268
+ my_field: [:number, :doesnt_end_in_oo]
242
269
  }
243
270
 
244
271
  Rulix::Validator.run data, rules
245
- #=> { my_field: ['contains non-digits', 'ends in oo'] }
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.deep_merge ruleset do |key, val1, val2|
9
- val2 = [val2] unless val2.is_a? Array
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
- ops = get_operations val2
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
- ops.reduce(val1) { |val, op| op.call(val) }
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
@@ -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 = data_for_ruleset dataset, ruleset
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(val1)
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] || min + 1
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
- (min..max).cover?(string.length) || [false, error_message(string)]
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
- /^[\d]*$/ === value || [false, error_message(value)]
15
+ /^\d+$/ === value || [false, error_message(value)]
16
16
  else
17
17
  [false, error_message(value)]
18
18
  end
@@ -2,7 +2,9 @@ module Rulix
2
2
  module Validators
3
3
  class RequiredValidator
4
4
  def call value
5
- (value && !value.nil?) || [false, 'is required']
5
+ return [false, 'is required'] unless value
6
+
7
+ true
6
8
  end
7
9
 
8
10
  def to_proc
data/lib/rulix/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rulix
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/rulix.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require_relative './rulix/version'
2
2
  require_relative './rulix/registry'
3
+ require_relative './rulix/base'
3
4
  require_relative './rulix/mutator'
4
5
  require_relative './rulix/validator'
5
6
  require_relative './rulix/active_record_validator'
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.3.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-24 00:00:00.000000000 Z
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