smart_schema 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile.lock +27 -16
  4. data/README.md +133 -1
  5. data/lib/smart_core/schema.rb +40 -0
  6. data/lib/smart_core/schema/checker.rb +81 -0
  7. data/lib/smart_core/schema/checker/reconciler.rb +85 -0
  8. data/lib/smart_core/schema/checker/reconciler/constructor.rb +28 -0
  9. data/lib/smart_core/schema/checker/reconciler/matcher.rb +53 -0
  10. data/lib/smart_core/schema/checker/reconciler/matcher/result.rb +86 -0
  11. data/lib/smart_core/schema/checker/reconciler/matcher/result_finalizer.rb +136 -0
  12. data/lib/smart_core/schema/checker/rules.rb +85 -0
  13. data/lib/smart_core/schema/checker/rules/base.rb +105 -0
  14. data/lib/smart_core/schema/checker/rules/extra_keys.rb +22 -0
  15. data/lib/smart_core/schema/checker/rules/extra_keys/failure.rb +36 -0
  16. data/lib/smart_core/schema/checker/rules/extra_keys/success.rb +21 -0
  17. data/lib/smart_core/schema/checker/rules/optional.rb +22 -0
  18. data/lib/smart_core/schema/checker/rules/options.rb +56 -0
  19. data/lib/smart_core/schema/checker/rules/options/empty.rb +43 -0
  20. data/lib/smart_core/schema/checker/rules/options/filled.rb +49 -0
  21. data/lib/smart_core/schema/checker/rules/options/type.rb +86 -0
  22. data/lib/smart_core/schema/checker/rules/required.rb +22 -0
  23. data/lib/smart_core/schema/checker/rules/requirement.rb +9 -0
  24. data/lib/smart_core/schema/checker/rules/requirement/optional.rb +36 -0
  25. data/lib/smart_core/schema/checker/rules/requirement/required.rb +36 -0
  26. data/lib/smart_core/schema/checker/rules/requirement/result.rb +95 -0
  27. data/lib/smart_core/schema/checker/rules/result.rb +9 -0
  28. data/lib/smart_core/schema/checker/rules/result/base.rb +44 -0
  29. data/lib/smart_core/schema/checker/rules/result/failure.rb +41 -0
  30. data/lib/smart_core/schema/checker/rules/result/success.rb +15 -0
  31. data/lib/smart_core/schema/checker/rules/type_aliases.rb +51 -0
  32. data/lib/smart_core/schema/checker/rules/verifier.rb +71 -0
  33. data/lib/smart_core/schema/checker/rules/verifier/result.rb +75 -0
  34. data/lib/smart_core/schema/checker/verifiable_hash.rb +65 -0
  35. data/lib/smart_core/schema/dsl.rb +57 -0
  36. data/lib/smart_core/schema/errors.rb +11 -0
  37. data/lib/smart_core/schema/key_control.rb +39 -0
  38. data/lib/smart_core/schema/result.rb +50 -0
  39. data/lib/smart_core/schema/version.rb +1 -1
  40. data/smart_schema.gemspec +14 -7
  41. metadata +83 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 324e316565a37d67eac797e5deff2308973fbb339c16c739dfff3cbfa7a4c7f1
4
- data.tar.gz: 4cf06afadcdca2b90fdf90492b95b79b448145ff453748069f2e5cd0ba0a6dcf
3
+ metadata.gz: 686b8bd1172752321dd2992f2cbe2aeb5230493142afb019c8c062347f215d97
4
+ data.tar.gz: f3395ec6d78bf5e8d6407018bb890e90043a8b730ec52ed1cabc090ab92953e2
5
5
  SHA512:
