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,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class MethodDecorator
|
|
5
|
+
class << self
|
|
6
|
+
def decorator_id(target, method_name, is_class_method)
|
|
7
|
+
"#{target.object_id}#{target.name}#{method_name}#{is_class_method}"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def add_decorator(decorator)
|
|
11
|
+
decorators[decorator.id] = decorator
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get_decorator(id)
|
|
15
|
+
decorators[id]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def decorators
|
|
19
|
+
@decorators ||= {}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
include Ree::Args
|
|
24
|
+
|
|
25
|
+
attr_reader :target # [Class] class ot superclass of decorated method
|
|
26
|
+
attr_reader :method_name # [Symbol] original method name
|
|
27
|
+
attr_reader :method_parameters # [Array] list of original method parameters
|
|
28
|
+
attr_reader :is_class_method # [Bool]
|
|
29
|
+
attr_reader :contract_definition # [ContractDefinition] definition of contract
|
|
30
|
+
attr_reader :errors # [Nilor[ArrayOf[Class]]] list of thrown errors
|
|
31
|
+
attr_reader :doc # [Nilor[String]] rdoc
|
|
32
|
+
attr_reader :args # [CalledArgsValidator]
|
|
33
|
+
attr_reader :return_validator # [BaseValidator] validator for return value
|
|
34
|
+
|
|
35
|
+
def initialize(method_name, is_class_method, target)
|
|
36
|
+
check_arg(method_name, :method_name, Symbol)
|
|
37
|
+
check_bool(is_class_method, :is_class_method)
|
|
38
|
+
check_arg_any(target, :target, [Class, Module])
|
|
39
|
+
|
|
40
|
+
engine = Engine.fetch_for(target)
|
|
41
|
+
|
|
42
|
+
@method_name = method_name
|
|
43
|
+
@is_class_method = is_class_method
|
|
44
|
+
@target = target
|
|
45
|
+
@contract_definition = engine.fetch_contract
|
|
46
|
+
@errors = engine.fetch_errors
|
|
47
|
+
@doc = engine.fetch_doc
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def call
|
|
51
|
+
return if Ree::Contracts.no_contracts?
|
|
52
|
+
return unless contract_definition
|
|
53
|
+
|
|
54
|
+
self.class.add_decorator(self)
|
|
55
|
+
|
|
56
|
+
@method_parameters = alias_target
|
|
57
|
+
.instance_method(method_name)
|
|
58
|
+
.parameters
|
|
59
|
+
.freeze
|
|
60
|
+
|
|
61
|
+
@args = CalledArgsValidator.new(
|
|
62
|
+
contract_definition,
|
|
63
|
+
method_parameters,
|
|
64
|
+
printed_name
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@return_validator = Validators.fetch_for(contract_definition.return_contract)
|
|
68
|
+
|
|
69
|
+
make_alias
|
|
70
|
+
make_definition
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def execute_on(target, args, kwargs, &blk)
|
|
74
|
+
@args.call(args, kwargs, blk)
|
|
75
|
+
result = target.send(method_alias, *args, **kwargs, &blk)
|
|
76
|
+
|
|
77
|
+
if !return_validator.call(result)
|
|
78
|
+
raise ReturnContractError, "Invalid return value for #{printed_name}\n #{
|
|
79
|
+
return_validator.message(result, 'returns', 0).strip
|
|
80
|
+
}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
result
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Unique ID of this Method Decorator
|
|
87
|
+
def id
|
|
88
|
+
@id ||= self.class.decorator_id(target, method_name, is_class_method)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Alias name for original method
|
|
92
|
+
def method_alias
|
|
93
|
+
@method_alias ||= :"__original_#{method_name}_#{SecureRandom.hex}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Target class to be used for alias method definition
|
|
97
|
+
def alias_target
|
|
98
|
+
@alias_target ||= begin
|
|
99
|
+
return Utils.eigenclass_of(target) if is_class_method
|
|
100
|
+
target
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def make_alias
|
|
107
|
+
alias_target.alias_method(method_alias, method_name)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def make_definition
|
|
111
|
+
file, line = alias_target.instance_method(method_alias).source_location
|
|
112
|
+
|
|
113
|
+
alias_target.class_eval(%Q(
|
|
114
|
+
def #{method_name}(*args, **kwargs, &blk)
|
|
115
|
+
decorator = Ree::Contracts::MethodDecorator.get_decorator('#{id}')
|
|
116
|
+
decorator.execute_on(self, args, kwargs, &blk)
|
|
117
|
+
end
|
|
118
|
+
), file, line - 3)
|
|
119
|
+
|
|
120
|
+
make_private if private_method?
|
|
121
|
+
make_protected if protected_method?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def private_method?
|
|
125
|
+
return target.private_methods.include?(method_alias) if is_class_method
|
|
126
|
+
target.private_instance_methods.include?(method_alias)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def protected_method?
|
|
130
|
+
return target.protected_methods.include?(method_alias) if is_class_method
|
|
131
|
+
target.protected_instance_methods.include?(method_alias)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def make_private
|
|
135
|
+
_method_name = method_name
|
|
136
|
+
_method_alias = method_alias
|
|
137
|
+
|
|
138
|
+
alias_target.class_eval do
|
|
139
|
+
private _method_name
|
|
140
|
+
private _method_alias
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def make_protected
|
|
145
|
+
_method_name = method_name
|
|
146
|
+
_method_alias = method_alias
|
|
147
|
+
|
|
148
|
+
alias_target.class_eval do
|
|
149
|
+
protected _method_name
|
|
150
|
+
protected _method_alias
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def printed_name
|
|
155
|
+
@printed_name ||= "#{target}#{is_class_method ? '.' : '#'}#{method_name}"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class ArrayValidator < BaseValidator
|
|
5
|
+
attr_reader :validators
|
|
6
|
+
|
|
7
|
+
def initialize(contract)
|
|
8
|
+
super(contract)
|
|
9
|
+
@validators = contract.map { |cont| Validators.fetch_for(cont) }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_s
|
|
13
|
+
"[#{validators.map(&:to_s).join(', ')}]"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(value)
|
|
17
|
+
return false unless value.is_a?(Array) && value.length == validators.length
|
|
18
|
+
|
|
19
|
+
value.zip(validators).all? do |val, validator|
|
|
20
|
+
validator.call(val)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def message(value, name, lvl = 1)
|
|
25
|
+
unless value.is_a?(Array)
|
|
26
|
+
return "expected Array, got #{value.class} => #{truncate(value.inspect)}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
unless value.length == validators.length
|
|
30
|
+
return "expected to have #{validators.length} #{pluralize(validators.length, 'item', 'items')}, got #{value.length} #{pluralize(value.length, 'item', 'items')} => #{truncate(value.inspect)}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
errors = []
|
|
34
|
+
sps = " " * lvl
|
|
35
|
+
|
|
36
|
+
value.zip(validators).each_with_index do |(val, validator), 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
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class BaseValidator
|
|
5
|
+
include Ree::Contracts::Truncatable
|
|
6
|
+
|
|
7
|
+
attr_reader :contract
|
|
8
|
+
|
|
9
|
+
def initialize(contract)
|
|
10
|
+
@contract = contract
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(value)
|
|
18
|
+
raise NotImplementedError
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def pluralize(num, single, plural)
|
|
24
|
+
num == 1 ? single : plural
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class ClassValidator < BaseValidator
|
|
5
|
+
def call(value)
|
|
6
|
+
value.is_a?(contract)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
contract.name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def message(value, _name, _lvl = 1)
|
|
14
|
+
"expected #{contract}, got #{value.class} => #{truncate(value.inspect)}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class DefaultValidator < BaseValidator
|
|
5
|
+
def call(value)
|
|
6
|
+
contract == value
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
truncate(
|
|
11
|
+
contract.respond_to?(:to_s) ? contract.to_s : contract.inspect,
|
|
12
|
+
10
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def message(value, _name, _lvl = 1)
|
|
17
|
+
"expected #{truncate(contract.inspect, 40)}, got #{truncate(value.inspect, 40)}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Ree::Contracts
|
|
6
|
+
class HashValidator < BaseValidator
|
|
7
|
+
attr_reader :opt_dict, :validators
|
|
8
|
+
|
|
9
|
+
def initialize(contract)
|
|
10
|
+
super(contract)
|
|
11
|
+
|
|
12
|
+
@opt_dict = Set.new
|
|
13
|
+
|
|
14
|
+
@validators = contract
|
|
15
|
+
.transform_values { |cont| Validators.fetch_for(cont) }
|
|
16
|
+
.transform_keys { |key|
|
|
17
|
+
next key unless key.is_a?(String) || key.is_a?(Symbol)
|
|
18
|
+
|
|
19
|
+
key_str = key.to_s
|
|
20
|
+
next key unless key_str.end_with?('?') && key_str.length > 1
|
|
21
|
+
|
|
22
|
+
opt_key = key_str[0..-2]
|
|
23
|
+
opt_key = opt_key.to_sym if key.is_a? Symbol
|
|
24
|
+
@opt_dict << opt_key
|
|
25
|
+
|
|
26
|
+
opt_key
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def call(value)
|
|
31
|
+
return false unless value.is_a?(Hash)
|
|
32
|
+
return false unless value.all? { |key, _| validators.has_key?(key) }
|
|
33
|
+
|
|
34
|
+
validators.all? do |key, validator|
|
|
35
|
+
value.has_key?(key) ? validator.call(value[key]) : optional?(key)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_s
|
|
40
|
+
"{#{validators.map { |k, v| "#{key_to_s(k)} => #{v.to_s}" }.join(', ')}}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def message(value, name, lvl = 1)
|
|
44
|
+
unless value.is_a?(Hash)
|
|
45
|
+
return "expected Hash, got #{value.class} => #{truncate(value.inspect)}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
errors = []
|
|
49
|
+
sps = " " * lvl
|
|
50
|
+
|
|
51
|
+
validators.each do |key, validator|
|
|
52
|
+
if errors.size > 3
|
|
53
|
+
errors << "\n\t#{sps} - ..."
|
|
54
|
+
break
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
unless value.key?(key)
|
|
58
|
+
errors << "\n\t#{sps} - #{name}[#{key.inspect}]: missing" unless optional?(key)
|
|
59
|
+
next
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
val = value[key]
|
|
63
|
+
next if validator.call(val)
|
|
64
|
+
|
|
65
|
+
msg = validator.message(val, "#{name}[#{key.inspect}]", lvl + 1)
|
|
66
|
+
errors << "\n\t#{sps} - #{name}[#{key.inspect}]: #{msg}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
value.each do |key, val|
|
|
70
|
+
if errors.size > 3
|
|
71
|
+
errors << "\n\t#{sps} - ..."
|
|
72
|
+
break
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
next if validators.key?(key)
|
|
76
|
+
|
|
77
|
+
errors << "\n\t#{sps} - #{name}[#{key.inspect}]: unexpected"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
errors.join
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def key_to_s(key)
|
|
86
|
+
k =if optional?(key)
|
|
87
|
+
v = "#{key}?"
|
|
88
|
+
key.is_a?(String) ? v : v.to_sym
|
|
89
|
+
else
|
|
90
|
+
key
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
k.inspect
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def optional?(key)
|
|
97
|
+
opt_dict.include?(key)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class ProcValidator < BaseValidator
|
|
5
|
+
def call(value)
|
|
6
|
+
contract.call value
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
'Proc#call'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def message(value, name, lvl = 1)
|
|
14
|
+
"proc validation failed for #{truncate(value.inspect)}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class RangeValidator < BaseValidator
|
|
5
|
+
def call(value)
|
|
6
|
+
contract.include? value
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
contract.inspect
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def message(value, _name, _lvl = 1)
|
|
14
|
+
"expected value to be in range #{contract}, got #{truncate(value.inspect)}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class RegexpValidator < BaseValidator
|
|
5
|
+
def call(value)
|
|
6
|
+
value =~ contract
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
contract.to_s
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def message(value, name, lvl = 1)
|
|
14
|
+
"expected to match #{contract.to_s}, got #{truncate(value.inspect)}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ree::Contracts
|
|
4
|
+
class ValidValidator < BaseValidator
|
|
5
|
+
def call(value)
|
|
6
|
+
contract.valid? value
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
if contract.respond_to?(:to_s)
|
|
11
|
+
contract.to_s
|
|
12
|
+
else
|
|
13
|
+
klass = contract.is_a?(Class) ? contract : contract.class
|
|
14
|
+
klass.name
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def message(value, name, lvl = 1)
|
|
19
|
+
if contract.respond_to?(:message)
|
|
20
|
+
contract.message(value, name, lvl)
|
|
21
|
+
else
|
|
22
|
+
obj_name = contract.is_a?(Class) ? contract : contract.class
|
|
23
|
+
op = contract.is_a?(Class) ? '.' : '#'
|
|
24
|
+
"#{obj_name}#{op}valid? failed for #{truncate(value.inspect)}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Ree::Contracts
|
|
6
|
+
class Validators
|
|
7
|
+
FORBIDDEN_CONTRACTS = Set.new([
|
|
8
|
+
ArgContracts::None, ArgContracts::Kwargs,
|
|
9
|
+
ArgContracts::Block, ArgContracts::Optblock,
|
|
10
|
+
ArgContracts::SplatOf, ArgContracts::Splat
|
|
11
|
+
])
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def fetch_for(contract)
|
|
15
|
+
validators[contract.object_id] ||= build(contract)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def build(contract)
|
|
21
|
+
if FORBIDDEN_CONTRACTS.include?(contract)
|
|
22
|
+
name = contract.name.split("::").last
|
|
23
|
+
raise Ree::Error.new("#{name} is not supported arg validator", :invalid_dsl_usage)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
return ProcValidator.new(contract) if contract.is_a?(Proc)
|
|
27
|
+
return ArrayValidator.new(contract) if contract.is_a?(Array)
|
|
28
|
+
return HashValidator.new(contract) if contract.is_a?(Hash)
|
|
29
|
+
return RangeValidator.new(contract) if contract.is_a?(Range)
|
|
30
|
+
return RegexpValidator.new(contract) if contract.is_a?(Regexp)
|
|
31
|
+
return ValidValidator.new(contract) if contract.respond_to?(:valid?)
|
|
32
|
+
return ClassValidator.new(contract) if contract.is_a?(Class) || contract.is_a?(Module)
|
|
33
|
+
|
|
34
|
+
DefaultValidator.new(contract)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validators
|
|
38
|
+
@validators ||= {}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal = true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'forwardable'
|
|
5
|
+
require 'delegate'
|
|
6
|
+
|
|
7
|
+
module Ree::Contracts
|
|
8
|
+
autoload :ArgContracts, 'ree/contracts/arg_contracts'
|
|
9
|
+
autoload :ArrayValidator, 'ree/contracts/validators/array_validator'
|
|
10
|
+
autoload :BadContractError, 'ree/contracts/errors/bad_contract_error'
|
|
11
|
+
autoload :BaseValidator, 'ree/contracts/validators/base_validator'
|
|
12
|
+
autoload :CalledArgsValidator, 'ree/contracts/called_args_validator'
|
|
13
|
+
autoload :ClassValidator, 'ree/contracts/validators/class_validator'
|
|
14
|
+
autoload :Contractable, 'ree/contracts/contractable'
|
|
15
|
+
autoload :ContractDefinition, 'ree/contracts/contract_definition'
|
|
16
|
+
autoload :ContractError, 'ree/contracts/errors/contract_error'
|
|
17
|
+
autoload :Core, 'ree/contracts/core'
|
|
18
|
+
autoload :DefaultValidator, 'ree/contracts/validators/default_validator'
|
|
19
|
+
autoload :Engine, 'ree/contracts/engine'
|
|
20
|
+
autoload :EngineProxy, 'ree/contracts/engine_proxy'
|
|
21
|
+
autoload :Error, 'ree/contracts/errors/error'
|
|
22
|
+
autoload :HashValidator, 'ree/contracts/validators/hash_validator'
|
|
23
|
+
autoload :MethodDecorator, 'ree/contracts/method_decorator'
|
|
24
|
+
autoload :ProcValidator, 'ree/contracts/validators/proc_validator'
|
|
25
|
+
autoload :RangeValidator, 'ree/contracts/validators/range_validator'
|
|
26
|
+
autoload :RegexpValidator, 'ree/contracts/validators/regexp_validator'
|
|
27
|
+
autoload :ReturnContractError, 'ree/contracts/errors/return_contract_error'
|
|
28
|
+
autoload :Truncatable, 'ree/contracts/truncatable'
|
|
29
|
+
autoload :Utils, 'ree/contracts/utils'
|
|
30
|
+
autoload :Validators, 'ree/contracts/validators'
|
|
31
|
+
autoload :ValidValidator, 'ree/contracts/validators/valid_validator'
|
|
32
|
+
|
|
33
|
+
def self.no_contracts?
|
|
34
|
+
ENV["NO_CONTRACTS"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.get_method_decorator(target, method_name, scope: :instance)
|
|
38
|
+
unless scope == :instance || scope == :class
|
|
39
|
+
raise Ree::Error.new(':scope should be either :class or :instance', :invalid_dsl_usage)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
decorator_id = MethodDecorator.decorator_id(target, method_name, scope == :class)
|
|
43
|
+
MethodDecorator.get_decorator(decorator_id)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal = true
|
|
2
|
+
|
|
3
|
+
class Ree::LinkValidator
|
|
4
|
+
def initialize(packages_facade)
|
|
5
|
+
@packages_facade = packages_facade
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Validates existance and uniqueness of linked object
|
|
9
|
+
# @param [Ree::Object] object
|
|
10
|
+
# @param [Ree::ObjectLink] link
|
|
11
|
+
# @return [nil]
|
|
12
|
+
def call(object, link)
|
|
13
|
+
link_package = @packages_facade.get_package(link.package_name)
|
|
14
|
+
link_object = link_package.get_object(link.object_name)
|
|
15
|
+
|
|
16
|
+
if !link_object
|
|
17
|
+
msg = <<~DOC
|
|
18
|
+
object: :#{object.name}
|
|
19
|
+
path: #{Ree::PathHelper.abs_object_path(object)}
|
|
20
|
+
error: Unable to find :#{link.object_name} in :#{link.package_name} package
|
|
21
|
+
DOC
|
|
22
|
+
|
|
23
|
+
raise Ree::Error.new(msg, :invalid_dsl_usage)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
existing_link = link_object.links.detect do |inj|
|
|
27
|
+
inj.object_name == link.object_name && inj.package_name == link.package_name
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if existing_link
|
|
31
|
+
msg = <<~DOC
|
|
32
|
+
object: :#{object.name}
|
|
33
|
+
path: #{Ree::PathHelper.abs_object_path(object)}
|
|
34
|
+
error: Duplicate link :#{link.object_name}
|
|
35
|
+
DOC
|
|
36
|
+
|
|
37
|
+
raise Ree::Error.new(msg, :invalid_dsl_usage)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
end
|