ree 1.0.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +21 -0
- data/README.md +474 -0
- data/Rakefile +8 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/exe/ree +264 -0
- data/lib/ree/args.rb +34 -0
- data/lib/ree/bean_dsl.rb +24 -0
- data/lib/ree/cli/generate_package.rb +18 -0
- data/lib/ree/cli/generate_package_schema.rb +54 -0
- data/lib/ree/cli/generate_packages_schema.rb +17 -0
- data/lib/ree/cli/generate_template.rb +20 -0
- data/lib/ree/cli/init.rb +22 -0
- data/lib/ree/cli/spec_runner.rb +184 -0
- data/lib/ree/cli.rb +12 -0
- data/lib/ree/container.rb +67 -0
- data/lib/ree/contracts/arg_contracts/any.rb +15 -0
- data/lib/ree/contracts/arg_contracts/array_of.rb +52 -0
- data/lib/ree/contracts/arg_contracts/block.rb +15 -0
- data/lib/ree/contracts/arg_contracts/bool.rb +21 -0
- data/lib/ree/contracts/arg_contracts/eq.rb +28 -0
- data/lib/ree/contracts/arg_contracts/exactly.rb +28 -0
- data/lib/ree/contracts/arg_contracts/hash_of.rb +59 -0
- data/lib/ree/contracts/arg_contracts/ksplat.rb +141 -0
- data/lib/ree/contracts/arg_contracts/kwargs.rb +22 -0
- data/lib/ree/contracts/arg_contracts/nilor.rb +16 -0
- data/lib/ree/contracts/arg_contracts/none.rb +15 -0
- data/lib/ree/contracts/arg_contracts/optblock.rb +11 -0
- data/lib/ree/contracts/arg_contracts/or.rb +30 -0
- data/lib/ree/contracts/arg_contracts/range_of.rb +51 -0
- data/lib/ree/contracts/arg_contracts/set_of.rb +54 -0
- data/lib/ree/contracts/arg_contracts/splat.rb +297 -0
- data/lib/ree/contracts/arg_contracts/splat_of.rb +64 -0
- data/lib/ree/contracts/arg_contracts/squarable.rb +11 -0
- data/lib/ree/contracts/arg_contracts/subclass_of.rb +28 -0
- data/lib/ree/contracts/arg_contracts.rb +29 -0
- data/lib/ree/contracts/called_args_validator.rb +291 -0
- data/lib/ree/contracts/contract_definition.rb +142 -0
- data/lib/ree/contracts/contractable.rb +34 -0
- data/lib/ree/contracts/core.rb +17 -0
- data/lib/ree/contracts/engine.rb +71 -0
- data/lib/ree/contracts/engine_proxy.rb +13 -0
- data/lib/ree/contracts/errors/bad_contract_error.rb +4 -0
- data/lib/ree/contracts/errors/contract_error.rb +4 -0
- data/lib/ree/contracts/errors/error.rb +4 -0
- data/lib/ree/contracts/errors/return_contract_error.rb +4 -0
- data/lib/ree/contracts/method_decorator.rb +158 -0
- data/lib/ree/contracts/truncatable.rb +9 -0
- data/lib/ree/contracts/utils.rb +9 -0
- data/lib/ree/contracts/validators/array_validator.rb +51 -0
- data/lib/ree/contracts/validators/base_validator.rb +27 -0
- data/lib/ree/contracts/validators/class_validator.rb +17 -0
- data/lib/ree/contracts/validators/default_validator.rb +20 -0
- data/lib/ree/contracts/validators/hash_validator.rb +100 -0
- data/lib/ree/contracts/validators/proc_validator.rb +17 -0
- data/lib/ree/contracts/validators/range_validator.rb +17 -0
- data/lib/ree/contracts/validators/regexp_validator.rb +17 -0
- data/lib/ree/contracts/validators/valid_validator.rb +28 -0
- data/lib/ree/contracts/validators.rb +42 -0
- data/lib/ree/contracts.rb +45 -0
- data/lib/ree/core/link_validator.rb +42 -0
- data/lib/ree/core/object.rb +132 -0
- data/lib/ree/core/object_error.rb +9 -0
- data/lib/ree/core/object_link.rb +21 -0
- data/lib/ree/core/object_schema.rb +47 -0
- data/lib/ree/core/object_schema_builder.rb +110 -0
- data/lib/ree/core/package.rb +177 -0
- data/lib/ree/core/package_dep.rb +9 -0
- data/lib/ree/core/package_env_var.rb +12 -0
- data/lib/ree/core/package_loader.rb +95 -0
- data/lib/ree/core/package_schema.rb +27 -0
- data/lib/ree/core/package_schema_builder.rb +53 -0
- data/lib/ree/core/package_schema_loader.rb +170 -0
- data/lib/ree/core/packages_detector.rb +43 -0
- data/lib/ree/core/packages_schema.rb +19 -0
- data/lib/ree/core/packages_schema_builder.rb +50 -0
- data/lib/ree/core/packages_schema_loader.rb +95 -0
- data/lib/ree/core/packages_schema_locator.rb +27 -0
- data/lib/ree/core/packages_store.rb +32 -0
- data/lib/ree/core/path_helper.rb +104 -0
- data/lib/ree/dsl/build_package_dsl.rb +155 -0
- data/lib/ree/dsl/domain_error.rb +4 -0
- data/lib/ree/dsl/error_builder.rb +39 -0
- data/lib/ree/dsl/error_dsl.rb +27 -0
- data/lib/ree/dsl/import_dsl.rb +106 -0
- data/lib/ree/dsl/link_import_builder.rb +66 -0
- data/lib/ree/dsl/object_dsl.rb +319 -0
- data/lib/ree/dsl/object_hooks.rb +6 -0
- data/lib/ree/dsl/package_require.rb +44 -0
- data/lib/ree/error.rb +11 -0
- data/lib/ree/facades/packages_facade.rb +197 -0
- data/lib/ree/fn_dsl.rb +24 -0
- data/lib/ree/gen/init.rb +64 -0
- data/lib/ree/gen/package.rb +56 -0
- data/lib/ree/gen.rb +8 -0
- data/lib/ree/handlers/template_handler.rb +118 -0
- data/lib/ree/link_dsl.rb +175 -0
- data/lib/ree/object_compiler.rb +149 -0
- data/lib/ree/package_dsl.rb +34 -0
- data/lib/ree/rspec_link_dsl.rb +19 -0
- data/lib/ree/spec_runner/command_generator.rb +49 -0
- data/lib/ree/spec_runner/command_params.rb +9 -0
- data/lib/ree/spec_runner/runner.rb +200 -0
- data/lib/ree/spec_runner/spec_filename_matcher.rb +27 -0
- data/lib/ree/spec_runner/view.rb +30 -0
- data/lib/ree/spec_runner.rb +11 -0
- data/lib/ree/templates/init/.gitignore +1 -0
- data/lib/ree/templates/init/.irbrc +13 -0
- data/lib/ree/templates/init/.rspec +3 -0
- data/lib/ree/templates/init/.ruby-version +1 -0
- data/lib/ree/templates/init/Gemfile +7 -0
- data/lib/ree/templates/init/Packages.schema.json +1 -0
- data/lib/ree/templates/init/bin/console +5 -0
- data/lib/ree/templates/init/readme.md +2 -0
- data/lib/ree/templates/init/ree.setup.rb +21 -0
- data/lib/ree/templates/init/spec.init.rb +7 -0
- data/lib/ree/templates/package/.gitignore +0 -0
- data/lib/ree/templates/package/.rspec +2 -0
- data/lib/ree/templates/package/<%=package_subdir_name%>/<%=package_name%>/.gitkeep +0 -0
- data/lib/ree/templates/package/<%=package_subdir_name%>/<%=package_name%>.rb +15 -0
- data/lib/ree/templates/package/Package.schema.json +0 -0
- data/lib/ree/templates/package/bin/console +5 -0
- data/lib/ree/templates/package/spec/package_schema_spec.rb +14 -0
- data/lib/ree/templates/package/spec/spec_helper.rb +3 -0
- data/lib/ree/templates/template_detector.rb +35 -0
- data/lib/ree/templates/template_renderer.rb +55 -0
- data/lib/ree/utils/render_utils.rb +20 -0
- data/lib/ree/utils/string_utils.rb +29 -0
- data/lib/ree/version.rb +5 -0
- data/lib/ree.rb +279 -0
- data/sig/ree.rbs +4 -0
- metadata +199 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal = true
|
|
2
|
+
|
|
3
|
+
require 'pathname'
|
|
4
|
+
|
|
5
|
+
class Ree::Container
|
|
6
|
+
class << self
|
|
7
|
+
SEMAPHORE = Mutex.new
|
|
8
|
+
private_constant :SEMAPHORE
|
|
9
|
+
|
|
10
|
+
# singleton
|
|
11
|
+
def instance
|
|
12
|
+
SEMAPHORE.synchronize do
|
|
13
|
+
@instance ||= begin
|
|
14
|
+
self.new
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@instance
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
include Ree::Args
|
|
23
|
+
|
|
24
|
+
MOUNT_AS = [:fn, :object]
|
|
25
|
+
|
|
26
|
+
attr_reader :packages_facade
|
|
27
|
+
|
|
28
|
+
def initialize
|
|
29
|
+
@packages_facade = Ree::PackagesFacade.new
|
|
30
|
+
@object_compiler = Ree::ObjectCompiler.new(@packages_facade)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @param [Ree::Package] package
|
|
34
|
+
# @param [Symbol] object_name
|
|
35
|
+
def compile(package, object_name)
|
|
36
|
+
compile_object("#{package.name}/#{object_name}")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param [String] name_with_package
|
|
40
|
+
# @return [Ree::Object]
|
|
41
|
+
def compile_object(name_with_package)
|
|
42
|
+
check_arg(name_with_package, :name_with_package, String)
|
|
43
|
+
|
|
44
|
+
list = name_with_package.to_s.split('/')
|
|
45
|
+
|
|
46
|
+
name = nil
|
|
47
|
+
package_name = nil
|
|
48
|
+
|
|
49
|
+
if list.size == 2
|
|
50
|
+
package_name = list.first.to_sym
|
|
51
|
+
name = list.last.to_sym
|
|
52
|
+
else
|
|
53
|
+
raise Ree::Error.new("'package/name' definition should be used to load object", :invalid_dsl_usage)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@packages_facade.get_loaded_package(package_name)
|
|
57
|
+
@packages_facade.load_package_object(package_name, name)
|
|
58
|
+
|
|
59
|
+
@object_compiler.call(package_name, name)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @param [Symbol] package_name
|
|
63
|
+
# @return [Ree::Package]
|
|
64
|
+
def load_package(package_name)
|
|
65
|
+
@packages_facade.load_entire_package(package_name)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class ArrayOf
|
|
6
|
+
extend Ree::Contracts::ArgContracts::Squarable
|
|
7
|
+
include Ree::Contracts::Truncatable
|
|
8
|
+
|
|
9
|
+
attr_reader :validator
|
|
10
|
+
|
|
11
|
+
def initialize(*contracts)
|
|
12
|
+
if contracts.size != 1
|
|
13
|
+
raise BadContractError, 'ArrayOf should accept exactly one contract'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@validator = Validators.fetch_for(contracts.first)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def valid?(value)
|
|
20
|
+
value.is_a?(Array) &&
|
|
21
|
+
value.all?(&validator.method(:call))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_s
|
|
25
|
+
"ArrayOf[#{validator.to_s}]"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def message(value, name, lvl = 1)
|
|
29
|
+
unless value.is_a?(Array)
|
|
30
|
+
return "expected Array, got #{value.class} => #{truncate(value.inspect)}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
errors = []
|
|
34
|
+
sps = " " * lvl
|
|
35
|
+
|
|
36
|
+
value.each_with_index do |val, idx|
|
|
37
|
+
next if validator.call(val)
|
|
38
|
+
|
|
39
|
+
msg = validator.message(val, "#{name}[#{idx}]", lvl + 1)
|
|
40
|
+
errors << "\n\t#{sps} - #{name}[#{idx}]: #{msg}"
|
|
41
|
+
|
|
42
|
+
if errors.size > 3
|
|
43
|
+
errors << "\n\t#{sps} - ..."
|
|
44
|
+
break
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
errors.join
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class Block
|
|
6
|
+
def self.valid?(*)
|
|
7
|
+
raise BadContractError, "#{name} contract is not allowed to use as argument contract"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_s
|
|
11
|
+
"Block"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class Bool
|
|
6
|
+
extend Ree::Contracts::Truncatable
|
|
7
|
+
|
|
8
|
+
def self.valid?(value)
|
|
9
|
+
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.message(value, name, lvl = 1)
|
|
13
|
+
"expected Bool, got #{value.class} => #{truncate(value.inspect)}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.to_s
|
|
17
|
+
"Bool"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class Eq
|
|
6
|
+
extend Ree::Contracts::ArgContracts::Squarable
|
|
7
|
+
include Ree::Contracts::Truncatable
|
|
8
|
+
|
|
9
|
+
attr_reader :contract
|
|
10
|
+
|
|
11
|
+
def initialize(contract)
|
|
12
|
+
@contract = contract
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def valid?(value)
|
|
16
|
+
value.equal?(contract)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_s
|
|
20
|
+
"Eq[#{contract.inspect}]"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def message(value, name, lvl = 1)
|
|
24
|
+
"expected #{truncate(self.to_s, 30)}, got #{truncate(value.inspect)}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class Exactly
|
|
6
|
+
extend Ree::Contracts::ArgContracts::Squarable
|
|
7
|
+
include Ree::Contracts::Truncatable
|
|
8
|
+
|
|
9
|
+
attr_reader :klass
|
|
10
|
+
|
|
11
|
+
def initialize(klass)
|
|
12
|
+
@klass = klass
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def valid?(value)
|
|
16
|
+
value.class == klass
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_s
|
|
20
|
+
"Exactly[#{klass.inspect}]"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def message(value, name, lvl = 1)
|
|
24
|
+
"expected #{truncate(self.to_s, 30)}, got #{truncate(value.class.inspect)}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class HashOf
|
|
6
|
+
extend Ree::Contracts::ArgContracts::Squarable
|
|
7
|
+
include Ree::Contracts::Truncatable
|
|
8
|
+
|
|
9
|
+
attr_reader :key_validator, :value_validator
|
|
10
|
+
|
|
11
|
+
def initialize(*contracts)
|
|
12
|
+
if contracts.size != 2
|
|
13
|
+
raise BadContractError, 'HashOf should accept exactly two contracts'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@key_validator = Validators.fetch_for(contracts[0])
|
|
17
|
+
@value_validator = Validators.fetch_for(contracts[1])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def valid?(value)
|
|
21
|
+
value.is_a?(Hash) &&
|
|
22
|
+
value.each_key.all?(&key_validator.method(:call)) &&
|
|
23
|
+
value.each_value.all?(&value_validator.method(:call))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_s
|
|
27
|
+
"HashOf[#{key_validator.to_s}, #{value_validator.to_s}]"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def message(value, name, lvl = 1)
|
|
31
|
+
unless value.is_a?(Hash)
|
|
32
|
+
return "expected Hash, got #{value.class} => #{truncate(value.inspect)}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
errors = []
|
|
36
|
+
sps = " " * lvl
|
|
37
|
+
|
|
38
|
+
value.each do |key, val|
|
|
39
|
+
if errors.size > 4
|
|
40
|
+
errors << "\n\t#{sps} - ..."
|
|
41
|
+
break
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
unless key_validator.call(key)
|
|
45
|
+
msg = key_validator.message(key, "#{name}[#{key.inspect}]", lvl + 1)
|
|
46
|
+
errors << "\n\t#{sps} - invalid key #{key.inspect}, #{msg}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
unless value_validator.call(val)
|
|
50
|
+
msg = key_validator.message(val, "#{name}[#{key.inspect}]", lvl + 1)
|
|
51
|
+
errors << "\n\t#{sps} - invalid value for #{key.inspect} key, #{msg}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
errors.join
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Ree::Contracts
|
|
6
|
+
module ArgContracts
|
|
7
|
+
class RestKeys; end
|
|
8
|
+
|
|
9
|
+
class Ksplat
|
|
10
|
+
include Ree::Contracts::Truncatable
|
|
11
|
+
|
|
12
|
+
attr_reader :validators, :rest_validator
|
|
13
|
+
|
|
14
|
+
def self.[](**contracts)
|
|
15
|
+
if contracts.empty?
|
|
16
|
+
raise BadContractError, 'Ksplat contract should accept at least one contract'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
new(**contracts)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(**contracts)
|
|
23
|
+
@contracts = contracts
|
|
24
|
+
@opt_dict = Set.new
|
|
25
|
+
|
|
26
|
+
@validators = contracts
|
|
27
|
+
.transform_values { Validators.fetch_for(_1) }
|
|
28
|
+
.transform_keys { |key|
|
|
29
|
+
next key unless key.is_a?(String) || key.is_a?(Symbol)
|
|
30
|
+
|
|
31
|
+
key_str = key.to_s
|
|
32
|
+
next key unless key_str.end_with?('?') && key_str.length > 1
|
|
33
|
+
|
|
34
|
+
opt_key = key_str[0..-2]
|
|
35
|
+
opt_key = opt_key.to_sym if key.is_a? Symbol
|
|
36
|
+
@opt_dict << opt_key
|
|
37
|
+
|
|
38
|
+
opt_key
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@rest_validator = @validators[RestKeys]
|
|
42
|
+
|
|
43
|
+
if @rest_validator
|
|
44
|
+
@validators.default = @rest_validator
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def valid?(value)
|
|
49
|
+
return false unless value.is_a?(Hash)
|
|
50
|
+
return false if value.has_key?(RestKeys)
|
|
51
|
+
|
|
52
|
+
is_valid = value.all? do |key, v|
|
|
53
|
+
if @validators.has_key?(key)
|
|
54
|
+
@validators[key].call(v)
|
|
55
|
+
elsif @rest_validator
|
|
56
|
+
@rest_validator.call(v)
|
|
57
|
+
else
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
return false if !is_valid
|
|
63
|
+
|
|
64
|
+
return false if @validators.detect do |key, validator|
|
|
65
|
+
next if key == RestKeys || optional?(key)
|
|
66
|
+
next if value.has_key?(key)
|
|
67
|
+
true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def to_s
|
|
74
|
+
"Ksplat[#{validators.map { |k, v| "#{key_to_s(k)} => #{v.to_s}" }.join(', ')}]"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def message(value, name, lvl = 1)
|
|
78
|
+
unless value.is_a?(Hash)
|
|
79
|
+
return "expected Hash, got #{value.class} => #{truncate(value.inspect)}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if value.has_key?(RestKeys)
|
|
83
|
+
return "RestKeys is a reserved key for Ksplat contract"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
errors = []
|
|
87
|
+
sps = " " * lvl
|
|
88
|
+
all_keys = (@validators.keys - [RestKeys] + value.keys).uniq
|
|
89
|
+
|
|
90
|
+
all_keys.each do |key|
|
|
91
|
+
validator = @validators[key]
|
|
92
|
+
|
|
93
|
+
if !validator
|
|
94
|
+
errors << "\n\t#{sps} - #{name}[#{key.inspect}]: unexpected"
|
|
95
|
+
next
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if !value.has_key?(key)
|
|
99
|
+
if !optional?(key)
|
|
100
|
+
errors << "\n\t#{sps} - #{name}[#{key.inspect}]: missing"
|
|
101
|
+
else
|
|
102
|
+
next
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
val = value[key]
|
|
107
|
+
next if validator.call(val)
|
|
108
|
+
|
|
109
|
+
msg = validator.message(val, "#{name}[#{key.inspect}]", lvl + 1)
|
|
110
|
+
errors << "\n\t#{sps} - #{name}[#{key.inspect}]: #{msg}"
|
|
111
|
+
|
|
112
|
+
if errors.size > 3
|
|
113
|
+
errors << "\n\t#{sps} - ..."
|
|
114
|
+
break
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
errors.join
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def key_to_s(key)
|
|
124
|
+
k = if optional?(key)
|
|
125
|
+
v = "#{key}?"
|
|
126
|
+
key.is_a?(String) ? v : v.to_sym
|
|
127
|
+
elsif key == RestKeys
|
|
128
|
+
'RestKeys'
|
|
129
|
+
else
|
|
130
|
+
key.to_s
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
k.inspect
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def optional?(key)
|
|
137
|
+
@opt_dict.include?(key)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class Kwargs < SimpleDelegator
|
|
6
|
+
def self.[](**contracts)
|
|
7
|
+
if contracts.empty?
|
|
8
|
+
raise BadContractError, 'Kwargs contract should accept at least one contract'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
new(**contracts)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :contracts
|
|
15
|
+
|
|
16
|
+
def initialize(**contracts)
|
|
17
|
+
@contracts = contracts
|
|
18
|
+
super(contracts)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class Nilor < Or
|
|
6
|
+
def initialize(*)
|
|
7
|
+
super
|
|
8
|
+
validators << Validators.fetch_for(nil)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
"Nilor[#{validators[0..-2].map(&:to_s).join(', ')}]"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class None
|
|
6
|
+
def self.valid?(*)
|
|
7
|
+
raise BadContractError, "None contract is not allowed to use as argument contract"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.to_s
|
|
11
|
+
"None"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class Or
|
|
6
|
+
extend Ree::Contracts::ArgContracts::Squarable
|
|
7
|
+
include Ree::Contracts::Truncatable
|
|
8
|
+
|
|
9
|
+
attr_reader :validators
|
|
10
|
+
|
|
11
|
+
def initialize(*contracts)
|
|
12
|
+
@validators = contracts.map(&Validators.method(:fetch_for))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_s
|
|
16
|
+
"Or[#{validators.map(&:to_s).join(', ')}]"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def valid?(value)
|
|
20
|
+
validators.any? do |validator|
|
|
21
|
+
validator.call(value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def message(value, name, lvl = 1)
|
|
26
|
+
"expected #{truncate(self.to_s, 30)}, got #{truncate(value.inspect)}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
module ArgContracts
|
|
5
|
+
class RangeOf
|
|
6
|
+
extend Ree::Contracts::ArgContracts::Squarable
|
|
7
|
+
include Ree::Contracts::Truncatable
|
|
8
|
+
|
|
9
|
+
attr_reader :validator
|
|
10
|
+
|
|
11
|
+
def initialize(*contracts)
|
|
12
|
+
if contracts.size != 1
|
|
13
|
+
raise BadContractError, 'RangeOf should accept exactly one contract'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@validator = Validators.fetch_for(contracts.first)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def valid?(value)
|
|
20
|
+
value.is_a?(Range) &&
|
|
21
|
+
validator.call(value.begin) &&
|
|
22
|
+
validator.call(value.end)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
"RangeOf[#{validator.to_s}]"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def message(value, name, lvl = 1)
|
|
30
|
+
unless value.is_a?(Range)
|
|
31
|
+
return "expected Range, got #{value.class} => #{truncate(value.inspect)}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
errors = []
|
|
35
|
+
sps = " " * lvl
|
|
36
|
+
|
|
37
|
+
unless validator.call(value.begin)
|
|
38
|
+
msg = validator.message(value.begin, "#{name}.begin", lvl + 1)
|
|
39
|
+
errors << "\n\t#{sps} - #{name}.begin: #{msg}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
unless validator.call(value.end)
|
|
43
|
+
msg = validator.message(value.end, "#{name}.end", lvl + 1)
|
|
44
|
+
errors << "\n\t#{sps} - #{name}.end: #{msg}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
errors.join
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Ree::Contracts
|
|
6
|
+
module ArgContracts
|
|
7
|
+
class SetOf
|
|
8
|
+
extend Ree::Contracts::ArgContracts::Squarable
|
|
9
|
+
include Ree::Contracts::Truncatable
|
|
10
|
+
|
|
11
|
+
attr_reader :validator
|
|
12
|
+
|
|
13
|
+
def initialize(*contracts)
|
|
14
|
+
if contracts.size != 1
|
|
15
|
+
raise BadContractError, 'SetOf should accept exactly one contract'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@validator = Validators.fetch_for(contracts.first)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def valid?(value)
|
|
22
|
+
value.is_a?(Set) &&
|
|
23
|
+
value.all?(&validator.method(:call))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_s
|
|
27
|
+
"SetOf[#{validator.to_s}]"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def message(value, name, lvl = 1)
|
|
31
|
+
unless value.is_a?(Set)
|
|
32
|
+
return "expected Set, got #{value.class} => #{truncate(value.inspect)}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
errors = []
|
|
36
|
+
sps = " " * lvl
|
|
37
|
+
|
|
38
|
+
value.each_with_index do |val, idx|
|
|
39
|
+
next if validator.call(val)
|
|
40
|
+
|
|
41
|
+
msg = validator.message(val, "#{name}[#{idx}]", lvl + 1)
|
|
42
|
+
errors << "\n\t#{sps} - #{name}[#{idx}]: #{msg}"
|
|
43
|
+
|
|
44
|
+
if errors.size > 3
|
|
45
|
+
errors << "\n\t#{sps} - ..."
|
|
46
|
+
break
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
errors.join
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|