rulix 0.6.1 → 0.7.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: 2e084df3e38d1b62de41b00ff6c877f355df97bc
4
- data.tar.gz: 259180456844ce65f3cb529bf9acf5d412739718
3
+ metadata.gz: 3844bac00c8405d5f9b563f4ad3fffc7745c8295
4
+ data.tar.gz: 786e4e7088079f2124e2ec8cabcdd022119d9e7f
5
5
  SHA512:
6
- metadata.gz: ec4bab9011252a286da382652e8d123be64141c25dca8c68e2b20b98f82d5fbc0e780a59d9ace47b940e49ed7185d823de9f3e332f81a02eb01490a02bb36bd0
7
- data.tar.gz: 7f47d35bb94f3b1e9ae4658ade9b55849a5a6a4b03e412d4488cfb2888c9c08b7a3a08903a3b2ae39930ff5515f253cc135e0afb7bb7f98a4fc61756005ff870
6
+ metadata.gz: 26a4ac9b1353fb14d1e0177af5e7089bc729b1bd3b1f5d44f390afbb1854041bd92d8af3f4a0b9bcfffba4aa89e0b7d3efef6cae2506359ecfaebd3f8b0761d2
7
+ data.tar.gz: 1c3eb703a716b2664f2799e32a4f82ef3eaf4ff1cdee24615f22f5ab953f23c791987eddb5b12c56cc715855e79812c572835be545608e683379db200a5eb6e7
data/lib/rulix/base.rb CHANGED
@@ -5,9 +5,17 @@ module Rulix
5
5
  def self.run dataset, ruleset
6
6
  return to_enum(__callee__) unless block_given?
7
7
 
8
+ coercion = -> (result) { result }
9
+
10
+ if dataset.is_a?(Hash)
11
+ coercion = -> (result) { result.deep_stringify_keys } if String === dataset.keys.first
12
+
13
+ dataset = dataset.deep_symbolize_keys
14
+ end
15
+
8
16
  dataset = data_for_ruleset dataset, ruleset
9
17
 
10
- dataset.deep_merge ruleset do |key, data_obj, operation_obj|
18
+ result = dataset.deep_merge ruleset do |key, data_obj, operation_obj|
11
19
  if (data_obj.is_a?(Array) && data_obj.all? { |o| o.respond_to?(:deep_merge) })
12
20
  data_obj.map do |data|
13
21
  data.deep_merge operation_obj.first do |k, d, o|
@@ -22,6 +30,8 @@ module Rulix
22
30
  yield *handle_merge(data_obj, operation_obj)
23
31
  end
24
32
  end
33
+
34
+ coercion.call(result)
25
35
  end
26
36
 
27
37
  def self.handle_merge data_obj, operation_obj
@@ -1,64 +1,227 @@
1
1
  # Stole this from Rails; rather copy the source than introduce
2
2
  # ActiveSupport as a dependency
3
- class Hash
4
- # Returns a new hash with +self+ and +other_hash+ merged recursively.
5
- #
6
- # h1 = { a: true, b: { c: [1, 2, 3] } }
7
- # h2 = { a: false, b: { x: [3, 4, 5] } }
8
- #
9
- # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
10
- #
11
- # Like with Hash#merge in the standard library, a block can be provided
12
- # to merge values:
13
- #
14
- # h1 = { a: 100, b: 200, c: { c1: 100 } }
15
- # h2 = { b: 250, c: { c1: 200 } }
16
- # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
17
- # # => { a: 100, b: 450, c: { c1: 300 } }
18
- def deep_merge(other_hash, &block)
19
- dup.deep_merge!(other_hash, &block)
20
- end
21
3
 
22
- # Same as +deep_merge+, but modifies +self+.
23
- def deep_merge!(other_hash, &block)
24
- other_hash.each_pair do |current_key, other_value|
25
- this_value = self[current_key]
4
+ unless Hash.instance_methods.include? :deep_merge
5
+ class Hash
6
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
7
+ #
8
+ # h1 = { a: true, b: { c: [1, 2, 3] } }
9
+ # h2 = { a: false, b: { x: [3, 4, 5] } }
10
+ #
11
+ # h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
12
+ #
13
+ # Like with Hash#merge in the standard library, a block can be provided
14
+ # to merge values:
15
+ #
16
+ # h1 = { a: 100, b: 200, c: { c1: 100 } }
17
+ # h2 = { b: 250, c: { c1: 200 } }
18
+ # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
19
+ # # => { a: 100, b: 450, c: { c1: 300 } }
20
+ def deep_merge(other_hash, &block)
21
+ dup.deep_merge!(other_hash, &block)
22
+ end
26
23
 
