gate 0.4.1 → 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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +9 -0
- data/.codeclimate.yml +2 -6
- data/README.md +17 -24
- data/gate.gemspec +7 -8
- data/lib/gate.rb +3 -16
- data/lib/gate/command.rb +47 -0
- data/lib/gate/version.rb +1 -1
- metadata +19 -37
- data/circle.yml +0 -3
- data/lib/gate/coercer.rb +0 -48
- data/lib/gate/coercer/any.rb +0 -7
- data/lib/gate/configuration.rb +0 -55
- data/lib/gate/guard.rb +0 -67
- data/lib/gate/result.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c5b6793497535f1a64c445c3dc79f96d51ab755
|
4
|
+
data.tar.gz: 8cb64c61b215efbb182cf7a8bc830d1c0d2e5f66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 522271efbce236d5a3b71fd5c695dd59fe2a21b823482bba9979481636dafe3247b4dbd5462998500a7e0df74f0546669d7886df80c3e5765dedb26de8b139c0
|
7
|
+
data.tar.gz: e6d172ccbee95ec78413c106a6c7fac857f092e11d6eaeaa353e5dc3b9d0a303ee264f2b2bcf42e771c869528682f95f0883ad0cf0704b4c4dd22ca873191c59
|
data/.codeclimate.yml
CHANGED
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
[](https://codeclimate.com/github/monterail/gate)
|
7
7
|
[](https://codeclimate.com/github/monterail/gate/coverage)
|
8
8
|
|
9
|
-
Gate is a small
|
9
|
+
Gate is a small wrapper on [dry-validation](http://dry-rb.org/gems/dry-validation/) that might be used as Command in CQRS pattern. It will raise `InvalidCommand` error for invalid input and provide simple struct with access to coerced input.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -29,38 +29,31 @@ Or install it yourself as:
|
|
29
29
|
Define structure
|
30
30
|
|
31
31
|
```ruby
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
class DoSomethingCommand
|
33
|
+
include Gate::Command
|
34
|
+
|
35
|
+
schema do
|
36
|
+
required(:id).filled
|
37
|
+
required(:message).schema do
|
38
|
+
required(:title).filled
|
39
|
+
optional(:value).maybe(:decimal?)
|
40
|
+
end
|
38
41
|
end
|
39
42
|
end
|
40
43
|
```
|
41
44
|
|
42
|
-
|
45
|
+
Use it
|
43
46
|
|
44
47
|
```ruby
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
If you need to handle `nil` values you can use `allow_nil` flag:
|
52
|
-
|
53
|
-
```ruby
|
54
|
-
gate = Gate.rules do
|
55
|
-
required :id, :Integer, allow_nil: true
|
56
|
-
required :message, allow_nil: true do
|
57
|
-
required :title
|
58
|
-
optional :value, :Decimal
|
59
|
-
end
|
48
|
+
begin
|
49
|
+
cmd = DoSomethingCommand.with(params)
|
50
|
+
cmd.id
|
51
|
+
cmd.message
|
52
|
+
rescue DoSomethingCommand::InvalidCommand => e
|
53
|
+
e.errors # => hash { key => [errors] }
|
60
54
|
end
|
61
55
|
```
|
62
56
|
|
63
|
-
|
64
57
|
## Development
|
65
58
|
|
66
59
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/gate.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Jan Dudulski"]
|
10
10
|
spec.email = ["jan@dudulski.pl"]
|
11
11
|
|
12
|
-
spec.summary = "
|
12
|
+
spec.summary = "CQRS Command"
|
13
13
|
spec.description = "Validate and coerce user input against defined structure."
|
14
14
|
spec.homepage = "https://github.com/monterail/gate"
|
15
15
|
spec.license = "MIT"
|
@@ -17,12 +17,11 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^test/}) }
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_runtime_dependency "
|
21
|
-
spec.add_runtime_dependency "axiom-types", "~> 0.1"
|
20
|
+
spec.add_runtime_dependency "dry-validation", "~> 0.11"
|
22
21
|
|
23
|
-
spec.add_development_dependency "bundler", "~> 1.
|
24
|
-
spec.add_development_dependency "rake", "~>
|
25
|
-
spec.add_development_dependency "pry", "~> 0.
|
26
|
-
spec.add_development_dependency "minitest", "~> 5.
|
27
|
-
spec.add_development_dependency "codeclimate-test-reporter", "~> 0
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
23
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
24
|
+
spec.add_development_dependency "pry", "~> 0.11"
|
25
|
+
spec.add_development_dependency "minitest", "~> 5.11"
|
26
|
+
spec.add_development_dependency "codeclimate-test-reporter", "~> 1.0"
|
28
27
|
end
|
data/lib/gate.rb
CHANGED
@@ -1,21 +1,8 @@
|
|
1
1
|
module Gate
|
2
|
-
|
3
|
-
|
4
|
-
def self.rules(&block)
|
5
|
-
configuration = Configuration.new(&block)
|
6
|
-
|
7
|
-
Guard.new(configuration)
|
8
|
-
end
|
2
|
+
InvalidCommand = Class.new(StandardError)
|
9
3
|
end
|
10
4
|
|
11
|
-
require "
|
12
|
-
require "coercible"
|
13
|
-
require "forwardable"
|
14
|
-
require "set"
|
5
|
+
require "dry-validation"
|
15
6
|
|
16
|
-
require "gate/
|
17
|
-
require "gate/coercer/any"
|
18
|
-
require "gate/configuration"
|
19
|
-
require "gate/guard"
|
20
|
-
require "gate/result"
|
7
|
+
require "gate/command"
|
21
8
|
require "gate/version"
|
data/lib/gate/command.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Gate
|
2
|
+
module Command
|
3
|
+
SchemaAlreadyRegistered = Class.new(StandardError)
|
4
|
+
SchemaNotDefined = Class.new(StandardError)
|
5
|
+
|
6
|
+
class InvalidCommand < StandardError
|
7
|
+
attr_reader :errors
|
8
|
+
|
9
|
+
def initialize(errors)
|
10
|
+
@errors = errors
|
11
|
+
super("Invalid command")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.send(:extend, ClassMethods)
|
17
|
+
base.send(:attr_reader, :result)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(data)
|
21
|
+
@result = data
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def schema(&block)
|
26
|
+
if block_given?
|
27
|
+
raise SchemaAlreadyRegistered if @schema
|
28
|
+
@schema = Dry::Validation.Form(&block)
|
29
|
+
@schema.rules.keys.each do |name|
|
30
|
+
define_method(name) do
|
31
|
+
result[name]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
else
|
35
|
+
raise SchemaNotDefined unless @schema
|
36
|
+
@schema
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def with(input)
|
41
|
+
result = schema.(input)
|
42
|
+
raise InvalidCommand, result.messages if result.failure?
|
43
|
+
new result.output
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/gate/version.rb
CHANGED
metadata
CHANGED
@@ -1,113 +1,99 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Dudulski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: dry-validation
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0.11'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: axiom-types
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.1'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0.1'
|
26
|
+
version: '0.11'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: bundler
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
31
|
- - "~>"
|
46
32
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
33
|
+
version: '1.16'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
38
|
- - "~>"
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
40
|
+
version: '1.16'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: rake
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - "~>"
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
47
|
+
version: '12.0'
|
62
48
|
type: :development
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
52
|
- - "~>"
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
54
|
+
version: '12.0'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: pry
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - "~>"
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0.
|
61
|
+
version: '0.11'
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0.
|
68
|
+
version: '0.11'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: minitest
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - "~>"
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version: '5.
|
75
|
+
version: '5.11'
|
90
76
|
type: :development
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
80
|
- - "~>"
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version: '5.
|
82
|
+
version: '5.11'
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: codeclimate-test-reporter
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
100
86
|
requirements:
|
101
87
|
- - "~>"
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0
|
89
|
+
version: '1.0'
|
104
90
|
type: :development
|
105
91
|
prerelease: false
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
107
93
|
requirements:
|
108
94
|
- - "~>"
|
109
95
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0
|
96
|
+
version: '1.0'
|
111
97
|
description: Validate and coerce user input against defined structure.
|
112
98
|
email:
|
113
99
|
- jan@dudulski.pl
|
@@ -115,6 +101,7 @@ executables: []
|
|
115
101
|
extensions: []
|
116
102
|
extra_rdoc_files: []
|
117
103
|
files:
|
104
|
+
- ".circleci/config.yml"
|
118
105
|
- ".codeclimate.yml"
|
119
106
|
- ".gitignore"
|
120
107
|
- ".rubocop.yml"
|
@@ -125,14 +112,9 @@ files:
|
|
125
112
|
- Rakefile
|
126
113
|
- bin/console
|
127
114
|
- bin/setup
|
128
|
-
- circle.yml
|
129
115
|
- gate.gemspec
|
130
116
|
- lib/gate.rb
|
131
|
-
- lib/gate/
|
132
|
-
- lib/gate/coercer/any.rb
|
133
|
-
- lib/gate/configuration.rb
|
134
|
-
- lib/gate/guard.rb
|
135
|
-
- lib/gate/result.rb
|
117
|
+
- lib/gate/command.rb
|
136
118
|
- lib/gate/version.rb
|
137
119
|
homepage: https://github.com/monterail/gate
|
138
120
|
licenses:
|
@@ -154,8 +136,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
136
|
version: '0'
|
155
137
|
requirements: []
|
156
138
|
rubyforge_project:
|
157
|
-
rubygems_version: 2.
|
139
|
+
rubygems_version: 2.6.14
|
158
140
|
signing_key:
|
159
141
|
specification_version: 4
|
160
|
-
summary:
|
142
|
+
summary: CQRS Command
|
161
143
|
test_files: []
|
data/circle.yml
DELETED
data/lib/gate/coercer.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
module Gate
|
2
|
-
class Coercer
|
3
|
-
PRIMITIVES = [TrueClass, FalseClass, Array, Hash, Numeric]
|
4
|
-
|
5
|
-
def initialize(engine, type, allow_nil: false)
|
6
|
-
unless coercible?(type)
|
7
|
-
fail CoercionError, "Doesn't know how to coerce into #{type}"
|
8
|
-
end
|
9
|
-
|
10
|
-
@engine = engine
|
11
|
-
@type = type
|
12
|
-
@allow_nil = allow_nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def coerce(input)
|
16
|
-
engine[detect_input_type(input)].public_send(coercion_method, input)
|
17
|
-
rescue Coercible::UnsupportedCoercion
|
18
|
-
raise CoercionError, "Doesn't know how to coerce #{input} into #{type}"
|
19
|
-
end
|
20
|
-
|
21
|
-
def allow_nil?
|
22
|
-
@allow_nil
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
attr_reader :engine, :type
|
28
|
-
|
29
|
-
def coercible?(type)
|
30
|
-
type == :Any or Axiom::Types.const_defined?(type)
|
31
|
-
end
|
32
|
-
|
33
|
-
def detect_input_type(input)
|
34
|
-
case input
|
35
|
-
when *PRIMITIVES, Any
|
36
|
-
input.class
|
37
|
-
else
|
38
|
-
String
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def coercion_method
|
43
|
-
return :to_any if type == :Any
|
44
|
-
|
45
|
-
Axiom::Types.const_get(type).coercion_method
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/gate/coercer/any.rb
DELETED
data/lib/gate/configuration.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
module Gate
|
2
|
-
class Configuration
|
3
|
-
extend Forwardable
|
4
|
-
|
5
|
-
attr_reader :coercer
|
6
|
-
attr_reader :required_set, :optional_set, :nested_set
|
7
|
-
attr_reader :rules
|
8
|
-
|
9
|
-
def_delegator :@rules, :reduce
|
10
|
-
|
11
|
-
def initialize(coercer: Coercible::Coercer.new, allow_nil: false, &block)
|
12
|
-
@coercer = coercer
|
13
|
-
@required_set = Set.new
|
14
|
-
@optional_set = Set.new
|
15
|
-
@nested_set = Set.new
|
16
|
-
@rules = {}
|
17
|
-
@allow_nil = allow_nil
|
18
|
-
|
19
|
-
instance_eval(&block)
|
20
|
-
end
|
21
|
-
|
22
|
-
def required?(name)
|
23
|
-
required_set.include?(name)
|
24
|
-
end
|
25
|
-
|
26
|
-
def allow_nil?
|
27
|
-
@allow_nil
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def required(name, type = :String, allow_nil: false, &block)
|
33
|
-
required_set.add(name)
|
34
|
-
register(name, type, allow_nil: allow_nil, &block)
|
35
|
-
end
|
36
|
-
|
37
|
-
def optional(name, type = :String, allow_nil: false, &block)
|
38
|
-
optional_set.add(name)
|
39
|
-
register(name, type, allow_nil: allow_nil, &block)
|
40
|
-
end
|
41
|
-
|
42
|
-
def register(name, type, allow_nil:, &block)
|
43
|
-
@rules[name] = setup_rule(name, type, allow_nil: allow_nil, &block)
|
44
|
-
end
|
45
|
-
|
46
|
-
def setup_rule(name, type, allow_nil:, &block)
|
47
|
-
if block_given?
|
48
|
-
nested_set.add(name)
|
49
|
-
Configuration.new(coercer: coercer, allow_nil: allow_nil, &block)
|
50
|
-
else
|
51
|
-
Coercer.new(coercer, type, allow_nil: allow_nil)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/lib/gate/guard.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
module Gate
|
2
|
-
class Guard
|
3
|
-
attr_reader :configuration
|
4
|
-
|
5
|
-
def initialize(configuration)
|
6
|
-
@configuration = configuration
|
7
|
-
end
|
8
|
-
|
9
|
-
def verify(input)
|
10
|
-
configuration.reduce(Result.new) do |result, (name, rule)|
|
11
|
-
Result.new(_verify(input, name, rule, result))
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def _verify(input, name, rule, result)
|
18
|
-
case
|
19
|
-
when input.key?(name)
|
20
|
-
coerced = handle(input[name], name, rule)
|
21
|
-
update(result, coerced)
|
22
|
-
when configuration.required?(name)
|
23
|
-
errors = result.errors.merge(name => :missing)
|
24
|
-
update(result, errors: errors)
|
25
|
-
else
|
26
|
-
result.to_h
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def handle(input, name, rule)
|
31
|
-
case
|
32
|
-
when input.nil?
|
33
|
-
coerce_nil(name, rule)
|
34
|
-
when rule.is_a?(Gate::Configuration)
|
35
|
-
coerce_nested(input, name, rule)
|
36
|
-
else
|
37
|
-
coerce(input, name, rule)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def coerce(input, name, rule)
|
42
|
-
{ attributes: { name => rule.coerce(input) } }
|
43
|
-
rescue CoercionError
|
44
|
-
# TODO: log error
|
45
|
-
{ errors: { name => :coercion_error } }
|
46
|
-
end
|
47
|
-
|
48
|
-
def coerce_nested(input, name, rule)
|
49
|
-
result = Gate::Guard.new(rule).verify(input)
|
50
|
-
{ attributes: { name => result.attributes },
|
51
|
-
errors: { name => result.errors } }
|
52
|
-
end
|
53
|
-
|
54
|
-
def coerce_nil(name, rule)
|
55
|
-
if rule.allow_nil?
|
56
|
-
{ attributes: { name => nil } }
|
57
|
-
else
|
58
|
-
{ errors: { name => :nil_not_allowed } }
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def update(result, attributes: {}, errors: {})
|
63
|
-
{ attributes: result.attributes.merge(attributes),
|
64
|
-
errors: result.errors.merge(errors) }
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
data/lib/gate/result.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
module Gate
|
2
|
-
class Result
|
3
|
-
attr_reader :attributes
|
4
|
-
|
5
|
-
def initialize(attributes: {}, errors: {})
|
6
|
-
@attributes = attributes
|
7
|
-
@errors = errors
|
8
|
-
end
|
9
|
-
|
10
|
-
def valid?
|
11
|
-
errors.empty?
|
12
|
-
end
|
13
|
-
|
14
|
-
def errors
|
15
|
-
_errors(@errors)
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_h
|
19
|
-
{
|
20
|
-
attributes: attributes,
|
21
|
-
errors: @errors
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def _errors(hash)
|
28
|
-
hash.each_with_object({}) do |(k, v), result|
|
29
|
-
if v.is_a?(Hash)
|
30
|
-
nested = _errors(v)
|
31
|
-
|
32
|
-
result[k] = nested if nested.any?
|
33
|
-
else
|
34
|
-
result[k] = v
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|