6
- metadata.gz: 7ba48da56544751547a3a716cf49a23c32fe6c28ff0a8b8657398b8fd30df526229bfbdd51b942b5a4be72305f619bb88903e8fe24e576edc68aa5f6bcce921a
7
- data.tar.gz: d9d08dc497ce96c42927f76fc30b65959aa355486f4648a453137aab29db1379606e054419fe67f07ef4007c6765c382a17417bf08968dd1a4311688be8a23b9
6
+ metadata.gz: f64e1350c2422b40e275638b0719b49f0300cb8c7e3e9e7e179029a94afba9d455fd8c8d5c82d83b8a5604eb4df1c2ed4cf3b4dd0af20ecef35628f89bf6fc9b
7
+ data.tar.gz: 1fb7354eeb85ae75471fc3075ddcbf6bc50a1b4c8f115a34c045a5a29a689fb7705a776fe006a3a8276c2f7b58796336e126192c3108bd93134cff3a0348e797
@@ -1,2 +1,6 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
+
4
+ # [0.1.0] - 2020-08-25
5
+
6
+ - Release :)
@@ -2,6 +2,8 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  smart_schema (0.1.0)
5
+ smart_engine (~> 0.8)
6
+ smart_types (~> 0.1)
5
7
 
6
8
  GEM
7
9
  remote: https://rubygems.org/
@@ -12,22 +14,27 @@ GEM
12
14
  minitest (~> 5.1)
13
15
  tzinfo (~> 1.1)
14
16
  zeitwerk (~> 2.2, >= 2.2.2)
15
- armitage-rubocop (0.88.0)
16
- rubocop (= 0.88.0)
17
+ armitage-rubocop (0.89.1)
18
+ rubocop (= 0.89.1)
17
19
  rubocop-performance (= 1.7.1)
18
- rubocop-rails (= 2.6.0)
20
+ rubocop-rails (= 2.7.1)
19
21
  rubocop-rake (= 0.5.1)
20
- rubocop-rspec (= 1.42.0)
22
+ rubocop-rspec (= 1.43.2)
21
23
  ast (2.4.1)
22
- concurrent-ruby (1.1.6)
24
+ coderay (1.1.3)
25
+ concurrent-ruby (1.1.7)
23
26
  diff-lcs (1.4.4)
24
27
  docile (1.3.2)
25
28
  i18n (1.8.5)
26
29
  concurrent-ruby (~> 1.0)
30
+ method_source (1.0.0)
27
31
  minitest (5.14.1)
28
32
  parallel (1.19.2)
29
33
  parser (2.7.1.4)
30
34
  ast (~> 2.4.1)
35
+ pry (0.13.1)
36
+ coderay (~> 1.1)
37
+ method_source (~> 1.0)
31
38
  rack (2.2.3)
32
39
  rainbow (3.0.0)
33
40
  rake (13.0.1)
@@ -46,32 +53,35 @@ GEM
46
53
  diff-lcs (>= 1.2.0, < 2.0)
47
54
  rspec-support (~> 3.9.0)
48
55
  rspec-support (3.9.3)
49
- rubocop (0.88.0)
56
+ rubocop (0.89.1)
50
57
  parallel (~> 1.10)
51
58
  parser (>= 2.7.1.1)
52
59
  rainbow (>= 2.2.2, < 4.0)
53
60
  regexp_parser (>= 1.7)
54
61
  rexml
55
- rubocop-ast (>= 0.1.0, < 1.0)
62
+ rubocop-ast (>= 0.3.0, < 1.0)
56
63
  ruby-progressbar (~> 1.7)
57
64
  unicode-display_width (>= 1.4.0, < 2.0)
58
- rubocop-ast (0.2.0)
59
- parser (>= 2.7.0.1)
65
+ rubocop-ast (0.3.0)
66
+ parser (>= 2.7.1.4)
60
67
  rubocop-performance (1.7.1)
61
68
  rubocop (>= 0.82.0)
62
- rubocop-rails (2.6.0)
69
+ rubocop-rails (2.7.1)
63
70
  activesupport (>= 4.2.0)
64
71
  rack (>= 1.1)
65
- rubocop (>= 0.82.0)
72
+ rubocop (>= 0.87.0)
66
73
  rubocop-rake (0.5.1)
67
74
  rubocop
68
- rubocop-rspec (1.42.0)
69
- rubocop (>= 0.87.0)
75
+ rubocop-rspec (1.43.2)
76
+ rubocop (~> 0.87)
70
77
  ruby-progressbar (1.10.1)
71
- simplecov (0.18.5)
78
+ simplecov (0.19.0)
72
79
  docile (~> 1.1)
73
80
  simplecov-html (~> 0.11)
74
81
  simplecov-html (0.12.2)
82
+ smart_engine (0.8.0)
83
+ smart_types (0.1.0)
84
+ smart_engine (~> 0.6)
75
85
  thread_safe (0.3.6)