27
- self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
28
- this_value.deep_merge(other_value, &block)
29
- else
30
- if block_given? && key?(current_key)
31
- block.call(current_key, this_value, other_value)
24
+ # Same as +deep_merge+, but modifies +self+.
25
+ def deep_merge!(other_hash, &block)
26
+ other_hash.each_pair do |current_key, other_value|
27
+ this_value = self[current_key]
28
+
29
+ self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
30
+ this_value.deep_merge(other_value, &block)
32
31
  else
33
- other_value
32
+ if block_given? && key?(current_key)
33
+ block.call(current_key, this_value, other_value)
34
+ else
35
+ other_value
36
+ end
34
37
  end
35
38
  end
36
- end
37
39
 
38
- self
40
+ self
41
+ end
39
42
  end
43
+ end
40
44
 
41
- # Returns a hash, removing all values that cause the block to evaluate to true
42
- # Iterates recursively over nested hashes
43
- def deep_reject &block
44
- dup.deep_reject! &block
45
- end
45
+ unless Hash.instance_methods.include? :deep_symbolize_keys
46
+ # If hash doesn't respond to :deep_symbolize_keys, we can assume
47
+ # it needs all the key transformation methods
46
48
 
47
- # Same as +deep_reject+, but modifies +self+.
48
- def deep_reject! &block
49
- each_pair do |current_key, value|
50
- this_value = self[current_key]
49
+ class Hash
50
+ # Returns a new hash with all keys converted using the +block+ operation.
51
+ #
52
+ # hash = { name: 'Rob', age: '28' }
53
+ #
54
+ # hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"}
55
+ #
56
+ # If you do not provide a +block+, it will return an Enumerator
57
+ # for chaining with other methods:
58
+ #
59
+ # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"}
60
+ def transform_keys
61
+ return enum_for(:transform_keys) { size } unless block_given?
62
+ result = {}
63
+ each_key do |key|
64
+ result[yield(key)] = self[key]
65
+ end
66
+ result
67
+ end
51
68
 
52
- if this_value.is_a?(Hash)
53
- self[current_key] = this_value.deep_reject &block
54
- else
55
- if block_given? && key?(current_key)
56
- self.delete current_key if block.call current_key, this_value
69
+ # Destructively converts all keys using the +block+ operations.
70
+ # Same as +transform_keys+ but modifies +self+.
71
+ def transform_keys!
72
+ return enum_for(:transform_keys!) { size } unless block_given?
73
+ keys.each do |key|
74
+ self[yield(key)] = delete(key)
75
+ end
76
+ self
77
+ end
78
+
79
+ # Returns a new hash with all keys converted to strings.
80
+ #
81
+ # hash = { name: 'Rob', age: '28' }
82
+ #
83
+ # hash.stringify_keys
84
+ # # => {"name"=>"Rob", "age"=>"28"}
85
+ def stringify_keys
86
+ transform_keys(&:to_s)
87
+ end
88
+
89
+ # Destructively converts all keys to strings. Same as
90
+ # +stringify_keys+, but modifies +self+.
91
+ def stringify_keys!
92
+ transform_keys!(&:to_s)
93
+ end
94
+
95
+ # Returns a new hash with all keys converted to symbols, as long as
96
+ # they respond to +to_sym+.
97
+ #
98
+ # hash = { 'name' => 'Rob', 'age' => '28' }
99
+ #
100
+ # hash.symbolize_keys
101
+ # # => {:name=>"Rob", :age=>"28"}
102
+ def symbolize_keys
103
+ transform_keys{ |key| key.to_sym rescue key }
104
+ end
105
+ alias_method :to_options, :symbolize_keys
106
+
107
+ # Destructively converts all keys to symbols, as long as they respond
108
+ # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
109
+ def symbolize_keys!
110
+ transform_keys!{ |key| key.to_sym rescue key }
111
+ end
112
+ alias_method :to_options!, :symbolize_keys!
113
+
114
+ # Validates all keys in a hash match <tt>*valid_keys</tt>, raising
115
+ # +ArgumentError+ on a mismatch.
116
+ #
117
+ # Note that keys are treated differently than HashWithIndifferentAccess,
118
+ # meaning that string and symbol keys will not match.
119
+ #
120
+ # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
121
+ # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
122
+ # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
123
+ def assert_valid_keys(*valid_keys)
124
+ valid_keys.flatten!
125
+ each_key do |k|
126
+ unless valid_keys.include?(k)
127
+ raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
57
128
  end
