entitlements-app 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bin/deploy-entitlements +10 -1
- data/lib/contracts-ruby2/CHANGELOG.markdown +115 -0
- data/lib/contracts-ruby2/Gemfile +17 -0
- data/lib/contracts-ruby2/LICENSE +23 -0
- data/lib/contracts-ruby2/README.md +108 -0
- data/lib/contracts-ruby2/Rakefile +8 -0
- data/lib/contracts-ruby2/TODO.markdown +6 -0
- data/lib/contracts-ruby2/TUTORIAL.md +773 -0
- data/lib/contracts-ruby2/benchmarks/bench.rb +67 -0
- data/lib/contracts-ruby2/benchmarks/hash.rb +69 -0
- data/lib/contracts-ruby2/benchmarks/invariants.rb +91 -0
- data/lib/contracts-ruby2/benchmarks/io.rb +62 -0
- data/lib/contracts-ruby2/benchmarks/wrap_test.rb +57 -0
- data/lib/contracts-ruby2/contracts.gemspec +17 -0
- data/lib/contracts-ruby2/cucumber.yml +1 -0
- data/lib/contracts-ruby2/dependabot.yml +20 -0
- data/lib/contracts-ruby2/features/README.md +17 -0
- data/lib/contracts-ruby2/features/basics/functype.feature +71 -0
- data/lib/contracts-ruby2/features/basics/pretty-print.feature +241 -0
- data/lib/contracts-ruby2/features/basics/simple_example.feature +210 -0
- data/lib/contracts-ruby2/features/builtin_contracts/README.md +22 -0
- data/lib/contracts-ruby2/features/builtin_contracts/and.feature +103 -0
- data/lib/contracts-ruby2/features/builtin_contracts/any.feature +44 -0
- data/lib/contracts-ruby2/features/builtin_contracts/args.feature +80 -0
- data/lib/contracts-ruby2/features/builtin_contracts/array_of.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/bool.feature +64 -0
- data/lib/contracts-ruby2/features/builtin_contracts/enum.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/eq.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/exactly.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/func.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/hash_of.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/int.feature +93 -0
- data/lib/contracts-ruby2/features/builtin_contracts/keyword_args.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/maybe.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/nat.feature +115 -0
- data/lib/contracts-ruby2/features/builtin_contracts/nat_pos.feature +119 -0
- data/lib/contracts-ruby2/features/builtin_contracts/neg.feature +115 -0
- data/lib/contracts-ruby2/features/builtin_contracts/none.feature +145 -0
- data/lib/contracts-ruby2/features/builtin_contracts/not.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/num.feature +64 -0
- data/lib/contracts-ruby2/features/builtin_contracts/or.feature +83 -0
- data/lib/contracts-ruby2/features/builtin_contracts/pos.feature +116 -0
- data/lib/contracts-ruby2/features/builtin_contracts/range_of.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/respond_to.feature +78 -0
- data/lib/contracts-ruby2/features/builtin_contracts/send.feature +147 -0
- data/lib/contracts-ruby2/features/builtin_contracts/set_of.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/xor.feature +99 -0
- data/lib/contracts-ruby2/features/support/env.rb +6 -0
- data/lib/contracts-ruby2/lib/contracts/attrs.rb +24 -0
- data/lib/contracts-ruby2/lib/contracts/builtin_contracts.rb +542 -0
- data/lib/contracts-ruby2/lib/contracts/call_with.rb +108 -0
- data/lib/contracts-ruby2/lib/contracts/core.rb +52 -0
- data/lib/contracts-ruby2/lib/contracts/decorators.rb +47 -0
- data/lib/contracts-ruby2/lib/contracts/engine/base.rb +136 -0
- data/lib/contracts-ruby2/lib/contracts/engine/eigenclass.rb +50 -0
- data/lib/contracts-ruby2/lib/contracts/engine/target.rb +70 -0
- data/lib/contracts-ruby2/lib/contracts/engine.rb +26 -0
- data/lib/contracts-ruby2/lib/contracts/errors.rb +71 -0
- data/lib/contracts-ruby2/lib/contracts/formatters.rb +136 -0
- data/lib/contracts-ruby2/lib/contracts/invariants.rb +68 -0
- data/lib/contracts-ruby2/lib/contracts/method_handler.rb +187 -0
- data/lib/contracts-ruby2/lib/contracts/method_reference.rb +100 -0
- data/lib/contracts-ruby2/lib/contracts/support.rb +61 -0
- data/lib/contracts-ruby2/lib/contracts/validators.rb +139 -0
- data/lib/contracts-ruby2/lib/contracts/version.rb +3 -0
- data/lib/contracts-ruby2/lib/contracts.rb +281 -0
- data/lib/contracts-ruby2/script/docs-release +3 -0
- data/lib/contracts-ruby2/script/docs-staging +3 -0
- data/lib/contracts-ruby2/script/rubocop.rb +5 -0
- data/lib/contracts-ruby2/spec/attrs_spec.rb +119 -0
- data/lib/contracts-ruby2/spec/builtin_contracts_spec.rb +461 -0
- data/lib/contracts-ruby2/spec/contracts_spec.rb +770 -0
- data/lib/contracts-ruby2/spec/fixtures/fixtures.rb +730 -0
- data/lib/contracts-ruby2/spec/invariants_spec.rb +17 -0
- data/lib/contracts-ruby2/spec/methods_spec.rb +54 -0
- data/lib/contracts-ruby2/spec/module_spec.rb +18 -0
- data/lib/contracts-ruby2/spec/override_validators_spec.rb +162 -0
- data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
- data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
- data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
- data/lib/contracts-ruby2/spec/spec_helper.rb +102 -0
- data/lib/contracts-ruby2/spec/support.rb +10 -0
- data/lib/contracts-ruby2/spec/support_spec.rb +21 -0
- data/lib/contracts-ruby2/spec/validators_spec.rb +47 -0
- data/lib/contracts-ruby3/CHANGELOG.markdown +117 -0
- data/lib/contracts-ruby3/Gemfile +21 -0
- data/lib/contracts-ruby3/LICENSE +23 -0
- data/lib/contracts-ruby3/README.md +114 -0
- data/lib/contracts-ruby3/Rakefile +10 -0
- data/lib/contracts-ruby3/TODO.markdown +6 -0
- data/lib/contracts-ruby3/TUTORIAL.md +773 -0
- data/lib/contracts-ruby3/benchmarks/bench.rb +67 -0
- data/lib/contracts-ruby3/benchmarks/hash.rb +69 -0
- data/lib/contracts-ruby3/benchmarks/invariants.rb +91 -0
- data/lib/contracts-ruby3/benchmarks/io.rb +62 -0
- data/lib/contracts-ruby3/benchmarks/wrap_test.rb +57 -0
- data/lib/contracts-ruby3/contracts.gemspec +20 -0
- data/lib/contracts-ruby3/cucumber.yml +1 -0
- data/lib/contracts-ruby3/dependabot.yml +20 -0
- data/lib/contracts-ruby3/features/README.md +17 -0
- data/lib/contracts-ruby3/features/basics/functype.feature +71 -0
- data/lib/contracts-ruby3/features/basics/pretty-print.feature +241 -0
- data/lib/contracts-ruby3/features/basics/simple_example.feature +210 -0
- data/lib/contracts-ruby3/features/builtin_contracts/README.md +22 -0
- data/lib/contracts-ruby3/features/builtin_contracts/and.feature +103 -0
- data/lib/contracts-ruby3/features/builtin_contracts/any.feature +44 -0
- data/lib/contracts-ruby3/features/builtin_contracts/args.feature +80 -0
- data/lib/contracts-ruby3/features/builtin_contracts/array_of.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/bool.feature +64 -0
- data/lib/contracts-ruby3/features/builtin_contracts/enum.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/eq.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/exactly.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/func.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/hash_of.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/int.feature +93 -0
- data/lib/contracts-ruby3/features/builtin_contracts/keyword_args.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/maybe.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/nat.feature +115 -0
- data/lib/contracts-ruby3/features/builtin_contracts/nat_pos.feature +119 -0
- data/lib/contracts-ruby3/features/builtin_contracts/neg.feature +115 -0
- data/lib/contracts-ruby3/features/builtin_contracts/none.feature +145 -0
- data/lib/contracts-ruby3/features/builtin_contracts/not.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/num.feature +64 -0
- data/lib/contracts-ruby3/features/builtin_contracts/or.feature +83 -0
- data/lib/contracts-ruby3/features/builtin_contracts/pos.feature +116 -0
- data/lib/contracts-ruby3/features/builtin_contracts/range_of.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/respond_to.feature +78 -0
- data/lib/contracts-ruby3/features/builtin_contracts/send.feature +147 -0
- data/lib/contracts-ruby3/features/builtin_contracts/set_of.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/xor.feature +99 -0
- data/lib/contracts-ruby3/features/support/env.rb +8 -0
- data/lib/contracts-ruby3/lib/contracts/attrs.rb +26 -0
- data/lib/contracts-ruby3/lib/contracts/builtin_contracts.rb +575 -0
- data/lib/contracts-ruby3/lib/contracts/call_with.rb +119 -0
- data/lib/contracts-ruby3/lib/contracts/core.rb +54 -0
- data/lib/contracts-ruby3/lib/contracts/decorators.rb +50 -0
- data/lib/contracts-ruby3/lib/contracts/engine/base.rb +137 -0
- data/lib/contracts-ruby3/lib/contracts/engine/eigenclass.rb +51 -0
- data/lib/contracts-ruby3/lib/contracts/engine/target.rb +72 -0
- data/lib/contracts-ruby3/lib/contracts/engine.rb +28 -0
- data/lib/contracts-ruby3/lib/contracts/errors.rb +74 -0
- data/lib/contracts-ruby3/lib/contracts/formatters.rb +140 -0
- data/lib/contracts-ruby3/lib/contracts/invariants.rb +72 -0
- data/lib/contracts-ruby3/lib/contracts/method_handler.rb +197 -0
- data/lib/contracts-ruby3/lib/contracts/method_reference.rb +102 -0
- data/lib/contracts-ruby3/lib/contracts/support.rb +63 -0
- data/lib/contracts-ruby3/lib/contracts/validators.rb +143 -0
- data/lib/contracts-ruby3/lib/contracts/version.rb +5 -0
- data/lib/contracts-ruby3/lib/contracts.rb +290 -0
- data/lib/contracts-ruby3/script/docs-release +3 -0
- data/lib/contracts-ruby3/script/docs-staging +3 -0
- data/lib/contracts-ruby3/script/rubocop.rb +5 -0
- data/lib/contracts-ruby3/spec/attrs_spec.rb +119 -0
- data/lib/contracts-ruby3/spec/builtin_contracts_spec.rb +457 -0
- data/lib/contracts-ruby3/spec/contracts_spec.rb +773 -0
- data/lib/contracts-ruby3/spec/fixtures/fixtures.rb +725 -0
- data/lib/contracts-ruby3/spec/invariants_spec.rb +17 -0
- data/lib/contracts-ruby3/spec/methods_spec.rb +54 -0
- data/lib/contracts-ruby3/spec/module_spec.rb +18 -0
- data/lib/contracts-ruby3/spec/override_validators_spec.rb +162 -0
- data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
- data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
- data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
- data/lib/contracts-ruby3/spec/spec_helper.rb +102 -0
- data/lib/contracts-ruby3/spec/support.rb +10 -0
- data/lib/contracts-ruby3/spec/support_spec.rb +21 -0
- data/lib/contracts-ruby3/spec/validators_spec.rb +47 -0
- data/lib/entitlements/data/groups/calculated/yaml.rb +7 -1
- data/lib/entitlements/data/people/yaml.rb +9 -1
- data/lib/entitlements/extras/ldap_group/rules/ldap_group.rb +5 -1
- data/lib/entitlements/extras/orgchart/person_methods.rb +7 -1
- data/lib/entitlements.rb +13 -2
- data/lib/ruby_version_check.rb +17 -0
- metadata +209 -14
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Contracts
|
4
|
+
# Handles class and instance methods addition
|
5
|
+
# Represents single such method
|
6
|
+
class MethodHandler
|
7
|
+
METHOD_REFERENCE_FACTORY = {
|
8
|
+
:class_methods => SingletonMethodReference,
|
9
|
+
:instance_methods => MethodReference,
|
10
|
+
}
|
11
|
+
|
12
|
+
RAW_METHOD_STRATEGY = {
|
13
|
+
:class_methods => lambda { |target, name| target.method(name) },
|
14
|
+
:instance_methods => lambda { |target, name| target.instance_method(name) },
|
15
|
+
}
|
16
|
+
|
17
|
+
# Creates new instance of MethodHandler
|
18
|
+
#
|
19
|
+
# @param [Symbol] method_name
|
20
|
+
# @param [Bool] is_class_method
|
21
|
+
# @param [Class] target - class that method got added to
|
22
|
+
def initialize(method_name, is_class_method, target)
|
23
|
+
@method_name = method_name
|
24
|
+
@is_class_method = is_class_method
|
25
|
+
@target = target
|
26
|
+
end
|
27
|
+
|
28
|
+
# Handles method addition
|
29
|
+
def handle
|
30
|
+
return unless engine?
|
31
|
+
return if decorators.empty?
|
32
|
+
|
33
|
+
validate_decorators!
|
34
|
+
validate_pattern_matching!
|
35
|
+
|
36
|
+
engine.add_method_decorator(method_type, method_name, decorator)
|
37
|
+
mark_pattern_matching_decorators
|
38
|
+
method_reference.make_alias(target)
|
39
|
+
redefine_method
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :method_name, :is_class_method, :target
|
45
|
+
|
46
|
+
def engine?
|
47
|
+
Engine.applied?(target)
|
48
|
+
end
|
49
|
+
|
50
|
+
def engine
|
51
|
+
Engine.fetch_from(target)
|
52
|
+
end
|
53
|
+
|
54
|
+
def decorators
|
55
|
+
@_decorators ||= engine.all_decorators
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_type
|
59
|
+
@_method_type ||= is_class_method ? :class_methods : :instance_methods
|
60
|
+
end
|
61
|
+
# _method_type is required for assigning it to local variable with
|
62
|
+
# the same name. See: #redefine_method
|
63
|
+
alias_method :_method_type, :method_type
|
64
|
+
|
65
|
+
def method_reference
|
66
|
+
@_method_reference ||= METHOD_REFERENCE_FACTORY[method_type].new(method_name, raw_method)
|
67
|
+
end
|
68
|
+
|
69
|
+
def raw_method
|
70
|
+
RAW_METHOD_STRATEGY[method_type].call(target, method_name)
|
71
|
+
end
|
72
|
+
|
73
|
+
def ignore_decorators?
|
74
|
+
ENV["NO_CONTRACTS"] && !pattern_matching?
|
75
|
+
end
|
76
|
+
|
77
|
+
def decorated_methods
|
78
|
+
@_decorated_methods ||= engine.decorated_methods_for(method_type, method_name)
|
79
|
+
end
|
80
|
+
|
81
|
+
def pattern_matching?
|
82
|
+
return @_pattern_matching if defined?(@_pattern_matching)
|
83
|
+
|
84
|
+
@_pattern_matching = decorated_methods.any? { |x| x.method != method_reference }
|
85
|
+
end
|
86
|
+
|
87
|
+
def mark_pattern_matching_decorators
|
88
|
+
return unless pattern_matching?
|
89
|
+
|
90
|
+
decorated_methods.each(&:pattern_match!)
|
91
|
+
end
|
92
|
+
|
93
|
+
def decorator
|
94
|
+
@_decorator ||= decorator_class.new(target, method_reference, *decorator_args)
|
95
|
+
end
|
96
|
+
|
97
|
+
def decorator_class
|
98
|
+
decorators.first[0]
|
99
|
+
end
|
100
|
+
|
101
|
+
def decorator_args
|
102
|
+
decorators.first[1]
|
103
|
+
end
|
104
|
+
|
105
|
+
def redefine_method
|
106
|
+
return if ignore_decorators?
|
107
|
+
|
108
|
+
# Those are required for instance_eval to be able to refer them
|
109
|
+
name = method_name
|
110
|
+
method_type = _method_type
|
111
|
+
current_engine = engine
|
112
|
+
|
113
|
+
# We are gonna redefine original method here
|
114
|
+
method_reference.make_definition(target) do |*args, **kargs, &blk|
|
115
|
+
engine = current_engine.nearest_decorated_ancestor
|
116
|
+
|
117
|
+
# If we weren't able to find any ancestor that has decorated methods
|
118
|
+
# FIXME : this looks like untested code (commenting it out doesn't make specs red)
|
119
|
+
unless engine
|
120
|
+
fail "Couldn't find decorator for method #{self.class.name}:#{name}.\nDoes this method look correct to you? If you are using contracts from rspec, rspec wraps classes in it's own class.\nLook at the specs for contracts.ruby as an example of how to write contracts in this case."
|
121
|
+
end
|
122
|
+
|
123
|
+
# Fetch decorated methods out of the contracts engine
|
124
|
+
decorated_methods = engine.decorated_methods_for(method_type, name)
|
125
|
+
|
126
|
+
# This adds support for overloading methods. Here we go
|
127
|
+
# through each method and call it with the arguments.
|
128
|
+
# If we get a failure_exception, we move to the next
|
129
|
+
# function. Otherwise we return the result.
|
130
|
+
# If we run out of functions, we raise the last error, but
|
131
|
+
# convert it to_contract_error.
|
132
|
+
|
133
|
+
expected_error = decorated_methods[0].failure_exception
|
134
|
+
last_error = nil
|
135
|
+
|
136
|
+
decorated_methods.each do |decorated_method|
|
137
|
+
result = decorated_method.call_with_inner(true, self, *args, **kargs, &blk)
|
138
|
+
return result unless result.is_a?(ParamContractError)
|
139
|
+
|
140
|
+
last_error = result
|
141
|
+
end
|
142
|
+
|
143
|
+
begin
|
144
|
+
if ::Contract.failure_callback(last_error&.data, use_pattern_matching: false)
|
145
|
+
decorated_methods.last.call_with_inner(false, self, *args, **kargs, &blk)
|
146
|
+
end
|
147
|
+
# rubocop:disable Naming/RescuedExceptionsVariableName
|
148
|
+
rescue expected_error => final_error
|
149
|
+
raise final_error.to_contract_error
|
150
|
+
# rubocop:enable Naming/RescuedExceptionsVariableName
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def validate_decorators!
|
156
|
+
return if decorators.size == 1
|
157
|
+
|
158
|
+
fail %{
|
159
|
+
Oops, it looks like method '#{method_name}' has multiple contracts:
|
160
|
+
#{decorators.map { |x| x[1][0].inspect }.join("\n")}
|
161
|
+
|
162
|
+
Did you accidentally put more than one contract on a single function, like so?
|
163
|
+
|
164
|
+
Contract String => String
|
165
|
+
Contract Num => String
|
166
|
+
def foo x
|
167
|
+
end
|
168
|
+
|
169
|
+
If you did NOT, then you have probably discovered a bug in this library.
|
170
|
+
Please file it along with the relevant code at:
|
171
|
+
https://github.com/egonSchiele/contracts.ruby/issues
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
def validate_pattern_matching!
|
176
|
+
new_args_contract = decorator.args_contracts
|
177
|
+
matched = decorated_methods.select do |contract|
|
178
|
+
contract.args_contracts == new_args_contract
|
179
|
+
end
|
180
|
+
|
181
|
+
return if matched.empty?
|
182
|
+
|
183
|
+
fail ContractError.new(
|
184
|
+
%{
|
185
|
+
It looks like you are trying to use pattern-matching, but
|
186
|
+
multiple definitions for function '#{method_name}' have the same
|
187
|
+
contract for input parameters:
|
188
|
+
|
189
|
+
#{(matched + [decorator]).map(&:to_s).join("\n")}
|
190
|
+
|
191
|
+
Each definition needs to have a different contract for the parameters.
|
192
|
+
},
|
193
|
+
{},
|
194
|
+
)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Contracts
|
4
|
+
# MethodReference represents original method reference that was
|
5
|
+
# decorated by contracts.ruby. Used for instance methods.
|
6
|
+
class MethodReference
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# name - name of the method
|
10
|
+
# method - method object
|
11
|
+
def initialize(name, method)
|
12
|
+
@name = name
|
13
|
+
@method = method
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns method_position, delegates to Support.method_position
|
17
|
+
def method_position
|
18
|
+
Support.method_position(@method)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Makes a method re-definition in proper way
|
22
|
+
def make_definition(this, &blk)
|
23
|
+
is_private = private?(this)
|
24
|
+
is_protected = protected?(this)
|
25
|
+
alias_target(this).send(:define_method, name, &blk)
|
26
|
+
make_private(this) if is_private
|
27
|
+
make_protected(this) if is_protected
|
28
|
+
end
|
29
|
+
|
30
|
+
# Aliases original method to a special unique name, which is known
|
31
|
+
# only to this class. Usually done right before re-defining the
|
32
|
+
# method.
|
33
|
+
def make_alias(this)
|
34
|
+
_aliased_name = aliased_name
|
35
|
+
original_name = name
|
36
|
+
|
37
|
+
alias_target(this).class_eval do
|
38
|
+
alias_method _aliased_name, original_name
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Calls original method on specified `this` argument with
|
43
|
+
# specified arguments `args` and block `&blk`.
|
44
|
+
def send_to(this, *args, **kargs, &blk)
|
45
|
+
this.send(aliased_name, *args, **kargs, &blk)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Makes a method private
|
51
|
+
def make_private(this)
|
52
|
+
original_name = name
|
53
|
+
alias_target(this).class_eval { private original_name }
|
54
|
+
end
|
55
|
+
|
56
|
+
def private?(this)
|
57
|
+
this.private_instance_methods.map(&:to_sym).include?(name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def protected?(this)
|
61
|
+
this.protected_instance_methods.map(&:to_sym).include?(name)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Makes a method protected
|
65
|
+
def make_protected(this)
|
66
|
+
original_name = name
|
67
|
+
alias_target(this).class_eval { protected original_name }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns alias target for instance methods, subject to be
|
71
|
+
# overriden in subclasses.
|
72
|
+
def alias_target(this)
|
73
|
+
this
|
74
|
+
end
|
75
|
+
|
76
|
+
def aliased_name
|
77
|
+
@_original_name ||= construct_unique_name
|
78
|
+
end
|
79
|
+
|
80
|
+
def construct_unique_name
|
81
|
+
:"__contracts_ruby_original_#{name}_#{Support.unique_id}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# The same as MethodReference, but used for singleton methods.
|
86
|
+
class SingletonMethodReference < MethodReference
|
87
|
+
private
|
88
|
+
|
89
|
+
def private?(this)
|
90
|
+
this.private_methods.map(&:to_sym).include?(name)
|
91
|
+
end
|
92
|
+
|
93
|
+
def protected?(this)
|
94
|
+
this.protected_methods.map(&:to_sym).include?(name)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Return alias target for singleton methods.
|
98
|
+
def alias_target(this)
|
99
|
+
Support.eigenclass_of this
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Contracts
|
4
|
+
module Support
|
5
|
+
class << self
|
6
|
+
def method_position(method)
|
7
|
+
return method.method_position if method.is_a?(MethodReference)
|
8
|
+
|
9
|
+
file, line = method.source_location
|
10
|
+
if file.nil? || line.nil?
|
11
|
+
""
|
12
|
+
else
|
13
|
+
"#{file}:#{line}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_name(method)
|
18
|
+
method.is_a?(Proc) ? "Proc" : method.name
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generates unique id, which can be used as a part of identifier
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
# Contracts::Support.unique_id # => "i53u6tiw5hbo"
|
25
|
+
def unique_id
|
26
|
+
# Consider using SecureRandom.hex here, and benchmark which one is better
|
27
|
+
(Time.now.to_f * 1000).to_i.to_s(36) + rand(1_000_000).to_s(36)
|
28
|
+
end
|
29
|
+
|
30
|
+
def contract_id(contract)
|
31
|
+
contract.object_id
|
32
|
+
end
|
33
|
+
|
34
|
+
def eigenclass_hierarchy_supported?
|
35
|
+
RUBY_PLATFORM != "java" || RUBY_VERSION.to_f >= 2.0
|
36
|
+
end
|
37
|
+
|
38
|
+
def eigenclass_of(target)
|
39
|
+
class << target; self; end
|
40
|
+
end
|
41
|
+
|
42
|
+
def eigenclass?(target)
|
43
|
+
module_eigenclass?(target) ||
|
44
|
+
target <= eigenclass_of(Object)
|
45
|
+
end
|
46
|
+
|
47
|
+
def indent_string(string, amount)
|
48
|
+
string.gsub(
|
49
|
+
/^(?!$)/,
|
50
|
+
(string[/^[ \t]/] || " ") * amount,
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Module eigenclass can be detected by its ancestor chain
|
57
|
+
# containing a Module
|
58
|
+
def module_eigenclass?(target)
|
59
|
+
target < Module
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Contracts
|
4
|
+
module Validators
|
5
|
+
DEFAULT_VALIDATOR_STRATEGIES = {
|
6
|
+
# e.g. lambda {true}
|
7
|
+
Proc => lambda { |contract| contract },
|
8
|
+
|
9
|
+
# e.g. [Num, String]
|
10
|
+
# TODO: account for these errors too
|
11
|
+
Array => lambda do |contract|
|
12
|
+
lambda do |arg|
|
13
|
+
return false unless arg.is_a?(Array) && arg.length == contract.length
|
14
|
+
|
15
|
+
arg.zip(contract).all? do |_arg, _contract|
|
16
|
+
Contract.valid?(_arg, _contract)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end,
|
20
|
+
|
21
|
+
# e.g. { :a => Num, :b => String }
|
22
|
+
Hash => lambda do |contract|
|
23
|
+
lambda do |arg|
|
24
|
+
return false unless arg.is_a?(Hash)
|
25
|
+
|
26
|
+
contract.keys.all? do |k|
|
27
|
+
Contract.valid?(arg[k], contract[k])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end,
|
31
|
+
|
32
|
+
Range => lambda do |contract|
|
33
|
+
lambda do |arg|
|
34
|
+
contract.include?(arg)
|
35
|
+
end
|
36
|
+
end,
|
37
|
+
|
38
|
+
Regexp => lambda do |contract|
|
39
|
+
lambda do |arg|
|
40
|
+
arg =~ contract
|
41
|
+
end
|
42
|
+
end,
|
43
|
+
|
44
|
+
Contracts::Args => lambda do |contract|
|
45
|
+
lambda do |arg|
|
46
|
+
Contract.valid?(arg, contract.contract)
|
47
|
+
end
|
48
|
+
end,
|
49
|
+
|
50
|
+
Contracts::Func => lambda do |_|
|
51
|
+
lambda do |arg|
|
52
|
+
arg.is_a?(Method) || arg.is_a?(Proc)
|
53
|
+
end
|
54
|
+
end,
|
55
|
+
|
56
|
+
:valid => lambda do |contract|
|
57
|
+
lambda { |arg| contract.valid?(arg) }
|
58
|
+
end,
|
59
|
+
|
60
|
+
:class => lambda do |contract|
|
61
|
+
lambda { |arg| arg.is_a?(contract) }
|
62
|
+
end,
|
63
|
+
|
64
|
+
:default => lambda do |contract|
|
65
|
+
lambda { |arg| contract == arg }
|
66
|
+
end,
|
67
|
+
}.freeze
|
68
|
+
|
69
|
+
# Allows to override validator with custom one.
|
70
|
+
# Example:
|
71
|
+
# Contract.override_validator(Array) do |contract|
|
72
|
+
# lambda do |arg|
|
73
|
+
# # .. implementation for Array contract ..
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# Contract.override_validator(:class) do |contract|
|
78
|
+
# lambda do |arg|
|
79
|
+
# arg.is_a?(contract) || arg.is_a?(RSpec::Mocks::Double)
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
def override_validator(name, &block)
|
83
|
+
validator_strategies[name] = block
|
84
|
+
end
|
85
|
+
|
86
|
+
# This is a little weird. For each contract
|
87
|
+
# we pre-make a proc to validate it so we
|
88
|
+
# don't have to go through this decision tree every time.
|
89
|
+
# Seems silly but it saves us a bunch of time (4.3sec vs 5.2sec)
|
90
|
+
def make_validator!(contract)
|
91
|
+
klass = contract.class
|
92
|
+
key = if validator_strategies.key?(klass)
|
93
|
+
klass
|
94
|
+
else
|
95
|
+
if contract.respond_to? :valid?
|
96
|
+
:valid
|
97
|
+
elsif [Class, Module].include?(klass)
|
98
|
+
:class
|
99
|
+
else
|
100
|
+
:default
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
validator_strategies[key].call(contract)
|
105
|
+
end
|
106
|
+
|
107
|
+
def make_validator(contract)
|
108
|
+
contract_id = Support.contract_id(contract)
|
109
|
+
|
110
|
+
if memoized_validators.key?(contract_id)
|
111
|
+
return memoized_validators[contract_id]
|
112
|
+
end
|
113
|
+
|
114
|
+
memoized_validators[contract_id] = make_validator!(contract)
|
115
|
+
end
|
116
|
+
|
117
|
+
# @private
|
118
|
+
def reset_validators
|
119
|
+
clean_memoized_validators
|
120
|
+
restore_validators
|
121
|
+
end
|
122
|
+
|
123
|
+
# @private
|
124
|
+
def validator_strategies
|
125
|
+
@_validator_strategies ||= restore_validators
|
126
|
+
end
|
127
|
+
|
128
|
+
# @private
|
129
|
+
def restore_validators
|
130
|
+
@_validator_strategies = DEFAULT_VALIDATOR_STRATEGIES.dup
|
131
|
+
end
|
132
|
+
|
133
|
+
# @private
|
134
|
+
def memoized_validators
|
135
|
+
@_memoized_validators ||= clean_memoized_validators
|
136
|
+
end
|
137
|
+
|
138
|
+
# @private
|
139
|
+
def clean_memoized_validators
|
140
|
+
@_memoized_validators = {}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|