76
86
  tzinfo (1.2.7)
77
87
  thread_safe (~> 0.1)
@@ -82,11 +92,12 @@ PLATFORMS
82
92
  ruby
83
93
 
84
94
  DEPENDENCIES
85
- armitage-rubocop (~> 0.88)
95
+ armitage-rubocop (~> 0.89)
86
96
  bundler (~> 2.1)
97
+ pry (~> 0.13)
87
98
  rake (~> 13.0)
88
99
  rspec (~> 3.9)
89
- simplecov (~> 0.18)
100
+ simplecov (~> 0.19)
90
101
  smart_schema!
91
102
 
92
103
  BUNDLED WITH
data/README.md CHANGED
@@ -1 +1,133 @@
1
- # SmartCore::Schema
1
+ # SmartCore::Schema [![Gem Version](https://badge.fury.io/rb/smart_schema.svg)](https://badge.fury.io/rb/smart_schema) [![Build Status](https://travis-ci.org/smart-rb/smart_schema.svg?branch=master)](https://travis-ci.org/smart-rb/smart_schema)
2
+
3
+ `SmartCore::Schema` is a schema validator for `Hash`-like data structures.
4
+
5
+ Provides convenient and concise DSL to define complex schemas in easiest way and public validation interface to achieve a comfortable work with detailed validation result.
6
+
7
+ Supports nested structures, type validation (via `smart_types`), required- and optional- schema keys, schema value presence validation, schema inheritance (soon), schema extending (soon) and schema composition (soon).
8
+
9
+ Works in predicate style and in OOP/Monadic result object style. Enjoy :)
10
+
11
+ ## Installation
12
+
13
+ ```ruby
14
+ gem 'smart_schema'
15
+ ```
16
+
17
+ ```shell
18
+ bundle install
19
+ # --- or ---
20
+ gem install smart_schema
21
+ ```
22
+
23
+ ```ruby
24
+ require 'smart_core/schema'
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Synopsis
30
+
31
+ - key requirement: `required` and `optional`;
32
+ - type validation: `type`;
33
+ - `nil` control: `filled`;
34
+ - nested definitions: `do ... end`;
35
+ - supported types: see `smart_types` gem;
36
+
37
+ ```ruby
38
+ class MySchema < SmartCore::Schema
39
+ schema do
40
+ required(:key) do
41
+ optional(:data).type(:string).filled
42
+ optional(:value).type(:numeric)
43
+ required(:name).type(:string)
44
+
45
+ required(:nested) do
46
+ optional(:version).filled
47
+ end
48
+ end
49
+
50
+ required(:another_key).filled
51
+ end
52
+
53
+ # you can open already defined schema and continue schema definitioning:
54
+ #
55
+ # schema do
56
+ # required(:third_key).filled.type(:string)
57
+ # end
58
+ end
59
+ ```
60
+
61
+ ```ruby
62
+ MySchema.new.valid?({
63
+ key: {
64
+ data: '5',
65
+ value: 1,
66
+ name: 'D@iVeR'
67
+ nested: {}
68
+ }
69
+ another_key: true
70
+ }) # => true
71
+
72
+ MySchema.new.valid?({
73
+ key: {
74
+ data: nil,
75
+ value: 1,
76
+ name: 'D@iVeR'
77
+ nested: {}
78
+ }
79
+ }) # => false (missing :another_key, key->data is not filled)
80
+ ```
81
+
82
+ ```ruby
83
+ result = MySchema.new.validate(
84
+ key: { data: nil, value: '1', name: 'D@iVeR' },
85
+ another_key: nil,
86
+ third_key: 'test'
87
+ )
88
+
89
+ # => outputs:
90
+ # #<SmartCore::Schema::Result:0x00007ffcd8926990
91
+ # @errors={"key.data"=>[:non_filled], "key.value"=>[:invalid_type], "key.nested"=>[:required_key_not_found], "another_key"=>[:non_filled]},
92
+ # @extra_keys=#<Set: {"third_key"}>,
93
+ # @source={:key=>{:data=>nil, :value=>"1", :name=>"D@iVeR"}, :another_key=>nil, :third_key=>"test"}>
94
+
95
+ result.success? # => false
96
+ result.extra_keys # => <Set: {"third_key"}>
97
+ result.errors # =>
98
+ {
99
+ "key.data"=>[:non_filled],
100
+ "key.value"=>[:invalid_type],
101
+ "key.nested"=>[:required_key_not_found],
102
+ "another_key"=>[:non_filled]
103
+ }
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Roadmap
109
+
110
+ - **(0.2.0)** - schema inheritance;
111
+ - **(0.3.0)** - schema composition (`required(:key).schema(SchemaClass)`) (`compose_with(AnotherSchema)`);
112
+ - **(0.4.0)** - error messages (that are consistent with error codes);
113
+ - **(0.5.0)** - `smart_type-system` integration;
114
+ - **(0.6.0)** - support for another data structures (such as YAML strings, JSON strings, `Struct`, `OpenStruct`s, custom `Object`s and etc);
115
+ - **(0.7.0)** - think about pattern matching;
116
+
117
+ ---
118
+
119
+ ## Contributing
120
+
121
+ - Fork it ( https://github.com/smart-rb/smart_schema )
122
+ - Create your feature branch (`git checkout -b feature/my-new-feature`)
123
+ - Commit your changes (`git commit -am '[feature_context] Add some feature'`)
124
+ - Push to the branch (`git push origin feature/my-new-feature`)
125
+ - Create new Pull Request
126
+
127
+ ## License
128
+
129
+ Released under MIT License.
130
+
131
+ ## Authors
132
+
133
+ [Rustam Ibragimov](https://github.com/0exp)
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'smart_core'
4
+ require 'smart_core/types'
5
+ require 'set'
6
+
3
7
  # @api pulic
4
8
  # @since 0.1.0
5
9
  module SmartCore
@@ -7,5 +11,41 @@ module SmartCore
7
11
  # @since 0.1.0
8
12
  class Schema
9
13
  require_relative 'schema/version'
14
+ require_relative 'schema/errors'
15
+ require_relative 'schema/key_control'
16
+ require_relative 'schema/result'
17
+ require_relative 'schema/checker'
18
+ require_relative 'schema/dsl'
19
+
20
+ # @since 0.1.0
21
+ include SmartCore::Schema::DSL
22
+
23
+ # @param verifiable_hash [Hash<String|Symbol,Any>]
24
+ # @return [Boolean]
25
+ #
26
+ # @api public
27
+ # @since 0.1.0
28
+ def valid?(verifiable_hash)
29
+ validate(verifiable_hash).success?
30
+ end
31
+
32
+ # @param verifiable_hash [Hash<String|Symbol,Any>]
33
+ # @return [SmartCore::Schema::Result]
34
+ #
35
+ # @api public
36
+ # @since 0.1.0
37
+ def validate(verifiable_hash)
38
+ schema_checker.check!(verifiable_hash)
39
+ end
40
+
41
+ private
42
+
43
+ # @return [SmartCore::Schema::Checker]
44
+ #
45
+ # @api private
46
+ # @since 0.1.0
47
+ def schema_checker
48
+ self.class.__schema_checker__
49
+ end
10
50
  end
11
51
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Schema::Checker
6
+ require_relative 'checker/verifiable_hash'
7
+ require_relative 'checker/rules'
8
+ require_relative 'checker/reconciler'
9
+
10
+ # @return [void]
11
+ #
12
+ # @api private
13
+ # @since 0.1.0
14
+ def initialize
15
+ @reconciler = Reconciler::Constructor.create
16
+ @lock = SmartCore::Engine::Lock.new
17
+ end
18
+
19
+ # @param verifiable_hash [Hash<String|Symbol,Any>]
20
+ # @return [SmartCore::Schema::Result]
21
+ #
22
+ # @api private
23
+ # @since 0.1.0
24
+ def check!(verifiable_hash)
25
+ thread_safe do
26
+ raise(SmartCore::Schema::ArgumentError, <<~ERROR_MESSAGE) unless verifiable_hash.is_a?(Hash)
27
+ Verifiable hash should be a type of ::Hash
28
+ ERROR_MESSAGE
29
+
30
+ reconciler.__match!(VerifiableHash.new(verifiable_hash)).complete!
31
+ end
32
+ end
33
+
34
+ # @param definitions [Block]
35
+ # @return [void]
36
+ #
37
+ # @api private
38
+ # @since 0.1.0
39
+ def append_schema_definitions(&definitions)
40
+ thread_safe { add_schema_definitions(&definitions) }
41
+ end
42
+
43
+ # @param another_checker [SmartCore::Schema::Checker]
44
+ # @return [SmartCore::Schema::Checker]
45
+ #
46
+ # @api private
47
+ # @since 0.1.0
48
+ def combine_with(another_checker)
49
+ thread_safe { self } # TODO (0.x.0): merge the definitions and return self
50
+ end
51
+
52
+ private
53
+
54
+ # @return [SmartCore::Schema::Checker::Reconciler]
55
+ #
56
+ # @api private
57
+ # @since 0.1.0
58
+ attr_reader :reconciler
59
+
60
+ # @param definitions [Block]
61
+ # @return [void]
62
+ #
63
+ # @api private
64
+ # @since 0.1.0
65
+ def add_schema_definitions(&definitions)
66
+ raise(SmartCore::Schema::ArgumentError, <<~ERROR_MESSAGE) unless block_given?
67
+ Schema definitions is not provided (you should provide Block argument)
68
+ ERROR_MESSAGE
69
+
70
+ Reconciler::Constructor.append_definitions(reconciler, &definitions)
71
+ end
72
+
73
+ # @param block [Block]
74
+ # @return [Any]
75
+ #
76
+ # @api private
77
+ # @since 0.1.0
78
+ def thread_safe(&block)
79
+ @lock.synchronize(&block)
80
+ end
81
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Schema::Checker::Reconciler
6
+ require_relative 'reconciler/constructor'
7
+ require_relative 'reconciler/matcher'
8
+
9
+ # @return [void]
10
+ #
11
+ # @api private
12
+ # @since 0.1.0
13
+ def initialize
14
+ @rules = SmartCore::Schema::Checker::Rules.new
15
+ @lock = SmartCore::Engine::Lock.new
16
+ end
17
+
18
+ # @param verifiable_hash [SmartCore::Schema::Checker::VerifiableHash]
19
+ # @return [void]
20
+ #
21
+ # @api private
22
+ # @since 0.1.0
23
+ def __match!(verifiable_hash)
24
+ thread_safe { SmartCore::Schema::Checker::Reconciler::Matcher.match(self, verifiable_hash) }
25
+ end
26
+
27
+ # @return [SmartCore::Schema::Checker::Rules::ExtraKeys]
28
+ #
29
+ # @api private
30
+ # @since 0.1.0
31
+ def __extra_keys_contract
32
+ SmartCore::Schema::Checker::Rules::ExtraKeys
33
+ end
34
+
35
+ # @return [SmartCore::Schema::Checker::Rules]
36
+ #
37
+ # @api private
38
+ # @since 0.1.0
39
+ def __contract_rules
40
+ thread_safe { rules }
41
+ end
42
+
43
+ # @param schema_key [String, Symbol]
44
+ # @param nested_definitions [Block]
45
+ # @return [SmartCore::Schema::Checker::Rules::Required]
46
+ #
47
+ # @api public
48
+ # @since 0.1.0
49
+ def required(schema_key, &nested_definitions)
50
+ thread_safe do
51
+ rule = SmartCore::Schema::Checker::Rules::Required.new(schema_key, &nested_definitions)
52
+ rule.tap { rules[rule.schema_key] = rule }
53
+ end
54
+ end
55
+
56
+ # @param schema_key [String, Symbol]
57
+ # @param nested_definitions [Block]
58
+ # @return [SmartCore::Schema::Checker::Rules::Optional]
59
+ #
60
+ # @api public
61
+ # @since 0.1.0
62
+ def optional(schema_key, &nested_definitions)
63
+ thread_safe do
64
+ rule = SmartCore::Schema::Checker::Rules::Optional.new(schema_key, &nested_definitions)
65
+ rule.tap { rules[rule.schema_key] = rule }
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # @return [SmartCore::Schema::Checker::Rules]
72
+ #
73
+ # @api private
74
+ # @since 0.1.0
75
+ attr_reader :rules
76
+
77
+ # @param block [Block]
78
+ # @return [Any]
79
+ #
80
+ # @api private
81
+ # @since 0.1.0
82
+ def thread_safe(&block)
83
+ @lock.synchronize(&block)
84
+ end
85
+ end