58
129
  end
59
130
  end
131
+
132
+ # Returns a new hash with all keys converted by the block operation.
133
+ # This includes the keys from the root hash and from all
134
+ # nested hashes and arrays.
135
+ #
136
+ # hash = { person: { name: 'Rob', age: '28' } }
137
+ #
138
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
139
+ # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
140
+ def deep_transform_keys(&block)
141
+ _deep_transform_keys_in_object(self, &block)
142
+ end
143
+
144
+ # Destructively converts all keys by using the block operation.
145
+ # This includes the keys from the root hash and from all
146
+ # nested hashes and arrays.
147
+ def deep_transform_keys!(&block)
148
+ _deep_transform_keys_in_object!(self, &block)
149
+ end
150
+
151
+ # Returns a new hash with all keys converted to strings.
152
+ # This includes the keys from the root hash and from all
153
+ # nested hashes and arrays.
154
+ #
155
+ # hash = { person: { name: 'Rob', age: '28' } }
156
+ #
157
+ # hash.deep_stringify_keys
158
+ # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
159
+ def deep_stringify_keys
160
+ deep_transform_keys(&:to_s)
161
+ end
162
+
163
+ # Destructively converts all keys to strings.
164
+ # This includes the keys from the root hash and from all
165
+ # nested hashes and arrays.
166
+ def deep_stringify_keys!
167
+ deep_transform_keys!(&:to_s)
168
+ end
169
+
170
+ # Returns a new hash with all keys converted to symbols, as long as
171
+ # they respond to +to_sym+. This includes the keys from the root hash
172
+ # and from all nested hashes and arrays.
173
+ #
174
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
175
+ #
176
+ # hash.deep_symbolize_keys
177
+ # # => {:person=>{:name=>"Rob", :age=>"28"}}
178
+ def deep_symbolize_keys
179
+ deep_transform_keys{ |key| key.to_sym rescue key }
180
+ end
181
+
182
+ # Destructively converts all keys to symbols, as long as they respond
183
+ # to +to_sym+. This includes the keys from the root hash and from all
184
+ # nested hashes and arrays.
185
+ def deep_symbolize_keys!
186
+ deep_transform_keys!{ |key| key.to_sym rescue key }
187
+ end
188
+
189
+ private
190
+ # support methods for deep transforming nested hashes and arrays
191
+ def _deep_transform_keys_in_object(object, &block)
192
+ case object
193
+ when Hash
194
+ object.each_with_object({}) do |(key, value), result|
195
+ result[yield(key)] = _deep_transform_keys_in_object(value, &block)
196
+ end
197
+ when Array
198
+ object.map {|e| _deep_transform_keys_in_object(e, &block) }
199
+ else
200
+ object
201
+ end
202
+ end
203
+
204
+ def _deep_transform_keys_in_object!(object, &block)
205
+ case object
206
+ when Hash
207
+ object.keys.each do |key|
208
+ value = object.delete(key)
209
+ object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
210
+ end
211
+ object
212
+ when Array
213
+ object.map! {|e| _deep_transform_keys_in_object!(e, &block)}
214
+ else
215
+ object
216
+ end
217
+ end
60
218
  end
219
+ end
61
220
 
221
+ # Useful hash method necessary for determining if a dataset
222
+ # is valid; we remove all empty elements until just key/vals
223
+ # with non-empty error message arrays are left
224
+ class Hash
62
225
  # Returns a hash, removing all elements that
63
226
  # respond to and return true from :empty?
64
227
  # Iterates recursively over nested hashes
