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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 534592273859c465d6782fd466fe95b2c3a43950
4
- data.tar.gz: 0737964adad3f9807f9886faae5ebb28ca26a3e0
3
+ metadata.gz: 0c5b6793497535f1a64c445c3dc79f96d51ab755
4
+ data.tar.gz: 8cb64c61b215efbb182cf7a8bc830d1c0d2e5f66
5
5
  SHA512:
6
- metadata.gz: f974fec5ea5b17c290b3c018ef20b5e42469caafb206cc67912c1d400b2cbdc5902d6e6708e3cbf50a1f7356af1e55c97d6bf7e13d12bc5892bc3fb90c2fb5cd
7
- data.tar.gz: 12d81ec727fd3f7063622daf3b4043f6fe5ba9d5ec51c186751932dce367e7c5a341bebfb854538df2b00a75e024c3fcf0f0c2e72f3fed595949071996786a42
6
+ metadata.gz: 522271efbce236d5a3b71fd5c695dd59fe2a21b823482bba9979481636dafe3247b4dbd5462998500a7e0df74f0546669d7886df80c3e5765dedb26de8b139c0
7
+ data.tar.gz: e6d172ccbee95ec78413c106a6c7fac857f092e11d6eaeaa353e5dc3b9d0a303ee264f2b2bcf42e771c869528682f95f0883ad0cf0704b4c4dd22ca873191c59
@@ -0,0 +1,9 @@
1
+ version: 2
2
+ jobs:
3
+ build:
4
+ docker:
5
+ - image: circleci/ruby:2.5.0
6
+ steps:
7
+ - checkout
8
+ - run: gem install bundler
9
+ - run: bundle exec rake
@@ -1,8 +1,4 @@
1
- engines:
1
+ version: 2
2
+ plugins:
2
3
  rubocop:
3
4
  enabled: true
4
- bundler-audit:
5
- enabled: true
6
- ratings:
7
- paths:
8
- - "lib/**"
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![Code Climate](https://codeclimate.com/github/monterail/gate/badges/gpa.svg)](https://codeclimate.com/github/monterail/gate)
7
7
  [![Test Coverage](https://codeclimate.com/github/monterail/gate/badges/coverage.svg)](https://codeclimate.com/github/monterail/gate/coverage)
8
8
 
9
- Gate is a small library which allows you to define allowed structure for user input with required and optional parameters and to coerce them into defined types.
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
- gate = Gate.rules do
33
- required :id, :Integer
34
- required :message do
35
- required :title # :String by default
36
- optional :value, :Decimal
37
- optional :anything, :Any # Just pass through original value
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
- Verify it
45
+ Use it
43
46
 
44
47
  ```ruby
45
- result = gate.verify(params)
46
- result.valid? # => true / false
47
- result.attributes # => hash with only allowed parameters
48
- result.errors # => hash { key => error }
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.
@@ -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 = "Handling user input with ease"
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 "coercible", "~> 1.0"
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.9"
24
- spec.add_development_dependency "rake", "~> 10.0"
25
- spec.add_development_dependency "pry", "~> 0.10"
26
- spec.add_development_dependency "minitest", "~> 5.7"
27
- spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4"
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
@@ -1,21 +1,8 @@
1
1
  module Gate
2
- class CoercionError < StandardError; end
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 "axiom-types"
12
- require "coercible"
13
- require "forwardable"
14
- require "set"
5
+ require "dry-validation"
15
6
 
16
- require "gate/coercer"
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"
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Gate
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
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.1
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: 2015-12-23 00:00:00.000000000 Z
11
+ date: 2018-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: coercible
14
+ name: dry-validation
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
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: '1.0'
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.9'
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.9'
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: '10.0'
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: '10.0'
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.10'
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.10'
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.7'
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.7'
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.4'
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.4'
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/coercer.rb
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.4.5.1
139
+ rubygems_version: 2.6.14
158
140
  signing_key:
159
141
  specification_version: 4
160
- summary: Handling user input with ease
142
+ summary: CQRS Command
161
143
  test_files: []
data/circle.yml DELETED
@@ -1,3 +0,0 @@
1
- machine:
2
- ruby:
3
- version: 2.2.0
@@ -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
@@ -1,7 +0,0 @@
1
- require 'coercible'
2
-
3
- class Gate::Coercer::Any < Coercible::Coercer::Object
4
- def to_any(value)
5
- value
6
- end
7
- end
@@ -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
@@ -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
@@ -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