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,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
|