mutations 0.5.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.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +16 -0
- data/README.md +271 -0
- data/Rakefile +8 -0
- data/lib/mutations.rb +24 -0
- data/lib/mutations/array_filter.rb +102 -0
- data/lib/mutations/boolean_filter.rb +33 -0
- data/lib/mutations/command.rb +149 -0
- data/lib/mutations/errors.rb +152 -0
- data/lib/mutations/exception.rb +13 -0
- data/lib/mutations/hash_filter.rb +131 -0
- data/lib/mutations/input_filter.rb +29 -0
- data/lib/mutations/integer_filter.rb +34 -0
- data/lib/mutations/model_filter.rb +52 -0
- data/lib/mutations/outcome.rb +19 -0
- data/lib/mutations/string_filter.rb +54 -0
- data/lib/mutations/version.rb +3 -0
- data/mutations.gemspec +19 -0
- data/spec/array_filter_spec.rb +150 -0
- data/spec/boolean_filter_spec.rb +55 -0
- data/spec/command_spec.rb +183 -0
- data/spec/default_spec.rb +0 -0
- data/spec/errors_spec.rb +93 -0
- data/spec/inheritance_spec.rb +39 -0
- data/spec/integer_filter_spec.rb +76 -0
- data/spec/model_filter_spec.rb +92 -0
- data/spec/mutations_spec.rb +9 -0
- data/spec/simple_command.rb +15 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/string_filter_spec.rb +138 -0
- metadata +108 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module Mutations
|
2
|
+
class InputFilter
|
3
|
+
@default_options = {}
|
4
|
+
|
5
|
+
def self.default_options
|
6
|
+
@default_options
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :options
|
10
|
+
|
11
|
+
def initialize(opts = {})
|
12
|
+
self.options = (self.class.default_options || {}).merge(opts)
|
13
|
+
end
|
14
|
+
|
15
|
+
# returns -> [sanitized data, error]
|
16
|
+
# If an error is returned, then data will be nil
|
17
|
+
def filter(data)
|
18
|
+
[data, nil]
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_default?
|
22
|
+
self.options.has_key?(:default)
|
23
|
+
end
|
24
|
+
|
25
|
+
def default
|
26
|
+
self.options[:default]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mutations
|
2
|
+
class IntegerFilter < InputFilter
|
3
|
+
@default_options = {
|
4
|
+
nils: false, # true allows an explicit nil to be valid. Overrides any other options
|
5
|
+
# TODO: add strict
|
6
|
+
min: nil, # lowest value, inclusive
|
7
|
+
max: nil # highest value, inclusive
|
8
|
+
}
|
9
|
+
|
10
|
+
def filter(data)
|
11
|
+
|
12
|
+
# Handle nil case
|
13
|
+
if data.nil?
|
14
|
+
return [nil, nil] if options[:nils]
|
15
|
+
return [nil, :nils]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Ensure it's the correct data type (Fixnum)
|
19
|
+
if !data.is_a?(Fixnum)
|
20
|
+
if data.is_a?(String) && data =~ /^-?\d/
|
21
|
+
data = data.to_i
|
22
|
+
else
|
23
|
+
return [data, :integer]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return [data, :min] if options[:min] && data < options[:min]
|
28
|
+
return [data, :max] if options[:max] && data > options[:max]
|
29
|
+
|
30
|
+
# We win, it's valid!
|
31
|
+
[data, nil]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Mutations
|
2
|
+
class ModelFilter < InputFilter
|
3
|
+
@default_options = {
|
4
|
+
nils: false, # true allows an explicit nil to be valid. Overrides any other options
|
5
|
+
class: nil, # default is the attribute name.to_s.camelize.constantize. This overrides it with class or class.constantize
|
6
|
+
builder: nil, # Could be a class or a string which will be constantized. If present, and a hash is passed, then we use that to construct a model
|
7
|
+
new_records: false, # If false, unsaved AR records are not valid. Things that don't respond to new_record? are valid. true: anything is valid
|
8
|
+
}
|
9
|
+
|
10
|
+
def initialize(name, opts = {})
|
11
|
+
super(opts)
|
12
|
+
@name = name
|
13
|
+
|
14
|
+
class_const = options[:class] || @name.to_s.camelize
|
15
|
+
class_const = class_const.constantize if class_const.is_a?(String)
|
16
|
+
self.options[:class] = class_const
|
17
|
+
|
18
|
+
if options[:builder]
|
19
|
+
options[:builder] = options[:builder].constantize if options[:builder].is_a?(String)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def filter(data)
|
24
|
+
|
25
|
+
# Handle nil case
|
26
|
+
if data.nil?
|
27
|
+
return [nil, nil] if options[:nils]
|
28
|
+
return [nil, :nils]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Passing in attributes. Let's see if we have a builder
|
32
|
+
if data.is_a?(Hash) && options[:builder]
|
33
|
+
ret = options[:builder].run(data)
|
34
|
+
|
35
|
+
if ret.success?
|
36
|
+
data = ret.result
|
37
|
+
else
|
38
|
+
return [data, ret.errors]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# We have a winner, someone passed in the correct data type!
|
43
|
+
if data.is_a?(options[:class])
|
44
|
+
return [data, :new_records] if !options[:new_records] && (data.respond_to?(:new_record?) && data.new_record?)
|
45
|
+
return [data, nil]
|
46
|
+
end
|
47
|
+
|
48
|
+
return [data, :model]
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Mutations
|
2
|
+
class Outcome
|
3
|
+
def initialize(is_success, result, errors)
|
4
|
+
@success, @result, @errors = is_success, result, errors
|
5
|
+
end
|
6
|
+
|
7
|
+
def success?
|
8
|
+
@success
|
9
|
+
end
|
10
|
+
|
11
|
+
def result
|
12
|
+
@result
|
13
|
+
end
|
14
|
+
|
15
|
+
def errors
|
16
|
+
@errors
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Mutations
|
2
|
+
class StringFilter < InputFilter
|
3
|
+
@default_options = {
|
4
|
+
strip: true, # true calls data.strip if data is a string
|
5
|
+
strict: false, # If false, then symbols, numbers, and booleans are converted to a string with to_s. # TODO: TEST
|
6
|
+
nils: false, # true allows an explicit nil to be valid. Overrides any other options
|
7
|
+
empty: false, # false disallows "". true allows "" and overrides any other validations (b/c they couldn't be true if it's empty)
|
8
|
+
min_length: nil, # Can be a number like 5, meaning that 5 codepoints are required
|
9
|
+
max_length: nil, # Can be a number like 10, meaning that at most 10 codepoints are permitted
|
10
|
+
matches: nil, # Can be a regexp
|
11
|
+
in: nil # Can be an array like %w(red blue green)
|
12
|
+
}
|
13
|
+
|
14
|
+
def filter(data)
|
15
|
+
|
16
|
+
# Handle nil case
|
17
|
+
if data.nil?
|
18
|
+
return [nil, nil] if options[:nils]
|
19
|
+
return [nil, :nils]
|
20
|
+
end
|
21
|
+
|
22
|
+
# At this point, data is not nil. If it's not a string, convert it to a string for some standard classes
|
23
|
+
data = data.to_s if !options[:strict] && [TrueClass, FalseClass, Fixnum, Symbol].include?(data.class)
|
24
|
+
|
25
|
+
# Now ensure it's a string:
|
26
|
+
return [data, :string] unless data.is_a?(String)
|
27
|
+
|
28
|
+
# At this point, data is a string. Now transform it using strip:
|
29
|
+
data = data.strip if options[:strip]
|
30
|
+
|
31
|
+
# Now check if it's blank:
|
32
|
+
if data == ""
|
33
|
+
if options[:empty]
|
34
|
+
return [data, nil]
|
35
|
+
else
|
36
|
+
return [data, :empty]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Now check to see if it's the correct size:
|
41
|
+
return [data, :min_length] if options[:min_length] && data.length < options[:min_length]
|
42
|
+
return [data, :max_length] if options[:max_length] && data.length > options[:max_length]
|
43
|
+
|
44
|
+
# Ensure it match
|
45
|
+
return [data, :in] if options[:in] && !options[:in].include?(data)
|
46
|
+
|
47
|
+
# Ensure it matches the regexp
|
48
|
+
return [data, :matches] if options[:matches] && (options[:matches] !~ data)
|
49
|
+
|
50
|
+
# We win, it's valid!
|
51
|
+
[data, nil]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/mutations.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require './lib/mutations/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'mutations'
|
5
|
+
s.version = Mutations::VERSION
|
6
|
+
s.author = 'Jonathan Novak'
|
7
|
+
s.email = 'jnovak@gmail.com'
|
8
|
+
s.homepage = 'http://github.com/cypriss/mutations'
|
9
|
+
s.summary = s.description = 'Compose your business logic into commands that sanitize and validate input.'
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.test_files = `git ls-files test`.split("\n")
|
13
|
+
s.require_path = 'lib'
|
14
|
+
|
15
|
+
s.add_dependency 'activesupport'
|
16
|
+
s.add_development_dependency 'minitest', '~> 4'
|
17
|
+
|
18
|
+
s.required_ruby_version = '>= 1.9.2'
|
19
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe "Mutations::ArrayFilter" do
|
4
|
+
|
5
|
+
it "allows arrays" do
|
6
|
+
f = Mutations::ArrayFilter.new(:arr)
|
7
|
+
filtered, errors = f.filter([1])
|
8
|
+
assert_equal [1], filtered
|
9
|
+
assert_equal nil, errors
|
10
|
+
end
|
11
|
+
|
12
|
+
it "considers non-arrays to be invalid" do
|
13
|
+
f = Mutations::ArrayFilter.new(:arr)
|
14
|
+
['hi', true, 1, {a: "1"}, Object.new].each do |thing|
|
15
|
+
filtered, errors = f.filter(thing)
|
16
|
+
assert_equal :array, errors
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "considers nil to be invalid" do
|
21
|
+
f = Mutations::ArrayFilter.new(:arr, nils: false)
|
22
|
+
filtered, errors = f.filter(nil)
|
23
|
+
assert_equal nil, filtered
|
24
|
+
assert_equal :nils, errors
|
25
|
+
end
|
26
|
+
|
27
|
+
it "considers nil to be valid" do
|
28
|
+
f = Mutations::ArrayFilter.new(:arr, nils: true)
|
29
|
+
filtered, errors = f.filter(nil)
|
30
|
+
filtered, errors = f.filter(nil)
|
31
|
+
assert_equal nil, errors
|
32
|
+
end
|
33
|
+
|
34
|
+
it "lets you specify a class, and has valid elements" do
|
35
|
+
f = Mutations::ArrayFilter.new(:arr, class: Fixnum)
|
36
|
+
filtered, errors = f.filter([1,2,3])
|
37
|
+
assert_equal nil, errors
|
38
|
+
assert_equal [1,2,3], filtered
|
39
|
+
end
|
40
|
+
|
41
|
+
it "lets you specify a class as a string, and has valid elements" do
|
42
|
+
f = Mutations::ArrayFilter.new(:arr, class: 'Fixnum')
|
43
|
+
filtered, errors = f.filter([1,2,3])
|
44
|
+
assert_equal nil, errors
|
45
|
+
assert_equal [1,2,3], filtered
|
46
|
+
end
|
47
|
+
|
48
|
+
it "lets you specify a class, and has invalid elements" do
|
49
|
+
f = Mutations::ArrayFilter.new(:arr, class: Fixnum)
|
50
|
+
filtered, errors = f.filter([1, "bob"])
|
51
|
+
assert_equal [nil, :class], errors.symbolic
|
52
|
+
assert_equal [1,"bob"], filtered
|
53
|
+
end
|
54
|
+
|
55
|
+
it "lets you use a block to supply an element filter" do
|
56
|
+
f = Mutations::ArrayFilter.new(:arr) { string }
|
57
|
+
|
58
|
+
filtered, errors = f.filter(["hi", {stuff: "ok"}])
|
59
|
+
assert_nil errors[0]
|
60
|
+
assert_equal :string, errors[1].symbolic
|
61
|
+
end
|
62
|
+
|
63
|
+
it "lets you array-ize everything" do
|
64
|
+
f = Mutations::ArrayFilter.new(:arr, arrayize: true) { string }
|
65
|
+
|
66
|
+
filtered, errors = f.filter("foo")
|
67
|
+
assert_equal ["foo"], filtered
|
68
|
+
assert_nil errors
|
69
|
+
end
|
70
|
+
|
71
|
+
it "lets you array-ize an empty string" do
|
72
|
+
f = Mutations::ArrayFilter.new(:arr, arrayize: true) { string }
|
73
|
+
|
74
|
+
filtered, errors = f.filter("")
|
75
|
+
assert_equal [], filtered
|
76
|
+
assert_nil errors
|
77
|
+
end
|
78
|
+
|
79
|
+
it "lets you pass integers in arrays" do
|
80
|
+
f = Mutations::ArrayFilter.new(:arr) { integer min: 4 }
|
81
|
+
|
82
|
+
filtered, errors = f.filter([5,6,1,"bob"])
|
83
|
+
assert_equal [5,6,1,"bob"], filtered
|
84
|
+
assert_equal [nil, nil, :min, :integer], errors.symbolic
|
85
|
+
end
|
86
|
+
|
87
|
+
it "lets you pass booleans in arrays" do
|
88
|
+
f = Mutations::ArrayFilter.new(:arr) { boolean }
|
89
|
+
|
90
|
+
filtered, errors = f.filter([true, false, "1"])
|
91
|
+
assert_equal [true, false, true], filtered
|
92
|
+
assert_equal nil, errors
|
93
|
+
end
|
94
|
+
|
95
|
+
it "lets you pass model in arrays" do
|
96
|
+
f = Mutations::ArrayFilter.new(:arr) { model :string }
|
97
|
+
|
98
|
+
filtered, errors = f.filter(["hey"])
|
99
|
+
assert_equal ["hey"], filtered
|
100
|
+
assert_equal nil, errors
|
101
|
+
end
|
102
|
+
|
103
|
+
it "lets you pass hashes in arrays" do
|
104
|
+
f = Mutations::ArrayFilter.new(:arr) do
|
105
|
+
hash do
|
106
|
+
required do
|
107
|
+
string :foo
|
108
|
+
integer :bar
|
109
|
+
end
|
110
|
+
|
111
|
+
optional do
|
112
|
+
boolean :baz
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
filtered, errors = f.filter([{foo: "f", bar: 3, baz: true}, {foo: "f", bar: 3}, {foo: "f"}])
|
118
|
+
assert_equal [{:foo=>"f", :bar=>3, :baz=>true}, {:foo=>"f", :bar=>3}, {:foo=>"f"}], filtered
|
119
|
+
|
120
|
+
assert_equal nil, errors[0]
|
121
|
+
assert_equal nil, errors[1]
|
122
|
+
assert_equal ({"bar"=>:required}), errors[2].symbolic
|
123
|
+
end
|
124
|
+
|
125
|
+
it "lets you pass arrays of arrays" do
|
126
|
+
f = Mutations::ArrayFilter.new(:arr) do
|
127
|
+
array do
|
128
|
+
string
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
filtered, errors = f.filter([["h", "e"], ["l"], [], ["lo"]])
|
133
|
+
assert_equal filtered, [["h", "e"], ["l"], [], ["lo"]]
|
134
|
+
assert_equal nil, errors
|
135
|
+
end
|
136
|
+
|
137
|
+
it "handles errors for arrays of arrays" do
|
138
|
+
f = Mutations::ArrayFilter.new(:arr) do
|
139
|
+
array do
|
140
|
+
string
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
filtered, errors = f.filter([["h", "e", {}], ["l"], [], [""]])
|
145
|
+
assert_equal [[nil, nil, :string], nil, nil, [:empty]], errors.symbolic
|
146
|
+
assert_equal [[nil, nil, "Array[2] isn't a string"], nil, nil, ["Array[0] can't be blank"]], errors.message
|
147
|
+
assert_equal ["Array[2] isn't a string", "Array[0] can't be blank"], errors.message_list
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe "Mutations::BooleanFilter" do
|
4
|
+
|
5
|
+
it "allows booleans" do
|
6
|
+
f = Mutations::BooleanFilter.new
|
7
|
+
filtered, errors = f.filter(true)
|
8
|
+
assert_equal true, filtered
|
9
|
+
assert_equal nil, errors
|
10
|
+
|
11
|
+
filtered, errors = f.filter(false)
|
12
|
+
assert_equal false, filtered
|
13
|
+
assert_equal nil, errors
|
14
|
+
end
|
15
|
+
|
16
|
+
it "considers non-booleans to be invalid" do
|
17
|
+
f = Mutations::BooleanFilter.new
|
18
|
+
[[true], {a: "1"}, Object.new].each do |thing|
|
19
|
+
filtered, errors = f.filter(thing)
|
20
|
+
assert_equal :boolean, errors
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "considers nil to be invalid" do
|
25
|
+
f = Mutations::BooleanFilter.new(nils: false)
|
26
|
+
filtered, errors = f.filter(nil)
|
27
|
+
assert_equal nil, filtered
|
28
|
+
assert_equal :nils, errors
|
29
|
+
end
|
30
|
+
|
31
|
+
it "considers nil to be valid" do
|
32
|
+
f = Mutations::BooleanFilter.new(nils: true)
|
33
|
+
filtered, errors = f.filter(nil)
|
34
|
+
assert_equal nil, filtered
|
35
|
+
assert_equal nil, errors
|
36
|
+
end
|
37
|
+
|
38
|
+
it "considers certain strings to be valid booleans" do
|
39
|
+
f = Mutations::BooleanFilter.new
|
40
|
+
[["true", true], ["TRUE", true], ["TrUe", true], ["1", true], ["false", false], ["FALSE", false], ["FalSe", false], ["0", false], [0, false], [1, true]].each do |(str, v)|
|
41
|
+
filtered, errors = f.filter(str)
|
42
|
+
assert_equal v, filtered
|
43
|
+
assert_equal nil, errors
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "considers other string to be invalid" do
|
48
|
+
f = Mutations::BooleanFilter.new
|
49
|
+
["", "truely", "2"].each do |str|
|
50
|
+
filtered, errors = f.filter(str)
|
51
|
+
assert_equal str, filtered
|
52
|
+
assert_equal :boolean, errors
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'simple_command'
|
3
|
+
|
4
|
+
describe "Command" do
|
5
|
+
|
6
|
+
describe "SimpleCommand" do
|
7
|
+
it "should allow valid in put in" do
|
8
|
+
outcome = SimpleCommand.run(name: "John", email: "john@gmail.com", amount: 5)
|
9
|
+
|
10
|
+
assert outcome.success?
|
11
|
+
assert_equal ({name: "John", email: "john@gmail.com", amount: 5}).stringify_keys, outcome.result
|
12
|
+
assert_equal nil, outcome.errors
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should filter out spurious params" do
|
16
|
+
outcome = SimpleCommand.run(name: "John", email: "john@gmail.com", amount: 5, buggers: true)
|
17
|
+
|
18
|
+
assert outcome.success?
|
19
|
+
assert_equal ({name: "John", email: "john@gmail.com", amount: 5}).stringify_keys, outcome.result
|
20
|
+
assert_equal nil, outcome.errors
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should discover errors in inputs" do
|
24
|
+
outcome = SimpleCommand.run(name: "JohnTooLong", email: "john@gmail.com")
|
25
|
+
|
26
|
+
assert !outcome.success?
|
27
|
+
assert :length, outcome.errors.symbolic[:name]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "shouldn't throw an exception with run!" do
|
31
|
+
result = SimpleCommand.run!(name: "John", email: "john@gmail.com", amount: 5)
|
32
|
+
assert_equal ({name: "John", email: "john@gmail.com", amount: 5}).stringify_keys, result
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should throw an exception with run!" do
|
36
|
+
assert_raises Mutations::ValidationException do
|
37
|
+
result = SimpleCommand.run!(name: "John", email: "john@gmail.com", amount: "bob")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should merge multiple hashes" do
|
42
|
+
outcome = SimpleCommand.run({name: "John", email: "john@gmail.com"}, {email: "bob@jones.com", amount: 5})
|
43
|
+
|
44
|
+
assert outcome.success?
|
45
|
+
assert_equal ({name: "John", email: "bob@jones.com", amount: 5}).stringify_keys, outcome.result
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should merge hashes indifferently" do
|
49
|
+
outcome = SimpleCommand.run({name: "John", email: "john@gmail.com"}, {"email" => "bob@jones.com", "amount" => 5})
|
50
|
+
|
51
|
+
assert outcome.success?
|
52
|
+
assert_equal ({name: "John", email: "bob@jones.com", amount: 5}).stringify_keys, outcome.result
|
53
|
+
end
|
54
|
+
|
55
|
+
it "shouldn't accept non-hashes" do
|
56
|
+
assert_raises ArgumentError do
|
57
|
+
outcome = SimpleCommand.run(nil)
|
58
|
+
end
|
59
|
+
|
60
|
+
assert_raises ArgumentError do
|
61
|
+
outcome = SimpleCommand.run(1)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should accept nothing at all" do
|
66
|
+
SimpleCommand.run # make sure nothing is raised
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "EigenCommand" do
|
71
|
+
class EigenCommand < Mutations::Command
|
72
|
+
|
73
|
+
required { string :name }
|
74
|
+
optional { string :email }
|
75
|
+
|
76
|
+
def execute
|
77
|
+
{name: name, email: email}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should define getter methods on params" do
|
82
|
+
mutation = EigenCommand.run(name: "John", email: "john@gmail.com")
|
83
|
+
assert_equal ({name: "John", email: "john@gmail.com"}), mutation.result
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "MutatatedCommand" do
|
88
|
+
class MutatatedCommand < Mutations::Command
|
89
|
+
|
90
|
+
required { string :name }
|
91
|
+
optional { string :email }
|
92
|
+
|
93
|
+
def execute
|
94
|
+
self.name, self.email = "bob", "bob@jones.com"
|
95
|
+
{name: inputs[:name], email: inputs[:email]}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should define setter methods on params" do
|
100
|
+
mutation = MutatatedCommand.run(name: "John", email: "john@gmail.com")
|
101
|
+
assert_equal ({name: "bob", email: "bob@jones.com"}), mutation.result
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "ErrorfulCommand" do
|
106
|
+
class ErrorfulCommand < Mutations::Command
|
107
|
+
|
108
|
+
required { string :name }
|
109
|
+
optional { string :email }
|
110
|
+
|
111
|
+
def execute
|
112
|
+
add_error("bob", :is_a_bob)
|
113
|
+
|
114
|
+
1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should let you add errors" do
|
119
|
+
outcome = ErrorfulCommand.run(name: "John", email: "john@gmail.com")
|
120
|
+
|
121
|
+
assert !outcome.success?
|
122
|
+
assert_nil outcome.result
|
123
|
+
assert :is_a_bob, outcome.errors.symbolic[:bob]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "MultiErrorCommand" do
|
128
|
+
class ErrorfulCommand < Mutations::Command
|
129
|
+
|
130
|
+
required { string :name }
|
131
|
+
optional { string :email }
|
132
|
+
|
133
|
+
def execute
|
134
|
+
moar_errors = Mutations::ErrorHash.new
|
135
|
+
moar_errors[:bob] = Mutations::ErrorAtom.new(:bob, :is_short)
|
136
|
+
moar_errors[:sally] = Mutations::ErrorAtom.new(:sally, :is_fat)
|
137
|
+
|
138
|
+
merge_errors(moar_errors)
|
139
|
+
|
140
|
+
1
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should let you merge errors" do
|
145
|
+
outcome = ErrorfulCommand.run(name: "John", email: "john@gmail.com")
|
146
|
+
|
147
|
+
assert !outcome.success?
|
148
|
+
assert_nil outcome.result
|
149
|
+
assert :is_short, outcome.errors.symbolic[:bob]
|
150
|
+
assert :is_fat, outcome.errors.symbolic[:sally]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "PresentCommand" do
|
155
|
+
class PresentCommand < Mutations::Command
|
156
|
+
|
157
|
+
optional do
|
158
|
+
string :email
|
159
|
+
string :name
|
160
|
+
end
|
161
|
+
|
162
|
+
def execute
|
163
|
+
if name_present? && email_present?
|
164
|
+
1
|
165
|
+
elsif !name_present? && email_present?
|
166
|
+
2
|
167
|
+
elsif name_present? && !email_present?
|
168
|
+
3
|
169
|
+
else
|
170
|
+
4
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should handle *_present? methods" do
|
176
|
+
assert_equal 1, PresentCommand.run!(name: "John", email: "john@gmail.com")
|
177
|
+
assert_equal 2, PresentCommand.run!(email: "john@gmail.com")
|
178
|
+
assert_equal 3, PresentCommand.run!(name: "John")
|
179
|
+
assert_equal 4, PresentCommand.run!
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|