mutations 0.5.9 → 0.5.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -6
- data/lib/mutations/array_filter.rb +23 -19
- data/lib/mutations/boolean_filter.rb +6 -6
- data/lib/mutations/command.rb +31 -31
- data/lib/mutations/errors.rb +10 -10
- data/lib/mutations/exception.rb +2 -2
- data/lib/mutations/hash_filter.rb +35 -31
- data/lib/mutations/input_filter.rb +8 -8
- data/lib/mutations/integer_filter.rb +5 -5
- data/lib/mutations/model_filter.rb +10 -10
- data/lib/mutations/outcome.rb +1 -1
- data/lib/mutations/string_filter.rb +10 -10
- data/lib/mutations/version.rb +1 -1
- data/mutations.gemspec +1 -1
- data/spec/array_filter_spec.rb +36 -28
- data/spec/boolean_filter_spec.rb +7 -7
- data/spec/command_spec.rb +42 -42
- data/spec/errors_spec.rb +19 -19
- data/spec/hash_filter_spec.rb +28 -19
- data/spec/inheritance_spec.rb +8 -8
- data/spec/integer_filter_spec.rb +11 -11
- data/spec/model_filter_spec.rb +21 -21
- data/spec/mutations_spec.rb +2 -2
- data/spec/simple_command.rb +3 -3
- data/spec/string_filter_spec.rb +18 -18
- metadata +1 -1
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Compose your business logic into commands that sanitize and validate input. Writ
|
|
7
7
|
## Installation
|
8
8
|
|
9
9
|
gem install mutations
|
10
|
-
|
10
|
+
|
11
11
|
Or add it to your Gemfile:
|
12
12
|
|
13
13
|
gem 'mutations'
|
@@ -23,12 +23,12 @@ class UserSignup < Mutations::Command
|
|
23
23
|
string :email, matches: EMAIL_REGEX
|
24
24
|
string :name
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
# These inputs are optional
|
28
28
|
optional do
|
29
29
|
boolean :newsletter_subscribe
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
# The execute method is called only if the inputs validate. It does your business action.
|
33
33
|
def execute
|
34
34
|
user = User.create!(inputs)
|
@@ -54,7 +54,7 @@ end
|
|
54
54
|
Some things to note about the example:
|
55
55
|
|
56
56
|
* We don't need attr_accessible or strong_attributes to protect against mass assignment attacks
|
57
|
-
* We're guaranteed that within execute, the inputs will be the correct data types, even if they needed some coercion (all strings are stripped by default, and strings like "1" / "0" are converted to true/false for newsletter_subscribe)
|
57
|
+
* We're guaranteed that within execute, the inputs will be the correct data types, even if they needed some coercion (all strings are stripped by default, and strings like "1" / "0" are converted to true/false for newsletter_subscribe)
|
58
58
|
* We don't need ActiveRecord validations
|
59
59
|
* We don't need callbacks on our models -- everything is in the execute method (helper methods are also encouraged)
|
60
60
|
* We don't use accepts_nested_attributes_for, even though multiple ActiveRecord models are created
|
@@ -119,7 +119,7 @@ class CreateComment < Mutations::Command
|
|
119
119
|
model :article
|
120
120
|
string :comment, max_length: 500
|
121
121
|
end
|
122
|
-
|
122
|
+
|
123
123
|
def execute; ...; end
|
124
124
|
end
|
125
125
|
|
@@ -186,7 +186,7 @@ Your execute method has access to the inputs passed into it:
|
|
186
186
|
```ruby
|
187
187
|
self.inputs # white-listed hash of all inputs passed to run. Hash has indifferent access.
|
188
188
|
```
|
189
|
-
|
189
|
+
|
190
190
|
If you define an input called _email_, then you'll have these three methods:
|
191
191
|
|
192
192
|
```ruby
|
@@ -5,57 +5,61 @@ module Mutations
|
|
5
5
|
class: nil, # A constant or string indicates that each element of the array needs to be one of these classes
|
6
6
|
arrayize: false # true will convert "hi" to ["hi"]. "" converts to []
|
7
7
|
}
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(name, opts = {}, &block)
|
10
10
|
super(opts)
|
11
|
-
|
11
|
+
|
12
12
|
@name = name
|
13
13
|
@element_filter = nil
|
14
|
-
|
14
|
+
|
15
15
|
if block_given?
|
16
16
|
instance_eval &block
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
raise ArgumentError.new("Can't supply both a class and a filter") if @element_filter && self.options[:class]
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def string(options = {})
|
23
23
|
@element_filter = StringFilter.new(options)
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def integer(options = {})
|
27
27
|
@element_filter = IntegerFilter.new(options)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
|
+
def float(options = {})
|
31
|
+
@element_filter = FloatFilter.new(options)
|
32
|
+
end
|
33
|
+
|
30
34
|
def boolean(options = {})
|
31
35
|
@element_filter = BooleanFilter.new(options)
|
32
36
|
end
|
33
|
-
|
37
|
+
|
34
38
|
def hash(options = {}, &block)
|
35
39
|
@element_filter = HashFilter.new(options, &block)
|
36
40
|
end
|
37
|
-
|
41
|
+
|
38
42
|
# Advanced types
|
39
43
|
def model(name, options = {})
|
40
44
|
@element_filter = ModelFilter.new(name.to_sym, options)
|
41
45
|
end
|
42
|
-
|
46
|
+
|
43
47
|
def array(options = {}, &block)
|
44
48
|
@element_filter = ArrayFilter.new(nil, options, &block)
|
45
49
|
end
|
46
|
-
|
50
|
+
|
47
51
|
def filter(data)
|
48
52
|
# Handle nil case
|
49
53
|
if data.nil?
|
50
54
|
return [nil, nil] if options[:nils]
|
51
55
|
return [nil, :nils]
|
52
56
|
end
|
53
|
-
|
57
|
+
|
54
58
|
if !data.is_a?(Array) && options[:arrayize]
|
55
59
|
return [[], nil] if data == ""
|
56
60
|
data = Array(data)
|
57
61
|
end
|
58
|
-
|
62
|
+
|
59
63
|
if data.is_a?(Array)
|
60
64
|
errors = ErrorArray.new
|
61
65
|
filtered_data = []
|
@@ -63,14 +67,14 @@ module Mutations
|
|
63
67
|
data.each_with_index do |el, i|
|
64
68
|
el_filtered, el_error = filter_element(el)
|
65
69
|
el_error = ErrorAtom.new(@name, el_error, index: i) if el_error.is_a?(Symbol)
|
66
|
-
|
70
|
+
|
67
71
|
errors << el_error
|
68
72
|
found_error = true if el_error
|
69
73
|
if !found_error
|
70
74
|
filtered_data << el_filtered
|
71
75
|
end
|
72
76
|
end
|
73
|
-
|
77
|
+
|
74
78
|
if found_error
|
75
79
|
[data, errors]
|
76
80
|
else
|
@@ -80,22 +84,22 @@ module Mutations
|
|
80
84
|
return [data, :array]
|
81
85
|
end
|
82
86
|
end
|
83
|
-
|
87
|
+
|
84
88
|
# Returns [filtered, errors]
|
85
89
|
def filter_element(data)
|
86
|
-
|
90
|
+
|
87
91
|
if @element_filter
|
88
92
|
data, el_errors = @element_filter.filter(data)
|
89
93
|
return [data, el_errors] if el_errors
|
90
94
|
elsif options[:class]
|
91
95
|
class_const = options[:class]
|
92
96
|
class_const = class_const.constantize if class_const.is_a?(String)
|
93
|
-
|
97
|
+
|
94
98
|
if !data.is_a?(class_const)
|
95
99
|
return [data, :class]
|
96
100
|
end
|
97
101
|
end
|
98
|
-
|
102
|
+
|
99
103
|
[data, nil]
|
100
104
|
end
|
101
105
|
end
|
@@ -3,23 +3,23 @@ module Mutations
|
|
3
3
|
@default_options = {
|
4
4
|
nils: false # true allows an explicit nil to be valid. Overrides any other options
|
5
5
|
}
|
6
|
-
|
6
|
+
|
7
7
|
BOOL_MAP = {"true" => true, "1" => true, "false" => false, "0" => false}
|
8
|
-
|
8
|
+
|
9
9
|
def filter(data)
|
10
|
-
|
10
|
+
|
11
11
|
# Handle nil case
|
12
12
|
if data.nil?
|
13
13
|
return [nil, nil] if options[:nils]
|
14
14
|
return [nil, :nils]
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
# If data is true or false, we win.
|
18
18
|
return [data, nil] if data == true || data == false
|
19
|
-
|
19
|
+
|
20
20
|
# If data is a Fixnum, like 1, let's convert it to a string first
|
21
21
|
data = data.to_s if data.is_a?(Fixnum)
|
22
|
-
|
22
|
+
|
23
23
|
# If data's a string, try to convert it to a boolean. If we can't, it's invalid.
|
24
24
|
if data.is_a?(String)
|
25
25
|
res = BOOL_MAP[data.downcase]
|
data/lib/mutations/command.rb
CHANGED
@@ -11,51 +11,51 @@
|
|
11
11
|
|
12
12
|
module Mutations
|
13
13
|
class Command
|
14
|
-
|
14
|
+
|
15
|
+
##
|
15
16
|
##
|
16
|
-
##
|
17
17
|
##
|
18
18
|
class << self
|
19
19
|
def required(&block)
|
20
20
|
self.input_filters.required(&block)
|
21
|
-
|
21
|
+
|
22
22
|
self.input_filters.required_keys.each do |key|
|
23
|
-
define_method(key) do
|
23
|
+
define_method(key) do
|
24
24
|
@filtered_input[key]
|
25
25
|
end
|
26
|
-
|
27
|
-
define_method("#{key}_present?") do
|
26
|
+
|
27
|
+
define_method("#{key}_present?") do
|
28
28
|
@filtered_input.has_key?(key)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
define_method("#{key}=") do |v|
|
32
32
|
@filtered_input[key] = v
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def optional(&block)
|
38
38
|
self.input_filters.optional(&block)
|
39
|
-
|
39
|
+
|
40
40
|
self.input_filters.optional_keys.each do |key|
|
41
|
-
define_method(key) do
|
41
|
+
define_method(key) do
|
42
42
|
@filtered_input[key]
|
43
43
|
end
|
44
|
-
|
45
|
-
define_method("#{key}_present?") do
|
44
|
+
|
45
|
+
define_method("#{key}_present?") do
|
46
46
|
@filtered_input.has_key?(key)
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
define_method("#{key}=") do |v|
|
50
50
|
@filtered_input[key] = v
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
def run(*args)
|
56
56
|
new(*args).execute!
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
def run!(*args)
|
60
60
|
m = run(*args)
|
61
61
|
if m.success?
|
@@ -64,12 +64,12 @@ module Mutations
|
|
64
64
|
raise ValidationException.new(m.errors)
|
65
65
|
end
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
# Validates input, but doesn't call execute. Returns an Outcome with errors anyway.
|
69
69
|
def validate(*args)
|
70
70
|
new(*args).validation_outcome
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
def input_filters
|
74
74
|
@input_filters ||= begin
|
75
75
|
if Command == self.superclass
|
@@ -79,9 +79,9 @@ module Mutations
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
# Instance methods
|
86
86
|
def initialize(*args)
|
87
87
|
if args.length == 0
|
@@ -91,24 +91,24 @@ module Mutations
|
|
91
91
|
raise ArgumentError.new("All arguments must be hashes") unless @original_hash.is_a?(Hash)
|
92
92
|
@original_hash = @original_hash.with_indifferent_access
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
args.each do |a|
|
96
96
|
raise ArgumentError.new("All arguments must be hashes") unless a.is_a?(Hash)
|
97
97
|
@original_hash.merge!(a)
|
98
98
|
end
|
99
|
-
|
99
|
+
|
100
100
|
@filtered_input, @errors = self.input_filters.filter(@original_hash)
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
def input_filters
|
104
104
|
self.class.input_filters
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
def execute!
|
108
108
|
return Outcome.new(false, nil, @errors) if @errors
|
109
|
-
|
109
|
+
|
110
110
|
# IDEA/TODO: run validate block
|
111
|
-
|
111
|
+
|
112
112
|
r = execute
|
113
113
|
if @errors # Execute can add errors
|
114
114
|
return Outcome.new(false, nil, @errors)
|
@@ -116,7 +116,7 @@ module Mutations
|
|
116
116
|
return Outcome.new(true, r, nil)
|
117
117
|
end
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
# Runs input thru the filter and sets @filtered_input and @errors
|
121
121
|
def validation_outcome
|
122
122
|
if @errors
|
@@ -125,14 +125,14 @@ module Mutations
|
|
125
125
|
Outcome.new(true, nil, nil)
|
126
126
|
end
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
# add_error("name", :too_short)
|
130
130
|
# add_error("colors.foreground", :not_a_color) # => to create errors = {colors: {foreground: :not_a_color}}
|
131
131
|
# or, supply a custom message:
|
132
132
|
# add_error("name", :too_short, "The name 'blahblahblah' is too short!")
|
133
133
|
def add_error(key, kind, message = nil)
|
134
134
|
raise ArgumentError.new("Invalid kind") unless kind.is_a?(Symbol)
|
135
|
-
|
135
|
+
|
136
136
|
@errors ||= ErrorHash.new
|
137
137
|
cur_errors = @errors
|
138
138
|
parts = key.to_s.split(".")
|
@@ -147,16 +147,16 @@ module Mutations
|
|
147
147
|
end
|
148
148
|
@errors
|
149
149
|
end
|
150
|
-
|
150
|
+
|
151
151
|
def merge_errors(hash)
|
152
152
|
@errors ||= ErrorHash.new
|
153
153
|
@errors.merge!(hash)
|
154
154
|
end
|
155
|
-
|
155
|
+
|
156
156
|
def inputs
|
157
157
|
@filtered_input
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
160
|
def execute
|
161
161
|
# Meant to be overridden
|
162
162
|
end
|
data/lib/mutations/errors.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
module Mutations
|
2
|
-
|
2
|
+
|
3
3
|
# Offers a non-localized, english only, non configurable way to get error messages. This probably isnt good enough for users as-is.
|
4
4
|
class DefaultErrorMessageCreator
|
5
|
-
|
5
|
+
|
6
6
|
MESSAGES = Hash.new("is invalid").tap do |h|
|
7
7
|
h.merge!(
|
8
8
|
# General
|
9
9
|
nils: "can't be nil",
|
10
10
|
required: "is required",
|
11
|
-
|
11
|
+
|
12
12
|
# Datatypes
|
13
13
|
string: "isn't a string",
|
14
14
|
integer: "isn't an integer",
|
@@ -16,26 +16,26 @@ module Mutations
|
|
16
16
|
hash: "isn't a hash",
|
17
17
|
array: "isn't an array",
|
18
18
|
model: "isn't the right class",
|
19
|
-
|
19
|
+
|
20
20
|
# String
|
21
21
|
empty: "can't be blank",
|
22
22
|
max_length: "is too long",
|
23
23
|
min_length: "is too short",
|
24
24
|
matches: "isn't in the right format",
|
25
25
|
in: "isn't an option",
|
26
|
-
|
26
|
+
|
27
27
|
# Array
|
28
28
|
class: "isn't the right class",
|
29
|
-
|
29
|
+
|
30
30
|
# Integer
|
31
31
|
min: "is too small",
|
32
32
|
max: "is too big",
|
33
|
-
|
33
|
+
|
34
34
|
# Model
|
35
35
|
new_records: "isn't a saved model"
|
36
36
|
)
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
# key: the name of the field, eg, :email. Could be nil if it's an array element
|
40
40
|
# error_symbol: the validation symbol, eg, :matches or :required
|
41
41
|
# options:
|
@@ -49,7 +49,7 @@ module Mutations
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
|
52
|
+
|
53
53
|
class ErrorAtom
|
54
54
|
|
55
55
|
# NOTE: in the future, could also pass in:
|
@@ -138,7 +138,7 @@ module Mutations
|
|
138
138
|
list
|
139
139
|
end
|
140
140
|
end
|
141
|
-
|
141
|
+
|
142
142
|
class ErrorArray < Array
|
143
143
|
def symbolic
|
144
144
|
map {|e| e && e.symbolic }
|
data/lib/mutations/exception.rb
CHANGED
@@ -3,22 +3,22 @@ module Mutations
|
|
3
3
|
@default_options = {
|
4
4
|
nils: false, # true allows an explicit nil to be valid. Overrides any other options
|
5
5
|
}
|
6
|
-
|
6
|
+
|
7
7
|
attr_accessor :optional_inputs
|
8
8
|
attr_accessor :required_inputs
|
9
|
-
|
9
|
+
|
10
10
|
def initialize(opts = {}, &block)
|
11
11
|
super(opts)
|
12
|
-
|
12
|
+
|
13
13
|
@optional_inputs = {}
|
14
14
|
@required_inputs = {}
|
15
15
|
@current_inputs = @required_inputs
|
16
|
-
|
16
|
+
|
17
17
|
if block_given?
|
18
18
|
instance_eval &block
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def dup
|
23
23
|
dupped = HashFilter.new
|
24
24
|
@optional_inputs.each_pair do |k, v|
|
@@ -29,96 +29,100 @@ module Mutations
|
|
29
29
|
end
|
30
30
|
dupped
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def required(&block)
|
34
34
|
# TODO: raise if nesting is wrong
|
35
35
|
@current_inputs = @required_inputs
|
36
36
|
instance_eval &block
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def optional(&block)
|
40
40
|
# TODO: raise if nesting is wrong
|
41
41
|
@current_inputs = @optional_inputs
|
42
42
|
instance_eval &block
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def required_keys
|
46
46
|
@required_inputs.keys
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
def optional_keys
|
50
50
|
@optional_inputs.keys
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
# Basic types:
|
54
54
|
def string(name, options = {})
|
55
55
|
@current_inputs[name.to_sym] = StringFilter.new(options)
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
def integer(name, options = {})
|
59
59
|
@current_inputs[name.to_sym] = IntegerFilter.new(options)
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
|
+
def float(name, options = {})
|
63
|
+
@current_inputs[name.to_sym] = FloatFilter.new(options)
|
64
|
+
end
|
65
|
+
|
62
66
|
def boolean(name, options = {})
|
63
67
|
@current_inputs[name.to_sym] = BooleanFilter.new(options)
|
64
68
|
end
|
65
|
-
|
69
|
+
|
66
70
|
def hash(name, options = {}, &block)
|
67
71
|
@current_inputs[name.to_sym] = HashFilter.new(options, &block)
|
68
72
|
end
|
69
|
-
|
73
|
+
|
70
74
|
# Advanced types
|
71
75
|
def model(name, options = {})
|
72
76
|
name_sym = name.to_sym
|
73
77
|
@current_inputs[name_sym] = ModelFilter.new(name_sym, options)
|
74
78
|
end
|
75
|
-
|
79
|
+
|
76
80
|
def array(name, options = {}, &block)
|
77
81
|
name_sym = name.to_sym
|
78
82
|
@current_inputs[name.to_sym] = ArrayFilter.new(name_sym, options, &block)
|
79
83
|
end
|
80
|
-
|
84
|
+
|
81
85
|
def filter(data)
|
82
|
-
|
86
|
+
|
83
87
|
# Handle nil case
|
84
88
|
if data.nil?
|
85
89
|
return [nil, nil] if options[:nils]
|
86
90
|
return [nil, :nils]
|
87
91
|
end
|
88
|
-
|
92
|
+
|
89
93
|
# Ensure it's a hash
|
90
94
|
return [data, :hash] unless data.is_a?(Hash)
|
91
|
-
|
95
|
+
|
92
96
|
# We always want a hash with indiffernet access
|
93
97
|
unless data.is_a?(HashWithIndifferentAccess)
|
94
98
|
data = data.with_indifferent_access
|
95
99
|
end
|
96
|
-
|
100
|
+
|
97
101
|
errors = ErrorHash.new
|
98
102
|
filtered_data = HashWithIndifferentAccess.new
|
99
103
|
wildcard_filterer = nil
|
100
|
-
|
104
|
+
|
101
105
|
[[@required_inputs, true], [@optional_inputs, false]].each do |(inputs, is_required)|
|
102
106
|
inputs.each_pair do |key, filterer|
|
103
|
-
|
107
|
+
|
104
108
|
# If we are doing wildcards, then record so and move on
|
105
109
|
if key == :*
|
106
110
|
wildcard_filterer = filterer
|
107
111
|
next
|
108
112
|
end
|
109
|
-
|
113
|
+
|
110
114
|
data_element = data[key]
|
111
|
-
|
115
|
+
|
112
116
|
# First, discard optional nils/empty params
|
113
117
|
data.delete(key) if !is_required && data.has_key?(key) && filterer.discard_nils? && data_element.nil?
|
114
118
|
data.delete(key) if !is_required && data.has_key?(key) && filterer.discard_empty? && data_element == ""
|
115
|
-
|
119
|
+
|
116
120
|
default_used = false
|
117
121
|
if !data.has_key?(key) && filterer.has_default?
|
118
122
|
data_element = filterer.default
|
119
123
|
default_used = true
|
120
124
|
end
|
121
|
-
|
125
|
+
|
122
126
|
if data.has_key?(key) || default_used
|
123
127
|
sub_data, sub_error = filterer.filter(data_element)
|
124
128
|
|
@@ -133,17 +137,17 @@ module Mutations
|
|
133
137
|
end
|
134
138
|
end
|
135
139
|
end
|
136
|
-
|
140
|
+
|
137
141
|
if wildcard_filterer
|
138
142
|
filtered_keys = data.keys - filtered_data.keys
|
139
|
-
|
143
|
+
|
140
144
|
filtered_keys.each do |key|
|
141
145
|
data_element = data[key]
|
142
|
-
|
146
|
+
|
143
147
|
# First, discard optional nils/empty params
|
144
148
|
next if data.has_key?(key) && wildcard_filterer.discard_nils? && data_element.nil?
|
145
149
|
next if data.has_key?(key) && wildcard_filterer.discard_empty? && data_element == ""
|
146
|
-
|
150
|
+
|
147
151
|
sub_data, sub_error = wildcard_filterer.filter(data_element)
|
148
152
|
if sub_error.nil?
|
149
153
|
filtered_data[key] = sub_data
|
@@ -153,7 +157,7 @@ module Mutations
|
|
153
157
|
end
|
154
158
|
end
|
155
159
|
end
|
156
|
-
|
160
|
+
|
157
161
|
if errors.any?
|
158
162
|
[data, errors]
|
159
163
|
else
|
@@ -1,36 +1,36 @@
|
|
1
1
|
module Mutations
|
2
2
|
class InputFilter
|
3
3
|
@default_options = {}
|
4
|
-
|
4
|
+
|
5
5
|
def self.default_options
|
6
6
|
@default_options
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
attr_accessor :options
|
10
|
-
|
10
|
+
|
11
11
|
def initialize(opts = {})
|
12
12
|
self.options = (self.class.default_options || {}).merge(opts)
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
# returns -> [sanitized data, error]
|
16
16
|
# If an error is returned, then data will be nil
|
17
17
|
def filter(data)
|
18
18
|
[data, nil]
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def has_default?
|
22
22
|
options.has_key?(:default)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def default
|
26
26
|
options[:default]
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
# Only relevant for optional params
|
30
30
|
def discard_nils?
|
31
31
|
!options[:nils]
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def discard_empty?
|
35
35
|
options[:discard_empty]
|
36
36
|
end
|