ree 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|