@@ -0,0 +1 @@
1
+ class AllowableNil < StandardError; end
@@ -3,19 +3,21 @@ module Rulix
3
3
  include Rulix::Registry
4
4
 
5
5
  def self.run dataset, ruleset
6
- result = super dataset, ruleset do |value, operations|
7
- success, errors = operations.reduce([true, []]) do |result, op|
8
- success, errors = result
6
+ super dataset, ruleset do |value, operations|
7
+ begin
8
+ success, errors = operations.reduce([true, []]) do |result, op|
9
+ success, errors = result
9
10
 
10
- new_success, *new_errors = op.call(value)
11
+ new_success, *new_errors = op.call(value)
11
12
 
12
- [success && new_success, errors.concat(new_errors)]
13
+ [success && new_success, errors.concat(new_errors)]
14
+ end
15
+ rescue AllowableNil
16
+ errors = []
13
17
  end
14
18
 
15
19
  errors
16
20
  end
17
-
18
- result
19
21
  end
20
22
 
21
23
  def self.valid? dataset, ruleset
@@ -0,0 +1,21 @@
1
+ require 'date'
2
+
3
+ module Rulix
4
+ module Validators
5
+ class AllowNilValidator
6
+ def self.to_proc
7
+ new.method(:call)
8
+ end
9
+
10
+ def call val
11
+ if val.nil?
12
+ raise AllowableNil
13
+ else
14
+ true
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Rulix::Validator.register :allow_nil, Rulix::Validators::AllowNilValidator
@@ -12,13 +12,13 @@ module Rulix
12
12
  when Integer, Float, Bignum, Fixnum
13
13
  true
14
14
  when String
15
- /^\d+$/ === value || [false, error_message(value)]
15
+ /\A[+-]?\d+\.?\d+?\Z/ === value || [false, error_message]
16
16
  else
17
- [false, error_message(value)]
17
+ [false, error_message]
18
18
  end
19
19
  end
20
20
 
21
- def error_message value = nil
21
+ def error_message
22
22
  "is not a number"
23
23
  end
24
24
  end
@@ -0,0 +1,21 @@
1
+ module Rulix
2
+ module Validators
3
+ class StringValidator
4
+ def self.to_proc
5
+ new.method(:call)
6
+ end
7
+
8
+ def call value
9
+ return [false, error_message] unless value
10
+
11
+ String === value || [false, error_message]
12
+ end
13
+
14
+ def error_message
15
+ "is not a string"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Rulix::Validator.register :string, Rulix::Validators::StringValidator
data/lib/rulix/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rulix
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
data/lib/rulix.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative './rulix/version'
2
+ require_relative './rulix/errors'
2
3
  require_relative './rulix/registry'
3
4
  require_relative './rulix/base'
4
5
  require_relative './rulix/mutator'
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.6.1
4
+ version: 0.7.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-29 00:00:00.000000000 Z
11
+ date: 2016-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,6 +71,7 @@ files:
71
71
  - lib/rulix/active_record_validator.rb
72
72
  - lib/rulix/base.rb
73
73
  - lib/rulix/core_ext/hash.rb
74
+ - lib/rulix/errors.rb
74
75
  - lib/rulix/mutator.rb
75
76
  - lib/rulix/mutators/default.rb
76
77
  - lib/rulix/mutators/replace.rb
@@ -79,6 +80,7 @@ files:
79
80
  - lib/rulix/mutators/truncate.rb
80
81
  - lib/rulix/registry.rb
81
82
  - lib/rulix/validator.rb
83
+ - lib/rulix/validators/allow_nil_validator.rb
82
84
  - lib/rulix/validators/alpha_numeric_validator.rb
83
85
  - lib/rulix/validators/alpha_validator.rb
84
86
  - lib/rulix/validators/date_validator.rb
@@ -89,6 +91,7 @@ files:
89
91
  - lib/rulix/validators/number_validator.rb
90
92
  - lib/rulix/validators/one_of_validator.rb
91
93
  - lib/rulix/validators/required_validator.rb
94
+ - lib/rulix/validators/string_validator.rb
92
95
  - lib/rulix/version.rb
93
96
  - rulix.gemspec
94
97
  homepage: https://github.com/blarshk/rulix