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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +27 -16
- data/README.md +133 -1
- data/lib/smart_core/schema.rb +40 -0
- data/lib/smart_core/schema/checker.rb +81 -0
- data/lib/smart_core/schema/checker/reconciler.rb +85 -0
- data/lib/smart_core/schema/checker/reconciler/constructor.rb +28 -0
- data/lib/smart_core/schema/checker/reconciler/matcher.rb +53 -0
- data/lib/smart_core/schema/checker/reconciler/matcher/result.rb +86 -0
- data/lib/smart_core/schema/checker/reconciler/matcher/result_finalizer.rb +136 -0
- data/lib/smart_core/schema/checker/rules.rb +85 -0
- data/lib/smart_core/schema/checker/rules/base.rb +105 -0
- data/lib/smart_core/schema/checker/rules/extra_keys.rb +22 -0
- data/lib/smart_core/schema/checker/rules/extra_keys/failure.rb +36 -0
- data/lib/smart_core/schema/checker/rules/extra_keys/success.rb +21 -0
- data/lib/smart_core/schema/checker/rules/optional.rb +22 -0
- data/lib/smart_core/schema/checker/rules/options.rb +56 -0
- data/lib/smart_core/schema/checker/rules/options/empty.rb +43 -0
- data/lib/smart_core/schema/checker/rules/options/filled.rb +49 -0
- data/lib/smart_core/schema/checker/rules/options/type.rb +86 -0
- data/lib/smart_core/schema/checker/rules/required.rb +22 -0
- data/lib/smart_core/schema/checker/rules/requirement.rb +9 -0
- data/lib/smart_core/schema/checker/rules/requirement/optional.rb +36 -0
- data/lib/smart_core/schema/checker/rules/requirement/required.rb +36 -0
- data/lib/smart_core/schema/checker/rules/requirement/result.rb +95 -0
- data/lib/smart_core/schema/checker/rules/result.rb +9 -0
- data/lib/smart_core/schema/checker/rules/result/base.rb +44 -0
- data/lib/smart_core/schema/checker/rules/result/failure.rb +41 -0
- data/lib/smart_core/schema/checker/rules/result/success.rb +15 -0
- data/lib/smart_core/schema/checker/rules/type_aliases.rb +51 -0
- data/lib/smart_core/schema/checker/rules/verifier.rb +71 -0
- data/lib/smart_core/schema/checker/rules/verifier/result.rb +75 -0
- data/lib/smart_core/schema/checker/verifiable_hash.rb +65 -0
- data/lib/smart_core/schema/dsl.rb +57 -0
- data/lib/smart_core/schema/errors.rb +11 -0
- data/lib/smart_core/schema/key_control.rb +39 -0
- data/lib/smart_core/schema/result.rb +50 -0
- data/lib/smart_core/schema/version.rb +1 -1
- data/smart_schema.gemspec +14 -7
- metadata +83 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 686b8bd1172752321dd2992f2cbe2aeb5230493142afb019c8c062347f215d97
|
4
|
+
data.tar.gz: f3395ec6d78bf5e8d6407018bb890e90043a8b730ec52ed1cabc090ab92953e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f64e1350c2422b40e275638b0719b49f0300cb8c7e3e9e7e179029a94afba9d455fd8c8d5c82d83b8a5604eb4df1c2ed4cf3b4dd0af20ecef35628f89bf6fc9b
|
7
|
+
data.tar.gz: 1fb7354eeb85ae75471fc3075ddcbf6bc50a1b4c8f115a34c045a5a29a689fb7705a776fe006a3a8276c2f7b58796336e126192c3108bd93134cff3a0348e797
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -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.
|
16
|
-
rubocop (= 0.
|
17
|
+
armitage-rubocop (0.89.1)
|
18
|
+
rubocop (= 0.89.1)
|
17
19
|
rubocop-performance (= 1.7.1)
|
18
|
-
rubocop-rails (= 2.
|
20
|
+
rubocop-rails (= 2.7.1)
|
19
21
|
rubocop-rake (= 0.5.1)
|
20
|
-
rubocop-rspec (= 1.
|
22
|
+
rubocop-rspec (= 1.43.2)
|
21
23
|
ast (2.4.1)
|
22
|
-
|
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.
|
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.
|
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.
|
59
|
-
parser (>= 2.7.
|
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.
|
69
|
+
rubocop-rails (2.7.1)
|
63
70
|
activesupport (>= 4.2.0)
|
64
71
|
rack (>= 1.1)
|
65
|
-
rubocop (>= 0.
|
72
|
+
rubocop (>= 0.87.0)
|
66
73
|
rubocop-rake (0.5.1)
|
67
74
|
rubocop
|
68
|
-
rubocop-rspec (1.
|
69
|
-
rubocop (
|
75
|
+
rubocop-rspec (1.43.2)
|
76
|
+
rubocop (~> 0.87)
|
70
77
|
ruby-progressbar (1.10.1)
|
71
|
-
simplecov (0.
|
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.
|
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.
|
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)
|
data/lib/smart_core/schema.rb
CHANGED
@@ -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
|